📄 goku_udc.c
字号:
prefetchw(buf); if (unlikely(ep->num == 0 && ep->dev->ep0state != EP0_OUT)) return -EL2HLT; dbuff = (ep->num == 1 || ep->num == 2); do { /* ack dataset irq matching the status we'll handle */ if (ep->num != 0) writel(~INT_EPxDATASET(ep->num), ®s->int_status); set = readl(®s->DataSet) & DATASET_AB(ep->num); size = readl(®s->EPxSizeLA[ep->num]); bufferspace = req->req.length - req->req.actual; /* usually do nothing without an OUT packet */ if (likely(ep->num != 0 || bufferspace != 0)) { if (unlikely(set == 0)) break; /* use ep1/ep2 double-buffering for OUT */ if (!(size & PACKET_ACTIVE)) size = readl(®s->EPxSizeLB[ep->num]); if (!(size & PACKET_ACTIVE)) // "can't happen" break; size &= DATASIZE; /* EPxSizeH == 0 */ /* ep0out no-out-data case for set_config, etc */ } else size = 0; /* read all bytes from this packet */ req->req.actual += size; is_short = (size < ep->ep.maxpacket);#ifdef USB_TRACE VDBG(ep->dev, "read %s %u bytes%s OUT req %p %u/%u\n", ep->ep.name, size, is_short ? "/S" : "", req, req->req.actual, req->req.length);#endif while (likely(size-- != 0)) { u8 byte = (u8) readl(ep->reg_fifo); if (unlikely(bufferspace == 0)) { /* this happens when the driver's buffer * is smaller than what the host sent. * discard the extra data in this packet. */ if (req->req.status != -EOVERFLOW) DBG(ep->dev, "%s overflow %u\n", ep->ep.name, size); req->req.status = -EOVERFLOW; } else { *buf++ = byte; bufferspace--; } } /* completion */ if (unlikely(is_short || req->req.actual == req->req.length)) { if (unlikely(ep->num == 0)) { /* non-control endpoints now usable? */ if (ep->dev->req_config) writel(ep->dev->configured ? USBSTATE_CONFIGURED : 0, ®s->UsbState); /* ep0out status stage */ writel(~(1<<0), ®s->EOP); ep->stopped = 1; ep->dev->ep0state = EP0_STATUS; } done(ep, req, 0); /* empty the second buffer asap */ if (dbuff && !list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct goku_request, queue); goto top; } return 1; } } while (dbuff); return 0;}static inline voidpio_irq_enable(struct goku_udc *dev, struct goku_udc_regs __iomem *regs, int epnum){ dev->int_enable |= INT_EPxDATASET (epnum); writel(dev->int_enable, ®s->int_enable); /* write may still be posted */}static inline voidpio_irq_disable(struct goku_udc *dev, struct goku_udc_regs __iomem *regs, int epnum){ dev->int_enable &= ~INT_EPxDATASET (epnum); writel(dev->int_enable, ®s->int_enable); /* write may still be posted */}static inline voidpio_advance(struct goku_ep *ep){ struct goku_request *req; if (unlikely(list_empty (&ep->queue))) return; req = list_entry(ep->queue.next, struct goku_request, queue); (ep->is_in ? write_fifo : read_fifo)(ep, req);}/*-------------------------------------------------------------------------*/// return: 0 = q running, 1 = q stopped, negative = errnostatic int start_dma(struct goku_ep *ep, struct goku_request *req){ struct goku_udc_regs __iomem *regs = ep->dev->regs; u32 master; u32 start = req->req.dma; u32 end = start + req->req.length - 1; master = readl(®s->dma_master) & MST_RW_BITS; /* re-init the bits affecting IN dma; careful with zlps */ if (likely(ep->is_in)) { if (unlikely(master & MST_RD_ENA)) { DBG (ep->dev, "start, IN active dma %03x!!\n", master);// return -EL2HLT; } writel(end, ®s->in_dma_end); writel(start, ®s->in_dma_start); master &= ~MST_R_BITS; if (unlikely(req->req.length == 0)) master = MST_RD_ENA | MST_RD_EOPB; else if ((req->req.length % ep->ep.maxpacket) != 0 || req->req.zero) master = MST_RD_ENA | MST_EOPB_ENA; else master = MST_RD_ENA | MST_EOPB_DIS; ep->dev->int_enable |= INT_MSTRDEND; /* Goku DMA-OUT merges short packets, which plays poorly with * protocols where short packets mark the transfer boundaries. * The chip supports a nonstandard policy with INT_MSTWRTMOUT, * ending transfers after 3 SOFs; we don't turn it on. */ } else { if (unlikely(master & MST_WR_ENA)) { DBG (ep->dev, "start, OUT active dma %03x!!\n", master);// return -EL2HLT; } writel(end, ®s->out_dma_end); writel(start, ®s->out_dma_start); master &= ~MST_W_BITS; master |= MST_WR_ENA | MST_TIMEOUT_DIS; ep->dev->int_enable |= INT_MSTWREND|INT_MSTWRTMOUT; } writel(master, ®s->dma_master); writel(ep->dev->int_enable, ®s->int_enable); return 0;}static void dma_advance(struct goku_udc *dev, struct goku_ep *ep){ struct goku_request *req; struct goku_udc_regs __iomem *regs = ep->dev->regs; u32 master; master = readl(®s->dma_master); if (unlikely(list_empty(&ep->queue))) {stop: if (ep->is_in) dev->int_enable &= ~INT_MSTRDEND; else dev->int_enable &= ~(INT_MSTWREND|INT_MSTWRTMOUT); writel(dev->int_enable, ®s->int_enable); return; } req = list_entry(ep->queue.next, struct goku_request, queue); /* normal hw dma completion (not abort) */ if (likely(ep->is_in)) { if (unlikely(master & MST_RD_ENA)) return; req->req.actual = readl(®s->in_dma_current); } else { if (unlikely(master & MST_WR_ENA)) return; /* hardware merges short packets, and also hides packet * overruns. a partial packet MAY be in the fifo here. */ req->req.actual = readl(®s->out_dma_current); } req->req.actual -= req->req.dma; req->req.actual++;#ifdef USB_TRACE VDBG(dev, "done %s %s dma, %u/%u bytes, req %p\n", ep->ep.name, ep->is_in ? "IN" : "OUT", req->req.actual, req->req.length, req);#endif done(ep, req, 0); if (list_empty(&ep->queue)) goto stop; req = list_entry(ep->queue.next, struct goku_request, queue); (void) start_dma(ep, req);}static void abort_dma(struct goku_ep *ep, int status){ struct goku_udc_regs __iomem *regs = ep->dev->regs; struct goku_request *req; u32 curr, master; /* NAK future host requests, hoping the implicit delay lets the * dma engine finish reading (or writing) its latest packet and * empty the dma buffer (up to 16 bytes). * * This avoids needing to clean up a partial packet in the fifo; * we can't do that for IN without side effects to HALT and TOGGLE. */ command(regs, COMMAND_FIFO_DISABLE, ep->num); req = list_entry(ep->queue.next, struct goku_request, queue); master = readl(®s->dma_master) & MST_RW_BITS; /* FIXME using these resets isn't usably documented. this may * not work unless it's followed by disabling the endpoint. * * FIXME the OUT reset path doesn't even behave consistently. */ if (ep->is_in) { if (unlikely((readl(®s->dma_master) & MST_RD_ENA) == 0)) goto finished; curr = readl(®s->in_dma_current); writel(curr, ®s->in_dma_end); writel(curr, ®s->in_dma_start); master &= ~MST_R_BITS; master |= MST_RD_RESET; writel(master, ®s->dma_master); if (readl(®s->dma_master) & MST_RD_ENA) DBG(ep->dev, "IN dma active after reset!\n"); } else { if (unlikely((readl(®s->dma_master) & MST_WR_ENA) == 0)) goto finished; curr = readl(®s->out_dma_current); writel(curr, ®s->out_dma_end); writel(curr, ®s->out_dma_start); master &= ~MST_W_BITS; master |= MST_WR_RESET; writel(master, ®s->dma_master); if (readl(®s->dma_master) & MST_WR_ENA) DBG(ep->dev, "OUT dma active after reset!\n"); } req->req.actual = (curr - req->req.dma) + 1; req->req.status = status; VDBG(ep->dev, "%s %s %s %d/%d\n", __FUNCTION__, ep->ep.name, ep->is_in ? "IN" : "OUT", req->req.actual, req->req.length); command(regs, COMMAND_FIFO_ENABLE, ep->num); return;finished: /* dma already completed; no abort needed */ command(regs, COMMAND_FIFO_ENABLE, ep->num); req->req.actual = req->req.length; req->req.status = 0;}/*-------------------------------------------------------------------------*/static intgoku_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags){ struct goku_request *req; struct goku_ep *ep; struct goku_udc *dev; unsigned long flags; int status; /* always require a cpu-view buffer so pio works */ req = container_of(_req, struct goku_request, req); if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) return -EINVAL; ep = container_of(_ep, struct goku_ep, ep); if (unlikely(!_ep || (!ep->desc && ep->num != 0))) return -EINVAL; dev = ep->dev; if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN; /* can't touch registers when suspended */ if (dev->ep0state == EP0_SUSPEND) return -EBUSY; /* set up dma mapping in case the caller didn't */ if (ep->dma && _req->dma == DMA_ADDR_INVALID) { _req->dma = pci_map_single(dev->pdev, _req->buf, _req->length, ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); req->mapped = 1; }#ifdef USB_TRACE VDBG(dev, "%s queue req %p, len %u buf %p\n", _ep->name, _req, _req->length, _req->buf);#endif spin_lock_irqsave(&dev->lock, flags); _req->status = -EINPROGRESS; _req->actual = 0; /* for ep0 IN without premature status, zlp is required and * writing EOP starts the status stage (OUT). */ if (unlikely(ep->num == 0 && ep->is_in)) _req->zero = 1; /* kickstart this i/o queue? */ status = 0; if (list_empty(&ep->queue) && likely(!ep->stopped)) { /* dma: done after dma completion IRQ (or error) * pio: done after last fifo operation */ if (ep->dma) status = start_dma(ep, req); else status = (ep->is_in ? write_fifo : read_fifo)(ep, req); if (unlikely(status != 0)) { if (status > 0) status = 0; req = NULL; } } /* else pio or dma irq handler advances the queue. */ if (likely(req != 0)) list_add_tail(&req->queue, &ep->queue); if (likely(!list_empty(&ep->queue)) && likely(ep->num != 0) && !ep->dma && !(dev->int_enable & INT_EPxDATASET (ep->num))) pio_irq_enable(dev, dev->regs, ep->num); spin_unlock_irqrestore(&dev->lock, flags); /* pci writes may still be posted */ return status;}/* dequeue ALL requests */static void nuke(struct goku_ep *ep, int status){ struct goku_request *req; ep->stopped = 1; if (list_empty(&ep->queue)) return; if (ep->dma) abort_dma(ep, status); while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct goku_request, queue); done(ep, req, status); }}/* dequeue JUST ONE request */static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req){ struct goku_request *req; struct goku_ep *ep; struct goku_udc *dev; unsigned long flags; ep = container_of(_ep, struct goku_ep, ep); if (!_ep || !_req || (!ep->desc && ep->num != 0)) return -EINVAL; dev = ep->dev; if (!dev->driver) return -ESHUTDOWN; /* we can't touch (dma) registers when suspended */ if (dev->ep0state == EP0_SUSPEND) return -EBUSY; VDBG(dev, "%s %s %s %s %p\n", __FUNCTION__, _ep->name, ep->is_in ? "IN" : "OUT", ep->dma ? "dma" : "pio", _req); spin_lock_irqsave(&dev->lock, flags); /* make sure it's actually queued on this endpoint */ list_for_each_entry (req, &ep->queue, queue) { if (&req->req == _req) break; } if (&req->req != _req) { spin_unlock_irqrestore (&dev->lock, flags); return -EINVAL; } if (ep->dma && ep->queue.next == &req->queue && !ep->stopped) { abort_dma(ep, -ECONNRESET); done(ep, req, -ECONNRESET); dma_advance(dev, ep); } else if (!list_empty(&req->queue)) done(ep, req, -ECONNRESET); else req = NULL; spin_unlock_irqrestore(&dev->lock, flags); return req ? 0 : -EOPNOTSUPP;}/*-------------------------------------------------------------------------*/static void goku_clear_halt(struct goku_ep *ep){ // assert (ep->num !=0) VDBG(ep->dev, "%s clear halt\n", ep->ep.name); command(ep->dev->regs, COMMAND_SETDATA0, ep->num); command(ep->dev->regs, COMMAND_STALL_CLEAR, ep->num); if (ep->stopped) { ep->stopped = 0; if (ep->dma) { struct goku_request *req; if (list_empty(&ep->queue)) return; req = list_entry(ep->queue.next, struct goku_request, queue); (void) start_dma(ep, req); } else pio_advance(ep); }}static int goku_set_halt(struct usb_ep *_ep, int value){ struct goku_ep *ep; unsigned long flags; int retval = 0; if (!_ep) return -ENODEV; ep = container_of (_ep, struct goku_ep, ep); if (ep->num == 0) { if (value) { ep->dev->ep0state = EP0_STALL; ep->dev->ep[0].stopped = 1; } else return -EINVAL; /* don't change EPxSTATUS_EP_INVALID to READY */ } else if (!ep->desc) { DBG(ep->dev, "%s %s inactive?\n", __FUNCTION__, ep->ep.name); return -EINVAL; } spin_lock_irqsave(&ep->dev->lock, flags); if (!list_empty(&ep->queue)) retval = -EAGAIN; else if (ep->is_in && value /* data in (either) packet buffer? */ && (readl(&ep->dev->regs->DataSet) & DATASET_AB(ep->num))) retval = -EAGAIN; else if (!value) goku_clear_halt(ep); else { ep->stopped = 1; VDBG(ep->dev, "%s set halt\n", ep->ep.name);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -