system/fastboot: Add serial (UART) transport backend.

Add a serial transport alongside the existing USB and TCP backends.
The serial backend reuses the TCP v1 wire framing (FB01 handshake
plus an 8-byte big-endian length prefix) so the host side needs no
new tool: socat bridges the UART to a TCP socket and the standard
fastboot tool connects via tcp:.

The serial port path is configured at build time through Kconfig
SYSTEM_FASTBOOTD_SERIAL_PORT.

Signed-off-by: fangpeina <fangpeina@xiaomi.com>
This commit is contained in:
fangpeina 2026-08-21 17:11:50 +08:00 • committed by GUIDINGLI
parent 43dcb51087
commit 3b993dfbb6
2 changed files with 161 additions and 62 deletions

View file

@ -6,12 +6,13 @@
menuconfig SYSTEM_FASTBOOTD
bool "fastbootd"
default n
depends on USBFASTBOOT || (NET_TCP && NET_TCPBACKLOG)
depends on USBFASTBOOT || (NET_TCP && NET_TCPBACKLOG) || SERIAL
---help---
Fastboot daemon supporting multiple transports concurrently.
Enable at least one transport (USB, TCP).
Enable at least one transport (USB, TCP, Serial).
The USB transport depends on USBFASTBOOT.
The TCP network transport depends on NET_TCP and NET_TCPBACKLOG.
The Serial transport depends on SERIAL.
Each transport can be individually disabled via
SYSTEM_FASTBOOTD_USB / SYSTEM_FASTBOOTD_TCP below.
@ -71,4 +72,18 @@ config SYSTEM_FASTBOOTD_NET_INIT
---help---
This option enables/disables all network initialization in fastboot server.
config SYSTEM_FASTBOOTD_SERIAL
bool "Fastboot serial transport"
default n
depends on SERIAL
---help---
Enable serial (UART) transport for fastboot daemon.
Reuses TCP v1 wire framing (FB01 handshake + 8-byte length prefix).
The host side can use socat to bridge the UART to a TCP socket.
config SYSTEM_FASTBOOTD_SERIAL_PORT
string "Serial device path"
default "/dev/ttyS1"
depends on SYSTEM_FASTBOOTD_SERIAL
endif # SYSTEM_FASTBOOTD

View file

@ -39,6 +39,7 @@
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <termios.h>
#include <unistd.h>
#include <netinet/in.h>
@ -155,10 +156,10 @@ struct fastboot_ctx_s
{
/* Transport file descriptors
*
* | idx | USB | TCP | poll |
* |-----|----------|---------------|------|
* | 0 |usbdev in |TCP socket | Y |
* | 1 |usbdev out|accepted socket| N |
* | idx | USB | TCP | Serial | poll |
* |-----|----------|---------------|----------|------|
* | 0 |usbdev in |TCP socket |serial fd | Y |
* | 1 |usbdev out|accepted socket|serial fd | N |
*/
int tran_fd[2];
@ -253,6 +254,17 @@ static int fastboot_tcp_write(FAR struct fastboot_ctx_s *ctx,
FAR const void *buf, size_t len);
#endif
/* Serial transport */
#ifdef CONFIG_SYSTEM_FASTBOOTD_SERIAL
static int fastboot_serial_initialize(FAR struct fastboot_ctx_s *ctx);
static void fastboot_serial_deinit(FAR struct fastboot_ctx_s *ctx);
static ssize_t fastboot_serial_read(FAR struct fastboot_ctx_s *ctx,
FAR void *buf, size_t len);
static int fastboot_serial_write(FAR struct fastboot_ctx_s *ctx,
FAR const void *buf, size_t len);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
@ -306,6 +318,14 @@ static const struct fastboot_transport_ops_s g_tran_ops[] =
.write = fastboot_tcp_write,
},
#endif
#ifdef CONFIG_SYSTEM_FASTBOOTD_SERIAL
{
.init = fastboot_serial_initialize,
.deinit = fastboot_serial_deinit,
.read = fastboot_serial_read,
.write = fastboot_serial_write,
},
#endif
};
/****************************************************************************
@ -1301,62 +1321,7 @@ static int fastboot_usbdev_write(FAR struct fastboot_ctx_s *ctx,
}
#endif
#ifdef CONFIG_SYSTEM_FASTBOOTD_TCP
static int fastboot_tcp_initialize(FAR struct fastboot_ctx_s *ctx)
{
struct sockaddr_in addr;
#ifdef CONFIG_SYSTEM_FASTBOOTD_NET_INIT
/* Bring up the network */
netinit_bringup();
#endif
ctx->tran_fd[0] = socket(AF_INET,
SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
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;
}
#if defined(CONFIG_SYSTEM_FASTBOOTD_TCP) || defined(CONFIG_SYSTEM_FASTBOOTD_SERIAL)
static ssize_t fastboot_read_all(int fd, FAR void *buf, size_t len)
{
size_t total = 0;
@ -1449,6 +1414,63 @@ static ssize_t fastboot_framed_read(FAR struct fastboot_ctx_s *ctx,
ctx->left -= nread;
return nread;
}
#endif
#ifdef CONFIG_SYSTEM_FASTBOOTD_TCP
static int fastboot_tcp_initialize(FAR struct fastboot_ctx_s *ctx)
{
struct sockaddr_in addr;
#ifdef CONFIG_SYSTEM_FASTBOOTD_NET_INIT
/* Bring up the network */
netinit_bringup();
#endif
ctx->tran_fd[0] = socket(AF_INET,
SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
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_tcp_read(FAR struct fastboot_ctx_s *ctx,
FAR void *buf, size_t len)
@ -1491,6 +1513,68 @@ static int fastboot_tcp_write(FAR struct fastboot_ctx_s *ctx,
}
#endif
#ifdef CONFIG_SYSTEM_FASTBOOTD_SERIAL
static int fastboot_serial_initialize(FAR struct fastboot_ctx_s *ctx)
{
int fd;
fd = open(CONFIG_SYSTEM_FASTBOOTD_SERIAL_PORT,
O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
{
fb_err("serial: open %s failed %d\n",
CONFIG_SYSTEM_FASTBOOTD_SERIAL_PORT, errno);
return -errno;
}
#ifdef CONFIG_SERIAL_TERMIOS
struct termios tio;
if (tcgetattr(fd, &tio) == 0)
{
cfmakeraw(&tio);
tcsetattr(fd, TCSANOW, &tio);
}
#endif
ctx->tran_fd[0] = fd;
ctx->tran_fd[1] = fd;
fb_info("serial: opened %s\n", CONFIG_SYSTEM_FASTBOOTD_SERIAL_PORT);
return 0;
}
static void fastboot_serial_deinit(FAR struct fastboot_ctx_s *ctx)
{
if (ctx->tran_fd[0] >= 0)
{
close(ctx->tran_fd[0]);
ctx->tran_fd[0] = -1;
ctx->tran_fd[1] = -1;
}
}
static ssize_t fastboot_serial_read(FAR struct fastboot_ctx_s *ctx,
FAR void *buf, size_t len)
{
return fastboot_framed_read(ctx, ctx->tran_fd[0], buf, len, true);
}
static int fastboot_serial_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[0], &data_size, sizeof(data_size));
if (ret < 0)
{
return ret;
}
return fastboot_write(ctx->tran_fd[0], buf, len);
}
#endif
static int fastboot_context_initialize(FAR struct fastboot_ctx_s *ctx,
size_t nctx)
{