summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--coroutine.c58
-rw-r--r--coroutine.h72
-rw-r--r--main.c45
-rw-r--r--tusb_config.h3
-rw-r--r--usb_common.c6
-rw-r--r--usb_common.h6
-rw-r--r--usb_keyboard.c70
-rw-r--r--usb_keyboard.h23
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_ */
diff --git a/main.c b/main.c
index 65db34d..fef917f 100644
--- a/main.c
+++ b/main.c
@@ -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_ */