📄 omap_udc.c
字号:
goto delegate; if (u.r.wLength != 0) goto do_stall; udc->ep0_set_config = 1; udc->ep0_reset_config = (u.r.wValue == 0); VDBG("set config %d\n", u.r.wValue); /* update udc NOW since gadget driver may start * queueing requests immediately; clear config * later if it fails the request. */ if (udc->ep0_reset_config) UDC_SYSCON2_REG = UDC_CLR_CFG; else UDC_SYSCON2_REG = UDC_DEV_CFG; update_otg(udc); goto delegate; case USB_REQ_CLEAR_FEATURE: /* clear endpoint halt */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; if (u.r.wValue != USB_ENDPOINT_HALT || u.r.wLength != 0) goto do_stall; ep = &udc->ep[u.r.wIndex & 0xf]; if (ep != ep0) { if (u.r.wIndex & USB_DIR_IN) ep += 16; if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC || !ep->desc) goto do_stall; use_ep(ep, 0); UDC_CTRL_REG = UDC_RESET_EP; ep->ackwait = 0; if (!(ep->bEndpointAddress & USB_DIR_IN)) { UDC_CTRL_REG = UDC_SET_FIFO_EN; ep->ackwait = 1 + ep->double_buf; } } VDBG("%s halt cleared by host\n", ep->name); goto ep0out_status_stage; case USB_REQ_SET_FEATURE: /* set endpoint halt */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; if (u.r.wValue != USB_ENDPOINT_HALT || u.r.wLength != 0) goto do_stall; ep = &udc->ep[u.r.wIndex & 0xf]; if (u.r.wIndex & USB_DIR_IN) ep += 16; if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC || ep == ep0 || !ep->desc) goto do_stall; if (use_dma && ep->has_dma) { /* this has rude side-effects (aborts) and * can't really work if DMA-IN is active */ DBG("%s host set_halt, NYET \n", ep->name); goto do_stall; } use_ep(ep, 0); /* can't halt if fifo isn't empty... */ UDC_CTRL_REG = UDC_CLR_EP; UDC_CTRL_REG = UDC_SET_HALT; VDBG("%s halted by host\n", ep->name);ep0out_status_stage: status = 0; UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; UDC_CTRL_REG = UDC_CLR_EP; UDC_CTRL_REG = UDC_SET_FIFO_EN; UDC_EP_NUM_REG = UDC_EP_DIR; udc->ep0_pending = 0; break; case USB_REQ_GET_STATUS: /* return interface status. if we were pedantic, * we'd detect non-existent interfaces, and stall. */ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) goto delegate; /* return two zero bytes */ UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; UDC_DATA_REG = 0; UDC_CTRL_REG = UDC_SET_FIFO_EN; UDC_EP_NUM_REG = UDC_EP_DIR; status = 0; VDBG("GET_STATUS, interface %d\n", u.r.wIndex); /* next, status stage */ break; default:delegate: /* activate the ep0out fifo right away */ if (!udc->ep0_in && u.r.wLength) { UDC_EP_NUM_REG = 0; UDC_CTRL_REG = UDC_SET_FIFO_EN; } /* gadget drivers see class/vendor specific requests, * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION}, * and more */ VDBG("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); /* The gadget driver may return an error here, * causing an immediate protocol stall. * * Else it must issue a response, either queueing a * response buffer for the DATA stage, or halting ep0 * (causing a protocol stall, not a real halt). A * zero length buffer means no DATA stage. * * It's fine to issue that response after the setup() * call returns, and this IRQ was handled. */ udc->ep0_setup = 1; spin_unlock(&udc->lock); status = udc->driver->setup (&udc->gadget, &u.r); spin_lock(&udc->lock); udc->ep0_setup = 0; } if (status < 0) {do_stall: VDBG("req %02x.%02x protocol STALL; stat %d\n", u.r.bRequestType, u.r.bRequest, status); if (udc->ep0_set_config) { if (udc->ep0_reset_config) WARN("error resetting config?\n"); else UDC_SYSCON2_REG = UDC_CLR_CFG; } UDC_SYSCON2_REG = UDC_STALL_CMD; udc->ep0_pending = 0; } }}/*-------------------------------------------------------------------------*/#define OTG_FLAGS (UDC_B_HNP_ENABLE|UDC_A_HNP_SUPPORT|UDC_A_ALT_HNP_SUPPORT)static void devstate_irq(struct omap_udc *udc, u16 irq_src){ u16 devstat, change; devstat = UDC_DEVSTAT_REG; change = devstat ^ udc->devstat; udc->devstat = devstat; if (change & (UDC_USB_RESET|UDC_ATT)) { udc_quiesce(udc); if (change & UDC_ATT) { /* driver for any external transceiver will * have called omap_vbus_session() already */ if (devstat & UDC_ATT) { udc->gadget.speed = USB_SPEED_FULL; VDBG("connect\n"); if (!udc->transceiver) pullup_enable(udc); // if (driver->connect) call it } else if (udc->gadget.speed != USB_SPEED_UNKNOWN) { udc->gadget.speed = USB_SPEED_UNKNOWN; if (!udc->transceiver) pullup_disable(udc); DBG("disconnect, gadget %s\n", udc->driver->driver.name); if (udc->driver->disconnect) { spin_unlock(&udc->lock); udc->driver->disconnect(&udc->gadget); spin_lock(&udc->lock); } } change &= ~UDC_ATT; } if (change & UDC_USB_RESET) { if (devstat & UDC_USB_RESET) { VDBG("RESET=1\n"); } else { udc->gadget.speed = USB_SPEED_FULL; INFO("USB reset done, gadget %s\n", udc->driver->driver.name); /* ep0 traffic is legal from now on */ UDC_IRQ_EN_REG = UDC_DS_CHG_IE | UDC_EP0_IE; } change &= ~UDC_USB_RESET; } } if (change & UDC_SUS) { if (udc->gadget.speed != USB_SPEED_UNKNOWN) { // FIXME tell isp1301 to suspend/resume (?) if (devstat & UDC_SUS) { VDBG("suspend\n"); update_otg(udc); /* HNP could be under way already */ if (udc->gadget.speed == USB_SPEED_FULL && udc->driver->suspend) { spin_unlock(&udc->lock); udc->driver->suspend(&udc->gadget); spin_lock(&udc->lock); } } else { VDBG("resume\n"); if (udc->gadget.speed == USB_SPEED_FULL && udc->driver->resume) { spin_unlock(&udc->lock); udc->driver->resume(&udc->gadget); spin_lock(&udc->lock); } } } change &= ~UDC_SUS; } if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) { update_otg(udc); change &= ~OTG_FLAGS; } change &= ~(UDC_CFG|UDC_DEF|UDC_ADD); if (change) VDBG("devstat %03x, ignore change %03x\n", devstat, change); UDC_IRQ_SRC_REG = UDC_DS_CHG;}static irqreturn_tomap_udc_irq(int irq, void *_udc, struct pt_regs *r){ struct omap_udc *udc = _udc; u16 irq_src; irqreturn_t status = IRQ_NONE; unsigned long flags; spin_lock_irqsave(&udc->lock, flags); irq_src = UDC_IRQ_SRC_REG; /* Device state change (usb ch9 stuff) */ if (irq_src & UDC_DS_CHG) { devstate_irq(_udc, irq_src); status = IRQ_HANDLED; irq_src &= ~UDC_DS_CHG; } /* EP0 control transfers */ if (irq_src & (UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX)) { ep0_irq(_udc, irq_src); status = IRQ_HANDLED; irq_src &= ~(UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX); } /* DMA transfer completion */ if (use_dma && (irq_src & (UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT))) { dma_irq(_udc, irq_src); status = IRQ_HANDLED; irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT); } irq_src &= ~(UDC_SOF|UDC_EPN_TX|UDC_EPN_RX); if (irq_src) DBG("udc_irq, unhandled %03x\n", irq_src); spin_unlock_irqrestore(&udc->lock, flags); return status;}/* workaround for seemingly-lost IRQs for RX ACKs... */#define PIO_OUT_TIMEOUT (jiffies + HZ/3)#define HALF_FULL(f) (!((f)&(UDC_NON_ISO_FIFO_FULL|UDC_NON_ISO_FIFO_EMPTY)))static void pio_out_timer(unsigned long _ep){ struct omap_ep *ep = (void *) _ep; unsigned long flags; u16 stat_flg; spin_lock_irqsave(&ep->udc->lock, flags); if (!list_empty(&ep->queue) && ep->ackwait) { use_ep(ep, 0); stat_flg = UDC_STAT_FLG_REG; if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN) || (ep->double_buf && HALF_FULL(stat_flg)))) { struct omap_req *req; VDBG("%s: lose, %04x\n", ep->ep.name, stat_flg); req = container_of(ep->queue.next, struct omap_req, queue); UDC_EP_NUM_REG = ep->bEndpointAddress | UDC_EP_SEL; (void) read_fifo(ep, req); UDC_EP_NUM_REG = ep->bEndpointAddress; UDC_CTRL_REG = UDC_SET_FIFO_EN; ep->ackwait = 1 + ep->double_buf; } } mod_timer(&ep->timer, PIO_OUT_TIMEOUT); spin_unlock_irqrestore(&ep->udc->lock, flags);}static irqreturn_tomap_udc_pio_irq(int irq, void *_dev, struct pt_regs *r){ u16 epn_stat, irq_src; irqreturn_t status = IRQ_NONE; struct omap_ep *ep; int epnum; struct omap_udc *udc = _dev; struct omap_req *req; unsigned long flags; spin_lock_irqsave(&udc->lock, flags); epn_stat = UDC_EPN_STAT_REG; irq_src = UDC_IRQ_SRC_REG; /* handle OUT first, to avoid some wasteful NAKs */ if (irq_src & UDC_EPN_RX) { epnum = (epn_stat >> 8) & 0x0f; UDC_IRQ_SRC_REG = UDC_EPN_RX; status = IRQ_HANDLED; ep = &udc->ep[epnum]; ep->irqs++; UDC_EP_NUM_REG = epnum | UDC_EP_SEL; ep->fnf = 0; if ((UDC_STAT_FLG_REG & UDC_ACK)) { ep->ackwait--; if (!list_empty(&ep->queue)) { int stat; req = container_of(ep->queue.next, struct omap_req, queue); stat = read_fifo(ep, req); if (!ep->double_buf) ep->fnf = 1; } } /* min 6 clock delay before clearing EP_SEL ... */ epn_stat = UDC_EPN_STAT_REG; epn_stat = UDC_EPN_STAT_REG; UDC_EP_NUM_REG = epnum; /* enabling fifo _after_ clearing ACK, contrary to docs, * reduces lossage; timer still needed though (sigh). */ if (ep->fnf) { UDC_CTRL_REG = UDC_SET_FIFO_EN; ep->ackwait = 1 + ep->double_buf; } mod_timer(&ep->timer, PIO_OUT_TIMEOUT); } /* then IN transfers */ else if (irq_src & UDC_EPN_TX) { epnum = epn_stat & 0x0f; UDC_IRQ_SRC_REG = UDC_EPN_TX; status = IRQ_HANDLED; ep = &udc->ep[16 + epnum]; ep->irqs++; UDC_EP_NUM_REG = epnum | UDC_EP_DIR | UDC_EP_SEL; if ((UDC_STAT_FLG_REG & UDC_ACK)) { ep->ackwait = 0; if (!list_empty(&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); (void) write_fifo(ep, req); } } /* min 6 clock delay before clearing EP_SEL ... */ epn_stat = UDC_EPN_STAT_REG; epn_stat = UDC_EPN_STAT_REG; UDC_EP_NUM_REG = epnum | UDC_EP_DIR; /* then 6 clocks before it'd tx */ } spin_unlock_irqrestore(&udc->lock, flags); return status;}#ifdef USE_ISOstatic irqreturn_tomap_udc_iso_irq(int irq, void *_dev, struct pt_regs *r){ struct omap_udc *udc = _dev; struct omap_ep *ep; int pending = 0; unsigned long flags; spin_lock_irqsave(&udc->lock, flags); /* handle all non-DMA ISO transfers */ list_for_each_entry (ep, &udc->iso, iso) { u16 stat; struct omap_req *req; if (ep->has_dma || list_empty(&ep->queue)) continue; req = list_entry(ep->queue.next, struct omap_req, queue); use_ep(ep, UDC_EP_SEL); stat = UDC_STAT_FLG_REG; /* NOTE: like the other controller drivers, this isn't * currently reporting lost or damaged frames. */ if (ep->bEndpointAddress & USB_DIR_IN) { if (stat & UDC_MISS_IN) /* done(ep, req, -EPROTO) */; else write_fifo(ep, req); } else { int status = 0; if (stat & UDC_NO_RXPACKET) status = -EREMOTEIO; else if (stat & UDC_ISO_ERR) status = -EILSEQ; else if (stat & UDC_DATA_FLUSH) status = -ENOSR; if (status) /* done(ep, req, status) */; else read_fifo(ep, req); } deselect_ep(); /* 6 wait states before next EP */ ep->irqs++; if (!list_empty(&ep->queue)) pending = 1; } if (!pending) UDC_IRQ_EN_REG &= ~UDC_SOF_IE; UDC_IRQ_SRC_REG = UDC_SOF; spin_unlock_irqrestore(&udc->lock, flags); return IRQ_HANDLED;}#endif/*-------------------------------------------------------------------------*/static struct omap_udc *udc;int usb_gadget_register_driver (struct usb_gadget_driver *driver){ int status = -ENODEV; struct omap_ep *ep; unsigned long flags; /* basic sanity tests */ if (!udc) return -ENODEV; if (!driver // FIXME if otg, check: driver->is_otg || driver->speed < USB_SPEED_FULL || !driver->bind || !driver->unbind || !driver->setup) return -EINVAL; spin_lock_irqsave(&udc->lock, flags); if (udc->driver) { spin_unlock_irqrestore(&udc->lock, flags); return -EBUSY; } /* reset state */ list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) { ep->irqs = 0; if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) continue; use_ep(ep, 0); UDC_CTRL_REG = UDC_SET_HALT; } udc->ep0_pending = 0; udc->ep[0].irqs = 0; udc->softconnect = 1; /* hook up the driver */ driver->driver.bus = 0; udc->driver = driver; udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc->lock, flags); status = driver->bind (&udc->gadget); if (status) { DBG("bind to %s --> %d\n", driver->driver.name, status); udc->gadget.dev.driver = 0; udc->driver = 0; goto done; } DBG("bound to driver %s\n", driver->driver.name); UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK; /* connect to bus through transceiver */ if (udc->transceiver) { status = otg_set_peripheral(udc->transceiver, &udc->gadget); if (status < 0) { ERR("can't bind to transceiver\n"); driver->unbind (&udc->gadget); udc->gadget.dev.driver = 0; udc->driver = 0; goto done;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -