diff --git a/.github/workflows/build_aarch64.yml b/.github/workflows/build_aarch64.yml
index 40fc8d63a..e5dbf9494 100644
--- a/.github/workflows/build_aarch64.yml
+++ b/.github/workflows/build_aarch64.yml
@@ -36,7 +36,7 @@ jobs:
- 'broadcom_64bit'
steps:
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
diff --git a/.github/workflows/build_arm.yml b/.github/workflows/build_arm.yml
index 47dea1925..9f3270c91 100644
--- a/.github/workflows/build_arm.yml
+++ b/.github/workflows/build_arm.yml
@@ -37,7 +37,7 @@ jobs:
# Alphabetical order
- 'broadcom_32bit'
- 'kinetis_k32l2'
- - 'lpc11 lpc13 lpc15 lpc17'
+ - 'lpc11 lpc13 lpc15'
- 'lpc51'
- 'mm32 msp432e4'
- 'samd11 same5x saml2x'
@@ -46,7 +46,7 @@ jobs:
- 'tm4c123 xmc4000'
steps:
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
diff --git a/.github/workflows/build_esp.yml b/.github/workflows/build_esp.yml
index 33caa0edb..8fbdb31ff 100644
--- a/.github/workflows/build_esp.yml
+++ b/.github/workflows/build_esp.yml
@@ -8,6 +8,7 @@ on:
- 'examples/**'
- 'lib/**'
- 'hw/**'
+ - 'test/hil/**'
- '.github/workflows/build_esp.yml'
pull_request:
branches: [ master ]
@@ -16,6 +17,7 @@ on:
- 'examples/**'
- 'lib/**'
- 'hw/**'
+ - 'test/hil/**'
- '.github/workflows/build_esp.yml'
concurrency:
@@ -35,7 +37,7 @@ jobs:
- 'espressif_s3_devkitc'
steps:
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
@@ -46,7 +48,7 @@ jobs:
uses: actions/checkout@v4
- name: Build
- run: docker run --rm -v $PWD:/project -w /project espressif/idf:latest python3 tools/build_esp32.py ${{ matrix.board }}
+ run: docker run --rm -v $PWD:/project -w /project espressif/idf:v5.1.1 python3 tools/build_esp32.py ${{ matrix.board }}
- name: Upload Artifacts for Hardware Testing
if: matrix.board == 'espressif_s3_devkitc' && github.repository_owner == 'hathach'
diff --git a/.github/workflows/build_iar.yml b/.github/workflows/build_iar.yml
index 9f2cc0351..a0a022ecc 100644
--- a/.github/workflows/build_iar.yml
+++ b/.github/workflows/build_iar.yml
@@ -9,6 +9,7 @@ on:
- 'lib/**'
- 'hw/**'
- 'tools/get_deps.py'
+ - 'test/hil/**'
- '.github/workflows/build_iar.yml'
pull_request:
branches: [ master ]
@@ -18,6 +19,7 @@ on:
- 'lib/**'
- 'hw/**'
- 'tools/get_deps.py'
+ - 'test/hil/**'
- '.github/workflows/build_iar.yml'
concurrency:
diff --git a/.github/workflows/build_msp430.yml b/.github/workflows/build_msp430.yml
index 296dbb766..f913df913 100644
--- a/.github/workflows/build_msp430.yml
+++ b/.github/workflows/build_msp430.yml
@@ -36,7 +36,7 @@ jobs:
steps:
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
diff --git a/.github/workflows/build_renesas.yml b/.github/workflows/build_renesas.yml
index 017a29eee..ec06c9426 100644
--- a/.github/workflows/build_renesas.yml
+++ b/.github/workflows/build_renesas.yml
@@ -35,7 +35,7 @@ jobs:
- 'rx'
steps:
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
diff --git a/.github/workflows/build_riscv.yml b/.github/workflows/build_riscv.yml
index e030bb77f..e891a3a51 100644
--- a/.github/workflows/build_riscv.yml
+++ b/.github/workflows/build_riscv.yml
@@ -37,7 +37,7 @@ jobs:
- 'gd32vf103'
steps:
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
diff --git a/.github/workflows/build_win_mac.yml b/.github/workflows/build_win_mac.yml
index 7fa1a6943..b33b5b593 100644
--- a/.github/workflows/build_win_mac.yml
+++ b/.github/workflows/build_win_mac.yml
@@ -35,7 +35,7 @@ jobs:
steps:
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
diff --git a/.github/workflows/cmake_arm.yml b/.github/workflows/cmake_arm.yml
index 13120f96f..f97645ad1 100644
--- a/.github/workflows/cmake_arm.yml
+++ b/.github/workflows/cmake_arm.yml
@@ -8,6 +8,7 @@ on:
- 'examples/**'
- 'lib/**'
- 'hw/**'
+ - 'test/hil/**'
- 'tools/get_deps.py'
- '.github/workflows/cmake_arm.yml'
pull_request:
@@ -17,6 +18,7 @@ on:
- 'examples/**'
- 'lib/**'
- 'hw/**'
+ - 'test/hil/**'
- 'tools/get_deps.py'
- '.github/workflows/cmake_arm.yml'
@@ -37,7 +39,7 @@ jobs:
# Alphabetical order
- 'imxrt'
- 'kinetis_kl'
- - 'lpc18 lpc40 lpc43'
+ - 'lpc17 lpc18 lpc40 lpc43'
- 'lpc54 lpc55'
- 'mcx'
- 'nrf'
@@ -57,7 +59,7 @@ jobs:
- 'stm32u5'
steps:
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index 2e9ae9264..e36259daa 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
diff --git a/.idea/cmake.xml b/.idea/cmake.xml
index d12e44789..3cb883e62 100644
--- a/.idea/cmake.xml
+++ b/.idea/cmake.xml
@@ -2,16 +2,17 @@
-
-
-
+
+
+
+
-
+
@@ -25,7 +26,7 @@
-
+
@@ -39,10 +40,10 @@
-
-
+
+
@@ -79,6 +80,7 @@
+
\ No newline at end of file
diff --git a/examples/device/video_capture/src/CMakeLists.txt b/examples/device/video_capture/src/CMakeLists.txt
new file mode 100644
index 000000000..cef2b46ee
--- /dev/null
+++ b/examples/device/video_capture/src/CMakeLists.txt
@@ -0,0 +1,4 @@
+# This file is for ESP-IDF only
+idf_component_register(SRCS "main.c" "usb_descriptors.c"
+ INCLUDE_DIRS "."
+ REQUIRES boards tinyusb_src)
diff --git a/examples/device/video_capture/src/images.h b/examples/device/video_capture/src/images.h
index 0398428b3..ac372cb16 100644
--- a/examples/device/video_capture/src/images.h
+++ b/examples/device/video_capture/src/images.h
@@ -1,4 +1,5 @@
-#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPG)
+#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
+// uncopmressed frame
static const unsigned char frame_buffer[128 * (96 + 1) * 2] = {
/* 0 */
0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80,
@@ -1650,8 +1651,10 @@ static const unsigned char frame_buffer[128 * (96 + 1) * 2] = {
0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80,
0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80,
};
+
#else
+// mpeg compressed data (not CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
#define color_bar_0_jpg_len 511
#define color_bar_1_jpg_len 512
#define color_bar_2_jpg_len 511
diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c
index c653761c1..2a2b0961f 100644
--- a/examples/device/video_capture/src/main.c
+++ b/examples/device/video_capture/src/main.c
@@ -40,7 +40,7 @@
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
-enum {
+enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
@@ -48,14 +48,25 @@ enum {
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
-void led_blinking_task(void);
-void video_task(void);
+void led_blinking_task(void* param);
+void usb_device_task(void *param);
+void video_task(void* param);
-/*------------- MAIN -------------*/
-int main(void)
-{
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+void freertos_init_task(void);
+#endif
+
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+int main(void) {
board_init();
+ // If using FreeRTOS: create blinky, tinyusb device, video task
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ freertos_init_task();
+#else
// init device stack on configured roothub port
tud_init(BOARD_TUD_RHPORT);
@@ -63,13 +74,12 @@ int main(void)
board_init_after_tusb();
}
- while (1)
- {
+ while (1) {
tud_task(); // tinyusb device task
- led_blinking_task();
-
- video_task();
+ led_blinking_task(NULL);
+ video_task(NULL);
}
+#endif
}
//--------------------------------------------------------------------+
@@ -77,33 +87,28 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
-void tud_mount_cb(void)
-{
+void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
-void tud_umount_cb(void)
-{
+void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
-void tud_suspend_cb(bool remote_wakeup_en)
-{
+void tud_suspend_cb(bool remote_wakeup_en) {
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
-void tud_resume_cb(void)
-{
+void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
-
//--------------------------------------------------------------------+
// USB Video
//--------------------------------------------------------------------+
@@ -111,11 +116,12 @@ static unsigned frame_num = 0;
static unsigned tx_busy = 0;
static unsigned interval_ms = 1000 / FRAME_RATE;
-/* YUY2 frame buffer */
#ifdef CFG_EXAMPLE_VIDEO_READONLY
+// For mcus that does not have enough SRAM for frame buffer, we use fixed frame data.
+// To further reduce the size, we use MJPEG format instead of YUY2.
#include "images.h"
-# if !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPG)
+#if !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
static struct {
uint32_t size;
uint8_t const *buffer;
@@ -129,29 +135,30 @@ static struct {
{color_bar_6_jpg_len, color_bar_6_jpg},
{color_bar_7_jpg_len, color_bar_7_jpg},
};
-# endif
+#endif
#else
+
+// YUY2 frame buffer
static uint8_t frame_buffer[FRAME_WIDTH * FRAME_HEIGHT * 16 / 8];
-static void fill_color_bar(uint8_t *buffer, unsigned start_position)
-{
- /* EBU color bars
- * See also https://stackoverflow.com/questions/6939422 */
+
+static void fill_color_bar(uint8_t* buffer, unsigned start_position) {
+ /* EBU color bars: https://stackoverflow.com/questions/6939422 */
static uint8_t const bar_color[8][4] = {
- /* Y, U, Y, V */
- { 235, 128, 235, 128}, /* 100% White */
- { 219, 16, 219, 138}, /* Yellow */
- { 188, 154, 188, 16}, /* Cyan */
- { 173, 42, 173, 26}, /* Green */
- { 78, 214, 78, 230}, /* Magenta */
- { 63, 102, 63, 240}, /* Red */
- { 32, 240, 32, 118}, /* Blue */
- { 16, 128, 16, 128}, /* Black */
+ /* Y, U, Y, V */
+ { 235, 128, 235, 128}, /* 100% White */
+ { 219, 16, 219, 138}, /* Yellow */
+ { 188, 154, 188, 16}, /* Cyan */
+ { 173, 42, 173, 26}, /* Green */
+ { 78, 214, 78, 230}, /* Magenta */
+ { 63, 102, 63, 240}, /* Red */
+ { 32, 240, 32, 118}, /* Blue */
+ { 16, 128, 16, 128}, /* Black */
};
- uint8_t *p;
+ uint8_t* p;
/* Generate the 1st line */
- uint8_t *end = &buffer[FRAME_WIDTH * 2];
+ uint8_t* end = &buffer[FRAME_WIDTH * 2];
unsigned idx = (FRAME_WIDTH / 2 - 1) - (start_position % (FRAME_WIDTH / 2));
p = &buffer[idx * 4];
for (unsigned i = 0; i < 8; ++i) {
@@ -163,6 +170,7 @@ static void fill_color_bar(uint8_t *buffer, unsigned start_position)
}
}
}
+
/* Duplicate the 1st line to the others */
p = &buffer[FRAME_WIDTH * 2];
for (unsigned i = 1; i < FRAME_HEIGHT; ++i) {
@@ -170,32 +178,33 @@ static void fill_color_bar(uint8_t *buffer, unsigned start_position)
p += FRAME_WIDTH * 2;
}
}
+
#endif
-void video_task(void)
-{
+void video_send_frame(void) {
static unsigned start_ms = 0;
static unsigned already_sent = 0;
if (!tud_video_n_streaming(0, 0)) {
- already_sent = 0;
- frame_num = 0;
+ already_sent = 0;
+ frame_num = 0;
return;
}
if (!already_sent) {
already_sent = 1;
+ tx_busy = 1;
start_ms = board_millis();
#ifdef CFG_EXAMPLE_VIDEO_READONLY
-# if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPG)
+ #if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
-# else
+ #else
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)frames[frame_num % 8].buffer, frames[frame_num % 8].size);
-# endif
+ #endif
#else
fill_color_bar(frame_buffer, frame_num);
- tud_video_n_frame_xfer(0, 0, (void*)frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16/8);
+ tud_video_n_frame_xfer(0, 0, (void*) frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
#endif
}
@@ -203,49 +212,140 @@ void video_task(void)
if (cur - start_ms < interval_ms) return; // not enough time
if (tx_busy) return;
start_ms += interval_ms;
+ tx_busy = 1;
#ifdef CFG_EXAMPLE_VIDEO_READONLY
-# if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPG)
+ #if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
-# else
+ #else
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)frames[frame_num % 8].buffer, frames[frame_num % 8].size);
-# endif
+ #endif
#else
fill_color_bar(frame_buffer, frame_num);
- tud_video_n_frame_xfer(0, 0, (void*)frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16/8);
+ tud_video_n_frame_xfer(0, 0, (void*) frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
#endif
}
-void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
-{
- (void)ctl_idx; (void)stm_idx;
+
+void video_task(void* param) {
+ (void) param;
+
+ while(1) {
+ video_send_frame();
+
+ #if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(interval_ms / portTICK_PERIOD_MS);
+ #else
+ return;
+ #endif
+ }
+}
+
+void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
+ (void) ctl_idx;
+ (void) stm_idx;
tx_busy = 0;
/* flip buffer */
++frame_num;
}
int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
- video_probe_and_commit_control_t const *parameters)
-{
- (void)ctl_idx; (void)stm_idx;
+ video_probe_and_commit_control_t const* parameters) {
+ (void) ctl_idx;
+ (void) stm_idx;
/* convert unit to ms from 100 ns */
interval_ms = parameters->dwFrameInterval / 10000;
return VIDEO_ERROR_NONE;
}
//--------------------------------------------------------------------+
-// BLINKING TASK
+// Blinking Task
//--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
+void led_blinking_task(void* param) {
+ (void) param;
static uint32_t start_ms = 0;
static bool led_state = false;
- // Blink every interval ms
- if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
- start_ms += blink_interval_ms;
+ while (1) {
+ #if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
+ #else
+ if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
+ #endif
- board_led_write(led_state);
- led_state = 1 - led_state; // toggle
+ start_ms += blink_interval_ms;
+ board_led_write(led_state);
+ led_state = 1 - led_state; // toggle
+ }
}
+
+//--------------------------------------------------------------------+
+// FreeRTOS
+//--------------------------------------------------------------------+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+
+#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
+#define VIDEO_STACK_SIZE (configMINIMAL_STACK_SIZE*4)
+
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+ #define USBD_STACK_SIZE 4096
+ int main(void);
+ void app_main(void) {
+ main();
+ }
+#else
+ // Increase stack size when debug log is enabled
+ #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+#endif
+
+// static task
+#if configSUPPORT_STATIC_ALLOCATION
+StackType_t blinky_stack[BLINKY_STACK_SIZE];
+StaticTask_t blinky_taskdef;
+
+StackType_t usb_device_stack[USBD_STACK_SIZE];
+StaticTask_t usb_device_taskdef;
+
+StackType_t video_stack[VIDEO_STACK_SIZE];
+StaticTask_t video_taskdef;
+#endif
+
+// USB Device Driver task
+// This top level thread process all usb events and invoke callbacks
+void usb_device_task(void *param) {
+ (void) param;
+
+ // init device stack on configured roothub port
+ // This should be called after scheduler/kernel is started.
+ // Otherwise, it could cause kernel issue since USB IRQ handler does use RTOS queue API.
+ tud_init(BOARD_TUD_RHPORT);
+
+ if (board_init_after_tusb) {
+ board_init_after_tusb();
+ }
+
+ // RTOS forever loop
+ while (1) {
+ // put this thread to waiting state until there is new events
+ tud_task();
+ }
+}
+
+void freertos_init_task(void) {
+ #if configSUPPORT_STATIC_ALLOCATION
+ xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
+ xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
+ xTaskCreateStatic(video_task, "cdc", VIDEO_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, video_stack, &video_taskdef);
+ #else
+ xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
+ xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+ xTaskCreate(video_task, "video", VIDEO_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL);
+ #endif
+
+ // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
+ #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+ vTaskStartScheduler();
+ #endif
+}
+#endif
diff --git a/examples/device/video_capture/src/tusb_config.h b/examples/device/video_capture/src/tusb_config.h
index 274bf2b9c..bdfc37d87 100644
--- a/examples/device/video_capture/src/tusb_config.h
+++ b/examples/device/video_capture/src/tusb_config.h
@@ -57,6 +57,11 @@
#define CFG_TUSB_OS OPT_OS_NONE
#endif
+// Espressif IDF requires "freertos/" prefix in include path
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#define CFG_TUSB_OS_INC_PATH freertos/
+#endif
+
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
@@ -97,11 +102,14 @@
// The number of video streaming interfaces
#define CFG_TUD_VIDEO_STREAMING 1
-// video streaming endpoint size
+// video streaming endpoint buffer size
#define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE 256
// use bulk endpoint for streaming interface
-#define CFG_TUD_VIDEO_STREAMING_BULK 0
+#define CFG_TUD_VIDEO_STREAMING_BULK 1
+
+//#define CFG_EXAMPLE_VIDEO_READONLY
+//#define CFG_EXAMPLE_VIDEO_DISABLE_MJPEG
#ifdef __cplusplus
}
diff --git a/examples/device/video_capture/src/usb_descriptors.c b/examples/device/video_capture/src/usb_descriptors.c
index 292d86cd9..f308c75a7 100644
--- a/examples/device/video_capture/src/usb_descriptors.c
+++ b/examples/device/video_capture/src/usb_descriptors.c
@@ -37,14 +37,36 @@
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VIDEO, 5) | _PID_MAP(VENDOR, 6) )
+#define USB_VID 0xCafe
+#define USB_BCD 0x0200
+
+// String Descriptor Index
+enum {
+ STRID_LANGID = 0,
+ STRID_MANUFACTURER,
+ STRID_PRODUCT,
+ STRID_SERIAL,
+ STRID_UVC_CONTROL,
+ STRID_UVC_STREAMING,
+};
+
+// array of pointer to string descriptors
+char const* string_desc_arr[] = {
+ (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409)
+ "TinyUSB", // 1: Manufacturer
+ "TinyUSB Device", // 2: Product
+ NULL, // 3: Serials will use unique ID if possible
+ "TinyUSB UVC Control", // 4: UVC Interface
+ "TinyUSB UVC Streaming", // 5: UVC Interface
+};
+
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
-{
+tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
- .bcdUSB = 0x0200,
+ .bcdUSB = USB_BCD,
// Use Interface Association Descriptor (IAD) for Video
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
@@ -54,130 +76,378 @@ tusb_desc_device_t const desc_device =
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
- .idVendor = 0xCafe,
+ .idVendor = USB_VID,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
- .iManufacturer = 0x01,
- .iProduct = 0x02,
- .iSerialNumber = 0x03,
+ .iManufacturer = STRID_MANUFACTURER,
+ .iProduct = STRID_PRODUCT,
+ .iSerialNumber = STRID_SERIAL,
.bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
-uint8_t const * tud_descriptor_device_cb(void)
-{
- return (uint8_t const *) &desc_device;
+uint8_t const* tud_descriptor_device_cb(void) {
+ return (uint8_t const*) &desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
-#if defined(CFG_EXAMPLE_VIDEO_READONLY) && !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
-# define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_VIDEO_CAPTURE_DESC_MJPEG_BULK_LEN)
-# else
-# define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_VIDEO_CAPTURE_DESC_MJPEG_LEN)
-# endif
-#else
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
-# define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_VIDEO_CAPTURE_DESC_UNCOMPR_BULK_LEN)
-# else
-# define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_VIDEO_CAPTURE_DESC_UNCOMPR_LEN)
-# endif
-#endif
+/* Time stamp base clock. It is a deprecated parameter. */
+#define UVC_CLOCK_FREQUENCY 27000000
+/* video capture path */
+#define UVC_ENTITY_CAP_INPUT_TERMINAL 0x01
+#define UVC_ENTITY_CAP_OUTPUT_TERMINAL 0x02
+
+enum {
+ ITF_NUM_VIDEO_CONTROL,
+ ITF_NUM_VIDEO_STREAMING,
+ ITF_NUM_TOTAL
+};
+
+// Select appropriate endpoint number
#if TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ...
-#if 1 == CFG_TUD_VIDEO_STREAMING_BULK
- #define EPNUM_VIDEO_IN 0x82
-#else
- #define EPNUM_VIDEO_IN 0x83
-#endif
-
+ #define EPNUM_VIDEO_IN (CFG_TUD_VIDEO_STREAMING_BULK ? 0x82 : 0x83)
#elif TU_CHECK_MCU(OPT_MCU_NRF5X)
// nRF5x ISO can only be endpoint 8
- #define EPNUM_VIDEO_IN 0x88
-
+ #define EPNUM_VIDEO_IN (CFG_TUD_VIDEO_STREAMING_BULK ? 0x81 : 0x88)
#else
#define EPNUM_VIDEO_IN 0x81
-
#endif
-uint8_t const desc_fs_configuration[] =
-{
- // Config number, interface count, string index, total length, attribute, power in mA
- TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 500),
-
- // IAD for Video Control
#if defined(CFG_EXAMPLE_VIDEO_READONLY) && !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
- TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(4, EPNUM_VIDEO_IN,
- FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
- 64)
-# else
- TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(4, EPNUM_VIDEO_IN,
- FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
- CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
-# endif
+ #define USE_MJPEG 1
#else
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
- TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK(4, EPNUM_VIDEO_IN,
- FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
- 64)
-# else
- TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(4, EPNUM_VIDEO_IN,
- FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
- CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
-# endif
+ #define USE_MJPEG 0
#endif
+
+#define USE_ISO_STREAMING (!CFG_TUD_VIDEO_STREAMING_BULK)
+
+typedef struct TU_ATTR_PACKED {
+ tusb_desc_interface_t itf;
+ tusb_desc_video_control_header_1itf_t header;
+ tusb_desc_video_control_camera_terminal_t camera_terminal;
+ tusb_desc_video_control_output_terminal_t output_terminal;
+} uvc_control_desc_t;
+
+/* Windows support YUY2 and NV12
+ * https://docs.microsoft.com/en-us/windows-hardware/drivers/stream/usb-video-class-driver-overview */
+
+typedef struct TU_ATTR_PACKED {
+ tusb_desc_interface_t itf;
+ tusb_desc_video_streaming_input_header_1byte_t header;
+
+#if USE_MJPEG
+ tusb_desc_video_format_mjpeg_t format;
+ tusb_desc_video_frame_mjpeg_continuous_t frame;
+#else
+ tusb_desc_video_format_uncompressed_t format;
+ tusb_desc_video_frame_uncompressed_continuous_t frame;
+#endif
+
+ tusb_desc_video_streaming_color_matching_t color;
+
+#if USE_ISO_STREAMING
+ // For ISO streaming, USB spec requires to alternate interface
+ tusb_desc_interface_t itf_alt;
+#endif
+
+ tusb_desc_endpoint_t ep;
+} uvc_streaming_desc_t;
+
+typedef struct TU_ATTR_PACKED {
+ tusb_desc_configuration_t config;
+ tusb_desc_interface_assoc_t iad;
+ uvc_control_desc_t video_control;
+ uvc_streaming_desc_t video_streaming;
+} uvc_cfg_desc_t;
+
+const uvc_cfg_desc_t desc_fs_configuration = {
+ .config = {
+ .bLength = sizeof(tusb_desc_configuration_t),
+ .bDescriptorType = TUSB_DESC_CONFIGURATION,
+
+ .wTotalLength = sizeof(uvc_cfg_desc_t),
+ .bNumInterfaces = ITF_NUM_TOTAL,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = TU_BIT(7),
+ .bMaxPower = 100 / 2
+ },
+ .iad = {
+ .bLength = sizeof(tusb_desc_interface_assoc_t),
+ .bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION,
+
+ .bFirstInterface = ITF_NUM_VIDEO_CONTROL,
+ .bInterfaceCount = 2,
+ .bFunctionClass = TUSB_CLASS_VIDEO,
+ .bFunctionSubClass = VIDEO_SUBCLASS_INTERFACE_COLLECTION,
+ .bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED,
+ .iFunction = 0
+ },
+
+ .video_control = {
+ .itf = {
+ .bLength = sizeof(tusb_desc_interface_t),
+ .bDescriptorType = TUSB_DESC_INTERFACE,
+
+ .bInterfaceNumber = ITF_NUM_VIDEO_CONTROL,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = TUSB_CLASS_VIDEO,
+ .bInterfaceSubClass = VIDEO_SUBCLASS_CONTROL,
+ .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15,
+ .iInterface = STRID_UVC_CONTROL
+ },
+ .header = {
+ .bLength = sizeof(tusb_desc_video_control_header_1itf_t),
+ .bDescriptorType = TUSB_DESC_CS_INTERFACE,
+ .bDescriptorSubType = VIDEO_CS_ITF_VC_HEADER,
+
+ .bcdUVC = VIDEO_BCD_1_50,
+ .wTotalLength = sizeof(uvc_control_desc_t) - sizeof(tusb_desc_interface_t), // CS VC descriptors only
+ .dwClockFrequency = UVC_CLOCK_FREQUENCY,
+ .bInCollection = 1,
+ .baInterfaceNr = { ITF_NUM_VIDEO_STREAMING }
+ },
+ .camera_terminal = {
+ .bLength = sizeof(tusb_desc_video_control_camera_terminal_t),
+ .bDescriptorType = TUSB_DESC_CS_INTERFACE,
+ .bDescriptorSubType = VIDEO_CS_ITF_VC_INPUT_TERMINAL,
+
+ .bTerminalID = UVC_ENTITY_CAP_INPUT_TERMINAL,
+ .wTerminalType = VIDEO_ITT_CAMERA,
+ .bAssocTerminal = 0,
+ .iTerminal = 0,
+ .wObjectiveFocalLengthMin = 0,
+ .wObjectiveFocalLengthMax = 0,
+ .wOcularFocalLength = 0,
+ .bControlSize = 3,
+ .bmControls = { 0, 0, 0 }
+ },
+ .output_terminal = {
+ .bLength = sizeof(tusb_desc_video_control_output_terminal_t),
+ .bDescriptorType = TUSB_DESC_CS_INTERFACE,
+ .bDescriptorSubType = VIDEO_CS_ITF_VC_OUTPUT_TERMINAL,
+
+ .bTerminalID = UVC_ENTITY_CAP_OUTPUT_TERMINAL,
+ .wTerminalType = VIDEO_TT_STREAMING,
+ .bAssocTerminal = 0,
+ .bSourceID = UVC_ENTITY_CAP_INPUT_TERMINAL,
+ .iTerminal = 0
+ }
+ },
+
+ .video_streaming = {
+ .itf = {
+ .bLength = sizeof(tusb_desc_interface_t),
+ .bDescriptorType = TUSB_DESC_INTERFACE,
+
+ .bInterfaceNumber = ITF_NUM_VIDEO_STREAMING,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = CFG_TUD_VIDEO_STREAMING_BULK, // bulk 1, iso 0
+ .bInterfaceClass = TUSB_CLASS_VIDEO,
+ .bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING,
+ .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15,
+ .iInterface = STRID_UVC_STREAMING
+ },
+ .header = {
+ .bLength = sizeof(tusb_desc_video_streaming_input_header_1byte_t),
+ .bDescriptorType = TUSB_DESC_CS_INTERFACE,
+ .bDescriptorSubType = VIDEO_CS_ITF_VS_INPUT_HEADER,
+
+ .bNumFormats = 1,
+ .wTotalLength = sizeof(uvc_streaming_desc_t) - sizeof(tusb_desc_interface_t)
+ - sizeof(tusb_desc_endpoint_t) - (USE_ISO_STREAMING ? sizeof(tusb_desc_interface_t) : 0) , // CS VS descriptors only
+ .bEndpointAddress = EPNUM_VIDEO_IN,
+ .bmInfo = 0,
+ .bTerminalLink = UVC_ENTITY_CAP_OUTPUT_TERMINAL,
+ .bStillCaptureMethod = 0,
+ .bTriggerSupport = 0,
+ .bTriggerUsage = 0,
+ .bControlSize = 1,
+ .bmaControls = { 0 }
+ },
+ .format = {
+#if USE_MJPEG
+ .bLength = sizeof(tusb_desc_video_format_mjpeg_t),
+ .bDescriptorType = TUSB_DESC_CS_INTERFACE,
+ .bDescriptorSubType = VIDEO_CS_ITF_VS_FORMAT_MJPEG,
+ .bFormatIndex = 1, // 1-based index
+ .bNumFrameDescriptors = 1,
+ .bmFlags = 0,
+#else
+ .bLength = sizeof(tusb_desc_video_format_uncompressed_t),
+ .bDescriptorType = TUSB_DESC_CS_INTERFACE,
+ .bDescriptorSubType = VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED,
+ .bFormatIndex = 1, // 1-based index
+ .bNumFrameDescriptors = 1,
+ .guidFormat = { TUD_VIDEO_GUID_YUY2 },
+ .bBitsPerPixel = 16,
+#endif
+ .bDefaultFrameIndex = 1,
+ .bAspectRatioX = 0,
+ .bAspectRatioY = 0,
+ .bmInterlaceFlags = 0,
+ .bCopyProtect = 0
+ },
+ .frame = {
+#if USE_MJPEG
+ .bLength = sizeof(tusb_desc_video_frame_mjpeg_continuous_t),
+ .bDescriptorType = TUSB_DESC_CS_INTERFACE,
+ .bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_MJPEG,
+#else
+ .bLength = sizeof(tusb_desc_video_frame_uncompressed_continuous_t),
+ .bDescriptorType = TUSB_DESC_CS_INTERFACE,
+ .bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED,
+#endif
+ .bFrameIndex = 1, // 1-based index
+ .bmCapabilities = 0,
+ .wWidth = FRAME_WIDTH,
+ .wHeight = FRAME_HEIGHT,
+ .dwMinBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * 1,
+ .dwMaxBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * FRAME_RATE,
+ .dwMaxVideoFrameBufferSize = FRAME_WIDTH * FRAME_HEIGHT * 16 / 8,
+ .dwDefaultFrameInterval = 10000000 / FRAME_RATE,
+ .bFrameIntervalType = 0, // continuous
+ .dwFrameInterval = {
+ 10000000 / FRAME_RATE, // min
+ 10000000, // max
+ 10000000 / FRAME_RATE // step
+ }
+ },
+ .color = {
+ .bLength = sizeof(tusb_desc_video_streaming_color_matching_t),
+ .bDescriptorType = TUSB_DESC_CS_INTERFACE,
+ .bDescriptorSubType = VIDEO_CS_ITF_VS_COLORFORMAT,
+
+ .bColorPrimaries = VIDEO_COLOR_PRIMARIES_BT709,
+ .bTransferCharacteristics = VIDEO_COLOR_XFER_CH_BT709,
+ .bMatrixCoefficients = VIDEO_COLOR_COEF_SMPTE170M
+ },
+
+#if USE_ISO_STREAMING
+ .itf_alt = {
+ .bLength = sizeof(tusb_desc_interface_t),
+ .bDescriptorType = TUSB_DESC_INTERFACE,
+
+ .bInterfaceNumber = ITF_NUM_VIDEO_STREAMING,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = TUSB_CLASS_VIDEO,
+ .bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING,
+ .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15,
+ .iInterface = STRID_UVC_STREAMING
+ },
+#endif
+
+ .ep = {
+ .bLength = sizeof(tusb_desc_endpoint_t),
+ .bDescriptorType = TUSB_DESC_ENDPOINT,
+
+ .bEndpointAddress = EPNUM_VIDEO_IN,
+ .bmAttributes = {
+ .xfer = CFG_TUD_VIDEO_STREAMING_BULK ? TUSB_XFER_BULK : TUSB_XFER_ISOCHRONOUS,
+ .sync = CFG_TUD_VIDEO_STREAMING_BULK ? 0 : 1 // asynchronous
+ },
+ .wMaxPacketSize = CFG_TUD_VIDEO_STREAMING_BULK ? 64 : CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE,
+ .bInterval = 1
+ }
+ }
};
+#if TUD_OPT_HIGH_SPEED
+uvc_cfg_desc_t desc_hs_configuration;
+
+static uint8_t * get_hs_configuration_desc(void) {
+ static bool init = false;
+
+ if (!init) {
+ desc_hs_configuration = desc_fs_configuration;
+ // change endpoint bulk size to 512 if bulk streaming
+ if (CFG_TUD_VIDEO_STREAMING_BULK) {
+ desc_hs_configuration.video_streaming.ep.wMaxPacketSize = 512;
+ }
+ }
+ init = true;
+
+ return (uint8_t *) &desc_hs_configuration;
+}
+
+// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
+tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = USB_BCD,
+
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00
+};
+
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
+// device_qualifier descriptor describes information about a high-speed capable device that would
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
+uint8_t const* tud_descriptor_device_qualifier_cb(void) {
+ return (uint8_t const*) &desc_device_qualifier;
+}
+
+// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index; // for multiple configurations
+ // if link speed is high return fullspeed config, and vice versa
+ if (tud_speed_get() == TUSB_SPEED_HIGH) {
+ return (uint8_t const*) &desc_fs_configuration;
+ } else {
+ return get_hs_configuration_desc();
+ }
+}
+#endif // highspeed
+
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
-{
+uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
(void) index; // for multiple configurations
- return desc_fs_configuration;
+#if TUD_OPT_HIGH_SPEED
+ // Although we are highspeed, host may be fullspeed.
+ if (tud_speed_get() == TUSB_SPEED_HIGH) {
+ return get_hs_configuration_desc();
+ } else
+#endif
+ {
+ return (uint8_t const*) &desc_fs_configuration;
+ }
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
-// String Descriptor Index
-enum {
- STRID_LANGID = 0,
- STRID_MANUFACTURER,
- STRID_PRODUCT,
- STRID_SERIAL,
-};
-
-// array of pointer to string descriptors
-char const *string_desc_arr[] =
-{
- (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
- "TinyUSB", // 1: Manufacturer
- "TinyUSB Device", // 2: Product
- NULL, // 3: Serials will use unique ID if possible
- "TinyUSB UVC", // 4: UVC Interface
-};
-
static uint16_t _desc_str[32 + 1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
-uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void) langid;
size_t chr_count;
- switch ( index ) {
+ switch (index) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
@@ -191,17 +461,17 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
- if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ if (index >= sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) return NULL;
- const char *str = string_desc_arr[index];
+ const char* str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
- if ( chr_count > max_count ) chr_count = max_count;
+ if (chr_count > max_count) chr_count = max_count;
// Convert ASCII string into UTF-16
- for ( size_t i = 0; i < chr_count; i++ ) {
+ for (size_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
break;
diff --git a/examples/device/video_capture/src/usb_descriptors.h b/examples/device/video_capture/src/usb_descriptors.h
index b924c8dbe..12d41b2f3 100644
--- a/examples/device/video_capture/src/usb_descriptors.h
+++ b/examples/device/video_capture/src/usb_descriptors.h
@@ -27,21 +27,11 @@
#ifndef _USB_DESCRIPTORS_H_
#define _USB_DESCRIPTORS_H_
-/* Time stamp base clock. It is a deprecated parameter. */
-#define UVC_CLOCK_FREQUENCY 27000000
-/* video capture path */
-#define UVC_ENTITY_CAP_INPUT_TERMINAL 0x01
-#define UVC_ENTITY_CAP_OUTPUT_TERMINAL 0x02
-
#define FRAME_WIDTH 128
#define FRAME_HEIGHT 96
#define FRAME_RATE 10
-enum {
- ITF_NUM_VIDEO_CONTROL,
- ITF_NUM_VIDEO_STREAMING,
- ITF_NUM_TOTAL
-};
+// NOTE: descriptor template is not used but leave here as reference
#define TUD_VIDEO_CAPTURE_DESC_UNCOMPR_LEN (\
TUD_VIDEO_DESC_IAD_LEN\
@@ -126,23 +116,17 @@ enum {
#define TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(_stridx, _epin, _width, _height, _fps, _epsize) \
TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \
/* Video control 0 */ \
- TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
- TUD_VIDEO_DESC_CS_VC( /* UVC 1.5*/ 0x0150, \
- /* wTotalLength - bLength */ \
- TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, \
- UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
- TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0,\
- /*wObjectiveFocalLengthMin*/0, /*wObjectiveFocalLengthMax*/0,\
- /*wObjectiveFocalLength*/0, /*bmControls*/0), \
+ TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
+ /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \
+ TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
+ /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \
+ TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \
TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \
/* Video stream alt. 0 */ \
TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 0, _stridx), \
/* Video stream header for without still image capture */ \
TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \
- /*wTotalLength - bLength */\
- TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN\
- + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN\
- + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
+ /*wTotalLength - bLength */ TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
_epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \
/*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \
/*bmaControls(1)*/0), \
@@ -152,7 +136,7 @@ enum {
/* Video stream frame format */ \
TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT(/*bFrameIndex */1, 0, _width, _height, \
_width * _height * 16, _width * _height * 16 * _fps, \
- _width * _height * 16, \
+ _width * _height * 16 / 8, \
(10000000/_fps), (10000000/_fps), (10000000/_fps)*_fps, (10000000/_fps)), \
TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(VIDEO_COLOR_PRIMARIES_BT709, VIDEO_COLOR_XFER_CH_BT709, VIDEO_COLOR_COEF_SMPTE170M), \
/* VS alt 1 */\
@@ -163,23 +147,17 @@ enum {
#define TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(_stridx, _epin, _width, _height, _fps, _epsize) \
TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \
/* Video control 0 */ \
- TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
- TUD_VIDEO_DESC_CS_VC( /* UVC 1.5*/ 0x0150, \
- /* wTotalLength - bLength */ \
- TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, \
- UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
- TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0,\
- /*wObjectiveFocalLengthMin*/0, /*wObjectiveFocalLengthMax*/0,\
- /*wObjectiveFocalLength*/0, /*bmControls*/0), \
+ TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
+ /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \
+ TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
+ /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \
+ TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \
TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \
/* Video stream alt. 0 */ \
TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 0, _stridx), \
/* Video stream header for without still image capture */ \
TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \
- /*wTotalLength - bLength */\
- TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN\
- + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN\
- + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
+ /*wTotalLength - bLength */ TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
_epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \
/*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \
/*bmaControls(1)*/0), \
@@ -202,22 +180,17 @@ enum {
TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \
/* Video control 0 */ \
TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
- TUD_VIDEO_DESC_CS_VC( /* UVC 1.5*/ 0x0150, \
- /* wTotalLength - bLength */ \
- TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, \
- UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
- TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0,\
- /*wObjectiveFocalLengthMin*/0, /*wObjectiveFocalLengthMax*/0,\
- /*wObjectiveFocalLength*/0, /*bmControls*/0), \
+ /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \
+ TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
+ /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \
+ TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \
TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \
/* Video stream alt. 0 */ \
TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 1, _stridx), \
/* Video stream header for without still image capture */ \
TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \
/*wTotalLength - bLength */\
- TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN\
- + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN\
- + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
+ TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
_epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \
/*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \
/*bmaControls(1)*/0), \
@@ -227,7 +200,7 @@ enum {
/* Video stream frame format */ \
TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT(/*bFrameIndex */1, 0, _width, _height, \
_width * _height * 16, _width * _height * 16 * _fps, \
- _width * _height * 16, \
+ _width * _height * 16 / 8, \
(10000000/_fps), (10000000/_fps), (10000000/_fps)*_fps, (10000000/_fps)), \
TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(VIDEO_COLOR_PRIMARIES_BT709, VIDEO_COLOR_XFER_CH_BT709, VIDEO_COLOR_COEF_SMPTE170M), \
TUD_VIDEO_DESC_EP_BULK(_epin, _epsize, 1)
@@ -235,23 +208,17 @@ enum {
#define TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(_stridx, _epin, _width, _height, _fps, _epsize) \
TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \
/* Video control 0 */ \
- TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
- TUD_VIDEO_DESC_CS_VC( /* UVC 1.5*/ 0x0150, \
- /* wTotalLength - bLength */ \
- TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, \
- UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
- TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0,\
- /*wObjectiveFocalLengthMin*/0, /*wObjectiveFocalLengthMax*/0,\
- /*wObjectiveFocalLength*/0, /*bmControls*/0), \
- TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \
+ TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
+ /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \
+ TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
+ /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \
+ TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \
+ TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, UVC_ENTITY_CAP_INPUT_TERMINAL, 0), \
/* Video stream alt. 0 */ \
TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 1, _stridx), \
/* Video stream header for without still image capture */ \
TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \
- /*wTotalLength - bLength */\
- TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN\
- + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN\
- + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
+ /*wTotalLength - bLength */ TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
_epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \
/*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \
/*bmaControls(1)*/0), \
diff --git a/examples/host/bare_api/src/tusb_config.h b/examples/host/bare_api/src/tusb_config.h
index ede9618d3..05ffbdf75 100644
--- a/examples/host/bare_api/src/tusb_config.h
+++ b/examples/host/bare_api/src/tusb_config.h
@@ -30,28 +30,8 @@
extern "C" {
#endif
-//--------------------------------------------------------------------+
-// Board Specific Configuration
-//--------------------------------------------------------------------+
-
-#if CFG_TUSB_MCU == OPT_MCU_RP2040
-// change to 1 if using pico-pio-usb as host controller for raspberry rp2040
-#define CFG_TUH_RPI_PIO_USB 0
-#define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB
-#endif
-
-// RHPort number used for host can be defined by board.mk, default to port 0
-#ifndef BOARD_TUH_RHPORT
-#define BOARD_TUH_RHPORT 0
-#endif
-
-// RHPort max operational speed can defined by board.mk
-#ifndef BOARD_TUH_MAX_SPEED
-#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
-#endif
-
//--------------------------------------------------------------------
-// COMMON CONFIGURATION
+// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
@@ -67,12 +47,6 @@
#define CFG_TUSB_DEBUG 0
#endif
-// Enable Host stack
-#define CFG_TUH_ENABLED 1
-
-// Default is max speed that hardware controller could support with on-chip PHY
-#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
-
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
@@ -85,11 +59,43 @@
#endif
#ifndef CFG_TUH_MEM_ALIGN
-#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
+#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
-// CONFIGURATION
+// Host Configuration
+//--------------------------------------------------------------------
+
+// Enable Host stack
+#define CFG_TUH_ENABLED 1
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+ // #define CFG_TUH_RPI_PIO_USB 1 // use max3421 as host controller
+
+ // host roothub port is 1 if using either pio-usb or max3421
+ #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+ #define BOARD_TUH_RHPORT 1
+ #endif
+#endif
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Driver Configuration
//--------------------------------------------------------------------
// Size of buffer to hold descriptors and other data used for enumeration
diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c
index a1b26e49c..e275e7943 100644
--- a/examples/host/cdc_msc_hid/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid/src/cdc_app.c
@@ -27,20 +27,11 @@
#include "tusb.h"
#include "bsp/board_api.h"
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
-
-
-//------------- IMPLEMENTATION -------------//
-
-size_t get_console_inputs(uint8_t* buf, size_t bufsize)
-{
+size_t get_console_inputs(uint8_t* buf, size_t bufsize) {
size_t count = 0;
- while (count < bufsize)
- {
+ while (count < bufsize) {
int ch = board_getchar();
- if ( ch <= 0 ) break;
+ if (ch <= 0) break;
buf[count] = (uint8_t) ch;
count++;
@@ -49,22 +40,18 @@ size_t get_console_inputs(uint8_t* buf, size_t bufsize)
return count;
}
-void cdc_app_task(void)
-{
- uint8_t buf[64+1]; // +1 for extra null character
- uint32_t const bufsize = sizeof(buf)-1;
+void cdc_app_task(void) {
+ uint8_t buf[64 + 1]; // +1 for extra null character
+ uint32_t const bufsize = sizeof(buf) - 1;
uint32_t count = get_console_inputs(buf, bufsize);
buf[count] = 0;
// loop over all mounted interfaces
- for(uint8_t idx=0; idx cdc interfaces
- if (count)
- {
+ if (count) {
tuh_cdc_write(idx, buf, count);
tuh_cdc_write_flush(idx);
}
@@ -72,11 +59,14 @@ void cdc_app_task(void)
}
}
+//--------------------------------------------------------------------+
+// TinyUSB callbacks
+//--------------------------------------------------------------------+
+
// Invoked when received new data
-void tuh_cdc_rx_cb(uint8_t idx)
-{
- uint8_t buf[64+1]; // +1 for extra null character
- uint32_t const bufsize = sizeof(buf)-1;
+void tuh_cdc_rx_cb(uint8_t idx) {
+ uint8_t buf[64 + 1]; // +1 for extra null character
+ uint32_t const bufsize = sizeof(buf) - 1;
// forward cdc interfaces -> console
uint32_t count = tuh_cdc_read(idx, buf, bufsize);
@@ -85,29 +75,35 @@ void tuh_cdc_rx_cb(uint8_t idx)
printf((char*) buf);
}
-void tuh_cdc_mount_cb(uint8_t idx)
-{
- tuh_itf_info_t itf_info = { 0 };
+// Invoked when a device with CDC interface is mounted
+// idx is index of cdc interface in the internal pool.
+void tuh_cdc_mount_cb(uint8_t idx) {
+ tuh_itf_info_t itf_info = {0};
tuh_cdc_itf_get_info(idx, &itf_info);
- printf("CDC Interface is mounted: address = %u, itf_num = %u\r\n", itf_info.daddr, itf_info.desc.bInterfaceNumber);
+ printf("CDC Interface is mounted: address = %u, itf_num = %u\r\n", itf_info.daddr,
+ itf_info.desc.bInterfaceNumber);
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
- // CFG_TUH_CDC_LINE_CODING_ON_ENUM must be defined for line coding is set by tinyusb in enumeration
- // otherwise you need to call tuh_cdc_set_line_coding() first
- cdc_line_coding_t line_coding = { 0 };
- if ( tuh_cdc_get_local_line_coding(idx, &line_coding) )
- {
+ // If CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined, line coding will be set by tinyusb stack
+ // while eneumerating new cdc device
+ cdc_line_coding_t line_coding = {0};
+ if (tuh_cdc_get_local_line_coding(idx, &line_coding)) {
printf(" Baudrate: %lu, Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
- printf(" Parity : %u, Data Width: %u\r\n", line_coding.parity , line_coding.data_bits);
+ printf(" Parity : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits);
}
+#else
+ // Set Line Coding upon mounted
+ cdc_line_coding_t new_line_coding = { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 };
+ tuh_cdc_set_line_coding(idx, &new_line_coding, NULL, 0);
#endif
}
-void tuh_cdc_umount_cb(uint8_t idx)
-{
- tuh_itf_info_t itf_info = { 0 };
+// Invoked when a device with CDC interface is unmounted
+void tuh_cdc_umount_cb(uint8_t idx) {
+ tuh_itf_info_t itf_info = {0};
tuh_cdc_itf_get_info(idx, &itf_info);
- printf("CDC Interface is unmounted: address = %u, itf_num = %u\r\n", itf_info.daddr, itf_info.desc.bInterfaceNumber);
+ printf("CDC Interface is unmounted: address = %u, itf_num = %u\r\n", itf_info.daddr,
+ itf_info.desc.bInterfaceNumber);
}
diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h
index a53000a11..76d59c316 100644
--- a/examples/host/cdc_msc_hid/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid/src/tusb_config.h
@@ -30,28 +30,8 @@
extern "C" {
#endif
-//--------------------------------------------------------------------+
-// Board Specific Configuration
-//--------------------------------------------------------------------+
-
-#if CFG_TUSB_MCU == OPT_MCU_RP2040
- // change to 1 if using pico-pio-usb as host controller for raspberry rp2040
- #define CFG_TUH_RPI_PIO_USB 0
- #define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB
-#endif
-
-// RHPort number used for host can be defined by board.mk, default to port 0
-#ifndef BOARD_TUH_RHPORT
-#define BOARD_TUH_RHPORT 0
-#endif
-
-// RHPort max operational speed can defined by board.mk
-#ifndef BOARD_TUH_MAX_SPEED
-#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
-#endif
-
//--------------------------------------------------------------------
-// COMMON CONFIGURATION
+// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
@@ -67,12 +47,6 @@
#define CFG_TUSB_DEBUG 0
#endif
-// Enable Host stack
-#define CFG_TUH_ENABLED 1
-
-// Default is max speed that hardware controller could support with on-chip PHY
-#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
-
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
@@ -85,11 +59,43 @@
#endif
#ifndef CFG_TUH_MEM_ALIGN
-#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
+#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
-// CONFIGURATION
+// Host Configuration
+//--------------------------------------------------------------------
+
+// Enable Host stack
+#define CFG_TUH_ENABLED 1
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+ // #define CFG_TUH_RPI_PIO_USB 1 // use max3421 as host controller
+
+ // host roothub port is 1 if using either pio-usb or max3421
+ #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+ #define BOARD_TUH_RHPORT 1
+ #endif
+#endif
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Driver Configuration
//--------------------------------------------------------------------
// Size of buffer to hold descriptors and other data used for enumeration
@@ -99,6 +105,7 @@
#define CFG_TUH_CDC 1 // CDC ACM
#define CFG_TUH_CDC_FTDI 1 // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_CP210X 1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API
+#define CFG_TUH_CDC_CH34X 1 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX) // typical keyboard + mouse device can have 3-4 HID interfaces
#define CFG_TUH_MSC 1
#define CFG_TUH_VENDOR 0
diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
index ba23301ed..bb7c3388d 100644
--- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
@@ -30,28 +30,8 @@
extern "C" {
#endif
-//--------------------------------------------------------------------+
-// Board Specific Configuration
-//--------------------------------------------------------------------+
-
-#if CFG_TUSB_MCU == OPT_MCU_RP2040
- // change to 1 if using pico-pio-usb as host controller for raspberry rp2040
- #define CFG_TUH_RPI_PIO_USB 0
- #define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB
-#endif
-
-// RHPort number used for host can be defined by board.mk, default to port 0
-#ifndef BOARD_TUH_RHPORT
-#define BOARD_TUH_RHPORT 0
-#endif
-
-// RHPort max operational speed can defined by board.mk
-#ifndef BOARD_TUH_MAX_SPEED
-#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
-#endif
-
//--------------------------------------------------------------------
-// COMMON CONFIGURATION
+// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
@@ -72,12 +52,6 @@
#define CFG_TUSB_DEBUG 0
#endif
-// Enable Host stack
-#define CFG_TUH_ENABLED 1
-
-// Default is max speed that hardware controller could support with on-chip PHY
-#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
-
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
@@ -90,11 +64,43 @@
#endif
#ifndef CFG_TUH_MEM_ALIGN
-#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
+#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
-// CONFIGURATION
+// Host Configuration
+//--------------------------------------------------------------------
+
+// Enable Host stack
+#define CFG_TUH_ENABLED 1
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+ // #define CFG_TUH_RPI_PIO_USB 1 // use max3421 as host controller
+
+ // host roothub port is 1 if using either pio-usb or max3421
+ #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+ #define BOARD_TUH_RHPORT 1
+ #endif
+#endif
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Driver Configuration
//--------------------------------------------------------------------
// Size of buffer to hold descriptors and other data used for enumeration
@@ -104,6 +110,7 @@
#define CFG_TUH_CDC 1 // CDC ACM
#define CFG_TUH_CDC_FTDI 1 // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_CP210X 1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API
+#define CFG_TUH_CDC_CH34X 1 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX) // typical keyboard + mouse device can have 3-4 HID interfaces
#define CFG_TUH_MSC 1
#define CFG_TUH_VENDOR 0
diff --git a/examples/host/hid_controller/src/tusb_config.h b/examples/host/hid_controller/src/tusb_config.h
index d37fc02d2..8ddaddda7 100644
--- a/examples/host/hid_controller/src/tusb_config.h
+++ b/examples/host/hid_controller/src/tusb_config.h
@@ -30,28 +30,8 @@
extern "C" {
#endif
-//--------------------------------------------------------------------+
-// Board Specific Configuration
-//--------------------------------------------------------------------+
-
- #if CFG_TUSB_MCU == OPT_MCU_RP2040
-// change to 1 if using pico-pio-usb as host controller for raspberry rp2040
-#define CFG_TUH_RPI_PIO_USB 0
-#define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB
-#endif
-
-// RHPort number used for host can be defined by board.mk, default to port 0
-#ifndef BOARD_TUH_RHPORT
-#define BOARD_TUH_RHPORT 0
-#endif
-
-// RHPort max operational speed can defined by board.mk
-#ifndef BOARD_TUH_MAX_SPEED
-#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
-#endif
-
//--------------------------------------------------------------------
-// COMMON CONFIGURATION
+// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
@@ -67,12 +47,6 @@
#define CFG_TUSB_DEBUG 0
#endif
-// Enable Host stack
-#define CFG_TUH_ENABLED 1
-
-// Default is max speed that hardware controller could support with on-chip PHY
-#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
-
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
@@ -85,11 +59,43 @@
#endif
#ifndef CFG_TUH_MEM_ALIGN
-#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
+#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
-// CONFIGURATION
+// Host Configuration
+//--------------------------------------------------------------------
+
+// Enable Host stack
+#define CFG_TUH_ENABLED 1
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+ // #define CFG_TUH_RPI_PIO_USB 1 // use max3421 as host controller
+
+ // host roothub port is 1 if using either pio-usb or max3421
+ #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+ #define BOARD_TUH_RHPORT 1
+ #endif
+#endif
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Driver Configuration
//--------------------------------------------------------------------
// Size of buffer to hold descriptors and other data used for enumeration
diff --git a/examples/host/msc_file_explorer/src/tusb_config.h b/examples/host/msc_file_explorer/src/tusb_config.h
index b4ccd8223..1703761a4 100644
--- a/examples/host/msc_file_explorer/src/tusb_config.h
+++ b/examples/host/msc_file_explorer/src/tusb_config.h
@@ -30,29 +30,8 @@
extern "C" {
#endif
-//--------------------------------------------------------------------+
-// Board Specific Configuration
-//--------------------------------------------------------------------+
-
-#if CFG_TUSB_MCU == OPT_MCU_RP2040
- // change to 1 if using pico-pio-usb as host controller for raspberry rp2040
- #define CFG_TUH_RPI_PIO_USB 0
- #define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB
-#endif
-
-
-// RHPort number used for host can be defined by board.mk, default to port 0
-#ifndef BOARD_TUH_RHPORT
-#define BOARD_TUH_RHPORT 0
-#endif
-
-// RHPort max operational speed can defined by board.mk
-#ifndef BOARD_TUH_MAX_SPEED
-#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
-#endif
-
//--------------------------------------------------------------------
-// COMMON CONFIGURATION
+// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
@@ -68,12 +47,6 @@
#define CFG_TUSB_DEBUG 0
#endif
-// Enable Host stack
-#define CFG_TUH_ENABLED 1
-
-// Default is max speed that hardware controller could support with on-chip PHY
-#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
-
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
@@ -86,11 +59,43 @@
#endif
#ifndef CFG_TUH_MEM_ALIGN
-#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
+#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
-// CONFIGURATION
+// Host Configuration
+//--------------------------------------------------------------------
+
+// Enable Host stack
+#define CFG_TUH_ENABLED 1
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+ // #define CFG_TUH_RPI_PIO_USB 1 // use max3421 as host controller
+
+ // host roothub port is 1 if using either pio-usb or max3421
+ #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+ #define BOARD_TUH_RHPORT 1
+ #endif
+#endif
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Driver Configuration
//--------------------------------------------------------------------
// Size of buffer to hold descriptors and other data used for enumeration
diff --git a/hw/bsp/board_api.h b/hw/bsp/board_api.h
index 404509a28..0c8a35924 100644
--- a/hw/bsp/board_api.h
+++ b/hw/bsp/board_api.h
@@ -33,9 +33,26 @@ extern "C" {
#include
#include
-
#include "tusb.h"
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+ // ESP-IDF need "freertos/" prefix in include path.
+ // CFG_TUSB_OS_INC_PATH should be defined accordingly.
+ #include "freertos/FreeRTOS.h"
+ #include "freertos/semphr.h"
+ #include "freertos/queue.h"
+ #include "freertos/task.h"
+ #include "freertos/timers.h"
+#else
+ #include "FreeRTOS.h"
+ #include "semphr.h"
+ #include "queue.h"
+ #include "task.h"
+ #include "timers.h"
+#endif
+#endif
+
// Define the default baudrate
#ifndef CFG_BOARD_UART_BAUDRATE
#define CFG_BOARD_UART_BAUDRATE 115200 ///< Default baud rate
diff --git a/hw/bsp/espressif/boards/adafruit_feather_esp32s2/board.h b/hw/bsp/espressif/boards/adafruit_feather_esp32s2/board.h
index 1f8dc2cea..9aa2e7535 100644
--- a/hw/bsp/espressif/boards/adafruit_feather_esp32s2/board.h
+++ b/hw/bsp/espressif/boards/adafruit_feather_esp32s2/board.h
@@ -38,6 +38,14 @@
#define BUTTON_PIN 0
#define BUTTON_STATE_ACTIVE 0
+// SPI for USB host shield
+#define MAX3421_SPI_HOST SPI2_HOST
+#define MAX3421_SCK_PIN 36
+#define MAX3421_MOSI_PIN 35
+#define MAX3421_MISO_PIN 37
+#define MAX3421_CS_PIN 10
+#define MAX3421_INTR_PIN 9
+
#ifdef __cplusplus
}
#endif
diff --git a/hw/bsp/espressif/boards/adafruit_metro_esp32s2/board.h b/hw/bsp/espressif/boards/adafruit_metro_esp32s2/board.h
index 44ff11aa0..137ea71ae 100644
--- a/hw/bsp/espressif/boards/adafruit_metro_esp32s2/board.h
+++ b/hw/bsp/espressif/boards/adafruit_metro_esp32s2/board.h
@@ -37,7 +37,7 @@
#define BUTTON_STATE_ACTIVE 0
// SPI for USB host shield
-#define MAX3421_SPI_HOST SPI2_HOST
+#define MAX3421_SPI_HOST SPI2_HOST
#define MAX3421_SCK_PIN 36
#define MAX3421_MOSI_PIN 35
#define MAX3421_MISO_PIN 37
diff --git a/hw/bsp/espressif/boards/family.c b/hw/bsp/espressif/boards/family.c
index 325e9ab0f..5599d1504 100644
--- a/hw/bsp/espressif/boards/family.c
+++ b/hw/bsp/espressif/boards/family.c
@@ -33,7 +33,7 @@
#include "hal/usb_hal.h"
#include "soc/usb_periph.h"
-#include "driver/rmt.h"
+#include "driver/gpio.h"
#include "driver/uart.h"
#if ESP_IDF_VERSION_MAJOR > 4
@@ -48,7 +48,7 @@
#ifdef NEOPIXEL_PIN
#include "led_strip.h"
-static led_strip_t* strip;
+static led_strip_handle_t led_strip;
#endif
#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
@@ -85,15 +85,23 @@ void board_init(void) {
#endif
// WS2812 Neopixel driver with RMT peripheral
- rmt_config_t config = RMT_DEFAULT_CONFIG_TX(NEOPIXEL_PIN, RMT_CHANNEL_0);
- config.clk_div = 2; // set counter clock to 40MHz
+ led_strip_rmt_config_t rmt_config = {
+ .clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
+ .resolution_hz = 10 * 1000 * 1000, // RMT counter clock frequency, default = 10 Mhz
+ .flags.with_dma = false, // DMA feature is available on ESP target like ESP32-S3
+ };
- rmt_config(&config);
- rmt_driver_install(config.channel, 0, 0);
+ led_strip_config_t strip_config = {
+ .strip_gpio_num = NEOPIXEL_PIN, // The GPIO that connected to the LED strip's data line
+ .max_leds = 1, // The number of LEDs in the strip,
+ .led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
+ .led_model = LED_MODEL_WS2812, // LED strip model
+ .flags.invert_out = false, // whether to invert the output signal
+ };
- led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(1, (led_strip_dev_t) config.channel);
- strip = led_strip_new_rmt_ws2812(&strip_config);
- strip->clear(strip, 100); // off led
+ ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
+
+ led_strip_clear(led_strip); // off
#endif
// Button
@@ -158,8 +166,8 @@ size_t board_get_unique_id(uint8_t id[], size_t max_len) {
void board_led_write(bool state) {
#ifdef NEOPIXEL_PIN
- strip->set_pixel(strip, 0, state ? 0x08 : 0x00, 0x00, 0x00);
- strip->refresh(strip, 100);
+ led_strip_set_pixel(led_strip, 0, state ? 0x08 : 0x00, 0x00, 0x00);
+ led_strip_refresh(led_strip);
#endif
}
diff --git a/hw/bsp/espressif/components/led_strip/CHANGELOG.md b/hw/bsp/espressif/components/led_strip/CHANGELOG.md
new file mode 100644
index 000000000..51c0cd30c
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/CHANGELOG.md
@@ -0,0 +1,38 @@
+## 2.5.0
+
+- Enabled support for IDF4.4 and above
+ - with RMT backend only
+- Added API `led_strip_set_pixel_hsv`
+
+## 2.4.0
+
+- Support configurable SPI mode to control leds
+ - recommend enabling DMA when using SPI mode
+
+## 2.3.0
+
+- Support configurable RMT channel size by setting `mem_block_symbols`
+
+## 2.2.0
+
+- Support for 4 components RGBW leds (SK6812):
+ - in led_strip_config_t new fields
+ led_pixel_format, controlling byte format (LED_PIXEL_FORMAT_GRB, LED_PIXEL_FORMAT_GRBW)
+ led_model, used to configure bit timing (LED_MODEL_WS2812, LED_MODEL_SK6812)
+ - new API led_strip_set_pixel_rgbw
+ - new interface type set_pixel_rgbw
+
+## 2.1.0
+
+- Support DMA feature, which offloads the CPU by a lot when it comes to drive a bunch of LEDs
+- Support various RMT clock sources
+- Acquire and release the power management lock before and after each refresh
+- New driver flag: `invert_out` which can invert the led control signal by hardware
+
+## 2.0.0
+
+- Reimplemented the driver using the new RMT driver (`driver/rmt_tx.h`)
+
+## 1.0.0
+
+- Initial driver version, based on the legacy RMT driver (`driver/rmt.h`)
diff --git a/hw/bsp/espressif/components/led_strip/CMakeLists.txt b/hw/bsp/espressif/components/led_strip/CMakeLists.txt
index 8266c5a1c..15de610cc 100644
--- a/hw/bsp/espressif/components/led_strip/CMakeLists.txt
+++ b/hw/bsp/espressif/components/led_strip/CMakeLists.txt
@@ -1,7 +1,22 @@
-set(component_srcs "src/led_strip_rmt_ws2812.c")
+include($ENV{IDF_PATH}/tools/cmake/version.cmake)
-idf_component_register(SRCS "${component_srcs}"
- INCLUDE_DIRS "include"
- PRIV_INCLUDE_DIRS ""
- PRIV_REQUIRES "driver"
- REQUIRES "")
+set(srcs "src/led_strip_api.c")
+
+if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
+ if(CONFIG_SOC_RMT_SUPPORTED)
+ list(APPEND srcs "src/led_strip_rmt_dev.c" "src/led_strip_rmt_encoder.c")
+ endif()
+else()
+ list(APPEND srcs "src/led_strip_rmt_dev_idf4.c")
+endif()
+
+# the SPI backend driver relies on something that was added in IDF 5.1
+if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.1")
+ if(CONFIG_SOC_GPSPI_SUPPORTED)
+ list(APPEND srcs "src/led_strip_spi_dev.c")
+ endif()
+endif()
+
+idf_component_register(SRCS ${srcs}
+ INCLUDE_DIRS "include" "interface"
+ REQUIRES "driver")
diff --git a/hw/bsp/espressif/components/led_strip/LICENSE b/hw/bsp/espressif/components/led_strip/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/hw/bsp/espressif/components/led_strip/README.md b/hw/bsp/espressif/components/led_strip/README.md
new file mode 100644
index 000000000..543d29642
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/README.md
@@ -0,0 +1,97 @@
+# LED Strip Driver
+
+[](https://components.espressif.com/components/espressif/led_strip)
+
+This driver is designed for addressable LEDs like [WS2812](http://www.world-semi.com/Certifications/WS2812B.html), where each LED is controlled by a single data line.
+
+## Backend Controllers
+
+### The [RMT](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html) Peripheral
+
+This is the most economical way to drive the LEDs because it only consumes one RMT channel, leaving other channels free to use. However, the memory usage increases dramatically with the number of LEDs. If the RMT hardware can't be assist by DMA, the driver will going into interrupt very frequently, thus result in a high CPU usage. What's worse, if the RMT interrupt is delayed or not serviced in time (e.g. if Wi-Fi interrupt happens on the same CPU core), the RMT transaction will be corrupted and the LEDs will display incorrect colors. If you want to use RMT to drive a large number of LEDs, you'd better to enable the DMA feature if possible [^1].
+
+#### Allocate LED Strip Object with RMT Backend
+
+```c
+#define BLINK_GPIO 0
+
+led_strip_handle_t led_strip;
+
+/* LED strip initialization with the GPIO and pixels number*/
+led_strip_config_t strip_config = {
+ .strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
+ .max_leds = 1, // The number of LEDs in the strip,
+ .led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
+ .led_model = LED_MODEL_WS2812, // LED strip model
+ .flags.invert_out = false, // whether to invert the output signal (useful when your hardware has a level inverter)
+};
+
+led_strip_rmt_config_t rmt_config = {
+#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
+ .rmt_channel = 0,
+#else
+ .clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
+ .resolution_hz = 10 * 1000 * 1000, // 10MHz
+ .flags.with_dma = false, // whether to enable the DMA feature
+#endif
+};
+ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
+```
+
+You can create multiple LED strip objects with different GPIOs and pixel numbers. The backend driver will automatically allocate the RMT channel for you if there is more available.
+
+### The [SPI](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html) Peripheral
+
+SPI peripheral can also be used to generate the timing required by the LED strip. However this backend is not as economical as the RMT one, because it will take up the whole **bus**, unlike the RMT just takes one **channel**. You **CAN'T** connect other devices to the same SPI bus if it's been used by the led_strip, because the led_strip doesn't have the concept of "Chip Select".
+
+Please note, the SPI backend has a dependency of **ESP-IDF >= 5.1**
+
+#### Allocate LED Strip Object with SPI Backend
+
+```c
+#define BLINK_GPIO 0
+
+led_strip_handle_t led_strip;
+
+/* LED strip initialization with the GPIO and pixels number*/
+led_strip_config_t strip_config = {
+ .strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
+ .max_leds = 1, // The number of LEDs in the strip,
+ .led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
+ .led_model = LED_MODEL_WS2812, // LED strip model
+ .flags.invert_out = false, // whether to invert the output signal (useful when your hardware has a level inverter)
+};
+
+led_strip_spi_config_t spi_config = {
+ .clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
+ .flags.with_dma = true, // Using DMA can improve performance and help drive more LEDs
+ .spi_bus = SPI2_HOST, // SPI bus ID
+};
+ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
+```
+
+The number of LED strip objects can be created depends on how many free SPI buses are free to use in your project.
+
+## FAQ
+
+* Which led_strip backend should I choose?
+ * It depends on your application requirement and target chip's ability.
+
+ ```mermaid
+ flowchart LR
+ A{Is RMT supported?}
+ A --> |No| B[SPI backend]
+ B --> C{Does the led strip has \n a larger number of LEDs?}
+ C --> |No| D[Don't have to enable the DMA of the backend]
+ C --> |Yes| E[Enable the DMA of the backend]
+ A --> |Yes| F{Does the led strip has \n a larger number of LEDs?}
+ F --> |Yes| G{Does RMT support DMA?}
+ G --> |Yes| E
+ G --> |No| B
+ F --> |No| H[RMT backend] --> D
+ ```
+
+* How to set the brightness of the LED strip?
+ * You can tune the brightness by scaling the value of each R-G-B element with a **same** factor. But pay attention to the overflow of the value.
+
+[^1]: The RMT DMA feature is not available on all ESP chips. Please check the data sheet before using it.
diff --git a/hw/bsp/espressif/components/led_strip/api.md b/hw/bsp/espressif/components/led_strip/api.md
new file mode 100644
index 000000000..6581d6604
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/api.md
@@ -0,0 +1,454 @@
+# API Reference
+
+## Header files
+
+- [include/led_strip.h](#file-includeled_striph)
+- [include/led_strip_rmt.h](#file-includeled_strip_rmth)
+- [include/led_strip_spi.h](#file-includeled_strip_spih)
+- [include/led_strip_types.h](#file-includeled_strip_typesh)
+- [interface/led_strip_interface.h](#file-interfaceled_strip_interfaceh)
+
+## File include/led_strip.h
+
+## Functions
+
+| Type | Name |
+| ---: | :--- |
+| esp\_err\_t | [**led\_strip\_clear**](#function-led_strip_clear) ([**led\_strip\_handle\_t**](#struct-led_strip_t) strip)
_Clear LED strip (turn off all LEDs)_ |
+| esp\_err\_t | [**led\_strip\_del**](#function-led_strip_del) ([**led\_strip\_handle\_t**](#struct-led_strip_t) strip)
_Free LED strip resources._ |
+| esp\_err\_t | [**led\_strip\_refresh**](#function-led_strip_refresh) ([**led\_strip\_handle\_t**](#struct-led_strip_t) strip)
_Refresh memory colors to LEDs._ |
+| esp\_err\_t | [**led\_strip\_set\_pixel**](#function-led_strip_set_pixel) ([**led\_strip\_handle\_t**](#struct-led_strip_t) strip, uint32\_t index, uint32\_t red, uint32\_t green, uint32\_t blue)
_Set RGB for a specific pixel._ |
+| esp\_err\_t | [**led\_strip\_set\_pixel\_hsv**](#function-led_strip_set_pixel_hsv) ([**led\_strip\_handle\_t**](#struct-led_strip_t) strip, uint32\_t index, uint16\_t hue, uint8\_t saturation, uint8\_t value)
_Set HSV for a specific pixel._ |
+| esp\_err\_t | [**led\_strip\_set\_pixel\_rgbw**](#function-led_strip_set_pixel_rgbw) ([**led\_strip\_handle\_t**](#struct-led_strip_t) strip, uint32\_t index, uint32\_t red, uint32\_t green, uint32\_t blue, uint32\_t white)
_Set RGBW for a specific pixel._ |
+
+## Functions Documentation
+
+### function `led_strip_clear`
+
+_Clear LED strip (turn off all LEDs)_
+
+```c
+esp_err_t led_strip_clear (
+ led_strip_handle_t strip
+)
+```
+
+**Parameters:**
+
+- `strip` LED strip
+
+**Returns:**
+
+- ESP\_OK: Clear LEDs successfully
+- ESP\_FAIL: Clear LEDs failed because some other error occurred
+
+### function `led_strip_del`
+
+_Free LED strip resources._
+
+```c
+esp_err_t led_strip_del (
+ led_strip_handle_t strip
+)
+```
+
+**Parameters:**
+
+- `strip` LED strip
+
+**Returns:**
+
+- ESP\_OK: Free resources successfully
+- ESP\_FAIL: Free resources failed because error occurred
+
+### function `led_strip_refresh`
+
+_Refresh memory colors to LEDs._
+
+```c
+esp_err_t led_strip_refresh (
+ led_strip_handle_t strip
+)
+```
+
+**Parameters:**
+
+- `strip` LED strip
+
+**Returns:**
+
+- ESP\_OK: Refresh successfully
+- ESP\_FAIL: Refresh failed because some other error occurred
+
+**Note:**
+
+: After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
+
+### function `led_strip_set_pixel`
+
+_Set RGB for a specific pixel._
+
+```c
+esp_err_t led_strip_set_pixel (
+ led_strip_handle_t strip,
+ uint32_t index,
+ uint32_t red,
+ uint32_t green,
+ uint32_t blue
+)
+```
+
+**Parameters:**
+
+- `strip` LED strip
+- `index` index of pixel to set
+- `red` red part of color
+- `green` green part of color
+- `blue` blue part of color
+
+**Returns:**
+
+- ESP\_OK: Set RGB for a specific pixel successfully
+- ESP\_ERR\_INVALID\_ARG: Set RGB for a specific pixel failed because of invalid parameters
+- ESP\_FAIL: Set RGB for a specific pixel failed because other error occurred
+
+### function `led_strip_set_pixel_hsv`
+
+_Set HSV for a specific pixel._
+
+```c
+esp_err_t led_strip_set_pixel_hsv (
+ led_strip_handle_t strip,
+ uint32_t index,
+ uint16_t hue,
+ uint8_t saturation,
+ uint8_t value
+)
+```
+
+**Parameters:**
+
+- `strip` LED strip
+- `index` index of pixel to set
+- `hue` hue part of color (0 - 360)
+- `saturation` saturation part of color (0 - 255)
+- `value` value part of color (0 - 255)
+
+**Returns:**
+
+- ESP\_OK: Set HSV color for a specific pixel successfully
+- ESP\_ERR\_INVALID\_ARG: Set HSV color for a specific pixel failed because of an invalid argument
+- ESP\_FAIL: Set HSV color for a specific pixel failed because other error occurred
+
+### function `led_strip_set_pixel_rgbw`
+
+_Set RGBW for a specific pixel._
+
+```c
+esp_err_t led_strip_set_pixel_rgbw (
+ led_strip_handle_t strip,
+ uint32_t index,
+ uint32_t red,
+ uint32_t green,
+ uint32_t blue,
+ uint32_t white
+)
+```
+
+**Note:**
+
+Only call this function if your led strip does have the white component (e.g. SK6812-RGBW)
+
+**Note:**
+
+Also see `led_strip_set_pixel` if you only want to specify the RGB part of the color and bypass the white component
+
+**Parameters:**
+
+- `strip` LED strip
+- `index` index of pixel to set
+- `red` red part of color
+- `green` green part of color
+- `blue` blue part of color
+- `white` separate white component
+
+**Returns:**
+
+- ESP\_OK: Set RGBW color for a specific pixel successfully
+- ESP\_ERR\_INVALID\_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
+- ESP\_FAIL: Set RGBW color for a specific pixel failed because other error occurred
+
+## File include/led_strip_rmt.h
+
+## Structures and Types
+
+| Type | Name |
+| ---: | :--- |
+| struct | [**led\_strip\_rmt\_config\_t**](#struct-led_strip_rmt_config_t)
_LED Strip RMT specific configuration._ |
+
+## Functions
+
+| Type | Name |
+| ---: | :--- |
+| esp\_err\_t | [**led\_strip\_new\_rmt\_device**](#function-led_strip_new_rmt_device) (const [**led\_strip\_config\_t**](#struct-led_strip_config_t) \*led\_config, const [**led\_strip\_rmt\_config\_t**](#struct-led_strip_rmt_config_t) \*rmt\_config, [**led\_strip\_handle\_t**](#struct-led_strip_t) \*ret\_strip)
_Create LED strip based on RMT TX channel._ |
+
+## Structures and Types Documentation
+
+### struct `led_strip_rmt_config_t`
+
+_LED Strip RMT specific configuration._
+
+Variables:
+
+- rmt\_clock\_source\_t clk_src
RMT clock source
+
+- struct [**led\_strip\_rmt\_config\_t**](#struct-led_strip_rmt_config_t) flags
Extra driver flags
+
+- size\_t mem_block_symbols
How many RMT symbols can one RMT channel hold at one time. Set to 0 will fallback to use the default size.
+
+- uint32\_t resolution_hz
RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied
+
+- uint32\_t with_dma
Use DMA to transmit data
+
+## Functions Documentation
+
+### function `led_strip_new_rmt_device`
+
+_Create LED strip based on RMT TX channel._
+
+```c
+esp_err_t led_strip_new_rmt_device (
+ const led_strip_config_t *led_config,
+ const led_strip_rmt_config_t *rmt_config,
+ led_strip_handle_t *ret_strip
+)
+```
+
+**Parameters:**
+
+- `led_config` LED strip configuration
+- `rmt_config` RMT specific configuration
+- `ret_strip` Returned LED strip handle
+
+**Returns:**
+
+- ESP\_OK: create LED strip handle successfully
+- ESP\_ERR\_INVALID\_ARG: create LED strip handle failed because of invalid argument
+- ESP\_ERR\_NO\_MEM: create LED strip handle failed because of out of memory
+- ESP\_FAIL: create LED strip handle failed because some other error
+
+## File include/led_strip_spi.h
+
+## Structures and Types
+
+| Type | Name |
+| ---: | :--- |
+| struct | [**led\_strip\_spi\_config\_t**](#struct-led_strip_spi_config_t)
_LED Strip SPI specific configuration._ |
+
+## Functions
+
+| Type | Name |
+| ---: | :--- |
+| esp\_err\_t | [**led\_strip\_new\_spi\_device**](#function-led_strip_new_spi_device) (const [**led\_strip\_config\_t**](#struct-led_strip_config_t) \*led\_config, const [**led\_strip\_spi\_config\_t**](#struct-led_strip_spi_config_t) \*spi\_config, [**led\_strip\_handle\_t**](#struct-led_strip_t) \*ret\_strip)
_Create LED strip based on SPI MOSI channel._ |
+
+## Structures and Types Documentation
+
+### struct `led_strip_spi_config_t`
+
+_LED Strip SPI specific configuration._
+
+Variables:
+
+- spi\_clock\_source\_t clk_src
SPI clock source
+
+- struct [**led\_strip\_spi\_config\_t**](#struct-led_strip_spi_config_t) flags
Extra driver flags
+
+- spi\_host\_device\_t spi_bus
SPI bus ID. Which buses are available depends on the specific chip
+
+- uint32\_t with_dma
Use DMA to transmit data
+
+## Functions Documentation
+
+### function `led_strip_new_spi_device`
+
+_Create LED strip based on SPI MOSI channel._
+
+```c
+esp_err_t led_strip_new_spi_device (
+ const led_strip_config_t *led_config,
+ const led_strip_spi_config_t *spi_config,
+ led_strip_handle_t *ret_strip
+)
+```
+
+**Note:**
+
+Although only the MOSI line is used for generating the signal, the whole SPI bus can't be used for other purposes.
+
+**Parameters:**
+
+- `led_config` LED strip configuration
+- `spi_config` SPI specific configuration
+- `ret_strip` Returned LED strip handle
+
+**Returns:**
+
+- ESP\_OK: create LED strip handle successfully
+- ESP\_ERR\_INVALID\_ARG: create LED strip handle failed because of invalid argument
+- ESP\_ERR\_NOT\_SUPPORTED: create LED strip handle failed because of unsupported configuration
+- ESP\_ERR\_NO\_MEM: create LED strip handle failed because of out of memory
+- ESP\_FAIL: create LED strip handle failed because some other error
+
+## File include/led_strip_types.h
+
+## Structures and Types
+
+| Type | Name |
+| ---: | :--- |
+| enum | [**led\_model\_t**](#enum-led_model_t)
_LED strip model._ |
+| enum | [**led\_pixel\_format\_t**](#enum-led_pixel_format_t)
_LED strip pixel format._ |
+| struct | [**led\_strip\_config\_t**](#struct-led_strip_config_t)
_LED Strip Configuration._ |
+| typedef struct [**led\_strip\_t**](#struct-led_strip_t) \* | [**led\_strip\_handle\_t**](#typedef-led_strip_handle_t)
_LED strip handle._ |
+
+## Structures and Types Documentation
+
+### enum `led_model_t`
+
+_LED strip model._
+
+```c
+enum led_model_t {
+ LED_MODEL_WS2812,
+ LED_MODEL_SK6812,
+ LED_MODEL_INVALID
+};
+```
+
+**Note:**
+
+Different led model may have different timing parameters, so we need to distinguish them.
+
+### enum `led_pixel_format_t`
+
+_LED strip pixel format._
+
+```c
+enum led_pixel_format_t {
+ LED_PIXEL_FORMAT_GRB,
+ LED_PIXEL_FORMAT_GRBW,
+ LED_PIXEL_FORMAT_INVALID
+};
+```
+
+### struct `led_strip_config_t`
+
+_LED Strip Configuration._
+
+Variables:
+
+- struct [**led\_strip\_config\_t**](#struct-led_strip_config_t) flags
Extra driver flags
+
+- uint32\_t invert_out
Invert output signal
+
+- led\_model\_t led_model
LED model
+
+- led\_pixel\_format\_t led_pixel_format
LED pixel format
+
+- uint32\_t max_leds
Maximum LEDs in a single strip
+
+- int strip_gpio_num
GPIO number that used by LED strip
+
+### typedef `led_strip_handle_t`
+
+_LED strip handle._
+
+```c
+typedef struct led_strip_t* led_strip_handle_t;
+```
+
+## File interface/led_strip_interface.h
+
+## Structures and Types
+
+| Type | Name |
+| ---: | :--- |
+| struct | [**led\_strip\_t**](#struct-led_strip_t)
_LED strip interface definition._ |
+| typedef struct [**led\_strip\_t**](#struct-led_strip_t) | [**led\_strip\_t**](#typedef-led_strip_t)
|
+
+## Structures and Types Documentation
+
+### struct `led_strip_t`
+
+_LED strip interface definition._
+
+Variables:
+
+- esp\_err\_t(\* clear
_Clear LED strip (turn off all LEDs)_
**Parameters:**
+
+- `strip` LED strip
+- `timeout_ms` timeout value for clearing task
+
+**Returns:**
+
+- ESP\_OK: Clear LEDs successfully
+- ESP\_FAIL: Clear LEDs failed because some other error occurred
+
+- esp\_err\_t(\* del
_Free LED strip resources._
**Parameters:**
+
+- `strip` LED strip
+
+**Returns:**
+
+- ESP\_OK: Free resources successfully
+- ESP\_FAIL: Free resources failed because error occurred
+
+- esp\_err\_t(\* refresh
_Refresh memory colors to LEDs._
**Parameters:**
+
+- `strip` LED strip
+- `timeout_ms` timeout value for refreshing task
+
+**Returns:**
+
+- ESP\_OK: Refresh successfully
+- ESP\_FAIL: Refresh failed because some other error occurred
+
+**Note:**
+
+: After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
+
+- esp\_err\_t(\* set_pixel
_Set RGB for a specific pixel._
**Parameters:**
+
+- `strip` LED strip
+- `index` index of pixel to set
+- `red` red part of color
+- `green` green part of color
+- `blue` blue part of color
+
+**Returns:**
+
+- ESP\_OK: Set RGB for a specific pixel successfully
+- ESP\_ERR\_INVALID\_ARG: Set RGB for a specific pixel failed because of invalid parameters
+- ESP\_FAIL: Set RGB for a specific pixel failed because other error occurred
+
+- esp\_err\_t(\* set_pixel_rgbw
_Set RGBW for a specific pixel. Similar to_ `set_pixel`_but also set the white component._
**Parameters:**
+
+- `strip` LED strip
+- `index` index of pixel to set
+- `red` red part of color
+- `green` green part of color
+- `blue` blue part of color
+- `white` separate white component
+
+**Returns:**
+
+- ESP\_OK: Set RGBW color for a specific pixel successfully
+- ESP\_ERR\_INVALID\_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
+- ESP\_FAIL: Set RGBW color for a specific pixel failed because other error occurred
+
+### typedef `led_strip_t`
+
+```c
+typedef struct led_strip_t led_strip_t;
+```
+
+Type of LED strip
diff --git a/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/CMakeLists.txt b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/CMakeLists.txt
new file mode 100644
index 000000000..923c46310
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/CMakeLists.txt
@@ -0,0 +1,9 @@
+# For more information about build system see
+# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
+# The following five lines of boilerplate have to be in your project's
+# CMakeLists in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.16)
+
+set(IDF_TARGET "esp32s3")
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(led_strip_rmt_ws2812)
diff --git a/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/README.md b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/README.md
new file mode 100644
index 000000000..ad52235d5
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/README.md
@@ -0,0 +1,31 @@
+# LED Strip Example (RMT backend + WS2812)
+
+This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component.
+
+## How to Use Example
+
+### Hardware Required
+
+* A development board with Espressif SoC
+* A USB cable for Power supply and programming
+* WS2812 LED strip
+
+### Configure the Example
+
+Before project configuration and build, be sure to set the correct chip target using `idf.py set-target `. Then assign the proper GPIO in the [source file](main/led_strip_rmt_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number.
+
+### Build and Flash
+
+Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project.
+
+(To exit the serial monitor, type ``Ctrl-]``.)
+
+See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
+
+## Example Output
+
+```text
+I (299) gpio: GPIO[8]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
+I (309) example: Created LED strip object with RMT backend
+I (309) example: Start blinking LED strip
+```
diff --git a/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/dependencies.lock b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/dependencies.lock
new file mode 100644
index 000000000..97840a153
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/dependencies.lock
@@ -0,0 +1,15 @@
+dependencies:
+ espressif/led_strip:
+ component_hash: null
+ source:
+ path: /home/hathach/code/idf-extra-components/led_strip
+ type: local
+ version: 2.5.2
+ idf:
+ component_hash: null
+ source:
+ type: idf
+ version: 5.1.1
+manifest_hash: 47d47762be26168b388cb0e6cbfee6b22c68d630ebf4b27a49c47c4c54191590
+target: esp32s3
+version: 1.0.0
diff --git a/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/main/CMakeLists.txt b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/main/CMakeLists.txt
new file mode 100644
index 000000000..37b9c1458
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/main/CMakeLists.txt
@@ -0,0 +1,2 @@
+idf_component_register(SRCS "led_strip_rmt_ws2812_main.c"
+ INCLUDE_DIRS ".")
diff --git a/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/main/idf_component.yml b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/main/idf_component.yml
new file mode 100644
index 000000000..916c366c7
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/main/idf_component.yml
@@ -0,0 +1,5 @@
+## IDF Component Manager Manifest File
+dependencies:
+ espressif/led_strip:
+ version: '^2'
+ override_path: '../../../'
diff --git a/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/main/led_strip_rmt_ws2812_main.c b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/main/led_strip_rmt_ws2812_main.c
new file mode 100644
index 000000000..4b20a5958
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/main/led_strip_rmt_ws2812_main.c
@@ -0,0 +1,75 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "led_strip.h"
+#include "esp_log.h"
+#include "esp_err.h"
+
+// GPIO assignment
+#define LED_STRIP_BLINK_GPIO 48
+// Numbers of the LED in the strip
+#define LED_STRIP_LED_NUMBERS 1
+// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
+#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
+
+static const char *TAG = "example";
+
+led_strip_handle_t configure_led(void)
+{
+ // LED strip general initialization, according to your led board design
+ led_strip_config_t strip_config = {
+ .strip_gpio_num = LED_STRIP_BLINK_GPIO, // The GPIO that connected to the LED strip's data line
+ .max_leds = LED_STRIP_LED_NUMBERS, // The number of LEDs in the strip,
+ .led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
+ .led_model = LED_MODEL_WS2812, // LED strip model
+ .flags.invert_out = false, // whether to invert the output signal
+ };
+
+ // LED strip backend configuration: RMT
+ led_strip_rmt_config_t rmt_config = {
+#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
+ .rmt_channel = 0,
+#else
+ .clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
+ .resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency
+ .flags.with_dma = false, // DMA feature is available on ESP target like ESP32-S3
+#endif
+ };
+
+ // LED Strip object handle
+ led_strip_handle_t led_strip;
+ ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
+ ESP_LOGI(TAG, "Created LED strip object with RMT backend");
+ return led_strip;
+}
+
+void app_main(void)
+{
+ led_strip_handle_t led_strip = configure_led();
+ bool led_on_off = false;
+
+ ESP_LOGI(TAG, "Start blinking LED strip");
+ while (1) {
+ if (led_on_off) {
+ /* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
+ for (int i = 0; i < LED_STRIP_LED_NUMBERS; i++) {
+ ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
+ }
+ /* Refresh the strip to send data */
+ ESP_ERROR_CHECK(led_strip_refresh(led_strip));
+ ESP_LOGI(TAG, "LED ON!");
+ } else {
+ /* Set all LED off to clear all pixels */
+ ESP_ERROR_CHECK(led_strip_clear(led_strip));
+ ESP_LOGI(TAG, "LED OFF!");
+ }
+
+ led_on_off = !led_on_off;
+ vTaskDelay(pdMS_TO_TICKS(500));
+ }
+}
diff --git a/hw/bsp/espressif/components/led_strip/idf_component.yml b/hw/bsp/espressif/components/led_strip/idf_component.yml
new file mode 100644
index 000000000..1fd9b83ee
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/idf_component.yml
@@ -0,0 +1,5 @@
+version: "2.5.2"
+description: Driver for Addressable LED Strip (WS2812, etc)
+url: https://github.com/espressif/idf-extra-components/tree/master/led_strip
+dependencies:
+ idf: ">=4.4"
diff --git a/hw/bsp/espressif/components/led_strip/include/led_strip.h b/hw/bsp/espressif/components/led_strip/include/led_strip.h
index a9dffc325..38711744a 100644
--- a/hw/bsp/espressif/components/led_strip/include/led_strip.h
+++ b/hw/bsp/espressif/components/led_strip/include/led_strip.h
@@ -1,125 +1,110 @@
-// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
#pragma once
+#include
+#include "esp_err.h"
+#include "led_strip_rmt.h"
+#include "esp_idf_version.h"
+
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
+#include "led_strip_spi.h"
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
-#include "esp_err.h"
-
/**
-* @brief LED Strip Type
-*
-*/
-typedef struct led_strip_s led_strip_t;
-
-/**
-* @brief LED Strip Device Type
-*
-*/
-typedef void *led_strip_dev_t;
-
-/**
-* @brief Declare of LED Strip Type
-*
-*/
-struct led_strip_s {
- /**
- * @brief Set RGB for a specific pixel
- *
- * @param strip: LED strip
- * @param index: index of pixel to set
- * @param red: red part of color
- * @param green: green part of color
- * @param blue: blue part of color
- *
- * @return
- * - ESP_OK: Set RGB for a specific pixel successfully
- * - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
- * - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
- */
- esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
-
- /**
- * @brief Refresh memory colors to LEDs
- *
- * @param strip: LED strip
- * @param timeout_ms: timeout value for refreshing task
- *
- * @return
- * - ESP_OK: Refresh successfully
- * - ESP_ERR_TIMEOUT: Refresh failed because of timeout
- * - ESP_FAIL: Refresh failed because some other error occurred
- *
- * @note:
- * After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
- */
- esp_err_t (*refresh)(led_strip_t *strip, uint32_t timeout_ms);
-
- /**
- * @brief Clear LED strip (turn off all LEDs)
- *
- * @param strip: LED strip
- * @param timeout_ms: timeout value for clearing task
- *
- * @return
- * - ESP_OK: Clear LEDs successfully
- * - ESP_ERR_TIMEOUT: Clear LEDs failed because of timeout
- * - ESP_FAIL: Clear LEDs failed because some other error occurred
- */
- esp_err_t (*clear)(led_strip_t *strip, uint32_t timeout_ms);
-
- /**
- * @brief Free LED strip resources
- *
- * @param strip: LED strip
- *
- * @return
- * - ESP_OK: Free resources successfully
- * - ESP_FAIL: Free resources failed because error occurred
- */
- esp_err_t (*del)(led_strip_t *strip);
-};
-
-/**
-* @brief LED Strip Configuration Type
-*
-*/
-typedef struct {
- uint32_t max_leds; /*!< Maximum LEDs in a single strip */
- led_strip_dev_t dev; /*!< LED strip device (e.g. RMT channel, PWM channel, etc) */
-} led_strip_config_t;
-
-/**
- * @brief Default configuration for LED strip
+ * @brief Set RGB for a specific pixel
*
+ * @param strip: LED strip
+ * @param index: index of pixel to set
+ * @param red: red part of color
+ * @param green: green part of color
+ * @param blue: blue part of color
+ *
+ * @return
+ * - ESP_OK: Set RGB for a specific pixel successfully
+ * - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
+ * - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
*/
-#define LED_STRIP_DEFAULT_CONFIG(number, dev_hdl) \
- { \
- .max_leds = number, \
- .dev = dev_hdl, \
- }
+esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
/**
-* @brief Install a new ws2812 driver (based on RMT peripheral)
-*
-* @param config: LED strip configuration
-* @return
-* LED strip instance or NULL
-*/
-led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config);
+ * @brief Set RGBW for a specific pixel
+ *
+ * @note Only call this function if your led strip does have the white component (e.g. SK6812-RGBW)
+ * @note Also see `led_strip_set_pixel` if you only want to specify the RGB part of the color and bypass the white component
+ *
+ * @param strip: LED strip
+ * @param index: index of pixel to set
+ * @param red: red part of color
+ * @param green: green part of color
+ * @param blue: blue part of color
+ * @param white: separate white component
+ *
+ * @return
+ * - ESP_OK: Set RGBW color for a specific pixel successfully
+ * - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
+ * - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
+ */
+esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
+
+/**
+ * @brief Set HSV for a specific pixel
+ *
+ * @param strip: LED strip
+ * @param index: index of pixel to set
+ * @param hue: hue part of color (0 - 360)
+ * @param saturation: saturation part of color (0 - 255)
+ * @param value: value part of color (0 - 255)
+ *
+ * @return
+ * - ESP_OK: Set HSV color for a specific pixel successfully
+ * - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
+ * - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
+ */
+esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value);
+
+/**
+ * @brief Refresh memory colors to LEDs
+ *
+ * @param strip: LED strip
+ *
+ * @return
+ * - ESP_OK: Refresh successfully
+ * - ESP_FAIL: Refresh failed because some other error occurred
+ *
+ * @note:
+ * After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
+ */
+esp_err_t led_strip_refresh(led_strip_handle_t strip);
+
+/**
+ * @brief Clear LED strip (turn off all LEDs)
+ *
+ * @param strip: LED strip
+ *
+ * @return
+ * - ESP_OK: Clear LEDs successfully
+ * - ESP_FAIL: Clear LEDs failed because some other error occurred
+ */
+esp_err_t led_strip_clear(led_strip_handle_t strip);
+
+/**
+ * @brief Free LED strip resources
+ *
+ * @param strip: LED strip
+ *
+ * @return
+ * - ESP_OK: Free resources successfully
+ * - ESP_FAIL: Free resources failed because error occurred
+ */
+esp_err_t led_strip_del(led_strip_handle_t strip);
#ifdef __cplusplus
}
diff --git a/hw/bsp/espressif/components/led_strip/include/led_strip_rmt.h b/hw/bsp/espressif/components/led_strip/include/led_strip_rmt.h
new file mode 100644
index 000000000..b575aeaba
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/include/led_strip_rmt.h
@@ -0,0 +1,53 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include
+#include "esp_err.h"
+#include "led_strip_types.h"
+#include "esp_idf_version.h"
+
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+#include "driver/rmt_types.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief LED Strip RMT specific configuration
+ */
+typedef struct {
+#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
+ uint8_t rmt_channel; /*!< Specify the channel number, the legacy RMT driver doesn't support channel allocator */
+#else // new driver supports specify the clock source and clock resolution
+ rmt_clock_source_t clk_src; /*!< RMT clock source */
+ uint32_t resolution_hz; /*!< RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied */
+#endif
+ size_t mem_block_symbols; /*!< How many RMT symbols can one RMT channel hold at one time. Set to 0 will fallback to use the default size. */
+ struct {
+ uint32_t with_dma: 1; /*!< Use DMA to transmit data */
+ } flags; /*!< Extra driver flags */
+} led_strip_rmt_config_t;
+
+/**
+ * @brief Create LED strip based on RMT TX channel
+ *
+ * @param led_config LED strip configuration
+ * @param rmt_config RMT specific configuration
+ * @param ret_strip Returned LED strip handle
+ * @return
+ * - ESP_OK: create LED strip handle successfully
+ * - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
+ * - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
+ * - ESP_FAIL: create LED strip handle failed because some other error
+ */
+esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/hw/bsp/espressif/components/led_strip/include/led_strip_spi.h b/hw/bsp/espressif/components/led_strip/include/led_strip_spi.h
new file mode 100644
index 000000000..eb3524936
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/include/led_strip_spi.h
@@ -0,0 +1,46 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include
+#include "esp_err.h"
+#include "driver/spi_master.h"
+#include "led_strip_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief LED Strip SPI specific configuration
+ */
+typedef struct {
+ spi_clock_source_t clk_src; /*!< SPI clock source */
+ spi_host_device_t spi_bus; /*!< SPI bus ID. Which buses are available depends on the specific chip */
+ struct {
+ uint32_t with_dma: 1; /*!< Use DMA to transmit data */
+ } flags; /*!< Extra driver flags */
+} led_strip_spi_config_t;
+
+/**
+ * @brief Create LED strip based on SPI MOSI channel
+ * @note Although only the MOSI line is used for generating the signal, the whole SPI bus can't be used for other purposes.
+ *
+ * @param led_config LED strip configuration
+ * @param spi_config SPI specific configuration
+ * @param ret_strip Returned LED strip handle
+ * @return
+ * - ESP_OK: create LED strip handle successfully
+ * - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
+ * - ESP_ERR_NOT_SUPPORTED: create LED strip handle failed because of unsupported configuration
+ * - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
+ * - ESP_FAIL: create LED strip handle failed because some other error
+ */
+esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/hw/bsp/espressif/components/led_strip/include/led_strip_types.h b/hw/bsp/espressif/components/led_strip/include/led_strip_types.h
new file mode 100644
index 000000000..691f0bc39
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/include/led_strip_types.h
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief LED strip pixel format
+ */
+typedef enum {
+ LED_PIXEL_FORMAT_GRB, /*!< Pixel format: GRB */
+ LED_PIXEL_FORMAT_GRBW, /*!< Pixel format: GRBW */
+ LED_PIXEL_FORMAT_INVALID /*!< Invalid pixel format */
+} led_pixel_format_t;
+
+/**
+ * @brief LED strip model
+ * @note Different led model may have different timing parameters, so we need to distinguish them.
+ */
+typedef enum {
+ LED_MODEL_WS2812, /*!< LED strip model: WS2812 */
+ LED_MODEL_SK6812, /*!< LED strip model: SK6812 */
+ LED_MODEL_INVALID /*!< Invalid LED strip model */
+} led_model_t;
+
+/**
+ * @brief LED strip handle
+ */
+typedef struct led_strip_t *led_strip_handle_t;
+
+/**
+ * @brief LED Strip Configuration
+ */
+typedef struct {
+ int strip_gpio_num; /*!< GPIO number that used by LED strip */
+ uint32_t max_leds; /*!< Maximum LEDs in a single strip */
+ led_pixel_format_t led_pixel_format; /*!< LED pixel format */
+ led_model_t led_model; /*!< LED model */
+
+ struct {
+ uint32_t invert_out: 1; /*!< Invert output signal */
+ } flags; /*!< Extra driver flags */
+} led_strip_config_t;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/hw/bsp/espressif/components/led_strip/interface/led_strip_interface.h b/hw/bsp/espressif/components/led_strip/interface/led_strip_interface.h
new file mode 100644
index 000000000..3de4c2715
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/interface/led_strip_interface.h
@@ -0,0 +1,95 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include
+#include "esp_err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct led_strip_t led_strip_t; /*!< Type of LED strip */
+
+/**
+ * @brief LED strip interface definition
+ */
+struct led_strip_t {
+ /**
+ * @brief Set RGB for a specific pixel
+ *
+ * @param strip: LED strip
+ * @param index: index of pixel to set
+ * @param red: red part of color
+ * @param green: green part of color
+ * @param blue: blue part of color
+ *
+ * @return
+ * - ESP_OK: Set RGB for a specific pixel successfully
+ * - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
+ * - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
+ */
+ esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
+
+ /**
+ * @brief Set RGBW for a specific pixel. Similar to `set_pixel` but also set the white component
+ *
+ * @param strip: LED strip
+ * @param index: index of pixel to set
+ * @param red: red part of color
+ * @param green: green part of color
+ * @param blue: blue part of color
+ * @param white: separate white component
+ *
+ * @return
+ * - ESP_OK: Set RGBW color for a specific pixel successfully
+ * - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
+ * - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
+ */
+ esp_err_t (*set_pixel_rgbw)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
+
+ /**
+ * @brief Refresh memory colors to LEDs
+ *
+ * @param strip: LED strip
+ * @param timeout_ms: timeout value for refreshing task
+ *
+ * @return
+ * - ESP_OK: Refresh successfully
+ * - ESP_FAIL: Refresh failed because some other error occurred
+ *
+ * @note:
+ * After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
+ */
+ esp_err_t (*refresh)(led_strip_t *strip);
+
+ /**
+ * @brief Clear LED strip (turn off all LEDs)
+ *
+ * @param strip: LED strip
+ * @param timeout_ms: timeout value for clearing task
+ *
+ * @return
+ * - ESP_OK: Clear LEDs successfully
+ * - ESP_FAIL: Clear LEDs failed because some other error occurred
+ */
+ esp_err_t (*clear)(led_strip_t *strip);
+
+ /**
+ * @brief Free LED strip resources
+ *
+ * @param strip: LED strip
+ *
+ * @return
+ * - ESP_OK: Free resources successfully
+ * - ESP_FAIL: Free resources failed because error occurred
+ */
+ esp_err_t (*del)(led_strip_t *strip);
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/hw/bsp/espressif/components/led_strip/src/led_strip_api.c b/hw/bsp/espressif/components/led_strip/src/led_strip_api.c
new file mode 100644
index 000000000..6eb86b8f1
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/src/led_strip_api.c
@@ -0,0 +1,94 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "esp_log.h"
+#include "esp_check.h"
+#include "led_strip.h"
+#include "led_strip_interface.h"
+
+static const char *TAG = "led_strip";
+
+esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
+{
+ ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
+ return strip->set_pixel(strip, index, red, green, blue);
+}
+
+esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value)
+{
+ ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
+
+ uint32_t red = 0;
+ uint32_t green = 0;
+ uint32_t blue = 0;
+
+ uint32_t rgb_max = value;
+ uint32_t rgb_min = rgb_max * (255 - saturation) / 255.0f;
+
+ uint32_t i = hue / 60;
+ uint32_t diff = hue % 60;
+
+ // RGB adjustment amount by hue
+ uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
+
+ switch (i) {
+ case 0:
+ red = rgb_max;
+ green = rgb_min + rgb_adj;
+ blue = rgb_min;
+ break;
+ case 1:
+ red = rgb_max - rgb_adj;
+ green = rgb_max;
+ blue = rgb_min;
+ break;
+ case 2:
+ red = rgb_min;
+ green = rgb_max;
+ blue = rgb_min + rgb_adj;
+ break;
+ case 3:
+ red = rgb_min;
+ green = rgb_max - rgb_adj;
+ blue = rgb_max;
+ break;
+ case 4:
+ red = rgb_min + rgb_adj;
+ green = rgb_min;
+ blue = rgb_max;
+ break;
+ default:
+ red = rgb_max;
+ green = rgb_min;
+ blue = rgb_max - rgb_adj;
+ break;
+ }
+
+ return strip->set_pixel(strip, index, red, green, blue);
+}
+
+esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
+{
+ ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
+ return strip->set_pixel_rgbw(strip, index, red, green, blue, white);
+}
+
+esp_err_t led_strip_refresh(led_strip_handle_t strip)
+{
+ ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
+ return strip->refresh(strip);
+}
+
+esp_err_t led_strip_clear(led_strip_handle_t strip)
+{
+ ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
+ return strip->clear(strip);
+}
+
+esp_err_t led_strip_del(led_strip_handle_t strip)
+{
+ ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
+ return strip->del(strip);
+}
diff --git a/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_dev.c b/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_dev.c
new file mode 100644
index 000000000..1cbf0e45a
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_dev.c
@@ -0,0 +1,164 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include
+#include
+#include
+#include "esp_log.h"
+#include "esp_check.h"
+#include "driver/rmt_tx.h"
+#include "led_strip.h"
+#include "led_strip_interface.h"
+#include "led_strip_rmt_encoder.h"
+
+#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution
+#define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
+// the memory size of each RMT channel, in words (4 bytes)
+#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
+#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
+#else
+#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
+#endif
+
+static const char *TAG = "led_strip_rmt";
+
+typedef struct {
+ led_strip_t base;
+ rmt_channel_handle_t rmt_chan;
+ rmt_encoder_handle_t strip_encoder;
+ uint32_t strip_len;
+ uint8_t bytes_per_pixel;
+ uint8_t pixel_buf[];
+} led_strip_rmt_obj;
+
+static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
+{
+ led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
+ ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
+ uint32_t start = index * rmt_strip->bytes_per_pixel;
+ // In thr order of GRB, as LED strip like WS2812 sends out pixels in this order
+ rmt_strip->pixel_buf[start + 0] = green & 0xFF;
+ rmt_strip->pixel_buf[start + 1] = red & 0xFF;
+ rmt_strip->pixel_buf[start + 2] = blue & 0xFF;
+ if (rmt_strip->bytes_per_pixel > 3) {
+ rmt_strip->pixel_buf[start + 3] = 0;
+ }
+ return ESP_OK;
+}
+
+static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
+{
+ led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
+ ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
+ ESP_RETURN_ON_FALSE(rmt_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel");
+ uint8_t *buf_start = rmt_strip->pixel_buf + index * 4;
+ // SK6812 component order is GRBW
+ *buf_start = green & 0xFF;
+ *++buf_start = red & 0xFF;
+ *++buf_start = blue & 0xFF;
+ *++buf_start = white & 0xFF;
+ return ESP_OK;
+}
+
+static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
+{
+ led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
+ rmt_transmit_config_t tx_conf = {
+ .loop_count = 0,
+ };
+
+ ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed");
+ ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf,
+ rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &tx_conf), TAG, "transmit pixels by RMT failed");
+ ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed");
+ ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed");
+ return ESP_OK;
+}
+
+static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
+{
+ led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
+ // Write zero to turn off all leds
+ memset(rmt_strip->pixel_buf, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
+ return led_strip_rmt_refresh(strip);
+}
+
+static esp_err_t led_strip_rmt_del(led_strip_t *strip)
+{
+ led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
+ ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed");
+ ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed");
+ free(rmt_strip);
+ return ESP_OK;
+}
+
+esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip)
+{
+ led_strip_rmt_obj *rmt_strip = NULL;
+ esp_err_t ret = ESP_OK;
+ ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
+ ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format");
+ uint8_t bytes_per_pixel = 3;
+ if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
+ bytes_per_pixel = 4;
+ } else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
+ bytes_per_pixel = 3;
+ } else {
+ assert(false);
+ }
+ rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
+ ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip");
+ uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION;
+
+ // for backward compatibility, if the user does not set the clk_src, use the default value
+ rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT;
+ if (rmt_config->clk_src) {
+ clk_src = rmt_config->clk_src;
+ }
+ size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
+ // override the default value if the user sets it
+ if (rmt_config->mem_block_symbols) {
+ mem_block_symbols = rmt_config->mem_block_symbols;
+ }
+ rmt_tx_channel_config_t rmt_chan_config = {
+ .clk_src = clk_src,
+ .gpio_num = led_config->strip_gpio_num,
+ .mem_block_symbols = mem_block_symbols,
+ .resolution_hz = resolution,
+ .trans_queue_depth = LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE,
+ .flags.with_dma = rmt_config->flags.with_dma,
+ .flags.invert_out = led_config->flags.invert_out,
+ };
+ ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed");
+
+ led_strip_encoder_config_t strip_encoder_conf = {
+ .resolution = resolution,
+ .led_model = led_config->led_model
+ };
+ ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed");
+
+
+ rmt_strip->bytes_per_pixel = bytes_per_pixel;
+ rmt_strip->strip_len = led_config->max_leds;
+ rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
+ rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw;
+ rmt_strip->base.refresh = led_strip_rmt_refresh;
+ rmt_strip->base.clear = led_strip_rmt_clear;
+ rmt_strip->base.del = led_strip_rmt_del;
+
+ *ret_strip = &rmt_strip->base;
+ return ESP_OK;
+err:
+ if (rmt_strip) {
+ if (rmt_strip->rmt_chan) {
+ rmt_del_channel(rmt_strip->rmt_chan);
+ }
+ if (rmt_strip->strip_encoder) {
+ rmt_del_encoder(rmt_strip->strip_encoder);
+ }
+ free(rmt_strip);
+ }
+ return ret;
+}
diff --git a/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_dev_idf4.c b/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_dev_idf4.c
new file mode 100644
index 000000000..a1067cd7c
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_dev_idf4.c
@@ -0,0 +1,194 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include
+#include
+#include
+#include "esp_log.h"
+#include "esp_check.h"
+#include "driver/rmt.h"
+#include "led_strip.h"
+#include "led_strip_interface.h"
+
+static const char *TAG = "led_strip_rmt";
+
+#define WS2812_T0H_NS (300)
+#define WS2812_T0L_NS (900)
+#define WS2812_T1H_NS (900)
+#define WS2812_T1L_NS (300)
+
+#define SK6812_T0H_NS (300)
+#define SK6812_T0L_NS (900)
+#define SK6812_T1H_NS (600)
+#define SK6812_T1L_NS (600)
+
+#define LED_STRIP_RESET_MS (10)
+
+// the memory size of each RMT channel, in words (4 bytes)
+#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
+#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
+#else
+#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
+#endif
+
+static uint32_t led_t0h_ticks = 0;
+static uint32_t led_t1h_ticks = 0;
+static uint32_t led_t0l_ticks = 0;
+static uint32_t led_t1l_ticks = 0;
+
+typedef struct {
+ led_strip_t base;
+ rmt_channel_t rmt_channel;
+ uint32_t strip_len;
+ uint8_t bytes_per_pixel;
+ uint8_t buffer[0];
+} led_strip_rmt_obj;
+
+static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
+ size_t wanted_num, size_t *translated_size, size_t *item_num)
+{
+ if (src == NULL || dest == NULL) {
+ *translated_size = 0;
+ *item_num = 0;
+ return;
+ }
+ const rmt_item32_t bit0 = {{{ led_t0h_ticks, 1, led_t0l_ticks, 0 }}}; //Logical 0
+ const rmt_item32_t bit1 = {{{ led_t1h_ticks, 1, led_t1l_ticks, 0 }}}; //Logical 1
+ size_t size = 0;
+ size_t num = 0;
+ uint8_t *psrc = (uint8_t *)src;
+ rmt_item32_t *pdest = dest;
+ while (size < src_size && num < wanted_num) {
+ for (int i = 0; i < 8; i++) {
+ // MSB first
+ if (*psrc & (1 << (7 - i))) {
+ pdest->val = bit1.val;
+ } else {
+ pdest->val = bit0.val;
+ }
+ num++;
+ pdest++;
+ }
+ size++;
+ psrc++;
+ }
+ *translated_size = size;
+ *item_num = num;
+}
+
+static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
+{
+ led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
+ ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of the maximum number of leds");
+ uint32_t start = index * rmt_strip->bytes_per_pixel;
+ // In thr order of GRB
+ rmt_strip->buffer[start + 0] = green & 0xFF;
+ rmt_strip->buffer[start + 1] = red & 0xFF;
+ rmt_strip->buffer[start + 2] = blue & 0xFF;
+ if (rmt_strip->bytes_per_pixel > 3) {
+ rmt_strip->buffer[start + 3] = 0;
+ }
+ return ESP_OK;
+}
+
+static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
+{
+ led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
+ ESP_RETURN_ON_ERROR(rmt_write_sample(rmt_strip->rmt_channel, rmt_strip->buffer, rmt_strip->strip_len * rmt_strip->bytes_per_pixel, true), TAG,
+ "transmit RMT samples failed");
+ vTaskDelay(pdMS_TO_TICKS(LED_STRIP_RESET_MS));
+ return ESP_OK;
+}
+
+static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
+{
+ led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
+ // Write zero to turn off all LEDs
+ memset(rmt_strip->buffer, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
+ return led_strip_rmt_refresh(strip);
+}
+
+static esp_err_t led_strip_rmt_del(led_strip_t *strip)
+{
+ led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
+ ESP_RETURN_ON_ERROR(rmt_driver_uninstall(rmt_strip->rmt_channel), TAG, "uninstall RMT driver failed");
+ free(rmt_strip);
+ return ESP_OK;
+}
+
+esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *dev_config, led_strip_handle_t *ret_strip)
+{
+ led_strip_rmt_obj *rmt_strip = NULL;
+ esp_err_t ret = ESP_OK;
+ ESP_RETURN_ON_FALSE(led_config && dev_config && ret_strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
+ ESP_RETURN_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, TAG, "invalid led_pixel_format");
+ ESP_RETURN_ON_FALSE(dev_config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA is not supported");
+
+ uint8_t bytes_per_pixel = 3;
+ if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
+ bytes_per_pixel = 4;
+ } else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
+ bytes_per_pixel = 3;
+ } else {
+ assert(false);
+ }
+
+ // allocate memory for led_strip object
+ rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
+ ESP_RETURN_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, TAG, "request memory for les_strip failed");
+
+ // install RMT channel driver
+ rmt_config_t config = RMT_DEFAULT_CONFIG_TX(led_config->strip_gpio_num, dev_config->rmt_channel);
+ // set the minimal clock division because the LED strip needs a high clock resolution
+ config.clk_div = 2;
+
+ uint8_t mem_block_num = 2;
+ // override the default value if the user specify the mem block size
+ if (dev_config->mem_block_symbols) {
+ mem_block_num = (dev_config->mem_block_symbols + LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS / 2) / LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
+ }
+ config.mem_block_num = mem_block_num;
+
+ ESP_GOTO_ON_ERROR(rmt_config(&config), err, TAG, "RMT config failed");
+ ESP_GOTO_ON_ERROR(rmt_driver_install(config.channel, 0, 0), err, TAG, "RMT install failed");
+
+ uint32_t counter_clk_hz = 0;
+ rmt_get_counter_clock((rmt_channel_t)dev_config->rmt_channel, &counter_clk_hz);
+ // ns -> ticks
+ float ratio = (float)counter_clk_hz / 1e9;
+ if (led_config->led_model == LED_MODEL_WS2812) {
+ led_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
+ led_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
+ led_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
+ led_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
+ } else if (led_config->led_model == LED_MODEL_SK6812) {
+ led_t0h_ticks = (uint32_t)(ratio * SK6812_T0H_NS);
+ led_t0l_ticks = (uint32_t)(ratio * SK6812_T0L_NS);
+ led_t1h_ticks = (uint32_t)(ratio * SK6812_T1H_NS);
+ led_t1l_ticks = (uint32_t)(ratio * SK6812_T1L_NS);
+ } else {
+ assert(false);
+ }
+
+ // adapter to translates the LES strip date frame into RMT symbols
+ rmt_translator_init((rmt_channel_t)dev_config->rmt_channel, ws2812_rmt_adapter);
+
+ rmt_strip->bytes_per_pixel = bytes_per_pixel;
+ rmt_strip->rmt_channel = (rmt_channel_t)dev_config->rmt_channel;
+ rmt_strip->strip_len = led_config->max_leds;
+ rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
+ rmt_strip->base.refresh = led_strip_rmt_refresh;
+ rmt_strip->base.clear = led_strip_rmt_clear;
+ rmt_strip->base.del = led_strip_rmt_del;
+
+ *ret_strip = &rmt_strip->base;
+ return ESP_OK;
+
+err:
+ if (rmt_strip) {
+ free(rmt_strip);
+ }
+ return ret;
+}
diff --git a/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_encoder.c b/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_encoder.c
new file mode 100644
index 000000000..d352ac07f
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_encoder.c
@@ -0,0 +1,146 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "esp_check.h"
+#include "led_strip_rmt_encoder.h"
+
+static const char *TAG = "led_rmt_encoder";
+
+typedef struct {
+ rmt_encoder_t base;
+ rmt_encoder_t *bytes_encoder;
+ rmt_encoder_t *copy_encoder;
+ int state;
+ rmt_symbol_word_t reset_code;
+} rmt_led_strip_encoder_t;
+
+static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
+{
+ rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
+ rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
+ rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
+ rmt_encode_state_t session_state = 0;
+ rmt_encode_state_t state = 0;
+ size_t encoded_symbols = 0;
+ switch (led_encoder->state) {
+ case 0: // send RGB data
+ encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
+ if (session_state & RMT_ENCODING_COMPLETE) {
+ led_encoder->state = 1; // switch to next state when current encoding session finished
+ }
+ if (session_state & RMT_ENCODING_MEM_FULL) {
+ state |= RMT_ENCODING_MEM_FULL;
+ goto out; // yield if there's no free space for encoding artifacts
+ }
+ // fall-through
+ case 1: // send reset code
+ encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
+ sizeof(led_encoder->reset_code), &session_state);
+ if (session_state & RMT_ENCODING_COMPLETE) {
+ led_encoder->state = 0; // back to the initial encoding session
+ state |= RMT_ENCODING_COMPLETE;
+ }
+ if (session_state & RMT_ENCODING_MEM_FULL) {
+ state |= RMT_ENCODING_MEM_FULL;
+ goto out; // yield if there's no free space for encoding artifacts
+ }
+ }
+out:
+ *ret_state = state;
+ return encoded_symbols;
+}
+
+static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
+{
+ rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
+ rmt_del_encoder(led_encoder->bytes_encoder);
+ rmt_del_encoder(led_encoder->copy_encoder);
+ free(led_encoder);
+ return ESP_OK;
+}
+
+static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
+{
+ rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
+ rmt_encoder_reset(led_encoder->bytes_encoder);
+ rmt_encoder_reset(led_encoder->copy_encoder);
+ led_encoder->state = 0;
+ return ESP_OK;
+}
+
+esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
+{
+ esp_err_t ret = ESP_OK;
+ rmt_led_strip_encoder_t *led_encoder = NULL;
+ ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
+ ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model");
+ led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t));
+ ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
+ led_encoder->base.encode = rmt_encode_led_strip;
+ led_encoder->base.del = rmt_del_led_strip_encoder;
+ led_encoder->base.reset = rmt_led_strip_encoder_reset;
+ rmt_bytes_encoder_config_t bytes_encoder_config;
+ if (config->led_model == LED_MODEL_SK6812) {
+ bytes_encoder_config = (rmt_bytes_encoder_config_t) {
+ .bit0 = {
+ .level0 = 1,
+ .duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
+ .level1 = 0,
+ .duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
+ },
+ .bit1 = {
+ .level0 = 1,
+ .duration0 = 0.6 * config->resolution / 1000000, // T1H=0.6us
+ .level1 = 0,
+ .duration1 = 0.6 * config->resolution / 1000000, // T1L=0.6us
+ },
+ .flags.msb_first = 1 // SK6812 transfer bit order: G7...G0R7...R0B7...B0(W7...W0)
+ };
+ } else if (config->led_model == LED_MODEL_WS2812) {
+ // different led strip might have its own timing requirements, following parameter is for WS2812
+ bytes_encoder_config = (rmt_bytes_encoder_config_t) {
+ .bit0 = {
+ .level0 = 1,
+ .duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
+ .level1 = 0,
+ .duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
+ },
+ .bit1 = {
+ .level0 = 1,
+ .duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us
+ .level1 = 0,
+ .duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us
+ },
+ .flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
+ };
+ } else {
+ assert(false);
+ }
+ ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
+ rmt_copy_encoder_config_t copy_encoder_config = {};
+ ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
+
+ uint32_t reset_ticks = config->resolution / 1000000 * 50 / 2; // reset code duration defaults to 50us
+ led_encoder->reset_code = (rmt_symbol_word_t) {
+ .level0 = 0,
+ .duration0 = reset_ticks,
+ .level1 = 0,
+ .duration1 = reset_ticks,
+ };
+ *ret_encoder = &led_encoder->base;
+ return ESP_OK;
+err:
+ if (led_encoder) {
+ if (led_encoder->bytes_encoder) {
+ rmt_del_encoder(led_encoder->bytes_encoder);
+ }
+ if (led_encoder->copy_encoder) {
+ rmt_del_encoder(led_encoder->copy_encoder);
+ }
+ free(led_encoder);
+ }
+ return ret;
+}
diff --git a/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_encoder.h b/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_encoder.h
new file mode 100644
index 000000000..ba71e60ab
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_encoder.h
@@ -0,0 +1,38 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include
+#include "driver/rmt_encoder.h"
+#include "led_strip_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Type of led strip encoder configuration
+ */
+typedef struct {
+ uint32_t resolution; /*!< Encoder resolution, in Hz */
+ led_model_t led_model; /*!< LED model */
+} led_strip_encoder_config_t;
+
+/**
+ * @brief Create RMT encoder for encoding LED strip pixels into RMT symbols
+ *
+ * @param[in] config Encoder configuration
+ * @param[out] ret_encoder Returned encoder handle
+ * @return
+ * - ESP_ERR_INVALID_ARG for any invalid arguments
+ * - ESP_ERR_NO_MEM out of memory when creating led strip encoder
+ * - ESP_OK if creating encoder successfully
+ */
+esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_ws2812.c b/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_ws2812.c
deleted file mode 100644
index fd1746cad..000000000
--- a/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_ws2812.c
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#include
-#include
-#include
-#include "esp_log.h"
-#include "esp_attr.h"
-#include "led_strip.h"
-#include "driver/rmt.h"
-
-static const char *TAG = "ws2812";
-#define STRIP_CHECK(a, str, goto_tag, ret_value, ...) \
- do \
- { \
- if (!(a)) \
- { \
- ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
- ret = ret_value; \
- goto goto_tag; \
- } \
- } while (0)
-
-#define WS2812_T0H_NS (350)
-#define WS2812_T0L_NS (1000)
-#define WS2812_T1H_NS (1000)
-#define WS2812_T1L_NS (350)
-#define WS2812_RESET_US (280)
-
-static uint32_t ws2812_t0h_ticks = 0;
-static uint32_t ws2812_t1h_ticks = 0;
-static uint32_t ws2812_t0l_ticks = 0;
-static uint32_t ws2812_t1l_ticks = 0;
-
-typedef struct {
- led_strip_t parent;
- rmt_channel_t rmt_channel;
- uint32_t strip_len;
- uint8_t buffer[0];
-} ws2812_t;
-
-/**
- * @brief Convert RGB data to RMT format.
- *
- * @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
- *
- * @param[in] src: source data, to converted to RMT format
- * @param[in] dest: place where to store the convert result
- * @param[in] src_size: size of source data
- * @param[in] wanted_num: number of RMT items that want to get
- * @param[out] translated_size: number of source data that got converted
- * @param[out] item_num: number of RMT items which are converted from source data
- */
-static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
- size_t wanted_num, size_t *translated_size, size_t *item_num)
-{
- if (src == NULL || dest == NULL) {
- *translated_size = 0;
- *item_num = 0;
- return;
- }
- const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0
- const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1
- size_t size = 0;
- size_t num = 0;
- uint8_t *psrc = (uint8_t *)src;
- rmt_item32_t *pdest = dest;
- while (size < src_size && num < wanted_num) {
- for (int i = 0; i < 8; i++) {
- // MSB first
- if (*psrc & (1 << (7 - i))) {
- pdest->val = bit1.val;
- } else {
- pdest->val = bit0.val;
- }
- num++;
- pdest++;
- }
- size++;
- psrc++;
- }
- *translated_size = size;
- *item_num = num;
-}
-
-static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
-{
- esp_err_t ret = ESP_OK;
- ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
- STRIP_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG);
- uint32_t start = index * 3;
- // In thr order of GRB
- ws2812->buffer[start + 0] = green & 0xFF;
- ws2812->buffer[start + 1] = red & 0xFF;
- ws2812->buffer[start + 2] = blue & 0xFF;
- return ESP_OK;
-err:
- return ret;
-}
-
-static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms)
-{
- esp_err_t ret = ESP_OK;
- ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
- STRIP_CHECK(rmt_write_sample(ws2812->rmt_channel, ws2812->buffer, ws2812->strip_len * 3, true) == ESP_OK,
- "transmit RMT samples failed", err, ESP_FAIL);
- return rmt_wait_tx_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms));
-err:
- return ret;
-}
-
-static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms)
-{
- ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
- // Write zero to turn off all leds
- memset(ws2812->buffer, 0, ws2812->strip_len * 3);
- return ws2812_refresh(strip, timeout_ms);
-}
-
-static esp_err_t ws2812_del(led_strip_t *strip)
-{
- ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
- free(ws2812);
- return ESP_OK;
-}
-
-led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config)
-{
- led_strip_t *ret = NULL;
- STRIP_CHECK(config, "configuration can't be null", err, NULL);
-
- // 24 bits per led
- uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3;
- ws2812_t *ws2812 = calloc(1, ws2812_size);
- STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL);
-
- uint32_t counter_clk_hz = 0;
- STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK,
- "get rmt counter clock failed", err, NULL);
- // ns -> ticks
- float ratio = (float)counter_clk_hz / 1e9;
- ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
- ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
- ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
- ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
-
- // set ws2812 to rmt adapter
- rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter);
-
- ws2812->rmt_channel = (rmt_channel_t)config->dev;
- ws2812->strip_len = config->max_leds;
-
- ws2812->parent.set_pixel = ws2812_set_pixel;
- ws2812->parent.refresh = ws2812_refresh;
- ws2812->parent.clear = ws2812_clear;
- ws2812->parent.del = ws2812_del;
-
- return &ws2812->parent;
-err:
- return ret;
-}
diff --git a/hw/bsp/espressif/components/led_strip/src/led_strip_spi_dev.c b/hw/bsp/espressif/components/led_strip/src/led_strip_spi_dev.c
new file mode 100644
index 000000000..12ea8fbf3
--- /dev/null
+++ b/hw/bsp/espressif/components/led_strip/src/led_strip_spi_dev.c
@@ -0,0 +1,209 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include
+#include
+#include
+#include "esp_log.h"
+#include "esp_check.h"
+#include "esp_rom_gpio.h"
+#include "soc/spi_periph.h"
+#include "led_strip.h"
+#include "led_strip_interface.h"
+#include "hal/spi_hal.h"
+
+#define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution
+#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
+
+#define SPI_BYTES_PER_COLOR_BYTE 3
+#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
+
+static const char *TAG = "led_strip_spi";
+
+typedef struct {
+ led_strip_t base;
+ spi_host_device_t spi_host;
+ spi_device_handle_t spi_device;
+ uint32_t strip_len;
+ uint8_t bytes_per_pixel;
+ uint8_t pixel_buf[];
+} led_strip_spi_obj;
+
+// please make sure to zero-initialize the buf before calling this function
+static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
+{
+ // Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
+ // So a color byte occupies 3 bytes of SPI.
+ *(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2);
+ *(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5);
+ *(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00;
+ *(buf + 1) |= BIT(0);
+ *(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3);
+ *(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6);
+ *(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1);
+ *(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4);
+ *(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7);
+}
+
+static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
+{
+ led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
+ ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
+ // LED_PIXEL_FORMAT_GRB takes 72bits(9bytes)
+ uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
+ memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
+ __led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
+ __led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]);
+ __led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
+ if (spi_strip->bytes_per_pixel > 3) {
+ __led_strip_spi_bit(0, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
+ }
+ return ESP_OK;
+}
+
+static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
+{
+ led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
+ ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
+ ESP_RETURN_ON_FALSE(spi_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel");
+ // LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
+ uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
+ // SK6812 component order is GRBW
+ memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
+ __led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
+ __led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]);
+ __led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
+ __led_strip_spi_bit(white, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
+
+ return ESP_OK;
+}
+
+static esp_err_t led_strip_spi_refresh(led_strip_t *strip)
+{
+ led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
+ spi_transaction_t tx_conf;
+ memset(&tx_conf, 0, sizeof(tx_conf));
+
+ tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
+ tx_conf.tx_buffer = spi_strip->pixel_buf;
+ tx_conf.rx_buffer = NULL;
+ ESP_RETURN_ON_ERROR(spi_device_transmit(spi_strip->spi_device, &tx_conf), TAG, "transmit pixels by SPI failed");
+
+ return ESP_OK;
+}
+
+static esp_err_t led_strip_spi_clear(led_strip_t *strip)
+{
+ led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
+ //Write zero to turn off all leds
+ memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
+ uint8_t *buf = spi_strip->pixel_buf;
+ for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) {
+ __led_strip_spi_bit(0, buf);
+ buf += SPI_BYTES_PER_COLOR_BYTE;
+ }
+
+ return led_strip_spi_refresh(strip);
+}
+
+static esp_err_t led_strip_spi_del(led_strip_t *strip)
+{
+ led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
+
+ ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed");
+ ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed");
+
+ free(spi_strip);
+ return ESP_OK;
+}
+
+esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip)
+{
+ led_strip_spi_obj *spi_strip = NULL;
+ esp_err_t ret = ESP_OK;
+ ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
+ ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format");
+ uint8_t bytes_per_pixel = 3;
+ if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
+ bytes_per_pixel = 4;
+ } else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
+ bytes_per_pixel = 3;
+ } else {
+ assert(false);
+ }
+ uint32_t mem_caps = MALLOC_CAP_DEFAULT;
+ if (spi_config->flags.with_dma) {
+ // DMA buffer must be placed in internal SRAM
+ mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
+ }
+ spi_strip = heap_caps_calloc(1, sizeof(led_strip_spi_obj) + led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE, mem_caps);
+
+ ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip");
+
+ spi_strip->spi_host = spi_config->spi_bus;
+ // for backward compatibility, if the user does not set the clk_src, use the default value
+ spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
+ if (spi_config->clk_src) {
+ clk_src = spi_config->clk_src;
+ }
+
+ spi_bus_config_t spi_bus_cfg = {
+ .mosi_io_num = led_config->strip_gpio_num,
+ //Only use MOSI to generate the signal, set -1 when other pins are not used.
+ .miso_io_num = -1,
+ .sclk_io_num = -1,
+ .quadwp_io_num = -1,
+ .quadhd_io_num = -1,
+ .max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE,
+ };
+ ESP_GOTO_ON_ERROR(spi_bus_initialize(spi_strip->spi_host, &spi_bus_cfg, spi_config->flags.with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED), err, TAG, "create SPI bus failed");
+
+ if (led_config->flags.invert_out == true) {
+ esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false);
+ }
+
+ spi_device_interface_config_t spi_dev_cfg = {
+ .clock_source = clk_src,
+ .command_bits = 0,
+ .address_bits = 0,
+ .dummy_bits = 0,
+ .clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
+ .mode = 0,
+ //set -1 when CS is not used
+ .spics_io_num = -1,
+ .queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE,
+ };
+
+ ESP_GOTO_ON_ERROR(spi_bus_add_device(spi_strip->spi_host, &spi_dev_cfg, &spi_strip->spi_device), err, TAG, "Failed to add spi device");
+
+ int clock_resolution_khz = 0;
+ spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz);
+ // TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
+ // But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION
+ ESP_GOTO_ON_FALSE(clock_resolution_khz == LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000, ESP_ERR_NOT_SUPPORTED, err,
+ TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
+
+ spi_strip->bytes_per_pixel = bytes_per_pixel;
+ spi_strip->strip_len = led_config->max_leds;
+ spi_strip->base.set_pixel = led_strip_spi_set_pixel;
+ spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw;
+ spi_strip->base.refresh = led_strip_spi_refresh;
+ spi_strip->base.clear = led_strip_spi_clear;
+ spi_strip->base.del = led_strip_spi_del;
+
+ *ret_strip = &spi_strip->base;
+ return ESP_OK;
+err:
+ if (spi_strip) {
+ if (spi_strip->spi_device) {
+ spi_bus_remove_device(spi_strip->spi_device);
+ }
+ if (spi_strip->spi_host) {
+ spi_bus_free(spi_strip->spi_host);
+ }
+ free(spi_strip);
+ }
+ return ret;
+}
diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake
index 539a776a6..ff3393cfd 100644
--- a/hw/bsp/family_support.cmake
+++ b/hw/bsp/family_support.cmake
@@ -211,7 +211,6 @@ function(family_configure_common TARGET RTOS)
target_link_options(${TARGET} PUBLIC "LINKER:--map=$.map")
endif()
-
# ETM Trace option
if (TRACE_ETM STREQUAL "1")
target_compile_definitions(${TARGET} PUBLIC TRACE_ETM)
diff --git a/hw/bsp/lpc17/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/lpc17/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..9b66fb110
--- /dev/null
+++ b/hw/bsp/lpc17/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,149 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+ #include "chip.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU 0
+#define configENABLE_FPU 0
+#define configENABLE_TRUSTZONE 0
+#define configMINIMAL_SECURE_STACK_SIZE (1024)
+
+#define configUSE_PREEMPTION 1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ SystemCoreClock
+#define configTICK_RATE_HZ ( 1000 )
+#define configMAX_PRIORITIES ( 5 )
+#define configMINIMAL_STACK_SIZE ( 128 )
+#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN 16
+#define configUSE_16_BIT_TICKS 0
+#define configIDLE_SHOULD_YIELD 1
+#define configUSE_MUTEXES 1
+#define configUSE_RECURSIVE_MUTEXES 1
+#define configUSE_COUNTING_SEMAPHORES 1
+#define configQUEUE_REGISTRY_SIZE 4
+#define configUSE_QUEUE_SETS 0
+#define configUSE_TIME_SLICING 0
+#define configUSE_NEWLIB_REENTRANT 0
+#define configENABLE_BACKWARD_COMPATIBILITY 1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0
+
+#define configSUPPORT_STATIC_ALLOCATION 1
+#define configSUPPORT_DYNAMIC_ALLOCATION 0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK 0
+#define configUSE_TICK_HOOK 0
+#define configUSE_MALLOC_FAILED_HOOK 0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW 2
+#define configCHECK_HANDLER_INSTALLATION 0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS 0
+#define configRECORD_STACK_HIGH_ADDRESS 1
+#define configUSE_TRACE_FACILITY 1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS 0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES 0
+#define configMAX_CO_ROUTINE_PRIORITIES 2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS 1
+#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH 32
+#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet 0
+#define INCLUDE_uxTaskPriorityGet 0
+#define INCLUDE_vTaskDelete 0
+#define INCLUDE_vTaskSuspend 1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR 0
+#define INCLUDE_vTaskDelayUntil 1
+#define INCLUDE_vTaskDelay 1
+#define INCLUDE_xTaskGetSchedulerState 0
+#define INCLUDE_xTaskGetCurrentTaskHandle 1
+#define INCLUDE_uxTaskGetStackHighWaterMark 0
+#define INCLUDE_xTaskGetIdleTaskHandle 0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName 0
+#define INCLUDE_eTaskGetState 0
+#define INCLUDE_xEventGroupSetBitFromISR 0
+#define INCLUDE_xTimerPendFunctionCall 0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler PendSV_Handler
+#define xPortSysTickHandler SysTick_Handler
+#define vPortSVCHandler SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS 3
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1<OTGClkCtrl = clk_en;
- while ( (LPC_USB->OTGClkSt & clk_en) != clk_en );
-
-#if CFG_TUH_ENABLED
- // set portfunc to host !!!
- LPC_USB->StCtrl = 0x3; // should be 1
-#endif
-}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
- Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
-}
-
-uint32_t board_button_read(void)
-{
- return BUTTON_STATE_ACTIVE == Chip_GPIO_GetPinState(LPC_GPIO, BUTTON_PORT, BUTTON_PIN);
-}
-
-int board_uart_read(uint8_t* buf, int len)
-{
-// return UART_ReceiveByte(BOARD_UART_PORT);
- (void) buf; (void) len;
- return 0;
-}
-
-int board_uart_write(void const * buf, int len)
-{
-// UART_Send(BOARD_UART_PORT, &c, 1, BLOCKING);
- (void) buf; (void) len;
- return 0;
-}
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-void SysTick_Handler (void)
-{
- system_ticks++;
-}
-
-uint32_t board_millis(void)
-{
- return system_ticks;
-}
-#endif
diff --git a/hw/bsp/lpc17/boards/mbed1768/board.cmake b/hw/bsp/lpc17/boards/mbed1768/board.cmake
new file mode 100644
index 000000000..688f34292
--- /dev/null
+++ b/hw/bsp/lpc17/boards/mbed1768/board.cmake
@@ -0,0 +1,11 @@
+set(MCU_VARIANT LPC1768)
+
+set(JLINK_DEVICE LPC1768)
+set(PYOCD_TARGET LPC1768)
+set(NXPLINK_DEVICE LPC1768:LPC1768)
+
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/lpc1768.ld)
+
+function(update_board TARGET)
+ # nothing to do
+endfunction()
diff --git a/hw/bsp/lpc17/boards/mbed1768/board.h b/hw/bsp/lpc17/boards/mbed1768/board.h
new file mode 100644
index 000000000..2b3ddc905
--- /dev/null
+++ b/hw/bsp/lpc17/boards/mbed1768/board.h
@@ -0,0 +1,71 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#define LED_PORT 1
+#define LED_PIN 18
+#define LED_STATE_ON 1
+
+// JOYSTICK_DOWN if using LPCXpresso Base Board
+#define BUTTON_PORT 0
+#define BUTTON_PIN 15
+#define BUTTON_STATE_ACTIVE 0
+
+#define BOARD_UART_PORT LPC_UART3
+
+/* System oscillator rate and RTC oscillator rate */
+const uint32_t OscRateIn = 10000000;
+const uint32_t RTCOscRateIn = 32768;
+
+// Pin muxing configuration
+static const PINMUX_GRP_T pinmuxing[] = {
+ {LED_PORT, LED_PIN, IOCON_MODE_INACT | IOCON_FUNC0},
+ {BUTTON_PORT, BUTTON_PIN, IOCON_FUNC0 | IOCON_MODE_PULLUP},
+};
+
+static const PINMUX_GRP_T pin_usb_mux[] = {
+ {0, 29, IOCON_MODE_INACT | IOCON_FUNC1}, // D+
+ {0, 30, IOCON_MODE_INACT | IOCON_FUNC1}, // D-
+ {2, 9, IOCON_MODE_INACT | IOCON_FUNC1}, // Soft Connect
+
+ {1, 19, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PPWR (Host mode)
+ {1, 22, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PWRD
+
+ // VBUS is not connected on this board, so leave the pin at default setting.
+ // Chip_IOCON_PinMux(LPC_IOCON, 1, 30, IOCON_MODE_INACT, IOCON_FUNC2); // USB VBUS
+};
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/hw/bsp/lpc17/boards/mbed1768/mbed1768.c b/hw/bsp/lpc17/family.c
similarity index 61%
rename from hw/bsp/lpc17/boards/mbed1768/mbed1768.c
rename to hw/bsp/lpc17/family.c
index 613dcb570..79281ba41 100644
--- a/hw/bsp/lpc17/boards/mbed1768/mbed1768.c
+++ b/hw/bsp/lpc17/family.c
@@ -26,58 +26,24 @@
#include "chip.h"
#include "bsp/board_api.h"
-
-#define LED_PORT 1
-#define LED_PIN 18
-#define LED_STATE_ON 1
-
-// JOYSTICK_DOWN if using LPCXpresso Base Board
-#define BUTTON_PORT 0
-#define BUTTON_PIN 15
-#define BUTTON_STATE_ACTIVE 0
-
-#define BOARD_UART_PORT LPC_UART3
-
-/* System oscillator rate and RTC oscillator rate */
-const uint32_t OscRateIn = 10000000;
-const uint32_t RTCOscRateIn = 32768;
-
-/* Pin muxing configuration */
-static const PINMUX_GRP_T pinmuxing[] =
-{
- {LED_PORT, LED_PIN, IOCON_MODE_INACT | IOCON_FUNC0},
- {BUTTON_PORT, BUTTON_PIN, IOCON_FUNC0 | IOCON_MODE_PULLUP},
-};
-
-static const PINMUX_GRP_T pin_usb_mux[] =
-{
- {0, 29, IOCON_MODE_INACT | IOCON_FUNC1}, // D+
- {0, 30, IOCON_MODE_INACT | IOCON_FUNC1}, // D-
- {2, 9, IOCON_MODE_INACT | IOCON_FUNC1}, // Connect
-
- {1, 19, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PPWR
- {1, 22, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PWRD
-
- /* VBUS is not connected on this board, so leave the pin at default setting. */
- /*Chip_IOCON_PinMux(LPC_IOCON, 1, 30, IOCON_MODE_INACT, IOCON_FUNC2);*/ /* USB VBUS */
-};
+#include "board.h"
// Invoked by startup code
-void SystemInit(void)
-{
+void SystemInit(void) {
#ifdef __USE_LPCOPEN
- extern void (* const g_pfnVectors[])(void);
- unsigned int *pSCB_VTOR = (unsigned int *) 0xE000ED08;
- *pSCB_VTOR = (unsigned int) g_pfnVectors;
+ extern void (* const g_pfnVectors[])(void);
+ unsigned int* pSCB_VTOR = (unsigned int*) 0xE000ED08;
+ *pSCB_VTOR = (unsigned int) g_pfnVectors;
#endif
Chip_IOCON_Init(LPC_IOCON);
Chip_IOCON_SetPinMuxing(LPC_IOCON, pinmuxing, sizeof(pinmuxing) / sizeof(PINMUX_GRP_T));
Chip_SetupXtalClocking();
+
+ Chip_SYSCTL_SetFLASHAccess(FLASHTIM_100MHZ_CPU);
}
-void board_init(void)
-{
+void board_init(void) {
SystemCoreClockUpdate();
#if CFG_TUSB_OS == OPT_OS_NONE
@@ -89,11 +55,7 @@ void board_init(void)
#endif
Chip_GPIO_Init(LPC_GPIO);
-
- // LED
Chip_GPIO_SetPinDIROutput(LPC_GPIO, LED_PORT, LED_PIN);
-
- // Button
Chip_GPIO_SetPinDIRInput(LPC_GPIO, BUTTON_PORT, BUTTON_PIN);
#if 0
@@ -106,34 +68,34 @@ void board_init(void)
.OpenDrain = 0,
.Pinmode = 0
};
- PINSEL_ConfigPin(&PinCfg);
+ PINSEL_ConfigPin(&PinCfg);
- PinCfg.Portnum = 0;
- PinCfg.Pinnum = 1; // RXD is P0.1
- PINSEL_ConfigPin(&PinCfg);
+ PinCfg.Portnum = 0;
+ PinCfg.Pinnum = 1; // RXD is P0.1
+ PINSEL_ConfigPin(&PinCfg);
- UART_CFG_Type UARTConfigStruct;
+ UART_CFG_Type UARTConfigStruct;
UART_ConfigStructInit(&UARTConfigStruct);
- UARTConfigStruct.Baud_rate = CFG_BOARD_UART_BAUDRATE;
+ UARTConfigStruct.Baud_rate = CFG_BOARD_UART_BAUDRATE;
- UART_Init(BOARD_UART_PORT, &UARTConfigStruct);
- UART_TxCmd(BOARD_UART_PORT, ENABLE); // Enable UART Transmit
+ UART_Init(BOARD_UART_PORT, &UARTConfigStruct);
+ UART_TxCmd(BOARD_UART_PORT, ENABLE); // Enable UART Transmit
#endif
- //------------- USB -------------//
+ //------------- USB -------------//
Chip_IOCON_SetPinMuxing(LPC_IOCON, pin_usb_mux, sizeof(pin_usb_mux) / sizeof(PINMUX_GRP_T));
- Chip_USB_Init();
+ Chip_USB_Init();
enum {
USBCLK_DEVCIE = 0x12, // AHB + Device
- USBCLK_HOST = 0x19, // AHB + Host + OTG
+ USBCLK_HOST = 0x19, // AHB + Host + OTG
// 0x1B // Host + Device + OTG + AHB
};
uint32_t const clk_en = CFG_TUD_ENABLED ? USBCLK_DEVCIE : USBCLK_HOST;
LPC_USB->OTGClkCtrl = clk_en;
- while ( (LPC_USB->OTGClkSt & clk_en) != clk_en );
+ while ((LPC_USB->OTGClkSt & clk_en) != clk_en) {}
#if CFG_TUH_ENABLED
// set portfunc to host !!!
@@ -141,57 +103,53 @@ void board_init(void)
#endif
}
-//--------------------------------------------------------------------+
-// USB Interrupt Handler
-//--------------------------------------------------------------------+
-void USB_IRQHandler(void)
-{
- #if CFG_TUD_ENABLED
- tud_int_handler(0);
- #endif
-
- #if CFG_TUH_ENABLED
- tuh_int_handler(0, true);
- #endif
-}
-
//--------------------------------------------------------------------+
// Board porting API
//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
- Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+void board_led_write(bool state) {
+ Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON));
}
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
return BUTTON_STATE_ACTIVE == Chip_GPIO_GetPinState(LPC_GPIO, BUTTON_PORT, BUTTON_PIN);
}
-int board_uart_read(uint8_t* buf, int len)
-{
+int board_uart_read(uint8_t* buf, int len) {
// return UART_ReceiveByte(BOARD_UART_PORT);
- (void) buf; (void) len;
+ (void) buf;
+ (void) len;
return 0;
}
-int board_uart_write(void const * buf, int len)
-{
+int board_uart_write(void const* buf, int len) {
// UART_Send(BOARD_UART_PORT, &c, 1, BLOCKING);
- (void) buf; (void) len;
+ (void) buf;
+ (void) len;
return 0;
}
#if CFG_TUSB_OS == OPT_OS_NONE
volatile uint32_t system_ticks = 0;
-void SysTick_Handler (void)
-{
+
+void SysTick_Handler(void) {
system_ticks++;
}
-uint32_t board_millis(void)
-{
+uint32_t board_millis(void) {
return system_ticks;
}
+
+//--------------------------------------------------------------------+
+// USB Interrupt Handler
+//--------------------------------------------------------------------+
+void USB_IRQHandler(void) {
+ #if CFG_TUD_ENABLED
+ tud_int_handler(0);
+ #endif
+
+ #if CFG_TUH_ENABLED
+ tuh_int_handler(0, true);
+ #endif
+}
+
#endif
diff --git a/hw/bsp/lpc17/family.cmake b/hw/bsp/lpc17/family.cmake
new file mode 100644
index 000000000..63ac3149c
--- /dev/null
+++ b/hw/bsp/lpc17/family.cmake
@@ -0,0 +1,102 @@
+include_guard()
+
+if (NOT BOARD)
+ message(FATAL_ERROR "BOARD not specified")
+endif ()
+
+set(SDK_DIR ${TOP}/hw/mcu/nxp/lpcopen/lpc175x_6x/lpc_chip_175x_6x)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR cortex-m3 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS LPC175X_6X CACHE INTERNAL "")
+
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+ if (NOT TARGET ${BOARD_TARGET})
+ add_library(${BOARD_TARGET} STATIC
+ ${SDK_DIR}/../gcc/cr_startup_lpc175x_6x.c
+ ${SDK_DIR}/src/chip_17xx_40xx.c
+ ${SDK_DIR}/src/clock_17xx_40xx.c
+ ${SDK_DIR}/src/gpio_17xx_40xx.c
+ ${SDK_DIR}/src/iocon_17xx_40xx.c
+ ${SDK_DIR}/src/sysctl_17xx_40xx.c
+ ${SDK_DIR}/src/sysinit_17xx_40xx.c
+ ${SDK_DIR}/src/uart_17xx_40xx.c
+ )
+ target_compile_options(${BOARD_TARGET} PUBLIC
+ -nostdlib
+ )
+ target_compile_definitions(${BOARD_TARGET} PUBLIC
+ __USE_LPCOPEN
+ CORE_M3
+ RTC_EV_SUPPORT=0
+ )
+ target_include_directories(${BOARD_TARGET} PUBLIC
+ ${SDK_DIR}/inc
+ )
+
+ update_board(${BOARD_TARGET})
+
+ if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ target_link_options(${BOARD_TARGET} PUBLIC
+ "LINKER:--script=${LD_FILE_GNU}"
+ # nanolib
+ --specs=nosys.specs --specs=nano.specs
+ )
+ elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+ target_link_options(${BOARD_TARGET} PUBLIC
+ "LINKER:--config=${LD_FILE_IAR}"
+ )
+ endif ()
+ endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+ family_configure_common(${TARGET} ${RTOS})
+
+ # Board target
+ add_board_target(board_${BOARD})
+
+ #---------- Port Specific ----------
+ # These files are built for each example since it depends on example's tusb_config.h
+ target_sources(${TARGET} PUBLIC
+ # BSP
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+ )
+ target_include_directories(${TARGET} PUBLIC
+ # family, hw, board
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+ )
+
+ # Add TinyUSB target and port source
+ family_add_tinyusb(${TARGET} OPT_MCU_LPC175X_6X ${RTOS})
+ target_sources(${TARGET}-tinyusb PUBLIC
+ ${TOP}/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
+ ${TOP}/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+ ${TOP}/src/portable/ohci/ohci.c
+ )
+ target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+ # Link dependencies
+ target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+ # Flashing
+ family_flash_jlink(${TARGET})
+ #family_flash_nxplink(${TARGET})
+endfunction()
diff --git a/hw/bsp/lpc17/family.mk b/hw/bsp/lpc17/family.mk
index 67d5e14b5..694b6cccf 100644
--- a/hw/bsp/lpc17/family.mk
+++ b/hw/bsp/lpc17/family.mk
@@ -34,4 +34,5 @@ SRC_C += \
$(MCU_DIR)/src/uart_17xx_40xx.c \
INC += \
- $(TOP)/$(MCU_DIR)/inc
+ $(TOP)/$(BOARD_PATH) \
+ $(TOP)/$(MCU_DIR)/inc \
diff --git a/hw/bsp/nrf/boards/feather_nrf52840_express/board.h b/hw/bsp/nrf/boards/feather_nrf52840_express/board.h
index 22946422b..3d59516d8 100644
--- a/hw/bsp/nrf/boards/feather_nrf52840_express/board.h
+++ b/hw/bsp/nrf/boards/feather_nrf52840_express/board.h
@@ -49,8 +49,8 @@
#define MAX3421_SCK_PIN 14
#define MAX3421_MOSI_PIN 13
#define MAX3421_MISO_PIN 15
-#define MAX3421_CS_PIN 6 // D11
-#define MAX3421_INTR_PIN 27 // D10
+#define MAX3421_CS_PIN 27 // D10
+#define MAX3421_INTR_PIN 26 // D9
#ifdef __cplusplus
}
diff --git a/hw/bsp/rp2040/board.h b/hw/bsp/rp2040/board.h
index 063c9580e..3849894ce 100644
--- a/hw/bsp/rp2040/board.h
+++ b/hw/bsp/rp2040/board.h
@@ -74,6 +74,17 @@
#define PICO_DEFAULT_PIO_USB_VBUSEN_STATE 1
#endif
+//--------------------------------------------------------------------
+// USB Host MAX3421E
+//--------------------------------------------------------------------
+
+#define MAX3421_SPI PICO_DEFAULT_SPI_INSTANCE
+#define MAX3421_SCK_PIN PICO_DEFAULT_SPI_SCK_PIN
+#define MAX3421_MOSI_PIN PICO_DEFAULT_SPI_TX_PIN
+#define MAX3421_MISO_PIN PICO_DEFAULT_SPI_RX_PIN
+#define MAX3421_CS_PIN 10
+#define MAX3421_INTR_PIN 9
+
#ifdef __cplusplus
}
#endif
diff --git a/hw/bsp/rp2040/family.c b/hw/bsp/rp2040/family.c
index 5ac104688..cffb632f3 100644
--- a/hw/bsp/rp2040/family.c
+++ b/hw/bsp/rp2040/family.c
@@ -37,10 +37,19 @@
#include "bsp/board_api.h"
#include "board.h"
+#ifdef UART_DEV
+static uart_inst_t *uart_inst;
+#endif
+
#if CFG_TUH_RPI_PIO_USB || CFG_TUD_RPI_PIO_USB
#include "pio_usb.h"
#endif
+#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
+#include "hardware/spi.h"
+static void max3421_init(void);
+#endif
+
#ifdef BUTTON_BOOTSEL
// This example blinks the Picoboard LED when the BOOTSEL button is pressed.
//
@@ -53,73 +62,66 @@
// This doesn't work if others are trying to access flash at the same time,
// e.g. XIP streamer, or the other core.
bool __no_inline_not_in_flash_func(get_bootsel_button)(void) {
- const uint CS_PIN_INDEX = 1;
+ const uint CS_PIN_INDEX = 1;
- // Must disable interrupts, as interrupt handlers may be in flash, and we
- // are about to temporarily disable flash access!
- uint32_t flags = save_and_disable_interrupts();
+ // Must disable interrupts, as interrupt handlers may be in flash, and we
+ // are about to temporarily disable flash access!
+ uint32_t flags = save_and_disable_interrupts();
- // Set chip select to Hi-Z
- hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
- GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
- IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
+ // Set chip select to Hi-Z
+ hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
+ GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
+ IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
- // Note we can't call into any sleep functions in flash right now
- for (volatile int i = 0; i < 1000; ++i);
+ // Note we can't call into any sleep functions in flash right now
+ for (volatile int i = 0; i < 1000; ++i);
- // The HI GPIO registers in SIO can observe and control the 6 QSPI pins.
- // Note the button pulls the pin *low* when pressed.
- bool button_state = (sio_hw->gpio_hi_in & (1u << CS_PIN_INDEX));
+ // The HI GPIO registers in SIO can observe and control the 6 QSPI pins.
+ // Note the button pulls the pin *low* when pressed.
+ bool button_state = (sio_hw->gpio_hi_in & (1u << CS_PIN_INDEX));
- // Need to restore the state of chip select, else we are going to have a
- // bad time when we return to code in flash!
- hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
- GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
- IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
+ // Need to restore the state of chip select, else we are going to have a
+ // bad time when we return to code in flash!
+ hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
+ GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
+ IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
- restore_interrupts(flags);
+ restore_interrupts(flags);
- return button_state;
+ return button_state;
}
#endif
//------------- Segger RTT retarget -------------//
#if defined(LOGGER_RTT)
-
// Logging with RTT
// - If RTT Control Block is not found by 'Auto Detection` try to use 'Search Range` with '0x20000000 0x10000'
// - SWD speed is rather slow around 1000Khz
-
#include "pico/stdio/driver.h"
#include "SEGGER_RTT.h"
-static void stdio_rtt_write (const char *buf, int length)
-{
+static void stdio_rtt_write (const char *buf, int length) {
SEGGER_RTT_Write(0, buf, (unsigned) length);
}
-static int stdio_rtt_read (char *buf, int len)
-{
+static int stdio_rtt_read (char *buf, int len) {
return (int) SEGGER_RTT_Read(0, buf, (unsigned) len);
}
-static stdio_driver_t stdio_rtt =
-{
+static stdio_driver_t stdio_rtt = {
.out_chars = stdio_rtt_write,
.out_flush = NULL,
.in_chars = stdio_rtt_read
};
-void stdio_rtt_init(void)
-{
+void stdio_rtt_init(void) {
stdio_set_driver_enabled(&stdio_rtt, true);
}
-
#endif
-#ifdef UART_DEV
-static uart_inst_t *uart_inst;
-#endif
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
void board_init(void)
{
@@ -165,7 +167,9 @@ void board_init(void)
#endif
#if CFG_TUH_ENABLED
- // set portfunc to host !!!
+ #if CFG_TUH_MAX3421
+ max3421_init();
+ #endif
#endif
#if !CFG_TUD_ENABLED && !CFG_TUH_ENABLED
@@ -242,3 +246,69 @@ int board_getchar(void) {
// rp2040 implementation will install appropriate handler when initializing
// tinyusb. There is no need to forward IRQ from application
//--------------------------------------------------------------------+
+
+//--------------------------------------------------------------------+
+// API: SPI transfer with MAX3421E, must be implemented by application
+//--------------------------------------------------------------------+
+#if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
+
+void max3421_int_handler(uint gpio, uint32_t event_mask) {
+ if (!(gpio == MAX3421_INTR_PIN && event_mask & GPIO_IRQ_EDGE_FALL)) return;
+ tuh_int_handler(BOARD_TUH_RHPORT, true);
+}
+
+static void max3421_init(void) {
+ // CS pin
+ gpio_init(MAX3421_CS_PIN);
+ gpio_set_dir(MAX3421_CS_PIN, GPIO_OUT);
+ gpio_put(MAX3421_CS_PIN, true);
+
+ // Interrupt pin
+ gpio_init(MAX3421_INTR_PIN);
+ gpio_set_dir(MAX3421_INTR_PIN, GPIO_IN);
+ gpio_pull_up(MAX3421_INTR_PIN);
+ gpio_set_irq_enabled_with_callback(MAX3421_INTR_PIN, GPIO_IRQ_EDGE_FALL, true, max3421_int_handler);
+
+ // SPI init
+ spi_init(MAX3421_SPI, 4*1000000ul);
+ gpio_set_function(MAX3421_SCK_PIN, GPIO_FUNC_SPI);
+ gpio_set_function(MAX3421_MOSI_PIN, GPIO_FUNC_SPI);
+ gpio_set_function(MAX3421_MISO_PIN, GPIO_FUNC_SPI);
+ spi_set_format(MAX3421_SPI, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
+}
+
+//// API to enable/disable MAX3421 INTR pin interrupt
+void tuh_max3421_int_api(uint8_t rhport, bool enabled) {
+ (void) rhport;
+ irq_set_enabled(IO_IRQ_BANK0, enabled);
+}
+
+// API to control MAX3421 SPI CS
+void tuh_max3421_spi_cs_api(uint8_t rhport, bool active) {
+ (void) rhport;
+ gpio_put(MAX3421_CS_PIN, !active);
+}
+
+// API to transfer data with MAX3421 SPI
+// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
+bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes) {
+ (void) rhport;
+
+ if (tx_buf == NULL && rx_buf == NULL) {
+ return false;
+ }
+
+ int ret;
+
+ if (tx_buf == NULL) {
+ ret = spi_read_blocking(MAX3421_SPI, 0, rx_buf, xfer_bytes);
+ }else if (rx_buf == NULL) {
+ ret = spi_write_blocking(MAX3421_SPI, tx_buf, xfer_bytes);
+ }else {
+ ret = spi_write_read_blocking(spi0, tx_buf, rx_buf, xfer_bytes);
+ }
+
+ return ret == (int) xfer_bytes;
+}
+
+#endif
diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake
index 77d8029d9..ec40806fd 100644
--- a/hw/bsp/rp2040/family.cmake
+++ b/hw/bsp/rp2040/family.cmake
@@ -103,6 +103,20 @@ target_compile_definitions(tinyusb_host_base INTERFACE
RP2040_USB_HOST_MODE=1
)
+#------------------------------------
+# Host MAX3421
+#------------------------------------
+add_library(tinyusb_host_max3421 INTERFACE)
+target_sources(tinyusb_host_max3421 INTERFACE
+ ${TOP}/src/portable/analog/max3421/hcd_max3421.c
+ )
+target_compile_definitions(tinyusb_host_max3421 INTERFACE
+ CFG_TUH_MAX3421=1
+ )
+target_link_libraries(tinyusb_host_max3421 INTERFACE
+ hardware_spi
+ )
+
#------------------------------------
# BSP & Additions
#------------------------------------
@@ -126,7 +140,7 @@ target_compile_definitions(tinyusb_additions INTERFACE
if(LOGGER STREQUAL "RTT" OR LOGGER STREQUAL "rtt")
target_compile_definitions(tinyusb_additions INTERFACE
LOGGER_RTT
- SEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
+ #SEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
)
target_sources(tinyusb_additions INTERFACE
@@ -186,6 +200,7 @@ function(family_add_pico_pio_usb TARGET)
target_link_libraries(${TARGET} PUBLIC tinyusb_pico_pio_usb)
endfunction()
+
# since Pico-PIO_USB compiler support may lag, and change from version to version, add a function that pico-sdk/pico-examples
# can check (if present) in case the user has updated their TinyUSB
function(is_compiler_supported_by_pico_pio_usb OUTVAR)
@@ -209,6 +224,11 @@ function(family_configure_host_example TARGET RTOS)
family_add_pico_pio_usb(${PROJECT})
endif()
endif()
+
+ # for max3421 host
+ if (MAX3421_HOST STREQUAL "1")
+ target_link_libraries(${TARGET} PUBLIC tinyusb_host_max3421)
+ endif()
endfunction()
diff --git a/hw/bsp/samd21/boards/feather_m0_express/board.h b/hw/bsp/samd21/boards/feather_m0_express/board.h
index fd3d16aaa..56ae3230d 100644
--- a/hw/bsp/samd21/boards/feather_m0_express/board.h
+++ b/hw/bsp/samd21/boards/feather_m0_express/board.h
@@ -53,10 +53,10 @@
#define MAX3421_TX_PAD 1 // MOSI = PAD_2, SCK = PAD_3
#define MAX3421_RX_PAD 0 // MISO = PAD_2
-#define MAX3421_CS_PIN 16 // D11
+#define MAX3421_CS_PIN 18 // D10
-#define MAX3421_INTR_PIN 18 // D10
-#define MAX3421_INTR_EIC_ID 2 // EIC2
+#define MAX3421_INTR_PIN 7 // D10
+#define MAX3421_INTR_EIC_ID 7 // EIC7
#ifdef __cplusplus
}
diff --git a/hw/bsp/samd51/boards/feather_m4_express/board.h b/hw/bsp/samd51/boards/feather_m4_express/board.h
index 474163c06..83de04266 100644
--- a/hw/bsp/samd51/boards/feather_m4_express/board.h
+++ b/hw/bsp/samd51/boards/feather_m4_express/board.h
@@ -55,10 +55,10 @@
#define MAX3421_TX_PAD 2 // MOSI = PAD_3, SCK = PAD_1
#define MAX3421_RX_PAD 2 // MISO = PAD_2
-#define MAX3421_CS_PIN 21 // D11
+#define MAX3421_CS_PIN 20 // D10
-#define MAX3421_INTR_PIN 20 // D10
-#define MAX3421_INTR_EIC_ID 4 // EIC4
+#define MAX3421_INTR_PIN 19 // D9
+#define MAX3421_INTR_EIC_ID 3 // EIC3
#ifdef __cplusplus
}
diff --git a/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h b/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h
index 8fa8f4ffa..2f30a09d4 100644
--- a/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h
+++ b/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h
@@ -42,12 +42,12 @@
#define BUTTON_STATE_ACTIVE 1
// UART
-//#define UART_DEV USART1
-//#define UART_CLK_EN __HAL_RCC_USART1_CLK_ENABLE
-//#define UART_GPIO_PORT GPIOA
+#define UART_DEV USART1
+#define UART_CLK_EN __HAL_RCC_USART1_CLK_ENABLE
+#define UART_GPIO_PORT GPIOA
//#define UART_GPIO_AF GPIO_AF1_USART1
-//#define UART_TX_PIN GPIO_PIN_9
-//#define UART_RX_PIN GPIO_PIN_10
+#define UART_TX_PIN GPIO_PIN_9
+#define UART_RX_PIN GPIO_PIN_10
//--------------------------------------------------------------------+
// RCC Clock
diff --git a/hw/bsp/stm32f1/family.c b/hw/bsp/stm32f1/family.c
index 3b1c5796b..0c1b362ab 100644
--- a/hw/bsp/stm32f1/family.c
+++ b/hw/bsp/stm32f1/family.c
@@ -46,6 +46,7 @@ void USBWakeUp_IRQHandler(void) {
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
//--------------------------------------------------------------------+
+UART_HandleTypeDef UartHandle;
void board_init(void) {
board_stm32f1_clock_init();
@@ -82,6 +83,30 @@ void board_init(void) {
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);
+#ifdef UART_DEV
+ // UART
+ UART_CLK_EN();
+
+ GPIO_InitStruct.Pin = UART_TX_PIN | UART_RX_PIN;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_PULLUP;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+ //GPIO_InitStruct.Alternate = UART_GPIO_AF;
+ HAL_GPIO_Init(UART_GPIO_PORT, &GPIO_InitStruct);
+
+ UartHandle = (UART_HandleTypeDef) {
+ .Instance = UART_DEV,
+ .Init.BaudRate = CFG_BOARD_UART_BAUDRATE,
+ .Init.WordLength = UART_WORDLENGTH_8B,
+ .Init.StopBits = UART_STOPBITS_1,
+ .Init.Parity = UART_PARITY_NONE,
+ .Init.HwFlowCtl = UART_HWCONTROL_NONE,
+ .Init.Mode = UART_MODE_TX_RX,
+ .Init.OverSampling = UART_OVERSAMPLING_16
+ };
+ HAL_UART_Init(&UartHandle);
+#endif
+
// USB Pins
// Configure USB DM and DP pins.
GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12);
@@ -127,9 +152,8 @@ int board_uart_read(uint8_t *buf, int len) {
}
int board_uart_write(void const *buf, int len) {
- (void) buf;
- (void) len;
- return 0;
+ HAL_UART_Transmit(&UartHandle, (uint8_t *) (uintptr_t) buf, len, 0xffff);
+ return len;
}
#if CFG_TUSB_OS == OPT_OS_NONE
diff --git a/hw/bsp/stm32f1/family.mk b/hw/bsp/stm32f1/family.mk
index 90a984bfe..8a5625551 100644
--- a/hw/bsp/stm32f1/family.mk
+++ b/hw/bsp/stm32f1/family.mk
@@ -18,6 +18,9 @@ CFLAGS_GCC += \
-flto \
-nostdlib -nostartfiles \
+# mcu driver cause following warnings
+CFLAGS_GCC += -Wno-error=cast-align
+
LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
# ------------------------
@@ -30,7 +33,8 @@ SRC_C += \
$(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_cortex.c \
$(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_rcc.c \
$(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_rcc_ex.c \
- $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_gpio.c
+ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_gpio.c \
+ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_uart.c
INC += \
$(TOP)/$(BOARD_PATH) \
diff --git a/hw/bsp/stm32f1/stm32f1xx_hal_conf.h b/hw/bsp/stm32f1/stm32f1xx_hal_conf.h
index 45ef993da..0fce774e1 100644
--- a/hw/bsp/stm32f1/stm32f1xx_hal_conf.h
+++ b/hw/bsp/stm32f1/stm32f1xx_hal_conf.h
@@ -38,7 +38,7 @@
/* #define HAL_ADC_MODULE_ENABLED */
/* #define HAL_CAN_MODULE_ENABLED */
/* #define HAL_CAN_LEGACY_MODULE_ENABLED */
-#define HAL_CORTEX_MODULE_ENABLED */
+#define HAL_CORTEX_MODULE_ENABLED
/* #define HAL_CRC_MODULE_ENABLED */
/* #define HAL_DAC_MODULE_ENABLED */
#define HAL_DMA_MODULE_ENABLED
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index d438d0715..4adc558a6 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -1729,7 +1729,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
// Find correct interface
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
{
-#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
uint8_t const * p_desc_parse_for_params = p_desc;
#endif
// From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h
index deec32ae4..5cbd658fe 100644
--- a/src/class/cdc/cdc.h
+++ b/src/class/cdc/cdc.h
@@ -136,8 +136,7 @@ typedef enum{
//--------------------------------------------------------------------+
/// Communication Interface Management Element Request Codes
-typedef enum
-{
+typedef enum {
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
CDC_REQUEST_SET_COMM_FEATURE = 0x02,
@@ -180,39 +179,38 @@ typedef enum
CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53,
CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60,
-}cdc_management_request_t;
+} cdc_management_request_t;
-enum {
+typedef enum {
CDC_CONTROL_LINE_STATE_DTR = 0x01,
CDC_CONTROL_LINE_STATE_RTS = 0x02,
-};
+} cdc_control_line_state_t;
-enum {
+typedef enum {
CDC_LINE_CODING_STOP_BITS_1 = 0, // 1 bit
CDC_LINE_CODING_STOP_BITS_1_5 = 1, // 1.5 bits
CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits
-};
+} cdc_line_coding_stopbits_t;
// TODO Backward compatible for typos. Maybe removed in the future release
#define CDC_LINE_CONDING_STOP_BITS_1 CDC_LINE_CODING_STOP_BITS_1
#define CDC_LINE_CONDING_STOP_BITS_1_5 CDC_LINE_CODING_STOP_BITS_1_5
#define CDC_LINE_CONDING_STOP_BITS_2 CDC_LINE_CODING_STOP_BITS_2
-enum {
+typedef enum {
CDC_LINE_CODING_PARITY_NONE = 0,
CDC_LINE_CODING_PARITY_ODD = 1,
CDC_LINE_CODING_PARITY_EVEN = 2,
CDC_LINE_CODING_PARITY_MARK = 3,
CDC_LINE_CODING_PARITY_SPACE = 4,
-};
+} cdc_line_coding_parity_t;
//--------------------------------------------------------------------+
// Management Element Notification (Notification Endpoint)
//--------------------------------------------------------------------+
/// 6.3 Notification Codes
-typedef enum
-{
+typedef enum {
CDC_NOTIF_NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status.
CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08,
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index a6dfb45ae..2fb37d835 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -22,6 +22,9 @@
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
+ *
+ * Contribution
+ * - Heiko Kuester: CH34x support
*/
#include "tusb_option.h"
@@ -50,12 +53,18 @@ typedef struct {
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
- uint8_t serial_drid; // Serial Driver ID
- cdc_acm_capability_t acm_capability;
uint8_t ep_notif;
+ uint8_t serial_drid; // Serial Driver ID
+ bool mounted; // Enumeration is complete
+ cdc_acm_capability_t acm_capability;
- uint8_t line_state; // DTR (bit0), RTS (bit1)
TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width
+ uint8_t line_state; // DTR (bit0), RTS (bit1)
+
+ #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
+ cdc_line_coding_t requested_line_coding;
+ // 1 byte padding
+ #endif
tuh_xfer_cb_t user_control_cb;
@@ -69,7 +78,6 @@ typedef struct {
uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
CFG_TUH_MEM_ALIGN uint8_t rx_ep_buf[CFG_TUH_CDC_TX_EPSIZE];
} stream;
-
} cdch_interface_t;
CFG_TUH_MEM_SECTION
@@ -80,47 +88,60 @@ static cdch_interface_t cdch_data[CFG_TUH_CDC];
//--------------------------------------------------------------------+
//------------- ACM prototypes -------------//
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
static void acm_process_config(tuh_xfer_t* xfer);
+static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
//------------- FTDI prototypes -------------//
#if CFG_TUH_CDC_FTDI
#include "serial/ftdi_sio.h"
-static uint16_t const ftdi_pids[] = { CFG_TUH_CDC_FTDI_PID_LIST };
-enum {
- FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0])
-};
-
-// Store last request baudrate since divisor to baudrate is not easy
-static uint32_t _ftdi_requested_baud;
+static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
static void ftdi_process_config(tuh_xfer_t* xfer);
-static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
//------------- CP210X prototypes -------------//
#if CFG_TUH_CDC_CP210X
#include "serial/cp210x.h"
-static uint16_t const cp210x_pids[] = { CFG_TUH_CDC_CP210X_PID_LIST };
-enum {
- CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0])
-};
+static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST};
static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
static void cp210x_process_config(tuh_xfer_t* xfer);
-static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
+//------------- CH34x prototypes -------------//
+#if CFG_TUH_CDC_CH34X
+#include "serial/ch34x.h"
+
+static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST};
+
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len);
+static void ch34x_process_config(tuh_xfer_t* xfer);
+
+static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+#endif
+
+//------------- Common -------------//
enum {
SERIAL_DRIVER_ACM = 0,
@@ -131,60 +152,96 @@ enum {
#if CFG_TUH_CDC_CP210X
SERIAL_DRIVER_CP210X,
#endif
+
+#if CFG_TUH_CDC_CH34X
+ SERIAL_DRIVER_CH34X,
+#endif
+
+ SERIAL_DRIVER_COUNT
};
typedef struct {
+ uint16_t const (*vid_pid_list)[2];
+ uint16_t const vid_pid_count;
+ bool (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
void (*const process_set_config)(tuh_xfer_t* xfer);
bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const set_data_format)(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
} cdch_serial_driver_t;
// Note driver list must be in the same order as SERIAL_DRIVER enum
static const cdch_serial_driver_t serial_drivers[] = {
- { .process_set_config = acm_process_config,
- .set_control_line_state = acm_set_control_line_state,
- .set_baudrate = acm_set_baudrate
+ {
+ .vid_pid_list = NULL,
+ .vid_pid_count = 0,
+ .open = acm_open,
+ .process_set_config = acm_process_config,
+ .set_control_line_state = acm_set_control_line_state,
+ .set_baudrate = acm_set_baudrate,
+ .set_data_format = acm_set_data_format,
+ .set_line_coding = acm_set_line_coding
},
#if CFG_TUH_CDC_FTDI
- { .process_set_config = ftdi_process_config,
- .set_control_line_state = ftdi_sio_set_modem_ctrl,
- .set_baudrate = ftdi_sio_set_baudrate
+ {
+ .vid_pid_list = ftdi_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
+ .open = ftdi_open,
+ .process_set_config = ftdi_process_config,
+ .set_control_line_state = ftdi_sio_set_modem_ctrl,
+ .set_baudrate = ftdi_sio_set_baudrate,
+ .set_data_format = ftdi_set_data_format,
+ .set_line_coding = ftdi_set_line_coding
},
#endif
#if CFG_TUH_CDC_CP210X
- { .process_set_config = cp210x_process_config,
- .set_control_line_state = cp210x_set_modem_ctrl,
- .set_baudrate = cp210x_set_baudrate
+ {
+ .vid_pid_list = cp210x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list),
+ .open = cp210x_open,
+ .process_set_config = cp210x_process_config,
+ .set_control_line_state = cp210x_set_modem_ctrl,
+ .set_baudrate = cp210x_set_baudrate,
+ .set_data_format = cp210x_set_data_format,
+ .set_line_coding = cp210x_set_line_coding
+ },
+ #endif
+
+ #if CFG_TUH_CDC_CH34X
+ {
+ .vid_pid_list = ch34x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list),
+ .open = ch34x_open,
+ .process_set_config = ch34x_process_config,
+ .set_control_line_state = ch34x_set_modem_ctrl,
+ .set_baudrate = ch34x_set_baudrate,
+ .set_data_format = ch34x_set_data_format,
+ .set_line_coding = ch34x_set_line_coding
},
#endif
};
-enum {
- SERIAL_DRIVER_COUNT = sizeof(serial_drivers) / sizeof(serial_drivers[0])
-};
+TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch");
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-static inline cdch_interface_t* get_itf(uint8_t idx)
-{
+static inline cdch_interface_t* get_itf(uint8_t idx) {
TU_ASSERT(idx < CFG_TUH_CDC, NULL);
cdch_interface_t* p_cdc = &cdch_data[idx];
return (p_cdc->daddr != 0) ? p_cdc : NULL;
}
-static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr)
-{
- for(uint8_t i=0; idaddr == daddr) &&
- (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr))
- {
+ (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr)) {
return i;
}
}
@@ -192,14 +249,10 @@ static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr)
return TUSB_INDEX_INVALID_8;
}
-
-static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc)
-{
- for(uint8_t i=0; idaddr = daddr;
p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber;
p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass;
@@ -220,20 +273,16 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer);
// APPLICATION API
//--------------------------------------------------------------------+
-uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num)
-{
- for(uint8_t i=0; idaddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i;
}
return TUSB_INDEX_INVALID_8;
}
-bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info)
-{
+bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc && info);
@@ -255,30 +304,27 @@ bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info)
return true;
}
-bool tuh_cdc_mounted(uint8_t idx)
-{
+bool tuh_cdc_mounted(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
- return p_cdc != NULL;
+ TU_VERIFY(p_cdc);
+ return p_cdc->mounted;
}
-bool tuh_cdc_get_dtr(uint8_t idx)
-{
+bool tuh_cdc_get_dtr(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false;
}
-bool tuh_cdc_get_rts(uint8_t idx)
-{
+bool tuh_cdc_get_rts(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false;
}
-bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding)
-{
+bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
@@ -291,32 +337,28 @@ bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding)
// Write
//--------------------------------------------------------------------+
-uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize)
-{
+uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize);
}
-uint32_t tuh_cdc_write_flush(uint8_t idx)
-{
+uint32_t tuh_cdc_write_flush(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_write_xfer(&p_cdc->stream.tx);
}
-bool tuh_cdc_write_clear(uint8_t idx)
-{
+bool tuh_cdc_write_clear(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_clear(&p_cdc->stream.tx);
}
-uint32_t tuh_cdc_write_available(uint8_t idx)
-{
+uint32_t tuh_cdc_write_available(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
@@ -327,32 +369,28 @@ uint32_t tuh_cdc_write_available(uint8_t idx)
// Read
//--------------------------------------------------------------------+
-uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize)
-{
+uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize);
}
-uint32_t tuh_cdc_read_available(uint8_t idx)
-{
+uint32_t tuh_cdc_read_available(uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_read_available(&p_cdc->stream.rx);
}
-bool tuh_cdc_peek(uint8_t idx, uint8_t* ch)
-{
+bool tuh_cdc_peek(uint8_t idx, uint8_t* ch) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return tu_edpt_stream_peek(&p_cdc->stream.rx, ch);
}
-bool tuh_cdc_read_clear (uint8_t idx)
-{
+bool tuh_cdc_read_clear (uint8_t idx) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
@@ -365,28 +403,25 @@ bool tuh_cdc_read_clear (uint8_t idx)
// Control Endpoint API
//--------------------------------------------------------------------+
-// internal control complete to update state such as line state, encoding
-static void cdch_internal_control_complete(tuh_xfer_t* xfer)
-{
- uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+static void process_internal_control_complete(tuh_xfer_t* xfer, uint8_t itf_num) {
uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
cdch_interface_t* p_cdc = get_itf(idx);
TU_ASSERT(p_cdc, );
+ uint16_t const value = tu_le16toh(xfer->setup->wValue);
- if (xfer->result == XFER_RESULT_SUCCESS)
- {
+ if (xfer->result == XFER_RESULT_SUCCESS) {
switch (p_cdc->serial_drid) {
case SERIAL_DRIVER_ACM:
switch (xfer->setup->bRequest) {
case CDC_REQUEST_SET_CONTROL_LINE_STATE:
- p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue);
+ p_cdc->line_state = (uint8_t) value;
break;
case CDC_REQUEST_SET_LINE_CODING: {
uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength));
memcpy(&p_cdc->line_coding, xfer->buffer, len);
- }
break;
+ }
default: break;
}
@@ -396,12 +431,11 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
case SERIAL_DRIVER_FTDI:
switch (xfer->setup->bRequest) {
case FTDI_SIO_MODEM_CTRL:
- p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
+ p_cdc->line_state = (uint8_t) value;
break;
case FTDI_SIO_SET_BAUD_RATE:
- // convert from divisor to baudrate is not supported
- p_cdc->line_coding.bit_rate = _ftdi_requested_baud;
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
break;
default: break;
@@ -413,15 +447,61 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
case SERIAL_DRIVER_CP210X:
switch(xfer->setup->bRequest) {
case CP210X_SET_MHS:
- p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
+ p_cdc->line_state = (uint8_t) value;
break;
case CP210X_SET_BAUDRATE: {
uint32_t baudrate;
memcpy(&baudrate, xfer->buffer, sizeof(uint32_t));
p_cdc->line_coding.bit_rate = tu_le32toh(baudrate);
- }
break;
+ }
+
+ default: break;
+ }
+ break;
+ #endif
+
+ #if CFG_TUH_CDC_CH34X
+ case SERIAL_DRIVER_CH34X:
+ switch (xfer->setup->bRequest) {
+ case CH34X_REQ_WRITE_REG:
+ // register write request
+ switch (value) {
+ case CH34X_REG16_DIVISOR_PRESCALER:
+ // baudrate
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+ break;
+
+ case CH32X_REG16_LCR2_LCR:
+ // data format
+ p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits;
+ p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity;
+ p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits;
+ break;
+
+ default: break;
+ }
+ break;
+
+ case CH34X_REQ_MODEM_CTRL: {
+ // set modem controls RTS/DTR request. Note: signals are inverted
+ uint16_t const modem_signal = ~value;
+ if (modem_signal & CH34X_BIT_RTS) {
+ p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS;
+ } else {
+ p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS;
+ }
+
+ if (modem_signal & CH34X_BIT_DTR) {
+ p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR;
+ } else {
+ p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR;
+ }
+ break;
+ }
+
+ default: break;
}
break;
#endif
@@ -436,14 +516,20 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
}
}
+// internal control complete to update state such as line state, encoding
+static void cdch_internal_control_complete(tuh_xfer_t* xfer) {
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ process_internal_control_complete(xfer, itf_num);
+}
+
bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
cdch_interface_t* p_cdc = get_itf(idx);
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
- if ( complete_cb ) {
+ if (complete_cb) {
return driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data);
- }else {
+ } else {
// blocking
xfer_result_t result = XFER_RESULT_INVALID;
bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result);
@@ -454,7 +540,6 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c
}
TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
-
p_cdc->line_state = (uint8_t) line_state;
return true;
}
@@ -465,9 +550,9 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
- if ( complete_cb ) {
+ if (complete_cb) {
return driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data);
- }else {
+ } else {
// blocking
xfer_result_t result = XFER_RESULT_INVALID;
bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result);
@@ -478,25 +563,23 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete
}
TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
-
p_cdc->line_coding.bit_rate = baudrate;
return true;
}
}
-bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
cdch_interface_t* p_cdc = get_itf(idx);
- // only ACM support this set line coding request
- TU_VERIFY(p_cdc && p_cdc->serial_drid == SERIAL_DRIVER_ACM);
- TU_VERIFY(p_cdc->acm_capability.support_line_request);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
- if ( complete_cb ) {
- return acm_set_line_coding(p_cdc, line_coding, complete_cb, user_data);
- }else {
+ if (complete_cb) {
+ return driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, user_data);
+ } else {
// blocking
xfer_result_t result = XFER_RESULT_INVALID;
- bool ret = acm_set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+ bool ret = driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, (uintptr_t) &result);
if (user_data) {
// user_data is not NULL, return result via user_data
@@ -504,7 +587,31 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
}
TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.stop_bits = stop_bits;
+ p_cdc->line_coding.parity = parity;
+ p_cdc->line_coding.data_bits = data_bits;
+ return true;
+ }
+}
+bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+ if ( complete_cb ) {
+ return driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data);
+ } else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
+ bool ret = driver->set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+
+ if (user_data) {
+ // user_data is not NULL, return result via user_data
+ *((xfer_result_t*) user_data) = result;
+ }
+
+ TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
p_cdc->line_coding = *line_coding;
return true;
}
@@ -514,39 +621,34 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
// CLASS-USBH API
//--------------------------------------------------------------------+
-void cdch_init(void)
-{
+void cdch_init(void) {
tu_memclr(cdch_data, sizeof(cdch_data));
- for(size_t i=0; istream.tx, true, true, false,
- p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
- p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE);
+ p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
+ p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE);
tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false,
- p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
- p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE);
+ p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
+ p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE);
}
}
-void cdch_close(uint8_t daddr)
-{
- for(uint8_t idx=0; idxdaddr == daddr)
- {
+ if (p_cdc->daddr == daddr) {
TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx);
// Invoke application callback
if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx);
- //tu_memclr(p_cdc, sizeof(cdch_interface_t));
p_cdc->daddr = 0;
p_cdc->bInterfaceNumber = 0;
+ p_cdc->mounted = false;
tu_edpt_stream_close(&p_cdc->stream.tx);
tu_edpt_stream_close(&p_cdc->stream.rx);
}
@@ -570,16 +672,11 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
// - xferred_bytes is multiple of EP Packet size and not zero
tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes);
}
- }
- else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
+ } else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
#if CFG_TUH_CDC_FTDI
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
// FTDI reserve 2 bytes for status
- // FTDI status
-// uint8_t status[2] = {
-// p_cdc->stream.rx.ep_buf[0],
-// p_cdc->stream.rx.ep_buf[1]
-// };
+ // uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]};
tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2);
}else
#endif
@@ -605,22 +702,15 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
// Enumeration
//--------------------------------------------------------------------+
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
-
-static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const *desc_ep)
-{
- for(size_t i=0; i<2; i++)
- {
+static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const* desc_ep) {
+ for (size_t i = 0; i < 2; i++) {
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
- TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
-
+ TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
- if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
- {
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
tu_edpt_stream_open(&p_cdc->stream.rx, p_cdc->daddr, desc_ep);
- }else
- {
+ } else {
tu_edpt_stream_open(&p_cdc->stream.tx, p_cdc->daddr, desc_ep);
}
@@ -630,49 +720,36 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t co
return true;
}
-bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
-{
+bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
(void) rhport;
- // Only support ACM subclass
+ // For CDC: only support ACM subclass
// Note: Protocol 0xFF can be RNDIS device
- if ( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
- CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass)
- {
+ if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) {
return acm_open(daddr, itf_desc, max_len);
}
- #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X
- else if ( 0xff == itf_desc->bInterfaceClass )
- {
+ else if (SERIAL_DRIVER_COUNT > 1 &&
+ TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) {
uint16_t vid, pid;
TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
- #if CFG_TUH_CDC_FTDI
- if (TU_FTDI_VID == vid) {
- for (size_t i = 0; i < FTDI_PID_COUNT; i++) {
- if (ftdi_pids[i] == pid) {
- return ftdi_open(daddr, itf_desc, max_len);
+ for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) {
+ cdch_serial_driver_t const* driver = &serial_drivers[dr];
+ for (size_t i = 0; i < driver->vid_pid_count; i++) {
+ if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) {
+ return driver->open(daddr, itf_desc, max_len);
}
}
}
- #endif
-
- #if CFG_TUH_CDC_CP210X
- if (TU_CP210X_VID == vid) {
- for (size_t i = 0; i < CP210X_PID_COUNT; i++) {
- if (cp210x_pids[i] == pid) {
- return cp210x_open(daddr, itf_desc, max_len);
- }
- }
- }
- #endif
}
- #endif
return false;
}
static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) {
+ TU_LOG_DRV("CDCh Set Configure complete\r\n");
+ p_cdc->mounted = true;
if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx);
// Prepare for incoming data
@@ -682,9 +759,7 @@ static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t i
usbh_driver_set_config_complete(p_cdc->daddr, itf_num);
}
-
-bool cdch_set_config(uint8_t daddr, uint8_t itf_num)
-{
+bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
tusb_control_request_t request;
request.wIndex = tu_htole16((uint16_t) itf_num);
@@ -713,94 +788,84 @@ enum {
CONFIG_ACM_COMPLETE,
};
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
-{
- uint8_t const * p_desc_end = ((uint8_t const*) itf_desc) + max_len;
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
+ uint8_t const* p_desc_end = ((uint8_t const*) itf_desc) + max_len;
- cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
+ cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
TU_VERIFY(p_cdc);
-
p_cdc->serial_drid = SERIAL_DRIVER_ACM;
//------------- Control Interface -------------//
- uint8_t const * p_desc = tu_desc_next(itf_desc);
+ uint8_t const* p_desc = tu_desc_next(itf_desc);
// Communication Functional Descriptors
- while( (p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) )
- {
- if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
- {
+ while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
+ if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) {
// save ACM bmCapabilities
- p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
+ p_cdc->acm_capability = ((cdc_desc_func_acm_t const*) p_desc)->bmCapabilities;
}
p_desc = tu_desc_next(p_desc);
}
// Open notification endpoint of control interface if any
- if (itf_desc->bNumEndpoints == 1)
- {
+ if (itf_desc->bNumEndpoints == 1) {
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
- tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
- TU_ASSERT( tuh_edpt_open(daddr, desc_ep) );
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
p_cdc->ep_notif = desc_ep->bEndpointAddress;
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
- if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
- (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
- {
+ if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+ (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) {
// next to endpoint descriptor
p_desc = tu_desc_next(p_desc);
// data endpoints expected to be in pairs
- TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
+ TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const*) p_desc));
}
return true;
}
-static void acm_process_config(tuh_xfer_t* xfer)
-{
+static void acm_process_config(tuh_xfer_t* xfer) {
uintptr_t const state = xfer->user_data;
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
- cdch_interface_t * p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc, );
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc,);
- switch(state)
- {
+ switch (state) {
case CONFIG_ACM_SET_CONTROL_LINE_STATE:
#if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
- if (p_cdc->acm_capability.support_line_request)
- {
- TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config,
- CONFIG_ACM_SET_LINE_CODING), );
+ if (p_cdc->acm_capability.support_line_request) {
+ TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, CONFIG_ACM_SET_LINE_CODING),);
break;
}
- #endif
+ #endif
TU_ATTR_FALLTHROUGH;
case CONFIG_ACM_SET_LINE_CODING:
- #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
- if (p_cdc->acm_capability.support_line_request)
- {
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ if (p_cdc->acm_capability.support_line_request) {
cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
- TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE), );
+ TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE),);
break;
}
- #endif
+ #endif
TU_ATTR_FALLTHROUGH;
case CONFIG_ACM_COMPLETE:
// itf_num+1 to account for data interface as well
- set_config_complete(p_cdc, idx, itf_num+1);
+ set_config_complete(p_cdc, idx, itf_num + 1);
break;
- default: break;
+ default:
+ break;
}
}
@@ -868,6 +933,19 @@ static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const
return true;
}
+static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("CDC ACM Set Data Format\r\n");
+
+ cdc_line_coding_t line_coding;
+ line_coding.bit_rate = p_cdc->line_coding.bit_rate;
+ line_coding.stop_bits = stop_bits;
+ line_coding.parity = parity;
+ line_coding.data_bits = data_bits;
+
+ return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
+}
+
static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_VERIFY(p_cdc->acm_capability.support_line_request);
cdc_line_coding_t line_coding = p_cdc->line_coding;
@@ -897,7 +975,6 @@ static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint
TU_VERIFY(p_cdc);
TU_LOG_DRV("FTDI opened\r\n");
-
p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
// endpoint pair
@@ -933,13 +1010,32 @@ static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint1
return tuh_control_xfer(&xfer);
}
-static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data);
}
-static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ (void) p_cdc;
+ (void) stop_bits;
+ (void) parity;
+ (void) data_bits;
+ (void) complete_cb;
+ (void) user_data;
+ // TODO not implemented yet
+ return false;
+}
+
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ (void) p_cdc;
+ (void) line_coding;
+ (void) complete_cb;
+ (void) user_data;
+ // TODO not implemented yet
+ return false;
+}
+
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_LOG_DRV("CDC FTDI Set Control Line State\r\n");
p_cdc->user_control_cb = complete_cb;
TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state,
@@ -947,8 +1043,7 @@ static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state
return true;
}
-static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base)
-{
+static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) {
const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
uint32_t divisor;
@@ -968,18 +1063,16 @@ static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base)
return divisor;
}
-static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
-{
+static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) {
return ftdi_232bm_baud_base_to_divisor(baud, 48000000u);
}
-static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate);
TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor);
p_cdc->user_control_cb = complete_cb;
- _ftdi_requested_baud = baudrate;
+ p_cdc->requested_line_coding.bit_rate = baudrate;
TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor,
complete_cb ? cdch_internal_control_complete : NULL, user_data));
@@ -1001,8 +1094,7 @@ static void ftdi_process_config(tuh_xfer_t* xfer) {
case CONFIG_FTDI_MODEM_CTRL:
#if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
- TU_ASSERT(
- ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
+ TU_ASSERT(ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1110,6 +1202,15 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe
return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data);
}
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // TODO implement later
+ (void) p_cdc;
+ (void) line_coding;
+ (void) complete_cb;
+ (void) user_data;
+ return false;
+}
+
static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\r\n", baudrate);
uint32_t baud_le = tu_htole32(baudrate);
@@ -1118,8 +1219,19 @@ static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_
complete_cb ? cdch_internal_control_complete : NULL, user_data);
}
-static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ (void) p_cdc;
+ (void) stop_bits;
+ (void) parity;
+ (void) data_bits;
+ (void) complete_cb;
+ (void) user_data;
+ // TODO not implemented yet
+ return false;
+}
+
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_LOG_DRV("CDC CP210x Set Control Line State\r\n");
p_cdc->user_control_cb = complete_cb;
return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0,
@@ -1159,8 +1271,7 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
case CONFIG_CP210X_SET_DTR_RTS:
#if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
- TU_ASSERT(
- cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
+ TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1176,4 +1287,374 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
#endif
+//--------------------------------------------------------------------+
+// CH34x (CH340 & CH341)
+//--------------------------------------------------------------------+
+
+#if CFG_TUH_CDC_CH34X
+
+static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits);
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval);
+
+//------------- control request -------------//
+
+static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value,
+ uint16_t index, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request_setup = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_VENDOR,
+ .direction = direction & 0x01u
+ },
+ .bRequest = request,
+ .wValue = tu_htole16 (value),
+ .wIndex = tu_htole16 (index),
+ .wLength = tu_htole16 (length)
+ };
+
+ // use usbh enum buf since application variable does not live long enough
+ uint8_t* enum_buf = NULL;
+
+ if (buffer && length > 0) {
+ enum_buf = usbh_get_enum_buf();
+ if (direction == TUSB_DIR_OUT) {
+ tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ }
+ }
+
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request_setup,
+ .buffer = enum_buf,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
+static inline bool ch34x_control_out(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_set_request(p_cdc, TUSB_DIR_OUT, request, value, index, NULL, 0, complete_cb, user_data);
+}
+
+static inline bool ch34x_control_in(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+ uint8_t* buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize,
+ complete_cb, user_data);
+}
+
+static inline bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t reg_value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data);
+}
+
+//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg,
+// uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+//{
+// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data );
+//}
+
+static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint16_t const div_ps = ch34x_get_divisor_prescaler(baudrate);
+ TU_VERIFY(div_ps);
+ TU_ASSERT(ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps,
+ complete_cb, user_data));
+ return true;
+}
+
+//------------- Driver API -------------//
+
+// internal control complete to update state such as line state, encoding
+static void ch34x_control_complete(tuh_xfer_t* xfer) {
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ process_internal_control_complete(xfer, 0);
+}
+
+static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->requested_line_coding.stop_bits = stop_bits;
+ p_cdc->requested_line_coding.parity = parity;
+ p_cdc->requested_line_coding.data_bits = data_bits;
+
+ uint8_t const lcr = ch34x_get_lcr(stop_bits, parity, data_bits);
+ TU_VERIFY(lcr);
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
+ return true;
+}
+
+static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->requested_line_coding.bit_rate = baudrate;
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, baudrate,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
+ return true;
+}
+
+static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) {
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ uint8_t const itf_num = 0;
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc, );
+
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ // stage 1 success, continue to stage 2
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+ TU_ASSERT(ch34x_set_data_format(p_cdc, p_cdc->requested_line_coding.stop_bits, p_cdc->requested_line_coding.parity,
+ p_cdc->requested_line_coding.data_bits, ch34x_control_complete, xfer->user_data), );
+ } else {
+ // stage 1 failed, notify user
+ xfer->complete_cb = p_cdc->user_control_cb;
+ if (xfer->complete_cb) {
+ xfer->complete_cb(xfer);
+ }
+ }
+}
+
+// 2 stages: set baudrate (stage1) + set data format (stage2)
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->requested_line_coding = *line_coding;
+ p_cdc->user_control_cb = complete_cb;
+
+ if (complete_cb) {
+ // stage 1 set baudrate
+ TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate,
+ ch34x_set_line_coding_stage1_complete, user_data));
+ } else {
+ // sync call
+ xfer_result_t result;
+
+ // stage 1 set baudrate
+ TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate, NULL, (uintptr_t) &result));
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.bit_rate = line_coding->bit_rate;
+
+ // stage 2 set data format
+ TU_ASSERT(ch34x_set_data_format(p_cdc, line_coding->stop_bits, line_coding->parity, line_coding->data_bits,
+ NULL, (uintptr_t) &result));
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.stop_bits = line_coding->stop_bits;
+ p_cdc->line_coding.parity = line_coding->parity;
+ p_cdc->line_coding.data_bits = line_coding->data_bits;
+
+ // update transfer result, user_data is expected to point to xfer_result_t
+ if (user_data) {
+ *((xfer_result_t*) user_data) = result;
+ }
+ }
+
+ return true;
+}
+
+static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint8_t control = 0;
+ if (line_state & CDC_CONTROL_LINE_STATE_RTS) {
+ control |= CH34X_BIT_RTS;
+ }
+ if (line_state & CDC_CONTROL_LINE_STATE_DTR) {
+ control |= CH34X_BIT_DTR;
+ }
+
+ // CH34x signals are inverted
+ control = ~control;
+
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
+ return true;
+}
+
+//------------- Enumeration -------------//
+enum {
+ CONFIG_CH34X_READ_VERSION = 0,
+ CONFIG_CH34X_SERIAL_INIT,
+ CONFIG_CH34X_SPECIAL_REG_WRITE,
+ CONFIG_CH34X_FLOW_CONTROL,
+ CONFIG_CH34X_MODEM_CONTROL,
+ CONFIG_CH34X_COMPLETE
+};
+
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
+ // CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints
+ TU_VERIFY (itf_desc->bNumEndpoints == 3);
+ TU_VERIFY (sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
+
+ cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY (p_cdc);
+
+ TU_LOG_DRV ("CH34x opened\r\n");
+ p_cdc->serial_drid = SERIAL_DRIVER_CH34X;
+
+ tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(itf_desc);
+
+ // data endpoints expected to be in pairs
+ TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
+ desc_ep += 2;
+
+ // Interrupt endpoint: not used for now
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) &&
+ TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer);
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
+
+ return true;
+}
+
+static void ch34x_process_config(tuh_xfer_t* xfer) {
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ uint8_t const itf_num = 0;
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t* p_cdc = get_itf(idx);
+ uintptr_t const state = xfer->user_data;
+ uint8_t buffer[2]; // TODO remove
+ TU_ASSERT (p_cdc,);
+ TU_ASSERT (xfer->result == XFER_RESULT_SUCCESS,);
+
+ switch (state) {
+ case CONFIG_CH34X_READ_VERSION:
+ TU_LOG_DRV("[%u] CDCh CH34x attempt to read Chip Version\r\n", p_cdc->daddr);
+ TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT),);
+ break;
+
+ case CONFIG_CH34X_SERIAL_INIT: {
+ // handle version read data, set CH34x line coding (incl. baudrate)
+ uint8_t const version = xfer->buffer[0];
+ TU_LOG_DRV("[%u] CDCh CH34x Chip Version = %02x\r\n", p_cdc->daddr, version);
+ // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD
+ TU_ASSERT (version >= 0x30,);
+ // init CH34x with line coding
+ cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
+ uint16_t const div_ps = ch34x_get_divisor_prescaler(line_coding.bit_rate);
+ TU_ASSERT(div_ps, );
+ uint8_t const lcr = ch34x_get_lcr(line_coding.stop_bits, line_coding.parity, line_coding.data_bits);
+ TU_ASSERT(lcr, );
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps,
+ ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),);
+ break;
+ }
+
+ case CONFIG_CH34X_SPECIAL_REG_WRITE:
+ // overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver
+ p_cdc->line_coding = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X);
+ TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),);
+ break;
+
+ case CONFIG_CH34X_FLOW_CONTROL:
+ // no hardware flow control
+ TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL),);
+ break;
+
+ case CONFIG_CH34X_MODEM_CONTROL:
+ // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT)
+ TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE),);
+ break;
+
+ case CONFIG_CH34X_COMPLETE:
+ set_config_complete(p_cdc, idx, itf_num);
+ break;
+
+ default:
+ TU_ASSERT (false,);
+ break;
+ }
+}
+
+//------------- CH34x helper -------------//
+
+// calculate divisor and prescaler for baudrate, return it as 16-bit combined value
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) {
+ uint8_t a;
+ uint8_t b;
+ uint32_t c;
+
+ TU_VERIFY(baval != 0 && baval <= 2000000, 0);
+ switch (baval) {
+ case 921600:
+ a = 0xf3;
+ b = 7;
+ break;
+
+ case 307200:
+ a = 0xd9;
+ b = 7;
+ break;
+
+ default:
+ if (baval > 6000000 / 255) {
+ b = 3;
+ c = 6000000;
+ } else if (baval > 750000 / 255) {
+ b = 2;
+ c = 750000;
+ } else if (baval > 93750 / 255) {
+ b = 1;
+ c = 93750;
+ } else {
+ b = 0;
+ c = 11719;
+ }
+ a = (uint8_t) (c / baval);
+ if (a == 0 || a == 0xFF) {
+ return 0;
+ }
+ if ((c / a - baval) > (baval - c / (a + 1))) {
+ a++;
+ }
+ a = (uint8_t) (256 - a);
+ break;
+ }
+
+ // reg divisor = a, reg prescaler = b
+ // According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE,
+ // otherwise the chip will buffer data.
+ return (uint16_t) ((uint16_t)a << 8 | 0x80 | b);
+}
+
+// calculate lcr value from data coding
+static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits) {
+ uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
+ TU_VERIFY(data_bits >= 5 && data_bits <= 8, 0);
+ lcr |= (uint8_t) (data_bits - 5);
+
+ switch(parity) {
+ case CDC_LINE_CODING_PARITY_NONE:
+ break;
+
+ case CDC_LINE_CODING_PARITY_ODD:
+ lcr |= CH34X_LCR_ENABLE_PAR;
+ break;
+
+ case CDC_LINE_CODING_PARITY_EVEN:
+ lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_PAR_EVEN;
+ break;
+
+ case CDC_LINE_CODING_PARITY_MARK:
+ lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE;
+ break;
+
+ case CDC_LINE_CODING_PARITY_SPACE:
+ lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN;
+ break;
+
+ default: break;
+ }
+
+ // 1.5 stop bits not supported
+ TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5, 0);
+ if (stop_bits == CDC_LINE_CODING_STOP_BITS_2) {
+ lcr |= CH34X_LCR_STOP_BITS_2;
+ }
+
+ return lcr;
+}
+
+
+#endif // CFG_TUH_CDC_CH34X
+
#endif
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index 9e5edd94e..d512a23a5 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -148,8 +148,11 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c
// Request to set baudrate
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-// Request to Set Line Coding (ACM only)
-// Should only use if you don't work with serial devices such as FTDI/CP210x
+// Request to set data format
+bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to Set Line Coding = baudrate + data format
+// Note: only implemented by ACM and CH34x, not supported by FTDI and CP210x yet
bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Request to Get Line Coding (ACM only)
@@ -159,15 +162,13 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
// Connect by set both DTR, RTS
TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
}
// Disconnect by clear both DTR, RTS
TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
}
diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h
new file mode 100644
index 000000000..c18066f57
--- /dev/null
+++ b/src/class/cdc/serial/ch34x.h
@@ -0,0 +1,84 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Heiko Kuester
+ *
+ * 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.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _CH34X_H_
+#define _CH34X_H_
+
+// There is no official documentation for the CH34x (CH340, CH341) chips. Reference can be found
+// - https://github.com/WCHSoftGroup/ch341ser_linux
+// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c
+// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uchcom.c
+
+// set line_coding @ enumeration
+#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#else // this default is necessary to work properly
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+#endif
+
+// USB requests
+#define CH34X_REQ_READ_VERSION 0x5F // dec 95
+#define CH34X_REQ_WRITE_REG 0x9A // dec 154
+#define CH34X_REQ_READ_REG 0x95 // dec 149
+#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161
+#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164
+
+// registers
+#define CH34X_REG_BREAK 0x05
+#define CH34X_REG_PRESCALER 0x12
+#define CH34X_REG_DIVISOR 0x13
+#define CH34X_REG_LCR 0x18
+#define CH34X_REG_LCR2 0x25
+#define CH34X_REG_MCR_MSR 0x06
+#define CH34X_REG_MCR_MSR2 0x07
+#define CH34X_NBREAK_BITS 0x01
+
+#define CH341_REG_0x0F 0x0F // undocumented register
+#define CH341_REG_0x2C 0x2C // undocumented register
+#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts)
+
+#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER)
+#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR)
+
+// modem control bits
+#define CH34X_BIT_RTS ( 1 << 6 )
+#define CH34X_BIT_DTR ( 1 << 5 )
+
+// line control bits
+#define CH34X_LCR_ENABLE_RX 0x80
+#define CH34X_LCR_ENABLE_TX 0x40
+#define CH34X_LCR_MARK_SPACE 0x20
+#define CH34X_LCR_PAR_EVEN 0x10
+#define CH34X_LCR_ENABLE_PAR 0x08
+#define CH34X_LCR_PAR_MASK 0x38 // all parity bits
+#define CH34X_LCR_STOP_BITS_2 0x04
+#define CH34X_LCR_CS8 0x03
+#define CH34X_LCR_CS7 0x02
+#define CH34X_LCR_CS6 0x01
+#define CH34X_LCR_CS5 0x00
+#define CH34X_LCR_CS_MASK 0x03 // all CSx bits
+
+#endif /* _CH34X_H_ */
diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c
index db52b503f..709fa3dd8 100644
--- a/src/class/hid/hid_host.c
+++ b/src/class/hid/hid_host.c
@@ -50,6 +50,7 @@ typedef struct
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
+ bool mounted; // Enumeration is complete
uint8_t itf_protocol; // None, Keyboard, Mouse
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
@@ -139,7 +140,8 @@ uint8_t tuh_hid_itf_get_total_count(void)
bool tuh_hid_mounted(uint8_t daddr, uint8_t idx)
{
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
- return p_hid != NULL;
+ TU_VERIFY(p_hid);
+ return p_hid->mounted;
}
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info)
@@ -463,6 +465,7 @@ void hidh_close(uint8_t daddr)
TU_LOG_DRV(" HIDh close addr = %u index = %u\r\n", daddr, i);
if(tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
p_hid->daddr = 0;
+ p_hid->mounted = false;
}
}
}
@@ -632,6 +635,7 @@ static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t con
{
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
TU_VERIFY(p_hid, );
+ p_hid->mounted = true;
// enumeration is complete
if (tuh_hid_mount_cb) tuh_hid_mount_cb(daddr, idx, desc_report, desc_len);
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index 389a29696..a68cb2156 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -49,10 +49,8 @@ typedef struct
uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
-#if CFG_FIFO_MUTEX
- osal_mutex_def_t rx_ff_mutex;
- osal_mutex_def_t tx_ff_mutex;
-#endif
+ OSAL_MUTEX_DEF(rx_ff_mutex);
+ OSAL_MUTEX_DEF(tx_ff_mutex);
// Endpoint Transfer buffer
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_VENDOR_EPSIZE];
@@ -183,10 +181,8 @@ void vendord_init(void)
tu_fifo_config(&p_itf->rx_ff, p_itf->rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, 1, false);
tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
-#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
-#endif
}
}
diff --git a/src/class/video/video.h b/src/class/video/video.h
index d9880c291..6319c6536 100644
--- a/src/class/video/video.h
+++ b/src/class/video/video.h
@@ -29,6 +29,10 @@
#include "common/tusb_common.h"
+enum {
+ VIDEO_BCD_1_50 = 0x0150,
+};
+
// Table 3-19 Color Matching Descriptor
typedef enum {
VIDEO_COLOR_PRIMARIES_UNDEFINED = 0x00,
@@ -198,55 +202,98 @@ typedef enum {
} video_terminal_type_t;
//--------------------------------------------------------------------+
-// Descriptors
+// Video Control (VC) Descriptors
//--------------------------------------------------------------------+
/* 2.3.4.2 */
+#define tusb_desc_video_control_header_nitf_t(_nitf) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint16_t bcdUVC; \
+ uint16_t wTotalLength; \
+ uint32_t dwClockFrequency; /* deprecated */ \
+ uint8_t bInCollection; \
+ uint8_t baInterfaceNr[_nitf]; \
+ }
+
+typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t;
+typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t;
+typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t;
+typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t;
+typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t;
+
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
- uint16_t bcdUVC;
- uint16_t wTotalLength;
- uint32_t dwClockFrequency;
- uint8_t bInCollection;
- uint8_t baInterfaceNr[];
-} tusb_desc_cs_video_ctl_itf_hdr_t;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t iTerminal;
+} tusb_desc_video_control_input_terminal_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_input_terminal_t) == 8, "size is not correct");
-/* 2.4.3.3 */
typedef struct TU_ATTR_PACKED {
- uint8_t bHeaderLength;
- union {
- uint8_t bmHeaderInfo;
- struct {
- uint8_t FrameID: 1;
- uint8_t EndOfFrame: 1;
- uint8_t PresentationTime: 1;
- uint8_t SourceClockReference: 1;
- uint8_t PayloadSpecific: 1;
- uint8_t StillImage: 1;
- uint8_t Error: 1;
- uint8_t EndOfHeader: 1;
- };
- };
-} tusb_video_payload_header_t;
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bSourceID;
+ uint8_t iTerminal;
+} tusb_desc_video_control_output_terminal_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_output_terminal_t) == 9, "size is not correct");
+
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t iTerminal;
+
+ uint16_t wObjectiveFocalLengthMin;
+ uint16_t wObjectiveFocalLengthMax;
+ uint16_t wOcularFocalLength;
+ uint8_t bControlSize;
+ uint8_t bmControls[3];
+} tusb_desc_video_control_camera_terminal_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_camera_terminal_t) == 18, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Video Streaming (VS) Descriptors
+//--------------------------------------------------------------------+
/* 3.9.2.1 */
-typedef struct TU_ATTR_PACKED {
- uint8_t bLength;
- uint8_t bDescriptorType;
- uint8_t bDescriptorSubType;
- uint8_t bNumFormats;
- uint16_t wTotalLength;
- uint8_t bEndpointAddress;
- uint8_t bmInfo;
- uint8_t bTerminalLink;
- uint8_t bStillCaptureMethod;
- uint8_t bTriggerSupport;
- uint8_t bTriggerUsage;
- uint8_t bControlSize;
- uint8_t bmaControls[];
-} tusb_desc_cs_video_stm_itf_in_hdr_t;
+#define tusb_desc_video_streaming_input_header_nbyte_t(_nb) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint8_t bNumFormats; /* Number of video payload Format descriptors for this interface */ \
+ uint16_t wTotalLength; \
+ uint8_t bEndpointAddress; \
+ uint8_t bmInfo; /* Bit 0: dynamic format change supported */ \
+ uint8_t bTerminalLink; \
+ uint8_t bStillCaptureMethod; \
+ uint8_t bTriggerSupport; /* Hardware trigger supported */ \
+ uint8_t bTriggerUsage; \
+ uint8_t bControlSize; /* sizeof of each control item */ \
+ uint8_t bmaControls[_nb]; \
+ }
+
+typedef tusb_desc_video_streaming_input_header_nbyte_t() tusb_desc_video_streaming_input_header_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(1) tusb_desc_video_streaming_input_header_1byte_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(2) tusb_desc_video_streaming_input_header_2byte_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(3) tusb_desc_video_streaming_input_header_3byte_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(4) tusb_desc_video_streaming_input_header_4byte_t;
/* 3.9.2.2 */
typedef struct TU_ATTR_PACKED {
@@ -259,7 +306,7 @@ typedef struct TU_ATTR_PACKED {
uint8_t bTerminalLink;
uint8_t bControlSize;
uint8_t bmaControls[];
-} tusb_desc_cs_video_stm_itf_out_hdr_t;
+} tusb_desc_video_streaming_output_header_t;
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
@@ -285,14 +332,33 @@ typedef struct TU_ATTR_PACKED {
uint8_t bmaControls[];
} output;
};
-} tusb_desc_cs_video_stm_itf_hdr_t;
+} tusb_desc_video_streaming_inout_header_t;
+// 3.9.2.6 Color Matching Descriptor
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bColorPrimaries;
+ uint8_t bTransferCharacteristics;
+ uint8_t bMatrixCoefficients;
+} tusb_desc_video_streaming_color_matching_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_streaming_color_matching_t) == 6, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Format and Frame Descriptor
+// Note: bFormatIndex & bFrameIndex are 1-based index
+//--------------------------------------------------------------------+
+
+//------------- Uncompressed -------------//
+// Uncompressed payload specs: 3.1.1 format descriptor
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFormatIndex;
- uint8_t bNumFrameDescriptors;
+ uint8_t bNumFrameDescriptors; // Number of frame descriptors for this format
uint8_t guidFormat[16];
uint8_t bBitsPerPixel;
uint8_t bDefaultFrameIndex;
@@ -300,22 +366,65 @@ typedef struct TU_ATTR_PACKED {
uint8_t bAspectRatioY;
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
-} tusb_desc_cs_video_fmt_uncompressed_t;
+} tusb_desc_video_format_uncompressed_t;
+// Uncompressed payload specs: 3.1.2 frame descriptor
+#define tusb_desc_video_frame_uncompressed_nint_t(_nint) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint8_t bFrameIndex; \
+ uint8_t bmCapabilities; \
+ uint16_t wWidth; \
+ uint16_t wHeight; \
+ uint32_t dwMinBitRate; \
+ uint32_t dwMaxBitRate; \
+ uint32_t dwMaxVideoFrameBufferSize; /* deprecated in 1.5 */ \
+ uint32_t dwDefaultFrameInterval; \
+ uint8_t bFrameIntervalType; \
+ uint32_t dwFrameInterval[_nint]; \
+ }
+
+typedef tusb_desc_video_frame_uncompressed_nint_t() tusb_desc_video_frame_uncompressed_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(1) tusb_desc_video_frame_uncompressed_1int_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(2) tusb_desc_video_frame_uncompressed_2int_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(3) tusb_desc_video_frame_uncompressed_3int_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(4) tusb_desc_video_frame_uncompressed_4int_t;
+
+// continuous = 3 intervals: min, max, step
+typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_uncompressed_continuous_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_frame_uncompressed_continuous_t) == 38, "size is not correct");
+
+//------------- MJPEG -------------//
+// MJPEG payload specs: 3.1.1 format descriptor
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFormatIndex;
uint8_t bNumFrameDescriptors;
- uint8_t bmFlags;
+ uint8_t bmFlags; // Bit 0: fixed size samples (1 = yes)
uint8_t bDefaultFrameIndex;
uint8_t bAspectRatioX;
uint8_t bAspectRatioY;
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
-} tusb_desc_cs_video_fmt_mjpeg_t;
+} tusb_desc_video_format_mjpeg_t;
+// MJPEG payload specs: 3.1.2 frame descriptor (same as uncompressed)
+typedef tusb_desc_video_frame_uncompressed_t tusb_desc_video_frame_mjpeg_t;
+typedef tusb_desc_video_frame_uncompressed_1int_t tusb_desc_video_frame_mjpeg_1int_t;
+typedef tusb_desc_video_frame_uncompressed_2int_t tusb_desc_video_frame_mjpeg_2int_t;
+typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_mjpeg_3int_t;
+typedef tusb_desc_video_frame_uncompressed_4int_t tusb_desc_video_frame_mjpeg_4int_t;
+
+// continuous = 3 intervals: min, max, step
+typedef tusb_desc_video_frame_mjpeg_3int_t tusb_desc_video_frame_mjpeg_continuous_t;
+
+//------------- DV -------------//
+// DV payload specs: 3.1.1
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -323,8 +432,9 @@ typedef struct TU_ATTR_PACKED {
uint8_t bFormatIndex;
uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
uint8_t bFormatType;
-} tusb_desc_cs_video_fmt_dv_t;
+} tusb_desc_video_format_dv_t;
+// Frame Based payload specs: 3.1.1
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -339,25 +449,7 @@ typedef struct TU_ATTR_PACKED {
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
uint8_t bVaribaleSize;
-} tusb_desc_cs_video_fmt_frame_based_t;
-
-typedef struct TU_ATTR_PACKED {
- uint8_t bLength;
- uint8_t bDescriptorType;
- uint8_t bDescriptorSubType;
- uint8_t bFrameIndex;
- uint8_t bmCapabilities;
- uint16_t wWidth;
- uint16_t wHeight;
- uint32_t dwMinBitRate;
- uint32_t dwMaxBitRate;
- uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
- uint32_t dwDefaultFrameInterval;
- uint8_t bFrameIntervalType;
- uint32_t dwFrameInterval[];
-} tusb_desc_cs_video_frm_uncompressed_t;
-
-typedef tusb_desc_cs_video_frm_uncompressed_t tusb_desc_cs_video_frm_mjpeg_t;
+} tusb_desc_video_format_framebased_t;
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
@@ -373,12 +465,30 @@ typedef struct TU_ATTR_PACKED {
uint8_t bFrameIntervalType;
uint32_t dwBytesPerLine;
uint32_t dwFrameInterval[];
-} tusb_desc_cs_video_frm_frame_based_t;
+} tusb_desc_video_frame_framebased_t;
//--------------------------------------------------------------------+
// Requests
//--------------------------------------------------------------------+
+/* 2.4.3.3 */
+typedef struct TU_ATTR_PACKED {
+ uint8_t bHeaderLength;
+ union {
+ uint8_t bmHeaderInfo;
+ struct {
+ uint8_t FrameID: 1;
+ uint8_t EndOfFrame: 1;
+ uint8_t PresentationTime: 1;
+ uint8_t SourceClockReference: 1;
+ uint8_t PayloadSpecific: 1;
+ uint8_t StillImage: 1;
+ uint8_t Error: 1;
+ uint8_t EndOfHeader: 1;
+ };
+ };
+} tusb_video_payload_header_t;
+
/* 4.3.1.1 */
typedef struct TU_ATTR_PACKED {
union {
@@ -537,7 +647,7 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c
/* Motion-JPEG 3.1.1 Table 3-2 and 3-4 */
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \
TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
- TUSB_DESC_CS_INTERFACE, VIDEO_CS_VS_INTERFACE_FRAME_MJPEG, \
+ TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_MJPEG, \
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index 3b29454a3..1affd615e 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -50,17 +50,17 @@
typedef struct {
tusb_desc_interface_t std;
- tusb_desc_cs_video_ctl_itf_hdr_t ctl;
+ tusb_desc_video_control_header_t ctl;
} tusb_desc_vc_itf_t;
typedef struct {
tusb_desc_interface_t std;
- tusb_desc_cs_video_stm_itf_hdr_t stm;
+ tusb_desc_video_streaming_inout_header_t stm;
} tusb_desc_vs_itf_t;
typedef union {
- tusb_desc_cs_video_ctl_itf_hdr_t ctl;
- tusb_desc_cs_video_stm_itf_hdr_t stm;
+ tusb_desc_video_control_header_t ctl;
+ tusb_desc_video_streaming_inout_header_t stm;
} tusb_desc_video_itf_hdr_t;
typedef struct TU_ATTR_PACKED {
@@ -78,9 +78,9 @@ typedef union {
uint8_t bFormatIndex;
uint8_t bNumFrameDescriptors;
};
- tusb_desc_cs_video_fmt_uncompressed_t uncompressed;
- tusb_desc_cs_video_fmt_mjpeg_t mjpeg;
- tusb_desc_cs_video_fmt_frame_based_t frame_based;
+ tusb_desc_video_format_uncompressed_t uncompressed;
+ tusb_desc_video_format_mjpeg_t mjpeg;
+ tusb_desc_video_format_framebased_t frame_based;
} tusb_desc_cs_video_fmt_t;
typedef union {
@@ -93,9 +93,9 @@ typedef union {
uint16_t wWidth;
uint16_t wHeight;
};
- tusb_desc_cs_video_frm_uncompressed_t uncompressed;
- tusb_desc_cs_video_frm_mjpeg_t mjpeg;
- tusb_desc_cs_video_frm_frame_based_t frame_based;
+ tusb_desc_video_frame_uncompressed_t uncompressed;
+ tusb_desc_video_frame_mjpeg_t mjpeg;
+ tusb_desc_video_frame_framebased_t frame_based;
} tusb_desc_cs_video_frm_t;
/* video streaming interface */
@@ -434,8 +434,9 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
uint_fast32_t interval_ms = interval / 10000;
TU_ASSERT(interval_ms);
uint_fast32_t payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
- if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
+ if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
+ }
param->dwMaxPayloadTransferSize = payload_size;
return true;
}
@@ -577,8 +578,9 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
} else {
payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
}
- if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
+ if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
+ }
param->dwMaxPayloadTransferSize = payload_size;
}
return true;
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index caeb5f2ef..1f08ce4ed 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -37,6 +37,7 @@
#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) )
#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) )
+#define TU_DIV_CEIL(n, d) (((n) + (d) - 1) / (d))
#define TU_U16(_high, _low) ((uint16_t) (((_high) << 8) | (_low)))
#define TU_U16_HIGH(_u16) ((uint8_t) (((_u16) >> 8) & 0x00ff))
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index fab680989..2c5dce723 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -44,43 +44,38 @@
*------------------------------------------------------------------*/
/// defined base on EHCI specs value for Endpoint Speed
-typedef enum
-{
+typedef enum {
TUSB_SPEED_FULL = 0,
TUSB_SPEED_LOW = 1,
TUSB_SPEED_HIGH = 2,
TUSB_SPEED_INVALID = 0xff,
-}tusb_speed_t;
+} tusb_speed_t;
/// defined base on USB Specs Endpoint's bmAttributes
-typedef enum
-{
+typedef enum {
TUSB_XFER_CONTROL = 0 ,
TUSB_XFER_ISOCHRONOUS ,
TUSB_XFER_BULK ,
TUSB_XFER_INTERRUPT
-}tusb_xfer_type_t;
+} tusb_xfer_type_t;
-typedef enum
-{
+typedef enum {
TUSB_DIR_OUT = 0,
TUSB_DIR_IN = 1,
TUSB_DIR_IN_MASK = 0x80
-}tusb_dir_t;
+} tusb_dir_t;
-enum
-{
+enum {
TUSB_EPSIZE_BULK_FS = 64,
- TUSB_EPSIZE_BULK_HS= 512,
+ TUSB_EPSIZE_BULK_HS = 512,
TUSB_EPSIZE_ISO_FS_MAX = 1023,
TUSB_EPSIZE_ISO_HS_MAX = 1024,
};
-/// Isochronous End Point Attributes
-typedef enum
-{
+/// Isochronous Endpoint Attributes
+typedef enum {
TUSB_ISO_EP_ATT_NO_SYNC = 0x00,
TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04,
TUSB_ISO_EP_ATT_ADAPTIVE = 0x08,
@@ -88,11 +83,10 @@ typedef enum
TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point
TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point
TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback
-}tusb_iso_ep_attribute_t;
+} tusb_iso_ep_attribute_t;
/// USB Descriptor Types
-typedef enum
-{
+typedef enum {
TUSB_DESC_DEVICE = 0x01,
TUSB_DESC_CONFIGURATION = 0x02,
TUSB_DESC_STRING = 0x03,
@@ -119,10 +113,9 @@ typedef enum
TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30,
TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31
-}tusb_desc_type_t;
+} tusb_desc_type_t;
-typedef enum
-{
+typedef enum {
TUSB_REQ_GET_STATUS = 0 ,
TUSB_REQ_CLEAR_FEATURE = 1 ,
TUSB_REQ_RESERVED = 2 ,
@@ -136,25 +129,22 @@ typedef enum
TUSB_REQ_GET_INTERFACE = 10 ,
TUSB_REQ_SET_INTERFACE = 11 ,
TUSB_REQ_SYNCH_FRAME = 12
-}tusb_request_code_t;
+} tusb_request_code_t;
-typedef enum
-{
+typedef enum {
TUSB_REQ_FEATURE_EDPT_HALT = 0,
TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1,
TUSB_REQ_FEATURE_TEST_MODE = 2
-}tusb_request_feature_selector_t;
+} tusb_request_feature_selector_t;
-typedef enum
-{
+typedef enum {
TUSB_REQ_TYPE_STANDARD = 0,
TUSB_REQ_TYPE_CLASS,
TUSB_REQ_TYPE_VENDOR,
TUSB_REQ_TYPE_INVALID
} tusb_request_type_t;
-typedef enum
-{
+typedef enum {
TUSB_REQ_RCPT_DEVICE =0,
TUSB_REQ_RCPT_INTERFACE,
TUSB_REQ_RCPT_ENDPOINT,
@@ -162,8 +152,7 @@ typedef enum
} tusb_request_recipient_t;
// https://www.usb.org/defined-class-codes
-typedef enum
-{
+typedef enum {
TUSB_CLASS_UNSPECIFIED = 0 ,
TUSB_CLASS_AUDIO = 1 ,
TUSB_CLASS_CDC = 2 ,
@@ -187,26 +176,23 @@ typedef enum
TUSB_CLASS_MISC = 0xEF ,
TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE ,
TUSB_CLASS_VENDOR_SPECIFIC = 0xFF
-}tusb_class_code_t;
+} tusb_class_code_t;
typedef enum
{
MISC_SUBCLASS_COMMON = 2
}misc_subclass_type_t;
-typedef enum
-{
+typedef enum {
MISC_PROTOCOL_IAD = 1
-}misc_protocol_type_t;
+} misc_protocol_type_t;
-typedef enum
-{
+typedef enum {
APP_SUBCLASS_USBTMC = 0x03,
APP_SUBCLASS_DFU_RUNTIME = 0x01
} app_subclass_type_t;
-typedef enum
-{
+typedef enum {
DEVICE_CAPABILITY_WIRELESS_USB = 0x01,
DEVICE_CAPABILITY_USB20_EXTENSION = 0x02,
DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03,
@@ -223,7 +209,7 @@ typedef enum
DEVICE_CAPABILITY_AUTHENTICATION = 0x0E,
DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F,
DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10
-}device_capability_type_t;
+} device_capability_type_t;
enum {
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5),
@@ -235,28 +221,25 @@ enum {
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
-typedef enum
-{
+typedef enum {
XFER_RESULT_SUCCESS = 0,
XFER_RESULT_FAILED,
XFER_RESULT_STALLED,
XFER_RESULT_TIMEOUT,
XFER_RESULT_INVALID
-}xfer_result_t;
+} xfer_result_t;
-enum // TODO remove
-{
+// TODO remove
+enum {
DESC_OFFSET_LEN = 0,
DESC_OFFSET_TYPE = 1
};
-enum
-{
+enum {
INTERFACE_INVALID_NUMBER = 0xff
};
-typedef enum
-{
+typedef enum {
MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00,
MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01,
MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02,
@@ -268,16 +251,14 @@ typedef enum
MS_OS_20_FEATURE_VENDOR_REVISION = 0x08
} microsoft_os_20_type_t;
-enum
-{
+enum {
CONTROL_STAGE_IDLE,
CONTROL_STAGE_SETUP,
CONTROL_STAGE_DATA,
CONTROL_STAGE_ACK
};
-enum
-{
+enum {
TUSB_INDEX_INVALID_8 = 0xFFu
};
@@ -290,8 +271,7 @@ TU_ATTR_PACKED_BEGIN
TU_ATTR_BIT_FIELD_ORDER_BEGIN
/// USB Device Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes.
uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
@@ -314,8 +294,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct");
// USB Binary Device Object Store (BOS) Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
uint16_t wTotalLength ; ///< Total length of data returned for this descriptor
@@ -325,8 +304,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct");
/// USB Configuration Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration.
@@ -341,8 +319,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct");
/// USB Interface Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes
uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type
@@ -358,8 +335,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct");
/// USB Endpoint Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; // Size of this descriptor in bytes
uint8_t bDescriptorType ; // ENDPOINT Descriptor Type
@@ -379,8 +355,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct");
/// USB Other Speed Configuration Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of descriptor
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
uint16_t wTotalLength ; ///< Total length of data returned
@@ -393,8 +368,7 @@ typedef struct TU_ATTR_PACKED
} tusb_desc_other_speed_t;
/// USB Device Qualifier Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of descriptor
uint8_t bDescriptorType ; ///< Device Qualifier Type
uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00)
@@ -411,8 +385,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct");
/// USB Interface Association Descriptor (IAD ECN)
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of descriptor
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
@@ -426,17 +399,17 @@ typedef struct TU_ATTR_PACKED
uint8_t iFunction ; ///< Index of the string descriptor describing the interface association.
} tusb_desc_interface_assoc_t;
+TU_VERIFY_STATIC( sizeof(tusb_desc_interface_assoc_t) == 8, "size is not correct");
+
// USB String Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes
uint8_t bDescriptorType ; ///< Descriptor Type
uint16_t unicode_string[];
} tusb_desc_string_t;
// USB Binary Device Object Store (BOS)
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType ;
uint8_t bDevCapabilityType;
@@ -445,9 +418,8 @@ typedef struct TU_ATTR_PACKED
uint8_t CapabilityData[];
} tusb_desc_bos_platform_t;
-// USB WebuSB URL Descriptor
-typedef struct TU_ATTR_PACKED
-{
+// USB WebUSB URL Descriptor
+typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bScheme;
@@ -455,8 +427,7 @@ typedef struct TU_ATTR_PACKED
} tusb_desc_webusb_url_t;
// DFU Functional Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -481,7 +452,7 @@ typedef struct TU_ATTR_PACKED
//
//--------------------------------------------------------------------+
-typedef struct TU_ATTR_PACKED{
+typedef struct TU_ATTR_PACKED {
union {
struct TU_ATTR_PACKED {
uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
@@ -500,7 +471,6 @@ typedef struct TU_ATTR_PACKED{
TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct");
-
TU_ATTR_PACKED_END // End of all packed definitions
TU_ATTR_BIT_FIELD_ORDER_END
@@ -509,36 +479,30 @@ TU_ATTR_BIT_FIELD_ORDER_END
//--------------------------------------------------------------------+
// Get direction from Endpoint address
-TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) {
return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
}
// Get Endpoint number from address
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr) {
return (uint8_t)(addr & (~TUSB_DIR_IN_MASK));
}
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) {
return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0));
}
-TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) {
return tu_le16toh(desc_ep->wMaxPacketSize) & TU_GENMASK(10, 0);
}
#if CFG_TUSB_DEBUG
-TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_dir_str(tusb_dir_t dir)
-{
+TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_dir_str(tusb_dir_t dir) {
tu_static const char *str[] = {"out", "in"};
return str[dir];
}
-TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t)
-{
+TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) {
tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"};
return str[t];
}
@@ -549,21 +513,18 @@ TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_
//--------------------------------------------------------------------+
// return next descriptor
-TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc) {
uint8_t const* desc8 = (uint8_t const*) desc;
return desc8 + desc8[DESC_OFFSET_LEN];
}
// get descriptor type
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) {
return ((uint8_t const*) desc)[DESC_OFFSET_TYPE];
}
// get descriptor length
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) {
return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
}
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index 1b5f53dfc..8aa66b4df 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -75,8 +75,8 @@
#define _MESS_FAILED() do {} while (0)
#endif
-// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33
-#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__)
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__)
#define TU_BREAKPOINT() do \
{ \
volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
diff --git a/src/device/dcd.h b/src/device/dcd.h
index 8c6813cf7..69c26bcf4 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -152,7 +152,7 @@ void dcd_sof_enable(uint8_t rhport, bool en);
// Invoked when a control transfer's status stage is complete.
// May help DCD to prepare for next control transfer, this API is optional.
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK;
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request);
// Configure endpoint's registers according to descriptor
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 59466b42e..5c94ebcc5 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -38,11 +38,19 @@
//--------------------------------------------------------------------+
// USBD Configuration
//--------------------------------------------------------------------+
-
#ifndef CFG_TUD_TASK_QUEUE_SZ
#define CFG_TUD_TASK_QUEUE_SZ 16
#endif
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
+ (void)rhport;
+ (void)eventid;
+ (void)in_isr;
+}
+
//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
@@ -50,10 +58,8 @@
// Invalid driver ID in itf2drv[] ep2drv[][] mapping
enum { DRVID_INVALID = 0xFFu };
-typedef struct
-{
- struct TU_ATTR_PACKED
- {
+typedef struct {
+ struct TU_ATTR_PACKED {
volatile uint8_t connected : 1;
volatile uint8_t addressed : 1;
volatile uint8_t suspended : 1;
@@ -85,151 +91,150 @@ tu_static usbd_device_t _usbd_dev;
#endif
// Built-in class drivers
-tu_static usbd_class_driver_t const _usbd_driver[] =
-{
- #if CFG_TUD_CDC
- {
- DRIVER_NAME("CDC")
- .init = cdcd_init,
- .reset = cdcd_reset,
- .open = cdcd_open,
- .control_xfer_cb = cdcd_control_xfer_cb,
- .xfer_cb = cdcd_xfer_cb,
- .sof = NULL
- },
- #endif
+tu_static usbd_class_driver_t const _usbd_driver[] = {
+ #if CFG_TUD_CDC
+ {
+ DRIVER_NAME("CDC")
+ .init = cdcd_init,
+ .reset = cdcd_reset,
+ .open = cdcd_open,
+ .control_xfer_cb = cdcd_control_xfer_cb,
+ .xfer_cb = cdcd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_MSC
- {
- DRIVER_NAME("MSC")
- .init = mscd_init,
- .reset = mscd_reset,
- .open = mscd_open,
- .control_xfer_cb = mscd_control_xfer_cb,
- .xfer_cb = mscd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_MSC
+ {
+ DRIVER_NAME("MSC")
+ .init = mscd_init,
+ .reset = mscd_reset,
+ .open = mscd_open,
+ .control_xfer_cb = mscd_control_xfer_cb,
+ .xfer_cb = mscd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_HID
- {
- DRIVER_NAME("HID")
- .init = hidd_init,
- .reset = hidd_reset,
- .open = hidd_open,
- .control_xfer_cb = hidd_control_xfer_cb,
- .xfer_cb = hidd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_HID
+ {
+ DRIVER_NAME("HID")
+ .init = hidd_init,
+ .reset = hidd_reset,
+ .open = hidd_open,
+ .control_xfer_cb = hidd_control_xfer_cb,
+ .xfer_cb = hidd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_AUDIO
- {
- DRIVER_NAME("AUDIO")
- .init = audiod_init,
- .reset = audiod_reset,
- .open = audiod_open,
- .control_xfer_cb = audiod_control_xfer_cb,
- .xfer_cb = audiod_xfer_cb,
- .sof = audiod_sof_isr
- },
- #endif
+ #if CFG_TUD_AUDIO
+ {
+ DRIVER_NAME("AUDIO")
+ .init = audiod_init,
+ .reset = audiod_reset,
+ .open = audiod_open,
+ .control_xfer_cb = audiod_control_xfer_cb,
+ .xfer_cb = audiod_xfer_cb,
+ .sof = audiod_sof_isr
+ },
+ #endif
- #if CFG_TUD_VIDEO
- {
- DRIVER_NAME("VIDEO")
- .init = videod_init,
- .reset = videod_reset,
- .open = videod_open,
- .control_xfer_cb = videod_control_xfer_cb,
- .xfer_cb = videod_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_VIDEO
+ {
+ DRIVER_NAME("VIDEO")
+ .init = videod_init,
+ .reset = videod_reset,
+ .open = videod_open,
+ .control_xfer_cb = videod_control_xfer_cb,
+ .xfer_cb = videod_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_MIDI
- {
- DRIVER_NAME("MIDI")
- .init = midid_init,
- .open = midid_open,
- .reset = midid_reset,
- .control_xfer_cb = midid_control_xfer_cb,
- .xfer_cb = midid_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_MIDI
+ {
+ DRIVER_NAME("MIDI")
+ .init = midid_init,
+ .open = midid_open,
+ .reset = midid_reset,
+ .control_xfer_cb = midid_control_xfer_cb,
+ .xfer_cb = midid_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_VENDOR
- {
- DRIVER_NAME("VENDOR")
- .init = vendord_init,
- .reset = vendord_reset,
- .open = vendord_open,
- .control_xfer_cb = tud_vendor_control_xfer_cb,
- .xfer_cb = vendord_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_VENDOR
+ {
+ DRIVER_NAME("VENDOR")
+ .init = vendord_init,
+ .reset = vendord_reset,
+ .open = vendord_open,
+ .control_xfer_cb = tud_vendor_control_xfer_cb,
+ .xfer_cb = vendord_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_USBTMC
- {
- DRIVER_NAME("TMC")
- .init = usbtmcd_init_cb,
- .reset = usbtmcd_reset_cb,
- .open = usbtmcd_open_cb,
- .control_xfer_cb = usbtmcd_control_xfer_cb,
- .xfer_cb = usbtmcd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_USBTMC
+ {
+ DRIVER_NAME("TMC")
+ .init = usbtmcd_init_cb,
+ .reset = usbtmcd_reset_cb,
+ .open = usbtmcd_open_cb,
+ .control_xfer_cb = usbtmcd_control_xfer_cb,
+ .xfer_cb = usbtmcd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_DFU_RUNTIME
- {
- DRIVER_NAME("DFU-RUNTIME")
- .init = dfu_rtd_init,
- .reset = dfu_rtd_reset,
- .open = dfu_rtd_open,
- .control_xfer_cb = dfu_rtd_control_xfer_cb,
- .xfer_cb = NULL,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_DFU_RUNTIME
+ {
+ DRIVER_NAME("DFU-RUNTIME")
+ .init = dfu_rtd_init,
+ .reset = dfu_rtd_reset,
+ .open = dfu_rtd_open,
+ .control_xfer_cb = dfu_rtd_control_xfer_cb,
+ .xfer_cb = NULL,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_DFU
- {
- DRIVER_NAME("DFU")
- .init = dfu_moded_init,
- .reset = dfu_moded_reset,
- .open = dfu_moded_open,
- .control_xfer_cb = dfu_moded_control_xfer_cb,
- .xfer_cb = NULL,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_DFU
+ {
+ DRIVER_NAME("DFU")
+ .init = dfu_moded_init,
+ .reset = dfu_moded_reset,
+ .open = dfu_moded_open,
+ .control_xfer_cb = dfu_moded_control_xfer_cb,
+ .xfer_cb = NULL,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
- {
- DRIVER_NAME("NET")
- .init = netd_init,
- .reset = netd_reset,
- .open = netd_open,
- .control_xfer_cb = netd_control_xfer_cb,
- .xfer_cb = netd_xfer_cb,
- .sof = NULL,
- },
- #endif
+ #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
+ {
+ DRIVER_NAME("NET")
+ .init = netd_init,
+ .reset = netd_reset,
+ .open = netd_open,
+ .control_xfer_cb = netd_control_xfer_cb,
+ .xfer_cb = netd_xfer_cb,
+ .sof = NULL,
+ },
+ #endif
- #if CFG_TUD_BTH
- {
- DRIVER_NAME("BTH")
- .init = btd_init,
- .reset = btd_reset,
- .open = btd_open,
- .control_xfer_cb = btd_control_xfer_cb,
- .xfer_cb = btd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_BTH
+ {
+ DRIVER_NAME("BTH")
+ .init = btd_init,
+ .reset = btd_reset,
+ .open = btd_open,
+ .control_xfer_cb = btd_control_xfer_cb,
+ .xfer_cb = btd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
};
enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };
@@ -275,7 +280,7 @@ tu_static osal_queue_t _usbd_q;
TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) {
bool ret = osal_queue_send(_usbd_q, event, in_isr);
- if (tud_event_hook_cb) tud_event_hook_cb(event->rhport, event->event_id, in_isr);
+ tud_event_hook_cb(event->rhport, event->event_id, in_isr);
return ret;
}
@@ -297,27 +302,23 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
// Debug
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
-tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] =
-{
- "Invalid" ,
- "Bus Reset" ,
- "Unplugged" ,
- "SOF" ,
- "Suspend" ,
- "Resume" ,
- "Setup Received" ,
- "Xfer Complete" ,
- "Func Call"
+tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] = {
+ "Invalid",
+ "Bus Reset",
+ "Unplugged",
+ "SOF",
+ "Suspend",
+ "Resume",
+ "Setup Received",
+ "Xfer Complete",
+ "Func Call"
};
// for usbd_control to print the name of control complete driver
-void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback)
-{
- for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
- {
- usbd_class_driver_t const * driver = get_driver(i);
- if ( driver && driver->control_xfer_cb == callback )
- {
+void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback) {
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+ usbd_class_driver_t const* driver = get_driver(i);
+ if (driver && driver->control_xfer_cb == callback) {
TU_LOG_USBD(" %s control complete\r\n", driver->name);
return;
}
@@ -329,43 +330,36 @@ void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback)
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
-tusb_speed_t tud_speed_get(void)
-{
+tusb_speed_t tud_speed_get(void) {
return (tusb_speed_t) _usbd_dev.speed;
}
-bool tud_connected(void)
-{
+bool tud_connected(void) {
return _usbd_dev.connected;
}
-bool tud_mounted(void)
-{
+bool tud_mounted(void) {
return _usbd_dev.cfg_num ? true : false;
}
-bool tud_suspended(void)
-{
+bool tud_suspended(void) {
return _usbd_dev.suspended;
}
-bool tud_remote_wakeup(void)
-{
+bool tud_remote_wakeup(void) {
// only wake up host if this feature is supported and enabled and we are suspended
- TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en );
+ TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en);
dcd_remote_wakeup(_usbd_rhport);
return true;
}
-bool tud_disconnect(void)
-{
+bool tud_disconnect(void) {
TU_VERIFY(dcd_disconnect);
dcd_disconnect(_usbd_rhport);
return true;
}
-bool tud_connect(void)
-{
+bool tud_connect(void) {
TU_VERIFY(dcd_connect);
dcd_connect(_usbd_rhport);
return true;
diff --git a/src/device/usbd.h b/src/device/usbd.h
index 5456148bf..3ab6c813f 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -147,7 +147,7 @@ TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
TU_ATTR_WEAK void tud_resume_cb(void);
// Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext()
-TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
+void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
// Invoked when received control request with VENDOR TYPE
TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c
index 76d062e40..35cce1f7e 100644
--- a/src/device/usbd_control.c
+++ b/src/device/usbd_control.c
@@ -32,24 +32,32 @@
#include "tusb.h"
#include "device/usbd_pvt.h"
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+ (void) rhport;
+ (void) request;
+}
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
extern void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback);
#endif
-enum
-{
+enum {
EDPT_CTRL_OUT = 0x00,
- EDPT_CTRL_IN = 0x80
+ EDPT_CTRL_IN = 0x80
};
-typedef struct
-{
+typedef struct {
tusb_control_request_t request;
-
uint8_t* buffer;
uint16_t data_len;
uint16_t total_xferred;
-
usbd_control_xfer_cb_t complete_cb;
} usbd_control_xfer_t;
@@ -63,20 +71,18 @@ tu_static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE];
//--------------------------------------------------------------------+
// Queue ZLP status transaction
-static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const * request)
-{
+static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const* request) {
// Opposite to endpoint in Data Phase
uint8_t const ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
return usbd_edpt_xfer(rhport, ep_addr, NULL, 0);
}
// Status phase
-bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
-{
- _ctrl_xfer.request = (*request);
- _ctrl_xfer.buffer = NULL;
+bool tud_control_status(uint8_t rhport, tusb_control_request_t const* request) {
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = NULL;
_ctrl_xfer.total_xferred = 0;
- _ctrl_xfer.data_len = 0;
+ _ctrl_xfer.data_len = 0;
return _status_stage_xact(rhport, request);
}
@@ -84,16 +90,15 @@ bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
// Queue a transaction in Data Stage
// Each transaction has up to Endpoint0's max packet size.
// This function can also transfer an zero-length packet
-static bool _data_stage_xact(uint8_t rhport)
-{
- uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
+static bool _data_stage_xact(uint8_t rhport) {
+ uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred,
+ CFG_TUD_ENDPOINT0_SIZE);
uint8_t ep_addr = EDPT_CTRL_OUT;
- if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN )
- {
+ if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) {
ep_addr = EDPT_CTRL_IN;
- if ( xact_len ) {
+ if (xact_len) {
TU_VERIFY(0 == tu_memcpy_s(_usbd_ctrl_buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len));
}
}
@@ -103,29 +108,24 @@ static bool _data_stage_xact(uint8_t rhport)
// Transmit data to/from the control endpoint.
// If the request's wLength is zero, a status packet is sent instead.
-bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len)
-{
- _ctrl_xfer.request = (*request);
- _ctrl_xfer.buffer = (uint8_t*) buffer;
+bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const* request, void* buffer, uint16_t len) {
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = (uint8_t*) buffer;
_ctrl_xfer.total_xferred = 0U;
- _ctrl_xfer.data_len = tu_min16(len, request->wLength);
+ _ctrl_xfer.data_len = tu_min16(len, request->wLength);
- if (request->wLength > 0U)
- {
- if(_ctrl_xfer.data_len > 0U)
- {
+ if (request->wLength > 0U) {
+ if (_ctrl_xfer.data_len > 0U) {
TU_ASSERT(buffer);
}
// TU_LOG2(" Control total data length is %u bytes\r\n", _ctrl_xfer.data_len);
// Data stage
- TU_ASSERT( _data_stage_xact(rhport) );
- }
- else
- {
+ TU_ASSERT(_data_stage_xact(rhport));
+ } else {
// Status stage
- TU_ASSERT( _status_stage_xact(rhport, request) );
+ TU_ASSERT(_status_stage_xact(rhport, request));
}
return true;
@@ -134,49 +134,42 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
//--------------------------------------------------------------------+
// USBD API
//--------------------------------------------------------------------+
-
void usbd_control_reset(void);
-void usbd_control_set_request(tusb_control_request_t const *request);
-void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp );
-bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void usbd_control_set_request(tusb_control_request_t const* request);
+void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp);
+bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void usbd_control_reset(void)
-{
+void usbd_control_reset(void) {
tu_varclr(&_ctrl_xfer);
}
// Set complete callback
-void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp )
-{
+void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) {
_ctrl_xfer.complete_cb = fp;
}
// for dcd_set_address where DCD is responsible for status response
-void usbd_control_set_request(tusb_control_request_t const *request)
-{
- _ctrl_xfer.request = (*request);
- _ctrl_xfer.buffer = NULL;
+void usbd_control_set_request(tusb_control_request_t const* request) {
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = NULL;
_ctrl_xfer.total_xferred = 0;
- _ctrl_xfer.data_len = 0;
+ _ctrl_xfer.data_len = 0;
}
// callback when a transaction complete on
// - DATA stage of control endpoint or
// - Status stage
-bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
// Endpoint Address is opposite to direction bit, this is Status Stage complete event
- if ( tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction )
- {
+ if (tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction) {
TU_ASSERT(0 == xferred_bytes);
// invoke optional dcd hook if available
- if (dcd_edpt0_status_complete) dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
+ dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
- if (_ctrl_xfer.complete_cb)
- {
+ if (_ctrl_xfer.complete_cb) {
// TODO refactor with usbd_driver_print_control_complete_name
_ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request);
}
@@ -184,8 +177,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
return true;
}
- if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
- {
+ if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) {
TU_VERIFY(_ctrl_xfer.buffer);
memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes);
TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _usbd_ctrl_buf, xferred_bytes, 2);
@@ -196,15 +188,14 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
// Data Stage is complete when all request's length are transferred or
// a short packet is sent including zero-length packet.
- if ( (_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE) )
- {
+ if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) ||
+ (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE)) {
// DATA stage is complete
bool is_ok = true;
// invoke complete callback if set
// callback can still stall control in status phase e.g out data does not make sense
- if ( _ctrl_xfer.complete_cb )
- {
+ if (_ctrl_xfer.complete_cb) {
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb);
#endif
@@ -212,21 +203,17 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request);
}
- if ( is_ok )
- {
+ if (is_ok) {
// Send status
- TU_ASSERT( _status_stage_xact(rhport, &_ctrl_xfer.request) );
- }else
- {
+ TU_ASSERT(_status_stage_xact(rhport, &_ctrl_xfer.request));
+ } else {
// Stall both IN and OUT control endpoint
dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
dcd_edpt_stall(rhport, EDPT_CTRL_IN);
}
- }
- else
- {
+ } else {
// More data to transfer
- TU_ASSERT( _data_stage_xact(rhport) );
+ TU_ASSERT(_data_stage_xact(rhport));
}
return true;
diff --git a/src/host/hub.c b/src/host/hub.c
index 32f5e0ac7..3bac18698 100644
--- a/src/host/hub.c
+++ b/src/host/hub.c
@@ -435,9 +435,12 @@ static void hub_port_get_status_complete (tuh_xfer_t* xfer)
// Other changes are: L1 state
// TODO clear change
- // prepare for next hub status
- // TODO continue with status_change, or maybe we can do it again with status
- hub_edpt_status_xfer(daddr);
+ else
+ {
+ // prepare for next hub status
+ // TODO continue with status_change, or maybe we can do it again with status
+ hub_edpt_status_xfer(daddr);
+ }
}
}
diff --git a/src/host/usbh.c b/src/host/usbh.c
index d4f308e48..67668cebe 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -36,7 +36,6 @@
//--------------------------------------------------------------------+
// USBH Configuration
//--------------------------------------------------------------------+
-
#ifndef CFG_TUH_TASK_QUEUE_SZ
#define CFG_TUH_TASK_QUEUE_SZ 16
#endif
@@ -45,12 +44,19 @@
#define CFG_TUH_INTERFACE_MAX 8
#endif
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
+ (void) rhport;
+ (void) eventid;
+ (void) in_isr;
+}
+
//--------------------------------------------------------------------+
// USBH-HCD common data structure
//--------------------------------------------------------------------+
-
-typedef struct
-{
+typedef struct {
// port
uint8_t rhport;
uint8_t hub_addr;
@@ -112,60 +118,58 @@ typedef struct {
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-
#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
#define DRIVER_NAME(_name) .name = _name,
#else
#define DRIVER_NAME(_name)
#endif
-static usbh_class_driver_t const usbh_class_drivers[] =
-{
- #if CFG_TUH_CDC
+static usbh_class_driver_t const usbh_class_drivers[] = {
+ #if CFG_TUH_CDC
{
- DRIVER_NAME("CDC")
- .init = cdch_init,
- .open = cdch_open,
- .set_config = cdch_set_config,
- .xfer_cb = cdch_xfer_cb,
- .close = cdch_close
+ DRIVER_NAME("CDC")
+ .init = cdch_init,
+ .open = cdch_open,
+ .set_config = cdch_set_config,
+ .xfer_cb = cdch_xfer_cb,
+ .close = cdch_close
},
- #endif
+ #endif
- #if CFG_TUH_MSC
+ #if CFG_TUH_MSC
{
- DRIVER_NAME("MSC")
- .init = msch_init,
- .open = msch_open,
- .set_config = msch_set_config,
- .xfer_cb = msch_xfer_cb,
- .close = msch_close
+ DRIVER_NAME("MSC")
+ .init = msch_init,
+ .open = msch_open,
+ .set_config = msch_set_config,
+ .xfer_cb = msch_xfer_cb,
+ .close = msch_close
},
- #endif
+ #endif
- #if CFG_TUH_HID
+ #if CFG_TUH_HID
{
- DRIVER_NAME("HID")
- .init = hidh_init,
- .open = hidh_open,
- .set_config = hidh_set_config,
- .xfer_cb = hidh_xfer_cb,
- .close = hidh_close
+ DRIVER_NAME("HID")
+ .init = hidh_init,
+ .open = hidh_open,
+ .set_config = hidh_set_config,
+ .xfer_cb = hidh_xfer_cb,
+ .close = hidh_close
},
- #endif
+ #endif
- #if CFG_TUH_HUB
+ #if CFG_TUH_HUB
{
- DRIVER_NAME("HUB")
- .init = hub_init,
- .open = hub_open,
- .set_config = hub_set_config,
- .xfer_cb = hub_xfer_cb,
- .close = hub_close
+ DRIVER_NAME("HUB")
+ .init = hub_init,
+ .open = hub_open,
+ .set_config = hub_set_config,
+ .xfer_cb = hub_xfer_cb,
+ .close = hub_close
},
- #endif
+ #endif
- #if CFG_TUH_VENDOR
+ #if CFG_TUH_VENDOR
{
DRIVER_NAME("VENDOR")
.init = cush_init,
@@ -173,7 +177,7 @@ static usbh_class_driver_t const usbh_class_drivers[] =
.xfer_cb = cush_isr,
.close = cush_close
}
- #endif
+ #endif
};
enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) };
@@ -233,8 +237,7 @@ static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE];
// Control transfers: since most controllers do not support multiple control transfers
// on multiple devices concurrently and control transfers are not used much except for
// enumeration, we will only execute control transfers one at a time.
-CFG_TUH_MEM_SECTION struct
-{
+CFG_TUH_MEM_SECTION struct {
CFG_TUH_MEM_ALIGN tusb_control_request_t request;
uint8_t* buffer;
tuh_xfer_cb_t complete_cb;
@@ -268,7 +271,7 @@ TU_ATTR_WEAK void osal_task_delay(uint32_t msec) {
TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
bool ret = osal_queue_send(_usbh_q, event, in_isr);
- if (tuh_event_hook_cb) tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
+ tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
return ret;
}
@@ -367,17 +370,14 @@ bool tuh_init(uint8_t controller_id) {
tu_memclr(_usbh_devices, sizeof(_usbh_devices));
tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
- for(uint8_t i=0; iname);
driver->init();
}
@@ -456,7 +456,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
#if CFG_TUH_HUB
// TODO remove
- if ( event.connection.hub_addr != 0) {
+ if ( event.connection.hub_addr != 0 && event.connection.hub_port != 0) {
// done with hub, waiting for next data on status pipe
(void) hub_edpt_status_xfer( event.connection.hub_addr );
}
@@ -545,8 +545,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
// Control transfer
//--------------------------------------------------------------------+
-static void _control_blocking_complete_cb(tuh_xfer_t* xfer)
-{
+static void _control_blocking_complete_cb(tuh_xfer_t* xfer) {
// update result
*((xfer_result_t*) xfer->user_data) = xfer->result;
}
@@ -625,21 +624,18 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
return true;
}
-TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage)
-{
+TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage) {
(void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
_ctrl_xfer.stage = stage;
(void) osal_mutex_unlock(_usbh_mutex);
}
-static void _xfer_complete(uint8_t daddr, xfer_result_t result)
-{
+static void _xfer_complete(uint8_t daddr, xfer_result_t result) {
TU_LOG_USBH("\r\n");
// duplicate xfer since user can execute control transfer within callback
tusb_control_request_t const request = _ctrl_xfer.request;
- tuh_xfer_t xfer_temp =
- {
+ tuh_xfer_t xfer_temp = {
.daddr = daddr,
.ep_addr = 0,
.result = result,
@@ -652,8 +648,7 @@ static void _xfer_complete(uint8_t daddr, xfer_result_t result)
_set_control_xfer_stage(CONTROL_STAGE_IDLE);
- if (xfer_temp.complete_cb)
- {
+ if (xfer_temp.complete_cb) {
xfer_temp.complete_cb(&xfer_temp);
}
}
@@ -710,17 +705,16 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
//
//--------------------------------------------------------------------+
-bool tuh_edpt_xfer(tuh_xfer_t* xfer)
-{
- uint8_t const daddr = xfer->daddr;
+bool tuh_edpt_xfer(tuh_xfer_t* xfer) {
+ uint8_t const daddr = xfer->daddr;
uint8_t const ep_addr = xfer->ep_addr;
TU_VERIFY(daddr && ep_addr);
TU_VERIFY(usbh_edpt_claim(daddr, ep_addr));
- if ( !usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen, xfer->complete_cb, xfer->user_data) )
- {
+ if (!usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen,
+ xfer->complete_cb, xfer->user_data)) {
usbh_edpt_release(daddr, ep_addr);
return false;
}
diff --git a/src/host/usbh.h b/src/host/usbh.h
index 7591c7672..9ff118543 100644
--- a/src/host/usbh.h
+++ b/src/host/usbh.h
@@ -94,7 +94,7 @@ TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr);
TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr);
// Invoked when there is a new usb event, which need to be processed by tuh_task()/tuh_task_ext()
-TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
+void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
//--------------------------------------------------------------------+
// APPLICATION API
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index 051b7eaa8..cc4799dd6 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -171,6 +171,8 @@ enum {
//--------------------------------------------------------------------+
typedef struct {
+ uint8_t daddr;
+
struct TU_ATTR_PACKED {
uint8_t ep_dir : 1;
uint8_t is_iso : 1;
@@ -179,17 +181,19 @@ typedef struct {
uint8_t xfer_pending : 1;
uint8_t xfer_complete : 1;
};
+
struct TU_ATTR_PACKED {
- uint8_t daddr : 4;
uint8_t ep_num : 4;
+ uint16_t packet_size : 12;
};
- uint16_t packet_size;
uint16_t total_len;
uint16_t xferred_len;
uint8_t* buf;
} max3421_ep_t;
+TU_VERIFY_STATIC(sizeof(max3421_ep_t) == 12, "size is not correct");
+
typedef struct {
// cached register
uint8_t sndbc;
@@ -320,7 +324,7 @@ static void fifo_read(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_is
static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr) {
reg_write(rhport, HIRQ_ADDR, data, in_isr);
// HIRQ write 1 is clear
- _hcd_data.hirq &= ~data;
+ _hcd_data.hirq &= (uint8_t) ~data;
}
static inline void hien_write(uint8_t rhport, uint8_t data, bool in_isr) {
@@ -390,7 +394,7 @@ static void free_ep(uint8_t daddr) {
}
static max3421_ep_t * find_next_pending_ep(max3421_ep_t * cur_ep) {
- size_t const idx = cur_ep - _hcd_data.ep;
+ size_t const idx = (size_t) (cur_ep - _hcd_data.ep);
// starting from next endpoint
for (size_t i = idx + 1; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
@@ -537,8 +541,8 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e
(void) rhport;
(void) daddr;
- uint8_t ep_num = tu_edpt_number(ep_desc->bEndpointAddress);
- uint8_t ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress);
+ uint8_t const ep_num = tu_edpt_number(ep_desc->bEndpointAddress);
+ tusb_dir_t const ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress);
max3421_ep_t * ep;
if (daddr == 0 && ep_num == 0) {
@@ -547,15 +551,15 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e
ep = allocate_ep();
TU_ASSERT(ep);
ep->daddr = daddr;
- ep->ep_num = ep_num;
- ep->ep_dir = ep_dir;
+ ep->ep_num = (uint8_t) (ep_num & 0x0f);
+ ep->ep_dir = (ep_dir == TUSB_DIR_IN) ? 1 : 0;
}
if ( TUSB_XFER_ISOCHRONOUS == ep_desc->bmAttributes.xfer ) {
ep->is_iso = 1;
}
- ep->packet_size = tu_edpt_packet_size(ep_desc);
+ ep->packet_size = (uint16_t) (tu_edpt_packet_size(ep_desc) & 0x7ff);
return true;
}
@@ -577,7 +581,7 @@ void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
}
sndbc_write(rhport, xact_len, in_isr);
- uint8_t hxfr = ep->ep_num | HXFR_OUT_NIN | (ep->is_iso ? HXFR_ISO : 0);
+ uint8_t const hxfr = (uint8_t ) (ep->ep_num | HXFR_OUT_NIN | (ep->is_iso ? HXFR_ISO : 0));
hxfr_write(rhport, hxfr, in_isr);
}
@@ -590,7 +594,7 @@ void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
reg_write(rhport, HCTL_ADDR, hctl, in_isr);
}
- uint8_t hxfr = ep->ep_num | (ep->is_iso ? HXFR_ISO : 0);
+ uint8_t const hxfr = (uint8_t) (ep->ep_num | (ep->is_iso ? HXFR_ISO : 0));
hxfr_write(rhport, hxfr, in_isr);
}
@@ -623,13 +627,13 @@ TU_ATTR_ALWAYS_INLINE static inline void xact_inout(uint8_t rhport, max3421_ep_t
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
uint8_t const ep_num = tu_edpt_number(ep_addr);
- uint8_t const ep_dir = tu_edpt_dir(ep_addr);
+ uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
TU_VERIFY(ep);
// control transfer can switch direction
- ep->ep_dir = ep_dir;
+ ep->ep_dir = ep_dir ? 1u : 0u;
ep->buf = buffer;
ep->total_len = buflen;
@@ -748,9 +752,9 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re
// save data toggle
if (ep->ep_dir) {
- ep->data_toggle = (hrsl & HRSL_RCVTOGRD) ? 1 : 0;
+ ep->data_toggle = (hrsl & HRSL_RCVTOGRD) ? 1u : 0u;
}else {
- ep->data_toggle = (hrsl & HRSL_SNDTOGRD) ? 1 : 0;
+ ep->data_toggle = (hrsl & HRSL_SNDTOGRD) ? 1u : 0u;
}
ep->xfer_pending = 0;
@@ -929,7 +933,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
}
// clear all interrupt except SNDBAV_IRQ (never clear by us). Note RCVDAV_IRQ, HXFRDN_IRQ already clear while processing
- hirq &= ~HIRQ_SNDBAV_IRQ;
+ hirq &= (uint8_t) ~HIRQ_SNDBAV_IRQ;
if ( hirq ) {
hirq_write(rhport, hirq, in_isr);
}
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c
index f978b0965..c59d4755e 100644
--- a/src/portable/ohci/ohci.c
+++ b/src/portable/ohci/ohci.c
@@ -157,6 +157,7 @@ static ohci_ed_t * const p_ed_head[] =
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed);
static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
+static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd);
//--------------------------------------------------------------------+
// USBH-HCD API
@@ -345,7 +346,7 @@ static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes)
tu_memclr(p_td, sizeof(ohci_gtd_t));
p_td->used = 1;
- p_td->expected_bytes = total_bytes;
+ gtd_get_extra_data(p_td)->expected_bytes = total_bytes;
p_td->buffer_rounding = 1; // less than queued length is not a error
p_td->delay_interrupt = OHCI_INT_ON_COMPLETE_NO;
@@ -610,6 +611,15 @@ static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd)
}
}
+static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd) {
+ if ( gtd_is_control(gtd) ) {
+ uint8_t idx = ((uintptr_t)gtd - (uintptr_t)&ohci_data.control->gtd) / sizeof(ohci_data.control[0]);
+ return &ohci_data.gtd_extra_control[idx];
+ }else {
+ return &ohci_data.gtd_extra[gtd - ohci_data.gtd_pool];
+ }
+}
+
static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer)
{
// 5.2.9 OHCI sample code
@@ -641,8 +651,7 @@ static void done_queue_isr(uint8_t hostid)
if ( (qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) )
{
ohci_ed_t * const ed = gtd_get_ed(qtd);
-
- uint32_t const xferred_bytes = qtd->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
+ uint32_t const xferred_bytes = gtd_get_extra_data(qtd)->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
// NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs.
// When there is a error resulting this ED is halted, and this EP still has other queued TD
@@ -651,7 +660,7 @@ static void done_queue_isr(uint8_t hostid)
// --> HC will not process Control list (due to service ratio when Bulk list not empty)
// To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt
// the TailP must be set back to NULL for processing remaining TDs
- if ((event != XFER_RESULT_SUCCESS))
+ if (event != XFER_RESULT_SUCCESS)
{
ed->td_tail &= 0x0Ful;
ed->td_tail |= tu_align16(ed->td_head.address); // mark halted EP as empty queue
diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h
index 2081ffabb..4feefd771 100644
--- a/src/portable/ohci/ohci.h
+++ b/src/portable/ohci/ohci.h
@@ -45,6 +45,9 @@ enum {
#define ED_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX)
#define GTD_MAX ED_MAX
+// tinyUSB's OHCI implementation caps number of EDs to 8 bits
+TU_VERIFY_STATIC (ED_MAX <= 256, "Reduce CFG_TUH_DEVICE_MAX or CFG_TUH_ENDPOINT_MAX");
+
//--------------------------------------------------------------------+
// OHCI Data Structure
//--------------------------------------------------------------------+
@@ -70,9 +73,8 @@ typedef struct TU_ATTR_ALIGNED(16)
{
// Word 0
uint32_t used : 1;
- uint32_t index : 4; // endpoint index the td belongs to, or device address in case of control xfer
- uint32_t expected_bytes : 13; // TODO available for hcd
-
+ uint32_t index : 8; // endpoint index the gtd belongs to, or device address in case of control xfer
+ uint32_t : 9; // can be used
uint32_t buffer_rounding : 1;
uint32_t pid : 2;
uint32_t delay_interrupt : 3;
@@ -152,9 +154,12 @@ typedef struct TU_ATTR_ALIGNED(32)
TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" );
+typedef struct {
+ uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits
+} gtd_extra_data_t;
+
// structure with member alignment required from large to small
-typedef struct TU_ATTR_ALIGNED(256)
-{
+typedef struct TU_ATTR_ALIGNED(256) {
ohci_hcca_t hcca;
ohci_ed_t bulk_head_ed; // static bulk head (dummy)
@@ -164,14 +169,17 @@ typedef struct TU_ATTR_ALIGNED(256)
struct {
ohci_ed_t ed;
ohci_gtd_t gtd;
- }control[CFG_TUH_DEVICE_MAX+CFG_TUH_HUB+1];
+ } control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
// ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32
ohci_ed_t ed_pool[ED_MAX];
ohci_gtd_t gtd_pool[GTD_MAX];
- volatile uint16_t frame_number_hi;
+ // extra data needed by TDs that can't fit in the TD struct
+ gtd_extra_data_t gtd_extra_control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
+ gtd_extra_data_t gtd_extra[GTD_MAX];
+ volatile uint16_t frame_number_hi;
} ohci_data_t;
//--------------------------------------------------------------------+
diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
index 4ed6d36bb..08aef9314 100644
--- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
@@ -27,7 +27,7 @@
#include "tusb_option.h"
-#if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUH_RPI_PIO_USB
+#if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUH_RPI_PIO_USB && !CFG_TUH_MAX3421
#include "pico.h"
#include "rp2040_usb.h"
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 21cd3da26..9c37f1f98 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -128,8 +128,8 @@
# define DCD_STM32_BTABLE_BASE 0U
#endif
-#ifndef DCD_STM32_BTABLE_LENGTH
-# define DCD_STM32_BTABLE_LENGTH (PMA_LENGTH - DCD_STM32_BTABLE_BASE)
+#ifndef DCD_STM32_BTABLE_SIZE
+# define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE)
#endif
/***************************************************
@@ -137,7 +137,7 @@
*/
TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware");
-TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_LENGTH))<=(PMA_LENGTH), "BTABLE does not fit in PMA RAM");
+TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM");
TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes");
//--------------------------------------------------------------------+
@@ -559,7 +559,7 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
// Must reset EP to NAK (in case it had been stalling) (though, maybe too late here)
pcd_set_ep_rx_status(USB,0u,USB_EP_RX_NAK);
pcd_set_ep_tx_status(USB,0u,USB_EP_TX_NAK);
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
dcd_event_setup_received(0, (uint8_t*)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true);
#else
// The setup_received function uses memcpy, so this must first copy the setup data into
@@ -673,13 +673,13 @@ void dcd_int_handler(uint8_t rhport) {
/* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */
if(int_status & USB_ISTR_SOF) {
- USB->ISTR &=~USB_ISTR_SOF;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
dcd_event_sof(0, USB->FNR & USB_FNR_FN, true);
}
if(int_status & USB_ISTR_RESET) {
// USBRST is start of reset.
- USB->ISTR &=~USB_ISTR_RESET;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET;
dcd_handle_bus_reset();
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
return; // Don't do the rest of the things here; perhaps they've been cleared?
@@ -697,7 +697,7 @@ void dcd_int_handler(uint8_t rhport) {
USB->CNTR &= ~USB_CNTR_LPMODE;
USB->CNTR &= ~USB_CNTR_FSUSP;
- USB->ISTR &=~USB_ISTR_WKUP;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP;
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
@@ -711,7 +711,7 @@ void dcd_int_handler(uint8_t rhport) {
USB->CNTR |= USB_CNTR_LPMODE;
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
- USB->ISTR &=~USB_ISTR_SUSP;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
@@ -724,7 +724,7 @@ void dcd_int_handler(uint8_t rhport) {
{
remoteWakeCountdown--;
}
- USB->ISTR &=~USB_ISTR_ESOF;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF;
}
}
@@ -786,7 +786,7 @@ static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length)
}
// Ensure allocated buffer is aligned
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
length = (length + 3) & ~0x03;
#else
length = (length + 1) & ~0x01;
@@ -798,7 +798,7 @@ static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length)
ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer
// Verify no overflow
- TU_ASSERT(ep_buf_ptr <= PMA_LENGTH, 0xFFFF);
+ TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF);
epXferCtl->pma_ptr = addr;
epXferCtl->pma_alloc_size = length;
@@ -1227,7 +1227,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
}
}
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes)
{
const uint8_t* srcVal = src;
@@ -1283,7 +1283,7 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui
__IO uint16_t *pdwVal;
srcVal = src;
- pdwVal = &pma[PMA_STRIDE*(dst>>1)];
+ pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)];
while (n--)
{
@@ -1291,7 +1291,7 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui
srcVal++;
temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)) ;
*pdwVal = temp2;
- pdwVal += PMA_STRIDE;
+ pdwVal += FSDEV_PMA_STRIDE;
srcVal++;
}
@@ -1323,7 +1323,7 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
// We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
// last lin byte will be combined with wrapped part
// To ensure PMA is always access aligned (dst aligned to 16 or 32 bit)
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
if((cnt_lin & 0x03) && cnt_wrap)
{
// Copy first linear part
@@ -1386,7 +1386,7 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
return true;
}
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes)
{
uint8_t* dstVal = dst;
@@ -1434,13 +1434,13 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t
__IO const uint16_t *pdwVal;
uint32_t temp;
- pdwVal = &pma[PMA_STRIDE*(src>>1)];
+ pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)];
uint8_t *dstVal = (uint8_t*)dst;
while (n--)
{
temp = *pdwVal;
- pdwVal += PMA_STRIDE;
+ pdwVal += FSDEV_PMA_STRIDE;
*dstVal++ = ((temp >> 0) & 0xFF);
*dstVal++ = ((temp >> 8) & 0xFF);
}
@@ -1448,7 +1448,7 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t
if (wNBytes & 0x01)
{
temp = *pdwVal;
- pdwVal += PMA_STRIDE;
+ pdwVal += FSDEV_PMA_STRIDE;
*dstVal++ = ((temp >> 0) & 0xFF);
}
return true;
@@ -1475,7 +1475,7 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNB
// We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part,
// last lin byte will be combined with wrapped part
// To ensure PMA is always access aligned (src aligned to 16 or 32 bit)
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
if((cnt_lin & 0x03) && cnt_wrap)
{
// Copy first linear part
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
index b71e4f498..3f4db985d 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
@@ -28,7 +28,7 @@
// This file contains source copied from ST's HAL, and thus should have their copyright statement.
-// PMA_LENGTH is PMA buffer size in bytes.
+// FSDEV_PMA_SIZE is PMA buffer size in bytes.
// On 512-byte devices, access with a stride of two words (use every other 16-bit address)
// On 1024-byte devices, access with a stride of one word (use every 16-bit address)
@@ -37,7 +37,7 @@
#if CFG_TUSB_MCU == OPT_MCU_STM32F0
#include "stm32f0xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
// F0x2 models are crystal-less
// All have internal D+ pull-up
// 070RB: 2 x 16 bits/word memory LPM Support, BCD Support
@@ -45,7 +45,7 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
#include "stm32f1xx.h"
- #define PMA_LENGTH (512u)
+ #define FSDEV_PMA_SIZE (512u)
// NO internal Pull-ups
// *B, and *C: 2 x 16 bits/word
@@ -56,7 +56,7 @@
defined(STM32F303xB) || defined(STM32F303xC) || \
defined(STM32F373xC)
#include "stm32f3xx.h"
- #define PMA_LENGTH (512u)
+ #define FSDEV_PMA_SIZE (512u)
// NO internal Pull-ups
// *B, and *C: 1 x 16 bits/word
// PMA dedicated to USB (no sharing with CAN)
@@ -65,27 +65,27 @@
defined(STM32F302xD) || defined(STM32F302xE) || \
defined(STM32F303xD) || defined(STM32F303xE)
#include "stm32f3xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
// NO internal Pull-ups
// *6, *8, *D, and *E: 2 x 16 bits/word LPM Support
// When CAN clock is enabled, USB can use first 768 bytes ONLY.
#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
#include "stm32l0xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
#include "stm32l1xx.h"
- #define PMA_LENGTH (512u)
+ #define FSDEV_PMA_SIZE (512u)
#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
#include "stm32g4xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
#include "stm32g0xx.h"
- #define PMA_32BIT_ACCESS
- #define PMA_LENGTH (2048u)
+ #define FSDEV_BUS_32BIT
+ #define FSDEV_PMA_SIZE (2048u)
#undef USB_PMAADDR
#define USB_PMAADDR USB_DRD_PMAADDR
#define USB_TypeDef USB_DRD_TypeDef
@@ -112,8 +112,8 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
#include "stm32h5xx.h"
- #define PMA_32BIT_ACCESS
- #define PMA_LENGTH (2048u)
+ #define FSDEV_BUS_32BIT
+ #define FSDEV_PMA_SIZE (2048u)
#undef USB_PMAADDR
#define USB_PMAADDR USB_DRD_PMAADDR
#define USB_TypeDef USB_DRD_TypeDef
@@ -141,18 +141,18 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
#include "stm32wbxx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
/* ST provided header has incorrect value */
#undef USB_PMAADDR
#define USB_PMAADDR USB1_PMAADDR
#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
#include "stm32l4xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
#include "stm32l5xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
#ifndef USB_PMAADDR
#define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
@@ -164,24 +164,28 @@
#endif
// For purposes of accessing the packet
-#if ((PMA_LENGTH) == 512u)
- #define PMA_STRIDE (2u)
-#elif ((PMA_LENGTH) == 1024u)
- #define PMA_STRIDE (1u)
+#if ((FSDEV_PMA_SIZE) == 512u)
+ #define FSDEV_PMA_STRIDE (2u)
+#elif ((FSDEV_PMA_SIZE) == 1024u)
+ #define FSDEV_PMA_STRIDE (1u)
#endif
+// The fsdev_bus_t type can be used for both register and PMA access necessities
// For type-safety create a new macro for the volatile address of PMAADDR
// The compiler should warn us if we cast it to a non-volatile type?
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
+typedef uint32_t fsdev_bus_t;
static __IO uint32_t * const pma32 = (__IO uint32_t*)USB_PMAADDR;
+
#else
+typedef uint16_t fsdev_bus_t;
// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden)
static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR;
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x)
{
size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
- total_word_offset *= PMA_STRIDE;
+ total_word_offset *= FSDEV_PMA_STRIDE;
return &(pma[total_word_offset]);
}
@@ -212,7 +216,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t si
/* SetENDPOINT */
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
__O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4);
*reg = wRegValue;
@@ -224,7 +228,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, ui
/* GetENDPOINT */
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
__I uint32_t *reg = (__I uint32_t *)(USB_DRD_BASE + bEpIdx*4);
#else
@@ -279,7 +283,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx,
*/
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
return (pma32[2*bEpIdx] & 0x03FF0000) >> 16;
#else
@@ -290,7 +294,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USB
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16;
#else
@@ -317,7 +321,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx,
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
return pma32[2*bEpIdx] & 0x0000FFFFu ;
#else
@@ -327,7 +331,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef *
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
return pma32[2*bEpIdx + 1] & 0x0000FFFFu;
#else
@@ -337,7 +341,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef *
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
#else
@@ -347,7 +351,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USB
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
#else
@@ -357,7 +361,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USB
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
#else
@@ -368,7 +372,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, u
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
#else
@@ -380,7 +384,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, u
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t blocksize, uint32_t numblocks)
{
/* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26);
#else
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index ac93632be..3e15d51c6 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -1130,8 +1130,8 @@ void dcd_int_handler(uint8_t rhport) {
dwc2->gotgint = otg_int;
}
- if (int_status & GINTSTS_SOF) {
- dwc2->gotgint = GINTSTS_SOF;
+ if(int_status & GINTSTS_SOF) {
+ dwc2->gintsts = GINTSTS_SOF;
if (_sof_en) {
uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8;
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index dd78ccd06..3237a50f6 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -149,7 +149,7 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
// https://community.st.com/t5/stm32cubemx-mcus/why-stm32h743-usb-fs-doesn-t-work-if-freertos-tickless-idle/m-p/349480#M18867
// H7 running on full-speed phy need to disable ULPI clock in sleep mode.
// Otherwise, USB won't work when mcu executing WFI/WFE instruction i.e tick-less RTOS.
- // Note: there may be other family that is affected by this, but only H7 is tested so far
+ // Note: there may be other family that is affected by this, but only H7 and F7 is tested so far
#if defined(USB_OTG_FS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB2OTGFSULPILPEN)
if ( USB_OTG_FS_PERIPH_BASE == (uint32_t) dwc2 ) {
RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB2OTGFSULPILPEN;
@@ -161,6 +161,13 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB1OTGHSULPILPEN;
}
#endif
+
+ #if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_OTGHSULPILPEN)
+ if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) {
+ RCC->AHB1LPENR &= ~RCC_AHB1LPENR_OTGHSULPILPEN;
+ }
+ #endif
+
} else {
#if CFG_TUSB_MCU != OPT_MCU_STM32U5
// Disable FS PHY, TODO on U5A5 (dwc2 4.11a) 16th bit is 'Host CDP behavior enable'
diff --git a/src/tusb_option.h b/src/tusb_option.h
index a76281c0c..767323bdd 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -453,10 +453,12 @@
#define CFG_TUH_CDC_FTDI 0
#endif
-#ifndef CFG_TUH_CDC_FTDI_PID_LIST
- // List of product IDs that can use the FTDI CDC driver
- #define CFG_TUH_CDC_FTDI_PID_LIST \
- 0x6001, 0x6006, 0x6010, 0x6011, 0x6014, 0x6015, 0x8372, 0xFBFA, 0xCD18
+#ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST
+ // List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID
+ #define CFG_TUH_CDC_FTDI_VID_PID_LIST \
+ {0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \
+ {0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \
+ {0x0403, 0xCD18}
#endif
#ifndef CFG_TUH_CDC_CP210X
@@ -464,10 +466,27 @@
#define CFG_TUH_CDC_CP210X 0
#endif
-#ifndef CFG_TUH_CDC_CP210X_PID_LIST
- // List of product IDs that can use the CP210X CDC driver
- #define CFG_TUH_CDC_CP210X_PID_LIST \
- 0xEA60, 0xEA70
+#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST
+ // List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
+ #define CFG_TUH_CDC_CP210X_VID_PID_LIST \
+ {0x10C4, 0xEA60}, {0x10C4, 0xEA70}
+#endif
+
+#ifndef CFG_TUH_CDC_CH34X
+ // CH34X is not part of CDC class, only to re-use CDC driver API
+ #define CFG_TUH_CDC_CH34X 0
+#endif
+
+#ifndef CFG_TUH_CDC_CH34X_VID_PID_LIST
+ // List of product IDs that can use the CH34X CDC driver
+ #define CFG_TUH_CDC_CH34X_VID_PID_LIST \
+ { 0x1a86, 0x5523 }, /* ch341 chip */ \
+ { 0x1a86, 0x7522 }, /* ch340k chip */ \
+ { 0x1a86, 0x7523 }, /* ch340 chip */ \
+ { 0x1a86, 0xe523 }, /* ch330 chip */ \
+ { 0x4348, 0x5523 }, /* ch340 custom chip */ \
+ { 0x2184, 0x0057 }, /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ \
+ { 0x9986, 0x7523 } /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */
#endif
#ifndef CFG_TUH_HID
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index 7c662aeea..7f03ce43f 100644
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -364,8 +364,15 @@ def main(config_file, board):
print(f' {test} ...', end='')
- # flash firmware
- ret = globals()[f'flash_{flasher}'](item, fw)
+ # flash firmware. It may fail randomly, retry a few times
+ for i in range(3):
+ ret = globals()[f'flash_{flasher}'](item, fw)
+ if ret.returncode == 0:
+ break
+ else:
+ print(f'Flashing failed, retry {i+1}')
+ time.sleep(1)
+
assert ret.returncode == 0, 'Flash failed\n' + ret.stdout.decode()
# run test