[U-Boot] [RFC PATCH v2 07/20] net: fastboot: Merge AOSP UDP fastboot

Joe Hershberger joe.hershberger at ni.com
Thu May 3 20:38:39 UTC 2018


On Mon, Apr 30, 2018 at 3:32 AM, Alex Kiernan <alex.kiernan at gmail.com> wrote:
> Merge UDP fastboot support from AOSP:
>
> https://android.googlesource.com/platform/external/u-boot/+/android-o-mr1-iot-preview-8
>
> Signed-off-by: Alex Kiernan <alex.kiernan at gmail.com>
> Signed-off-by: Alex Deymo <deymo at google.com>
> ---
>
> Changes in v2:
> - ensure fastboot syntax is backward compatible - 'fastboot 0' means
>   'fastboot usb 0'
>
>  cmd/fastboot.c               |  35 ++-
>  cmd/net.c                    |   6 +
>  drivers/fastboot/Kconfig     |  16 +-
>  drivers/fastboot/fb_common.c |  18 ++
>  drivers/fastboot/fb_mmc.c    |  34 ++-
>  include/fastboot.h           |  13 ++
>  include/net.h                |   6 +-
>  include/net/fastboot.h       |  27 +++
>  net/Makefile                 |   1 +
>  net/fastboot.c               | 542 +++++++++++++++++++++++++++++++++++++++++++
>  net/net.c                    |   9 +
>  11 files changed, 695 insertions(+), 12 deletions(-)
>  create mode 100644 include/net/fastboot.h
>  create mode 100644 net/fastboot.c
>
> diff --git a/cmd/fastboot.c b/cmd/fastboot.c
> index 8adcca5..68a41de 100644
> --- a/cmd/fastboot.c
> +++ b/cmd/fastboot.c
> @@ -11,17 +11,41 @@
>  #include <command.h>
>  #include <console.h>
>  #include <g_dnl.h>
> +#include <net.h>
>  #include <usb.h>
>
>  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;
>
> +       if (!strcmp(argv[1], "udp")) {
> +#ifndef CONFIG_UDP_FUNCTION_FASTBOOT
> +               pr_err("Fastboot UDP not enabled\n");
> +               return -1;

Commands should always return an enum command_ret_t.

> +#else
> +               return do_fastboot_udp(cmdtp, flag, argc, argv);
> +#endif
> +       }
> +
> +       if (!strcmp(argv[1], "usb")) {
> +               argv++;
> +               argc--;
> +       }
> +
> +       if (argc < 2)
> +               return CMD_RET_USAGE;
> +
> +#ifndef CONFIG_USB_FUNCTION_FASTBOOT
> +       pr_err("Fastboot USB not enabled\n");
> +       return -1;

Commands should always return an enum command_ret_t.

> +#else
>         usb_controller = argv[1];
>         controller_index = simple_strtoul(usb_controller, NULL, 0);
>
> @@ -59,11 +83,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",
> -       "<USB_controller>\n"
> -       "    - run as a fastboot usb device"
> +       fastboot, 3, 1, do_fastboot,
> +       "use USB or UDP Fastboot protocol",
> +       "[usb,udp] <USB_controller>\n"
> +       " - run as a fastboot usb or udp device\n"
> +       "   usb: specify <USB_controller>\n"
> +       "   udp: requires ip_addr set and ethernet initialized\n"
>  );
> diff --git a/cmd/net.c b/cmd/net.c
> index 67888d4..668f344 100644
> --- a/cmd/net.c
> +++ b/cmd/net.c
> @@ -74,6 +74,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[])

You should move this to cmd/fastboot.c and make it static.

> +{
> +       return netboot_common(FASTBOOT, cmdtp, argc, argv);

Is this really what you want? You are passing these command
parameters, but you don't really want them to be interpreted (as a
load address or file name), right?

I think you just want this:

        int err = net_loop(FASTBOOT);

        if (err < 0) {
                printf("fastboot udp error: %i\n", err);
                return CMD_RET_FAILURE;
        }

        return CMD_RET_SUCCESS;


> +}
> +#endif
>
>  #ifdef CONFIG_CMD_RARP
>  int do_rarpb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
> index 64b94f0..53c337f 100644
> --- a/drivers/fastboot/Kconfig
> +++ b/drivers/fastboot/Kconfig
> @@ -2,6 +2,8 @@ menu "Fastboot support"
>
>  config FASTBOOT
>         bool
> +       imply ANDROID_BOOT_IMAGE
> +       imply CMD_FASTBOOT
>
>  config USB_FUNCTION_FASTBOOT
>         bool "Enable USB fastboot gadget"
> @@ -9,12 +11,17 @@ config USB_FUNCTION_FASTBOOT
>         default y if ARCH_SUNXI && USB_MUSB_GADGET
>         select FASTBOOT
>         select USB_GADGET_DOWNLOAD
> -       imply ANDROID_BOOT_IMAGE
> -       imply CMD_FASTBOOT
>         help
>           This enables the USB part of the fastboot gadget.
>
> -if USB_FUNCTION_FASTBOOT
> +config UDP_FUNCTION_FASTBOOT
> +       depends on NET

This is the correct level of dependency once you start using net_loop
directly, but based on how you have it now, you would have to depend
on CMD_NET.

> +       select FASTBOOT
> +       bool "Enable fastboot protocol over UDP"
> +       help
> +         This enables the fastboot protocol over UDP.
> +
> +if USB_FUNCTION_FASTBOOT || UDP_FUNCTION_FASTBOOT
>
>  config FASTBOOT_BUF_ADDR
>         hex "Define FASTBOOT buffer address"
> @@ -46,6 +53,7 @@ config FASTBOOT_BUF_SIZE
>
>  config FASTBOOT_USB_DEV
>         int "USB controller number"
> +       depends on USB_FUNCTION_FASTBOOT
>         default 0
>         help
>           Some boards have USB OTG controller other than 0. Define this
> @@ -117,6 +125,6 @@ config FASTBOOT_MBR_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
>
>  endmenu
> diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c
> index fe58803..7367fbb 100644
> --- a/drivers/fastboot/fb_common.c
> +++ b/drivers/fastboot/fb_common.c
> @@ -12,6 +12,9 @@
>
>  #include <common.h>
>  #include <fastboot.h>
> +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
> +#include <net/fastboot.h>
> +#endif
>
>  /**
>   * Writes a response to response buffer of the form "$tag$reason".
> @@ -44,3 +47,18 @@ void fastboot_okay(const char *reason, char *response)
>  {
>         fastboot_response("OKAY", response, "%s", reason);
>  }
> +
> +void timed_send_info(ulong *start, const char *msg)

This should be called fastboot_send_info()... it is in the global namespace!

> +{
> +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
> +       /* Initialize timer */
> +       if (*start == 0)

You should probably be a little more defensive and check for NULL
before dereferencing.

> +               *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_send_info(msg);

This should call fastboot_(udp_)send_info()

> +       }
> +#endif
> +}
> diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c
> index 02864aa..304bda1 100644
> --- a/drivers/fastboot/fb_mmc.c
> +++ b/drivers/fastboot/fb_mmc.c
> @@ -29,6 +29,9 @@
>  #define CONFIG_FASTBOOT_MBR_NAME "mbr"
>  #endif
>
> +#define FASTBOOT_MAX_BLK_WRITE 16384
> +static ulong timer;

This timer seems like it should be a static variable inside of
fastboot_send_info() (the function currently called timed_send_info).
Then you don't have to pass it as a parameter and fb_mmc.c doesn't
need to know what fb_common is doing with the info update.

