xref: /rk3399_rockchip-uboot/net/fastboot.c (revision 965eda410b8d28439dc1ba4f76061880d72978fd)
1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6 
7 #include <common.h>
8 #include <net.h>
9 #include <net/fastboot.h>
10 #include <stdlib.h>
11 #include <version.h>
12 
13 /* Fastboot port # defined in spec */
14 #define WELL_KNOWN_PORT 5554
15 
16 enum {
17 	FASTBOOT_ERROR = 0,
18 	FASTBOOT_QUERY = 1,
19 	FASTBOOT_INIT = 2,
20 	FASTBOOT_FASTBOOT = 3,
21 };
22 
23 struct __attribute__((packed)) fastboot_header {
24 	uchar id;
25 	uchar flags;
26 	unsigned short seq;
27 };
28 
29 #define PACKET_SIZE 1024
30 #define FASTBOOT_HEADER_SIZE sizeof(struct fastboot_header)
31 
32 /* Sequence number sent for every packet */
33 static unsigned short fb_sequence_number = 1;
34 static const unsigned short fb_packet_size = PACKET_SIZE;
35 static const unsigned short fb_udp_version = 1;
36 
37 /* Keep track of last packet for resubmission */
38 static uchar last_packet[PACKET_SIZE];
39 static unsigned int last_packet_len = 0;
40 
41 static struct in_addr fastboot_remote_ip;
42 /* The UDP port at their end */
43 static int fastboot_remote_port;
44 /* The UDP port at our end */
45 static int fastboot_our_port;
46 
47 /**
48  * Constructs and sends a packet in response to received fastboot packet
49  *
50  * @param fb_header    Header for response packet
51  * @param retransmit   Nonzero if sending last sent packet
52  */
53 static void fastboot_send(struct fastboot_header fb_header, uchar retransmit)
54 {
55 	uchar *packet;
56 	uchar *packet_base;
57 	int len = 0;
58 	const char *error_msg = "An error occurred.";
59 	short tmp;
60 	struct fastboot_header fb_response_header = fb_header;
61 	/*
62 	 *	We will always be sending some sort of packet, so
63 	 *	cobble together the packet headers now.
64 	 */
65 	packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
66 	packet_base = packet;
67 
68 	/* Resend last packet */
69 	if (retransmit) {
70 		memcpy(packet, last_packet, last_packet_len);
71 		net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
72 				    fastboot_remote_port, fastboot_our_port, last_packet_len);
73 		return;
74 	}
75 
76 	fb_response_header.seq = htons(fb_response_header.seq);
77 	memcpy(packet, &fb_response_header, sizeof(fb_response_header));
78 	packet += sizeof(fb_response_header);
79 
80 	switch (fb_header.id) {
81 	case FASTBOOT_QUERY:
82 		tmp = htons(fb_sequence_number);
83 		memcpy(packet, &tmp, sizeof(tmp));
84 		packet += sizeof(tmp);
85 		break;
86 	case FASTBOOT_INIT:
87 		tmp = htons(fb_udp_version);
88 		memcpy(packet, &tmp, sizeof(tmp));
89 		packet += sizeof(tmp);
90 		tmp = htons(fb_packet_size);
91 		memcpy(packet, &tmp, sizeof(tmp));
92 		packet += sizeof(tmp);
93 		break;
94 	case FASTBOOT_ERROR:
95 		memcpy(packet, error_msg, strlen(error_msg));
96 		packet += strlen(error_msg);
97 		break;
98 	case FASTBOOT_FASTBOOT:
99 	default:
100 		error("ID %d not implemented.\n", fb_header.id);
101 		return;
102 	}
103 
104 	len = packet-packet_base;
105 
106 	/* Save packet for retransmitting */
107 	last_packet_len = len;
108 	memcpy(last_packet, packet_base, last_packet_len);
109 
110 	net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
111 			    fastboot_remote_port, fastboot_our_port, len);
112 }
113 
114 /**
115  * Incoming UDP packet handler.
116  *
117  * @param packet  Pointer to incoming UDP packet
118  * @param dport   Destination UDP port
119  * @param sip     Source IP address
120  * @param sport   Source UDP port
121  * @param len     Packet length
122  */
123 static void fastboot_handler(uchar *packet, unsigned dport, struct in_addr sip,
124 		unsigned sport, unsigned len)
125 {
126 	struct fastboot_header fb_header;
127 
128 	if (dport != fastboot_our_port) {
129 		return;
130 	}
131 
132 	fastboot_remote_ip = sip;
133 	fastboot_remote_port = sport;
134 
135 	if (len < FASTBOOT_HEADER_SIZE || len > PACKET_SIZE) {
136 		return;
137 	}
138 	memcpy(&fb_header, packet, sizeof(fb_header));
139 	fb_header.flags = 0;
140 	fb_header.seq = ntohs(fb_header.seq);
141 	packet += sizeof(fb_header);
142 	len -= sizeof(fb_header);
143 
144 	switch (fb_header.id) {
145 	case FASTBOOT_QUERY:
146 		fastboot_send(fb_header, 0);
147 		break;
148 	case FASTBOOT_INIT:
149 		if (fb_header.seq == fb_sequence_number) {
150 			fastboot_send(fb_header, 0);
151 			fb_sequence_number++;
152 		} else if (fb_header.seq == fb_sequence_number - 1) {
153 			/* Retransmit last sent packet */
154 			fastboot_send(fb_header, 1);
155 		}
156 		break;
157 	case FASTBOOT_FASTBOOT:
158 	default:
159 		error("ID %d not implemented.\n", fb_header.id);
160 		fb_header.id = FASTBOOT_ERROR;
161 		fastboot_send(fb_header, 0);
162 		break;
163 	}
164 }
165 
166 void fastboot_start_server(void)
167 {
168 	printf("Using %s device\n", eth_get_name());
169 	printf("Listening for fastboot command on %pI4\n", &net_ip);
170 
171 	fastboot_our_port = WELL_KNOWN_PORT;
172 
173 	net_set_udp_handler(fastboot_handler);
174 
175 	/* zero out server ether in case the server ip has changed */
176 	memset(net_server_ethaddr, 0, 6);
177 }
178