added usbd_edpt_xfer/usbd_edpt_busy to replace dcd_edpt_transfer/dcd_edpt_busy()

- improve fifo write/read_n with only one lock
- use usbd_edpt_xfer/usbd_edpt_busy for hid/cdc/msc class driver
- replace cdc read's pending_read_from_host by usbd_edpt_busy()
This commit is contained in:
hathach 2019-05-29 16:55:15 +07:00
parent dfdae7a4d2
commit a0307bafda
8 changed files with 146 additions and 84 deletions

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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);