📄 goku_udc.c
字号:
command(ep->dev->regs, COMMAND_STALL, ep->num); readl(ep->reg_status); } spin_unlock_irqrestore(&ep->dev->lock, flags); return retval;}static int goku_fifo_status(struct usb_ep *_ep){ struct goku_ep *ep; struct goku_udc_regs __iomem *regs; u32 size; if (!_ep) return -ENODEV; ep = container_of(_ep, struct goku_ep, ep); /* size is only reported sanely for OUT */ if (ep->is_in) return -EOPNOTSUPP; /* ignores 16-byte dma buffer; SizeH == 0 */ regs = ep->dev->regs; size = readl(®s->EPxSizeLA[ep->num]) & DATASIZE; size += readl(®s->EPxSizeLB[ep->num]) & DATASIZE; VDBG(ep->dev, "%s %s %u\n", __FUNCTION__, ep->ep.name, size); return size;}static void goku_fifo_flush(struct usb_ep *_ep){ struct goku_ep *ep; struct goku_udc_regs __iomem *regs; u32 size; if (!_ep) return; ep = container_of(_ep, struct goku_ep, ep); VDBG(ep->dev, "%s %s\n", __FUNCTION__, ep->ep.name); /* don't change EPxSTATUS_EP_INVALID to READY */ if (!ep->desc && ep->num != 0) { DBG(ep->dev, "%s %s inactive?\n", __FUNCTION__, ep->ep.name); return; } regs = ep->dev->regs; size = readl(®s->EPxSizeLA[ep->num]); size &= DATASIZE; /* Non-desirable behavior: FIFO_CLEAR also clears the * endpoint halt feature. For OUT, we _could_ just read * the bytes out (PIO, if !ep->dma); for in, no choice. */ if (size) command(regs, COMMAND_FIFO_CLEAR, ep->num);}static struct usb_ep_ops goku_ep_ops = { .enable = goku_ep_enable, .disable = goku_ep_disable, .alloc_request = goku_alloc_request, .free_request = goku_free_request, .alloc_buffer = goku_alloc_buffer, .free_buffer = goku_free_buffer, .queue = goku_queue, .dequeue = goku_dequeue, .set_halt = goku_set_halt, .fifo_status = goku_fifo_status, .fifo_flush = goku_fifo_flush,};/*-------------------------------------------------------------------------*/static int goku_get_frame(struct usb_gadget *_gadget){ return -EOPNOTSUPP;}static const struct usb_gadget_ops goku_ops = { .get_frame = goku_get_frame, // no remote wakeup // not selfpowered};/*-------------------------------------------------------------------------*/static inline char *dmastr(void){ if (use_dma == 0) return "(dma disabled)"; else if (use_dma == 2) return "(dma IN and OUT)"; else return "(dma IN)";}#ifdef CONFIG_USB_GADGET_DEBUG_FILESstatic const char proc_node_name [] = "driver/udc";#define FOURBITS "%s%s%s%s"#define EIGHTBITS FOURBITS FOURBITSstatic voiddump_intmask(const char *label, u32 mask, char **next, unsigned *size){ int t; /* int_status is the same format ... */ t = scnprintf(*next, *size, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n", label, mask, (mask & INT_PWRDETECT) ? " power" : "", (mask & INT_SYSERROR) ? " sys" : "", (mask & INT_MSTRDEND) ? " in-dma" : "", (mask & INT_MSTWRTMOUT) ? " wrtmo" : "", (mask & INT_MSTWREND) ? " out-dma" : "", (mask & INT_MSTWRSET) ? " wrset" : "", (mask & INT_ERR) ? " err" : "", (mask & INT_SOF) ? " sof" : "", (mask & INT_EP3NAK) ? " ep3nak" : "", (mask & INT_EP2NAK) ? " ep2nak" : "", (mask & INT_EP1NAK) ? " ep1nak" : "", (mask & INT_EP3DATASET) ? " ep3" : "", (mask & INT_EP2DATASET) ? " ep2" : "", (mask & INT_EP1DATASET) ? " ep1" : "", (mask & INT_STATUSNAK) ? " ep0snak" : "", (mask & INT_STATUS) ? " ep0status" : "", (mask & INT_SETUP) ? " setup" : "", (mask & INT_ENDPOINT0) ? " ep0" : "", (mask & INT_USBRESET) ? " reset" : "", (mask & INT_SUSPEND) ? " suspend" : ""); *size -= t; *next += t;}static intudc_proc_read(char *buffer, char **start, off_t off, int count, int *eof, void *_dev){ char *buf = buffer; struct goku_udc *dev = _dev; struct goku_udc_regs __iomem *regs = dev->regs; char *next = buf; unsigned size = count; unsigned long flags; int i, t, is_usb_connected; u32 tmp; if (off != 0) return 0; local_irq_save(flags); /* basic device status */ tmp = readl(®s->power_detect); is_usb_connected = tmp & PW_DETECT; t = scnprintf(next, size, "%s - %s\n" "%s version: %s %s\n" "Gadget driver: %s\n" "Host %s, %s\n" "\n", pci_name(dev->pdev), driver_desc, driver_name, DRIVER_VERSION, dmastr(), dev->driver ? dev->driver->driver.name : "(none)", is_usb_connected ? ((tmp & PW_PULLUP) ? "full speed" : "powered") : "disconnected", ({char *tmp; switch(dev->ep0state){ case EP0_DISCONNECT: tmp = "ep0_disconnect"; break; case EP0_IDLE: tmp = "ep0_idle"; break; case EP0_IN: tmp = "ep0_in"; break; case EP0_OUT: tmp = "ep0_out"; break; case EP0_STATUS: tmp = "ep0_status"; break; case EP0_STALL: tmp = "ep0_stall"; break; case EP0_SUSPEND: tmp = "ep0_suspend"; break; default: tmp = "ep0_?"; break; } tmp; }) ); size -= t; next += t; dump_intmask("int_status", readl(®s->int_status), &next, &size); dump_intmask("int_enable", readl(®s->int_enable), &next, &size); if (!is_usb_connected || !dev->driver || (tmp & PW_PULLUP) == 0) goto done; /* registers for (active) device and ep0 */ t = scnprintf(next, size, "\nirqs %lu\ndataset %02x " "single.bcs %02x.%02x state %x addr %u\n", dev->irqs, readl(®s->DataSet), readl(®s->EPxSingle), readl(®s->EPxBCS), readl(®s->UsbState), readl(®s->address)); size -= t; next += t; tmp = readl(®s->dma_master); t = scnprintf(next, size, "dma %03X =" EIGHTBITS "%s %s\n", tmp, (tmp & MST_EOPB_DIS) ? " eopb-" : "", (tmp & MST_EOPB_ENA) ? " eopb+" : "", (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "", (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "", (tmp & MST_RD_EOPB) ? " eopb" : "", (tmp & MST_RD_RESET) ? " in_reset" : "", (tmp & MST_WR_RESET) ? " out_reset" : "", (tmp & MST_RD_ENA) ? " IN" : "", (tmp & MST_WR_ENA) ? " OUT" : "", (tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in"); size -= t; next += t; /* dump endpoint queues */ for (i = 0; i < 4; i++) { struct goku_ep *ep = &dev->ep [i]; struct goku_request *req; int t; if (i && !ep->desc) continue; tmp = readl(ep->reg_status); t = scnprintf(next, size, "%s %s max %u %s, irqs %lu, " "status %02x (%s) " FOURBITS "\n", ep->ep.name, ep->is_in ? "in" : "out", ep->ep.maxpacket, ep->dma ? "dma" : "pio", ep->irqs, tmp, ({ char *s; switch (tmp & EPxSTATUS_EP_MASK) { case EPxSTATUS_EP_READY: s = "ready"; break; case EPxSTATUS_EP_DATAIN: s = "packet"; break; case EPxSTATUS_EP_FULL: s = "full"; break; case EPxSTATUS_EP_TX_ERR: // host will retry s = "tx_err"; break; case EPxSTATUS_EP_RX_ERR: s = "rx_err"; break; case EPxSTATUS_EP_BUSY: /* ep0 only */ s = "busy"; break; case EPxSTATUS_EP_STALL: s = "stall"; break; case EPxSTATUS_EP_INVALID: // these "can't happen" s = "invalid"; break; default: s = "?"; break; }; s; }), (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0", (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "", (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "", (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "" ); 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) { if (ep->dma && req->queue.prev == &ep->queue) { if (i == UDC_MSTRD_ENDPOINT) tmp = readl(®s->in_dma_current); else tmp = readl(®s->out_dma_current); tmp -= req->req.dma; tmp++; } else tmp = req->req.actual; t = scnprintf(next, size, "\treq %p len %u/%u buf %p\n", &req->req, tmp, 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;}#endif /* CONFIG_USB_GADGET_DEBUG_FILES *//*-------------------------------------------------------------------------*/static void udc_reinit (struct goku_udc *dev){ static char *names [] = { "ep0", "ep1-bulk", "ep2-bulk", "ep3-bulk" }; unsigned i; INIT_LIST_HEAD (&dev->gadget.ep_list); dev->gadget.ep0 = &dev->ep [0].ep; dev->gadget.speed = USB_SPEED_UNKNOWN; dev->ep0state = EP0_DISCONNECT; dev->irqs = 0; for (i = 0; i < 4; i++) { struct goku_ep *ep = &dev->ep[i]; ep->num = i; ep->ep.name = names[i]; ep->reg_fifo = &dev->regs->ep_fifo [i]; ep->reg_status = &dev->regs->ep_status [i]; ep->reg_mode = &dev->regs->ep_mode[i]; ep->ep.ops = &goku_ep_ops; list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); ep->dev = dev; INIT_LIST_HEAD (&ep->queue); ep_reset(NULL, ep); } dev->ep[0].reg_mode = NULL; dev->ep[0].ep.maxpacket = MAX_EP0_SIZE; list_del_init (&dev->ep[0].ep.ep_list);}static void udc_reset(struct goku_udc *dev){ struct goku_udc_regs __iomem *regs = dev->regs; writel(0, ®s->power_detect); writel(0, ®s->int_enable); readl(®s->int_enable); dev->int_enable = 0; /* deassert reset, leave USB D+ at hi-Z (no pullup) * don't let INT_PWRDETECT sequence begin */ udelay(250); writel(PW_RESETB, ®s->power_detect); readl(®s->int_enable);}static void ep0_start(struct goku_udc *dev){ struct goku_udc_regs __iomem *regs = dev->regs; unsigned i; VDBG(dev, "%s\n", __FUNCTION__); udc_reset(dev); udc_reinit (dev); //writel(MST_EOPB_ENA | MST_TIMEOUT_ENA, ®s->dma_master); /* hw handles set_address, set_feature, get_status; maybe more */ writel( G_REQMODE_SET_INTF | G_REQMODE_GET_INTF | G_REQMODE_SET_CONF | G_REQMODE_GET_CONF | G_REQMODE_GET_DESC | G_REQMODE_CLEAR_FEAT , ®s->reqmode); for (i = 0; i < 4; i++) dev->ep[i].irqs = 0; /* can't modify descriptors after writing UsbReady */ for (i = 0; i < DESC_LEN; i++) writel(0, ®s->descriptors[i]); writel(0, ®s->UsbReady); /* expect ep0 requests when the host drops reset */ writel(PW_RESETB | PW_PULLUP, ®s->power_detect); dev->int_enable = INT_DEVWIDE | INT_EP0; writel(dev->int_enable, &dev->regs->int_enable); readl(®s->int_enable); dev->gadget.speed = USB_SPEED_FULL; dev->ep0state = EP0_IDLE;}static void udc_enable(struct goku_udc *dev){ /* start enumeration now, or after power detect irq */ if (readl(&dev->regs->power_detect) & PW_DETECT) ep0_start(dev); else { DBG(dev, "%s\n", __FUNCTION__); dev->int_enable = INT_PWRDETECT; writel(dev->int_enable, &dev->regs->int_enable); }}/*-------------------------------------------------------------------------*//* keeping it simple: * - one bus driver, initted first; * - one function driver, initted second */static struct goku_udc *the_controller;/* 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 goku_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; /* hook up the driver */ driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; retval = driver->bind(&dev->gadget); if (retval) { DBG(dev, "bind to driver %s --> error %d\n", driver->driver.name, retval); dev->driver = NULL; dev->gadget.dev.driver = NULL; return retval; } /* then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ udc_enable(dev); DBG(dev, "registered gadget driver '%s'\n", driver->driver.name); return 0;}EXPORT_SYMBOL(usb_gadget_register_driver);static voidstop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver){ unsigned i; DBG (dev, "%s\n", __FUNCTION__); if (dev->gadget.speed == USB_SPEED_UNKNOWN) driver = NULL; /* disconnect gadget driver after quiesceing hw and the driver */ udc_reset (dev); for (i = 0; i < 4; i++) nuke(&dev->ep [i], -ESHUTDOWN); if (driver) { spin_unlock(&dev->lock); driver->disconnect(&dev->gadget); spin_lock(&dev->lock); } if (dev->driver) udc_enable(dev);}int usb_gadget_unregister_driver(struct usb_gadget_driver *driver){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -