musb_host.c
来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 1,976 行 · 第 1/4 页
C
1,976 行
pUrb->error_count++; } d = pUrb->iso_frame_desc + qh->iso_idx; pBuffer = buffer + d->offset; length = d->length; if (wRxCount > length) { if (status == 0) { status = -EOVERFLOW; pUrb->error_count++; } DBG(2, "** OVERFLOW %d into %d\n", wRxCount, length); do_flush = 1; } else length = wRxCount; pUrb->actual_length += length; d->actual_length = length; d->status = status; /* see if we are done */ bDone = (++qh->iso_idx >= pUrb->number_of_packets); } else { /* non-isoch */ pBuffer = buffer + qh->offset; length = pUrb->transfer_buffer_length - qh->offset; if (wRxCount > length) { if (pUrb->status == -EINPROGRESS) pUrb->status = -EOVERFLOW; DBG(2, "** OVERFLOW %d into %d\n", wRxCount, length); do_flush = 1; } else length = wRxCount; pUrb->actual_length += length; qh->offset += length; /* see if we are done */ bDone = (pUrb->actual_length == pUrb->transfer_buffer_length) || (wRxCount < qh->maxpacket) || (pUrb->status != -EINPROGRESS); if (bDone && (pUrb->status == -EINPROGRESS) && (pUrb->transfer_flags & URB_SHORT_NOT_OK) && (pUrb->actual_length < pUrb->transfer_buffer_length)) pUrb->status = -EREMOTEIO; } musb_read_fifo(pEnd, length, pBuffer); wCsr = musb_readw(epio, MGC_O_HDRC_RXCSR); wCsr |= MGC_M_RXCSR_H_WZC_BITS; if (unlikely(do_flush)) musb_h_flush_rxfifo(pEnd, wCsr); else { /* REVISIT this assumes AUTOCLEAR is never set */ wCsr &= ~(MGC_M_RXCSR_RXPKTRDY | MGC_M_RXCSR_H_REQPKT); if (!bDone) wCsr |= MGC_M_RXCSR_H_REQPKT; musb_writew(epio, MGC_O_HDRC_RXCSR, wCsr); } return bDone;}/* we don't always need to reinit a given side of an endpoint... * when we do, use tx/rx reinit routine and then construct a new CSR * to address data toggle, NYET, and DMA or PIO. * * it's possible that driver bugs (especially for DMA) or aborting a * transfer might have left the endpoint busier than it should be. * the busy/not-empty tests are basically paranoia. */static voidmusb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep){ u16 csr; /* NOTE: we know the "rx" fifo reinit never triggers for ep0. * That always uses tx_reinit since ep0 repurposes TX register * offsets; the initial SETUP packet is also a kind of OUT. */ /* if programmed for Tx, put it in RX mode */ if (ep->bIsSharedFifo) { csr = musb_readw(ep->regs, MGC_O_HDRC_TXCSR); if (csr & MGC_M_TXCSR_MODE) { musb_h_tx_flush_fifo(ep); musb_writew(ep->regs, MGC_O_HDRC_TXCSR, MGC_M_TXCSR_FRCDATATOG); } /* clear mode (and everything else) to enable Rx */ musb_writew(ep->regs, MGC_O_HDRC_TXCSR, 0); /* scrub all previous state, clearing toggle */ } else { csr = musb_readw(ep->regs, MGC_O_HDRC_RXCSR); if (csr & MGC_M_RXCSR_RXPKTRDY) WARN("rx%d, packet/%d ready?\n", ep->bLocalEnd, musb_readw(ep->regs, MGC_O_HDRC_RXCOUNT)); musb_h_flush_rxfifo(ep, MGC_M_RXCSR_CLRDATATOG); } /* target addr and (for multipoint) hub addr/port */ if (musb->bIsMultipoint) { musb_writeb(ep->target_regs, MGC_O_HDRC_RXFUNCADDR, qh->addr_reg); musb_writeb(ep->target_regs, MGC_O_HDRC_RXHUBADDR, qh->h_addr_reg); musb_writeb(ep->target_regs, MGC_O_HDRC_RXHUBPORT, qh->h_port_reg); } else musb_writeb(musb->pRegs, MGC_O_HDRC_FADDR, qh->addr_reg); /* protocol/endpoint, interval/NAKlimit, i/o size */ musb_writeb(ep->regs, MGC_O_HDRC_RXTYPE, qh->type_reg); musb_writeb(ep->regs, MGC_O_HDRC_RXINTERVAL, qh->intv_reg); /* NOTE: bulk combining rewrites high bits of maxpacket */ musb_writew(ep->regs, MGC_O_HDRC_RXMAXP, qh->maxpacket); ep->rx_reinit = 0;}/* * Program an HDRC endpoint as per the given URB * Context: irqs blocked, controller lock held */static void musb_ep_program(struct musb *pThis, u8 bEnd, struct urb *pUrb, unsigned int is_out, u8 * pBuffer, u32 dwLength){ struct dma_controller *pDmaController; struct dma_channel *pDmaChannel; u8 bDmaOk; void __iomem *pBase = pThis->pRegs; struct musb_hw_ep *pEnd = pThis->aLocalEnd + bEnd; void __iomem *epio = pEnd->regs; struct musb_qh *qh; if (!is_out || pEnd->bIsSharedFifo) qh = pEnd->in_qh; else qh = pEnd->out_qh; DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s " "h_addr%02x h_port%02x bytes %d\n", is_out ? "-->" : "<--", bEnd, pUrb, pUrb->dev->speed, qh->addr_reg, qh->epnum, is_out ? "out" : "in", qh->h_addr_reg, qh->h_port_reg, dwLength); MGC_SelectEnd(pBase, bEnd); /* candidate for DMA? */ pDmaController = pThis->pDmaController; if (is_dma_capable() && bEnd && pDmaController) { pDmaChannel = is_out ? pEnd->tx_channel : pEnd->rx_channel; if (!pDmaChannel) { pDmaChannel = pDmaController->channel_alloc( pDmaController, pEnd, is_out); if (is_out) pEnd->tx_channel = pDmaChannel; else pEnd->rx_channel = pDmaChannel; } } else pDmaChannel = NULL; /* make sure we clear DMAEnab, autoSet bits from previous run */ /* OUT/transmit/EP0 or IN/receive? */ if (is_out) { u16 wCsr; u16 wIntrTxE; u16 wLoadCount; wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR); /* disable interrupt in case we flush */ wIntrTxE = musb_readw(pBase, MGC_O_HDRC_INTRTXE); musb_writew(pBase, MGC_O_HDRC_INTRTXE, wIntrTxE & ~(1 << bEnd)); /* general endpoint setup */ if (bEnd) { u16 csr = wCsr; /* ASSERT: TXCSR_DMAENAB was already cleared */ /* flush all old state, set default */ musb_h_tx_flush_fifo(pEnd); csr &= ~(MGC_M_TXCSR_H_NAKTIMEOUT | MGC_M_TXCSR_DMAMODE | MGC_M_TXCSR_FRCDATATOG | MGC_M_TXCSR_H_RXSTALL | MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_TXPKTRDY ); csr |= MGC_M_TXCSR_MODE; if (usb_gettoggle(pUrb->dev, qh->epnum, 1)) csr |= MGC_M_TXCSR_H_WR_DATATOGGLE | MGC_M_TXCSR_H_DATATOGGLE; else csr |= MGC_M_TXCSR_CLRDATATOG; /* twice in case of double packet buffering */ musb_writew(epio, MGC_O_HDRC_TXCSR, csr); /* REVISIT may need to clear FLUSHFIFO ... */ musb_writew(epio, MGC_O_HDRC_TXCSR, csr); wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR); } else { /* endpoint 0: just flush */ musb_writew(epio, MGC_O_HDRC_CSR0, wCsr | MGC_M_CSR0_FLUSHFIFO); musb_writew(epio, MGC_O_HDRC_CSR0, wCsr | MGC_M_CSR0_FLUSHFIFO); } /* target addr and (for multipoint) hub addr/port */ if (pThis->bIsMultipoint) { musb_writeb(pBase, MGC_BUSCTL_OFFSET(bEnd, MGC_O_HDRC_TXFUNCADDR), qh->addr_reg); musb_writeb(pBase, MGC_BUSCTL_OFFSET(bEnd, MGC_O_HDRC_TXHUBADDR), qh->h_addr_reg); musb_writeb(pBase, MGC_BUSCTL_OFFSET(bEnd, MGC_O_HDRC_TXHUBPORT), qh->h_port_reg);/* FIXME if !bEnd, do the same for RX ... */ } else musb_writeb(pBase, MGC_O_HDRC_FADDR, qh->addr_reg); /* protocol/endpoint/interval/NAKlimit */ if (bEnd) { musb_writeb(epio, MGC_O_HDRC_TXTYPE, qh->type_reg); if (can_bulk_split(pThis, qh->type)) musb_writew(epio, MGC_O_HDRC_TXMAXP, qh->maxpacket | ((pEnd->wMaxPacketSizeTx / qh->maxpacket) - 1) << 11); else musb_writew(epio, MGC_O_HDRC_TXMAXP, qh->maxpacket); musb_writeb(epio, MGC_O_HDRC_TXINTERVAL, qh->intv_reg); } else { musb_writeb(epio, MGC_O_HDRC_NAKLIMIT0, qh->intv_reg); if (pThis->bIsMultipoint) musb_writeb(epio, MGC_O_HDRC_TYPE0, qh->type_reg); } if (can_bulk_split(pThis, qh->type)) wLoadCount = min((u32) pEnd->wMaxPacketSizeTx, dwLength); else wLoadCount = min((u32) qh->maxpacket, dwLength); /* candidate for DMA */ if ((is_cppi_enabled() || tusb_dma_omap()) && pDmaChannel) { /* program endpoint CSRs first, then setup DMA. * assume CPPI setup succeeds. * defer enabling dma. */ wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR); wCsr &= ~(MGC_M_TXCSR_AUTOSET | MGC_M_TXCSR_DMAMODE | MGC_M_TXCSR_DMAENAB); wCsr |= MGC_M_TXCSR_MODE; musb_writew(epio, MGC_O_HDRC_TXCSR, wCsr | MGC_M_TXCSR_MODE); pDmaChannel->dwActualLength = 0L; qh->segsize = dwLength; /* TX uses "rndis" mode automatically, but needs help * to identify the zero-length-final-packet case. */ bDmaOk = pDmaController->channel_program( pDmaChannel, qh->maxpacket, (pUrb->transfer_flags & URB_ZERO_PACKET) == URB_ZERO_PACKET, pUrb->transfer_dma, qh->segsize); if (bDmaOk) { wLoadCount = 0; } else { pDmaController->channel_release(pDmaChannel); pDmaChannel = pEnd->tx_channel = NULL; /* REVISIT there's an error path here that * needs handling: can't do dma, but * there's no pio buffer address... */ } } if (wLoadCount) { /* ASSERT: TXCSR_DMAENAB was already cleared */ /* PIO to load FIFO */ qh->segsize = wLoadCount; musb_write_fifo(pEnd, wLoadCount, pBuffer); wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR); wCsr &= ~(MGC_M_TXCSR_DMAENAB | MGC_M_TXCSR_DMAMODE | MGC_M_TXCSR_AUTOSET); /* write CSR */ wCsr |= MGC_M_TXCSR_MODE; if (bEnd) musb_writew(epio, MGC_O_HDRC_TXCSR, wCsr); } /* re-enable interrupt */ musb_writew(pBase, MGC_O_HDRC_INTRTXE, wIntrTxE); /* IN/receive */ } else { u16 csr; if (pEnd->rx_reinit) { musb_rx_reinit(pThis, qh, pEnd); /* init new state: toggle and NYET, maybe DMA later */ if (usb_gettoggle(pUrb->dev, qh->epnum, 0)) csr = MGC_M_RXCSR_H_WR_DATATOGGLE | MGC_M_RXCSR_H_DATATOGGLE; else csr = 0; if (qh->type == USB_ENDPOINT_XFER_INT) csr |= MGC_M_RXCSR_DISNYET; } else { csr = musb_readw(pEnd->regs, MGC_O_HDRC_RXCSR); if (csr & (MGC_M_RXCSR_RXPKTRDY | MGC_M_RXCSR_DMAENAB | MGC_M_RXCSR_H_REQPKT)) ERR("broken !rx_reinit, ep%d csr %04x\n", pEnd->bLocalEnd, csr); /* scrub any stale state, leaving toggle alone */ csr &= MGC_M_RXCSR_DISNYET; } /* kick things off */ if ((is_cppi_enabled() || tusb_dma_omap()) && pDmaChannel) { /* candidate for DMA */ if (pDmaChannel) { pDmaChannel->dwActualLength = 0L; qh->segsize = dwLength; /* AUTOREQ is in a DMA register */ musb_writew(pEnd->regs, MGC_O_HDRC_RXCSR, csr); csr = musb_readw(pEnd->regs, MGC_O_HDRC_RXCSR); /* unless caller treats short rx transfers as * errors, we dare not queue multiple transfers. */ bDmaOk = pDmaController->channel_program( pDmaChannel, qh->maxpacket, !(pUrb->transfer_flags & URB_SHORT_NOT_OK), pUrb->transfer_dma, qh->segsize); if (!bDmaOk) { pDmaController->channel_release( pDmaChannel); pDmaChannel = pEnd->rx_channel = NULL; } else csr |= MGC_M_RXCSR_DMAENAB; } } csr |= MGC_M_RXCSR_H_REQPKT; DBG(7, "RXCSR%d := %04x\n", bEnd, csr); musb_writew(pEnd->regs, MGC_O_HDRC_RXCSR, csr); csr = musb_readw(pEnd->regs, MGC_O_HDRC_RXCSR); }}/* * Service the default endpoint (ep0) as host. * Return TRUE until it's time to start the status stage. */static int musb_h_ep0_continue(struct musb *pThis, u16 wCount, struct urb *pUrb){ int bMore = FALSE; u8 *pFifoDest = NULL; u16 wFifoCount = 0; struct musb_hw_ep *pEnd = pThis->control_ep; struct musb_qh *qh = pEnd->in_qh; struct usb_ctrlrequest *pRequest; switch (pThis->bEnd0Stage) { case MGC_END0_IN: pFifoDest = pUrb->transfer_buffer + pUrb->actual_length; wFifoCount = min(wCount, ((u16) (pUrb->transfer_buffer_length - pUrb->actual_length))); if (wFifoCount < wCount) pUrb->status = -EOVERFLOW; musb_read_fifo(pEnd, wFifoCount, pFifoDest); pUrb->actual_length += wFifoCount; if (wCount < qh->maxpacket) { /* always terminate on short read; it's * rarely reported as an error. */ } else if (pUrb->actual_length < pUrb->transfer_buffer_length) bMore = TRUE; break; case MGC_END0_START: pRequest = (struct usb_ctrlrequest *) pUrb->setup_packet; if (!pRequest->wLength) { DBG(4, "start no-DATA\n"); break; } else if (pRequest->bRequestType & USB_DIR_IN) { DBG(4, "start IN-DATA\n"); pThis->bEnd0Stage = MGC_END0_IN; bMore = TRUE; break; } else { DBG(4, "start OUT-DATA\n"); pThis->bEnd0Stage = MGC_END0_OUT; bMore = TRUE; } /* FALLTHROUGH */ case MGC_END0_OUT: wFifoCount = min(qh->maxpacket, ((u16) (pUrb->transfer_buffer_length - pUrb->actual_length))); if (wFifoCount) { pFifoDest = (u8 *) (pUrb->transfer_buffer + pUrb->actual_length); DBG(3, "Sending %d bytes to %p\n", wFifoCount, pFifoDest); musb_write_fifo(pEnd, wFifoCount, pFifoDest); pUrb->actual_length += wFifoCount; bMore = TRUE; } break; default: ERR("bogus ep0 stage %d\n", pThis->bEnd0Stage); break; } return bMore;}/* * Handle default endpoint interrupt as host. Only called in IRQ time * from the LinuxIsr() interrupt service routine. * * called with controller irqlocked */irqreturn_t musb_h_ep0_irq(struct musb *pThis){ struct urb *pUrb; u16 wCsrVal, wCount; int status = 0; void __iomem *pBase = pThis->pRegs; struct musb_hw_ep *pEnd = pThis->control_ep; void __iomem *epio = pEnd->regs; struct musb_qh *qh = pEnd->in_qh; u8 bComplete = FALSE; irqreturn_t retval = IRQ_NONE; /* ep0 only has one queue, "in" */ pUrb = next_urb(qh); MGC_SelectEnd(pBase, 0); wCsrVal = musb_readw(epio, MGC_O_HDRC_CSR0); wCount = (wCsrVal & MGC_M_CSR0_RXPKTRDY) ? musb_readb(epio, MGC_O_HDRC_COUNT0) : 0; DBG(4, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d\n",
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?