📄 jz4740_udc.c
字号:
(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) { DEBUG("%s, bad params\n", __FUNCTION__); return -EINVAL; } ep = container_of(_ep, struct jz4740_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); jz4740_ep0_kick(dev, ep); req = 0; } else if (use_dma) { /* DMA */ kick_dma(ep, req); } /* PIO */ else if (ep_is_in(ep)) { /* EP1 & EP2 */ usb_set_index(ep_index(ep)); csr = usb_readb(ep->csr); pio_irq_enable(ep); if (!(csr & USB_INCSR_FFNOTEMPT)) { if (write_fifo(ep, req) == 1) req = 0; } } else { /* EP1 */ usb_set_index(ep_index(ep)); csr = usb_readb(ep->csr); pio_irq_enable(ep); if (csr & USB_OUTCSR_OUTPKTRDY) { 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 jz4740_dequeue(struct usb_ep *_ep, struct usb_request *_req){ struct jz4740_ep *ep; struct jz4740_request *req; unsigned long flags; DEBUG("%s, %p\n", __FUNCTION__, _ep); ep = container_of(_ep, struct jz4740_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 jz4740_set_halt(struct usb_ep *_ep, int value){ struct jz4740_ep *ep; unsigned long flags; ep = container_of(_ep, struct jz4740_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_setb(USB_REG_CSR0, USB_CSR0_SENDSTALL); } else if (ep_is_in(ep)) { u32 csr = usb_readb(ep->csr); if (value && ((csr & USB_INCSR_FFNOTEMPT) || !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 hasnt collected. */ spin_unlock_irqrestore(&ep->dev->lock, flags); DEBUG ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n", (csr & USB_INCSR_FFNOTEMPT), !list_empty(&ep->queue)); return -EAGAIN; } flush(ep); if (value) { usb_setb(ep->csr, USB_INCSR_SENDSTALL); } else { usb_clearb(ep->csr, USB_INCSR_SENDSTALL); usb_setb(ep->csr, USB_INCSR_CDT); } } else { flush(ep); if (value) { usb_setb(ep->csr, USB_OUTCSR_SENDSTALL); } else { usb_clearb(ep->csr, USB_OUTCSR_SENDSTALL); usb_setb(ep->csr, USB_OUTCSR_CDT); } } 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 jz4740_fifo_status(struct usb_ep *_ep){ u32 csr; int count = 0; struct jz4740_ep *ep; ep = container_of(_ep, struct jz4740_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_readb(ep->csr); if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN || csr & 0x1) { count = usb_readw(USB_REG_OUTCOUNT); } return count;}/** Flush EP FIFO * NOTE: Sets INDEX register to EP */static void jz4740_fifo_flush(struct usb_ep *_ep){ struct jz4740_ep *ep; ep = container_of(_ep, struct jz4740_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 jz4740_ep *ep, struct jz4740_request *req){ u32 max; unsigned count; int is_last; max = ep_maxpacket(ep); 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 jz4740_fifo_read(struct jz4740_ep *ep, unsigned char *cp, int max){ int bytes; int count = usb_readw(USB_REG_OUTCOUNT); volatile u8 *fifo = (volatile u8 *)ep->fifo; if (count > max) count = max; bytes = count; while (count--) *cp++ = *fifo; return bytes;}static __inline__ void jz4740_fifo_write(struct jz4740_ep *ep, unsigned char *cp, int count){ volatile u8 *fifo = (volatile u8 *)ep->fifo; DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count); while (count--) *fifo = *cp++;}static int read_fifo_ep0(struct jz4740_ep *ep, struct jz4740_request *req){ u32 csr; u8 *buf; unsigned bufferspace, count, is_short; volatile u8 *fifo = (volatile u8 *)ep->fifo; DEBUG_EP0("%s\n", __FUNCTION__); csr = usb_readb(USB_REG_CSR0); if (!(csr & USB_CSR0_OUTPKTRDY)) 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 & USB_CSR0_OUTPKTRDY)) { count = usb_readw(USB_REG_OUTCOUNT); 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 jz4740_udc *dev, unsigned char address){ DEBUG_EP0("%s: %d\n", __FUNCTION__, address); dev->usb_address = address; usb_writeb(USB_REG_FADDR, address);}/* * DATA_STATE_RECV (USB_CSR0_OUTPKTRDY) * - if error * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits * - else * set USB_CSR0_SVDOUTPKTRDY bit if last set USB_CSR0_DATAEND bit */static void jz4740_ep0_out(struct jz4740_udc *dev, u32 csr){ struct jz4740_request *req; struct jz4740_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 jz4740_request, queue); if (req) { if (req->req.length == 0) { DEBUG_EP0("ZERO LENGTH OUT!\n");/* usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); */ usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY)); 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_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); dev->ep0state = WAIT_FOR_SETUP; } else { /* Not done yet.. */ DEBUG_EP0("%s: not finished\n", __FUNCTION__); usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); } } else { DEBUG_EP0("NO REQ??!\n"); }}/* * DATA_STATE_XMIT */static int jz4740_ep0_in(struct jz4740_udc *dev, u32 csr){ struct jz4740_request *req; struct jz4740_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 jz4740_request, queue); if (!req) { DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); return 0; } if (req->req.length == 0) { usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); dev->ep0state = WAIT_FOR_SETUP; return 1; } if (req->req.length - req->req.actual == EP0_MAXPACKETSIZE) { /* 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_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); dev->ep0state = WAIT_FOR_SETUP; } else { DEBUG_EP0("%s: not finished\n", __FUNCTION__); usb_setb(USB_REG_CSR0, USB_CSR0_INPKTRDY); } if (need_zlp) { DEBUG_EP0("%s: Need ZLP!\n", __FUNCTION__); usb_setb(USB_REG_CSR0, USB_CSR0_INPKTRDY); dev->ep0state = DATA_STATE_NEED_ZLP; } return 1;}#if 0static int jz4740_handle_get_status(struct jz4740_udc *dev, struct usb_ctrlrequest *ctrl){ struct jz4740_ep *ep0 = &dev->ep[0]; struct jz4740_ep *qep; int reqtype = (ctrl->bRequestType & USB_RECIP_MASK); u16 val = 0; if (reqtype == USB_RECIP_INTERFACE) { /* This is not supported. * And according to the USB spec, this one does nothing.. * Just return 0 */ DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); } else if (reqtype == USB_RECIP_DEVICE) { DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); val |= (1 << 0); /* Self powered */ /*val |= (1<<1); *//* Remote wakeup */ } else if (reqtype == USB_RECIP_ENDPOINT) { int ep_num = (ctrl->wIndex & ~USB_DIR_IN); DEBUG_SETUP ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n", ep_num, ctrl->wLength); if (ctrl->wLength > 2 || ep_num > 3) return -EOPNOTSUPP; qep = &dev->ep[ep_num]; if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0) && ep_index(qep) != 0) { return -EOPNOTSUPP; } usb_set_index(ep_index(qep)); /* Return status on next IN token */ switch (qep->ep_type) { case ep_control: val = (usb_readb(qep->csr) & USB_CSR0_SENDSTALL) == USB_CSR0_SENDSTALL; break; case ep_bulk_in: case ep_interrupt: val = (usb_readb(qep->csr) & USB_INCSR_SENDSTALL) == USB_INCSR_SENDSTALL; break; case ep_bulk_out: val = (usb_readb(qep->csr) & USB_OUTCSR_SENDSTALL) == USB_OUTCSR_SENDSTALL; break; } /* Back to EP0 index */ usb_set_index(0); DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num, ctrl->wIndex, val); } else { DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype); return -EOPNOTSUPP; } /* Clear "out packet ready" */ usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); /* Put status to FIFO */ jz4740_fifo_write(ep0, (u8 *) & val, sizeof(val)); /* Issue "In packet ready" */ usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); return 0;}#endif/* * WAIT_FOR_SETUP (OUTPKTRDY) * - read data packet from EP0 FIFO * - decode command * - if error * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits * - else * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND bits */static void jz4740_ep0_setup(struct jz4740_udc *dev, u32 csr){ struct jz4740_ep *ep = &dev->ep[0]; struct usb_ctrlrequest ctrl; int i; DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr); /* Nuke all previous transfers */ nuke(ep, -EPROTO); /* read control req from fifo (8 bytes) */ jz4740_fifo_read(ep, (unsigned char *)&ctrl, 8);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -