diff options
Diffstat (limited to 'cmd/sbc_harness')
20 files changed, 967 insertions, 87 deletions
diff --git a/cmd/sbc_harness/CMakeLists.txt b/cmd/sbc_harness/CMakeLists.txt index 6199e0c..6e722d7 100644 --- a/cmd/sbc_harness/CMakeLists.txt +++ b/cmd/sbc_harness/CMakeLists.txt @@ -1,6 +1,6 @@ # cmd/sbc_harness/CMakeLists.txt - Build script for main sbc_harness.uf2 firmware file # -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later if (PICO_PLATFORM STREQUAL "rp2040") @@ -10,23 +10,42 @@ if (PICO_PLATFORM STREQUAL "rp2040") 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}) +target_include_directories(sbc_harness_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(sbc_harness_objs - pico_stdlib + pico_runtime + pico_stdio_uart + hardware_flash + hardware_watchdog libmisc + libfmt libusb libdhcp - libhw + libhw_cr + lib9p_srv + lib9p_util +) +pico_minimize_runtime(sbc_harness_objs + INCLUDE PRINTF PRINTF_MINIMAL PRINTF_LONG_LONG PRINTF_PTRDIFF_T +) +target_compile_definitions(sbc_harness_objs PRIVATE + #PICO_USE_FASTEST_SUPPORTED_CLOCK=1 + + # Calculated by `./3rd-party/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py --cmake-only 170` + PLL_SYS_REFDIV=2 + PLL_SYS_VCO_FREQ_HZ=1530000000 + PLL_SYS_POSTDIV1=3 + PLL_SYS_POSTDIV2=3 + SYS_CLK_HZ=170000000 ) -pico_minimize_runtime(sbc_harness_objs) -pico_enable_stdio_usb(sbc_harness_objs 0) -pico_enable_stdio_uart(sbc_harness_objs 1) -pico_enable_stdio_semihosting(sbc_harness_objs 0) -pico_enable_stdio_rtt(sbc_harness_objs 0) suppress_tinyusb_warnings() @@ -37,10 +56,6 @@ add_stack_analysis(sbc_harness_stack.c sbc_harness_objs) # Link ######################################################################### add_executable(sbc_harness) -set_source_files_properties("$<TARGET_OBJECTS:sbc_harness_objs>" PROPERTIES - EXTERNAL_OBJECT true - GENERATED true -) target_sources(sbc_harness PRIVATE sbc_harness_stack.c "$<TARGET_OBJECTS:sbc_harness_objs>" @@ -48,7 +63,22 @@ target_sources(sbc_harness PRIVATE target_link_libraries(sbc_harness pico_standard_link ) +target_link_options(sbc_harness PRIVATE "$<TARGET_PROPERTY:sbc_harness_objs,LINK_OPTIONS>") pico_add_extra_outputs(sbc_harness) # create .map/.bin/.hex/.uf2 files in addition to .elf pico_set_program_url(sbc_harness "https://git.lukeshu.com/sbc-harness") +# Embed ######################################################################## + +target_embed_sources(sbc_harness_objs sbc_harness static.h + static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md + static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt + static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt + static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt + static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt + 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 b557287..ca23462 100644 --- a/cmd/sbc_harness/config/config.h +++ b/cmd/sbc_harness/config/config.h @@ -1,18 +1,24 @@ /* config.h - Compile-time configuration for sbc_harness * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ #ifndef _CONFIG_H_ #define _CONFIG_H_ -/* W5500 **********************************************************************/ +#include <stddef.h> /* for size_t */ -/** - * How many W5500 chips we have. - */ -#define CONFIG_W5500_NUM 1 +#define CONFIG_FLASH_DEBUG 1 + +#define _CONFIG_9P_MAX_CONNS 3 /* FIXME: bump this back up to 8 */ +#define _CONFIG_9P_MAX_REQS (2*_CONFIG_9P_MAX_CONNS) + +/* RP2040 *********************************************************************/ + +#define CONFIG_RP2040_SPI_DEBUG 1 /* bool */ + +/* W5500 **********************************************************************/ /** * When allocating an arbitrary local port, what range should it be @@ -25,9 +31,26 @@ #define CONFIG_W5500_LOCAL_PORT_MIN 32768 #define CONFIG_W5500_LOCAL_PORT_MAX 60999 +#define CONFIG_W5500_VALIDATE_SPI 1 /* bool */ +#define CONFIG_W5500_DEBUG 0 /* bool */ +#define CONFIG_W5500_LL_DEBUG 0 /* bool */ + /* 9P *************************************************************************/ -#define CONFIG_9P_PORT 564 +#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ + +#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */ + +/* 9P_SRV *********************************************************************/ + +#define CONFIG_9P_SRV_DEBUG 1 /* bool */ + +#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24) + /** * This max-msg-size is sized so that a Twrite message can return * 8KiB of data. @@ -45,41 +68,53 @@ * negotiated. In Plan 9 1e it was (8*1024)+128, and was bumped to * (8*1024)+160 in 2e and 3e. */ -#define CONFIG_9P_MAX_MSG_SIZE ((4*1024)+24) +#define CONFIG_9P_SRV_MAX_MSG_SIZE ((4*1024)+24) /** * Maximum host-data-structure size. A message may be larger in * unmarshaled-host-structures than marshaled-net-bytes due to (1) - * struct padding, (2) nul-terminator byes for strings. + * struct padding, (2) array pointers. */ -#define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16 -#define CONFIG_9P_MAX_FIDS 16 -#define CONFIG_9P_MAX_REQS 2 -#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ -#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ +#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16 /* DHCP ***********************************************************************/ #define CONFIG_DHCP_CAN_RECV_UNICAST_IP_WITHOUT_IP 0 /* bool */ -#define CONFIG_DHCP_DEBUG 0 /* bool */ +#define CONFIG_DHCP_DEBUG 1 /* bool */ #define CONFIG_DHCP_OPT_SIZE 312 /* minimum of 312 */ #define CONFIG_DHCP_SELECTING_NS (5*NS_PER_S) +/* USB KEYBOARD ***************************************************************/ + +/** + * Which USB port to use for the Root Hub. + * + * The RP2040 only has 1 port, so it's gotta be port #0. + */ +#define CONFIG_USB_COMMON_RHPORT 0 + +#define CONFIG_USB_COMMON_DEBUG 1 /* bool */ + /* COROUTINE ******************************************************************/ -#define CONFIG_COROUTINE_DEFAULT_STACK_SIZE (2*1024) +extern const size_t CONFIG_COROUTINE_STACK_SIZE_dhcp_cr; +extern const size_t CONFIG_COROUTINE_STACK_SIZE_init_cr; +extern const size_t CONFIG_COROUTINE_STACK_SIZE_read9p_cr; +extern const size_t CONFIG_COROUTINE_STACK_SIZE_write9p_cr; +extern const size_t CONFIG_COROUTINE_STACK_SIZE_usb_common_cr; +extern const size_t CONFIG_COROUTINE_STACK_SIZE_usb_keyboard_cr; +extern const size_t CONFIG_COROUTINE_STACK_SIZE_w5500_irq_cr; +#define CONFIG_COROUTINE_NAME_LEN 16 #define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */ #define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */ #define CONFIG_COROUTINE_DEBUG 0 /* bool */ #define CONFIG_COROUTINE_VALGRIND 0 /* bool */ +#define CONFIG_COROUTINE_GDB 1 /* bool */ -#define _CONFIG_9P_NUM_SOCKS 7 -#define CONFIG_COROUTINE_NUM ( \ - 1 /* usb_common */ + \ - 1 /* usb_keyboard */ + \ - CONFIG_W5500_NUM /* irq handler */ + \ - _CONFIG_9P_NUM_SOCKS /* 9P accept()+read() */ + \ - (CONFIG_9P_MAX_REQS*_CONFIG_9P_NUM_SOCKS) /* 9P work+write() */ ) +#define CONFIG_COROUTINE_NUM ( \ + 1 /* usb_common */ + \ + 1 /* usb_keyboard */ + \ + 1 /* W5500 irq handler */ + \ + _CONFIG_9P_MAX_CONNS /* 9P accept()+read() */ + \ + _CONFIG_9P_MAX_REQS /* 9P work+write() */ ) #endif /* _CONFIG_H_ */ diff --git a/cmd/sbc_harness/config/tusb_config.h b/cmd/sbc_harness/config/tusb_config.h index aeff31c..5240311 100644 --- a/cmd/sbc_harness/config/tusb_config.h +++ b/cmd/sbc_harness/config/tusb_config.h @@ -1,6 +1,6 @@ /* tusb_config.h - Compile-time configuration for the TinyUSB library * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later * * SPDX-License-Identifier: MIT @@ -36,16 +36,35 @@ extern "C" { #endif //-------------------------------------------------------------------- -// TinyUSB Device (TUD) initialization for rp2040-based boards +// Override the default definition of TU_ASSERT() to use our logging //-------------------------------------------------------------------- -// Which USB port to use for the RootHub. -// The rp2040 only has 1 port, so it's gotta be port #0. -#define BOARD_TUD_RHPORT 0 - -// RHPort max operational speed. -// Use OPT_MODE_DEFAULT_SPEED for max speed supported by MCU. -#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +// "magically" select between the 1-arg and 2-args variants, inject a +// stringified version of `_cond`. +// +// Note: Use GNU-C `, ##__VA_ARGS__`, not standard C `__VA_OPT__(,) +// __VA_ARGS__`; because __VA_OPT__ doesn't handle present-but-empty +// arguments the way that we need. +#define TU_ASSERT(_cond, ...) \ + _GET_3RD_ARG(_cond, ##__VA_ARGS__, \ + _LIBMISC_TU_ASSERT_2ARGS, _LIBMISC_TU_ASSERT_1ARGS, _dummy) \ + (_cond, #_cond, ##__VA_ARGS__) + +#define _LIBMISC_TU_ASSERT_1ARGS(_cond, _cond_str) \ + _LIBMISC_TU_ASSERT_2ARGS(_cond, _cond_str, false) + +#define _LIBMISC_TU_ASSERT_2ARGS(_cond, _cond_str, _ret) \ + do { \ + if ( !(_cond) ) { \ + _libmisc_tu_mess_failed(_cond_str, \ + __FILE__, __LINE__, __func__); \ + TU_BREAKPOINT(); \ + return _ret; \ + } \ + } while (0) + +void _libmisc_tu_mess_failed(const char *expr, + const char *file, unsigned int line, const char *func); //-------------------------------------------------------------------- // Configuration: common @@ -66,13 +85,13 @@ extern "C" { // Tinyusb use follows macros to declare transferring memory so that they can be put // into those specific section. // e.g -// - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) -// - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) +// - CFG_TUSB_MEM SECTION : [[gnu::section(".usb_ram")]] +// - CFG_TUSB_MEM_ALIGN : [[gnu::aligned(4)]] #define CFG_TUSB_MEM_SECTION /* blank */ -#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#define CFG_TUSB_MEM_ALIGN [[gnu::aligned(4)]] #define CFG_TUD_ENABLED 1 -#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED +#define CFG_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED //-------------------------------------------------------------------- // Configuration: TinyUSB Device (TUD) 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..5920b85 --- /dev/null +++ b/cmd/sbc_harness/fs_harness_flash_bin.c @@ -0,0 +1,307 @@ +/* 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 <string.h> + +#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_srv_stat flash_file_stat(struct flash_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + + return (struct lib9p_srv_stat){ + .qid = flash_file_qid(self), + .mode = LIB9P_DM_EXCL|0666, + .atime_sec = UTIL9P_ATIME, + .mtime_sec = UTIL9P_MTIME, + .size = DATA_SIZE, + .name = lib9p_str(self->name), + .owner_uid = { .name = lib9p_str("root"), .num = 0 }, + .owner_gid = { .name = lib9p_str("root"), .num = 0 }, + .last_modifier_uid = { .name = lib9p_str("root"), .num = 0 }, + .extension = lib9p_str(NULL), + }; +} +static void flash_file_wstat(struct flash_file *self, struct lib9p_srv_ctx *ctx, + struct lib9p_srv_stat) { + assert(self); + assert(ctx); + + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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, + LIB9P_ERRNO_L_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, + LIB9P_ERRNO_L_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, + LIB9P_ERRNO_L_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..f7b755f --- /dev/null +++ b/cmd/sbc_harness/fs_harness_uptime_txt.c @@ -0,0 +1,151 @@ +/* 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 <libhw/generic/alarmclock.h> +#include <util9p/static.h> +#include <libmisc/alloc.h> /* for heap_alloc(), free() */ + +#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_srv_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_srv_stat){ + .qid = uptime_file_qid(self), + .mode = 0444, + .atime_sec = UTIL9P_ATIME, + .mtime_sec = UTIL9P_MTIME, + .size = size, + .name = lib9p_str(self->name), + .owner_uid = { .name = lib9p_str("root"), .num = 0 }, + .owner_gid = { .name = lib9p_str("root"), .num = 0 }, + .last_modifier_uid = { .name = lib9p_str("root"), .num = 0 }, + .extension = lib9p_str(NULL), + }; +} +static void uptime_file_wstat(struct uptime_file *self, struct lib9p_srv_ctx *ctx, + struct lib9p_srv_stat) { + assert(self); + assert(ctx); + + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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 = heap_alloc(1, 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, + LIB9P_ERRNO_L_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, LIB9P_ERRNO_L_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 17d32cf..5630e83 100644 --- a/cmd/sbc_harness/main.c +++ b/cmd/sbc_harness/main.c @@ -1,25 +1,127 @@ /* sbc_harness/main.c - Main entry point and event loop for sbc-harness * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include <string.h> /* libc: for strlen() */ -#include <stdio.h> /* libc: for printf() */ +/* libc */ +#include <string.h> /* libc: for strlen() */ -#include <pico/stdlib.h> /* pico-sdk:pico_stdlib: for stdio_uart_init() */ -#include <hardware/flash.h> /* pico-sdk:hardware_flash: for flash_get_unique_id() */ +/* pico-sdk */ +#include <pico/stdio_uart.h> /* pico-sdk:pico_stdio_uart: for stdio_uart_init() */ +#include <hardware/flash.h> /* pico-sdk:hardware_flash: for flash_get_unique_id() */ +/* our OS */ #include <libcr/coroutine.h> +#include <libhw/generic/alarmclock.h> /* so we can set `bootclock` */ #include <libhw/rp2040_hwspi.h> +#include <libhw/rp2040_hwtimer.h> #include <libhw/w5500.h> -#include <libmisc/hash.h> -#include <libusb/usb_common.h> + +/* our application libraries */ #include <libdhcp/client.h> +#include <libusb/usb_common.h> +#include <lib9p/srv.h> +#include <util9p/static.h> +/* our utility libraries */ +#include <libmisc/hash.h> +#define LOG_NAME MAIN +#include <libmisc/log.h> + +/* local headers */ #include "usb_keyboard.h" +#include "static.h" +#include "fs_harness_flash_bin.h" +#include "fs_harness_uptime_txt.h" + +/* configuration **************************************************************/ + +#include "config.h" + +#ifndef _CONFIG_9P_MAX_CONNS + #error config.h must define _CONFIG_9P_MAX_CONNS +#endif +#ifndef _CONFIG_9P_MAX_REQS + #error config.h must define _CONFIG_9P_MAX_REQS +#endif + +/* file tree ******************************************************************/ + +enum { PATH_BASE = __COUNTER__ }; +#define PATH_COUNTER __COUNTER__ - PATH_BASE + +#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", + STATIC_FILE("YOUR_RIGHTS_AND_OBLIGATIONS.md", + .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_md_start, + .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_md_end), + STATIC_DIR("YOUR_RIGHTS_AND_OBLIGATIONS", + STATIC_FILE("agpl-3.0.txt", + .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_agpl_3_0_txt_start, + .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_agpl_3_0_txt_end), + STATIC_FILE("pico-sdk.bsd3.txt", + .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_start, + .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_pico_sdk_bsd3_txt_end), + STATIC_FILE("printf.mit.txt", + .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_printf_mit_txt_start, + .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_printf_mit_txt_end), + STATIC_FILE("tinyusb.mit.txt", + .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_start, + .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_tinyusb_mit_txt_end), + STATIC_FILE("newlib.txt", + .data_start = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_start, + .data_end = _binary_static_Documentation_YOUR_RIGHTS_AND_OBLIGATIONS_newlib_txt_end), + ), + STATIC_FILE("harness_rom_bin.txt", + .data_start = _binary_static_Documentation_harness_rom_bin_txt_start, + .data_end = _binary_static_Documentation_harness_rom_bin_txt_end), + 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), + API_FILE("flash.bin", + flash), + API_FILE("uptime.txt", + uptime), + // TODO: system.log + // TODO: proc.txt + // TODO: cpuinfo.txt + // TODO: ctl + ), + STATIC_DIR("dut", + // TODO: hdmi.nut + // TODO: uart.txt + // TODO: usb-keyboard.txt + ), + ); + +static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) { + return root; +} -COROUTINE hello_world_cr(void *_chan) { +/* Code ***********************************************************************/ + +/* +static COROUTINE hello_world_cr(void *_chan) { const char *msg = "Hello world!\n"; usb_keyboard_rpc_t *chan = _chan; cr_begin(); @@ -27,28 +129,52 @@ COROUTINE hello_world_cr(void *_chan) { for (size_t i = 0;; i = (i+1) % strlen(msg)) { int result = usb_keyboard_rpc_send_req(chan, (uint32_t)msg[i]); if (result < 1) { - printf("error!\n"); + errorf("error sending rune U+%d", (uint32_t)msg[i]); break; } } cr_end(); } +*/ + +struct { + struct rp2040_hwspi dev_spi; + struct w5500 dev_w5500; + usb_keyboard_rpc_t keyboard_chan; + uint16_t usb_serial[sizeof(uint64_t)*2]; /* UTF-16 */ + struct lib9p_srv srv; +} globals; -COROUTINE dhcp_cr(void *_chip) { - struct w5500 *chip = _chip; +static COROUTINE dhcp_cr(void *) { cr_begin(); - dhcp_client_main(chip, "harness"); + dhcp_client_main(lo_box_w5500_if_as_net_iface(&globals.dev_w5500), "harness"); cr_end(); } -struct { - struct rp2040_hwspi dev_spi; - struct w5500 dev_w5500; - usb_keyboard_rpc_t keyboard_chan; -} globals; +static COROUTINE read9p_cr(void *) { + cr_begin(); + + lo_interface net_iface iface = lo_box_w5500_if_as_net_iface(&globals.dev_w5500); + lo_interface net_stream_listener listener = LO_CALL(iface, tcp_listen, LIB9P_DEFAULT_PORT_9FS); + + lib9p_srv_accept_and_read_loop(&globals.srv, listener); + + cr_end(); +} + +static COROUTINE write9p_cr(void *) { + cr_begin(); + + lib9p_srv_worker_loop(&globals.srv); + + cr_end(); +} + +const char *const hexdig = "0123456789ABCDEF"; +static_assert(_CONFIG_9P_MAX_REQS <= 16); COROUTINE init_cr(void *) { cr_begin(); @@ -69,12 +195,16 @@ COROUTINE init_cr(void *) { rp2040_hwspi_init(&globals.dev_spi, "W5500", RP2040_HWSPI_0, SPI_MODE_0, /* the W5500 supports mode 0 or mode 3 */ - 5*1000*1000, /* run at the W5500's max rate of 80MHz */ - 16, /* PIN_MISO */ - 19, /* PIN_MOSI */ - 18, /* PIN_CLK */ - 17); /* PIN_CS */ - w5500_init(&globals.dev_w5500, "W5500", &globals.dev_spi, + 42500000, /* min(w5500, hwspi); w5500=80MHz; hwspi=42.5MHz, see rp2040_hwspi.h for a comment about why this is so low */ + 30, /* W5500 datasheet says min(T_CS = SCSn High Time) = 30ns */ + 0, /* bogus write write data when doing a read */ + 16, /* PIN_MISO */ + 19, /* PIN_MOSI */ + 18, /* PIN_CLK */ + 17, /* PIN_CS */ + 0, 1, 2, 3); /* DMA channels */ + w5500_init(&globals.dev_w5500, "W5500", + lo_box_rp2040_hwspi_as_spi(&globals.dev_spi), 21, /* PIN_INTR */ 20, /* PIN_RESET */ ((struct net_eth_addr){{ @@ -84,23 +214,40 @@ COROUTINE init_cr(void *) { flash_id24[0], flash_id24[1], flash_id24[2], }})); - usb_common_earlyinit(); + static_assert(sizeof(flash_id64)*2 == LM_ARRAY_LEN(globals.usb_serial)); + for (size_t i = 0; i < LM_ARRAY_LEN(globals.usb_serial); i++) + globals.usb_serial[i] = hexdig[(flash_id64 >> ((sizeof(flash_id64)*8)-((i+1)*4))) & 0xF]; + usb_common_earlyinit(globals.usb_serial, sizeof(globals.usb_serial)); usb_keyboard_init(); usb_common_lateinit(); globals.keyboard_chan = (usb_keyboard_rpc_t){0}; + globals.srv.rootdir = get_root; + /* set up coroutines **************************************************/ coroutine_add("usb_common", usb_common_cr, NULL); coroutine_add("usb_keyboard", usb_keyboard_cr, &globals.keyboard_chan); - //coroutine_add("hello_world", hello_world_cr, &keyboard_chan); - coroutine_add_with_stack_size(4*1024, "dhcp", dhcp_cr, &globals.dev_w5500); + //coroutine_add("hello_world", hello_world_cr, &globals.keyboard_chan); + coroutine_add("dhcp", dhcp_cr, NULL); + for (int i = 0; i < _CONFIG_9P_MAX_CONNS; i++) { + char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; + coroutine_add(name, read9p_cr, NULL); + } + for (int i = 0; i < _CONFIG_9P_MAX_REQS; i++) { + char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; + coroutine_add(name, write9p_cr, NULL); + } cr_exit(); } int main() { + bootclock = rp2040_hwtimer(0); stdio_uart_init(); + /* char *hdr = "=" * (80-strlen("info : MAIN: ")); */ + infof("==================================================================="); coroutine_add("init", init_cr, NULL); coroutine_main(); + assert_notreached("all coroutines exited"); } diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md new file mode 100644 index 0000000..b3fc12e --- /dev/null +++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md @@ -0,0 +1,29 @@ +<!-- + Documentation/YOUR_RIGHTS_AND_OBLIGATIONS.md - Overview of your + rights and obligations with regard to the SBC-Harness firmware + + Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + SPDX-License-Identifier: AGPL-3.0-or-later +--> + +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 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 +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 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 verbatim-or-modified copies of the + firmware. diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt new file mode 120000 index 0000000..8a9b6f3 --- /dev/null +++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/agpl-3.0.txt @@ -0,0 +1 @@ +../../../../../COPYING.txt
\ No newline at end of file diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt new file mode 120000 index 0000000..5c5939c --- /dev/null +++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/newlib.txt @@ -0,0 +1 @@ +../../../../../3rd-party/COPYING.newlib.txt
\ No newline at end of file diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt new file mode 120000 index 0000000..52c4374 --- /dev/null +++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/pico-sdk.bsd3.txt @@ -0,0 +1 @@ +../../../../../3rd-party/pico-sdk/LICENSE.TXT
\ No newline at end of file diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt new file mode 120000 index 0000000..5a6e342 --- /dev/null +++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/printf.mit.txt @@ -0,0 +1 @@ +../../../../../3rd-party/pico-sdk/src/rp2_common/pico_printf/LICENSE
\ No newline at end of file diff --git a/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt new file mode 120000 index 0000000..22a67cf --- /dev/null +++ b/cmd/sbc_harness/static/Documentation/YOUR_RIGHTS_AND_OBLIGATIONS/tinyusb.mit.txt @@ -0,0 +1 @@ +../../../../../3rd-party/pico-sdk/lib/tinyusb/LICENSE
\ No newline at end of file diff --git a/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt b/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt new file mode 100644 index 0000000..115f2ee --- /dev/null +++ b/cmd/sbc_harness/static/Documentation/harness_flash_bin.txt @@ -0,0 +1,33 @@ +NAME + /harness/flash.bin + +DESCRIPTION + Access to the flash storage chip (where the harness firmware + is stored). + + 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 + than it was compiled for, then the upper part of the chip + will not be accessible with this file. If the firmware is + loaded onto hardware with a smaller flash chip than it was + compiled for, then accessing the missing upper part of the + chip will crash. + + - 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> + SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/cmd/sbc_harness/static/Documentation/harness_rom_bin.txt b/cmd/sbc_harness/static/Documentation/harness_rom_bin.txt new file mode 100644 index 0000000..63fd0a3 --- /dev/null +++ b/cmd/sbc_harness/static/Documentation/harness_rom_bin.txt @@ -0,0 +1,41 @@ +NAME + /harness/rom.bin + +DESCRIPTION + Read access to the RP2040 CPU's ROM. This contains code that + initializes the chip to load the main firmware from the + external flash chip, provides a failsafe USB-programmable + mode, and provides a few functions that the main firmware can + call to. + +BUGS + This ROM is programmed into the chip at the factory; revising + it means issuing a new revison of the RP2040 CPU. So while + the source code to the ROM is freely available to be used, + studied, and shared; one cannot install modified versions onto + the CPU. + +HISTORY + - RP2040 B0 : chips manufactured before September 2020 or so + - RP2040 B1 : chips manufactured after September 2020 or so + - Released to the public January 2021; chance whether you get + a B0 or a B1 chip. + - RP2040 B2 : released September 2021 + + Printed on the physical CPU is a label that indicates which + revision it is. For example: + + RP2-B2 21/24 + + indicates that it is the "B2" revision (and was manufactured + the 21st week (late May) of 2024). + +SEE ALSO + - /harness/cpuinfo.txt can report which CPU version you have. + + - The source code to each ROM revision is published at + https://github.com/raspberrypi/pico-bootrom-rp2040 + +AUTHOR + Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + SPDX-License-Identifier: AGPL-3.0-or-later 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/cmd/sbc_harness/tusb_log.c b/cmd/sbc_harness/tusb_log.c new file mode 100644 index 0000000..4c6b7df --- /dev/null +++ b/cmd/sbc_harness/tusb_log.c @@ -0,0 +1,15 @@ +/* sbc_harness/tusb_log.c - Logger for tusb_config.h + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#define LOG_NAME TINY_USB +#include <libmisc/log.h> + +void _libmisc_tu_mess_failed(const char *expr, + const char *file, unsigned int line, const char *func) { + errorf("%s:%u:%s(): assertion \"%s\" failed", + file, line, func, + expr); +} diff --git a/cmd/sbc_harness/usb_keyboard.c b/cmd/sbc_harness/usb_keyboard.c index 3500e31..7dd8a24 100644 --- a/cmd/sbc_harness/usb_keyboard.c +++ b/cmd/sbc_harness/usb_keyboard.c @@ -1,6 +1,6 @@ /* sbc_harness/usb_keyboard.c - Implementation of a USB keyboard device * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -8,11 +8,10 @@ #include <libusb/tusb_helpers.h> /* for TUD_ENDPOINT_IN */ #include <libusb/usb_common.h> +#include <libmisc/macro.h> #include "usb_keyboard.h" -#define UNUSED(name) /* name __attribute__ ((unused)) */ - /** * A USB-HID "Report Descriptor" (see USB-HID 1.11 ยง6.2.2 "Report * Descriptor") describing a keyboard. @@ -24,7 +23,6 @@ static uint8_t kbd_ifc = 0; void usb_keyboard_init() { if (kbd_ifc) return; - usb_common_earlyinit(); kbd_ifc = usb_add_interface(cfgnum_std, TUD_HID_DESC_LEN, (uint8_t[]){ /* USB-HID input-only descriptor for inclusion in the config descriptor; consisting of 3 parts: @@ -56,8 +54,8 @@ COROUTINE usb_keyboard_cr(void *_chan) { while (!tud_hid_n_ready(kbd_ifc)) cr_yield(); - if (usb_keyboard_rpc_can_recv_req(chan)) { - usb_keyboard_rpc_req_t req = usb_keyboard_rpc_recv_req(chan); + if (cr_rpc_can_recv_req(chan)) { + usb_keyboard_rpc_req_t req = cr_rpc_recv_req(chan); uint32_t rune = req.req; modifier = ascii2keycode[rune][0] ? KEYBOARD_MODIFIER_LEFTSHIFT : 0; @@ -71,7 +69,7 @@ COROUTINE usb_keyboard_cr(void *_chan) { keycodes[0] = 0; tud_hid_n_keyboard_report(kbd_ifc, report_id, modifier, keycodes); - usb_keyboard_rpc_send_resp(req, 1); + cr_rpc_send_resp(req, 1); } else { modifier = 0; keycodes[0] = 0; @@ -97,7 +95,7 @@ uint8_t const *tud_hid_descriptor_report_cb(uint8_t index) { uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { - // TODO not Implemented + // TODO not implemented (void) instance; (void) report_id; (void) report_type; @@ -109,7 +107,11 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t // Invoked when received SET_REPORT control request or // received data on OUT endpoint ( Report ID = 0, Type = 0 ) -void tud_hid_set_report_cb(uint8_t UNUSED(instance), uint8_t UNUSED(report_id), hid_report_type_t UNUSED(report_type), uint8_t const *UNUSED(buffer), uint16_t UNUSED(bufsize)) +void tud_hid_set_report_cb(uint8_t LM_UNUSED(instance), + uint8_t LM_UNUSED(report_id), + hid_report_type_t LM_UNUSED(report_type), + uint8_t const *LM_UNUSED(buffer), + uint16_t LM_UNUSED(bufsize)) { // TODO not implemented } diff --git a/cmd/sbc_harness/usb_keyboard.h b/cmd/sbc_harness/usb_keyboard.h index 210014d..cf8483b 100644 --- a/cmd/sbc_harness/usb_keyboard.h +++ b/cmd/sbc_harness/usb_keyboard.h @@ -1,6 +1,6 @@ /* sbc_harness/usb_keyboard.h - Implementation of a USB keyboard device * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -12,7 +12,7 @@ #include <libcr/coroutine.h> /* for COROUTINE */ #include <libcr_ipc/rpc.h> /* for CR_RPC_DECLARE */ -CR_RPC_DECLARE(usb_keyboard_rpc, uint32_t, int) +CR_RPC_DECLARE(usb_keyboard_rpc, uint32_t, int); void usb_keyboard_init(void); COROUTINE usb_keyboard_cr(void *arg); |