blob: d1479510d61aa02f23bec74199531063a7e88b84 [file] [log] [blame]
Alex Kiernand5aa57c2018-05-29 15:30:53 +00001// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * Copyright (C) 2016 The Android Open Source Project
4 */
5
Simon Glassed38aef2020-05-10 11:40:03 -06006#include <command.h>
Alex Kiernand5aa57c2018-05-29 15:30:53 +00007#include <fastboot.h>
8#include <net.h>
Dmitrii Merkurev308252d2023-04-12 19:49:30 +01009#include <net/fastboot_udp.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060010#include <linux/printk.h>
Alex Kiernand5aa57c2018-05-29 15:30:53 +000011
Alex Kiernand5aa57c2018-05-29 15:30:53 +000012enum {
13 FASTBOOT_ERROR = 0,
14 FASTBOOT_QUERY = 1,
15 FASTBOOT_INIT = 2,
16 FASTBOOT_FASTBOOT = 3,
17};
18
19struct __packed fastboot_header {
20 uchar id;
21 uchar flags;
22 unsigned short seq;
23};
24
25#define PACKET_SIZE 1024
26#define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header))
27
28/* Sequence number sent for every packet */
29static unsigned short sequence_number = 1;
30static const unsigned short packet_size = PACKET_SIZE;
31static const unsigned short udp_version = 1;
32
33/* Keep track of last packet for resubmission */
34static uchar last_packet[PACKET_SIZE];
35static unsigned int last_packet_len;
36
37static struct in_addr fastboot_remote_ip;
38/* The UDP port at their end */
39static int fastboot_remote_port;
40/* The UDP port at our end */
41static int fastboot_our_port;
42
Alex Kiernand5aa57c2018-05-29 15:30:53 +000043/**
Ion Agorria1c387032024-01-05 09:22:06 +020044 * fastboot_udp_send_response() - Send an response into UDP
Alex Kiernand5aa57c2018-05-29 15:30:53 +000045 *
Ion Agorria1c387032024-01-05 09:22:06 +020046 * @response: Response to send
Alex Kiernand5aa57c2018-05-29 15:30:53 +000047 */
Ion Agorria1c387032024-01-05 09:22:06 +020048static void fastboot_udp_send_response(const char *response)
Alex Kiernand5aa57c2018-05-29 15:30:53 +000049{
50 uchar *packet;
51 uchar *packet_base;
52 int len = 0;
Alex Kiernand5aa57c2018-05-29 15:30:53 +000053
54 struct fastboot_header response_header = {
55 .id = FASTBOOT_FASTBOOT,
56 .flags = 0,
57 .seq = htons(sequence_number)
58 };
59 ++sequence_number;
60 packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
61 packet_base = packet;
62
63 /* Write headers */
64 memcpy(packet, &response_header, sizeof(response_header));
65 packet += sizeof(response_header);
66 /* Write response */
Alex Kiernand5aa57c2018-05-29 15:30:53 +000067 memcpy(packet, response, strlen(response));
68 packet += strlen(response);
69
70 len = packet - packet_base;
71
72 /* Save packet for retransmitting */
73 last_packet_len = len;
74 memcpy(last_packet, packet_base, last_packet_len);
75
76 net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
77 fastboot_remote_port, fastboot_our_port, len);
78}
79
80/**
81 * fastboot_timed_send_info() - Send INFO packet every 30 seconds
82 *
83 * @msg: String describing the reason for waiting
84 *
85 * Send an INFO packet during long commands based on timer. An INFO packet
86 * is sent if the time is 30 seconds after start. Else, noop.
87 */
88static void fastboot_timed_send_info(const char *msg)
89{
90 static ulong start;
Ion Agorria1c387032024-01-05 09:22:06 +020091 char response[FASTBOOT_RESPONSE_LEN] = {0};
Alex Kiernand5aa57c2018-05-29 15:30:53 +000092
93 /* Initialize timer */
94 if (start == 0)
95 start = get_timer(0);
96 ulong time = get_timer(start);
97 /* Send INFO packet to host every 30 seconds */
98 if (time >= 30000) {
99 start = get_timer(0);
Ion Agorria1c387032024-01-05 09:22:06 +0200100 fastboot_response("INFO", response, "%s", msg);
101 fastboot_udp_send_response(response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000102 }
103}
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000104
105/**
106 * fastboot_send() - Sends a packet in response to received fastboot packet
107 *
108 * @header: Header for response packet
109 * @fastboot_data: Pointer to received fastboot data
110 * @fastboot_data_len: Length of received fastboot data
111 * @retransmit: Nonzero if sending last sent packet
112 */
113static void fastboot_send(struct fastboot_header header, char *fastboot_data,
114 unsigned int fastboot_data_len, uchar retransmit)
115{
116 uchar *packet;
117 uchar *packet_base;
118 int len = 0;
119 const char *error_msg = "An error occurred.";
120 short tmp;
121 struct fastboot_header response_header = header;
122 static char command[FASTBOOT_COMMAND_LEN];
123 static int cmd = -1;
124 static bool pending_command;
125 char response[FASTBOOT_RESPONSE_LEN] = {0};
126
127 /*
128 * We will always be sending some sort of packet, so
129 * cobble together the packet headers now.
130 */
131 packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
132 packet_base = packet;
133
134 /* Resend last packet */
135 if (retransmit) {
136 memcpy(packet, last_packet, last_packet_len);
137 net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
138 fastboot_remote_port, fastboot_our_port,
139 last_packet_len);
140 return;
141 }
142
143 response_header.seq = htons(response_header.seq);
144 memcpy(packet, &response_header, sizeof(response_header));
145 packet += sizeof(response_header);
146
147 switch (header.id) {
148 case FASTBOOT_QUERY:
149 tmp = htons(sequence_number);
150 memcpy(packet, &tmp, sizeof(tmp));
151 packet += sizeof(tmp);
152 break;
153 case FASTBOOT_INIT:
154 tmp = htons(udp_version);
155 memcpy(packet, &tmp, sizeof(tmp));
156 packet += sizeof(tmp);
157 tmp = htons(packet_size);
158 memcpy(packet, &tmp, sizeof(tmp));
159 packet += sizeof(tmp);
160 break;
161 case FASTBOOT_ERROR:
162 memcpy(packet, error_msg, strlen(error_msg));
163 packet += strlen(error_msg);
164 break;
165 case FASTBOOT_FASTBOOT:
166 if (cmd == FASTBOOT_COMMAND_DOWNLOAD) {
167 if (!fastboot_data_len && !fastboot_data_remaining()) {
168 fastboot_data_complete(response);
169 } else {
170 fastboot_data_download(fastboot_data,
171 fastboot_data_len,
172 response);
173 }
174 } else if (!pending_command) {
175 strlcpy(command, fastboot_data,
176 min((size_t)fastboot_data_len + 1,
177 sizeof(command)));
178 pending_command = true;
179 } else {
180 cmd = fastboot_handle_command(command, response);
181 pending_command = false;
Ion Agorria1c387032024-01-05 09:22:06 +0200182
183 if (!strncmp(FASTBOOT_MULTIRESPONSE_START, response, 4)) {
184 while (1) {
185 /* Call handler to obtain next response */
186 fastboot_multiresponse(cmd, response);
187
188 /*
189 * Send more responses or break to send
190 * final OKAY/FAIL response
191 */
192 if (strncmp("OKAY", response, 4) &&
193 strncmp("FAIL", response, 4))
194 fastboot_udp_send_response(response);
195 else
196 break;
197 }
198 }
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000199 }
200 /*
201 * Sent some INFO packets, need to update sequence number in
202 * header
203 */
204 if (header.seq != sequence_number) {
205 response_header.seq = htons(sequence_number);
206 memcpy(packet_base, &response_header,
207 sizeof(response_header));
208 }
209 /* Write response to packet */
210 memcpy(packet, response, strlen(response));
211 packet += strlen(response);
212 break;
213 default:
214 pr_err("ID %d not implemented.\n", header.id);
215 return;
216 }
217
218 len = packet - packet_base;
219
220 /* Save packet for retransmitting */
221 last_packet_len = len;
222 memcpy(last_packet, packet_base, last_packet_len);
223
224 net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
225 fastboot_remote_port, fastboot_our_port, len);
226
Dmitrii Merkurev07882902023-04-12 19:49:31 +0100227 fastboot_handle_boot(cmd, strncmp("OKAY", response, 4) == 0);
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000228
229 if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4))
230 cmd = -1;
231}
232
233/**
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000234 * fastboot_handler() - Incoming UDP packet handler.
235 *
236 * @packet: Pointer to incoming UDP packet
237 * @dport: Destination UDP port
238 * @sip: Source IP address
239 * @sport: Source UDP port
240 * @len: Packet length
241 */
242static void fastboot_handler(uchar *packet, unsigned int dport,
243 struct in_addr sip, unsigned int sport,
244 unsigned int len)
245{
246 struct fastboot_header header;
247 char fastboot_data[DATA_SIZE] = {0};
248 unsigned int fastboot_data_len = 0;
249
250 if (dport != fastboot_our_port)
251 return;
252
253 fastboot_remote_ip = sip;
254 fastboot_remote_port = sport;
255
256 if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE)
257 return;
258 memcpy(&header, packet, sizeof(header));
259 header.flags = 0;
260 header.seq = ntohs(header.seq);
261 packet += sizeof(header);
262 len -= sizeof(header);
263
264 switch (header.id) {
265 case FASTBOOT_QUERY:
266 fastboot_send(header, fastboot_data, 0, 0);
267 break;
268 case FASTBOOT_INIT:
269 case FASTBOOT_FASTBOOT:
270 fastboot_data_len = len;
271 if (len > 0)
272 memcpy(fastboot_data, packet, len);
273 if (header.seq == sequence_number) {
274 fastboot_send(header, fastboot_data,
275 fastboot_data_len, 0);
276 sequence_number++;
277 } else if (header.seq == sequence_number - 1) {
278 /* Retransmit last sent packet */
279 fastboot_send(header, fastboot_data,
280 fastboot_data_len, 1);
281 }
282 break;
283 default:
284 pr_err("ID %d not implemented.\n", header.id);
285 header.id = FASTBOOT_ERROR;
286 fastboot_send(header, fastboot_data, 0, 0);
287 break;
288 }
289}
290
Dmitrii Merkurev308252d2023-04-12 19:49:30 +0100291void fastboot_udp_start_server(void)
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000292{
293 printf("Using %s device\n", eth_get_name());
294 printf("Listening for fastboot command on %pI4\n", &net_ip);
295
Christian Gmeiner7fd97aa2022-01-13 08:40:06 +0100296 fastboot_our_port = CONFIG_UDP_FUNCTION_FASTBOOT_PORT;
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000297
Simon Glass3eaba0792023-02-05 15:39:53 -0700298 if (IS_ENABLED(CONFIG_FASTBOOT_FLASH))
Patrick Delaunayf82f9e42022-12-15 10:15:50 +0100299 fastboot_set_progress_callback(fastboot_timed_send_info);
300
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000301 net_set_udp_handler(fastboot_handler);
302
303 /* zero out server ether in case the server ip has changed */
304 memset(net_server_ethaddr, 0, 6);
305}