From d92b810df7670aae0e195fd02abef5014470c2f7 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 28 Oct 2025 13:21:38 +0700 Subject: fix Identifiers that start with '__' or '_[A-Z]' are reserved. fix compiling with nuc family --- src/tusb.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/tusb.h') diff --git a/src/tusb.h b/src/tusb.h index 6a469eef4..62b3b9783 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _TUSB_H_ -#define _TUSB_H_ +#ifndef TUSB_H_ +#define TUSB_H_ #ifdef __cplusplus extern "C" { @@ -178,4 +178,4 @@ bool tusb_deinit(uint8_t rhport); } #endif -#endif /* _TUSB_H_ */ +#endif /* TUSB_H_ */ -- cgit v1.3.1 From cab1b2f6f741ea151e87f139b2115d5234228bbd Mon Sep 17 00:00:00 2001 From: Rémi Berthoz Date: Sun, 4 Jan 2026 16:55:43 +0100 Subject: Implement Printer Device Class --- examples/device/printer_to_hid/CMakeLists.txt | 29 ++ examples/device/printer_to_hid/CMakePresets.json | 6 + examples/device/printer_to_hid/Makefile | 11 + examples/device/printer_to_hid/README.md | 0 examples/device/printer_to_hid/src/main.c | 219 +++++++++++++++ examples/device/printer_to_hid/src/tusb_config.h | 115 ++++++++ .../device/printer_to_hid/src/usb_descriptors.c | 247 +++++++++++++++++ .../device/printer_to_hid/src/usb_descriptors.h | 30 ++ src/CMakeLists.txt | 1 + src/class/printer/printer.h | 277 +++++++++++++++++++ src/class/printer/printer_device.c | 306 +++++++++++++++++++++ src/class/printer/printer_device.h | 79 ++++++ src/device/usbd.c | 13 + src/device/usbd.h | 15 + src/tinyusb.mk | 1 + src/tusb.h | 4 + src/tusb_option.h | 4 + tools/iar_template.ipcf | 5 + 18 files changed, 1362 insertions(+) create mode 100644 examples/device/printer_to_hid/CMakeLists.txt create mode 100644 examples/device/printer_to_hid/CMakePresets.json create mode 100644 examples/device/printer_to_hid/Makefile create mode 100644 examples/device/printer_to_hid/README.md create mode 100644 examples/device/printer_to_hid/src/main.c create mode 100644 examples/device/printer_to_hid/src/tusb_config.h create mode 100644 examples/device/printer_to_hid/src/usb_descriptors.c create mode 100644 examples/device/printer_to_hid/src/usb_descriptors.h create mode 100644 src/class/printer/printer.h create mode 100644 src/class/printer/printer_device.c create mode 100644 src/class/printer/printer_device.h (limited to 'src/tusb.h') diff --git a/examples/device/printer_to_hid/CMakeLists.txt b/examples/device/printer_to_hid/CMakeLists.txt new file mode 100644 index 000000000..f58759059 --- /dev/null +++ b/examples/device/printer_to_hid/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.20) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +project(printer_to_hid C CXX ASM) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}) +# Espressif has its own cmake build system +if(FAMILY STREQUAL "espressif") + return() +endif() + +add_executable(${PROJECT_NAME}) + +# Example source +target_sources(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example without RTOS. +# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT_NAME} noos) diff --git a/examples/device/printer_to_hid/CMakePresets.json b/examples/device/printer_to_hid/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/printer_to_hid/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/printer_to_hid/Makefile b/examples/device/printer_to_hid/Makefile new file mode 100644 index 000000000..1a4b428dc --- /dev/null +++ b/examples/device/printer_to_hid/Makefile @@ -0,0 +1,11 @@ +include ../../../hw/bsp/family_support.mk + +INC += \ + src \ + + +# Example source +EXAMPLE_SOURCE += $(wildcard src/*.c) +SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../../hw/bsp/family_rules.mk diff --git a/examples/device/printer_to_hid/README.md b/examples/device/printer_to_hid/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/examples/device/printer_to_hid/src/main.c b/examples/device/printer_to_hid/src/main.c new file mode 100644 index 000000000..f5012478c --- /dev/null +++ b/examples/device/printer_to_hid/src/main.c @@ -0,0 +1,219 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 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. + * + */ + +#include +#include +#include + +#include "bsp/board_api.h" +#include "tusb.h" + +#include "usb_descriptors.h" + +// -------------------------------------------------------------------+ +// Variables controlled with USB endpoint callbacks +// -------------------------------------------------------------------+ + +// usb interface pointer +uint8_t printer_itf = 0; +// pendings bytes in usb endpoint buffer ; must process these bytes +uint8_t pending_bytes_on_usb_ep = 0; + + +// -------------------------------------------------------------------+ +// Variables controlled locally +// -------------------------------------------------------------------+ + +// local data buffer (copy data from usb endpoint into this buffer) ; acts as fifo +uint8_t data_buffer[16] = {0}; +// write offset for usb/printer incoming data to data_buffer +size_t data_rx_offset = 0; +// read offset for usb/hid outgoing data from data_buffer +size_t data_tx_offset = 0; +// available space in data_buffer +size_t data_available = sizeof(data_buffer); + +// next keycode to send on usb/hid +uint8_t next_keycode = 0; +// next key modifiers to send on usb/hid +uint8_t next_modifiers = 0; +// whether the next usb/hid report must be NULL to release the last keystroke +uint8_t next_keycode_is_release = false; + + +// -------------------------------------------------------------------+ +// Tasks +// -------------------------------------------------------------------+ + +// Every 10ms, we will place HID data in the usb/hid endpoint, ready to +// sent to the host when required. The tasks will read the keycode in +// next_keycode and place it in the HID report, then set next_is_null +// such that the key is released by the next report. This seem to help +// stroking the same key twice when the character is repeated in the data. +void hid_tx_task(void) { + // Poll every 10ms + const uint32_t interval_ms = 10; + static uint32_t start_ms = 0; + + if (!tud_hid_ready()) { + return; + } + + if (board_millis() - start_ms < interval_ms) { + return; // not enough time + } + start_ms += interval_ms; + + if (next_keycode_is_release || next_keycode == 0) { + tud_hid_keyboard_report(1, 0, NULL); + next_keycode_is_release = false; + return; + } + + uint8_t keycode_array[6] = {0}; + keycode_array[0] = next_keycode; + tud_hid_keyboard_report(1, next_modifiers, keycode_array); + next_keycode_is_release = true; + next_keycode = 0; +} + +// Whenever there are pendings bytes on the USB endpoint, we will pull them from the +// endpoint buffer and write then in the local data buffer. We must take care to not +// overwrite local data that is not processed yet, so we use data_buffer as a fifo. +// We do not have to take care of reading correctly from the endpoint buffer, as all +// is done well by tud_printer_n_read(). +void printer_rx_task(void) { + if (pending_bytes_on_usb_ep > 0) { + size_t len1 = data_available; + size_t len2 = 0; + if (len1 < 0) { + len1 = 0; + } + if (data_rx_offset + len1 > sizeof(data_buffer)) { + len2 = len1 - (sizeof(data_buffer) - data_rx_offset); + len1 = sizeof(data_buffer) - data_rx_offset; + } + uint32_t count = tud_printer_n_read(printer_itf, data_buffer + data_rx_offset, len1); + if (len2 > 0) { + count += tud_printer_n_read(printer_itf, data_buffer, len2); + } + + if (count > 0) { + data_available -= count; + pending_bytes_on_usb_ep -= count; + data_rx_offset = (data_rx_offset + count) % sizeof(data_buffer); + } + } +} + +// The HID keycodes are not binary mapped like UTF8 codes. If we want to send the +// data received as a usb/printer, we have to translate the binary data for the +// usb/hid interface. Note that the simple mapping below will be valid only for +// hosts expecting hid data from a QWERTY keyboard. Also note that only a-zA-Z0-9 +// characters are converted, for simplicity of the example. Other characters are +// converted to spaces. +void translation_task(void) { + if (data_tx_offset != data_rx_offset || data_available == 0) { + // If data_tx_offset and data_rx_offset have different values, then we + // can proceed: translate, prepare for TX, and advance data_tx_offset. + // + // If the buffer is full (data_available == 0), then we must also + // translate data and prepare it for TX. But the data_tx_offset and + // data_rx_offset will have the same value, since RX caught up to TX. + // Hence the OR. + + // Translate UTF8 to HID keystroke + char c = data_buffer[data_tx_offset]; + uint8_t m = 0; + if ('a' <= c && c <= 'z') { + c -= 'a'; + c += HID_KEY_A; + } else if ('A' <= c && c <= 'Z') { + c -= 'a'; + c += HID_KEY_A; + m = KEYBOARD_MODIFIER_LEFTSHIFT; + } else if ('1' <= c && c <= '9') { + c -= '1'; + c += HID_KEY_1; + } else if (c == '0') { + c = HID_KEY_0; + } else { + c = HID_KEY_SPACE; + } + + // Proceed only if there are no characters pending for TX + if (next_keycode == 0) { + // Prepare next keystroke with translated data + next_keycode = c; + next_modifiers = m; + // Increment read offset + data_tx_offset += 1; + data_tx_offset %= sizeof(data_buffer); + data_available += 1; + } + } +} + +int main(void) { + + board_init(); + tud_init(BOARD_TUD_RHPORT); // init device stack on configured roothub port + if (board_init_after_tusb) { + board_init_after_tusb(); + } + + while (1) { + tud_task(); // tinyusb device task + printer_rx_task(); // read data sent by host on our printer interface + translation_task(); // translate printer's UTF8 to HID keycodes + hid_tx_task(); // send data to host with our HID interface + } +} + + +//--------------------------------------------------------------------+ +// Printer callbacks +//--------------------------------------------------------------------+ + +// Data was received on endpoint buffer +void tud_printer_rx_cb(uint8_t itf, size_t n) { + printer_itf = itf; // get interface from which to read endpoint buffer + pending_bytes_on_usb_ep += n; // count pending bytes, counter must decrement when reading from the endpoint buffer +} + + +//--------------------------------------------------------------------+ +// HID callbacks +//--------------------------------------------------------------------+ + +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, + uint16_t reqlen) { + return 0; +} + +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, + uint16_t bufsize) { + return; +} diff --git a/examples/device/printer_to_hid/src/tusb_config.h b/examples/device/printer_to_hid/src/tusb_config.h new file mode 100644 index 000000000..a8dbac89d --- /dev/null +++ b/examples/device/printer_to_hid/src/tusb_config.h @@ -0,0 +1,115 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 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. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +#define BOARD_DEVICE_RHPORT_NUM 3 + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT + #define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED + #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS + #define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG + #define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_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. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION + #define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN + #define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE + #define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID 1 +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 +#define CFG_TUD_PRINTER 1 + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 16 + +// Printer buffer size Should be sufficient to hold data +#define CFG_TUD_PRINTER_RX_BUFSIZE 16 +#define CFG_TUD_PRINTER_TX_BUFSIZE 16 +#define CFG_TUD_PRINTER_EP_BUFSIZE 16 + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/printer_to_hid/src/usb_descriptors.c b/examples/device/printer_to_hid/src/usb_descriptors.c new file mode 100644 index 000000000..161bbb7c5 --- /dev/null +++ b/examples/device/printer_to_hid/src/usb_descriptors.c @@ -0,0 +1,247 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 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. + * + */ + +#include "bsp/board_api.h" +#include "tusb.h" +#include "usb_descriptors.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ +#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0) +#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \ + PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) ) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +static tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .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; +} + +//--------------------------------------------------------------------+ +// HID Report Descriptor +//--------------------------------------------------------------------+ + +uint8_t const desc_hid_report[] = +{ + TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD )) +}; + +// Invoked when received GET HID REPORT DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance) +{ + (void) instance; + return desc_hid_report; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum +{ + ITF_NUM_HID, + ITF_NUM_PRINTER, + ITF_NUM_TOTAL +}; + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) + +// HID interface endpoints +#define EPADDR_HID 0x81 // Interrupt In, MSB must be 1 +// Printer interface endpoints +#define EPADDR_PRINTER_OUT 0x01 // Bulk Out, MSB must be 0 +#define EPADDR_PRINTER_IN 0x82 // Bulk In, MSB must be 1 + +uint8_t const desc_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // HID: + // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval + TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPADDR_HID, CFG_TUD_HID_EP_BUFSIZE, 5), + + // Printer: + // Interface number, string index, EP Bulk Out address, EP Bulk In address, EP size + TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 0, EPADDR_PRINTER_OUT, EPADDR_PRINTER_IN, CFG_TUD_PRINTER_EP_BUFSIZE) +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration + +// other speed configuration +static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +static tusb_desc_device_qualifier_t const desc_device_qualifier = +{ + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + + .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 + + // other speed config is basically configuration with type = OTHER_SPEED_CONFIG + memcpy(desc_other_speed_config, desc_configuration, CONFIG_TOTAL_LEN); + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + // this example use the same configuration for both high and full speed mode + return desc_other_speed_config; +} + +#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) +{ + (void) index; // for multiple configurations + + // This example use the same configuration for both high and full speed mode + return desc_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// String Descriptor Index +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, +}; + +// array of pointer to string descriptors +static 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 +}; + +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) { + (void) langid; + size_t chr_count; + + switch ( index ) { + case STRID_LANGID: + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + + case STRID_SERIAL: + chr_count = board_usb_get_serial(_desc_str + 1, 32); + break; + + default: + // 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; + + 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; + + // Convert ASCII string into UTF-16 + for ( size_t i = 0; i < chr_count; i++ ) { + _desc_str[1 + i] = str[i]; + } + break; + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/examples/device/printer_to_hid/src/usb_descriptors.h b/examples/device/printer_to_hid/src/usb_descriptors.h new file mode 100644 index 000000000..53c4e1fbd --- /dev/null +++ b/examples/device/printer_to_hid/src/usb_descriptors.h @@ -0,0 +1,30 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 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. + */ + +#ifndef USB_DESCRIPTORS_H_ +#define USB_DESCRIPTORS_H_ + +REPORT_ID_KEYBOARD = 1 + +#endif /* USB_DESCRIPTORS_H_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 48dc75e50..00f466007 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,7 @@ function(tinyusb_sources_get OUTPUT_VAR) ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/mtp/mtp_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ecm_rndis_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ncm_device.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/printer/printer_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/usbtmc/usbtmc_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/video/video_device.c diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h new file mode 100644 index 000000000..4cd1d62fc --- /dev/null +++ b/src/class/printer/printer.h @@ -0,0 +1,277 @@ +#include +#include "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_PRINTER) + + #include "device/usbd.h" + #include "device/usbd_pvt.h" + +// #include "bsp/board_api.h" + + #include "printer_device.h" + #include "printer.h" + + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +typedef struct { + uint8_t itf_num; + uint8_t ep_in; + uint8_t ep_out; + /*------------- From this point, data is not cleared by bus reset -------------*/ + + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + + uint8_t rx_ff_buf[CFG_TUD_PRINTER_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_PRINTER_TX_BUFSIZE]; + + OSAL_MUTEX_DEF(rx_ff_mutex); + OSAL_MUTEX_DEF(tx_ff_mutex); +} printer_interface_t; + + #define ITF_MEM_RESET_SIZE offsetof(printer_interface_t, wanted_char) + +typedef struct { + TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_EP_BUFSIZE); + TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_EP_BUFSIZE); +} printer_epbuf_t; + +static printer_interface_t _printer_itf[CFG_TUD_PRINTER]; +CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; + + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +static tud_printer_configure_fifo_t _printer_fifo_cfg; + +static bool _prep_out_transaction(uint8_t itf) { + const uint8_t rhport = 0; + printer_interface_t *p_printer = &_printer_itf[itf]; + printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; + + // Skip if usb is not ready yet + TU_VERIFY(tud_ready() && p_printer->ep_out); + + uint16_t available = tu_fifo_remaining(&p_printer->rx_ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= CFG_TUD_PRINTER_EP_BUFSIZE); + + // claim endpoint + TU_VERIFY(usbd_edpt_claim(rhport, p_printer->ep_out)); + + // fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&p_printer->rx_ff); + + if (available >= CFG_TUD_PRINTER_EP_BUFSIZE) { + return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE); + } else { + // Release endpoint since we don't make any transfer + usbd_edpt_release(rhport, p_printer->ep_out); + return false; + } +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ + +uint32_t tud_printer_n_available(uint8_t itf) { + return tu_fifo_count(&_printer_itf[itf].rx_ff); +} + +uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize) { + printer_interface_t *p_printer = &_printer_itf[itf]; + uint32_t num_read = tu_fifo_read_n(&p_printer->rx_ff, buffer, (uint16_t)TU_MIN(bufsize, UINT16_MAX)); + _prep_out_transaction(itf); + return num_read; +} + +bool tud_printer_n_peek(uint8_t itf, uint8_t *chr) { + return tu_fifo_peek(&_printer_itf[itf].rx_ff, chr); +} + +void tud_printer_n_read_flush(uint8_t itf) { + printer_interface_t *p_printer = &_printer_itf[itf]; + tu_fifo_clear(&p_printer->rx_ff); + _prep_out_transaction(itf); +} + + +//--------------------------------------------------------------------+ +// USBD PRINTER DRIVER API +//--------------------------------------------------------------------+ + +void printer_init(void) { + tu_memclr(_printer_itf, sizeof(_printer_itf)); + tu_memclr(&_printer_fifo_cfg, sizeof(_printer_fifo_cfg)); + + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + + tu_fifo_config(&p_printer->rx_ff, p_printer->rx_ff_buf, TU_ARRAY_SIZE(p_printer->rx_ff_buf), 1, false); + tu_fifo_config(&p_printer->tx_ff, p_printer->tx_ff_buf, TU_ARRAY_SIZE(p_printer->tx_ff_buf), 1, true); + + #if OSAL_MUTEX_REQUIRED + osal_mutex_t mutex_rd = osal_mutex_create(&p_printer->rx_ff_mutex); + osal_mutex_t mutex_wr = osal_mutex_create(&p_printer->tx_ff_mutex); + TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, ); + + tu_fifo_config_mutex(&p_printer->rx_ff, NULL, mutex_rd); + tu_fifo_config_mutex(&p_printer->tx_ff, mutex_wr, NULL); + #endif + } +} + +bool printer_deinit(void) { + #if OSAL_MUTEX_REQUIRED + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + osal_mutex_t mutex_rd = p_printer->rx_ff.mutex_rd; + osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_rd; + + if (mutex_rd) { + osal_mutex_delete(mutex_rd); + tu_fifo_config_mutex(&p_printer->rx_ff, NULL, NULL); + } + + if (mutex_wr) { + osal_mutex_delete(mutex_wr); + tu_fifo_config_mutex(&p_printer->tx_ff, NULL, NULL); + } + } + #endif + + return true; +} + +void printer_reset(uint8_t rhport) { + (void)rhport; + + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + + tu_memclr(p_printer, sizeof(p_printer)); + if (!_printer_fifo_cfg.rx_persistent) { + tu_fifo_clear(&p_printer->rx_ff); + } + if (!_printer_fifo_cfg.tx_persistent) { + tu_fifo_clear(&p_printer->tx_ff); + } + // tu_fifo_set_overwritable(&p_printer->rx_ff, true); + tu_fifo_set_overwritable(&p_printer->tx_ff, true); + } +} + +uint16_t printer_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { + TU_VERIFY(TUSB_CLASS_PRINTER == itf_desc->bInterfaceClass, 0); + + // Identify available interface to open + printer_interface_t *p_printer; + uint8_t printer_id; + for (printer_id = 0; printer_id < CFG_TUD_PRINTER; printer_id++) { + p_printer = &_printer_itf[printer_id]; + if (p_printer->ep_out == 0) { + break; + } + } + TU_ASSERT(printer_id < CFG_TUD_PRINTER); + + //------------- Interface -------------// + uint16_t drv_len = sizeof(tusb_desc_interface_t); + + //------------- Endpoints -------------// + TU_ASSERT(itf_desc->bNumEndpoints == 2); + drv_len += 2 * sizeof(tusb_desc_endpoint_t); + p_printer->itf_num = 2; + const uint8_t *p_desc = tu_desc_next(itf_desc); + TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_printer->ep_out, &p_printer->ep_in), 0); + + _prep_out_transaction(printer_id); + + return drv_len; +} + +bool printer_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) { + TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); + + if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { + //------------- STD Request -------------// + if (stage != CONTROL_STAGE_SETUP) { + return true; + } + } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { + switch (request->bRequest) { + // https://www.usb.org/sites/default/files/usbprint11a021811.pdf + case PRINTER_REQ_CONTROL_GET_DEVICE_ID: + if (stage == CONTROL_STAGE_SETUP) { + const char deviceId[] = "MANUFACTURER:ACME Manufacturing;" + "MODEL:LaserBeam 9;" + "COMMAND SET:PS;" + "COMMENT:Anything you like;" + "ACTIVE COMMAND SET:PS;"; + char buffer[256]; + strcpy(buffer + 2, deviceId); + buffer[0] = 0x00; + buffer[1] = strlen(deviceId); + return tud_control_xfer(rhport, request, buffer, strlen(deviceId) + 2); + } + break; + case PRINTER_REQ_CONTROL_GET_PORT_STATUS: + if (stage == CONTROL_STAGE_SETUP) { + static uint8_t port_status = (0 << 3) | (1 << 1) | (1 << 2); // ~Paper empty + Selected + NoError + return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); + } + break; + case PRINTER_REQ_CONTROL_SOFT_RESET: + if (stage == CONTROL_STAGE_SETUP) { + return false; // what to do ? + } + break; + default: + return false; + } + } else { + return false; + } + return true; +} + +bool printer_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + uint8_t itf; + printer_interface_t *p_printer; + + // Identify which interface to use + for (itf = 0; itf < CFG_TUD_PRINTER; itf++) { + p_printer = &_printer_itf[itf]; + if (ep_addr == p_printer->ep_out) { + break; + } + } + TU_ASSERT(itf < CFG_TUD_PRINTER); + printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; + + // Received new data + if (ep_addr == p_printer->ep_out) { + tu_fifo_write_n(&p_printer->rx_ff, p_epbuf->epout, (uint16_t)xferred_bytes); + // invoke receive callback (if there is still data) + if (tud_printer_rx_cb && !tu_fifo_empty(&p_printer->rx_ff)) { + tud_printer_rx_cb(itf, xferred_bytes); + } + // prepare for OUT transaction + _prep_out_transaction(itf); + } + + return true; +} + +#endif diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c new file mode 100644 index 000000000..efeae100f --- /dev/null +++ b/src/class/printer/printer_device.c @@ -0,0 +1,306 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 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. + */ + +#include "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_PRINTER) + + //--------------------------------------------------------------------+ + // INCLUDE + //--------------------------------------------------------------------+ + #include "device/usbd.h" + #include "device/usbd_pvt.h" + + #include "printer_device.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +typedef struct { + uint8_t itf_num; + uint8_t ep_out; // Bulk Out endpoint + uint8_t ep_in; // optional Bulk In endpoint + + /*------------- From this point, data is not cleared by bus reset -------------*/ + + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + + uint8_t rx_ff_buf[CFG_TUD_PRINTER_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_PRINTER_TX_BUFSIZE]; + + OSAL_MUTEX_DEF(rx_ff_mutex); + OSAL_MUTEX_DEF(tx_ff_mutex); +} printer_interface_t; + +typedef struct { + TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_EP_BUFSIZE); + TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_EP_BUFSIZE); +} printer_epbuf_t; + +static printer_interface_t _printer_itf[CFG_TUD_PRINTER]; +CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; + + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +static tud_printer_configure_fifo_t _printer_fifo_cfg; + +static bool _prep_out_transaction(uint8_t itf) { + const uint8_t rhport = 0; + printer_interface_t *p_printer = &_printer_itf[itf]; + printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; + + // Skip if usb is not ready yet + TU_VERIFY(tud_ready() && p_printer->ep_out); + + uint16_t available = tu_fifo_remaining(&p_printer->rx_ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= CFG_TUD_PRINTER_EP_BUFSIZE); + + // claim endpoint + TU_VERIFY(usbd_edpt_claim(rhport, p_printer->ep_out)); + + // fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&p_printer->rx_ff); + + if (available >= CFG_TUD_PRINTER_EP_BUFSIZE) { + return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE); + } else { + // Release endpoint since we don't make any transfer + usbd_edpt_release(rhport, p_printer->ep_out); + return false; + } +} + +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf, size_t n) { + (void)itf; + (void)n; +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +uint32_t tud_printer_n_available(uint8_t itf) { + return tu_fifo_count(&_printer_itf[itf].rx_ff); +} + +uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize) { + printer_interface_t *p_printer = &_printer_itf[itf]; + uint32_t num_read = tu_fifo_read_n(&p_printer->rx_ff, buffer, (uint16_t)TU_MIN(bufsize, UINT16_MAX)); + _prep_out_transaction(itf); + return num_read; +} + +bool tud_printer_n_peek(uint8_t itf, uint8_t *chr) { + return tu_fifo_peek(&_printer_itf[itf].rx_ff, chr); +} + +void tud_printer_n_read_flush(uint8_t itf) { + printer_interface_t *p_printer = &_printer_itf[itf]; + tu_fifo_clear(&p_printer->rx_ff); + _prep_out_transaction(itf); +} + + +//--------------------------------------------------------------------+ +// USBD-CLASS API +//--------------------------------------------------------------------+ +void printerd_init(void) { + tu_memclr(_printer_itf, sizeof(_printer_itf)); + tu_memclr(&_printer_fifo_cfg, sizeof(_printer_fifo_cfg)); + + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + + tu_fifo_config(&p_printer->rx_ff, p_printer->rx_ff_buf, TU_ARRAY_SIZE(p_printer->rx_ff_buf), 1, false); + tu_fifo_config(&p_printer->tx_ff, p_printer->tx_ff_buf, TU_ARRAY_SIZE(p_printer->tx_ff_buf), 1, true); + + #if OSAL_MUTEX_REQUIRED + osal_mutex_t mutex_rd = osal_mutex_create(&p_printer->rx_ff_mutex); + osal_mutex_t mutex_wr = osal_mutex_create(&p_printer->tx_ff_mutex); + TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, ); + + tu_fifo_config_mutex(&p_printer->rx_ff, NULL, mutex_rd); + tu_fifo_config_mutex(&p_printer->tx_ff, mutex_wr, NULL); + #endif + } +} + +bool printerd_deinit(void) { + #if OSAL_MUTEX_REQUIRED + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + osal_mutex_t mutex_rd = p_printer->rx_ff.mutex_rd; + osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_rd; + + if (mutex_rd) { + osal_mutex_delete(mutex_rd); + tu_fifo_config_mutex(&p_printer->rx_ff, NULL, NULL); + } + + if (mutex_wr) { + osal_mutex_delete(mutex_wr); + tu_fifo_config_mutex(&p_printer->tx_ff, NULL, NULL); + } + } + #endif + + return true; +} + +void printerd_reset(uint8_t rhport) { + (void)rhport; + + for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { + printer_interface_t *p_printer = &_printer_itf[i]; + + tu_memclr(p_printer, sizeof(p_printer)); + if (!_printer_fifo_cfg.rx_persistent) { + tu_fifo_clear(&p_printer->rx_ff); + } + if (!_printer_fifo_cfg.tx_persistent) { + tu_fifo_clear(&p_printer->tx_ff); + } + // tu_fifo_set_overwritable(&p_printer->rx_ff, true); + tu_fifo_set_overwritable(&p_printer->tx_ff, true); + } +} + +uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { + TU_VERIFY(TUSB_CLASS_PRINTER == itf_desc->bInterfaceClass, 0); + + // Identify available interface to open + printer_interface_t *p_printer; + uint8_t printer_id; + for (printer_id = 0; printer_id < CFG_TUD_PRINTER; printer_id++) { + p_printer = &_printer_itf[printer_id]; + if (p_printer->ep_out == 0) { + break; + } + } + TU_ASSERT(printer_id < CFG_TUD_PRINTER); + + //------------- Interface -------------// + uint16_t drv_len = sizeof(tusb_desc_interface_t); + + //------------- Endpoints -------------// + TU_ASSERT(itf_desc->bNumEndpoints == 2); + drv_len += 2 * sizeof(tusb_desc_endpoint_t); + p_printer->itf_num = 2; + const uint8_t *p_desc = tu_desc_next(itf_desc); + TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_printer->ep_out, &p_printer->ep_in), 0); + + _prep_out_transaction(printer_id); + + return drv_len; +} + +bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) { + TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); + + if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { + //------------- STD Request -------------// + if (stage != CONTROL_STAGE_SETUP) { + return true; + } + } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { + switch (request->bRequest) { + // https://www.usb.org/sites/default/files/usbprint11a021811.pdf + case PRINTER_REQ_CONTROL_GET_DEVICE_ID: + if (stage == CONTROL_STAGE_SETUP) { + const char deviceId[] = "MANUFACTURER:ACME Manufacturing;" + "MODEL:LaserBeam 9;" + "COMMAND SET:PS;" + "COMMENT:Anything you like;" + "ACTIVE COMMAND SET:PS;"; + char buffer[256]; + strcpy(buffer + 2, deviceId); + buffer[0] = 0x00; + buffer[1] = strlen(deviceId); + return tud_control_xfer(rhport, request, buffer, strlen(deviceId) + 2); + } + break; + case PRINTER_REQ_CONTROL_GET_PORT_STATUS: + if (stage == CONTROL_STAGE_SETUP) { + static uint8_t port_status = (0 << 3) | (1 << 1) | (1 << 2); // ~Paper empty + Selected + NoError + return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); + } + break; + case PRINTER_REQ_CONTROL_SOFT_RESET: + if (stage == CONTROL_STAGE_SETUP) { + return false; // what to do ? + } + break; + default: + return false; + } + } else { + return false; + } + return true; +} + +bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + uint8_t itf; + printer_interface_t *p_printer; + + // Identify which interface to use + for (itf = 0; itf < CFG_TUD_PRINTER; itf++) { + p_printer = &_printer_itf[itf]; + if (ep_addr == p_printer->ep_out) { + break; + } + } + TU_ASSERT(itf < CFG_TUD_PRINTER); + printer_epbuf_t *p_epbuf = &_printer_epbuf[itf]; + + // Received new data + if (ep_addr == p_printer->ep_out) { + tu_fifo_write_n(&p_printer->rx_ff, p_epbuf->epout, (uint16_t)xferred_bytes); + // invoke receive callback (if there is still data) + if (tud_printer_rx_cb && !tu_fifo_empty(&p_printer->rx_ff)) { + tud_printer_rx_cb(itf, xferred_bytes); + } + // prepare for OUT transaction + _prep_out_transaction(itf); + } + + return true; +} + +#endif diff --git a/src/class/printer/printer_device.h b/src/class/printer/printer_device.h new file mode 100644 index 000000000..8c21e39ce --- /dev/null +++ b/src/class/printer/printer_device.h @@ -0,0 +1,79 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 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 TUSB_PRINTER_DEVICE_H_ +#define TUSB_PRINTER_DEVICE_H_ + +#include "printer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct TU_ATTR_PACKED { + uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect + uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect +} tud_printer_configure_fifo_t; + +//--------------------------------------------------------------------+ +// Application API (Multiple Ports) i.e. CFG_TUD_PRINTER > 1 +//--------------------------------------------------------------------+ + +// Get the number of bytes available for reading +uint32_t tud_printer_n_available(uint8_t itf); + +// Read received bytes +uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize); + +// Clear the received FIFO +void tud_printer_n_read_flush(uint8_t itf); + +// Get a byte from FIFO without removing it +bool tud_printer_n_peek(uint8_t itf, uint8_t *ui8); + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when received new data +TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf, size_t n); + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void printerd_init(void); +bool printerd_deinit(void); +void printerd_reset(uint8_t rhport); +uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len); +bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request); +bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/device/usbd.c b/src/device/usbd.c index 1e21c667a..32b3a3ee2 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -334,6 +334,19 @@ static const usbd_class_driver_t _usbd_driver[] = { .sof = NULL }, #endif + + #if CFG_TUD_PRINTER + { + .name = DRIVER_NAME("PRINTER"), + .init = printerd_init, + .deinit = printerd_deinit, + .reset = printerd_reset, + .open = printerd_open, + .control_xfer_cb = printerd_control_xfer_cb, + .xfer_cb = printerd_xfer_cb, + .sof = NULL + }, + #endif }; enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; diff --git a/src/device/usbd.h b/src/device/usbd.h index bd5a3c395..3b296feea 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -312,6 +312,21 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 +//--------------------------------------------------------------------+ +// Printer Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_PRINTER_DESC_LEN (9 + 7 + 7) // one interface, two endpoints + +#define TUD_PRINTER_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_PRINTER, 1, 2, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + + //--------------------------------------------------------------------+ // HID Descriptor Templates //--------------------------------------------------------------------+ diff --git a/src/tinyusb.mk b/src/tinyusb.mk index 8f9d52de9..e7d1c0b5b 100644 --- a/src/tinyusb.mk +++ b/src/tinyusb.mk @@ -15,6 +15,7 @@ TINYUSB_SRC_C += \ src/class/mtp/mtp_device.c \ src/class/net/ecm_rndis_device.c \ src/class/net/ncm_device.c \ + src/class/printer/printer_device.c \ src/class/usbtmc/usbtmc_device.c \ src/class/video/video_device.c \ src/class/vendor/vendor_device.c \ diff --git a/src/tusb.h b/src/tusb.h index 62b3b9783..256a239e7 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -92,6 +92,10 @@ #include "class/mtp/mtp_device.h" #endif + #if CFG_TUD_PRINTER + #include "class/printer/printer_device.h" + #endif + #if CFG_TUD_AUDIO #include "class/audio/audio_device.h" #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 64fe899db..80cbeab36 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -614,6 +614,10 @@ #define CFG_TUD_NCM 0 #endif +#ifndef CFG_TUD_PRINTER + #define CFG_TUD_PRINTER 0 +#endif + #ifndef CFG_TUD_EDPT_DEDICATED_HWFIFO #define CFG_TUD_EDPT_DEDICATED_HWFIFO 0 #endif diff --git a/tools/iar_template.ipcf b/tools/iar_template.ipcf index caddda826..035e40b94 100644 --- a/tools/iar_template.ipcf +++ b/tools/iar_template.ipcf @@ -69,6 +69,11 @@ $TUSB_DIR$/src/class/net/ncm.h $TUSB_DIR$/src/class/net/net_device.h + + $TUSB_DIR$/src/class/printer/printer_device.c + $TUSB_DIR$/src/class/printer/printer.h + $TUSB_DIR$/src/class/printer/printer_device.h + $TUSB_DIR$/src/class/usbtmc/usbtmc_device.c $TUSB_DIR$/src/class/usbtmc/usbtmc.h -- cgit v1.3.1 From 0e5241aa83c52792959ad8a2ef0535a3759530a7 Mon Sep 17 00:00:00 2001 From: Rémi Berthoz Date: Wed, 7 Jan 2026 17:04:48 +0100 Subject: Adjust printer device class to 0.20.0 and fix compilation warnings --- examples/device/printer_to_hid/src/main.c | 73 +++--- .../device/printer_to_hid/src/usb_descriptors.c | 278 +++++++-------------- .../device/printer_to_hid/src/usb_descriptors.h | 48 +++- hw/bsp/rp2040/family.cmake | 1 + src/class/printer/printer.h | 9 +- src/class/printer/printer_device.c | 13 +- src/device/usbd.h | 13 + src/tusb.h | 4 + 8 files changed, 208 insertions(+), 231 deletions(-) (limited to 'src/tusb.h') diff --git a/examples/device/printer_to_hid/src/main.c b/examples/device/printer_to_hid/src/main.c index f5012478c..46203b9db 100644 --- a/examples/device/printer_to_hid/src/main.c +++ b/examples/device/printer_to_hid/src/main.c @@ -39,7 +39,7 @@ // usb interface pointer uint8_t printer_itf = 0; // pendings bytes in usb endpoint buffer ; must process these bytes -uint8_t pending_bytes_on_usb_ep = 0; +size_t pending_bytes_on_usb_ep = 0; // -------------------------------------------------------------------+ @@ -72,7 +72,7 @@ uint8_t next_keycode_is_release = false; // next_keycode and place it in the HID report, then set next_is_null // such that the key is released by the next report. This seem to help // stroking the same key twice when the character is repeated in the data. -void hid_tx_task(void) { +static void hid_tx_task(void) { // Poll every 10ms const uint32_t interval_ms = 10; static uint32_t start_ms = 0; @@ -104,28 +104,30 @@ void hid_tx_task(void) { // overwrite local data that is not processed yet, so we use data_buffer as a fifo. // We do not have to take care of reading correctly from the endpoint buffer, as all // is done well by tud_printer_n_read(). -void printer_rx_task(void) { - if (pending_bytes_on_usb_ep > 0) { - size_t len1 = data_available; - size_t len2 = 0; - if (len1 < 0) { - len1 = 0; - } - if (data_rx_offset + len1 > sizeof(data_buffer)) { - len2 = len1 - (sizeof(data_buffer) - data_rx_offset); - len1 = sizeof(data_buffer) - data_rx_offset; - } - uint32_t count = tud_printer_n_read(printer_itf, data_buffer + data_rx_offset, len1); - if (len2 > 0) { - count += tud_printer_n_read(printer_itf, data_buffer, len2); - } +static void printer_rx_task(void) { - if (count > 0) { - data_available -= count; - pending_bytes_on_usb_ep -= count; - data_rx_offset = (data_rx_offset + count) % sizeof(data_buffer); - } + if (pending_bytes_on_usb_ep == 0) { + return; + } + + size_t len1 = data_available; + size_t len2 = 0; + if (data_rx_offset + len1 > sizeof(data_buffer)) { + len2 = len1 - (sizeof(data_buffer) - data_rx_offset); + len1 = sizeof(data_buffer) - data_rx_offset; + } + uint32_t count = tud_printer_n_read(printer_itf, data_buffer + data_rx_offset, len1); + if (len2 > 0) { + count += tud_printer_n_read(printer_itf, data_buffer, len2); } + + if (count == 0) { + return; + } + + data_available -= count; + pending_bytes_on_usb_ep -= count; + data_rx_offset = (data_rx_offset + count) % sizeof(data_buffer); } // The HID keycodes are not binary mapped like UTF8 codes. If we want to send the @@ -134,7 +136,7 @@ void printer_rx_task(void) { // hosts expecting hid data from a QWERTY keyboard. Also note that only a-zA-Z0-9 // characters are converted, for simplicity of the example. Other characters are // converted to spaces. -void translation_task(void) { +static void translation_task(void) { if (data_tx_offset != data_rx_offset || data_available == 0) { // If data_tx_offset and data_rx_offset have different values, then we // can proceed: translate, prepare for TX, and advance data_tx_offset. @@ -177,18 +179,22 @@ void translation_task(void) { } int main(void) { - board_init(); - tud_init(BOARD_TUD_RHPORT); // init device stack on configured roothub port - if (board_init_after_tusb) { - board_init_after_tusb(); - } + // init device and host stack on configured roothub port + tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO}; + tusb_init(BOARD_TUD_RHPORT, &dev_init); + board_init_after_tusb(); while (1) { tud_task(); // tinyusb device task printer_rx_task(); // read data sent by host on our printer interface translation_task(); // translate printer's UTF8 to HID keycodes hid_tx_task(); // send data to host with our HID interface + if (pending_bytes_on_usb_ep > 0) { + board_led_on(); + } else { + board_led_off(); + } } } @@ -197,7 +203,6 @@ int main(void) { // Printer callbacks //--------------------------------------------------------------------+ -// Data was received on endpoint buffer void tud_printer_rx_cb(uint8_t itf, size_t n) { printer_itf = itf; // get interface from which to read endpoint buffer pending_bytes_on_usb_ep += n; // count pending bytes, counter must decrement when reading from the endpoint buffer @@ -210,10 +215,20 @@ void tud_printer_rx_cb(uint8_t itf, size_t n) { uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { + (void)instance; + (void)report_id; + (void)report_type; + (void)buffer; + (void)reqlen; return 0; } void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, uint16_t bufsize) { + (void)instance; + (void)report_id; + (void)report_type; + (void)buffer; + (void)bufsize; return; } diff --git a/examples/device/printer_to_hid/src/usb_descriptors.c b/examples/device/printer_to_hid/src/usb_descriptors.c index 161bbb7c5..7d529e724 100644 --- a/examples/device/printer_to_hid/src/usb_descriptors.c +++ b/examples/device/printer_to_hid/src/usb_descriptors.c @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2026 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 @@ -25,223 +25,119 @@ #include "bsp/board_api.h" #include "tusb.h" -#include "usb_descriptors.h" - -/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. - * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. - * - * Auto ProductID layout's Bitmap: - * [MSB] HID | MSC | CDC [LSB] - */ -#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0) -#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \ - PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) ) - -#define USB_VID 0xCafe -#define USB_BCD 0x0200 - -//--------------------------------------------------------------------+ -// Device Descriptors -//--------------------------------------------------------------------+ -static tusb_desc_device_t const desc_device = -{ - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = USB_BCD, - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = 0x0100, - - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - - .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; -} -//--------------------------------------------------------------------+ -// HID Report Descriptor -//--------------------------------------------------------------------+ - -uint8_t const desc_hid_report[] = -{ - TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD )) -}; +#include "usb_descriptors.h" -// Invoked when received GET HID REPORT DESCRIPTOR -// Application return pointer to descriptor -// Descriptor contents must exist long enough for transfer to complete -uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance) -{ - (void) instance; - return desc_hid_report; -} //--------------------------------------------------------------------+ -// Configuration Descriptor +// Report definitions //--------------------------------------------------------------------+ -enum -{ - ITF_NUM_HID, - ITF_NUM_PRINTER, - ITF_NUM_TOTAL +// Values of the string descriptors. Order must match the order defined by STRING_DESCRIPTOR_INDICES. +const char *STRING_DESCRIPTOR_VALUES[] = { + (const char[]){0x09, 0x04}, // 0: Supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + NULL, // 3: Serial number, will use unique ID from the Pi Pico board hardware + "Config1", // 4: Configuration + "Hid1", // 5: HID interface + "Print1", // 6: Printer interface }; -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) +uint8_t HID_REPORT_DESCRIPTOR[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD))}; -// HID interface endpoints -#define EPADDR_HID 0x81 // Interrupt In, MSB must be 1 -// Printer interface endpoints -#define EPADDR_PRINTER_OUT 0x01 // Bulk Out, MSB must be 0 -#define EPADDR_PRINTER_IN 0x82 // Bulk In, MSB must be 1 - -uint8_t const desc_configuration[] = -{ +uint8_t CONFIG_INTERFACE_ENDPOINT_DESCRIPTOR[] = { // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUD_CONFIG_DESCRIPTOR(1, ITF_COUNT, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), // HID: // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval - TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPADDR_HID, CFG_TUD_HID_EP_BUFSIZE, 5), + TUD_HID_DESCRIPTOR(ITF_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(HID_REPORT_DESCRIPTOR), EPADDR_HID, + CFG_TUD_HID_EP_BUFSIZE, 5), // Printer: // Interface number, string index, EP Bulk Out address, EP Bulk In address, EP size - TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 0, EPADDR_PRINTER_OUT, EPADDR_PRINTER_IN, CFG_TUD_PRINTER_EP_BUFSIZE) + TUD_PRINTER_DESCRIPTOR(ITF_PRINTER, 0, EPADDR_PRINTER_OUT, EPADDR_PRINTER_IN, CFG_TUD_PRINTER_EP_BUFSIZE)}; + +static const tusb_desc_device_t DEVICE_DESCRIPTOR = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + .bDeviceClass = 0x00, // Define class at interface level + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIAL, + + .bNumConfigurations = 0x01 }; -#if TUD_OPT_HIGH_SPEED -// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration - -// other speed configuration -static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; - -// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed -static tusb_desc_device_qualifier_t const desc_device_qualifier = -{ - .bLength = sizeof(tusb_desc_device_qualifier_t), - .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, - .bcdUSB = USB_BCD, - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .bNumConfigurations = 0x01, - .bReserved = 0x00 -}; +//--------------------------------------------------------------------+ +// TinyUSB callbacks (descriptor requests) +//--------------------------------------------------------------------+ -// 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; +// TinyUSB GET HID REPORT DESCRIPTOR callback. +const uint8_t *tud_hid_descriptor_report_cb(uint8_t instance) { + (void)instance; + return HID_REPORT_DESCRIPTOR; } -// 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 - - // other speed config is basically configuration with type = OTHER_SPEED_CONFIG - memcpy(desc_other_speed_config, desc_configuration, CONFIG_TOTAL_LEN); - desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; - - // this example use the same configuration for both high and full speed mode - return desc_other_speed_config; +// TinyUSB GET CONFIGURATION DESCRIPTOR callback. +const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; + return CONFIG_INTERFACE_ENDPOINT_DESCRIPTOR; } -#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) -{ - (void) index; // for multiple configurations - - // This example use the same configuration for both high and full speed mode - return desc_configuration; +// TinyUSB GET DEVICE DESCRIPTOR callback. +const uint8_t *tud_descriptor_device_cb(void) { + return (const uint8_t *)&DEVICE_DESCRIPTOR; } -//--------------------------------------------------------------------+ -// String Descriptors -//--------------------------------------------------------------------+ - -// String Descriptor Index -enum { - STRID_LANGID = 0, - STRID_MANUFACTURER, - STRID_PRODUCT, - STRID_SERIAL, -}; - -// array of pointer to string descriptors -static 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 -}; - -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) { - (void) langid; - size_t chr_count; - - switch ( index ) { - case STRID_LANGID: - memcpy(&_desc_str[1], string_desc_arr[0], 2); - chr_count = 1; - break; - - case STRID_SERIAL: - chr_count = board_usb_get_serial(_desc_str + 1, 32); - break; - - default: - // 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; - - 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; - - // Convert ASCII string into UTF-16 - for ( size_t i = 0; i < chr_count; i++ ) { - _desc_str[1 + i] = str[i]; - } - break; +// Storage buffer array for string descriptor to be sent to host. +static uint16_t string_descriptor_buffer[STRING_DESCRIPTOR_MAX_LENGTH + 1]; + +// TinyUSB GET STRING DESCRIPTOR callback. +const uint16_t *tud_descriptor_string_cb(uint8_t index, [[maybe_unused]] uint16_t langid) { + size_t utf16_string_length; + + if (index == LANGID) { + // langid is not a string as in a series of characters: language ID code is binary, 2 bytes + memcpy(string_descriptor_buffer + 1, STRING_DESCRIPTOR_VALUES[LANGID], 2); + utf16_string_length = 1; // 2 bytes = 1 UTF16 word + + } else if (index == STR_SERIAL) { + // serialnumber is generated from pi pico: see note in STRING_DESCRIPTOR_VALUES definition + utf16_string_length = board_usb_get_serial(string_descriptor_buffer + 1, STRING_DESCRIPTOR_MAX_LENGTH); + + } else if (index < STRING_COUNT) { + // Get adequate descriptor string + const char *str = STRING_DESCRIPTOR_VALUES[index]; + utf16_string_length = strlen(str); + if (utf16_string_length > STRING_DESCRIPTOR_MAX_LENGTH) { + utf16_string_length = STRING_DESCRIPTOR_MAX_LENGTH; + } + // Convert ASCII string from memory (char*) to UTF16 (for buffer), + // store in buffer with 1 UTF16 word offset (2 bytes, for buffer header) + for (size_t i = 0; i < utf16_string_length; i++) { + string_descriptor_buffer[i + 1] = str[i]; + } + + } else { + return NULL; } - // first byte is length (including header), second byte is string type - _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + // Set buffer header: + // byte 1 - buffer length in bytes (including header) + // byte 0 - string descriptor type (0x03). + string_descriptor_buffer[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * utf16_string_length + 2)); - return _desc_str; + return string_descriptor_buffer; } diff --git a/examples/device/printer_to_hid/src/usb_descriptors.h b/examples/device/printer_to_hid/src/usb_descriptors.h index 53c4e1fbd..0d9793a9c 100644 --- a/examples/device/printer_to_hid/src/usb_descriptors.h +++ b/examples/device/printer_to_hid/src/usb_descriptors.h @@ -25,6 +25,52 @@ #ifndef USB_DESCRIPTORS_H_ #define USB_DESCRIPTORS_H_ -REPORT_ID_KEYBOARD = 1 +#include "bsp/board_api.h" +#include "tusb.h" + +#define USB_VID 0xCafe // unassigned vendor id +#define USB_PID 0x4004 // random product id +#define USB_BCD 0x0200 // binary coded version: 2.00 + +// Configuration +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) + +// HID interface endpoints +#define EPADDR_HID 0x81 // Interrupt In, MSB must be 1 +// Printer interface endpoints +#define EPADDR_PRINTER_OUT 0x01 // Bulk Out, MSB must be 0 +#define EPADDR_PRINTER_IN 0x82 // Bulk In, MSB must be 1 + +// HID report ID +#define REPORT_ID_KEYBOARD 1 + +// The maximum length of the string that will be sent to the host via the STRING DESCRIPTOR. Note that the +// string descriptor itself is two bytes wider than the string. +#define STRING_DESCRIPTOR_MAX_LENGTH 32 + +//--------------------------------------------------------------------+ +// Configuration, interface, endpoint descriptors +//--------------------------------------------------------------------+ + +enum { + ITF_HID, + ITF_PRINTER, + ITF_COUNT, +}; + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +enum { + LANGID = 0, + STR_MANUFACTURER, + STR_PRODUCT, + STR_SERIAL, + STR_CONFIGURATION, + STR_HID_INTERFACE, + STR_PRINTER_INTERFACE, + STRING_COUNT, +}; #endif /* USB_DESCRIPTORS_H_ */ diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 1602e35eb..55ce6e1f8 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -98,6 +98,7 @@ target_sources(tinyusb_device_base INTERFACE ${TOP}/src/class/mtp/mtp_device.c ${TOP}/src/class/net/ecm_rndis_device.c ${TOP}/src/class/net/ncm_device.c + ${TOP}/src/class/printer/printer_device.c ${TOP}/src/class/usbtmc/usbtmc_device.c ${TOP}/src/class/vendor/vendor_device.c ${TOP}/src/class/video/video_device.c diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h index 27e95b997..c9ed3cebc 100644 --- a/src/class/printer/printer.h +++ b/src/class/printer/printer.h @@ -30,19 +30,18 @@ #include "common/tusb_common.h" #ifdef __cplusplus - extern "C" { +extern "C" { #endif /// Printer Class Specific Control Request -typedef enum -{ +typedef enum { PRINTER_REQ_CONTROL_GET_DEVICE_ID = 0x01, ///< Get device ID PRINTER_REQ_CONTROL_GET_PORT_STATUS = 0x02, ///< Get port status PRINTER_REQ_CONTROL_SOFT_RESET = 0x03, ///< Soft reset -}printer_request_enum_t; +} printer_request_enum_t; #ifdef __cplusplus - } +} #endif #endif /* _TUSB_PRINTER_H__ */ diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c index efeae100f..76151949b 100644 --- a/src/class/printer/printer_device.c +++ b/src/class/printer/printer_device.c @@ -96,7 +96,7 @@ static bool _prep_out_transaction(uint8_t itf) { available = tu_fifo_remaining(&p_printer->rx_ff); if (available >= CFG_TUD_PRINTER_EP_BUFSIZE) { - return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE); + return usbd_edpt_xfer(rhport, p_printer->ep_out, p_epbuf->epout, CFG_TUD_PRINTER_EP_BUFSIZE, false); } else { // Release endpoint since we don't make any transfer usbd_edpt_release(rhport, p_printer->ep_out); @@ -189,7 +189,7 @@ void printerd_reset(uint8_t rhport) { for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { printer_interface_t *p_printer = &_printer_itf[i]; - tu_memclr(p_printer, sizeof(p_printer)); + tu_memclr(p_printer, sizeof(&p_printer)); if (!_printer_fifo_cfg.rx_persistent) { tu_fifo_clear(&p_printer->rx_ff); } @@ -202,6 +202,7 @@ void printerd_reset(uint8_t rhport) { } uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { + (void)max_len; TU_VERIFY(TUSB_CLASS_PRINTER == itf_desc->bInterfaceClass, 0); // Identify available interface to open @@ -257,13 +258,13 @@ bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_ break; case PRINTER_REQ_CONTROL_GET_PORT_STATUS: if (stage == CONTROL_STAGE_SETUP) { - static uint8_t port_status = (0 << 3) | (1 << 1) | (1 << 2); // ~Paper empty + Selected + NoError + static uint8_t port_status = 0b00011000; // paper not empty, selected, no error return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); } break; case PRINTER_REQ_CONTROL_SOFT_RESET: if (stage == CONTROL_STAGE_SETUP) { - return false; // what to do ? + return false; // TODO: reset buffers, reset Bulk In and Out endpoints, clear stall conditions } break; default: @@ -276,6 +277,8 @@ bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_ } bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + (void)rhport; + (void)result; uint8_t itf; printer_interface_t *p_printer; @@ -293,7 +296,7 @@ bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uin if (ep_addr == p_printer->ep_out) { tu_fifo_write_n(&p_printer->rx_ff, p_epbuf->epout, (uint16_t)xferred_bytes); // invoke receive callback (if there is still data) - if (tud_printer_rx_cb && !tu_fifo_empty(&p_printer->rx_ff)) { + if (!tu_fifo_empty(&p_printer->rx_ff)) { tud_printer_rx_cb(itf, xferred_bytes); } // prepare for OUT transaction diff --git a/src/device/usbd.h b/src/device/usbd.h index 3b296feea..d473aea2a 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -292,6 +292,19 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ /* Endpoint In */\ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 +//--------------------------------------------------------------------+ +// Printer Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_PRINTER_DESC_LEN (9 + 7 + 7) // one interface, two endpoints + +#define TUD_PRINTER_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_PRINTER, 1, 2, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 //--------------------------------------------------------------------+ // MTP Descriptor Templates diff --git a/src/tusb.h b/src/tusb.h index 256a239e7..1f4d4b73a 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -88,6 +88,10 @@ #include "class/msc/msc_device.h" #endif + #if CFG_TUD_PRINTER + #include "class/printer/printer_device.h" + #endif + #if CFG_TUD_MTP #include "class/mtp/mtp_device.h" #endif -- cgit v1.3.1 From 7ea02fd6c9e376679bc1fe025bb73e43664e17c6 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 27 Feb 2026 23:34:48 +0700 Subject: add osal_time_millis() to osal requirement implement tusb_time_millis_api() with osal_time_millis() when OS is not NONE --- src/common/tusb_common.h | 6 ------ src/osal/osal.h | 2 ++ src/osal/osal_freertos.h | 4 ++++ src/osal/osal_mynewt.h | 4 ++++ src/osal/osal_none.h | 2 ++ src/osal/osal_pico.h | 4 ++++ src/osal/osal_rtthread.h | 4 ++++ src/osal/osal_rtx4.h | 4 ++++ src/osal/osal_zephyr.h | 4 ++++ src/tusb.c | 10 ++++++++-- src/tusb.h | 12 +++++++++++- 11 files changed, 47 insertions(+), 9 deletions(-) (limited to 'src/tusb.h') diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index 6ac1405f3..9eb0a9337 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -90,12 +90,6 @@ // TODO move to a more obvious place/file //--------------------------------------------------------------------+ -// Get current milliseconds, required by some port/configuration without RTOS -extern uint32_t tusb_time_millis_api(void); - -// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS -extern void tusb_time_delay_ms_api(uint32_t ms); - // flush data cache extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size); diff --git a/src/osal/osal.h b/src/osal/osal.h index 44521620f..c0292a008 100644 --- a/src/osal/osal.h +++ b/src/osal/osal.h @@ -74,6 +74,8 @@ typedef void (*osal_task_func_t)(void* param); /*-------------------------------------------------------------------- OSAL Porting API Should be implemented as static inline function in osal_port.h header + uint32_t osal_time_millis(void); + void osal_spin_init(osal_spinlock_t *ctx); void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr); diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h index 9aeda4d01..32ee2d55c 100644 --- a/src/osal/osal_freertos.h +++ b/src/osal/osal_freertos.h @@ -99,6 +99,10 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) { vTaskDelay(pdMS_TO_TICKS(msec)); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t osal_time_millis(void) { + return pdTICKS_TO_MS(xTaskGetTickCount()); +} + //--------------------------------------------------------------------+ // Spinlock API //--------------------------------------------------------------------+ diff --git a/src/osal/osal_mynewt.h b/src/osal/osal_mynewt.h index 6d51f8ec3..94124ca81 100644 --- a/src/osal/osal_mynewt.h +++ b/src/osal/osal_mynewt.h @@ -40,6 +40,10 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) { os_time_delay( os_time_ms_to_ticks32(msec) ); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t osal_time_millis(void) { + return os_time_ticks_to_ms32(os_time_get()); +} + //--------------------------------------------------------------------+ // Spinlock API //--------------------------------------------------------------------+ diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h index bba42716d..7bf6029d6 100644 --- a/src/osal/osal_none.h +++ b/src/osal/osal_none.h @@ -31,6 +31,8 @@ extern "C" { #endif +// osal_time_millis() is not provided, tusb_time_millis_api() must be implemented by user application + //--------------------------------------------------------------------+ // Spinlock API //--------------------------------------------------------------------+ diff --git a/src/osal/osal_pico.h b/src/osal/osal_pico.h index 79b728e9a..6a0a21bb3 100644 --- a/src/osal/osal_pico.h +++ b/src/osal/osal_pico.h @@ -43,6 +43,10 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) { sleep_ms(msec); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t osal_time_millis(void) { + return to_ms_since_boot(get_absolute_time()); +} + //--------------------------------------------------------------------+ // Spinlock API //--------------------------------------------------------------------+ diff --git a/src/osal/osal_rtthread.h b/src/osal/osal_rtthread.h index a778f5425..f560281c5 100644 --- a/src/osal/osal_rtthread.h +++ b/src/osal/osal_rtthread.h @@ -42,6 +42,10 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) { rt_thread_mdelay(msec); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t osal_time_millis(void) { + return (uint32_t)((((uint64_t)rt_tick_get()) * 1000) / RT_TICK_PER_SECOND); +} + //--------------------------------------------------------------------+ // Spinlock API //--------------------------------------------------------------------+ diff --git a/src/osal/osal_rtx4.h b/src/osal/osal_rtx4.h index 35860ddd5..e1930c96c 100644 --- a/src/osal/osal_rtx4.h +++ b/src/osal/osal_rtx4.h @@ -46,6 +46,10 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) { os_dly_wait(lo); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t osal_time_millis(void) { + return os_time_get(); +} + TU_ATTR_ALWAYS_INLINE static inline uint16_t msec2wait(uint32_t msec) { if (msec == OSAL_TIMEOUT_WAIT_FOREVER) { return 0xFFFF; diff --git a/src/osal/osal_zephyr.h b/src/osal/osal_zephyr.h index 91f225f79..900ac786c 100644 --- a/src/osal/osal_zephyr.h +++ b/src/osal/osal_zephyr.h @@ -35,6 +35,10 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) { k_msleep(msec); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t osal_time_millis(void) { + return k_uptime_get_32(); +} + //--------------------------------------------------------------------+ // Spinlock API //--------------------------------------------------------------------+ diff --git a/src/tusb.c b/src/tusb.c index 6075e9db4..40d0e8adf 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -45,17 +45,23 @@ tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM] = { TUSB_ROLE_INVALID }; // Weak/Default API, can be overwritten by Application //-------------------------------------------------------------------- + #if CFG_TUSB_OS != OPT_OS_NONE +uint32_t tusb_time_millis_api(void) { + return osal_time_millis(); +} + #endif + TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms) { #if CFG_TUSB_OS != OPT_OS_NONE osal_task_delay(ms); #else - // delay using millis() (if implemented) and/or frame number if possible + // delay using millis() const uint32_t time_ms = tusb_time_millis_api(); while ((tusb_time_millis_api() - time_ms) < ms) {} #endif } -TU_ATTR_WEAK void* tusb_app_virt_to_phys(void *virt_addr) { +TU_ATTR_WEAK void *tusb_app_virt_to_phys(void *virt_addr) { return virt_addr; } diff --git a/src/tusb.h b/src/tusb.h index 62b3b9783..742009a2e 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -135,7 +135,7 @@ //--------------------------------------------------------------------+ -// User API +// Application API //--------------------------------------------------------------------+ #if CFG_TUH_ENABLED || CFG_TUD_ENABLED @@ -174,6 +174,16 @@ bool tusb_deinit(uint8_t rhport); #endif +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +// Get current milliseconds, required by some port/configuration without RTOS +extern uint32_t tusb_time_millis_api(void); + +// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS +extern void tusb_time_delay_ms_api(uint32_t ms); + #ifdef __cplusplus } #endif -- cgit v1.3.1 From 8444c25ab6bc841bad81fd04afc1e66a9d57a115 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 27 Feb 2026 23:51:36 +0700 Subject: replace board_millis() with tusb_time_millis_api() --- examples/device/audio_4_channel_mic/src/main.c | 4 +-- examples/device/audio_test/src/main.c | 4 +-- examples/device/audio_test_multi_rate/src/main.c | 4 +-- examples/device/board_test/src/main.c | 4 +-- examples/device/cdc_dual_ports/src/main.c | 2 +- examples/device/cdc_msc/src/main.c | 2 +- examples/device/cdc_uac2/src/uac2_app.c | 4 +-- examples/device/dfu/src/main.c | 2 +- examples/device/dfu_runtime/src/main.c | 2 +- examples/device/dynamic_configuration/src/main.c | 4 +-- examples/device/hid_boot_interface/src/main.c | 4 +-- examples/device/hid_composite/src/main.c | 4 +-- examples/device/hid_generic_inout/src/main.c | 2 +- examples/device/hid_multiple_interface/src/main.c | 4 +-- examples/device/midi_test/src/main.c | 4 +-- examples/device/msc_dual_lun/src/main.c | 2 +- examples/device/mtp/src/main.c | 2 +- examples/device/net_lwip_webserver/src/main.c | 2 +- examples/device/uac2_headset/src/main.c | 6 ++-- examples/device/uac2_speaker_fb/src/main.c | 6 ++-- examples/device/usbtmc/src/main.c | 6 ++-- examples/device/usbtmc/src/usbtmc_app.c | 8 ++--- examples/device/video_capture/src/main.c | 6 ++-- examples/device/video_capture_2ch/src/main.c | 6 ++-- examples/device/webusb_serial/src/main.c | 2 +- examples/dual/host_hid_to_device_cdc/src/main.c | 2 +- examples/dual/host_info_to_device_cdc/src/main.c | 6 ++-- examples/host/bare_api/src/main.c | 2 +- examples/host/cdc_msc_hid/src/main.c | 2 +- examples/host/device_info/src/main.c | 2 +- examples/host/hid_controller/src/hid_app.c | 2 +- examples/host/hid_controller/src/main.c | 2 +- examples/host/midi_rx/src/main.c | 2 +- examples/host/msc_file_explorer/src/main.c | 2 +- examples/typec/power_delivery/src/main.c | 2 +- hw/bsp/at32f402_405/family.c | 2 +- hw/bsp/at32f403a_407/family.c | 2 +- hw/bsp/at32f413/family.c | 2 +- hw/bsp/at32f415/family.c | 2 +- hw/bsp/at32f423/family.c | 2 +- hw/bsp/at32f425/family.c | 2 +- hw/bsp/at32f435_437/family.c | 2 +- hw/bsp/at32f45x/family.c | 2 +- hw/bsp/board.c | 4 --- hw/bsp/board_api.h | 41 ++--------------------- hw/bsp/broadcom_32bit/family.c | 2 +- hw/bsp/broadcom_64bit/family.c | 2 +- hw/bsp/brtmm90x/family.c | 2 +- hw/bsp/ch32f20x/family.c | 2 +- hw/bsp/ch32v10x/family.c | 2 +- hw/bsp/ch32v20x/family.c | 2 +- hw/bsp/ch32v30x/family.c | 2 +- hw/bsp/cxd56/family.c | 2 +- hw/bsp/da1469x/family.c | 2 +- hw/bsp/efm32/family.c | 2 +- hw/bsp/f1c100s/family.c | 2 +- hw/bsp/fomu/family.c | 2 +- hw/bsp/gd32vf103/family.c | 2 +- hw/bsp/hpmicro/family.c | 2 +- hw/bsp/imxrt/family.c | 2 +- hw/bsp/kinetis_k/family.c | 2 +- hw/bsp/kinetis_k32l2/family.c | 2 +- hw/bsp/kinetis_kl/family.c | 2 +- hw/bsp/lpc11/family.c | 2 +- hw/bsp/lpc13/family.c | 2 +- hw/bsp/lpc15/family.c | 2 +- hw/bsp/lpc17/family.c | 2 +- hw/bsp/lpc18/family.c | 2 +- hw/bsp/lpc40/family.c | 2 +- hw/bsp/lpc43/family.c | 2 +- hw/bsp/lpc51/family.c | 2 +- hw/bsp/lpc54/family.c | 2 +- hw/bsp/lpc55/family.c | 2 +- hw/bsp/maxim/family.c | 2 +- hw/bsp/mcx/family.c | 2 +- hw/bsp/mm32/family.c | 2 +- hw/bsp/msp430/family.c | 10 +++--- hw/bsp/msp432e4/family.c | 2 +- hw/bsp/nrf/family.c | 2 +- hw/bsp/nuc100_120/family.c | 2 +- hw/bsp/nuc121_125/family.c | 2 +- hw/bsp/nuc126/family.c | 2 +- hw/bsp/nuc505/family.c | 2 +- hw/bsp/pic32mz/family.c | 2 +- hw/bsp/ra/family.c | 2 +- hw/bsp/rw61x/family.c | 2 +- hw/bsp/rx/boards/gr_citrus/gr_citrus.c | 2 +- hw/bsp/rx/boards/rx65n_target/rx65n_target.c | 2 +- hw/bsp/samd11/family.c | 2 +- hw/bsp/samd2x_l2x/family.c | 2 +- hw/bsp/samd5x_e5x/family.c | 2 +- hw/bsp/same7x/family.c | 2 +- hw/bsp/samg/family.c | 2 +- hw/bsp/stm32c0/family.c | 2 +- hw/bsp/stm32f0/family.c | 2 +- hw/bsp/stm32f1/family.c | 2 +- hw/bsp/stm32f2/family.c | 2 +- hw/bsp/stm32f3/family.c | 2 +- hw/bsp/stm32f4/family.c | 2 +- hw/bsp/stm32f7/family.c | 2 +- hw/bsp/stm32g0/family.c | 2 +- hw/bsp/stm32g4/family.c | 2 +- hw/bsp/stm32h5/family.c | 2 +- hw/bsp/stm32h7/family.c | 2 +- hw/bsp/stm32h7rs/family.c | 2 +- hw/bsp/stm32l0/family.c | 2 +- hw/bsp/stm32l4/family.c | 2 +- hw/bsp/stm32n6/family.c | 2 +- hw/bsp/stm32u0/family.c | 2 +- hw/bsp/stm32u5/family.c | 2 +- hw/bsp/stm32wb/family.c | 2 +- hw/bsp/stm32wba/family.c | 2 +- hw/bsp/tm4c/family.c | 2 +- hw/bsp/xmc4000/family.c | 2 +- src/common/tusb_common.h | 6 ++++ src/host/usbh.c | 2 +- src/portable/sunxi/dcd_sunxi_musb.c | 3 -- src/tusb.c | 13 ++++--- src/tusb.h | 10 ------ 119 files changed, 158 insertions(+), 203 deletions(-) (limited to 'src/tusb.h') diff --git a/examples/device/audio_4_channel_mic/src/main.c b/examples/device/audio_4_channel_mic/src/main.c index 5767c7453..c9c6dd46c 100644 --- a/examples/device/audio_4_channel_mic/src/main.c +++ b/examples/device/audio_4_channel_mic/src/main.c @@ -155,7 +155,7 @@ void tud_resume_cb(void) { // In a real application, this would be replaced with actual I2S receive callback. void audio_task(void) { static uint32_t start_ms = 0; - uint32_t curr_ms = board_millis(); + uint32_t curr_ms = tusb_time_millis_api(); if (start_ms == curr_ms) { return; // not enough time } @@ -408,7 +408,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/audio_test/src/main.c b/examples/device/audio_test/src/main.c index 2441eefbc..876a41d06 100644 --- a/examples/device/audio_test/src/main.c +++ b/examples/device/audio_test/src/main.c @@ -138,7 +138,7 @@ void tud_resume_cb(void) { // In a real application, this would be replaced with actual I2S receive callback. void audio_task(void) { static uint32_t start_ms = 0; - uint32_t curr_ms = board_millis(); + uint32_t curr_ms = tusb_time_millis_api(); if (start_ms == curr_ms) { return; // not enough time } @@ -402,7 +402,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/audio_test_multi_rate/src/main.c b/examples/device/audio_test_multi_rate/src/main.c index baeec870f..952176997 100644 --- a/examples/device/audio_test_multi_rate/src/main.c +++ b/examples/device/audio_test_multi_rate/src/main.c @@ -146,7 +146,7 @@ void tud_resume_cb(void) { // In a real application, this would be replaced with actual I2S receive callback. void audio_task(void) { static uint32_t start_ms = 0; - uint32_t curr_ms = board_millis(); + uint32_t curr_ms = tusb_time_millis_api(); if (start_ms == curr_ms) { return; // not enough time } @@ -614,7 +614,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/board_test/src/main.c b/examples/device/board_test/src/main.c index ee0829e5b..757876ac8 100644 --- a/examples/device/board_test/src/main.c +++ b/examples/device/board_test/src/main.c @@ -58,8 +58,8 @@ int main(void) { } // Blink and print every interval ms - if (!(board_millis() - start_ms < interval_ms)) { - start_ms = board_millis(); + if (!(tusb_time_millis_api() - start_ms < interval_ms)) { + start_ms = tusb_time_millis_api(); if (ch < 0) { // skip if echoing diff --git a/examples/device/cdc_dual_ports/src/main.c b/examples/device/cdc_dual_ports/src/main.c index 5ccb06a8a..6f918218d 100644 --- a/examples/device/cdc_dual_ports/src/main.c +++ b/examples/device/cdc_dual_ports/src/main.c @@ -157,7 +157,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/cdc_msc/src/main.c b/examples/device/cdc_msc/src/main.c index 06a4f732f..b00a0e3a9 100644 --- a/examples/device/cdc_msc/src/main.c +++ b/examples/device/cdc_msc/src/main.c @@ -160,7 +160,7 @@ void led_blinking_task(void) { if (blink_enable) { // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c index 73a262d0c..7760c402b 100644 --- a/examples/device/cdc_uac2/src/uac2_app.c +++ b/examples/device/cdc_uac2/src/uac2_app.c @@ -66,7 +66,7 @@ uint8_t current_resolution; // In a real application, this would be replaced with actual I2S send/receive callback. void audio_task(void) { static uint32_t start_ms = 0; - uint32_t curr_ms = board_millis(); + uint32_t curr_ms = tusb_time_millis_api(); if (start_ms == curr_ms) { return; // not enough time } @@ -303,7 +303,7 @@ void led_blinking_task(void) static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; } start_ms += blink_interval_ms; diff --git a/examples/device/dfu/src/main.c b/examples/device/dfu/src/main.c index 77632bf1a..fb3c22630 100644 --- a/examples/device/dfu/src/main.c +++ b/examples/device/dfu/src/main.c @@ -199,7 +199,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/dfu_runtime/src/main.c b/examples/device/dfu_runtime/src/main.c index 5de651bcd..6f412e8de 100644 --- a/examples/device/dfu_runtime/src/main.c +++ b/examples/device/dfu_runtime/src/main.c @@ -132,7 +132,7 @@ void led_blinking_task(void) static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/dynamic_configuration/src/main.c b/examples/device/dynamic_configuration/src/main.c index dac74bb7a..8ebb42f9a 100644 --- a/examples/device/dynamic_configuration/src/main.c +++ b/examples/device/dynamic_configuration/src/main.c @@ -170,7 +170,7 @@ void midi_task(void) { while( tud_midi_available() ) tud_midi_packet_read(packet); // send note every 1000 ms - if (board_millis() - start_ms < 286) { + if (tusb_time_millis_api() - start_ms < 286) { return; // not enough time } start_ms += 286; @@ -209,7 +209,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return;// not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/hid_boot_interface/src/main.c b/examples/device/hid_boot_interface/src/main.c index 44a91db67..4de319f52 100644 --- a/examples/device/hid_boot_interface/src/main.c +++ b/examples/device/hid_boot_interface/src/main.c @@ -109,7 +109,7 @@ void hid_task(void) { const uint32_t interval_ms = 10; static uint32_t start_ms = 0; - if (board_millis() - start_ms < interval_ms) { + if (tusb_time_millis_api() - start_ms < interval_ms) { return; // not enough time } start_ms += interval_ms; @@ -238,7 +238,7 @@ void led_blinking_task(void) { } // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/hid_composite/src/main.c b/examples/device/hid_composite/src/main.c index 9693d564d..7c9d5af8d 100644 --- a/examples/device/hid_composite/src/main.c +++ b/examples/device/hid_composite/src/main.c @@ -209,7 +209,7 @@ void hid_task(void) { const uint32_t interval_ms = 10; static uint32_t start_ms = 0; - if (board_millis() - start_ms < interval_ms) { + if (tusb_time_millis_api() - start_ms < interval_ms) { return; // not enough time } start_ms += interval_ms; @@ -298,7 +298,7 @@ void led_blinking_task(void) { } // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/device/hid_generic_inout/src/main.c b/examples/device/hid_generic_inout/src/main.c index 9837a47d9..2a6b91a8a 100644 --- a/examples/device/hid_generic_inout/src/main.c +++ b/examples/device/hid_generic_inout/src/main.c @@ -168,7 +168,7 @@ void led_blinking_task(void) static bool led_state = false; // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/examples/device/hid_multiple_interface/src/main.c b/examples/device/hid_multiple_interface/src/main.c index 0bccd13c1..c71a9cbad 100644 --- a/examples/device/hid_multiple_interface/src/main.c +++ b/examples/device/hid_multiple_interface/src/main.c @@ -120,7 +120,7 @@ void hid_task(void) const uint32_t interval_ms = 10; static uint32_t start_ms = 0; - if ( board_millis() - start_ms < interval_ms) return; // not enough time + if ( tusb_time_millis_api() - start_ms < interval_ms) return; // not enough time start_ms += interval_ms; uint32_t const btn = board_button_read(); @@ -205,7 +205,7 @@ void led_blinking_task(void) static bool led_state = false; // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/examples/device/midi_test/src/main.c b/examples/device/midi_test/src/main.c index fd58e3021..1154c1d60 100644 --- a/examples/device/midi_test/src/main.c +++ b/examples/device/midi_test/src/main.c @@ -134,7 +134,7 @@ void midi_task(void) } // send note periodically - if (board_millis() - start_ms < 286) { + if (tusb_time_millis_api() - start_ms < 286) { return; // not enough time } start_ms += 286; @@ -174,7 +174,7 @@ void led_blinking_task(void) static bool led_state = false; // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/examples/device/msc_dual_lun/src/main.c b/examples/device/msc_dual_lun/src/main.c index 62b1c872a..b459871f7 100644 --- a/examples/device/msc_dual_lun/src/main.c +++ b/examples/device/msc_dual_lun/src/main.c @@ -103,7 +103,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) return; // not enough time + if (tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/examples/device/mtp/src/main.c b/examples/device/mtp/src/main.c index 57d1535b2..6ffa435d1 100644 --- a/examples/device/mtp/src/main.c +++ b/examples/device/mtp/src/main.c @@ -102,7 +102,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) return; // not enough time + if (tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/examples/device/net_lwip_webserver/src/main.c b/examples/device/net_lwip_webserver/src/main.c index 867cf2812..9f26da2ba 100644 --- a/examples/device/net_lwip_webserver/src/main.c +++ b/examples/device/net_lwip_webserver/src/main.c @@ -291,5 +291,5 @@ void sys_arch_unprotect(sys_prot_t pval) { /* lwip needs a millisecond time source, and the TinyUSB board support code has one available */ uint32_t sys_now(void) { - return board_millis(); + return tusb_time_millis_api(); } diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c index 96fa66f1e..0ea63d8f7 100644 --- a/examples/device/uac2_headset/src/main.c +++ b/examples/device/uac2_headset/src/main.c @@ -559,7 +559,7 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_reques // In a real application, this would be replaced with actual I2S send/receive callback. void audio_task(void) { static uint32_t start_ms = 0; - uint32_t curr_ms = board_millis(); + uint32_t curr_ms = tusb_time_millis_api(); if (start_ms == curr_ms) return;// not enough time start_ms = curr_ms; // When new data arrived, copy data from speaker buffer, to microphone buffer @@ -605,7 +605,7 @@ void audio_control_task(void) { static uint32_t start_ms = 0; static uint32_t btn_prev = 0; - if (board_millis() - start_ms < interval_ms) return;// not enough time + if (tusb_time_millis_api() - start_ms < interval_ms) return;// not enough time start_ms += interval_ms; uint32_t btn = board_button_read(); @@ -644,7 +644,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) return; + if (tusb_time_millis_api() - start_ms < blink_interval_ms) return; start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c index 7b4e2d64c..8323d82e8 100644 --- a/examples/device/uac2_speaker_fb/src/main.c +++ b/examples/device/uac2_speaker_fb/src/main.c @@ -583,7 +583,7 @@ bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t fu // In a real application, this would be replaced with actual I2S transmit callback. void audio_task(void) { static uint32_t start_ms = 0; - uint32_t curr_ms = board_millis(); + uint32_t curr_ms = tusb_time_millis_api(); if (start_ms == curr_ms) return;// not enough time start_ms = curr_ms; @@ -610,7 +610,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) return; + if (tusb_time_millis_api() - start_ms < blink_interval_ms) return; start_ms += blink_interval_ms; board_led_write(led_state); @@ -624,7 +624,7 @@ void led_blinking_task(void) { // Every 1ms, we will sent 1 debug information report void audio_debug_task(void) { static uint32_t start_ms = 0; - uint32_t curr_ms = board_millis(); + uint32_t curr_ms = tusb_time_millis_api(); if (start_ms == curr_ms) return;// not enough time start_ms = curr_ms; diff --git a/examples/device/usbtmc/src/main.c b/examples/device/usbtmc/src/main.c index 5cbbb85ef..b1269b117 100644 --- a/examples/device/usbtmc/src/main.c +++ b/examples/device/usbtmc/src/main.c @@ -124,12 +124,12 @@ void led_blinking_task(void) { led_state = true; board_led_write(true); - start_ms = board_millis(); + start_ms = tusb_time_millis_api(); doPulse = false; } else if (led_state == true) { - if ( board_millis() - start_ms < 750) //Spec says blink must be between 500 and 1000 ms. + if ( tusb_time_millis_api() - start_ms < 750) //Spec says blink must be between 500 and 1000 ms. { return; // not enough time } @@ -140,7 +140,7 @@ void led_blinking_task(void) else { // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/examples/device/usbtmc/src/usbtmc_app.c b/examples/device/usbtmc/src/usbtmc_app.c index 4c3724ac4..35e8618f5 100644 --- a/examples/device/usbtmc/src/usbtmc_app.c +++ b/examples/device/usbtmc/src/usbtmc_app.c @@ -209,19 +209,19 @@ void usbtmc_app_task_iter(void) { case 0: break; case 1: - queryDelayStart = board_millis(); + queryDelayStart = tusb_time_millis_api(); queryState = 2; break; case 2: - if( (board_millis() - queryDelayStart) > resp_delay) { - queryDelayStart = board_millis(); + if( (tusb_time_millis_api() - queryDelayStart) > resp_delay) { + queryDelayStart = tusb_time_millis_api(); queryState=3; status |= 0x10u; // MAV status |= 0x40u; // SRQ } break; case 3: - if( (board_millis() - queryDelayStart) > resp_delay) { + if( (tusb_time_millis_api() - queryDelayStart) > resp_delay) { queryState = 4; } break; diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c index ffa2a7afa..df9f77a1c 100644 --- a/examples/device/video_capture/src/main.c +++ b/examples/device/video_capture/src/main.c @@ -231,7 +231,7 @@ static void video_send_frame(void) { if (!already_sent) { already_sent = 1; tx_busy = 1; - start_ms = board_millis(); + start_ms = tusb_time_millis_api(); #if defined(CFG_EXAMPLE_VIDEO_BUFFERLESS) tud_video_n_frame_xfer(0, 0, NULL, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8); #elif defined (CFG_EXAMPLE_VIDEO_READONLY) @@ -247,7 +247,7 @@ static void video_send_frame(void) { #endif } - unsigned cur = board_millis(); + unsigned cur = tusb_time_millis_api(); if (cur - start_ms < interval_ms) { return; // not enough time } @@ -316,7 +316,7 @@ void led_blinking_task(void* param) { #if CFG_TUSB_OS == OPT_OS_FREERTOS vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS); #else - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } #endif diff --git a/examples/device/video_capture_2ch/src/main.c b/examples/device/video_capture_2ch/src/main.c index 79b149f2b..debd336fd 100644 --- a/examples/device/video_capture_2ch/src/main.c +++ b/examples/device/video_capture_2ch/src/main.c @@ -221,13 +221,13 @@ static void video_send_frame(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) { if (!(already_sent & (1u << idx))) { already_sent |= 1u << idx; tx_busy |= 1u << idx; - start_ms[idx] = board_millis(); + start_ms[idx] = tusb_time_millis_api(); fb_size = get_framebuf(ctl_idx, stm_idx, frame_num[idx], &fp); tud_video_n_frame_xfer(ctl_idx, stm_idx, fp, fb_size); } - unsigned cur = board_millis(); + unsigned cur = tusb_time_millis_api(); if (cur - start_ms[idx] < interval_ms[idx]) return; // not enough time if (tx_busy & (1u << idx)) return; start_ms[idx] += interval_ms[idx]; @@ -280,7 +280,7 @@ void led_blinking_task(void* param) { #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 + if (tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time #endif start_ms += blink_interval_ms; diff --git a/examples/device/webusb_serial/src/main.c b/examples/device/webusb_serial/src/main.c index 155768b76..4be5e4db4 100644 --- a/examples/device/webusb_serial/src/main.c +++ b/examples/device/webusb_serial/src/main.c @@ -257,7 +257,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } start_ms += blink_interval_ms; diff --git a/examples/dual/host_hid_to_device_cdc/src/main.c b/examples/dual/host_hid_to_device_cdc/src/main.c index 8c53588c3..ba8ba019a 100644 --- a/examples/dual/host_hid_to_device_cdc/src/main.c +++ b/examples/dual/host_hid_to_device_cdc/src/main.c @@ -284,7 +284,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) return; // not enough time + if (tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/examples/dual/host_info_to_device_cdc/src/main.c b/examples/dual/host_info_to_device_cdc/src/main.c index fffb54d58..7cf43aef3 100644 --- a/examples/dual/host_info_to_device_cdc/src/main.c +++ b/examples/dual/host_info_to_device_cdc/src/main.c @@ -213,13 +213,13 @@ void cdc_task(void) { static uint32_t connected_ms = 0; if (!tud_cdc_connected()) { - connected_ms = board_millis(); + connected_ms = tusb_time_millis_api(); return; } // delay a bit otherwise we can outpace host's terminal. Linux will set LineState (DTR) then Line Coding. // If we send data before Linux's terminal set Line Coding, it can be ignored --> missing data with hardware test loop - if (board_millis() - connected_ms < 100) { + if (tusb_time_millis_api() - connected_ms < 100) { return; // wait for stable connection } @@ -309,7 +309,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return;// not enough time } start_ms += blink_interval_ms; diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c index c693d6b00..ced2eaa32 100644 --- a/examples/host/bare_api/src/main.c +++ b/examples/host/bare_api/src/main.c @@ -335,7 +335,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < interval_ms) { + if (tusb_time_millis_api() - start_ms < interval_ms) { return; // not enough time } start_ms += interval_ms; diff --git a/examples/host/cdc_msc_hid/src/main.c b/examples/host/cdc_msc_hid/src/main.c index c309a7cae..c27ad93fe 100644 --- a/examples/host/cdc_msc_hid/src/main.c +++ b/examples/host/cdc_msc_hid/src/main.c @@ -92,7 +92,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < interval_ms) { + if (tusb_time_millis_api() - start_ms < interval_ms) { return;// not enough time } start_ms += interval_ms; diff --git a/examples/host/device_info/src/main.c b/examples/host/device_info/src/main.c index ab617e989..fd4e9c3ed 100644 --- a/examples/host/device_info/src/main.c +++ b/examples/host/device_info/src/main.c @@ -246,7 +246,7 @@ void led_blinking_task(void* param) { #if CFG_TUSB_OS == OPT_OS_FREERTOS vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS); #else - if (board_millis() - start_ms < blink_interval_ms) { + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { return; // not enough time } #endif diff --git a/examples/host/hid_controller/src/hid_app.c b/examples/host/hid_controller/src/hid_app.c index f8c3d029b..5417811f0 100644 --- a/examples/host/hid_controller/src/hid_app.c +++ b/examples/host/hid_controller/src/hid_app.c @@ -168,7 +168,7 @@ void hid_app_task(void) const uint32_t interval_ms = 200; static uint32_t start_ms = 0; - uint32_t current_time_ms = board_millis(); + uint32_t current_time_ms = tusb_time_millis_api(); if ( current_time_ms - start_ms >= interval_ms) { start_ms = current_time_ms; diff --git a/examples/host/hid_controller/src/main.c b/examples/host/hid_controller/src/main.c index fa70d7d1a..a9eebc90b 100644 --- a/examples/host/hid_controller/src/main.c +++ b/examples/host/hid_controller/src/main.c @@ -79,7 +79,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if ( board_millis() - start_ms < interval_ms) return; // not enough time + if ( tusb_time_millis_api() - start_ms < interval_ms) return; // not enough time start_ms += interval_ms; board_led_write(led_state); diff --git a/examples/host/midi_rx/src/main.c b/examples/host/midi_rx/src/main.c index 78b1a11b9..f189e0864 100644 --- a/examples/host/midi_rx/src/main.c +++ b/examples/host/midi_rx/src/main.c @@ -72,7 +72,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < interval_ms) return;// not enough time + if (tusb_time_millis_api() - start_ms < interval_ms) return;// not enough time start_ms += interval_ms; board_led_write(led_state); diff --git a/examples/host/msc_file_explorer/src/main.c b/examples/host/msc_file_explorer/src/main.c index f9ec0ff5f..0a8967380 100644 --- a/examples/host/msc_file_explorer/src/main.c +++ b/examples/host/msc_file_explorer/src/main.c @@ -118,7 +118,7 @@ void led_blinking_task(void) { static bool led_state = false; // Blink every interval ms - if (board_millis() - start_ms < interval_ms) return; // not enough time + if (tusb_time_millis_api() - start_ms < interval_ms) return; // not enough time start_ms += interval_ms; board_led_write(led_state); diff --git a/examples/typec/power_delivery/src/main.c b/examples/typec/power_delivery/src/main.c index de0db4721..f6191bfe8 100644 --- a/examples/typec/power_delivery/src/main.c +++ b/examples/typec/power_delivery/src/main.c @@ -184,7 +184,7 @@ void led_blinking_task(void) static bool led_state = false; // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + if ( tusb_time_millis_api() - start_ms < blink_interval_ms) return; // not enough time start_ms += blink_interval_ms; board_led_write(led_state); diff --git a/hw/bsp/at32f402_405/family.c b/hw/bsp/at32f402_405/family.c index a6c2217fe..b7dbcbd98 100644 --- a/hw/bsp/at32f402_405/family.c +++ b/hw/bsp/at32f402_405/family.c @@ -258,7 +258,7 @@ size_t board_get_unique_id(uint8_t id[], size_t max_len) { system_ticks++; } - uint32_t board_millis(void) + uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/at32f403a_407/family.c b/hw/bsp/at32f403a_407/family.c index dd9b85dc5..d4a7e446d 100644 --- a/hw/bsp/at32f403a_407/family.c +++ b/hw/bsp/at32f403a_407/family.c @@ -250,7 +250,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/at32f413/family.c b/hw/bsp/at32f413/family.c index bdaed523c..adf29e097 100644 --- a/hw/bsp/at32f413/family.c +++ b/hw/bsp/at32f413/family.c @@ -250,7 +250,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/at32f415/family.c b/hw/bsp/at32f415/family.c index 2fbd4c821..b592bf6c5 100644 --- a/hw/bsp/at32f415/family.c +++ b/hw/bsp/at32f415/family.c @@ -246,7 +246,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/at32f423/family.c b/hw/bsp/at32f423/family.c index f30c6a83f..71cb559dc 100644 --- a/hw/bsp/at32f423/family.c +++ b/hw/bsp/at32f423/family.c @@ -250,7 +250,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/at32f425/family.c b/hw/bsp/at32f425/family.c index 4ff4c8d6a..7f443509e 100644 --- a/hw/bsp/at32f425/family.c +++ b/hw/bsp/at32f425/family.c @@ -254,7 +254,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/at32f435_437/family.c b/hw/bsp/at32f435_437/family.c index 01dd429f8..6c6bc4d72 100644 --- a/hw/bsp/at32f435_437/family.c +++ b/hw/bsp/at32f435_437/family.c @@ -320,7 +320,7 @@ volatile uint32_t system_ticks = 0; void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } void SVC_Handler(void) { diff --git a/hw/bsp/at32f45x/family.c b/hw/bsp/at32f45x/family.c index 0593e5115..fa0c1139f 100644 --- a/hw/bsp/at32f45x/family.c +++ b/hw/bsp/at32f45x/family.c @@ -227,7 +227,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/board.c b/hw/bsp/board.c index 483d9dc28..91e7de9fe 100644 --- a/hw/bsp/board.c +++ b/hw/bsp/board.c @@ -161,10 +161,6 @@ void board_putchar(int c) { (void) sys_write(0, (const char*)&c, 1); } -uint32_t tusb_time_millis_api(void) { - return board_millis(); -} - //-------------------------------------------------------------------- // FreeRTOS hooks //-------------------------------------------------------------------- diff --git a/hw/bsp/board_api.h b/hw/bsp/board_api.h index 606ac484f..4487871eb 100644 --- a/hw/bsp/board_api.h +++ b/hw/bsp/board_api.h @@ -98,43 +98,6 @@ int board_uart_read(uint8_t *buf, int len); // Send characters to UART. Return number of sent bytes int board_uart_write(void const *buf, int len); -#if CFG_TUSB_OS == OPT_OS_NONE -// Get current milliseconds, must be implemented when no RTOS is used -uint32_t board_millis(void); - -#elif CFG_TUSB_OS == OPT_OS_FREERTOS -static inline uint32_t board_millis(void) { - return ( ( ((uint64_t) xTaskGetTickCount()) * 1000) / configTICK_RATE_HZ ); -} - -#elif CFG_TUSB_OS == OPT_OS_MYNEWT -static inline uint32_t board_millis(void) { - return os_time_ticks_to_ms32( os_time_get() ); -} - -#elif CFG_TUSB_OS == OPT_OS_PICO -#include "pico/time.h" -static inline uint32_t board_millis(void) { - return to_ms_since_boot(get_absolute_time()); -} - -#elif CFG_TUSB_OS == OPT_OS_RTTHREAD -static inline uint32_t board_millis(void) { - return (((uint64_t)rt_tick_get()) * 1000 / RT_TICK_PER_SECOND); -} - -#elif CFG_TUSB_OS == OPT_OS_CUSTOM -// Implement your own board_millis() in any of .c file -uint32_t board_millis(void); - -#elif CFG_TUSB_OS == OPT_OS_ZEPHYR -static inline uint32_t board_millis(void) { - return k_uptime_get_32(); -} -#else - #error "board_millis() is not implemented for this OS" -#endif - //--------------------------------------------------------------------+ // Helper functions //--------------------------------------------------------------------+ @@ -175,8 +138,8 @@ static inline size_t board_usb_get_serial(uint16_t desc_str1[], size_t max_chars // TODO remove static inline void board_delay(uint32_t ms) { - uint32_t start_ms = board_millis(); - while ( board_millis() - start_ms < ms ) { + uint32_t start_ms = tusb_time_millis_api(); + while ( tusb_time_millis_api() - start_ms < ms ) { // take chance to run usb background #if CFG_TUD_ENABLED tud_task(); diff --git a/hw/bsp/broadcom_32bit/family.c b/hw/bsp/broadcom_32bit/family.c index f8f3b0b70..399397bb4 100644 --- a/hw/bsp/broadcom_32bit/family.c +++ b/hw/bsp/broadcom_32bit/family.c @@ -146,7 +146,7 @@ void TIMER_1_IRQHandler(void) { SYSTMR->CS_b.M1 = 1; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/broadcom_64bit/family.c b/hw/bsp/broadcom_64bit/family.c index f8f3b0b70..399397bb4 100644 --- a/hw/bsp/broadcom_64bit/family.c +++ b/hw/bsp/broadcom_64bit/family.c @@ -146,7 +146,7 @@ void TIMER_1_IRQHandler(void) { SYSTMR->CS_b.M1 = 1; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/brtmm90x/family.c b/hw/bsp/brtmm90x/family.c index 15ff4b8ee..ff24cfe89 100644 --- a/hw/bsp/brtmm90x/family.c +++ b/hw/bsp/brtmm90x/family.c @@ -234,7 +234,7 @@ int board_uart_write(void const *buf, int len) } // Get current milliseconds -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { uint32_t safe_ms; diff --git a/hw/bsp/ch32f20x/family.c b/hw/bsp/ch32f20x/family.c index 7eae62fa4..dd84b7c77 100644 --- a/hw/bsp/ch32f20x/family.c +++ b/hw/bsp/ch32f20x/family.c @@ -101,7 +101,7 @@ void SysTick_Handler(void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/ch32v10x/family.c b/hw/bsp/ch32v10x/family.c index dfc041462..344dcaf0b 100644 --- a/hw/bsp/ch32v10x/family.c +++ b/hw/bsp/ch32v10x/family.c @@ -61,7 +61,7 @@ static uint32_t SysTick_Config(uint32_t ticks) { return 0; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/ch32v20x/family.c b/hw/bsp/ch32v20x/family.c index 690acee1e..4c22450f9 100644 --- a/hw/bsp/ch32v20x/family.c +++ b/hw/bsp/ch32v20x/family.c @@ -85,7 +85,7 @@ static uint32_t SysTick_Config(uint32_t ticks) { return 0; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/ch32v30x/family.c b/hw/bsp/ch32v30x/family.c index c694f1a08..6295f7723 100644 --- a/hw/bsp/ch32v30x/family.c +++ b/hw/bsp/ch32v30x/family.c @@ -144,7 +144,7 @@ __attribute__((interrupt)) void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/cxd56/family.c b/hw/bsp/cxd56/family.c index a8e2fd52b..fd92bb9d9 100644 --- a/hw/bsp/cxd56/family.c +++ b/hw/bsp/cxd56/family.c @@ -96,7 +96,7 @@ int board_uart_write(void const *buf, int len) } // Get current milliseconds -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { struct timespec tp; diff --git a/hw/bsp/da1469x/family.c b/hw/bsp/da1469x/family.c index a64ffce67..a4f7f2e8d 100644 --- a/hw/bsp/da1469x/family.c +++ b/hw/bsp/da1469x/family.c @@ -140,7 +140,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/efm32/family.c b/hw/bsp/efm32/family.c index 39166bc4d..d318e20d6 100644 --- a/hw/bsp/efm32/family.c +++ b/hw/bsp/efm32/family.c @@ -689,7 +689,7 @@ void SysTick_Handler(void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/f1c100s/family.c b/hw/bsp/f1c100s/family.c index 9e864363f..1e71333d4 100644 --- a/hw/bsp/f1c100s/family.c +++ b/hw/bsp/f1c100s/family.c @@ -79,7 +79,7 @@ int board_uart_write(void const* buf, int len) { #if CFG_TUSB_OS == OPT_OS_NONE volatile uint32_t system_ticks = 0; -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/fomu/family.c b/hw/bsp/fomu/family.c index 9d7977bea..cf04a1f6f 100644 --- a/hw/bsp/fomu/family.c +++ b/hw/bsp/fomu/family.c @@ -117,7 +117,7 @@ int board_uart_write(void const * buf, int len) } #if CFG_TUSB_OS == OPT_OS_NONE -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/gd32vf103/family.c b/hw/bsp/gd32vf103/family.c index 9d15755fc..4c1099317 100644 --- a/hw/bsp/gd32vf103/family.c +++ b/hw/bsp/gd32vf103/family.c @@ -179,7 +179,7 @@ void eclic_mtip_handler(void) { system_ticks++; SysTick_Reload(TIMER_TICKS); } -uint32_t board_millis(void) { return system_ticks; } +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif #ifdef USE_FULL_ASSERT diff --git a/hw/bsp/hpmicro/family.c b/hw/bsp/hpmicro/family.c index ffec2523a..a80a8d913 100644 --- a/hw/bsp/hpmicro/family.c +++ b/hw/bsp/hpmicro/family.c @@ -108,7 +108,7 @@ int board_uart_write(void const *buf, int len) { #if CFG_TUSB_OS == OPT_OS_NONE // Get current milliseconds, must be implemented when no RTOS is used -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return (hpm_csr_get_core_cycle() / clock_get_core_clock_ticks_per_ms()); } diff --git a/hw/bsp/imxrt/family.c b/hw/bsp/imxrt/family.c index d54b41bdb..c1ee34b1a 100644 --- a/hw/bsp/imxrt/family.c +++ b/hw/bsp/imxrt/family.c @@ -239,7 +239,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/kinetis_k/family.c b/hw/bsp/kinetis_k/family.c index 98ef52739..1505defe0 100644 --- a/hw/bsp/kinetis_k/family.c +++ b/hw/bsp/kinetis_k/family.c @@ -143,7 +143,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/kinetis_k32l2/family.c b/hw/bsp/kinetis_k32l2/family.c index 2062b8b18..ec8dc6ecf 100644 --- a/hw/bsp/kinetis_k32l2/family.c +++ b/hw/bsp/kinetis_k32l2/family.c @@ -148,7 +148,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/kinetis_kl/family.c b/hw/bsp/kinetis_kl/family.c index c257f4b2b..f89434d06 100644 --- a/hw/bsp/kinetis_kl/family.c +++ b/hw/bsp/kinetis_kl/family.c @@ -142,7 +142,7 @@ void SysTick_Handler(void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/lpc11/family.c b/hw/bsp/lpc11/family.c index c9f18bd2f..76c2bd17c 100644 --- a/hw/bsp/lpc11/family.c +++ b/hw/bsp/lpc11/family.c @@ -130,7 +130,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/lpc13/family.c b/hw/bsp/lpc13/family.c index e212c6a63..8513f4df4 100644 --- a/hw/bsp/lpc13/family.c +++ b/hw/bsp/lpc13/family.c @@ -83,7 +83,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/lpc15/family.c b/hw/bsp/lpc15/family.c index 5f22df175..0d092d3a9 100644 --- a/hw/bsp/lpc15/family.c +++ b/hw/bsp/lpc15/family.c @@ -140,7 +140,7 @@ void SysTick_Handler (void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/lpc17/family.c b/hw/bsp/lpc17/family.c index ba59fccca..f398f7e3c 100644 --- a/hw/bsp/lpc17/family.c +++ b/hw/bsp/lpc17/family.c @@ -145,7 +145,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/lpc18/family.c b/hw/bsp/lpc18/family.c index 6c02c711f..2043cef99 100644 --- a/hw/bsp/lpc18/family.c +++ b/hw/bsp/lpc18/family.c @@ -143,7 +143,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/lpc40/family.c b/hw/bsp/lpc40/family.c index 5ea95e9b8..237cd996b 100644 --- a/hw/bsp/lpc40/family.c +++ b/hw/bsp/lpc40/family.c @@ -156,7 +156,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/lpc43/family.c b/hw/bsp/lpc43/family.c index f440fb119..bade53b07 100644 --- a/hw/bsp/lpc43/family.c +++ b/hw/bsp/lpc43/family.c @@ -257,7 +257,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/lpc51/family.c b/hw/bsp/lpc51/family.c index bec86f87f..847972350 100644 --- a/hw/bsp/lpc51/family.c +++ b/hw/bsp/lpc51/family.c @@ -122,7 +122,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/lpc54/family.c b/hw/bsp/lpc54/family.c index 7bb73afbc..945e27154 100644 --- a/hw/bsp/lpc54/family.c +++ b/hw/bsp/lpc54/family.c @@ -217,7 +217,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/lpc55/family.c b/hw/bsp/lpc55/family.c index ad0e502b5..7485ed4d5 100644 --- a/hw/bsp/lpc55/family.c +++ b/hw/bsp/lpc55/family.c @@ -350,7 +350,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/maxim/family.c b/hw/bsp/maxim/family.c index 7ad7d6ff9..6ef4c12c1 100644 --- a/hw/bsp/maxim/family.c +++ b/hw/bsp/maxim/family.c @@ -202,7 +202,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/mcx/family.c b/hw/bsp/mcx/family.c index 3b91678b1..a3969c217 100644 --- a/hw/bsp/mcx/family.c +++ b/hw/bsp/mcx/family.c @@ -219,7 +219,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/mm32/family.c b/hw/bsp/mm32/family.c index 663c30818..330b01f6d 100644 --- a/hw/bsp/mm32/family.c +++ b/hw/bsp/mm32/family.c @@ -175,7 +175,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/msp430/family.c b/hw/bsp/msp430/family.c index 390a9915e..413ad7db6 100644 --- a/hw/bsp/msp430/family.c +++ b/hw/bsp/msp430/family.c @@ -102,10 +102,10 @@ static void SystemClock_Config(void) // VUSB enabled automatically. // Wait two milliseconds to stabilize, per manual recommendation. - uint32_t ms_elapsed = board_millis(); + uint32_t ms_elapsed = tusb_time_millis_api(); do { - while((board_millis() - ms_elapsed) < 2); + while((tusb_time_millis_api() - ms_elapsed) < 2); }while(!(USBPWRCTL & USBBGVBV)); // USB uses XT2 (4 MHz) directly. Enable the PLL. @@ -113,11 +113,11 @@ static void SystemClock_Config(void) USBPLLCTL |= (UPFDEN | UPLLEN); // Wait until PLL locks. Check every 2ms, per manual. - ms_elapsed = board_millis(); + ms_elapsed = tusb_time_millis_api(); do { USBPLLIR &= ~USBOOLIFG; - while((board_millis() - ms_elapsed) < 2); + while((tusb_time_millis_api() - ms_elapsed) < 2); }while(USBPLLIR & USBOOLIFG); USBKEYPID = 0; @@ -207,7 +207,7 @@ void TIMER0_A0_ISR (void) { // TAxCCR0 CCIFG resets itself as soon as interrupt is invoked. } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { uint32_t systick_mirror; diff --git a/hw/bsp/msp432e4/family.c b/hw/bsp/msp432e4/family.c index 0e1b0528a..90eb945f4 100644 --- a/hw/bsp/msp432e4/family.c +++ b/hw/bsp/msp432e4/family.c @@ -202,7 +202,7 @@ void SysTick_Handler(void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/nrf/family.c b/hw/bsp/nrf/family.c index ee3ac61e2..04bfbf320 100644 --- a/hw/bsp/nrf/family.c +++ b/hw/bsp/nrf/family.c @@ -296,7 +296,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/nuc100_120/family.c b/hw/bsp/nuc100_120/family.c index 752af2a56..cfe60121c 100644 --- a/hw/bsp/nuc100_120/family.c +++ b/hw/bsp/nuc100_120/family.c @@ -87,7 +87,7 @@ void SysTick_Handler (void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/nuc121_125/family.c b/hw/bsp/nuc121_125/family.c index 089855207..dce5b4d62 100644 --- a/hw/bsp/nuc121_125/family.c +++ b/hw/bsp/nuc121_125/family.c @@ -86,7 +86,7 @@ void SysTick_Handler (void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/nuc126/family.c b/hw/bsp/nuc126/family.c index f992fcab4..3343064e5 100644 --- a/hw/bsp/nuc126/family.c +++ b/hw/bsp/nuc126/family.c @@ -111,7 +111,7 @@ void SysTick_Handler (void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/nuc505/family.c b/hw/bsp/nuc505/family.c index 00ed92310..f1a77e4a5 100644 --- a/hw/bsp/nuc505/family.c +++ b/hw/bsp/nuc505/family.c @@ -88,7 +88,7 @@ void SysTick_Handler (void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/pic32mz/family.c b/hw/bsp/pic32mz/family.c index da97f67a9..2bfc876e1 100644 --- a/hw/bsp/pic32mz/family.c +++ b/hw/bsp/pic32mz/family.c @@ -106,7 +106,7 @@ TU_ATTR_WEAK int board_uart_write(void const * buf, int len) } #if CFG_TUSB_OS == OPT_OS_NONE -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { // COUNTER is system clock (200MHz / 2 = 100MHz) convert to ms) return _CP0_GET_COUNT() / (100000000 / 1000); diff --git a/hw/bsp/ra/family.c b/hw/bsp/ra/family.c index 1f75b47c1..f371e694b 100644 --- a/hw/bsp/ra/family.c +++ b/hw/bsp/ra/family.c @@ -169,7 +169,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/rw61x/family.c b/hw/bsp/rw61x/family.c index 265d5fcc0..fcc7fb262 100644 --- a/hw/bsp/rw61x/family.c +++ b/hw/bsp/rw61x/family.c @@ -128,7 +128,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/rx/boards/gr_citrus/gr_citrus.c b/hw/bsp/rx/boards/gr_citrus/gr_citrus.c index 26ad4a6aa..e5b24bf69 100644 --- a/hw/bsp/rx/boards/gr_citrus/gr_citrus.c +++ b/hw/bsp/rx/boards/gr_citrus/gr_citrus.c @@ -248,7 +248,7 @@ void INT_Excep_CMT0_CMI0(void) ++system_ticks; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/rx/boards/rx65n_target/rx65n_target.c b/hw/bsp/rx/boards/rx65n_target/rx65n_target.c index 66a319541..d5c2de05a 100644 --- a/hw/bsp/rx/boards/rx65n_target/rx65n_target.c +++ b/hw/bsp/rx/boards/rx65n_target/rx65n_target.c @@ -299,7 +299,7 @@ void INT_Excep_CMT0_CMI0(void) ++system_ticks; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/samd11/family.c b/hw/bsp/samd11/family.c index 6cbf02412..bccbac8ea 100644 --- a/hw/bsp/samd11/family.c +++ b/hw/bsp/samd11/family.c @@ -155,7 +155,7 @@ void SysTick_Handler (void) system_ticks++; } -uint32_t board_millis(void) +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/samd2x_l2x/family.c b/hw/bsp/samd2x_l2x/family.c index a2dc8a8d4..737219b6c 100644 --- a/hw/bsp/samd2x_l2x/family.c +++ b/hw/bsp/samd2x_l2x/family.c @@ -361,7 +361,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/samd5x_e5x/family.c b/hw/bsp/samd5x_e5x/family.c index 5a7105894..c008b9719 100644 --- a/hw/bsp/samd5x_e5x/family.c +++ b/hw/bsp/samd5x_e5x/family.c @@ -210,7 +210,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/same7x/family.c b/hw/bsp/same7x/family.c index 6feefa3b5..ff6ecf277 100644 --- a/hw/bsp/same7x/family.c +++ b/hw/bsp/same7x/family.c @@ -192,7 +192,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/samg/family.c b/hw/bsp/samg/family.c index 5c5fc3c14..b27134305 100644 --- a/hw/bsp/samg/family.c +++ b/hw/bsp/samg/family.c @@ -148,7 +148,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32c0/family.c b/hw/bsp/stm32c0/family.c index e2ba47f45..e20c0ee15 100644 --- a/hw/bsp/stm32c0/family.c +++ b/hw/bsp/stm32c0/family.c @@ -173,7 +173,7 @@ void SysTick_Handler(void) { HAL_IncTick(); } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/stm32f0/family.c b/hw/bsp/stm32f0/family.c index b99b0a8cc..5a35a3e50 100644 --- a/hw/bsp/stm32f0/family.c +++ b/hw/bsp/stm32f0/family.c @@ -160,7 +160,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32f1/family.c b/hw/bsp/stm32f1/family.c index 3147061cf..fae61ca9e 100644 --- a/hw/bsp/stm32f1/family.c +++ b/hw/bsp/stm32f1/family.c @@ -202,7 +202,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32f2/family.c b/hw/bsp/stm32f2/family.c index f95128040..b901bf4cc 100644 --- a/hw/bsp/stm32f2/family.c +++ b/hw/bsp/stm32f2/family.c @@ -145,7 +145,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/stm32f3/family.c b/hw/bsp/stm32f3/family.c index 95bcc7882..fde1e9f6d 100644 --- a/hw/bsp/stm32f3/family.c +++ b/hw/bsp/stm32f3/family.c @@ -156,7 +156,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32f4/family.c b/hw/bsp/stm32f4/family.c index f0e9620f2..665ea114a 100644 --- a/hw/bsp/stm32f4/family.c +++ b/hw/bsp/stm32f4/family.c @@ -251,7 +251,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32f7/family.c b/hw/bsp/stm32f7/family.c index d8f0da201..ce9049abe 100644 --- a/hw/bsp/stm32f7/family.c +++ b/hw/bsp/stm32f7/family.c @@ -312,7 +312,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32g0/family.c b/hw/bsp/stm32g0/family.c index 4b175b0ec..b25897264 100644 --- a/hw/bsp/stm32g0/family.c +++ b/hw/bsp/stm32g0/family.c @@ -181,7 +181,7 @@ void SysTick_Handler(void) { HAL_IncTick(); } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/stm32g4/family.c b/hw/bsp/stm32g4/family.c index d8afa0f95..2e13a1d4b 100644 --- a/hw/bsp/stm32g4/family.c +++ b/hw/bsp/stm32g4/family.c @@ -211,7 +211,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/stm32h5/family.c b/hw/bsp/stm32h5/family.c index fdb12e44f..1e8acd502 100644 --- a/hw/bsp/stm32h5/family.c +++ b/hw/bsp/stm32h5/family.c @@ -207,7 +207,7 @@ void SysTick_Handler(void) { HAL_IncTick(); } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32h7/family.c b/hw/bsp/stm32h7/family.c index a320a7e72..2759dac63 100644 --- a/hw/bsp/stm32h7/family.c +++ b/hw/bsp/stm32h7/family.c @@ -295,7 +295,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32h7rs/family.c b/hw/bsp/stm32h7rs/family.c index 2cc39b7ac..3bf75ba97 100644 --- a/hw/bsp/stm32h7rs/family.c +++ b/hw/bsp/stm32h7rs/family.c @@ -468,7 +468,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32l0/family.c b/hw/bsp/stm32l0/family.c index 6aeab1259..192f014f4 100644 --- a/hw/bsp/stm32l0/family.c +++ b/hw/bsp/stm32l0/family.c @@ -168,7 +168,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/stm32l4/family.c b/hw/bsp/stm32l4/family.c index 65f6b9ab3..c87b643b8 100644 --- a/hw/bsp/stm32l4/family.c +++ b/hw/bsp/stm32l4/family.c @@ -231,7 +231,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32n6/family.c b/hw/bsp/stm32n6/family.c index c839e6b3e..4354616c3 100644 --- a/hw/bsp/stm32n6/family.c +++ b/hw/bsp/stm32n6/family.c @@ -366,7 +366,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32u0/family.c b/hw/bsp/stm32u0/family.c index 50b513d8f..0f91d1f30 100644 --- a/hw/bsp/stm32u0/family.c +++ b/hw/bsp/stm32u0/family.c @@ -174,7 +174,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif diff --git a/hw/bsp/stm32u5/family.c b/hw/bsp/stm32u5/family.c index dfcf5c537..55ca25d58 100644 --- a/hw/bsp/stm32u5/family.c +++ b/hw/bsp/stm32u5/family.c @@ -270,7 +270,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32wb/family.c b/hw/bsp/stm32wb/family.c index 153d10a09..de503c072 100644 --- a/hw/bsp/stm32wb/family.c +++ b/hw/bsp/stm32wb/family.c @@ -174,7 +174,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/stm32wba/family.c b/hw/bsp/stm32wba/family.c index d05415755..e058a80a1 100644 --- a/hw/bsp/stm32wba/family.c +++ b/hw/bsp/stm32wba/family.c @@ -198,7 +198,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { return system_ticks; } +uint32_t tusb_time_millis_api(void) { return system_ticks; } #endif void HardFault_Handler(void) { asm( "bkpt 1" ); } diff --git a/hw/bsp/tm4c/family.c b/hw/bsp/tm4c/family.c index ae7f22f00..503d0a8c9 100644 --- a/hw/bsp/tm4c/family.c +++ b/hw/bsp/tm4c/family.c @@ -211,7 +211,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/hw/bsp/xmc4000/family.c b/hw/bsp/xmc4000/family.c index 6fef53025..d0acd04cb 100644 --- a/hw/bsp/xmc4000/family.c +++ b/hw/bsp/xmc4000/family.c @@ -147,7 +147,7 @@ void SysTick_Handler(void) { system_ticks++; } -uint32_t board_millis(void) { +uint32_t tusb_time_millis_api(void) { return system_ticks; } diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index 9eb0a9337..6ac1405f3 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -90,6 +90,12 @@ // TODO move to a more obvious place/file //--------------------------------------------------------------------+ +// Get current milliseconds, required by some port/configuration without RTOS +extern uint32_t tusb_time_millis_api(void); + +// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS +extern void tusb_time_delay_ms_api(uint32_t ms); + // flush data cache extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size); diff --git a/src/host/usbh.c b/src/host/usbh.c index 60d78605f..0da5166c2 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -685,7 +685,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) { has_deferred_attach = osal_queue_receive(_usbh_daq, &event, 0); } - if (!has_deferred_attach) // skip event queue to process deferred at + if (!has_deferred_attach) // skip event queue to process deferred attach #endif { if (!osal_queue_receive(_usbh_q, &event, timeout_ms)) { diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c index b413121a5..9fac0bc1c 100644 --- a/src/portable/sunxi/dcd_sunxi_musb.c +++ b/src/portable/sunxi/dcd_sunxi_musb.c @@ -36,9 +36,6 @@ #include #include "musb_def.h" -//#include "bsp/board_api.h" -extern uint32_t board_millis(void); // TODO remove - typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; diff --git a/src/tusb.c b/src/tusb.c index 40d0e8adf..37aecf693 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -45,21 +45,24 @@ tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM] = { TUSB_ROLE_INVALID }; // Weak/Default API, can be overwritten by Application //-------------------------------------------------------------------- - #if CFG_TUSB_OS != OPT_OS_NONE -uint32_t tusb_time_millis_api(void) { +#if CFG_TUSB_OS != OPT_OS_NONE +TU_ATTR_WEAK uint32_t tusb_time_millis_api(void) { return osal_time_millis(); } - #endif TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms) { -#if CFG_TUSB_OS != OPT_OS_NONE osal_task_delay(ms); +} + #else +// tusb_time_millis_api() must be implemented by user application. + +TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms) { // delay using millis() const uint32_t time_ms = tusb_time_millis_api(); while ((tusb_time_millis_api() - time_ms) < ms) {} -#endif } +#endif TU_ATTR_WEAK void *tusb_app_virt_to_phys(void *virt_addr) { return virt_addr; diff --git a/src/tusb.h b/src/tusb.h index 742009a2e..3876bf863 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -174,16 +174,6 @@ bool tusb_deinit(uint8_t rhport); #endif -//--------------------------------------------------------------------+ -// -//--------------------------------------------------------------------+ - -// Get current milliseconds, required by some port/configuration without RTOS -extern uint32_t tusb_time_millis_api(void); - -// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS -extern void tusb_time_delay_ms_api(uint32_t ms); - #ifdef __cplusplus } #endif -- cgit v1.3.1 From 5838c7f09dda8921fe6f350871211f8bd5a97208 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 6 Mar 2026 15:11:59 +0700 Subject: update printer class: enhance descriptors, buffer sizes, and callbacks --- AGENTS.md | 3 + examples/device/CMakeLists.txt | 1 + examples/device/printer_to_hid/src/main.c | 18 +- examples/device/printer_to_hid/src/tusb_config.h | 10 +- .../device/printer_to_hid/src/usb_descriptors.c | 243 +++++++++++++-------- .../device/printer_to_hid/src/usb_descriptors.h | 47 +--- src/class/printer/printer.h | 29 ++- src/class/printer/printer_device.c | 105 +++++---- src/class/printer/printer_device.h | 22 +- src/device/usbd.h | 15 -- src/tusb.h | 4 - 11 files changed, 284 insertions(+), 213 deletions(-) (limited to 'src/tusb.h') diff --git a/AGENTS.md b/AGENTS.md index 34fc57cb8..bbbd7c36d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -332,6 +332,9 @@ take 2-5 minutes. NEVER CANCEL. Set timeout to 20+ minutes. - `examples/device/cdc_msc/`: Most commonly used example for testing - `test/unit-test/project.yml`: Ceedling test configuration +#### MCU Reference Manuals and Datasheets +- Look in `$HOME/Documents/Calibre Library` for all MCU reference manuals, datasheets and board schematics. + #### Debugging Build Issues - **Missing compiler**: Install `gcc-arm-none-eabi` package - **Missing dependencies**: Run `python3 tools/get_deps.py FAMILY` diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt index 660df67cb..dbcb8df6a 100644 --- a/examples/device/CMakeLists.txt +++ b/examples/device/CMakeLists.txt @@ -30,6 +30,7 @@ set(EXAMPLE_LIST msc_dual_lun mtp net_lwip_webserver + printer_to_hid uac2_headset uac2_speaker_fb usbtmc diff --git a/examples/device/printer_to_hid/src/main.c b/examples/device/printer_to_hid/src/main.c index 765d1e653..02b54c7d0 100644 --- a/examples/device/printer_to_hid/src/main.c +++ b/examples/device/printer_to_hid/src/main.c @@ -81,7 +81,7 @@ static void hid_tx_task(void) { return; } - if (board_millis() - start_ms < interval_ms) { + if (tusb_time_millis_api() - start_ms < interval_ms) { return; // not enough time } start_ms += interval_ms; @@ -208,6 +208,22 @@ void tud_printer_rx_cb(uint8_t itf, size_t n) { pending_bytes_on_usb_ep += n; // count pending bytes, counter must decrement when reading from the endpoint buffer } +// IEEE 1284 Device ID: first 2 bytes are big-endian total length (including the 2 length bytes). +// The rest is the Device ID string using standard abbreviated keys. +static const char printer_device_id[] = + "\x00\x34" // total length = 52 = 0x0034 (big-endian) + "MFG:TinyUSB;" + "MDL:Printer to HID;" + "CMD:PS;" + "CLS:PRINTER;"; + +TU_VERIFY_STATIC(sizeof(printer_device_id) - 1 == 52, "device ID length mismatch"); + +uint8_t const *tud_printer_get_device_id_cb(uint8_t itf) { + (void)itf; + return (uint8_t const *)printer_device_id; +} + //--------------------------------------------------------------------+ // HID callbacks diff --git a/examples/device/printer_to_hid/src/tusb_config.h b/examples/device/printer_to_hid/src/tusb_config.h index a8dbac89d..0988be166 100644 --- a/examples/device/printer_to_hid/src/tusb_config.h +++ b/examples/device/printer_to_hid/src/tusb_config.h @@ -34,8 +34,6 @@ extern "C" { // Board Specific Configuration //--------------------------------------------------------------------+ -#define BOARD_DEVICE_RHPORT_NUM 3 - // RHPort number used for device can be defined by board.mk, default to port 0 #ifndef BOARD_TUD_RHPORT #define BOARD_TUD_RHPORT 0 @@ -103,10 +101,10 @@ extern "C" { // HID buffer size Should be sufficient to hold ID (if any) + Data #define CFG_TUD_HID_EP_BUFSIZE 16 -// Printer buffer size Should be sufficient to hold data -#define CFG_TUD_PRINTER_RX_BUFSIZE 16 -#define CFG_TUD_PRINTER_TX_BUFSIZE 16 -#define CFG_TUD_PRINTER_EP_BUFSIZE 16 +// Printer buffer sizes +#define CFG_TUD_PRINTER_RX_BUFSIZE 512 +#define CFG_TUD_PRINTER_TX_BUFSIZE 512 +#define CFG_TUD_PRINTER_EP_BUFSIZE 512 #ifdef __cplusplus } diff --git a/examples/device/printer_to_hid/src/usb_descriptors.c b/examples/device/printer_to_hid/src/usb_descriptors.c index 9a88cb891..1cbabb02e 100644 --- a/examples/device/printer_to_hid/src/usb_descriptors.c +++ b/examples/device/printer_to_hid/src/usb_descriptors.c @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2026 Ha Thach (tinyusb.org) + * Copyright (c) 2019 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 @@ -28,117 +28,188 @@ #include "usb_descriptors.h" +#define USB_VID 0xCafe +#define USB_PID 0x4004 +#define USB_BCD 0x0200 //--------------------------------------------------------------------+ -// Report definitions +// Device Descriptors //--------------------------------------------------------------------+ +static tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, -// Values of the string descriptors. Order must match the order defined by STRING_DESCRIPTOR_INDICES. -const char *STRING_DESCRIPTOR_VALUES[] = { - (const char[]){0x09, 0x04}, // 0: Supported language is English (0x0409) - "TinyUSB", // 1: Manufacturer - "TinyUSB Device", // 2: Product - NULL, // 3: Serial number, will use unique ID from the Pi Pico board hardware - "Config1", // 4: Configuration - "Hid1", // 5: HID interface - "Print1", // 6: Printer interface + .bNumConfigurations = 0x01 }; -uint8_t HID_REPORT_DESCRIPTOR[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD))}; +uint8_t const *tud_descriptor_device_cb(void) { + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// HID Report Descriptor +//--------------------------------------------------------------------+ +static uint8_t const desc_hid_report[] = { + TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)) +}; -uint8_t CONFIG_INTERFACE_ENDPOINT_DESCRIPTOR[] = { +uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) { + (void)instance; + return desc_hid_report; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +// Endpoint numbers +#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) + #define EPNUM_HID 0x81 + #define EPNUM_PRINTER_OUT 0x02 + #define EPNUM_PRINTER_IN 0x83 +#else + #define EPNUM_HID 0x81 + #define EPNUM_PRINTER_OUT 0x02 + #define EPNUM_PRINTER_IN 0x82 +#endif + +// full speed configuration +static uint8_t const desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_COUNT, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), - // HID: // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval - TUD_HID_DESCRIPTOR(ITF_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(HID_REPORT_DESCRIPTOR), EPADDR_HID, + TUD_HID_DESCRIPTOR(ITF_NUM_HID, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5), - // Printer: // Interface number, string index, EP Bulk Out address, EP Bulk In address, EP size - TUD_PRINTER_DESCRIPTOR(ITF_PRINTER, 0, EPADDR_PRINTER_OUT, EPADDR_PRINTER_IN, CFG_TUD_PRINTER_EP_BUFSIZE)}; - -static const tusb_desc_device_t DEVICE_DESCRIPTOR = { - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = USB_BCD, - .bDeviceClass = 0x00, // Define class at interface level - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 64), +}; - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = 0x0100, +#if TUD_OPT_HIGH_SPEED +// high speed configuration +static uint8_t const desc_hs_configuration[] = { + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIAL, + TUD_HID_DESCRIPTOR(ITF_NUM_HID, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, + CFG_TUD_HID_EP_BUFSIZE, 5), - .bNumConfigurations = 0x01 + TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 512), }; +// other speed configuration +static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; -//--------------------------------------------------------------------+ -// TinyUSB callbacks (descriptor requests) -//--------------------------------------------------------------------+ +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +static tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, -// TinyUSB GET HID REPORT DESCRIPTOR callback. -const uint8_t *tud_hid_descriptor_report_cb(uint8_t instance) { - (void)instance; - return HID_REPORT_DESCRIPTOR; + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *) &desc_device_qualifier; } -// TinyUSB GET CONFIGURATION DESCRIPTOR callback. -const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { - (void)index; - return CONFIG_INTERFACE_ENDPOINT_DESCRIPTOR; +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void) index; + + // if link speed is high return fullspeed config, and vice versa + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; } -// TinyUSB GET DEVICE DESCRIPTOR callback. -const uint8_t *tud_descriptor_device_cb(void) { - return (const uint8_t *)&DEVICE_DESCRIPTOR; +#endif // TUD_OPT_HIGH_SPEED + +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void) index; + +#if TUD_OPT_HIGH_SPEED + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif } -// Storage buffer array for string descriptor to be sent to host. -static uint16_t string_descriptor_buffer[STRING_DESCRIPTOR_MAX_LENGTH + 1]; - -// TinyUSB GET STRING DESCRIPTOR callback. -const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - (void)langid; - size_t utf16_string_length; - - if (index == LANGID) { - // langid is not a string as in a series of characters: language ID code is binary, 2 bytes - memcpy(string_descriptor_buffer + 1, STRING_DESCRIPTOR_VALUES[LANGID], 2); - utf16_string_length = 1; // 2 bytes = 1 UTF16 word - - } else if (index == STR_SERIAL) { - // serialnumber is generated from pi pico: see note in STRING_DESCRIPTOR_VALUES definition - utf16_string_length = board_usb_get_serial(string_descriptor_buffer + 1, STRING_DESCRIPTOR_MAX_LENGTH); - - } else if (index < STRING_COUNT) { - // Get adequate descriptor string - const char *str = STRING_DESCRIPTOR_VALUES[index]; - utf16_string_length = strlen(str); - if (utf16_string_length > STRING_DESCRIPTOR_MAX_LENGTH) { - utf16_string_length = STRING_DESCRIPTOR_MAX_LENGTH; - } - // Convert ASCII string from memory (char*) to UTF16 (for buffer), - // store in buffer with 1 UTF16 word offset (2 bytes, for buffer header) - for (size_t i = 0; i < utf16_string_length; i++) { - string_descriptor_buffer[i + 1] = str[i]; - } - - } else { - return NULL; - } +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, + STRID_HID, + STRID_PRINTER, +}; - // Set buffer header: - // byte 1 - buffer length in bytes (including header) - // byte 0 - string descriptor type (0x03). - string_descriptor_buffer[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * utf16_string_length + 2)); +static char const *string_desc_arr[] = { + (const char[]) { 0x09, 0x04 }, // 0: supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + NULL, // 3: Serial, use unique ID if possible + "TinyUSB HID", // 4: HID Interface + "TinyUSB Printer", // 5: Printer Interface +}; + +static uint16_t _desc_str[32 + 1]; + +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void) langid; + size_t chr_count; + + switch (index) { + case STRID_LANGID: + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + + case STRID_SERIAL: + chr_count = board_usb_get_serial(_desc_str + 1, 32); + break; + + default: + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { return NULL; } + + const char *str = string_desc_arr[index]; + + chr_count = strlen(str); + size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; + if (chr_count > max_count) { chr_count = max_count; } + + for (size_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + break; + } - return string_descriptor_buffer; + _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + return _desc_str; } diff --git a/examples/device/printer_to_hid/src/usb_descriptors.h b/examples/device/printer_to_hid/src/usb_descriptors.h index 0d9793a9c..20c34f151 100644 --- a/examples/device/printer_to_hid/src/usb_descriptors.h +++ b/examples/device/printer_to_hid/src/usb_descriptors.h @@ -25,52 +25,17 @@ #ifndef USB_DESCRIPTORS_H_ #define USB_DESCRIPTORS_H_ -#include "bsp/board_api.h" -#include "tusb.h" - -#define USB_VID 0xCafe // unassigned vendor id -#define USB_PID 0x4004 // random product id -#define USB_BCD 0x0200 // binary coded version: 2.00 - -// Configuration -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) - -// HID interface endpoints -#define EPADDR_HID 0x81 // Interrupt In, MSB must be 1 -// Printer interface endpoints -#define EPADDR_PRINTER_OUT 0x01 // Bulk Out, MSB must be 0 -#define EPADDR_PRINTER_IN 0x82 // Bulk In, MSB must be 1 - // HID report ID -#define REPORT_ID_KEYBOARD 1 - -// The maximum length of the string that will be sent to the host via the STRING DESCRIPTOR. Note that the -// string descriptor itself is two bytes wider than the string. -#define STRING_DESCRIPTOR_MAX_LENGTH 32 - -//--------------------------------------------------------------------+ -// Configuration, interface, endpoint descriptors -//--------------------------------------------------------------------+ - enum { - ITF_HID, - ITF_PRINTER, - ITF_COUNT, + REPORT_ID_KEYBOARD = 1, }; -//--------------------------------------------------------------------+ -// String Descriptors -//--------------------------------------------------------------------+ - enum { - LANGID = 0, - STR_MANUFACTURER, - STR_PRODUCT, - STR_SERIAL, - STR_CONFIGURATION, - STR_HID_INTERFACE, - STR_PRINTER_INTERFACE, - STRING_COUNT, + ITF_NUM_HID, + ITF_NUM_PRINTER, + ITF_NUM_TOTAL, }; +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_PRINTER_DESC_LEN) + #endif /* USB_DESCRIPTORS_H_ */ diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h index c9ed3cebc..b32543077 100644 --- a/src/class/printer/printer.h +++ b/src/class/printer/printer.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _TUSB_PRINTER_H_ -#define _TUSB_PRINTER_H_ +#ifndef TUSB_PRINTER_H_ +#define TUSB_PRINTER_H_ #include "common/tusb_common.h" @@ -35,13 +35,28 @@ extern "C" { /// Printer Class Specific Control Request typedef enum { - PRINTER_REQ_CONTROL_GET_DEVICE_ID = 0x01, ///< Get device ID - PRINTER_REQ_CONTROL_GET_PORT_STATUS = 0x02, ///< Get port status - PRINTER_REQ_CONTROL_SOFT_RESET = 0x03, ///< Soft reset -} printer_request_enum_t; + TUSB_PRINTER_REQUEST_GET_DEVICE_ID = 0x01, ///< Get device ID + TUSB_PRINTER_REQUEST_GET_PORT_STATUS = 0x02, ///< Get port status + TUSB_PRINTER_REQUEST_SOFT_RESET = 0x03, ///< Soft reset +} tusb_printer_request_type_t; + +/// Printer Port Status (returned by GET_PORT_STATUS request) +/// USB Printer Class spec 1.1, Section 4.2 +typedef union TU_ATTR_PACKED { + uint8_t status; + struct TU_ATTR_PACKED { + uint8_t reserved0 : 3; ///< Reserved (bits 0-2) + uint8_t not_error : 1; ///< 1 = no error, 0 = error + uint8_t selected : 1; ///< 1 = selected (online), 0 = not selected + uint8_t paper_empty : 1; ///< 1 = paper empty, 0 = paper not empty + uint8_t reserved6 : 2; ///< Reserved (bits 6-7) + } status_bm; +} tusb_printer_port_status_t; + +TU_VERIFY_STATIC(sizeof(tusb_printer_port_status_t) == 1, "size is not correct"); #ifdef __cplusplus } #endif -#endif /* _TUSB_PRINTER_H__ */ +#endif diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c index aaea4c988..d288014a1 100644 --- a/src/class/printer/printer_device.c +++ b/src/class/printer/printer_device.c @@ -28,13 +28,13 @@ #if (CFG_TUD_ENABLED && CFG_TUD_PRINTER) - //--------------------------------------------------------------------+ - // INCLUDE - //--------------------------------------------------------------------+ - #include "device/usbd.h" - #include "device/usbd_pvt.h" +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "device/usbd.h" +#include "device/usbd_pvt.h" - #include "printer_device.h" +#include "printer_device.h" //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF @@ -63,6 +63,8 @@ typedef struct { TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_EP_BUFSIZE); } printer_epbuf_t; +#define ITF_MEM_RESET_SIZE offsetof(printer_interface_t, rx_ff) + static printer_interface_t _printer_itf[CFG_TUD_PRINTER]; CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; @@ -70,9 +72,6 @@ CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER]; //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ - -static tud_printer_configure_fifo_t _printer_fifo_cfg; - static bool _prep_out_transaction(uint8_t itf) { const uint8_t rhport = 0; printer_interface_t *p_printer = &_printer_itf[itf]; @@ -112,6 +111,25 @@ TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf, size_t n) { (void)n; } +TU_ATTR_WEAK void tud_printer_request_complete_cb(uint8_t itf, tusb_control_request_t const *request) { + (void)itf; + (void)request; +} + +TU_ATTR_WEAK uint8_t const *tud_printer_get_device_id_cb(uint8_t itf) { + (void)itf; + return NULL; +} + +TU_ATTR_WEAK uint8_t tud_printer_get_port_status_cb(uint8_t itf) { + (void)itf; + return 0x18; // not error, selected, paper not empty +} + +TU_ATTR_WEAK void tud_printer_soft_reset_cb(uint8_t itf) { + (void)itf; +} + //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ @@ -142,7 +160,6 @@ void tud_printer_n_read_flush(uint8_t itf) { //--------------------------------------------------------------------+ void printerd_init(void) { tu_memclr(_printer_itf, sizeof(_printer_itf)); - tu_memclr(&_printer_fifo_cfg, sizeof(_printer_fifo_cfg)); for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { printer_interface_t *p_printer = &_printer_itf[i]; @@ -166,7 +183,7 @@ bool printerd_deinit(void) { for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { printer_interface_t *p_printer = &_printer_itf[i]; osal_mutex_t mutex_rd = p_printer->rx_ff.mutex_rd; - osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_rd; + osal_mutex_t mutex_wr = p_printer->tx_ff.mutex_wr; if (mutex_rd) { osal_mutex_delete(mutex_rd); @@ -189,15 +206,9 @@ void printerd_reset(uint8_t rhport) { for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) { printer_interface_t *p_printer = &_printer_itf[i]; - tu_memclr(p_printer, sizeof(&p_printer)); - if (!_printer_fifo_cfg.rx_persistent) { - tu_fifo_clear(&p_printer->rx_ff); - } - if (!_printer_fifo_cfg.tx_persistent) { - tu_fifo_clear(&p_printer->tx_ff); - } - // tu_fifo_set_overwritable(&p_printer->rx_ff, true); - tu_fifo_set_overwritable(&p_printer->tx_ff, true); + tu_memclr(p_printer, ITF_MEM_RESET_SIZE); + tu_fifo_clear(&p_printer->rx_ff); + tu_fifo_clear(&p_printer->tx_ff); } } @@ -222,7 +233,7 @@ uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, ui //------------- Endpoints -------------// TU_ASSERT(itf_desc->bNumEndpoints == 2); drv_len += 2 * sizeof(tusb_desc_endpoint_t); - p_printer->itf_num = 2; + p_printer->itf_num = itf_desc->bInterfaceNumber; const uint8_t *p_desc = tu_desc_next(itf_desc); TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_printer->ep_out, &p_printer->ep_in), 0); @@ -233,6 +244,7 @@ uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, ui bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) { TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); + uint8_t const itf_num = (uint8_t)request->wIndex; if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { //------------- STD Request -------------// @@ -240,39 +252,38 @@ bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_ return true; } } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { - switch (request->bRequest) { - // https://www.usb.org/sites/default/files/usbprint11a021811.pdf - case PRINTER_REQ_CONTROL_GET_DEVICE_ID: - if (stage == CONTROL_STAGE_SETUP) { - const char deviceId[] = "MANUFACTURER:ACME Manufacturing;" - "MODEL:LaserBeam 9;" - "COMMAND SET:PS;" - "COMMENT:Anything you like;" - "ACTIVE COMMAND SET:PS;"; - char buffer[256]; - strcpy(buffer + 2, deviceId); - buffer[0] = 0x00; - buffer[1] = strlen(deviceId); - return tud_control_xfer(rhport, request, buffer, strlen(deviceId) + 2); + // https://www.usb.org/sites/default/files/usbprint11a021811.pdf + if (stage == CONTROL_STAGE_SETUP) { + switch (request->bRequest) { + case TUSB_PRINTER_REQUEST_GET_DEVICE_ID: { + // App provides buffer with IEEE 1284 format (first 2 bytes = big-endian length) + const uint8_t *device_id = tud_printer_get_device_id_cb(itf_num); + TU_VERIFY(device_id); + const uint16_t total_len = (uint16_t)((device_id[0] << 8) | device_id[1]); + return tud_control_xfer(rhport, request, (void *)(uintptr_t)device_id, total_len); } - break; - case PRINTER_REQ_CONTROL_GET_PORT_STATUS: - if (stage == CONTROL_STAGE_SETUP) { - static uint8_t port_status = 0b00011000; // paper not empty, selected, no error + + case TUSB_PRINTER_REQUEST_GET_PORT_STATUS: { + static uint8_t port_status; + port_status = tud_printer_get_port_status_cb(itf_num); return tud_control_xfer(rhport, request, &port_status, sizeof(port_status)); } - break; - case PRINTER_REQ_CONTROL_SOFT_RESET: - if (stage == CONTROL_STAGE_SETUP) { - return false; // TODO: reset buffers, reset Bulk In and Out endpoints, clear stall conditions - } - break; - default: - return false; + + case TUSB_PRINTER_REQUEST_SOFT_RESET: + tud_printer_soft_reset_cb(itf_num); + tud_control_status(rhport, request); + return true; + + default: + return false; + } + } else if (stage == CONTROL_STAGE_ACK) { + tud_printer_request_complete_cb(itf_num, request); } } else { return false; } + return true; } diff --git a/src/class/printer/printer_device.h b/src/class/printer/printer_device.h index 8c21e39ce..aeff1ffc5 100644 --- a/src/class/printer/printer_device.h +++ b/src/class/printer/printer_device.h @@ -33,11 +33,6 @@ extern "C" { #endif -typedef struct TU_ATTR_PACKED { - uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect - uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect -} tud_printer_configure_fifo_t; - //--------------------------------------------------------------------+ // Application API (Multiple Ports) i.e. CFG_TUD_PRINTER > 1 //--------------------------------------------------------------------+ @@ -59,7 +54,22 @@ bool tud_printer_n_peek(uint8_t itf, uint8_t *ui8); //--------------------------------------------------------------------+ // Invoked when received new data -TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf, size_t n); +void tud_printer_rx_cb(uint8_t itf, size_t n); + +// Invoked when host requests device ID string (IEEE 1284). +// Application returns pointer to device ID buffer (must remain valid until transfer completes). +// First 2 bytes of returned buffer must contain big-endian length (including the 2 length bytes). +const uint8_t *tud_printer_get_device_id_cb(uint8_t itf); + +// Invoked when host requests port status. +uint8_t tud_printer_get_port_status_cb(uint8_t itf); + +// Invoked when host requests soft reset. +void tud_printer_soft_reset_cb(uint8_t itf); + +// Invoked when a control request is completed (GET_DEVICE_ID, GET_PORT_STATUS, etc.) +void tud_printer_request_complete_cb(uint8_t itf, tusb_control_request_t const *request); + //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/device/usbd.h b/src/device/usbd.h index c6e7d31c8..d3a6dccbb 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -330,21 +330,6 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 -//--------------------------------------------------------------------+ -// Printer Descriptor Templates -//--------------------------------------------------------------------+ - -#define TUD_PRINTER_DESC_LEN (9 + 7 + 7) // one interface, two endpoints - -#define TUD_PRINTER_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ - /* Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_PRINTER, 1, 2, _stridx,\ - /* Endpoint Out */\ - 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ - /* Endpoint In */\ - 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 - - //--------------------------------------------------------------------+ // HID Descriptor Templates //--------------------------------------------------------------------+ diff --git a/src/tusb.h b/src/tusb.h index 4f76397cc..c80c8433c 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -96,10 +96,6 @@ #include "class/mtp/mtp_device.h" #endif - #if CFG_TUD_PRINTER - #include "class/printer/printer_device.h" - #endif - #if CFG_TUD_AUDIO #include "class/audio/audio_device.h" #endif -- cgit v1.3.1 From bc8ce76ae800b312b4d3ef591cc42e8726e68d7f Mon Sep 17 00:00:00 2001 From: Saulo Veríssimo Date: Tue, 24 Mar 2026 23:37:48 -0300 Subject: feat: add MIDI 2.0 Device class driver (USB-MIDI 2.0) Add native USB-MIDI 2.0 Device class driver to TinyUSB. Implements the USB-MIDI 2.0 specification with both Alt Setting 0 (MIDI 1.0 fallback) and Alt Setting 1 (UMP native) descriptor support. Driver features: - UMP (Universal MIDI Packet) read/write with atomic message framing - Protocol negotiation: Endpoint Discovery, Config Request/Notify, Function Block Discovery (embedded in driver) - Group Terminal Block descriptor via GET_DESCRIPTOR - Alt Setting switch handler with endpoint re-arm - Static allocation, no dynamic memory, ISR-safe Build system: - Register midi2d_* in usbd.c driver table - Add TUD_MIDI2_DESCRIPTOR macros to usbd.h - Add config defaults (CFG_TUD_MIDI2_*) to tusb_option.h - Add midi2_ump_word_count() to midi.h (shared by Device and Host) - Add midi2_device.c/h to family.cmake and CMakeLists.txt - Add midi2_device.h include to tusb.h All changes guarded by #if CFG_TUD_MIDI2 (default 0). Zero impact on existing drivers and examples. Tested: Raspberry Pi Pico (RP2040), Linux ALSA, Windows MIDI Services --- hw/bsp/rp2040/family.cmake | 2 + src/CMakeLists.txt | 2 + src/class/midi/midi.h | 18 ++ src/class/midi/midi2_device.c | 604 ++++++++++++++++++++++++++++++++++++++++++ src/class/midi/midi2_device.h | 125 +++++++++ src/device/usbd.c | 14 + src/device/usbd.h | 37 +++ src/tusb.h | 8 + src/tusb_option.h | 52 ++++ 9 files changed, 862 insertions(+) create mode 100644 src/class/midi/midi2_device.c create mode 100644 src/class/midi/midi2_device.h (limited to 'src/tusb.h') diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 075582554..aab9a4fae 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -99,6 +99,7 @@ target_sources(tinyusb_device_base INTERFACE ${TOP}/src/class/dfu/dfu_rt_device.c ${TOP}/src/class/hid/hid_device.c ${TOP}/src/class/midi/midi_device.c + ${TOP}/src/class/midi/midi2_device.c ${TOP}/src/class/msc/msc_device.c ${TOP}/src/class/mtp/mtp_device.c ${TOP}/src/class/net/ecm_rndis_device.c @@ -121,6 +122,7 @@ target_sources(tinyusb_host_base INTERFACE ${TOP}/src/class/cdc/cdc_host.c ${TOP}/src/class/hid/hid_host.c ${TOP}/src/class/midi/midi_host.c + ${TOP}/src/class/midi/midi2_host.c ${TOP}/src/class/msc/msc_host.c ${TOP}/src/class/vendor/vendor_host.c ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c7a5184c5..b3e05f60f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ function(tinyusb_sources_get OUTPUT_VAR) ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/dfu/dfu_rt_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/hid/hid_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/midi/midi_device.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/midi/midi2_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/msc/msc_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/mtp/mtp_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ecm_rndis_device.c @@ -28,6 +29,7 @@ function(tinyusb_sources_get OUTPUT_VAR) ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_host.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/hid/hid_host.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/midi/midi_host.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/midi/midi2_host.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/msc/msc_host.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_host.c # typec diff --git a/src/class/midi/midi.h b/src/class/midi/midi.h index cd67640e4..8121ec016 100644 --- a/src/class/midi/midi.h +++ b/src/class/midi/midi.h @@ -185,6 +185,24 @@ typedef midi_desc_cs_endpoint_n_t(1) midi_desc_cs_endpoint_1jack_t; TU_VERIFY_STATIC(sizeof(midi_desc_cs_endpoint_1jack_t) == 4+1, "size is not correct"); +//--------------------------------------------------------------------+ +// MIDI 2.0 UMP Helpers +//--------------------------------------------------------------------+ + +// Return the number of 32-bit words for a UMP message given its Message Type +static inline uint8_t midi2_ump_word_count(uint8_t mt) { + switch (mt) { + case 0x0: case 0x1: case 0x2: case 0x6: case 0x7: + return 1; + case 0x3: case 0x4: case 0x8: case 0x9: case 0xA: + return 2; + case 0xB: case 0xC: + return 3; + default: // 0x5, 0xD, 0xE, 0xF + return 4; + } +} + //--------------------------------------------------------------------+ // For Internal Driver Use //--------------------------------------------------------------------+ diff --git a/src/class/midi/midi2_device.c b/src/class/midi/midi2_device.c new file mode 100644 index 000000000..9363aac11 --- /dev/null +++ b/src/class/midi/midi2_device.c @@ -0,0 +1,604 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Saulo Verissimo + * + * 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. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED && CFG_TUD_MIDI2 + +#include + +#include "device/usbd.h" +#include "device/usbd_pvt.h" +#include "midi2_device.h" + +//--------------------------------------------------------------------+ +// Weak stubs +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_midi2_rx_cb(uint8_t itf) { (void) itf; } +TU_ATTR_WEAK void tud_midi2_set_itf_cb(uint8_t itf, uint8_t alt) { (void) itf; (void) alt; } +TU_ATTR_WEAK bool tud_midi2_get_req_itf_cb(uint8_t rhport, const tusb_control_request_t* request) { + (void) rhport; (void) request; return false; +} + +//--------------------------------------------------------------------+ +// UMP Stream Message Constants +//--------------------------------------------------------------------+ +// UMP Message Type for Stream messages (bits 31:28) +enum { + MT_STREAM = 0x0F, +}; + +// UMP Stream Status values (10-bit, bits 25:16) +enum { + STREAM_ENDPOINT_DISCOVERY = 0x000, + STREAM_ENDPOINT_INFO = 0x001, + STREAM_EP_NAME = 0x003, + STREAM_PROD_INSTANCE_ID = 0x004, + STREAM_CONFIG_REQUEST = 0x005, + STREAM_CONFIG_NOTIFY = 0x006, + STREAM_FB_DISCOVERY = 0x010, + STREAM_FB_INFO = 0x011, +}; + +// MIDI Protocol values (per USB-MIDI 2.0 spec) +enum { + MIDI_PROTOCOL_MIDI1 = 0x01, + MIDI_PROTOCOL_MIDI2 = 0x02, +}; + +enum { + UMP_VER_MAJOR = 1, + UMP_VER_MINOR = 1, +}; + +// Group Terminal Block descriptor types (USB-MIDI 2.0) +enum { + MIDI2_CS_GRP_TRM_BLOCK = 0x26, + MIDI2_GRP_TRM_BLOCK_HEADER = 0x01, + MIDI2_GRP_TRM_BLOCK_ENTRY = 0x02, +}; + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct { + uint8_t rhport; + uint8_t itf_num; + uint8_t alt_setting; + uint8_t protocol; + bool negotiated; + + /*------------- From this point, data is not cleared by bus reset -------------*/ + struct { + tu_edpt_stream_t tx; + tu_edpt_stream_t rx; + + uint8_t rx_ff_buf[CFG_TUD_MIDI2_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_MIDI2_TX_BUFSIZE]; + } ep_stream; +} midi2d_interface_t; + +TU_VERIFY_STATIC(CFG_TUD_MIDI2_NUM_GROUPS >= 1 && CFG_TUD_MIDI2_NUM_GROUPS <= 16, + "CFG_TUD_MIDI2_NUM_GROUPS must be 1..16"); +TU_VERIFY_STATIC(CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS >= 1 && CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS <= 32, + "CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS must be 1..32"); + +#define ITF_MEM_RESET_SIZE offsetof(midi2d_interface_t, ep_stream) + +static midi2d_interface_t _midi2d_itf[CFG_TUD_MIDI2]; + +#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 +typedef struct { + TUD_EPBUF_DEF(epin, CFG_TUD_MIDI2_TX_EPSIZE); + TUD_EPBUF_DEF(epout, CFG_TUD_MIDI2_RX_EPSIZE); +} midi2d_epbuf_t; + +CFG_TUD_MEM_SECTION static midi2d_epbuf_t _midi2d_epbuf[CFG_TUD_MIDI2]; +#endif + +// Default Group Terminal Block descriptor (USB-MIDI 2.0 spec, Table 5-5/5-6) +static const uint8_t _default_gtb_desc[] = { + // GTB Header (5 bytes) + 5, // bLength + MIDI2_CS_GRP_TRM_BLOCK, // bDescriptorType + MIDI2_GRP_TRM_BLOCK_HEADER, // bDescriptorSubtype + U16_TO_U8S_LE(18), // wTotalLength (5 + 13 = 18) + + // GTB Entry (13 bytes) + 13, // bLength + MIDI2_CS_GRP_TRM_BLOCK, // bDescriptorType + MIDI2_GRP_TRM_BLOCK_ENTRY, // bDescriptorSubtype + 1, // bGrpTrmBlkID + 0x00, // bGrpTrmBlkType: bidirectional + 0x00, // nGroupTrm: first group (0) + CFG_TUD_MIDI2_NUM_GROUPS, // nNumGroupTrm + 0, // iBlockItem: no string + 0x00, // bMIDIProtocol: unknown/not fixed + 0, 0, // wMaxInputBandwidth: unknown + 0, 0 // wMaxOutputBandwidth: unknown +}; + +//--------------------------------------------------------------------+ +// Protocol Negotiation +//--------------------------------------------------------------------+ +static void _nego_send_ump(midi2d_interface_t* p_midi, const uint32_t* words, uint8_t count) { + tu_edpt_stream_t* ep_tx = &p_midi->ep_stream.tx; + if (!tu_edpt_stream_is_opened(ep_tx)) return; + if (tu_edpt_stream_write_available(ep_tx) < count * 4) return; + tu_edpt_stream_write(ep_tx, words, count * 4); + tu_edpt_stream_write_xfer(ep_tx); +} + +static void _nego_send_endpoint_info(midi2d_interface_t* p_midi) { + uint32_t msg[4] = {0}; + msg[0] = ((uint32_t) MT_STREAM << 28) + | ((uint32_t) STREAM_ENDPOINT_INFO << 16) + | ((uint32_t) UMP_VER_MAJOR << 8) + | (uint32_t) UMP_VER_MINOR; + msg[1] = (1u << 31) // Static Function Blocks flag + | ((uint32_t)(CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS & 0x7F) << 24) + | (1u << 9) // MIDI 2.0 Protocol capability + | (1u << 8); // MIDI 1.0 Protocol capability + _nego_send_ump(p_midi, msg, 4); +} + +static void _nego_send_stream_text(midi2d_interface_t* p_midi, uint16_t status, const char* str) { + if (!str || str[0] == '\0') return; + + uint16_t total_len = (uint16_t) strlen(str); + uint16_t offset = 0; + + while (offset < total_len) { + uint16_t remaining = total_len - offset; + uint8_t n = (uint8_t)((remaining > 14) ? 14 : remaining); + bool is_first = (offset == 0); + bool is_last = (remaining <= 14); + + uint8_t form; + if (is_first && is_last) form = 0; + else if (is_first) form = 1; + else if (is_last) form = 3; + else form = 2; + + uint32_t msg[4] = {0}; + msg[0] = ((uint32_t) MT_STREAM << 28) + | ((uint32_t) form << 26) + | ((uint32_t) status << 16); + + const char* p = str + offset; + if (n > 0) msg[0] |= ((uint32_t)(uint8_t) p[0] << 8); + if (n > 1) msg[0] |= (uint32_t)(uint8_t) p[1]; + for (uint8_t i = 2; i < n; i++) { + uint8_t word_idx = (uint8_t)(1 + (i - 2) / 4); + uint8_t shift = (uint8_t)(24 - ((i - 2) % 4) * 8); + msg[word_idx] |= ((uint32_t)(uint8_t) p[i] << shift); + } + + _nego_send_ump(p_midi, msg, 4); + offset += n; + } +} + +static void _nego_send_config_notify(midi2d_interface_t* p_midi, uint8_t protocol) { + uint32_t msg[4] = {0}; + msg[0] = ((uint32_t) MT_STREAM << 28) + | ((uint32_t) STREAM_CONFIG_NOTIFY << 16) + | ((uint32_t) protocol << 8); + _nego_send_ump(p_midi, msg, 4); +} + +static void _nego_send_fb_info(midi2d_interface_t* p_midi, uint8_t fb_idx) { + uint32_t msg[4] = {0}; + msg[0] = ((uint32_t) MT_STREAM << 28) + | ((uint32_t) STREAM_FB_INFO << 16) + | (1u << 15) + | ((uint32_t) fb_idx << 8) + | 0x02; // bDirection: bidirectional + msg[1] = ((uint32_t) 0 << 24) // bFirstGroup + | ((uint32_t) CFG_TUD_MIDI2_NUM_GROUPS << 16); + _nego_send_ump(p_midi, msg, 4); +} + +static void _nego_handle_stream_msg(midi2d_interface_t* p_midi, const uint32_t* words) { + uint16_t status = (words[0] >> 16) & 0x3FF; + + switch (status) { + case STREAM_ENDPOINT_DISCOVERY: + _nego_send_endpoint_info(p_midi); + _nego_send_stream_text(p_midi, STREAM_EP_NAME, CFG_TUD_MIDI2_EP_NAME); + _nego_send_stream_text(p_midi, STREAM_PROD_INSTANCE_ID, CFG_TUD_MIDI2_PRODUCT_ID); + break; + + case STREAM_CONFIG_REQUEST: { + uint8_t req_proto = (words[0] >> 8) & 0xFF; + if (req_proto == MIDI_PROTOCOL_MIDI1 || req_proto == MIDI_PROTOCOL_MIDI2) { + p_midi->protocol = req_proto; + } + _nego_send_config_notify(p_midi, p_midi->protocol); + p_midi->negotiated = true; + break; + } + + case STREAM_FB_DISCOVERY: { + uint8_t fb_idx = (words[0] >> 8) & 0xFF; + if (fb_idx == 0xFF) { + for (uint8_t f = 0; f < CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS; f++) { + _nego_send_fb_info(p_midi, f); + } + } else if (fb_idx < CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS) { + _nego_send_fb_info(p_midi, fb_idx); + } + break; + } + + default: + break; + } +} + +static void _nego_process_rx(midi2d_interface_t* p_midi) { + tu_edpt_stream_t* ep_rx = &p_midi->ep_stream.rx; + uint8_t first_byte; + + while (tu_edpt_stream_peek(ep_rx, &first_byte)) { + uint8_t mt = (first_byte >> 4) & 0x0F; + uint8_t pkt_words = midi2_ump_word_count(mt); + uint32_t pkt_bytes = (uint32_t)pkt_words * 4; + + if (mt != MT_STREAM) break; + if (tu_edpt_stream_read_available(ep_rx) < pkt_bytes) break; + + uint32_t buf[4] = {0}; + tu_edpt_stream_read(ep_rx, buf, pkt_bytes); + _nego_handle_stream_msg(p_midi, buf); + } +} + +//--------------------------------------------------------------------+ +// READ API +//--------------------------------------------------------------------+ +bool tud_midi2_n_mounted(uint8_t itf) { + TU_VERIFY(itf < CFG_TUD_MIDI2, false); + midi2d_interface_t* p_midi = &_midi2d_itf[itf]; + return tu_edpt_stream_is_opened(&p_midi->ep_stream.tx) && + tu_edpt_stream_is_opened(&p_midi->ep_stream.rx); +} + +uint32_t tud_midi2_n_available(uint8_t itf) { + TU_VERIFY(itf < CFG_TUD_MIDI2, 0); + midi2d_interface_t* p_midi = &_midi2d_itf[itf]; + return tu_edpt_stream_read_available(&p_midi->ep_stream.rx) / 4; +} + +uint32_t tud_midi2_n_ump_read(uint8_t itf, uint32_t* words, uint32_t max_words) { + TU_VERIFY(itf < CFG_TUD_MIDI2 && words != NULL && max_words > 0, 0); + midi2d_interface_t* p_midi = &_midi2d_itf[itf]; + tu_edpt_stream_t* ep_rx = &p_midi->ep_stream.rx; + + uint32_t total_read = 0; + while (total_read < max_words) { + uint8_t first_byte; + if (!tu_edpt_stream_peek(ep_rx, &first_byte)) break; + + uint8_t mt = (first_byte >> 4) & 0x0F; + uint8_t pkt_words = midi2_ump_word_count(mt); + + if (total_read + pkt_words > max_words) break; + if (tu_edpt_stream_read_available(ep_rx) < (uint32_t)pkt_words * 4) break; + + tu_edpt_stream_read(ep_rx, &words[total_read], pkt_words * 4); + total_read += pkt_words; + } + + return total_read; +} + +bool tud_midi2_n_packet_read(uint8_t itf, uint8_t packet[4]) { + TU_VERIFY(itf < CFG_TUD_MIDI2, false); + midi2d_interface_t* p_midi = &_midi2d_itf[itf]; + return 4 == tu_edpt_stream_read(&p_midi->ep_stream.rx, packet, 4); +} + +//--------------------------------------------------------------------+ +// WRITE API +//--------------------------------------------------------------------+ +uint32_t tud_midi2_n_ump_write(uint8_t itf, const uint32_t* words, uint32_t count) { + TU_VERIFY(itf < CFG_TUD_MIDI2 && words != NULL && count > 0, 0); + midi2d_interface_t* p_midi = &_midi2d_itf[itf]; + tu_edpt_stream_t* ep_tx = &p_midi->ep_stream.tx; + TU_VERIFY(tu_edpt_stream_is_opened(ep_tx), 0); + + uint32_t written = 0; + while (written < count) { + uint8_t mt = (uint8_t)((words[written] >> 28) & 0x0F); + uint8_t pkt_words = midi2_ump_word_count(mt); + + if (written + pkt_words > count) break; + if (tu_edpt_stream_write_available(ep_tx) < pkt_words * 4) break; + + tu_edpt_stream_write(ep_tx, &words[written], pkt_words * 4); + written += pkt_words; + } + + (void) tu_edpt_stream_write_xfer(ep_tx); + return written; +} + +bool tud_midi2_n_packet_write(uint8_t itf, const uint8_t packet[4]) { + TU_VERIFY(itf < CFG_TUD_MIDI2, false); + midi2d_interface_t* p_midi = &_midi2d_itf[itf]; + tu_edpt_stream_t* ep_tx = &p_midi->ep_stream.tx; + TU_VERIFY(tu_edpt_stream_is_opened(ep_tx), false); + TU_VERIFY(tu_edpt_stream_write_available(ep_tx) >= 4, false); + TU_VERIFY(tu_edpt_stream_write(ep_tx, packet, 4) > 0, false); + (void) tu_edpt_stream_write_xfer(ep_tx); + return true; +} + +//--------------------------------------------------------------------+ +// STATE GETTERS +//--------------------------------------------------------------------+ +uint8_t tud_midi2_n_alt_setting(uint8_t itf) { + TU_VERIFY(itf < CFG_TUD_MIDI2, 0); + return _midi2d_itf[itf].alt_setting; +} + +bool tud_midi2_n_negotiated(uint8_t itf) { + TU_VERIFY(itf < CFG_TUD_MIDI2, false); + return _midi2d_itf[itf].negotiated; +} + +uint8_t tud_midi2_n_protocol(uint8_t itf) { + TU_VERIFY(itf < CFG_TUD_MIDI2, 0); + return _midi2d_itf[itf].protocol; +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void midi2d_init(void) { + tu_memclr(_midi2d_itf, sizeof(_midi2d_itf)); + for (uint8_t i = 0; i < CFG_TUD_MIDI2; i++) { + midi2d_interface_t* p_midi = &_midi2d_itf[i]; + p_midi->protocol = MIDI_PROTOCOL_MIDI2; + + #if CFG_TUD_EDPT_DEDICATED_HWFIFO + uint8_t* epout_buf = NULL; + uint8_t* epin_buf = NULL; + #else + midi2d_epbuf_t* p_epbuf = &_midi2d_epbuf[i]; + uint8_t* epout_buf = p_epbuf->epout; + uint8_t* epin_buf = p_epbuf->epin; + #endif + + tu_edpt_stream_init(&p_midi->ep_stream.rx, false, false, false, + p_midi->ep_stream.rx_ff_buf, CFG_TUD_MIDI2_RX_BUFSIZE, epout_buf); + tu_edpt_stream_init(&p_midi->ep_stream.tx, false, true, false, + p_midi->ep_stream.tx_ff_buf, CFG_TUD_MIDI2_TX_BUFSIZE, epin_buf); + } +} + +bool midi2d_deinit(void) { + for (uint8_t i = 0; i < CFG_TUD_MIDI2; i++) { + midi2d_interface_t* p_midi = &_midi2d_itf[i]; + tu_edpt_stream_deinit(&p_midi->ep_stream.rx); + tu_edpt_stream_deinit(&p_midi->ep_stream.tx); + } + return true; +} + +void midi2d_reset(uint8_t rhport) { + (void) rhport; + for (uint8_t i = 0; i < CFG_TUD_MIDI2; i++) { + midi2d_interface_t* p_midi = &_midi2d_itf[i]; + tu_memclr(p_midi, ITF_MEM_RESET_SIZE); + + tu_edpt_stream_clear(&p_midi->ep_stream.rx); + tu_edpt_stream_close(&p_midi->ep_stream.rx); + + tu_edpt_stream_clear(&p_midi->ep_stream.tx); + tu_edpt_stream_close(&p_midi->ep_stream.tx); + } +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t find_midi2_itf(uint8_t ep_addr) { + for (uint8_t idx = 0; idx < CFG_TUD_MIDI2; idx++) { + const midi2d_interface_t* p_midi = &_midi2d_itf[idx]; + if (ep_addr == p_midi->ep_stream.rx.ep_addr || ep_addr == p_midi->ep_stream.tx.ep_addr) { + return idx; + } + } + return TUSB_INDEX_INVALID_8; +} + +static uint8_t find_midi2_itf_by_num(uint8_t itf_num) { + for (uint8_t idx = 0; idx < CFG_TUD_MIDI2; idx++) { + if (_midi2d_itf[idx].itf_num == itf_num) return idx; + } + return TUSB_INDEX_INVALID_8; +} + +uint16_t midi2d_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) { + const uint8_t* p_desc = (const uint8_t*) desc_itf; + const uint8_t* desc_end = p_desc + max_len; + + // 1st Interface: Audio Control v1 (optional) + if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass && + AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass && + AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) { + p_desc = tu_desc_next(desc_itf); + while (tu_desc_in_bounds(p_desc, desc_end) && TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) { + p_desc = tu_desc_next(p_desc); + } + } + + // 2nd Interface: MIDI Streaming + TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0); + const tusb_desc_interface_t* desc_midi = (const tusb_desc_interface_t*) p_desc; + + TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass && + AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass && + AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol, + 0); + + uint8_t idx = find_midi2_itf(0); + TU_ASSERT(idx < CFG_TUD_MIDI2, 0); + midi2d_interface_t* p_midi = &_midi2d_itf[idx]; + + p_midi->rhport = rhport; + p_midi->itf_num = desc_midi->bInterfaceNumber; + p_midi->alt_setting = 0; + p_midi->protocol = MIDI_PROTOCOL_MIDI2; + p_midi->negotiated = false; + + p_desc = tu_desc_next(p_desc); + + // Skip class-specific descriptors + while (tu_desc_in_bounds(p_desc, desc_end) && TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) { + p_desc = tu_desc_next(p_desc); + } + + // Find and open endpoint descriptors + uint8_t found_ep = 0; + while ((found_ep < desc_midi->bNumEndpoints) && tu_desc_in_bounds(p_desc, desc_end)) { + if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) { + const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc; + TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0); + const uint8_t ep_addr = desc_ep->bEndpointAddress; + + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + tu_edpt_stream_open(&p_midi->ep_stream.tx, rhport, desc_ep, CFG_TUD_MIDI2_TX_EPSIZE); + tu_edpt_stream_clear(&p_midi->ep_stream.tx); + } else { + tu_edpt_stream_open(&p_midi->ep_stream.rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep)); + tu_edpt_stream_clear(&p_midi->ep_stream.rx); + TU_ASSERT(tu_edpt_stream_read_xfer(&p_midi->ep_stream.rx) > 0, 0); + } + + found_ep++; + } + + p_desc = tu_desc_next(p_desc); + } + + // Skip remaining descriptors (alt setting 1, CS endpoints, GTB) + while (tu_desc_in_bounds(p_desc, desc_end)) { + uint8_t dtype = tu_desc_type(p_desc); + if (dtype != TUSB_DESC_CS_INTERFACE && dtype != TUSB_DESC_CS_ENDPOINT && + dtype != TUSB_DESC_INTERFACE && dtype != TUSB_DESC_ENDPOINT) { + break; + } + p_desc = tu_desc_next(p_desc); + } + + return (uint16_t)(p_desc - (const uint8_t*) desc_itf); +} + +bool midi2d_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) { + TU_LOG2("MIDI2 ctrl: stage=%u bRequest=0x%02X wValue=0x%04X wIndex=0x%04X wLength=%u\r\n", + stage, request->bRequest, request->wValue, request->wIndex, request->wLength); + + if (stage != CONTROL_STAGE_SETUP) return true; + + switch (request->bRequest) { + case TUSB_REQ_SET_INTERFACE: { + uint8_t itf_num = tu_u16_low(request->wIndex); + uint8_t alt = tu_u16_low(request->wValue); + + uint8_t idx = find_midi2_itf_by_num(itf_num); + if (idx >= CFG_TUD_MIDI2) return false; + + midi2d_interface_t* p_midi = &_midi2d_itf[idx]; + p_midi->alt_setting = alt; + + tu_edpt_stream_clear(&p_midi->ep_stream.rx); + tu_edpt_stream_clear(&p_midi->ep_stream.tx); + + if (alt == 1) { + p_midi->negotiated = false; + p_midi->protocol = MIDI_PROTOCOL_MIDI2; + } + + // Re-arm RX endpoint for receiving data after alt setting change + tu_edpt_stream_read_xfer(&p_midi->ep_stream.rx); + + tud_midi2_set_itf_cb(idx, alt); + tud_control_status(rhport, request); + return true; + } + + case TUSB_REQ_GET_DESCRIPTOR: { + // wValue: descriptor type (high) | index (low) + // 0x26 = CS_GRP_TRM_BLOCK, index 0x01 + if (request->wValue == ((uint16_t)MIDI2_CS_GRP_TRM_BLOCK << 8 | 0x01)) { + if (tud_midi2_get_req_itf_cb(rhport, request)) return true; + + uint16_t len = request->wLength; + if (len > sizeof(_default_gtb_desc)) { + len = sizeof(_default_gtb_desc); + } + tud_control_xfer(rhport, request, (void*)(uintptr_t) _default_gtb_desc, len); + return true; + } + return false; + } + + default: + return false; + } +} + +bool midi2d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + (void) rhport; + + uint8_t idx = find_midi2_itf(ep_addr); + TU_ASSERT(idx < CFG_TUD_MIDI2); + midi2d_interface_t* p_midi = &_midi2d_itf[idx]; + + tu_edpt_stream_t* ep_rx = &p_midi->ep_stream.rx; + tu_edpt_stream_t* ep_tx = &p_midi->ep_stream.tx; + + if (ep_addr == ep_rx->ep_addr) { + if (result == XFER_RESULT_SUCCESS) { + tu_edpt_stream_read_xfer_complete(ep_rx, xferred_bytes); + if (p_midi->alt_setting == 1) { + _nego_process_rx(p_midi); + } + tud_midi2_rx_cb(idx); + } + tu_edpt_stream_read_xfer(ep_rx); + } else if (ep_addr == ep_tx->ep_addr && result == XFER_RESULT_SUCCESS) { + if (0 == tu_edpt_stream_write_xfer(ep_tx)) { + (void) tu_edpt_stream_write_zlp_if_needed(ep_tx, xferred_bytes); + } + } else { + return false; + } + + return true; +} + +#endif diff --git a/src/class/midi/midi2_device.h b/src/class/midi/midi2_device.h new file mode 100644 index 000000000..dac1f0124 --- /dev/null +++ b/src/class/midi/midi2_device.h @@ -0,0 +1,125 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Saulo Verissimo + * + * 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 TUSB_MIDI2_DEVICE_H_ +#define TUSB_MIDI2_DEVICE_H_ + +#include "class/audio/audio.h" +#include "midi.h" + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ + +// Config defaults are in tusb_option.h: +// CFG_TUD_MIDI2_RX_EPSIZE, CFG_TUD_MIDI2_TX_EPSIZE, +// CFG_TUD_MIDI2_RX_BUFSIZE, CFG_TUD_MIDI2_TX_BUFSIZE, +// CFG_TUD_MIDI2_NUM_GROUPS, CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS, +// CFG_TUD_MIDI2_EP_NAME, CFG_TUD_MIDI2_PRODUCT_ID + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Application Callback API (weak, optional) +//--------------------------------------------------------------------+ +void tud_midi2_rx_cb(uint8_t itf); +void tud_midi2_set_itf_cb(uint8_t itf, uint8_t alt); +bool tud_midi2_get_req_itf_cb(uint8_t rhport, const tusb_control_request_t* request); + +//--------------------------------------------------------------------+ +// Application API (Multiple Interfaces) +//--------------------------------------------------------------------+ + +bool tud_midi2_n_mounted(uint8_t itf); +uint32_t tud_midi2_n_available(uint8_t itf); +uint8_t tud_midi2_n_alt_setting(uint8_t itf); +bool tud_midi2_n_negotiated(uint8_t itf); +uint8_t tud_midi2_n_protocol(uint8_t itf); + +uint32_t tud_midi2_n_ump_read(uint8_t itf, uint32_t* words, uint32_t max_words); +uint32_t tud_midi2_n_ump_write(uint8_t itf, const uint32_t* words, uint32_t count); + +bool tud_midi2_n_packet_read(uint8_t itf, uint8_t packet[4]); +bool tud_midi2_n_packet_write(uint8_t itf, const uint8_t packet[4]); + +//--------------------------------------------------------------------+ +// Application API (Single Interface) +//--------------------------------------------------------------------+ +TU_ATTR_ALWAYS_INLINE static inline bool tud_midi2_mounted(void) { + return tud_midi2_n_mounted(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi2_available(void) { + return tud_midi2_n_available(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_midi2_alt_setting(void) { + return tud_midi2_n_alt_setting(0); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_midi2_negotiated(void) { + return tud_midi2_n_negotiated(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_midi2_protocol(void) { + return tud_midi2_n_protocol(0); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t +tud_midi2_ump_read(uint32_t* words, uint32_t max_words) { + return tud_midi2_n_ump_read(0, words, max_words); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t +tud_midi2_ump_write(const uint32_t* words, uint32_t count) { + return tud_midi2_n_ump_write(0, words, count); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_midi2_packet_read(uint8_t packet[4]) { + return tud_midi2_n_packet_read(0, packet); +} + +TU_ATTR_ALWAYS_INLINE static inline bool tud_midi2_packet_write(const uint8_t packet[4]) { + return tud_midi2_n_packet_write(0, packet); +} + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void midi2d_init(void); +bool midi2d_deinit(void); +void midi2d_reset(uint8_t rhport); +uint16_t midi2d_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len); +bool midi2d_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request); +bool midi2d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/device/usbd.c b/src/device/usbd.c index 0e58f70bf..55ad330c1 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -254,6 +254,20 @@ static const usbd_class_driver_t _usbd_driver[] = { }, #endif + #if CFG_TUD_MIDI2 + { + .name = DRIVER_NAME("MIDI2"), + .init = midi2d_init, + .deinit = midi2d_deinit, + .open = midi2d_open, + .reset = midi2d_reset, + .control_xfer_cb = midi2d_control_xfer_cb, + .xfer_cb = midi2d_xfer_cb, + .xfer_isr = NULL, + .sof = NULL + }, + #endif + #if CFG_TUD_VENDOR { .name = DRIVER_NAME("VENDOR"), diff --git a/src/device/usbd.h b/src/device/usbd.h index 473e697ac..af37eff56 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -424,6 +424,43 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ TUD_MIDI_DESC_EP(_epin, _epsize, 1),\ TUD_MIDI_JACKID_OUT_EMB(1) +//--------------------------------------------------------------------+ +// MIDI 2.0 Descriptor Templates (USB-MIDI 2.0) +//--------------------------------------------------------------------+ + +// Alt Setting 1: MS Interface + MS Header (bcdMSC=0x0200) +#define TUD_MIDI2_DESC_ALT1_HEAD_LEN (9 + 7) +#define TUD_MIDI2_DESC_ALT1_HEAD(_itfnum, _stridx) \ + /* MIDI Streaming Interface, Alt Setting 1 */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 1, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, _stridx,\ + /* MS Header (MIDI 2.0) */\ + 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0200), U16_TO_U8S_LE(7) + +// Alt Setting 1: Standard USB Endpoint (7 bytes) + CS Endpoint (subtype 0x02) +#define TUD_MIDI2_DESC_ALT1_EP_LEN(_numgtbs) (7 + 4 + (_numgtbs)) +#define TUD_MIDI2_DESC_ALT1_EP(_ep, _epsize, _numgtbs) \ + 7, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0, \ + (uint8_t)(4 + (_numgtbs)), TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL_2_0, _numgtbs + +// Total length: Alt 0 (MIDI 1.0) + Alt 1 (UMP) +#define TUD_MIDI2_DESC_LEN (TUD_MIDI_DESC_LEN + TUD_MIDI2_DESC_ALT1_HEAD_LEN + TUD_MIDI2_DESC_ALT1_EP_LEN(1) * 2) + +// Complete MIDI 2.0 descriptor with both alternate settings (single cable/GTB) +#define TUD_MIDI2_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Alt Setting 0 (MIDI 1.0) */\ + TUD_MIDI_DESC_HEAD(_itfnum, _stridx, 1),\ + TUD_MIDI_DESC_JACK_DESC(1, 0),\ + TUD_MIDI_DESC_EP(_epout, _epsize, 1),\ + TUD_MIDI_JACKID_IN_EMB(1),\ + TUD_MIDI_DESC_EP(_epin, _epsize, 1),\ + TUD_MIDI_JACKID_OUT_EMB(1),\ + /* Alt Setting 1 (UMP) */\ + TUD_MIDI2_DESC_ALT1_HEAD(_itfnum, _stridx),\ + TUD_MIDI2_DESC_ALT1_EP(_epout, _epsize, 1),\ + 1, /* bAssoGrpTrmBlkID = 1 */\ + TUD_MIDI2_DESC_ALT1_EP(_epin, _epsize, 1),\ + 1 /* bAssoGrpTrmBlkID = 1 */ + //--------------------------------------------------------------------+ // Audio Descriptor Templates //--------------------------------------------------------------------+ diff --git a/src/tusb.h b/src/tusb.h index c80c8433c..aa6b461e5 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -63,6 +63,10 @@ #include "class/midi/midi_host.h" #endif + #if CFG_TUH_MIDI2 + #include "class/midi/midi2_host.h" + #endif + #if CFG_TUH_VENDOR #include "class/vendor/vendor_host.h" #endif @@ -108,6 +112,10 @@ #include "class/midi/midi_device.h" #endif + #if CFG_TUD_MIDI2 + #include "class/midi/midi2_device.h" + #endif + #if CFG_TUD_VENDOR #include "class/vendor/vendor_device.h" #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 154f8e2a4..4483c2200 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -646,6 +646,42 @@ #define CFG_TUD_MIDI 0 #endif +#ifndef CFG_TUD_MIDI2 + #define CFG_TUD_MIDI2 0 +#endif + +#ifndef CFG_TUD_MIDI2_TX_BUFSIZE + #define CFG_TUD_MIDI2_TX_BUFSIZE 256 +#endif + +#ifndef CFG_TUD_MIDI2_RX_BUFSIZE + #define CFG_TUD_MIDI2_RX_BUFSIZE 256 +#endif + +#ifndef CFG_TUD_MIDI2_TX_EPSIZE + #define CFG_TUD_MIDI2_TX_EPSIZE 64 +#endif + +#ifndef CFG_TUD_MIDI2_RX_EPSIZE + #define CFG_TUD_MIDI2_RX_EPSIZE 64 +#endif + +#ifndef CFG_TUD_MIDI2_NUM_GROUPS + #define CFG_TUD_MIDI2_NUM_GROUPS 1 +#endif + +#ifndef CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS + #define CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS 1 +#endif + +#ifndef CFG_TUD_MIDI2_EP_NAME + #define CFG_TUD_MIDI2_EP_NAME "TinyUSB MIDI 2.0" +#endif + +#ifndef CFG_TUD_MIDI2_PRODUCT_ID + #define CFG_TUD_MIDI2_PRODUCT_ID "TinyUSB-MIDI2" +#endif + #ifndef CFG_TUD_VENDOR #define CFG_TUD_VENDOR 0 #endif @@ -815,6 +851,22 @@ #define CFG_TUH_MIDI 0 #endif +#ifndef CFG_TUH_MIDI2 + #define CFG_TUH_MIDI2 0 +#endif + +#ifndef CFG_TUH_MIDI2_RX_BUFSIZE + #define CFG_TUH_MIDI2_RX_BUFSIZE (4 * TUH_EPSIZE_BULK_MAX) +#endif + +#ifndef CFG_TUH_MIDI2_TX_BUFSIZE + #define CFG_TUH_MIDI2_TX_BUFSIZE (4 * TUH_EPSIZE_BULK_MAX) +#endif + +#ifndef CFG_TUH_MIDI2_LOG_LEVEL + #define CFG_TUH_MIDI2_LOG_LEVEL CFG_TUH_LOG_LEVEL +#endif + #ifndef CFG_TUH_MSC #define CFG_TUH_MSC 0 #endif -- cgit v1.3.1