> +
>  #define BOOT_PARTITION_NAME "boot"
>
>  struct fb_mmc_sparse {
> @@ -57,13 +60,38 @@ static int part_get_info_by_name_or_alias(struct blk_desc *dev_desc,
>         return ret;
>  }
>
> +static lbaint_t fb_mmc_blk_write(struct blk_desc *block_dev, lbaint_t start,
> +                                lbaint_t blkcnt, const void *buffer)
> +{
> +       lbaint_t blk = start;
> +       lbaint_t blks_written;
> +       lbaint_t cur_blkcnt;
> +       lbaint_t blks = 0;
> +       int i;
> +
> +       for (i = 0; i < blkcnt; i += FASTBOOT_MAX_BLK_WRITE) {
> +               cur_blkcnt = min((int)blkcnt - i, FASTBOOT_MAX_BLK_WRITE);
> +               if (!buffer) {
> +                       timed_send_info(&timer, "writing");
> +                       blks_written = blk_dwrite(block_dev, blk, cur_blkcnt,
> +                                                 buffer + (i * block_dev->blksz));
> +               } else {
> +                       timed_send_info(&timer, "erasing");
> +                       blks_written = blk_derase(block_dev, blk, cur_blkcnt);
> +               }
> +               blk += blks_written;
> +               blks += blks_written;
> +       }
> +       return blks;
> +}
> +
>  static lbaint_t fb_mmc_sparse_write(struct sparse_storage *info,
>                 lbaint_t blk, lbaint_t blkcnt, const void *buffer)
>  {
>         struct fb_mmc_sparse *sparse = info->priv;
>         struct blk_desc *dev_desc = sparse->dev_desc;
>
> -       return blk_dwrite(dev_desc, blk, blkcnt, buffer);
> +       return fb_mmc_blk_write(dev_desc, blk, blkcnt, buffer);
>  }
>
>  static lbaint_t fb_mmc_sparse_reserve(struct sparse_storage *info,
> @@ -91,7 +119,7 @@ static void write_raw_image(struct blk_desc *dev_desc, disk_partition_t *info,
>
>         puts("Flashing Raw Image\n");
>
> -       blks = blk_dwrite(dev_desc, info->start, blkcnt, buffer);
> +       blks = fb_mmc_blk_write(dev_desc, info->start, blkcnt, buffer);
>         if (blks != blkcnt) {
>                 pr_err("failed writing to device %d\n", dev_desc->devnum);
>                 fastboot_fail("failed writing to device", response);
> @@ -399,7 +427,7 @@ void fb_mmc_erase(const char *cmd, char *response)
>         printf("Erasing blocks " LBAFU " to " LBAFU " due to alignment\n",
>                blks_start, blks_start + blks_size);
>
> -       blks = blk_derase(dev_desc, blks_start, blks_size);
> +       blks = fb_mmc_blk_write(dev_desc, blks_start, blks_size, NULL);
>         if (blks != blks_size) {
>                 pr_err("failed erasing from device %d", dev_desc->devnum);
>                 fastboot_fail("failed erasing from device", response);
> diff --git a/include/fastboot.h b/include/fastboot.h
> index 2140c94..6f69423 100644
> --- a/include/fastboot.h
> +++ b/include/fastboot.h
> @@ -23,4 +23,17 @@ void fastboot_response(const char *tag, char *response,
>  void fastboot_fail(const char *reason, char *response);
>  void fastboot_okay(const char *reason, char *response);
>
> +/**
> + * Send an INFO packet during long commands based on timer. If
> + * CONFIG_UDP_FUNCTION_FASTBOOT is defined, an INFO packet is sent
> + * if the time is 30 seconds after start. Else, noop.
> + *
> + * TODO: Handle the situation where both UDP and USB fastboot are
> + *       enabled.
> + *
> + * @param start:  Time since last INFO packet was sent.
> + * @param msg:    String describing the reason for waiting
> + */
> +void timed_send_info(ulong *start, const char *msg);
> +
>  #endif /* _FASTBOOT_H_ */
> diff --git a/include/net.h b/include/net.h
> index 3469811..890ae27 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -535,7 +535,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 */
> @@ -549,6 +549,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)

Probably not necessary to guard - the linker would complain if it's not there.

> +int do_fastboot_udp(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);

This would be better as a static function in cmd/fastboot.c

> +#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 0000000..c0dd033
> --- /dev/null
> +++ b/include/net/fastboot.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (C) 2016 The Android Open Source Project
> + */
> +
> +#ifndef __NET_FASTBOOT_H__
> +#define __NET_FASTBOOT_H__
> +
> +/**********************************************************************/
> +/*
> + *     Global functions and variables.
> + */
> +
> +/**
> + * Wait for incoming fastboot comands.
> + */
> +void fastboot_start_server(void);

Since this is in the global namespace, it should probably be
qualified... fastboot_udp_start_server()

> +/**
> + * Send an INFO packet during long commands
> + *
> + * @param msg: String describing the reason for waiting
> + */
> +void fastboot_send_info(const char *msg);

Same here... fastboot_udp_send_info()

> +
> +/**********************************************************************/
> +
> +#endif /* __NET_FASTBOOT_H__ */
> diff --git a/net/Makefile b/net/Makefile
> index ce6e5ad..3489ce5 100644
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_CMD_PING) += ping.o
>  obj-$(CONFIG_CMD_RARP) += rarp.o
>  obj-$(CONFIG_CMD_SNTP) += sntp.o
>  obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
> +obj-$(CONFIG_UDP_FUNCTION_FASTBOOT)  += fastboot.o
>
>  # Disable this warning as it is triggered by:
>  # sprintf(buf, index ? "foo%d" : "foo", index)
> diff --git a/net/fastboot.c b/net/fastboot.c
> new file mode 100644
> index 0000000..32cb581
> --- /dev/null
> +++ b/net/fastboot.c
> @@ -0,0 +1,542 @@
> +// SPDX-License-Identifier: BSD-2-Clause
> +/*
> + * Copyright (C) 2016 The Android Open Source Project
> + */
> +
> +#include <common.h>
> +#include <fastboot.h>
> +#include <fb_mmc.h>
> +#include <net.h>
> +#include <net/fastboot.h>
> +#include <part.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 __packed fastboot_header {
> +       uchar id;
> +       uchar flags;
> +       unsigned short seq;
> +};
> +
> +#define PACKET_SIZE 1024
> +#define FASTBOOT_HEADER_SIZE sizeof(struct fastboot_header)

This define doesn't seem any better than just using the sizeof() directly.

> +#define DATA_SIZE (PACKET_SIZE - FASTBOOT_HEADER_SIZE)
> +#define FASTBOOT_VERSION "0.4"
> +
> +/* 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;
> +
> +/* Parsed from first fastboot command packet */
> +static char *cmd_string;
> +static char *cmd_parameter;
> +
> +/* Fastboot download parameters */
> +static unsigned int bytes_received;
> +static unsigned int bytes_expected;

These two are only ever used in fb_download(). They should be static
variables in that function.

> +static unsigned int image_size;
> +
> +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 fb_getvar(char *);
> +static void fb_download(char *, unsigned int, char *);
> +static void fb_flash(char *);
> +static void fb_erase(char *);
> +static void fb_continue(char *);
> +static void fb_reboot(char *);
> +static void boot_downloaded_image(void);
> +static void cleanup_command_data(void);
> +static void write_fb_response(const char *, const char *, char *);
> +
> +void fastboot_send_info(const char *msg)
> +{
> +       uchar *packet;
> +       uchar *packet_base;
> +       int len = 0;
> +       char response[FASTBOOT_RESPONSE_LEN] = {0};
> +
> +       struct fastboot_header fb_response_header = {
> +               .id = FASTBOOT_FASTBOOT,
> +               .flags = 0,
> +               .seq = htons(fb_sequence_number)
> +       };
> +       ++fb_sequence_number;
> +       packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
> +       packet_base = packet;
> +
> +       /* Write headers */
> +       memcpy(packet, &fb_response_header, sizeof(fb_response_header));
> +       packet += sizeof(fb_response_header);
> +       /* Write response */
> +       write_fb_response("INFO", msg, response);
> +       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);
> +}
> +
> +/**
> + * Constructs and sends a packet in response to received fastboot packet
> + *
> + * @param fb_header            Header for response packet
> + * @param fastboot_data        Pointer to received fastboot data
> + * @param fastboot_data_len    Length of received fastboot data
> + * @param retransmit           Nonzero if sending last sent packet
> + */
> +static void fastboot_send(struct fastboot_header fb_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 fb_response_header = fb_header;
> +       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;
> +       }
> +
> +       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:
> +               if (!cmd_string) {
> +                       /* Parse command and send ack */
> +                       cmd_parameter = fastboot_data;

This seems unnecessary. There are only 4 cases handled, and of them
only download seems to be a command that happens more than once. And
in download, the first past through this parameter is saved internally
as bytes_expected.

> +                       cmd_string = strsep(&cmd_parameter, ":");
> +                       cmd_string = strdup(cmd_string);
> +                       if (cmd_parameter)
> +                               cmd_parameter = strdup(cmd_parameter);
> +               } else if (!strcmp("getvar", cmd_string)) {
> +                       fb_getvar(response);

Seems like you should simply pass the "fastboot_data" as a parameter
here rather than using a global.

> +               } else if (!strcmp("download", cmd_string)) {
> +                       fb_download(fastboot_data, fastboot_data_len, response);
> +               } else if (!strcmp("flash", cmd_string)) {
> +                       fb_flash(response);
> +               } else if (!strcmp("erase", cmd_string)) {
> +                       fb_erase(response);
> +               } else if (!strcmp("boot", cmd_string)) {
> +                       write_fb_response("OKAY", "", response);
> +               } else if (!strcmp("continue", cmd_string)) {
> +                       fb_continue(response);
> +               } else if (!strncmp("reboot", cmd_string, 6)) {
> +                       fb_reboot(response);
> +               } else if (!strcmp("set_active", cmd_string)) {
> +                       /* A/B not implemented, for now do nothing */
> +                       write_fb_response("OKAY", "", response);
> +               } else {
> +                       pr_err("command %s not implemented.\n", cmd_string);
> +                       write_fb_response("FAIL", "unrecognized command",
> +                                         response);
> +               }
> +               /* Sent some INFO packets, need to update sequence number in
> +                * header
> +                */
> +               if (fb_header.seq != fb_sequence_number) {
> +                       fb_response_header.seq = htons(fb_sequence_number);
> +                       memcpy(packet_base, &fb_response_header,
> +                              sizeof(fb_response_header));
> +               }
> +               /* Write response to packet */
> +               memcpy(packet, response, strlen(response));
> +               packet += strlen(response);
> +               break;
> +       default:
> +               pr_err("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);
> +
> +       /* Continue boot process after sending response */
> +       if (!strncmp("OKAY", response, 4)) {
> +               if (!strcmp("boot", cmd_string)) {
> +                       boot_downloaded_image();
> +               } else if (!strcmp("continue", cmd_string)) {
> +                       run_command(env_get("bootcmd"), CMD_FLAG_ENV);
> +               } else if (!strncmp("reboot", cmd_string, 6)) {
> +                       /* Matches reboot or reboot-bootloader */
> +                       do_reset(NULL, 0, 0, NULL);
> +               }
> +       }
> +
> +       /* OKAY and FAIL indicate command is complete */
> +       if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4))
> +               cleanup_command_data();
> +}
> +
> +/**
> + * Writes ascii string specified by cmd_parameter to response.
> + *
> + * @param repsonse    Pointer to fastboot response buffer
> + */
> +static void fb_getvar(char *response)
> +{
> +       if (!cmd_parameter) {
> +               write_fb_response("FAIL", "missing var", response);
> +       } else if (!strcmp("version", cmd_parameter)) {
> +               write_fb_response("OKAY", FASTBOOT_VERSION, response);
> +       } else if (!strcmp("bootloader-version", cmd_parameter) ||
> +                  !strcmp("version-bootloader", cmd_parameter)) {
> +               write_fb_response("OKAY", U_BOOT_VERSION, response);
> +       } else if (!strcmp("downloadsize", cmd_parameter) ||
> +                  !strcmp("max-download-size", cmd_parameter)) {
> +               char buf_size_str[12];
> +
> +               sprintf(buf_size_str, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE);
> +               write_fb_response("OKAY", buf_size_str, response);
> +       } else if (!strcmp("serialno", cmd_parameter)) {
> +               const char *tmp = env_get("serial#");
> +
> +               if (tmp)
> +                       write_fb_response("OKAY", tmp, response);
> +               else
> +                       write_fb_response("FAIL", "Value not set", response);
> +       } else if (!strcmp("version-baseband", cmd_parameter)) {
> +               write_fb_response("OKAY", "N/A", response);
> +       } else if (!strcmp("product", cmd_parameter)) {
> +               const char *board = env_get("board");
> +
> +               if (board)
> +                       write_fb_response("OKAY", board, response);
> +               else
> +                       write_fb_response("FAIL", "Board not set", response);
> +       } else if (!strcmp("current-slot", cmd_parameter)) {
> +               /* A/B not implemented, for now always return _a */
> +               write_fb_response("OKAY", "_a", response);
> +       } else if (!strcmp("slot-suffixes", cmd_parameter)) {
> +               write_fb_response("OKAY", "_a,_b", response);
> +       } else if (!strncmp("has-slot", cmd_parameter, 8)) {
> +               char *part_name = cmd_parameter;
> +
> +               cmd_parameter = strsep(&part_name, ":");
> +               if (!strcmp(part_name, "boot") || !strcmp(part_name, "system"))
> +                       write_fb_response("OKAY", "yes", response);
> +               else
> +                       write_fb_response("OKAY", "no", response);
> +       } else if (!strncmp("partition-type", cmd_parameter, 14) ||
> +                  !strncmp("partition-size", cmd_parameter, 14)) {
> +               disk_partition_t part_info;
> +               struct blk_desc *dev_desc;
> +               char *part_name = cmd_parameter;
> +               char part_size_str[20];
> +
> +               cmd_parameter = strsep(&part_name, ":");
> +               dev_desc = blk_get_dev("mmc", 0);
> +               if (!dev_desc) {
> +                       write_fb_response("FAIL", "block device not found",
> +                                         response);
> +               } else if (part_get_info_by_name(dev_desc, part_name,
> +                                                &part_info) < 0) {
> +                       write_fb_response("FAIL", "partition not found",
> +                                         response);
> +               } else if (!strncmp("partition-type", cmd_parameter, 14)) {
> +                       write_fb_response("OKAY", (char *)part_info.type,
> +                                         response);
> +               } else if (!strncmp("partition-size", cmd_parameter, 14)) {
> +                       sprintf(part_size_str, "0x%016x", (int)part_info.size);
> +                       write_fb_response("OKAY", part_size_str, response);
> +               }
> +       } else {
> +               printf("WARNING: unknown variable: %s\n", cmd_parameter);
> +               write_fb_response("FAIL", "Variable not implemented",
> +                                 response);
> +       }
> +}
> +
> +/**
> + * Copies image data from fastboot_data to CONFIG_FASTBOOT_BUF_ADDR.
> + * Writes to response.
> + *
> + * @param fastboot_data        Pointer to received fastboot data
> + * @param fastboot_data_len    Length of received fastboot data
> + * @param repsonse             Pointer to fastboot response buffer
> + */
> +static void fb_download(char *fastboot_data, unsigned int fastboot_data_len,
> +                       char *response)
> +{
> +       char *tmp;
> +
> +       if (bytes_expected == 0) {
> +               if (!cmd_parameter) {
> +                       write_fb_response("FAIL", "Expected command parameter",
> +                                         response);
> +                       return;
> +               }
> +               bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16);
> +               if (bytes_expected == 0) {
> +                       write_fb_response("FAIL", "Expected nonzero image size",
> +                                         response);
> +                       return;
> +               }
> +       }
> +       if (fastboot_data_len == 0 && bytes_received == 0) {
> +               /* Nothing to download yet. Response is of the form:
> +                * [DATA|FAIL]$cmd_parameter
> +                *
> +                * where cmd_parameter is an 8 digit hexadecimal number
> +                */
> +               if (bytes_expected > CONFIG_FASTBOOT_BUF_SIZE)
> +                       write_fb_response("FAIL", cmd_parameter, response);
> +               else
> +                       write_fb_response("DATA", cmd_parameter, response);
> +       } else if (fastboot_data_len == 0 &&
> +                  (bytes_received >= bytes_expected)) {
> +               /* Download complete. Respond with "OKAY" */
> +               write_fb_response("OKAY", "", response);
> +               image_size = bytes_received;
> +               bytes_expected = 0;
> +               bytes_received = 0;
> +       } else {
> +               if (fastboot_data_len == 0 ||
> +                   (bytes_received + fastboot_data_len) > bytes_expected) {
> +                       write_fb_response("FAIL",
> +                                         "Received invalid data length",
> +                                         response);
> +                       return;
> +               }
> +               /* Download data to CONFIG_FASTBOOT_BUF_ADDR */
> +               memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR + bytes_received,
> +                      fastboot_data, fastboot_data_len);

