Allow EP0 to use xfer sizes larger than one packet

This commit is contained in:
Jan Dümpelmann 2020-05-15 22:26:14 +02:00
parent 28696de390
commit 42edbc0006
1 changed files with 47 additions and 3 deletions

View File

@ -109,6 +109,9 @@ typedef volatile uint32_t * usb_fifo_t;
xfer_ctl_t xfer_status[EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
// EP0 transfers are limited to 1 packet - larger sizes has to be split
static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
// Setup the control endpoint 0.
static void bus_reset(void) {
@ -356,13 +359,26 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
xfer->total_len = total_bytes;
uint16_t num_packets = (total_bytes / xfer->max_size);
uint8_t short_packet_size = total_bytes % xfer->max_size;
uint8_t const short_packet_size = total_bytes % xfer->max_size;
// Zero-size packet is special case.
if(short_packet_size > 0 || (total_bytes == 0)) {
num_packets++;
}
// EP0 can only handle one packet
if(epnum == 0) {
num_packets = 1;
if(total_bytes > xfer->max_size) {
ep0_pending[dir] = total_bytes - xfer->max_size;
total_bytes = xfer->max_size;
// Decrement pointer to make EP0 buffers compatible with existing routines.
xfer->buffer -= ep0_pending[dir];
} else {
ep0_pending[dir] = 0;
}
}
// IN and OUT endpoint xfers are interrupt-driven, we just schedule them here.
if(dir == TUSB_DIR_IN) {
// A full IN transfer (multiple packets, possibly) triggers XFRC.
@ -549,6 +565,9 @@ static void handle_rxflvl_ints(USB_OTG_OUTEndpointTypeDef * out_ep) {
uint16_t remaining_bytes = ((out_ep[epnum].DOEPTSIZ & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) \
>> USB_OTG_DOEPTSIZ_XFRSIZ_Pos) + bcnt;
// Adjust remaining bytes in case of multi packet EP0 transfer
if(epnum == 0) remaining_bytes += ep0_pending[TUSB_DIR_OUT];
// Read packet off RxFIFO
read_fifo_packet((xfer->buffer + xfer->total_len - remaining_bytes), bcnt);
}
@ -592,7 +611,18 @@ static void handle_epout_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_OUTEndpointTy
// OUT XFER complete
if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_XFRC) {
out_ep[n].DOEPINT = USB_OTG_DOEPINT_XFRC;
dcd_event_xfer_complete(0, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
// EP0 can only handle one packet
if((n == 0) && ep0_pending[TUSB_DIR_OUT]){
uint16_t const total_bytes = tu_min16(ep0_pending[TUSB_DIR_OUT], xfer->max_size);
ep0_pending[TUSB_DIR_OUT] -= total_bytes;
// Schedule another packet to be received.
out_ep[0].DOEPTSIZ |= (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos) |
((total_bytes << USB_OTG_DOEPTSIZ_XFRSIZ_Pos) & USB_OTG_DOEPTSIZ_XFRSIZ_Msk);
out_ep[0].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
} else {
dcd_event_xfer_complete(0, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
}
}
}
@ -612,7 +642,18 @@ static void handle_epin_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_INEndpointType
if ( in_ep[n].DIEPINT & USB_OTG_DIEPINT_XFRC )
{
in_ep[n].DIEPINT = USB_OTG_DIEPINT_XFRC;
dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
// EP0 can only handle one packet
if((n == 0) && ep0_pending[TUSB_DIR_IN]){
uint16_t const total_bytes = tu_min16(ep0_pending[TUSB_DIR_IN], xfer->max_size);
ep0_pending[TUSB_DIR_IN] -= total_bytes;
// Schedule another packet to be received.
in_ep[0].DIEPTSIZ |= (1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos) |
((total_bytes << USB_OTG_DIEPTSIZ_XFRSIZ_Pos) & USB_OTG_DIEPTSIZ_XFRSIZ_Msk);
in_ep[0].DIEPCTL |= USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK;
} else {
dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
}
// XFER FIFO empty
@ -637,6 +678,9 @@ static void handle_epin_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_INEndpointType
break;
}
// Adjust remaining bytes in case of multi packet EP0 transfer
if(n == 0) remaining_bytes += ep0_pending[TUSB_DIR_IN];
// Push packet to Tx-FIFO
write_fifo_packet(n, (xfer->buffer + xfer->total_len - remaining_bytes), packet_size);
}