summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
authorZixun LI <[email protected]>2026-03-12 10:55:18 +0100
committerZixun LI <[email protected]>2026-03-12 10:55:18 +0100
commite2884a1b88ce89b6e724e3d3b441b8ee1f83c06c (patch)
treec322110908d4282ca7e37fe52dc9ce42be15b073 /src/class
parentc06dc871d651ddaf756afdc59ed148c4b1af314d (diff)
parentac61a5b176b44db503d8bcc287a622463b65e48e (diff)
Merge branch 'master' into ncm_restart
Diffstat (limited to 'src/class')
-rw-r--r--src/class/audio/audio_device.c34
-rw-r--r--src/class/cdc/cdc_device.c22
-rw-r--r--src/class/cdc/cdc_device.h64
-rw-r--r--src/class/cdc/cdc_host.c8
-rw-r--r--src/class/cdc/cdc_host.h8
-rw-r--r--src/class/dfu/dfu_device.c25
-rw-r--r--src/class/midi/midi_device.c114
-rw-r--r--src/class/midi/midi_device.h29
-rw-r--r--src/class/midi/midi_host.c10
-rw-r--r--src/class/midi/midi_host.h6
-rw-r--r--src/class/printer/printer.h62
-rw-r--r--src/class/printer/printer_device.c329
-rw-r--r--src/class/printer/printer_device.h151
-rw-r--r--src/class/vendor/vendor_device.c45
-rw-r--r--src/class/vendor/vendor_device.h25
-rw-r--r--src/class/video/video_device.c13
16 files changed, 832 insertions, 113 deletions
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index be0224811..995bf8a3e 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -147,7 +147,9 @@ tu_static CFG_TUD_MEM_SECTION struct {
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
// Control buffer
-CFG_TUD_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SZ];
+#if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE
+tu_static CFG_TUD_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SZ];
+#endif
// Aligned buffer for feedback EP
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@@ -423,6 +425,15 @@ bool tud_audio_n_mounted(uint8_t func_id) {
return audio->mounted;
}
+static inline uint8_t* get_ctrl_buffer(void) {
+ // Use EP0 buffer if it is large enough, otherwise use dedicated buffer
+ #if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE
+ return ctrl_buf;
+ #else
+ return usbd_get_ctrl_buf();
+ #endif
+}
+
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
@@ -1296,20 +1307,20 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
if (tud_audio_n_version(func_id) == 2) {
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
- _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(ctrl_buf);
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer());
audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
}
}
#endif
// Invoke callback
- return tud_audio_set_req_entity_cb(rhport, p_request, ctrl_buf);
+ return tud_audio_set_req_entity_cb(rhport, p_request, get_ctrl_buffer());
} else {
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
// Invoke callback
- return tud_audio_set_req_itf_cb(rhport, p_request, ctrl_buf);
+ return tud_audio_set_req_itf_cb(rhport, p_request, get_ctrl_buffer());
}
} break;
@@ -1324,7 +1335,7 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
if (_audiod_fct[func_id].ep_in == ep) {
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
if (ctrlSel == AUDIO10_EP_CTRL_SAMPLING_FREQ && p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
- _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(ctrl_buf) & 0x00FFFFFF;
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer()) & 0x00FFFFFF;
audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
}
}
@@ -1332,7 +1343,7 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
#endif
// Invoke callback
- bool ret = tud_audio_set_req_ep_cb(rhport, p_request, ctrl_buf);
+ bool ret = tud_audio_set_req_ep_cb(rhport, p_request, get_ctrl_buffer());
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
if (ret && tud_audio_n_version(func_id) == 1) {
@@ -1429,7 +1440,7 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const
}
// If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished
- TU_VERIFY(tud_control_xfer(rhport, p_request, ctrl_buf, sizeof(ctrl_buf)));
+ TU_VERIFY(tud_control_xfer(rhport, p_request, get_ctrl_buffer(), CFG_TUD_AUDIO_CTRL_BUF_SZ));
return true;
}
@@ -1695,11 +1706,8 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
return false;
}
- // Crop length
- if (len > sizeof(ctrl_buf)) len = sizeof(ctrl_buf);
-
// Copy into buffer
- TU_VERIFY(0 == tu_memcpy_s(ctrl_buf, sizeof(ctrl_buf), data, (size_t) len));
+ TU_VERIFY(0 == tu_memcpy_s(get_ctrl_buffer(), CFG_TUD_AUDIO_CTRL_BUF_SZ, data, (size_t) len));
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
if (tud_audio_n_version(func_id) == 2) {
@@ -1708,7 +1716,7 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
- _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(ctrl_buf);
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer());
audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
}
}
@@ -1716,7 +1724,7 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
#endif
// Schedule transmit
- return tud_control_xfer(rhport, p_request, ctrl_buf, len);
+ return tud_control_xfer(rhport, p_request, get_ctrl_buffer(), len);
}
// Verify an entity with the given ID exists and returns also the corresponding driver index
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index e2819ae4b..c7547c92b 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -63,10 +63,10 @@ typedef struct {
#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, line_coding)
// Skip local EP buffer if dedicated hw FIFO is supported
- #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
typedef struct {
- TUD_EPBUF_DEF(epout, CFG_TUD_CDC_EP_BUFSIZE);
- TUD_EPBUF_DEF(epin, CFG_TUD_CDC_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_CDC_RX_EPSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_CDC_TX_EPSIZE);
#if CFG_TUD_CDC_NOTIFY
TUD_EPBUF_TYPE_DEF(cdc_notify_msg_t, epnotify);
@@ -116,7 +116,7 @@ TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) {
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
-static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT();
+static tud_cdc_configure_t _cdcd_cfg = CFG_TUD_CDC_CONFIGURE_DEFAULT();
TU_ATTR_ALWAYS_INLINE static inline uint8_t find_cdc_itf(uint8_t ep_addr) {
for (uint8_t idx = 0; idx < CFG_TUD_CDC; idx++) {
@@ -267,14 +267,13 @@ void cdcd_init(void) {
uint8_t *epin_buf = _cdcd_epbuf[i].epin;
#endif
- tu_edpt_stream_init(&p_cdc->rx_stream, false, false, false, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, epout_buf,
- CFG_TUD_CDC_EP_BUFSIZE);
+ tu_edpt_stream_init(&p_cdc->rx_stream, false, false, false, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, epout_buf);
// TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not
// know if data is actually polled by terminal. This way the most current data is prioritized.
// Default: is overwritable
tu_edpt_stream_init(&p_cdc->tx_stream, false, true, _cdcd_cfg.tx_overwritabe_if_not_connected, p_cdc->tx_ff_buf,
- CFG_TUD_CDC_TX_BUFSIZE, epin_buf, CFG_TUD_CDC_EP_BUFSIZE);
+ CFG_TUD_CDC_TX_BUFSIZE, epin_buf);
}
}
@@ -348,8 +347,7 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream;
-
- tu_edpt_stream_open(stream_tx, rhport, desc_ep);
+ tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_CDC_TX_EPSIZE);
if (_cdcd_cfg.tx_persistent) {
tu_edpt_stream_write_xfer(stream_tx); // flush pending data
} else {
@@ -357,8 +355,8 @@ uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16
}
} else {
tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream;
-
- tu_edpt_stream_open(stream_rx, rhport, desc_ep);
+ tu_edpt_stream_open(stream_rx, rhport, desc_ep,
+ CFG_TUD_CDC_RX_NEED_ZLP ? CFG_TUD_CDC_RX_EPSIZE : tu_edpt_packet_size(desc_ep));
if (!_cdcd_cfg.rx_persistent) {
tu_edpt_stream_clear(stream_rx);
}
@@ -513,7 +511,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
}
// Data sent to host, we continue to fetch from tx fifo to send.
- // Note: This will cause incorrect baudrate set in line coding. Though maybe the baudrate is not really important !
+ // Note: This will cause incorrect baudrate set in line coding. Though maybe the baudrate is not really important!
if (ep_addr == stream_tx->ep_addr) {
tud_cdc_tx_complete_cb(itf); // invoke callback to possibly refill tx fifo
diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index 0809b578f..0348bd2ec 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -29,6 +29,10 @@
#include "cdc.h"
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
@@ -37,41 +41,52 @@
#endif
#ifndef CFG_TUD_CDC_TX_BUFSIZE
- #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+ #define CFG_TUD_CDC_TX_BUFSIZE TUD_EPSIZE_BULK_MAX
#endif
#ifndef CFG_TUD_CDC_RX_BUFSIZE
- #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+ #define CFG_TUD_CDC_RX_BUFSIZE TUD_EPSIZE_BULK_MAX
#endif
-#if !defined(CFG_TUD_CDC_EP_BUFSIZE) && defined(CFG_TUD_CDC_EPSIZE)
- #warning CFG_TUD_CDC_EPSIZE is renamed to CFG_TUD_CDC_EP_BUFSIZE, please update to use the new name
- #define CFG_TUD_CDC_EP_BUFSIZE CFG_TUD_CDC_EPSIZE
+// EP_BUFSIZE is separated to RX_EPSIZE and TX_EPSIZE
+#ifndef CFG_TUD_CDC_RX_EPSIZE
+ #ifdef CFG_TUD_CDC_EP_BUFSIZE
+ #define CFG_TUD_CDC_RX_EPSIZE CFG_TUD_CDC_EP_BUFSIZE
+ #else
+ #define CFG_TUD_CDC_RX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
#endif
-#ifndef CFG_TUD_CDC_EP_BUFSIZE
- #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#ifndef CFG_TUD_CDC_TX_EPSIZE
+ #ifdef CFG_TUD_CDC_EP_BUFSIZE
+ #define CFG_TUD_CDC_TX_EPSIZE CFG_TUD_CDC_EP_BUFSIZE
+ #else
+ #define CFG_TUD_CDC_TX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
#endif
-#ifdef __cplusplus
- extern "C" {
+// Enable multi-packet RX transfer with ZLP termination for better throughput. Requires host support for ZLP.
+#ifndef CFG_TUD_CDC_RX_NEED_ZLP
+ #define CFG_TUD_CDC_RX_NEED_ZLP 0
+#endif
+
+#ifndef CFG_TUD_CDC_CONFIGURE_DEFAULT
+ #define CFG_TUD_CDC_CONFIGURE_DEFAULT() \
+ { \
+ .rx_persistent = false, \
+ .tx_persistent = false, \
+ .tx_overwritabe_if_not_connected = true, \
+ }
#endif
//--------------------------------------------------------------------+
// Driver Configuration
//--------------------------------------------------------------------+
-typedef struct TU_ATTR_PACKED {
- bool rx_persistent : 1; // keep rx fifo data even with bus reset or disconnect
- bool tx_persistent : 1; // keep tx fifo data even with reset or disconnect
- bool tx_overwritabe_if_not_connected : 1; // if not connected, tx fifo can be overwritten
+typedef struct {
+ bool rx_persistent; // keep rx fifo data even with bus reset or disconnect
+ bool tx_persistent; // keep tx fifo data even with reset or disconnect
+ bool tx_overwritabe_if_not_connected; // if not connected, tx fifo can be overwritten
} tud_cdc_configure_t;
-TU_VERIFY_STATIC(sizeof(tud_cdc_configure_t) == 1, "size is not correct");
-
-#define TUD_CDC_CONFIGURE_DEFAULT() { \
- .rx_persistent = false, \
- .tx_persistent = false, \
- .tx_overwritabe_if_not_connected = true, \
-}
// Configure CDC driver behavior
bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg);
@@ -84,13 +99,13 @@ bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg);
// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1
//--------------------------------------------------------------------+
-// Check if interface is ready
+// Check if the interface is ready
bool tud_cdc_n_ready(uint8_t itf);
-// Check if terminal is connected to this port
+// Check if the terminal is connected to this port
bool tud_cdc_n_connected(uint8_t itf);
-// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
+// Get the current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
uint8_t tud_cdc_n_get_line_state(uint8_t itf);
// Get current line encoding: bit rate, stop bits parity etc ..
@@ -136,10 +151,9 @@ uint32_t tud_cdc_n_write_flush(uint8_t itf);
// Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation.
uint32_t tud_cdc_n_write_available(uint8_t itf);
-// Clear the transmit FIFO
+// Clear the TX FIFO
bool tud_cdc_n_write_clear(uint8_t itf);
-
#if CFG_TUD_CDC_NOTIFY
bool tud_cdc_n_notify_msg(uint8_t itf, cdc_notify_msg_t *msg);
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 8f6dd7200..62c313b83 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -650,10 +650,10 @@ bool cdch_init(void) {
for (size_t i = 0; i < CFG_TUH_CDC; i++) {
cdch_interface_t *p_cdc = &cdch_data[i];
cdch_epbuf_t *epbuf = &cdch_epbuf[i];
- TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false, p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
- epbuf->tx, CFG_TUH_CDC_TX_EPSIZE));
+ TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false, p_cdc->stream.tx_ff_buf,
+ CFG_TUH_CDC_TX_BUFSIZE, epbuf->tx));
TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false, p_cdc->stream.rx_ff_buf,
- CFG_TUH_CDC_RX_BUFSIZE, epbuf->rx, CFG_TUH_CDC_RX_EPSIZE));
+ CFG_TUH_CDC_RX_BUFSIZE, epbuf->rx));
}
return true;
@@ -737,7 +737,7 @@ static bool open_ep_stream_pair(cdch_interface_t *p_cdc, tusb_desc_endpoint_t co
TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
const uint8_t ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress);
tu_edpt_stream_t *stream = (ep_dir == TUSB_DIR_IN) ? &p_cdc->stream.rx : &p_cdc->stream.tx;
- tu_edpt_stream_open(stream, p_cdc->daddr, desc_ep);
+ tu_edpt_stream_open(stream, p_cdc->daddr, desc_ep, tu_edpt_packet_size(desc_ep));
tu_edpt_stream_clear(stream);
desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(desc_ep);
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index 57919c7ff..1b1709b18 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -39,22 +39,22 @@ extern "C" {
// RX FIFO size
#ifndef CFG_TUH_CDC_RX_BUFSIZE
- #define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
// RX Endpoint size
#ifndef CFG_TUH_CDC_RX_EPSIZE
- #define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MAX
#endif
// TX FIFO size
#ifndef CFG_TUH_CDC_TX_BUFSIZE
- #define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
// TX Endpoint size
#ifndef CFG_TUH_CDC_TX_EPSIZE
- #define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MAX
#endif
//--------------------------------------------------------------------+
diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c
index d3cc53918..ee57621b8 100644
--- a/src/class/dfu/dfu_device.c
+++ b/src/class/dfu/dfu_device.c
@@ -60,7 +60,9 @@ typedef struct {
static dfu_state_ctx_t _dfu_ctx;
+#if CFG_TUD_DFU_XFER_BUFSIZE > CFG_TUD_ENDPOINT0_BUFSIZE
TU_ATTR_ALIGNED(4) uint8_t _transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
+#endif
static void reset_state(void) {
_dfu_ctx.state = DFU_IDLE;
@@ -68,6 +70,15 @@ static void reset_state(void) {
_dfu_ctx.flashing_in_progress = false;
}
+static inline uint8_t* get_xfer_buffer(void) {
+ // Use EP0 buffer if it is large enough, otherwise use dedicated buffer
+ #if CFG_TUD_DFU_XFER_BUFSIZE > CFG_TUD_ENDPOINT0_BUFSIZE
+ return _transfer_buf;
+ #else
+ return usbd_get_ctrl_buf();
+ #endif
+}
+
static bool reply_getstatus(uint8_t rhport, const tusb_control_request_t* request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request);
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request);
@@ -283,10 +294,10 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
- const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _transfer_buf,
+ const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, get_xfer_buffer(),
request->wLength);
- return tud_control_xfer(rhport, request, _transfer_buf, xfer_len);
+ return tud_control_xfer(rhport, request, get_xfer_buffer(), xfer_len);
}
break;
@@ -306,7 +317,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
if (request->wLength > 0) {
// Download with payload -> transition to DOWNLOAD SYNC
_dfu_ctx.state = DFU_DNLOAD_SYNC;
- return tud_control_xfer(rhport, request, _transfer_buf, request->wLength);
+ return tud_control_xfer(rhport, request, get_xfer_buffer(), request->wLength);
} else {
// Download is complete -> transition to MANIFEST SYNC
_dfu_ctx.state = DFU_MANIFEST_SYNC;
@@ -327,7 +338,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
default:
if (stage == CONTROL_STAGE_SETUP) {
- return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
+ return reply_getstatus(rhport, request, (dfu_state_t) _dfu_ctx.state, (dfu_status_t) _dfu_ctx.status, 0);
}
break;
}
@@ -376,11 +387,11 @@ static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tus
timeout = 0;
}
- return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
+ return reply_getstatus(rhport, request, next_state, (dfu_status_t) _dfu_ctx.status, timeout);
} else if (stage == CONTROL_STAGE_ACK) {
if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_DNBUSY;
- tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _transfer_buf, _dfu_ctx.length);
+ tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, get_xfer_buffer(), _dfu_ctx.length);
} else {
_dfu_ctx.state = DFU_DNLOAD_IDLE;
}
@@ -405,7 +416,7 @@ static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tus
timeout = 0;
}
- return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
+ return reply_getstatus(rhport, request, next_state, (dfu_status_t) _dfu_ctx.status, timeout);
} else if (stage == CONTROL_STAGE_ACK) {
if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_MANIFEST;
diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c
index 023a81595..de4ff5dd8 100644
--- a/src/class/midi/midi_device.c
+++ b/src/class/midi/midi_device.c
@@ -74,8 +74,8 @@ static midid_interface_t _midid_itf[CFG_TUD_MIDI];
#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
// Endpoint Transfer buffer: not used if dedicated hw FIFO is available
typedef struct {
- TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_EP_BUFSIZE);
- TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_TX_EPSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_RX_EPSIZE);
} midid_epbuf_t;
CFG_TUD_MEM_SECTION static midid_epbuf_t _midid_epbuf[CFG_TUD_MIDI];
@@ -168,6 +168,108 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, ui
return total_read;
}
+// Note: this function shares stream->buffer with tud_midi_n_stream_read().
+// Do not mix calls to both functions on the same interface.
+uint32_t tud_midi_n_demux_stream_read(uint8_t itf, uint8_t *p_cable_num, void *buffer, uint32_t bufsize) {
+ TU_VERIFY(p_cable_num != NULL && buffer != NULL && bufsize > 0, 0);
+
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ midi_driver_stream_t *stream = &p_midi->stream_read;
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
+
+ uint8_t *buf8 = (uint8_t *)buffer;
+ uint32_t total_read = 0;
+
+ // Initialize to invalid cable so callers can detect "no data" even when
+ // the return value is 0.
+ *p_cable_num = 0xff;
+
+ // If there are leftover bytes from a previous partial read, return them first
+ if (stream->total > 0) {
+ *p_cable_num = (stream->buffer[0] >> 4) & 0x0f;
+ const uint8_t count = (uint8_t)tu_min32((uint32_t)(stream->total - stream->index), bufsize);
+ TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1 + stream->index, count));
+
+ total_read += count;
+ stream->index += count;
+ buf8 += count;
+ bufsize -= count;
+
+ if (stream->total == stream->index) {
+ stream->index = 0;
+ stream->total = 0;
+ }
+
+ if (bufsize == 0) {
+ return total_read;
+ }
+ }
+
+ while (bufsize > 0) {
+ // Peek at next packet header to get cable number without consuming
+ uint8_t one_byte;
+ if (!tu_edpt_stream_peek(ep_str, &one_byte)) {
+ break;
+ }
+
+ const uint8_t next_cable = (one_byte >> 4) & 0x0f;
+
+ // Stop if cable changed (covers both leftover-originated reads and
+ // freshly consumed packets — total_read > 0 in either case)
+ if (total_read > 0 && next_cable != *p_cable_num) {
+ break;
+ }
+ *p_cable_num = next_cable;
+
+ // Consume the packet
+ if (!tud_midi_n_packet_read(itf, stream->buffer)) {
+ break;
+ }
+
+ const uint8_t code_index = stream->buffer[0] & 0x0f;
+ uint8_t msg_bytes;
+
+ // MIDI 1.0 Table 4-1: Code Index Number Classifications
+ switch (code_index) {
+ case MIDI_CIN_MISC:
+ case MIDI_CIN_CABLE_EVENT:
+ // Reserved and unused, skip this packet
+ continue;
+
+ case MIDI_CIN_SYSEX_END_1BYTE:
+ case MIDI_CIN_1BYTE_DATA:
+ msg_bytes = 1;
+ break;
+
+ case MIDI_CIN_SYSCOM_2BYTE:
+ case MIDI_CIN_SYSEX_END_2BYTE:
+ case MIDI_CIN_PROGRAM_CHANGE:
+ case MIDI_CIN_CHANNEL_PRESSURE:
+ msg_bytes = 2;
+ break;
+
+ default:
+ msg_bytes = 3;
+ break;
+ }
+
+ const uint8_t count = (uint8_t)tu_min32((uint32_t)msg_bytes, bufsize);
+ TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1, count));
+
+ total_read += count;
+ buf8 += count;
+ bufsize -= count;
+
+ if (count < msg_bytes) {
+ // Output buffer full, save remaining for next call
+ stream->total = msg_bytes;
+ stream->index = count;
+ }
+ }
+
+ return total_read;
+}
+
bool tud_midi_n_packet_read(uint8_t itf, uint8_t packet[4]) {
midid_interface_t *p_midi = &_midid_itf[itf];
tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
@@ -324,10 +426,10 @@ void midid_init(void) {
#endif
tu_edpt_stream_init(&p_midi->ep_stream.rx, false, false, false, p_midi->ep_stream.rx_ff_buf,
- CFG_TUD_MIDI_RX_BUFSIZE, epout_buf, CFG_TUD_MIDI_EP_BUFSIZE);
+ CFG_TUD_MIDI_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_MIDI_TX_BUFSIZE,
- epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
+ epin_buf);
}
}
@@ -408,11 +510,11 @@ uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint1
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
tu_edpt_stream_t *stream_tx = &p_midi->ep_stream.tx;
- tu_edpt_stream_open(stream_tx, rhport, desc_ep);
+ tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_MIDI_TX_EPSIZE);
tu_edpt_stream_clear(stream_tx);
} else {
tu_edpt_stream_t *stream_rx = &p_midi->ep_stream.rx;
- tu_edpt_stream_open(stream_rx, rhport, desc_ep);
+ tu_edpt_stream_open(stream_rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep));
tu_edpt_stream_clear(stream_rx);
TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); // prepare to receive data
}
diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h
index ddbc2f9f0..57eabec1f 100644
--- a/src/class/midi/midi_device.h
+++ b/src/class/midi/midi_device.h
@@ -34,13 +34,20 @@
// Class Driver Configuration
//--------------------------------------------------------------------+
-#if !defined(CFG_TUD_MIDI_EP_BUFSIZE) && defined(CFG_TUD_MIDI_EPSIZE)
- #warning CFG_TUD_MIDI_EPSIZE is renamed to CFG_TUD_MIDI_EP_BUFSIZE, please update to use the new name
- #define CFG_TUD_MIDI_EP_BUFSIZE CFG_TUD_MIDI_EPSIZE
+#ifndef CFG_TUD_MIDI_RX_EPSIZE
+ #ifdef CFG_TUD_MIDI_EP_BUFSIZE
+ #define CFG_TUD_MIDI_RX_EPSIZE CFG_TUD_MIDI_EP_BUFSIZE
+ #else
+ #define CFG_TUD_MIDI_RX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
#endif
-#ifndef CFG_TUD_MIDI_EP_BUFSIZE
- #define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#ifndef CFG_TUD_MIDI_TX_EPSIZE
+ #ifdef CFG_TUD_MIDI_EP_BUFSIZE
+ #define CFG_TUD_MIDI_TX_EPSIZE CFG_TUD_MIDI_EP_BUFSIZE
+ #else
+ #define CFG_TUD_MIDI_TX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
#endif
#ifdef __cplusplus
@@ -66,6 +73,13 @@ uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num);
// Read byte stream (legacy)
uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, uint32_t bufsize);
+// Read byte stream with cable demultiplexing: returns the cable number of the
+// data that was read. Reads from a single cable per call; stops when the next
+// packet belongs to a different cable so the caller can dispatch per-cable.
+// Note: shares internal state with tud_midi_n_stream_read(); do not mix both
+// on the same interface.
+uint32_t tud_midi_n_demux_stream_read(uint8_t itf, uint8_t *p_cable_num, void *buffer, uint32_t bufsize);
+
// Write byte Stream (legacy)
uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize);
@@ -97,6 +111,11 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_stream_read(void *buffer,
}
TU_ATTR_ALWAYS_INLINE static inline uint32_t
+tud_midi_demux_stream_read(uint8_t *p_cable_num, void *buffer, uint32_t bufsize) {
+ return tud_midi_n_demux_stream_read(0, p_cable_num, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t
tud_midi_stream_write(uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize) {
return tud_midi_n_stream_write(0, cable_num, buffer, bufsize);
}
diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c
index 5548a0ba8..0feb5106d 100644
--- a/src/class/midi/midi_host.c
+++ b/src/class/midi/midi_host.c
@@ -83,8 +83,8 @@ typedef struct {
}midih_interface_t;
typedef struct {
- TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MPS);
- TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MPS);
+ TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MAX);
+ TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MAX);
} midih_epbuf_t;
static midih_interface_t _midi_host[CFG_TUH_MIDI];
@@ -121,9 +121,9 @@ bool midih_init(void) {
for (int inst = 0; inst < CFG_TUH_MIDI; inst++) {
midih_interface_t *p_midi_host = &_midi_host[inst];
tu_edpt_stream_init(&p_midi_host->ep_stream.rx, true, false, false,
- p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf->rx, TUH_EPSIZE_BULK_MPS);
+ p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf[inst].rx);
tu_edpt_stream_init(&p_midi_host->ep_stream.tx, true, true, false,
- p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf->tx, TUH_EPSIZE_BULK_MPS);
+ p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf[inst].tx);
}
return true;
}
@@ -306,7 +306,7 @@ uint16_t midih_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_
ep_stream = &p_midi->ep_stream.rx;
}
TU_ASSERT(tuh_edpt_open(dev_addr, p_ep), 0);
- tu_edpt_stream_open(ep_stream, dev_addr, p_ep);
+ tu_edpt_stream_open(ep_stream, dev_addr, p_ep, tu_edpt_packet_size(p_ep));
tu_edpt_stream_clear(ep_stream);
break;
diff --git a/src/class/midi/midi_host.h b/src/class/midi/midi_host.h
index 8a8dccab4..b9ab0130d 100644
--- a/src/class/midi/midi_host.h
+++ b/src/class/midi/midi_host.h
@@ -38,15 +38,15 @@ extern "C" {
// Class Driver Configuration
//--------------------------------------------------------------------+
#ifndef CFG_TUH_MIDI_RX_BUFSIZE
- #define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
#ifndef CFG_TUH_MIDI_TX_BUFSIZE
- #define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
#ifndef CFG_TUH_MIDI_EP_BUFSIZE
- #define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
// Enable the MIDI stream read/write API. Some library can work with raw USB MIDI packet
diff --git a/src/class/printer/printer.h b/src/class/printer/printer.h
new file mode 100644
index 000000000..09d1a8956
--- /dev/null
+++ b/src/class/printer/printer.h
@@ -0,0 +1,62 @@
+/*
+ * 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_H_
+#define TUSB_PRINTER_H_
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Printer Class Specific Control Request
+typedef enum {
+ TUSB_PRINTER_REQUEST_GET_DEVICE_ID = 0x00, ///< Get device ID
+ TUSB_PRINTER_REQUEST_GET_PORT_STATUS = 0x01, ///< Get port status
+ TUSB_PRINTER_REQUEST_SOFT_RESET = 0x02, ///< 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
diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c
new file mode 100644
index 000000000..d2dc9b163
--- /dev/null
+++ b/src/class/printer/printer_device.c
@@ -0,0 +1,329 @@
+/*
+ * 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 "device/usbd.h"
+#include "device/usbd_pvt.h"
+
+#include "printer_device.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+typedef struct {
+ uint8_t itf_num;
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+
+ tu_edpt_stream_t rx_stream;
+ tu_edpt_stream_t tx_stream;
+
+ uint8_t rx_ff_buf[CFG_TUD_PRINTER_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUD_PRINTER_TX_BUFSIZE];
+} printer_interface_t;
+
+#define ITF_MEM_RESET_SIZE offsetof(printer_interface_t, rx_stream)
+
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
+typedef struct {
+ TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_RX_EPSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_TX_EPSIZE);
+} printer_epbuf_t;
+
+CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER];
+#endif
+
+static printer_interface_t _printer_itf[CFG_TUD_PRINTER];
+
+//--------------------------------------------------------------------+
+// INTERNAL HELPERS
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t _find_itf(uint8_t ep_addr) {
+ for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) {
+ const printer_interface_t *p = &_printer_itf[i];
+ if (ep_addr == p->rx_stream.ep_addr || ep_addr == p->tx_stream.ep_addr) {
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf) {
+ (void)itf;
+}
+
+TU_ATTR_WEAK void tud_printer_tx_complete_cb(uint8_t itf) {
+ (void)itf;
+}
+
+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;
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+uint32_t tud_printer_n_read_available(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_read_available(&_printer_itf[itf].rx_stream);
+}
+
+uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_read(&_printer_itf[itf].rx_stream, buffer, bufsize);
+}
+
+bool tud_printer_n_peek(uint8_t itf, uint8_t *chr) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER);
+ return tu_edpt_stream_peek(&_printer_itf[itf].rx_stream, chr);
+}
+
+void tud_printer_n_read_flush(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, );
+ printer_interface_t *p = &_printer_itf[itf];
+ tu_edpt_stream_clear(&p->rx_stream);
+ tu_edpt_stream_read_xfer(&p->rx_stream);
+}
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+uint32_t tud_printer_n_write(uint8_t itf, const void *buffer, uint32_t bufsize) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_write(&_printer_itf[itf].tx_stream, buffer, bufsize);
+}
+
+uint32_t tud_printer_n_write_flush(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_write_xfer(&_printer_itf[itf].tx_stream);
+}
+
+uint32_t tud_printer_n_write_available(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_write_available(&_printer_itf[itf].tx_stream);
+}
+
+bool tud_printer_n_write_clear(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER);
+ tu_edpt_stream_clear(&_printer_itf[itf].tx_stream);
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// USBD-CLASS API
+//--------------------------------------------------------------------+
+void printerd_init(void) {
+ tu_memclr(_printer_itf, sizeof(_printer_itf));
+
+ for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) {
+ printer_interface_t *p = &_printer_itf[i];
+
+ #if CFG_TUD_EDPT_DEDICATED_HWFIFO
+ uint8_t *epout_buf = NULL;
+ uint8_t *epin_buf = NULL;
+ #else
+ uint8_t *epout_buf = _printer_epbuf[i].epout;
+ uint8_t *epin_buf = _printer_epbuf[i].epin;
+ #endif
+
+ tu_edpt_stream_init(&p->rx_stream, false, false, false,
+ p->rx_ff_buf, CFG_TUD_PRINTER_RX_BUFSIZE, epout_buf);
+
+ tu_edpt_stream_init(&p->tx_stream, false, true, true,
+ p->tx_ff_buf, CFG_TUD_PRINTER_TX_BUFSIZE, epin_buf);
+ }
+}
+
+bool printerd_deinit(void) {
+ for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) {
+ printer_interface_t *p = &_printer_itf[i];
+ tu_edpt_stream_deinit(&p->rx_stream);
+ tu_edpt_stream_deinit(&p->tx_stream);
+ }
+ 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_itf[i];
+ tu_memclr(p, ITF_MEM_RESET_SIZE);
+ tu_edpt_stream_close(&p->rx_stream);
+ tu_edpt_stream_close(&p->tx_stream);
+ }
+}
+
+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);
+
+ // Find available interface slot
+ uint8_t const printer_id = _find_itf(0);
+ TU_ASSERT(printer_id < CFG_TUD_PRINTER, 0);
+ printer_interface_t *p = &_printer_itf[printer_id];
+
+ p->itf_num = itf_desc->bInterfaceNumber;
+
+ //------------- Endpoints -------------//
+ const uint8_t *p_desc = (const uint8_t *)itf_desc;
+ const uint8_t *desc_end = p_desc + max_len;
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+
+ p_desc = tu_desc_next(itf_desc);
+ for (uint8_t e = 0; e < itf_desc->bNumEndpoints; e++) {
+ TU_VERIFY(tu_desc_in_bounds(p_desc, desc_end), 0);
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0);
+
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
+
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ tu_edpt_stream_t *stream_tx = &p->tx_stream;
+ tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_PRINTER_TX_EPSIZE);
+ tu_edpt_stream_clear(stream_tx);
+ } else {
+ tu_edpt_stream_t *stream_rx = &p->rx_stream;
+ tu_edpt_stream_open(stream_rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep));
+ tu_edpt_stream_clear(stream_rx);
+ TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0);
+ }
+
+ drv_len += sizeof(tusb_desc_endpoint_t);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ 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 &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+
+ // GET_DEVICE_ID: wIndex = (interface_number << 8) | alt_setting
+ // GET_PORT_STATUS / SOFT_RESET: wIndex = interface_number
+ uint8_t itf_num;
+ if (TUSB_PRINTER_REQUEST_GET_DEVICE_ID == request->bRequest) {
+ itf_num = tu_u16_high(request->wIndex);
+ } else {
+ itf_num = tu_u16_low(request->wIndex);
+ }
+
+ // Find the printer instance index from the USB interface number
+ uint8_t itf = TUSB_INDEX_INVALID_8;
+ for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) {
+ if (_printer_itf[i].itf_num == itf_num) {
+ itf = i;
+ break;
+ }
+ }
+ TU_VERIFY(itf < CFG_TUD_PRINTER);
+
+ // https://www.usb.org/sites/default/files/usbprint11a021811.pdf
+ if (stage == CONTROL_STAGE_SETUP) {
+ switch (request->bRequest) {
+ case TUSB_PRINTER_REQUEST_GET_DEVICE_ID: {
+ const uint8_t *device_id = tud_printer_get_device_id_cb(itf);
+ 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);
+ }
+
+ case TUSB_PRINTER_REQUEST_GET_PORT_STATUS: {
+ static uint8_t port_status;
+ port_status = tud_printer_get_port_status_cb(itf);
+ return tud_control_xfer(rhport, request, &port_status, sizeof(port_status));
+ }
+
+ case TUSB_PRINTER_REQUEST_SOFT_RESET:
+ tud_printer_soft_reset_cb(itf);
+ tud_control_status(rhport, request);
+ return true;
+
+ default:
+ return false;
+ }
+ } else if (stage == CONTROL_STAGE_ACK) {
+ tud_printer_request_complete_cb(itf, request);
+ }
+
+ return true;
+}
+
+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 const itf = _find_itf(ep_addr);
+ TU_ASSERT(itf < CFG_TUD_PRINTER);
+ printer_interface_t *p = &_printer_itf[itf];
+
+ // Received new data
+ if (ep_addr == p->rx_stream.ep_addr) {
+ tu_edpt_stream_read_xfer_complete(&p->rx_stream, xferred_bytes);
+
+ if (!tu_edpt_stream_empty(&p->rx_stream)) {
+ tud_printer_rx_cb(itf);
+ }
+
+ tu_edpt_stream_read_xfer(&p->rx_stream);
+ }
+
+ // Data sent to host
+ if (ep_addr == p->tx_stream.ep_addr) {
+ tud_printer_tx_complete_cb(itf);
+
+ if (0 == tu_edpt_stream_write_xfer(&p->tx_stream)) {
+ tu_edpt_stream_write_zlp_if_needed(&p->tx_stream, xferred_bytes);
+ }
+ }
+
+ 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..afde1f022
--- /dev/null
+++ b/src/class/printer/printer_device.h
@@ -0,0 +1,151 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "printer.h"
+
+//--------------------------------------------------------------------+
+// Configuration
+//--------------------------------------------------------------------+
+#ifndef CFG_TUD_PRINTER_RX_EPSIZE
+ #define CFG_TUD_PRINTER_RX_EPSIZE TUD_EPSIZE_BULK_MAX
+#endif
+
+#ifndef CFG_TUD_PRINTER_TX_EPSIZE
+ #define CFG_TUD_PRINTER_TX_EPSIZE TUD_EPSIZE_BULK_MAX
+#endif
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Ports) i.e. CFG_TUD_PRINTER > 1
+//--------------------------------------------------------------------+
+
+// Get the number of bytes available for reading
+uint32_t tud_printer_n_read_available(uint8_t itf);
+
+// Read received bytes
+uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize);
+
+// Get the number of bytes available for writing
+uint32_t tud_printer_n_write_available(uint8_t itf);
+
+// 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);
+
+// Write data to host
+uint32_t tud_printer_n_write(uint8_t itf, const void *buffer, uint32_t bufsize);
+
+// Force sending data in the TX FIFO
+uint32_t tud_printer_n_write_flush(uint8_t itf);
+
+// Clear the transmit FIFO
+bool tud_printer_n_write_clear(uint8_t itf);
+
+//--------------------------------------------------------------------+
+// Application API (Single Port)
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_read_available(void) {
+ return tud_printer_n_read_available(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_write_available(void) {
+ return tud_printer_n_write_available(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_read(void *buffer, uint32_t bufsize) {
+ return tud_printer_n_read(0, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tud_printer_read_flush(void) {
+ tud_printer_n_read_flush(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_printer_peek(uint8_t *ui8) {
+ return tud_printer_n_peek(0, ui8);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_write(const void *buffer, uint32_t bufsize) {
+ return tud_printer_n_write(0, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_write_flush(void) {
+ return tud_printer_n_write_flush(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_printer_write_clear(void) {
+ return tud_printer_n_write_clear(0);
+}
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received new data
+void tud_printer_rx_cb(uint8_t itf);
+
+// Invoked when last write transfer is completed
+void tud_printer_tx_complete_cb(uint8_t itf);
+
+// 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
+//--------------------------------------------------------------------+
+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/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index b917c8dc7..e1017ba48 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -49,8 +49,7 @@ typedef struct {
#else
uint8_t ep_in;
uint8_t ep_out;
- uint16_t ep_in_mps;
- uint16_t ep_out_mps;
+ uint16_t rx_xfer_len;
#endif
} vendord_interface_t;
@@ -62,15 +61,15 @@ typedef struct {
static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
- // Skip local EP buffer if dedicated hw FIFO is supported or no fifo mode
- #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 || !CFG_TUD_VENDOR_TXRX_BUFFERED
+// Skip local EP buffer if dedicated hw FIFO is supported or no fifo mode
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 || !CFG_TUD_VENDOR_TXRX_BUFFERED
typedef struct {
- TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_EPSIZE);
- TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_EPSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_RX_EPSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_TX_EPSIZE);
} vendord_epbuf_t;
CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR];
- #endif
+#endif
//--------------------------------------------------------------------+
// Weak stubs: invoked if no strong implementation is available
@@ -86,9 +85,6 @@ TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes) {
(void) sent_bytes;
}
-//--------------------------------------------------------------------
-// Application API
-//--------------------------------------------------------------------
bool tud_vendor_n_mounted(uint8_t idx) {
TU_VERIFY(idx < CFG_TUD_VENDOR);
vendord_interface_t *p_itf = &_vendord_itf[idx];
@@ -128,9 +124,9 @@ void tud_vendor_n_read_flush(uint8_t idx) {
tu_edpt_stream_clear(&p_itf->rx_stream);
tu_edpt_stream_read_xfer(&p_itf->rx_stream);
}
-#endif
+ #endif
-#if CFG_TUD_VENDOR_RX_MANUAL_XFER
+ #if CFG_TUD_VENDOR_RX_MANUAL_XFER
bool tud_vendor_n_read_xfer(uint8_t idx) {
TU_VERIFY(idx < CFG_TUD_VENDOR);
vendord_interface_t *p_itf = &_vendord_itf[idx];
@@ -141,7 +137,7 @@ bool tud_vendor_n_read_xfer(uint8_t idx) {
#else
// Non-FIFO mode
TU_VERIFY(usbd_edpt_claim(p_itf->rhport, p_itf->ep_out));
- return usbd_edpt_xfer(p_itf->rhport, p_itf->ep_out, _vendord_epbuf[idx].epout, CFG_TUD_VENDOR_EPSIZE, false);
+ return usbd_edpt_xfer(p_itf->rhport, p_itf->ep_out, _vendord_epbuf[idx].epout, p_itf->rx_xfer_len, false);
#endif
}
#endif
@@ -160,7 +156,7 @@ uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize) {
#else
// non-fifo mode: direct transfer
TU_VERIFY(usbd_edpt_claim(p_itf->rhport, p_itf->ep_in), 0);
- const uint32_t xact_len = tu_min32(bufsize, CFG_TUD_VENDOR_EPSIZE);
+ const uint32_t xact_len = tu_min32(bufsize, CFG_TUD_VENDOR_TX_EPSIZE);
memcpy(_vendord_epbuf[idx].epin, buffer, xact_len);
TU_ASSERT(usbd_edpt_xfer(p_itf->rhport, p_itf->ep_in, _vendord_epbuf[idx].epin, (uint16_t)xact_len, false), 0);
return xact_len;
@@ -177,7 +173,7 @@ uint32_t tud_vendor_n_write_available(uint8_t idx) {
#else
// Non-FIFO mode
TU_VERIFY(p_itf->ep_in > 0, 0); // must be opened
- return usbd_edpt_busy(p_itf->rhport, p_itf->ep_in) ? 0 : CFG_TUD_VENDOR_EPSIZE;
+ return usbd_edpt_busy(p_itf->rhport, p_itf->ep_in) ? 0 : CFG_TUD_VENDOR_TX_EPSIZE;
#endif
}
@@ -215,12 +211,10 @@ void vendord_init(void) {
#endif
uint8_t *rx_ff_buf = p_itf->rx_ff_buf;
- tu_edpt_stream_init(&p_itf->rx_stream, false, false, false, rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, epout_buf,
- CFG_TUD_VENDOR_EPSIZE);
+ tu_edpt_stream_init(&p_itf->rx_stream, false, false, false, rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, epout_buf);
uint8_t *tx_ff_buf = p_itf->tx_ff_buf;
- tu_edpt_stream_init(&p_itf->tx_stream, false, true, false, tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, epin_buf,
- CFG_TUD_VENDOR_EPSIZE);
+ tu_edpt_stream_init(&p_itf->tx_stream, false, true, false, tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, epin_buf);
}
#endif
}
@@ -302,30 +296,31 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
+ uint16_t rx_xfer_len = CFG_TUD_VENDOR_RX_NEED_ZLP ? CFG_TUD_VENDOR_RX_EPSIZE : tu_edpt_packet_size(desc_ep);
+
#if CFG_TUD_VENDOR_TXRX_BUFFERED
// open endpoint stream
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
tu_edpt_stream_t *tx_stream = &p_vendor->tx_stream;
- tu_edpt_stream_open(tx_stream, rhport, desc_ep);
+ tu_edpt_stream_open(tx_stream, rhport, desc_ep, CFG_TUD_VENDOR_TX_EPSIZE);
tu_edpt_stream_write_xfer(tx_stream); // flush pending data
} else {
tu_edpt_stream_t *rx_stream = &p_vendor->rx_stream;
- tu_edpt_stream_open(rx_stream, rhport, desc_ep);
+ tu_edpt_stream_open(rx_stream, rhport, desc_ep, rx_xfer_len);
#if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
TU_ASSERT(tu_edpt_stream_read_xfer(rx_stream) > 0, 0); // prepare for incoming data
#endif
}
#else
+ p_vendor->rx_xfer_len = rx_xfer_len;
// Non-FIFO mode: store endpoint info
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
p_vendor->ep_in = desc_ep->bEndpointAddress;
- p_vendor->ep_in_mps = tu_edpt_packet_size(desc_ep);
} else {
p_vendor->ep_out = desc_ep->bEndpointAddress;
- p_vendor->ep_out_mps = tu_edpt_packet_size(desc_ep);
#if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
// Prepare for incoming data
- TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, CFG_TUD_VENDOR_EPSIZE, false), 0);
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, rx_xfer_len, false), 0);
#endif
}
#endif
@@ -367,7 +362,7 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
// Non-FIFO mode: invoke callback with buffer
tud_vendor_rx_cb(idx, _vendord_epbuf[idx].epout, xferred_bytes);
#if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
- usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, CFG_TUD_VENDOR_EPSIZE, false);
+ usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, p_vendor->rx_xfer_len, false);
#endif
} else if (ep_addr == p_vendor->ep_in) {
// Send complete
diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h
index 101765bb1..cdca9fd15 100644
--- a/src/class/vendor/vendor_device.h
+++ b/src/class/vendor/vendor_device.h
@@ -36,18 +36,30 @@ extern "C" {
//--------------------------------------------------------------------+
// Configuration
//--------------------------------------------------------------------+
-#ifndef CFG_TUD_VENDOR_EPSIZE
- #define CFG_TUD_VENDOR_EPSIZE 64
+#ifndef CFG_TUD_VENDOR_RX_EPSIZE
+ #ifdef CFG_TUD_VENDOR_EPSIZE
+ #define CFG_TUD_VENDOR_RX_EPSIZE CFG_TUD_VENDOR_EPSIZE
+ #else
+ #define CFG_TUD_VENDOR_RX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
+#endif
+
+#ifndef CFG_TUD_VENDOR_TX_EPSIZE
+ #ifdef CFG_TUD_VENDOR_EPSIZE
+ #define CFG_TUD_VENDOR_TX_EPSIZE CFG_TUD_VENDOR_EPSIZE
+ #else
+ #define CFG_TUD_VENDOR_TX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
#endif
// RX FIFO can be disabled by setting this value to 0
#ifndef CFG_TUD_VENDOR_RX_BUFSIZE
- #define CFG_TUD_VENDOR_RX_BUFSIZE 64
+ #define CFG_TUD_VENDOR_RX_BUFSIZE TUD_EPSIZE_BULK_MAX
#endif
// TX FIFO can be disabled by setting this value to 0
#ifndef CFG_TUD_VENDOR_TX_BUFSIZE
- #define CFG_TUD_VENDOR_TX_BUFSIZE 64
+ #define CFG_TUD_VENDOR_TX_BUFSIZE TUD_EPSIZE_BULK_MAX
#endif
// Vendor is buffered (FIFO mode) if both TX and RX buffers are configured
@@ -62,6 +74,11 @@ extern "C" {
#define CFG_TUD_VENDOR_RX_MANUAL_XFER 0
#endif
+// Enable multi-packet RX transfer with ZLP termination for better throughput. Requires host support for ZLP.
+#ifndef CFG_TUD_VENDOR_RX_NEED_ZLP
+ #define CFG_TUD_VENDOR_RX_NEED_ZLP 0
+#endif
+
//--------------------------------------------------------------------+
// Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1
//--------------------------------------------------------------------+
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index b2375def9..bbcfe45d5 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -70,6 +70,13 @@ typedef struct TU_ATTR_PACKED {
uint8_t bEntityId;
} tusb_desc_cs_video_entity_itf_t;
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint16_t wMaxTransferSize;
+} tusb_desc_cs_video_vc_ep_t;
+
typedef union {
struct TU_ATTR_PACKED {
uint8_t bLength;
@@ -740,6 +747,9 @@ static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self)
/* The end of the video control interface descriptor. */
void const *end = _end_of_control_descriptor(vc);
if (vc->std.bNumEndpoints != 0) {
+ /* Extend end to cover the standard endpoint and class-specific endpoint descriptors
+ * that follow wTotalLength */
+ end = (uint8_t const*)end + sizeof(tusb_desc_endpoint_t) + sizeof(tusb_desc_cs_video_vc_ep_t);
/* Find the notification endpoint descriptor. */
cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT);
TU_ASSERT(cur < end);
@@ -780,6 +790,9 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t
if (vc->std.bNumEndpoints != 0) {
/* Support for 1 endpoint only. */
TU_VERIFY(1 == vc->std.bNumEndpoints);
+ /* Extend end to cover the standard endpoint and class-specific endpoint descriptors
+ * that follow wTotalLength */
+ end = (uint8_t const*)end + sizeof(tusb_desc_endpoint_t) + sizeof(tusb_desc_cs_video_vc_ep_t);
/* Find the notification endpoint descriptor. */
cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT);
TU_VERIFY(cur < end);