diff --git a/cmd/fastboot.c b/cmd/fastboot.c index 488822a2ee..3e24fa0bd2 100644 --- a/cmd/fastboot.c +++ b/cmd/fastboot.c @@ -11,18 +11,37 @@ #include #include #include +#include #include static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { +#ifdef CONFIG_USB_FUNCTION_FASTBOOT int controller_index; char *usb_controller; int ret; +#endif if (argc < 2) return CMD_RET_USAGE; - usb_controller = argv[1]; + if (!strcmp(argv[1], "udp")) { +#ifndef CONFIG_UDP_FUNCTION_FASTBOOT + error("Fastboot UDP not enabled\n"); + return -1; +#else + return do_fastboot_udp(cmdtp, flag, argc, argv); +#endif + } + + if (strcmp(argv[1], "usb") || argc < 3) + return CMD_RET_USAGE; + +#ifndef CONFIG_USB_FUNCTION_FASTBOOT + error("Fastboot USB not enabled\n"); + return -1; +#else + usb_controller = argv[2]; controller_index = simple_strtoul(usb_controller, NULL, 0); ret = board_usb_init(controller_index, USB_INIT_DEVICE); @@ -59,11 +78,14 @@ exit: board_usb_cleanup(controller_index, USB_INIT_DEVICE); return ret; +#endif } U_BOOT_CMD( - fastboot, 2, 1, do_fastboot, - "use USB Fastboot protocol", - "\n" - " - run as a fastboot usb device" + fastboot, 3, 1, do_fastboot, + "use USB or UDP Fastboot protocol", + "[usb,udp] \n" + " - run as a fastboot usb or udp device\n" + " usb: specify \n" + " udp: requires ip_addr set and ethernet initialized\n" ); diff --git a/cmd/fastboot/Kconfig b/cmd/fastboot/Kconfig index a2638a8bba..453cc49057 100644 --- a/cmd/fastboot/Kconfig +++ b/cmd/fastboot/Kconfig @@ -11,17 +11,25 @@ config USB_FUNCTION_FASTBOOT help This enables the USB part of the fastboot gadget. +config UDP_FUNCTION_FASTBOOT + select NET + bool "Enable fastboot protocol over UDP" + help + This enables the fastboot protocol over UDP. + config CMD_FASTBOOT bool "Enable FASTBOOT command" + depends on USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT help This enables the command "fastboot" which enables the Android - fastboot mode for the platform's USB device. Fastboot is a USB - protocol for downloading images, flashing and device control - used on Android devices. + fastboot mode for the platform. Fastboot is a protocol for + downloading images, flashing and device control used on + Android devices. Fastboot requires either network stack + enabled or support for acting as a USB device. See doc/README.android-fastboot for more information. -if USB_FUNCTION_FASTBOOT +if USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT config FASTBOOT_BUF_ADDR hex "Define FASTBOOT buffer address" @@ -74,29 +82,6 @@ config FASTBOOT_FLASH_MMC_DEV regarding the non-volatile storage device. Define this to the eMMC device that fastboot should use to store the image. -config FASTBOOT_GPT_NAME - string "Target name for updating GPT" - depends on FASTBOOT_FLASH - default "gpt" - help - The fastboot "flash" command supports writing the downloaded - image to the Protective MBR and the Primary GUID Partition - Table. (Additionally, this downloaded image is post-processed - to generate and write the Backup GUID Partition Table.) - This occurs when the specified "partition name" on the - "fastboot flash" command line matches the value defined here. - The default target name for updating GPT is "gpt". - -config FASTBOOT_MBR_NAME - string "Target name for updating MBR" - depends on FASTBOOT_FLASH - default "mbr" - help - The fastboot "flash" command allows to write the downloaded image - to the Master Boot Record. This occurs when the "partition name" - specified on the "fastboot flash" command line matches the value - defined here. The default target name for updating MBR is "mbr". - -endif # USB_FUNCTION_FASTBOOT +endif # USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT endif # FASTBOOT diff --git a/cmd/net.c b/cmd/net.c index d7c776aacf..1727198c86 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -70,6 +70,12 @@ U_BOOT_CMD( ); #endif +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT +int do_fastboot_udp(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + return netboot_common(FASTBOOT, cmdtp, argc, argv); +} +#endif #ifdef CONFIG_CMD_RARP int do_rarpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) diff --git a/include/net.h b/include/net.h index 455b48f6c7..109c15e283 100644 --- a/include/net.h +++ b/include/net.h @@ -538,7 +538,7 @@ extern int net_restart_wrap; /* Tried all network devices */ enum proto_t { BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP, - TFTPSRV, TFTPPUT, LINKLOCAL + TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT }; extern char net_boot_file_name[1024];/* Boot File name */ @@ -552,6 +552,10 @@ extern char *net_dns_resolve; /* The host to resolve */ extern char *net_dns_env_var; /* the env var to put the ip into */ #endif +#if defined(CONFIG_UDP_FUNCTION_FASTBOOT) +int do_fastboot_udp(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); +#endif + #if defined(CONFIG_CMD_PING) extern struct in_addr net_ping_ip; /* the ip address to ping */ #endif diff --git a/include/net/fastboot.h b/include/net/fastboot.h new file mode 100644 index 0000000000..10a5e39be1 --- /dev/null +++ b/include/net/fastboot.h @@ -0,0 +1,22 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* SPDX-License-Identifier: BSD-2-Clause +*/ + +#ifndef __NET_FASTBOOT_H__ +#define __NET_FASTBOOT_H__ + +/**********************************************************************/ +/* + * Global functions and variables. + */ + +/** + * Wait for incoming fastboot comands. + */ +void fastboot_start_server(void); + +/**********************************************************************/ + +#endif /* __NET_FASTBOOT_H__ */ diff --git a/net/Makefile b/net/Makefile index ae54eee5af..6dfd82e647 100644 --- a/net/Makefile +++ b/net/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_CMD_NET) += tftp.o # sprintf(buf, index ? "foo%d" : "foo", index) # and this is intentional usage. CFLAGS_eth_common.o += -Wno-format-extra-args +obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fastboot.o diff --git a/net/fastboot.c b/net/fastboot.c new file mode 100644 index 0000000000..eefd648df4 --- /dev/null +++ b/net/fastboot.c @@ -0,0 +1,177 @@ +/* +* Copyright (C) 2016 The Android Open Source Project +* +* SPDX-License-Identifier: BSD-2-Clause +*/ + +#include +#include +#include +#include +#include + +/* Fastboot port # defined in spec */ +#define WELL_KNOWN_PORT 5554 + +enum { + FASTBOOT_ERROR = 0, + FASTBOOT_QUERY = 1, + FASTBOOT_INIT = 2, + FASTBOOT_FASTBOOT = 3, +}; + +struct __attribute__((packed)) fastboot_header { + uchar id; + uchar flags; + unsigned short seq; +}; + +#define PACKET_SIZE 1024 +#define FASTBOOT_HEADER_SIZE sizeof(struct fastboot_header) + +/* Sequence number sent for every packet */ +static unsigned short fb_sequence_number = 1; +static const unsigned short fb_packet_size = PACKET_SIZE; +static const unsigned short fb_udp_version = 1; + +/* Keep track of last packet for resubmission */ +static uchar last_packet[PACKET_SIZE]; +static unsigned int last_packet_len = 0; + +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; + +/** + * Constructs and sends a packet in response to received fastboot packet + * + * @param fb_header Header for response packet + * @param retransmit Nonzero if sending last sent packet + */ +static void fastboot_send(struct fastboot_header fb_header, uchar retransmit) +{ + uchar *packet; + uchar *packet_base; + int len = 0; + const char *error_msg = "An error occurred."; + short tmp; + struct fastboot_header fb_response_header = fb_header; + /* + * 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; + } + + fb_response_header.seq = htons(fb_response_header.seq); + memcpy(packet, &fb_response_header, sizeof(fb_response_header)); + packet += sizeof(fb_response_header); + + switch (fb_header.id) { + case FASTBOOT_QUERY: + tmp = htons(fb_sequence_number); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + break; + case FASTBOOT_INIT: + tmp = htons(fb_udp_version); + memcpy(packet, &tmp, sizeof(tmp)); + packet += sizeof(tmp); + tmp = htons(fb_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: + default: + error("ID %d not implemented.\n", fb_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); +} + +/** + * Incoming UDP packet handler. + * + * @param packet Pointer to incoming UDP packet + * @param dport Destination UDP port + * @param sip Source IP address + * @param sport Source UDP port + * @param len Packet length + */ +static void fastboot_handler(uchar *packet, unsigned dport, struct in_addr sip, + unsigned sport, unsigned len) +{ + struct fastboot_header fb_header; + + if (dport != fastboot_our_port) { + return; + } + + fastboot_remote_ip = sip; + fastboot_remote_port = sport; + + if (len < FASTBOOT_HEADER_SIZE || len > PACKET_SIZE) { + return; + } + memcpy(&fb_header, packet, sizeof(fb_header)); + fb_header.flags = 0; + fb_header.seq = ntohs(fb_header.seq); + packet += sizeof(fb_header); + len -= sizeof(fb_header); + + switch (fb_header.id) { + case FASTBOOT_QUERY: + fastboot_send(fb_header, 0); + break; + case FASTBOOT_INIT: + if (fb_header.seq == fb_sequence_number) { + fastboot_send(fb_header, 0); + fb_sequence_number++; + } else if (fb_header.seq == fb_sequence_number - 1) { + /* Retransmit last sent packet */ + fastboot_send(fb_header, 1); + } + break; + case FASTBOOT_FASTBOOT: + default: + error("ID %d not implemented.\n", fb_header.id); + fb_header.id = FASTBOOT_ERROR; + fastboot_send(fb_header, 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; + + net_set_udp_handler(fastboot_handler); + + /* zero out server ether in case the server ip has changed */ + memset(net_server_ethaddr, 0, 6); +} diff --git a/net/net.c b/net/net.c index 4259c9e321..3469a456ea 100644 --- a/net/net.c +++ b/net/net.c @@ -87,6 +87,9 @@ #include #include #include +#if defined(CONFIG_UDP_FUNCTION_FASTBOOT) +#include +#endif #include #if defined(CONFIG_LED_STATUS) #include @@ -453,6 +456,11 @@ restart: tftp_start_server(); break; #endif +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT + case FASTBOOT: + fastboot_start_server(); + break; +#endif #if defined(CONFIG_CMD_DHCP) case DHCP: bootp_reset(); @@ -1324,6 +1332,7 @@ common: /* Fall through */ case NETCONS: + case FASTBOOT: case TFTPSRV: if (net_ip.s_addr == 0) { puts("*** ERROR: `ipaddr' not set\n");