⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 omap_udc.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
	/* maybe kickstart non-iso i/o queues */	if (is_iso)		UDC_IRQ_EN_REG |= UDC_SOF_IE;	else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) {		int	is_in;		if (ep->bEndpointAddress == 0) {			if (!udc->ep0_pending || !list_empty (&ep->queue)) {				spin_unlock_irqrestore(&udc->lock, flags);				return -EL2HLT;			}			/* empty DATA stage? */			is_in = udc->ep0_in;			if (!req->req.length) {				/* chip became CONFIGURED or ADDRESSED				 * earlier; drivers may already have queued				 * requests to non-control endpoints				 */				if (udc->ep0_set_config) {					u16	irq_en = UDC_IRQ_EN_REG;					irq_en |= UDC_DS_CHG_IE | UDC_EP0_IE;					if (!udc->ep0_reset_config)						irq_en |= UDC_EPN_RX_IE							| UDC_EPN_TX_IE;					UDC_IRQ_EN_REG = irq_en;				}				/* STATUS for zero length DATA stages is				 * always an IN ... even for IN transfers,				 * a wierd case which seem to stall OMAP.				 */				UDC_EP_NUM_REG = (UDC_EP_SEL|UDC_EP_DIR);				UDC_CTRL_REG = UDC_CLR_EP;				UDC_CTRL_REG = UDC_SET_FIFO_EN;				UDC_EP_NUM_REG = UDC_EP_DIR;				/* cleanup */				udc->ep0_pending = 0;				done(ep, req, 0);				req = NULL;			/* non-empty DATA stage */			} else if (is_in) {				UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;			} else {				if (udc->ep0_setup)					goto irq_wait;				UDC_EP_NUM_REG = UDC_EP_SEL;			}		} else {			is_in = ep->bEndpointAddress & USB_DIR_IN;			if (!ep->has_dma)				use_ep(ep, UDC_EP_SEL);			/* if ISO: SOF IRQs must be enabled/disabled! */		}		if (ep->has_dma)			(is_in ? next_in_dma : next_out_dma)(ep, req);		else if (req) {			if ((is_in ? write_fifo : read_fifo)(ep, req) == 1)				req = NULL;			deselect_ep();			if (!is_in) {				UDC_CTRL_REG = UDC_SET_FIFO_EN;				ep->ackwait = 1 + ep->double_buf;			}			/* IN: 6 wait states before it'll tx */		}	}irq_wait:	/* irq handler advances the queue */	if (req != NULL)		list_add_tail(&req->queue, &ep->queue);	spin_unlock_irqrestore(&udc->lock, flags);	return 0;}static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req){	struct omap_ep	*ep = container_of(_ep, struct omap_ep, ep);	struct omap_req	*req;	unsigned long	flags;	if (!_ep || !_req)		return -EINVAL;	spin_lock_irqsave(&ep->udc->lock, flags);	/* make sure it's actually queued on this endpoint */	list_for_each_entry (req, &ep->queue, queue) {		if (&req->req == _req)			break;	}	if (&req->req != _req) {		spin_unlock_irqrestore(&ep->udc->lock, flags);		return -EINVAL;	}	if (use_dma && ep->dma_channel && ep->queue.next == &req->queue) {		int channel = ep->dma_channel;		/* releasing the channel cancels the request,		 * reclaiming the channel restarts the queue		 */		dma_channel_release(ep);		dma_channel_claim(ep, channel);	} else 		done(ep, req, -ECONNRESET);	spin_unlock_irqrestore(&ep->udc->lock, flags);	return 0;}/*-------------------------------------------------------------------------*/static int omap_ep_set_halt(struct usb_ep *_ep, int value){	struct omap_ep	*ep = container_of(_ep, struct omap_ep, ep);	unsigned long	flags;	int		status = -EOPNOTSUPP;	spin_lock_irqsave(&ep->udc->lock, flags);	/* just use protocol stalls for ep0; real halts are annoying */	if (ep->bEndpointAddress == 0) {		if (!ep->udc->ep0_pending)			status = -EINVAL;		else if (value) {			if (ep->udc->ep0_set_config) {				WARN("error changing config?\n");				UDC_SYSCON2_REG = UDC_CLR_CFG;			}			UDC_SYSCON2_REG = UDC_STALL_CMD;			ep->udc->ep0_pending = 0;			status = 0;		} else /* NOP */			status = 0;	/* otherwise, all active non-ISO endpoints can halt */	} else if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC && ep->desc) {		/* IN endpoints must already be idle */		if ((ep->bEndpointAddress & USB_DIR_IN)				&& !list_empty(&ep->queue)) { 			status = -EAGAIN;			goto done;		}		if (value) {			int	channel;			if (use_dma && ep->dma_channel					&& !list_empty(&ep->queue)) {				channel = ep->dma_channel;				dma_channel_release(ep);			} else				channel = 0;			use_ep(ep, UDC_EP_SEL);			if (UDC_STAT_FLG_REG & UDC_NON_ISO_FIFO_EMPTY) {				UDC_CTRL_REG = UDC_SET_HALT;				status = 0;			} else				status = -EAGAIN;			deselect_ep();			if (channel)				dma_channel_claim(ep, channel);		} else {			use_ep(ep, 0);			UDC_CTRL_REG = ep->udc->clr_halt;			ep->ackwait = 0;			if (!(ep->bEndpointAddress & USB_DIR_IN)) {				UDC_CTRL_REG = UDC_SET_FIFO_EN;				ep->ackwait = 1 + ep->double_buf;			}		}	}done:	VDBG("%s %s halt stat %d\n", ep->ep.name,		value ? "set" : "clear", status);	spin_unlock_irqrestore(&ep->udc->lock, flags);	return status;}static struct usb_ep_ops omap_ep_ops = {	.enable		= omap_ep_enable,	.disable	= omap_ep_disable,	.alloc_request	= omap_alloc_request,	.free_request	= omap_free_request,	.alloc_buffer	= omap_alloc_buffer,	.free_buffer	= omap_free_buffer,	.queue		= omap_ep_queue,	.dequeue	= omap_ep_dequeue,	.set_halt	= omap_ep_set_halt,	// fifo_status ... report bytes in fifo	// fifo_flush ... flush fifo};/*-------------------------------------------------------------------------*/static int omap_get_frame(struct usb_gadget *gadget){	u16	sof = UDC_SOF_REG;	return (sof & UDC_TS_OK) ? (sof & UDC_TS) : -EL2NSYNC;}static int omap_wakeup(struct usb_gadget *gadget){	struct omap_udc	*udc;	unsigned long	flags;	int		retval = -EHOSTUNREACH;	udc = container_of(gadget, struct omap_udc, gadget);	spin_lock_irqsave(&udc->lock, flags);	if (udc->devstat & UDC_SUS) {		/* NOTE:  OTG spec erratum says that OTG devices may		 * issue wakeups without host enable.		 */		if (udc->devstat & (UDC_B_HNP_ENABLE|UDC_R_WK_OK)) {			DBG("remote wakeup...\n");			UDC_SYSCON2_REG = UDC_RMT_WKP;			retval = 0;		}	/* NOTE:  non-OTG systems may use SRP TOO... */	} else if (!(udc->devstat & UDC_ATT)) {		if (udc->transceiver)			retval = otg_start_srp(udc->transceiver);	}	spin_unlock_irqrestore(&udc->lock, flags);	return retval;}static intomap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered){	struct omap_udc	*udc;	unsigned long	flags;	u16		syscon1;	udc = container_of(gadget, struct omap_udc, gadget);	spin_lock_irqsave(&udc->lock, flags);	syscon1 = UDC_SYSCON1_REG;	if (is_selfpowered)		syscon1 |= UDC_SELF_PWR;	else		syscon1 &= ~UDC_SELF_PWR;	UDC_SYSCON1_REG = syscon1;	spin_unlock_irqrestore(&udc->lock, flags);	return 0;}static int can_pullup(struct omap_udc *udc){	return udc->driver && udc->softconnect && udc->vbus_active;}static void pullup_enable(struct omap_udc *udc){	udc->gadget.dev.parent->power.power_state = PMSG_ON;	udc->gadget.dev.power.power_state = PMSG_ON;	UDC_SYSCON1_REG |= UDC_PULLUP_EN;#ifndef CONFIG_USB_OTG	if (!cpu_is_omap15xx())		OTG_CTRL_REG |= OTG_BSESSVLD;#endif	UDC_IRQ_EN_REG = UDC_DS_CHG_IE;}static void pullup_disable(struct omap_udc *udc){#ifndef CONFIG_USB_OTG	if (!cpu_is_omap15xx())		OTG_CTRL_REG &= ~OTG_BSESSVLD;#endif	UDC_IRQ_EN_REG = UDC_DS_CHG_IE;	UDC_SYSCON1_REG &= ~UDC_PULLUP_EN;}/* * Called by whatever detects VBUS sessions:  external transceiver * driver, or maybe GPIO0 VBUS IRQ.  May request 48 MHz clock. */static int omap_vbus_session(struct usb_gadget *gadget, int is_active){	struct omap_udc	*udc;	unsigned long	flags;	udc = container_of(gadget, struct omap_udc, gadget);	spin_lock_irqsave(&udc->lock, flags);	VDBG("VBUS %s\n", is_active ? "on" : "off");	udc->vbus_active = (is_active != 0);	if (cpu_is_omap15xx()) {		/* "software" detect, ignored if !VBUS_MODE_1510 */		if (is_active)			FUNC_MUX_CTRL_0_REG |= VBUS_CTRL_1510;		else			FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510;	}	if (can_pullup(udc))		pullup_enable(udc);	else		pullup_disable(udc);	spin_unlock_irqrestore(&udc->lock, flags);	return 0;}static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA){	struct omap_udc	*udc;	udc = container_of(gadget, struct omap_udc, gadget);	if (udc->transceiver)		return otg_set_power(udc->transceiver, mA);	return -EOPNOTSUPP;}static int omap_pullup(struct usb_gadget *gadget, int is_on){	struct omap_udc	*udc;	unsigned long	flags;	udc = container_of(gadget, struct omap_udc, gadget);	spin_lock_irqsave(&udc->lock, flags);	udc->softconnect = (is_on != 0);	if (can_pullup(udc))		pullup_enable(udc);	else		pullup_disable(udc);	spin_unlock_irqrestore(&udc->lock, flags);	return 0;}static struct usb_gadget_ops omap_gadget_ops = {	.get_frame		= omap_get_frame,	.wakeup			= omap_wakeup,	.set_selfpowered	= omap_set_selfpowered,	.vbus_session		= omap_vbus_session,	.vbus_draw		= omap_vbus_draw,	.pullup			= omap_pullup,};/*-------------------------------------------------------------------------*//* dequeue ALL requests; caller holds udc->lock */static void nuke(struct omap_ep *ep, int status){	struct omap_req	*req;	ep->stopped = 1;	if (use_dma && ep->dma_channel)		dma_channel_release(ep);	use_ep(ep, 0);	UDC_CTRL_REG = UDC_CLR_EP;	if (ep->bEndpointAddress && ep->bmAttributes != USB_ENDPOINT_XFER_ISOC)		UDC_CTRL_REG = UDC_SET_HALT;	while (!list_empty(&ep->queue)) {		req = list_entry(ep->queue.next, struct omap_req, queue);		done(ep, req, status);	}}/* caller holds udc->lock */static void udc_quiesce(struct omap_udc *udc){	struct omap_ep	*ep;	udc->gadget.speed = USB_SPEED_UNKNOWN;	nuke(&udc->ep[0], -ESHUTDOWN);	list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list)		nuke(ep, -ESHUTDOWN);}/*-------------------------------------------------------------------------*/static void update_otg(struct omap_udc *udc){	u16	devstat;	if (!udc->gadget.is_otg)		return;	if (OTG_CTRL_REG & OTG_ID)		devstat = UDC_DEVSTAT_REG;	else		devstat = 0;	udc->gadget.b_hnp_enable = !!(devstat & UDC_B_HNP_ENABLE);	udc->gadget.a_hnp_support = !!(devstat & UDC_A_HNP_SUPPORT);	udc->gadget.a_alt_hnp_support = !!(devstat & UDC_A_ALT_HNP_SUPPORT);	/* Enable HNP early, avoiding races on suspend irq path.	 * ASSUMES OTG state machine B_BUS_REQ input is true.	 */	if (udc->gadget.b_hnp_enable)		OTG_CTRL_REG = (OTG_CTRL_REG | OTG_B_HNPEN | OTG_B_BUSREQ)				& ~OTG_PULLUP;}static void ep0_irq(struct omap_udc *udc, u16 irq_src){	struct omap_ep	*ep0 = &udc->ep[0];	struct omap_req	*req = NULL;	ep0->irqs++;	/* Clear any pending requests and then scrub any rx/tx state	 * before starting to handle the SETUP request.	 */	if (irq_src & UDC_SETUP) {		u16	ack = irq_src & (UDC_EP0_TX|UDC_EP0_RX);		nuke(ep0, 0);		if (ack) {			UDC_IRQ_SRC_REG = ack;			irq_src = UDC_SETUP;		}	}	/* IN/OUT packets mean we're in the DATA or STATUS stage.  	 * This driver uses only uses protocol stalls (ep0 never halts),	 * and if we got this far the gadget driver already had a	 * chance to stall.  Tries to be forgiving of host oddities.	 *	 * NOTE:  the last chance gadget drivers have to stall control	 * requests is during their request completion callback.	 */	if (!list_empty(&ep0->queue))		req = container_of(ep0->queue.next, struct omap_req, queue);	/* IN == TX to host */	if (irq_src & UDC_EP0_TX) {		int	stat;		UDC_IRQ_SRC_REG = UDC_EP0_TX;		UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;		stat = UDC_STAT_FLG_REG;		if (stat & UDC_ACK) {			if (udc->ep0_in) {				/* write next IN packet from response,				 * or set up the status stage.				 */				if (req)					stat = write_fifo(ep0, req);				UDC_EP_NUM_REG = UDC_EP_DIR;				if (!req && udc->ep0_pending) {					UDC_EP_NUM_REG = UDC_EP_SEL;					UDC_CTRL_REG = UDC_CLR_EP;					UDC_CTRL_REG = UDC_SET_FIFO_EN;					UDC_EP_NUM_REG = 0;					udc->ep0_pending = 0;				} /* else:  6 wait states before it'll tx */			} else {				/* ack status stage of OUT transfer */				UDC_EP_NUM_REG = UDC_EP_DIR;				if (req)					done(ep0, req, 0);			}			req = NULL;		} else if (stat & UDC_STALL) {			UDC_CTRL_REG = UDC_CLR_HALT;			UDC_EP_NUM_REG = UDC_EP_DIR;		} else {			UDC_EP_NUM_REG = UDC_EP_DIR;		}	}	/* OUT == RX from host */	if (irq_src & UDC_EP0_RX) {		int	stat;		UDC_IRQ_SRC_REG = UDC_EP0_RX;		UDC_EP_NUM_REG = UDC_EP_SEL;		stat = UDC_STAT_FLG_REG;		if (stat & UDC_ACK) {			if (!udc->ep0_in) {				stat = 0;				/* read next OUT packet of request, maybe				 * reactiviting the fifo; stall on errors.				 */				if (!req || (stat = read_fifo(ep0, req)) < 0) {					UDC_SYSCON2_REG = UDC_STALL_CMD;					udc->ep0_pending = 0;					stat = 0;				} else if (stat == 0)					UDC_CTRL_REG = UDC_SET_FIFO_EN;				UDC_EP_NUM_REG = 0;								/* activate status stage */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -