musb_gadget.c
来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 1,834 行 · 第 1/4 页
C
1,834 行
if (pRequest->ep != pEnd) return -EINVAL; DBG(4, "<== to %s request=%p\n", ep->name, req); /* request is mine now... */ pRequest->request.actual = 0; pRequest->request.status = -EINPROGRESS; pRequest->bEnd = pEnd->bEndNumber; pRequest->bTx = pEnd->is_in; if (is_dma_capable() && pEnd->dma) { if (pRequest->request.dma == DMA_ADDR_INVALID) { pRequest->request.dma = dma_map_single( musb->controller, pRequest->request.buf, pRequest->request.length, pRequest->bTx ? DMA_TO_DEVICE : DMA_FROM_DEVICE); pRequest->mapped = 1; } else { dma_sync_single_for_device(musb->controller, pRequest->request.dma, pRequest->request.length, pRequest->bTx ? DMA_TO_DEVICE : DMA_FROM_DEVICE); pRequest->mapped = 0; } } else if (!req->buf) { return -ENODATA; } else pRequest->mapped = 0; spin_lock_irqsave(&musb->Lock, lockflags); /* don't queue if the ep is down */ if (!pEnd->desc) { DBG(4, "req %p queued to %s while ep %s\n", req, ep->name, "disabled"); status = -ESHUTDOWN; goto cleanup; } /* add pRequest to the list */ list_add_tail(&(pRequest->request.list), &(pEnd->req_list)); /* it this is the head of the queue, start i/o ... */ if (!pEnd->busy && &pRequest->request.list == pEnd->req_list.next) musb_ep_restart(musb, pRequest);cleanup: spin_unlock_irqrestore(&musb->Lock, lockflags); return status;}static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *pRequest){ struct musb_ep *pEnd = to_musb_ep(ep); struct usb_request *r; unsigned long flags; int status = 0; struct musb *musb = pEnd->pThis; if (!ep || !pRequest || to_musb_request(pRequest)->ep != pEnd) return -EINVAL; spin_lock_irqsave(&musb->Lock, flags); list_for_each_entry(r, &pEnd->req_list, list) { if (r == pRequest) break; } if (r != pRequest) { DBG(3, "request %p not queued to %s\n", pRequest, ep->name); status = -EINVAL; goto done; } /* if the hardware doesn't have the request, easy ... */ if (pEnd->req_list.next != &pRequest->list || pEnd->busy) musb_g_giveback(pEnd, pRequest, -ECONNRESET); /* ... else abort the dma transfer ... */ else if (is_dma_capable() && pEnd->dma) { struct dma_controller *c = musb->pDmaController; MGC_SelectEnd(musb->pRegs, pEnd->bEndNumber); if (c->channel_abort) status = c->channel_abort(pEnd->dma); else status = -EBUSY; if (status == 0) musb_g_giveback(pEnd, pRequest, -ECONNRESET); } else { /* NOTE: by sticking to easily tested hardware/driver states, * we leave counting of in-flight packets imprecise. */ musb_g_giveback(pEnd, pRequest, -ECONNRESET); }done: spin_unlock_irqrestore(&musb->Lock, flags); return status;}/* * Set or clear the halt bit of an endpoint. A halted enpoint won't tx/rx any * data but will queue requests. * * exported to ep0 code */int musb_gadget_set_halt(struct usb_ep *ep, int value){ struct musb_ep *pEnd = to_musb_ep(ep); u8 bEnd = pEnd->bEndNumber; struct musb *musb = pEnd->pThis; void __iomem *epio = musb->aLocalEnd[bEnd].regs; void __iomem *pBase; unsigned long flags; u16 wCsr; struct musb_request *pRequest = NULL; int status = 0; if (!ep) return -EINVAL; pBase = musb->pRegs; spin_lock_irqsave(&musb->Lock, flags); if ((USB_ENDPOINT_XFER_ISOC == pEnd->type)) { status = -EINVAL; goto done; } MGC_SelectEnd(pBase, bEnd); /* cannot portably stall with non-empty FIFO */ pRequest = to_musb_request(next_request(pEnd)); if (value && pEnd->is_in) { wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR); if (wCsr & MGC_M_TXCSR_FIFONOTEMPTY) { DBG(3, "%s fifo busy, cannot halt\n", ep->name); spin_unlock_irqrestore(&musb->Lock, flags); return -EAGAIN; } } /* set/clear the stall and toggle bits */ DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear"); if (pEnd->is_in) { wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR); if (wCsr & MGC_M_TXCSR_FIFONOTEMPTY) wCsr |= MGC_M_TXCSR_FLUSHFIFO; wCsr |= MGC_M_TXCSR_P_WZC_BITS | MGC_M_TXCSR_CLRDATATOG; if (value) wCsr |= MGC_M_TXCSR_P_SENDSTALL; else wCsr &= ~(MGC_M_TXCSR_P_SENDSTALL | MGC_M_TXCSR_P_SENTSTALL); wCsr &= ~MGC_M_TXCSR_TXPKTRDY; musb_writew(epio, MGC_O_HDRC_TXCSR, wCsr); } else { wCsr = musb_readw(epio, MGC_O_HDRC_RXCSR); wCsr |= MGC_M_RXCSR_P_WZC_BITS | MGC_M_RXCSR_FLUSHFIFO | MGC_M_RXCSR_CLRDATATOG; if (value) wCsr |= MGC_M_RXCSR_P_SENDSTALL; else wCsr &= ~(MGC_M_RXCSR_P_SENDSTALL | MGC_M_RXCSR_P_SENTSTALL); musb_writew(epio, MGC_O_HDRC_RXCSR, wCsr); }done: /* maybe start the first request in the queue */ if (!pEnd->busy && !value && pRequest) { DBG(3, "restarting the request\n"); musb_ep_restart(musb, pRequest); } spin_unlock_irqrestore(&musb->Lock, flags); return status;}static int musb_gadget_fifo_status(struct usb_ep *ep){ struct musb_ep *musb_ep = to_musb_ep(ep); void __iomem *epio = musb_ep->hw_ep->regs; int retval = -EINVAL; if (musb_ep->desc && !musb_ep->is_in) { struct musb *musb = musb_ep->pThis; int bEnd = musb_ep->bEndNumber; void __iomem *mbase = musb->pRegs; unsigned long flags; spin_lock_irqsave(&musb->Lock, flags); MGC_SelectEnd(mbase, bEnd); /* FIXME return zero unless RXPKTRDY is set */ retval = musb_readw(epio, MGC_O_HDRC_RXCOUNT); spin_unlock_irqrestore(&musb->Lock, flags); } return retval;}static void musb_gadget_fifo_flush(struct usb_ep *ep){ struct musb_ep *musb_ep = to_musb_ep(ep); struct musb *musb = musb_ep->pThis; u8 nEnd = musb_ep->bEndNumber; void __iomem *epio = musb->aLocalEnd[nEnd].regs; void __iomem *mbase; unsigned long flags; u16 wCsr, wIntrTxE; mbase = musb->pRegs; spin_lock_irqsave(&musb->Lock, flags); MGC_SelectEnd(mbase, (u8) nEnd); /* disable interrupts */ wIntrTxE = musb_readw(mbase, MGC_O_HDRC_INTRTXE); musb_writew(mbase, MGC_O_HDRC_INTRTXE, wIntrTxE & ~(1 << nEnd)); if (musb_ep->is_in) { wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR); if (wCsr & MGC_M_TXCSR_FIFONOTEMPTY) { wCsr |= MGC_M_TXCSR_FLUSHFIFO | MGC_M_TXCSR_P_WZC_BITS; musb_writew(epio, MGC_O_HDRC_TXCSR, wCsr); /* REVISIT may be inappropriate w/o FIFONOTEMPTY ... */ musb_writew(epio, MGC_O_HDRC_TXCSR, wCsr); } } else { wCsr = musb_readw(epio, MGC_O_HDRC_RXCSR); wCsr |= MGC_M_RXCSR_FLUSHFIFO | MGC_M_RXCSR_P_WZC_BITS; musb_writew(epio, MGC_O_HDRC_RXCSR, wCsr); musb_writew(epio, MGC_O_HDRC_RXCSR, wCsr); } /* re-enable interrupt */ musb_writew(mbase, MGC_O_HDRC_INTRTXE, wIntrTxE); spin_unlock_irqrestore(&musb->Lock, flags);}static const struct usb_ep_ops musb_ep_ops = { .enable = musb_gadget_enable, .disable = musb_gadget_disable, .alloc_request = musb_alloc_request, .free_request = musb_free_request, .alloc_buffer = musb_gadget_alloc_buffer, .free_buffer = musb_gadget_free_buffer, .queue = musb_gadget_queue, .dequeue = musb_gadget_dequeue, .set_halt = musb_gadget_set_halt, .fifo_status = musb_gadget_fifo_status, .fifo_flush = musb_gadget_fifo_flush};/***********************************************************************/static int musb_gadget_get_frame(struct usb_gadget *gadget){ struct musb *musb = gadget_to_musb(gadget); return (int)musb_readw(musb->pRegs, MGC_O_HDRC_FRAME);}static int musb_gadget_wakeup(struct usb_gadget *gadget){ struct musb *musb = gadget_to_musb(gadget); void __iomem *mregs = musb->pRegs; unsigned long flags; int status = -EINVAL; u8 power, devctl; int retries; spin_lock_irqsave(&musb->Lock, flags); switch (musb->xceiv.state) { case OTG_STATE_B_PERIPHERAL: /* NOTE: OTG state machine doesn't include B_SUSPENDED; * that's part of the standard usb 1.1 state machine, and * doesn't affect OTG transitions. */ if (musb->may_wakeup && musb->is_suspended) break; goto done; case OTG_STATE_B_IDLE: /* Start SRP ... OTG not required. */ devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL); DBG(2, "Sending SRP: devctl: %02x\n", devctl); devctl |= MGC_M_DEVCTL_SESSION; musb_writeb(mregs, MGC_O_HDRC_DEVCTL, devctl); devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL); retries = 100; while (!(devctl & MGC_M_DEVCTL_SESSION)) { devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL); if (retries-- < 1) break; } retries = 10000; while (devctl & MGC_M_DEVCTL_SESSION) { devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL); if (retries-- < 1) break; } /* Block idling for at least 1s */ musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(1 * HZ)); status = 0; goto done; default: goto done; } status = 0; power = musb_readb(mregs, MGC_O_HDRC_POWER); power |= MGC_M_POWER_RESUME; musb_writeb(mregs, MGC_O_HDRC_POWER, power); DBG(2, "issue wakeup\n"); /* Switch off RESUME signalling after 10ms */ musb_otg_timer.data = (unsigned long)musb; mod_timer(&musb_otg_timer, jiffies + msecs_to_jiffies(TIME_RESUME_SIGNALLING));done: spin_unlock_irqrestore(&musb->Lock, flags); return status;}static intmusb_gadget_set_self_powered(struct usb_gadget *gadget, int is_selfpowered){ struct musb *musb = gadget_to_musb(gadget); musb->is_self_powered = !!is_selfpowered; return 0;}static void musb_pullup(struct musb *musb, int is_on){ u8 power; power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER); if (is_on) power |= MGC_M_POWER_SOFTCONN; else power &= ~MGC_M_POWER_SOFTCONN; /* FIXME if on, HdrcStart; if off, HdrcStop */ DBG(3, "gadget %s D+ pullup %s\n", musb->pGadgetDriver->function, is_on ? "on" : "off"); musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power);}static int musb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA){ struct musb *musb = gadget_to_musb(gadget); if (!musb->xceiv.set_power) return -EOPNOTSUPP; return otg_set_power(&musb->xceiv, mA);}static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on){ struct musb *musb = gadget_to_musb(gadget); unsigned long flags; is_on = !!is_on; /* NOTE: this assumes we are sensing vbus; we'd rather * not pullup unless the B-session is active. */ spin_lock_irqsave(&musb->Lock, flags); if (is_on != musb->softconnect) { musb->softconnect = is_on; musb_pullup(musb, is_on); } spin_unlock_irqrestore(&musb->Lock, flags); return 0;}static const struct usb_gadget_ops musb_gadget_operations = { .get_frame = musb_gadget_get_frame, .wakeup = musb_gadget_wakeup, .set_selfpowered = musb_gadget_set_self_powered, //.vbus_session = musb_gadget_vbus_session, .vbus_draw = musb_gadget_vbus_draw, .pullup = musb_gadget_pullup,};/**************************************************************** * Registration operations ****************************************************************//* Only this registration code "knows" the rule (from USB standards) * about there being only one external upstream port. It assumes * all peripheral ports are external... */static struct musb *the_gadget;static void musb_gadget_release(struct device *dev){ // kref_put(WHAT) dev_dbg(dev, "%s\n", __FUNCTION__);}static void __initinit_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 bEnd, int is_in){ struct musb_hw_ep *hw_ep = musb->aLocalEnd + bEnd; memset(ep, 0, sizeof *ep); ep->bEndNumber = bEnd; ep->pThis = musb; ep->hw_ep = hw_ep; ep->is_in = is_in; INIT_LIST_HEAD(&ep->req_list); sprintf(ep->name, "ep%d%s", bEnd, (!bEnd || hw_ep->bIsSharedFifo) ? "" : ( is_in ? "in" : "out")); ep->end_point.name = ep->name; INIT_LIST_HEAD(&ep->end_point.ep_list); if (!bEnd) { ep->end_point.maxpacket = 64; ep->end_point.ops = &musb_g_ep0_ops; musb->g.ep0 = &ep->end_point; } else { if (is_in) ep->end_point.maxpacket = hw_ep->wMaxPacketSizeTx; else ep->end_point.maxpacket = hw_ep->wMaxPacketSizeRx; ep->end_point.ops = &musb_ep_ops; list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list); }}/* * Initialize the endpoints exposed to peripheral drivers, with backlinks
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?