📄 pxa2xx_udc.c
字号:
read_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req){ u8 *buf, byte; unsigned bufferspace; buf = req->req.buf + req->req.actual; bufferspace = req->req.length - req->req.actual; while (UDCCS0 & UDCCS0_RNE) { byte = (u8) UDDR0; 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) DMSG("%s overflow\n", ep->ep.name); req->req.status = -EOVERFLOW; } else { *buf++ = byte; req->req.actual++; bufferspace--; } } UDCCS0 = UDCCS0_OPR | UDCCS0_IPR; /* completion */ if (req->req.actual >= req->req.length) return 1; /* finished that packet. the next one may be waiting... */ return 0;}/*-------------------------------------------------------------------------*/static intpxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags){ struct pxa2xx_request *req; struct pxa2xx_ep *ep; struct pxa2xx_udc *dev; unsigned long flags; req = container_of(_req, struct pxa2xx_request, req); if (unlikely (!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) { DMSG("%s, bad params\n", __FUNCTION__); return -EINVAL; } ep = container_of(_ep, struct pxa2xx_ep, ep); if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { DMSG("%s, bad ep\n", __FUNCTION__); return -EINVAL; } dev = ep->dev; if (unlikely (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { DMSG("%s, bogus device state\n", __FUNCTION__); return -ESHUTDOWN; } /* iso is always one packet per request, that's the only way * we can report per-packet status. that also helps with dma. */ if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC && req->req.length > le16_to_cpu (ep->desc->wMaxPacketSize))) return -EMSGSIZE; DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); local_irq_save(flags); _req->status = -EINPROGRESS; _req->actual = 0; /* kickstart this i/o queue? */ if (list_empty(&ep->queue) && !ep->stopped) { if (ep->desc == 0 /* ep0 */) { unsigned length = _req->length; switch (dev->ep0state) { case EP0_IN_DATA_PHASE: dev->stats.write.ops++; if (write_ep0_fifo(ep, req)) req = NULL; break; case EP0_OUT_DATA_PHASE: dev->stats.read.ops++; /* messy ... */ if (dev->req_config) { DBG(DBG_VERBOSE, "ep0 config ack%s\n", dev->has_cfr ? "" : " raced"); if (dev->has_cfr) UDCCFR = UDCCFR_AREN|UDCCFR_ACM |UDCCFR_MB1; done(ep, req, 0); dev->ep0state = EP0_END_XFER; local_irq_restore (flags); return 0; } if (dev->req_pending) ep0start(dev, UDCCS0_IPR, "OUT"); if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0 && read_ep0_fifo(ep, req))) { ep0_idle(dev); done(ep, req, 0); req = NULL; } break; default: DMSG("ep0 i/o, odd state %d\n", dev->ep0state); local_irq_restore (flags); return -EL2HLT; } /* can the FIFO can satisfy the request immediately? */ } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 && write_fifo(ep, req)) req = NULL; } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 && read_fifo(ep, req)) { req = NULL; } if (likely (req && ep->desc)) pio_irq_enable(ep->bEndpointAddress); } /* pio or dma irq handler advances the queue. */ if (likely (req != 0)) list_add_tail(&req->queue, &ep->queue); local_irq_restore(flags); return 0;}/* * nuke - dequeue ALL requests */static void nuke(struct pxa2xx_ep *ep, int status){ struct pxa2xx_request *req; /* called with irqs blocked */ while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct pxa2xx_request, queue); done(ep, req, status); } if (ep->desc) pio_irq_disable (ep->bEndpointAddress);}/* dequeue JUST ONE request */static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req){ struct pxa2xx_ep *ep; struct pxa2xx_request *req; unsigned long flags; ep = container_of(_ep, struct pxa2xx_ep, ep); if (!_ep || ep->ep.name == ep0name) return -EINVAL; local_irq_save(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) { local_irq_restore(flags); return -EINVAL; } done(ep, req, -ECONNRESET); local_irq_restore(flags); return 0;}/*-------------------------------------------------------------------------*/static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value){ struct pxa2xx_ep *ep; unsigned long flags; ep = container_of(_ep, struct pxa2xx_ep, ep); if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name)) || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { DMSG("%s, bad ep\n", __FUNCTION__); return -EINVAL; } if (value == 0) { /* this path (reset toggle+halt) is needed to implement * SET_INTERFACE on normal hardware. but it can't be * done from software on the PXA UDC, and the hardware * forgets to do it as part of SET_INTERFACE automagic. */ DMSG("only host can clear %s halt\n", _ep->name); return -EROFS; } local_irq_save(flags); if ((ep->bEndpointAddress & USB_DIR_IN) != 0 && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0 || !list_empty(&ep->queue))) { local_irq_restore(flags); return -EAGAIN; } /* FST bit is the same for control, bulk in, bulk out, interrupt in */ *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF; /* ep0 needs special care */ if (!ep->desc) { start_watchdog(ep->dev); ep->dev->req_pending = 0; ep->dev->ep0state = EP0_STALL; /* and bulk/intr endpoints like dropping stalls too */ } else { unsigned i; for (i = 0; i < 1000; i += 20) { if (*ep->reg_udccs & UDCCS_BI_SST) break; udelay(20); } } local_irq_restore(flags); DBG(DBG_VERBOSE, "%s halt\n", _ep->name); return 0;}static int pxa2xx_ep_fifo_status(struct usb_ep *_ep){ struct pxa2xx_ep *ep; ep = container_of(_ep, struct pxa2xx_ep, ep); if (!_ep) { DMSG("%s, bad ep\n", __FUNCTION__); return -ENODEV; } /* pxa can't report unclaimed bytes from IN fifos */ if ((ep->bEndpointAddress & USB_DIR_IN) != 0) return -EOPNOTSUPP; if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN || (*ep->reg_udccs & UDCCS_BO_RFS) == 0) return 0; else return (*ep->reg_ubcr & 0xfff) + 1;}static void pxa2xx_ep_fifo_flush(struct usb_ep *_ep){ struct pxa2xx_ep *ep; ep = container_of(_ep, struct pxa2xx_ep, ep); if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { DMSG("%s, bad ep\n", __FUNCTION__); return; } /* toggle and halt bits stay unchanged */ /* for OUT, just read and discard the FIFO contents. */ if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0) (void) *ep->reg_uddr; return; } /* most IN status is the same, but ISO can't stall */ *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) ? 0 : UDCCS_BI_SST;}static struct usb_ep_ops pxa2xx_ep_ops = { .enable = pxa2xx_ep_enable, .disable = pxa2xx_ep_disable, .alloc_request = pxa2xx_ep_alloc_request, .free_request = pxa2xx_ep_free_request, .queue = pxa2xx_ep_queue, .dequeue = pxa2xx_ep_dequeue, .set_halt = pxa2xx_ep_set_halt, .fifo_status = pxa2xx_ep_fifo_status, .fifo_flush = pxa2xx_ep_fifo_flush,};/* --------------------------------------------------------------------------- * device-scoped parts of the api to the usb controller hardware * --------------------------------------------------------------------------- */static int pxa2xx_udc_get_frame(struct usb_gadget *_gadget){ return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff);}static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget){ /* host may not have enabled remote wakeup */ if ((UDCCS0 & UDCCS0_DRWF) == 0) return -EHOSTUNREACH; udc_set_mask_UDCCR(UDCCR_RSM); return 0;}static void stop_activity(struct pxa2xx_udc *, struct usb_gadget_driver *);static void udc_enable (struct pxa2xx_udc *);static void udc_disable(struct pxa2xx_udc *);/* We disable the UDC -- and its 48 MHz clock -- whenever it's not * in active use. */static int pullup(struct pxa2xx_udc *udc, int is_active){ is_active = is_active && udc->vbus && udc->pullup; DMSG("%s\n", is_active ? "active" : "inactive"); if (is_active) udc_enable(udc); else { if (udc->gadget.speed != USB_SPEED_UNKNOWN) { DMSG("disconnect %s\n", udc->driver ? udc->driver->driver.name : "(no driver)"); stop_activity(udc, udc->driver); } udc_disable(udc); } return 0;}/* VBUS reporting logically comes from a transceiver */static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active){ struct pxa2xx_udc *udc; udc = container_of(_gadget, struct pxa2xx_udc, gadget); udc->vbus = is_active = (is_active != 0); DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); pullup(udc, is_active); return 0;}/* drivers may have software control over D+ pullup */static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active){ struct pxa2xx_udc *udc; udc = container_of(_gadget, struct pxa2xx_udc, gadget); /* not all boards support pullup control */ if (!udc->mach->gpio_pullup && !udc->mach->udc_command) return -EOPNOTSUPP; is_active = (is_active != 0); udc->pullup = is_active; pullup(udc, is_active); return 0;}static const struct usb_gadget_ops pxa2xx_udc_ops = { .get_frame = pxa2xx_udc_get_frame, .wakeup = pxa2xx_udc_wakeup, .vbus_session = pxa2xx_udc_vbus_session, .pullup = pxa2xx_udc_pullup, // .vbus_draw ... boards may consume current from VBUS, up to // 100-500mA based on config. the 500uA suspend ceiling means // that exclusively vbus-powered PXA designs violate USB specs.};/*-------------------------------------------------------------------------*/#ifdef CONFIG_USB_GADGET_DEBUG_FILESstatic const char proc_node_name [] = "driver/udc";static intudc_proc_read(char *page, char **start, off_t off, int count, int *eof, void *_dev){ char *buf = page; struct pxa2xx_udc *dev = _dev; char *next = buf; unsigned size = count; unsigned long flags; int i, t; u32 tmp; if (off != 0) return 0; local_irq_save(flags); /* basic device status */ t = scnprintf(next, size, DRIVER_DESC "\n" "%s version: %s\nGadget driver: %s\nHost %s\n\n", driver_name, DRIVER_VERSION SIZE_STR "(pio)", dev->driver ? dev->driver->driver.name : "(none)", is_vbus_present() ? "full speed" : "disconnected"); size -= t; next += t; /* registers for device and ep0 */ t = scnprintf(next, size, "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); size -= t; next += t; tmp = UDCCR; t = scnprintf(next, size, "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, (tmp & UDCCR_REM) ? " rem" : "", (tmp & UDCCR_RSTIR) ? " rstir" : "", (tmp & UDCCR_SRM) ? " srm" : "", (tmp & UDCCR_SUSIR) ? " susir" : "", (tmp & UDCCR_RESIR) ? " resir" : "", (tmp & UDCCR_RSM) ? " rsm" : "", (tmp & UDCCR_UDA) ? " uda" : "", (tmp & UDCCR_UDE) ? " ude" : ""); size -= t; next += t; tmp = UDCCS0; t = scnprintf(next, size, "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, (tmp & UDCCS0_SA) ? " sa" : "", (tmp & UDCCS0_RNE) ? " rne" : "", (tmp & UDCCS0_FST) ? " fst" : "", (tmp & UDCCS0_SST) ? " sst" : "", (tmp & UDCCS0_DRWF) ? " dwrf" : "", (tmp & UDCCS0_FTF) ? " ftf" : "", (tmp & UDCCS0_IPR) ? " ipr" : "", (tmp & UDCCS0_OPR) ? " opr" : ""); size -= t; next += t; if (dev->has_cfr) { tmp = UDCCFR; t = scnprintf(next, size, "udccfr %02X =%s%s\n", tmp, (tmp & UDCCFR_AREN) ? " aren" : "", (tmp & UDCCFR_ACM) ? " acm" : ""); size -= t; next += t; } if (!is_vbus_present() || !dev->driver) goto done; t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", dev->stats.write.bytes, dev->stats.write.ops, dev->stats.read.bytes, dev->stats.read.ops, dev->stats.irqs); size -= t; next += t; /* dump endpoint queues */ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { struct pxa2xx_ep *ep = &dev->ep [i]; struct pxa2xx_request *req; if (i != 0) { const struct usb_endpoint_descriptor *d; d = ep->desc; if (!d) continue; tmp = *dev->ep [i].reg_udccs; t = scnprintf(next, size, "%s max %d %s udccs %02x irqs %lu\n", ep->ep.name, le16_to_cpu (d->wMaxPacketSize), "pio", tmp, ep->pio_irqs); /* TODO translate all five groups of udccs bits! */ } else /* ep0 should only have one transfer queued */ t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n", ep->pio_irqs); if (t <= 0 || t > size) goto done; size -= t; next += t; if (list_empty(&ep->queue)) { t = scnprintf(next, size, "\t(nothing queued)\n"); if (t <= 0 || t > size) goto done; size -= t; next += t; continue; } list_for_each_entry(req, &ep->queue, queue) { t = scnprintf(next, size, "\treq %p len %d/%d buf %p\n", &req->req, req->req.actual, req->req.length, req->req.buf); if (t <= 0 || t > size) goto done; size -= t; next += t; } }done: local_irq_restore(flags); *eof = 1; return count - size;}#define create_proc_files() \ create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)#define remove_proc_files() \ remove_proc_entry(proc_node_name, NULL)#else /* !CONFIG_USB_GADGET_DEBUG_FILES */#define create_proc_files() do {} while (0)#define remove_proc_files() do {} while (0)#endif /* CONFIG_USB_GADGET_DEBUG_FILES *//*-------------------------------------------------------------------------*//* * udc_disable - disable USB device controller */static void udc_disable(struct pxa2xx_udc *dev){ /* block all irqs */ udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); UICR0 = UICR1 = 0xff; UFNRH = UFNRH_SIM; /* if hardware supports it, disconnect from usb */ pullup_off(); udc_clear_mask_UDCCR(UDCCR_UDE);#ifdef CONFIG_ARCH_PXA /* Disable clock for USB device */ clk_disable(dev->clk);#endif ep0_idle (dev); dev->gadget.speed = USB_SPEED_UNKNOWN;}/* * udc_reinit - initialize software state */static void udc_reinit(struct pxa2xx_udc *dev){ u32 i; /* device/ep0 records init */ INIT_LIST_HEAD (&dev->gadget.ep_list); INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); dev->ep0state = EP0_IDLE; /* basic endpoint records init */ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { struct pxa2xx_ep *ep = &dev->ep[i]; if (i != 0) list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); ep->desc = NULL; ep->stopped = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -