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

📄 dummy_hcd.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * dummy_hcd.c -- Dummy/Loopback USB host and device emulator driver. * * Maintainer: Alan Stern <stern@rowland.harvard.edu> * * Copyright (C) 2003 David Brownell * Copyright (C) 2003-2005 Alan Stern * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *//* * This exposes a device side "USB gadget" API, driven by requests to a * Linux-USB host controller driver.  USB traffic is simulated; there's * no need for USB hardware.  Use this with two other drivers: * *  - Gadget driver, responding to requests (slave); *  - Host-side device driver, as already familiar in Linux. * * Having this all in one kernel can help some stages of development, * bypassing some hardware (and driver) issues.  UML could help too. */#define DEBUG#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/usb.h>#include <linux/usb_gadget.h>#include <asm/byteorder.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/unaligned.h>#include "../core/hcd.h"#define DRIVER_DESC	"USB Host+Gadget Emulator"#define DRIVER_VERSION	"02 May 2005"static const char	driver_name [] = "dummy_hcd";static const char	driver_desc [] = "USB Host+Gadget Emulator";static const char	gadget_name [] = "dummy_udc";MODULE_DESCRIPTION (DRIVER_DESC);MODULE_AUTHOR ("David Brownell");MODULE_LICENSE ("GPL");/*-------------------------------------------------------------------------*//* gadget side driver data structres */struct dummy_ep {	struct list_head		queue;	unsigned long			last_io;	/* jiffies timestamp */	struct usb_gadget		*gadget;	const struct usb_endpoint_descriptor *desc;	struct usb_ep			ep;	unsigned			halted : 1;	unsigned			already_seen : 1;	unsigned			setup_stage : 1;};struct dummy_request {	struct list_head		queue;		/* ep's requests */	struct usb_request		req;};static inline struct dummy_ep *usb_ep_to_dummy_ep (struct usb_ep *_ep){	return container_of (_ep, struct dummy_ep, ep);}static inline struct dummy_request *usb_request_to_dummy_request		(struct usb_request *_req){	return container_of (_req, struct dummy_request, req);}/*-------------------------------------------------------------------------*//* * Every device has ep0 for control requests, plus up to 30 more endpoints, * in one of two types: * *   - Configurable:  direction (in/out), type (bulk, iso, etc), and endpoint *     number can be changed.  Names like "ep-a" are used for this type. * *   - Fixed Function:  in other cases.  some characteristics may be mutable; *     that'd be hardware-specific.  Names like "ep12out-bulk" are used. * * Gadget drivers are responsible for not setting up conflicting endpoint * configurations, illegal or unsupported packet lengths, and so on. */static const char ep0name [] = "ep0";static const char *const ep_name [] = {	ep0name,				/* everyone has ep0 */	/* act like a net2280: high speed, six configurable endpoints */	"ep-a", "ep-b", "ep-c", "ep-d", "ep-e", "ep-f",	/* or like pxa250: fifteen fixed function endpoints */	"ep1in-bulk", "ep2out-bulk", "ep3in-iso", "ep4out-iso", "ep5in-int",	"ep6in-bulk", "ep7out-bulk", "ep8in-iso", "ep9out-iso", "ep10in-int",	"ep11in-bulk", "ep12out-bulk", "ep13in-iso", "ep14out-iso",		"ep15in-int",	/* or like sa1100: two fixed function endpoints */	"ep1out-bulk", "ep2in-bulk",};#define DUMMY_ENDPOINTS	(sizeof(ep_name)/sizeof(char *))/*-------------------------------------------------------------------------*/#define FIFO_SIZE		64struct urbp {	struct urb		*urb;	struct list_head	urbp_list;};enum dummy_rh_state {	DUMMY_RH_RESET,	DUMMY_RH_SUSPENDED,	DUMMY_RH_RUNNING};struct dummy {	spinlock_t			lock;	/*	 * SLAVE/GADGET side support	 */	struct dummy_ep			ep [DUMMY_ENDPOINTS];	int				address;	struct usb_gadget		gadget;	struct usb_gadget_driver	*driver;	struct dummy_request		fifo_req;	u8				fifo_buf [FIFO_SIZE];	u16				devstatus;	unsigned			udc_suspended:1;	unsigned			pullup:1;	unsigned			active:1;	unsigned			old_active:1;	/*	 * MASTER/HOST side support	 */	enum dummy_rh_state		rh_state;	struct timer_list		timer;	u32				port_status;	u32				old_status;	unsigned			resuming:1;	unsigned long			re_timeout;	struct usb_device		*udev;	struct list_head		urbp_list;};static inline struct dummy *hcd_to_dummy (struct usb_hcd *hcd){	return (struct dummy *) (hcd->hcd_priv);}static inline struct usb_hcd *dummy_to_hcd (struct dummy *dum){	return container_of((void *) dum, struct usb_hcd, hcd_priv);}static inline struct device *dummy_dev (struct dummy *dum){	return dummy_to_hcd(dum)->self.controller;}static inline struct device *udc_dev (struct dummy *dum){	return dum->gadget.dev.parent;}static inline struct dummy *ep_to_dummy (struct dummy_ep *ep){	return container_of (ep->gadget, struct dummy, gadget);}static inline struct dummy *gadget_to_dummy (struct usb_gadget *gadget){	return container_of (gadget, struct dummy, gadget);}static inline struct dummy *gadget_dev_to_dummy (struct device *dev){	return container_of (dev, struct dummy, gadget.dev);}static struct dummy			*the_controller;/*-------------------------------------------------------------------------*//* SLAVE/GADGET SIDE UTILITY ROUTINES *//* called with spinlock held */static void nuke (struct dummy *dum, struct dummy_ep *ep){	while (!list_empty (&ep->queue)) {		struct dummy_request	*req;		req = list_entry (ep->queue.next, struct dummy_request, queue);		list_del_init (&req->queue);		req->req.status = -ESHUTDOWN;		spin_unlock (&dum->lock);		req->req.complete (&ep->ep, &req->req);		spin_lock (&dum->lock);	}}/* caller must hold lock */static voidstop_activity (struct dummy *dum){	struct dummy_ep	*ep;	/* prevent any more requests */	dum->address = 0;	/* The timer is left running so that outstanding URBs can fail */	/* nuke any pending requests first, so driver i/o is quiesced */	list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list)		nuke (dum, ep);	/* driver now does any non-usb quiescing necessary */}/* caller must hold lock */static voidset_link_state (struct dummy *dum){	dum->active = 0;	if ((dum->port_status & USB_PORT_STAT_POWER) == 0)		dum->port_status = 0;	/* UDC suspend must cause a disconnect */	else if (!dum->pullup || dum->udc_suspended) {		dum->port_status &= ~(USB_PORT_STAT_CONNECTION |					USB_PORT_STAT_ENABLE |					USB_PORT_STAT_LOW_SPEED |					USB_PORT_STAT_HIGH_SPEED |					USB_PORT_STAT_SUSPEND);		if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0)			dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);	} else {		dum->port_status |= USB_PORT_STAT_CONNECTION;		if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0)			dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);		if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0)			dum->port_status &= ~USB_PORT_STAT_SUSPEND;		else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&				dum->rh_state != DUMMY_RH_SUSPENDED)			dum->active = 1;	}	if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 || dum->active)		dum->resuming = 0;	if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 ||			(dum->port_status & USB_PORT_STAT_RESET) != 0) {		if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 &&				(dum->old_status & USB_PORT_STAT_RESET) == 0 &&				dum->driver) {			stop_activity (dum);			spin_unlock (&dum->lock);			dum->driver->disconnect (&dum->gadget);			spin_lock (&dum->lock);		}	} else if (dum->active != dum->old_active) {		if (dum->old_active && dum->driver->suspend) {			spin_unlock (&dum->lock);			dum->driver->suspend (&dum->gadget);			spin_lock (&dum->lock);		} else if (!dum->old_active && dum->driver->resume) {			spin_unlock (&dum->lock);			dum->driver->resume (&dum->gadget);			spin_lock (&dum->lock);		}	}	dum->old_status = dum->port_status;	dum->old_active = dum->active;}/*-------------------------------------------------------------------------*//* SLAVE/GADGET SIDE DRIVER * * This only tracks gadget state.  All the work is done when the host * side tries some (emulated) i/o operation.  Real device controller * drivers would do real i/o using dma, fifos, irqs, timers, etc. */#define is_enabled(dum) \	(dum->port_status & USB_PORT_STAT_ENABLE)static intdummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){	struct dummy		*dum;	struct dummy_ep		*ep;	unsigned		max;	int			retval;	ep = usb_ep_to_dummy_ep (_ep);	if (!_ep || !desc || ep->desc || _ep->name == ep0name			|| desc->bDescriptorType != USB_DT_ENDPOINT)		return -EINVAL;	dum = ep_to_dummy (ep);	if (!dum->driver || !is_enabled (dum))		return -ESHUTDOWN;	max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff;	/* drivers must not request bad settings, since lower levels	 * (hardware or its drivers) may not check.  some endpoints	 * can't do iso, many have maxpacket limitations, etc.	 *	 * since this "hardware" driver is here to help debugging, we	 * have some extra sanity checks.  (there could be more though,	 * especially for "ep9out" style fixed function ones.)	 */	retval = -EINVAL;	switch (desc->bmAttributes & 0x03) {	case USB_ENDPOINT_XFER_BULK:		if (strstr (ep->ep.name, "-iso")				|| strstr (ep->ep.name, "-int")) {			goto done;		}		switch (dum->gadget.speed) {		case USB_SPEED_HIGH:			if (max == 512)				break;			/* conserve return statements */		default:			switch (max) {			case 8: case 16: case 32: case 64:				/* we'll fake any legal size */				break;			default:		case USB_SPEED_LOW:				goto done;			}		}		break;	case USB_ENDPOINT_XFER_INT:		if (strstr (ep->ep.name, "-iso")) /* bulk is ok */			goto done;		/* real hardware might not handle all packet sizes */		switch (dum->gadget.speed) {		case USB_SPEED_HIGH:			if (max <= 1024)				break;			/* save a return statement */		case USB_SPEED_FULL:			if (max <= 64)				break;			/* save a return statement */		default:			if (max <= 8)				break;			goto done;		}		break;	case USB_ENDPOINT_XFER_ISOC:		if (strstr (ep->ep.name, "-bulk")				|| strstr (ep->ep.name, "-int"))			goto done;		/* real hardware might not handle all packet sizes */		switch (dum->gadget.speed) {		case USB_SPEED_HIGH:			if (max <= 1024)				break;			/* save a return statement */		case USB_SPEED_FULL:			if (max <= 1023)				break;			/* save a return statement */		default:			goto done;		}		break;	default:		/* few chips support control except on ep0 */		goto done;	}	_ep->maxpacket = max;	ep->desc = desc;	dev_dbg (udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n",		_ep->name,		desc->bEndpointAddress & 0x0f,		(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",		({ char *val;		 switch (desc->bmAttributes & 0x03) {		 case USB_ENDPOINT_XFER_BULK: val = "bulk"; break;		 case USB_ENDPOINT_XFER_ISOC: val = "iso"; break;		 case USB_ENDPOINT_XFER_INT: val = "intr"; break;		 default: val = "ctrl"; break;		 }; val; }),		max);	/* at this point real hardware should be NAKing transfers	 * to that endpoint, until a buffer is queued to it.	 */	retval = 0;done:	return retval;}static int dummy_disable (struct usb_ep *_ep){	struct dummy_ep		*ep;	struct dummy		*dum;	unsigned long		flags;	int			retval;	ep = usb_ep_to_dummy_ep (_ep);	if (!_ep || !ep->desc || _ep->name == ep0name)		return -EINVAL;	dum = ep_to_dummy (ep);	spin_lock_irqsave (&dum->lock, flags);	ep->desc = NULL;	retval = 0;	nuke (dum, ep);	spin_unlock_irqrestore (&dum->lock, flags);	dev_dbg (udc_dev(dum), "disabled %s\n", _ep->name);	return retval;}static struct usb_request *dummy_alloc_request (struct usb_ep *_ep, gfp_t mem_flags){	struct dummy_ep		*ep;	struct dummy_request	*req;	if (!_ep)		return NULL;	ep = usb_ep_to_dummy_ep (_ep);	req = kmalloc (sizeof *req, mem_flags);	if (!req)		return NULL;	memset (req, 0, sizeof *req);	INIT_LIST_HEAD (&req->queue);	return &req->req;}static voiddummy_free_request (struct usb_ep *_ep, struct usb_request *_req){	struct dummy_ep		*ep;	struct dummy_request	*req;	ep = usb_ep_to_dummy_ep (_ep);	if (!ep || !_req || (!ep->desc && _ep->name != ep0name))		return;	req = usb_request_to_dummy_request (_req);	WARN_ON (!list_empty (&req->queue));	kfree (req);}static void *dummy_alloc_buffer (	struct usb_ep *_ep,	unsigned bytes,	dma_addr_t *dma,	gfp_t mem_flags

⌨️ 快捷键说明

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