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

📄 omap_udc.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
				if (stat == 1) {					done(ep0, req, 0);					/* that may have STALLed ep0... */					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;					udc->ep0_pending = 0;				}			} else {				/* ack status stage of IN transfer */				UDC_EP_NUM_REG = 0;				if (req)					done(ep0, req, 0);			}		} else if (stat & UDC_STALL) {			UDC_CTRL_REG = UDC_CLR_HALT;			UDC_EP_NUM_REG = 0;		} else {			UDC_EP_NUM_REG = 0;		}	}	/* SETUP starts all control transfers */	if (irq_src & UDC_SETUP) {		union u {			u16			word[4];			struct usb_ctrlrequest	r;		} u;		int			status = -EINVAL;		struct omap_ep		*ep;		/* read the (latest) SETUP message */		do {			UDC_EP_NUM_REG = UDC_SETUP_SEL;			/* two bytes at a time */			u.word[0] = UDC_DATA_REG;			u.word[1] = UDC_DATA_REG;			u.word[2] = UDC_DATA_REG;			u.word[3] = UDC_DATA_REG;			UDC_EP_NUM_REG = 0;		} while (UDC_IRQ_SRC_REG & UDC_SETUP);#define	w_value		le16_to_cpup (&u.r.wValue)#define	w_index		le16_to_cpup (&u.r.wIndex)#define	w_length	le16_to_cpup (&u.r.wLength)		/* Delegate almost all control requests to the gadget driver,		 * except for a handful of ch9 status/feature requests that		 * hardware doesn't autodecode _and_ the gadget API hides.		 */		udc->ep0_in = (u.r.bRequestType & USB_DIR_IN) != 0;		udc->ep0_set_config = 0;		udc->ep0_pending = 1;		ep0->stopped = 0;		ep0->ackwait = 0;		switch (u.r.bRequest) {		case USB_REQ_SET_CONFIGURATION:			/* udc needs to know when ep != 0 is valid */			if (u.r.bRequestType != USB_RECIP_DEVICE)				goto delegate;			if (w_length != 0)				goto do_stall;			udc->ep0_set_config = 1;			udc->ep0_reset_config = (w_value == 0);			VDBG("set config %d\n", w_value);			/* update udc NOW since gadget driver may start			 * queueing requests immediately; clear config			 * later if it fails the request.			 */			if (udc->ep0_reset_config)				UDC_SYSCON2_REG = UDC_CLR_CFG;			else				UDC_SYSCON2_REG = UDC_DEV_CFG;			update_otg(udc);			goto delegate;		case USB_REQ_CLEAR_FEATURE:			/* clear endpoint halt */			if (u.r.bRequestType != USB_RECIP_ENDPOINT)				goto delegate;			if (w_value != USB_ENDPOINT_HALT					|| w_length != 0)				goto do_stall;			ep = &udc->ep[w_index & 0xf];			if (ep != ep0) {				if (w_index & USB_DIR_IN)					ep += 16;				if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC						|| !ep->desc)					goto do_stall;				use_ep(ep, 0);				UDC_CTRL_REG = 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;				}				/* NOTE:  assumes the host behaves sanely,				 * only clearing real halts.  Else we may				 * need to kill pending transfers and then				 * restart the queue... very messy for DMA!				 */			}			VDBG("%s halt cleared by host\n", ep->name);			goto ep0out_status_stage;		case USB_REQ_SET_FEATURE:			/* set endpoint halt */			if (u.r.bRequestType != USB_RECIP_ENDPOINT)				goto delegate;			if (w_value != USB_ENDPOINT_HALT					|| w_length != 0)				goto do_stall;			ep = &udc->ep[w_index & 0xf];			if (w_index & USB_DIR_IN)				ep += 16;			if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC					|| ep == ep0 || !ep->desc)				goto do_stall;			if (use_dma && ep->has_dma) {				/* this has rude side-effects (aborts) and				 * can't really work if DMA-IN is active				 */				DBG("%s host set_halt, NYET \n", ep->name);				goto do_stall;			}			use_ep(ep, 0);			/* can't halt if fifo isn't empty... */			UDC_CTRL_REG = UDC_CLR_EP;			UDC_CTRL_REG = UDC_SET_HALT;			VDBG("%s halted by host\n", ep->name);ep0out_status_stage:			status = 0;			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;			udc->ep0_pending = 0;			break;		case USB_REQ_GET_STATUS:			/* return interface status.  if we were pedantic,			 * we'd detect non-existent interfaces, and stall.			 */			if (u.r.bRequestType					!= (USB_DIR_IN|USB_RECIP_INTERFACE))				goto delegate;			/* return two zero bytes */			UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;			UDC_DATA_REG = 0;			UDC_CTRL_REG = UDC_SET_FIFO_EN;			UDC_EP_NUM_REG = UDC_EP_DIR;			status = 0;			VDBG("GET_STATUS, interface %d\n", w_index);			/* next, status stage */			break;		default:delegate:			/* activate the ep0out fifo right away */			if (!udc->ep0_in && w_length) {				UDC_EP_NUM_REG = 0;				UDC_CTRL_REG = UDC_SET_FIFO_EN;			}			/* gadget drivers see class/vendor specific requests,			 * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION},			 * and more			 */			VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",				u.r.bRequestType, u.r.bRequest,				w_value, w_index, w_length);#undef	w_value#undef	w_index#undef	w_length			/* The gadget driver may return an error here,			 * causing an immediate protocol stall.			 *			 * Else it must issue a response, either queueing a			 * response buffer for the DATA stage, or halting ep0			 * (causing a protocol stall, not a real halt).  A			 * zero length buffer means no DATA stage.			 *			 * It's fine to issue that response after the setup()			 * call returns, and this IRQ was handled.			 */			udc->ep0_setup = 1;			spin_unlock(&udc->lock);			status = udc->driver->setup (&udc->gadget, &u.r);			spin_lock(&udc->lock);			udc->ep0_setup = 0;		}		if (status < 0) {do_stall:			VDBG("req %02x.%02x protocol STALL; stat %d\n",					u.r.bRequestType, u.r.bRequest, status);			if (udc->ep0_set_config) {				if (udc->ep0_reset_config)					WARN("error resetting config?\n");				else					UDC_SYSCON2_REG = UDC_CLR_CFG;			}			UDC_SYSCON2_REG = UDC_STALL_CMD;			udc->ep0_pending = 0;		}	}}/*-------------------------------------------------------------------------*/#define OTG_FLAGS (UDC_B_HNP_ENABLE|UDC_A_HNP_SUPPORT|UDC_A_ALT_HNP_SUPPORT)static void devstate_irq(struct omap_udc *udc, u16 irq_src){	u16	devstat, change;	devstat = UDC_DEVSTAT_REG;	change = devstat ^ udc->devstat;	udc->devstat = devstat;	if (change & (UDC_USB_RESET|UDC_ATT)) {		udc_quiesce(udc);		if (change & UDC_ATT) {			/* driver for any external transceiver will			 * have called omap_vbus_session() already			 */			if (devstat & UDC_ATT) {				udc->gadget.speed = USB_SPEED_FULL;				VDBG("connect\n");				if (!udc->transceiver)					pullup_enable(udc);				// if (driver->connect) call it			} else if (udc->gadget.speed != USB_SPEED_UNKNOWN) {				udc->gadget.speed = USB_SPEED_UNKNOWN;				if (!udc->transceiver)					pullup_disable(udc);				DBG("disconnect, gadget %s\n",					udc->driver->driver.name);				if (udc->driver->disconnect) {					spin_unlock(&udc->lock);					udc->driver->disconnect(&udc->gadget);					spin_lock(&udc->lock);				}			}			change &= ~UDC_ATT;		}		if (change & UDC_USB_RESET) {			if (devstat & UDC_USB_RESET) {				VDBG("RESET=1\n");			} else {				udc->gadget.speed = USB_SPEED_FULL;				INFO("USB reset done, gadget %s\n",					udc->driver->driver.name);				/* ep0 traffic is legal from now on */				UDC_IRQ_EN_REG = UDC_DS_CHG_IE | UDC_EP0_IE;			}			change &= ~UDC_USB_RESET;		}	}	if (change & UDC_SUS) {		if (udc->gadget.speed != USB_SPEED_UNKNOWN) {			// FIXME tell isp1301 to suspend/resume (?)			if (devstat & UDC_SUS) {				VDBG("suspend\n");				update_otg(udc);				/* HNP could be under way already */				if (udc->gadget.speed == USB_SPEED_FULL						&& udc->driver->suspend) {					spin_unlock(&udc->lock);					udc->driver->suspend(&udc->gadget);					spin_lock(&udc->lock);				}				if (udc->transceiver)					otg_set_suspend(udc->transceiver, 1);			} else {				VDBG("resume\n");				if (udc->transceiver)					otg_set_suspend(udc->transceiver, 0);				if (udc->gadget.speed == USB_SPEED_FULL						&& udc->driver->resume) {					spin_unlock(&udc->lock);					udc->driver->resume(&udc->gadget);					spin_lock(&udc->lock);				}			}		}		change &= ~UDC_SUS;	}	if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) {		update_otg(udc);		change &= ~OTG_FLAGS;	}	change &= ~(UDC_CFG|UDC_DEF|UDC_ADD);	if (change)		VDBG("devstat %03x, ignore change %03x\n",			devstat,  change);	UDC_IRQ_SRC_REG = UDC_DS_CHG;}static irqreturn_tomap_udc_irq(int irq, void *_udc, struct pt_regs *r){	struct omap_udc	*udc = _udc;	u16		irq_src;	irqreturn_t	status = IRQ_NONE;	unsigned long	flags;	spin_lock_irqsave(&udc->lock, flags);	irq_src = UDC_IRQ_SRC_REG;	/* Device state change (usb ch9 stuff) */	if (irq_src & UDC_DS_CHG) {		devstate_irq(_udc, irq_src);		status = IRQ_HANDLED;		irq_src &= ~UDC_DS_CHG;	}	/* EP0 control transfers */	if (irq_src & (UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX)) {		ep0_irq(_udc, irq_src);		status = IRQ_HANDLED;		irq_src &= ~(UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX);	}	/* DMA transfer completion */	if (use_dma && (irq_src & (UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT))) {		dma_irq(_udc, irq_src);		status = IRQ_HANDLED;		irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT);	}	irq_src &= ~(UDC_SOF|UDC_EPN_TX|UDC_EPN_RX);	if (irq_src)		DBG("udc_irq, unhandled %03x\n", irq_src);	spin_unlock_irqrestore(&udc->lock, flags);	return status;}/* workaround for seemingly-lost IRQs for RX ACKs... */#define PIO_OUT_TIMEOUT	(jiffies + HZ/3)#define HALF_FULL(f)	(!((f)&(UDC_NON_ISO_FIFO_FULL|UDC_NON_ISO_FIFO_EMPTY)))static void pio_out_timer(unsigned long _ep){	struct omap_ep	*ep = (void *) _ep;	unsigned long	flags;	u16		stat_flg;	spin_lock_irqsave(&ep->udc->lock, flags);	if (!list_empty(&ep->queue) && ep->ackwait) {		use_ep(ep, 0);		stat_flg = UDC_STAT_FLG_REG;		if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN)				|| (ep->double_buf && HALF_FULL(stat_flg)))) {			struct omap_req	*req;			VDBG("%s: lose, %04x\n", ep->ep.name, stat_flg);			req = container_of(ep->queue.next,					struct omap_req, queue);			UDC_EP_NUM_REG = ep->bEndpointAddress | UDC_EP_SEL;			(void) read_fifo(ep, req);			UDC_EP_NUM_REG = ep->bEndpointAddress;			UDC_CTRL_REG = UDC_SET_FIFO_EN;			ep->ackwait = 1 + ep->double_buf;		}	}	mod_timer(&ep->timer, PIO_OUT_TIMEOUT);	spin_unlock_irqrestore(&ep->udc->lock, flags);}static irqreturn_tomap_udc_pio_irq(int irq, void *_dev, struct pt_regs *r){	u16		epn_stat, irq_src;	irqreturn_t	status = IRQ_NONE;	struct omap_ep	*ep;	int		epnum;	struct omap_udc	*udc = _dev;	struct omap_req	*req;	unsigned long	flags;	spin_lock_irqsave(&udc->lock, flags);	epn_stat = UDC_EPN_STAT_REG;	irq_src = UDC_IRQ_SRC_REG;	/* handle OUT first, to avoid some wasteful NAKs */	if (irq_src & UDC_EPN_RX) {		epnum = (epn_stat >> 8) & 0x0f;		UDC_IRQ_SRC_REG = UDC_EPN_RX;		status = IRQ_HANDLED;		ep = &udc->ep[epnum];		ep->irqs++;		UDC_EP_NUM_REG = epnum | UDC_EP_SEL;		ep->fnf = 0;		if ((UDC_STAT_FLG_REG & UDC_ACK)) {			ep->ackwait--;			if (!list_empty(&ep->queue)) {				int stat;				req = container_of(ep->queue.next,						struct omap_req, queue);				stat = read_fifo(ep, req);				if (!ep->double_buf)					ep->fnf = 1;			}		}		/* min 6 clock delay before clearing EP_SEL ... */		epn_stat = UDC_EPN_STAT_REG;		epn_stat = UDC_EPN_STAT_REG;		UDC_EP_NUM_REG = epnum;		/* enabling fifo _after_ clearing ACK, contrary to docs,		 * reduces lossage; timer still needed though (sigh).		 */		if (ep->fnf) {			UDC_CTRL_REG = UDC_SET_FIFO_EN;			ep->ackwait = 1 + ep->double_buf;		}		mod_timer(&ep->timer, PIO_OUT_TIMEOUT);	}	/* then IN transfers */	else if (irq_src & UDC_EPN_TX) {		epnum = epn_stat & 0x0f;		UDC_IRQ_SRC_REG = UDC_EPN_TX;		status = IRQ_HANDLED;		ep = &udc->ep[16 + epnum];		ep->irqs++;		UDC_EP_NUM_REG = epnum | UDC_EP_DIR | UDC_EP_SEL;		if ((UDC_STAT_FLG_REG & UDC_ACK)) {			ep->ackwait = 0;			if (!list_empty(&ep->queue)) {				req = container_of(ep->queue.next,						struct omap_req, queue);				(void) write_fifo(ep, req);			}		}		/* min 6 clock delay before clearing EP_SEL ... */		epn_stat = UDC_EPN_STAT_REG;		epn_stat = UDC_EPN_STAT_REG;		UDC_EP_NUM_REG = epnum | UDC_EP_DIR;		/* then 6 clocks before it'd tx */	}	spin_unlock_irqrestore(&udc->lock, flags);	return status;}#ifdef	USE_ISOstatic irqreturn_tomap_udc_iso_irq(int irq, void *_dev, struct pt_regs *r){	struct omap_udc	*udc = _dev;	struct omap_ep	*ep;	int		pending = 0;	unsigned long	flags;	spin_lock_irqsave(&udc->lock, flags);	/* handle all non-DMA ISO transfers */	list_for_each_entry (ep, &udc->iso, iso) {		u16		stat;		struct omap_req	*req;		if (ep->has_dma || list_empty(&ep->queue))			continue;		req = list_entry(ep->queue.next, struct omap_req, queue);		use_ep(ep, UDC_EP_SEL);		stat = UDC_STAT_FLG_REG;		/* NOTE: like the other controller drivers, this isn't		 * currently reporting lost or damaged frames.		 */		if (ep->bEndpointAddress & USB_DIR_IN) {			if (stat & UDC_MISS_IN)				/* done(ep, req, -EPROTO) */;			else				write_fifo(ep, req);		} else {			int	status = 0;			if (stat & UDC_NO_RXPACKET)				status = -EREMOTEIO;			else if (stat & UDC_ISO_ERR)				status = -EILSEQ;			else if (stat & UDC_DATA_FLUSH)				status = -ENOSR;			if (status)				/* done(ep, req, status) */;			else				read_fifo(ep, req);		}		deselect_ep();		/* 6 wait states before next EP */

⌨️ 快捷键说明

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