summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------3rd-party/pico-sdk0
-rw-r--r--README.md5
-rw-r--r--cmd/sbc_harness/CMakeLists.txt5
-rw-r--r--cmd/sbc_harness/config/config.h2
-rw-r--r--cmd/sbc_harness/fs_harness_flash_bin.c310
-rw-r--r--cmd/sbc_harness/fs_harness_flash_bin.h30
-rw-r--r--cmd/sbc_harness/fs_harness_uptime_txt.c156
-rw-r--r--cmd/sbc_harness/fs_harness_uptime_txt.h19
-rw-r--r--cmd/sbc_harness/main.c20
-rw-r--r--cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md12
-rw-r--r--cmd/sbc_harness/static/Documentation/harness_flash_bin.txt13
-rw-r--r--cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt17
-rw-r--r--gdb-helpers/rp2040.py4
-rw-r--r--lib9p/CMakeLists.txt2
-rw-r--r--lib9p/include/lib9p/srv.h8
-rw-r--r--lib9p/srv.c17
-rw-r--r--lib9p/tests/test_server/main.c8
-rw-r--r--lib9p_util/CMakeLists.txt2
-rw-r--r--libcr/CMakeLists.txt2
-rw-r--r--libcr/coroutine.c6
-rw-r--r--libcr_ipc/CMakeLists.txt2
-rw-r--r--libcr_ipc/include/libcr_ipc/chan.h10
-rw-r--r--libcr_ipc/include/libcr_ipc/rpc.h22
-rw-r--r--libcr_ipc/include/libcr_ipc/select.h3
-rw-r--r--libcr_ipc/include/libcr_ipc/sema.h4
-rw-r--r--libdhcp/CMakeLists.txt2
-rw-r--r--libfmt/CMakeLists.txt2
-rw-r--r--libfmt/include/libfmt/fmt.h2
-rw-r--r--libfmt/libmisc.c2
-rw-r--r--libhw_cr/CMakeLists.txt4
-rw-r--r--libhw_cr/host_alarmclock.c2
-rw-r--r--libhw_cr/host_include/libhw/host_alarmclock.h6
-rw-r--r--libhw_cr/host_include/libhw/host_net.h18
-rw-r--r--libhw_cr/host_net.c24
-rw-r--r--libhw_cr/rp2040_dma.h3
-rw-r--r--libhw_cr/rp2040_hwspi.c47
-rw-r--r--libhw_cr/rp2040_include/libhw/rp2040_hwspi.h8
-rw-r--r--libhw_cr/rp2040_include/libhw/w5500.h30
-rw-r--r--libhw_cr/w5500.c26
-rw-r--r--libhw_generic/CMakeLists.txt2
-rw-r--r--libhw_generic/include/libhw/generic/alarmclock.h6
-rw-r--r--libhw_generic/include/libhw/generic/io.h12
-rw-r--r--libhw_generic/include/libhw/generic/net.h8
-rw-r--r--libhw_generic/include/libhw/generic/spi.h2
-rw-r--r--libmisc/CMakeLists.txt2
-rw-r--r--libmisc/include/libmisc/_intercept.h4
-rw-r--r--libmisc/include/libmisc/private.h4
-rw-r--r--libmisc/include/libmisc/rand.h4
-rw-r--r--libmisc/tests/test_macro.c1
-rw-r--r--libmisc/tests/test_private.c8
-rw-r--r--libobj/CMakeLists.txt2
-rw-r--r--libobj/include/libobj/obj.h8
-rw-r--r--libobj/tests/test_nest.c18
-rw-r--r--libobj/tests/test_obj.c6
-rw-r--r--libusb/CMakeLists.txt2
-rw-r--r--notes.md21
56 files changed, 798 insertions, 167 deletions
diff --git a/3rd-party/pico-sdk b/3rd-party/pico-sdk
-Subproject 1c00d64a4e0fdf948494c9aaf4d257b5739796a
+Subproject c8a16c00453e4db4b771d7f1281391057c7477d
diff --git a/README.md b/README.md
index de909d3..a1eebb9 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,10 @@ There are several ways of putting this firmware file onto the harness:
mount the device and copy the `.uf2` file to the device. It will
automatically reboot into the new firmware image.
2. debug port: Using OpenOCD (see `HACKING.md`), run the OpenOCD command
- `program /path/to/sbc_harness.elf reset`.
+ `program /path/to/sbc_harness.elf reset` (TODO: I don't really
+ understand what OpenOCD is doing that it wants the `.elf` instead
+ of the `.bin`)
+ .
If OpenOCD is not already running:
```
diff --git a/cmd/sbc_harness/CMakeLists.txt b/cmd/sbc_harness/CMakeLists.txt
index 64bf356..678af07 100644
--- a/cmd/sbc_harness/CMakeLists.txt
+++ b/cmd/sbc_harness/CMakeLists.txt
@@ -11,6 +11,9 @@ add_library(sbc_harness_objs OBJECT
main.c
usb_keyboard.c
tusb_log.c
+
+ fs_harness_flash_bin.c
+ fs_harness_uptime_txt.c
)
target_include_directories(sbc_harness_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config)
target_include_directories(sbc_harness_objs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
@@ -20,6 +23,7 @@ target_link_libraries(sbc_harness_objs
pico_stdio_uart
hardware_flash
+ hardware_watchdog
libmisc
libfmt
@@ -74,6 +78,7 @@ target_embed_sources(sbc_harness_objs sbc_harness static.h
static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt
static/Documentation/harness_rom_bin.txt
static/Documentation/harness_flash_bin.txt
+ static/Documentation/harness_uptime_txt.txt
)
endif()
diff --git a/cmd/sbc_harness/config/config.h b/cmd/sbc_harness/config/config.h
index da3edad..5e7bc06 100644
--- a/cmd/sbc_harness/config/config.h
+++ b/cmd/sbc_harness/config/config.h
@@ -9,6 +9,8 @@
#include <stddef.h> /* for size_t */
+#define CONFIG_FLASH_DEBUG 1
+
/* RP2040 *********************************************************************/
#define CONFIG_RP2040_SPI_DEBUG 1 /* bool */
diff --git a/cmd/sbc_harness/fs_harness_flash_bin.c b/cmd/sbc_harness/fs_harness_flash_bin.c
new file mode 100644
index 0000000..bdb8da4
--- /dev/null
+++ b/cmd/sbc_harness/fs_harness_flash_bin.c
@@ -0,0 +1,310 @@
+/* sbc_harness/fs_harness_flash_bin.c - 9P access to flash storage
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <hardware/flash.h>
+#include <hardware/watchdog.h>
+
+#define LOG_NAME FLASH
+#include <libmisc/log.h>
+
+#include <util9p/static.h>
+
+#define IMPLEMENTATION_FOR_FS_HARNESS_FLASH_BIN YES
+#include "fs_harness_flash_bin.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct flash_file, flash_file, static);
+
+LO_IMPLEMENTATION_H(lib9p_srv_fio, struct flash_file, flash_file);
+LO_IMPLEMENTATION_C(lib9p_srv_fio, struct flash_file, flash_file, static);
+
+#define DATA_START ((const char *)(XIP_NOALLOC_BASE))
+#define DATA_SIZE PICO_FLASH_SIZE_BYTES
+#define DATA_HSIZE (DATA_SIZE/2)
+static_assert(DATA_SIZE % FLASH_SECTOR_SIZE == 0);
+static_assert(DATA_HSIZE % FLASH_SECTOR_SIZE == 0);
+
+/* There are some memcpy()s (and memcmp()s?) in here that can (and
+ * arguably should) be replaced with SSI DMA. */
+
+/* ab_flash_* (mid-level utilities for our A/B write scheme) ******************/
+
+/**
+ * Copy the upper half of flash to the lower half of flash, then reboot.
+ *
+ * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE
+ */
+[[noreturn]] static void __no_inline_not_in_flash_func(ab_flash_finalize)(uint8_t *buf) {
+ assert(buf);
+
+ infof("copying upper flash to lower flash...");
+
+ cr_save_and_disable_interrupts();
+
+ for (size_t off = 0; off < DATA_HSIZE; off += FLASH_SECTOR_SIZE) {
+ memcpy(buf, DATA_START+DATA_HSIZE+off, FLASH_SECTOR_SIZE);
+ if (memcmp(DATA_START+off, buf, FLASH_SECTOR_SIZE) == 0)
+ continue;
+ flash_range_erase(off, FLASH_SECTOR_SIZE);
+ flash_range_program(off, buf, FLASH_SECTOR_SIZE);
+ }
+
+ infof("rebooting...");
+
+ watchdog_reboot(0, 0, 300);
+
+ for (;;)
+ asm volatile ("nop");
+}
+
+/**
+ * Set the upper half of flash to all zero bytes.
+ *
+ * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE
+ */
+static void ab_flash_initialize_zero(uint8_t *buf) {
+ assert(buf);
+
+ memset(buf, 0, FLASH_SECTOR_SIZE);
+
+ infof("zeroing upper flash...");
+ for (size_t off = DATA_HSIZE; off < DATA_SIZE; off += FLASH_SECTOR_SIZE) {
+ if (memcmp(buf, DATA_START+off, FLASH_SECTOR_SIZE) == 0)
+ continue;
+ bool saved = cr_save_and_disable_interrupts();
+ /* No need to `flash_range_erase()`; the way the flash
+ * works is that _erase() sets all bits to 1, and
+ * _program() sets some bits to 0. If we don't need
+ * any bits to be 1, then we can skip the
+ * _erase(). */
+ flash_range_program(off, buf, FLASH_SECTOR_SIZE);
+ cr_restore_interrupts(saved);
+ }
+ debugf("... zeroed");
+}
+
+/**
+ * Copy the lower half of flash to the upper half of flash.
+ *
+ * @param buf : a scratch buffer that is at least FLASH_SECTOR_SIZE
+ */
+static void ab_flash_initialize(uint8_t *buf) {
+ assert(buf);
+
+ infof("initializing upper flash...");
+ for (size_t off = 0; off < DATA_HSIZE; off += FLASH_SECTOR_SIZE) {
+ memcpy(buf, DATA_START+off, FLASH_SECTOR_SIZE);
+ if (memcmp(buf, DATA_START+DATA_HSIZE+off, FLASH_SECTOR_SIZE) == 0)
+ continue;
+ bool saved = cr_save_and_disable_interrupts();
+ flash_range_erase(DATA_HSIZE+off, FLASH_SECTOR_SIZE);
+ flash_range_program(DATA_HSIZE+off, buf, FLASH_SECTOR_SIZE);
+ cr_restore_interrupts(saved);
+ }
+ debugf("... initialized");
+}
+
+/**
+ * Write `dat` to flash sector `pos`+(DATA_SIZE/2) (i.e. `pos` is a
+ * sector in the lower half, but this function writes to the upper
+ * half).
+ *
+ * @param pos : start-position of the sector to write to, must be in the upper half of the flash
+ * @param dat : the FLASH_SECTOR_SIZE bytes to write
+ */
+static void ab_flash_write_sector(size_t pos, uint8_t *dat) {
+ assert(pos < DATA_HSIZE);
+ assert(pos % FLASH_SECTOR_SIZE == 0);
+ assert(dat);
+
+ pos += DATA_HSIZE;
+
+ infof("write flash sector @ %zu...", pos);
+ if (memcmp(dat, DATA_START+pos, FLASH_SECTOR_SIZE) != 0) {
+ bool saved = cr_save_and_disable_interrupts();
+ flash_range_erase(pos, FLASH_SECTOR_SIZE);
+ flash_range_program(pos, dat, FLASH_SECTOR_SIZE);
+ cr_restore_interrupts(saved);
+ }
+ debugf("... written");
+}
+
+/* srv_file *******************************************************************/
+
+static void flash_file_free(struct flash_file *self) {
+ assert(self);
+}
+static struct lib9p_qid flash_file_qid(struct flash_file *self) {
+ assert(self);
+
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE|LIB9P_QT_EXCL,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+static struct lib9p_stat flash_file_stat(struct flash_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ return (struct lib9p_stat){
+ .kern_type = 0,
+ .kern_dev = 0,
+ .file_qid = flash_file_qid(self),
+ .file_mode = LIB9P_DM_EXCL|0666,
+ .file_atime = UTIL9P_ATIME,
+ .file_mtime = UTIL9P_MTIME,
+ .file_size = DATA_SIZE,
+ .file_name = lib9p_str(self->name),
+ .file_owner_uid = lib9p_str("root"),
+ .file_owner_gid = lib9p_str("root"),
+ .file_last_modified_uid = lib9p_str("root"),
+ .file_extension = lib9p_str(NULL),
+ .file_owner_n_uid = 0,
+ .file_owner_n_gid = 0,
+ .file_last_modified_n_uid = 0,
+ };
+}
+static void flash_file_wstat(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ struct lib9p_stat) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+}
+static void flash_file_remove(struct flash_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+}
+
+LIB9P_SRV_NOTDIR(struct flash_file, flash_file);
+
+static lo_interface lib9p_srv_fio flash_file_fopen(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ bool rd, bool wr, bool trunc) {
+ assert(self);
+ assert(ctx);
+
+ if (rd) {
+ self->rbuf.ok = false;
+ }
+
+ if (wr) {
+ if (trunc) {
+ ab_flash_initialize_zero(self->wbuf.dat);
+ self->written = true;
+ } else {
+ ab_flash_initialize(self->wbuf.dat);
+ self->written = false;
+ }
+ self->wbuf.ok = false;
+ }
+
+ return lo_box_flash_file_as_lib9p_srv_fio(self);
+}
+
+/* srv_fio ********************************************************************/
+
+static uint32_t flash_file_iounit(struct flash_file *self) {
+ assert(self);
+ return FLASH_SECTOR_SIZE;
+}
+
+static void flash_file_iofree(struct flash_file *self) {
+ assert(self);
+
+ if (self->wbuf.ok)
+ ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat);
+
+ if (self->written)
+ ab_flash_finalize(self->wbuf.dat);
+}
+
+static void flash_file_pread(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ uint32_t byte_count, uint64_t byte_offset,
+ struct iovec *ret) {
+ assert(self);
+ assert(ctx);
+ assert(ret);
+
+ if (byte_offset > DATA_SIZE) {
+ lib9p_error(&ctx->basectx,
+ LINUX_EINVAL, "offset is past the chip size");
+ return;
+ }
+
+ /* Assume that somewhere down the line the iovec we return
+ * will be passed to DMA. We don't want the DMA engine to hit
+ * (slow) XIP (for instance, this can cause reads/writes to
+ * the SSP to get out of sync with eachother), so copy the
+ * data to a buffer in (fast) RAM first. It's lame that the
+ * DMA engine can only have a DREQ on one side of the channel.
+ */
+ if (byte_offset == DATA_SIZE) {
+ *ret = (struct iovec){
+ .iov_len = 0,
+ };
+ return;
+ }
+ size_t sector_base = LM_ROUND_DOWN(byte_offset, FLASH_SECTOR_SIZE);
+ if (byte_offset + byte_count > sector_base + FLASH_SECTOR_SIZE)
+ byte_count = (sector_base + FLASH_SECTOR_SIZE) - byte_offset;
+ assert(byte_offset + byte_count <= DATA_SIZE);
+
+ if (!self->rbuf.ok || self->rbuf.pos != sector_base) {
+ self->rbuf.ok = true;
+ self->rbuf.pos = sector_base;
+ memcpy(self->rbuf.dat, DATA_START+sector_base, FLASH_SECTOR_SIZE);
+ }
+
+ *ret = (struct iovec){
+ .iov_base = &self->rbuf.dat[byte_offset-sector_base],
+ .iov_len = byte_count,
+ };
+}
+
+/* TODO: Short/corrupt writes are dangerous. This should either (1)
+ * check a checksum, (2) use uf2 instead of verbatim data, or (3) use
+ * ihex instead of verbatim data. */
+static uint32_t flash_file_pwrite(struct flash_file *self, struct lib9p_srv_ctx *ctx,
+ void *buf,
+ uint32_t byte_count,
+ uint64_t byte_offset) {
+ assert(self);
+ assert(ctx);
+
+ if (byte_offset > DATA_HSIZE) {
+ lib9p_error(&ctx->basectx,
+ LINUX_EINVAL, "offset is past half the chip size");
+ return 0;
+ }
+ if (byte_count == 0)
+ return 0;
+ if (byte_offset == DATA_HSIZE) {
+ lib9p_error(&ctx->basectx,
+ LINUX_EINVAL, "offset is at half the chip size");
+ return 0;
+ }
+
+ size_t sector_base = LM_ROUND_DOWN(byte_offset, FLASH_SECTOR_SIZE);
+ if (byte_offset + byte_count > sector_base + FLASH_SECTOR_SIZE)
+ byte_count = (sector_base + FLASH_SECTOR_SIZE) - byte_offset;
+ assert(byte_offset + byte_count < DATA_HSIZE);
+
+ if (self->wbuf.ok && self->wbuf.pos != sector_base)
+ ab_flash_write_sector(self->wbuf.pos, self->wbuf.dat);
+ if (!self->wbuf.ok || self->wbuf.pos != sector_base) {
+ self->wbuf.ok = true;
+ self->wbuf.pos = sector_base;
+ if (byte_count != FLASH_SECTOR_SIZE)
+ memcpy(self->wbuf.dat, DATA_START+DATA_HSIZE+sector_base, FLASH_SECTOR_SIZE);
+ }
+ memcpy(&self->wbuf.dat[byte_offset-sector_base], buf, byte_count);
+
+ self->written = true;
+ return byte_count;
+}
diff --git a/cmd/sbc_harness/fs_harness_flash_bin.h b/cmd/sbc_harness/fs_harness_flash_bin.h
new file mode 100644
index 0000000..36382be
--- /dev/null
+++ b/cmd/sbc_harness/fs_harness_flash_bin.h
@@ -0,0 +1,30 @@
+/* sbc_harness/fs_harness_flash_bin.h - 9P access to flash storage
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_
+#define _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_
+
+#include <hardware/flash.h> /* for FLASH_SECTOR_SIZE */
+
+#include <lib9p/srv.h>
+
+struct flash_file {
+ char *name;
+ uint64_t pathnum;
+
+ BEGIN_PRIVATE(FS_HARNESS_FLASH_BIN);
+ bool written;
+ struct {
+ bool ok;
+ size_t pos;
+ uint8_t dat[FLASH_SECTOR_SIZE];
+ } wbuf, rbuf;
+ END_PRIVATE(FS_HARNESS_FLASH_BIN);
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct flash_file, flash_file);
+#define lo_box_flash_file_as_lib9p_srv_file(obj) util9p_box(flash_file, obj)
+
+#endif /* _SBC_HARNESS_FS_HARNESS_FLASH_BIN_H_ */
diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.c b/cmd/sbc_harness/fs_harness_uptime_txt.c
new file mode 100644
index 0000000..9216986
--- /dev/null
+++ b/cmd/sbc_harness/fs_harness_uptime_txt.c
@@ -0,0 +1,156 @@
+/* sbc_harness/fs_harness_uptime_txt.c - 9P access to harness uptime
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stdio.h> /* for snprintf() */
+#include <stdlib.h> /* for malloc(), free() */
+
+#include <libhw/generic/alarmclock.h>
+#include <util9p/static.h>
+
+#include "fs_harness_uptime_txt.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct uptime_file, uptime_file, static);
+
+struct uptime_fio {
+ struct uptime_file *parent;
+ size_t buf_len;
+ char buf[24]; /* len(str(UINT64_MAX)+"ns\n\0") */
+};
+
+LO_IMPLEMENTATION_H(lib9p_srv_fio, struct uptime_fio, uptime_fio);
+LO_IMPLEMENTATION_C(lib9p_srv_fio, struct uptime_fio, uptime_fio, static);
+
+/* srv_file *******************************************************************/
+
+static void uptime_file_free(struct uptime_file *self) {
+ assert(self);
+}
+static struct lib9p_qid uptime_file_qid(struct uptime_file *self) {
+ assert(self);
+
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+static struct lib9p_stat uptime_file_stat(struct uptime_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ uint64_t now = LO_CALL(bootclock, get_time_ns);
+ uint64_t size = 0;
+ while (now) {
+ size++;
+ now /= 10;
+ }
+ if (!size)
+ size++;
+ size += 3;
+
+ return (struct lib9p_stat){
+ .kern_type = 0,
+ .kern_dev = 0,
+ .file_qid = uptime_file_qid(self),
+ .file_mode = 0444,
+ .file_atime = UTIL9P_ATIME,
+ .file_mtime = UTIL9P_MTIME,
+ .file_size = size,
+ .file_name = lib9p_str(self->name),
+ .file_owner_uid = lib9p_str("root"),
+ .file_owner_gid = lib9p_str("root"),
+ .file_last_modified_uid = lib9p_str("root"),
+ .file_extension = lib9p_str(NULL),
+ .file_owner_n_uid = 0,
+ .file_owner_n_gid = 0,
+ .file_last_modified_n_uid = 0,
+ };
+}
+static void uptime_file_wstat(struct uptime_file *self, struct lib9p_srv_ctx *ctx,
+ struct lib9p_stat) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+}
+static void uptime_file_remove(struct uptime_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+}
+
+LIB9P_SRV_NOTDIR(struct uptime_file, uptime_file);
+
+static lo_interface lib9p_srv_fio uptime_file_fopen(struct uptime_file *self, struct lib9p_srv_ctx *ctx,
+ bool LM_UNUSED(rd), bool LM_UNUSED(wr), bool LM_UNUSED(trunc)) {
+ assert(self);
+ assert(ctx);
+
+ struct uptime_fio *ret = malloc(sizeof(struct uptime_fio));
+ ret->parent = self;
+ ret->buf_len = 0;
+
+ return lo_box_uptime_fio_as_lib9p_srv_fio(ret);
+}
+
+/* srv_fio ********************************************************************/
+
+static uint32_t uptime_fio_iounit(struct uptime_fio *self) {
+ assert(self);
+ return sizeof(self->buf)-1;
+}
+
+static void uptime_fio_iofree(struct uptime_fio *self) {
+ assert(self);
+ free(self);
+}
+
+static struct lib9p_qid uptime_fio_qid(struct uptime_fio *self) {
+ assert(self);
+ assert(self->parent);
+ return uptime_file_qid(self->parent);
+}
+
+static void uptime_fio_pread(struct uptime_fio *self, struct lib9p_srv_ctx *ctx,
+ uint32_t byte_count, uint64_t byte_offset,
+ struct iovec *ret) {
+ assert(self);
+ assert(ctx);
+ assert(ret);
+
+ if (byte_offset == 0 || self->buf_len == 0) {
+ uint64_t now = LO_CALL(bootclock, get_time_ns);
+ self->buf_len = snprintf(self->buf, sizeof(self->buf), "%"PRIu64"ns\n", now);
+ }
+
+ if (byte_offset > (uint64_t)self->buf_len) {
+ lib9p_error(&ctx->basectx,
+ LINUX_EINVAL, "offset is past end-of-file length");
+ return;
+ }
+
+ size_t beg_off = (size_t)byte_offset;
+ size_t end_off = beg_off + (size_t)byte_count;
+ if (end_off > self->buf_len)
+ end_off = self->buf_len;
+ *ret = (struct iovec){
+ .iov_base = &self->buf[beg_off],
+ .iov_len = end_off-beg_off,
+ };
+}
+
+static uint32_t uptime_fio_pwrite(struct uptime_fio *self, struct lib9p_srv_ctx *ctx,
+ void *LM_UNUSED(buf),
+ uint32_t LM_UNUSED(byte_count),
+ uint64_t LM_UNUSED(byte_offset)) {
+ assert(self);
+ assert(ctx);
+
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
+ return 0;
+}
diff --git a/cmd/sbc_harness/fs_harness_uptime_txt.h b/cmd/sbc_harness/fs_harness_uptime_txt.h
new file mode 100644
index 0000000..7bf2945
--- /dev/null
+++ b/cmd/sbc_harness/fs_harness_uptime_txt.h
@@ -0,0 +1,19 @@
+/* sbc_harness/fs_harness_uptime_txt.h - 9P access to harness uptime
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _SBC_HARNESS_FS_HARNESS_UPTIME_TXT_H_
+#define _SBC_HARNESS_FS_HARNESS_UPTIME_TXT_H_
+
+#include <lib9p/srv.h>
+
+struct uptime_file {
+ char *name;
+ uint64_t pathnum;
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct uptime_file, uptime_file);
+#define lo_box_uptime_file_as_lib9p_srv_file(obj) util9p_box(uptime_file, obj)
+
+#endif /* _SBC_HARNESS_FS_HARNESS_UPTIME_TXT_H_ */
diff --git a/cmd/sbc_harness/main.c b/cmd/sbc_harness/main.c
index 2b2b3f8..25b122c 100644
--- a/cmd/sbc_harness/main.c
+++ b/cmd/sbc_harness/main.c
@@ -32,6 +32,8 @@
/* local headers */
#include "usb_keyboard.h"
#include "static.h"
+#include "fs_harness_flash_bin.h"
+#include "fs_harness_uptime_txt.h"
/* configuration **************************************************************/
@@ -45,6 +47,13 @@ enum { PATH_BASE = __COUNTER__ };
#define STATIC_FILE(STRNAME, ...) UTIL9P_STATIC_FILE(PATH_COUNTER, STRNAME, __VA_ARGS__)
#define STATIC_DIR(STRNAME, ...) UTIL9P_STATIC_DIR(PATH_COUNTER, STRNAME, __VA_ARGS__)
+#define API_FILE(STRNAME, SYMNAME, ...) \
+ lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \
+ .name = STRNAME, \
+ .pathnum = PATH_COUNTER \
+ __VA_OPT__(,) __VA_ARGS__ \
+ }))
+
struct lib9p_srv_file root =
STATIC_DIR("",
STATIC_DIR("Documentation",
@@ -74,15 +83,18 @@ struct lib9p_srv_file root =
STATIC_FILE("harness_flash_bin.txt",
.data_start = _binary_static_Documentation_harness_flash_bin_txt_start,
.data_end = _binary_static_Documentation_harness_flash_bin_txt_end),
+ STATIC_FILE("harness_uptime_txt.txt",
+ .data_start = _binary_static_Documentation_harness_uptime_txt_txt_start,
+ .data_end = _binary_static_Documentation_harness_uptime_txt_txt_end),
),
STATIC_DIR("harness",
STATIC_FILE("rom.bin",
.data_start = (void*)0x00000000,
.data_size = 16*1024),
- // TODO: Make flash.bin writable.
- STATIC_FILE("flash.bin",
- .data_start = (void*)0x10000000,
- .data_size = PICO_FLASH_SIZE_BYTES),
+ API_FILE("flash.bin",
+ flash),
+ API_FILE("uptime.txt",
+ uptime),
// TODO: system.log
// TODO: proc.txt
// TODO: cpuinfo.txt
diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
index 1e89d72..b3fc12e 100644
--- a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
+++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md
@@ -8,22 +8,22 @@
The firmware running on the SBC-Harness is Free Software -- if you
have access to this file, then you have the freedom to use, study,
-share, and improve; as long as you follow a few conditions that
-basically amount to "paying it forward".
+share, and improve the firmware; as long as you follow a few
+conditions that basically amount to "paying it forward".
The precise terms of your rights and obligations are given in the
files in the `YOUR_RIGHTS_AND_OBLIGATIONS/` directory. You must obey
-each of the files in that directory when distributing copies or
-modified copies of the firmware:
+each of the files in that directory when distributing
+verbatim-or-modified copies of the firmware:
- `agpl-3.0.txt`: The firmware is as-a-whole licensed to you under
the terms of the GNU Affero General Public License (GNU AGPL) as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version. This is the main
- document that protects your rights and defines obligations.
+ document that protects your rights and defines your obligations.
- other files: The firmware makes use of other code that is under
various other licenses. When taken with the AGPL, they amount to
requiring that you pass along the copyright and license text in
- those files when you distribute copies or modified copies of the
+ those files when you distribute verbatim-or-modified copies of the
firmware.
diff --git a/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt b/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt
index 982d7e0..115f2ee 100644
--- a/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt
+++ b/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt
@@ -7,6 +7,10 @@ DESCRIPTION
Any number of readers may read the flash contents.
+ Only one writer can have the file open at a time; once the
+ file is closed, the harness reboots into the new firmware.
+ Writes to the top half of the chip will fail.
+
BUGS
- The size of the chip is configured at compile-time. If the
firmware is loaded onto hardware with a larger flash chip
@@ -16,8 +20,13 @@ BUGS
compiled for, then accessing the missing upper part of the
chip will crash.
- - This file is not writable; it aught to be possible to update
- the harness firmware by writing to this file.
+ - When writing to the flash using this file, only half of the
+ chip capacity is usable; the top half and bottom half are
+ mirrors of each-other. This is to avoid the firmware
+ crashing as its program text is overwritten; the firmware is
+ executing out of the bottom half, and writing to the top
+ half; once the file is closed, a minimal in-RAM function
+ copies the top half to the bottom half and reboots.
AUTHOR
Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
diff --git a/cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt b/cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt
new file mode 100644
index 0000000..1ab86f7
--- /dev/null
+++ b/cmd/sbc_harness/static/Documentation/harness_uptime_txt.txt
@@ -0,0 +1,17 @@
+NAME
+ /harness/uptime.txt
+
+DESCRIPTION
+ Reading this file gives a text string of the format
+ `sprintf("%uns\n", uptime_ns)` indicating the harness's uptime
+ in an integer number of nanoseconds.
+
+BUGS
+ Using nanoseconds gives the illusion of more precision than
+ there actually is; the harness' clock only has microsecond
+ resolution; the last 3 digits of the returned nanosecond count
+ will always be 0.
+
+AUTHOR
+ Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/gdb-helpers/rp2040.py b/gdb-helpers/rp2040.py
index 087974d..f019299 100644
--- a/gdb-helpers/rp2040.py
+++ b/gdb-helpers/rp2040.py
@@ -152,7 +152,7 @@ ICSR : {fmt32(icsr) } Control and State
╓endiannes (0=little, 1=big)
vect_key┐ ║ ╓sys_reset_req
┌───────┴──────┐║ ║╓vect_clr_active
-AIRCR : {fmt32(read_mmreg(base+0xed0c)) } Application Interrupt and Reset Control
+AIRCR : {fmt32(read_mmreg(base+0xed0c)) } Application {{Interrupt+Reset}} Control
╓sleep_deep
s_ev_on_pend╖ ║╓sleep_on_exit
@@ -172,7 +172,7 @@ PRIMASK : {fmt32(read_reg('primask')) } Priority Mask
app exec intr
┌┴─┐ ┌───────┴──────┐┌───┴───┐
-xPSR : {fmt32(psr) } {{Application,Execution,Interrupt}} Program Status
+xPSR : {fmt32(psr) } {{App,Exec,Intr}} Program Status
║║║║ ║ ║└───┬───┘
[N]egative╜║║║ ║ ║ └{psr&0x1FF} ({exception_names[psr&0x1FF]})
[Z]ero╜║║ ╙[T]humb ╙require [a]lignment
diff --git a/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt
index 11a58ba..d433a12 100644
--- a/lib9p/CMakeLists.txt
+++ b/lib9p/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(lib9p INTERFACE)
-target_include_directories(lib9p SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(lib9p PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(lib9p INTERFACE
9p.generated.c
9p.c
diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h
index ff5ebdc..070cf5a 100644
--- a/lib9p/include/lib9p/srv.h
+++ b/lib9p/include/lib9p/srv.h
@@ -26,9 +26,9 @@ struct lib9p_srv_ctx {
uint32_t uid;
struct lib9p_s uname;
- BEGIN_PRIVATE(LIB9P_SRV_H)
+ BEGIN_PRIVATE(LIB9P_SRV_H);
_lib9p_srv_flushch_t _flushch;
- END_PRIVATE(LIB9P_SRV_H)
+ END_PRIVATE(LIB9P_SRV_H);
};
bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx);
@@ -140,11 +140,11 @@ struct lib9p_srv {
lo_interface lib9p_srv_file (*rootdir)(struct lib9p_srv_ctx *, struct lib9p_s treename);
/* For internal use */
- BEGIN_PRIVATE(LIB9P_SRV_H)
+ BEGIN_PRIVATE(LIB9P_SRV_H);
unsigned int readers;
unsigned int writers;
_lib9p_srv_reqch_t _reqch;
- END_PRIVATE(LIB9P_SRV_H)
+ END_PRIVATE(LIB9P_SRV_H);
};
/**
diff --git a/lib9p/srv.c b/lib9p/srv.c
index bfeb06f..a425dc9 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -199,7 +199,11 @@ static void respond_error(struct _lib9p_srv_req *req) {
LIB9P_TYP_Rerror, &host,
&net);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
infof("< %v", lo_box_lib9p_msg_Rerror_as_fmt_formatter(&host));
+#pragma GCC diagnostic pop
r = write_Rmsg(req, &net);
if (r < 0)
nonrespond_errorf("write: %s", net_strerror(-r));
@@ -425,7 +429,11 @@ static void handle_message(struct _lib9p_srv_req *ctx) {
enum lib9p_msg_type typ;
lib9p_Tmsg_unmarshal(&ctx->ctx.basectx, ctx->net_bytes,
&typ, host_req);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
infof("> %v", lo_box_lib9p_msg_as_fmt_formatter(&ctx->ctx.basectx, typ, host_req));
+#pragma GCC diagnostic pop
/* Handle it. */
tmessage_handlers[typ](ctx, (void *)host_req, (void *)host_resp);
@@ -439,7 +447,11 @@ static void handle_message(struct _lib9p_srv_req *ctx) {
typ+1, host_resp,
&net_resp))
goto write;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
infof("< %v", lo_box_lib9p_msg_as_fmt_formatter(&ctx->ctx.basectx, typ+1, &host_resp));
+#pragma GCC diagnostic pop
write_Rmsg(ctx, &net_resp);
}
if (host_req)
@@ -903,6 +915,7 @@ static void handle_Topen(struct _lib9p_srv_req *ctx,
if (!srv_util_check_perm(ctx, &stat, perm_bits)) {
lib9p_error(&ctx->ctx.basectx,
LINUX_EACCES, "permission denied");
+ return;
}
/* Actually make the call. */
@@ -953,6 +966,8 @@ static void handle_Tread(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Rread *resp) {
util_handler_common(ctx, req, resp);
+ /* TODO: serialize simultaneous reads to the same FID */
+
/* Check that the FID is valid for this. */
struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
@@ -1015,6 +1030,8 @@ static void handle_Twrite(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Rwrite *resp) {
util_handler_common(ctx, req, resp);
+ /* TODO: serialize simultaneous writes to the same FID */
+
/* Check that the FID is valid for this. */
struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid);
if (!fidinfo) {
diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c
index c759029..a31c083 100644
--- a/lib9p/tests/test_server/main.c
+++ b/lib9p/tests/test_server/main.c
@@ -47,11 +47,11 @@ struct {
struct api_file {
uint64_t pathnum;
};
-LO_IMPLEMENTATION_H(lib9p_srv_file, struct api_file, api)
-LO_IMPLEMENTATION_H(lib9p_srv_fio, struct api_file, api)
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct api_file, api);
+LO_IMPLEMENTATION_H(lib9p_srv_fio, struct api_file, api);
-LO_IMPLEMENTATION_C(lib9p_srv_file, struct api_file, api, static)
-LO_IMPLEMENTATION_C(lib9p_srv_fio, struct api_file, api, static)
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct api_file, api, static);
+LO_IMPLEMENTATION_C(lib9p_srv_fio, struct api_file, api, static);
static void api_free(struct api_file *self) {
assert(self);
diff --git a/lib9p_util/CMakeLists.txt b/lib9p_util/CMakeLists.txt
index 4fbdb7a..2e5790e 100644
--- a/lib9p_util/CMakeLists.txt
+++ b/lib9p_util/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(lib9p_util INTERFACE)
-target_include_directories(lib9p_util SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(lib9p_util PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(lib9p_util INTERFACE
static.c
)
diff --git a/libcr/CMakeLists.txt b/libcr/CMakeLists.txt
index 0a4696c..80a4ece 100644
--- a/libcr/CMakeLists.txt
+++ b/libcr/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libcr INTERFACE)
-target_include_directories(libcr SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+target_include_directories(libcr PUBLIC INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_sources(libcr INTERFACE
coroutine.c
)
diff --git a/libcr/coroutine.c b/libcr/coroutine.c
index 33e8141..bf44219 100644
--- a/libcr/coroutine.c
+++ b/libcr/coroutine.c
@@ -424,9 +424,9 @@ static const uint8_t stack_pattern[] = {
#endif
#if CONFIG_COROUTINE_PROTECT_STACK
#define CR_STACK_GUARD_SIZE \
- LM_ROUND_UP(sizeof(stack_pattern), CR_PLAT_STACK_ALIGNMENT)
+ ((size_t)LM_ROUND_UP(sizeof(stack_pattern), CR_PLAT_STACK_ALIGNMENT))
#else
- #define CR_STACK_GUARD_SIZE 0
+ #define CR_STACK_GUARD_SIZE ((size_t)0)
#endif
/* global variables ***********************************************************/
@@ -584,7 +584,7 @@ cid_t coroutine_add_with_stack_size(size_t stack_size,
name, stack_size, CR_STACK_GUARD_SIZE, coroutine_table[child-1].stack_size);
coroutine_table[child-1].stack =
aligned_alloc(CR_PLAT_STACK_ALIGNMENT, coroutine_table[child-1].stack_size);
- infof("... done, stack is [%#zx,%#zx)",
+ infof("... done, stack is [0x%p,0x%p)",
coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE,
coroutine_table[child-1].stack + CR_STACK_GUARD_SIZE + stack_size);
#if CONFIG_COROUTINE_MEASURE_STACK || CONFIG_COROUTINE_PROTECT_STACK
diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt
index 3746584..8545798 100644
--- a/libcr_ipc/CMakeLists.txt
+++ b/libcr_ipc/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libcr_ipc INTERFACE)
-target_include_directories(libcr_ipc SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+target_include_directories(libcr_ipc PUBLIC INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_sources(libcr_ipc INTERFACE
chan.c
select.c
diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h
index 5b1e583..dafc92d 100644
--- a/libcr_ipc/include/libcr_ipc/chan.h
+++ b/libcr_ipc/include/libcr_ipc/chan.h
@@ -21,7 +21,7 @@
*
* type:
*
- * /**
+ * / **
* * A NAME##_t is a fair unbuffered channel that transports
* * values of type `VAL_T`.
* *
@@ -35,7 +35,7 @@
*
* methods:
*
- * /**
+ * / **
* * NAME##_send(ch, val) sends `val` over `ch`.
* *
* * @runs_in coroutine
@@ -44,7 +44,7 @@
* * /
* void NAME##_send(NAME##_t *ch, VAL_T val);
*
- * /**
+ * / **
* * NAME##_recv(ch) reads and returns a value from ch.
* *
* * @runs_in coroutine
@@ -53,7 +53,7 @@
* * /
* VAL_T NAME##_recv(NAME##_t *ch);
*
- * /**
+ * / **
* * NAME##_can_send(ch) returns whether NAME##_send(ch, val)
* * would run without pausing.
* *
@@ -63,7 +63,7 @@
* * /
* bool NAME##_can_send(NAME##_t *ch);
*
- * /**
+ * / **
* * NAME##_can_recv(ch) returns whether NAME##_recv(ch) would
* * return without pausing.
* *
diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h
index 0ff8bbf..0600399 100644
--- a/libcr_ipc/include/libcr_ipc/rpc.h
+++ b/libcr_ipc/include/libcr_ipc/rpc.h
@@ -20,7 +20,7 @@
*
* type:
*
- * /**
+ * / **
* * A NAME##_t is a fair rpc-channel on which the requester submits a
* * value of type `REQ_T` and the responder responds with a value of
* * type `RESP_T`.
@@ -35,7 +35,7 @@
* * _recv_req() and _send_resp().
* typedef ... NAME##_t;
*
- * /**
+ * / **
* * A NAME##_req_t is handle that wraps a REQ_T and is used to return
* * the response RESP_T to the correct requester. `REQ_T req` is the
* * only public member.
@@ -43,7 +43,7 @@
*
* methods:
*
- * /**
+ * / **
* * NAME##_send_req(ch, req) submits the `req` request over `ch` and
* * returns the response.
* *
@@ -53,7 +53,7 @@
* * /
* RESP_T NAME##_send_req(NAME##_t *ch, REQ_T req);
*
- * /**
+ * / **
* * NAME##_recv_req(ch) reads a request from ch, and returns a
* * NAME##_req_t handle wrapping that request.
* *
@@ -63,7 +63,7 @@
* * /
* NAME##_req_t NAME##_recv_req(NAME##_t *ch);
*
- * /**
+ * / **
* * NAME##_can_recv_req(ch) returns whether NAME##_recv_req(ch)
* * would return without pausing.
* *
@@ -75,7 +75,7 @@
*
* type:
*
- * /**
+ * / **
* * A NAME##_req_t is a handle that wraps a REQ_T, and is a channel
* * that a response may be written to.
* * /
@@ -83,7 +83,7 @@
*
* methods:
*
- * /**
+ * / **
* * cr_rpc_send_resp(req, resp) sends the given response to the given
* * request.
* *
@@ -111,7 +111,7 @@
RESP_T resp; \
_cr_rpc_send_req(&ch->core, \
&req, sizeof(req), \
- &resp, sizeof(resp)); \
+ &resp); \
return resp; \
} \
\
@@ -152,8 +152,8 @@ struct _cr_rpc_requester {
struct _cr_rpc_responder {
_cr_ipc_sll_node;
- /* /* before enqueued | after dequeued */
- /* /* -------------------+-------------------- */
+ /* before enqueued | after dequeued */
+ /* -------------------+-------------------- */
cid_t cid; /* responder cid | requester cid */
void *ptr; /* where to write req | where to write resp */
};
@@ -165,7 +165,7 @@ struct _cr_rpc {
static inline void _cr_rpc_send_req(struct _cr_rpc *ch,
void *req_ptr, size_t req_size,
- void *resp_ptr, size_t resp_size)
+ void *resp_ptr)
{
assert(ch);
assert(req_ptr);
diff --git a/libcr_ipc/include/libcr_ipc/select.h b/libcr_ipc/include/libcr_ipc/select.h
index b845082..0e35351 100644
--- a/libcr_ipc/include/libcr_ipc/select.h
+++ b/libcr_ipc/include/libcr_ipc/select.h
@@ -33,8 +33,6 @@ struct cr_select_arg {
};
#define CR_SELECT_RECV(CH, VALP) ({ \
- assert(CH); \
- assert(VALP); \
/* The _valp indirection is to get the \
* compiler to check that the types are \
* compatible. */ \
@@ -47,7 +45,6 @@ struct cr_select_arg {
}); \
})
#define CR_SELECT_SEND(CH, VAL) ({ \
- assert(CH); \
typeof((CH)->vals[0]) val_lvalue = VAL; \
((struct cr_select_arg){ \
.op = _CR_SELECT_OP_SEND, \
diff --git a/libcr_ipc/include/libcr_ipc/sema.h b/libcr_ipc/include/libcr_ipc/sema.h
index 6db4015..fcabf28 100644
--- a/libcr_ipc/include/libcr_ipc/sema.h
+++ b/libcr_ipc/include/libcr_ipc/sema.h
@@ -84,9 +84,9 @@ static inline void cr_sema_wait(cr_sema_t *sema) {
.cid = cr_getcid(),
};
_cr_ipc_sll_push_to_rear(&sema->waiters, &self);
- if (sema->waiters.front != &self || !sema->cnt)
+ if (sema->waiters.front != &self._cr_ipc_sll_node || !sema->cnt)
cr_pause_and_yield();
- assert(sema->waiters.front == &self && sema->cnt);
+ assert(sema->waiters.front == &self._cr_ipc_sll_node && sema->cnt);
_cr_ipc_sll_pop_from_front(&sema->waiters);
sema->cnt--;
if (sema->cnt && sema->waiters.front)
diff --git a/libdhcp/CMakeLists.txt b/libdhcp/CMakeLists.txt
index 72b0952..dee7cb6 100644
--- a/libdhcp/CMakeLists.txt
+++ b/libdhcp/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libdhcp INTERFACE)
-target_include_directories(libdhcp SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(libdhcp PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(libdhcp INTERFACE
dhcp_client.c
)
diff --git a/libfmt/CMakeLists.txt b/libfmt/CMakeLists.txt
index 1b3a80f..f65d462 100644
--- a/libfmt/CMakeLists.txt
+++ b/libfmt/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libfmt INTERFACE)
-target_include_directories(libfmt SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(libfmt PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(libfmt INTERFACE
libmisc.c
libobj.c
diff --git a/libfmt/include/libfmt/fmt.h b/libfmt/include/libfmt/fmt.h
index 4dba82f..1b5bde1 100644
--- a/libfmt/include/libfmt/fmt.h
+++ b/libfmt/include/libfmt/fmt.h
@@ -18,6 +18,6 @@
*/
#define fmt_formatter_LO_IFACE \
LO_FUNC(void, format, struct fmt_state *)
-LO_INTERFACE(fmt_formatter)
+LO_INTERFACE(fmt_formatter);
#endif /* _LIBFMT_FMT_H_ */
diff --git a/libfmt/libmisc.c b/libfmt/libmisc.c
index 4586c30..97a2c80 100644
--- a/libfmt/libmisc.c
+++ b/libfmt/libmisc.c
@@ -17,6 +17,8 @@
#if LIB_PICO_STDIO
static void libfmt_light_fct(char character, void *LM_UNUSED(arg)) {
+ if (character == '\n')
+ stdio_putchar_raw('\r');
stdio_putchar_raw(character);
}
#else
diff --git a/libhw_cr/CMakeLists.txt b/libhw_cr/CMakeLists.txt
index caeac21..ba20b26 100644
--- a/libhw_cr/CMakeLists.txt
+++ b/libhw_cr/CMakeLists.txt
@@ -14,7 +14,7 @@ target_sources(libhw_cr INTERFACE
)
if (PICO_PLATFORM STREQUAL "rp2040")
- target_include_directories(libhw_cr SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rp2040_include)
+ target_include_directories(libhw_cr PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rp2040_include)
target_link_libraries(libhw_cr INTERFACE
libcr_ipc
)
@@ -34,7 +34,7 @@ if (PICO_PLATFORM STREQUAL "rp2040")
endif()
if (PICO_PLATFORM STREQUAL "host")
- target_include_directories(libhw_cr SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/host_include)
+ target_include_directories(libhw_cr PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/host_include)
target_sources(libhw_cr INTERFACE
host_util.c
host_alarmclock.c
diff --git a/libhw_cr/host_alarmclock.c b/libhw_cr/host_alarmclock.c
index 2f255e0..9eedec2 100644
--- a/libhw_cr/host_alarmclock.c
+++ b/libhw_cr/host_alarmclock.c
@@ -19,7 +19,7 @@
#include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_ns_time() */
-LO_IMPLEMENTATION_C(alarmclock, struct hostclock, hostclock, static)
+LO_IMPLEMENTATION_C(alarmclock, struct hostclock, hostclock, static);
static uint64_t hostclock_get_time_ns(struct hostclock *alarmclock) {
assert(alarmclock);
diff --git a/libhw_cr/host_include/libhw/host_alarmclock.h b/libhw_cr/host_include/libhw/host_alarmclock.h
index 89df68a..0cb8d30 100644
--- a/libhw_cr/host_include/libhw/host_alarmclock.h
+++ b/libhw_cr/host_include/libhw/host_alarmclock.h
@@ -16,12 +16,12 @@
struct hostclock {
clockid_t clock_id;
- BEGIN_PRIVATE(LIBHW_HOST_ALARMCLOCK_H)
+ BEGIN_PRIVATE(LIBHW_HOST_ALARMCLOCK_H);
bool initialized;
timer_t timer_id;
struct alarmclock_trigger *queue;
- END_PRIVATE(LIBHW_HOST_ALARMCLOCK_H)
+ END_PRIVATE(LIBHW_HOST_ALARMCLOCK_H);
};
-LO_IMPLEMENTATION_H(alarmclock, struct hostclock, hostclock)
+LO_IMPLEMENTATION_H(alarmclock, struct hostclock, hostclock);
#endif /* _LIBHW_HOST_ALARMCLOCK_H_ */
diff --git a/libhw_cr/host_include/libhw/host_net.h b/libhw_cr/host_include/libhw/host_net.h
index fced229..a16ed01 100644
--- a/libhw_cr/host_include/libhw/host_net.h
+++ b/libhw_cr/host_include/libhw/host_net.h
@@ -14,30 +14,30 @@
#include <libhw/generic/net.h>
struct _hostnet_tcp_conn {
- BEGIN_PRIVATE(LIBHW_HOST_NET_H)
+ BEGIN_PRIVATE(LIBHW_HOST_NET_H);
int fd;
uint64_t read_deadline_ns;
- END_PRIVATE(LIBHW_HOST_NET_H)
+ END_PRIVATE(LIBHW_HOST_NET_H);
};
-LO_IMPLEMENTATION_H(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp)
+LO_IMPLEMENTATION_H(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp);
struct hostnet_tcp_listener {
- BEGIN_PRIVATE(LIBHW_HOST_NET_H)
+ BEGIN_PRIVATE(LIBHW_HOST_NET_H);
int fd;
struct _hostnet_tcp_conn active_conn;
- END_PRIVATE(LIBHW_HOST_NET_H)
+ END_PRIVATE(LIBHW_HOST_NET_H);
};
-LO_IMPLEMENTATION_H(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist)
+LO_IMPLEMENTATION_H(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist);
void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port);
struct hostnet_udp_conn {
- BEGIN_PRIVATE(LIBHW_HOST_NET_H)
+ BEGIN_PRIVATE(LIBHW_HOST_NET_H);
int fd;
uint64_t read_deadline_ns;
- END_PRIVATE(LIBHW_HOST_NET_H)
+ END_PRIVATE(LIBHW_HOST_NET_H);
};
-LO_IMPLEMENTATION_H(net_packet_conn, struct hostnet_udp_conn, hostnet_udp)
+LO_IMPLEMENTATION_H(net_packet_conn, struct hostnet_udp_conn, hostnet_udp);
void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port);
diff --git a/libhw_cr/host_net.c b/libhw_cr/host_net.c
index f1c988c..6ed6e46 100644
--- a/libhw_cr/host_net.c
+++ b/libhw_cr/host_net.c
@@ -30,18 +30,18 @@
#include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_us_time() */
-LO_IMPLEMENTATION_C(io_closer, struct hostnet_tcp_listener, hostnet_tcplist, static)
-LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist, static)
-
-LO_IMPLEMENTATION_C(io_reader, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(io_writer, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(io_readwriter, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(io_closer, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(io_bidi_closer, struct _hostnet_tcp_conn, hostnet_tcp, static)
-LO_IMPLEMENTATION_C(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp, static)
-
-LO_IMPLEMENTATION_C(io_closer, struct hostnet_udp_conn, hostnet_udp, static)
-LO_IMPLEMENTATION_C(net_packet_conn, struct hostnet_udp_conn, hostnet_udp, static)
+LO_IMPLEMENTATION_C(io_closer, struct hostnet_tcp_listener, hostnet_tcplist, static);
+LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist, static);
+
+LO_IMPLEMENTATION_C(io_reader, struct _hostnet_tcp_conn, hostnet_tcp, static);
+LO_IMPLEMENTATION_C(io_writer, struct _hostnet_tcp_conn, hostnet_tcp, static);
+LO_IMPLEMENTATION_C(io_readwriter, struct _hostnet_tcp_conn, hostnet_tcp, static);
+LO_IMPLEMENTATION_C(io_closer, struct _hostnet_tcp_conn, hostnet_tcp, static);
+LO_IMPLEMENTATION_C(io_bidi_closer, struct _hostnet_tcp_conn, hostnet_tcp, static);
+LO_IMPLEMENTATION_C(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp, static);
+
+LO_IMPLEMENTATION_C(io_closer, struct hostnet_udp_conn, hostnet_udp, static);
+LO_IMPLEMENTATION_C(net_packet_conn, struct hostnet_udp_conn, hostnet_udp, static);
/* common *********************************************************************/
diff --git a/libhw_cr/rp2040_dma.h b/libhw_cr/rp2040_dma.h
index e295adf..c7f5a8f 100644
--- a/libhw_cr/rp2040_dma.h
+++ b/libhw_cr/rp2040_dma.h
@@ -11,6 +11,7 @@
#define _LIBHW_CR_RP2040_DMA_H_
#include <assert.h>
+#include <stddef.h> /* for offsetof() */
#include <stdint.h> /* for uint32_t */
#include <hardware/regs/dreq.h> /* for DREQ_* for use with DMA_CTRL_TREQ_SEL() */
@@ -105,6 +106,8 @@ struct dma_alias3_short3 { READ_ADDR ; };
struct dma_alias2_short3: &dma_channel_hw_addr(CH)->al2_write_addr_trig, \
struct dma_alias3_short3: &dma_channel_hw_addr(CH)->al3_read_addr_trig))
+#define DMA_IS_TRIGGER(TYP, FIELD) (offsetof(TYP, FIELD) == 0xC)
+
#define DMA_CHAN_WR_TRANS_COUNT(TYP) \
(sizeof(TYP)/4)
diff --git a/libhw_cr/rp2040_hwspi.c b/libhw_cr/rp2040_hwspi.c
index d4adb11..646d8ba 100644
--- a/libhw_cr/rp2040_hwspi.c
+++ b/libhw_cr/rp2040_hwspi.c
@@ -30,8 +30,8 @@
#error config.h must define CONFIG_RP2040_SPI_DEBUG (bool)
#endif
-LO_IMPLEMENTATION_C(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi, static)
-LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi, static)
+LO_IMPLEMENTATION_C(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi, static);
+LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi, static);
static void rp2040_hwspi_intrhandler(void *_self, enum dmairq LM_UNUSED(irq), uint LM_UNUSED(channel)) {
struct rp2040_hwspi *self = _self;
@@ -133,6 +133,8 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
self->sema = (cr_sema_t){0};
/* Initialize the interrupt handler. */
+ /* We do this on (just) the rx channel, because the way the
+ * SSP works reads necessarily complete *after* writes. */
dmairq_set_and_enable_exclusive_handler(DMAIRQ_0, self->dma_rx_data, rp2040_hwspi_intrhandler, self);
}
@@ -163,23 +165,43 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
if (!pruned_iovcnt)
return;
- /* For tx_data_blocks, it doesn't really matter which aliases
- * we choose:
+ /* It doesn't *really* matter which aliases we choose:
+ *
* - None of our fields can be NULL (so no
* false-termination).
+ *
* - Moving const fields first so they don't have to be
- * re-programmed each time isn't possible for us there need
- * to be at least 2 const fields, and we only have 1
+ * re-programmed each time isn't possible for us; there
+ * need to be at least 2 const fields, and we only have 1
* (read_addr for rx_data_blocks, and write_addr for
* tx_data_blocks).
*
- * But for rx_data_blocks, we need ctrl to be the trigger
- * register so that the DMA_CTRL_IRQ_QUIET flag isn't cleared
- * before we get to the trigger; and while for tx_data_blocks
- * it doesn't really matter, the inverse would be nice.
+ * The code following this initial declaration is generic to
+ * the alias, so changing which alias is used is easy.
+ *
+ * Since we have no hard requirements, here are some mild
+ * preferences:
+ *
+ * - I like the aliases being different for each channel,
+ * because it helps prevent alias-specific code from
+ * sneaking in.
+ *
+ * - I like the rx channel (the channel the interrupt handler
+ * is wired to) having ctrl be the trigger, so that we
+ * don't have to worry about DMA_CTRL_IRQ_QUIET being
+ * cleared before the trigger, and at the end the control
+ * block is clean and zeroed-out.
+ *
+ * - Conversely, I like the tx channel (the non-interrupt
+ * channel) having ctrl *not* be the trigger, so that
+ * DMA_CTRL_IRQ_QUIET is cleared by the time the trigger
+ * happens, so the IRQ machinery doesn't need to be engaged
+ * at all.
*/
struct dma_alias1 *tx_data_blocks = alloca(sizeof(struct dma_alias1)*(pruned_iovcnt+1));
struct dma_alias0 *rx_data_blocks = alloca(sizeof(struct dma_alias0)*(pruned_iovcnt+1));
+ static_assert(!DMA_IS_TRIGGER(typeof(tx_data_blocks[0]), ctrl));
+ static_assert(DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl));
for (int i = 0, j = 0; i < iovcnt; i++) {
if (!iov[i].iov_len)
@@ -210,6 +232,11 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
}
tx_data_blocks[pruned_iovcnt] = (typeof(tx_data_blocks[0])){0};
rx_data_blocks[pruned_iovcnt] = (typeof(rx_data_blocks[0])){0};
+ /* If ctrl isn't the trigger then we need to make sure that
+ * DMA_CTRL_IRQ_QUIET isn't cleared before the trigger
+ * happens. */
+ if (!DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl))
+ rx_data_blocks[pruned_iovcnt].ctrl = DMA_CTRL_IRQ_QUIET;
/* Set up ctrl. */
DMA_NONTRIGGER(self->dma_tx_ctrl, read_addr) = tx_data_blocks;
diff --git a/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h b/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
index 9d99f7b..4951136 100644
--- a/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
+++ b/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
@@ -20,7 +20,7 @@ enum rp2040_hwspi_instance {
};
struct rp2040_hwspi {
- BEGIN_PRIVATE(LIBHW_RP2040_HWSPI_H)
+ BEGIN_PRIVATE(LIBHW_RP2040_HWSPI_H);
/* const */
LM_IF(IS_IMPLEMENTATION_FOR(LIBHW_RP2040_HWSPI_H))(spi_inst_t)(void) *inst;
uint64_t min_delay_ns;
@@ -34,10 +34,10 @@ struct rp2040_hwspi {
/* mutable */
uint64_t dead_until_ns;
cr_sema_t sema;
- END_PRIVATE(LIBHW_RP2040_HWSPI_H)
+ END_PRIVATE(LIBHW_RP2040_HWSPI_H);
};
-LO_IMPLEMENTATION_H(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi)
-LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi)
+LO_IMPLEMENTATION_H(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi);
+LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi);
/**
* Initialize an instance of `struct rp2040_hwspi`.
diff --git a/libhw_cr/rp2040_include/libhw/w5500.h b/libhw_cr/rp2040_include/libhw/w5500.h
index 51effba..8db6a58 100644
--- a/libhw_cr/rp2040_include/libhw/w5500.h
+++ b/libhw_cr/rp2040_include/libhw/w5500.h
@@ -20,7 +20,7 @@
CR_CHAN_DECLARE(_w5500_sockintr_ch, uint8_t)
struct _w5500_socket {
- BEGIN_PRIVATE(LIBHW_W5500_H)
+ BEGIN_PRIVATE(LIBHW_W5500_H);
/* const-after-init */
uint8_t socknum;
@@ -38,24 +38,24 @@ struct _w5500_socket {
_w5500_sockintr_ch_t write_ch; /* MODE_{TCP,UDP} */
bool list_open, read_open, write_open; /* MODE_TCP */
- END_PRIVATE(LIBHW_W5500_H)
+ END_PRIVATE(LIBHW_W5500_H);
};
-LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcplist)
-LO_IMPLEMENTATION_H(net_stream_listener, struct _w5500_socket, w5500_tcplist)
+LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcplist);
+LO_IMPLEMENTATION_H(net_stream_listener, struct _w5500_socket, w5500_tcplist);
-LO_IMPLEMENTATION_H(io_reader, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(io_writer, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(io_readwriter, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(io_bidi_closer, struct _w5500_socket, w5500_tcp)
-LO_IMPLEMENTATION_H(net_stream_conn, struct _w5500_socket, w5500_tcp)
+LO_IMPLEMENTATION_H(io_reader, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_H(io_writer, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_H(io_readwriter, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_H(io_bidi_closer, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_H(net_stream_conn, struct _w5500_socket, w5500_tcp);
-LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_udp)
-LO_IMPLEMENTATION_H(net_packet_conn, struct _w5500_socket, w5500_udp)
+LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_udp);
+LO_IMPLEMENTATION_H(net_packet_conn, struct _w5500_socket, w5500_udp);
struct w5500 {
- BEGIN_PRIVATE(LIBHW_W5500_H)
+ BEGIN_PRIVATE(LIBHW_W5500_H);
/* const-after-init */
lo_interface spi spidev;
uint pin_intr;
@@ -68,9 +68,9 @@ struct w5500 {
struct _w5500_socket *free;
cr_sema_t intr;
cr_mutex_t mu;
- END_PRIVATE(LIBHW_W5500_H)
+ END_PRIVATE(LIBHW_W5500_H);
};
-LO_IMPLEMENTATION_H(net_iface, struct w5500, w5500_if)
+LO_IMPLEMENTATION_H(net_iface, struct w5500, w5500_if);
/**
* Initialize a WIZnet W5500 Ethernet-and-TCP/IP-offload chip.
diff --git a/libhw_cr/w5500.c b/libhw_cr/w5500.c
index 295add2..fa427d9 100644
--- a/libhw_cr/w5500.c
+++ b/libhw_cr/w5500.c
@@ -126,20 +126,20 @@ static const char *w5500_state_str(uint8_t state) {
/* libobj *********************************************************************/
-LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcplist, static)
-LO_IMPLEMENTATION_C(net_stream_listener, struct _w5500_socket, w5500_tcplist, static)
+LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcplist, static);
+LO_IMPLEMENTATION_C(net_stream_listener, struct _w5500_socket, w5500_tcplist, static);
-LO_IMPLEMENTATION_C(io_reader, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(io_writer, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(io_readwriter, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(io_bidi_closer, struct _w5500_socket, w5500_tcp, static)
-LO_IMPLEMENTATION_C(net_stream_conn, struct _w5500_socket, w5500_tcp, static)
+LO_IMPLEMENTATION_C(io_reader, struct _w5500_socket, w5500_tcp, static);
+LO_IMPLEMENTATION_C(io_writer, struct _w5500_socket, w5500_tcp, static);
+LO_IMPLEMENTATION_C(io_readwriter, struct _w5500_socket, w5500_tcp, static);
+LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcp, static);
+LO_IMPLEMENTATION_C(io_bidi_closer, struct _w5500_socket, w5500_tcp, static);
+LO_IMPLEMENTATION_C(net_stream_conn, struct _w5500_socket, w5500_tcp, static);
-LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_udp, static)
-LO_IMPLEMENTATION_C(net_packet_conn, struct _w5500_socket, w5500_udp, static)
+LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_udp, static);
+LO_IMPLEMENTATION_C(net_packet_conn, struct _w5500_socket, w5500_udp, static);
-LO_IMPLEMENTATION_C(net_iface, struct w5500, w5500_if, static)
+LO_IMPLEMENTATION_C(net_iface, struct w5500, w5500_if, static);
/* mid-level utilities ********************************************************/
@@ -706,7 +706,7 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec
if (count == 0)
return 0;
- struct alarmclock_trigger trigger = {0};
+ struct alarmclock_trigger trigger = {};
if (socket->read_deadline_ns)
LO_CALL(bootclock, add_trigger, &trigger,
socket->read_deadline_ns,
@@ -885,7 +885,7 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
assert(buf);
assert(count);
- struct alarmclock_trigger trigger = {0};
+ struct alarmclock_trigger trigger = {};
if (socket->read_deadline_ns)
LO_CALL(bootclock, add_trigger, &trigger,
socket->read_deadline_ns,
diff --git a/libhw_generic/CMakeLists.txt b/libhw_generic/CMakeLists.txt
index 5a6014b..603f30b 100644
--- a/libhw_generic/CMakeLists.txt
+++ b/libhw_generic/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libhw_generic INTERFACE)
-target_include_directories(libhw_generic SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(libhw_generic PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(libhw_generic INTERFACE
libmisc
libobj
diff --git a/libhw_generic/include/libhw/generic/alarmclock.h b/libhw_generic/include/libhw/generic/alarmclock.h
index 3817b4b..02789f5 100644
--- a/libhw_generic/include/libhw/generic/alarmclock.h
+++ b/libhw_generic/include/libhw/generic/alarmclock.h
@@ -23,14 +23,14 @@
struct alarmclock_trigger;
struct alarmclock_trigger {
- BEGIN_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H)
+ BEGIN_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H);
void *alarmclock;
struct alarmclock_trigger *prev, *next;
uint64_t fire_at_ns;
void (*cb)(void *);
void *cb_arg;
- END_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H)
+ END_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H);
};
/* Interface ******************************************************************/
@@ -57,7 +57,7 @@ struct alarmclock_trigger {
void *cb_arg) \
\
LO_FUNC(void, del_trigger, struct alarmclock_trigger *trigger)
-LO_INTERFACE(alarmclock)
+LO_INTERFACE(alarmclock);
/* Utilities ******************************************************************/
diff --git a/libhw_generic/include/libhw/generic/io.h b/libhw_generic/include/libhw/generic/io.h
index 7825c9f..62ddbb3 100644
--- a/libhw_generic/include/libhw/generic/io.h
+++ b/libhw_generic/include/libhw/generic/io.h
@@ -61,7 +61,7 @@ void io_slice_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, in
*/
#define io_reader_LO_IFACE \
LO_FUNC(ssize_t, readv, const struct iovec *iov, int iovcnt)
-LO_INTERFACE(io_reader)
+LO_INTERFACE(io_reader);
#define io_readv(r, iov, iovcnt) LO_CALL(r, readv, iov, iovcnt)
#define io_read(r, buf, count) LO_CALL(r, readv, &((struct iovec){.iov_base = buf, .iov_len = count}), 1)
@@ -74,7 +74,7 @@ LO_INTERFACE(io_reader)
*/
#define io_writer_LO_IFACE \
LO_FUNC(ssize_t, writev, const struct iovec *iov, int iovcnt)
-LO_INTERFACE(io_writer)
+LO_INTERFACE(io_writer);
#define io_writev(w, iov, iovcnt) LO_CALL(w, writev, iov, iovcnt)
#define io_write(w, buf, count) LO_CALL(w, writev, &((struct iovec){.iov_base = buf, .iov_len = count}), 1)
@@ -83,7 +83,7 @@ LO_INTERFACE(io_writer)
*/
#define io_closer_LO_IFACE \
LO_FUNC(int, close)
-LO_INTERFACE(io_closer)
+LO_INTERFACE(io_closer);
#define io_close(c) LO_CALL(c, close)
/**
@@ -93,7 +93,7 @@ LO_INTERFACE(io_closer)
LO_NEST(io_closer) \
LO_FUNC(int, close_read) \
LO_FUNC(int, close_write)
-LO_INTERFACE(io_bidi_closer)
+LO_INTERFACE(io_bidi_closer);
#define io_close_read(c) LO_CALL(c, close_read)
#define io_close_write(c) LO_CALL(c, close_write)
@@ -103,7 +103,7 @@ LO_INTERFACE(io_bidi_closer)
*/
#define io_duplex_readwriter_LO_IFACE \
LO_FUNC(void, readwritev, const struct duplex_iovec *iov, int iovcnt)
-LO_INTERFACE(io_duplex_readwriter)
+LO_INTERFACE(io_duplex_readwriter);
#define io_readwritev(rw, iov, iovcnt) \
LO_CALL(rw, readwritev, iov, iovcnt)
@@ -113,6 +113,6 @@ LO_INTERFACE(io_duplex_readwriter)
#define io_readwriter_LO_IFACE \
LO_NEST(io_reader) \
LO_NEST(io_writer)
-LO_INTERFACE(io_readwriter)
+LO_INTERFACE(io_readwriter);
#endif /* _LIBHW_GENERIC_IO_H_ */
diff --git a/libhw_generic/include/libhw/generic/net.h b/libhw_generic/include/libhw/generic/net.h
index e88d705..4af574b 100644
--- a/libhw_generic/include/libhw/generic/net.h
+++ b/libhw_generic/include/libhw/generic/net.h
@@ -70,7 +70,7 @@ lo_interface net_stream_conn;
* valid after the listener is closed. \
*/ \
LO_NEST(io_closer)
-LO_INTERFACE(net_stream_listener)
+LO_INTERFACE(net_stream_listener);
#define net_stream_conn_LO_IFACE \
LO_NEST(io_readwriter) \
@@ -88,7 +88,7 @@ LO_INTERFACE(net_stream_listener)
* risk of this overflowing) \
*/ \
LO_FUNC(void, set_read_deadline, uint64_t ns_since_boot)
-LO_INTERFACE(net_stream_conn)
+LO_INTERFACE(net_stream_conn);
/* Packets (e.g. UDP) *********************************************************/
@@ -121,7 +121,7 @@ LO_INTERFACE(net_stream_conn)
uint64_t ns_since_boot) \
\
LO_NEST(io_closer)
-LO_INTERFACE(net_packet_conn)
+LO_INTERFACE(net_packet_conn);
/* Interfaces *****************************************************************/
@@ -139,6 +139,6 @@ struct net_iface_config {
LO_FUNC(lo_interface net_stream_listener, tcp_listen, uint16_t local_port) \
LO_FUNC(lo_interface net_stream_conn , tcp_dial , struct net_ip4_addr remote_node, uint16_t remote_port) \
LO_FUNC(lo_interface net_packet_conn , udp_conn , uint16_t local_port)
-LO_INTERFACE(net_iface)
+LO_INTERFACE(net_iface);
#endif /* _LIBHW_GENERIC_NET_H_ */
diff --git a/libhw_generic/include/libhw/generic/spi.h b/libhw_generic/include/libhw/generic/spi.h
index a4dbcae..4bbf649 100644
--- a/libhw_generic/include/libhw/generic/spi.h
+++ b/libhw_generic/include/libhw/generic/spi.h
@@ -32,6 +32,6 @@ enum spi_mode {
*/
#define spi_LO_IFACE \
LO_NEST(io_duplex_readwriter)
-LO_INTERFACE(spi)
+LO_INTERFACE(spi);
#endif /* _LIBHW_GENERIC_SPI_H_ */
diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt
index 70ec691..4599ead 100644
--- a/libmisc/CMakeLists.txt
+++ b/libmisc/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libmisc INTERFACE)
-target_include_directories(libmisc SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(libmisc PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(libmisc INTERFACE
assert.c
intercept.c
diff --git a/libmisc/include/libmisc/_intercept.h b/libmisc/include/libmisc/_intercept.h
index ab76857..a264144 100644
--- a/libmisc/include/libmisc/_intercept.h
+++ b/libmisc/include/libmisc/_intercept.h
@@ -13,7 +13,7 @@
* own `__lm_` wrappers that GCC/glibc won't interfere with.
*/
-[[format(printf, 1, 2)]]
+[[gnu::format(printf, 1, 2)]]
int __lm_printf(const char *format, ...);
[[noreturn]] void __lm_abort(void);
@@ -21,7 +21,7 @@ int __lm_printf(const char *format, ...);
/* __lm_light_printf is expected to have less stack use than regular
* __lm_printf, if possible. */
-[[format(printf, 1, 2)]]
+[[gnu::format(printf, 1, 2)]]
int __lm_light_printf(const char *format, ...);
#endif /* _LIBMISC__INTERCEPT_H_ */
diff --git a/libmisc/include/libmisc/private.h b/libmisc/include/libmisc/private.h
index c5382a7..5518d1f 100644
--- a/libmisc/include/libmisc/private.h
+++ b/libmisc/include/libmisc/private.h
@@ -11,7 +11,7 @@
#define YES LM_SENTINEL()
#define IS_IMPLEMENTATION_FOR(name) LM_IS_SENTINEL(IMPLEMENTATION_FOR_##name)
-#define BEGIN_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))()(struct {)
-#define END_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))()(} LM_CAT2_(_HIDDEN_, __COUNTER__);)
+#define BEGIN_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))()(struct {) struct {} LM_CAT2_(_PRIVATE_FORCE_SEMICOLON_, __COUNTER__)
+#define END_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))(struct {} LM_CAT2_(_PRIVATE_FORCE_SEMICOLON_, __COUNTER__))(} LM_CAT2_(_PRIVATE_, __COUNTER__))
#endif /* _LIBMISC_PRIVATE_H_ */
diff --git a/libmisc/include/libmisc/rand.h b/libmisc/include/libmisc/rand.h
index 8072841..bb1ec0b 100644
--- a/libmisc/include/libmisc/rand.h
+++ b/libmisc/include/libmisc/rand.h
@@ -29,14 +29,14 @@ static inline uint64_t rand_uint63n(uint64_t cnt) {
uint64_t fair_cnt = ((UINT64_C(1)<<62) / cnt) * cnt;
uint64_t rnd;
do {
- rnd = (random() << 31) | random();
+ rnd = (((uint64_t)random()) << 31) | random();
} while (rnd >= fair_cnt);
return rnd % cnt;
} else if (cnt <= UINT64_C(1)<<63) {
uint64_t fair_cnt = ((UINT64_C(1)<<63) / cnt) * cnt;
uint64_t rnd;
do {
- rnd = (random() << 62) | (random() << 31) | random();
+ rnd = (((uint64_t)random()) << 62) | (((uint64_t)random()) << 31) | random();
} while (rnd >= fair_cnt);
return rnd % cnt;
}
diff --git a/libmisc/tests/test_macro.c b/libmisc/tests/test_macro.c
index 69655d1..1320eb3 100644
--- a/libmisc/tests/test_macro.c
+++ b/libmisc/tests/test_macro.c
@@ -27,7 +27,6 @@ int main() {
/* ... */
test_assert(LM_NEXT_POWER_OF_2(0x8000000000000000-1) == 0x8000000000000000);
/* Valid up to 0x8000000000000000-1 = (1<<63)-1 */
- test_assert(LM_NEXT_POWER_OF_2(0x8000000000000000) == 0); /* :( */
printf("== LM_FLOORLOG2 ===========================================\n");
/* valid down to 1. */
diff --git a/libmisc/tests/test_private.c b/libmisc/tests/test_private.c
index 7aaf1ee..9b39932 100644
--- a/libmisc/tests/test_private.c
+++ b/libmisc/tests/test_private.c
@@ -8,18 +8,18 @@
struct a {
int foo;
- BEGIN_PRIVATE(A)
+ BEGIN_PRIVATE(A);
int bar;
- END_PRIVATE(A)
+ END_PRIVATE(A);
};
#define IMPLEMENTATION_FOR_B YES
struct b {
int foo;
- BEGIN_PRIVATE(B)
+ BEGIN_PRIVATE(B);
int bar;
- END_PRIVATE(B)
+ END_PRIVATE(B);
};
int main() {
diff --git a/libobj/CMakeLists.txt b/libobj/CMakeLists.txt
index 1cc552c..e4d8095 100644
--- a/libobj/CMakeLists.txt
+++ b/libobj/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libobj INTERFACE)
-target_include_directories(libobj SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_include_directories(libobj PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(libobj INTERFACE
libmisc
)
diff --git a/libobj/include/libobj/obj.h b/libobj/include/libobj/obj.h
index d8a528a..7a9041e 100644
--- a/libobj/include/libobj/obj.h
+++ b/libobj/include/libobj/obj.h
@@ -49,7 +49,8 @@
const _lo_##_ARG_iface_name##_vtable *vtable; \
}; \
LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
- _LO_IFACE_PROTO, _ARG_iface_name)
+ _LO_IFACE_PROTO, _ARG_iface_name) \
+ extern int LM_CAT2_(_HIDDEN_BOGUS_, __COUNTER__)
#define _LO_IFACE_VTABLE(_tuple_typ, ...) \
_LO_IFACE_VTABLE_##_tuple_typ(__VA_ARGS__)
#define _LO_IFACE_VTABLE_lo_nest(_ARG_child_iface_name) \
@@ -113,7 +114,8 @@
.self = self, \
.vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable, \
}; \
- }
+ } \
+ extern int LM_CAT2_(_LO_FORCE_SEMICOLON_, __COUNTER__)
/**
* Use `LO_IMPLEMENTATION_C(iface_name, impl_type, impl_name[, static])` in a .c
@@ -135,7 +137,7 @@
_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable = { \
LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
_LO_IMPL_VTABLE, _ARG_impl_name) \
- }; \
+ }
#define _LO_IMPL_PROTO(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _tuple_typ, ...) \
_LO_IMPL_PROTO_##_tuple_typ(_ARG_impl_type, _ARG_impl_name, _ARG_quals, __VA_ARGS__)
diff --git a/libobj/tests/test_nest.c b/libobj/tests/test_nest.c
index c9f9eba..f18b018 100644
--- a/libobj/tests/test_nest.c
+++ b/libobj/tests/test_nest.c
@@ -14,16 +14,16 @@
#define reader_LO_IFACE \
LO_FUNC(ssize_t, read, void *, size_t)
-LO_INTERFACE(reader)
+LO_INTERFACE(reader);
#define writer_LO_IFACE \
LO_FUNC(ssize_t, write, void *, size_t)
-LO_INTERFACE(writer)
+LO_INTERFACE(writer);
#define read_writer_LO_IFACE \
LO_NEST(reader) \
LO_NEST(writer)
-LO_INTERFACE(read_writer)
+LO_INTERFACE(read_writer);
/* implementation header ******************************************************/
@@ -31,15 +31,15 @@ struct myclass {
size_t len;
char buf[512];
};
-LO_IMPLEMENTATION_H(reader, struct myclass, myclass)
-LO_IMPLEMENTATION_H(writer, struct myclass, myclass)
-LO_IMPLEMENTATION_H(read_writer, struct myclass, myclass)
+LO_IMPLEMENTATION_H(reader, struct myclass, myclass);
+LO_IMPLEMENTATION_H(writer, struct myclass, myclass);
+LO_IMPLEMENTATION_H(read_writer, struct myclass, myclass);
/* implementation main ********************************************************/
-LO_IMPLEMENTATION_C(reader, struct myclass, myclass, static)
-LO_IMPLEMENTATION_C(writer, struct myclass, myclass, static)
-LO_IMPLEMENTATION_C(read_writer, struct myclass, myclass, static)
+LO_IMPLEMENTATION_C(reader, struct myclass, myclass, static);
+LO_IMPLEMENTATION_C(writer, struct myclass, myclass, static);
+LO_IMPLEMENTATION_C(read_writer, struct myclass, myclass, static);
static ssize_t myclass_read(struct myclass *self, void *buf, size_t count) {
test_assert(self);
diff --git a/libobj/tests/test_obj.c b/libobj/tests/test_obj.c
index 89fff68..d6861dc 100644
--- a/libobj/tests/test_obj.c
+++ b/libobj/tests/test_obj.c
@@ -17,18 +17,18 @@
LO_FUNC(int, frob1, int) \
/** Function that returns nothing. */ \
LO_FUNC(void, frob0)
-LO_INTERFACE(frobber)
+LO_INTERFACE(frobber);
/* `struct myclass` header ****************************************************/
struct myclass {
int a;
};
-LO_IMPLEMENTATION_H(frobber, struct myclass, myclass)
+LO_IMPLEMENTATION_H(frobber, struct myclass, myclass);
/* `struct myclass` implementation ********************************************/
-LO_IMPLEMENTATION_C(frobber, struct myclass, myclass, static)
+LO_IMPLEMENTATION_C(frobber, struct myclass, myclass, static);
static int myclass_frob(struct myclass *self) {
test_assert(self);
diff --git a/libusb/CMakeLists.txt b/libusb/CMakeLists.txt
index 012ab71..9be44ac 100644
--- a/libusb/CMakeLists.txt
+++ b/libusb/CMakeLists.txt
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
add_library(libusb INTERFACE)
-target_include_directories(libusb SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+target_include_directories(libusb PUBLIC INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_sources(libusb INTERFACE
usb_common.c
)
diff --git a/notes.md b/notes.md
index 497363e..8f2f44c 100644
--- a/notes.md
+++ b/notes.md
@@ -166,3 +166,24 @@ Compression is an optional feature introduced in HDMI 2.1 :(
----
PIO-based USB needs the system clock to be a multiple of 120mhz
+
+----
+
+https://electronics.stackexchange.com/questions/684221/usb-c-on-off-switch-design-that-is-pd-compatible-off-similar-to-cold-plugging
+
+----
+
+OpenBMC
+
+~$300 price range:
+ - Motherboards with a BMC
+ - Old proprietary IP-KVM
+ - TinyPilot KVM
+ - PiKVM
+~$60 price range:
+ - DIY PiKVM
+ - JetKVM
+~$30 price range
+ - Sipeed NanoKVM
+ - https://github.com/stefanklug/picoKVM (~$20 Arduino, ~$5 HDMI
+ capture), non-IP