This is different than all the other approaches, which take the load
address as a command parameter. Is there a good reason to have it
baked in like this?

> +               bytes_received += fastboot_data_len;
> +       }
> +}
> +
> +/**
> + * Writes the previously downloaded image to the partition indicated by
> + * cmd_parameter. Writes to response.
> + *
> + * @param repsonse    Pointer to fastboot response buffer
> + */
> +static void fb_flash(char *response)
> +{
> +       fb_mmc_flash_write(cmd_parameter, (void *)CONFIG_FASTBOOT_BUF_ADDR,
> +                          image_size, response);

It's peculiar that this hard-codes mmc.

> +}
> +
> +/**
> + * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes
> + * to response.
> + *
> + * @param repsonse    Pointer to fastboot response buffer
> + */
> +static void fb_erase(char *response)
> +{
> +       fb_mmc_erase(cmd_parameter, response);
> +}
> +
> +/**
> + * Continues normal boot process by running "bootcmd". Writes
> + * to response.
> + *
> + * @param repsonse    Pointer to fastboot response buffer
> + */
> +static void fb_continue(char *response)
> +{
> +       char *bootcmd;
> +
> +       bootcmd = env_get("bootcmd");
> +       if (bootcmd)
> +               write_fb_response("OKAY", "", response);
> +       else
> +               write_fb_response("FAIL", "bootcmd not set", response);
> +}
> +
> +/**
> + * Sets reboot bootloader flag if requested. Writes to response.
> + *
> + * @param repsonse    Pointer to fastboot response buffer
> + */
> +static void fb_reboot(char *response)
> +{
> +       write_fb_response("OKAY", "", response);
> +       if (!strcmp("reboot-bootloader", cmd_string))
> +               strcpy((char *)CONFIG_FASTBOOT_BUF_ADDR, "reboot-bootloader");
> +}
> +
> +/**
> + * Boots into downloaded image.
> + */
> +static void boot_downloaded_image(void)
> +{
> +       char kernel_addr[12];
> +       char *fdt_addr = env_get("fdt_addr_r");
> +       char *const bootm_args[] = {
> +               "bootm", kernel_addr, "-", fdt_addr, NULL
> +       };

It seems like this should be able to affected from the host side...
for instance choosing a config in an ITB.

How is the fdt supposed to be populated in the scenario at all?

> +
> +       sprintf(kernel_addr, "0x%lx", (long)CONFIG_FASTBOOT_BUF_ADDR);
> +
> +       printf("\nBooting kernel at %s with fdt at %s...\n\n\n",
> +              kernel_addr, fdt_addr);
> +       do_bootm(NULL, 0, 4, bootm_args);
> +
> +       /* This only happens if image is faulty so we start over. */
> +       do_reset(NULL, 0, 0, NULL);
> +}
> +
> +/**
> + * Writes a response to response buffer of the form "$tag$reason".
> + *
> + * @param tag         The first part of the response
> + * @param reason      The second part of the response
> + * @param repsonse    Pointer to fastboot response buffer
> + */
> +static void write_fb_response(const char *tag, const char *reason,
> +                             char *response)
> +{
> +       strncpy(response, tag, strlen(tag));
> +       strncat(response, reason, FASTBOOT_RESPONSE_LEN - strlen(tag) - 1);
> +}
> +
> +/**
> + * Frees any resources allocated during current fastboot command.
> + */
> +static void cleanup_command_data(void)
> +{
> +       /* cmd_parameter and cmd_string potentially point to memory allocated by

Use correct multi-line comment format. "/*" on its own line.

> +        * strdup
> +        */
> +       if (cmd_parameter)
> +               free(cmd_parameter);
> +       if (cmd_string)
> +               free(cmd_string);
> +       cmd_parameter = NULL;
> +       cmd_string = NULL;
> +}
> +
> +/**
> + * 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 int dport,
> +                            struct in_addr sip, unsigned int sport,
> +                            unsigned int len)
> +{
> +       struct fastboot_header fb_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 < 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, fastboot_data, 0, 0);
> +               break;
> +       case FASTBOOT_INIT:
> +       case FASTBOOT_FASTBOOT:
> +               fastboot_data_len = len;
> +               if (len > 0)
> +                       memcpy(fastboot_data, packet, len);
> +               if (fb_header.seq == fb_sequence_number) {
> +                       fastboot_send(fb_header, fastboot_data,
> +                                     fastboot_data_len, 0);
> +                       fb_sequence_number++;
> +               } else if (fb_header.seq == fb_sequence_number - 1) {
> +                       /* Retransmit last sent packet */
> +                       fastboot_send(fb_header, fastboot_data,
> +                                     fastboot_data_len, 1);
> +               }
> +               break;
> +       default:
> +               pr_err("ID %d not implemented.\n", fb_header.id);
> +               fb_header.id = FASTBOOT_ERROR;
> +               fastboot_send(fb_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;
> +
> +       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 d222c1f..554df7a 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -87,6 +87,9 @@
>  #include <environment.h>
>  #include <errno.h>
>  #include <net.h>
> +#if defined(CONFIG_UDP_FUNCTION_FASTBOOT)

I'd prefer we not proliferate the guards around includes.

> +#include <net/fastboot.h>
> +#endif
>  #include <net/tftp.h>
>  #if defined(CONFIG_LED_STATUS)
>  #include <miiphy.h>
> @@ -451,6 +454,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();
> @@ -1322,6 +1330,7 @@ common:
>                 /* Fall through */
>
>         case NETCONS:
> +       case FASTBOOT:
>         case TFTPSRV:
>                 if (net_ip.s_addr == 0) {
>                         puts("*** ERROR: `ipaddr' not set\n");
> --
> 2.7.4
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot


More information about the U-Boot mailing list