wangjianyu3 c4ff19a7e4 system/fastboot: fix typos of response error log
Fix typos of sparse header comments and download/upload response error log.

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
2025-06-25 10:01:27 +02:00

1512 lines
38 KiB
C

/****************************************************************************
* apps/system/fastboot/fastboot.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/memoryregion.h>
#include <nuttx/mtd/mtd.h>
#include <nuttx/version.h>
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/boardctl.h>
#include <sys/epoll.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>
#include <sys/wait.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define FASTBOOT_USBDEV "/dev/fastboot"
#define FASTBOOT_BLKDEV "/dev/%s"
#define FASTBOOT_EP_BULKIN_IDX 1
#define FASTBOOT_EP_BULKOUT_IDX 2
#define FASTBOOT_EP_RETRY_TIMES 100
#define FASTBOOT_EP_RETRY_DELAY_MS 10
#define FASTBOOT_MSG_LEN 64
#define FASTBOOT_SPARSE_MAGIC 0xed26ff3a
#define FASTBOOT_CHUNK_RAW 0xcac1
#define FASTBOOT_CHUNK_FILL 0xcac2
#define FASTBOOT_CHUNK_DONT_CARE 0xcac3
#define FASTBOOT_CHUNK_CRC32 0xcac4
#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__);
/****************************************************************************
* Private types
****************************************************************************/
struct fastboot_ctx_s;
struct fastboot_var_s
{
FAR struct fastboot_var_s *next;
FAR const char *name;
FAR const char *string;
int data;
};
struct fastboot_sparse_header_s
{
uint32_t magic; /* 0xed26ff3a */
uint16_t major_version; /* (0x1) - reject images with higher major versions */
uint16_t minor_version; /* (0x0) - allow images with higher minor versions */
uint16_t file_hdr_sz; /* 28 bytes for first revision of the file format */
uint16_t chunk_hdr_sz; /* 12 bytes for first revision of the file format */
uint32_t blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */
uint32_t total_blks; /* total blocks in the non-sparse output image */
uint32_t total_chunks; /* total chunks in the sparse input image */
uint32_t image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
/* as 0. Standard 802.3 polynomial, use a Public Domain */
/* table implementation */
};
struct fastboot_chunk_header_s
{
uint16_t chunk_type; /* 0xcac1 -> raw; 0xcac2 -> fill; 0xcac3 -> don't care */
uint16_t reserved1;
uint32_t chunk_sz; /* in blocks in output image */
uint32_t total_sz; /* in bytes of chunk input file including chunk header and data */
};
struct fastboot_mem_s
{
FAR void *addr;
};
struct fastboot_file_s
{
char path[PATH_MAX];
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
{
/* 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;
/* Store wait_ms argument before poll of fastboot_command_loop, and
* TCP transport remaining data size later.
*/
uint64_t left;
FAR void *handle;
FAR void *download_buffer;
FAR struct fastboot_var_s *varlist;
CODE int (*upload_func)(FAR struct fastboot_ctx_s *);
FAR const struct fastboot_transport_ops_s *ops;
struct
{
size_t size;
union
{
struct fastboot_mem_s mem;
struct fastboot_file_s file;
} u;
} upload_param;
};
struct fastboot_cmd_s
{
FAR const char *prefix;
CODE void (*handle)(FAR struct fastboot_ctx_s *, FAR const char *);
};
typedef void (*memdump_print_t)(FAR void *, FAR const char *);
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void fastboot_getvar(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
static void fastboot_download(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
static void fastboot_erase(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
static void fastboot_flash(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
static void fastboot_reboot(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
static void fastboot_reboot_bootloader(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
static void fastboot_oem(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
static void fastboot_upload(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
static void fastboot_memdump(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
static void fastboot_filedump(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg);
#ifdef CONFIG_SYSTEM_FASTBOOTD_SHELL
static void fastboot_shell(FAR struct fastboot_ctx_s *ctx,
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);
#endif
/* TCP transport */
#ifdef CONFIG_NET_TCP
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
****************************************************************************/
static const struct fastboot_cmd_s g_fast_cmd[] =
{
{ "getvar:", fastboot_getvar },
{ "download:", fastboot_download },
{ "erase:", fastboot_erase },
{ "flash:", fastboot_flash },
{ "reboot-bootloader", fastboot_reboot_bootloader},
{ "reboot", fastboot_reboot },
{ "oem", fastboot_oem },
{ "upload", fastboot_upload }
};
static const struct fastboot_cmd_s g_oem_cmd[] =
{
{ "filedump", fastboot_filedump },
{ "memdump", fastboot_memdump },
#ifdef CONFIG_SYSTEM_FASTBOOTD_SHELL
{ "shell", fastboot_shell },
#endif
};
#ifdef CONFIG_BOARD_MEMORY_RANGE
static const struct memory_region_s g_memory_region[] =
{
CONFIG_BOARD_MEMORY_RANGE
};
#endif
static const struct fastboot_transport_ops_s g_tran_ops[] =
{
#ifdef CONFIG_USBFASTBOOT
{
.init = fastboot_usbdev_initialize,
.deinit = fastboot_usbdev_deinit,
.read = fastboot_usbdev_read,
.write = fastboot_usbdev_write,
},
#endif
#ifdef CONFIG_NET_TCP
{
.init = fastboot_tcp_initialize,
.deinit = fastboot_tcp_deinit,
.read = fastboot_tcp_read,
.write = fastboot_tcp_write,
},
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
static FAR void *fastboot_memset32(FAR void *m, uint32_t val, size_t count)
{
FAR uint32_t *buf = m;
while (count--)
{
*buf++ = val;
}
return m;
}
static ssize_t fastboot_read(int fd, FAR void *buf, size_t len)
{
ssize_t r = read(fd, buf, len);
return r < 0 ? -errno : r;
}
static int fastboot_write(int fd, FAR const void *buf, size_t len)
{
FAR const char *data = buf;
while (len > 0)
{
ssize_t r = write(fd, data, len);
if (r < 0)
{
return -errno;
}
data += r;
len -= r;
}
return OK;
}
static void fastboot_ack(FAR struct fastboot_ctx_s *ctx,
FAR const char *code, FAR const char *reason)
{
char response[FASTBOOT_MSG_LEN];
if (reason == NULL)
{
reason = "";
}
snprintf(response, FASTBOOT_MSG_LEN, "%s%s", code, reason);
ctx->ops->write(ctx, response, strlen(response));
}
static void fastboot_fail(FAR struct fastboot_ctx_s *ctx,
FAR const char *fmt, ...)
{
char reason[FASTBOOT_MSG_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(reason, sizeof(reason), fmt, ap);
fastboot_ack(ctx, "FAIL", reason);
va_end(ap);
}
static void fastboot_okay(FAR struct fastboot_ctx_s *ctx,
FAR const char *info)
{
fastboot_ack(ctx, "OKAY", info);
}
static int fastboot_flash_open(FAR const char *name)
{
int fd = open(name, O_RDWR | O_CLOEXEC);
if (fd < 0)
{
fb_err("Open %s error\n", name);
return -errno;
}
return fd;
}
static void fastboot_flash_close(int fd)
{
if (fd >= 0)
{
fsync(fd);
close(fd);
}
}
static int fastboot_flash_write(int fd, off_t offset,
FAR void *data,
size_t size)
{
int ret;
offset = lseek(fd, offset, SEEK_SET);
if (offset < 0)
{
fb_err("Seek error:%d\n", errno);
return -errno;
}
ret = fastboot_write(fd, data, size);
if (ret < 0)
{
fb_err("Flash write error:%d\n", -ret);
}
return ret;
}
static int ffastboot_flash_fill(int fd, off_t offset,
uint32_t fill_data,
uint32_t blk_sz,
uint32_t blk_num)
{
FAR void *buffer;
int ret = OK;
int i;
buffer = malloc(blk_sz);
if (buffer == NULL)
{
fb_err("Flash bwrite malloc fail\n");
return -ENOMEM;
}
fastboot_memset32(buffer, fill_data, blk_sz / 4);
for (i = 0; i < blk_num; i++)
{
ret = fastboot_flash_write(fd, offset, buffer, blk_sz);
if (ret < 0)
{
goto out;
}
offset += blk_sz;
}
out:
free(buffer);
return ret;
}
static int fastboot_flash_erase(int fd)
{
int ret;
ret = ioctl(fd, MTDIOC_BULKERASE, 0);
if (ret < 0)
{
fb_err("Erase device failed\n");
}
return ret < 0 ? -errno : ret;
}
static int fastboot_flash_program(FAR struct fastboot_ctx_s *ctx, int fd)
{
FAR char *chunk_ptr = ctx->download_buffer;
FAR char *end_ptr = chunk_ptr + ctx->download_size;
FAR struct fastboot_sparse_header_s *sparse;
uint32_t chunk_num;
int ret = OK;
/* No sparse header, write flash directly */
sparse = (FAR struct fastboot_sparse_header_s *)chunk_ptr;
if (sparse->magic != FASTBOOT_SPARSE_MAGIC)
{
ret = fastboot_flash_write(fd, 0, ctx->download_buffer,
ctx->download_size);
goto end;
}
if (ctx->total_imgsize == 0)
{
ctx->total_imgsize = sparse->blk_sz * sparse->total_blks;
}
chunk_num = sparse->total_chunks;
chunk_ptr += FASTBOOT_SPARSE_HEADER;
while (chunk_ptr < end_ptr && chunk_num--)
{
FAR struct fastboot_chunk_header_s *chunk =
(FAR struct fastboot_chunk_header_s *)chunk_ptr;
chunk_ptr += FASTBOOT_CHUNK_HEADER;
switch (chunk->chunk_type)
{
case FASTBOOT_CHUNK_RAW:
{
uint32_t chunk_size = chunk->chunk_sz * sparse->blk_sz;
ret = fastboot_flash_write(fd, ctx->download_offset,
chunk_ptr, chunk_size);
if (ret < 0)
{
goto end;
}
ctx->download_offset += chunk_size;
chunk_ptr += chunk_size;
}
break;
case FASTBOOT_CHUNK_FILL:
{
uint32_t fill_data = be32toh(*(FAR uint32_t *)chunk_ptr);
uint32_t chunk_size = chunk->chunk_sz * sparse->blk_sz;
ret = ffastboot_flash_fill(fd, ctx->download_offset, fill_data,
sparse->blk_sz, chunk->chunk_sz);
if (ret < 0)
{
goto end;
}
ctx->download_offset += chunk_size;
chunk_ptr += 4;
}
break;
case FASTBOOT_CHUNK_DONT_CARE:
case FASTBOOT_CHUNK_CRC32:
break;
default:
fb_err("Error chunk type:%d, skip\n", chunk->chunk_type);
break;
}
}
if (ctx->download_offset < ctx->total_imgsize)
{
return 1;
}
end:
ctx->total_imgsize = 0;
ctx->download_offset = 0;
return ret;
}
static void fastboot_flash(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
char blkdev[PATH_MAX];
int ret;
snprintf(blkdev, PATH_MAX, FASTBOOT_BLKDEV, arg);
if (ctx->flash_fd < 0)
{
ctx->flash_fd = fastboot_flash_open(blkdev);
if (ctx->flash_fd < 0)
{
fastboot_fail(ctx, "Flash open failure");
return;
}
}
ret = fastboot_flash_program(ctx, ctx->flash_fd);
if (ret < 0)
{
fastboot_fail(ctx, "Image flash failure");
}
else
{
fastboot_okay(ctx, "");
}
if (ret <= 0)
{
fastboot_flash_close(ctx->flash_fd);
ctx->flash_fd = -1;
}
}
static void fastboot_erase(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
char blkdev[PATH_MAX];
int ret;
int fd;
snprintf(blkdev, PATH_MAX, FASTBOOT_BLKDEV, arg);
fb_info("Erase %s\n", blkdev);
fd = fastboot_flash_open(blkdev);
if (fd < 0)
{
fastboot_fail(ctx, "Flash open failure");
return;
}
ret = fastboot_flash_erase(fd);
if (ret == -ENOTTY)
{
struct stat sb;
ret = fstat(fd, &sb);
if (ret >= 0)
{
memset(ctx->download_buffer, 0xff, ctx->download_max);
while (sb.st_size > 0)
{
size_t len = MIN(sb.st_size, ctx->download_max);
ret = fastboot_write(fd, ctx->download_buffer, len);
if (ret < 0)
{
break;
}
sb.st_size -= len;
}
}
}
if (ret < 0)
{
fastboot_fail(ctx, "Flash erase failure");
}
else
{
fastboot_okay(ctx, "");
}
fastboot_flash_close(fd);
}
static void fastboot_download(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
FAR char *download;
char response[FASTBOOT_MSG_LEN];
unsigned long len;
int ret;
len = strtoul(arg, NULL, 16);
if (len > ctx->download_max)
{
fastboot_fail(ctx, "Data too large");
return;
}
snprintf(response, FASTBOOT_MSG_LEN, "DATA%08lx", len);
ret = ctx->ops->write(ctx, response, strlen(response));
if (ret < 0)
{
fb_err("Response error [%d]\n", -ret);
return;
}
download = ctx->download_buffer;
ctx->download_size = len;
while (len > 0)
{
ssize_t r = ctx->ops->read(ctx, download, len);
if (r < 0)
{
if (errno == EAGAIN)
{
continue;
}
ctx->download_size = 0;
fb_err("fastboot_download usb read error\n");
return;
}
len -= r;
download += r;
}
fastboot_okay(ctx, "");
}
static void fastboot_getvar(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
FAR struct fastboot_var_s *var;
char buffer[FASTBOOT_MSG_LEN];
for (var = ctx->varlist; var != NULL; var = var->next)
{
if (!strcmp(var->name, arg))
{
if (var->string == NULL)
{
itoa(var->data, buffer, 10);
fastboot_okay(ctx, buffer);
}
else
{
fastboot_okay(ctx, var->string);
}
return;
}
}
fastboot_okay(ctx, "");
}
static void fastboot_reboot(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
#ifdef CONFIG_BOARDCTL_RESET
fastboot_okay(ctx, "");
boardctl(BOARDIOC_RESET, BOARDIOC_SOFTRESETCAUSE_USER_REBOOT);
#else
fastboot_fail(ctx, "Operation not supported");
#endif
}
static void fastboot_reboot_bootloader(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
#ifdef CONFIG_BOARDCTL_RESET
fastboot_okay(ctx, "");
boardctl(BOARDIOC_RESET, BOARDIOC_SOFTRESETCAUSE_ENTER_BOOTLOADER);
#else
fastboot_fail(ctx, "Operation not supported");
#endif
}
static int fastboot_memdump_upload(FAR struct fastboot_ctx_s *ctx)
{
return ctx->ops->write(ctx, ctx->upload_param.u.mem.addr,
ctx->upload_param.size);
}
static void fastboot_memdump_region(memdump_print_t memprint, FAR void *priv)
{
#ifdef CONFIG_BOARD_MEMORY_RANGE
char response[FASTBOOT_MSG_LEN - 4];
size_t index;
for (index = 0; index < nitems(g_memory_region); index++)
{
snprintf(response, sizeof(response),
"fastboot oem memdump 0x%" PRIxPTR " 0x%" PRIxPTR "\n",
g_memory_region[index].start,
g_memory_region[index].end - g_memory_region[index].start);
memprint(priv, response);
snprintf(response, sizeof(response),
"fastboot get_staged 0x%" PRIxPTR ".bin\n",
g_memory_region[index].start);
memprint(priv, response);
}
#endif
}
static void fastboot_memdump_syslog(FAR void *priv, FAR const char *response)
{
fb_err(" %s", response);
}
static void fastboot_memdump_response(FAR void *priv,
FAR const char *response)
{
fastboot_ack((FAR struct fastboot_ctx_s *)priv, "TEXT", response);
}
/* Usage(host):
* fastboot oem memdump <addr> <size>
*
* Example
* fastboot oem memdump 0x44000000 0xb6c00
* fastboot get_staged mem_44000000_440b6c00.bin
*/
static void fastboot_memdump(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
if (!arg ||
sscanf(arg, "%p %zx", &ctx->upload_param.u.mem.addr,
&ctx->upload_param.size) != 2)
{
fastboot_memdump_region(fastboot_memdump_response, ctx);
fastboot_fail(ctx, "Invalid argument");
return;
}
fb_info("Memdump Addr: %p, Size: 0x%zx\n", ctx->upload_param.u.mem.addr,
ctx->upload_param.size);
ctx->upload_func = fastboot_memdump_upload;
fastboot_okay(ctx, "");
}
static int fastboot_filedump_upload(FAR struct fastboot_ctx_s *ctx)
{
size_t size = ctx->upload_param.size;
int fd;
fd = open(ctx->upload_param.u.file.path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
{
fb_err("No such file or directory %d\n", errno);
return -errno;
}
if (ctx->upload_param.u.file.offset &&
lseek(fd, ctx->upload_param.u.file.offset,
ctx->upload_param.u.file.offset > 0 ? SEEK_SET : SEEK_END) < 0)
{
fb_err("Invalid argument, offset: %" PRIdOFF "\n",
ctx->upload_param.u.file.offset);
close(fd);
return -errno;
}
while (size > 0)
{
ssize_t nread = fastboot_read(fd, ctx->download_buffer,
MIN(size, ctx->download_max));
if (nread == 0)
{
break;
}
else if (nread < 0 ||
ctx->ops->write(ctx, ctx->download_buffer, nread) < 0)
{
fb_err("Upload failed (%zu bytes left)\n", size);
close(fd);
return -errno;
}
size -= nread;
}
close(fd);
return 0;
}
/* Usage(host):
* fastboot oem filedump <PATH> [<offset> <size>]
*
* Example
* a. Upload the entire file:
* fastboot oem filedump /dev/bootloader
* fastboot get_staged bl_all.bin
*
* b. Upload 4096 bytes of /dev/mem from offset 2048:
* fastboot oem filedump /dev/mem 2048 4096
* fastboot get_staged bl_2048_6144.bin
*
* c. Get 2048 bytes from offset -1044480
* fastboot oem "filedump /dev/bootloader -1044480 2048"
* fastboot get_staged bl_l1044480_l1042432.txt
*/
static void fastboot_filedump(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
struct stat sb;
int ret;
if (!arg)
{
fastboot_fail(ctx, "Invalid argument");
return;
}
ret = sscanf(arg, "%s %" PRIdOFF " %zu", ctx->upload_param.u.file.path,
&ctx->upload_param.u.file.offset, &ctx->upload_param.size);
if (ret != 1 && ret != 3)
{
fastboot_fail(ctx, "Failed to parse arguments");
return;
}
else if (ret == 1)
{
ret = stat(ctx->upload_param.u.file.path, &sb);
if (ret < 0)
{
fastboot_fail(ctx, "No such file or directory");
return;
}
ctx->upload_param.size = sb.st_size;
ctx->upload_param.u.file.offset = 0;
}
fb_info("Filedump Path: %s, Offset: %" PRIdOFF ", Size: %zu\n",
ctx->upload_param.u.file.path, ctx->upload_param.u.file.offset,
ctx->upload_param.size);
ctx->upload_func = fastboot_filedump_upload;
fastboot_okay(ctx, "");
}
#ifdef CONFIG_SYSTEM_FASTBOOTD_SHELL
static void fastboot_shell(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
char response[FASTBOOT_MSG_LEN - 4];
FILE *fp;
int ret;
fp = popen(arg, "r");
if (fp == NULL)
{
fastboot_fail(ctx, "popen() fails %d", errno);
return;
}
while (fgets(response, sizeof(response), fp))
{
fastboot_ack(ctx, "TEXT", response);
}
ret = pclose(fp);
if (WIFEXITED(ret) && WEXITSTATUS(ret) == 0)
{
fastboot_okay(ctx, "");
return;
}
fastboot_fail(ctx, "error detected 0x%x %d", ret, errno);
}
#endif
static void fastboot_upload(FAR struct fastboot_ctx_s *ctx,
FAR const char *arg)
{
char response[FASTBOOT_MSG_LEN];
int ret;
if (!ctx->upload_param.size || !ctx->upload_func)
{
fastboot_fail(ctx, "No data staged by the last command");
return;
}
snprintf(response, FASTBOOT_MSG_LEN, "DATA%08zx", ctx->upload_param.size);
ret = ctx->ops->write(ctx, response, strlen(response));
if (ret < 0)
{
fb_err("Response error [%d]\n", -ret);
goto done;
}
ret = ctx->upload_func(ctx);
if (ret < 0)
{
fb_err("Upload failed, [%d]\n", -ret);
fastboot_fail(ctx, "Upload failed");
}
else
{
fastboot_okay(ctx, "");
}
done:
ctx->upload_param.size = 0;
ctx->upload_func = NULL;
}
static void fastboot_oem(FAR struct fastboot_ctx_s *ctx, FAR const char *arg)
{
size_t ncmds = nitems(g_oem_cmd);
size_t index;
arg++;
for (index = 0; index < ncmds; index++)
{
size_t len = strlen(g_oem_cmd[index].prefix);
if (memcmp(arg, g_oem_cmd[index].prefix, len) == 0)
{
arg += len;
g_oem_cmd[index].handle(ctx, *arg == ' ' ? ++arg : NULL);
break;
}
}
if (index == ncmds)
{
fastboot_fail(ctx, "Unknown command");
}
}
static void fastboot_command_loop(FAR struct fastboot_ctx_s *ctx,
size_t nctx)
{
FAR struct fastboot_ctx_s *c;
struct epoll_event ev[nctx];
int epfd;
int n;
epfd = epoll_create(1);
if (epfd < 0)
{
fb_err("open epoll failed %d", errno);
return;
}
for (c = ctx, n = nctx; n-- > 0; c++)
{
ev[n].events = EPOLLIN;
ev[n].data.ptr = c;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, c->tran_fd[0], &ev[n]) < 0)
{
fb_err("err add poll %d", c->tran_fd[0]);
goto epoll_close;
}
}
if (ctx->left > 0)
{
if (epoll_wait(epfd, ev, nitems(ev), ctx->left) <= 0)
{
goto epoll_close;
}
}
/* Reinitialize for storing TCP transport remaining data size. */
for (c = ctx, n = nctx; n-- > 0; c++)
{
c->left = 0;
}
while (1)
{
char buffer[FASTBOOT_MSG_LEN + 1];
size_t ncmds = nitems(g_fast_cmd);
size_t index;
n = epoll_wait(epfd, ev, nitems(ev), -1);
for (n--; n >= 0; )
{
c = (FAR struct fastboot_ctx_s *)ev[n].data.ptr;
ssize_t r = c->ops->read(c, buffer, FASTBOOT_MSG_LEN);
if (r <= 0)
{
n--;
continue;
}
buffer[r] = '\0';
for (index = 0; index < ncmds; index++)
{
size_t len = strlen(g_fast_cmd[index].prefix);
if (memcmp(buffer, g_fast_cmd[index].prefix, len) == 0)
{
g_fast_cmd[index].handle(c, buffer + len);
break;
}
}
if (index == ncmds)
{
fastboot_fail(c, "Unknown command");
}
}
}
epoll_close:
while (--c >= ctx)
{
epoll_ctl(epfd, EPOLL_CTL_DEL, c->tran_fd[0], NULL);
}
close(epfd);
}
static void fastboot_publish(FAR struct fastboot_ctx_s *ctx,
FAR const char *name,
FAR const char *string,
int data)
{
FAR struct fastboot_var_s *var;
var = malloc(sizeof(*var));
if (var == NULL)
{
fb_err("ERROR: Could not allocate the memory.\n");
return;
}
var->name = name;
var->string = string;
var->data = data;
var->next = ctx->varlist;
ctx->varlist = var;
}
static void fastboot_create_publish(FAR struct fastboot_ctx_s *ctx,
size_t nctx)
{
for (; nctx-- > 0; ctx++)
{
fastboot_publish(ctx, "product", "NuttX", 0);
fastboot_publish(ctx, "kernel", "NuttX", 0);
fastboot_publish(ctx, "version", CONFIG_VERSION_STRING, 0);
fastboot_publish(ctx, "slot-count", "1", 0);
fastboot_publish(ctx, "max-download-size", NULL,
CONFIG_SYSTEM_FASTBOOTD_DOWNLOAD_MAX);
}
}
static void fastboot_free_publish(FAR struct fastboot_ctx_s *ctx,
size_t nctx)
{
FAR struct fastboot_var_s *next;
for (; nctx-- > 0; ctx++)
{
while (ctx->varlist)
{
next = ctx->varlist->next;
free(ctx->varlist);
ctx->varlist = next;
}
}
}
#ifdef CONFIG_USBFASTBOOT
static int fastboot_open_usb(int index, int flags)
{
int try = FASTBOOT_EP_RETRY_TIMES;
char usbdev[32];
int ret;
snprintf(usbdev, sizeof(usbdev),
"%s/ep%d", FASTBOOT_USBDEV, index);
do
{
ret = open(usbdev, flags);
if (ret >= 0)
{
return ret;
}
usleep(FASTBOOT_EP_RETRY_DELAY_MS * 1000);
}
while (try--);
fb_err("open [%s] error %d\n", usbdev, errno);
return -errno;
}
static int fastboot_usbdev_initialize(FAR struct fastboot_ctx_s *ctx)
{
#ifdef CONFIG_SYSTEM_FASTBOOTD_USB_BOARDCTL
struct boardioc_usbdev_ctrl_s ctrl;
# ifdef CONFIG_USBDEV_COMPOSITE
uint8_t dev = BOARDIOC_USBDEV_COMPOSITE;
# else
uint8_t dev = BOARDIOC_USBDEV_FASTBOOT;
# endif
int ret;
ctrl.usbdev = dev;
ctrl.action = BOARDIOC_USBDEV_INITIALIZE;
ctrl.instance = 0;
ctrl.config = 0;
ctrl.handle = NULL;
ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl);
if (ret < 0)
{
fb_err("boardctl(BOARDIOC_USBDEV_INITIALIZE) failed: %d\n", ret);
return ret;
}
ctrl.usbdev = dev;
ctrl.action = BOARDIOC_USBDEV_CONNECT;
ctrl.instance = 0;
ctrl.config = 0;
ctrl.handle = &ctx->handle;
ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl);
if (ret < 0)
{
fb_err("boardctl(BOARDIOC_USBDEV_CONNECT) failed: %d\n", ret);
return ret;
}
#endif /* SYSTEM_FASTBOOTD_USB_BOARDCTL */
ctx->tran_fd[0] = fastboot_open_usb(FASTBOOT_EP_BULKOUT_IDX,
O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (ctx->tran_fd[0] < 0)
{
return ctx->tran_fd[0];
}
ctx->tran_fd[1] = fastboot_open_usb(FASTBOOT_EP_BULKIN_IDX,
O_WRONLY | O_CLOEXEC | O_NONBLOCK);
if (ctx->tran_fd[1] < 0)
{
close(ctx->tran_fd[0]);
ctx->tran_fd[0] = -1;
return ctx->tran_fd[1];
}
return 0;
}
static void fastboot_usbdev_deinit(FAR struct fastboot_ctx_s *ctx)
{
#ifdef CONFIG_SYSTEM_FASTBOOTD_USB_BOARDCTL
struct boardioc_usbdev_ctrl_s ctrl;
#endif
int i;
for (i = 0; i < nitems(ctx->tran_fd); i++)
{
close(ctx->tran_fd[i]);
ctx->tran_fd[i] = -1;
}
#ifdef CONFIG_SYSTEM_FASTBOOTD_USB_BOARDCTL
if (ctx->handle)
{
# ifdef CONFIG_USBDEV_COMPOSITE
ctrl.usbdev = BOARDIOC_USBDEV_COMPOSITE;
# else
ctrl.usbdev = BOARDIOC_USBDEV_FASTBOOT;
# endif
ctrl.action = BOARDIOC_USBDEV_DISCONNECT;
ctrl.instance = 0;
ctrl.config = 0;
ctrl.handle = &ctx->handle;
i = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl);
if (i < 0)
{
fb_err("boardctl(BOARDIOC_USBDEV_DISCONNECT) failed: %d\n", i);
}
}
#endif /* SYSTEM_FASTBOOTD_USB_BOARDCTL */
}
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);
}
#endif
#ifdef 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,
SOCK_CLOEXEC | SOCK_NONBLOCK);
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,
size_t nctx)
{
int ret;
for (; nctx-- > 0; ctx++)
{
ctx->download_max = CONFIG_SYSTEM_FASTBOOTD_DOWNLOAD_MAX;
ctx->download_offset = 0;
ctx->download_size = 0;
ctx->flash_fd = -1;
ctx->total_imgsize = 0;
ctx->varlist = NULL;
ctx->left = ctx[0].left;
ctx->ops = &g_tran_ops[nctx];
ctx->tran_fd[0] = -1;
ctx->tran_fd[1] = -1;
ctx->download_buffer = malloc(CONFIG_SYSTEM_FASTBOOTD_DOWNLOAD_MAX);
if (ctx->download_buffer == NULL)
{
fb_err("ERROR: Could not allocate the memory.\n");
continue;
}
ret = ctx->ops->init(ctx);
if (ret < 0)
{
free(ctx->download_buffer);
ctx->download_buffer = NULL;
ctx->ops->deinit(ctx);
}
}
return 0;
}
static void fastboot_context_deinit(FAR struct fastboot_ctx_s *ctx,
size_t nctx)
{
for (; nctx-- > 0; ctx++)
{
if (ctx->download_buffer)
{
ctx->ops->deinit(ctx);
free(ctx->download_buffer);
}
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
int main(int argc, FAR char **argv)
{
struct fastboot_ctx_s context[nitems(g_tran_ops)];
int ret;
if (argc > 1)
{
if (strcmp(argv[1], "-h") == 0)
{
fb_err("Usage: fastbootd [wait_ms]\n");
fb_err("\nmemdump: \n");
fastboot_memdump_region(fastboot_memdump_syslog, NULL);
return 0;
}
if (sscanf(argv[1], "%" SCNu64 , &context[0].left) != 1)
{
return -EINVAL;
}
}
ret = fastboot_context_initialize(context, nitems(context));
if (ret < 0)
{
return ret;
}
fastboot_create_publish(context, nitems(context));
fastboot_command_loop(context, nitems(context));
fastboot_free_publish(context, nitems(context));
fastboot_context_deinit(context, nitems(context));
return ret;
}