diff --git a/examples/device/cdc_msc_hid/src/main.c b/examples/device/cdc_msc_hid/src/main.c index 7c0fd39d5..8ebdddd80 100644 --- a/examples/device/cdc_msc_hid/src/main.c +++ b/examples/device/cdc_msc_hid/src/main.c @@ -201,7 +201,7 @@ void hid_task(void) tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0); // delay a bit before attempt to send keyboard report - board_delay(2); + board_delay(10); } } diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 1583b71fa..0a41147c9 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -73,23 +73,21 @@ typedef struct //--------------------------------------------------------------------+ CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; -// TODO will be replaced by dcd_edpt_busy() -bool pending_read_from_host; +//bool pending_read_from_host; TODO remove static void _prep_out_transaction (uint8_t itf) { cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; // skip if previous transfer not complete - // dcd_edpt_busy() doesn't work, probably transfer is complete but not properly handled by the stack -// if ( dcd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return; - if (pending_read_from_host) return; + if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return; + //if (pending_read_from_host) return; // Prepare for incoming data but only allow what we can store in the ring buffer. uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff); if ( max_read >= CFG_TUD_CDC_EPSIZE ) { - dcd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE); - pending_read_from_host = true; + usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE); +// pending_read_from_host = true; } } @@ -183,13 +181,13 @@ uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) bool tud_cdc_n_write_flush (uint8_t itf) { cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; - TU_VERIFY( !dcd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete + TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, CFG_TUD_CDC_EPSIZE); if ( count ) { TU_VERIFY( tud_cdc_n_connected(itf) ); // fifo is empty if not connected - TU_ASSERT( dcd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) ); + TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) ); } return true; @@ -298,7 +296,7 @@ bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t } // Prepare for incoming data - pending_read_from_host = false; +// pending_read_from_host = false; _prep_out_transaction(cdc_id); return true; @@ -394,7 +392,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf); // prepare for OUT transaction - pending_read_from_host = false; +// pending_read_from_host = false; _prep_out_transaction(itf); } diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c index aea16efc1..d3f59ed62 100644 --- a/src/class/hid/hid_device.c +++ b/src/class/hid/hid_device.c @@ -73,28 +73,31 @@ bool tud_hid_ready(void) { uint8_t itf = 0; uint8_t const ep_in = _hidd_itf[itf].ep_in; - return tud_ready() && (ep_in != 0) && !dcd_edpt_busy(TUD_OPT_RHPORT, ep_in); + return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(TUD_OPT_RHPORT, ep_in); } bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len) { - TU_VERIFY( tud_hid_ready() && (len <= CFG_TUD_HID_BUFSIZE) ); + TU_VERIFY( tud_hid_ready() ); uint8_t itf = 0; hidd_interface_t * p_hid = &_hidd_itf[itf]; - // If report id = 0, skip ID field if (report_id) { + len = tu_min8(len, CFG_TUD_HID_BUFSIZE-1); + p_hid->epin_buf[0] = report_id; memcpy(p_hid->epin_buf+1, report, len); len++; }else { + // If report id = 0, skip ID field + len = tu_min8(len, CFG_TUD_HID_BUFSIZE); memcpy(p_hid->epin_buf, report, len); } - return dcd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len); + return usbd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len); } bool tud_hid_boot_mode(void) @@ -180,7 +183,7 @@ bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t *p_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t); // Prepare for output endpoint - if (p_hid->ep_out) TU_ASSERT(dcd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))); + if (p_hid->ep_out) TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))); return true; } @@ -303,7 +306,7 @@ bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ if (ep_addr == p_hid->ep_out) { tud_hid_set_report_cb(0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes); - TU_ASSERT(dcd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))); + TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))); } return true; diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index 2fd3d44a1..5681e4f07 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -139,7 +139,7 @@ bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t (*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); // Prepare for Command Block Wrapper - TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); return true; } @@ -394,7 +394,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if ( (p_cbw->total_bytes > 0 ) && !tu_bit_test(p_cbw->dir, 7) ) { // queue transfer - TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) ); }else { int32_t resplen; @@ -428,7 +428,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if (p_msc->total_len) { TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect - TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) ); }else { p_msc->stage = MSC_STAGE_STATUS; @@ -543,7 +543,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t p_msc->stage = MSC_STAGE_CMD; // Send SCSI Status - TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)) ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)) ); // Invoke complete callback if defined if ( SCSI_CMD_READ_10 == p_cbw->command[0]) @@ -560,7 +560,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t } // Queue for the next CBW - TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); } } @@ -602,7 +602,7 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) } else { - TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), ); } } @@ -627,7 +627,7 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc) int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len); // Write10 callback will be called later when usb transfer complete - TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), ); + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), ); } #endif diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index e2ed51cd6..455b1ad34 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -71,6 +71,35 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si return true; } +// retrieve data from fifo +static void _tu_ff_pull(tu_fifo_t* f, void * buffer) +{ + memcpy(buffer, + f->buffer + (f->rd_idx * f->item_size), + f->item_size); + + f->rd_idx = (f->rd_idx + 1) % f->depth; + f->count--; +} + +// send data to fifo +static void _tu_ff_push(tu_fifo_t* f, void const * data) +{ + memcpy( f->buffer + (f->wr_idx * f->item_size), + data, + f->item_size); + + f->wr_idx = (f->wr_idx + 1) % f->depth; + + if (tu_fifo_full(f)) + { + f->rd_idx = f->wr_idx; // keep the full state (rd == wr && len = size) + } + else + { + f->count++; + } +} /******************************************************************************/ /*! @@ -82,23 +111,19 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si @param[in] f Pointer to the FIFO buffer to manipulate - @param[in] p_buffer + @param[in] buffer Pointer to the place holder for data read from the buffer @returns TRUE if the queue is not empty */ /******************************************************************************/ -bool tu_fifo_read(tu_fifo_t* f, void * p_buffer) +bool tu_fifo_read(tu_fifo_t* f, void * buffer) { if( tu_fifo_empty(f) ) return false; tu_fifo_lock(f); - memcpy(p_buffer, - f->buffer + (f->rd_idx * f->item_size), - f->item_size); - f->rd_idx = (f->rd_idx + 1) % f->depth; - f->count--; + _tu_ff_pull(f, buffer); tu_fifo_unlock(f); @@ -113,7 +138,7 @@ bool tu_fifo_read(tu_fifo_t* f, void * p_buffer) @param[in] f Pointer to the FIFO buffer to manipulate - @param[in] p_data + @param[in] buffer The pointer to data location @param[in] count Number of element that buffer can afford @@ -121,27 +146,28 @@ bool tu_fifo_read(tu_fifo_t* f, void * p_buffer) @returns number of items read from the FIFO */ /******************************************************************************/ -uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t count) +uint16_t tu_fifo_read_n (tu_fifo_t* f, void * buffer, uint16_t count) { if( tu_fifo_empty(f) ) return 0; + tu_fifo_lock(f); + /* Limit up to fifo's count */ if ( count > f->count ) count = f->count; - /* Could copy up to 2 portions marked as 'x' if queue is wrapped around - * case 1: ....RxxxxW....... - * case 2: xxxxxW....Rxxxxxx - */ -// uint16_t index2upper = tu_min16(count, f->count-f->rd_idx); - - uint8_t* p_buf = (uint8_t*) p_buffer; + uint8_t* buf8 = (uint8_t*) buffer; uint16_t len = 0; - while( (len < count) && tu_fifo_read(f, p_buf) ) + + while (len < count) { + _tu_ff_pull(f, buf8); + len++; - p_buf += f->item_size; + buf8 += f->item_size; } + tu_fifo_unlock(f); + return len; } @@ -182,33 +208,20 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer) @param[in] f Pointer to the FIFO buffer to manipulate - @param[in] p_data + @param[in] data The byte to add to the FIFO @returns TRUE if the data was written to the FIFO (overwrittable FIFO will always return TRUE) */ /******************************************************************************/ -bool tu_fifo_write (tu_fifo_t* f, const void * p_data) +bool tu_fifo_write (tu_fifo_t* f, const void * data) { if ( tu_fifo_full(f) && !f->overwritable ) return false; tu_fifo_lock(f); - memcpy( f->buffer + (f->wr_idx * f->item_size), - p_data, - f->item_size); - - f->wr_idx = (f->wr_idx + 1) % f->depth; - - if (tu_fifo_full(f)) - { - f->rd_idx = f->wr_idx; // keep the full state (rd == wr && len = size) - } - else - { - f->count++; - } + _tu_ff_push(f, data); tu_fifo_unlock(f); @@ -223,26 +236,35 @@ bool tu_fifo_write (tu_fifo_t* f, const void * p_data) @param[in] f Pointer to the FIFO buffer to manipulate - @param[in] p_data + @param[in] data The pointer to data to add to the FIFO @param[in] count Number of element @return Number of written elements */ /******************************************************************************/ -uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * p_data, uint16_t count) +uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * data, uint16_t count) { if ( count == 0 ) return 0; - uint8_t const* p_buf = (uint8_t const*) p_data; + tu_fifo_lock(f); + // Not overwritable limit up to full + if (!f->overwritable) count = tu_min16(count, tu_fifo_remaining(f)); + + uint8_t const* buf8 = (uint8_t const*) data; uint16_t len = 0; - while( (len < count) && tu_fifo_write(f, p_buf) ) + + while (len < count) { + _tu_ff_push(f, buf8); + len++; - p_buf += f->item_size; + buf8 += f->item_size; } + tu_fifo_unlock(f); + return len; } diff --git a/src/device/dcd.h b/src/device/dcd.h index 35164ac62..bd7088fe4 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -100,25 +100,28 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num); // Wake up host void dcd_remote_wakeup(uint8_t rhport); -/*------------------------------------------------------------------*/ -/* Endpoint API - * - open : Configure endpoint's registers - * - xfer : Submit a transfer. When complete dcd_event_xfer_complete - * must be called to notify the stack - * - busy : Check if endpoint transferring is complete (TODO remove) - * - stall : stall endpoint - * - clear_stall : clear stall, data toggle is also reset to DATA0 - *------------------------------------------------------------------*/ +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Configure endpoint's registers according to descriptor bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc); + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes); + +// Check if endpoint transferring is complete (TODO remove) bool dcd_edpt_busy (uint8_t rhport, uint8_t ep_addr); +// Stall endpoint void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr); + +// clear stall, data toggle is also reset to DATA0 void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr); /*------------------------------------------------------------------*/ /* Event Function - * Called by DCD to notify USBD + * Called by DCD to notify device stack *------------------------------------------------------------------*/ void dcd_event_handler(dcd_event_t const * event, bool in_isr); diff --git a/src/device/usbd.c b/src/device/usbd.c index 56d895388..e22de4728 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -51,8 +51,8 @@ typedef struct { uint8_t self_powered : 1; // configuration descriptor's attribute }; -// uint8_t ep_busy_mask[2]; // bit mask for busy endpoint - uint8_t ep_stall_mask[2]; // bit mask for stalled endpoint + uint8_t ep_busy_map[2]; // bit mask for busy endpoint + uint8_t ep_stall_map[2]; // bit map for stalled endpoint uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid ) @@ -287,6 +287,10 @@ void tud_task (void) { // Invoke the class callback associated with the endpoint address uint8_t const ep_addr = event.xfer_complete.ep_addr; + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + _usbd_dev.ep_busy_map[dir] = (uint8_t) tu_bit_clear(_usbd_dev.ep_busy_map[dir], epnum); if ( 0 == tu_edpt_number(ep_addr) ) { @@ -621,7 +625,7 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr) case DCD_EVENT_SUSPEND: // NOTE: When plugging/unplugging device, the D+/D- state are unstable and can accidentally meet the - // SUSPEND condition ( Idle for 3ms ). Some MCUs such as samd don't distinguish suspend vs disconnect as well. + // SUSPEND condition ( Idle for 3ms ). Some MCUs such as SAMD doesn't distinguish suspend vs disconnect as well. // We will skip handling SUSPEND/RESUME event if not currently connected if ( _usbd_dev.connected ) { @@ -643,7 +647,8 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr) break; case DCD_EVENT_XFER_COMPLETE: - // skip zero-length control status complete event, should dcd notifies us. + // skip zero-length control status complete event, should DCD notify us. + // TODO could cause issue with actual zero length data used by class such as DFU if ( (0 == tu_edpt_number(event->xfer_complete.ep_addr)) && (event->xfer_complete.len == 0) ) break; osal_queue_send(_usbd_q, event, in_isr); @@ -733,13 +738,37 @@ void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) //--------------------------------------------------------------------+ // USBD Endpoint API //--------------------------------------------------------------------+ + +bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + TU_VERIFY( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) ); + + _usbd_dev.ep_busy_map[dir] = (uint8_t) tu_bit_set(_usbd_dev.ep_busy_map[dir], epnum); + + return true; +} + +bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return tu_bit_test(_usbd_dev.ep_busy_map[dir], epnum); +} + + void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); dcd_edpt_stall(rhport, ep_addr); - _usbd_dev.ep_stall_mask[dir] = (uint8_t) tu_bit_set(_usbd_dev.ep_stall_mask[dir], epnum); + _usbd_dev.ep_stall_map[dir] = (uint8_t) tu_bit_set(_usbd_dev.ep_stall_map[dir], epnum); } void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) @@ -748,7 +777,7 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) uint8_t const dir = tu_edpt_dir(ep_addr); dcd_edpt_clear_stall(rhport, ep_addr); - _usbd_dev.ep_stall_mask[dir] = (uint8_t) tu_bit_clear(_usbd_dev.ep_stall_mask[dir], epnum); + _usbd_dev.ep_stall_map[dir] = (uint8_t) tu_bit_clear(_usbd_dev.ep_stall_map[dir], epnum); } bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) @@ -758,7 +787,7 @@ bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - return tu_bit_test(_usbd_dev.ep_stall_mask[dir], epnum); + return tu_bit_test(_usbd_dev.ep_stall_map[dir], epnum); } #endif diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h index f20532627..706857d45 100644 --- a/src/device/usbd_pvt.h +++ b/src/device/usbd_pvt.h @@ -33,11 +33,12 @@ extern "C" { #endif -//--------------------------------------------------------------------+ -// INTERNAL API for stack management -//--------------------------------------------------------------------+ bool usbd_init (void); +//--------------------------------------------------------------------+ +// USBD Endpoint API +//--------------------------------------------------------------------+ + // Carry out Data and Status stage of control transfer // - If len = 0, it is equivalent to sending status only // - If len > wLength : it will be truncated @@ -46,6 +47,12 @@ bool usbd_control_xfer(uint8_t rhport, tusb_control_request_t const * request, v // Send STATUS (zero length) packet bool usbd_control_status(uint8_t rhport, tusb_control_request_t const * request); +// Submit a usb transfer +bool usbd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes); + +// Check if endpoint transferring is complete +bool usbd_edpt_busy (uint8_t rhport, uint8_t ep_addr); + void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr); void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr); bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr);