📄 s3c2410_udc.c
字号:
udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); return 0;}static const struct usb_gadget_ops s3c2410_ops = { .get_frame = s3c2410_g_get_frame, .wakeup = s3c2410_wakeup, .set_selfpowered = s3c2410_set_selfpowered,};/*-------------------------------------------------------------------------*//* "function" sysfs attribute */static ssize_tshow_function (struct device *_dev, char *buf){ struct s3c2410_udc *udc = the_controller; if (!udc->driver->function || strlen (udc->driver->function) > PAGE_SIZE) return 0; return snprintf (buf, PAGE_SIZE, "%s\n", udc->driver->function);}DEVICE_ATTR (function, S_IRUGO, show_function, NULL);/*-------------------------------------------------------------------------*//* * Driver registration/unregistration. * * This is basically hardware-specific; there's usually only one real USB * device (not host) controller since that's how USB devices are intended * to work. So most implementations of these api calls will rely on the * fact that only one driver will ever bind to the hardware. But curious * hardware can be built with discrete components, so the gadget API doesn't * require that assumption. */#if 0static voids3c2410_udc_release (struct device *dev){ struct s3c2410_udc *udc = gadget_dev_to_udc (dev); complete (&udc->released);}static ints3c2410_register_udc (struct s3c2410_udc *udc){ int rc; strcpy (udc->gadget.dev.bus_id, "udc"); udc->gadget.dev.parent = &udc->pdev.dev; udc->gadget.dev.release = s3c2410_udc_release; rc = device_register (&udc->gadget.dev); if (rc == 0) device_create_file (&udc->gadget.dev, &dev_attr_function); return rc;}static voids3c2410_unregister_udc (struct s3c2410_udc *udc){ device_remove_file (&udc->gadget.dev, &dev_attr_function); init_completion (&udc->released); device_unregister (&udc->gadget.dev); wait_for_completion (&udc->released);}#endifintusb_gadget_register_driver (struct usb_gadget_driver *driver){ struct s3c2410_udc *udc = the_controller; int retval, i; if (!udc) return -EINVAL; if (udc->driver) return -EBUSY; if (!driver->bind || !driver->unbind || !driver->setup || driver->speed == USB_SPEED_UNKNOWN) return -EINVAL; /* * SLAVE side init ... the layer above hardware, which * can't enumerate without help from the driver we're binding. */ udc->gadget.name = gadget_name; udc->gadget.ops = &s3c2410_ops; udc->gadget.is_dualspeed = 1; udc->devstatus = 0; INIT_LIST_HEAD (&udc->gadget.ep_list); for (i = 0; i < S3C2410_ENDPOINTS; i++) { struct s3c2410_ep *ep = &udc->ep [i]; if (!ep_name [i]) break; ep->ep.name = ep_name [i]; ep->ep.ops = &s3c2410_ep_ops; list_add_tail (&ep->ep.ep_list, &udc->gadget.ep_list); ep->halted = ep->already_seen = ep->setup_stage = 0; ep->ep.maxpacket = ~0; ep->last_io = jiffies; ep->gadget = &udc->gadget; ep->desc = 0; INIT_LIST_HEAD (&ep->queue); } udc->gadget.ep0 = &udc->ep[0].ep; udc->ep[0].ep.maxpacket = 64; list_del_init (&udc->ep[0].ep.ep_list); INIT_LIST_HEAD(&udc->fifo_req.queue); udc->driver = driver; udc->gadget.dev.driver = &driver->driver; dprintk( "binding gadget driver '%s'\n", driver->driver.name); if ((retval = driver->bind (&udc->gadget)) != 0) { udc->driver = 0; udc->gadget.dev.driver = 0; return retval; } // FIXME: Check these calls for errors and re-order// driver->driver.bus = udc->pdev.dev.bus; driver_register (&driver->driver); device_bind_driver (&udc->gadget.dev); /* khubd will enumerate this in a while */ udc->port_status |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION); return 0;}EXPORT_SYMBOL (usb_gadget_register_driver);/* caller must hold lock */static voidstop_activity (struct s3c2410_udc *udc, struct usb_gadget_driver *driver){ struct s3c2410_ep *ep; /* prevent any more requests */ udc->address = 0; /* The timer is left running so that outstanding URBs can fail */ /* nuke any pending requests first, so driver i/o is quiesced */ list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) nuke (udc, ep); /* driver now does any non-usb quiescing necessary */ if (driver) { spin_unlock (&udc->lock); driver->disconnect (&udc->gadget); spin_lock (&udc->lock); }}intusb_gadget_unregister_driver (struct usb_gadget_driver *driver){ struct s3c2410_udc *udc = the_controller; unsigned long flags; if (!udc) return -ENODEV; if (!driver || driver != udc->driver) return -EINVAL; dprintk( "unregister gadget driver '%s'\n", driver->driver.name); spin_lock_irqsave (&udc->lock, flags); stop_activity (udc, driver); udc->port_status &= ~USB_PORT_STAT_CONNECTION; udc->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); spin_unlock_irqrestore (&udc->lock, flags); driver->unbind (&udc->gadget); udc->driver = 0; device_release_driver (&udc->gadget.dev); driver_unregister (&driver->driver); return 0;}EXPORT_SYMBOL (usb_gadget_unregister_driver);#undef is_enabledint s3c2410_set_fifo_mode (struct usb_gadget *gadget, int mode){ return -ENOSYS;}EXPORT_SYMBOL (s3c2410_set_fifo_mode);/*-------------------------------------------------------------------------*//*-------------------------------------------------------------------------*/static void nop_release (struct device *dev){ dprintk("%s %s\n", __FUNCTION__, dev->bus_id);}#define EP0_FIFO_SIZE 16#define BULK_FIFO_SIZE 64/* this uses load-time allocation and initialization (instead of * doing it at run-time) to save code, eliminate fault paths, and * be more obviously correct. */static struct s3c2410_udc memory = { .gadget = { .ops = &s3c2410_ops, .ep0 = &memory.ep[0].ep, .name = gadget_name, .dev = { .bus_id = "gadget", .release = nop_release, }, }, /* control endpoint */ .ep[0] = { .ep = { .name = ep0name, .ops = &s3c2410_ep_ops, .maxpacket = EP0_FIFO_SIZE, }, .dev = &memory,// .reg_udccs = &UDCCS0,// .reg_uddr = &UDDR0, }, /* group of endpoints */ .ep[1] = { .ep = { .name = "ep1-bulk", .ops = &s3c2410_ep_ops, .maxpacket = BULK_FIFO_SIZE, }, .dev = &memory, .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = 1, .bmAttributes = USB_ENDPOINT_XFER_BULK,// .reg_udccs = &UDCCS1,// .reg_uddr = &UDDR1,// drcmr (25) }, .ep[2] = { .ep = { .name = "ep2-bulk", .ops = &s3c2410_ep_ops, .maxpacket = BULK_FIFO_SIZE, }, .dev = &memory, .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = 2, .bmAttributes = USB_ENDPOINT_XFER_BULK,// .reg_udccs = &UDCCS2,// .reg_ubcr = &UBCR2,// .reg_uddr = &UDDR2,// drcmr (26) }, .ep[3] = { .ep = { .name = "ep3-bulk", .ops = &s3c2410_ep_ops, .maxpacket = BULK_FIFO_SIZE, }, .dev = &memory, .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = 3, .bmAttributes = USB_ENDPOINT_XFER_BULK,// .reg_udccs = &UDCCS3,// .reg_uddr = &UDDR3,// drcmr (27) },};/* * s3c2410_udc_irq - interrupt handler * * avoid delays in ep0 processing. the control handshaking isn't always * under software control, and delays could cause usb protocol errors. */static irqreturn_ts3c2410_udc_irq(int irq, void *_dev, struct pt_regs *r){ struct s3c2410_udc *dev = _dev; static int sb_debug_cnt = 1; int handled; dprintk("s3c2410_udc_irq\n"); do { int saveIdx = __raw_readl(S3C2410_UDC_INDEX_REG); int usb_status = __raw_readl(S3C2410_UDC_USB_INT_REG); int usbd_status = __raw_readl(S3C2410_UDC_EP_INT_REG); handled = 0; dprintk("usb_status = 0x%02x, usbd_status = 0x%02x\n", usb_status, usbd_status); /* ReSeT Interrupt Request - USB reset */ if (usb_status & S3C2410_UDC_USBINT_RESET) { dprintk("USB reset\n"); /* clear interrupt */ __raw_writel(S3C2410_UDC_USBINT_RESET, S3C2410_UDC_USB_INT_REG); dev->gadget.speed = USB_SPEED_FULL; // memset(&dev->stats, 0, sizeof dev->stats); /* driver and endpoints are still reset */ // enable_disconnect_irq(); handled = 1; } /* RESume Interrupt Request */ if (usb_status & S3C2410_UDC_USBINT_RESUME) { dprintk("USB resume\n"); /* clear interrupt */ __raw_writel(S3C2410_UDC_USBINT_RESUME, S3C2410_UDC_USB_INT_REG); if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume /* && is_usb_connected() */) dev->driver->resume(&dev->gadget); handled = 1; } /* SUSpend Interrupt Request */ if (usb_status & S3C2410_UDC_USBINT_SUSPEND) { dprintk("USB suspend\n"); /* clear interrupt */ __raw_writel(S3C2410_UDC_USBINT_SUSPEND, S3C2410_UDC_USB_INT_REG); // if (!is_usb_connected()) stop_activity(dev, dev->driver); /* else */ if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->suspend) dev->driver->suspend(&dev->gadget); // ep0_idle (dev); handled = 1; } // UD_INDEX= saveIdx; /* restore idx */ } while (handled); return IRQ_HANDLED;}/* * probe - binds to the platform device */static int __init s3c2410_udc_probe(struct device *_dev){ struct s3c2410_udc *udc = &memory; int retval, out_dma = 0; dprintk("s3c2410_udc_probe\n"); /* other non-static parts of init */// udc->dev = _dev;// udc->mach = _dev->platform_data;/* init_timer(&dev->timer); dev->timer.function = udc_watchdog; dev->timer.data = (unsigned long) dev;*/ device_initialize(&udc->gadget.dev); udc->gadget.dev.parent = _dev; udc->gadget.dev.dma_mask = _dev->dma_mask; the_controller = udc; dev_set_drvdata(_dev, udc); // udc_disable(udc); // udc_reinit(udc); /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USBD, s3c2410_udc_irq, SA_INTERRUPT, gadget_name, udc); if (retval != 0) { printk(KERN_ERR "%s: can't get irq %i, err %d\n", gadget_name, IRQ_USBD, retval); return -EBUSY; } dprintk("%s: got irq %i\n", gadget_name, IRQ_USBD); udc->got_irq = 1;// create_proc_files(); return 0;}static int __exit s3c2410_udc_remove(struct device *_dev){ struct s3c2410_udc *udc = _dev->driver_data; dprintk("s3c2410_udc_remove\n"); udc_disable(udc);// remove_proc_files(); usb_gadget_unregister_driver(udc->driver); if (udc->got_irq) { free_irq(IRQ_USBD, udc); udc->got_irq = 0; } dev_set_drvdata(_dev, 0); the_controller = 0; return 0;}/*-------------------------------------------------------------------------*/static struct device_driver udc_driver = { .name = "s3c2410-udc", .bus = &platform_bus_type, .probe = s3c2410_udc_probe, .remove = __exit_p(s3c2410_udc_remove), // FIXME power management support // .suspend = ... disable UDC // .resume = ... re-enable UDC};static int __init udc_init(void){ int tmp; dprintk("%s: version %s\n", gadget_name, DRIVER_VERSION); tmp = __raw_readl(S3C2410_CLKCON); tmp &= ~S3C2410_CLKCON_USBD; __raw_writel(tmp, S3C2410_CLKCON); tmp = __raw_readl(S3C2410_MISCCR); tmp &= ~S3C2410_MISCCR_USBHOST; __raw_writel(tmp, S3C2410_MISCCR); /* UPLLCON */ tmp = (0x78 << S3C2410_PLLCON_MDIVSHIFT) | (0x02 << S3C2410_PLLCON_PDIVSHIFT) | (0x03 << S3C2410_PLLCON_SDIVSHIFT); __raw_writel(tmp, S3C2410_UPLLCON); tmp = __raw_readl(S3C2410_CLKCON); tmp |= S3C2410_CLKCON_USBD; __raw_writel(tmp, S3C2410_CLKCON);#ifdef MACH_TOMTOMGO s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_OUTP); s3c2410_gpio_setpin(S3C2410_GPE13, 1); dev_info (&dev->dev, "TomTomGo USB device pullup enabled.\n");#endif mdelay(10); return driver_register(&udc_driver);}module_init(udc_init);static void __exit udc_exit(void){ driver_unregister(&udc_driver);}module_exit(udc_exit);MODULE_DESCRIPTION (DRIVER_DESC);MODULE_AUTHOR ("Herbert P鰐zl");MODULE_LICENSE ("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -