mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-18 11:02:16 +00:00
class/vendor: add interrupt/iso endpoint pairs and alt-setting support
Non-buffered per-type source/sink endpoints (bulk/int/iso) with manual RX arming across altsettings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
This commit is contained in:
597
src/class/vendor/vendor_device.c
vendored
597
src/class/vendor/vendor_device.c
vendored
@ -21,6 +21,26 @@ typedef struct {
|
||||
uint8_t rhport;
|
||||
uint8_t itf_num;
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
uint8_t ep_int_out;
|
||||
uint16_t int_rx_xfer_len;
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
uint8_t ep_int_in;
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
uint8_t ep_iso_out;
|
||||
uint16_t iso_rx_xfer_len;
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
uint8_t ep_iso_in;
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_ALT_SETTINGS // implies non-buffered: fields cleared by bus reset
|
||||
uint8_t cur_alt;
|
||||
const uint8_t* p_itf_desc; // whole interface block incl. all altsettings (static app descriptor)
|
||||
uint16_t itf_desc_len;
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_TXRX_BUFFERED
|
||||
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||
tu_edpt_stream_t tx_stream;
|
||||
@ -35,7 +55,10 @@ typedef struct {
|
||||
} vendord_interface_t;
|
||||
|
||||
#if CFG_TUD_VENDOR_TXRX_BUFFERED
|
||||
#define ITF_MEM_RESET_SIZE (offsetof(vendord_interface_t, itf_num) + TU_FIELD_SIZE(vendord_interface_t, itf_num))
|
||||
// The reset region is everything before the streams; tx_stream is the first preserved field
|
||||
// (see the struct comment), so its offset is exactly that boundary regardless of which endpoint
|
||||
// gates are enabled.
|
||||
#define ITF_MEM_RESET_SIZE offsetof(vendord_interface_t, tx_stream)
|
||||
#else
|
||||
#define ITF_MEM_RESET_SIZE sizeof(vendord_interface_t)
|
||||
#endif
|
||||
@ -52,6 +75,32 @@ typedef struct {
|
||||
CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR];
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT || CFG_TUD_VENDOR_EP_INT_IN
|
||||
typedef struct {
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
TUD_EPBUF_DEF(int_out, CFG_TUD_VENDOR_EP_INT_OUT_BUFSIZE);
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
TUD_EPBUF_DEF(int_in, CFG_TUD_VENDOR_EP_INT_IN_BUFSIZE);
|
||||
#endif
|
||||
} vendord_int_epbuf_t;
|
||||
|
||||
CFG_TUD_MEM_SECTION static vendord_int_epbuf_t _vendord_int_epbuf[CFG_TUD_VENDOR];
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT || CFG_TUD_VENDOR_EP_ISO_IN
|
||||
typedef struct {
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
TUD_EPBUF_DEF(iso_out, CFG_TUD_VENDOR_EP_ISO_OUT_BUFSIZE);
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
TUD_EPBUF_DEF(iso_in, CFG_TUD_VENDOR_EP_ISO_IN_BUFSIZE);
|
||||
#endif
|
||||
} vendord_iso_epbuf_t;
|
||||
|
||||
CFG_TUD_MEM_SECTION static vendord_iso_epbuf_t _vendord_iso_epbuf[CFG_TUD_VENDOR];
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
@ -66,15 +115,61 @@ TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes) {
|
||||
(void) sent_bytes;
|
||||
}
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
TU_ATTR_WEAK void tud_vendor_int_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize) {
|
||||
(void)idx;
|
||||
(void)buffer;
|
||||
(void)bufsize;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
TU_ATTR_WEAK void tud_vendor_int_tx_cb(uint8_t idx, uint32_t sent_bytes) {
|
||||
(void)idx;
|
||||
(void)sent_bytes;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
TU_ATTR_WEAK void tud_vendor_iso_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize) {
|
||||
(void)idx;
|
||||
(void)buffer;
|
||||
(void)bufsize;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
TU_ATTR_WEAK void tud_vendor_iso_tx_cb(uint8_t idx, uint32_t sent_bytes) {
|
||||
(void)idx;
|
||||
(void)sent_bytes;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool tud_vendor_n_mounted(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR);
|
||||
vendord_interface_t *p_itf = &_vendord_itf[idx];
|
||||
|
||||
// bulk may be absent (interrupt-only vendor interface): count the interrupt endpoints too
|
||||
#if CFG_TUD_VENDOR_TXRX_BUFFERED
|
||||
return (p_itf->rx_stream.ep_addr != 0) || (p_itf->tx_stream.ep_addr != 0);
|
||||
bool mounted = (p_itf->rx_stream.ep_addr != 0) || (p_itf->tx_stream.ep_addr != 0);
|
||||
#else
|
||||
return (p_itf->ep_out != 0) || (p_itf->ep_in != 0);
|
||||
bool mounted = (p_itf->ep_out != 0) || (p_itf->ep_in != 0);
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
mounted = mounted || (p_itf->ep_int_out != 0);
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
mounted = mounted || (p_itf->ep_int_in != 0);
|
||||
#endif
|
||||
// an altsetting may expose only isochronous endpoints; count them so apps that gate an iso
|
||||
// pump on tud_vendor_mounted() still arm it
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
mounted = mounted || (p_itf->ep_iso_out != 0);
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
mounted = mounted || (p_itf->ep_iso_in != 0);
|
||||
#endif
|
||||
return mounted;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -107,6 +202,30 @@ void tud_vendor_n_read_flush(uint8_t idx) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Shared non-buffered transfer helpers for the bulk / interrupt / isochronous endpoints, which are
|
||||
// identical apart from the endpoint, its epbuf and its buffer size. TU_ATTR_UNUSED: in buffered
|
||||
// mode with the int/iso gates off none is referenced, and clang/IAR error on an unused static.
|
||||
TU_ATTR_UNUSED static inline uint32_t vendord_ep_write(vendord_interface_t *p_itf, uint8_t ep, uint8_t *epbuf,
|
||||
uint32_t bufsize, const void *buffer, uint32_t len) {
|
||||
TU_VERIFY(ep > 0, 0); // must be opened
|
||||
TU_VERIFY(usbd_edpt_claim(p_itf->rhport, ep), 0);
|
||||
const uint32_t xact_len = tu_min32(len, bufsize);
|
||||
memcpy(epbuf, buffer, xact_len);
|
||||
TU_ASSERT(usbd_edpt_xfer(p_itf->rhport, ep, epbuf, (uint16_t) xact_len, false), 0);
|
||||
return xact_len;
|
||||
}
|
||||
|
||||
TU_ATTR_UNUSED static inline uint32_t vendord_ep_write_available(vendord_interface_t *p_itf, uint8_t ep, uint32_t bufsize) {
|
||||
TU_VERIFY(ep > 0, 0); // must be opened
|
||||
return usbd_edpt_busy(p_itf->rhport, ep) ? 0 : bufsize;
|
||||
}
|
||||
|
||||
TU_ATTR_UNUSED static inline bool vendord_ep_read_xfer(vendord_interface_t *p_itf, uint8_t ep, uint8_t *epbuf, uint16_t xfer_len) {
|
||||
TU_VERIFY(ep > 0); // must be opened
|
||||
TU_VERIFY(usbd_edpt_claim(p_itf->rhport, ep));
|
||||
return usbd_edpt_xfer(p_itf->rhport, ep, epbuf, xfer_len, false);
|
||||
}
|
||||
|
||||
#if CFG_TUD_VENDOR_RX_MANUAL_XFER
|
||||
bool tud_vendor_n_read_xfer(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR);
|
||||
@ -116,9 +235,8 @@ bool tud_vendor_n_read_xfer(uint8_t idx) {
|
||||
return tu_edpt_stream_read_xfer(&p_itf->rx_stream);
|
||||
|
||||
#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, p_itf->rx_xfer_len, false);
|
||||
// Non-FIFO mode (0 while an altsetting without a bulk OUT ep is active)
|
||||
return vendord_ep_read_xfer(p_itf, p_itf->ep_out, _vendord_epbuf[idx].epout, p_itf->rx_xfer_len);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@ -135,12 +253,8 @@ uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize) {
|
||||
return tu_edpt_stream_write(&p_itf->tx_stream, buffer, (uint16_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_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;
|
||||
// non-fifo mode: direct transfer (ep_in is 0 while an altsetting without a bulk IN ep is active)
|
||||
return vendord_ep_write(p_itf, p_itf->ep_in, _vendord_epbuf[idx].epin, CFG_TUD_VENDOR_TX_EPSIZE, buffer, bufsize);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -152,9 +266,7 @@ uint32_t tud_vendor_n_write_available(uint8_t idx) {
|
||||
return tu_edpt_stream_write_available(&p_itf->tx_stream);
|
||||
|
||||
#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_TX_EPSIZE;
|
||||
return vendord_ep_write_available(p_itf, p_itf->ep_in, CFG_TUD_VENDOR_TX_EPSIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -173,6 +285,63 @@ bool tud_vendor_n_write_clear(uint8_t idx) {
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interrupt endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
bool tud_vendor_n_int_read_xfer(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR);
|
||||
vendord_interface_t *p_itf = &_vendord_itf[idx];
|
||||
return vendord_ep_read_xfer(p_itf, p_itf->ep_int_out, _vendord_int_epbuf[idx].int_out, p_itf->int_rx_xfer_len);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
uint32_t tud_vendor_n_int_write(uint8_t idx, const void *buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t *p_itf = &_vendord_itf[idx];
|
||||
return vendord_ep_write(p_itf, p_itf->ep_int_in, _vendord_int_epbuf[idx].int_in, CFG_TUD_VENDOR_EP_INT_IN_BUFSIZE, buffer, bufsize);
|
||||
}
|
||||
|
||||
uint32_t tud_vendor_n_int_write_available(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t *p_itf = &_vendord_itf[idx];
|
||||
return vendord_ep_write_available(p_itf, p_itf->ep_int_in, CFG_TUD_VENDOR_EP_INT_IN_BUFSIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Isochronous endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
bool tud_vendor_n_iso_read_xfer(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR);
|
||||
vendord_interface_t *p_itf = &_vendord_itf[idx];
|
||||
return vendord_ep_read_xfer(p_itf, p_itf->ep_iso_out, _vendord_iso_epbuf[idx].iso_out, p_itf->iso_rx_xfer_len);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
uint32_t tud_vendor_n_iso_write(uint8_t idx, const void *buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t *p_itf = &_vendord_itf[idx];
|
||||
return vendord_ep_write(p_itf, p_itf->ep_iso_in, _vendord_iso_epbuf[idx].iso_in, CFG_TUD_VENDOR_EP_ISO_IN_BUFSIZE, buffer, bufsize);
|
||||
}
|
||||
|
||||
uint32_t tud_vendor_n_iso_write_available(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t *p_itf = &_vendord_itf[idx];
|
||||
return vendord_ep_write_available(p_itf, p_itf->ep_iso_in, CFG_TUD_VENDOR_EP_ISO_IN_BUFSIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
uint8_t tud_vendor_n_alt(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
|
||||
return _vendord_itf[idx].cur_alt;
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
@ -232,17 +401,61 @@ static uint8_t find_vendor_itf(uint8_t ep_addr) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUD_VENDOR; idx++) {
|
||||
const vendord_interface_t *p_vendor = &_vendord_itf[idx];
|
||||
if (ep_addr == 0) {
|
||||
// find unused: require both ep == 0
|
||||
#if CFG_TUD_VENDOR_TXRX_BUFFERED
|
||||
if (p_vendor->rx_stream.ep_addr == 0 && p_vendor->tx_stream.ep_addr == 0) {
|
||||
// find unused interface slot
|
||||
#if CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
// an opened interface parked in an altsetting without endpoints (the mandatory empty alt 0)
|
||||
// has all ep fields 0, so the endpoint fields cannot distinguish free from open: use p_itf_desc
|
||||
if (p_vendor->p_itf_desc == NULL) {
|
||||
return idx;
|
||||
}
|
||||
#elif CFG_TUD_VENDOR_TXRX_BUFFERED
|
||||
// A slot is free only if none of its endpoints are assigned; bulk may be absent
|
||||
// (an interrupt-only vendor interface), so check the interrupt endpoints too.
|
||||
if (p_vendor->rx_stream.ep_addr == 0 && p_vendor->tx_stream.ep_addr == 0
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
&& p_vendor->ep_int_out == 0
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
&& p_vendor->ep_int_in == 0
|
||||
#endif
|
||||
) {
|
||||
return idx;
|
||||
}
|
||||
#else
|
||||
if (p_vendor->ep_out == 0 && p_vendor->ep_in == 0) {
|
||||
// A slot is free only if none of its endpoints are assigned. Bulk may be absent (an
|
||||
// interrupt-only vendor interface), so the interrupt endpoints must be checked too.
|
||||
if (p_vendor->ep_out == 0 && p_vendor->ep_in == 0
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
&& p_vendor->ep_int_out == 0
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
&& p_vendor->ep_int_in == 0
|
||||
#endif
|
||||
) {
|
||||
return idx;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
if (ep_addr == p_vendor->ep_int_out) {
|
||||
return idx;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
if (ep_addr == p_vendor->ep_int_in) {
|
||||
return idx;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
if (ep_addr == p_vendor->ep_iso_out) {
|
||||
return idx;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
if (ep_addr == p_vendor->ep_iso_in) {
|
||||
return idx;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_TXRX_BUFFERED
|
||||
if (ep_addr == p_vendor->rx_stream.ep_addr || ep_addr == p_vendor->tx_stream.ep_addr) {
|
||||
return idx;
|
||||
@ -257,6 +470,266 @@ static uint8_t find_vendor_itf(uint8_t ep_addr) {
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
#if CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
|
||||
// Reserve an isochronous endpoint at open time. Ports with a dedicated iso allocator
|
||||
// (TUP_DCD_EDPT_ISO_ALLOC) reserve the FIFO here and (re)activate on altsetting selection;
|
||||
// ports with dcd_edpt_close instead open it once here (open == allocate + activate).
|
||||
static inline bool vendord_iso_ep_alloc(uint8_t rhport, const tusb_desc_endpoint_t* desc_ep) {
|
||||
#ifdef TUP_DCD_EDPT_ISO_ALLOC
|
||||
return usbd_edpt_iso_alloc(rhport, desc_ep->bEndpointAddress, tu_edpt_packet_size(desc_ep));
|
||||
#else
|
||||
return usbd_edpt_open(rhport, desc_ep);
|
||||
#endif
|
||||
}
|
||||
|
||||
// (Re)activate an isochronous endpoint on altsetting selection.
|
||||
static inline bool vendord_iso_ep_activate(uint8_t rhport, const tusb_desc_endpoint_t* desc_ep) {
|
||||
#ifdef TUP_DCD_EDPT_ISO_ALLOC
|
||||
return usbd_edpt_iso_activate(rhport, desc_ep); // resets ep_status, aborting any stale transfer
|
||||
#else
|
||||
// No iso alloc/activate API: close (which zeros ep_status and frees a stale claim left by a
|
||||
// prior selection) then re-open, so a re-selected altsetting starts from a clean state.
|
||||
usbd_edpt_close(rhport, desc_ep->bEndpointAddress);
|
||||
return usbd_edpt_open(rhport, desc_ep);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Abort any in-flight transfer on a tracked endpoint and release its usbd claim; no-op for an
|
||||
// unset (0) address. stall disables the endpoint in the dcd, clear-stall resets it to DATA0.
|
||||
static inline void vendord_abort_ep(uint8_t rhport, uint8_t ep_addr) {
|
||||
if (ep_addr) {
|
||||
usbd_edpt_stall(rhport, ep_addr);
|
||||
usbd_edpt_clear_stall(rhport, ep_addr);
|
||||
}
|
||||
}
|
||||
|
||||
// Select an altsetting. Endpoints were hardware-opened once at vendord_open (dcds like
|
||||
// dwc2 allocate FIFO linearly and cannot close/re-open endpoints dynamically): switching
|
||||
// only re-targets the API to the selected altsetting's endpoints. Bulk/interrupt
|
||||
// endpoints get a stall/clear-stall cycle, which portably aborts any in-flight transfer
|
||||
// (stall disables the endpoint in the dcd) and resets the data toggle to DATA0 as
|
||||
// SET_INTERFACE requires. Isochronous endpoints are (re)activated, which does the same.
|
||||
// Single pass: the current altsetting's endpoints are dropped only once the target altsetting
|
||||
// is confirmed present, so a SET_INTERFACE to an unknown alt leaves the interface intact.
|
||||
static bool vendord_set_alt(uint8_t rhport, uint8_t idx, uint8_t alt) {
|
||||
vendord_interface_t *p_vendor = &_vendord_itf[idx];
|
||||
const uint8_t* p_desc = p_vendor->p_itf_desc;
|
||||
const uint8_t* desc_end = p_desc + p_vendor->itf_desc_len;
|
||||
bool in_target_alt = false;
|
||||
bool alt_found = false;
|
||||
|
||||
while (tu_desc_in_bounds(p_desc, desc_end)) {
|
||||
const uint8_t desc_type = tu_desc_type(p_desc);
|
||||
if (desc_type == TUSB_DESC_INTERFACE) {
|
||||
in_target_alt = (((const tusb_desc_interface_t*)p_desc)->bAlternateSetting == alt);
|
||||
if (in_target_alt && !alt_found) {
|
||||
alt_found = true;
|
||||
// target altsetting confirmed present: abort then drop the previous altsetting's endpoints,
|
||||
// so a bulk/interrupt endpoint absent from the target altsetting can't stay armed and keep
|
||||
// its usbd claim in the dcd. (Endpoints the target altsetting reuses are reset again below;
|
||||
// a double reset is harmless. Iso endpoints are re-activated on reselection.)
|
||||
vendord_abort_ep(rhport, p_vendor->ep_in);
|
||||
vendord_abort_ep(rhport, p_vendor->ep_out);
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
vendord_abort_ep(rhport, p_vendor->ep_int_out);
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
vendord_abort_ep(rhport, p_vendor->ep_int_in);
|
||||
#endif
|
||||
p_vendor->ep_in = 0;
|
||||
p_vendor->ep_out = 0;
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
p_vendor->ep_int_out = 0;
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
p_vendor->ep_int_in = 0;
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
p_vendor->ep_iso_out = 0;
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
p_vendor->ep_iso_in = 0;
|
||||
#endif
|
||||
}
|
||||
} else if (in_target_alt && desc_type == TUSB_DESC_ENDPOINT) {
|
||||
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
|
||||
const uint8_t ep_addr = desc_ep->bEndpointAddress;
|
||||
const bool is_in = tu_edpt_dir(ep_addr) == TUSB_DIR_IN;
|
||||
(void) is_in;
|
||||
|
||||
switch (desc_ep->bmAttributes.xfer) {
|
||||
case TUSB_XFER_BULK:
|
||||
// abort in-flight transfer + reset data toggle
|
||||
usbd_edpt_stall(rhport, ep_addr);
|
||||
usbd_edpt_clear_stall(rhport, ep_addr);
|
||||
if (is_in) {
|
||||
p_vendor->ep_in = ep_addr;
|
||||
} else {
|
||||
p_vendor->ep_out = ep_addr;
|
||||
p_vendor->rx_xfer_len =
|
||||
CFG_TUD_VENDOR_RX_NEED_ZLP ? CFG_TUD_VENDOR_RX_EPSIZE : tu_edpt_packet_size(desc_ep);
|
||||
#if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout,
|
||||
p_vendor->rx_xfer_len, false));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN || CFG_TUD_VENDOR_EP_INT_OUT
|
||||
case TUSB_XFER_INTERRUPT:
|
||||
// stall/clear only for the enabled direction (an endpoint of a disabled
|
||||
// direction was never opened, so must not be poked in the dcd)
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
if (is_in) {
|
||||
usbd_edpt_stall(rhport, ep_addr);
|
||||
usbd_edpt_clear_stall(rhport, ep_addr);
|
||||
p_vendor->ep_int_in = ep_addr;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
if (!is_in) {
|
||||
usbd_edpt_stall(rhport, ep_addr);
|
||||
usbd_edpt_clear_stall(rhport, ep_addr);
|
||||
p_vendor->ep_int_out = ep_addr;
|
||||
p_vendor->int_rx_xfer_len = tu_edpt_packet_size(desc_ep);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN || CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
case TUSB_XFER_ISOCHRONOUS:
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
if (is_in) {
|
||||
TU_ASSERT(vendord_iso_ep_activate(rhport, desc_ep));
|
||||
p_vendor->ep_iso_in = ep_addr;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
if (!is_in) {
|
||||
TU_ASSERT(vendord_iso_ep_activate(rhport, desc_ep));
|
||||
p_vendor->ep_iso_out = ep_addr;
|
||||
p_vendor->iso_rx_xfer_len = tu_edpt_packet_size(desc_ep);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break; // unsupported endpoint type / direction gate disabled: ignore
|
||||
}
|
||||
}
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
TU_VERIFY(alt_found); // unknown alt: endpoints were never cleared, current altsetting intact
|
||||
p_vendor->cur_alt = alt;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
|
||||
const uint8_t* desc_end = (const uint8_t*)desc_itf + max_len;
|
||||
|
||||
const uint8_t idx = find_vendor_itf(0);
|
||||
TU_ASSERT(idx < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t *p_vendor = &_vendord_itf[idx];
|
||||
p_vendor->rhport = rhport;
|
||||
p_vendor->itf_num = desc_itf->bInterfaceNumber;
|
||||
// p_itf_desc is assigned only after the parse succeeds: find_vendor_itf() treats a non-NULL
|
||||
// p_itf_desc as an occupied slot, so setting it before a mid-parse TU_ASSERT could fail would
|
||||
// leak the slot (a retry would find no free interface until the next bus reset).
|
||||
|
||||
// Consume every altsetting of this interface and hardware-open each endpoint exactly once
|
||||
// (bulk/interrupt via usbd_edpt_open, isochronous FIFO-allocated; iso activation and the
|
||||
// toggle reset happen on altsetting selection). An endpoint address that recurs in another
|
||||
// altsetting must carry an identical configuration, since it is opened only on first sight;
|
||||
// reconfiguring the same address per-alt is not supported and is rejected here.
|
||||
uint8_t seen_type[CFG_TUD_ENDPPOINT_MAX][2];
|
||||
uint16_t seen_mps[CFG_TUD_ENDPPOINT_MAX][2];
|
||||
tu_memclr(seen_type, sizeof(seen_type)); // 0 == TUSB_XFER_CONTROL, never used as a data ep here
|
||||
const uint8_t* p_desc = tu_desc_next(desc_itf);
|
||||
while (tu_desc_in_bounds(p_desc, desc_end)) {
|
||||
const uint8_t desc_type = tu_desc_type(p_desc);
|
||||
if (desc_type == TUSB_DESC_INTERFACE_ASSOCIATION) {
|
||||
break;
|
||||
}
|
||||
if (desc_type == TUSB_DESC_INTERFACE) {
|
||||
if (((const tusb_desc_interface_t*)p_desc)->bInterfaceNumber != p_vendor->itf_num) {
|
||||
break; // next interface
|
||||
}
|
||||
} else if (desc_type == TUSB_DESC_ENDPOINT) {
|
||||
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
|
||||
const uint8_t epnum = tu_edpt_number(desc_ep->bEndpointAddress);
|
||||
const uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress);
|
||||
const uint8_t xfer = desc_ep->bmAttributes.xfer;
|
||||
const uint16_t mps = tu_edpt_packet_size(desc_ep);
|
||||
TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX, 0);
|
||||
|
||||
if (seen_type[epnum][dir] != 0) {
|
||||
// reused address in a later altsetting: must be an exact match (opened only once)
|
||||
TU_ASSERT(seen_type[epnum][dir] == xfer && seen_mps[epnum][dir] == mps, 0);
|
||||
} else {
|
||||
switch (xfer) {
|
||||
case TUSB_XFER_BULK:
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
|
||||
break;
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN || CFG_TUD_VENDOR_EP_INT_OUT
|
||||
case TUSB_XFER_INTERRUPT:
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
TU_ASSERT(mps <= CFG_TUD_VENDOR_EP_INT_IN_BUFSIZE, 0);
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
TU_ASSERT(mps <= CFG_TUD_VENDOR_EP_INT_OUT_BUFSIZE, 0);
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN || CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
case TUSB_XFER_ISOCHRONOUS:
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
TU_ASSERT(mps <= CFG_TUD_VENDOR_EP_ISO_IN_BUFSIZE, 0);
|
||||
TU_ASSERT(vendord_iso_ep_alloc(rhport, desc_ep), 0);
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
TU_ASSERT(mps <= CFG_TUD_VENDOR_EP_ISO_OUT_BUFSIZE, 0);
|
||||
TU_ASSERT(vendord_iso_ep_alloc(rhport, desc_ep), 0);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break; // unsupported endpoint type / direction gate disabled: ignore
|
||||
}
|
||||
seen_type[epnum][dir] = xfer;
|
||||
seen_mps[epnum][dir] = mps;
|
||||
}
|
||||
}
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
// parse succeeded: commit the descriptor pointer (marks the slot occupied) before selecting alt 0
|
||||
p_vendor->p_itf_desc = (const uint8_t*) desc_itf;
|
||||
p_vendor->itf_desc_len = (uint16_t)((uintptr_t)p_desc - (uintptr_t)desc_itf);
|
||||
|
||||
// default altsetting active until the host selects another
|
||||
TU_ASSERT(vendord_set_alt(rhport, idx, 0), 0);
|
||||
return p_vendor->itf_desc_len;
|
||||
}
|
||||
|
||||
#else // !CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
|
||||
uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
|
||||
const uint8_t* desc_end = (const uint8_t*)desc_itf + max_len;
|
||||
@ -275,6 +748,31 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin
|
||||
break; // end of this interface
|
||||
} else if (desc_type == TUSB_DESC_ENDPOINT) {
|
||||
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT || CFG_TUD_VENDOR_EP_INT_IN
|
||||
if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
|
||||
const bool is_int_in = tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN;
|
||||
(void) is_int_in;
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
if (is_int_in) {
|
||||
TU_ASSERT(tu_edpt_packet_size(desc_ep) <= CFG_TUD_VENDOR_EP_INT_IN_BUFSIZE, 0);
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
|
||||
p_vendor->ep_int_in = desc_ep->bEndpointAddress;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
if (!is_int_in) {
|
||||
TU_ASSERT(tu_edpt_packet_size(desc_ep) <= CFG_TUD_VENDOR_EP_INT_OUT_BUFSIZE, 0);
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
|
||||
p_vendor->ep_int_out = desc_ep->bEndpointAddress;
|
||||
p_vendor->int_rx_xfer_len = tu_edpt_packet_size(desc_ep);
|
||||
}
|
||||
#endif
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
@ -313,6 +811,40 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uin
|
||||
return (uint16_t)((uintptr_t)p_desc - (uintptr_t)desc_itf);
|
||||
}
|
||||
|
||||
#endif // CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
|
||||
// Handle interface standard requests (GET/SET_INTERFACE when altsettings are enabled),
|
||||
// delegate everything else to the application callback as before.
|
||||
bool vendord_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
|
||||
#if CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
|
||||
request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) {
|
||||
const uint8_t itf_num = tu_u16_low(request->wIndex);
|
||||
uint8_t idx;
|
||||
for (idx = 0; idx < CFG_TUD_VENDOR; idx++) {
|
||||
if (_vendord_itf[idx].itf_num == itf_num && _vendord_itf[idx].p_itf_desc != NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx < CFG_TUD_VENDOR) {
|
||||
if (request->bRequest == TUSB_REQ_SET_INTERFACE) {
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(vendord_set_alt(rhport, idx, tu_u16_low(request->wValue)));
|
||||
return tud_control_status(rhport, request);
|
||||
}
|
||||
return true;
|
||||
} else if (request->bRequest == TUSB_REQ_GET_INTERFACE) {
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
return tud_control_xfer(rhport, request, &_vendord_itf[idx].cur_alt, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return tud_vendor_control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void)rhport;
|
||||
(void)result;
|
||||
@ -320,6 +852,33 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
|
||||
TU_VERIFY(idx < CFG_TUD_VENDOR);
|
||||
vendord_interface_t *p_vendor = &_vendord_itf[idx];
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
if (ep_addr == p_vendor->ep_int_out) {
|
||||
// not re-armed automatically: application calls tud_vendor_n_int_read_xfer()
|
||||
tud_vendor_int_rx_cb(idx, _vendord_int_epbuf[idx].int_out, xferred_bytes);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
if (ep_addr == p_vendor->ep_int_in) {
|
||||
tud_vendor_int_tx_cb(idx, xferred_bytes);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
if (ep_addr == p_vendor->ep_iso_out) {
|
||||
// not re-armed automatically: application calls tud_vendor_n_iso_read_xfer()
|
||||
tud_vendor_iso_rx_cb(idx, _vendord_iso_epbuf[idx].iso_out, xferred_bytes);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
if (ep_addr == p_vendor->ep_iso_in) {
|
||||
tud_vendor_iso_tx_cb(idx, xferred_bytes);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_TXRX_BUFFERED
|
||||
if (ep_addr == p_vendor->rx_stream.ep_addr) {
|
||||
// Put received data to FIFO
|
||||
|
||||
161
src/class/vendor/vendor_device.h
vendored
161
src/class/vendor/vendor_device.h
vendored
@ -60,6 +60,68 @@ extern "C" {
|
||||
#define CFG_TUD_VENDOR_RX_NEED_ZLP 0
|
||||
#endif
|
||||
|
||||
// Enable support for an optional interrupt OUT / interrupt IN endpoint in the vendor
|
||||
// interface, each direction gated separately. Interrupt endpoints are non-buffered:
|
||||
// OUT is armed manually one packet at a time with tud_vendor_n_int_read_xfer() (data
|
||||
// delivered via tud_vendor_int_rx_cb), IN is a direct transfer via tud_vendor_n_int_write().
|
||||
#ifndef CFG_TUD_VENDOR_EP_INT_OUT
|
||||
#define CFG_TUD_VENDOR_EP_INT_OUT 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_VENDOR_EP_INT_IN
|
||||
#define CFG_TUD_VENDOR_EP_INT_IN 0
|
||||
#endif
|
||||
|
||||
// Buffer sizes for interrupt endpoint transfers, must be >= the endpoint max packet size
|
||||
#ifndef CFG_TUD_VENDOR_EP_INT_OUT_BUFSIZE
|
||||
#define CFG_TUD_VENDOR_EP_INT_OUT_BUFSIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_VENDOR_EP_INT_IN_BUFSIZE
|
||||
#define CFG_TUD_VENDOR_EP_INT_IN_BUFSIZE 64
|
||||
#endif
|
||||
|
||||
// Enable support for an optional isochronous OUT / IN endpoint, each direction gated
|
||||
// separately, with the same non-buffered API shape as the interrupt pair. Isochronous
|
||||
// endpoints must not claim bandwidth in the default altsetting (USB 2.0 5.6.3): place
|
||||
// them in a non-zero altsetting and enable CFG_TUD_VENDOR_ALT_SETTINGS.
|
||||
#ifndef CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
#define CFG_TUD_VENDOR_EP_ISO_OUT 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_VENDOR_EP_ISO_IN
|
||||
#define CFG_TUD_VENDOR_EP_ISO_IN 0
|
||||
#endif
|
||||
|
||||
// Buffer sizes for isochronous endpoint transfers, must be >= the endpoint max packet size
|
||||
#ifndef CFG_TUD_VENDOR_EP_ISO_OUT_BUFSIZE
|
||||
#define CFG_TUD_VENDOR_EP_ISO_OUT_BUFSIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_VENDOR_EP_ISO_IN_BUFSIZE
|
||||
#define CFG_TUD_VENDOR_EP_ISO_IN_BUFSIZE 64
|
||||
#endif
|
||||
|
||||
// Enable alternate-setting support: the vendor interface may carry multiple altsettings,
|
||||
// each with its own endpoint set. GET_INTERFACE is answered and SET_INTERFACE performed by
|
||||
// closing the current altsetting's endpoints and opening the requested one's (isochronous
|
||||
// endpoints are FIFO-allocated at open and activated on selection). The configuration
|
||||
// descriptor must stay valid while mounted (static, the usual TinyUSB pattern).
|
||||
// Non-buffered mode only.
|
||||
#ifndef CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
#define CFG_TUD_VENDOR_ALT_SETTINGS 0
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_ALT_SETTINGS && CFG_TUD_VENDOR_TXRX_BUFFERED
|
||||
#error CFG_TUD_VENDOR_ALT_SETTINGS requires non-buffered mode (CFG_TUD_VENDOR_RX/TX_BUFSIZE = 0)
|
||||
#endif
|
||||
|
||||
// An isochronous endpoint must not claim bandwidth in the default altsetting (USB 2.0 5.6.3),
|
||||
// so it can only live in a non-zero altsetting, which requires alternate-setting support.
|
||||
#if (CFG_TUD_VENDOR_EP_ISO_OUT || CFG_TUD_VENDOR_EP_ISO_IN) && !CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
#error CFG_TUD_VENDOR_EP_ISO_OUT/IN requires CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1
|
||||
//--------------------------------------------------------------------+
|
||||
@ -107,6 +169,43 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str(uint8_t idx,
|
||||
return tud_vendor_n_write(idx, str, strlen(str));
|
||||
}
|
||||
|
||||
//------------- Interrupt endpoints -------------//
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
// Arm the interrupt OUT endpoint for one packet, return false if a transfer is still ongoing.
|
||||
// Received data is delivered via tud_vendor_int_rx_cb(); re-arm from the callback or by polling.
|
||||
bool tud_vendor_n_int_read_xfer(uint8_t idx);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
// Send on the interrupt IN endpoint (direct transfer, up to CFG_TUD_VENDOR_EP_INT_IN_BUFSIZE
|
||||
// bytes). Returns number of bytes queued, 0 if the endpoint is busy or not opened.
|
||||
uint32_t tud_vendor_n_int_write(uint8_t idx, const void *buffer, uint32_t bufsize);
|
||||
|
||||
// Return available bytes for interrupt IN write: 0 while busy, else the buffer size
|
||||
uint32_t tud_vendor_n_int_write_available(uint8_t idx);
|
||||
#endif
|
||||
|
||||
//------------- Isochronous endpoints -------------//
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
// Arm the isochronous OUT endpoint for one packet, return false if a transfer is still ongoing.
|
||||
// Received data is delivered via tud_vendor_iso_rx_cb(); re-arm from the callback or by polling.
|
||||
bool tud_vendor_n_iso_read_xfer(uint8_t idx);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
// Send on the isochronous IN endpoint (direct transfer, up to CFG_TUD_VENDOR_EP_ISO_IN_BUFSIZE
|
||||
// bytes). Returns number of bytes queued, 0 if the endpoint is busy or not opened.
|
||||
uint32_t tud_vendor_n_iso_write(uint8_t idx, const void *buffer, uint32_t bufsize);
|
||||
|
||||
// Return available bytes for isochronous IN write: 0 while busy, else the buffer size
|
||||
uint32_t tud_vendor_n_iso_write_available(uint8_t idx);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
// Return the currently selected alternate setting
|
||||
uint8_t tud_vendor_n_alt(uint8_t idx);
|
||||
#endif
|
||||
|
||||
// backward compatible
|
||||
#define tud_vendor_n_flush(idx) tud_vendor_n_write_flush(idx)
|
||||
|
||||
@ -161,6 +260,44 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) {
|
||||
return tud_vendor_n_write_available(0);
|
||||
}
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_int_read_xfer(void) {
|
||||
return tud_vendor_n_int_read_xfer(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_int_write(const void *buffer, uint32_t bufsize) {
|
||||
return tud_vendor_n_int_write(0, buffer, bufsize);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_int_write_available(void) {
|
||||
return tud_vendor_n_int_write_available(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_iso_read_xfer(void) {
|
||||
return tud_vendor_n_iso_read_xfer(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_iso_write(const void *buffer, uint32_t bufsize) {
|
||||
return tud_vendor_n_iso_write(0, buffer, bufsize);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_iso_write_available(void) {
|
||||
return tud_vendor_n_iso_write_available(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_ALT_SETTINGS
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_vendor_alt(void) {
|
||||
return tud_vendor_n_alt(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// backward compatible
|
||||
#define tud_vendor_flush() tud_vendor_write_flush()
|
||||
|
||||
@ -176,6 +313,29 @@ void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize);
|
||||
// Invoked when tx transfer is finished
|
||||
void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes);
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_OUT
|
||||
// Invoked when data is received on the interrupt OUT endpoint. The endpoint is not
|
||||
// re-armed automatically: call tud_vendor_n_int_read_xfer() to receive more.
|
||||
void tud_vendor_int_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_INT_IN
|
||||
// Invoked when an interrupt IN transfer is finished
|
||||
void tud_vendor_int_tx_cb(uint8_t idx, uint32_t sent_bytes);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_OUT
|
||||
// Invoked when data is received on the isochronous OUT endpoint. The endpoint is not
|
||||
// re-armed automatically: call tud_vendor_n_iso_read_xfer() to receive more.
|
||||
void tud_vendor_iso_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR_EP_ISO_IN
|
||||
// Invoked when an isochronous IN transfer is finished (result may be a missed frame:
|
||||
// the data was not necessarily taken by the host, re-arm regardless)
|
||||
void tud_vendor_iso_tx_cb(uint8_t idx, uint32_t sent_bytes);
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
@ -183,6 +343,7 @@ void vendord_init(void);
|
||||
bool vendord_deinit(void);
|
||||
void vendord_reset(uint8_t rhport);
|
||||
uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *idx_desc, uint16_t max_len);
|
||||
bool vendord_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in New Issue
Block a user