📄 pxa2xx_udc.c
字号:
dev->gadget.dev.driver = &driver->driver; device_add (&dev->gadget.dev); retval = driver->bind(&dev->gadget); if (retval) { DMSG("bind to driver %s --> error %d\n", driver->driver.name, retval); device_del (&dev->gadget.dev); dev->driver = 0; dev->gadget.dev.driver = 0; return retval; } device_create_file(dev->dev, &dev_attr_function); /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. * NOTE: this shouldn't power up until later. */ DMSG("registered gadget driver '%s'\n", driver->driver.name); udc_enable(dev); dump_state(dev); return 0;}EXPORT_SYMBOL(usb_gadget_register_driver);static voidstop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver){ int i; /* don't disconnect drivers more than once */ if (dev->gadget.speed == USB_SPEED_UNKNOWN) driver = 0; dev->gadget.speed = USB_SPEED_UNKNOWN; /* prevent new request submissions, kill any outstanding requests */ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { struct pxa2xx_ep *ep = &dev->ep[i]; ep->stopped = 1; nuke(ep, -ESHUTDOWN); } del_timer_sync(&dev->timer); /* report disconnect; the driver is already quiesced */ LED_CONNECTED_OFF; if (driver) driver->disconnect(&dev->gadget); /* re-init driver-visible data structures */ udc_reinit(dev);}int usb_gadget_unregister_driver(struct usb_gadget_driver *driver){ struct pxa2xx_udc *dev = the_controller; if (!dev) return -ENODEV; if (!driver || driver != dev->driver) return -EINVAL; local_irq_disable(); udc_disable(dev); stop_activity(dev, driver); local_irq_enable(); driver->unbind(&dev->gadget); dev->driver = 0; device_del (&dev->gadget.dev); device_remove_file(dev->dev, &dev_attr_function); DMSG("unregistered gadget driver '%s'\n", driver->driver.name); dump_state(dev); return 0;}EXPORT_SYMBOL(usb_gadget_unregister_driver);/*-------------------------------------------------------------------------*/#ifdef CONFIG_ARCH_LUBBOCK#ifndef CONFIG_ARCH_FS_PXA255/* Lubbock can report connect or disconnect irqs. Likely more hardware * could support it as a timer callback. * * FIXME for better power management, keep the hardware powered down * until a host is powering the link. means scheduling work later * in some task that can udc_enable(). */#define enable_disconnect_irq() \ if (machine_is_lubbock()) { enable_irq(LUBBOCK_USB_DISC_IRQ); }#define disable_disconnect_irq() \ if (machine_is_lubbock()) { disable_irq(LUBBOCK_USB_DISC_IRQ); }static irqreturn_tusb_connection_irq(int irq, void *_dev, struct pt_regs *r){ struct pxa2xx_udc *dev = _dev; dev->stats.irqs++; HEX_DISPLAY(dev->stats.irqs); if (!is_usb_connected()) { LED_CONNECTED_OFF; disable_disconnect_irq(); /* report disconnect just once */ if (dev->gadget.speed != USB_SPEED_UNKNOWN) { DMSG("disconnect %s\n", dev->driver ? dev->driver->driver.name : 0); stop_activity(dev, dev->driver); // udc_disable (dev); // no more udc irqs // maybe "ACTION=disconnect /sbin/hotplug gadget". } } else if (dev->gadget.speed == USB_SPEED_UNKNOWN) { LED_CONNECTED_ON; DMSG("?? connect irq ??\n"); // if there's no driver bound, ignore; else // udc_enable (dev); // UDC irqs drive the rest. // maybe "ACTION=connect /sbin/hotplug gadget". } return IRQ_HANDLED;}#endif#endif#ifndef enable_disconnect_irq#warning USB disconnect() is not yet reported.#define enable_disconnect_irq() do {} while (0)#define disable_disconnect_irq() do {} while (0)#endif/*-------------------------------------------------------------------------*/static inline void clear_ep_state (struct pxa2xx_udc *dev){ unsigned i; /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint * fifos, and pending transactions mustn't be continued in any case. */ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) nuke(&dev->ep[i], -ECONNABORTED);}static void udc_watchdog(unsigned long _dev){ struct pxa2xx_udc *dev = (void *)_dev; local_irq_disable(); if (dev->ep0state == EP0_STALL && (UDCCS0 & UDCCS0_FST) == 0 && (UDCCS0 & UDCCS0_SST) == 0) { UDCCS0 = UDCCS0_FST|UDCCS0_FTF; DBG(DBG_VERBOSE, "ep0 re-stall\n"); start_watchdog(dev); } local_irq_enable();}static void handle_ep0 (struct pxa2xx_udc *dev){ u32 udccs0 = UDCCS0; struct pxa2xx_ep *ep = &dev->ep [0]; struct pxa2xx_request *req; union { struct usb_ctrlrequest r; u8 raw [8]; u32 word [2]; } u; if (list_empty(&ep->queue)) req = 0; else req = list_entry(ep->queue.next, struct pxa2xx_request, queue); /* clear stall status */ if (udccs0 & UDCCS0_SST) { nuke(ep, -EPIPE); UDCCS0 = UDCCS0_SST; del_timer(&dev->timer); ep0_idle(dev); } /* previous request unfinished? non-error iff back-to-back ... */ if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { nuke(ep, 0); del_timer(&dev->timer); ep0_idle(dev); } switch (dev->ep0state) { case EP0_IDLE: /* late-breaking status? */ udccs0 = UDCCS0; /* start control request? */ if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) { int i; nuke (ep, -EPROTO); /* read SETUP packet */ for (i = 0; i < 8; i++) { if (unlikely(!(UDCCS0 & UDCCS0_RNE))) {bad_setup: DMSG("SETUP %d!\n", i); goto stall; } u.raw [i] = (u8) UDDR0; } if (unlikely((UDCCS0 & UDCCS0_RNE) != 0)) goto bad_setup;got_setup: le16_to_cpus (&u.r.wValue); le16_to_cpus (&u.r.wIndex); le16_to_cpus (&u.r.wLength); LED_EP0_ON; DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", u.r.bRequestType, u.r.bRequest, u.r.wValue, u.r.wIndex, u.r.wLength); /* cope with automagic for some standard requests. */ dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD; dev->req_config = 0; dev->req_pending = 1; switch (u.r.bRequest) { /* hardware restricts gadget drivers here! */ case USB_REQ_SET_CONFIGURATION: if (u.r.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: if (u.r.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(?). */ DMSG("broken set_interface (%d/%d)\n", u.r.wIndex, u.r.wValue); goto config_change; } break; /* hardware was supposed to hide this */ case USB_REQ_SET_ADDRESS: if (u.r.bRequestType == USB_RECIP_DEVICE) { ep0start(dev, 0, "address"); return; } break; } if (u.r.bRequestType & USB_DIR_IN) dev->ep0state = EP0_IN_DATA_PHASE; else dev->ep0state = EP0_OUT_DATA_PHASE; i = dev->driver->setup(&dev->gadget, &u.r); if (i < 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... */ WARN("config change %02x fail %d?\n", u.r.bRequest, i); return; /* TODO experiment: if has_cfr, * hardware didn't ACK; maybe we * could actually STALL! */ } DBG(DBG_VERBOSE, "protocol STALL, " "%02x err %d\n", UDCCS0, i);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; LED_EP0_OFF; /* deferred i/o == no response yet */ } else if (dev->req_pending) { if (likely(dev->ep0state == EP0_IN_DATA_PHASE || dev->req_std || u.r.wLength)) ep0start(dev, 0, "defer"); else ep0start(dev, UDCCS0_IPR, "defer/IPR"); } /* expect at least one data or status stage irq */ return; } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA)) == (UDCCS0_OPR|UDCCS0_SA))) { unsigned i; /* pxa210/250 erratum 131 for B0/B1 says RNE lies. * still observed on a pxa255 a0. */ DBG(DBG_VERBOSE, "e131\n"); nuke(ep, -EPROTO); /* read SETUP data, but don't trust it too much */ for (i = 0; i < 8; i++) u.raw [i] = (u8) UDDR0; if ((u.r.bRequestType & USB_RECIP_MASK) > USB_RECIP_OTHER) goto stall; if (u.word [0] == 0 && u.word [1] == 0) goto stall; goto got_setup; } else { /* some random early IRQ: * - we acked FST * - IPR cleared * - OPR got set, without SA (likely status stage) */ UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR); } break; case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ if (udccs0 & UDCCS0_OPR) { UDCCS0 = UDCCS0_OPR|UDCCS0_FTF; DBG(DBG_VERBOSE, "ep0in premature status\n"); 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 */ } break; case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ 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 */ { DBG(DBG_VERBOSE, "ep0out premature status\n"); if (req) done(ep, req, 0); ep0_idle(dev); } break; case EP0_END_XFER: 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); break; case EP0_STALL: UDCCS0 = UDCCS0_FST; break; } USIR0 = USIR0_IR0;}static void handle_ep(struct pxa2xx_ep *ep){ struct pxa2xx_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 pxa2xx_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)) {#ifdef USE_OUT_DMA// TODO didn't yet debug out-dma. this approach assumes// the worst about short packets and RPC; it might be better. if (likely(ep->dma >= 0)) { if (!(udccs & UDCCS_BO_RSP)) { *ep->reg_udccs = UDCCS_BO_RPC; ep->dma_irqs++; return; } }#endif completed = read_fifo(ep, req); } else pio_irq_disable (ep->bEndpointAddress); } ep->pio_irqs++; } while (completed);}/* * pxa2xx_udc_irq - interrupt handler * * avoid delays in ep0 processing. the control handshaking isn't always * under software control (pxa250c0 and the pxa255 are better), and delays * could cause usb protocol errors. */static irqreturn_tpxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r){ struct pxa2xx_udc *dev = _dev; int handled; dev->stats.irqs++; HEX_DISPLAY(dev->stats.irqs); do { u32 udccr = UDCCR; handled = 0; /* SUSpend Interrupt Request */ if (unlikely(udccr & UDCCR_SUSIR)) { udc_ack_int_UDCCR(UDCCR_SUSIR); handled = 1; DBG(DBG_VERBOSE, "USB suspend%s\n", is_usb_connected() ? "" : "+disconnect"); 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); } /* RESume Interrupt Request */ if (unlikely(udccr & UDCCR_RESIR)) { udc_ack_int_UDCCR(UDCCR_RESIR); handled = 1; DBG(DBG_VERBOSE, "USB resume\n"); if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume && is_usb_connected()) dev->driver->resume(&dev->gadget); } /* ReSeT Interrupt Request - USB reset */ if (unlikely(udccr & UDCCR_RSTIR)) { udc_ack_int_UDCCR(UDCCR_RSTIR); handled = 1; if ((UDCCR & UDCCR_UDA) == 0) { DBG(DBG_VERBOSE, "USB reset start\n"); if (dev->gadget.speed != USB_SPEED_UNKNOWN) disable_disconnect_irq(); /* reset driver and endpoints, * in case that's not yet done
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -