📄 s3c2410_udc.c
字号:
len = read_fifo_crq(&crq); if (len != sizeof(crq)) { dprintk("setup begin: fifo READ ERROR" " wanted %d bytes got %d. Stalling out...\n", sizeof(crq), len); set_ep0_ss; return; }/* nuke (ep, -EPROTO);*/ /* cope with automagic for some standard requests. */ dev->req_std = (crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD; dev->req_config = 0; dev->req_pending = 1; switch (crq.bRequest) { /* hardware restricts gadget drivers here! */ case USB_REQ_SET_CONFIGURATION: dprintk("USB_REQ_SET_CONFIGURATION ... \n"); if (crq.bRequestType == USB_RECIP_DEVICE) { /* reflect hardware's automagic * up to the gadget driver. */config_change: dev->req_config = 1; clear_ep_state(dev); /* if !has_cfr, there's no synch * else use AREN (later) not SA|OPR * USIR0_IR0 acts edge sensitive */ } break; /* ... and here, even more ... */ case USB_REQ_SET_INTERFACE: dprintk("USB_REQ_SET_INTERFACE ... \n"); if (crq.bRequestType == USB_RECIP_INTERFACE) { /* udc hardware is broken by design: * - altsetting may only be zero; * - hw resets all interfaces' eps; * - ep reset doesn't include halt(?). */ goto config_change; } break; /* hardware was supposed to hide this */ case USB_REQ_SET_ADDRESS: dprintk("USB_REQ_SET_ADDRESS ... \n"); tmp = crq.wValue & 0x7F; __raw_writel((tmp | 0x80), S3C2410_UDC_FUNC_ADDR_REG); // usbd_info.address = address; // usbctl_next_state_on_event( kEvAddress ); out_pkt_ack(1); if (crq.bRequestType == USB_RECIP_DEVICE) { // ep0start(dev, 0, "address"); return; } break; } if (crq.bRequestType & USB_DIR_IN) dev->ep0state = EP0_IN_DATA_PHASE; else dev->ep0state = EP0_OUT_DATA_PHASE; ret = dev->driver->setup(&dev->gadget, &crq); if (ret < 0) { /* hardware automagic preventing STALL... */ if (dev->req_config) { /* hardware sometimes neglects to tell * tell us about config change events, * so later ones may fail... */ dprintk("config change %02x fail %d?\n", crq.bRequest, ret); return; /* TODO experiment: if has_cfr, * hardware didn't ACK; maybe we * could actually STALL! */ }// stall: /* the watchdog timer helps deal with cases * where udc seems to clear FST wrongly, and * then NAKs instead of STALLing. */ // ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); // start_watchdog(dev); dev->ep0state = EP0_STALL; /* deferred i/o == no response yet */ } else if (dev->req_pending) { dprintk("dev->req_pending... what now?\n");#if 0 if (likely(dev->ep0state == EP0_IN_DATA_PHASE || dev->req_std || crq.wLength)) ep0start(dev, 0, "defer"); else ep0start(dev, UDCCS0_IPR, "defer/IPR");#endif } /* expect at least one data or status stage irq */ return; } break; case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ dprintk("EP0_IN_DATA_PHASE ... what now?\n");#if 0 if (udccs0 & UDCCS0_OPR) { UDCCS0 = UDCCS0_OPR|UDCCS0_FTF; if (req) done(ep, req, 0); ep0_idle(dev); } else /* irq was IPR clearing */ { if (req) { /* this IN packet might finish the request */ (void) write_ep0_fifo(ep, req); } /* else IN token before response was written */ }#endif break; case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ dprintk("EP0_OUT_DATA_PHASE ... what now?\n");#if 0 if (udccs0 & UDCCS0_OPR) { if (req) { /* this OUT packet might finish the request */ if (read_ep0_fifo(ep, req)) done(ep, req, 0); /* else more OUT packets expected */ } /* else OUT token before read was issued */ } else /* irq was IPR clearing */ { if (req) done(ep, req, 0); ep0_idle(dev); }#endif break; case EP0_END_XFER: dprintk("EP0_END_XFER ... what now?\n");#if 0 if (req) done(ep, req, 0); /* ack control-IN status (maybe in-zlp was skipped) * also appears after some config change events. */ if (udccs0 & UDCCS0_OPR) UDCCS0 = UDCCS0_OPR; ep0_idle(dev);#endif break; case EP0_STALL: set_ep0_ss; break; }// USIR0 = USIR0_IR0;}#if 0static void handle_ep(struct s3c2410_ep *ep){ struct s3c2410_request *req; int is_in = ep->bEndpointAddress & USB_DIR_IN; int completed; u32 udccs, tmp; do { completed = 0; if (likely (!list_empty(&ep->queue))) req = list_entry(ep->queue.next, struct s3c2410_request, queue); else req = 0; // TODO check FST handling udccs = *ep->reg_udccs; if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */ tmp = UDCCS_BI_TUR; if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) tmp |= UDCCS_BI_SST; tmp &= udccs; if (likely (tmp)) *ep->reg_udccs = tmp; if (req && likely ((udccs & UDCCS_BI_TFS) != 0)) completed = write_fifo(ep, req); } else { /* irq from RPC (or for ISO, ROF) */ if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) tmp = UDCCS_BO_SST | UDCCS_BO_DME; else tmp = UDCCS_IO_ROF | UDCCS_IO_DME; tmp &= udccs; if (likely(tmp)) *ep->reg_udccs = tmp; /* fifos can hold packets, ready for reading... */ if (likely(req)) { completed = read_fifo(ep, req); } else pio_irq_disable (ep->bEndpointAddress); } ep->pio_irqs++; } while (completed);}#endif/* * 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("irq: %d ...\n", irq); 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("check usbs=0x%02x, usbds=0x%02x\n", usb_status, usbd_status); /* ReSeT Interrupt Request - USB reset */ if (usb_status & S3C2410_UDC_USBINT_RESET) { dprintk("USB reset\n"); // if( usbctl_next_state_on_event(kEvReset) != kError ) /* 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; } /* EP interrupts */ if (usbd_status) { int i; /* control traffic */ if (usbd_status & S3C2410_UDC_INT_EP0) { // dev->ep[0].pio_irqs++; dprintk("USB ep0 irq\n"); handle_ep0(dev); // ep0_int_hndlr(); /* Clear the interrupt bit by setting it to 1 */ __raw_writel(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG); handled = 1; } /* endpoint data transfers */ for (i = 1; i < 5; i++) { u32 tmp = 1 << i; if (usbd_status & tmp) { dprintk("USB ep%d irq\n", i); // handle_ep(&dev->ep[i]); /* Clear the interrupt bit by setting it to 1 */ __raw_writel(tmp, S3C2410_UDC_EP_INT_REG); handled = 1; } } } // UD_INDEX= saveIdx; /* restore idx */ } while (handled); dprintk("irq: %d done.\n\n", irq); 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); 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_LICENSE ("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -