mirror of
https://github.com/apache/nuttx-apps.git
synced 2025-07-04 11:04:13 +08:00

Fix typos of sparse header comments and download/upload response error log. Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
1512 lines
38 KiB
C
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;
|
|
}
|