📄 lh7a40x_udc.c
字号:
_ep ? ep->ep.name : NULL); return -EINVAL; } spin_lock_irqsave(&ep->dev->lock, flags); usb_set_index(ep_index(ep)); /* Nuke all pending requests (does flush) */ nuke(ep, -ESHUTDOWN); /* Disable ep IRQ */ pio_irq_disable(ep_index(ep)); ep->desc = 0; ep->stopped = 1; spin_unlock_irqrestore(&ep->dev->lock, flags); DEBUG("%s: disabled %s\n", __FUNCTION__, _ep->name); return 0;}static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, gfp_t gfp_flags){ struct lh7a40x_request *req; DEBUG("%s, %p\n", __FUNCTION__, ep); req = kmalloc(sizeof *req, gfp_flags); if (!req) return 0; memset(req, 0, sizeof *req); INIT_LIST_HEAD(&req->queue); return &req->req;}static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *_req){ struct lh7a40x_request *req; DEBUG("%s, %p\n", __FUNCTION__, ep); req = container_of(_req, struct lh7a40x_request, req); WARN_ON(!list_empty(&req->queue)); kfree(req);}static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned bytes, dma_addr_t * dma, gfp_t gfp_flags){ char *retval; DEBUG("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags); retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM)); if (retval) *dma = virt_to_bus(retval); return retval;}static void lh7a40x_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma, unsigned bytes){ DEBUG("%s, %p\n", __FUNCTION__, ep); kfree(buf);}/** Queue one request * Kickstart transfer if needed * NOTE: Sets INDEX register */static int lh7a40x_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags){ struct lh7a40x_request *req; struct lh7a40x_ep *ep; struct lh7a40x_udc *dev; unsigned long flags; DEBUG("\n\n\n%s, %p\n", __FUNCTION__, _ep); req = container_of(_req, struct lh7a40x_request, req); if (unlikely (!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) { DEBUG("%s, bad params\n", __FUNCTION__); return -EINVAL; } ep = container_of(_ep, struct lh7a40x_ep, ep); if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { DEBUG("%s, bad ep\n", __FUNCTION__); return -EINVAL; } dev = ep->dev; if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { DEBUG("%s, bogus device state %p\n", __FUNCTION__, dev->driver); return -ESHUTDOWN; } DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); spin_lock_irqsave(&dev->lock, flags); _req->status = -EINPROGRESS; _req->actual = 0; /* kickstart this i/o queue? */ DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue), ep->stopped); if (list_empty(&ep->queue) && likely(!ep->stopped)) { u32 csr; if (unlikely(ep_index(ep) == 0)) { /* EP0 */ list_add_tail(&req->queue, &ep->queue); lh7a40x_ep0_kick(dev, ep); req = 0; } else if (ep_is_in(ep)) { /* EP1 & EP3 */ usb_set_index(ep_index(ep)); csr = usb_read(ep->csr1); pio_irq_enable(ep_index(ep)); if ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) == 0) { if (write_fifo(ep, req) == 1) req = 0; } } else { /* EP2 */ usb_set_index(ep_index(ep)); csr = usb_read(ep->csr1); pio_irq_enable(ep_index(ep)); if (!(csr & USB_OUT_CSR1_FIFO_FULL)) { if (read_fifo(ep, req) == 1) req = 0; } } } /* pio or dma irq handler advances the queue. */ if (likely(req != 0)) list_add_tail(&req->queue, &ep->queue); spin_unlock_irqrestore(&dev->lock, flags); return 0;}/* dequeue JUST ONE request */static int lh7a40x_dequeue(struct usb_ep *_ep, struct usb_request *_req){ struct lh7a40x_ep *ep; struct lh7a40x_request *req; unsigned long flags; DEBUG("%s, %p\n", __FUNCTION__, _ep); ep = container_of(_ep, struct lh7a40x_ep, ep); if (!_ep || ep->ep.name == ep0name) return -EINVAL; spin_lock_irqsave(&ep->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(&ep->dev->lock, flags); return -EINVAL; } done(ep, req, -ECONNRESET); spin_unlock_irqrestore(&ep->dev->lock, flags); return 0;}/** Halt specific EP * Return 0 if success * NOTE: Sets INDEX register to EP ! */static int lh7a40x_set_halt(struct usb_ep *_ep, int value){ struct lh7a40x_ep *ep; unsigned long flags; ep = container_of(_ep, struct lh7a40x_ep, ep); if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { DEBUG("%s, bad ep\n", __FUNCTION__); return -EINVAL; } usb_set_index(ep_index(ep)); DEBUG("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value); spin_lock_irqsave(&ep->dev->lock, flags); if (ep_index(ep) == 0) { /* EP0 */ usb_set(EP0_SEND_STALL, ep->csr1); } else if (ep_is_in(ep)) { u32 csr = usb_read(ep->csr1); if (value && ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) || !list_empty(&ep->queue))) { /* * Attempts to halt IN endpoints will fail (returning -EAGAIN) * if any transfer requests are still queued, or if the controller * FIFO still holds bytes that the host hasn抰 collected. */ spin_unlock_irqrestore(&ep->dev->lock, flags); DEBUG ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n", (csr & USB_IN_CSR1_FIFO_NOT_EMPTY), !list_empty(&ep->queue)); return -EAGAIN; } flush(ep); if (value) usb_set(USB_IN_CSR1_SEND_STALL, ep->csr1); else { usb_clear(USB_IN_CSR1_SEND_STALL, ep->csr1); usb_set(USB_IN_CSR1_CLR_DATA_TOGGLE, ep->csr1); } } else { flush(ep); if (value) usb_set(USB_OUT_CSR1_SEND_STALL, ep->csr1); else { usb_clear(USB_OUT_CSR1_SEND_STALL, ep->csr1); usb_set(USB_OUT_CSR1_CLR_DATA_REG, ep->csr1); } } if (value) { ep->stopped = 1; } else { ep->stopped = 0; } spin_unlock_irqrestore(&ep->dev->lock, flags); DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS"); return 0;}/** Return bytes in EP FIFO * NOTE: Sets INDEX register to EP */static int lh7a40x_fifo_status(struct usb_ep *_ep){ u32 csr; int count = 0; struct lh7a40x_ep *ep; ep = container_of(_ep, struct lh7a40x_ep, ep); if (!_ep) { DEBUG("%s, bad ep\n", __FUNCTION__); return -ENODEV; } DEBUG("%s, %d\n", __FUNCTION__, ep_index(ep)); /* LPD can't report unclaimed bytes from IN fifos */ if (ep_is_in(ep)) return -EOPNOTSUPP; usb_set_index(ep_index(ep)); csr = usb_read(ep->csr1); if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN || csr & USB_OUT_CSR1_OUT_PKT_RDY) { count = usb_read(USB_OUT_FIFO_WC1); } return count;}/** Flush EP FIFO * NOTE: Sets INDEX register to EP */static void lh7a40x_fifo_flush(struct usb_ep *_ep){ struct lh7a40x_ep *ep; ep = container_of(_ep, struct lh7a40x_ep, ep); if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { DEBUG("%s, bad ep\n", __FUNCTION__); return; } usb_set_index(ep_index(ep)); flush(ep);}/****************************************************************//* End Point 0 related functions *//****************************************************************//* return: 0 = still running, 1 = completed, negative = errno */static int write_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req){ u32 max; unsigned count; int is_last; max = ep_maxpacket(ep); DEBUG_EP0("%s\n", __FUNCTION__); count = write_packet(ep, req, max); /* last packet is usually short (or a zlp) */ if (unlikely(count != max)) is_last = 1; else { if (likely(req->req.length != req->req.actual) || req->req.zero) is_last = 0; else is_last = 1; } DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, ep->ep.name, count, is_last ? "/L" : "", req->req.length - req->req.actual, req); /* requests complete when all IN data is in the FIFO */ if (is_last) { done(ep, req, 0); return 1; } return 0;}static __inline__ int lh7a40x_fifo_read(struct lh7a40x_ep *ep, unsigned char *cp, int max){ int bytes; int count = usb_read(USB_OUT_FIFO_WC1); volatile u32 *fifo = (volatile u32 *)ep->fifo; if (count > max) count = max; bytes = count; while (count--) *cp++ = *fifo & 0xFF; return bytes;}static __inline__ void lh7a40x_fifo_write(struct lh7a40x_ep *ep, unsigned char *cp, int count){ volatile u32 *fifo = (volatile u32 *)ep->fifo; DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count); while (count--) *fifo = *cp++;}static int read_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req){ u32 csr; u8 *buf; unsigned bufferspace, count, is_short; volatile u32 *fifo = (volatile u32 *)ep->fifo; DEBUG_EP0("%s\n", __FUNCTION__); csr = usb_read(USB_EP0_CSR); if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) return 0; buf = req->req.buf + req->req.actual; prefetchw(buf); bufferspace = req->req.length - req->req.actual; /* read all bytes from this packet */ if (likely(csr & EP0_OUT_PKT_RDY)) { count = usb_read(USB_OUT_FIFO_WC1); req->req.actual += min(count, bufferspace); } else /* zlp */ count = 0; is_short = (count < ep->ep.maxpacket); DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n", ep->ep.name, csr, count, is_short ? "/S" : "", req, req->req.actual, req->req.length); while (likely(count-- != 0)) { u8 byte = (u8) (*fifo & 0xff); if (unlikely(bufferspace == 0)) { /* this happens when the driver's buffer * is smaller than what the host sent. * discard the extra data. */ if (req->req.status != -EOVERFLOW) DEBUG_EP0("%s overflow %d\n", ep->ep.name, count); req->req.status = -EOVERFLOW; } else { *buf++ = byte; bufferspace--; } } /* completion */ if (is_short || req->req.actual == req->req.length) { done(ep, req, 0); return 1; } /* finished that packet. the next one may be waiting... */ return 0;}/** * udc_set_address - set the USB address for this device * @address: * * Called from control endpoint function after it decodes a set address setup packet. */static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address){ DEBUG_EP0("%s: %d\n", __FUNCTION__, address); /* c.f. 15.1.2.2 Table 15-4 address will be used after DATA_END is set */ dev->usb_address = address; usb_set((address & USB_FA_FUNCTION_ADDR), USB_FA); usb_set(USB_FA_ADDR_UPDATE | (address & USB_FA_FUNCTION_ADDR), USB_FA); /* usb_read(USB_FA); */}/* * DATA_STATE_RECV (OUT_PKT_RDY) * - if error * set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits * - else * set EP0_CLR_OUT bit if last set EP0_DATA_END bit */static void lh7a40x_ep0_out(struct lh7a40x_udc *dev, u32 csr){ struct lh7a40x_request *req; struct lh7a40x_ep *ep = &dev->ep[0]; int ret; DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); if (list_empty(&ep->queue)) req = 0; else req = list_entry(ep->queue.next, struct lh7a40x_request, queue); if (req) { if (req->req.length == 0) { DEBUG_EP0("ZERO LENGTH OUT!\n"); usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); dev->ep0state = WAIT_FOR_SETUP; return; } ret = read_fifo_ep0(ep, req); if (ret) { /* Done! */ DEBUG_EP0("%s: finished, waiting for status\n", __FUNCTION__); usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); dev->ep0state = WAIT_FOR_SETUP; } else { /* Not done yet.. */ DEBUG_EP0("%s: not finished\n", __FUNCTION__); usb_set(EP0_CLR_OUT, USB_EP0_CSR); } } else { DEBUG_EP0("NO REQ??!\n"); }}/* * DATA_STATE_XMIT */static int lh7a40x_ep0_in(struct lh7a40x_udc *dev, u32 csr){ struct lh7a40x_request *req; struct lh7a40x_ep *ep = &dev->ep[0]; int ret, need_zlp = 0; DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); if (list_empty(&ep->queue)) req = 0; else req = list_entry(ep->queue.next, struct lh7a40x_request, queue); if (!req) { DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); return 0; } if (req->req.length == 0) { usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); dev->ep0state = WAIT_FOR_SETUP; return 1; } if (req->req.length - req->req.actual == EP0_PACKETSIZE) { /* Next write will end with the packet size, */ /* so we need Zero-length-packet */ need_zlp = 1; } ret = write_fifo_ep0(ep, req); if (ret == 1 && !need_zlp) { /* Last packet */ DEBUG_EP0("%s: finished, waiting for status\n", __FUNCTION__); usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); dev->ep0state = WAIT_FOR_SETUP; } else { DEBUG_EP0("%s: not finished\n", __FUNCTION__); usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR); } if (need_zlp) { DEBUG_EP0("%s: Need ZLP!\n", __FUNCTION__); usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR); dev->ep0state = DATA_STATE_NEED_ZLP; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -