mirror of
https://github.com/hathach/tinyusb.git
synced 2025-03-31 07:20:23 +00:00
stm32h7: Implement IN and OUT receive for EP0; device descriptor returned.
This commit is contained in:
parent
f602534536
commit
0d0b802ee0
@ -219,11 +219,43 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
|||||||
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
||||||
{
|
{
|
||||||
(void) rhport;
|
(void) rhport;
|
||||||
(void) ep_addr;
|
USB_OTG_DeviceTypeDef * dev = DEVICE_BASE;
|
||||||
(void) buffer;
|
USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE;
|
||||||
(void) total_bytes;
|
USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE;
|
||||||
|
|
||||||
return false;
|
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||||
|
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||||
|
|
||||||
|
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
|
||||||
|
xfer->buffer = buffer;
|
||||||
|
xfer->total_len = total_bytes;
|
||||||
|
xfer->queued_len = 0;
|
||||||
|
xfer->short_packet = false;
|
||||||
|
|
||||||
|
uint16_t num_packets = (total_bytes / xfer->max_size);
|
||||||
|
uint8_t short_packet_size = total_bytes % xfer->max_size;
|
||||||
|
|
||||||
|
// Zero-size packet is special case.
|
||||||
|
if(short_packet_size > 0 || (total_bytes == 0)) {
|
||||||
|
num_packets++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
in_ep[epnum].DIEPTSIZ = (num_packets << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | \
|
||||||
|
((total_bytes & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) << USB_OTG_DIEPTSIZ_XFRSIZ_Pos);
|
||||||
|
in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK;
|
||||||
|
dev->DIEPEMPMSK |= (1 << epnum);
|
||||||
|
} else {
|
||||||
|
// Each complete packet for OUT xfers triggers XFRC.
|
||||||
|
out_ep[epnum].DOEPTSIZ = (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos) | \
|
||||||
|
((xfer->max_size & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) << USB_OTG_DOEPTSIZ_XFRSIZ_Pos);
|
||||||
|
out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: The logic for STALLing and disabling an endpoint is very similar
|
// TODO: The logic for STALLing and disabling an endpoint is very similar
|
||||||
@ -242,6 +274,108 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
|
|||||||
|
|
||||||
/*------------------------------------------------------------------*/
|
/*------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
// TODO: Split into "receive on endpoint 0" and "receive generic"; endpoint 0's
|
||||||
|
// DOEPTSIZ register is smaller than the others, and so is insufficient for
|
||||||
|
// determining how much of an OUT transfer is actually remaining.
|
||||||
|
static void receive_packet(xfer_ctl_t * xfer, /* USB_OTG_OUTEndpointTypeDef * out_ep, */ uint16_t xfer_size) {
|
||||||
|
uint32_t * rx_fifo = FIFO_BASE(0);
|
||||||
|
|
||||||
|
// See above TODO
|
||||||
|
// uint16_t remaining = (out_ep->DOEPTSIZ & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DOEPTSIZ_XFRSIZ_Pos;
|
||||||
|
// xfer->queued_len = xfer->total_len - remaining;
|
||||||
|
|
||||||
|
uint16_t remaining = xfer->total_len - xfer->queued_len;
|
||||||
|
uint16_t to_recv_size;
|
||||||
|
|
||||||
|
if(remaining <= xfer->max_size) {
|
||||||
|
// Avoid buffer overflow.
|
||||||
|
to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
|
||||||
|
} else {
|
||||||
|
// Room for full packet, choose recv_size based on what the microcontroller
|
||||||
|
// claims.
|
||||||
|
to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t to_recv_rem = to_recv_size % 4;
|
||||||
|
uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
|
||||||
|
|
||||||
|
// Do not assume xfer buffer is aligned.
|
||||||
|
uint8_t * base = (xfer->buffer + xfer->queued_len);
|
||||||
|
|
||||||
|
// This for loop always runs at least once- skip if less than 4 bytes
|
||||||
|
// to collect.
|
||||||
|
if(to_recv_size >= 4) {
|
||||||
|
for(uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
|
||||||
|
uint32_t tmp = (* rx_fifo);
|
||||||
|
base[i] = tmp & 0x000000FF;
|
||||||
|
base[i + 1] = (tmp & 0x0000FF00) >> 8;
|
||||||
|
base[i + 2] = (tmp & 0x00FF0000) >> 16;
|
||||||
|
base[i + 3] = (tmp & 0xFF000000) >> 24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not read invalid bytes from RX FIFO.
|
||||||
|
if(to_recv_rem != 0) {
|
||||||
|
uint32_t tmp = (* rx_fifo);
|
||||||
|
uint8_t * last_32b_bound = base + to_recv_size_aligned;
|
||||||
|
|
||||||
|
last_32b_bound[0] = tmp & 0x000000FF;
|
||||||
|
if(to_recv_rem > 1) {
|
||||||
|
last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
|
||||||
|
}
|
||||||
|
if(to_recv_rem > 2) {
|
||||||
|
last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xfer->queued_len += xfer_size;
|
||||||
|
|
||||||
|
// Per USB spec, a short OUT packet (including length 0) is always
|
||||||
|
// indicative of the end of a transfer (at least for ctl, bulk, int).
|
||||||
|
xfer->short_packet = (xfer_size < xfer->max_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void transmit_packet(xfer_ctl_t * xfer, USB_OTG_INEndpointTypeDef * in_ep, uint8_t fifo_num) {
|
||||||
|
uint32_t * tx_fifo = FIFO_BASE(fifo_num);
|
||||||
|
|
||||||
|
uint16_t remaining = (in_ep->DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos;
|
||||||
|
xfer->queued_len = xfer->total_len - remaining;
|
||||||
|
|
||||||
|
uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining;
|
||||||
|
uint8_t to_xfer_rem = to_xfer_size % 4;
|
||||||
|
uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
|
||||||
|
|
||||||
|
// Buffer might not be aligned to 32b, so we need to force alignment
|
||||||
|
// by copying to a temp var.
|
||||||
|
uint8_t * base = (xfer->buffer + xfer->queued_len);
|
||||||
|
|
||||||
|
// This for loop always runs at least once- skip if less than 4 bytes
|
||||||
|
// to send off.
|
||||||
|
if(to_xfer_size >= 4) {
|
||||||
|
for(uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
|
||||||
|
uint32_t tmp = base[i] | (base[i + 1] << 8) | \
|
||||||
|
(base[i + 2] << 16) | (base[i + 3] << 24);
|
||||||
|
(* tx_fifo) = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not read beyond end of buffer if not divisible by 4.
|
||||||
|
if(to_xfer_rem != 0) {
|
||||||
|
uint32_t tmp = 0;
|
||||||
|
uint8_t * last_32b_bound = base + to_xfer_size_aligned;
|
||||||
|
|
||||||
|
tmp |= last_32b_bound[0];
|
||||||
|
if(to_xfer_rem > 1) {
|
||||||
|
tmp |= (last_32b_bound[1] << 8);
|
||||||
|
}
|
||||||
|
if(to_xfer_rem > 2) {
|
||||||
|
tmp |= (last_32b_bound[2] << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
(* tx_fifo) = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void read_rx_fifo(USB_OTG_OUTEndpointTypeDef * out_ep) {
|
static void read_rx_fifo(USB_OTG_OUTEndpointTypeDef * out_ep) {
|
||||||
uint32_t * rx_fifo = FIFO_BASE(0);
|
uint32_t * rx_fifo = FIFO_BASE(0);
|
||||||
|
|
||||||
@ -252,12 +386,14 @@ static void read_rx_fifo(USB_OTG_OUTEndpointTypeDef * out_ep) {
|
|||||||
uint8_t epnum = (ctl_word & USB_OTG_GRXSTSP_EPNUM_Msk) >> USB_OTG_GRXSTSP_EPNUM_Pos;
|
uint8_t epnum = (ctl_word & USB_OTG_GRXSTSP_EPNUM_Msk) >> USB_OTG_GRXSTSP_EPNUM_Pos;
|
||||||
uint16_t bcnt = (ctl_word & USB_OTG_GRXSTSP_BCNT_Msk) >> USB_OTG_GRXSTSP_BCNT_Pos;
|
uint16_t bcnt = (ctl_word & USB_OTG_GRXSTSP_BCNT_Msk) >> USB_OTG_GRXSTSP_BCNT_Pos;
|
||||||
|
|
||||||
(void) bcnt;
|
|
||||||
|
|
||||||
switch(pktsts) {
|
switch(pktsts) {
|
||||||
case 0x01: // Global OUT NAK (Interrupt)
|
case 0x01: // Global OUT NAK (Interrupt)
|
||||||
break;
|
break;
|
||||||
case 0x02: // Out packet recvd
|
case 0x02: // Out packet recvd
|
||||||
|
{
|
||||||
|
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
|
||||||
|
receive_packet(xfer, bcnt);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 0x03: // Out packet done (Interrupt)
|
case 0x03: // Out packet done (Interrupt)
|
||||||
break;
|
break;
|
||||||
@ -274,7 +410,7 @@ static void read_rx_fifo(USB_OTG_OUTEndpointTypeDef * out_ep) {
|
|||||||
_setup_packet[5 - 2*setup_left] = (* rx_fifo);
|
_setup_packet[5 - 2*setup_left] = (* rx_fifo);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: // Invalid, do something here, like breakpoint?
|
default: // Invalid
|
||||||
TU_BREAKPOINT();
|
TU_BREAKPOINT();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -283,7 +419,8 @@ static void read_rx_fifo(USB_OTG_OUTEndpointTypeDef * out_ep) {
|
|||||||
static void handle_epout_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_OUTEndpointTypeDef * out_ep) {
|
static void handle_epout_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_OUTEndpointTypeDef * out_ep) {
|
||||||
// DAINT for a given EP clears when DOEPINTx is cleared.
|
// DAINT for a given EP clears when DOEPINTx is cleared.
|
||||||
// OEPINT will be cleared when DAINT's out bits are cleared.
|
// OEPINT will be cleared when DAINT's out bits are cleared.
|
||||||
for(int n = 0; n < 4; n++) {
|
for(int n = 0; n < 8; n++) {
|
||||||
|
xfer_ctl_t * xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
|
||||||
if(dev->DAINT & (1 << (USB_OTG_DAINT_OEPINT_Pos + n))) {
|
if(dev->DAINT & (1 << (USB_OTG_DAINT_OEPINT_Pos + n))) {
|
||||||
// SETUP packet Setup Phase done.
|
// SETUP packet Setup Phase done.
|
||||||
if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_STUP) {
|
if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_STUP) {
|
||||||
@ -292,6 +429,50 @@ static void handle_epout_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_OUTEndpointTy
|
|||||||
_setup_offs = 0;
|
_setup_offs = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OUT XFER complete (single packet).
|
||||||
|
if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_XFRC) {
|
||||||
|
out_ep[n].DOEPINT = USB_OTG_DOEPINT_XFRC;
|
||||||
|
|
||||||
|
// TODO: Because of endpoint 0's constrained size, we handle XFRC
|
||||||
|
// on a packet-basis. The core can internally handle multiple OUT
|
||||||
|
// packets; it would be more efficient to only trigger XFRC on a
|
||||||
|
// completed transfer for non-0 endpoints.
|
||||||
|
|
||||||
|
// Transfer complete if short packet or total len is transferred
|
||||||
|
if(xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
|
||||||
|
xfer->short_packet = false;
|
||||||
|
dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true);
|
||||||
|
} else {
|
||||||
|
// Schedule another packet to be received.
|
||||||
|
out_ep[n].DOEPTSIZ = (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos) | \
|
||||||
|
((xfer->max_size & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) << USB_OTG_DOEPTSIZ_XFRSIZ_Pos);
|
||||||
|
out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_epin_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_INEndpointTypeDef * in_ep) {
|
||||||
|
// DAINT for a given EP clears when DIEPINTx is cleared.
|
||||||
|
// IEPINT will be cleared when DAINT's out bits are cleared.
|
||||||
|
for(uint8_t n = 0; n < 8; n++) {
|
||||||
|
xfer_ctl_t * xfer = XFER_CTL_BASE(n, TUSB_DIR_IN);
|
||||||
|
|
||||||
|
if(dev->DAINT & (1 << (USB_OTG_DAINT_IEPINT_Pos + n))) {
|
||||||
|
// IN XFER complete (entire xfer).
|
||||||
|
if(in_ep[n].DIEPINT & USB_OTG_DIEPINT_XFRC) {
|
||||||
|
in_ep[n].DIEPINT = USB_OTG_DIEPINT_XFRC;
|
||||||
|
dev->DIEPEMPMSK &= ~(1 << n); // Turn off TXFE b/c xfer inactive.
|
||||||
|
dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XFER FIFO empty
|
||||||
|
if(in_ep[n].DIEPINT & USB_OTG_DIEPINT_TXFE) {
|
||||||
|
in_ep[n].DIEPINT = USB_OTG_DIEPINT_TXFE;
|
||||||
|
transmit_packet(xfer, &in_ep[n], n);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,6 +480,7 @@ void OTG_FS_IRQHandler (void)
|
|||||||
{
|
{
|
||||||
USB_OTG_DeviceTypeDef * dev = DEVICE_BASE;
|
USB_OTG_DeviceTypeDef * dev = DEVICE_BASE;
|
||||||
USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE;
|
USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE;
|
||||||
|
USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE;
|
||||||
|
|
||||||
uint32_t int_status = USB_OTG_FS->GINTSTS;
|
uint32_t int_status = USB_OTG_FS->GINTSTS;
|
||||||
|
|
||||||
@ -315,6 +497,11 @@ void OTG_FS_IRQHandler (void)
|
|||||||
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
|
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(int_status & USB_OTG_GINTSTS_SOF) {
|
||||||
|
USB_OTG_FS->GINTSTS = USB_OTG_GINTSTS_SOF;
|
||||||
|
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
|
||||||
|
}
|
||||||
|
|
||||||
if(int_status & USB_OTG_GINTSTS_RXFLVL) {
|
if(int_status & USB_OTG_GINTSTS_RXFLVL) {
|
||||||
read_rx_fifo(out_ep);
|
read_rx_fifo(out_ep);
|
||||||
}
|
}
|
||||||
@ -323,6 +510,11 @@ void OTG_FS_IRQHandler (void)
|
|||||||
if(int_status & USB_OTG_GINTSTS_OEPINT) {
|
if(int_status & USB_OTG_GINTSTS_OEPINT) {
|
||||||
handle_epout_ints(dev, out_ep);
|
handle_epout_ints(dev, out_ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IN endpoint interrupt handling.
|
||||||
|
if(int_status & USB_OTG_GINTSTS_IEPINT) {
|
||||||
|
handle_epin_ints(dev, in_ep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user