This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
commit 7790894bb67ee2591ea6dfc88710e3b2e69580af Author: wangjianyu3 <wangjian...@xiaomi.com> AuthorDate: Fri Jun 13 20:26:02 2025 +0800 system/fastboot: add support for fastboot tcp Add TCP network transport support for fastboot, users can add "-s tcp:HOST[:PORT]" option to specify a network device. Examples fastboot -s tcp:192.168.100.2 getvar version fastboot -s tcp:192.168.100.2 flash virtblk0 fastboot.c fastboot -s tcp:192.168.100.2 oem shell "uname -a" Signed-off-by: wangjianyu3 <wangjian...@xiaomi.com> --- system/fastboot/Kconfig | 3 +- system/fastboot/fastboot.c | 347 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 310 insertions(+), 40 deletions(-) diff --git a/system/fastboot/Kconfig b/system/fastboot/Kconfig index eb5edcfd9..4b7cad267 100644 --- a/system/fastboot/Kconfig +++ b/system/fastboot/Kconfig @@ -6,7 +6,7 @@ menuconfig SYSTEM_FASTBOOTD bool "fastbootd" default n - depends on USBFASTBOOT + depends on USBFASTBOOT || NET_TCP ---help--- support usb fastboot function. @@ -29,6 +29,7 @@ config SYSTEM_FASTBOOTD_USB_BOARDCTL default n depends on BOARDCTL depends on BOARDCTL_USBDEVCTRL + depends on USBFASTBOOT ---help--- Connect usbdev before running fastboot daemon. diff --git a/system/fastboot/fastboot.c b/system/fastboot/fastboot.c index bf97fdb61..063476e45 100644 --- a/system/fastboot/fastboot.c +++ b/system/fastboot/fastboot.c @@ -41,9 +41,11 @@ #include <syslog.h> #include <unistd.h> +#include <netinet/in.h> #include <sys/boardctl.h> #include <sys/ioctl.h> #include <sys/param.h> +#include <sys/socket.h> #include <sys/stat.h> #include <sys/statfs.h> #include <sys/types.h> @@ -73,6 +75,19 @@ #define FASTBOOT_SPARSE_HEADER sizeof(struct fastboot_sparse_header_s) #define FASTBOOT_CHUNK_HEADER sizeof(struct fastboot_chunk_header_s) +/* Fastboot TCP Protocol v1 + * + * handshake: chars "FB" followed by a 2-digit base-10 ASCII version number + * data_size: 8-byte big-endian binary value before fastboot packet + * + * https://android.googlesource.com/platform/system/core/+/refs/heads/main\ + * /fastboot/README.md#tcp-protocol-v1 + */ + +#define FASTBOOT_TCP_HANDSHAKE "FB01" +#define FASTBOOT_TCP_HANDSHAKE_LEN 4 +#define FASTBOOT_TCP_PORT 5554 + #define fb_info(...) syslog(LOG_INFO, ##__VA_ARGS__); #define fb_err(...) syslog(LOG_ERR, ##__VA_ARGS__); @@ -80,6 +95,8 @@ * Private types ****************************************************************************/ +struct fastboot_ctx_s; + struct fastboot_var_s { FAR struct fastboot_var_s *next; @@ -122,19 +139,40 @@ struct fastboot_file_s off_t offset; }; +struct fastboot_transport_ops_s +{ + CODE int (*init)(FAR struct fastboot_ctx_s *); + CODE void (*deinit)(FAR struct fastboot_ctx_s *); + CODE ssize_t (*read)(FAR struct fastboot_ctx_s *, FAR void *, size_t); + CODE int (*write)(FAR struct fastboot_ctx_s *, FAR const void *, size_t); +}; + struct fastboot_ctx_s { - int usbdev_in; - int usbdev_out; + /* Transport file descriptors + * + * | idx | USB | TCP | poll | + * |-----|----------|---------------|------| + * | 0 |usbdev in |TCP socket | Y | + * | 1 |usbdev out|accepted socket| N | + */ + + int tran_fd[2]; int flash_fd; size_t download_max; size_t download_size; size_t download_offset; size_t total_imgsize; - int wait_ms; + + /* Store wait_ms argument before poll of fastboot_command_loop, and + * TCP transport remaining data size later. + */ + + uint64_t left; FAR void *download_buffer; FAR struct fastboot_var_s *varlist; CODE int (*upload_func)(FAR struct fastboot_ctx_s *context); + FAR const struct fastboot_transport_ops_s *ops; struct { size_t size; @@ -185,6 +223,27 @@ static void fastboot_shell(FAR struct fastboot_ctx_s *context, FAR const char *arg); #endif +/* USB transport */ + +#ifdef CONFIG_USBFASTBOOT +static int fastboot_usbdev_initialize(FAR struct fastboot_ctx_s *ctx); +static void fastboot_usbdev_deinit(FAR struct fastboot_ctx_s *ctx); +static ssize_t fastboot_usbdev_read(FAR struct fastboot_ctx_s *ctx, + FAR void *buf, size_t len); +static int fastboot_usbdev_write(FAR struct fastboot_ctx_s *ctx, + FAR const void *buf, size_t len); +#elif defined(CONFIG_NET_TCP) + +/* TCP transport */ + +static int fastboot_tcp_initialize(FAR struct fastboot_ctx_s *ctx); +static void fastboot_tcp_deinit(FAR struct fastboot_ctx_s *ctx); +static ssize_t fastboot_tcp_read(FAR struct fastboot_ctx_s *ctx, + FAR void *buf, size_t len); +static int fastboot_tcp_write(FAR struct fastboot_ctx_s *ctx, + FAR const void *buf, size_t len); +#endif + /**************************************************************************** * Private Data ****************************************************************************/ @@ -217,6 +276,24 @@ static const struct memory_region_s g_memory_region[] = }; #endif +#ifdef CONFIG_USBFASTBOOT +static const struct fastboot_transport_ops_s g_tran_ops_usb = +{ + .init = fastboot_usbdev_initialize, + .deinit = fastboot_usbdev_deinit, + .read = fastboot_usbdev_read, + .write = fastboot_usbdev_write, +}; +#elif defined(CONFIG_NET_TCP) +static const struct fastboot_transport_ops_s g_tran_ops_tcp = +{ + .init = fastboot_tcp_initialize, + .deinit = fastboot_tcp_deinit, + .read = fastboot_tcp_read, + .write = fastboot_tcp_write, +}; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -239,9 +316,9 @@ static ssize_t fastboot_read(int fd, FAR void *buf, size_t len) return r < 0 ? -errno : r; } -static int fastboot_write(int fd, FAR void *buf, size_t len) +static int fastboot_write(int fd, FAR const void *buf, size_t len) { - FAR char *data = buf; + FAR const char *data = buf; while (len > 0) { @@ -270,7 +347,7 @@ static void fastboot_ack(FAR struct fastboot_ctx_s *context, } snprintf(response, FASTBOOT_MSG_LEN, "%s%s", code, reason); - fastboot_write(context->usbdev_out, response, strlen(response)); + context->ops->write(context, response, strlen(response)); } static void fastboot_fail(FAR struct fastboot_ctx_s *context, @@ -572,7 +649,7 @@ static void fastboot_download(FAR struct fastboot_ctx_s *context, } snprintf(response, FASTBOOT_MSG_LEN, "DATA%08lx", len); - ret = fastboot_write(context->usbdev_out, response, strlen(response)); + ret = context->ops->write(context, response, strlen(response)); if (ret < 0) { fb_err("Reponse error [%d]\n", -ret); @@ -584,8 +661,7 @@ static void fastboot_download(FAR struct fastboot_ctx_s *context, while (len > 0) { - ssize_t r = fastboot_read(context->usbdev_in, - download, len); + ssize_t r = context->ops->read(context, download, len); if (r < 0) { context->download_size = 0; @@ -651,9 +727,8 @@ static void fastboot_reboot_bootloader(FAR struct fastboot_ctx_s *context, static int fastboot_memdump_upload(FAR struct fastboot_ctx_s *context) { - return fastboot_write(context->usbdev_out, - context->upload_param.u.mem.addr, - context->upload_param.size); + return context->ops->write(context, context->upload_param.u.mem.addr, + context->upload_param.size); } static void fastboot_memdump_region(memdump_print_t memprint, FAR void *priv) @@ -748,9 +823,8 @@ static int fastboot_filedump_upload(FAR struct fastboot_ctx_s *context) break; } else if (nread < 0 || - fastboot_write(context->usbdev_out, - context->download_buffer, - nread) < 0) + context->ops->write(context, context->download_buffer, + nread) < 0) { fb_err("Upload failed (%zu bytes left)\n", size); close(fd); @@ -869,7 +943,7 @@ static void fastboot_upload(FAR struct fastboot_ctx_s *context, snprintf(response, FASTBOOT_MSG_LEN, "DATA%08zx", context->upload_param.size); - ret = fastboot_write(context->usbdev_out, response, strlen(response)); + ret = context->ops->write(context, response, strlen(response)); if (ret < 0) { fb_err("Reponse error [%d]\n", -ret); @@ -919,31 +993,34 @@ static void fastboot_oem(FAR struct fastboot_ctx_s *context, static void fastboot_command_loop(FAR struct fastboot_ctx_s *context) { - if (context->wait_ms > 0) + if (context->left > 0) { struct pollfd fds[1]; - fds[0].fd = context->usbdev_in; + fds[0].fd = context->tran_fd[0]; fds[0].events = POLLIN; - if (poll(fds, 1, context->wait_ms) <= 0) + if (poll(fds, 1, context->left) <= 0) { return; } } + /* Reinitialize for storing TCP transport remaining data size. */ + + context->left = 0; + while (1) { char buffer[FASTBOOT_MSG_LEN + 1]; size_t ncmds = nitems(g_fast_cmd); size_t index; - ssize_t r = fastboot_read(context->usbdev_in, - buffer, FASTBOOT_MSG_LEN); + ssize_t r = context->ops->read(context, buffer, FASTBOOT_MSG_LEN); if (r < 0) { - fb_err("USB read error\n"); - break; + fb_err("fastboot transport read() %zd\n", r); + continue; } buffer[r] = '\0'; @@ -1007,6 +1084,7 @@ static void fastboot_free_publish(FAR struct fastboot_ctx_s *context) } } +#ifdef CONFIG_USBFASTBOOT static int fastboot_open_usb(int index, int flags) { int try = FASTBOOT_EP_RETRY_TIMES; @@ -1071,20 +1149,20 @@ static int fastboot_usbdev_initialize(FAR struct fastboot_ctx_s *ctx) } #endif /* SYSTEM_FASTBOOTD_USB_BOARDCTL */ - ctx->usbdev_in = + ctx->tran_fd[0] = fastboot_open_usb(FASTBOOT_EP_BULKOUT_IDX, O_RDONLY | O_CLOEXEC); - if (ctx->usbdev_in < 0) + if (ctx->tran_fd[0] < 0) { - return ctx->usbdev_in; + return ctx->tran_fd[0]; } - ctx->usbdev_out = + ctx->tran_fd[1] = fastboot_open_usb(FASTBOOT_EP_BULKIN_IDX, O_WRONLY | O_CLOEXEC); - if (ctx->usbdev_out < 0) + if (ctx->tran_fd[1] < 0) { - close(ctx->usbdev_in); - ctx->usbdev_in = -1; - return ctx->usbdev_out; + close(ctx->tran_fd[0]); + ctx->tran_fd[0] = -1; + return ctx->tran_fd[1]; } return 0; @@ -1092,12 +1170,193 @@ static int fastboot_usbdev_initialize(FAR struct fastboot_ctx_s *ctx) static void fastboot_usbdev_deinit(FAR struct fastboot_ctx_s *ctx) { - close(ctx->usbdev_out); - ctx->usbdev_out = -1; - close(ctx->usbdev_in); - ctx->usbdev_in = -1; + int i; + + for (i = 0; i < nitems(ctx->tran_fd); i++) + { + close(ctx->tran_fd[i]); + ctx->tran_fd[i] = -1; + } +} + +static ssize_t fastboot_usbdev_read(FAR struct fastboot_ctx_s *ctx, + FAR void *buf, size_t len) +{ + return fastboot_read(ctx->tran_fd[0], buf, len); +} + +static int fastboot_usbdev_write(FAR struct fastboot_ctx_s *ctx, + FAR const void *buf, size_t len) +{ + return fastboot_write(ctx->tran_fd[1], buf, len); +} + +#elif defined(CONFIG_NET_TCP) +static int fastboot_tcp_initialize(FAR struct fastboot_ctx_s *ctx) +{ + struct sockaddr_in addr; + + ctx->tran_fd[0] = socket(AF_INET, SOCK_STREAM, 0); + if (ctx->tran_fd[0] < 0) + { + fb_err("create socket failed %d", errno); + return -errno; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(FASTBOOT_TCP_PORT); + if (bind(ctx->tran_fd[0], (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + fb_err("bind() failed %d", errno); + goto error; + } + + if (listen(ctx->tran_fd[0], 1) < 0) + { + fb_err("listen() failed %d", errno); + goto error; + } + + return 0; +error: + close(ctx->tran_fd[0]); + ctx->tran_fd[0] = -1; + return -errno; +} + +static void fastboot_tcp_disconn(FAR struct fastboot_ctx_s *ctx) +{ + close(ctx->tran_fd[1]); + ctx->tran_fd[1] = -1; } +static void fastboot_tcp_deinit(FAR struct fastboot_ctx_s *ctx) +{ + fastboot_tcp_disconn(ctx); + close(ctx->tran_fd[0]); + ctx->tran_fd[0] = -1; +} + +static ssize_t fastboot_read_all(int fd, FAR void *buf, size_t len) +{ + size_t total = 0; + ssize_t nread; + + while (total < len) + { + nread = fastboot_read(fd, buf, len); + if (nread <= 0) + { + if (total == 0) + { + return nread; + } + + break; + } + + total += nread; + } + + return total; +} + +static ssize_t fastboot_tcp_read(FAR struct fastboot_ctx_s *ctx, + FAR void *buf, size_t len) +{ + char handshake[FASTBOOT_TCP_HANDSHAKE_LEN]; + uint64_t data_size; + ssize_t nread; + + if (ctx->tran_fd[1] == -1) + { + while (1) + { + /* Accept a connection, not care the address of the peer socket */ + + ctx->tran_fd[1] = accept(ctx->tran_fd[0], NULL, 0); + if (ctx->tran_fd[1] < 0) + { + continue; + } + + /* Handshake */ + + memset(handshake, 0, sizeof(handshake)); + if (fastboot_read_all(ctx->tran_fd[1], handshake, + sizeof(handshake)) != sizeof(handshake) || + strncmp(handshake, FASTBOOT_TCP_HANDSHAKE, + sizeof(handshake)) != 0 || + fastboot_write(ctx->tran_fd[1], handshake, + sizeof(handshake)) < 0) + { + fb_err("%s err handshake %d 0x%" PRIx32, __func__, errno, + *(FAR uint32_t *)handshake); + fastboot_tcp_disconn(ctx); + continue; + } + + break; + } + } + + if (ctx->left == 0) + { + nread = + fastboot_read_all(ctx->tran_fd[1], &data_size, sizeof(data_size)); + if (nread != sizeof(data_size)) + { + /* As normal, end of file if client has closed the connection */ + + if (nread != 0) + { + fb_err("%s err read data_size %zd %d", __func__, nread, errno); + } + + fastboot_tcp_disconn(ctx); + return nread; + } + + ctx->left = be64toh(data_size); + } + + if (len > ctx->left) + { + len = ctx->left; + } + + nread = fastboot_read(ctx->tran_fd[1], buf, len); + if (nread <= 0) + { + fastboot_tcp_disconn(ctx); + ctx->left = 0; + } + else + { + ctx->left -= nread; + } + + return nread; +} + +static int fastboot_tcp_write(FAR struct fastboot_ctx_s *ctx, + FAR const void *buf, size_t len) +{ + uint64_t data_size = htobe64(len); + int ret; + + ret = fastboot_write(ctx->tran_fd[1], &data_size, sizeof(data_size)); + if (ret < 0) + { + return ret; + } + + return fastboot_write(ctx->tran_fd[1], buf, len); +} +#endif + static int fastboot_context_initialize(FAR struct fastboot_ctx_s *ctx) { ctx->download_max = CONFIG_SYSTEM_FASTBOOTD_DOWNLOAD_MAX; @@ -1106,7 +1365,14 @@ static int fastboot_context_initialize(FAR struct fastboot_ctx_s *ctx) ctx->flash_fd = -1; ctx->total_imgsize = 0; ctx->varlist = NULL; - ctx->wait_ms = 0; + ctx->left = 0; +#ifdef CONFIG_USBFASTBOOT + ctx->ops = &g_tran_ops_usb; +#elif defined(CONFIG_NET_TCP) + ctx->ops = &g_tran_ops_tcp; +#endif + ctx->tran_fd[0] = -1; + ctx->tran_fd[1] = -1; ctx->download_buffer = malloc(CONFIG_SYSTEM_FASTBOOTD_DOWNLOAD_MAX); if (ctx->download_buffer == NULL) @@ -1148,10 +1414,13 @@ int main(int argc, FAR char **argv) return 0; } - context.wait_ms = atoi(argv[1]); + if (sscanf(argv[1], "%" SCNu64 , &context.left) != 1) + { + return -EINVAL; + } } - ret = fastboot_usbdev_initialize(&context); + ret = context.ops->init(&context); if (ret < 0) { return ret; @@ -1160,7 +1429,7 @@ int main(int argc, FAR char **argv) fastboot_create_publish(&context); fastboot_command_loop(&context); fastboot_free_publish(&context); - fastboot_usbdev_deinit(&context); + context.ops->deinit(&context); fastboot_context_deinit(&context); return ret;