📄 goku_udc.c
字号:
struct goku_udc *dev = the_controller; unsigned long flags; if (!dev) return -ENODEV; if (!driver || driver != dev->driver) return -EINVAL; spin_lock_irqsave(&dev->lock, flags); dev->driver = NULL; stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); driver->unbind(&dev->gadget); DBG(dev, "unregistered driver '%s'\n", driver->driver.name); return 0;}EXPORT_SYMBOL(usb_gadget_unregister_driver);/*-------------------------------------------------------------------------*/static void ep0_setup(struct goku_udc *dev){ struct goku_udc_regs __iomem *regs = dev->regs; struct usb_ctrlrequest ctrl; int tmp; /* read SETUP packet and enter DATA stage */ ctrl.bRequestType = readl(®s->bRequestType); ctrl.bRequest = readl(®s->bRequest); ctrl.wValue = cpu_to_le16((readl(®s->wValueH) << 8) | readl(®s->wValueL)); ctrl.wIndex = cpu_to_le16((readl(®s->wIndexH) << 8) | readl(®s->wIndexL)); ctrl.wLength = cpu_to_le16((readl(®s->wLengthH) << 8) | readl(®s->wLengthL)); writel(0, ®s->SetupRecv); nuke(&dev->ep[0], 0); dev->ep[0].stopped = 0; if (likely(ctrl.bRequestType & USB_DIR_IN)) { dev->ep[0].is_in = 1; dev->ep0state = EP0_IN; /* detect early status stages */ writel(ICONTROL_STATUSNAK, &dev->regs->IntControl); } else { dev->ep[0].is_in = 0; dev->ep0state = EP0_OUT; /* NOTE: CLEAR_FEATURE is done in software so that we can * synchronize transfer restarts after bulk IN stalls. data * won't even enter the fifo until the halt is cleared. */ switch (ctrl.bRequest) { case USB_REQ_CLEAR_FEATURE: switch (ctrl.bRequestType) { case USB_RECIP_ENDPOINT: tmp = le16_to_cpu(ctrl.wIndex) & 0x0f; /* active endpoint */ if (tmp > 3 || (!dev->ep[tmp].desc && tmp != 0)) goto stall; if (ctrl.wIndex & __constant_cpu_to_le16( USB_DIR_IN)) { if (!dev->ep[tmp].is_in) goto stall; } else { if (dev->ep[tmp].is_in) goto stall; } if (ctrl.wValue != __constant_cpu_to_le16( USB_ENDPOINT_HALT)) goto stall; if (tmp) goku_clear_halt(&dev->ep[tmp]);succeed: /* start ep0out status stage */ writel(~(1<<0), ®s->EOP); dev->ep[0].stopped = 1; dev->ep0state = EP0_STATUS; return; case USB_RECIP_DEVICE: /* device remote wakeup: always clear */ if (ctrl.wValue != __constant_cpu_to_le16(1)) goto stall; VDBG(dev, "clear dev remote wakeup\n"); goto succeed; case USB_RECIP_INTERFACE: goto stall; default: /* pass to gadget driver */ break; } break; default: break; } }#ifdef USB_TRACE VDBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", ctrl.bRequestType, ctrl.bRequest, le16_to_cpu(ctrl.wValue), le16_to_cpu(ctrl.wIndex), le16_to_cpu(ctrl.wLength));#endif /* hw wants to know when we're configured (or not) */ dev->req_config = (ctrl.bRequest == USB_REQ_SET_CONFIGURATION && ctrl.bRequestType == USB_RECIP_DEVICE); if (unlikely(dev->req_config)) dev->configured = (ctrl.wValue != __constant_cpu_to_le16(0)); /* delegate everything to the gadget driver. * it may respond after this irq handler returns. */ spin_unlock (&dev->lock); tmp = dev->driver->setup(&dev->gadget, &ctrl); spin_lock (&dev->lock); if (unlikely(tmp < 0)) {stall:#ifdef USB_TRACE VDBG(dev, "req %02x.%02x protocol STALL; err %d\n", ctrl.bRequestType, ctrl.bRequest, tmp);#endif command(regs, COMMAND_STALL, 0); dev->ep[0].stopped = 1; dev->ep0state = EP0_STALL; } /* expect at least one data or status stage irq */}#define ACK(irqbit) { \ stat &= ~irqbit; \ writel(~irqbit, ®s->int_status); \ handled = 1; \ }static irqreturn_t goku_irq(int irq, void *_dev, struct pt_regs *r){ struct goku_udc *dev = _dev; struct goku_udc_regs __iomem *regs = dev->regs; struct goku_ep *ep; u32 stat, handled = 0; unsigned i, rescans = 5; spin_lock(&dev->lock);rescan: stat = readl(®s->int_status) & dev->int_enable; if (!stat) goto done; dev->irqs++; /* device-wide irqs */ if (unlikely(stat & INT_DEVWIDE)) { if (stat & INT_SYSERROR) { ERROR(dev, "system error\n"); stop_activity(dev, dev->driver); stat = 0; handled = 1; // FIXME have a neater way to prevent re-enumeration dev->driver = NULL; goto done; } if (stat & INT_PWRDETECT) { writel(~stat, ®s->int_status); if (readl(&dev->regs->power_detect) & PW_DETECT) { VDBG(dev, "connect\n"); ep0_start(dev); } else { DBG(dev, "disconnect\n"); if (dev->gadget.speed == USB_SPEED_FULL) stop_activity(dev, dev->driver); dev->ep0state = EP0_DISCONNECT; dev->int_enable = INT_DEVWIDE; writel(dev->int_enable, &dev->regs->int_enable); } stat = 0; handled = 1; goto done; } if (stat & INT_SUSPEND) { ACK(INT_SUSPEND); if (readl(®s->ep_status[0]) & EPxSTATUS_SUSPEND) { switch (dev->ep0state) { case EP0_DISCONNECT: case EP0_SUSPEND: goto pm_next; default: break; } DBG(dev, "USB suspend\n"); dev->ep0state = EP0_SUSPEND; if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->suspend) { spin_unlock(&dev->lock); dev->driver->suspend(&dev->gadget); spin_lock(&dev->lock); } } else { if (dev->ep0state != EP0_SUSPEND) { DBG(dev, "bogus USB resume %d\n", dev->ep0state); goto pm_next; } DBG(dev, "USB resume\n"); dev->ep0state = EP0_IDLE; if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume) { spin_unlock(&dev->lock); dev->driver->resume(&dev->gadget); spin_lock(&dev->lock); } } }pm_next: if (stat & INT_USBRESET) { /* hub reset done */ ACK(INT_USBRESET); INFO(dev, "USB reset done, gadget %s\n", dev->driver->driver.name); } // and INT_ERR on some endpoint's crc/bitstuff/... problem } /* progress ep0 setup, data, or status stages. * no transition {EP0_STATUS, EP0_STALL} --> EP0_IDLE; saves irqs */ if (stat & INT_SETUP) { ACK(INT_SETUP); dev->ep[0].irqs++; ep0_setup(dev); } if (stat & INT_STATUSNAK) { ACK(INT_STATUSNAK|INT_ENDPOINT0); if (dev->ep0state == EP0_IN) { ep = &dev->ep[0]; ep->irqs++; nuke(ep, 0); writel(~(1<<0), ®s->EOP); dev->ep0state = EP0_STATUS; } } if (stat & INT_ENDPOINT0) { ACK(INT_ENDPOINT0); ep = &dev->ep[0]; ep->irqs++; pio_advance(ep); } /* dma completion */ if (stat & INT_MSTRDEND) { /* IN */ ACK(INT_MSTRDEND); ep = &dev->ep[UDC_MSTRD_ENDPOINT]; ep->irqs++; dma_advance(dev, ep); } if (stat & INT_MSTWREND) { /* OUT */ ACK(INT_MSTWREND); ep = &dev->ep[UDC_MSTWR_ENDPOINT]; ep->irqs++; dma_advance(dev, ep); } if (stat & INT_MSTWRTMOUT) { /* OUT */ ACK(INT_MSTWRTMOUT); ep = &dev->ep[UDC_MSTWR_ENDPOINT]; ep->irqs++; ERROR(dev, "%s write timeout ?\n", ep->ep.name); // reset dma? then dma_advance() } /* pio */ for (i = 1; i < 4; i++) { u32 tmp = INT_EPxDATASET(i); if (!(stat & tmp)) continue; ep = &dev->ep[i]; pio_advance(ep); if (list_empty (&ep->queue)) pio_irq_disable(dev, regs, i); stat &= ~tmp; handled = 1; ep->irqs++; } if (rescans--) goto rescan;done: (void)readl(®s->int_enable); spin_unlock(&dev->lock); if (stat) DBG(dev, "unhandled irq status: %05x (%05x, %05x)\n", stat, readl(®s->int_status), dev->int_enable); return IRQ_RETVAL(handled);}#undef ACK/*-------------------------------------------------------------------------*/static void gadget_release(struct device *_dev){ struct goku_udc *dev = dev_get_drvdata(_dev); kfree(dev);}/* tear down the binding between this driver and the pci device */static void goku_remove(struct pci_dev *pdev){ struct goku_udc *dev = pci_get_drvdata(pdev); DBG(dev, "%s\n", __FUNCTION__); /* start with the driver above us */ if (dev->driver) { /* should have been done already by driver model core */ WARN(dev, "pci remove, driver '%s' is still registered\n", dev->driver->driver.name); usb_gadget_unregister_driver(dev->driver); }#ifdef CONFIG_USB_GADGET_DEBUG_FILES remove_proc_entry(proc_node_name, NULL);#endif if (dev->regs) udc_reset(dev); if (dev->got_irq) free_irq(pdev->irq, dev); if (dev->regs) iounmap(dev->regs); if (dev->got_region) release_mem_region(pci_resource_start (pdev, 0), pci_resource_len (pdev, 0)); if (dev->enabled) pci_disable_device(pdev); device_unregister(&dev->gadget.dev); pci_set_drvdata(pdev, NULL); dev->regs = NULL; the_controller = NULL; INFO(dev, "unbind\n");}/* wrap this driver around the specified pci device, but * don't respond over USB until a gadget driver binds to us. */static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id){ struct goku_udc *dev = NULL; unsigned long resource, len; void __iomem *base = NULL; int retval; char buf [8], *bufp; /* if you want to support more than one controller in a system, * usb_gadget_driver_{register,unregister}() must change. */ if (the_controller) { WARN(dev, "ignoring %s\n", pci_name(pdev)); return -EBUSY; } if (!pdev->irq) { printk(KERN_ERR "Check PCI %s IRQ setup!\n", pci_name(pdev)); retval = -ENODEV; goto done; } /* alloc, and start init */ dev = kmalloc (sizeof *dev, SLAB_KERNEL); if (dev == NULL){ pr_debug("enomem %s\n", pci_name(pdev)); retval = -ENOMEM; goto done; } memset(dev, 0, sizeof *dev); spin_lock_init(&dev->lock); dev->pdev = pdev; dev->gadget.ops = &goku_ops; /* the "gadget" abstracts/virtualizes the controller */ strcpy(dev->gadget.dev.bus_id, "gadget"); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; /* now all the pci goodies ... */ retval = pci_enable_device(pdev); if (retval < 0) { DBG(dev, "can't enable, %d\n", retval); goto done; } dev->enabled = 1; resource = pci_resource_start(pdev, 0); len = pci_resource_len(pdev, 0); if (!request_mem_region(resource, len, driver_name)) { DBG(dev, "controller already in use\n"); retval = -EBUSY; goto done; } dev->got_region = 1; base = ioremap_nocache(resource, len); if (base == NULL) { DBG(dev, "can't map memory\n"); retval = -EFAULT; goto done; } dev->regs = (struct goku_udc_regs __iomem *) base; pci_set_drvdata(pdev, dev); INFO(dev, "%s\n", driver_desc); INFO(dev, "version: " DRIVER_VERSION " %s\n", dmastr());#ifndef __sparc__ scnprintf(buf, sizeof buf, "%d", pdev->irq); bufp = buf;#else bufp = __irq_itoa(pdev->irq);#endif INFO(dev, "irq %s, pci mem %p\n", bufp, base); /* init to known state, then setup irqs */ udc_reset(dev); udc_reinit (dev); if (request_irq(pdev->irq, goku_irq, SA_SHIRQ/*|SA_SAMPLE_RANDOM*/, driver_name, dev) != 0) { DBG(dev, "request interrupt %s failed\n", bufp); retval = -EBUSY; goto done; } dev->got_irq = 1; if (use_dma) pci_set_master(pdev);#ifdef CONFIG_USB_GADGET_DEBUG_FILES create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev);#endif /* done */ the_controller = dev; device_register(&dev->gadget.dev); return 0;done: if (dev) goku_remove (pdev); return retval;}/*-------------------------------------------------------------------------*/static struct pci_device_id pci_ids [] = { { .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), .class_mask = ~0, .vendor = 0x102f, /* Toshiba */ .device = 0x0107, /* this UDC */ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID,}, { /* end: all zeroes */ }};MODULE_DEVICE_TABLE (pci, pci_ids);static struct pci_driver goku_pci_driver = { .name = (char *) driver_name, .id_table = pci_ids, .probe = goku_probe, .remove = goku_remove, /* FIXME add power management support */};static int __init init (void){ return pci_register_driver (&goku_pci_driver);}module_init (init);static void __exit cleanup (void){ pci_unregister_driver (&goku_pci_driver);}module_exit (cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -