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

📄 pxa2xx_udc.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 5 页
字号:
 * 	nuke - dequeue ALL requests
 */
static void nuke(struct pxa2xx_ep *ep, int status)
{
	struct pxa2xx_request *req;

	/* called with irqs blocked */
#ifdef	USE_DMA
	if (ep->dma >= 0 && !ep->stopped)
		cancel_dma(ep);
#endif
	while (!list_empty(&ep->queue)) {
		req = list_entry(ep->queue.next,
				struct pxa2xx_request,
				queue);
		done(ep, req, status);
	}
	if (ep->desc)
		pio_irq_disable (ep->bEndpointAddress);
}


/* dequeue JUST ONE request */
static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
	struct pxa2xx_ep	*ep;
	struct pxa2xx_request	*req;
	unsigned long		flags;

	ep = container_of(_ep, struct pxa2xx_ep, ep);
	if (!_ep || ep->ep.name == ep0name)
		return -EINVAL;

	local_irq_save(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) {
		local_irq_restore(flags);
		return -EINVAL;
	}

#ifdef	USE_DMA
	if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) {
		cancel_dma(ep);
		done(ep, req, -ECONNRESET);
		/* restart i/o */
		if (!list_empty(&ep->queue)) {
			req = list_entry(ep->queue.next,
					struct pxa2xx_request, queue);
			kick_dma(ep, req);
		}
	} else
#endif
		done(ep, req, -ECONNRESET);

	local_irq_restore(flags);
	return 0;
}

/*-------------------------------------------------------------------------*/

static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value)
{
	struct pxa2xx_ep	*ep;
	unsigned long		flags;

	ep = container_of(_ep, struct pxa2xx_ep, ep);
	if (unlikely (!_ep
			|| (!ep->desc && ep->ep.name != ep0name))
			|| ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
		DMSG("%s, bad ep\n", __FUNCTION__);
		return -EINVAL;
	}
	if (value == 0) {
		/* this path (reset toggle+halt) is needed to implement
		 * SET_INTERFACE on normal hardware.  but it can't be
		 * done from software on the PXA UDC, and the hardware
		 * forgets to do it as part of SET_INTERFACE automagic.
		 */
		DMSG("only host can clear %s halt\n", _ep->name);
		return -EROFS;
	}

	local_irq_save(flags);

	if ((ep->bEndpointAddress & USB_DIR_IN) != 0
			&& ((*ep->reg_udccs & UDCCS_BI_TFS) == 0
			   || !list_empty(&ep->queue))) {
		local_irq_restore(flags);
		return -EAGAIN;
	}

	/* FST bit is the same for control, bulk in, bulk out, interrupt in */
	*ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF;

	/* ep0 needs special care */
	if (!ep->desc) {
		start_watchdog(ep->dev);
		ep->dev->req_pending = 0;
		ep->dev->ep0state = EP0_STALL;

 	/* and bulk/intr endpoints like dropping stalls too */
 	} else {
 		unsigned i;
 		for (i = 0; i < 1000; i += 20) {
 			if (*ep->reg_udccs & UDCCS_BI_SST)
 				break;
 			udelay(20);
 		}
  	}
 	local_irq_restore(flags);

	DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
	return 0;
}

static int pxa2xx_ep_fifo_status(struct usb_ep *_ep)
{
	struct pxa2xx_ep        *ep;

	ep = container_of(_ep, struct pxa2xx_ep, ep);
	if (!_ep) {
		DMSG("%s, bad ep\n", __FUNCTION__);
		return -ENODEV;
	}
	/* pxa can't report unclaimed bytes from IN fifos */
	if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
		return -EOPNOTSUPP;
	if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN
			|| (*ep->reg_udccs & UDCCS_BO_RFS) == 0)
		return 0;
	else
		return (*ep->reg_ubcr & 0xfff) + 1;
}

static void pxa2xx_ep_fifo_flush(struct usb_ep *_ep)
{
	struct pxa2xx_ep        *ep;

	ep = container_of(_ep, struct pxa2xx_ep, ep);
	if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {
		DMSG("%s, bad ep\n", __FUNCTION__);
		return;
	}

	/* toggle and halt bits stay unchanged */

	/* for OUT, just read and discard the FIFO contents. */
	if ((ep->bEndpointAddress & USB_DIR_IN) == 0) {
		while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0)
			(void) *ep->reg_uddr;
		return;
	}

	/* most IN status is the same, but ISO can't stall */
	*ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR
		| (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
			? 0 : UDCCS_BI_SST;
}


static struct usb_ep_ops pxa2xx_ep_ops = {
	.enable		= pxa2xx_ep_enable,
	.disable	= pxa2xx_ep_disable,

	.alloc_request	= pxa2xx_ep_alloc_request,
	.free_request	= pxa2xx_ep_free_request,

	.alloc_buffer	= pxa2xx_ep_alloc_buffer,
	.free_buffer	= pxa2xx_ep_free_buffer,

	.queue		= pxa2xx_ep_queue,
	.dequeue	= pxa2xx_ep_dequeue,

	.set_halt	= pxa2xx_ep_set_halt,
	.fifo_status	= pxa2xx_ep_fifo_status,
	.fifo_flush	= pxa2xx_ep_fifo_flush,
};


/* ---------------------------------------------------------------------------
 * 	device-scoped parts of the api to the usb controller hardware
 * ---------------------------------------------------------------------------
 */

static int pxa2xx_udc_get_frame(struct usb_gadget *_gadget)
{
	return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff);
}

static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget)
{
	/* host may not have enabled remote wakeup */
	if ((UDCCS0 & UDCCS0_DRWF) == 0)
		return -EHOSTUNREACH;
	udc_set_mask_UDCCR(UDCCR_RSM);
	return 0;
}

static void stop_activity(struct pxa2xx_udc *, struct usb_gadget_driver *);
static void udc_enable (struct pxa2xx_udc *);
static void udc_disable(struct pxa2xx_udc *);

/* We disable the UDC -- and its 48 MHz clock -- whenever it's not
 * in active use.  
 */
static int pullup(struct pxa2xx_udc *udc, int is_active)
{
	is_active = is_active && udc->vbus && udc->pullup;
	DMSG("%s\n", is_active ? "active" : "inactive");
	if (is_active)
		udc_enable(udc);
	else {
		if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
			DMSG("disconnect %s\n", udc->driver
				? udc->driver->driver.name
				: "(no driver)");
			stop_activity(udc, udc->driver);
		}
		udc_disable(udc);
	}
	return 0;
}

/* VBUS reporting logically comes from a transceiver */
static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
{
	struct pxa2xx_udc	*udc;

	udc = container_of(_gadget, struct pxa2xx_udc, gadget);
	udc->vbus = is_active = (is_active != 0);
	DMSG("vbus %s\n", is_active ? "supplied" : "inactive");
	pullup(udc, is_active);
	return 0;
}

/* drivers may have software control over D+ pullup */
static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active)
{
	struct pxa2xx_udc	*udc;

	udc = container_of(_gadget, struct pxa2xx_udc, gadget);

	/* not all boards support pullup control */
	if (!udc->mach->udc_command)
		return -EOPNOTSUPP;

	is_active = (is_active != 0);
	udc->pullup = is_active;
	pullup(udc, is_active);
	return 0;
}

static const struct usb_gadget_ops pxa2xx_udc_ops = {
	.get_frame	= pxa2xx_udc_get_frame,
	.wakeup		= pxa2xx_udc_wakeup,
	.vbus_session	= pxa2xx_udc_vbus_session,
	.pullup		= pxa2xx_udc_pullup,

	// .vbus_draw ... boards may consume current from VBUS, up to
	// 100-500mA based on config.  the 500uA suspend ceiling means
	// that exclusively vbus-powered PXA designs violate USB specs.
};

/*-------------------------------------------------------------------------*/

#ifdef CONFIG_USB_GADGET_DEBUG_FILES

static const char proc_node_name [] = "driver/udc";

static int
udc_proc_read(char *page, char **start, off_t off, int count,
		int *eof, void *_dev)
{
	char			*buf = page;
	struct pxa2xx_udc	*dev = _dev;
	char			*next = buf;
	unsigned		size = count;
	unsigned long		flags;
	int			i, t;
	u32			tmp;

	if (off != 0)
		return 0;

	local_irq_save(flags);

	/* basic device status */
	t = scnprintf(next, size, DRIVER_DESC "\n"
		"%s version: %s\nGadget driver: %s\nHost %s\n\n",
		driver_name, DRIVER_VERSION SIZE_STR DMASTR,
		dev->driver ? dev->driver->driver.name : "(none)",
		is_vbus_present() ? "full speed" : "disconnected");
	size -= t;
	next += t;

	/* registers for device and ep0 */
	t = scnprintf(next, size,
		"uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
		UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
	size -= t;
	next += t;

	tmp = UDCCR;
	t = scnprintf(next, size,
		"udccr %02X =%s%s%s%s%s%s%s%s\n", tmp,
		(tmp & UDCCR_REM) ? " rem" : "",
		(tmp & UDCCR_RSTIR) ? " rstir" : "",
		(tmp & UDCCR_SRM) ? " srm" : "",
		(tmp & UDCCR_SUSIR) ? " susir" : "",
		(tmp & UDCCR_RESIR) ? " resir" : "",
		(tmp & UDCCR_RSM) ? " rsm" : "",
		(tmp & UDCCR_UDA) ? " uda" : "",
		(tmp & UDCCR_UDE) ? " ude" : "");
	size -= t;
	next += t;

	tmp = UDCCS0;
	t = scnprintf(next, size,
		"udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp,
		(tmp & UDCCS0_SA) ? " sa" : "",
		(tmp & UDCCS0_RNE) ? " rne" : "",
		(tmp & UDCCS0_FST) ? " fst" : "",
		(tmp & UDCCS0_SST) ? " sst" : "",
		(tmp & UDCCS0_DRWF) ? " dwrf" : "",
		(tmp & UDCCS0_FTF) ? " ftf" : "",
		(tmp & UDCCS0_IPR) ? " ipr" : "",
		(tmp & UDCCS0_OPR) ? " opr" : "");
	size -= t;
	next += t;

	if (dev->has_cfr) {
		tmp = UDCCFR;
		t = scnprintf(next, size,
			"udccfr %02X =%s%s\n", tmp,
			(tmp & UDCCFR_AREN) ? " aren" : "",
			(tmp & UDCCFR_ACM) ? " acm" : "");
		size -= t;
		next += t;
	}

	if (!is_vbus_present() || !dev->driver)
		goto done;

	t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
		dev->stats.write.bytes, dev->stats.write.ops,
		dev->stats.read.bytes, dev->stats.read.ops,
		dev->stats.irqs);
	size -= t;
	next += t;

	/* dump endpoint queues */
	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
		struct pxa2xx_ep	*ep = &dev->ep [i];
		struct pxa2xx_request	*req;
		int			t;

		if (i != 0) {
			const struct usb_endpoint_descriptor	*d;

			d = ep->desc;
			if (!d)
				continue;
			tmp = *dev->ep [i].reg_udccs;
			t = scnprintf(next, size,
				"%s max %d %s udccs %02x irqs %lu/%lu\n",
				ep->ep.name, le16_to_cpu (d->wMaxPacketSize),
				(ep->dma >= 0) ? "dma" : "pio", tmp,
				ep->pio_irqs, ep->dma_irqs);
			/* TODO translate all five groups of udccs bits! */

		} else /* ep0 should only have one transfer queued */
			t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n",
				ep->pio_irqs);
		if (t <= 0 || t > size)
			goto done;
		size -= t;
		next += t;

		if (list_empty(&ep->queue)) {
			t = scnprintf(next, size, "\t(nothing queued)\n");
			if (t <= 0 || t > size)
				goto done;
			size -= t;
			next += t;
			continue;
		}
		list_for_each_entry(req, &ep->queue, queue) {
#ifdef	USE_DMA
			if (ep->dma >= 0 && req->queue.prev == &ep->queue)
				t = scnprintf(next, size,
					"\treq %p len %d/%d "
					"buf %p (dma%d dcmd %08x)\n",
					&req->req, req->req.actual,
					req->req.length, req->req.buf,
					ep->dma, DCMD(ep->dma)
					// low 13 bits == bytes-to-go
					);
			else
#endif
				t = scnprintf(next, size,
					"\treq %p len %d/%d buf %p\n",
					&req->req, req->req.actual,
					req->req.length, req->req.buf);
			if (t <= 0 || t > size)
				goto done;
			size -= t;
			next += t;
		}
	}

done:
	local_irq_restore(flags);
	*eof = 1;
	return count - size;
}

#define create_proc_files() \
	create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
#define remove_proc_files() \
	remove_proc_entry(proc_node_name, NULL)

#else	/* !CONFIG_USB_GADGET_DEBUG_FILES */

#define create_proc_files() do {} while (0)
#define remove_proc_files() do {} while (0)

#endif	/* CONFIG_USB_GADGET_DEBUG_FILES */

/* "function" sysfs attribute */
static ssize_t
show_function (struct device *_dev, struct device_attribute *attr, char *buf)
{
	struct pxa2xx_udc	*dev = dev_get_drvdata (_dev);

	if (!dev->driver
			|| !dev->driver->function
			|| strlen (dev->driver->function) > PAGE_SIZE)
		return 0;
	return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
}
static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);

/*-------------------------------------------------------------------------*/

/*
 * 	udc_disable - disable USB device controller
 */
static void udc_disable(struct pxa2xx_udc *dev)
{
	/* block all irqs */
	udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM);
	UICR0 = UICR1 = 0xff;
	UFNRH = UFNRH_SIM;

	/* if hardware supports it, disconnect from usb */
	pullup_off();

	udc_clear_mask_UDCCR(UDCCR_UDE);

#ifdef	CONFIG_ARCH_PXA
        /* Disable clock for USB device */
	pxa_set_cken(CKEN11_USB, 0);
#endif

	ep0_idle (dev);
	dev->gadget.speed = USB_SPEED_UNKNOWN;
	LED_CONNECTED_OFF;
}


/*
 * 	udc_reinit - initialize software state
 */
static void udc_reinit(struct pxa2xx_udc *dev)
{
	u32	i;

	/* device/ep0 records init */
	INIT_LIST_HEAD (&dev->gadget.ep_list);
	INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
	dev->ep0state = EP0_IDLE;

	/* basic endpoint records init */
	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
		struct pxa2xx_ep *ep = &dev->ep[i];

		if (i != 0)
			list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);

		ep->desc = NULL;
		ep->stopped = 0;
		INIT_LIST_HEAD (&ep->queue);
		ep->pio_irqs = ep->dma_irqs = 0;
	}

⌨️ 快捷键说明

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