g_ep0.c
来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 982 行 · 第 1/2 页
C
982 行
/****************************************************************** * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * * This file is part of the Inventra Controller Driver for Linux. * * The Inventra Controller Driver for Linux is free software; you * can redistribute it and/or modify it under the terms of the GNU * General Public License version 2 as published by the Free Software * Foundation. * * The Inventra Controller Driver for Linux is distributed in * the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with The Inventra Controller Driver for Linux ; if not, * write to the Free Software Foundation, Inc., 59 Temple Place, * Suite 330, Boston, MA 02111-1307 USA * * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER. * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND * NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR * GRAPHICS SUPPORT CUSTOMER. ******************************************************************/#include <linux/kernel.h>#include <linux/list.h>#include <linux/timer.h>#include <linux/spinlock.h>#include <linux/init.h>#include <linux/device.h>#include <linux/interrupt.h>#include "musbdefs.h"/* ep0 is always musb->aLocalEnd[0].ep_in */#define next_ep0_request(musb) next_in_request(&(musb)->aLocalEnd[0])/* * Locking note: we use only the controller lock, for simpler correctness. * It's always held with IRQs blocked. * * It protects the ep0 request queue as well as ep0_state, not just the * controller and indexed registers. And that lock stays held unless it * needs to be dropped to allow reentering this driver ... like upcalls to * the gadget driver, or adjusting endpoint halt status. */static char *decode_ep0stage(u8 stage){ switch(stage) { case MGC_END0_STAGE_SETUP: return "idle"; case MGC_END0_STAGE_TX: return "in"; case MGC_END0_STAGE_RX: return "out"; case MGC_END0_STAGE_ACKWAIT: return "wait"; case MGC_END0_STAGE_STATUSIN: return "in/status"; case MGC_END0_STAGE_STATUSOUT: return "out/status"; default: return "?"; }}/* handle a standard GET_STATUS request * Context: caller holds controller lock */static int service_tx_status_request( struct musb *musb, const struct usb_ctrlrequest *pControlRequest){ void __iomem *pBase = musb->pRegs; int handled = 1; u8 bResult[2], bEnd = 0; const u8 bRecip = pControlRequest->bRequestType & USB_RECIP_MASK; bResult[1] = 0; switch (bRecip) { case USB_RECIP_DEVICE: bResult[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED; bResult[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; if (musb->g.is_otg) { bResult[0] |= musb->g.b_hnp_enable << USB_DEVICE_B_HNP_ENABLE; bResult[0] |= musb->g.a_alt_hnp_support << USB_DEVICE_A_ALT_HNP_SUPPORT; bResult[0] |= musb->g.a_hnp_support << USB_DEVICE_A_HNP_SUPPORT; } break; case USB_RECIP_INTERFACE: bResult[0] = 0; break; case USB_RECIP_ENDPOINT: { int is_in; struct musb_ep *ep; u16 tmp; void __iomem *regs; bEnd = (u8) pControlRequest->wIndex; if (!bEnd) { bResult[0] = 0; break; } is_in = bEnd & USB_DIR_IN; if (is_in) { bEnd &= 0x0f; ep = &musb->aLocalEnd[bEnd].ep_in; } else { ep = &musb->aLocalEnd[bEnd].ep_out; } regs = musb->aLocalEnd[bEnd].regs; if (bEnd >= MUSB_C_NUM_EPS || !ep->desc) { handled = -EINVAL; break; } MGC_SelectEnd(pBase, bEnd); if (is_in) tmp = musb_readw(regs, MGC_O_HDRC_TXCSR) & MGC_M_TXCSR_P_SENDSTALL; else tmp = musb_readw(regs, MGC_O_HDRC_RXCSR) & MGC_M_RXCSR_P_SENDSTALL; MGC_SelectEnd(pBase, 0); bResult[0] = tmp ? 1 : 0; } break; default: /* class, vendor, etc ... delegate */ handled = 0; break; } /* fill up the fifo; caller updates csr0 */ if (handled > 0) { u16 len = le16_to_cpu(pControlRequest->wLength); if (len > 2) len = 2; musb_write_fifo(&musb->aLocalEnd[0], len, bResult); } return handled;}/* * handle a control-IN request, the end0 buffer contains the current request * that is supposed to be a standard control request. Assumes the fifo to * be at least 2 bytes long. * * @return 0 if the request was NOT HANDLED, * < 0 when error * > 0 when the request is processed * * Context: caller holds controller lock */static intservice_in_request(struct musb *musb, const struct usb_ctrlrequest *pControlRequest){ int handled = 0; /* not handled */ if ((pControlRequest->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { switch (pControlRequest->bRequest) { case USB_REQ_GET_STATUS: handled = service_tx_status_request(musb, pControlRequest); break; /* case USB_REQ_SYNC_FRAME: */ default: break; } } return handled;}/* * Context: caller holds controller lock */static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req){ musb->ep0_state = MGC_END0_STAGE_SETUP; musb_g_giveback(&musb->aLocalEnd[0].ep_in, req, 0);}/* * Tries to start B-device HNP negotiation if enabled via sysfs */static inline void musb_try_b_hnp_enable(struct musb *musb){ void __iomem *pBase = musb->pRegs; u8 devctl; DBG(1, "HNP: Setting HR\n"); devctl = musb_readb(pBase, MGC_O_HDRC_DEVCTL); musb_writeb(pBase, MGC_O_HDRC_DEVCTL, devctl | MGC_M_DEVCTL_HR);}/* * Handle all control requests with no DATA stage, including standard * requests such as: * USB_REQ_SET_CONFIGURATION, USB_REQ_SET_INTERFACE, unrecognized * always delegated to the gadget driver * USB_REQ_SET_ADDRESS, USB_REQ_CLEAR_FEATURE, USB_REQ_SET_FEATURE * always handled here, except for class/vendor/... features * * Context: caller holds controller lock */static intservice_zero_data_request(struct musb *musb, struct usb_ctrlrequest *pControlRequest)__releases(musb->Lock)__acquires(musb->Lock){ int handled = -EINVAL; void __iomem *pBase = musb->pRegs; const u8 bRecip = pControlRequest->bRequestType & USB_RECIP_MASK; /* the gadget driver handles everything except what we MUST handle */ if ((pControlRequest->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { switch (pControlRequest->bRequest) { case USB_REQ_SET_ADDRESS: /* change it after the status stage */ musb->bSetAddress = TRUE; musb->bAddress = (u8) (pControlRequest->wValue & 0x7f); handled = 1; break; case USB_REQ_CLEAR_FEATURE: switch (bRecip) { case USB_RECIP_DEVICE: if (pControlRequest->wValue != USB_DEVICE_REMOTE_WAKEUP) break; musb->may_wakeup = 0; handled = 1; break; case USB_RECIP_INTERFACE: break; case USB_RECIP_ENDPOINT:{ const u8 bEnd = pControlRequest->wIndex & 0x0f; struct musb_ep *pEnd; if (bEnd == 0 || bEnd >= MUSB_C_NUM_EPS || pControlRequest->wValue != USB_ENDPOINT_HALT) break; if (pControlRequest->wIndex & USB_DIR_IN) pEnd = &musb->aLocalEnd[bEnd].ep_in; else pEnd = &musb->aLocalEnd[bEnd].ep_out; if (!pEnd->desc) break; /* REVISIT do it directly, no locking games */ spin_unlock(&musb->Lock); musb_gadget_set_halt(&pEnd->end_point, 0); spin_lock(&musb->Lock); /* select ep0 again */ MGC_SelectEnd(pBase, 0); handled = 1; } break; default: /* class, vendor, etc ... delegate */ handled = 0; break; } break; case USB_REQ_SET_FEATURE: switch (bRecip) { case USB_RECIP_DEVICE: handled = 1; switch (pControlRequest->wValue) { case USB_DEVICE_REMOTE_WAKEUP: musb->may_wakeup = 1; break; case USB_DEVICE_TEST_MODE: if (musb->g.speed != USB_SPEED_HIGH) goto stall; if (pControlRequest->wIndex & 0xff) goto stall; switch (pControlRequest->wIndex >> 8) { case 1: pr_debug("TEST_J\n"); /* TEST_J */ musb->bTestModeValue = MGC_M_TEST_J; break; case 2: /* TEST_K */ pr_debug("TEST_K\n"); musb->bTestModeValue = MGC_M_TEST_K; break; case 3: /* TEST_SE0_NAK */ pr_debug("TEST_SE0_NAK\n"); musb->bTestModeValue = MGC_M_TEST_SE0_NAK; break; case 4: /* TEST_PACKET */ pr_debug("TEST_PACKET\n"); musb->bTestModeValue = MGC_M_TEST_PACKET; break; default: goto stall; } /* enter test mode after irq */ if (handled > 0) musb->bTestMode = TRUE; break; case USB_DEVICE_B_HNP_ENABLE: if (!musb->g.is_otg) goto stall; musb->g.b_hnp_enable = 1; musb_try_b_hnp_enable(musb); break; case USB_DEVICE_A_HNP_SUPPORT: if (!musb->g.is_otg) goto stall; musb->g.a_hnp_support = 1; break; case USB_DEVICE_A_ALT_HNP_SUPPORT: if (!musb->g.is_otg) goto stall; musb->g.a_alt_hnp_support = 1; break;stall: default: handled = -EINVAL; break; } break; case USB_RECIP_INTERFACE: break; case USB_RECIP_ENDPOINT:{ const u8 bEnd = pControlRequest->wIndex & 0x0f; struct musb_ep *pEnd; struct musb_hw_ep *ep; void __iomem *regs; int is_in; u16 csr; if (bEnd == 0 || bEnd >= MUSB_C_NUM_EPS || pControlRequest->wValue != USB_ENDPOINT_HALT) break; ep = musb->aLocalEnd + bEnd; regs = ep->regs; is_in = pControlRequest->wIndex & USB_DIR_IN; if (is_in) pEnd = &ep->ep_in; else pEnd = &ep->ep_out; if (!pEnd->desc) break; MGC_SelectEnd(pBase, bEnd); if (is_in) { csr = musb_readw(regs, MGC_O_HDRC_TXCSR); if (csr & MGC_M_TXCSR_FIFONOTEMPTY) csr |= MGC_M_TXCSR_FLUSHFIFO; csr |= MGC_M_TXCSR_P_SENDSTALL | MGC_M_TXCSR_CLRDATATOG | MGC_M_TXCSR_P_WZC_BITS; musb_writew(regs, MGC_O_HDRC_TXCSR, csr); } else { csr = musb_readw(regs, MGC_O_HDRC_RXCSR); csr |= MGC_M_RXCSR_P_SENDSTALL | MGC_M_RXCSR_FLUSHFIFO | MGC_M_RXCSR_CLRDATATOG | MGC_M_TXCSR_P_WZC_BITS; musb_writew(regs, MGC_O_HDRC_RXCSR, csr); } /* select ep0 again */ MGC_SelectEnd(pBase, 0); handled = 1; } break; default: /* class, vendor, etc ... delegate */ handled = 0; break; } break; default: /* delegate SET_CONFIGURATION, etc */ handled = 0; } } else handled = 0; return handled;}/* we have an ep0out data packet * Context: caller holds controller lock */static void ep0_rxstate(struct musb *this){ void __iomem *regs = this->control_ep->regs; struct usb_request *req; u16 tmp; req = next_ep0_request(this); /* read packet and ack; or stall because of gadget driver bug: * should have provided the rx buffer before setup() returned. */ if (req) { void *buf = req->buf + req->actual; unsigned len = req->length - req->actual; /* read the buffer */ tmp = musb_readb(regs, MGC_O_HDRC_COUNT0); if (tmp > len) { req->status = -EOVERFLOW; tmp = len; } musb_read_fifo(&this->aLocalEnd[0], tmp, buf); req->actual += tmp; tmp = MGC_M_CSR0_P_SVDRXPKTRDY; if (tmp < 64 || req->actual == req->length) { this->ep0_state = MGC_END0_STAGE_STATUSIN; tmp |= MGC_M_CSR0_P_DATAEND; } else req = NULL; } else tmp = MGC_M_CSR0_P_SVDRXPKTRDY | MGC_M_CSR0_P_SENDSTALL; musb_writew(regs, MGC_O_HDRC_CSR0, tmp); /* NOTE: we "should" hold off reporting DATAEND and going to * STATUSIN until after the completion handler decides whether * to issue a stall instead, since this hardware can do that. */ if (req) musb_g_ep0_giveback(this, req);}/* * transmitting to the host (IN), this code might be called from IRQ * and from kernel thread. * * Context: caller holds controller lock */static void ep0_txstate(struct musb *musb){ void __iomem *regs = musb->control_ep->regs; struct usb_request *pRequest = next_ep0_request(musb); u16 wCsrVal = MGC_M_CSR0_TXPKTRDY; u8 *pFifoSource; u8 wFifoCount; if (!pRequest) { // WARN_ON(1); DBG(2, "odd; csr0 %04x\n", musb_readw(regs, MGC_O_HDRC_CSR0)); return;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?