In single mode EP_DMA_STS.PKT_SIZE on an OUT endpoint is where the controller reports the length it received. It is not a buffer size. The model took the copy length from it, so a zero in that field meant no data was copied at all.
One maximum packet is the most that can arrive, so copy at most EP_CONFIG.MAX_PKT bytes per call instead. That field cannot hold 1024, so a value of 0 there means 1024 bytes. The IN path decoded that by hand; move it into one helper and use it from both paths. Take the packet size from the combined packet when there is one, as the IN paths do. A combined packet holds more than p->iov says, so reading p->iov.size cut the transfer short. Signed-off-by: Jamin Lin <[email protected]> --- hw/usb/aspeed-udc.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/hw/usb/aspeed-udc.c b/hw/usb/aspeed-udc.c index dcced1b8fb..09f5913aef 100644 --- a/hw/usb/aspeed-udc.c +++ b/hw/usb/aspeed-udc.c @@ -416,6 +416,13 @@ static const MemoryRegionOps aspeed_udc_setup_ops = { }, }; +static uint32_t aspeed_udc_ep_get_max_packet(AspeedUDCEP *e) +{ + uint32_t mps = FIELD_EX32(e->regs[R_EP_CONFIG], EP_CONFIG, MAX_PKT); + + return mps ? mps : ASPEED_UDC_EP_MAXPKT; +} + /* * Copy len bytes from guest memory at addr into the IN packet, going through * a bounce buffer one buf-full at a time. Returns false on DMA failure. @@ -464,10 +471,10 @@ static AspeedUDCXferResult aspeed_udc_ep_xfer_in_desc(AspeedUDCState *s, { QEMUIOVector *pktiov = p->combined ? &p->combined->iov : &p->iov; AspeedUDCEP *e = &s->ep[ep]; - uint32_t mps = FIELD_EX32(e->regs[R_EP_CONFIG], EP_CONFIG, MAX_PKT); uint32_t wptr = FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, WPTR); uint32_t rptr = FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, RPTR); uint32_t desc_base = e->regs[R_EP_DMA_BUFF]; + uint32_t mps = aspeed_udc_ep_get_max_packet(e); uint32_t desc_addr; uint32_t remaining; uint32_t desc_ctrl; @@ -480,11 +487,6 @@ static AspeedUDCXferResult aspeed_udc_ep_xfer_in_desc(AspeedUDCState *s, bool done = false; bool ack = false; - if (mps == 0) { - /* a MAX_PKT field of 0 means the maximum packet size */ - mps = ASPEED_UDC_EP_MAXPKT; - } - trace_aspeed_udc_ep_data_in(ep, rptr, wptr, pktiov->size); /* walk the queued descriptors, filling the packet */ @@ -624,19 +626,21 @@ static AspeedUDCXferResult aspeed_udc_ep_xfer_in_single(AspeedUDCState *s, * OUT transfer: receive data from the host by copying its OUT packet into the * buffer the guest gadget driver set up. * - * A host packet can be bigger than one buffer, so we copy at most PKT_SIZE - * bytes per call, continuing from where the last call stopped - * (p->actual_length). The caller keeps the packet parked until it is fully - * copied. + * In single mode PKT_SIZE is where the controller reports the length it + * received, so it is not a buffer size. One maximum packet is the most that + * can arrive, so copy at most that much per call, continuing from where the + * last call stopped (p->actual_length). The caller keeps the packet parked + * until it is fully copied. */ static AspeedUDCXferResult aspeed_udc_ep_xfer_out_single(AspeedUDCState *s, int ep, USBPacket *p) { + QEMUIOVector *pktiov = p->combined ? &p->combined->iov : &p->iov; AspeedUDCEP *e = &s->ep[ep]; - uint32_t chunk = FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, PKT_SIZE); - uint32_t remaining = p->iov.size - (uint32_t)p->actual_length; + uint32_t remaining = pktiov->size > (uint32_t)p->actual_length ? + pktiov->size - (uint32_t)p->actual_length : 0; + uint32_t len = MIN(remaining, aspeed_udc_ep_get_max_packet(e)); uint32_t data_buf_addr = e->regs[R_EP_DMA_BUFF]; - uint32_t len = MIN(remaining, chunk); g_autofree uint8_t *buf = g_malloc(len); if (data_buf_addr && len) { @@ -659,7 +663,7 @@ static AspeedUDCXferResult aspeed_udc_ep_xfer_out_single(AspeedUDCState *s, PROC_STS, EP_DMA_CTRL_STS_RX_IDLE); aspeed_udc_raise_ep_ack(s, ep); - if ((uint32_t)p->actual_length >= p->iov.size) { + if ((uint32_t)p->actual_length >= pktiov->size) { return ASPEED_UDC_XFER_DONE; } -- 2.53.0
