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 + -
显示快捷键?