Add support for fastboot command over UDP

The Raspberry Pi does not support USB device mode, so fastboot over
USB does not work. This patch adds support for and partially
implements the device side fastboot UDP protocol as a U-boot command.
It requires an ip address set and ethernet initialized.

- Modify U-boot fastboot command to do fastboot over UDP
- Add net/fastboot.c, which implements fastboot server functions
- Handle QUERY and INIT fastboot packets
- Add CONFIG_UDP_FUNCION_FASTBOOT to enable fastboot over UDP

Bug: 31887729
Test: Tested with fastboot, responds correctly to query and init
      packets.

Change-Id: I4045abaf7a4455d756b7c1c00215afaa90c70a6e
This commit is contained in:
Jocelyn Bohr 2016-11-07 08:43:55 -08:00 committed by Kever Yang
parent 83a51ebaaa
commit 965eda410b
8 changed files with 260 additions and 34 deletions

View File

@ -11,18 +11,37 @@
#include <command.h> #include <command.h>
#include <console.h> #include <console.h>
#include <g_dnl.h> #include <g_dnl.h>
#include <net.h>
#include <usb.h> #include <usb.h>
static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{ {
#ifdef CONFIG_USB_FUNCTION_FASTBOOT
int controller_index; int controller_index;
char *usb_controller; char *usb_controller;
int ret; int ret;
#endif
if (argc < 2) if (argc < 2)
return CMD_RET_USAGE; 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); controller_index = simple_strtoul(usb_controller, NULL, 0);
ret = board_usb_init(controller_index, USB_INIT_DEVICE); ret = board_usb_init(controller_index, USB_INIT_DEVICE);
@ -59,11 +78,14 @@ exit:
board_usb_cleanup(controller_index, USB_INIT_DEVICE); board_usb_cleanup(controller_index, USB_INIT_DEVICE);
return ret; return ret;
#endif
} }
U_BOOT_CMD( U_BOOT_CMD(
fastboot, 2, 1, do_fastboot, fastboot, 3, 1, do_fastboot,
"use USB Fastboot protocol", "use USB or UDP Fastboot protocol",
"<USB_controller>\n" "[usb,udp] <USB_controller>\n"
" - run as a fastboot usb device" " - run as a fastboot usb or udp device\n"
" usb: specify <USB_controller>\n"
" udp: requires ip_addr set and ethernet initialized\n"
); );

View File

@ -11,17 +11,25 @@ config USB_FUNCTION_FASTBOOT
help help
This enables the USB part of the fastboot gadget. 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 config CMD_FASTBOOT
bool "Enable FASTBOOT command" bool "Enable FASTBOOT command"
depends on USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT
help help
This enables the command "fastboot" which enables the Android This enables the command "fastboot" which enables the Android
fastboot mode for the platform's USB device. Fastboot is a USB fastboot mode for the platform. Fastboot is a protocol for
protocol for downloading images, flashing and device control downloading images, flashing and device control used on
used on Android devices. Android devices. Fastboot requires either network stack
enabled or support for acting as a USB device.
See doc/README.android-fastboot for more information. See doc/README.android-fastboot for more information.
if USB_FUNCTION_FASTBOOT if USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT
config FASTBOOT_BUF_ADDR config FASTBOOT_BUF_ADDR
hex "Define FASTBOOT buffer address" hex "Define FASTBOOT buffer address"
@ -74,29 +82,6 @@ config FASTBOOT_FLASH_MMC_DEV
regarding the non-volatile storage device. Define this to regarding the non-volatile storage device. Define this to
the eMMC device that fastboot should use to store the image. the eMMC device that fastboot should use to store the image.
config FASTBOOT_GPT_NAME endif # USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT
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 # FASTBOOT endif # FASTBOOT

View File

@ -70,6 +70,12 @@ U_BOOT_CMD(
); );
#endif #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 #ifdef CONFIG_CMD_RARP
int do_rarpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) int do_rarpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])

View File

@ -538,7 +538,7 @@ extern int net_restart_wrap; /* Tried all network devices */
enum proto_t { enum proto_t {
BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP, 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 */ 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 */ extern char *net_dns_env_var; /* the env var to put the ip into */
#endif #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) #if defined(CONFIG_CMD_PING)
extern struct in_addr net_ping_ip; /* the ip address to ping */ extern struct in_addr net_ping_ip; /* the ip address to ping */
#endif #endif

22
include/net/fastboot.h Normal file
View File

@ -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__ */

View File

@ -30,3 +30,4 @@ obj-$(CONFIG_CMD_NET) += tftp.o
# sprintf(buf, index ? "foo%d" : "foo", index) # sprintf(buf, index ? "foo%d" : "foo", index)
# and this is intentional usage. # and this is intentional usage.
CFLAGS_eth_common.o += -Wno-format-extra-args CFLAGS_eth_common.o += -Wno-format-extra-args
obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fastboot.o

177
net/fastboot.c Normal file
View File

@ -0,0 +1,177 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <common.h>
#include <net.h>
#include <net/fastboot.h>
#include <stdlib.h>
#include <version.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 __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);
}

View File

@ -87,6 +87,9 @@
#include <environment.h> #include <environment.h>
#include <errno.h> #include <errno.h>
#include <net.h> #include <net.h>
#if defined(CONFIG_UDP_FUNCTION_FASTBOOT)
#include <net/fastboot.h>
#endif
#include <net/tftp.h> #include <net/tftp.h>
#if defined(CONFIG_LED_STATUS) #if defined(CONFIG_LED_STATUS)
#include <miiphy.h> #include <miiphy.h>
@ -453,6 +456,11 @@ restart:
tftp_start_server(); tftp_start_server();
break; break;
#endif #endif
#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
case FASTBOOT:
fastboot_start_server();
break;
#endif
#if defined(CONFIG_CMD_DHCP) #if defined(CONFIG_CMD_DHCP)
case DHCP: case DHCP:
bootp_reset(); bootp_reset();
@ -1324,6 +1332,7 @@ common:
/* Fall through */ /* Fall through */
case NETCONS: case NETCONS:
case FASTBOOT:
case TFTPSRV: case TFTPSRV:
if (net_ip.s_addr == 0) { if (net_ip.s_addr == 0) {
puts("*** ERROR: `ipaddr' not set\n"); puts("*** ERROR: `ipaddr' not set\n");