summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/renesas-ra/Makefile29
-rw-r--r--ports/renesas-ra/main.c5
-rw-r--r--ports/renesas-ra/mpconfigport.h52
-rw-r--r--ports/renesas-ra/mphalport.c231
-rw-r--r--ports/renesas-ra/mphalport.h4
-rw-r--r--ports/renesas-ra/uart.c6
-rw-r--r--ports/renesas-ra/usbd.c48
7 files changed, 357 insertions, 18 deletions
diff --git a/ports/renesas-ra/Makefile b/ports/renesas-ra/Makefile
index 77077d6cc..a8cc6d191 100644
--- a/ports/renesas-ra/Makefile
+++ b/ports/renesas-ra/Makefile
@@ -44,7 +44,7 @@ FROZEN_MANIFEST ?= boards/manifest.py
include $(TOP)/py/py.mk
include $(TOP)/extmod/extmod.mk
-GIT_SUBMODULES += lib/fsp
+GIT_SUBMODULES += lib/fsp lib/tinyusb
MCU_SERIES_UPPER = $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]')
CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]')
@@ -69,6 +69,9 @@ INC += -I$(TOP)/$(HAL_DIR)/ra/fsp/inc
INC += -I$(TOP)/$(HAL_DIR)/ra/fsp/inc/api
INC += -I$(TOP)/$(HAL_DIR)/ra/fsp/inc/instances
INC += -I$(TOP)/$(HAL_DIR)/ra/fsp/src/bsp/cmsis/Device/RENESAS/Include
+INC += -I$(TOP)/lib/tinyusb/hw
+INC += -I$(TOP)/lib/tinyusb/src
+INC += -I$(TOP)/shared/tinyusb
#INC += -Ilwip_inc
ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),RA4M1 RA4W1 RA6M1 RA6M2 RA6M5))
INC += -Ira
@@ -82,6 +85,7 @@ INC += -Idebug
CFLAGS += -D$(CMSIS_MCU)
CFLAGS += -DRA_HAL_H='<$(CMSIS_MCU)_hal.h>'
CFLAGS += -DRA_CFG_H='<$(FSP_BOARD_NAME)_conf.h>'
+CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_RAXXX
# Basic Cortex-M flags
CFLAGS_CORTEX_M = -mthumb
@@ -171,6 +175,27 @@ SHARED_SRC_C += $(addprefix shared/,\
runtime/stdout_helpers.c \
runtime/sys_stdio_mphal.c \
timeutils/timeutils.c \
+ tinyusb/mp_cdc_common.c \
+ tinyusb/mp_usbd.c \
+ tinyusb/mp_usbd_descriptor.c \
+ )
+
+# TinyUSB Stack source
+TINYUSB_SRC_C += $(addprefix lib/tinyusb/,\
+ src/class/cdc/cdc_device.c \
+ src/class/dfu/dfu_rt_device.c \
+ src/class/hid/hid_device.c \
+ src/class/midi/midi_device.c \
+ src/class/msc/msc_device.c \
+ src/class/usbtmc/usbtmc_device.c \
+ src/class/vendor/vendor_device.c \
+ src/common/tusb_fifo.c \
+ src/device/usbd.c \
+ src/device/usbd_control.c \
+ src/portable/renesas/rusb2/dcd_rusb2.c \
+ src/portable/renesas/rusb2/hcd_rusb2.c \
+ src/portable/renesas/rusb2/rusb2_common.c \
+ src/tusb.c \
)
ifeq ($(MICROPY_FLOAT_IMPL),double)
@@ -307,6 +332,7 @@ SRC_C += \
flashbdev.c \
storage.c \
fatfs_port.c \
+ usbd.c \
$(wildcard $(BOARD_DIR)/*.c)
SRC_C += $(addprefix $(BOARD_DIR)/ra_gen/,\
@@ -401,6 +427,7 @@ OBJ += $(LIBM_O)
OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(HAL_SRC_C:.c=.o))
+OBJ += $(addprefix $(BUILD)/, $(TINYUSB_SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_O))
diff --git a/ports/renesas-ra/main.c b/ports/renesas-ra/main.c
index 8f4baa8e2..f23539625 100644
--- a/ports/renesas-ra/main.c
+++ b/ports/renesas-ra/main.c
@@ -62,6 +62,7 @@
#include "usrsw.h"
#include "rtc.h"
#include "storage.h"
+#include "tusb.h"
#define RA_EARLY_PRINT 1 /* for enabling mp_print in boardctrl. */
@@ -259,6 +260,10 @@ int main(void) {
state.reset_mode = 1;
state.log_soft_reset = false;
+ #if MICROPY_HW_ENABLE_USBDEV
+ tusb_init();
+ #endif
+
MICROPY_BOARD_BEFORE_SOFT_RESET_LOOP(&state);
soft_reset:
diff --git a/ports/renesas-ra/mpconfigport.h b/ports/renesas-ra/mpconfigport.h
index 34b299bd1..eb21db843 100644
--- a/ports/renesas-ra/mpconfigport.h
+++ b/ports/renesas-ra/mpconfigport.h
@@ -37,6 +37,28 @@
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
#endif
+#ifndef MICROPY_HW_ENABLE_USBDEV
+#define MICROPY_HW_ENABLE_USBDEV (0)
+#endif
+#ifndef MICROPY_HW_ENABLE_UART_REPL
+#define MICROPY_HW_ENABLE_UART_REPL (1) // useful if there is no USB
+#endif
+
+#if MICROPY_HW_ENABLE_USBDEV
+// Enable USB-CDC serial port
+#ifndef MICROPY_HW_USB_CDC
+#define MICROPY_HW_USB_CDC (1)
+#endif
+// Enable USB Mass Storage with FatFS filesystem.
+#ifndef MICROPY_HW_USB_MSC
+#define MICROPY_HW_USB_MSC (0)
+#endif
+// RA unique ID is 16 bytes (hex string == 32)
+#ifndef MICROPY_HW_USB_DESC_STR_MAX
+#define MICROPY_HW_USB_DESC_STR_MAX (32)
+#endif
+#endif
+
// memory allocation policies
#ifndef MICROPY_GC_STACK_ENTRY_TYPE
#if MICROPY_HW_SDRAM_SIZE
@@ -139,6 +161,11 @@
#define MICROPY_FATFS_USE_LABEL (1)
#define MICROPY_FATFS_RPATH (2)
#define MICROPY_FATFS_MULTI_PARTITION (1)
+#if MICROPY_HW_USB_MSC
+// Set FatFS block size to flash sector size to avoid caching
+// the flash sector in memory to support smaller block sizes.
+#define MICROPY_FATFS_MAX_SS (FLASH_SECTOR_SIZE)
+#endif
#if MICROPY_PY_MACHINE
#define MACHINE_BUILTIN_MODULE_CONSTANTS \
@@ -153,6 +180,15 @@
#define MP_STATE_PORT MP_STATE_VM
+// Miscellaneous settings
+
+#ifndef MICROPY_HW_USB_VID
+#define MICROPY_HW_USB_VID (0xf055)
+#endif
+#ifndef MICROPY_HW_USB_PID
+#define MICROPY_HW_USB_PID (0x9800)
+#endif
+
// type definitions for the specific machine
#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((uint32_t)(p) | 1))
@@ -190,10 +226,25 @@ static inline mp_uint_t disable_irq(void) {
#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq()
#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state)
+#if MICROPY_HW_ENABLE_USBDEV
+#define MICROPY_HW_USBDEV_TASK_HOOK extern void usbd_task(void); usbd_task();
+#define MICROPY_VM_HOOK_COUNT (10)
+#define MICROPY_VM_HOOK_INIT static uint vm_hook_divisor = MICROPY_VM_HOOK_COUNT;
+#define MICROPY_VM_HOOK_POLL if (--vm_hook_divisor == 0) { \
+ vm_hook_divisor = MICROPY_VM_HOOK_COUNT; \
+ MICROPY_HW_USBDEV_TASK_HOOK \
+}
+#define MICROPY_VM_HOOK_LOOP MICROPY_VM_HOOK_POLL
+#define MICROPY_VM_HOOK_RETURN MICROPY_VM_HOOK_POLL
+#else
+#define MICROPY_HW_USBDEV_TASK_HOOK
+#endif
+
#if MICROPY_PY_THREAD
#define MICROPY_EVENT_POLL_HOOK \
do { \
extern void mp_handle_pending(bool); \
+ MICROPY_HW_USBDEV_TASK_HOOK \
mp_handle_pending(true); \
if (pyb_thread_enabled) { \
MP_THREAD_GIL_EXIT(); \
@@ -209,6 +260,7 @@ static inline mp_uint_t disable_irq(void) {
#define MICROPY_EVENT_POLL_HOOK \
do { \
extern void mp_handle_pending(bool); \
+ MICROPY_HW_USBDEV_TASK_HOOK \
mp_handle_pending(true); \
__WFI(); \
} while (0);
diff --git a/ports/renesas-ra/mphalport.c b/ports/renesas-ra/mphalport.c
index b35ab7571..91705129c 100644
--- a/ports/renesas-ra/mphalport.c
+++ b/ports/renesas-ra/mphalport.c
@@ -3,8 +3,9 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2018 Damien P. George
+ * Copyright (c) 2018,2021 Damien P. George
* Copyright (c) 2021,2022 Renesas Electronics Corporation
+ * Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,15 +26,32 @@
* THE SOFTWARE.
*/
-#include <string.h>
-
#include "py/runtime.h"
#include "py/stream.h"
+#include "py/mphal.h"
#include "py/mperrno.h"
#include "py/mphal.h"
+#include "py/ringbuf.h"
#include "extmod/misc.h"
+#include "shared/runtime/interrupt_char.h"
+#include "tusb.h"
#include "uart.h"
+#if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE
+void flash_cache_commit(void);
+#endif
+
+#if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC
+
+#ifndef MICROPY_HW_STDIN_BUFFER_LEN
+#define MICROPY_HW_STDIN_BUFFER_LEN 512
+#endif
+
+STATIC uint8_t stdin_ringbuf_array[MICROPY_HW_STDIN_BUFFER_LEN];
+ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array) };
+
+#endif
+
// this table converts from HAL_StatusTypeDef to POSIX errno
const byte mp_hal_status_to_errno_table[4] = {
[HAL_OK] = 0,
@@ -46,42 +64,128 @@ NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
mp_raise_OSError(mp_hal_status_to_errno_table[status]);
}
-MP_WEAK uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
- uintptr_t ret = 0;
- if (MP_STATE_PORT(pyb_stdio_uart) != NULL) {
- mp_obj_t pyb_stdio_uart = MP_OBJ_FROM_PTR(MP_STATE_PORT(pyb_stdio_uart));
- int errcode;
- const mp_stream_p_t *stream_p = mp_get_stream(pyb_stdio_uart);
- ret = stream_p->ioctl(pyb_stdio_uart, MP_STREAM_POLL, poll_flags, &errcode);
+#if MICROPY_HW_USB_CDC
+
+uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll
+
+void poll_cdc_interfaces(void) {
+ // any CDC interfaces left to poll?
+ if (cdc_itf_pending && ringbuf_free(&stdin_ringbuf)) {
+ for (uint8_t itf = 0; itf < 8; ++itf) {
+ if (cdc_itf_pending & (1 << itf)) {
+ tud_cdc_rx_cb(itf);
+ if (!cdc_itf_pending) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void tud_cdc_rx_cb(uint8_t itf) {
+ // consume pending USB data immediately to free usb buffer and keep the endpoint from stalling.
+ // in case the ringbuffer is full, mark the CDC interface that need attention later on for polling
+ cdc_itf_pending &= ~(1 << itf);
+ for (uint32_t bytes_avail = tud_cdc_n_available(itf); bytes_avail > 0; --bytes_avail) {
+ if (ringbuf_free(&stdin_ringbuf)) {
+ int data_char = tud_cdc_read_char();
+ if (data_char == mp_interrupt_char) {
+ mp_sched_keyboard_interrupt();
+ } else {
+ ringbuf_put(&stdin_ringbuf, data_char);
+ }
+ } else {
+ cdc_itf_pending |= (1 << itf);
+ return;
+ }
}
- return ret | mp_os_dupterm_poll(poll_flags);
}
-#if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE
-void flash_cache_commit(void);
#endif
-MP_WEAK int mp_hal_stdin_rx_chr(void) {
+uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
+ uintptr_t ret = 0;
+ #if MICROPY_HW_USB_CDC
+ poll_cdc_interfaces();
+ #endif
+ #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC
+ if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) {
+ ret |= MP_STREAM_POLL_RD;
+ }
+ if (poll_flags & MP_STREAM_POLL_WR) {
+ #if MICROPY_HW_ENABLE_UART_REPL
+ ret |= MP_STREAM_POLL_WR;
+ #else
+ if (tud_cdc_connected() && tud_cdc_write_available() > 0) {
+ ret |= MP_STREAM_POLL_WR;
+ }
+ #endif
+ }
+ #endif
+ #if MICROPY_PY_OS_DUPTERM
+ ret |= mp_os_dupterm_poll(poll_flags);
+ #endif
+ return ret;
+}
+
+// Receive single character
+int mp_hal_stdin_rx_chr(void) {
for (;;) {
+ #if MICROPY_HW_USB_CDC
+ poll_cdc_interfaces();
+ #endif
+
#if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE
flash_cache_commit();
#endif
- if (MP_STATE_PORT(pyb_stdio_uart) != NULL && uart_rx_any(MP_STATE_PORT(pyb_stdio_uart))) {
- return uart_rx_char(MP_STATE_PORT(pyb_stdio_uart));
+
+ int c = ringbuf_get(&stdin_ringbuf);
+ if (c != -1) {
+ return c;
}
+ #if MICROPY_PY_OS_DUPTERM
int dupterm_c = mp_os_dupterm_rx_chr();
if (dupterm_c >= 0) {
return dupterm_c;
}
+ #endif
MICROPY_EVENT_POLL_HOOK
}
}
-MP_WEAK void mp_hal_stdout_tx_strn(const char *str, size_t len) {
+// Send string of given length
+void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
+ #if MICROPY_HW_ENABLE_UART_REPL
if (MP_STATE_PORT(pyb_stdio_uart) != NULL) {
uart_tx_strn(MP_STATE_PORT(pyb_stdio_uart), str, len);
}
+ #endif
+
+ #if MICROPY_HW_USB_CDC
+ if (tud_cdc_connected()) {
+ for (size_t i = 0; i < len;) {
+ uint32_t n = len - i;
+ if (n > CFG_TUD_CDC_EP_BUFSIZE) {
+ n = CFG_TUD_CDC_EP_BUFSIZE;
+ }
+ int timeout = 0;
+ // Wait with a max of USC_CDC_TIMEOUT ms
+ while (n > tud_cdc_write_available() && timeout++ < MICROPY_HW_USB_CDC_TX_TIMEOUT) {
+ MICROPY_EVENT_POLL_HOOK
+ }
+ if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) {
+ break;
+ }
+ uint32_t n2 = tud_cdc_write(str + i, n);
+ tud_cdc_write_flush();
+ i += n2;
+ }
+ }
+ #endif
+
+ #if MICROPY_PY_OS_DUPTERM
mp_os_dupterm_tx_strn(str, len);
+ #endif
}
void mp_hal_ticks_cpu_enable(void) {
@@ -119,4 +223,97 @@ void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest) {
}
}
+#if MICROPY_HW_ENABLE_USBDEV
+void usbfs_interrupt_handler(void) {
+ IRQn_Type irq = R_FSP_CurrentIrqGet();
+ R_BSP_IrqStatusClear(irq);
+
+ #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST
+ tuh_int_handler(0);
+ #endif
+
+ #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE
+ tud_int_handler(0);
+ #endif
+}
+
+void usbfs_resume_handler(void) {
+ IRQn_Type irq = R_FSP_CurrentIrqGet();
+ R_BSP_IrqStatusClear(irq);
+
+ #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST
+ tuh_int_handler(0);
+ #endif
+
+ #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE
+ tud_int_handler(0);
+ #endif
+}
+
+void usbfs_d0fifo_handler(void) {
+ IRQn_Type irq = R_FSP_CurrentIrqGet();
+ R_BSP_IrqStatusClear(irq);
+
+ #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST
+ tuh_int_handler(0);
+ #endif
+
+ #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE
+ tud_int_handler(0);
+ #endif
+}
+
+void usbfs_d1fifo_handler(void) {
+ IRQn_Type irq = R_FSP_CurrentIrqGet();
+ R_BSP_IrqStatusClear(irq);
+
+ #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST
+ tuh_int_handler(0);
+ #endif
+
+ #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE
+ tud_int_handler(0);
+ #endif
+}
+
+void usbhs_interrupt_handler(void) {
+ IRQn_Type irq = R_FSP_CurrentIrqGet();
+ R_BSP_IrqStatusClear(irq);
+
+ #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST
+ tuh_int_handler(1);
+ #endif
+
+ #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE
+ tud_int_handler(1);
+ #endif
+}
+
+void usbhs_d0fifo_handler(void) {
+ IRQn_Type irq = R_FSP_CurrentIrqGet();
+ R_BSP_IrqStatusClear(irq);
+
+ #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST
+ tuh_int_handler(1);
+ #endif
+
+ #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE
+ tud_int_handler(1);
+ #endif
+}
+
+void usbhs_d1fifo_handler(void) {
+ IRQn_Type irq = R_FSP_CurrentIrqGet();
+ R_BSP_IrqStatusClear(irq);
+
+ #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST
+ tuh_int_handler(1);
+ #endif
+
+ #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE
+ tud_int_handler(1);
+ #endif
+}
+#endif
+
MP_REGISTER_ROOT_POINTER(struct _machine_uart_obj_t *pyb_stdio_uart);
diff --git a/ports/renesas-ra/mphalport.h b/ports/renesas-ra/mphalport.h
index 2648e22f9..2b25a36fb 100644
--- a/ports/renesas-ra/mphalport.h
+++ b/ports/renesas-ra/mphalport.h
@@ -27,8 +27,12 @@
#include RA_HAL_H
#include "pin.h"
+#include "py/ringbuf.h"
+
+#define MICROPY_HW_USB_CDC_TX_TIMEOUT (500)
extern const unsigned char mp_hal_status_to_errno_table[4];
+extern ringbuf_t stdin_ringbuf;
static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) {
return -mp_hal_status_to_errno_table[status];
diff --git a/ports/renesas-ra/uart.c b/ports/renesas-ra/uart.c
index e41c68f0c..47c793e7d 100644
--- a/ports/renesas-ra/uart.c
+++ b/ports/renesas-ra/uart.c
@@ -33,6 +33,7 @@
#include "py/stream.h"
#include "py/mperrno.h"
#include "py/mphal.h"
+#include "py/ringbuf.h"
#include "shared/runtime/interrupt_char.h"
#include "shared/runtime/mpirq.h"
#include "uart.h"
@@ -75,6 +76,11 @@ static void uart_rx_cb(uint32_t ch, int d) {
(*keyex_cb[ch])(d);
}
#endif
+
+ #if MICROPY_HW_ENABLE_UART_REPL
+ ringbuf_put(&stdin_ringbuf, d);
+ #endif
+
// Check the flags to see if the user handler should be called
if (self->mp_irq_trigger) {
mp_irq_handler(self->mp_irq_obj);
diff --git a/ports/renesas-ra/usbd.c b/ports/renesas-ra/usbd.c
new file mode 100644
index 000000000..704750386
--- /dev/null
+++ b/ports/renesas-ra/usbd.c
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+
+#if MICROPY_HW_ENABLE_USBDEV
+
+#include "mp_usbd.h"
+#include "string.h"
+#include "tusb.h"
+
+void mp_usbd_port_get_serial_number(char *serial_buf) {
+ const bsp_unique_id_t *id = R_BSP_UniqueIdGet();
+ // convert to hex
+ int hexlen = sizeof(id->unique_id_bytes) * 2;
+ MP_STATIC_ASSERT(hexlen <= MICROPY_HW_USB_DESC_STR_MAX);
+ for (int i = 0; i < hexlen; i += 2) {
+ static const char *hexdig = "0123456789abcdef";
+ serial_buf[i] = hexdig[id->unique_id_bytes[i / 2] >> 4];
+ serial_buf[i + 1] = hexdig[id->unique_id_bytes[i / 2] & 0x0f];
+ }
+ serial_buf[hexlen] = 0;
+}
+
+#endif