diff options
-rw-r--r-- | CMakeLists.txt | 6 | ||||
-rw-r--r-- | coroutine.c | 58 | ||||
-rw-r--r-- | coroutine.h | 72 | ||||
-rw-r--r-- | main.c | 45 | ||||
-rw-r--r-- | tusb_config.h | 3 | ||||
-rw-r--r-- | usb_common.c | 6 | ||||
-rw-r--r-- | usb_common.h | 6 | ||||
-rw-r--r-- | usb_keyboard.c | 70 | ||||
-rw-r--r-- | usb_keyboard.h | 23 |
9 files changed, 243 insertions, 46 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index bb5abcf..35a63ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,8 @@ +# CMAkeLists.txt - Main build script for sbc-harness +# +# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-Licence-Identifier: AGPL-3.0-or-later + cmake_minimum_required(VERSION 3.13) set(PICO_SDK_PATH "${CMAKE_SOURCE_DIR}/3rd-party/pico-sdk") @@ -10,6 +15,7 @@ pico_sdk_init() add_executable(sbc_harness) target_sources(sbc_harness PUBLIC main.c + coroutine.c usb_common.c usb_keyboard.c ) diff --git a/coroutine.c b/coroutine.c new file mode 100644 index 0000000..3228663 --- /dev/null +++ b/coroutine.c @@ -0,0 +1,58 @@ +/* coroutine.c - Simple coroutine and request/response implementation + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + +#include <stdlib.h> /* for malloc(pico_malloc) and realloc(pico_malloc) */ + +#include "coroutine.h" + +static cid_t coroutine_table_len = 0; +_cr_entry_t *_coroutine_table = NULL; +cid_t _cur_cid = 0; + +void coroutine_init(void) { + if (coroutine_table_len) + return; + coroutine_table_len = 1; + _coroutine_table = malloc(sizeof _coroutine_table[0]); + _coroutine_table[0] = (_cr_entry_t){ + .fn = (cr_fn_t)0xDEAD, + .stack = NULL, + .state = 0, + .blocked = true, + }; +} + +cid_t coroutine_add(cr_fn_t fn, void *stack) { + cid_t cid = 0; + for (cid_t i = 1; cid == 0 && i < coroutine_table_len; i++) + if (_coroutine_table[i].fn == NULL) + cid = i; + if (cid = 0) { + cid = coroutine_table_len++; + _coroutine_table = realloc(_coroutine_table, (sizeof _coroutine_table[0]) * coroutine_table_len); + } + _coroutine_table[cid] = (_cr_entry_t){ + .fn = fn, + .stack = stack, + .state = 0, + .blocked = false, + }; + return cid; +} + +void coroutine_task(void) { + cid_t start = (_cur_cid + 1) % coroutine_table_len; + cid_t shift; + for (shift = 0; + shift < coroutine_table_len && + (_coroutine_table[(start+shift)%coroutine_table_len].fn == NULL || + _coroutine_table[(start+shift)%coroutine_table_len].blocked); + shift++) {} + if (shift == coroutine_table_len) + return; + _cur_cid = (start + shift) % coroutine_table_len; + _coroutine_table[_cur_cid].fn(_coroutine_table[_cur_cid].stack); +} diff --git a/coroutine.h b/coroutine.h new file mode 100644 index 0000000..77cb0a3 --- /dev/null +++ b/coroutine.h @@ -0,0 +1,72 @@ +/* coroutine.h - Simple coroutine and request/response implementation + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + +#ifndef _COROUTINE_H_ +#define _COROUTINE_H_ + +#include <stddef.h> /* for size_t */ +#include <stdbool.h> /* for bool, true, false */ + +/* typedefs *******************************************************************/ +typedef size_t cid_t; /* 0=none; otherwise 1-indexed */ +typedef void (*cr_fn_t)(void *stack); +typedef struct { + cr_fn_t fn; + void *stack; + unsigned int state; + bool blocked; +} _cr_entry_t; + +/* globals ********************************************************************/ +extern _cr_entry_t *_coroutine_table; +extern cid_t _cur_cid; + +/* bona-fide functions ********************************************************/ +void coroutine_init(void); +cid_t coroutine_add(cr_fn_t fn, void *stack); +void coroutine_task(void); + +/* core macros for use in a cr_fn_t() *****************************************/ +#define cr_begin() switch(_coroutine_table[_cur_cid].state) { case 0: +#define cr_exit() do { _coroutine_table[_cur_cid] = (cr_entry_t){0}; return; } while (0) +#define cr_yield() _cr_yield(__COUNTER__) +#define _cr_yield(_state) do { _coroutine_table[_cur_cid].state = _state; return; case _state:; } while (0) +#define cr_end() } + +/* request/response channels **************************************************/ +#define cr_chan_t(req_t, resp_t) struct { \ + cid_t requester; \ + cid_t responder; \ + req_t req; \ + resp_t resp; \ + } +#define cr_chan_req(ch, _resp_p, _req) do { \ + (ch)->requester = _cur_cid; \ + (ch)->req = (_req); \ + if ((ch)->responder != 0) \ + _coroutine_table[(ch)->responder].blocked = false; \ + _coroutine_table[_cur_cid].blocked = true; \ + cr_yield(); \ + if ((typeof(&(ch)->resp))(_resp_p)) \ + *((typeof(&(ch)->resp))(_resp_p)) = (ch)->resp; \ + } while (0) +#define cr_chan_have_req(ch) ((ch)->requester != 0) +#define cr_chan_recv_req(ch, _req_p) do { \ + (ch)->responder = _cur_cid; \ + if ((ch)->requester == 0) { \ + _coroutine_table[_cur_cid].blocked = true; \ + cr_yield(); \ + } \ + *(_req_p) = (ch)->req; \ + } while (0) +#define cr_chan_send_resp(ch, _resp) do { \ + (ch)->responder = 0; \ + (ch)->requester = 0; \ + (ch)->resp = (_resp); \ + _coroutine_table[(ch)->requester].blocked = false; \ + } while (0) + +#endif /* _COROUTINE_H_ */ @@ -1,3 +1,9 @@ +/* main.c - Main entry point and event loop for sbc-harness + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + /* pico-sdk */ #include <stdio.h> #include "pico/stdlib.h" @@ -7,8 +13,26 @@ #include "tusb.h" /* local */ +#include "coroutine.h" #include "usb_keyboard.h" +typedef struct { + usb_keyboard_chan_t *chan; + size_t i; +} hello_world_stack_t; + +void hello_world_cr(void *_stack) { + const char *msg = "Hello world!\n"; + hello_world_stack_t *stack = _stack; + + cr_begin(); + for (;;) { + cr_chan_req(stack->chan, NULL, msg[stack->i]); + stack->i = (stack->i + 1) % strlen(msg); + } + cr_end(); +} + int main() { /* pico-sdk initialization */ stdio_uart_init(); @@ -23,14 +47,23 @@ int main() { if (board_init_after_tusb) board_init_after_tusb(); + /* coroutine initialization */ + coroutine_init(); + + usb_keyboard_chan_t keyboard_chan; + + usb_keyboard_init(); + usb_keyboard_stack_t usb_keyboard_stack = {0}; + usb_keyboard_stack.chan = &keyboard_chan; + coroutine_add(usb_keyboard_cr, &usb_keyboard_stack); + + hello_world_stack_t hello_world_stack = {0}; + hello_world_stack.chan = &keyboard_chan; + coroutine_add(hello_world_cr, &hello_world_stack); + /* Event loop. */ for (;;) { tud_task(); - usb_keyboard_task(); - if (usb_keyboard_is_flushed()) { - char *msg = "Hello world!\n"; - for (size_t i = 0; i < strlen(msg); i++) - usb_keyboard_send_char(msg[i]); - } + coroutine_task(); } } diff --git a/tusb_config.h b/tusb_config.h index 37b14b3..bc8243d 100644 --- a/tusb_config.h +++ b/tusb_config.h @@ -1,4 +1,5 @@ -/* +/* tusb_config.h - Compile-time configuration for the TinyUSB library + * * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) diff --git a/usb_common.c b/usb_common.c index 9d59afe..65882b2 100644 --- a/usb_common.c +++ b/usb_common.c @@ -1,3 +1,9 @@ +/* usb_common.c - Common framework for implementing multiple USB devices at once + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + #include <stdint.h> /* for uint{n}_t types */ #include <string.h> /* memcpy(newlib) */ #include <assert.h> /* for assert(newlib) */ diff --git a/usb_common.h b/usb_common.h index f0ed844..a6006fa 100644 --- a/usb_common.h +++ b/usb_common.h @@ -1,3 +1,9 @@ +/* usb_common.h - Common framework for implementing multiple USB devices at once + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + #ifndef _USB_COMMON_H_ #define _USB_COMMON_H_ diff --git a/usb_keyboard.c b/usb_keyboard.c index eed1b88..8212c60 100644 --- a/usb_keyboard.c +++ b/usb_keyboard.c @@ -1,3 +1,9 @@ +/* usb_keyboard.c - Implementation of a USB keyboard device + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + #include "tusb.h" #include "tusb_helpers.h" /* for TUD_ENDPOINT_IN */ @@ -34,46 +40,40 @@ void usb_keyboard_init() { }); } -#define kbd_buf_cap 1024 -static uint32_t kbd_buf[kbd_buf_cap]; -static size_t kbd_buf_beg = 0; -static size_t kbd_buf_len = 0; - static uint8_t ascii2keycode[128][2] = { HID_ASCII_TO_KEYCODE }; -void usb_keyboard_task(void) { - static bool sent_key = false; - if (tud_hid_n_ready(kbd_ifc)) { - uint8_t const report_id = 0; - uint8_t modifier = 0; - uint8_t keycodes[6] = {0}; - - if (kbd_buf_len && !sent_key) { - if (ascii2keycode[kbd_buf[kbd_buf_beg]][0]) - modifier = KEYBOARD_MODIFIER_LEFTSHIFT; - keycodes[0] = ascii2keycode[kbd_buf[kbd_buf_beg]][1]; - - kbd_buf_beg++; - if (kbd_buf_beg == kbd_buf_cap) - kbd_buf_beg = 0; - kbd_buf_len--; - } +void usb_keyboard_cr(void *_stack) { + usb_keyboard_stack_t *stack = _stack; - tud_hid_n_keyboard_report(kbd_ifc, report_id, modifier, keycodes); - sent_key = !sent_key; - } -} + cr_begin(); -bool usb_keyboard_send_char(uint32_t ch) { - if (kbd_buf_len == kbd_buf_cap) /* buffer full */ - return false; - if (ch > 0x7F) /* not ASCII */ - return false; - kbd_buf[(kbd_buf_beg + kbd_buf_len++) % kbd_buf_cap] = ch; -} + for (;;) { + while (!tud_hid_n_ready(kbd_ifc)) + cr_yield(); + + if (cr_chan_have_req(stack->chan)) { + cr_chan_recv_req(stack->chan, &stack->rune); + + stack->modifier = ascii2keycode[stack->rune][0] ? KEYBOARD_MODIFIER_LEFTSHIFT : 0; + stack->keycodes[0] = ascii2keycode[stack->rune][1]; + tud_hid_n_keyboard_report(kbd_ifc, stack->report_id, stack->modifier, stack->keycodes); + + while (!tud_hid_n_ready(kbd_ifc)) + cr_yield(); + + stack->modifier = 0; + stack->keycodes[0] = 0; + tud_hid_n_keyboard_report(kbd_ifc, stack->report_id, stack->modifier, stack->keycodes); + + cr_chan_send_resp(stack->chan, 1); + } else { + stack->modifier = 0; + stack->keycodes[0] = 0; + tud_hid_n_keyboard_report(kbd_ifc, stack->report_id, stack->modifier, stack->keycodes); + } + } -bool usb_keyboard_is_flushed(void) { - return kbd_buf_len == 0; + cr_end() } /** diff --git a/usb_keyboard.h b/usb_keyboard.h index 9460feb..e989700 100644 --- a/usb_keyboard.h +++ b/usb_keyboard.h @@ -1,10 +1,25 @@ +/* usb_keyboard.h - Implementation of a USB keyboard device + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + #ifndef _USB_KEYBOARD_H_ #define _USB_KEYBOARD_H_ -void usb_keyboard_init(void); -void usb_keyboard_task(void); +#include "coroutine.h" + +typedef cr_chan_t(uint32_t, int) usb_keyboard_chan_t; -bool usb_keyboard_send_char(uint32_t ch); -bool usb_keyboard_is_flushed(void); +typedef struct { + usb_keyboard_chan_t *chan; + uint32_t rune; + uint8_t report_id; + uint8_t modifier; + uint8_t keycodes[0]; +} usb_keyboard_stack_t; + +void usb_keyboard_init(void); +void usb_keyboard_cr(void *stack); #endif /* _USB_KEYBOARD_H_ */ |