g_ep0.c
来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 982 行 · 第 1/2 页
C
982 行
} /* load the data */ pFifoSource = (u8 *) pRequest->buf + pRequest->actual; wFifoCount = min((unsigned) MGC_END0_FIFOSIZE, pRequest->length - pRequest->actual); musb_write_fifo(&musb->aLocalEnd[0], wFifoCount, pFifoSource); pRequest->actual += wFifoCount; /* update the flags */ if (wFifoCount < MUSB_MAX_END0_PACKET || pRequest->actual == pRequest->length) { /* Check if ZLP needs to be sent */ if (!pRequest->zero) { musb->ep0_state = MGC_END0_STAGE_STATUSOUT; wCsrVal |= MGC_M_CSR0_P_DATAEND; } else { pRequest->zero = 0; pRequest = NULL; } } else pRequest = NULL; /* send it out, triggering a "txpktrdy cleared" irq */ musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal); /* report completions as soon as the fifo's loaded; there's no * win in waiting till this last packet gets acked. (other than * very precise fault reporting, needed by USB TMC; possible with * this hardware, but not usable from portable gadget drivers.) */ if (pRequest) musb_g_ep0_giveback(musb, pRequest);}/* * Read a SETUP packet (struct usb_ctrlrequest) from the hardware. * Fields are left in USB byte-order. * * Context: caller holds controller lock. */static voidmusb_read_setup(struct musb *musb, struct usb_ctrlrequest *req){ struct usb_request *r; void __iomem *regs = musb->control_ep->regs; musb_read_fifo(&musb->aLocalEnd[0], sizeof *req, (u8 *)req); /* NOTE: earlier 2.6 versions changed setup packets to host * order, but now USB packets always stay in USB byte order. */ DBG(3, "SETUP req%02x.%02x v%04x i%04x l%d\n", req->bRequestType, req->bRequest, le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength)); /* clean up any leftover transfers */ r = next_ep0_request(musb); if (r) musb_g_ep0_giveback(musb, r); /* For zero-data requests we want to delay the STATUS stage to * avoid SETUPEND errors. If we read data (OUT), delay accepting * packets until there's a buffer to store them in. * * If we write data, the controller acts happier if we enable * the TX FIFO right away, and give the controller a moment * to switch modes... */ musb->bSetAddress = FALSE; musb->ackpend = MGC_M_CSR0_P_SVDRXPKTRDY; if (req->wLength == 0) { if (req->bRequestType & USB_DIR_IN) musb->ackpend |= MGC_M_CSR0_TXPKTRDY; musb->ep0_state = MGC_END0_STAGE_ACKWAIT; } else if (req->bRequestType & USB_DIR_IN) { musb->ep0_state = MGC_END0_STAGE_TX; musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_P_SVDRXPKTRDY); while ((musb_readw(regs, MGC_O_HDRC_CSR0) & MGC_M_CSR0_RXPKTRDY) != 0) cpu_relax(); musb->ackpend = 0; } else musb->ep0_state = MGC_END0_STAGE_RX;}static intforward_to_driver(struct musb *musb, const struct usb_ctrlrequest *pControlRequest)__releases(musb->Lock)__acquires(musb->Lock){ int retval; if (!musb->pGadgetDriver) return -EOPNOTSUPP; spin_unlock(&musb->Lock); retval = musb->pGadgetDriver->setup(&musb->g, pControlRequest); spin_lock(&musb->Lock); return retval;}/* * Handle peripheral ep0 interrupt * * Context: irq handler; we won't re-enter the driver that way. */irqreturn_t musb_g_ep0_irq(struct musb *musb){ u16 wCsrVal; u16 wCount; void __iomem *pBase = musb->pRegs; void __iomem *regs = musb->aLocalEnd[0].regs; irqreturn_t retval = IRQ_NONE; MGC_SelectEnd(pBase, 0); /* select ep0 */ wCsrVal = musb_readw(regs, MGC_O_HDRC_CSR0); wCount = musb_readb(regs, MGC_O_HDRC_COUNT0); DBG(4, "csr %04x, count %d, myaddr %d, ep0stage %s\n", wCsrVal, wCount, musb_readb(pBase, MGC_O_HDRC_FADDR), decode_ep0stage(musb->ep0_state)); /* I sent a stall.. need to acknowledge it now.. */ if (wCsrVal & MGC_M_CSR0_P_SENTSTALL) { musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal & ~MGC_M_CSR0_P_SENTSTALL); retval = IRQ_HANDLED; musb->ep0_state = MGC_END0_STAGE_SETUP; wCsrVal = musb_readw(regs, MGC_O_HDRC_CSR0); } /* request ended "early" */ if (wCsrVal & MGC_M_CSR0_P_SETUPEND) { musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_P_SVDSETUPEND); retval = IRQ_HANDLED; musb->ep0_state = MGC_END0_STAGE_SETUP; wCsrVal = musb_readw(regs, MGC_O_HDRC_CSR0); /* NOTE: request may need completion */ } /* docs from Mentor only describe tx, rx, and idle/setup states. * we need to handle nuances around status stages, and also the * case where status and setup stages come back-to-back ... */ switch (musb->ep0_state) { case MGC_END0_STAGE_TX: /* irq on clearing txpktrdy */ if ((wCsrVal & MGC_M_CSR0_TXPKTRDY) == 0) { ep0_txstate(musb); retval = IRQ_HANDLED; } break; case MGC_END0_STAGE_RX: /* irq on set rxpktrdy */ if (wCsrVal & MGC_M_CSR0_RXPKTRDY) { ep0_rxstate(musb); retval = IRQ_HANDLED; } break; case MGC_END0_STAGE_STATUSIN: /* end of sequence #2 (OUT/RX state) or #3 (no data) */ /* update address (if needed) only @ the end of the * status phase per usb spec, which also guarantees * we get 10 msec to receive this irq... until this * is done we won't see the next packet. */ if (musb->bSetAddress) { musb->bSetAddress = FALSE; musb_writeb(pBase, MGC_O_HDRC_FADDR, musb->bAddress); } /* enter test mode if needed (exit by reset) */ else if (musb->bTestMode) { DBG(1, "entering TESTMODE\n"); if (MGC_M_TEST_PACKET == musb->bTestModeValue) musb_load_testpacket(musb); musb_writeb(pBase, MGC_O_HDRC_TESTMODE, musb->bTestModeValue); } /* FALLTHROUGH */ case MGC_END0_STAGE_STATUSOUT: /* end of sequence #1: write to host (TX state) */ { struct usb_request *req; req = next_ep0_request(musb); if (req) musb_g_ep0_giveback(musb, req); } retval = IRQ_HANDLED; musb->ep0_state = MGC_END0_STAGE_SETUP; /* FALLTHROUGH */ case MGC_END0_STAGE_SETUP: if (wCsrVal & MGC_M_CSR0_RXPKTRDY) { struct usb_ctrlrequest setup; int handled = 0; if (wCount != 8) { ERR("SETUP packet len %d != 8 ?\n", wCount); break; } musb_read_setup(musb, &setup); retval = IRQ_HANDLED; /* sometimes the RESET won't be reported */ if (unlikely(musb->g.speed == USB_SPEED_UNKNOWN)) { u8 power; printk(KERN_NOTICE "%s: peripheral reset " "irq lost!\n", musb_driver_name); power = musb_readb(pBase, MGC_O_HDRC_POWER); musb->g.speed = (power & MGC_M_POWER_HSMODE) ? USB_SPEED_HIGH : USB_SPEED_FULL; } switch (musb->ep0_state) { /* sequence #3 (no data stage), includes requests * we can't forward (notably SET_ADDRESS and the * device/endpoint feature set/clear operations) * plus SET_CONFIGURATION and others we must */ case MGC_END0_STAGE_ACKWAIT: handled = service_zero_data_request( musb, &setup); /* status stage might be immediate */ if (handled > 0) { musb->ackpend |= MGC_M_CSR0_P_DATAEND; musb->ep0_state = MGC_END0_STAGE_STATUSIN; } break; /* sequence #1 (IN to host), includes GET_STATUS * requests that we can't forward, GET_DESCRIPTOR * and others that we must */ case MGC_END0_STAGE_TX: handled = service_in_request(musb, &setup); if (handled > 0) { musb->ackpend = MGC_M_CSR0_TXPKTRDY | MGC_M_CSR0_P_DATAEND; musb->ep0_state = MGC_END0_STAGE_STATUSOUT; } break; /* sequence #2 (OUT from host), always forward */ default: /* MGC_END0_STAGE_RX */ break; } DBG(3, "handled %d, csr %04x, ep0stage %s\n", handled, wCsrVal, decode_ep0stage(musb->ep0_state)); /* unless we need to delegate this to the gadget * driver, we know how to wrap this up: csr0 has * not yet been written. */ if (handled < 0) goto stall; else if (handled > 0) goto finish; handled = forward_to_driver(musb, &setup); if (handled < 0) { MGC_SelectEnd(pBase, 0);stall: DBG(3, "stall (%d)\n", handled); musb->ackpend |= MGC_M_CSR0_P_SENDSTALL; musb->ep0_state = MGC_END0_STAGE_SETUP;finish: musb_writew(regs, MGC_O_HDRC_CSR0, musb->ackpend); musb->ackpend = 0; } } break; case MGC_END0_STAGE_ACKWAIT: /* This should not happen. But happens with tusb6010 with * g_file_storage and high speed. Do nothing. */ retval = IRQ_HANDLED; break; default: /* "can't happen" */ WARN_ON(1); musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_P_SENDSTALL); musb->ep0_state = MGC_END0_STAGE_SETUP; break; } return retval;}static intmusb_g_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc){ /* always enabled */ return -EINVAL;}static int musb_g_ep0_disable(struct usb_ep *e){ /* always enabled */ return -EINVAL;}static void *musb_g_ep0_alloc_buffer(struct usb_ep *ep, unsigned bytes, dma_addr_t * dma, gfp_t gfp_flags){ *dma = DMA_ADDR_INVALID; return kmalloc(bytes, gfp_flags);}static void musb_g_ep0_free_buffer(struct usb_ep *ep, void *address, dma_addr_t dma, unsigned bytes){ kfree(address);}static intmusb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags){ struct musb_ep *ep; struct musb_request *req; struct musb *musb; int status; unsigned long lockflags; void __iomem *regs; if (!e || !r) return -EINVAL; ep = to_musb_ep(e); musb = ep->pThis; regs = musb->control_ep->regs; req = to_musb_request(r); req->musb = musb; req->request.actual = 0; req->request.status = -EINPROGRESS; req->bTx = ep->is_in; spin_lock_irqsave(&musb->Lock, lockflags); if (!list_empty(&ep->req_list)) { status = -EBUSY; goto cleanup; } switch (musb->ep0_state) { case MGC_END0_STAGE_RX: /* control-OUT data */ case MGC_END0_STAGE_TX: /* control-IN data */ case MGC_END0_STAGE_ACKWAIT: /* zero-length data */ status = 0; break; default: DBG(1, "ep0 request queued in state %d\n", musb->ep0_state); status = -EINVAL; goto cleanup; } /* add request to the list */ list_add_tail(&(req->request.list), &(ep->req_list)); DBG(3, "queue to %s (%s), length=%d\n", ep->name, ep->is_in ? "IN/TX" : "OUT/RX", req->request.length); MGC_SelectEnd(musb->pRegs, 0); /* sequence #1, IN ... start writing the data */ if (musb->ep0_state == MGC_END0_STAGE_TX) ep0_txstate(musb); /* sequence #3, no-data ... issue IN status */ else if (musb->ep0_state == MGC_END0_STAGE_ACKWAIT) { if (req->request.length) status = -EINVAL; else { musb->ep0_state = MGC_END0_STAGE_STATUSIN; musb_writew(regs, MGC_O_HDRC_CSR0, musb->ackpend | MGC_M_CSR0_P_DATAEND); musb->ackpend = 0; musb_g_ep0_giveback(ep->pThis, r); } /* else for sequence #2 (OUT), caller provides a buffer * before the next packet arrives. deferred responses * (after SETUP is acked) are racey. */ } else if (musb->ackpend) { musb_writew(regs, MGC_O_HDRC_CSR0, musb->ackpend); musb->ackpend = 0; }cleanup: spin_unlock_irqrestore(&musb->Lock, lockflags); return status;}static intmusb_g_ep0_dequeue(struct usb_ep *ep, struct usb_request *req){ /* we just won't support this */ return -EINVAL;}static int musb_g_ep0_halt(struct usb_ep *e, int value){ struct musb_ep *ep; struct musb *musb; void __iomem *base, *regs; unsigned long flags; int status; u16 csr; if (!e || !value) return -EINVAL; ep = to_musb_ep(e); musb = ep->pThis; base = musb->pRegs; regs = musb->control_ep->regs; spin_lock_irqsave(&musb->Lock, flags); if (!list_empty(&ep->req_list)) { status = -EBUSY; goto cleanup; } switch (musb->ep0_state) { case MGC_END0_STAGE_TX: /* control-IN data */ case MGC_END0_STAGE_ACKWAIT: /* STALL for zero-length data */ case MGC_END0_STAGE_RX: /* control-OUT data */ status = 0; MGC_SelectEnd(base, 0); csr = musb_readw(regs, MGC_O_HDRC_CSR0); csr |= MGC_M_CSR0_P_SENDSTALL; musb_writew(regs, MGC_O_HDRC_CSR0, csr); musb->ep0_state = MGC_END0_STAGE_SETUP; break; default: DBG(1, "ep0 can't halt in state %d\n", musb->ep0_state); status = -EINVAL; }cleanup: spin_unlock_irqrestore(&musb->Lock, flags); return status;}const struct usb_ep_ops musb_g_ep0_ops = { .enable = musb_g_ep0_enable, .disable = musb_g_ep0_disable, .alloc_request = musb_alloc_request, .free_request = musb_free_request, .alloc_buffer = musb_g_ep0_alloc_buffer, .free_buffer = musb_g_ep0_free_buffer, .queue = musb_g_ep0_queue, .dequeue = musb_g_ep0_dequeue, .set_halt = musb_g_ep0_halt, .fifo_status = NULL, .fifo_flush = NULL,};
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?