xref: /rk3399_rockchip-uboot/net/fastboot.c (revision 02ad78925c7d198b08fbeca995163189ee18def5)
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 #define DATA_SIZE (PACKET_SIZE - FASTBOOT_HEADER_SIZE)
32 #define FASTBOOT_RESPONSE_LEN (64 + 1)
33 #define FASTBOOT_VERSION "0.4"
34 
35 /* Sequence number sent for every packet */
36 static unsigned short fb_sequence_number = 1;
37 static const unsigned short fb_packet_size = PACKET_SIZE;
38 static const unsigned short fb_udp_version = 1;
39 
40 /* Keep track of last packet for resubmission */
41 static uchar last_packet[PACKET_SIZE];
42 static unsigned int last_packet_len = 0;
43 
44 /* Parsed from first fastboot command packet */
45 static char *cmd_string = NULL;
46 static char *cmd_parameter = NULL;
47 
48 static struct in_addr fastboot_remote_ip;
49 /* The UDP port at their end */
50 static int fastboot_remote_port;
51 /* The UDP port at our end */
52 static int fastboot_our_port;
53 
54 static void fb_getvar(char*);
55 static void cleanup_command_data(void);
56 static void write_fb_response(const char*, const char*, char*);
57 
58 /**
59  * Constructs and sends a packet in response to received fastboot packet
60  *
61  * @param fb_header            Header for response packet
62  * @param fastboot_data        Pointer to received fastboot data
63  * @param fastboot_data_len    Length of received fastboot data
64  * @param retransmit           Nonzero if sending last sent packet
65  */
66 static void fastboot_send(struct fastboot_header fb_header, char *fastboot_data,
67 		unsigned int fastboot_data_len, uchar retransmit)
68 {
69 	uchar *packet;
70 	uchar *packet_base;
71 	int len = 0;
72 	const char *error_msg = "An error occurred.";
73 	short tmp;
74 	struct fastboot_header fb_response_header = fb_header;
75 	char response[FASTBOOT_RESPONSE_LEN] = {0};
76 	/*
77 	 *	We will always be sending some sort of packet, so
78 	 *	cobble together the packet headers now.
79 	 */
80 	packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
81 	packet_base = packet;
82 
83 	/* Resend last packet */
84 	if (retransmit) {
85 		memcpy(packet, last_packet, last_packet_len);
86 		net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
87 				    fastboot_remote_port, fastboot_our_port, last_packet_len);
88 		return;
89 	}
90 
91 	fb_response_header.seq = htons(fb_response_header.seq);
92 	memcpy(packet, &fb_response_header, sizeof(fb_response_header));
93 	packet += sizeof(fb_response_header);
94 
95 	switch (fb_header.id) {
96 	case FASTBOOT_QUERY:
97 		tmp = htons(fb_sequence_number);
98 		memcpy(packet, &tmp, sizeof(tmp));
99 		packet += sizeof(tmp);
100 		break;
101 	case FASTBOOT_INIT:
102 		tmp = htons(fb_udp_version);
103 		memcpy(packet, &tmp, sizeof(tmp));
104 		packet += sizeof(tmp);
105 		tmp = htons(fb_packet_size);
106 		memcpy(packet, &tmp, sizeof(tmp));
107 		packet += sizeof(tmp);
108 		break;
109 	case FASTBOOT_ERROR:
110 		memcpy(packet, error_msg, strlen(error_msg));
111 		packet += strlen(error_msg);
112 		break;
113 	case FASTBOOT_FASTBOOT:
114 		if (cmd_string == NULL) {
115 			/* Parse command and send ack */
116 			cmd_parameter = fastboot_data;
117 			cmd_string = strsep(&cmd_parameter, ":");
118 			cmd_string = strdup(cmd_string);
119 			if (cmd_parameter) {
120 				cmd_parameter = strdup(cmd_parameter);
121 			}
122 		} else if (!strcmp("getvar", cmd_string)) {
123 			fb_getvar(response);
124 		} else {
125 			error("command %s not implemented.\n", cmd_string);
126 			write_fb_response("FAIL", "unrecognized command", response);
127 		}
128 		/* Write response to packet */
129 		memcpy(packet, response, strlen(response));
130 		packet += strlen(response);
131 		break;
132 	default:
133 		error("ID %d not implemented.\n", fb_header.id);
134 		return;
135 	}
136 
137 	len = packet-packet_base;
138 
139 	/* Save packet for retransmitting */
140 	last_packet_len = len;
141 	memcpy(last_packet, packet_base, last_packet_len);
142 
143 	net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
144 			    fastboot_remote_port, fastboot_our_port, len);
145 
146 	/* OKAY and FAIL indicate command is complete */
147 	if (!strncmp("OKAY", response, 4) ||
148 			!strncmp("FAIL", response, 4)) {
149 		cleanup_command_data();
150 	}
151 }
152 
153 /**
154  * Writes ascii string specified by cmd_parameter to response.
155  *
156  * @param repsonse    Pointer to fastboot response buffer
157  */
158 static void fb_getvar(char *response)
159 {
160 
161 	if (cmd_parameter == NULL) {
162 		write_fb_response("FAIL", "missing var", response);
163 	} else if (!strcmp("version", cmd_parameter)) {
164 		write_fb_response("OKAY", FASTBOOT_VERSION, response);
165 	} else if (!strcmp("bootloader-version", cmd_parameter)) {
166 		write_fb_response("OKAY", U_BOOT_VERSION, response);
167 	} else if (!strcmp("downloadsize", cmd_parameter) ||
168 		!strcmp("max-download-size", cmd_parameter)) {
169 		char buf_size_str[12];
170 		sprintf(buf_size_str, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE);
171 		write_fb_response("OKAY", buf_size_str, response);
172 	} else if (!strcmp("serialno", cmd_parameter)) {
173 		const char *tmp = getenv("serial#");
174 		if (tmp) {
175 			write_fb_response("OKAY", tmp, response);
176 		} else {
177 			write_fb_response("FAIL", "Value not set", response);
178 		}
179 	} else {
180 		printf("WARNING: unknown variable: %s\n", cmd_parameter);
181 		write_fb_response("FAIL", "Variable not implemented", response);
182 	}
183 }
184 
185 /**
186  * Writes a response to response buffer of the form "$tag$reason".
187  *
188  * @param tag         The first part of the response
189  * @param reason      The second part of the response
190  * @param repsonse    Pointer to fastboot response buffer
191  */
192 static void write_fb_response(const char* tag, const char *reason,
193 		char *response)
194 {
195 	strncpy(response, tag, strlen(tag));
196 	strncat(response, reason, FASTBOOT_RESPONSE_LEN - strlen(tag) - 1);
197 }
198 
199 /**
200  * Frees any resources allocated during current fastboot command.
201  */
202 static void cleanup_command_data(void)
203 {
204 	/* cmd_parameter and cmd_string potentially point to memory allocated by
205 	 * strdup
206 	 */
207 	if (cmd_parameter) {
208 		free(cmd_parameter);
209 	}
210 	if (cmd_string) {
211 		free(cmd_string);
212 	}
213 	cmd_parameter = cmd_string = NULL;
214 }
215 
216 /**
217  * Incoming UDP packet handler.
218  *
219  * @param packet  Pointer to incoming UDP packet
220  * @param dport   Destination UDP port
221  * @param sip     Source IP address
222  * @param sport   Source UDP port
223  * @param len     Packet length
224  */
225 static void fastboot_handler(uchar *packet, unsigned dport, struct in_addr sip,
226 		unsigned sport, unsigned len)
227 {
228 	struct fastboot_header fb_header;
229 	char fastboot_data[DATA_SIZE] = {0};
230 	unsigned int fastboot_data_len = 0;
231 
232 	if (dport != fastboot_our_port) {
233 		return;
234 	}
235 
236 	fastboot_remote_ip = sip;
237 	fastboot_remote_port = sport;
238 
239 	if (len < FASTBOOT_HEADER_SIZE || len > PACKET_SIZE) {
240 		return;
241 	}
242 	memcpy(&fb_header, packet, sizeof(fb_header));
243 	fb_header.flags = 0;
244 	fb_header.seq = ntohs(fb_header.seq);
245 	packet += sizeof(fb_header);
246 	len -= sizeof(fb_header);
247 
248 	switch (fb_header.id) {
249 	case FASTBOOT_QUERY:
250 		fastboot_send(fb_header, fastboot_data, 0, 0);
251 		break;
252 	case FASTBOOT_INIT:
253 	case FASTBOOT_FASTBOOT:
254 		fastboot_data_len = len;
255 		if (len > 0) {
256 			memcpy(fastboot_data, packet, len);
257 		}
258 		if (fb_header.seq == fb_sequence_number) {
259 			fastboot_send(fb_header, fastboot_data, fastboot_data_len, 0);
260 			fb_sequence_number++;
261 		} else if (fb_header.seq == fb_sequence_number - 1) {
262 			/* Retransmit last sent packet */
263 			fastboot_send(fb_header, fastboot_data, fastboot_data_len, 1);
264 		}
265 		break;
266 	default:
267 		error("ID %d not implemented.\n", fb_header.id);
268 		fb_header.id = FASTBOOT_ERROR;
269 		fastboot_send(fb_header, fastboot_data, 0, 0);
270 		break;
271 	}
272 }
273 
274 void fastboot_start_server(void)
275 {
276 	printf("Using %s device\n", eth_get_name());
277 	printf("Listening for fastboot command on %pI4\n", &net_ip);
278 
279 	fastboot_our_port = WELL_KNOWN_PORT;
280 
281 	net_set_udp_handler(fastboot_handler);
282 
283 	/* zero out server ether in case the server ip has changed */
284 	memset(net_server_ethaddr, 0, 6);
285 }
286