📄 dummy_hcd.c
字号:
/*
* 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/version.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 64
struct 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 void
stop_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 void
set_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 int
dummy_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, unsigned 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 void
dummy_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,
unsigned mem_flags
) {
char *retval;
struct dummy_ep *ep;
struct dummy *dum;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -