diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3ab0839 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,243 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2021 Canonical Ltd. +# Copyright (c) 2023 Linaro Ltd +# Author: Krzysztof Kozlowski +# +# +# Loosely based on: https://github.com/linux-test-project/ltp +# https://github.com/linux-nfc/neard +# +name: "Builds" +on: + pull_request: + push: + schedule: + # Run at 1:01 PM, every Tuesday + - cron: '1 13 * * 2' + workflow_dispatch: + +jobs: + job: + name: Build + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + arch: [x86-64] + family: [x86-64] + compiler: [gcc, clang] + container: + - archlinux:latest + - debian:testing + - debian:stable + - debian:bookworm + - debian:bullseye + - debian:buster + # Fails on configure on GCC and clang (process restrictions?) + # - fedora:rawhide + - fedora:latest + # Uses ligpiod 2.x + # - fedora:39 + - fedora:38 + - fedora:37 + - ubuntu:lunar # EOL 01.2024 + - ubuntu:jammy + - ubuntu:focal + - ubuntu:bionic + # Meson version on Ubuntu Xenial is really too old + #- ubuntu:xenial + cross_compile: [""] + variant: [""] + include: + # Debian 32-bit builds + - container: "debian:testing" + arch: i386 + family: x86 + compiler: gcc + cross_compile: i686-linux-gnu + pkg_config_path: /usr/lib/i386-linux-gnu/pkgconfig + variant: i386 + + - container: "debian:stable" + arch: i386 + family: x86 + compiler: gcc + cross_compile: i686-linux-gnu + pkg_config_path: /usr/lib/i386-linux-gnu/pkgconfig + variant: i386 + + - container: "debian:bookworm" + arch: i386 + family: x86 + compiler: gcc + cross_compile: i686-linux-gnu + pkg_config_path: /usr/lib/i386-linux-gnu/pkgconfig + variant: i386 + + - container: "debian:buster" + arch: i386 + family: x86 + compiler: gcc -m32 + cross_compile: i686-linux-gnu + pkg_config_path: /usr/lib/i386-linux-gnu/pkgconfig + variant: i386 + + # Debian cross compilation builds + - container: "debian:testing" + arch: armhf + family: arm + compiler: arm-linux-gnueabihf-gcc + cross_compile: arm-linux-gnueabihf + pkg_config_path: /usr/lib/arm-linux-gnueabihf/pkgconfig + variant: cross-compile + + - container: "debian:testing" + arch: arm64 + family: aarch64 + compiler: aarch64-linux-gnu-gcc + cross_compile: aarch64-linux-gnu + pkg_config_path: /usr/lib/aarch64-linux-gnu/pkgconfig + variant: cross-compile + + - container: "debian:testing" + arch: ppc64el + family: ppc64 + compiler: powerpc64le-linux-gnu-gcc + cross_compile: powerpc64le-linux-gnu + pkg_config_path: /usr/lib/powerpc64le-linux-gnu/pkgconfig + variant: cross-compile + + - container: "debian:testing" + arch: s390x + family: s390x + compiler: s390x-linux-gnu-gcc + cross_compile: s390x-linux-gnu + pkg_config_path: /usr/lib/s390x-linux-gnu/pkgconfig + variant: cross-compile + + - container: "debian:stable" + arch: armhf + family: arm + compiler: arm-linux-gnueabihf-gcc + cross_compile: arm-linux-gnueabihf + pkg_config_path: /usr/lib/arm-linux-gnueabihf/pkgconfig + variant: cross-compile + + - container: "debian:stable" + arch: arm64 + family: aarch64 + compiler: aarch64-linux-gnu-gcc + cross_compile: aarch64-linux-gnu + pkg_config_path: /usr/lib/aarch64-linux-gnu/pkgconfig + variant: cross-compile + + - container: "debian:stable" + arch: ppc64el + family: ppc64 + compiler: powerpc64le-linux-gnu-gcc + cross_compile: powerpc64le-linux-gnu + pkg_config_path: /usr/lib/powerpc64le-linux-gnu/pkgconfig + variant: cross-compile + + - container: "debian:stable" + arch: s390x + family: s390x + compiler: s390x-linux-gnu-gcc + cross_compile: s390x-linux-gnu + pkg_config_path: /usr/lib/s390x-linux-gnu/pkgconfig + variant: cross-compile + + container: + image: ${{ matrix.container }} + env: + ARCH: ${{ matrix.arch }} + FAMILY: ${{ matrix.family }} + CC: ${{ matrix.compiler }} + CROSS_COMPILE: ${{ matrix.cross_compile }} + PKG_CONFIG_PATH: ${{ matrix.pkg_config_path }} + VARIANT: ${{ matrix.variant }} + + steps: + - name: Show OS + run: cat /etc/os-release + + - name: Show env (matrix settings) + run: | + echo "ARCH: $ARCH" + echo "FAMILY: $FAMILY" + echo "CC: $CC" + echo "CROSS_COMPILE: $CROSS_COMPILE" + echo "VARIANT: $VARIANT" + echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH" + + - name: Git checkout + uses: actions/checkout@v3 + + - name: Install additional packages + run: | + INSTALL=${{ matrix.container }} + INSTALL="${INSTALL%%:*}" + INSTALL="${INSTALL%%/*}" + ./ci/$INSTALL.sh + if [ "$VARIANT" ]; then ./ci/$INSTALL.$VARIANT.sh; fi + + - name: Compiler version + run: $CC --version + + - name: Display environment and Linux version + run: | + test -f /etc/issue && cat /etc/issue + echo "############################################" + lsb_release -a || true + echo "############################################" + cat /usr/include/linux/version.h + echo "############################################" + uname -a + echo "############################################" + cat /proc/cmdline + echo "############################################" + printenv + + # i386 build on x86_64 only requires passing -m32 to CFLAGS & LDFLAGS + - name: Meson init for i386 + if: ${{ matrix.variant == 'i386' }} + run: | + mkdir build + CFLAGS="-m32" LDFLAGS="-m32" meson setup . build + + - name: Meson init with cross compile + if: ${{ matrix.variant == 'cross-compile' }} + run: | + # Generate cross compile file (see https://mesonbuild.com/Cross-compilation.html#cross-compilation) + echo "[binaries]" > cross.txt + echo "c = '${CROSS_COMPILE}-gcc'" >> cross.txt + echo "strip = '${CROSS_COMPILE}-strip'" >> cross.txt + # Forcing pkgconfig binary to 'pkg-config' is required for cross build to work + echo "pkgconfig = 'pkg-config'" >> cross.txt + echo "[host_machine]" >> cross.txt + echo "system = 'linux'" >> cross.txt + echo "cpu_family = '${FAMILY}'" >> cross.txt + echo "cpu = '${ARCH}'" >> cross.txt + echo "endian = 'little'" >> cross.txt + echo "[properties]" >> cross.txt + echo "pkg_config_libdir = '${PKG_CONFIG_PATH}'" >> cross.txt + cat cross.txt + mkdir build + meson setup --cross-file cross.txt . build + + - name: Meson init + if: ${{ matrix.variant == '' }} + run: | + mkdir build + meson setup . build + + - name: Compile + run: ninja -C build + + - name: Install + run: ninja -C build install diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..92f7cd0 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2021 Canonical Ltd. +# Copyright (c) 2023 Linaro Ltd +# Author: Krzysztof Kozlowski +# +# +name: "CodeQL" +on: [push, pull_request, workflow_dispatch] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + + - name: Install additional packages + run: sudo ./ci/ubuntu.sh + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.gitignore b/.gitignore index 2d89b51..bd1acab 100644 --- a/.gitignore +++ b/.gitignore @@ -52,5 +52,5 @@ Mkfile.old dkms.conf # Binaries generated by the program -cdba-server -cdba +abcd-server +abcd diff --git a/99-gpiod.rules b/99-gpiod.rules new file mode 100644 index 0000000..04500b1 --- /dev/null +++ b/99-gpiod.rules @@ -0,0 +1 @@ +SUBSYSTEM=="gpio", KERNEL=="gpiochip0", GROUP="plugdev", MODE="0660" diff --git a/99-libftdi.rules b/99-libftdi.rules new file mode 100644 index 0000000..adb2f2e --- /dev/null +++ b/99-libftdi.rules @@ -0,0 +1 @@ +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", GROUP="dialout", MODE="0660" diff --git a/99-pyamlboot.rules b/99-pyamlboot.rules new file mode 100644 index 0000000..eb2bb0a --- /dev/null +++ b/99-pyamlboot.rules @@ -0,0 +1 @@ +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b8e", ATTRS{idProduct}=="c003", GROUP="dialout", MODE="0660" diff --git a/Makefile b/Makefile deleted file mode 100644 index 19800a2..0000000 --- a/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -CLIENT := cdba -SERVER := cdba-server - -.PHONY: all - -all: $(CLIENT) $(SERVER) - -CFLAGS := -Wall -g -O2 -LDFLAGS := -ludev -lyaml - -CLIENT_SRCS := cdba.c circ_buf.c -CLIENT_OBJS := $(CLIENT_SRCS:.c=.o) - -SERVER_SRCS := cdba-server.c cdb_assist.c circ_buf.c conmux.c device.c device_parser.c fastboot.c alpaca.c console.c qcomlt_dbg.c -SERVER_OBJS := $(SERVER_SRCS:.c=.o) - -$(CLIENT): $(CLIENT_OBJS) - $(CC) $(LDFLAGS) -o $@ $^ - -$(SERVER): $(SERVER_OBJS) - $(CC) -o $@ $^ $(LDFLAGS) - -clean: - rm -f $(CLIENT) $(CLIENT_OBJS) $(SERVER) $(SERVER_OBJS) - -install: $(CLIENT) $(SERVER) - install -D -m 755 $(CLIENT) $(DESTDIR)$(prefix)/bin/$(CLIENT) - install -D -m 755 $(SERVER) $(DESTDIR)$(prefix)/bin/$(SERVER) diff --git a/README b/README index 2be1339..5a810d9 100644 --- a/README +++ b/README @@ -1,19 +1,26 @@ -= CDBA control tool -The CDBA control tool is used for remotely booting images on development boards -attached using a CDB Assist [https://github.com/sonyxperiadev/CDB-Assist] or Conmux. += ABCD control tool +The ABCD control tool is used for remotely booting Amlogic TF-A images on development boards + +This is a friendly fork on CDBA (https://github.com/linux-msm/cdba) for the only +purpose to boot amlogic bootloader to run the U-Boot test suite. = Dependencies -sudo apt-get install libudev-dev libyaml-dev for debian systems -dnf install systemd-devel libyaml-devel for fedora systems +sudo apt-get install libudev-dev libyaml-dev libftdi1-dev pkg-config meson for debian systems +dnf install systemd-devel libyaml-devel libftdi1-devel pkg-config meson for fedora systems = Device side -On the host with the CDB Assist or Conmux attached the "cdba-server" executable is run -from sandbox/cdba/cdba-server. Available devices are read from $HOME/.cdba +On the host with the CDB Assist or Conmux attached the "abcd-server" executable is run +from sandbox/abcd/abcd-server. Available devices are read from $HOME/.abcd + += Build instructions + +# meson . build +# ninja -C build = Client side The client is invoked as: - cdba -b -h [-c ] boot.img + abcd -b -h [-c ] u-boot.bin will be connected to using ssh and will be selected for operation. As the board's fastboot interface shows up the given boot.img will @@ -27,12 +34,12 @@ restart the board the given number of times. Each time booting the given boot.img. == Device configuration -The list of attached devices is read from $HOME/.cdba and is YAML formatted. +The list of attached devices is read from $HOME/.abcd and is YAML formatted. === Example devices: - board: db2k - cdba: 00000001 + abcd: 00000001 name: "DragonBoard2k" fastboot: abcdef1 voltage: 8000 @@ -43,6 +50,16 @@ devices: fastboot_set_active: true - board: evb2k + users: + - username console: /dev/ttyUSB0 fastboot: abcdef3 fastboot_set_active: true + + - board: qrd8550 + alpaca: /dev/serial/by-id/usb-QUALCOMM_Inc._Embedded_Power_Measurement__EPM__device_6E02020620151F14-if01 + console: /dev/serial/by-id/usb-QUALCOMM_MTP_Debug_Board_MT77TGG2-if00-port0 + name: QRD8550 + fastboot: 91671140 + fastboot_set_active: true + fastboot_key_timeout: 2 diff --git a/cdba-server.c b/abcd-server.c similarity index 78% rename from cdba-server.c rename to abcd-server.c index 4453875..08258c5 100644 --- a/cdba-server.c +++ b/abcd-server.c @@ -39,14 +39,15 @@ #include #include -#include "cdba-server.h" +#include "abcd-server.h" #include "circ_buf.h" #include "device.h" #include "device_parser.h" -#include "fastboot.h" +#include "boot.h" #include "list.h" static bool quit_invoked; +static const char *username; struct device *selected_device; @@ -78,91 +79,81 @@ int tty_open(const char *tty, struct termios *old) return fd; } -static void fastboot_opened(struct fastboot *fb, void *data) +static void boot_opened(void) { const uint8_t one = 1; - struct msg *msg; - - warnx("fastboot connection opened"); - msg = alloca(sizeof(*msg) + 1); - msg->type = MSG_FASTBOOT_PRESENT; - msg->len = 1; - memcpy(msg->data, &one, 1); + warnx("boot connection opened"); - write(STDOUT_FILENO, msg, sizeof(*msg) + 1); + abcd_send_buf(MSG_BOOT_PRESENT, 1, &one); } -static void fastboot_info(struct fastboot *fb, const void *buf, size_t len) +static void boot_info(const void *buf, size_t len) { - fprintf(stderr, "%s\n", (char *)buf); + fprintf(stderr, "%s\n", (const char *)buf); } -static void fastboot_disconnect(void *data) +static void boot_disconnect(void) { const uint8_t zero = 0; - struct msg *msg; - - msg = alloca(sizeof(*msg) + 1); - msg->type = MSG_FASTBOOT_PRESENT; - msg->len = 1; - memcpy(msg->data, &zero, 1); - write(STDOUT_FILENO, msg, sizeof(*msg) + 1); + abcd_send_buf(MSG_BOOT_PRESENT, 1, &zero); } -static struct fastboot_ops fastboot_ops = { - .opened = fastboot_opened, - .disconnect = fastboot_disconnect, - .info = fastboot_info, +static struct boot_ops abcd_boot_ops = { + .opened = boot_opened, + .disconnect = boot_disconnect, + .info = boot_info, }; static void msg_select_board(const void *param) { - struct msg reply = { MSG_SELECT_BOARD, 0 }; - - selected_device = device_open(param, &fastboot_ops); + selected_device = device_open(param, username, &abcd_boot_ops); if (!selected_device) { fprintf(stderr, "failed to open %s\n", (const char *)param); quit_invoked = true; } - write(STDOUT_FILENO, &reply, sizeof(reply)); + abcd_send(MSG_SELECT_BOARD); } -static void *fastboot_payload; -static size_t fastboot_size; +static void *boot_payload; +static size_t boot_size; -static void msg_fastboot_download(const void *data, size_t len) +static void msg_boot_download(const void *data, size_t len) { - struct msg reply = { MSG_FASTBOOT_DOWNLOAD, }; - size_t new_size = fastboot_size + len; - void *newp; + size_t new_size = boot_size + len; + char *newp; - newp = realloc(fastboot_payload, new_size); + newp = realloc(boot_payload, new_size); if (!newp) - err(1, "failed too expant fastboot scratch area"); + err(1, "failed too expant boot scratch area"); - memcpy(newp + fastboot_size, data, len); + memcpy(newp + boot_size, data, len); - fastboot_payload = newp; - fastboot_size = new_size; + boot_payload = newp; + boot_size = new_size; if (!len) { - device_boot(selected_device, fastboot_payload, fastboot_size); + device_boot(selected_device, boot_payload, boot_size); - write(STDOUT_FILENO, &reply, sizeof(reply)); - free(fastboot_payload); - fastboot_payload = NULL; - fastboot_size = 0; + abcd_send(MSG_BOOT_DOWNLOAD); + free(boot_payload); + boot_payload = NULL; + boot_size = 0; } } -static void invoke_reply(int reply) +void abcd_send_buf(int type, size_t len, const void *buf) { - struct msg msg = { reply, }; + struct msg msg = { + .type = type, + .len = len + }; write(STDOUT_FILENO, &msg, sizeof(msg)); + if (len) + write(STDOUT_FILENO, buf, len); } static int handle_stdin(int fd, void *buf) @@ -194,7 +185,7 @@ static int handle_stdin(int fd, void *buf) case MSG_CONSOLE: device_write(selected_device, msg->data, msg->len); break; - case MSG_FASTBOOT_PRESENT: + case MSG_BOOT_PRESENT: break; case MSG_SELECT_BOARD: msg_select_board(msg->data); @@ -205,18 +196,18 @@ static int handle_stdin(int fd, void *buf) case MSG_POWER_ON: device_power(selected_device, true); - invoke_reply(MSG_POWER_ON); + abcd_send(MSG_POWER_ON); break; case MSG_POWER_OFF: device_power(selected_device, false); - invoke_reply(MSG_POWER_OFF); + abcd_send(MSG_POWER_OFF); break; - case MSG_FASTBOOT_DOWNLOAD: - msg_fastboot_download(msg->data, msg->len); + case MSG_BOOT_DOWNLOAD: + msg_boot_download(msg->data, msg->len); break; - case MSG_FASTBOOT_BOOT: - // fprintf(stderr, "fastboot boot\n"); + case MSG_BOOT: + fprintf(stderr, "boot\n"); break; case MSG_STATUS_UPDATE: device_print_status(selected_device); @@ -231,10 +222,10 @@ static int handle_stdin(int fd, void *buf) device_send_break(selected_device); break; case MSG_LIST_DEVICES: - device_list_devices(); + device_list_devices(username); break; case MSG_BOARD_INFO: - device_info(msg->data, msg->len); + device_info(username, msg->data, msg->len); break; default: fprintf(stderr, "unk %d len %d\n", msg->type, msg->len); @@ -266,6 +257,19 @@ struct timer { static struct list_head read_watches = LIST_INIT(read_watches); static struct list_head timer_watches = LIST_INIT(timer_watches); +void watch_del_readfd(int fd) +{ + struct watch *w; + struct watch *tmp; + + list_for_each_entry_safe(w, tmp, &read_watches, node) { + if (w->fd == fd) { + list_del(&w->node); + free(w); + } + } +} + void watch_add_readfd(int fd, int (*cb)(int, void*), void *data) { struct watch *w; @@ -304,7 +308,6 @@ static struct timeval *watch_timer_next(void) struct timeval now; struct timer *next; struct timer *t; - int count = 0; if (list_empty(&timer_watches)) return NULL; @@ -314,7 +317,6 @@ static struct timeval *watch_timer_next(void) list_for_each_entry(t, &timer_watches, node) { if (timercmp(&t->tv, &next->tv, <)) next = t; - count++; } gettimeofday(&now, NULL); @@ -366,9 +368,11 @@ int main(int argc, char **argv) signal(SIGPIPE, sigpipe_handler); - ret = device_parser(".cdba"); + username = getenv("ABCD_USER"); + + ret = device_parser(".abcd"); if (ret) { - ret = device_parser("/etc/cdba"); + ret = device_parser("/etc/abcd"); if (ret) { fprintf(stderr, "device parser: unable to open config file\n"); exit(1); diff --git a/cdba-server.h b/abcd-server.h similarity index 69% rename from cdba-server.h rename to abcd-server.h index 5c870d7..3a54523 100644 --- a/cdba-server.h +++ b/abcd-server.h @@ -4,9 +4,10 @@ #include #include -#include "cdba.h" +#include "abcd.h" void watch_add_readfd(int fd, int (*cb)(int, void*), void *data); +void watch_del_readfd(int fd); int watch_add_quit(int (*cb)(int, void*), void *data); void watch_timer_add(int timeout_ms, void (*cb)(void *), void *data); void watch_quit(void); @@ -14,4 +15,7 @@ int watch_run(void); int tty_open(const char *tty, struct termios *old); +void abcd_send_buf(int type, size_t len, const void *buf); +#define abcd_send(type) abcd_send_buf(type, 0, NULL) + #endif diff --git a/cdba.c b/abcd.c similarity index 80% rename from cdba.c rename to abcd.c index ad0e0bb..9c87864 100644 --- a/cdba.c +++ b/abcd.c @@ -44,15 +44,18 @@ #include #include -#include "cdba.h" +#include "abcd.h" #include "circ_buf.h" #include "list.h" +#define MAX_BOOT_FILES 4 + static bool quit; -static bool fastboot_repeat; -static bool fastboot_done; +static bool boot_done; -static const char *fastboot_file; +static const char *boot_files[MAX_BOOT_FILES]; +static unsigned int boot_num_stages; +static unsigned int boot_stage; static struct termios *tty_unbuffer(void) { @@ -144,10 +147,30 @@ static int fork_ssh(const char *host, const char *cmd, int *pipes) return 0; } +#define abcd_send(fd, type) abcd_send_buf(fd, type, 0, NULL) +static int abcd_send_buf(int fd, int type, size_t len, const void *buf) +{ + int ret; + + struct msg msg = { + .type = type, + .len = len + }; + + ret = write(fd, &msg, sizeof(msg)); + if (ret < 0) + return ret; + + if (len) + ret = write(fd, buf, len); + + return ret < 0 ? ret : 0; +} + static int tty_callback(int *ssh_fds) { + static const char ctrl_a = 0x1; static bool special; - struct msg hdr; char buf[32]; ssize_t k; ssize_t n; @@ -157,7 +180,7 @@ static int tty_callback(int *ssh_fds) return n; for (k = 0; k < n; k++) { - if (buf[k] == 0x1) { + if (buf[k] == ctrl_a) { special = true; } else if (special) { switch (buf[k]) { @@ -165,51 +188,31 @@ static int tty_callback(int *ssh_fds) quit = true; break; case 'P': - hdr.type = MSG_POWER_ON; - hdr.len = 0; - write(ssh_fds[0], &hdr, sizeof(hdr)); + abcd_send(ssh_fds[0], MSG_POWER_ON); break; case 'p': - hdr.type = MSG_POWER_OFF; - hdr.len = 0; - write(ssh_fds[0], &hdr, sizeof(hdr)); + abcd_send(ssh_fds[0], MSG_POWER_OFF); break; case 's': - hdr.type = MSG_STATUS_UPDATE; - hdr.len = 0; - write(ssh_fds[0], &hdr, sizeof(hdr)); + abcd_send(ssh_fds[0], MSG_STATUS_UPDATE); break; case 'V': - hdr.type = MSG_VBUS_ON; - hdr.len = 0; - write(ssh_fds[0], &hdr, sizeof(hdr)); + abcd_send(ssh_fds[0], MSG_VBUS_ON); break; case 'v': - hdr.type = MSG_VBUS_OFF; - hdr.len = 0; - write(ssh_fds[0], &hdr, sizeof(hdr)); + abcd_send(ssh_fds[0], MSG_VBUS_OFF); break; case 'a': - hdr.type = MSG_CONSOLE; - hdr.len = 1; - - write(ssh_fds[0], &hdr, sizeof(hdr)); - write(ssh_fds[0], "\001", 1); + abcd_send_buf(ssh_fds[0], MSG_CONSOLE, 1, &ctrl_a); break; case 'B': - hdr.type = MSG_SEND_BREAK; - hdr.len = 0; - write(ssh_fds[0], &hdr, sizeof(hdr)); + abcd_send(ssh_fds[0], MSG_SEND_BREAK); break; } special = false; } else { - hdr.type = MSG_CONSOLE; - hdr.len = 1; - - write(ssh_fds[0], &hdr, sizeof(hdr)); - write(ssh_fds[0], buf + k, 1); + abcd_send_buf(ssh_fds[0], MSG_CONSOLE, 1, buf + k); } } @@ -226,14 +229,10 @@ static struct list_head work_items = LIST_INIT(work_items); static void list_boards_fn(struct work *work, int ssh_stdin) { - struct msg msg; - ssize_t n; - - msg.type = MSG_LIST_DEVICES; - msg.len = 0; + int ret; - n = write(ssh_stdin, &msg, sizeof(msg)); - if (n < 0) + ret = abcd_send(ssh_stdin, MSG_LIST_DEVICES); + if (ret < 0) err(1, "failed to send board list request"); free(work); @@ -257,17 +256,12 @@ struct board_info_request { static void board_info_fn(struct work *work, int ssh_stdin) { struct board_info_request *board = container_of(work, struct board_info_request, work); - size_t blen = strlen(board->board) + 1; - struct msg *msg; - ssize_t n; - - msg = alloca(sizeof(*msg) + blen); - msg->type = MSG_BOARD_INFO; - msg->len = blen; - memcpy(msg->data, board->board, blen); + int ret; - n = write(ssh_stdin, msg, sizeof(*msg) + blen); - if (n < 0) + ret = abcd_send_buf(ssh_stdin, MSG_BOARD_INFO, + strlen(board->board) + 1, + board->board); + if (ret < 0) err(1, "failed to send board info request"); free(work); @@ -293,17 +287,12 @@ struct select_board { static void select_board_fn(struct work *work, int ssh_stdin) { struct select_board *board = container_of(work, struct select_board, work); - size_t blen = strlen(board->board) + 1; - struct msg *msg; - ssize_t n; - - msg = alloca(sizeof(*msg) + blen); - msg->type = MSG_SELECT_BOARD; - msg->len = blen; - memcpy(msg->data, board->board, blen); + int ret; - n = write(ssh_stdin, msg, sizeof(*msg) + blen); - if (n < 0) + ret = abcd_send_buf(ssh_stdin, MSG_SELECT_BOARD, + strlen(board->board) + 1, + board->board); + if (ret < 0) err(1, "failed to send power on request"); free(work); @@ -322,21 +311,19 @@ static void request_select_board(const char *board) static void request_power_on_fn(struct work *work, int ssh_stdin) { - struct msg msg = { MSG_POWER_ON, }; - ssize_t n; + int ret; - n = write(ssh_stdin, &msg, sizeof(msg)); - if (n < 0) + ret = abcd_send(ssh_stdin, MSG_POWER_ON); + if (ret < 0) err(1, "failed to send power on request"); } static void request_power_off_fn(struct work *work, int ssh_stdin) { - struct msg msg = { MSG_POWER_OFF, }; - ssize_t n; + int ret; - n = write(ssh_stdin, &msg, sizeof(msg)); - if (n < 0) + ret = abcd_send(ssh_stdin, MSG_POWER_OFF); + if (ret < 0) err(1, "failed to send power off request"); } @@ -354,7 +341,7 @@ static void request_power_off(void) list_add(&work_items, &work.node); } -struct fastboot_download_work { +struct boot_download_work { struct work work; void *data; @@ -362,49 +349,51 @@ struct fastboot_download_work { size_t size; }; -static void fastboot_work_fn(struct work *_work, int ssh_stdin) +static void boot_work_fn(struct work *_work, int ssh_stdin) { - struct fastboot_download_work *work = container_of(_work, struct fastboot_download_work, work); - struct msg *msg; - size_t left; - ssize_t n; + struct boot_download_work *work = container_of(_work, struct boot_download_work, work); + ssize_t left; + int ret; left = MIN(2048, work->size - work->offset); - msg = alloca(sizeof(*msg) + left); - msg->type = MSG_FASTBOOT_DOWNLOAD; - msg->len = left; - memcpy(msg->data, work->data + work->offset, left); - - n = write(ssh_stdin, msg, sizeof(*msg) + msg->len); - if (n < 0 && errno == EAGAIN) { + ret = abcd_send_buf(ssh_stdin, MSG_BOOT_DOWNLOAD, left, + (char *)work->data + work->offset); + if (ret < 0 && errno == EAGAIN) { list_add(&work_items, &_work->node); return; - } else if (n < 0) { - err(1, "failed to write fastboot message"); + } else if (ret < 0) { + err(1, "failed to write boot message"); } - work->offset += msg->len; + work->offset += left; /* We've sent the entire image, and a zero length packet */ - if (!msg->len) + if (!left) free(work); else list_add(&work_items, &_work->node); } -static void request_fastboot_files(void) +static void request_boot_files(void) { - struct fastboot_download_work *work; + struct boot_download_work *work; struct stat sb; int fd; + if (boot_stage >= boot_num_stages) { + errx(1, "Boot stage %u doesn't have a boot file", + boot_stage); + + return; + } + work = calloc(1, sizeof(*work)); - work->work.fn = fastboot_work_fn; + work->work.fn = boot_work_fn; - fd = open(fastboot_file, O_RDONLY); + fd = open(boot_files[boot_stage], O_RDONLY); if (fd < 0) - err(1, "failed to open \"%s\"", fastboot_file); + err(1, "failed to open \"%s\"", boot_files[boot_stage]); fstat(fd, &sb); @@ -453,6 +442,7 @@ static void handle_board_info(const void *data, size_t len) quit = true; } +static int power_cycles = -1; static bool received_power_off; static bool reached_timeout; @@ -462,7 +452,8 @@ static void handle_console(const void *data, size_t len) const char *p = data; int i; - for (i = 0; i < len; i++) { + /* Don't process the line by default (power_cycles = -1) */ + for (i = 0; i < len && power_cycles >= 0; i++) { if (*p++ == '~') { if (power_off_chars++ == 19) { received_power_off = true; @@ -517,22 +508,26 @@ static int handle_message(struct circ_buf *buf) request_power_on(); } break; - case MSG_FASTBOOT_PRESENT: + case MSG_BOOT_PRESENT: if (*(uint8_t*)msg->data) { // printf("======================================== MSG_FASTBOOT_PRESENT(on)\n"); - if (!fastboot_done || fastboot_repeat) - request_fastboot_files(); + if (!boot_done) + request_boot_files(); else quit = true; } else { - fastboot_done = true; + ++boot_stage; + warnx("new boot_stage: %u\n", boot_stage); + + if (boot_stage >= boot_num_stages) + boot_done = true; // printf("======================================== MSG_FASTBOOT_PRESENT(off)\n"); } break; - case MSG_FASTBOOT_DOWNLOAD: + case MSG_BOOT_DOWNLOAD: // printf("======================================== MSG_FASTBOOT_DOWNLOAD\n"); break; - case MSG_FASTBOOT_BOOT: + case MSG_BOOT: // printf("======================================== MSG_FASTBOOT_BOOT\n"); break; case MSG_STATUS_UPDATE: @@ -573,7 +568,7 @@ static void usage(void) extern const char *__progname; fprintf(stderr, "usage: %s -b -h [-t ] " - "[-T ] boot.img\n", + "[-T ] boot.bin [boot2.bin [bootX.bin [...]]]\n", __progname); fprintf(stderr, "usage: %s -i -b -h \n", __progname); @@ -583,9 +578,9 @@ static void usage(void) } enum { - CDBA_BOOT, - CDBA_LIST, - CDBA_INFO, + ABCD_BOOT, + ABCD_LIST, + ABCD_INFO, }; int main(int argc, char **argv) @@ -594,7 +589,7 @@ int main(int argc, char **argv) struct timeval timeout_inactivity_tv; struct timeval timeout_total_tv; struct termios *orig_tios; - const char *server_binary = "cdba-server"; + const char *server_binary = "abcd-server"; int timeout_inactivity = 0; int timeout_total = 600; struct work *next; @@ -604,7 +599,6 @@ int main(int argc, char **argv) const char *host = NULL; struct timeval now; struct timeval tv; - int power_cycles = 0; struct stat sb; int ssh_fds[3]; char buf[128]; @@ -612,7 +606,7 @@ int main(int argc, char **argv) fd_set wfds; ssize_t n; int nfds; - int verb = CDBA_BOOT; + int verb = ABCD_BOOT; int opt; int ret; @@ -631,13 +625,10 @@ int main(int argc, char **argv) host = optarg; break; case 'i': - verb = CDBA_INFO; + verb = ABCD_INFO; break; case 'l': - verb = CDBA_LIST; - break; - case 'R': - fastboot_repeat = true; + verb = ABCD_LIST; break; case 'S': server_binary = optarg; @@ -657,22 +648,29 @@ int main(int argc, char **argv) usage(); switch (verb) { - case CDBA_BOOT: + case ABCD_BOOT: if (optind >= argc || !board) usage(); - fastboot_file = argv[optind]; - if (lstat(fastboot_file, &sb)) - err(1, "unable to read \"%s\"", fastboot_file); - if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode)) - errx(1, "\"%s\" is not a regular file", fastboot_file); + while (optind < argc) { + boot_files[boot_num_stages] = argv[optind]; + if (lstat(boot_files[boot_num_stages], &sb)) + err(1, "unable to read \"%s\"", + boot_files[boot_num_stages]); + if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode)) + errx(1, "\"%s\" is not a regular file", + boot_files[boot_num_stages]); + + ++optind; + ++boot_num_stages; + } request_select_board(board); break; - case CDBA_LIST: + case ABCD_LIST: request_board_list(); break; - case CDBA_INFO: + case ABCD_INFO: if (!board) usage(); @@ -691,7 +689,7 @@ int main(int argc, char **argv) while (!quit) { if (received_power_off || reached_timeout) { - if (!power_cycles) + if (power_cycles <= 0) break; if (reached_timeout && !power_cycle_on_timeout) @@ -801,7 +799,7 @@ int main(int argc, char **argv) close(ssh_fds[1]); close(ssh_fds[2]); - if (verb == CDBA_BOOT) + if (verb == ABCD_BOOT) printf("Waiting for ssh to finish\n"); wait(NULL); @@ -809,7 +807,7 @@ int main(int argc, char **argv) tty_reset(orig_tios); if (reached_timeout) - return fastboot_done ? 110 : 2; + return boot_done ? 110 : 2; return (quit || received_power_off) ? 0 : 1; } diff --git a/cdba.h b/abcd.h similarity index 77% rename from cdba.h rename to abcd.h index 138ca7e..e5fd95c 100644 --- a/cdba.h +++ b/abcd.h @@ -1,5 +1,5 @@ -#ifndef __CDBA_H__ -#define __CDBA_H__ +#ifndef __ABCD_H__ +#define __ABCD_H__ #include @@ -20,13 +20,13 @@ enum { MSG_HARDRESET, MSG_POWER_ON, MSG_POWER_OFF, - MSG_FASTBOOT_PRESENT, - MSG_FASTBOOT_DOWNLOAD, - MSG_FASTBOOT_BOOT, + MSG_BOOT_PRESENT, + MSG_BOOT_DOWNLOAD, + MSG_BOOT, MSG_STATUS_UPDATE, MSG_VBUS_ON, MSG_VBUS_OFF, - MSG_FASTBOOT_REBOOT, + MSG_BOOT_REBOOT, MSG_SEND_BREAK, MSG_LIST_DEVICES, MSG_BOARD_INFO, diff --git a/alpaca.c b/alpaca.c deleted file mode 100644 index fd7e996..0000000 --- a/alpaca.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2018, Linaro Ltd. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cdba-server.h" -#include "alpaca.h" - -struct alpaca { - int alpaca_fd; - - struct termios alpaca_tios; -}; - -static int alpaca_device_power(struct alpaca *alpaca, int on); -static int alpaca_usb_device_power(struct alpaca *alpaca, int on); - -void *alpaca_open(struct device *dev) -{ - struct alpaca *alpaca; - - dev->has_power_key = true; - - 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); - - alpaca_device_power(alpaca, 0); - - if (dev->usb_always_on) - alpaca_usb_device_power(alpaca, 1); - else - alpaca_usb_device_power(alpaca, 0); - - usleep(500000); - - return alpaca; -} - -static int alpaca_device_power(struct alpaca *alpaca, int on) -{ - char buf[32]; - int n; - - n = sprintf(buf, "devicePower %d\r", !!on); - - return write(alpaca->alpaca_fd, buf, n); -} - -static int alpaca_usb_device_power(struct alpaca *alpaca, int on) -{ - char buf[32]; - int n; - - n = sprintf(buf, "usbDevicePower %d\r", !!on); - - return write(alpaca->alpaca_fd, buf, n); -} - -static int alpaca_output_bit(struct alpaca *alpaca, int bit, int value) -{ - char buf[32]; - int n; - - n = sprintf(buf, "ttl outputBit %d %d\r", bit, !!value); - - return write(alpaca->alpaca_fd, buf, n); -} - -static int alpaca_power_on(struct device *dev) -{ - alpaca_device_power(dev->cdb, 1); - - return 0; -} - -static int alpaca_power_off(struct device *dev) -{ - alpaca_device_power(dev->cdb, 0); - - return 0; -} - -int alpaca_power(struct device *dev, bool on) -{ - if (on) - return alpaca_power_on(dev); - else - return alpaca_power_off(dev); -} - -void alpaca_usb(struct device *dev, bool on) -{ - struct alpaca *alpaca = dev->cdb; - - alpaca_usb_device_power(alpaca, on); -} - -void alpaca_key(struct device *dev, int key, bool asserted) -{ - switch (key) { - case DEVICE_KEY_FASTBOOT: - alpaca_output_bit(dev->cdb, 2, asserted); - break; - case DEVICE_KEY_POWER: - alpaca_output_bit(dev->cdb, 1, asserted); - break; - } -} diff --git a/alpaca.h b/alpaca.h deleted file mode 100644 index 930d35c..0000000 --- a/alpaca.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __ALPACA_H__ -#define __ALPACA_H__ - -#include "device.h" - -struct alpaca; - -void *alpaca_open(struct device *dev); -int alpaca_power(struct device *dev, bool on); -void alpaca_usb(struct device *dev, bool on); -void alpaca_key(struct device *dev, int key, bool on); - -#endif diff --git a/boot.h b/boot.h new file mode 100644 index 0000000..40d1ec5 --- /dev/null +++ b/boot.h @@ -0,0 +1,10 @@ +#ifndef __BOOT_H__ +#define __BOOT_H__ + +struct boot_ops { + void (*opened)(void); + void (*disconnect)(void); + void (*info)(const void *, size_t); +}; + +#endif diff --git a/cdb_assist.c b/cdb_assist.c deleted file mode 100644 index 92d9cf3..0000000 --- a/cdb_assist.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (c) 2016-2018, Linaro Ltd. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cdba-server.h" -#include "cdb_assist.h" - -struct cdb_assist { - char serial[9]; - - int control_tty; - - struct termios control_tios; - struct termios target_tios; - - struct cdb_assist *next; - - /* parser */ - unsigned state; - unsigned num[2]; - char key[32]; - unsigned key_idx; - bool voltage; - - /* state */ - unsigned current_actual; - unsigned current_set; - unsigned voltage_actual; - unsigned voltage_set; - - bool vbat; - bool btn[3]; - bool vbus; - unsigned vref; -}; - -enum { - STATE_, - STATE_key, - STATE_key_bool, - STATE_key_value, - STATE_key_o, - STATE_key_of, - STATE_key_num, - STATE_key_num_m, - STATE_num, - STATE_num_m, - STATE_num_mX, - STATE_num_mX_, - STATE_num_num_m, -}; - -static void cdb_parser_bool(struct cdb_assist *cdb, const char *key, bool set) -{ - static const char *sz_keys[] = { "vbat", "btn1", "btn2", "btn3", "vbus" }; - int i; - - for (i = 0; i < 5; i++) - if (strcmp(key, sz_keys[i]) == 0) - break; - - switch (i) { - case 0: - cdb->vbat = set; - break; - case 1: - case 2: - case 3: - cdb->btn[i-1] = set; - break; - case 4: - cdb->vbus = set; - break; - } -} -static void cdb_parser_current(struct cdb_assist *cdb, unsigned set, unsigned actual) -{ - cdb->current_actual = actual; - cdb->current_set = set; -} - -static void cdb_parser_voltage(struct cdb_assist *cdb, unsigned set, unsigned actual) -{ - cdb->voltage_actual = actual; - cdb->voltage_set = set; -} - -static void cdb_parser_vref(struct cdb_assist *cdb, unsigned vref) -{ - cdb->vref = vref; -} - -static void cdb_parser_push(struct cdb_assist *cdb, char ch) -{ - switch (cdb->state) { - case STATE_: - if (isdigit(ch)) { - cdb->num[0] = ch - '0'; - cdb->state = STATE_num; - } else if (isalpha(ch)) { - cdb->key[0] = ch; - cdb->key_idx = 1; - cdb->state = STATE_key; - } - break; - case STATE_num: - if (isdigit(ch)) { - cdb->num[0] *= 10; - cdb->num[0] += ch - '0'; - } else if (ch == 'm') { - cdb->state = STATE_num_m; - } else { - cdb->state = STATE_; - } - break; - case STATE_num_m: - if (ch == 'v') { - cdb->voltage = true; - cdb->state = STATE_num_mX; - } else if (ch == 'a') { - cdb->voltage = false; - cdb->state = STATE_num_mX; - } else { - cdb->state = STATE_; - } - break; - case STATE_num_mX: - if (ch == '/') { - cdb->num[1] = 0; - cdb->state = STATE_num_mX_; - } else { - cdb->state = STATE_; - } - break; - case STATE_num_mX_: - if (isdigit(ch)) { - cdb->num[1] *= 10; - cdb->num[1] += ch - '0'; - } else if (ch == 'm') { - cdb->state = STATE_num_num_m; - } else { - cdb->state = STATE_; - } - break; - case STATE_num_num_m: - if (ch == 'v' && cdb->voltage) - cdb_parser_voltage(cdb, cdb->num[0], cdb->num[1]); - else if (ch == 'a' && !cdb->voltage) - cdb_parser_current(cdb, cdb->num[0], cdb->num[1]); - - cdb->state = STATE_; - break; - case STATE_key: - if (isalnum(ch)) { - cdb->key[cdb->key_idx++] = ch; - } else if (ch == ':') { - cdb->key[cdb->key_idx] = '\0'; - cdb->state = STATE_key_bool; - } else if (ch == '=') { - cdb->key[cdb->key_idx] = '\0'; - cdb->state = STATE_key_value; - } else { - cdb->state = STATE_; - } - break; - case STATE_key_bool: - if (ch == 'o') - cdb->state = STATE_key_o; - else - cdb->state = STATE_; - break; - case STATE_key_o: - if (ch == 'f') { - cdb->state = STATE_key_of; - } else if (ch == 'n') { - cdb_parser_bool(cdb, cdb->key, true); - cdb->state = STATE_; - } else { - cdb->state = STATE_; - } - break; - case STATE_key_of: - if (ch == 'f') - cdb_parser_bool(cdb, cdb->key, false); - cdb->state = STATE_; - break; - case STATE_key_value: - if (isdigit(ch)) { - cdb->num[0] = ch - '0'; - cdb->state = STATE_key_num; - } else { - cdb->state = STATE_; - } - break; - case STATE_key_num: - if (isdigit(ch)) { - cdb->num[0] *= 10; - cdb->num[0] += ch - '0'; - } else if (ch == 'm') { - cdb->state = STATE_key_num_m; - } else { - cdb->state = STATE_; - } - break; - case STATE_key_num_m: - if (ch == 'v') - cdb_parser_vref(cdb, cdb->num[0]); - cdb->state = STATE_; - break; - } -} - -static int cdb_assist_ctrl_data(int fd, void *data) -{ - struct cdb_assist *cdb = data; - char buf[10]; - ssize_t n; - ssize_t k; - - n = read(fd, buf, sizeof(buf) - 1); - if (n < 0) - return n; - - for (k = 0; k < n; k++) - cdb_parser_push(cdb, tolower(buf[k])); - - return 0; -} - -static int cdb_ctrl_write(struct cdb_assist *cdb, const char *buf, size_t len) -{ - return write(cdb->control_tty, buf, len); -} - -void *cdb_assist_open(struct device *dev) -{ - struct cdb_assist *cdb; - int ret; - - cdb = calloc(1, sizeof(*cdb)); - - cdb->control_tty = tty_open(dev->control_dev, &cdb->control_tios); - if (cdb->control_tty < 0) - return NULL; - - watch_add_readfd(cdb->control_tty, cdb_assist_ctrl_data, cdb); - - ret = cdb_ctrl_write(cdb, "vpabc", 5); - if (ret < 0) - return NULL; - - cdb_set_voltage(cdb, dev->voltage); - - return cdb; -} - -void cdb_assist_close(struct device *dev) -{ - struct cdb_assist *cdb = dev->cdb; - - tcflush(cdb->control_tty, TCIFLUSH); - - close(cdb->control_tty); -} - -static void cdb_power(struct cdb_assist *cdb, bool on) -{ - const char cmd[] = "pP"; - - cdb_ctrl_write(cdb, &cmd[on], 1); -} - -void cdb_vbus(struct cdb_assist *cdb, bool on) -{ - const char cmd[] = "vV"; - - cdb_ctrl_write(cdb, &cmd[on], 1); -} - -int cdb_assist_power(struct device *dev, bool on) -{ - struct cdb_assist *cdb = dev->cdb; - - cdb_power(cdb, on); - - return 0; -} - -void cdb_assist_usb(struct device *dev, bool on) -{ - cdb_vbus(dev->cdb, on); -} - -void cdb_gpio(struct cdb_assist *cdb, int gpio, bool on) -{ - const char *cmd[] = { "aA", "bB", "cC" }; - cdb_ctrl_write(cdb, &cmd[gpio][on], 1); -} - -unsigned int cdb_vref(struct cdb_assist *cdb) -{ - return cdb->vref; -} - -void cdb_assist_print_status(struct device *dev) -{ - struct cdb_assist *cdb = dev->cdb; - struct msg hdr; - char buf[128]; - int n; - - n = sprintf(buf, "%dmV %dmA%s%s%s%s%s ref: %dmV", - cdb->voltage_set, - cdb->current_actual, - cdb->vbat ? " vbat" : "", - cdb->vbus ? " vbus" : "", - cdb->btn[0] ? " btn1" : "", - cdb->btn[1] ? " btn2" : "", - cdb->btn[2] ? " btn3" : "", - cdb->vref); - - hdr.type = MSG_STATUS_UPDATE; - hdr.len = n; - write(STDOUT_FILENO, &hdr, sizeof(hdr)); - write(STDOUT_FILENO, buf, n); -} - -void cdb_set_voltage(struct cdb_assist *cdb, unsigned mV) -{ - char buf[20]; - int n; - - n = sprintf(buf, "u%d\r\n", mV); - cdb_ctrl_write(cdb, buf, n); -} - -void cdb_assist_key(struct device *dev, int key, bool asserted) -{ - struct cdb_assist *cdb = dev->cdb; - - switch (key) { - case DEVICE_KEY_FASTBOOT: - cdb_gpio(cdb, 1, asserted); - break; - case DEVICE_KEY_POWER: - cdb_gpio(cdb, 0, asserted); - break; - } -} diff --git a/cdb_assist.h b/cdb_assist.h deleted file mode 100644 index babcd65..0000000 --- a/cdb_assist.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __CDB_ASSIST_H__ -#define __CDB_ASSIST_H__ - -#include - -#include "device.h" - -struct cdb_assist; - -void *cdb_assist_open(struct device *dev); -void cdb_assist_close(struct device *dev); - -int cdb_assist_power(struct device *dev, bool on); -void cdb_assist_usb(struct device *dev, bool on); -void cdb_assist_key(struct device *dev, int key, bool asserted); -void cdb_gpio(struct cdb_assist *cdb, int gpio, bool on); -int cdb_target_write(struct device *dev, const void *buf, size_t len); -void cdb_send_break(struct device *dev); -unsigned int cdb_vref(struct cdb_assist *cdb); -void cdb_assist_print_status(struct device *dev); -void cdb_set_voltage(struct cdb_assist *cdb, unsigned mV); - -#endif diff --git a/ci/archlinux.sh b/ci/archlinux.sh new file mode 100755 index 0000000..9add942 --- /dev/null +++ b/ci/archlinux.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2021 Canonical Ltd. +# Copyright (c) 2023 Linaro Ltd +# Author: Krzysztof Kozlowski +# +# + +set -ex + +PKGS_CC="gcc" +case $CC in + clang*) + PKGS_CC="clang" + ;; +esac + +pacman -Syu --noconfirm \ + libftdi-compat \ + libyaml \ + libgpiod \ + systemd-libs \ + pkgconf \ + meson \ + $PKGS_CC + +echo "Install finished: $0" diff --git a/ci/debian.cross-compile.sh b/ci/debian.cross-compile.sh new file mode 100755 index 0000000..04be957 --- /dev/null +++ b/ci/debian.cross-compile.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2018-2020 Petr Vorel +# Copyright (c) 2021 Canonical Ltd. +# Copyright (c) 2023 Linaro Ltd +# Author: Krzysztof Kozlowski +# +# + +set -ex + +if [ -z "$ARCH" ]; then + echo "missing \$ARCH!" >&2 + exit 1 +fi + +dpkg --add-architecture $ARCH +apt update + +apt install -y --no-install-recommends \ + libftdi-dev:${ARCH} \ + libudev-dev:${ARCH} \ + libyaml-dev:${ARCH} \ + libgpiod-dev:${ARCH} \ + gcc-`dpkg-architecture -a ${ARCH} -q DEB_TARGET_GNU_TYPE` + +echo "Install finished: $0" diff --git a/ci/debian.i386.sh b/ci/debian.i386.sh new file mode 100755 index 0000000..100cacc --- /dev/null +++ b/ci/debian.i386.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2018-2020 Petr Vorel +# Copyright (c) 2021 Canonical Ltd. +# Copyright (c) 2023 Linaro Ltd +# Author: Krzysztof Kozlowski +# +# + +set -ex + +dpkg --add-architecture i386 +apt update + +# gcc-multilib are also needed for clang 32-bit builds +PKGS_CC="gcc-multilib" + +apt install -y --no-install-recommends \ + linux-libc-dev:i386 + +apt install -y --no-install-recommends \ + libftdi-dev:i386 \ + libudev-dev:i386 \ + libyaml-dev:i386 \ + libgpiod-dev:i386 \ + $PKGS_CC + +echo "Install finished: $0" diff --git a/ci/debian.sh b/ci/debian.sh new file mode 100755 index 0000000..2fdc362 --- /dev/null +++ b/ci/debian.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2021 Canonical Ltd. +# Copyright (c) 2023 Linaro Ltd +# Author: Krzysztof Kozlowski +# +# + +set -ex + +apt update + +# Some distros (e.g. Ubuntu Hirsute) might pull tzdata which asks questions +export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true + +# Choose some random place in Europe +echo "tzdata tzdata/Areas select Europe +tzdata tzdata/Zones/Europe select Berlin +" > /tmp/tzdata-preseed.txt +debconf-set-selections /tmp/tzdata-preseed.txt + +PKGS_CC="build-essential" +case $CC in + clang*) + PKGS_CC="clang" + ;; +esac + +apt install -y --no-install-recommends \ + pkg-config \ + libftdi-dev \ + libudev-dev \ + libyaml-dev \ + libgpiod-dev \ + meson \ + $PKGS_CC + +echo "Install finished: $0" diff --git a/ci/fedora.sh b/ci/fedora.sh new file mode 100755 index 0000000..72f186a --- /dev/null +++ b/ci/fedora.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2021 Canonical Ltd. +# Copyright (c) 2023 Linaro Ltd +# Author: Krzysztof Kozlowski +# +# + +set -ex + +PKGS_CC="gcc" +case $CC in + clang*) + PKGS_CC="clang" + ;; +esac + +dnf -y install \ + libftdi-devel \ + libudev-devel \ + libyaml-devel \ + libgpiod-devel \ + meson \ + $PKGS_CC + +echo "Install finished: $0" diff --git a/ci/ubuntu.cross-compile.sh b/ci/ubuntu.cross-compile.sh new file mode 120000 index 0000000..18f77ad --- /dev/null +++ b/ci/ubuntu.cross-compile.sh @@ -0,0 +1 @@ +debian.cross-compile.sh \ No newline at end of file diff --git a/ci/ubuntu.sh b/ci/ubuntu.sh new file mode 120000 index 0000000..0edcb8b --- /dev/null +++ b/ci/ubuntu.sh @@ -0,0 +1 @@ +debian.sh \ No newline at end of file diff --git a/circ_buf.c b/circ_buf.c index ab39f7a..08766be 100644 --- a/circ_buf.c +++ b/circ_buf.c @@ -45,8 +45,7 @@ */ ssize_t circ_fill(int fd, struct circ_buf *circ) { - size_t space; - size_t count = 0; + ssize_t space; ssize_t n = 0; do { @@ -63,8 +62,6 @@ ssize_t circ_fill(int fd, struct circ_buf *circ) } else if (n < 0) return -1; - count += n; - circ->head = (circ->head + n) & (CIRC_BUF_SIZE - 1); } while (n != space); @@ -85,7 +82,7 @@ size_t circ_peak(struct circ_buf *circ, void *buf, size_t len) tail = (tail + 1) & (CIRC_BUF_SIZE - 1); } - return (void*)p - buf; + return p - (char *)buf; } size_t circ_read(struct circ_buf *circ, void *buf, size_t len) @@ -101,5 +98,5 @@ size_t circ_read(struct circ_buf *circ, void *buf, size_t len) circ->tail = (circ->tail + 1) & (CIRC_BUF_SIZE - 1); } - return (void*)p - buf; + return p - (char *)buf; } diff --git a/conmux.c b/conmux.c index 49ef643..2ca75a2 100644 --- a/conmux.c +++ b/conmux.c @@ -40,7 +40,7 @@ #include #include -#include "cdba-server.h" +#include "abcd-server.h" #include "conmux.h" extern int h_errno; @@ -51,7 +51,7 @@ struct conmux { struct conmux_lookup { char *host; - int port; + char *port; }; struct conmux_response { @@ -166,7 +166,7 @@ static int registry_lookup(const char *service, struct conmux_lookup *result) err(1, "failed to connect to registry"); ret = snprintf(buf, sizeof(buf), "LOOKUP service=%s\n", service); - if (ret >= sizeof(buf)) + if (ret >= (int)sizeof(buf)) errx(1, "service name too long for registry lookup request"); n = write(fd, buf, ret + 1); @@ -193,7 +193,7 @@ static int registry_lookup(const char *service, struct conmux_lookup *result) *p++ = '\0'; result->host = strdup(resp.result); - result->port = strtol(p, NULL, 10); + result->port = strdup(p); ret = strcmp(resp.status, "OK") ? -1 : 0; @@ -207,7 +207,6 @@ static int registry_lookup(const char *service, struct conmux_lookup *result) static int conmux_data(int fd, void *data) { - struct msg hdr; char buf[128]; ssize_t n; @@ -219,10 +218,7 @@ static int conmux_data(int fd, void *data) fprintf(stderr, "Received EOF from conmux\n"); watch_quit(); } else { - hdr.type = MSG_CONSOLE; - hdr.len = n; - write(STDOUT_FILENO, &hdr, sizeof(hdr)); - write(STDOUT_FILENO, buf, n); + abcd_send_buf(MSG_CONSOLE, n, buf); } return 0; @@ -230,17 +226,16 @@ static int conmux_data(int fd, void *data) void *conmux_open(struct device *dev) { + struct addrinfo hints = {0}, *addrs, *addr; struct conmux_response resp = {}; struct conmux_lookup lookup; - struct sockaddr_in saddr; struct conmux *conmux; - struct hostent *hent; const char *service = dev->control_dev; const char *user; ssize_t n; char req[256]; int ret; - int fd; + int fd = -1; user = getenv("USER"); if (!user) @@ -250,30 +245,34 @@ void *conmux_open(struct device *dev) if (ret) exit(1); - fprintf(stderr, "conmux device at %s:%d\n", lookup.host, lookup.port); + fprintf(stderr, "conmux device at %s:%s\n", lookup.host, lookup.port); - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd < 0) - err(1, "failed to create registry socket"); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(lookup.port); + ret = getaddrinfo(lookup.host, lookup.port, &hints, &addrs); + if (ret != 0) + errx(1, "failed resolve \"%s\": %s", lookup.host, gai_strerror(ret)); - hent = gethostbyname(lookup.host); - if (!hent) { - errno = h_errno; - err(1, "failed resolve \"%s\"", lookup.host); - } + for (addr = addrs; addr; addr = addr->ai_next) { + fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (fd < 0) + err(1, "failed to create registry socket"); - saddr.sin_addr = *(struct in_addr *)hent->h_addr_list[0]; + ret = connect(fd, addr->ai_addr, addr->ai_addrlen); + if (ret < 0) { + warn("failed to connect to conmux instance"); + close(fd); + fd = -1; + } + } - ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); - if (ret < 0) - err(1, "failed to connect to conmux instance"); + if (fd == -1) + errx(1, "failed to connect to conmux instance"); - ret = snprintf(req, sizeof(req), "CONNECT id=cdba:%s to=console\n", user); - if (ret >= sizeof(req)) + ret = snprintf(req, sizeof(req), "CONNECT id=abcd:%s to=console\n", user); + if (ret >= (int)sizeof(req)) errx(1, "unable to fit connect request in buffer"); n = write(fd, req, ret + 1); diff --git a/console.c b/console.c index 2cab31a..d96048a 100644 --- a/console.c +++ b/console.c @@ -34,12 +34,11 @@ #include #include -#include "cdba-server.h" +#include "abcd-server.h" #include "device.h" static int console_data(int fd, void *data) { - struct msg hdr; char buf[128]; ssize_t n; @@ -47,10 +46,7 @@ static int console_data(int fd, void *data) if (n < 0) return n; - hdr.type = MSG_CONSOLE; - hdr.len = n; - write(STDOUT_FILENO, &hdr, sizeof(hdr)); - write(STDOUT_FILENO, buf, n); + abcd_send_buf(MSG_CONSOLE, n, buf); return 0; } diff --git a/device.c b/device.c index 35805c5..2df3783 100644 --- a/device.c +++ b/device.c @@ -38,12 +38,16 @@ #include #include #include +#include -#include "cdba-server.h" +#include "abcd-server.h" #include "device.h" -#include "fastboot.h" +#include "boot.h" +#include "pyamlboot.h" +#include "dfu.h" #include "console.h" #include "list.h" +#include "ppps.h" #define ARRAY_SIZE(x) ((sizeof(x)/sizeof((x)[0]))) @@ -60,8 +64,8 @@ static void device_lock(struct device *device) int fd; int n; - n = snprintf(lock, sizeof(lock), "/tmp/cdba-%s.lock", device->board); - if (n >= sizeof(lock)) + n = snprintf(lock, sizeof(lock), "/tmp/abcd-%s.lock", device->board); + if (n >= (int)sizeof(lock)) errx(1, "failed to build lockfile path"); fd = open(lock, O_RDONLY | O_CREAT, 0666); @@ -83,8 +87,84 @@ static void device_lock(struct device *device) err(1, "failed to lock lockfile %s", lock); } +static bool device_check_access(struct device *device, + const char *username) +{ + struct device_user *user; + + if (!device->users) + return true; + + if (!username) + return false; + + list_for_each_entry(user, device->users, node) { + if (!strcmp(user->username, username)) + return true; + } + + return false; +} + +static void device_open_boot(struct device *device) +{ + void *boot_stage_data; + char *boot_stage_options; + + if (device->boot_stage >= MAX_BOOT_STAGES || + device->boot_stage >= device->boot_num_stages) + return; + + boot_stage_options = device->boot_stage_options[device->boot_stage]; + + switch (device->boot_stages[device->boot_stage]) { + case BOOT_PYAMLBOOT: + boot_stage_data = pyamlboot_open(device, device->boot_ops, boot_stage_options); + break; + case BOOT_DFU: + boot_stage_data = dfu_open(device, device->boot_ops, boot_stage_options); + break; + default: + errx(1, "No boot type defined for stage %u", device->boot_stage); + } + + device->boot_stage_data[device->boot_stage] = boot_stage_data; +} + +void device_boot(struct device *device, const void *data, size_t len) +{ + void *boot_stage_data = device->boot_stage_data[device->boot_stage]; + + warnx("booting the board..."); + + device->do_boot(boot_stage_data, data, len); + + switch (device->boot_stages[device->boot_stage]) { + case BOOT_PYAMLBOOT: + pyamlboot_close(device, boot_stage_data); + break; + case BOOT_DFU: + dfu_close(device, boot_stage_data); + break; + default: + errx(1, "No boot type defined for stage %u", device->boot_stage); + } + + device->do_boot = NULL; + + /* Increase boot stage */ + ++device->boot_stage; + + if (device->boot_stage >= MAX_BOOT_STAGES || + device->boot_stage >= device->boot_num_stages) + return; + + device_open_boot(device); +} + struct device *device_open(const char *board, - struct fastboot_ops *fastboot_ops) + const char *username, + struct boot_ops *boot_ops) { struct device *device; @@ -98,6 +178,9 @@ struct device *device_open(const char *board, found: assert(device->open || device->console_dev); + if (!device_check_access(device, username)) + return NULL; + device_lock(device); if (device->open) { @@ -112,7 +195,9 @@ struct device *device_open(const char *board, if (device->usb_always_on) device_usb(device, true); - device->fastboot = fastboot_open(device->serial, fastboot_ops, NULL); + device->boot_ops = boot_ops; + + device_open_boot(device); return device; } @@ -133,7 +218,7 @@ enum { DEVICE_STATE_CONNECT, DEVICE_STATE_PRESS, DEVICE_STATE_RELEASE_PWR, - DEVICE_STATE_RELEASE_FASTBOOT, + DEVICE_STATE_RELEASE_BOOT, DEVICE_STATE_RUNNING, }; @@ -143,9 +228,8 @@ static void device_tick(void *data) switch (device->state) { case DEVICE_STATE_START: - /* Make sure power key is not engaged */ - if (device->fastboot_key_timeout) - device_key(device, DEVICE_KEY_FASTBOOT, true); + if (device->boot_key_timeout) + device_key(device, DEVICE_KEY_BOOT, true); if (device->has_power_key) device_key(device, DEVICE_KEY_POWER, false); @@ -160,9 +244,9 @@ static void device_tick(void *data) if (device->has_power_key) { device->state = DEVICE_STATE_PRESS; watch_timer_add(250, device_tick, device); - } else if (device->fastboot_key_timeout) { - device->state = DEVICE_STATE_RELEASE_FASTBOOT; - watch_timer_add(device->fastboot_key_timeout * 1000, device_tick, device); + } else if (device->boot_key_timeout) { + device->state = DEVICE_STATE_RELEASE_BOOT; + watch_timer_add(device->boot_key_timeout * 1000, device_tick, device); } else { device->state = DEVICE_STATE_RUNNING; } @@ -178,15 +262,15 @@ static void device_tick(void *data) /* Release power key */ device_key(device, DEVICE_KEY_POWER, false); - if (device->fastboot_key_timeout) { - device->state = DEVICE_STATE_RELEASE_FASTBOOT; - watch_timer_add(device->fastboot_key_timeout * 1000, device_tick, device); + if (device->boot_key_timeout) { + device->state = DEVICE_STATE_RELEASE_BOOT; + watch_timer_add(device->boot_key_timeout * 1000, device_tick, device); } else { device->state = DEVICE_STATE_RUNNING; } break; - case DEVICE_STATE_RELEASE_FASTBOOT: - device_key(device, DEVICE_KEY_FASTBOOT, false); + case DEVICE_STATE_RELEASE_BOOT: + device_key(device, DEVICE_KEY_BOOT, false); device->state = DEVICE_STATE_RUNNING; break; } @@ -229,8 +313,12 @@ void device_print_status(struct device *device) void device_usb(struct device *device, bool on) { - if (device->usb) - device->usb(device, on); + if (device->usb) { + if (device->ppps_path) + ppps_power(device, on); + else + device->usb(device, on); + } } int device_write(struct device *device, const void *buf, size_t len) @@ -243,76 +331,54 @@ int device_write(struct device *device, const void *buf, size_t len) return device->write(device, buf, len); } -void device_fastboot_boot(struct device *device) -{ - fastboot_boot(device->fastboot); -} - -void device_fastboot_flash_reboot(struct device *device) -{ - fastboot_flash(device->fastboot, "boot"); - fastboot_reboot(device->fastboot); -} - -void device_boot(struct device *device, const void *data, size_t len) -{ - warnx("booting the board..."); - if (device->set_active) - fastboot_set_active(device->fastboot, "a"); - fastboot_download(device->fastboot, data, len); - device->boot(device); -} - void device_send_break(struct device *device) { if (device->send_break) device->send_break(device); } -void device_list_devices(void) +void device_list_devices(const char *username) { struct device *device; - struct msg hdr; size_t len; char buf[80]; list_for_each_entry(device, &devices, node) { + if (!device_check_access(device, username)) + continue; + if (device->name) len = snprintf(buf, sizeof(buf), "%-20s %s", device->board, device->name); else len = snprintf(buf, sizeof(buf), "%s", device->board); - hdr.type = MSG_LIST_DEVICES; - hdr.len = len; - write(STDOUT_FILENO, &hdr, sizeof(hdr)); - write(STDOUT_FILENO, buf, len); + abcd_send_buf(MSG_LIST_DEVICES, len, buf); } - hdr.type = MSG_LIST_DEVICES; - hdr.len = 0; - write(STDOUT_FILENO, &hdr, sizeof(hdr)); + abcd_send_buf(MSG_LIST_DEVICES, 0, NULL); } -void device_info(const void *data, size_t dlen) +void device_info(const char *username, const void *data, size_t dlen) { + char *description = NULL; struct device *device; - struct msg hdr; - char *description; size_t len = 0; list_for_each_entry(device, &devices, node) { - if (!strncmp(device->board, data, dlen) && device->description) { + if (strncmp(device->board, data, dlen)) + continue; + + if (!device_check_access(device, username)) + continue; + + if (device->description) { description = device->description; len = strlen(device->description); break; } } - hdr.type = MSG_BOARD_INFO; - hdr.len = len; - write(STDOUT_FILENO, &hdr, sizeof(hdr)); - if (len) - write(STDOUT_FILENO, description, len); + abcd_send_buf(MSG_BOARD_INFO, len, description); } void device_close(struct device *dev) diff --git a/device.h b/device.h index 4077d4e..ef4516f 100644 --- a/device.h +++ b/device.h @@ -4,26 +4,39 @@ #include #include "list.h" -struct cdb_assist; -struct fastboot; -struct fastboot_ops; +struct boot_ops; + +#define MAX_BOOT_STAGES 4 + +enum boot_stage { + BOOT_NONE = 0, + BOOT_PYAMLBOOT, + BOOT_DFU, +}; struct device { char *board; char *control_dev; char *console_dev; char *name; - char *serial; char *description; + char *ppps_path; + struct list_head *users; unsigned voltage; bool tickle_mmc; bool usb_always_on; - struct fastboot *fastboot; - unsigned int fastboot_key_timeout; + + unsigned int boot_key_timeout; int state; bool has_power_key; - void (*boot)(struct device *); + struct boot_ops *boot_ops; + char *boot_stage_options[MAX_BOOT_STAGES]; + void *boot_stage_data[MAX_BOOT_STAGES]; + enum boot_stage boot_stages[MAX_BOOT_STAGES]; + unsigned int boot_num_stages; + unsigned int boot_stage; + int (*do_boot)(void *boot_data, const void *data, size_t len); void *(*open)(struct device *dev); void (*close)(struct device *dev); @@ -31,11 +44,10 @@ struct device { void (*usb)(struct device *dev, bool on); void (*print_status)(struct device *dev); int (*write)(struct device *dev, const void *buf, size_t len); - void (*fastboot_key)(struct device *dev, bool on); + void (*boot_key)(struct device *dev, bool on); void (*key)(struct device *device, int key, bool asserted); void (*send_break)(struct device *dev); - bool set_active; void *cdb; @@ -45,9 +57,17 @@ struct device { struct list_head node; }; +struct device_user { + const char *username; + + struct list_head node; +}; + void device_add(struct device *device); -struct device *device_open(const char *board, struct fastboot_ops *fastboot_ops); +struct device *device_open(const char *board, + const char *username, + struct boot_ops *boot_ops); void device_close(struct device *dev); int device_power(struct device *device, bool on); @@ -57,14 +77,12 @@ int device_write(struct device *device, const void *buf, size_t len); void device_boot(struct device *device, const void *data, size_t len); -void device_fastboot_boot(struct device *device); -void device_fastboot_flash_reboot(struct device *device); void device_send_break(struct device *device); -void device_list_devices(void); -void device_info(const void *data, size_t dlen); +void device_list_devices(const char *username); +void device_info(const char *username, const void *data, size_t dlen); enum { - DEVICE_KEY_FASTBOOT, + DEVICE_KEY_BOOT, DEVICE_KEY_POWER, }; diff --git a/device_parser.c b/device_parser.c index 5f208bc..f3dfdab 100644 --- a/device_parser.c +++ b/device_parser.c @@ -33,11 +33,11 @@ #include #include "device.h" -#include "alpaca.h" -#include "cdb_assist.h" +#include "ftdi-gpio.h" +#include "local-gpio.h" #include "conmux.h" #include "console.h" -#include "qcomlt_dbg.h" +#include "ppps.h" #define TOKEN_LENGTH 16384 @@ -49,7 +49,7 @@ struct device_parser { static void nextsym(struct device_parser *dp) { if (!yaml_parser_parse(&dp->parser, &dp->event)) { - fprintf(stderr, "device parser: error %d\n", dp->parser.error); + fprintf(stderr, "device parser: error %u\n", dp->parser.error); exit(1); } } @@ -76,7 +76,7 @@ static bool expect(struct device_parser *dp, int type, char *scalar) return true; } - fprintf(stderr, "device parser: expected %d got %d\n", type, dp->event.type); + fprintf(stderr, "device parser: expected %d got %u\n", type, dp->event.type); exit(1); } @@ -89,70 +89,107 @@ static void parse_board(struct device_parser *dp) dev = calloc(1, sizeof(*dev)); while (accept(dp, YAML_SCALAR_EVENT, key)) { + if (!strcmp(key, "users")) { + dev->users = calloc(1, sizeof(*dev->users)); + list_init(dev->users); + + if (accept(dp, YAML_SCALAR_EVENT, value)) + continue; + + expect(dp, YAML_SEQUENCE_START_EVENT, NULL); + + while (accept(dp, YAML_SCALAR_EVENT, key)) { + struct device_user *user = calloc(1, sizeof(*user)); + + user->username = strdup(key); + + list_add(dev->users, &user->node); + } + + expect(dp, YAML_SEQUENCE_END_EVENT, NULL); + + continue; + } + + if (!strcmp(key, "boot-stages")) { + if (accept(dp, YAML_SCALAR_EVENT, value)) + continue; + + expect(dp, YAML_SEQUENCE_START_EVENT, NULL); + + while (accept(dp, YAML_MAPPING_START_EVENT, NULL)) { + while (accept(dp, YAML_SCALAR_EVENT, key)) { + enum boot_stage boot_stage_type = BOOT_NONE; + + expect(dp, YAML_SCALAR_EVENT, value); + + if (!strcmp(key, "pyamlboot")) { + boot_stage_type = BOOT_PYAMLBOOT; + } else if (!strcmp(key, "dfu")) { + boot_stage_type = BOOT_DFU; + } else { + fprintf(stderr, "device parser: Unknown boot stage '%s'\n", key); + } + + dev->boot_stages[dev->boot_num_stages] = boot_stage_type; + dev->boot_stage_options[dev->boot_num_stages] = strdup(value); + ++dev->boot_num_stages; + } + + expect(dp, YAML_MAPPING_END_EVENT, NULL); + } + expect(dp, YAML_SEQUENCE_END_EVENT, NULL); + + continue; + } + expect(dp, YAML_SCALAR_EVENT, value); if (!strcmp(key, "board")) { dev->board = strdup(value); } else if (!strcmp(key, "name")) { dev->name = strdup(value); - } else if (!strcmp(key, "cdba")) { - dev->control_dev = strdup(value); - - dev->open = cdb_assist_open; - dev->close = cdb_assist_close; - dev->power = cdb_assist_power; - dev->print_status = cdb_assist_print_status; - dev->usb = cdb_assist_usb; - dev->key = cdb_assist_key; } else if (!strcmp(key, "conmux")) { dev->control_dev = strdup(value); dev->open = conmux_open; dev->power = conmux_power; dev->write = conmux_write; - } else if (!strcmp(key, "alpaca")) { + } else if (!strcmp(key, "ftdi_gpio")) { dev->control_dev = strdup(value); - dev->open = alpaca_open; - dev->power = alpaca_power; - dev->usb = alpaca_usb; - dev->key = alpaca_key; - } else if (!strcmp(key, "qcomlt_debug_board")) { + dev->open = ftdi_gpio_open; + dev->power = ftdi_gpio_power; + dev->usb = ftdi_gpio_usb; + dev->key = ftdi_gpio_key; + } else if (!strcmp(key, "local_gpio")) { dev->control_dev = strdup(value); - dev->open = qcomlt_dbg_open; - dev->power = qcomlt_dbg_power; - dev->usb = qcomlt_dbg_usb; - dev->key = qcomlt_dbg_key; + dev->open = local_gpio_open; + dev->power = local_gpio_power; + dev->usb = local_gpio_usb; + dev->key = local_gpio_key; } else if (!strcmp(key, "console")) { dev->console_dev = strdup(value); dev->write = console_write; dev->send_break = console_send_break; } else if (!strcmp(key, "voltage")) { dev->voltage = strtoul(value, NULL, 10); - } else if (!strcmp(key, "fastboot")) { - dev->serial = strdup(value); - - if (!dev->boot) - dev->boot = device_fastboot_boot; - } else if (!strcmp(key, "fastboot_set_active")) { - dev->set_active = !strcmp(value, "true"); - } else if (!strcmp(key, "broken_fastboot_boot")) { - if (!strcmp(value, "true")) - dev->boot = device_fastboot_flash_reboot; } else if (!strcmp(key, "description")) { dev->description = strdup(value); - } else if (!strcmp(key, "fastboot_key_timeout")) { - dev->fastboot_key_timeout = strtoul(value, NULL, 10); + } else if (!strcmp(key, "boot_key_timeout")) { + dev->boot_key_timeout = strtoul(value, NULL, 10); } else if (!strcmp(key, "usb_always_on")) { dev->usb_always_on = !strcmp(value, "true"); + } else if (!strcmp(key, "ppps_path")) { + dev->ppps_path = strdup(value); } else { fprintf(stderr, "device parser: unknown key \"%s\"\n", key); exit(1); } } - if (!dev->board || !dev->serial || !(dev->open || dev->console_dev)) { + if (!dev->board || !dev->boot_num_stages || !(dev->open || dev->console_dev)) { fprintf(stderr, "device parser: insufficiently defined device\n"); exit(1); } diff --git a/dfu.c b/dfu.c new file mode 100644 index 0000000..dc72915 --- /dev/null +++ b/dfu.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2016-2018, Linaro Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#define _GNU_SOURCE +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abcd-server.h" +#include "dfu.h" +#include "boot.h" + +struct dfu { + const char *usb_path; + + char *dev_path; + + struct boot_ops *ops; + + struct udev_monitor *mon; + int fd_mon; + + bool disconnected; +}; + +static int handle_dfu_add(struct dfu *dfu, const char *path) +{ + dfu->dev_path = strdup(path); + + if (dfu->ops && dfu->ops->opened) + dfu->ops->opened(); + + return 0; +} + +static int handle_udev_event(int fd, void *data) +{ + struct dfu *dfu = data; + struct udev_device* dev; + const char *dev_path; + const char *action; + const char *vendor; + const char *product; + + /* Monitor has been closed */ + if (!dfu->mon) + return 0; + + dev = udev_monitor_receive_device(dfu->mon); + + action = udev_device_get_action(dev); + dev_path = udev_device_get_devpath(dev); + + if (!action || !dev_path) + goto unref_dev; + + if (!strcmp(action, "add")) { + if (dfu->dev_path) + goto unref_dev; + + vendor = udev_device_get_property_value(dev, "ID_VENDOR"); + product = udev_device_get_property_value(dev, "ID_MODEL"); + if (!vendor || strcmp(vendor, "U-Boot")) + goto unref_dev; + if (!product || strcmp(product, "USB_download_gadget")) + goto unref_dev; + + /* Udev path (/devices/xxx/xxx/xxxx/x-x.x.x.x) should contain the usb_path */ + if (!strstr(dev_path, dfu->usb_path)) + goto unref_dev; + + handle_dfu_add(dfu, dev_path); + } else if (!strcmp(action, "remove")) { + if (!dfu->dev_path || strcmp(dev_path, dfu->dev_path)) + goto unref_dev; + + free(dfu->dev_path); + dfu->dev_path = NULL; + + if (dfu->ops && dfu->ops->disconnect) + dfu->ops->disconnect(); + + dfu->disconnected = true; + } + +unref_dev: + udev_device_unref(dev); + + return 0; +} + +void dfu_close(struct device *device, void *boot_data) +{ + struct dfu *dfu = boot_data; + + device->do_boot = NULL; + + watch_del_readfd(dfu->fd_mon); + udev_monitor_filter_remove(dfu->mon); + udev_monitor_unref(dfu->mon); + + dfu->mon = NULL; + dfu->fd_mon = -1; + + if (dfu->dev_path) + free(dfu->dev_path); + if (!dfu->disconnected && dfu->ops && dfu->ops->disconnect) + dfu->ops->disconnect(); + + free(dfu); +} + +void *dfu_open(struct device *device, struct boot_ops *ops, char *options) +{ + struct dfu *dfu; + struct udev* udev; + struct udev_enumerate* udev_enum; + struct udev_list_entry* first, *item; + + udev = udev_new(); + if (!udev) + err(1, "udev_new() failed"); + + dfu = calloc(1, sizeof(struct dfu)); + if (!dfu) + err(1, "failed to allocate dfu structure"); + + dfu->usb_path = options; + dfu->ops = ops; + + dfu->mon = udev_monitor_new_from_netlink(udev, "udev"); + udev_monitor_filter_add_match_subsystem_devtype(dfu->mon, "usb", NULL); + udev_monitor_enable_receiving(dfu->mon); + + dfu->fd_mon = udev_monitor_get_fd(dfu->mon); + + watch_add_readfd(dfu->fd_mon, handle_udev_event, dfu); + + udev_enum = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(udev_enum, "usb"); + udev_enumerate_add_match_property(udev_enum, "ID_VENDOR", "U-Boot"); + udev_enumerate_add_match_property(udev_enum, "ID_MODEL", "USB_download_gadget"); + + udev_enumerate_scan_devices(udev_enum); + + first = udev_enumerate_get_list_entry(udev_enum); + udev_list_entry_foreach(item, first) { + const char *path; + + path = udev_list_entry_get_name(item); + handle_dfu_add(dfu, path); + } + + udev_enumerate_unref(udev_enum); + + device->do_boot = dfu_boot; + + return dfu; +} + +static int dfu_execute(const char *command) +{ + pid_t pid, pid_ret; + int status; + + pid = fork(); + switch (pid) { + case 0: + /* Do not clobber stdout with program messages or abcd will become confused */ + dup2(2, 1); + exit(system(command)); + case -1: + return -1; + default: + break; + } + + pid_ret = waitpid(pid, &status, 0); + if (pid_ret < 0) + return pid_ret; + + if (WIFEXITED(status)) + return WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + errno = -EINTR; + else + errno = -EIO; + + return -1; +} + +int dfu_boot(void *boot_data, const void *data, size_t len) +{ + struct dfu *dfu = boot_data; + char *cmd = NULL; + char *boot_file; + int ret; + int fd; + + boot_file = strdup("/tmp/dfu-XXXXXX"); + fd = mkstemp(boot_file); + if (fd < 0) { + err(1, "Failed to create tmp file"); + return -1; + } + + write(fd, data, len); + close(fd); + + asprintf(&cmd, "dfu-util -p %s -a 0 -D %s", + dfu->usb_path, boot_file); + + ret = dfu_execute(cmd); + + unlink(boot_file); + + if (ret) + return ret; + + asprintf(&cmd, "dfu-util -p %s -e", dfu->usb_path); + ret = dfu_execute(cmd); + + return ret; +} diff --git a/dfu.h b/dfu.h new file mode 100644 index 0000000..53e8077 --- /dev/null +++ b/dfu.h @@ -0,0 +1,12 @@ +#ifndef __DFU_H__ +#define __DFU_H__ + +#include "device.h" + +struct boot_ops; + +void *dfu_open(struct device *device, struct boot_ops *ops, char *options); +void dfu_close(struct device *device, void *boot_data); +int dfu_boot(void *boot_data, const void *data, size_t len); + +#endif diff --git a/fastboot.c b/fastboot.c index 34e6e77..e09c816 100644 --- a/fastboot.c +++ b/fastboot.c @@ -44,7 +44,7 @@ #include #include -#include "cdba-server.h" +#include "abcd-server.h" #include "fastboot.h" #define MAX_USBFS_BULK_SIZE (16*1024) @@ -121,12 +121,13 @@ static int fastboot_write(struct fastboot *fb, const void *data, size_t len) { struct usbdevfs_bulktransfer bulk = {0}; size_t count = 0; + char *buf = (char *)data; int n; do { bulk.ep = fb->ep_out; bulk.len = MIN(len, MAX_USBFS_BULK_SIZE); - bulk.data = (void*)data; + bulk.data = buf; bulk.timeout = 1000; n = ioctl(fb->fd, USBDEVFS_BULK, &bulk); @@ -135,7 +136,7 @@ static int fastboot_write(struct fastboot *fb, const void *data, size_t len) return -1; } - data += n; + buf += n; len -= n; count += n; } while (len > 0); @@ -156,8 +157,8 @@ static int parse_usb_desc(int usbfd, unsigned *ep_in, unsigned *ep_out) unsigned k; unsigned l; ssize_t n; - void *ptr; - void *end; + char *ptr; + char *end; char desc[1024]; int ret; int id; @@ -166,15 +167,15 @@ static int parse_usb_desc(int usbfd, unsigned *ep_in, unsigned *ep_out) if (n < 0) return n; - ptr = (void*)desc; + ptr = desc; end = ptr + n; - dev = ptr; + dev = (void *)ptr; ptr += dev->bLength; if (ptr >= end || dev->bDescriptorType != USB_DT_DEVICE) return -EINVAL; - cfg = ptr; + cfg = (void *)ptr; ptr += cfg->bLength; if (ptr >= end || cfg->bDescriptorType != USB_DT_CONFIG) return -EINVAL; @@ -184,7 +185,7 @@ static int parse_usb_desc(int usbfd, unsigned *ep_in, unsigned *ep_out) return -EINVAL; do { - ifc = ptr; + ifc = (void *)ptr; if (ifc->bLength < USB_DT_INTERFACE_SIZE) return -EINVAL; @@ -199,7 +200,7 @@ static int parse_usb_desc(int usbfd, unsigned *ep_in, unsigned *ep_out) return -EINVAL; do { - ept = ptr; + ept = (void *)ptr; if (ept->bLength < USB_DT_ENDPOINT_SIZE) return -EINVAL; @@ -218,7 +219,7 @@ static int parse_usb_desc(int usbfd, unsigned *ep_in, unsigned *ep_out) if (ptr >= end) break; - hdr = ptr; + hdr = (void *)ptr; if (hdr->bDescriptorType == USB_DT_SS_ENDPOINT_COMP) ptr += USB_DT_SS_EP_COMP_SIZE; } @@ -411,7 +412,7 @@ int fastboot_download(struct fastboot *fb, const void *data, size_t len) while (len > 0) { xfer = MIN(len, MAX_USBFS_BULK_SIZE); - ret = fastboot_write(fb, data + offset, xfer); + ret = fastboot_write(fb, (const char *)data + offset, xfer); if (ret < 0) goto out; diff --git a/fastboot.h b/fastboot.h deleted file mode 100644 index ffed3d4..0000000 --- a/fastboot.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __FASTBOOT_H__ -#define __FASTBOOT_H__ - -struct fastboot; - -struct fastboot_ops { - void (*opened)(struct fastboot *, void *); - void (*disconnect)(void *); - void (*info)(struct fastboot *, const void *, size_t); -}; - -struct fastboot *fastboot_open(const char *serial, struct fastboot_ops *ops, void *); -int fastboot_getvar(struct fastboot *fb, const char *var, char *buf, size_t len); -int fastboot_download(struct fastboot *fb, const void *data, size_t len); -int fastboot_boot(struct fastboot *fb); -int fastboot_erase(struct fastboot *fb, const char *partition); -int fastboot_set_active(struct fastboot *fb, const char *active); -int fastboot_flash(struct fastboot *fb, const char *partition); -int fastboot_reboot(struct fastboot *fb); - -#endif diff --git a/ftdi-gpio.c b/ftdi-gpio.c new file mode 100644 index 0000000..8aecab6 --- /dev/null +++ b/ftdi-gpio.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2023, Linaro Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abcd-server.h" +#include "ftdi-gpio.h" + +#include + +enum { + GPIO_POWER = 0, // Power input enable + GPIO_FASTBOOT_KEY, // Usually volume key + GPIO_POWER_KEY, // Key to power the device + GPIO_USB_DISCONNECT, // Simulate main USB connection + GPIO_COUNT +}; + +enum { + GPIO_ACTIVE_HIGH = 0, + GPIO_ACTIVE_LOW, +}; + +struct ftdi_gpio { + struct ftdi_context *gpio; + char *ftdi_device; + unsigned int ftdi_interface; + unsigned int gpio_present[GPIO_COUNT]; + unsigned int gpio_offset[GPIO_COUNT]; + unsigned int gpio_polarity[GPIO_COUNT]; + unsigned char gpio_lines; +}; + +static int ftdi_gpio_device_power(struct ftdi_gpio *ftdi_gpio, bool on); +static void ftdi_gpio_device_usb(struct ftdi_gpio *ftdi_gpio, bool on); + +/* + * fdio_gpio parameter: ;[[;...]] + * - libftdi description: "s:0xVEND:0xPROD:SERIAL" + * - interface: A, B, C or D (default A) + * - gpios: type,id,polarity + * - type: POWER, FASTBOOT_KEY, POWER_KEY or USB_DISCONNECT + * - id: 0, 1, 2, 3, 4, 5, 6 or 7 + * - polarity: ACTIVE_HIGH or ACTIVE_LOW + * + * Example: s:0xVEND:0xPROD:SERIAL;D;POWER,0,ACTIVE_LOW;FASTBOOT_KEY,1,ACTIVE_HIGH;POWER_KEY,2,ACTIVE_HIGH;USB_DISCONNECT,3,ACTIVE_LOW + */ + +static void ftdi_gpio_parse_config(struct ftdi_gpio *ftdi_gpio, char *control_dev) +{ + char *c, *interface; + size_t device_len; + + // First libftdi description + c = strchr(control_dev, ';'); + if (!c) + device_len = strlen(control_dev); + else + device_len = c - control_dev; + + ftdi_gpio->ftdi_device = strndup(control_dev, device_len); + + if (!c) + return; + + // Interface + interface = c + 1; + if (*interface != 'A' && + *interface != 'B' && + *interface != 'C' && + *interface != 'D') { + errx(1, "Invalid interface '%c'", *interface); + } + ftdi_gpio->ftdi_interface = *interface - 'A'; + + c = strchr(interface, ';'); + + // GPIOs + while(c) { + char *name, *off, *pol; + unsigned gpio_type; + unsigned gpio_offset; + unsigned gpio_polarity; + + name = c + 1; + off = strchr(name, ','); + if (!off) + errx(1, "GPIOs config invalid"); + off += 1; + pol = strchr(off, ','); + if (!pol) + errx(1, "GPIOs config invalid"); + pol += 1; + + c = strchr(pol, ';'); + + if (strncmp("POWER", name, off - name - 1) == 0) + gpio_type = GPIO_POWER; + else if (strncmp("FASTBOOT_KEY", name, off - name - 1) == 0) + gpio_type = GPIO_FASTBOOT_KEY; + else if (strncmp("POWER_KEY", name, off - name - 1) == 0) + gpio_type = GPIO_POWER_KEY; + else if (strncmp("USB_DISCONNECT", name, off - name - 1) == 0) + gpio_type = GPIO_USB_DISCONNECT; + else + errx(1, "GPIOs type invalid: '%s'", name); + + gpio_offset = strtoul(off, NULL, 0); + if (gpio_offset > 7) + errx(1, "GPIOs offset invalid: '%u'", gpio_offset); + + if (strncmp("ACTIVE_HIGH", pol, c - pol - 1) == 0) + gpio_polarity = GPIO_ACTIVE_HIGH; + else if (strncmp("ACTIVE_LOW", pol, c - pol - 1) == 0) + gpio_polarity = GPIO_ACTIVE_LOW; + else + errx(1, "GPIOs polarity invalid: '%s'", pol); + + ftdi_gpio->gpio_present[gpio_type] = 1; + ftdi_gpio->gpio_offset[gpio_type] = gpio_offset; + ftdi_gpio->gpio_polarity[gpio_type] = gpio_polarity; + } +} + +void *ftdi_gpio_open(struct device *dev) +{ + struct ftdi_gpio *ftdi_gpio; + int ret; + + ftdi_gpio = calloc(1, sizeof(*ftdi_gpio)); + + ftdi_gpio_parse_config(ftdi_gpio, dev->control_dev); + + if ((ftdi_gpio->gpio = ftdi_new()) == 0) + errx(1, "failed to allocate ftdi gpio struct"); + + ftdi_set_interface(ftdi_gpio->gpio, INTERFACE_A + ftdi_gpio->ftdi_interface); + + ret = ftdi_usb_open_string(ftdi_gpio->gpio, ftdi_gpio->ftdi_device); + if (ret < 0) + errx(1, "failed to open ftdi gpio device '%s' (%d)", ftdi_gpio->ftdi_device, ret); + + ftdi_set_bitmode(ftdi_gpio->gpio, 0xFF, BITMODE_BITBANG); + + if (ftdi_gpio->gpio_present[GPIO_POWER_KEY]) + dev->has_power_key = true; + + ftdi_gpio_device_power(ftdi_gpio, 0); + + if (dev->usb_always_on) + ftdi_gpio_device_usb(ftdi_gpio, 1); + else + ftdi_gpio_device_usb(ftdi_gpio, 0); + + usleep(500000); + + return ftdi_gpio; +} + +static int ftdi_gpio_toggle_io(struct ftdi_gpio *ftdi_gpio, unsigned int gpio, bool on) +{ + unsigned int bit; + + if (!ftdi_gpio->gpio_present[gpio]) + return -EINVAL; + + bit = ftdi_gpio->gpio_offset[gpio]; + + if (ftdi_gpio->gpio_polarity[gpio]) + on = !on; + + if (on) + ftdi_gpio->gpio_lines |= (1 << bit); + else + ftdi_gpio->gpio_lines &= ~(1 << bit); + + return ftdi_write_data(ftdi_gpio->gpio, &ftdi_gpio->gpio_lines, 1); +} + +static int ftdi_gpio_device_power(struct ftdi_gpio *ftdi_gpio, bool on) +{ + return ftdi_gpio_toggle_io(ftdi_gpio, GPIO_POWER, on); +} + +static void ftdi_gpio_device_usb(struct ftdi_gpio *ftdi_gpio, bool on) +{ + ftdi_gpio_toggle_io(ftdi_gpio, GPIO_USB_DISCONNECT, on); +} + +int ftdi_gpio_power(struct device *dev, bool on) +{ + struct ftdi_gpio *ftdi_gpio = dev->cdb; + + return ftdi_gpio_device_power(ftdi_gpio, on); +} + +void ftdi_gpio_usb(struct device *dev, bool on) +{ + struct ftdi_gpio *ftdi_gpio = dev->cdb; + + ftdi_gpio_device_usb(ftdi_gpio, on); +} + +void ftdi_gpio_key(struct device *dev, int key, bool asserted) +{ + struct ftdi_gpio *ftdi_gpio = dev->cdb; + + switch (key) { + case DEVICE_KEY_BOOT: + ftdi_gpio_toggle_io(ftdi_gpio, GPIO_FASTBOOT_KEY, asserted); + break; + case DEVICE_KEY_POWER: + ftdi_gpio_toggle_io(ftdi_gpio, GPIO_POWER_KEY, asserted); + break; + } +} diff --git a/ftdi-gpio.h b/ftdi-gpio.h new file mode 100644 index 0000000..a705d28 --- /dev/null +++ b/ftdi-gpio.h @@ -0,0 +1,13 @@ +#ifndef __FTDI_GPIO_H__ +#define __FTDI_GPIO_H__ + +#include "device.h" + +struct ftdi_gpio; + +void *ftdi_gpio_open(struct device *dev); +int ftdi_gpio_power(struct device *dev, bool on); +void ftdi_gpio_usb(struct device *dev, bool on); +void ftdi_gpio_key(struct device *dev, int key, bool on); + +#endif diff --git a/list.h b/list.h index 263316c..14b9095 100644 --- a/list.h +++ b/list.h @@ -35,8 +35,8 @@ #include #define container_of(ptr, type, member) ({ \ - const typeof(((type *)0)->member)*__mptr = (ptr); \ - (type *)((char *)__mptr - offsetof(type, member)); \ + typeof(((type *)0)->member)*__mptr = (ptr); \ + (type *)(void *)((char *)__mptr - offsetof(type, member)); \ }) struct list_head { diff --git a/local-gpio.c b/local-gpio.c new file mode 100644 index 0000000..845332a --- /dev/null +++ b/local-gpio.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2023, Linaro Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abcd-server.h" +#include "local-gpio.h" + +#include + +enum { + GPIO_POWER = 0, // Power input enable + GPIO_FASTBOOT_KEY, // Usually volume key + GPIO_POWER_KEY, // Key to power the device + GPIO_USB_DISCONNECT, // Simulate main USB connection + GPIO_COUNT +}; + +enum { + GPIO_ACTIVE_HIGH = 0, + GPIO_ACTIVE_LOW, +}; + +struct local_gpio { + char * gpiochip_desc; + struct gpiod_chip *chip; + unsigned int gpio_present[GPIO_COUNT]; + unsigned int gpio_offset[GPIO_COUNT]; + unsigned int gpio_polarity[GPIO_COUNT]; + struct gpiod_line *gpio_line[GPIO_COUNT]; +}; + +static int local_gpio_device_power(struct local_gpio *local_gpio, bool on); +static void local_gpio_device_usb(struct local_gpio *local_gpio, bool on); + +/* + * fdio_gpio parameter: ;[;...] + * - gpiod chip description: "gpiochip0" + * - gpios: type,id,polarity + * - type: POWER, FASTBOOT_KEY, POWER_KEY or USB_DISCONNECT + * - offset: line offset in chip + * - polarity: ACTIVE_HIGH or ACTIVE_LOW + * + * Example: gpiochip0;POWER,0,ACTIVE_LOW;FASTBOOT_KEY,1,ACTIVE_HIGH;POWER_KEY,2,ACTIVE_HIGH;USB_DISCONNECT,3,ACTIVE_LOW + */ + +static void local_gpio_parse_config(struct local_gpio *local_gpio, char *control_dev) +{ + char *c; + size_t device_len; + + // First liblocal description + c = strchr(control_dev, ';'); + if (!c) + device_len = strlen(control_dev); + else + device_len = c - control_dev; + + local_gpio->gpiochip_desc = strndup(control_dev, device_len); + + if (!c) + return; + + // GPIOs + while(c) { + char *name, *off, *pol; + unsigned gpio_type; + unsigned gpio_offset; + unsigned gpio_polarity; + + name = c + 1; + off = strchr(name, ','); + if (!off) + errx(1, "GPIOs config invalid"); + off += 1; + pol = strchr(off, ','); + if (!pol) + errx(1, "GPIOs config invalid"); + pol += 1; + + c = strchr(pol, ';'); + + if (strncmp("POWER", name, off - name - 1) == 0) + gpio_type = GPIO_POWER; + else if (strncmp("FASTBOOT_KEY", name, off - name - 1) == 0) + gpio_type = GPIO_FASTBOOT_KEY; + else if (strncmp("POWER_KEY", name, off - name - 1) == 0) + gpio_type = GPIO_POWER_KEY; + else if (strncmp("USB_DISCONNECT", name, off - name - 1) == 0) + gpio_type = GPIO_USB_DISCONNECT; + else + errx(1, "GPIOs type invalid: '%s'", name); + + gpio_offset = strtoul(off, NULL, 0); + + if (strncmp("ACTIVE_HIGH", pol, c - pol - 1) == 0) + gpio_polarity = GPIO_ACTIVE_HIGH; + else if (strncmp("ACTIVE_LOW", pol, c - pol - 1) == 0) + gpio_polarity = GPIO_ACTIVE_LOW; + else + errx(1, "GPIOs polarity invalid: '%s'", pol); + + local_gpio->gpio_present[gpio_type] = 1; + local_gpio->gpio_offset[gpio_type] = gpio_offset; + local_gpio->gpio_polarity[gpio_type] = gpio_polarity; + } +} + +void *local_gpio_open(struct device *dev) +{ + struct local_gpio *local_gpio; + int i; + + local_gpio = calloc(1, sizeof(*local_gpio)); + + local_gpio_parse_config(local_gpio, dev->control_dev); + + local_gpio->chip = gpiod_chip_open_lookup(local_gpio->gpiochip_desc); + if (!local_gpio->chip) { + err(1, "Unable to open gpiochip '%s'", local_gpio->gpiochip_desc); + return NULL; + } + + for (i = 0; i < GPIO_COUNT; ++i) { + struct gpiod_line_request_config cfg; + + if (!local_gpio->gpio_present[i]) + continue; + + cfg.consumer = "ABCD"; + cfg.request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; + cfg.flags = 0; + + if (local_gpio->gpio_polarity[i]) + cfg.flags = GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; + + local_gpio->gpio_line[i] = gpiod_chip_get_line(local_gpio->chip, + local_gpio->gpio_offset[i]); + + if (!local_gpio->gpio_line[i]) { + err(1, "Unable to find gpio %d offset %u", i, local_gpio->gpio_offset[i]); + return NULL; + } + + if (gpiod_line_request(local_gpio->gpio_line[i], &cfg, 0)) { + err(1, "Unable to request gpio %d offset %u", i, local_gpio->gpio_offset[i]); + return NULL; + } + } + + if (local_gpio->gpio_present[GPIO_POWER_KEY]) + dev->has_power_key = true; + + local_gpio_device_power(local_gpio, 0); + + if (dev->usb_always_on) + local_gpio_device_usb(local_gpio, 1); + else + local_gpio_device_usb(local_gpio, 0); + + usleep(500000); + + return local_gpio; +} + +static int local_gpio_toggle_io(struct local_gpio *local_gpio, unsigned int gpio, bool on) +{ + if (!local_gpio->gpio_present[gpio]) + return -EINVAL; + + if (gpiod_line_set_value(local_gpio->gpio_line[gpio], on)) + warnx("%s:%d unable to set value", __func__, __LINE__); + + return 0; +} + +static int local_gpio_device_power(struct local_gpio *local_gpio, bool on) +{ + return local_gpio_toggle_io(local_gpio, GPIO_POWER, on); +} + +static void local_gpio_device_usb(struct local_gpio *local_gpio, bool on) +{ + local_gpio_toggle_io(local_gpio, GPIO_USB_DISCONNECT, on); +} + +int local_gpio_power(struct device *dev, bool on) +{ + struct local_gpio *local_gpio = dev->cdb; + + return local_gpio_device_power(local_gpio, on); +} + +void local_gpio_usb(struct device *dev, bool on) +{ + struct local_gpio *local_gpio = dev->cdb; + + local_gpio_device_usb(local_gpio, on); +} + +void local_gpio_key(struct device *dev, int key, bool asserted) +{ + struct local_gpio *local_gpio = dev->cdb; + + switch (key) { + case DEVICE_KEY_BOOT: + local_gpio_toggle_io(local_gpio, GPIO_FASTBOOT_KEY, asserted); + break; + case DEVICE_KEY_POWER: + local_gpio_toggle_io(local_gpio, GPIO_POWER_KEY, asserted); + break; + } +} diff --git a/local-gpio.h b/local-gpio.h new file mode 100644 index 0000000..e486cd5 --- /dev/null +++ b/local-gpio.h @@ -0,0 +1,13 @@ +#ifndef __LOCAL_GPIO_H__ +#define __LOCAL_GPIO_H__ + +#include "device.h" + +struct local_gpio; + +void *local_gpio_open(struct device *dev); +int local_gpio_power(struct device *dev, bool on); +void local_gpio_usb(struct device *dev, bool on); +void local_gpio_key(struct device *dev, int key, bool on); + +#endif diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..6290908 --- /dev/null +++ b/meson.build @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0 + +project('abcd', + 'c', + license : [ 'BSD-3-Clause'], + meson_version : '>= 0.43.0', # for compiler.get_supported_arguments() + default_options: [ + 'warning_level=2', # sets -Wextra + 'buildtype=release', + ]) + +# Set advanced compiler flags +compiler = meson.get_compiler('c') + +compiler_cflags = ['-Wno-unused-parameter', + '-Wno-unused-result', + '-Wno-missing-field-initializers', + '-Wno-sign-compare', + '-Wundef', + '-Wnull-dereference', + '-Wdouble-promotion', + '-Wshadow', + '-Wpointer-arith', + '-Wwrite-strings', + '-Wstrict-overflow=4'] + +# TODO add clang specific options +if compiler.get_id() == 'gcc' + compiler_cflags += ['-Werror', # Only set it on GCC + '-Wformat-signedness', + '-Wduplicated-cond', + '-Wduplicated-branches', + '-Wvla-larger-than=1', + '-Walloc-zero', + '-Wunsafe-loop-optimizations', + '-Wcast-align', + '-Wlogical-op', + '-Wjump-misses-init'] +endif + +add_global_arguments(compiler.get_supported_arguments(compiler_cflags), + language: 'c') + +client_srcs = ['abcd.c', + 'circ_buf.c'] +executable('abcd', + client_srcs, + install : true) + +ftdi_dep = dependency('libftdi1', required: false) +if not ftdi_dep.found() + ftdi_dep = dependency('libftdi') +endif + +server_deps = [dependency('libudev'), + dependency('yaml-0.1'), + dependency('libgpiod'), + ftdi_dep] +server_srcs = ['abcd-server.c', + 'circ_buf.c', + 'conmux.c', + 'device.c', + 'device_parser.c', + 'pyamlboot.c', + 'dfu.c', + 'ftdi-gpio.c', + 'local-gpio.c', + 'console.c', + 'ppps.c'] +executable('abcd-server', + server_srcs, + dependencies : server_deps, + install : true) diff --git a/qcomlt_dbg.c b/ppps.c similarity index 58% rename from qcomlt_dbg.c rename to ppps.c index 92d04a8..494a805 100644 --- a/qcomlt_dbg.c +++ b/ppps.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Linaro Ltd. + * Copyright (c) 2023, Linaro Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,73 +29,49 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define _GNU_SOURCE /* for asprintf */ +#include #include +#include #include -#include #include #include #include -#include #include -#include #include -#include +#include #include +#include -#include "cdba-server.h" -#include "qcomlt_dbg.h" - -struct qcomlt_dbg { - int fd; - struct termios orig_tios; -}; - -void *qcomlt_dbg_open(struct device *dev) -{ - struct qcomlt_dbg *dbg; - - dev->has_power_key = true; - - 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); - - // fprintf(stderr, "qcomlt_dbg_open()\n"); - write(dbg->fd, "brpu", 4); +#include "device.h" - return dbg; -} +#define PPPS_BASE_PATH "/sys/bus/usb/devices" +#define PPPS_BASE_PATH_LEN strlen(PPPS_BASE_PATH) -int qcomlt_dbg_power(struct device *dev, bool on) +void ppps_power(struct device *dev, bool on) { - struct qcomlt_dbg *dbg = dev->cdb; + static char *path = NULL; + int rc, fd; - // fprintf(stderr, "qcomlt_dbg_power(%d)\n", on); - return write(dbg->fd, &("pP"[on]), 1); -} - -void qcomlt_dbg_usb(struct device *dev, bool on) -{ - struct qcomlt_dbg *dbg = dev->cdb; + /* Only need to figure out the whole string once */ + if (!path) { + /* ppps_path should be like "2-2:1.0/2-2-port2" */ + asprintf(&path, "%s/%s/disable", PPPS_BASE_PATH, dev->ppps_path); + } - // fprintf(stderr, "qcomlt_dbg_usb(%d)\n", on); - write(dbg->fd, &("uU"[on]), 1); -} + // fprintf(stderr, "ppps_power: %-3s %s\n", on ? "on" : "off", path); -void qcomlt_dbg_key(struct device *dev, int key, bool asserted) -{ - struct qcomlt_dbg *dbg = dev->cdb; + fd = open(path, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + if (errno != ENOENT) + fprintf(stderr, "Maybe missing permissions (see https://git.io/JIB2Z)\n"); + return; + } - // fprintf(stderr, "qcomlt_dbg_key(%d, %d)\n", key, asserted); + rc = write(fd, on ? "0" : "1", 1); + if (rc < 0) + fprintf(stderr, "failed to write to %s: %s\n", path, strerror(errno)); - switch (key) { - case DEVICE_KEY_FASTBOOT: - write(dbg->fd, &("rR"[asserted]), 1); - break; - case DEVICE_KEY_POWER: - write(dbg->fd, &("bB"[asserted]), 1); - break; - } + close(fd); } diff --git a/ppps.h b/ppps.h new file mode 100644 index 0000000..e6cba63 --- /dev/null +++ b/ppps.h @@ -0,0 +1,6 @@ +#ifndef __PPPS_H__ +#define __PPPS_H__ + +void ppps_power(struct device *dev, bool on); + +#endif diff --git a/pyamlboot.c b/pyamlboot.c new file mode 100644 index 0000000..5c9b0d1 --- /dev/null +++ b/pyamlboot.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2016-2018, Linaro Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#define _GNU_SOURCE +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abcd-server.h" +#include "pyamlboot.h" +#include "boot.h" + +struct pyamlboot { + const char *cmd; + + char *dev_path; + + struct boot_ops *ops; + + struct udev_monitor *mon; + int fd_mon; + + bool disconnected; +}; + +static int handle_pyamlboot_add(struct pyamlboot *pyamlboot, const char *path) +{ + pyamlboot->dev_path = strdup(path); + + if (pyamlboot->ops && pyamlboot->ops->opened) + pyamlboot->ops->opened(); + + return 0; +} + +static int handle_udev_event(int fd, void *data) +{ + struct pyamlboot *pyamlboot = data; + struct udev_device* dev; + const char *dev_path; + const char *action; + const char *vendor_id; + const char *product_id; + + /* Monitor has been closed */ + if (!pyamlboot->mon) + return 0; + + dev = udev_monitor_receive_device(pyamlboot->mon); + + action = udev_device_get_action(dev); + dev_path = udev_device_get_devpath(dev); + + if (!action || !dev_path) + goto unref_dev; + + if (!strcmp(action, "add")) { + if (pyamlboot->dev_path) + goto unref_dev; + + vendor_id = udev_device_get_property_value(dev, "ID_VENDOR_ID"); + product_id = udev_device_get_property_value(dev, "ID_MODEL_ID"); + if (!vendor_id || strcmp(vendor_id, "1b8e")) + goto unref_dev; + if (!product_id || strcmp(product_id, "c003")) + goto unref_dev; + + handle_pyamlboot_add(pyamlboot, dev_path); + } else if (!strcmp(action, "remove")) { + if (!pyamlboot->dev_path || strcmp(dev_path, pyamlboot->dev_path)) + goto unref_dev; + + free(pyamlboot->dev_path); + pyamlboot->dev_path = NULL; + + if (pyamlboot->ops && pyamlboot->ops->disconnect) + pyamlboot->ops->disconnect(); + + pyamlboot->disconnected = true; + } + +unref_dev: + udev_device_unref(dev); + + return 0; +} + +void pyamlboot_close(struct device *device, void *boot_data) +{ + struct pyamlboot *pyamlboot = boot_data; + + device->do_boot = NULL; + + watch_del_readfd(pyamlboot->fd_mon); + udev_monitor_filter_remove(pyamlboot->mon); + udev_monitor_unref(pyamlboot->mon); + + pyamlboot->mon = NULL; + pyamlboot->fd_mon = -1; + + if (pyamlboot->dev_path) + free(pyamlboot->dev_path); + + if (!pyamlboot->disconnected && pyamlboot->ops && pyamlboot->ops->disconnect) + pyamlboot->ops->disconnect(); + + free(pyamlboot); +} + +void *pyamlboot_open(struct device *device, struct boot_ops *ops, char *options) +{ + struct pyamlboot *pyamlboot; + struct udev* udev; + struct udev_enumerate* udev_enum; + struct udev_list_entry* first, *item; + + udev = udev_new(); + if (!udev) + err(1, "udev_new() failed"); + + pyamlboot = calloc(1, sizeof(struct pyamlboot)); + if (!pyamlboot) + err(1, "failed to allocate pyamlboot structure"); + + pyamlboot->cmd = options; + pyamlboot->ops = ops; + + pyamlboot->mon = udev_monitor_new_from_netlink(udev, "udev"); + udev_monitor_filter_add_match_subsystem_devtype(pyamlboot->mon, "usb", NULL); + udev_monitor_enable_receiving(pyamlboot->mon); + + pyamlboot->fd_mon = udev_monitor_get_fd(pyamlboot->mon); + + watch_add_readfd(pyamlboot->fd_mon, handle_udev_event, pyamlboot); + + udev_enum = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(udev_enum, "usb"); + udev_enumerate_add_match_property(udev_enum, "ID_VENDOR_ID", "1b8e"); + udev_enumerate_add_match_property(udev_enum, "ID_MODEL_ID", "c003"); + + udev_enumerate_scan_devices(udev_enum); + + first = udev_enumerate_get_list_entry(udev_enum); + udev_list_entry_foreach(item, first) { + const char *path; + + path = udev_list_entry_get_name(item); + handle_pyamlboot_add(pyamlboot, path); + } + + udev_enumerate_unref(udev_enum); + + device->do_boot = pyamlboot_boot; + + return pyamlboot; +} + +static int pyamlboot_execute(const char *command) +{ + pid_t pid, pid_ret; + int status; + + pid = fork(); + switch (pid) { + case 0: + /* Do not clobber stdout with program messages or abcd will become confused */ + dup2(2, 1); + exit(system(command)); + case -1: + return -1; + default: + break; + } + + pid_ret = waitpid(pid, &status, 0); + if (pid_ret < 0) + return pid_ret; + + if (WIFEXITED(status)) + return WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + errno = -EINTR; + else + errno = -EIO; + + return -1; +} + +int pyamlboot_boot(void *boot_data, const void *data, size_t len) +{ + struct pyamlboot *pyamlboot = boot_data; + char *cmd = NULL; + int ret; + + if (strstr(pyamlboot->cmd, "boot-g12")) { + char *boot_file; + int fd; + + boot_file = strdup("/tmp/pyamlboot-XXXXXX"); + fd = mkstemp(boot_file); + if (fd < 0) { + err(1, "Failed to create tmp file"); + return -1; + } + + write(fd, data, len); + close(fd); + + asprintf(&cmd, pyamlboot->cmd, boot_file); + ret = pyamlboot_execute(cmd); + + unlink(boot_file); + } else { + char *boot_dir, *boot_file_tpl, *boot_file_bl2; + int fd; + + if (len < 49152) { + err(1, "Invalid file length for pre-g12 boot"); + return -1; + } + + boot_dir = strdup("/tmp/pyamlboot-XXXXXX"); + if (!mkdtemp(boot_dir)) { + err(1, "Failed to create tmp dir"); + return -1; + } + + asprintf(&boot_file_bl2, "%s/u-boot.bin.usb.bl2", boot_dir); + asprintf(&boot_file_tpl, "%s/u-boot.bin.usb.tpl", boot_dir); + + fd = open(boot_file_bl2, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd < 0) { + err(1, "Failed to create bl2 tmp file"); + return -1; + } + write(fd, data, 49152); + close(fd); + + fd = open(boot_file_tpl, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd < 0) { + err(1, "Failed to create tpl tmp file"); + return -1; + } + write(fd, &((const char *)data)[49152], len - 49152); + close(fd); + + asprintf(&cmd, pyamlboot->cmd, boot_dir); + ret = pyamlboot_execute(cmd); + + unlink(boot_file_bl2); + unlink(boot_file_tpl); + rmdir(boot_dir); + } + + return ret; +} diff --git a/pyamlboot.h b/pyamlboot.h new file mode 100644 index 0000000..38879e5 --- /dev/null +++ b/pyamlboot.h @@ -0,0 +1,12 @@ +#ifndef __PYAMLBOOT_H__ +#define __PYAMLBOOT_H__ + +#include "device.h" + +struct boot_ops; + +void *pyamlboot_open(struct device *device, struct boot_ops *ops, char *options); +void pyamlboot_close(struct device *device, void *boot_data); +int pyamlboot_boot(void *boot_data, const void *data, size_t len); + +#endif diff --git a/qcomlt_dbg.h b/qcomlt_dbg.h deleted file mode 100644 index 1fc976e..0000000 --- a/qcomlt_dbg.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __QCOMLT_DBG_H__ -#define __QCOMLT_DBG_H__ - -#include "device.h" - -void *qcomlt_dbg_open(struct device *dev); -int qcomlt_dbg_power(struct device *dev, bool on); -void qcomlt_dbg_usb(struct device *dev, bool on); -void qcomlt_dbg_key(struct device *dev, int key, bool asserted); - -#endif diff --git a/shell/README b/shell/README index 4ac2fa5..44dd93e 100644 --- a/shell/README +++ b/shell/README @@ -1,8 +1,8 @@ -Create an account, such as "cdba" and run ./setup.sh as this user. +Create an account, such as "abcd" and run ./setup.sh as this user. Give the user a password, or setup authorized_keys -Then from the other machine: git clone cdba@host +Then from the other machine: git clone abcd@host -Add a file "admins" listing the names of the admins, add your cdba config file -as "cdba" and create a directory "keydir" populated with id_rsa.pub files, +Add a file "admins" listing the names of the admins, add your abcd config file +as "abcd" and create a directory "keydir" populated with id_rsa.pub files, named .pub - where is referenced against the admins list. diff --git a/shell/cdba-shell b/shell/abcd-shell old mode 100644 new mode 100755 similarity index 69% rename from shell/cdba-shell rename to shell/abcd-shell index b23c197..076a70b --- a/shell/cdba-shell +++ b/shell/abcd-shell @@ -1,10 +1,10 @@ #!/bin/sh -user=$1 +export ABCD_USER="$1" cmd=${SSH_ORIGINAL_COMMAND%% *} if [ "$cmd" = "git-upload-pack" -o "$cmd" = "git-receive-pack" ]; then - if grep -Fxq $user $HOME/admins ; then + if grep -Fxq $ABCD_USER $HOME/admins ; then exec sh -c "$SSH_ORIGINAL_COMMAND" fi @@ -12,4 +12,4 @@ if [ "$cmd" = "git-upload-pack" -o "$cmd" = "git-receive-pack" ]; then exit 1 fi -exec cdba-server +exec abcd-server diff --git a/shell/post-receive b/shell/post-receive index 1c0c81a..5392e44 100644 --- a/shell/post-receive +++ b/shell/post-receive @@ -17,14 +17,14 @@ git cat-file -p main:keydir | while read LINE; do USER=$(basename $NAME .pub) PUBKEY=$(git cat-file blob main:keydir/$NAME) - echo "command=\"$HOME/bin/cdba-shell $USER\" $PUBKEY" >> $AUTHORIZED_TMP + echo "command=\"$HOME/bin/abcd-shell $USER\" $PUBKEY" >> $AUTHORIZED_TMP done mv $AUTHORIZED_TMP $AUTHORIZED_KEYS # -# Install .cdba +# Install .abcd # -git cat-file blob main:cdba > $HOME/.cdba +git cat-file blob main:abcd > $HOME/.abcd # # Install admins list diff --git a/shell/setup.sh b/shell/setup.sh index 76f4d5a..0d80eab 100755 --- a/shell/setup.sh +++ b/shell/setup.sh @@ -1,8 +1,8 @@ #!/bin/sh -e -git init --bare $HOME/cdba-admin -install -m 755 post-receive $HOME/cdba-admin/hooks/ -install -m 755 update $HOME/cdba-admin/hooks/ +git init --bare $HOME/abcd-admin +install -m 755 post-receive $HOME/abcd-admin/hooks/ +install -m 755 update $HOME/abcd-admin/hooks/ mkdir -p $HOME/bin -install -m 755 cdba-shell $HOME/bin/ +install -m 755 abcd-shell $HOME/bin/