summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorRémi Berthoz <[email protected]>2026-01-07 17:04:48 +0100
committerRémi Berthoz <[email protected]>2026-01-07 17:36:01 +0100
commit0e5241aa83c52792959ad8a2ef0535a3759530a7 (patch)
tree9ca0493aa275f540041deddd6cec3371f62797a6 /examples
parent158fe86b55ee4704b57ad07a95ceb546b82534af (diff)
Adjust printer device class to 0.20.0 and fix compilation warnings
Diffstat (limited to 'examples')
-rw-r--r--examples/device/printer_to_hid/src/main.c73
-rw-r--r--examples/device/printer_to_hid/src/usb_descriptors.c262
-rw-r--r--examples/device/printer_to_hid/src/usb_descriptors.h48
3 files changed, 170 insertions, 213 deletions
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)
-};
-
-#if TUD_OPT_HIGH_SPEED
-// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
+ TUD_PRINTER_DESCRIPTOR(ITF_PRINTER, 0, EPADDR_PRINTER_OUT, EPADDR_PRINTER_IN, CFG_TUD_PRINTER_EP_BUFSIZE)};
-// other speed configuration
-static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+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,
-// 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,
+ .idVendor = USB_VID,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100,
- .bDeviceClass = 0x00,
- .bDeviceSubClass = 0x00,
- .bDeviceProtocol = 0x00,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT,
+ .iSerialNumber = STR_SERIAL,
- .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
- .bNumConfigurations = 0x01,
- .bReserved = 0x00
+ .bNumConfigurations = 0x01
};
-// 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
+// TinyUSB callbacks (descriptor requests)
//--------------------------------------------------------------------+
-// 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];
+// 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 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;
+// TinyUSB GET CONFIGURATION DESCRIPTOR callback.
+const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
+ (void)index;
+ return CONFIG_INTERFACE_ENDPOINT_DESCRIPTOR;
+}
- switch ( index ) {
- case STRID_LANGID:
- memcpy(&_desc_str[1], string_desc_arr[0], 2);
- chr_count = 1;
- break;
+// TinyUSB GET DEVICE DESCRIPTOR callback.
+const uint8_t *tud_descriptor_device_cb(void) {
+ return (const uint8_t *)&DEVICE_DESCRIPTOR;
+}
- case STRID_SERIAL:
- chr_count = board_usb_get_serial(_desc_str + 1, 32);
- break;
+// Storage buffer array for string descriptor to be sent to host.
+static uint16_t string_descriptor_buffer[STRING_DESCRIPTOR_MAX_LENGTH + 1];
- 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
+// 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 < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+ 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
- const char *str = string_desc_arr[index];
+ } 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);
- // 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;
+ } 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];
+ }
- // Convert ASCII string into UTF-16
- for ( size_t i = 0; i < chr_count; i++ ) {
- _desc_str[1 + i] = str[i];
- }
- break;
+ } 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_ */