diff --git a/port/dwc2/usb_hc_dwc2.c b/port/dwc2/usb_hc_dwc2.c index 3678b405..437d8c34 100644 --- a/port/dwc2/usb_hc_dwc2.c +++ b/port/dwc2/usb_hc_dwc2.c @@ -42,6 +42,11 @@ struct dwc2_chan { uint8_t chidx; bool inuse; bool dir_in; + bool do_ssplit; + bool do_csplit; + uint8_t hub_addr; + uint8_t hub_port; + uint16_t ssplit_frame; usb_osal_sem_t waitsem; struct usbh_urb *urb; uint32_t iso_frame_idx; @@ -216,12 +221,6 @@ static inline void dwc2_chan_char_init(struct usbh_bus *bus, uint8_t ch_num, uin regval |= USB_OTG_HCCHAR_EPDIR; } - if ((usbh_get_port_speed(bus, 0) == USB_SPEED_HIGH) && (speed != USB_SPEED_HIGH)) { - USB_LOG_ERR("Do not support LS/FS device on HS hub\r\n"); - while (1) { - } - } - /* LS device plugged to HUB */ if ((speed == USB_SPEED_LOW) && (usbh_get_port_speed(bus, 0) != USB_SPEED_LOW)) { regval |= USB_OTG_HCCHAR_LSDEV; @@ -234,6 +233,30 @@ static inline void dwc2_chan_char_init(struct usbh_bus *bus, uint8_t ch_num, uin USB_OTG_HC((uint32_t)ch_num)->HCCHAR = regval; } +static inline void dwc2_chan_splt_init(struct usbh_bus *bus, uint8_t ch_num) +{ + struct dwc2_chan *chan; + uint32_t hcsplt; + + chan = &g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[ch_num]; + + if (chan->do_ssplit) { + hcsplt = USB_OTG_HCSPLT_SPLITEN; + hcsplt |= (chan->hub_addr << USB_OTG_HCSPLT_HUBADDR_Pos); + hcsplt |= chan->hub_port; + + if (chan->do_csplit) { + hcsplt |= USB_OTG_HCSPLT_COMPLSPLT; + } else { + hcsplt &= ~USB_OTG_HCSPLT_COMPLSPLT; + } + + USB_OTG_HC((uint32_t)ch_num)->HCSPLT = hcsplt; + } else { + USB_OTG_HC((uint32_t)ch_num)->HCSPLT = 0U; + } +} + static void dwc2_chan_init(struct usbh_bus *bus, uint8_t ch_num, uint8_t devaddr, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_mps, uint8_t speed) { uint32_t regval; @@ -242,18 +265,13 @@ static void dwc2_chan_init(struct usbh_bus *bus, uint8_t ch_num, uint8_t devaddr USB_OTG_HC((uint32_t)ch_num)->HCINT = 0xFFFFFFFFU; /* Enable channel interrupts required for this transfer. */ - regval = USB_OTG_HCINTMSK_CHHM; - - if (ep_type == USB_ENDPOINT_TYPE_INTERRUPT) { - regval |= USB_OTG_HCINTMSK_NAKM; - } - - USB_OTG_HC((uint32_t)ch_num)->HCINTMSK = regval; + USB_OTG_HC((uint32_t)ch_num)->HCINTMSK = USB_OTG_HCINTMSK_CHHM; /* Enable the top level host channel interrupt. */ USB_OTG_HOST->HAINTMSK |= 1UL << (ch_num & 0xFU); dwc2_chan_char_init(bus, ch_num, devaddr, ep_addr, ep_type, ep_mps, speed); + dwc2_chan_splt_init(bus, ch_num); } /* For IN channel HCTSIZ.XferSize is expected to be an integer multiple of ep_mps size.*/ @@ -352,6 +370,14 @@ static inline uint32_t dwc2_get_glb_intstatus(struct usbh_bus *bus) return tmpreg; } +static inline uint16_t dwc2_get_full_frame_num(struct usbh_bus *bus) +{ + uint16_t frame = usbh_get_frame_number(bus); + + /* USB_OTG_HFNUM_FRNUM_Msk is 0xFFFF��but max frame num is 0x3FFF */ + return ((frame & 0x3FFF) >> 3); +} + static int dwc2_chan_alloc(struct usbh_bus *bus) { size_t flags; @@ -362,6 +388,9 @@ static int dwc2_chan_alloc(struct usbh_bus *bus) if (!g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].inuse) { g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].inuse = true; usb_osal_leave_critical_section(flags); + + g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].do_ssplit = 0; + g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].do_csplit = 0; return chidx; } } @@ -404,9 +433,29 @@ static uint16_t dwc2_calculate_packet_num(uint32_t input_size, uint8_t ep_addr, static void dwc2_control_urb_init(struct usbh_bus *bus, uint8_t chidx, struct usbh_urb *urb, struct usb_setup_packet *setup, uint8_t *buffer, uint32_t buflen) { struct dwc2_chan *chan; + uint32_t datalen; + uint8_t data_pid; chan = &g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx]; + /* split buflen with ep mps */ + if (chan->do_ssplit && (chan->ep0_state == DWC2_EP0_STATE_INDATA || chan->ep0_state == DWC2_EP0_STATE_OUTDATA)) { + if (buflen > USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize)) { + datalen = USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize); + } else { + datalen = buflen; + } + + if (urb->data_toggle == 0) { + data_pid = HC_PID_DATA0; + } else { + data_pid = HC_PID_DATA1; + } + } else { + datalen = buflen; // buflen = setup->wLength + data_pid = HC_PID_DATA1; + } + if (chan->ep0_state == DWC2_EP0_STATE_SETUP) /* fill setup */ { chan->num_packets = dwc2_calculate_packet_num(8, 0x00, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen); @@ -414,14 +463,14 @@ static void dwc2_control_urb_init(struct usbh_bus *bus, uint8_t chidx, struct us dwc2_chan_transfer(bus, chidx, 0x00, (uint8_t *)setup, chan->xferlen, chan->num_packets, HC_PID_SETUP); } else if (chan->ep0_state == DWC2_EP0_STATE_INDATA) /* fill in data */ { - chan->num_packets = dwc2_calculate_packet_num(setup->wLength, 0x80, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen); + chan->num_packets = dwc2_calculate_packet_num(datalen, 0x80, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen); dwc2_chan_init(bus, chidx, urb->hport->dev_addr, 0x80, USB_ENDPOINT_TYPE_CONTROL, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), urb->hport->speed); - dwc2_chan_transfer(bus, chidx, 0x80, buffer, chan->xferlen, chan->num_packets, HC_PID_DATA1); + dwc2_chan_transfer(bus, chidx, 0x80, buffer, chan->xferlen, chan->num_packets, data_pid); } else if (chan->ep0_state == DWC2_EP0_STATE_OUTDATA) /* fill out data */ { - chan->num_packets = dwc2_calculate_packet_num(setup->wLength, 0x00, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen); + chan->num_packets = dwc2_calculate_packet_num(datalen, 0x00, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen); dwc2_chan_init(bus, chidx, urb->hport->dev_addr, 0x00, USB_ENDPOINT_TYPE_CONTROL, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), urb->hport->speed); - dwc2_chan_transfer(bus, chidx, 0x00, buffer, chan->xferlen, chan->num_packets, HC_PID_DATA1); + dwc2_chan_transfer(bus, chidx, 0x00, buffer, chan->xferlen, chan->num_packets, data_pid); } else if (chan->ep0_state == DWC2_EP0_STATE_INSTATUS) /* fill in status */ { chan->num_packets = dwc2_calculate_packet_num(0, 0x80, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen); @@ -438,10 +487,21 @@ static void dwc2_control_urb_init(struct usbh_bus *bus, uint8_t chidx, struct us static void dwc2_bulk_intr_urb_init(struct usbh_bus *bus, uint8_t chidx, struct usbh_urb *urb, uint8_t *buffer, uint32_t buflen) { struct dwc2_chan *chan; + uint32_t datalen; chan = &g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx]; - chan->num_packets = dwc2_calculate_packet_num(buflen, urb->ep->bEndpointAddress, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen); + if (chan->do_ssplit) { + if (buflen > USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize)) { + datalen = USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize); + } else { + datalen = buflen; + } + } else { + datalen = buflen; + } + + chan->num_packets = dwc2_calculate_packet_num(datalen, urb->ep->bEndpointAddress, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen); dwc2_chan_init(bus, chidx, urb->hport->dev_addr, urb->ep->bEndpointAddress, USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes), USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), urb->hport->speed); dwc2_chan_transfer(bus, chidx, urb->ep->bEndpointAddress, buffer, chan->xferlen, chan->num_packets, urb->data_toggle == 0 ? HC_PID_DATA0 : HC_PID_DATA1); } @@ -802,6 +862,15 @@ int usbh_submit_urb(struct usbh_urb *urb) chan = &g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx]; chan->chidx = chidx; chan->urb = urb; + chan->do_ssplit = 0; + + if (urb->hport->speed != USB_SPEED_HIGH && + usbh_get_port_speed(bus, 0) == USB_SPEED_HIGH) { + chan->do_ssplit = 1; + chan->do_csplit = 0; + chan->hub_port = urb->hport->port; + chan->hub_addr = urb->hport->parent->hub_addr; + } urb->hcpriv = chan; urb->errorcode = -USB_ERR_BUSY; @@ -919,6 +988,7 @@ static void dwc2_inchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) //uint32_t has_used_packets = chan->num_packets - ((USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_PKTCNT) >> 19); /* how many packets have used */ urb->actual_length += count; + urb->transfer_buffer_length -= count; uint8_t data_toggle = ((USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_DPID) >> USB_OTG_HCTSIZ_DPID_Pos); @@ -932,17 +1002,27 @@ static void dwc2_inchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) usb_dcache_invalidate((uintptr_t)urb->transfer_buffer, USB_ALIGN_UP(count, CONFIG_USB_ALIGN_SIZE)); } + chan->do_csplit = 0; + if (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_CONTROL) { if (chan->ep0_state == DWC2_EP0_STATE_INDATA) { - chan->ep0_state = DWC2_EP0_STATE_OUTSTATUS; - dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length); + if (chan->do_ssplit && urb->transfer_buffer_length > 0) { + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length); + } else { + chan->ep0_state = DWC2_EP0_STATE_OUTSTATUS; + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length); + } } else if (chan->ep0_state == DWC2_EP0_STATE_INSTATUS) { chan->ep0_state = DWC2_EP0_STATE_SETUP; dwc2_urb_waitup(urb); } } else if (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_ISOCHRONOUS) { } else { - dwc2_urb_waitup(urb); + if (chan->do_ssplit && urb->transfer_buffer_length > 0 && (count == USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize))) { + dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length); + } else { + dwc2_urb_waitup(urb); + } } } else if (chan_intstatus & USB_OTG_HCINT_AHBERR) { urb->errorcode = -USB_ERR_IO; @@ -951,11 +1031,74 @@ static void dwc2_inchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) urb->errorcode = -USB_ERR_STALL; dwc2_urb_waitup(urb); } else if (chan_intstatus & USB_OTG_HCINT_NAK) { - urb->errorcode = -USB_ERR_NAK; - dwc2_urb_waitup(urb); + if (chan->do_ssplit) { + /* + * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and + * interrupt. Re-start the SSPLIT transfer. + */ + switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) { + case USB_ENDPOINT_TYPE_CONTROL: + chan->do_csplit = 0; + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length); + break; + case USB_ENDPOINT_TYPE_BULK: + case USB_ENDPOINT_TYPE_INTERRUPT: + chan->do_csplit = 0; + dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length); + break; + default: + break; + } + } else { + urb->errorcode = -USB_ERR_NAK; + dwc2_urb_waitup(urb); + } + } else if (chan_intstatus & USB_OTG_HCINT_ACK) { + if (chan->do_ssplit) { + /* Handle ACK on SSPLIT. ACK should not occur in CSPLIT. */ + switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) { + case USB_ENDPOINT_TYPE_CONTROL: + chan->do_csplit = 1; + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length); + break; + case USB_ENDPOINT_TYPE_BULK: + case USB_ENDPOINT_TYPE_INTERRUPT: + //printf("intr ack, len:%d\r\n", urb->actual_length); + chan->do_csplit = 1; + chan->ssplit_frame = dwc2_get_full_frame_num(bus); + dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length); + break; + default: + break; + } + } } else if (chan_intstatus & USB_OTG_HCINT_NYET) { - urb->errorcode = -USB_ERR_NAK; - dwc2_urb_waitup(urb); + if (chan->do_ssplit) { + /* + * NYET on CSPLIT + * re-do the CSPLIT immediately on non-periodic + */ + switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) { + case USB_ENDPOINT_TYPE_CONTROL: + chan->do_csplit = 1; + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length); + break; + case USB_ENDPOINT_TYPE_BULK: + case USB_ENDPOINT_TYPE_INTERRUPT: + if ((dwc2_get_full_frame_num(bus) - chan->ssplit_frame) > 4) { + chan->do_csplit = 0; + } else { + chan->do_csplit = 1; + } + dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length); + break; + default: + break; + } + } else { + urb->errorcode = -USB_ERR_NAK; + dwc2_urb_waitup(urb); + } } else if (chan_intstatus & USB_OTG_HCINT_TXERR) { urb->errorcode = -USB_ERR_IO; dwc2_urb_waitup(urb); @@ -992,7 +1135,16 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) uint32_t count = USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_XFRSIZ; /* last packet size */ uint32_t has_used_packets = chan->num_packets - ((USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_PKTCNT) >> 19); /* how many packets have used */ - urb->actual_length += (has_used_packets - 1) * USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize) + count; //the same with urb->actual_length += chan->xferlen; + uint32_t olen = (has_used_packets - 1) * USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize) + count; //the same with urb->actual_length += chan->xferlen; + urb->actual_length += olen; + + if (chan->ep0_state == DWC2_EP0_STATE_OUTDATA || urb->setup == NULL) { + if (urb->transfer_buffer_length > olen) { + urb->transfer_buffer_length -= olen; + } else { + urb->transfer_buffer_length = 0; + } + } uint8_t data_toggle = ((USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_DPID) >> USB_OTG_HCTSIZ_DPID_Pos); @@ -1002,6 +1154,8 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) urb->data_toggle = 1; } + chan->do_csplit = 0; + if (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_CONTROL) { if (chan->ep0_state == DWC2_EP0_STATE_SETUP) { if (urb->setup->wLength) { @@ -1015,15 +1169,23 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) } dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length); } else if (chan->ep0_state == DWC2_EP0_STATE_OUTDATA) { - chan->ep0_state = DWC2_EP0_STATE_INSTATUS; - dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length); + if (chan->do_ssplit && urb->transfer_buffer_length > 0) { + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length); + } else { + chan->ep0_state = DWC2_EP0_STATE_INSTATUS; + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length); + } } else if (chan->ep0_state == DWC2_EP0_STATE_OUTSTATUS) { chan->ep0_state = DWC2_EP0_STATE_SETUP; dwc2_urb_waitup(urb); } } else if (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_ISOCHRONOUS) { } else { - dwc2_urb_waitup(urb); + if (chan->do_ssplit && urb->transfer_buffer_length > 0) { + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length); + } else { + dwc2_urb_waitup(urb); + } } } else if (chan_intstatus & USB_OTG_HCINT_AHBERR) { urb->errorcode = -USB_ERR_IO; @@ -1032,11 +1194,73 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num) urb->errorcode = -USB_ERR_STALL; dwc2_urb_waitup(urb); } else if (chan_intstatus & USB_OTG_HCINT_NAK) { - urb->errorcode = -USB_ERR_NAK; - dwc2_urb_waitup(urb); + if (chan->do_ssplit) { + /* + * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and + * interrupt. Re-start the SSPLIT transfer. + */ + switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) { + case USB_ENDPOINT_TYPE_CONTROL: + chan->do_csplit = 0; + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length); + break; + case USB_ENDPOINT_TYPE_BULK: + case USB_ENDPOINT_TYPE_INTERRUPT: + chan->do_csplit = 0; + dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length); + break; + default: + break; + } + } else { + urb->errorcode = -USB_ERR_NAK; + dwc2_urb_waitup(urb); + } + } else if (chan_intstatus & USB_OTG_HCINT_ACK) { + if (chan->do_ssplit) { + /* Handle ACK on SSPLIT. ACK should not occur in CSPLIT. */ + switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) { + case USB_ENDPOINT_TYPE_CONTROL: + chan->do_csplit = 1; + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length); + break; + case USB_ENDPOINT_TYPE_BULK: + case USB_ENDPOINT_TYPE_INTERRUPT: + chan->do_csplit = 1; + chan->ssplit_frame = dwc2_get_full_frame_num(bus); + dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length); + break; + default: + break; + } + } } else if (chan_intstatus & USB_OTG_HCINT_NYET) { - urb->errorcode = -USB_ERR_NAK; - dwc2_urb_waitup(urb); + if (chan->do_ssplit) { + /* + * NYET on CSPLIT + * re-do the CSPLIT immediately on non-periodic + */ + switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) { + case USB_ENDPOINT_TYPE_CONTROL: + chan->do_csplit = 1; + dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length); + break; + case USB_ENDPOINT_TYPE_BULK: + case USB_ENDPOINT_TYPE_INTERRUPT: + if ((dwc2_get_full_frame_num(bus) - chan->ssplit_frame) > 4) { + chan->do_csplit = 0; + } else { + chan->do_csplit = 1; + } + dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length); + break; + default: + break; + } + } else { + urb->errorcode = -USB_ERR_NAK; + dwc2_urb_waitup(urb); + } } else if (chan_intstatus & USB_OTG_HCINT_TXERR) { urb->errorcode = -USB_ERR_IO; dwc2_urb_waitup(urb);