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