mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
8.1 KiB
319 lines
8.1 KiB
// SPDX-License-Identifier: BSD-2-Clause |
|
/* |
|
* Copyright (C) 2016 The Android Open Source Project |
|
*/ |
|
|
|
#include <common.h> |
|
#include <fastboot.h> |
|
#include <net.h> |
|
#include <net/fastboot.h> |
|
|
|
/* Fastboot port # defined in spec */ |
|
#define WELL_KNOWN_PORT 5554 |
|
|
|
enum { |
|
FASTBOOT_ERROR = 0, |
|
FASTBOOT_QUERY = 1, |
|
FASTBOOT_INIT = 2, |
|
FASTBOOT_FASTBOOT = 3, |
|
}; |
|
|
|
struct __packed fastboot_header { |
|
uchar id; |
|
uchar flags; |
|
unsigned short seq; |
|
}; |
|
|
|
#define PACKET_SIZE 1024 |
|
#define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header)) |
|
|
|
/* Sequence number sent for every packet */ |
|
static unsigned short sequence_number = 1; |
|
static const unsigned short packet_size = PACKET_SIZE; |
|
static const unsigned short udp_version = 1; |
|
|
|
/* Keep track of last packet for resubmission */ |
|
static uchar last_packet[PACKET_SIZE]; |
|
static unsigned int last_packet_len; |
|
|
|
static struct in_addr fastboot_remote_ip; |
|
/* The UDP port at their end */ |
|
static int fastboot_remote_port; |
|
/* The UDP port at our end */ |
|
static int fastboot_our_port; |
|
|
|
static void boot_downloaded_image(void); |
|
|
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
|
/** |
|
* fastboot_udp_send_info() - Send an INFO packet during long commands. |
|
* |
|
* @msg: String describing the reason for waiting |
|
*/ |
|
static void fastboot_udp_send_info(const char *msg) |
|
{ |
|
uchar *packet; |
|
uchar *packet_base; |
|
int len = 0; |
|
char response[FASTBOOT_RESPONSE_LEN] = {0}; |
|
|
|
struct fastboot_header response_header = { |
|
.id = FASTBOOT_FASTBOOT, |
|
.flags = 0, |
|
.seq = htons(sequence_number) |
|
}; |
|
++sequence_number; |
|
packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; |
|
packet_base = packet; |
|
|
|
/* Write headers */ |
|
memcpy(packet, &response_header, sizeof(response_header)); |
|
packet += sizeof(response_header); |
|
/* Write response */ |
|
fastboot_response("INFO", response, "%s", msg); |
|
memcpy(packet, response, strlen(response)); |
|
packet += strlen(response); |
|
|
|
len = packet - packet_base; |
|
|
|
/* Save packet for retransmitting */ |
|
last_packet_len = len; |
|
memcpy(last_packet, packet_base, last_packet_len); |
|
|
|
net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, |
|
fastboot_remote_port, fastboot_our_port, len); |
|
} |
|
|
|
/** |
|
* fastboot_timed_send_info() - Send INFO packet every 30 seconds |
|
* |
|
* @msg: String describing the reason for waiting |
|
* |
|
* Send an INFO packet during long commands based on timer. An INFO packet |
|
* is sent if the time is 30 seconds after start. Else, noop. |
|
*/ |
|
static void fastboot_timed_send_info(const char *msg) |
|
{ |
|
static ulong start; |
|
|
|
/* Initialize timer */ |
|
if (start == 0) |
|
start = get_timer(0); |
|
ulong time = get_timer(start); |
|
/* Send INFO packet to host every 30 seconds */ |
|
if (time >= 30000) { |
|
start = get_timer(0); |
|
fastboot_udp_send_info(msg); |
|
} |
|
} |
|
#endif |
|
|
|
/** |
|
* fastboot_send() - Sends a packet in response to received fastboot packet |
|
* |
|
* @header: Header for response packet |
|
* @fastboot_data: Pointer to received fastboot data |
|
* @fastboot_data_len: Length of received fastboot data |
|
* @retransmit: Nonzero if sending last sent packet |
|
*/ |
|
static void fastboot_send(struct fastboot_header header, char *fastboot_data, |
|
unsigned int fastboot_data_len, uchar retransmit) |
|
{ |
|
uchar *packet; |
|
uchar *packet_base; |
|
int len = 0; |
|
const char *error_msg = "An error occurred."; |
|
short tmp; |
|
struct fastboot_header response_header = header; |
|
static char command[FASTBOOT_COMMAND_LEN]; |
|
static int cmd = -1; |
|
static bool pending_command; |
|
char response[FASTBOOT_RESPONSE_LEN] = {0}; |
|
|
|
/* |
|
* We will always be sending some sort of packet, so |
|
* cobble together the packet headers now. |
|
*/ |
|
packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; |
|
packet_base = packet; |
|
|
|
/* Resend last packet */ |
|
if (retransmit) { |
|
memcpy(packet, last_packet, last_packet_len); |
|
net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, |
|
fastboot_remote_port, fastboot_our_port, |
|
last_packet_len); |
|
return; |
|
} |
|
|
|
response_header.seq = htons(response_header.seq); |
|
memcpy(packet, &response_header, sizeof(response_header)); |
|
packet += sizeof(response_header); |
|
|
|
switch (header.id) { |
|
case FASTBOOT_QUERY: |
|
tmp = htons(sequence_number); |
|
memcpy(packet, &tmp, sizeof(tmp)); |
|
packet += sizeof(tmp); |
|
break; |
|
case FASTBOOT_INIT: |
|
tmp = htons(udp_version); |
|
memcpy(packet, &tmp, sizeof(tmp)); |
|
packet += sizeof(tmp); |
|
tmp = htons(packet_size); |
|
memcpy(packet, &tmp, sizeof(tmp)); |
|
packet += sizeof(tmp); |
|
break; |
|
case FASTBOOT_ERROR: |
|
memcpy(packet, error_msg, strlen(error_msg)); |
|
packet += strlen(error_msg); |
|
break; |
|
case FASTBOOT_FASTBOOT: |
|
if (cmd == FASTBOOT_COMMAND_DOWNLOAD) { |
|
if (!fastboot_data_len && !fastboot_data_remaining()) { |
|
fastboot_data_complete(response); |
|
} else { |
|
fastboot_data_download(fastboot_data, |
|
fastboot_data_len, |
|
response); |
|
} |
|
} else if (!pending_command) { |
|
strlcpy(command, fastboot_data, |
|
min((size_t)fastboot_data_len + 1, |
|
sizeof(command))); |
|
pending_command = true; |
|
} else { |
|
cmd = fastboot_handle_command(command, response); |
|
pending_command = false; |
|
} |
|
/* |
|
* Sent some INFO packets, need to update sequence number in |
|
* header |
|
*/ |
|
if (header.seq != sequence_number) { |
|
response_header.seq = htons(sequence_number); |
|
memcpy(packet_base, &response_header, |
|
sizeof(response_header)); |
|
} |
|
/* Write response to packet */ |
|
memcpy(packet, response, strlen(response)); |
|
packet += strlen(response); |
|
break; |
|
default: |
|
pr_err("ID %d not implemented.\n", header.id); |
|
return; |
|
} |
|
|
|
len = packet - packet_base; |
|
|
|
/* Save packet for retransmitting */ |
|
last_packet_len = len; |
|
memcpy(last_packet, packet_base, last_packet_len); |
|
|
|
net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, |
|
fastboot_remote_port, fastboot_our_port, len); |
|
|
|
/* Continue boot process after sending response */ |
|
if (!strncmp("OKAY", response, 4)) { |
|
switch (cmd) { |
|
case FASTBOOT_COMMAND_BOOT: |
|
boot_downloaded_image(); |
|
break; |
|
|
|
case FASTBOOT_COMMAND_CONTINUE: |
|
net_set_state(NETLOOP_SUCCESS); |
|
break; |
|
|
|
case FASTBOOT_COMMAND_REBOOT: |
|
case FASTBOOT_COMMAND_REBOOT_BOOTLOADER: |
|
do_reset(NULL, 0, 0, NULL); |
|
break; |
|
} |
|
} |
|
|
|
if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) |
|
cmd = -1; |
|
} |
|
|
|
/** |
|
* boot_downloaded_image() - Boots into downloaded image. |
|
*/ |
|
static void boot_downloaded_image(void) |
|
{ |
|
fastboot_boot(); |
|
net_set_state(NETLOOP_SUCCESS); |
|
} |
|
|
|
/** |
|
* fastboot_handler() - Incoming UDP packet handler. |
|
* |
|
* @packet: Pointer to incoming UDP packet |
|
* @dport: Destination UDP port |
|
* @sip: Source IP address |
|
* @sport: Source UDP port |
|
* @len: Packet length |
|
*/ |
|
static void fastboot_handler(uchar *packet, unsigned int dport, |
|
struct in_addr sip, unsigned int sport, |
|
unsigned int len) |
|
{ |
|
struct fastboot_header header; |
|
char fastboot_data[DATA_SIZE] = {0}; |
|
unsigned int fastboot_data_len = 0; |
|
|
|
if (dport != fastboot_our_port) |
|
return; |
|
|
|
fastboot_remote_ip = sip; |
|
fastboot_remote_port = sport; |
|
|
|
if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE) |
|
return; |
|
memcpy(&header, packet, sizeof(header)); |
|
header.flags = 0; |
|
header.seq = ntohs(header.seq); |
|
packet += sizeof(header); |
|
len -= sizeof(header); |
|
|
|
switch (header.id) { |
|
case FASTBOOT_QUERY: |
|
fastboot_send(header, fastboot_data, 0, 0); |
|
break; |
|
case FASTBOOT_INIT: |
|
case FASTBOOT_FASTBOOT: |
|
fastboot_data_len = len; |
|
if (len > 0) |
|
memcpy(fastboot_data, packet, len); |
|
if (header.seq == sequence_number) { |
|
fastboot_send(header, fastboot_data, |
|
fastboot_data_len, 0); |
|
sequence_number++; |
|
} else if (header.seq == sequence_number - 1) { |
|
/* Retransmit last sent packet */ |
|
fastboot_send(header, fastboot_data, |
|
fastboot_data_len, 1); |
|
} |
|
break; |
|
default: |
|
pr_err("ID %d not implemented.\n", header.id); |
|
header.id = FASTBOOT_ERROR; |
|
fastboot_send(header, fastboot_data, 0, 0); |
|
break; |
|
} |
|
} |
|
|
|
void fastboot_start_server(void) |
|
{ |
|
printf("Using %s device\n", eth_get_name()); |
|
printf("Listening for fastboot command on %pI4\n", &net_ip); |
|
|
|
fastboot_our_port = WELL_KNOWN_PORT; |
|
|
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
|
fastboot_set_progress_callback(fastboot_timed_send_info); |
|
#endif |
|
net_set_udp_handler(fastboot_handler); |
|
|
|
/* zero out server ether in case the server ip has changed */ |
|
memset(net_server_ethaddr, 0, 6); |
|
}
|
|
|