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

📄 omap_udc.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 5 页
字号:
		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 */
				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);
				}
			} else {
				VDBG("resume\n");
				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_t
omap_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_t
omap_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++;

⌨️ 快捷键说明

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