📄 pxa2xx_udc.c
字号:
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; }#ifdef USE_DMA if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) { cancel_dma(ep); done(ep, req, -ECONNRESET); /* restart i/o */ if (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct pxa2xx_request, queue); kick_dma(ep, req); } } else#endif 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; LED_EP0_OFF; /* 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, .alloc_buffer = pxa2xx_ep_alloc_buffer, .free_buffer = pxa2xx_ep_free_buffer, .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 const struct usb_gadget_ops pxa2xx_udc_ops = { .get_frame = pxa2xx_udc_get_frame, .wakeup = pxa2xx_udc_wakeup, // current versions must always be self-powered};/*-------------------------------------------------------------------------*/#ifdef UDC_PROC_FILEstatic 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 DMASTR, dev->driver ? dev->driver->driver.name : "(none)", is_usb_connected() ? "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_usb_connected() || !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; int t; 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/%lu\n", ep->ep.name, le16_to_cpu (d->wMaxPacketSize), (ep->dma >= 0) ? "dma" : "pio", tmp, ep->pio_irqs, ep->dma_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) {#ifdef USE_DMA if (ep->dma >= 0 && req->queue.prev == &ep->queue) t = scnprintf(next, size, "\treq %p len %d/%d " "buf %p (dma%d dcmd %08x)\n", &req->req, req->req.actual, req->req.length, req->req.buf, ep->dma, DCMD(ep->dma) // low 13 bits == bytes-to-go ); else#endif 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 /* !UDC_PROC_FILE */#define create_proc_files() do {} while (0)#define remove_proc_files() do {} while (0)#endif /* UDC_PROC_FILE *//* "function" sysfs attribute */static ssize_tshow_function (struct device *_dev, char *buf){ struct pxa2xx_udc *dev = dev_get_drvdata (_dev); if (!dev->driver || !dev->driver->function || strlen (dev->driver->function) > PAGE_SIZE) return 0; return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);}static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);/*-------------------------------------------------------------------------*//* * 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 */ make_usb_disappear(); udc_clear_mask_UDCCR(UDCCR_UDE);#ifdef CONFIG_ARCH_PXA /* Disable clock for USB device */ CKEN &= ~CKEN11_USB;#endif ep0_idle (dev); dev->gadget.speed = USB_SPEED_UNKNOWN; LED_CONNECTED_OFF;}/* * 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 = 0; ep->stopped = 0; INIT_LIST_HEAD (&ep->queue); ep->pio_irqs = ep->dma_irqs = 0; } /* the rest was statically initialized, and is read-only */}/* until it's enabled, this UDC should be completely invisible * to any USB host. */static void udc_enable (struct pxa2xx_udc *dev){ udc_clear_mask_UDCCR(UDCCR_UDE);#ifdef CONFIG_ARCH_PXA /* Enable clock for USB device */ CKEN |= CKEN11_USB;#endif /* try to clear these bits before we enable the udc */ udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); ep0_idle(dev); dev->gadget.speed = USB_SPEED_UNKNOWN; dev->stats.irqs = 0; /* * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: * - enable UDC * - if RESET is already in progress, ack interrupt * - unmask reset interrupt */ udc_set_mask_UDCCR(UDCCR_UDE); if (!(UDCCR & UDCCR_UDA)) udc_ack_int_UDCCR(UDCCR_RSTIR); if (dev->has_cfr /* UDC_RES2 is defined */) { /* pxa255 (a0+) can avoid a set_config race that could * prevent gadget drivers from configuring correctly */ UDCCFR = UDCCFR_ACM; } else { /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) * which could result in missing packets and interrupts. * supposedly one bit per endpoint, controlling whether it * double buffers or not; ACM/AREN bits fit into the holes. * zero bits (like USIR0_IRx) disable double buffering. */ UDC_RES1 = 0x00; UDC_RES2 = 0x00; }#ifdef DISABLE_TEST_MODE /* "test mode" seems to have become the default in later chip * revs, preventing double buffering (and invalidating docs). * this EXPERIMENT enables it for bulk endpoints by tweaking * undefined/reserved register bits (that other drivers clear). * Belcarra code comments noted this usage. */ if (fifo_mode & 1) { /* IN endpoints */ UDC_RES1 |= USIR0_IR1|USIR0_IR6; UDC_RES2 |= USIR1_IR11; } if (fifo_mode & 2) { /* OUT endpoints */ UDC_RES1 |= USIR0_IR2|USIR0_IR7; UDC_RES2 |= USIR1_IR12; }#endif /* caller must be able to sleep in order to cope * with startup transients. */ schedule_timeout(HZ/10); /* enable suspend/resume and reset irqs */ udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); /* enable ep0 irqs */ UICR0 &= ~UICR0_IM0; /* if hardware supports it, connect to usb and wait for host */ let_usb_appear();}/* when a driver is successfully registered, it will receive * control requests including set_configuration(), which enables * non-control requests. then usb traffic follows until a * disconnect is reported. then a host may connect again, or * the driver might get unbound. */int usb_gadget_register_driver(struct usb_gadget_driver *driver){ struct pxa2xx_udc *dev = the_controller; int retval; if (!driver || driver->speed != USB_SPEED_FULL || !driver->bind || !driver->unbind || !driver->disconnect || !driver->setup) return -EINVAL; if (!dev) return -ENODEV; if (dev->driver) return -EBUSY; /* first hook up the driver ... */ dev->driver = driver;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -