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

📄 goku_udc.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
		command(ep->dev->regs, COMMAND_STALL, ep->num);
		readl(ep->reg_status);
	}
	spin_unlock_irqrestore(&ep->dev->lock, flags);
	return retval;
}

static int goku_fifo_status(struct usb_ep *_ep)
{
	struct goku_ep			*ep;
	struct goku_udc_regs __iomem	*regs;
	u32				size;

	if (!_ep)
		return -ENODEV;
	ep = container_of(_ep, struct goku_ep, ep);

	/* size is only reported sanely for OUT */
	if (ep->is_in)
		return -EOPNOTSUPP;

	/* ignores 16-byte dma buffer; SizeH == 0 */
	regs = ep->dev->regs;
	size = readl(&regs->EPxSizeLA[ep->num]) & DATASIZE;
	size += readl(&regs->EPxSizeLB[ep->num]) & DATASIZE;
	VDBG(ep->dev, "%s %s %u\n", __FUNCTION__, ep->ep.name, size);
	return size;
}

static void goku_fifo_flush(struct usb_ep *_ep)
{
	struct goku_ep			*ep;
	struct goku_udc_regs __iomem	*regs;
	u32				size;

	if (!_ep)
		return;
	ep = container_of(_ep, struct goku_ep, ep);
	VDBG(ep->dev, "%s %s\n", __FUNCTION__, ep->ep.name);

	/* don't change EPxSTATUS_EP_INVALID to READY */
	if (!ep->desc && ep->num != 0) {
		DBG(ep->dev, "%s %s inactive?\n", __FUNCTION__, ep->ep.name);
		return;
	}

	regs = ep->dev->regs;
	size = readl(&regs->EPxSizeLA[ep->num]);
	size &= DATASIZE;

	/* Non-desirable behavior:  FIFO_CLEAR also clears the
	 * endpoint halt feature.  For OUT, we _could_ just read
	 * the bytes out (PIO, if !ep->dma); for in, no choice.
	 */
	if (size)
		command(regs, COMMAND_FIFO_CLEAR, ep->num);
}

static struct usb_ep_ops goku_ep_ops = {
	.enable		= goku_ep_enable,
	.disable	= goku_ep_disable,

	.alloc_request	= goku_alloc_request,
	.free_request	= goku_free_request,

	.alloc_buffer	= goku_alloc_buffer,
	.free_buffer	= goku_free_buffer,

	.queue		= goku_queue,
	.dequeue	= goku_dequeue,

	.set_halt	= goku_set_halt,
	.fifo_status	= goku_fifo_status,
	.fifo_flush	= goku_fifo_flush,
};

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

static int goku_get_frame(struct usb_gadget *_gadget)
{
	return -EOPNOTSUPP;
}

static const struct usb_gadget_ops goku_ops = {
	.get_frame	= goku_get_frame,
	// no remote wakeup
	// not selfpowered
};

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

static inline char *dmastr(void)
{
	if (use_dma == 0)
		return "(dma disabled)";
	else if (use_dma == 2)
		return "(dma IN and OUT)";
	else
		return "(dma IN)";
}

#ifdef CONFIG_USB_GADGET_DEBUG_FILES

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

#define FOURBITS "%s%s%s%s"
#define EIGHTBITS FOURBITS FOURBITS

static void
dump_intmask(const char *label, u32 mask, char **next, unsigned *size)
{
	int t;

	/* int_status is the same format ... */
	t = scnprintf(*next, *size,
		"%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
		label, mask,
		(mask & INT_PWRDETECT) ? " power" : "",
		(mask & INT_SYSERROR) ? " sys" : "",
		(mask & INT_MSTRDEND) ? " in-dma" : "",
		(mask & INT_MSTWRTMOUT) ? " wrtmo" : "",

		(mask & INT_MSTWREND) ? " out-dma" : "",
		(mask & INT_MSTWRSET) ? " wrset" : "",
		(mask & INT_ERR) ? " err" : "",
		(mask & INT_SOF) ? " sof" : "",

		(mask & INT_EP3NAK) ? " ep3nak" : "",
		(mask & INT_EP2NAK) ? " ep2nak" : "",
		(mask & INT_EP1NAK) ? " ep1nak" : "",
		(mask & INT_EP3DATASET) ? " ep3" : "",

		(mask & INT_EP2DATASET) ? " ep2" : "",
		(mask & INT_EP1DATASET) ? " ep1" : "",
		(mask & INT_STATUSNAK) ? " ep0snak" : "",
		(mask & INT_STATUS) ? " ep0status" : "",

		(mask & INT_SETUP) ? " setup" : "",
		(mask & INT_ENDPOINT0) ? " ep0" : "",
		(mask & INT_USBRESET) ? " reset" : "",
		(mask & INT_SUSPEND) ? " suspend" : "");
	*size -= t;
	*next += t;
}


static int
udc_proc_read(char *buffer, char **start, off_t off, int count,
		int *eof, void *_dev)
{
	char				*buf = buffer;
	struct goku_udc			*dev = _dev;
	struct goku_udc_regs __iomem	*regs = dev->regs;
	char				*next = buf;
	unsigned			size = count;
	unsigned long			flags;
	int				i, t, is_usb_connected;
	u32				tmp;

	if (off != 0)
		return 0;

	local_irq_save(flags);

	/* basic device status */
	tmp = readl(&regs->power_detect);
	is_usb_connected = tmp & PW_DETECT;
	t = scnprintf(next, size,
		"%s - %s\n"
		"%s version: %s %s\n"
		"Gadget driver: %s\n"
		"Host %s, %s\n"
		"\n",
		pci_name(dev->pdev), driver_desc,
		driver_name, DRIVER_VERSION, dmastr(),
		dev->driver ? dev->driver->driver.name : "(none)",
		is_usb_connected
			? ((tmp & PW_PULLUP) ? "full speed" : "powered")
			: "disconnected",
		({char *tmp;
		switch(dev->ep0state){
		case EP0_DISCONNECT:	tmp = "ep0_disconnect"; break;
		case EP0_IDLE:		tmp = "ep0_idle"; break;
		case EP0_IN:		tmp = "ep0_in"; break;
		case EP0_OUT:		tmp = "ep0_out"; break;
		case EP0_STATUS:	tmp = "ep0_status"; break;
		case EP0_STALL:		tmp = "ep0_stall"; break;
		case EP0_SUSPEND:	tmp = "ep0_suspend"; break;
		default:		tmp = "ep0_?"; break;
		} tmp; })
		);
	size -= t;
	next += t;

	dump_intmask("int_status", readl(&regs->int_status), &next, &size);
	dump_intmask("int_enable", readl(&regs->int_enable), &next, &size);

	if (!is_usb_connected || !dev->driver || (tmp & PW_PULLUP) == 0)
		goto done;

	/* registers for (active) device and ep0 */
	t = scnprintf(next, size, "\nirqs %lu\ndataset %02x "
			"single.bcs %02x.%02x state %x addr %u\n",
			dev->irqs, readl(&regs->DataSet),
			readl(&regs->EPxSingle), readl(&regs->EPxBCS),
			readl(&regs->UsbState),
			readl(&regs->address));
	size -= t;
	next += t;

	tmp = readl(&regs->dma_master);
	t = scnprintf(next, size,
		"dma %03X =" EIGHTBITS "%s %s\n", tmp,
		(tmp & MST_EOPB_DIS) ? " eopb-" : "",
		(tmp & MST_EOPB_ENA) ? " eopb+" : "",
		(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
		(tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",

		(tmp & MST_RD_EOPB) ? " eopb" : "",
		(tmp & MST_RD_RESET) ? " in_reset" : "",
		(tmp & MST_WR_RESET) ? " out_reset" : "",
		(tmp & MST_RD_ENA) ? " IN" : "",

		(tmp & MST_WR_ENA) ? " OUT" : "",
		(tmp & MST_CONNECTION)
			? "ep1in/ep2out"
			: "ep1out/ep2in");
	size -= t;
	next += t;

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

		if (i && !ep->desc)
			continue;

		tmp = readl(ep->reg_status);
		t = scnprintf(next, size,
			"%s %s max %u %s, irqs %lu, "
			"status %02x (%s) " FOURBITS "\n",
			ep->ep.name,
			ep->is_in ? "in" : "out",
			ep->ep.maxpacket,
			ep->dma ? "dma" : "pio",
			ep->irqs,
			tmp, ({ char *s;
			switch (tmp & EPxSTATUS_EP_MASK) {
			case EPxSTATUS_EP_READY:
				s = "ready"; break;
			case EPxSTATUS_EP_DATAIN:
				s = "packet"; break;
			case EPxSTATUS_EP_FULL:
				s = "full"; break;
			case EPxSTATUS_EP_TX_ERR:	// host will retry
				s = "tx_err"; break;
			case EPxSTATUS_EP_RX_ERR:
				s = "rx_err"; break;
			case EPxSTATUS_EP_BUSY:		/* ep0 only */
				s = "busy"; break;
			case EPxSTATUS_EP_STALL:
				s = "stall"; break;
			case EPxSTATUS_EP_INVALID:	// these "can't happen"
				s = "invalid"; break;
			default:
				s = "?"; break;
			}; s; }),
			(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
			(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
			(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
			(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : ""
			);
		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) {
			if (ep->dma && req->queue.prev == &ep->queue) {
				if (i == UDC_MSTRD_ENDPOINT)
					tmp = readl(&regs->in_dma_current);
				else
					tmp = readl(&regs->out_dma_current);
				tmp -= req->req.dma;
				tmp++;
			} else
				tmp = req->req.actual;

			t = scnprintf(next, size,
				"\treq %p len %u/%u buf %p\n",
				&req->req, tmp, 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;
}

#endif	/* CONFIG_USB_GADGET_DEBUG_FILES */

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

static void udc_reinit (struct goku_udc *dev)
{
	static char *names [] = { "ep0", "ep1-bulk", "ep2-bulk", "ep3-bulk" };
	
	unsigned i;

	INIT_LIST_HEAD (&dev->gadget.ep_list);
	dev->gadget.ep0 = &dev->ep [0].ep;
	dev->gadget.speed = USB_SPEED_UNKNOWN;
	dev->ep0state = EP0_DISCONNECT;
	dev->irqs = 0;

	for (i = 0; i < 4; i++) {
		struct goku_ep	*ep = &dev->ep[i];

		ep->num = i;
		ep->ep.name = names[i];
		ep->reg_fifo = &dev->regs->ep_fifo [i];
		ep->reg_status = &dev->regs->ep_status [i];
		ep->reg_mode = &dev->regs->ep_mode[i];

		ep->ep.ops = &goku_ep_ops;
		list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
		ep->dev = dev;
		INIT_LIST_HEAD (&ep->queue);

		ep_reset(NULL, ep);
	}

	dev->ep[0].reg_mode = NULL;
	dev->ep[0].ep.maxpacket = MAX_EP0_SIZE;
	list_del_init (&dev->ep[0].ep.ep_list);
}

static void udc_reset(struct goku_udc *dev)
{
	struct goku_udc_regs __iomem	*regs = dev->regs;

	writel(0, &regs->power_detect);
	writel(0, &regs->int_enable);
	readl(&regs->int_enable);
	dev->int_enable = 0;

	/* deassert reset, leave USB D+ at hi-Z (no pullup)
	 * don't let INT_PWRDETECT sequence begin
	 */
	udelay(250);
	writel(PW_RESETB, &regs->power_detect);
	readl(&regs->int_enable);
}

static void ep0_start(struct goku_udc *dev)
{
	struct goku_udc_regs __iomem	*regs = dev->regs;
	unsigned			i;

	VDBG(dev, "%s\n", __FUNCTION__);

	udc_reset(dev);
	udc_reinit (dev);
	//writel(MST_EOPB_ENA | MST_TIMEOUT_ENA, &regs->dma_master);

	/* hw handles set_address, set_feature, get_status; maybe more */
	writel(   G_REQMODE_SET_INTF | G_REQMODE_GET_INTF
		| G_REQMODE_SET_CONF | G_REQMODE_GET_CONF
		| G_REQMODE_GET_DESC
		| G_REQMODE_CLEAR_FEAT
		, &regs->reqmode);

	for (i = 0; i < 4; i++)
		dev->ep[i].irqs = 0;

	/* can't modify descriptors after writing UsbReady */
	for (i = 0; i < DESC_LEN; i++)
		writel(0, &regs->descriptors[i]);
	writel(0, &regs->UsbReady);

	/* expect ep0 requests when the host drops reset */
	writel(PW_RESETB | PW_PULLUP, &regs->power_detect);
	dev->int_enable = INT_DEVWIDE | INT_EP0;
	writel(dev->int_enable, &dev->regs->int_enable);
	readl(&regs->int_enable);
	dev->gadget.speed = USB_SPEED_FULL;
	dev->ep0state = EP0_IDLE;
}

static void udc_enable(struct goku_udc *dev)
{
	/* start enumeration now, or after power detect irq */
	if (readl(&dev->regs->power_detect) & PW_DETECT)
		ep0_start(dev);
	else {
		DBG(dev, "%s\n", __FUNCTION__);
		dev->int_enable = INT_PWRDETECT;
		writel(dev->int_enable, &dev->regs->int_enable);
	}
}

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

/* keeping it simple:
 * - one bus driver, initted first;
 * - one function driver, initted second
 */

static struct goku_udc	*the_controller;

/* when a driver is successfully registered, it will receive
 * control requests including set_configuration(), which enables
 * non-control requests.  then usb traffic follows until a
 * disconnect is reported.  then a host may connect again, or
 * the driver might get unbound.
 */
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
	struct goku_udc	*dev = the_controller;
	int			retval;

	if (!driver
			|| driver->speed != USB_SPEED_FULL
			|| !driver->bind
			|| !driver->unbind
			|| !driver->disconnect
			|| !driver->setup)
		return -EINVAL;
	if (!dev)
		return -ENODEV;
	if (dev->driver)
		return -EBUSY;

	/* hook up the driver */
	driver->driver.bus = NULL;
	dev->driver = driver;
	dev->gadget.dev.driver = &driver->driver;
	retval = driver->bind(&dev->gadget);
	if (retval) {
		DBG(dev, "bind to driver %s --> error %d\n",
				driver->driver.name, retval);
		dev->driver = NULL;
		dev->gadget.dev.driver = NULL;
		return retval;
	}

	/* then enable host detection and ep0; and we're ready
	 * for set_configuration as well as eventual disconnect.
	 */
	udc_enable(dev);

	DBG(dev, "registered gadget driver '%s'\n", driver->driver.name);
	return 0;
}
EXPORT_SYMBOL(usb_gadget_register_driver);

static void
stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver)
{
	unsigned	i;

	DBG (dev, "%s\n", __FUNCTION__);

	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
		driver = NULL;

	/* disconnect gadget driver after quiesceing hw and the driver */
	udc_reset (dev);
	for (i = 0; i < 4; i++)
		nuke(&dev->ep [i], -ESHUTDOWN);
	if (driver) {
		spin_unlock(&dev->lock);
		driver->disconnect(&dev->gadget);
		spin_lock(&dev->lock);
	}

	if (dev->driver)
		udc_enable(dev);
}

int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{

⌨️ 快捷键说明

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