From 97cae6f9c551e7b70f0df191a5fc0cc7086e0bb3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 12 Oct 2026 11:00:39 +0200 Subject: [PATCH 1/4] tty: don't bail-out on error in tty_open() We may want to ignore failures to open the tty, for instance: when powering off all devices in a system that has fewer USB ports than are needed to register all the consoles. Signed-off-by: Bartosz Golaszewski --- drivers/alpaca.c | 6 ++++-- drivers/pic32cx.c | 6 ++++-- drivers/qcomlt_dbg.c | 6 ++++-- tty.c | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/alpaca.c b/drivers/alpaca.c index f720670..37628fb 100644 --- a/drivers/alpaca.c +++ b/drivers/alpaca.c @@ -38,8 +38,10 @@ static void *alpaca_open(struct device *dev) alpaca = calloc(1, sizeof(*alpaca)); alpaca->alpaca_fd = tty_open(dev->control_dev, &alpaca->alpaca_tios); - if (alpaca->alpaca_fd < 0) - err(1, "failed to open %s", dev->control_dev); + if (alpaca->alpaca_fd < 0) { + free(alpaca); + return NULL; + } alpaca_device_power(alpaca, 0); diff --git a/drivers/pic32cx.c b/drivers/pic32cx.c index 0049fe1..ea032b4 100644 --- a/drivers/pic32cx.c +++ b/drivers/pic32cx.c @@ -49,8 +49,10 @@ static void *pic32cx_open(struct device *dev) pic32cx = calloc(1, sizeof(*pic32cx)); pic32cx->fd = tty_open(dev->control_dev, &pic32cx->tios); - if (pic32cx->fd < 0) - err(1, "failed to open %s", dev->control_dev); + if (pic32cx->fd < 0) { + free(pic32cx); + return NULL; + } pic32cx_device_power(pic32cx, 1); diff --git a/drivers/qcomlt_dbg.c b/drivers/qcomlt_dbg.c index fe55cf4..d058955 100644 --- a/drivers/qcomlt_dbg.c +++ b/drivers/qcomlt_dbg.c @@ -52,8 +52,10 @@ static void *qcomlt_dbg_open(struct device *dev) dbg = calloc(1, sizeof(*dbg)); dbg->fd = tty_open(dev->control_dev, &dbg->orig_tios); - if (dbg->fd < 0) - err(1, "failed to open %s", dev->control_dev); + if (dbg->fd < 0) { + free(dbg); + return NULL; + } // fprintf(stderr, "qcomlt_dbg_open()\n"); write(dbg->fd, "brpu", 4); diff --git a/tty.c b/tty.c index 4c75cfe..e4f49a1 100644 --- a/tty.c +++ b/tty.c @@ -20,8 +20,10 @@ int tty_open(const char *tty, struct termios *old) int fd; fd = open(tty, O_RDWR | O_NOCTTY | O_EXCL); - if (fd < 0) - err(1, "unable to open \"%s\"", tty); + if (fd < 0) { + warn("unable to open \"%s\"", tty); + return -1; + } ret = tcgetattr(fd, old); if (ret < 0) From 60ecda03c2b83641b0899f80e229feafba02e8a2 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 12 Oct 2026 11:01:38 +0200 Subject: [PATCH 2/4] drivers: ftdi-gpio: don't bail-out on failure in ftdi_gpio_open() We may want to ignore the failure to open the ftdi port, for instance in situations where the system has fewer USB ports than are needed to register all the consoles with multiple devices and all we want is to just power them all off. Signed-off-by: Bartosz Golaszewski --- drivers/ftdi-gpio.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/ftdi-gpio.c b/drivers/ftdi-gpio.c index 070b3d3..52c7442 100644 --- a/drivers/ftdi-gpio.c +++ b/drivers/ftdi-gpio.c @@ -308,9 +308,18 @@ static void *ftdi_gpio_open(struct device *dev) ret = ftdi_usb_open_string(ftdi_gpio->interface[ftdi_interface], ftdi_gpio->options->ftdi.description); - if (ret < 0) - errx(1, "failed to open ftdi gpio device '%s' (%d)", - ftdi_gpio->options->ftdi.description, ret); + if (ret < 0) { + warnx("failed to open ftdi gpio device '%s' (%d)", + ftdi_gpio->options->ftdi.description, ret); + for (i = 0; i < FTDI_INTERFACE_COUNT; ++i) { + if (!ftdi_gpio->interface[i]) + continue; + ftdi_usb_close(ftdi_gpio->interface[i]); + ftdi_free(ftdi_gpio->interface[i]); + } + free(ftdi_gpio); + return NULL; + } ftdi_set_bitmode(ftdi_gpio->interface[ftdi_interface], 0xFF, BITMODE_BITBANG); From 9a84f615e1a3bad48b181cf9f4fff5e0ecf6a8be Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 12 Oct 2026 11:02:16 +0200 Subject: [PATCH 3/4] device: provide device_open_power_only() Provide a variant of device_open() that doesn't bail-out on failure to open the console. This will be used by an upcoming new command of cdba-power that allows to power-off all configured boards. Signed-off-by: Bartosz Golaszewski --- device.c | 30 +++++++++++++++++++++++++++--- device.h | 2 ++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/device.c b/device.c index 8b76524..3cefcaa 100644 --- a/device.c +++ b/device.c @@ -103,8 +103,8 @@ static bool device_check_access(struct device *device, return false; } -struct device *device_open(const char *board, - const char *username) +static struct device *__device_open(const char *board, const char *username, + bool power_only) { struct device *device; @@ -133,10 +133,20 @@ struct device *device_open(const char *board, if (device_has_control(device, open)) { device->cdb = device_control(device, open); - if (!device->cdb) + if (!device->cdb) { + if (power_only) { + warnx("failed to open device controller for %s", + board); + return NULL; + } errx(1, "failed to open device controller"); + } } + /* Switching power and USB off needs neither console nor fastboot. */ + if (power_only) + return device; + device->console = device_console(device, open); if (!device->console) errx(1, "failed to open device console"); @@ -159,6 +169,20 @@ struct device *device_open(const char *board, return device; } +struct device *device_open(const char *board, const char *username) +{ + return __device_open(board, username, false); +} + +/* + * Open only the board's controller (no console), enough to switch power and + * USB off. Unlike device_open() this doesn't exit if the console is missing. + */ +struct device *device_open_power_only(const char *board, const char *username) +{ + return __device_open(board, username, true); +} + static void device_impl_power(struct device *device, bool on) { device_control(device, power, on); diff --git a/device.h b/device.h index 664f942..56b39da 100644 --- a/device.h +++ b/device.h @@ -97,6 +97,8 @@ void device_add(struct device *device); struct device *device_open(const char *board, const char *username); +struct device *device_open_power_only(const char *board, + const char *username); void device_close(struct device *dev); int device_power_on(struct device *device, enum power_on_mode mode); int device_power_off(struct device *device); From 9f0ea653e616e6c5fd2cd161db60a0f1d9a12836 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 28 Sep 2026 09:45:58 +0200 Subject: [PATCH 4/4] cdba-power: implement all-off Extend cdba-power with a command allowing to disable all configured boards at once. This is useful on boot of the cdba host where all boards get powered up. One can set up a one-shot systemd service that disables all the boards. Signed-off-by: Bartosz Golaszewski --- cdba-power.c | 45 +++++++++++++++++++++++++++++++++++++-------- device.c | 11 +++++++++++ device.h | 1 + 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/cdba-power.c b/cdba-power.c index 8d3f786..d8902d2 100644 --- a/cdba-power.c +++ b/cdba-power.c @@ -23,6 +23,7 @@ void cdba_send_buf(int type, size_t len, const void *buf) static void usage(const char *name) { fprintf(stderr, "Usage: %s on|off\n", name); + fprintf(stderr, "Usage: %s all-off\n", name); exit(EXIT_FAILURE); } @@ -33,22 +34,47 @@ bool ready(void) return device_is_running(selected_device); } +static int all_off(void) +{ + struct device *next = NULL, *dev; + int ret = 0; + + while ((next = device_next(next))) { + dev = device_open_power_only(next->board, "nobody"); + if (!dev) { + fprintf(stderr, "failed to open %s\n", next->board); + ret = EXIT_FAILURE; + continue; + } + + device_usb(dev, false); + device_power_off(dev); + device_close(dev); + } + + return ret; +} + int main(int argc, char **argv) { const char *home; const char *name; + bool all = false; bool on; int ret; - if (argc != 3) - usage(argv[0]); - - if (!strcmp(argv[2], "on")) - on = true; - else if (!strcmp(argv[2], "off")) - on = false; - else + if (argc == 2 && !strcmp(argv[1], "all-off")) { + all = true; + } else if (argc == 3) { + if (!strcmp(argv[2], "on")) + on = true; + else if (!strcmp(argv[2], "off")) + on = false; + else + usage(argv[0]); + } else { usage(argv[0]); + } home = getenv("HOME"); if (home) @@ -63,6 +89,9 @@ int main(int argc, char **argv) } } + if (all) + return all_off(); + name = argv[1]; selected_device = device_open(name, "nobody"); if (!selected_device) { diff --git a/device.c b/device.c index 3cefcaa..3380507 100644 --- a/device.c +++ b/device.c @@ -49,6 +49,17 @@ void device_add(struct device *device) list_append(&devices, &device->node); } +struct device *device_next(struct device *device) +{ + struct list_head *item; + + item = device ? device->node.next : devices.next; + if (item == &devices) + return NULL; + + return list_entry(item, struct device, node); +} + static void device_lock(struct device *device) { char lock[PATH_MAX]; diff --git a/device.h b/device.h index 56b39da..54a6aaa 100644 --- a/device.h +++ b/device.h @@ -94,6 +94,7 @@ struct device_qdl_user { }; void device_add(struct device *device); +struct device *device_next(struct device *device); struct device *device_open(const char *board, const char *username);