Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds 2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

127
drivers/usb/Kconfig Normal file
View file

@ -0,0 +1,127 @@
#
# USB device configuration
#
menu "USB support"
# Host-side USB depends on having a host controller
# NOTE: dummy_hcd is always an option, but it's ignored here ...
# NOTE: SL-811 option should be board-specific ...
config USB_ARCH_HAS_HCD
boolean
default y if USB_ARCH_HAS_OHCI
default y if ARM # SL-811
default PCI
# many non-PCI SOC chips embed OHCI
config USB_ARCH_HAS_OHCI
boolean
# ARM:
default y if SA1111
default y if ARCH_OMAP
default y if ARCH_LH7A404
default y if PXA27x
# PPC:
default y if STB03xxx
default y if PPC_MPC52xx
# MIPS:
default y if SOC_AU1X00
# more:
default PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
config USB
tristate "Support for Host-side USB"
depends on USB_ARCH_HAS_HCD
---help---
Universal Serial Bus (USB) is a specification for a serial bus
subsystem which offers higher speeds and more features than the
traditional PC serial port. The bus supplies power to peripherals
and allows for hot swapping. Up to 127 USB peripherals can be
connected to a single USB host in a tree structure.
The USB host is the root of the tree, the peripherals are the
leaves and the inner nodes are special USB devices called hubs.
Most PCs now have USB host ports, used to connect peripherals
such as scanners, keyboards, mice, modems, cameras, disks,
flash memory, network links, and printers to the PC.
Say Y here if your computer has a host-side USB port and you want
to use USB devices. You then need to say Y to at least one of the
Host Controller Driver (HCD) options below. Choose a USB 1.1
controller, such as "UHCI HCD support" or "OHCI HCD support",
and "EHCI HCD (USB 2.0) support" except for older systems that
do not have USB 2.0 support. It doesn't normally hurt to select
them all if you are not certain.
If your system has a device-side USB port, used in the peripheral
side of the USB protocol, see the "USB Gadget" framework instead.
After choosing your HCD, then select drivers for the USB peripherals
you'll be using. You may want to check out the information provided
in <file:Documentation/usb/> and especially the links given in
<file:Documentation/usb/usb-help.txt>.
To compile this driver as a module, choose M here: the
module will be called usbcore.
source "drivers/usb/core/Kconfig"
source "drivers/usb/host/Kconfig"
source "drivers/usb/class/Kconfig"
source "drivers/usb/storage/Kconfig"
source "drivers/usb/input/Kconfig"
source "drivers/usb/image/Kconfig"
source "drivers/usb/media/Kconfig"
source "drivers/usb/net/Kconfig"
source "drivers/usb/mon/Kconfig"
comment "USB port drivers"
depends on USB
config USB_USS720
tristate "USS720 parport driver"
depends on USB && PARPORT
select PARPORT_NOT_PC
---help---
This driver is for USB parallel port adapters that use the Lucent
Technologies USS-720 chip. These cables are plugged into your USB
port and provide USB compatibility to peripherals designed with
parallel port interfaces.
The chip has two modes: automatic mode and manual mode. In automatic
mode, it looks to the computer like a standard USB printer. Only
printers may be connected to the USS-720 in this mode. The generic
USB printer driver ("USB Printer support", above) may be used in
that mode, and you can say N here if you want to use the chip only
in this mode.
Manual mode is not limited to printers, any parallel port
device should work. This driver utilizes manual mode.
Note however that some operations are three orders of magnitude
slower than on a PCI/ISA Parallel Port, so timing critical
applications might not work.
Say Y here if you own an USS-720 USB->Parport cable and intend to
connect anything other than a printer to it.
To compile this driver as a module, choose M here: the
module will be called uss720.
source "drivers/usb/serial/Kconfig"
source "drivers/usb/misc/Kconfig"
source "drivers/usb/atm/Kconfig"
source "drivers/usb/gadget/Kconfig"
endmenu

74
drivers/usb/Makefile Normal file
View file

@ -0,0 +1,74 @@
#
# Makefile for the kernel USB device drivers.
#
# Object files in subdirectories
obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
obj-$(CONFIG_ETRAX_USB_HOST) += host/
obj-$(CONFIG_USB_ACM) += class/
obj-$(CONFIG_USB_AUDIO) += class/
obj-$(CONFIG_USB_BLUETOOTH_TTY) += class/
obj-$(CONFIG_USB_MIDI) += class/
obj-$(CONFIG_USB_PRINTER) += class/
obj-$(CONFIG_USB_STORAGE) += storage/
obj-$(CONFIG_USB_AIPTEK) += input/
obj-$(CONFIG_USB_ATI_REMOTE) += input/
obj-$(CONFIG_USB_HID) += input/
obj-$(CONFIG_USB_KBD) += input/
obj-$(CONFIG_USB_KBTAB) += input/
obj-$(CONFIG_USB_MOUSE) += input/
obj-$(CONFIG_USB_MTOUCH) += input/
obj-$(CONFIG_USB_POWERMATE) += input/
obj-$(CONFIG_USB_WACOM) += input/
obj-$(CONFIG_USB_XPAD) += input/
obj-$(CONFIG_USB_DABUSB) += media/
obj-$(CONFIG_USB_DSBR) += media/
obj-$(CONFIG_USB_IBMCAM) += media/
obj-$(CONFIG_USB_KONICAWC) += media/
obj-$(CONFIG_USB_OV511) += media/
obj-$(CONFIG_USB_PWC) += media/
obj-$(CONFIG_USB_SE401) += media/
obj-$(CONFIG_USB_SN9C102) += media/
obj-$(CONFIG_USB_STV680) += media/
obj-$(CONFIG_USB_VICAM) += media/
obj-$(CONFIG_USB_W9968CF) += media/
obj-$(CONFIG_USB_CATC) += net/
obj-$(CONFIG_USB_KAWETH) += net/
obj-$(CONFIG_USB_PEGASUS) += net/
obj-$(CONFIG_USB_RTL8150) += net/
obj-$(CONFIG_USB_USBNET) += net/
obj-$(CONFIG_USB_ZD1201) += net/
obj-$(CONFIG_USB_MDC800) += image/
obj-$(CONFIG_USB_MICROTEK) += image/
obj-$(CONFIG_USB_SERIAL) += serial/
obj-$(CONFIG_USB_AUERSWALD) += misc/
obj-$(CONFIG_USB_CYTHERM) += misc/
obj-$(CONFIG_USB_EMI26) += misc/
obj-$(CONFIG_USB_EMI62) += misc/
obj-$(CONFIG_USB_IDMOUSE) += misc/
obj-$(CONFIG_USB_LCD) += misc/
obj-$(CONFIG_USB_LED) += misc/
obj-$(CONFIG_USB_LEGOTOWER) += misc/
obj-$(CONFIG_USB_RIO500) += misc/
obj-$(CONFIG_USB_TEST) += misc/
obj-$(CONFIG_USB_USS720) += misc/
obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/

54
drivers/usb/README Normal file
View file

@ -0,0 +1,54 @@
To understand all the Linux-USB framework, you'll use these resources:
* This source code. This is necessarily an evolving work, and
includes kerneldoc that should help you get a current overview.
("make pdfdocs", and then look at "usb.pdf" for host side and
"gadget.pdf" for peripheral side.) Also, Documentation/usb has
more information.
* The USB 2.0 specification (from www.usb.org), with supplements
such as those for USB OTG and the various device classes.
The USB specification has a good overview chapter, and USB
peripherals conform to the widely known "Chapter 9".
* Chip specifications for USB controllers. Examples include
host controllers (on PCs, servers, and more); peripheral
controllers (in devices with Linux firmware, like printers or
cell phones); and hard-wired peripherals like Ethernet adapters.
* Specifications for other protocols implemented by USB peripheral
functions. Some are vendor-specific; others are vendor-neutral
but just standardized outside of the www.usb.org team.
Here is a list of what each subdirectory here is, and what is contained in
them.
core/ - This is for the core USB host code, including the
usbfs files and the hub class driver ("khubd").
host/ - This is for USB host controller drivers. This
includes UHCI, OHCI, EHCI, and others that might
be used with more specialized "embedded" systems.
gadget/ - This is for USB peripheral controller drivers and
the various gadget drivers which talk to them.
Individual USB driver directories. A new driver should be added to the
first subdirectory in the list below that it fits into.
image/ - This is for still image drivers, like scanners or
digital cameras.
input/ - This is for any driver that uses the input subsystem,
like keyboard, mice, touchscreens, tablets, etc.
media/ - This is for multimedia drivers, like video cameras,
radios, and any other drivers that talk to the v4l
subsystem.
net/ - This is for network drivers.
serial/ - This is for USB to serial drivers.
storage/ - This is for USB mass-storage drivers.
class/ - This is for all USB device drivers that do not fit
into any of the above categories, and work for a range
of USB Class specified devices.
misc/ - This is for all USB device drivers that do not fit
into any of the above categories.

30
drivers/usb/atm/Kconfig Normal file
View file

@ -0,0 +1,30 @@
#
# USB ATM driver configuration
#
comment "USB ATM/DSL drivers"
depends on USB
config USB_ATM
tristate "Generic USB ATM/DSL core I/O support"
depends on USB && ATM
select CRC32
default n
help
This provides a library which is used for packet I/O by USB DSL
modems, such as the SpeedTouch driver below.
To compile this driver as a module, choose M here: the
module will be called usb_atm.
config USB_SPEEDTOUCH
tristate "Alcatel Speedtouch USB support"
depends on USB && ATM
select USB_ATM
help
Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
modem. In order to use your modem you will need to install the
two parts of the firmware, extracted by the user space tools; see
<http://www.linux-usb.org/SpeedTouch/> for details.
To compile this driver as a module, choose M here: the
module will be called speedtch.

7
drivers/usb/atm/Makefile Normal file
View file

@ -0,0 +1,7 @@
#
# Makefile for the rest of the USB drivers
# (the ones that don't fit into any other categories)
#
obj-$(CONFIG_USB_ATM) += usb_atm.o
obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o

847
drivers/usb/atm/speedtch.c Normal file
View file

@ -0,0 +1,847 @@
/******************************************************************************
* speedtch.c - Alcatel SpeedTouch USB xDSL modem driver
*
* Copyright (C) 2001, Alcatel
* Copyright (C) 2003, Duncan Sands
* Copyright (C) 2004, David Woodhouse
*
* 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.
*
******************************************************************************/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/crc32.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include "usb_atm.h"
#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
# define USE_FW_LOADER
#endif
#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <[email protected]>"
#define DRIVER_VERSION "1.8"
#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
static const char speedtch_driver_name[] = "speedtch";
#define SPEEDTOUCH_VENDORID 0x06b9
#define SPEEDTOUCH_PRODUCTID 0x4061
/* Timeout in jiffies */
#define CTRL_TIMEOUT 2000
#define DATA_TIMEOUT 2000
#define OFFSET_7 0 /* size 1 */
#define OFFSET_b 1 /* size 8 */
#define OFFSET_d 9 /* size 4 */
#define OFFSET_e 13 /* size 1 */
#define OFFSET_f 14 /* size 1 */
#define TOTAL 15
#define SIZE_7 1
#define SIZE_b 8
#define SIZE_d 4
#define SIZE_e 1
#define SIZE_f 1
static int dl_512_first = 0;
static int sw_buffering = 0;
module_param(dl_512_first, bool, 0444);
MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware");
module_param(sw_buffering, uint, 0444);
MODULE_PARM_DESC(sw_buffering, "Enable software buffering");
#define UDSL_IOCTL_LINE_UP 1
#define UDSL_IOCTL_LINE_DOWN 2
#define SPEEDTCH_ENDPOINT_INT 0x81
#define SPEEDTCH_ENDPOINT_DATA 0x07
#define SPEEDTCH_ENDPOINT_FIRMWARE 0x05
#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
static struct usb_device_id speedtch_usb_ids[] = {
{USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)},
{}
};
MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
struct speedtch_instance_data {
struct udsl_instance_data u;
/* Status */
struct urb *int_urb;
unsigned char int_data[16];
struct work_struct poll_work;
struct timer_list poll_timer;
};
/* USB */
static int speedtch_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id);
static void speedtch_usb_disconnect(struct usb_interface *intf);
static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
void *user_data);
static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs);
static void speedtch_poll_status(struct speedtch_instance_data *instance);
static struct usb_driver speedtch_usb_driver = {
.owner = THIS_MODULE,
.name = speedtch_driver_name,
.probe = speedtch_usb_probe,
.disconnect = speedtch_usb_disconnect,
.ioctl = speedtch_usb_ioctl,
.id_table = speedtch_usb_ids,
};
/***************
** firmware **
***************/
static void speedtch_got_firmware(struct speedtch_instance_data *instance,
int got_it)
{
int err;
struct usb_interface *intf;
down(&instance->u.serialize); /* vs self, speedtch_firmware_start */
if (instance->u.status == UDSL_LOADED_FIRMWARE)
goto out;
if (!got_it) {
instance->u.status = UDSL_NO_FIRMWARE;
goto out;
}
if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) {
dbg("speedtch_got_firmware: usb_set_interface returned %d!", err);
instance->u.status = UDSL_NO_FIRMWARE;
goto out;
}
/* Set up interrupt endpoint */
intf = usb_ifnum_to_if(instance->u.usb_dev, 0);
if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) {
instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
if (instance->int_urb) {
usb_fill_int_urb(instance->int_urb, instance->u.usb_dev,
usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT),
instance->int_data,
sizeof(instance->int_data),
speedtch_handle_int, instance, 50);
err = usb_submit_urb(instance->int_urb, GFP_KERNEL);
if (err) {
/* Doesn't matter; we'll poll anyway */
dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err);
usb_free_urb(instance->int_urb);
instance->int_urb = NULL;
usb_driver_release_interface(&speedtch_usb_driver, intf);
}
}
}
/* Start status polling */
mod_timer(&instance->poll_timer, jiffies + (1 * HZ));
instance->u.status = UDSL_LOADED_FIRMWARE;
tasklet_schedule(&instance->u.receive_tasklet);
out:
up(&instance->u.serialize);
wake_up_interruptible(&instance->u.firmware_waiters);
}
static int speedtch_set_swbuff(struct speedtch_instance_data *instance,
int state)
{
struct usb_device *dev = instance->u.usb_dev;
int ret;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x32, 0x40, state ? 0x01 : 0x00,
0x00, NULL, 0, 100);
if (ret < 0) {
printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n",
state ? "En" : "Dis", ret);
return ret;
}
dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
return 0;
}
static void speedtch_test_sequence(struct speedtch_instance_data *instance)
{
struct usb_device *dev = instance->u.usb_dev;
unsigned char buf[10];
int ret;
/* URB 147 */
buf[0] = 0x1c;
buf[1] = 0x50;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01, 0x40, 0x0b, 0x00, buf, 2, 100);
if (ret < 0)
printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret);
/* URB 148 */
buf[0] = 0x32;
buf[1] = 0x00;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01, 0x40, 0x02, 0x00, buf, 2, 100);
if (ret < 0)
printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret);
/* URB 149 */
buf[0] = 0x01;
buf[1] = 0x00;
buf[2] = 0x01;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01, 0x40, 0x03, 0x00, buf, 3, 100);
if (ret < 0)
printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret);
/* URB 150 */
buf[0] = 0x01;
buf[1] = 0x00;
buf[2] = 0x01;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01, 0x40, 0x04, 0x00, buf, 3, 100);
if (ret < 0)
printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret);
}
static int speedtch_start_synchro(struct speedtch_instance_data *instance)
{
struct usb_device *dev = instance->u.usb_dev;
unsigned char buf[2];
int ret;
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x04, 0x00,
buf, sizeof(buf), CTRL_TIMEOUT);
if (ret < 0) {
printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret);
return ret;
}
dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]);
return 0;
}
static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs)
{
struct speedtch_instance_data *instance = urb->context;
unsigned int count = urb->actual_length;
int ret;
/* The magic interrupt for "up state" */
const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
/* The magic interrupt for "down state" */
const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
switch (urb->status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated; clean up */
dbg("%s - urb shutting down with status: %d", __func__, urb->status);
return;
default:
dbg("%s - nonzero urb status received: %d", __func__, urb->status);
goto exit;
}
if (count < 6) {
dbg("%s - int packet too short", __func__);
goto exit;
}
if (!memcmp(up_int, instance->int_data, 6)) {
del_timer(&instance->poll_timer);
printk(KERN_NOTICE "DSL line goes up\n");
} else if (!memcmp(down_int, instance->int_data, 6)) {
printk(KERN_NOTICE "DSL line goes down\n");
} else {
int i;
printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count);
for (i = 0; i < count; i++)
printk(" %02x", instance->int_data[i]);
printk("\n");
}
schedule_work(&instance->poll_work);
exit:
rmb();
if (!instance->int_urb)
return;
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret)
err("%s - usb_submit_urb failed with result %d", __func__, ret);
}
static int speedtch_get_status(struct speedtch_instance_data *instance,
unsigned char *buf)
{
struct usb_device *dev = instance->u.usb_dev;
int ret;
memset(buf, 0, TOTAL);
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
CTRL_TIMEOUT);
if (ret < 0) {
dbg("MSG 7 failed");
return ret;
}
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b,
CTRL_TIMEOUT);
if (ret < 0) {
dbg("MSG B failed");
return ret;
}
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d,
CTRL_TIMEOUT);
if (ret < 0) {
dbg("MSG D failed");
return ret;
}
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e,
CTRL_TIMEOUT);
if (ret < 0) {
dbg("MSG E failed");
return ret;
}
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f,
CTRL_TIMEOUT);
if (ret < 0) {
dbg("MSG F failed");
return ret;
}
return 0;
}
static void speedtch_poll_status(struct speedtch_instance_data *instance)
{
unsigned char buf[TOTAL];
int ret;
ret = speedtch_get_status(instance, buf);
if (ret) {
printk(KERN_WARNING
"SpeedTouch: Error %d fetching device status\n", ret);
return;
}
dbg("Line state %02x", buf[OFFSET_7]);
switch (buf[OFFSET_7]) {
case 0:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
printk(KERN_NOTICE "ADSL line is down\n");
}
break;
case 0x08:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
printk(KERN_NOTICE "ADSL line is blocked?\n");
}
break;
case 0x10:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
printk(KERN_NOTICE "ADSL line is synchronising\n");
}
break;
case 0x20:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) {
int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8)
| (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24);
int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8)
| (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24);
if (!(down_speed & 0x0000ffff) &&
!(up_speed & 0x0000ffff)) {
down_speed >>= 16;
up_speed >>= 16;
}
instance->u.atm_dev->link_rate = down_speed * 1000 / 424;
instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
printk(KERN_NOTICE
"ADSL line is up (%d Kib/s down | %d Kib/s up)\n",
down_speed, up_speed);
}
break;
default:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]);
}
break;
}
}
static void speedtch_timer_poll(unsigned long data)
{
struct speedtch_instance_data *instance = (void *)data;
schedule_work(&instance->poll_work);
mod_timer(&instance->poll_timer, jiffies + (5 * HZ));
}
#ifdef USE_FW_LOADER
static void speedtch_upload_firmware(struct speedtch_instance_data *instance,
const struct firmware *fw1,
const struct firmware *fw2)
{
unsigned char *buffer;
struct usb_device *usb_dev = instance->u.usb_dev;
struct usb_interface *intf;
int actual_length, ret;
int offset;
dbg("speedtch_upload_firmware");
if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
dbg("speedtch_upload_firmware: interface not found!");
goto fail;
}
if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
dbg("speedtch_upload_firmware: no memory for buffer!");
goto fail;
}
/* A user-space firmware loader may already have claimed interface #2 */
if ((ret =
usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) {
dbg("speedtch_upload_firmware: interface in use (%d)!", ret);
goto fail_free;
}
/* URB 7 */
if (dl_512_first) { /* some modems need a read before writing the firmware */
ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer, 0x200, &actual_length, 2000);
if (ret < 0 && ret != -ETIMEDOUT)
dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret);
else
dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret);
}
/* URB 8 : both leds are static green */
for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
memcpy(buffer, fw1->data + offset, thislen);
ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer, thislen, &actual_length, DATA_TIMEOUT);
if (ret < 0) {
dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret);
goto fail_release;
}
dbg("speedtch_upload_firmware: BLOCK1 uploaded (%zu bytes)", fw1->size);
}
/* USB led blinking green, ADSL led off */
/* URB 11 */
ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer, 0x200, &actual_length, DATA_TIMEOUT);
if (ret < 0) {
dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret);
goto fail_release;
}
dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length);
/* URBs 12 to 139 - USB led blinking green, ADSL led off */
for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
memcpy(buffer, fw2->data + offset, thislen);
ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer, thislen, &actual_length, DATA_TIMEOUT);
if (ret < 0) {
dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret);
goto fail_release;
}
}
dbg("speedtch_upload_firmware: BLOCK3 uploaded (%zu bytes)", fw2->size);
/* USB led static green, ADSL led static red */
/* URB 142 */
ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer, 0x200, &actual_length, DATA_TIMEOUT);
if (ret < 0) {
dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret);
goto fail_release;
}
/* success */
dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length);
/* Delay to allow firmware to start up. We can do this here
because we're in our own kernel thread anyway. */
msleep(1000);
/* Enable software buffering, if requested */
if (sw_buffering)
speedtch_set_swbuff(instance, 1);
/* Magic spell; don't ask us what this does */
speedtch_test_sequence(instance);
/* Start modem synchronisation */
if (speedtch_start_synchro(instance))
dbg("speedtch_start_synchro: failed");
speedtch_got_firmware(instance, 1);
free_page((unsigned long)buffer);
return;
fail_release:
/* Only release interface #2 if uploading failed; we don't release it
we succeeded. This prevents the userspace tools from trying to load
the firmware themselves */
usb_driver_release_interface(&speedtch_usb_driver, intf);
fail_free:
free_page((unsigned long)buffer);
fail:
speedtch_got_firmware(instance, 0);
}
static int speedtch_find_firmware(struct speedtch_instance_data
*instance, int phase,
const struct firmware **fw_p)
{
char buf[24];
const u16 bcdDevice = le16_to_cpu(instance->u.usb_dev->descriptor.bcdDevice);
const u8 major_revision = bcdDevice >> 8;
const u8 minor_revision = bcdDevice & 0xff;
sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
dbg("speedtch_find_firmware: looking for %s", buf);
if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
dbg("speedtch_find_firmware: looking for %s", buf);
if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
sprintf(buf, "speedtch-%d.bin", phase);
dbg("speedtch_find_firmware: looking for %s", buf);
if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase);
return -ENOENT;
}
}
}
dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf);
return 0;
}
static int speedtch_load_firmware(void *arg)
{
const struct firmware *fw1, *fw2;
struct speedtch_instance_data *instance = arg;
BUG_ON(!instance);
daemonize("firmware/speedtch");
if (!speedtch_find_firmware(instance, 1, &fw1)) {
if (!speedtch_find_firmware(instance, 2, &fw2)) {
speedtch_upload_firmware(instance, fw1, fw2);
release_firmware(fw2);
}
release_firmware(fw1);
}
/* In case we failed, set state back to NO_FIRMWARE so that
another later attempt may work. Otherwise, we never actually
manage to recover if, for example, the firmware is on /usr and
we look for it too early. */
speedtch_got_firmware(instance, 0);
module_put(THIS_MODULE);
udsl_put_instance(&instance->u);
return 0;
}
#endif /* USE_FW_LOADER */
static void speedtch_firmware_start(struct speedtch_instance_data *instance)
{
#ifdef USE_FW_LOADER
int ret;
#endif
dbg("speedtch_firmware_start");
down(&instance->u.serialize); /* vs self, speedtch_got_firmware */
if (instance->u.status >= UDSL_LOADING_FIRMWARE) {
up(&instance->u.serialize);
return;
}
instance->u.status = UDSL_LOADING_FIRMWARE;
up(&instance->u.serialize);
#ifdef USE_FW_LOADER
udsl_get_instance(&instance->u);
try_module_get(THIS_MODULE);
ret = kernel_thread(speedtch_load_firmware, instance,
CLONE_FS | CLONE_FILES);
if (ret >= 0)
return; /* OK */
dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret);
module_put(THIS_MODULE);
udsl_put_instance(&instance->u);
/* Just pretend it never happened... hope modem_run happens */
#endif /* USE_FW_LOADER */
speedtch_got_firmware(instance, 0);
}
static int speedtch_firmware_wait(struct udsl_instance_data *instance)
{
speedtch_firmware_start((void *)instance);
if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0)
return -ERESTARTSYS;
return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN;
}
/**********
** USB **
**********/
static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
void *user_data)
{
struct speedtch_instance_data *instance = usb_get_intfdata(intf);
dbg("speedtch_usb_ioctl entered");
if (!instance) {
dbg("speedtch_usb_ioctl: NULL instance!");
return -ENODEV;
}
switch (code) {
case UDSL_IOCTL_LINE_UP:
instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
speedtch_got_firmware(instance, 1);
return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO;
case UDSL_IOCTL_LINE_DOWN:
instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
return 0;
default:
return -ENOTTY;
}
}
static int speedtch_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
int ifnum = intf->altsetting->desc.bInterfaceNumber;
struct speedtch_instance_data *instance;
unsigned char mac_str[13];
int ret, i;
char buf7[SIZE_7];
dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct), ifnum);
if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) ||
(ifnum != 1))
return -ENODEV;
dbg("speedtch_usb_probe: device accepted");
/* instance init */
instance = kmalloc(sizeof(*instance), GFP_KERNEL);
if (!instance) {
dbg("speedtch_usb_probe: no memory for instance data!");
return -ENOMEM;
}
memset(instance, 0, sizeof(struct speedtch_instance_data));
if ((ret = usb_set_interface(dev, 0, 0)) < 0)
goto fail;
if ((ret = usb_set_interface(dev, 2, 0)) < 0)
goto fail;
instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA;
instance->u.firmware_wait = speedtch_firmware_wait;
instance->u.driver_name = speedtch_driver_name;
ret = udsl_instance_setup(dev, &instance->u);
if (ret)
goto fail;
init_timer(&instance->poll_timer);
instance->poll_timer.function = speedtch_timer_poll;
instance->poll_timer.data = (unsigned long)instance;
INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance);
/* set MAC address, it is stored in the serial number */
memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi));
if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
for (i = 0; i < 6; i++)
instance->u.atm_dev->esi[i] =
(hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1]));
}
/* First check whether the modem already seems to be alive */
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, 500);
if (ret == SIZE_7) {
dbg("firmware appears to be already loaded");
speedtch_got_firmware(instance, 1);
speedtch_poll_status(instance);
} else {
speedtch_firmware_start(instance);
}
usb_set_intfdata(intf, instance);
return 0;
fail:
kfree(instance);
return -ENOMEM;
}
static void speedtch_usb_disconnect(struct usb_interface *intf)
{
struct speedtch_instance_data *instance = usb_get_intfdata(intf);
dbg("speedtch_usb_disconnect entered");
if (!instance) {
dbg("speedtch_usb_disconnect: NULL instance!");
return;
}
/*QQ need to handle disconnects on interface #2 while uploading firmware */
/*QQ and what about interface #1? */
if (instance->int_urb) {
struct urb *int_urb = instance->int_urb;
instance->int_urb = NULL;
wmb();
usb_unlink_urb(int_urb);
usb_free_urb(int_urb);
}
instance->int_data[0] = 1;
del_timer_sync(&instance->poll_timer);
wmb();
flush_scheduled_work();
udsl_instance_disconnect(&instance->u);
/* clean up */
usb_set_intfdata(intf, NULL);
udsl_put_instance(&instance->u);
}
/***********
** init **
***********/
static int __init speedtch_usb_init(void)
{
dbg("speedtch_usb_init: driver version " DRIVER_VERSION);
return usb_register(&speedtch_usb_driver);
}
static void __exit speedtch_usb_cleanup(void)
{
dbg("speedtch_usb_cleanup entered");
usb_deregister(&speedtch_usb_driver);
}
module_init(speedtch_usb_init);
module_exit(speedtch_usb_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);

1188
drivers/usb/atm/usb_atm.c Normal file

File diff suppressed because it is too large Load diff

176
drivers/usb/atm/usb_atm.h Normal file
View file

@ -0,0 +1,176 @@
/******************************************************************************
* usb_atm.h - Generic USB xDSL driver core
*
* Copyright (C) 2001, Alcatel
* Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
* Copyright (C) 2004, David Woodhouse
*
* 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.
*
******************************************************************************/
#include <linux/config.h>
#include <linux/list.h>
#include <linux/kref.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <asm/semaphore.h>
/*
#define DEBUG
#define VERBOSE_DEBUG
*/
#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
# define DEBUG
#endif
#include <linux/usb.h>
#ifdef DEBUG
#define UDSL_ASSERT(x) BUG_ON(!(x))
#else
#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0)
#endif
#define UDSL_MAX_RCV_URBS 4
#define UDSL_MAX_SND_URBS 4
#define UDSL_MAX_RCV_BUFS 8
#define UDSL_MAX_SND_BUFS 8
#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
#define UDSL_DEFAULT_RCV_URBS 2
#define UDSL_DEFAULT_SND_URBS 2
#define UDSL_DEFAULT_RCV_BUFS 4
#define UDSL_DEFAULT_SND_BUFS 4
#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
/* receive */
struct udsl_receive_buffer {
struct list_head list;
unsigned char *base;
unsigned int filled_cells;
};
struct udsl_receiver {
struct list_head list;
struct udsl_receive_buffer *buffer;
struct urb *urb;
struct udsl_instance_data *instance;
};
struct udsl_vcc_data {
/* vpi/vci lookup */
struct list_head list;
short vpi;
int vci;
struct atm_vcc *vcc;
/* raw cell reassembly */
struct sk_buff *sarb;
};
/* send */
struct udsl_send_buffer {
struct list_head list;
unsigned char *base;
unsigned char *free_start;
unsigned int free_cells;
};
struct udsl_sender {
struct list_head list;
struct udsl_send_buffer *buffer;
struct urb *urb;
struct udsl_instance_data *instance;
};
struct udsl_control {
struct atm_skb_data atm_data;
unsigned int num_cells;
unsigned int num_entire;
unsigned int pdu_padding;
unsigned char aal5_trailer[ATM_AAL5_TRAILER];
};
#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb)
/* main driver data */
enum udsl_status {
UDSL_NO_FIRMWARE,
UDSL_LOADING_FIRMWARE,
UDSL_LOADED_FIRMWARE
};
struct udsl_instance_data {
struct kref refcount;
struct semaphore serialize;
/* USB device part */
struct usb_device *usb_dev;
char description[64];
int data_endpoint;
int snd_padding;
int rcv_padding;
const char *driver_name;
/* ATM device part */
struct atm_dev *atm_dev;
struct list_head vcc_list;
/* firmware */
int (*firmware_wait) (struct udsl_instance_data *);
enum udsl_status status;
wait_queue_head_t firmware_waiters;
/* receive */
struct udsl_receiver receivers[UDSL_MAX_RCV_URBS];
struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS];
spinlock_t receive_lock;
struct list_head spare_receivers;
struct list_head filled_receive_buffers;
struct tasklet_struct receive_tasklet;
struct list_head spare_receive_buffers;
/* send */
struct udsl_sender senders[UDSL_MAX_SND_URBS];
struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
struct sk_buff_head sndqueue;
spinlock_t send_lock;
struct list_head spare_senders;
struct list_head spare_send_buffers;
struct tasklet_struct send_tasklet;
struct sk_buff *current_skb; /* being emptied */
struct udsl_send_buffer *current_buffer; /* being filled */
struct list_head filled_send_buffers;
};
extern int udsl_instance_setup(struct usb_device *dev,
struct udsl_instance_data *instance);
extern void udsl_instance_disconnect(struct udsl_instance_data *instance);
extern void udsl_get_instance(struct udsl_instance_data *instance);
extern void udsl_put_instance(struct udsl_instance_data *instance);

86
drivers/usb/class/Kconfig Normal file
View file

@ -0,0 +1,86 @@
#
# USB Class driver configuration
#
comment "USB Device Class drivers"
depends on USB
config USB_AUDIO
tristate "USB Audio support"
depends on USB && SOUND
help
Say Y here if you want to connect USB audio equipment such as
speakers to your computer's USB port. You only need this if you use
the OSS sound driver; ALSA has its own option for usb audio support.
To compile this driver as a module, choose M here: the
module will be called audio.
comment "USB Bluetooth TTY can only be used with disabled Bluetooth subsystem"
depends on USB && BT
config USB_BLUETOOTH_TTY
tristate "USB Bluetooth TTY support"
depends on USB && BT=n
---help---
This driver implements a nonstandard tty interface to a Bluetooth
device that can be used only by specialized Bluetooth HCI software.
Say Y here if you want to use OpenBT Bluetooth stack (available
at <http://developer.axis.com/software>), or other TTY based
Bluetooth stacks, and want to connect a USB Bluetooth device
to your computer's USB port.
Do *not* enable this driver if you want to use generic Linux
Bluetooth support.
If in doubt, say N here.
To compile this driver as a module, choose M here: the
module will be called bluetty.
config USB_MIDI
tristate "USB MIDI support"
depends on USB && SOUND
---help---
Say Y here if you want to connect a USB MIDI device to your
computer's USB port. This driver is for devices that comply with
'Universal Serial Bus Device Class Definition for MIDI Device'.
The following devices are known to work:
* Steinberg USB2MIDI
* Roland MPU64
* Roland PC-300
* Roland SC8850
* Roland UM-1
* Roland UM-2
* Roland UA-100
* Yamaha MU1000
To compile this driver as a module, choose M here: the
module will be called usb-midi.
config USB_ACM
tristate "USB Modem (CDC ACM) support"
depends on USB
---help---
This driver supports USB modems and ISDN adapters which support the
Communication Device Class Abstract Control Model interface.
Please read <file:Documentation/usb/acm.txt> for details.
If your modem only reports "Cls=ff(vend.)" in the descriptors in
/proc/bus/usb/devices, then your modem will not work with this
driver.
To compile this driver as a module, choose M here: the
module will be called cdc-acm.
config USB_PRINTER
tristate "USB Printer support"
depends on USB
help
Say Y here if you want to connect a USB printer to your computer's
USB port.
To compile this driver as a module, choose M here: the
module will be called usblp.

View file

@ -0,0 +1,10 @@
#
# Makefile for USB Class drivers
# (one step up from the misc category)
#
obj-$(CONFIG_USB_ACM) += cdc-acm.o
obj-$(CONFIG_USB_AUDIO) += audio.o
obj-$(CONFIG_USB_BLUETOOTH_TTY) += bluetty.o
obj-$(CONFIG_USB_MIDI) += usb-midi.o
obj-$(CONFIG_USB_PRINTER) += usblp.o

3882
drivers/usb/class/audio.c Normal file

File diff suppressed because it is too large Load diff

110
drivers/usb/class/audio.h Normal file
View file

@ -0,0 +1,110 @@
#define CS_AUDIO_UNDEFINED 0x20
#define CS_AUDIO_DEVICE 0x21
#define CS_AUDIO_CONFIGURATION 0x22
#define CS_AUDIO_STRING 0x23
#define CS_AUDIO_INTERFACE 0x24
#define CS_AUDIO_ENDPOINT 0x25
#define HEADER 0x01
#define INPUT_TERMINAL 0x02
#define OUTPUT_TERMINAL 0x03
#define MIXER_UNIT 0x04
#define SELECTOR_UNIT 0x05
#define FEATURE_UNIT 0x06
#define PROCESSING_UNIT 0x07
#define EXTENSION_UNIT 0x08
#define AS_GENERAL 0x01
#define FORMAT_TYPE 0x02
#define FORMAT_SPECIFIC 0x03
#define EP_GENERAL 0x01
#define MAX_CHAN 9
#define MAX_FREQ 16
#define MAX_IFACE 8
#define MAX_FORMAT 8
#define MAX_ALT 32 /* Sorry, we need quite a few for the Philips webcams */
struct usb_audio_terminal
{
u8 flags;
u8 assoc;
u16 type; /* Mic etc */
u8 channels;
u8 source;
u16 chancfg;
};
struct usb_audio_format
{
u8 type;
u8 channels;
u8 num_freq;
u8 sfz;
u8 bits;
u16 freq[MAX_FREQ];
};
struct usb_audio_interface
{
u8 terminal;
u8 delay;
u16 num_formats;
u16 format_type;
u8 flags;
u8 idleconf; /* Idle config */
#define AU_IFACE_FOUND 1
struct usb_audio_format format[MAX_FORMAT];
};
struct usb_audio_device
{
struct list_head list;
u8 mixer;
u8 selector;
void *irq_handle;
u8 num_channels;
u8 num_dsp_iface;
u8 channel_map[MAX_CHAN];
struct usb_audio_terminal terminal[MAX_CHAN];
struct usb_audio_interface interface[MAX_IFACE][MAX_ALT];
};
/* Audio Class specific Request Codes */
#define SET_CUR 0x01
#define GET_CUR 0x81
#define SET_MIN 0x02
#define GET_MIN 0x82
#define SET_MAX 0x03
#define GET_MAX 0x83
#define SET_RES 0x04
#define GET_RES 0x84
#define SET_MEM 0x05
#define GET_MEM 0x85
#define GET_STAT 0xff
/* Terminal Control Selectors */
#define COPY_PROTECT_CONTROL 0x01
/* Feature Unit Control Selectors */
#define MUTE_CONTROL 0x01
#define VOLUME_CONTROL 0x02
#define BASS_CONTROL 0x03
#define MID_CONTROL 0x04
#define TREBLE_CONTROL 0x05
#define GRAPHIC_EQUALIZER_CONTROL 0x06
#define AUTOMATIC_GAIN_CONTROL 0x07
#define DELAY_CONTROL 0x08
#define BASS_BOOST_CONTROL 0x09
#define LOUDNESS_CONTROL 0x0a
/* Endpoint Control Selectors */
#define SAMPLING_FREQ_CONTROL 0x01
#define PITCH_CONTROL 0x02

1279
drivers/usb/class/bluetty.c Normal file

File diff suppressed because it is too large Load diff

942
drivers/usb/class/cdc-acm.c Normal file
View file

@ -0,0 +1,942 @@
/*
* cdc-acm.c
*
* Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
* Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
* Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com>
* Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2004 Oliver Neukum <oliver@neukum.name>
*
* USB Abstract Control Model driver for USB modems and ISDN adapters
*
* Sponsored by SuSE
*
* ChangeLog:
* v0.9 - thorough cleaning, URBification, almost a rewrite
* v0.10 - some more cleanups
* v0.11 - fixed flow control, read error doesn't stop reads
* v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
* v0.13 - added termios, added hangup
* v0.14 - sized down struct acm
* v0.15 - fixed flow control again - characters could be lost
* v0.16 - added code for modems with swapped data and control interfaces
* v0.17 - added new style probing
* v0.18 - fixed new style probing for devices with more configurations
* v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
* v0.20 - switched to probing on interface (rather than device) class
* v0.21 - revert to probing on device for devices with multiple configs
* v0.22 - probe only the control interface. if usbcore doesn't choose the
* config we want, sysadmin changes bConfigurationValue in sysfs.
* v0.23 - use softirq for rx processing, as needed by tty layer
* v0.24 - change probe method to evaluate CDC union descriptor
*/
/*
* 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
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/usb_cdc.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#include "cdc-acm.h"
/*
* Version Information
*/
#define DRIVER_VERSION "v0.23"
#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik"
#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
static struct usb_driver acm_driver;
static struct tty_driver *acm_tty_driver;
static struct acm *acm_table[ACM_TTY_MINORS];
static DECLARE_MUTEX(open_sem);
#define ACM_READY(acm) (acm && acm->dev && acm->used)
/*
* Functions for ACM control messages.
*/
static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
{
int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
request, USB_RT_ACM, value,
acm->control->altsetting[0].desc.bInterfaceNumber,
buf, len, 5000);
dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
return retval < 0 ? retval : 0;
}
/* devices aren't required to support these requests.
* the cdc acm descriptor tells whether they do...
*/
#define acm_set_control(acm, control) \
acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
#define acm_set_line(acm, line) \
acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
#define acm_send_break(acm, ms) \
acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
/*
* Interrupt handlers for various ACM device responses
*/
/* control interface reports status changes with "interrupt" transfers */
static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
{
struct acm *acm = urb->context;
struct usb_cdc_notification *dr = urb->transfer_buffer;
unsigned char *data;
int newctrl;
int status;
switch (urb->status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
return;
default:
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
goto exit;
}
if (!ACM_READY(acm))
goto exit;
data = (unsigned char *)(dr + 1);
switch (dr->bNotificationType) {
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
break;
case USB_CDC_NOTIFY_SERIAL_STATE:
newctrl = le16_to_cpu(get_unaligned((__le16 *) data));
if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
dbg("calling hangup");
tty_hangup(acm->tty);
}
acm->ctrlin = newctrl;
dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
break;
default:
dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
dr->bNotificationType, dr->wIndex,
dr->wLength, data[0], data[1]);
break;
}
exit:
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
err ("%s - usb_submit_urb failed with result %d",
__FUNCTION__, status);
}
/* data interface returns incoming bytes, or we got unthrottled */
static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
{
struct acm *acm = urb->context;
dbg("Entering acm_read_bulk with status %d\n", urb->status);
if (!ACM_READY(acm))
return;
if (urb->status)
dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status);
/* calling tty_flip_buffer_push() in_irq() isn't allowed */
tasklet_schedule(&acm->bh);
}
static void acm_rx_tasklet(unsigned long _acm)
{
struct acm *acm = (void *)_acm;
struct urb *urb = acm->readurb;
struct tty_struct *tty = acm->tty;
unsigned char *data = urb->transfer_buffer;
int i = 0;
dbg("Entering acm_rx_tasklet");
if (urb->actual_length > 0 && !acm->throttle) {
for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
/* if we insert more than TTY_FLIPBUF_SIZE characters,
* we drop them. */
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty_flip_buffer_push(tty);
}
tty_insert_flip_char(tty, data[i], 0);
}
dbg("Handed %d bytes to tty layer", i+1);
tty_flip_buffer_push(tty);
}
spin_lock(&acm->throttle_lock);
if (acm->throttle) {
dbg("Throtteling noticed");
memmove(data, data + i, urb->actual_length - i);
urb->actual_length -= i;
acm->resubmit_to_unthrottle = 1;
spin_unlock(&acm->throttle_lock);
return;
}
spin_unlock(&acm->throttle_lock);
urb->actual_length = 0;
urb->dev = acm->dev;
i = usb_submit_urb(urb, GFP_ATOMIC);
if (i)
dev_dbg(&acm->data->dev, "bulk rx resubmit %d\n", i);
}
/* data interface wrote those outgoing bytes */
static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
{
struct acm *acm = (struct acm *)urb->context;
dbg("Entering acm_write_bulk with status %d\n", urb->status);
if (!ACM_READY(acm))
goto out;
if (urb->status)
dbg("nonzero write bulk status received: %d", urb->status);
schedule_work(&acm->work);
out:
acm->ready_for_write = 1;
}
static void acm_softint(void *private)
{
struct acm *acm = private;
dbg("Entering acm_softint.\n");
if (!ACM_READY(acm))
return;
tty_wakeup(acm->tty);
}
/*
* TTY handlers
*/
static int acm_tty_open(struct tty_struct *tty, struct file *filp)
{
struct acm *acm;
int rv = -EINVAL;
dbg("Entering acm_tty_open.\n");
down(&open_sem);
acm = acm_table[tty->index];
if (!acm || !acm->dev)
goto err_out;
else
rv = 0;
tty->driver_data = acm;
acm->tty = tty;
if (acm->used++) {
goto done;
}
acm->ctrlurb->dev = acm->dev;
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
dbg("usb_submit_urb(ctrl irq) failed");
goto bail_out;
}
acm->readurb->dev = acm->dev;
if (usb_submit_urb(acm->readurb, GFP_KERNEL)) {
dbg("usb_submit_urb(read bulk) failed");
goto bail_out_and_unlink;
}
if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS))
goto full_bailout;
/* force low_latency on so that our tty_push actually forces the data through,
otherwise it is scheduled, and with high data rates data can get lost. */
tty->low_latency = 1;
done:
err_out:
up(&open_sem);
return rv;
full_bailout:
usb_kill_urb(acm->readurb);
bail_out_and_unlink:
usb_kill_urb(acm->ctrlurb);
bail_out:
acm->used--;
up(&open_sem);
return -EIO;
}
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
{
struct acm *acm = tty->driver_data;
if (!acm || !acm->used)
return;
down(&open_sem);
if (!--acm->used) {
if (acm->dev) {
acm_set_control(acm, acm->ctrlout = 0);
usb_kill_urb(acm->ctrlurb);
usb_kill_urb(acm->writeurb);
usb_kill_urb(acm->readurb);
} else {
tty_unregister_device(acm_tty_driver, acm->minor);
acm_table[acm->minor] = NULL;
usb_free_urb(acm->ctrlurb);
usb_free_urb(acm->readurb);
usb_free_urb(acm->writeurb);
kfree(acm);
}
}
up(&open_sem);
}
static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
struct acm *acm = tty->driver_data;
int stat;
dbg("Entering acm_tty_write to write %d bytes,\n", count);
if (!ACM_READY(acm))
return -EINVAL;
if (!acm->ready_for_write)
return 0;
if (!count)
return 0;
count = (count > acm->writesize) ? acm->writesize : count;
dbg("Get %d bytes...", count);
memcpy(acm->write_buffer, buf, count);
dbg(" Successfully copied.\n");
acm->writeurb->transfer_buffer_length = count;
acm->writeurb->dev = acm->dev;
acm->ready_for_write = 0;
stat = usb_submit_urb(acm->writeurb, GFP_ATOMIC);
if (stat < 0) {
dbg("usb_submit_urb(write bulk) failed");
acm->ready_for_write = 1;
return stat;
}
return count;
}
static int acm_tty_write_room(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm))
return -EINVAL;
return !acm->ready_for_write ? 0 : acm->writesize;
}
static int acm_tty_chars_in_buffer(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm))
return -EINVAL;
return !acm->ready_for_write ? acm->writeurb->transfer_buffer_length : 0;
}
static void acm_tty_throttle(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm))
return;
spin_lock_bh(&acm->throttle_lock);
acm->throttle = 1;
spin_unlock_bh(&acm->throttle_lock);
}
static void acm_tty_unthrottle(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm))
return;
spin_lock_bh(&acm->throttle_lock);
acm->throttle = 0;
spin_unlock_bh(&acm->throttle_lock);
if (acm->resubmit_to_unthrottle) {
acm->resubmit_to_unthrottle = 0;
acm_read_bulk(acm->readurb, NULL);
}
}
static void acm_tty_break_ctl(struct tty_struct *tty, int state)
{
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm))
return;
if (acm_send_break(acm, state ? 0xffff : 0))
dbg("send break failed");
}
static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
{
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm))
return -EINVAL;
return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
(acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
(acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
(acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
(acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
TIOCM_CTS;
}
static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear)
{
struct acm *acm = tty->driver_data;
unsigned int newctrl;
if (!ACM_READY(acm))
return -EINVAL;
newctrl = acm->ctrlout;
set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
newctrl = (newctrl & ~clear) | set;
if (acm->ctrlout == newctrl)
return 0;
return acm_set_control(acm, acm->ctrlout = newctrl);
}
static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm))
return -EINVAL;
return -ENOIOCTLCMD;
}
static __u32 acm_tty_speed[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600,
1200, 1800, 2400, 4800, 9600, 19200, 38400,
57600, 115200, 230400, 460800, 500000, 576000,
921600, 1000000, 1152000, 1500000, 2000000,
2500000, 3000000, 3500000, 4000000
};
static __u8 acm_tty_size[] = {
5, 6, 7, 8
};
static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_old)
{
struct acm *acm = tty->driver_data;
struct termios *termios = tty->termios;
struct usb_cdc_line_coding newline;
int newctrl = acm->ctrlout;
if (!ACM_READY(acm))
return;
newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
newline.bParityType = termios->c_cflag & PARENB ?
(termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
if (!newline.dwDTERate) {
newline.dwDTERate = acm->line.dwDTERate;
newctrl &= ~ACM_CTRL_DTR;
} else newctrl |= ACM_CTRL_DTR;
if (newctrl != acm->ctrlout)
acm_set_control(acm, acm->ctrlout = newctrl);
if (memcmp(&acm->line, &newline, sizeof newline)) {
memcpy(&acm->line, &newline, sizeof newline);
dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
newline.bCharFormat, newline.bParityType,
newline.bDataBits);
acm_set_line(acm, &acm->line);
}
}
/*
* USB probe and disconnect routines.
*/
static int acm_probe (struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_cdc_union_desc *union_header = NULL;
char *buffer = intf->altsetting->extra;
int buflen = intf->altsetting->extralen;
struct usb_interface *control_interface;
struct usb_interface *data_interface;
struct usb_endpoint_descriptor *epctrl;
struct usb_endpoint_descriptor *epread;
struct usb_endpoint_descriptor *epwrite;
struct usb_device *usb_dev = interface_to_usbdev(intf);
struct acm *acm;
int minor;
int ctrlsize,readsize;
u8 *buf;
u8 ac_management_function = 0;
u8 call_management_function = 0;
int call_interface_num = -1;
int data_interface_num;
unsigned long quirks;
/* handle quirks deadly to normal probing*/
quirks = (unsigned long)id->driver_info;
if (quirks == NO_UNION_NORMAL) {
data_interface = usb_ifnum_to_if(usb_dev, 1);
control_interface = usb_ifnum_to_if(usb_dev, 0);
goto skip_normal_probe;
}
/* normal probing*/
if (!buffer) {
err("Wierd descriptor references\n");
return -EINVAL;
}
if (!buflen) {
if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
buflen = intf->cur_altsetting->endpoint->extralen;
buffer = intf->cur_altsetting->endpoint->extra;
} else {
err("Zero length descriptor references\n");
return -EINVAL;
}
}
while (buflen > 0) {
if (buffer [1] != USB_DT_CS_INTERFACE) {
err("skipping garbage\n");
goto next_desc;
}
switch (buffer [2]) {
case USB_CDC_UNION_TYPE: /* we've found it */
if (union_header) {
err("More than one union descriptor, skipping ...");
goto next_desc;
}
union_header = (struct usb_cdc_union_desc *)
buffer;
break;
case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */
break; /* for now we ignore it */
case USB_CDC_HEADER_TYPE: /* maybe check version */
break; /* for now we ignore it */
case USB_CDC_ACM_TYPE:
ac_management_function = buffer[3];
break;
case USB_CDC_CALL_MANAGEMENT_TYPE:
call_management_function = buffer[3];
call_interface_num = buffer[4];
if ((call_management_function & 3) != 3)
err("This device cannot do calls on its own. It is no modem.");
break;
default:
err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]);
break;
}
next_desc:
buflen -= buffer[0];
buffer += buffer[0];
}
if (!union_header) {
if (call_interface_num > 0) {
dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
control_interface = intf;
} else {
dev_dbg(&intf->dev,"No union descriptor, giving up\n");
return -ENODEV;
}
} else {
control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
if (!control_interface || !data_interface) {
dev_dbg(&intf->dev,"no interfaces\n");
return -ENODEV;
}
}
if (data_interface_num != call_interface_num)
dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n");
skip_normal_probe:
/*workaround for switched interfaces */
if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
struct usb_interface *t;
dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
t = control_interface;
control_interface = data_interface;
data_interface = t;
} else {
return -EINVAL;
}
}
if (usb_interface_claimed(data_interface)) { /* valid in this context */
dev_dbg(&intf->dev,"The data interface isn't available\n");
return -EBUSY;
}
if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
return -EINVAL;
epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
epread = &data_interface->cur_altsetting->endpoint[0].desc;
epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
/* workaround for switched endpoints */
if ((epread->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN) {
/* descriptors are swapped */
struct usb_endpoint_descriptor *t;
dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
t = epread;
epread = epwrite;
epwrite = t;
}
dbg("interfaces are valid");
for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
if (minor == ACM_TTY_MINORS) {
err("no more free acm devices");
return -ENODEV;
}
if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) {
dev_dbg(&intf->dev, "out of memory (acm kmalloc)\n");
goto alloc_fail;
}
memset(acm, 0, sizeof(struct acm));
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
readsize = le16_to_cpu(epread->wMaxPacketSize);
acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize);
acm->control = control_interface;
acm->data = data_interface;
acm->minor = minor;
acm->dev = usb_dev;
acm->ctrl_caps = ac_management_function;
acm->ctrlsize = ctrlsize;
acm->readsize = readsize;
acm->bh.func = acm_rx_tasklet;
acm->bh.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint, acm);
spin_lock_init(&acm->throttle_lock);
acm->ready_for_write = 1;
buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
if (!buf) {
dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
goto alloc_fail2;
}
acm->ctrl_buffer = buf;
buf = usb_buffer_alloc(usb_dev, readsize, GFP_KERNEL, &acm->read_dma);
if (!buf) {
dev_dbg(&intf->dev, "out of memory (read buffer alloc)\n");
goto alloc_fail3;
}
acm->read_buffer = buf;
buf = usb_buffer_alloc(usb_dev, acm->writesize, GFP_KERNEL, &acm->write_dma);
if (!buf) {
dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
goto alloc_fail4;
}
acm->write_buffer = buf;
acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->ctrlurb) {
dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
goto alloc_fail5;
}
acm->readurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->readurb) {
dev_dbg(&intf->dev, "out of memory (readurb kmalloc)\n");
goto alloc_fail6;
}
acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->writeurb) {
dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n");
goto alloc_fail7;
}
usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
acm->ctrlurb->transfer_dma = acm->ctrl_dma;
usb_fill_bulk_urb(acm->readurb, usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress),
acm->read_buffer, readsize, acm_read_bulk, acm);
acm->readurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
acm->readurb->transfer_dma = acm->read_dma;
usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
acm->write_buffer, acm->writesize, acm_write_bulk, acm);
acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
acm->writeurb->transfer_dma = acm->write_dma;
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
acm_set_control(acm, acm->ctrlout);
acm->line.dwDTERate = cpu_to_le32(9600);
acm->line.bDataBits = 8;
acm_set_line(acm, &acm->line);
usb_driver_claim_interface(&acm_driver, data_interface, acm);
tty_register_device(acm_tty_driver, minor, &intf->dev);
acm_table[minor] = acm;
usb_set_intfdata (intf, acm);
return 0;
alloc_fail7:
usb_free_urb(acm->readurb);
alloc_fail6:
usb_free_urb(acm->ctrlurb);
alloc_fail5:
usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma);
alloc_fail4:
usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma);
alloc_fail3:
usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
alloc_fail2:
kfree(acm);
alloc_fail:
return -ENOMEM;
}
static void acm_disconnect(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata (intf);
struct usb_device *usb_dev = interface_to_usbdev(intf);
if (!acm || !acm->dev) {
dbg("disconnect on nonexisting interface");
return;
}
down(&open_sem);
acm->dev = NULL;
usb_set_intfdata (intf, NULL);
usb_kill_urb(acm->ctrlurb);
usb_kill_urb(acm->readurb);
usb_kill_urb(acm->writeurb);
flush_scheduled_work(); /* wait for acm_softint */
usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma);
usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma);
usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
usb_driver_release_interface(&acm_driver, acm->data);
if (!acm->used) {
tty_unregister_device(acm_tty_driver, acm->minor);
acm_table[acm->minor] = NULL;
usb_free_urb(acm->ctrlurb);
usb_free_urb(acm->readurb);
usb_free_urb(acm->writeurb);
kfree(acm);
up(&open_sem);
return;
}
up(&open_sem);
if (acm->tty)
tty_hangup(acm->tty);
}
/*
* USB driver structure.
*/
static struct usb_device_id acm_ids[] = {
/* quirky and broken devices */
{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
/* control interfaces with various AT-command sets */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_V25TER) },
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_PCCA101) },
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_GSM) },
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_3G ) },
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_CDMA) },
/* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
{ }
};
MODULE_DEVICE_TABLE (usb, acm_ids);
static struct usb_driver acm_driver = {
.owner = THIS_MODULE,
.name = "cdc_acm",
.probe = acm_probe,
.disconnect = acm_disconnect,
.id_table = acm_ids,
};
/*
* TTY driver structures.
*/
static struct tty_operations acm_ops = {
.open = acm_tty_open,
.close = acm_tty_close,
.write = acm_tty_write,
.write_room = acm_tty_write_room,
.ioctl = acm_tty_ioctl,
.throttle = acm_tty_throttle,
.unthrottle = acm_tty_unthrottle,
.chars_in_buffer = acm_tty_chars_in_buffer,
.break_ctl = acm_tty_break_ctl,
.set_termios = acm_tty_set_termios,
.tiocmget = acm_tty_tiocmget,
.tiocmset = acm_tty_tiocmset,
};
/*
* Init / exit.
*/
static int __init acm_init(void)
{
int retval;
acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
if (!acm_tty_driver)
return -ENOMEM;
acm_tty_driver->owner = THIS_MODULE,
acm_tty_driver->driver_name = "acm",
acm_tty_driver->name = "ttyACM",
acm_tty_driver->devfs_name = "usb/acm/",
acm_tty_driver->major = ACM_TTY_MAJOR,
acm_tty_driver->minor_start = 0,
acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
acm_tty_driver->init_termios = tty_std_termios;
acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(acm_tty_driver, &acm_ops);
retval = tty_register_driver(acm_tty_driver);
if (retval) {
put_tty_driver(acm_tty_driver);
return retval;
}
retval = usb_register(&acm_driver);
if (retval) {
tty_unregister_driver(acm_tty_driver);
put_tty_driver(acm_tty_driver);
return retval;
}
info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
static void __exit acm_exit(void)
{
usb_deregister(&acm_driver);
tty_unregister_driver(acm_tty_driver);
put_tty_driver(acm_tty_driver);
}
module_init(acm_init);
module_exit(acm_exit);
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,82 @@
/*
*
* Includes for cdc-acm.c
*
* Mainly take from usbnet's cdc-ether part
*
*/
/*
* CMSPAR, some architectures can't have space and mark parity.
*/
#ifndef CMSPAR
#define CMSPAR 0
#endif
/*
* Major and minor numbers.
*/
#define ACM_TTY_MAJOR 166
#define ACM_TTY_MINORS 32
/*
* Requests.
*/
#define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
/*
* Output control lines.
*/
#define ACM_CTRL_DTR 0x01
#define ACM_CTRL_RTS 0x02
/*
* Input control lines and line errors.
*/
#define ACM_CTRL_DCD 0x01
#define ACM_CTRL_DSR 0x02
#define ACM_CTRL_BRK 0x04
#define ACM_CTRL_RI 0x08
#define ACM_CTRL_FRAMING 0x10
#define ACM_CTRL_PARITY 0x20
#define ACM_CTRL_OVERRUN 0x40
/*
* Internal driver structures.
*/
struct acm {
struct usb_device *dev; /* the corresponding usb device */
struct usb_interface *control; /* control interface */
struct usb_interface *data; /* data interface */
struct tty_struct *tty; /* the corresponding tty */
struct urb *ctrlurb, *readurb, *writeurb; /* urbs */
u8 *ctrl_buffer, *read_buffer, *write_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma, read_dma, write_dma; /* dma handles of buffers */
struct usb_cdc_line_coding line; /* bits, stop, parity */
struct work_struct work; /* work queue entry for line discipline waking up */
struct tasklet_struct bh; /* rx processing */
spinlock_t throttle_lock; /* synchronize throtteling and read callback */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */
unsigned int writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
unsigned int used; /* someone has this acm's device open */
unsigned int minor; /* acm minor number */
unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */
unsigned char ready_for_write; /* write urb can be used */
unsigned char resubmit_to_unthrottle; /* throtteling has disabled the read urb */
unsigned int ctrl_caps; /* control capabilities from the class specific header */
};
#define CDC_DATA_INTERFACE_TYPE 0x0a
/* constants describing various quirks and errors */
#define NO_UNION_NORMAL 1

2154
drivers/usb/class/usb-midi.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,164 @@
/*
usb-midi.h -- USB-MIDI driver
Copyright (C) 2001
NAGANO Daisuke <breeze.nagano@nifty.ne.jp>
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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* ------------------------------------------------------------------------- */
#ifndef _USB_MIDI_H_
#define _USB_MIDI_H_
#ifndef USB_SUBCLASS_MIDISTREAMING
#define USB_SUBCLASS_MIDISTREAMING 3
#endif
/* ------------------------------------------------------------------------- */
/* Roland MIDI Devices */
#define USB_VENDOR_ID_ROLAND 0x0582
#define USBMIDI_ROLAND_UA100G 0x0000
#define USBMIDI_ROLAND_MPU64 0x0002
#define USBMIDI_ROLAND_SC8850 0x0003
#define USBMIDI_ROLAND_SC8820 0x0007
#define USBMIDI_ROLAND_UM2 0x0005
#define USBMIDI_ROLAND_UM1 0x0009
#define USBMIDI_ROLAND_PC300 0x0008
/* YAMAHA MIDI Devices */
#define USB_VENDOR_ID_YAMAHA 0x0499
#define USBMIDI_YAMAHA_MU1000 0x1001
/* Steinberg MIDI Devices */
#define USB_VENDOR_ID_STEINBERG 0x0763
#define USBMIDI_STEINBERG_USB2MIDI 0x1001
/* Mark of the Unicorn MIDI Devices */
#define USB_VENDOR_ID_MOTU 0x07fd
#define USBMIDI_MOTU_FASTLANE 0x0001
/* ------------------------------------------------------------------------- */
/* Supported devices */
struct usb_midi_endpoint {
int endpoint;
int cableId; /* if bit-n == 1 then cableId-n is enabled (n: 0 - 15) */
};
struct usb_midi_device {
char *deviceName;
u16 idVendor;
u16 idProduct;
int interface;
int altSetting; /* -1: auto detect */
struct usb_midi_endpoint in[15];
struct usb_midi_endpoint out[15];
};
static struct usb_midi_device usb_midi_devices[] = {
{ /* Roland UM-1 */
"Roland UM-1",
USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM1, 2, -1,
{ { 0x81, 1 }, {-1, -1} },
{ { 0x01, 1,}, {-1, -1} },
},
{ /* Roland UM-2 */
"Roland UM-2" ,
USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM2, 2, -1,
{ { 0x81, 3 }, {-1, -1} },
{ { 0x01, 3,}, {-1, -1} },
},
/** Next entry courtesy research by Michael Minn <[email protected]> **/
{ /* Roland UA-100 */
"Roland UA-100",
USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UA100G, 2, -1,
{ { 0x82, 7 }, {-1, -1} }, /** cables 0,1 and 2 for SYSEX **/
{ { 0x02, 7 }, {-1, -1} },
},
/** Next entry courtesy research by Michael Minn <[email protected]> **/
{ /* Roland SC8850 */
"Roland SC8850",
USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8850, 2, -1,
{ { 0x81, 0x3f }, {-1, -1} },
{ { 0x01, 0x3f }, {-1, -1} },
},
{ /* Roland SC8820 */
"Roland SC8820",
USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820, 2, -1,
{ { 0x81, 0x13 }, {-1, -1} },
{ { 0x01, 0x13 }, {-1, -1} },
},
{ /* Roland SC8820 */
"Roland SC8820",
USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820, 2, -1,
{ { 0x81, 17 }, {-1, -1} },
{ { 0x01, 17 }, {-1, -1} },
},
{ /* YAMAHA MU1000 */
"YAMAHA MU1000",
USB_VENDOR_ID_YAMAHA, USBMIDI_YAMAHA_MU1000, 0, -1,
{ { 0x81, 1 }, {-1, -1} },
{ { 0x01, 15 }, {-1, -1} },
},
{ /* Roland PC-300 */
"Roland PC-300",
USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_PC300, 2, -1,
{ { 0x81, 1 }, {-1, -1} },
{ { 0x01, 1 }, {-1, -1} },
},
{ /* MOTU Fastlane USB */
"MOTU Fastlane USB",
USB_VENDOR_ID_MOTU, USBMIDI_MOTU_FASTLANE, 1, 0,
{ { 0x82, 3 }, {-1, -1} },
{ { 0x02, 3 }, {-1, -1} },
}
};
#define VENDOR_SPECIFIC_USB_MIDI_DEVICES (sizeof(usb_midi_devices)/sizeof(struct usb_midi_device))
/* for Hot-Plugging */
static struct usb_device_id usb_midi_ids [] = {
{ .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
.bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING},
{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM1 ) },
{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM2 ) },
{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UA100G ) },
{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_PC300 ) },
{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8850 ) },
{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820 ) },
{ USB_DEVICE( USB_VENDOR_ID_YAMAHA, USBMIDI_YAMAHA_MU1000 ) },
{ USB_DEVICE( USB_VENDOR_ID_MOTU, USBMIDI_MOTU_FASTLANE ) },
/* { USB_DEVICE( USB_VENDOR_ID_STEINBERG, USBMIDI_STEINBERG_USB2MIDI ) },*/
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_midi_ids);
/* ------------------------------------------------------------------------- */
#endif /* _USB_MIDI_H_ */

1214
drivers/usb/class/usblp.c Normal file

File diff suppressed because it is too large Load diff

99
drivers/usb/core/Kconfig Normal file
View file

@ -0,0 +1,99 @@
#
# USB Core configuration
#
config USB_DEBUG
bool "USB verbose debug messages"
depends on USB
help
Say Y here if you want the USB core & hub drivers to produce a bunch
of debug messages to the system log. Select this if you are having a
problem with USB support and want to see more of what is going on.
comment "Miscellaneous USB options"
depends on USB
config USB_DEVICEFS
bool "USB device filesystem"
depends on USB
---help---
If you say Y here (and to "/proc file system support" in the "File
systems" section, above), you will get a file /proc/bus/usb/devices
which lists the devices currently connected to your USB bus or
busses, and for every connected device a file named
"/proc/bus/usb/xxx/yyy", where xxx is the bus number and yyy the
device number; the latter files can be used by user space programs
to talk directly to the device. These files are "virtual", meaning
they are generated on the fly and not stored on the hard drive.
You may need to mount the usbfs file system to see the files, use
mount -t usbfs none /proc/bus/usb
For the format of the various /proc/bus/usb/ files, please read
<file:Documentation/usb/proc_usb_info.txt>.
Please note that this code is completely unrelated to devfs, the
"/dev file system support".
Most users want to say Y here.
config USB_BANDWIDTH
bool "Enforce USB bandwidth allocation (EXPERIMENTAL)"
depends on USB && EXPERIMENTAL
help
If you say Y here, the USB subsystem enforces USB bandwidth
allocation and will prevent some device opens from succeeding
if they would cause USB bandwidth usage to go above 90% of
the bus bandwidth.
If you say N here, these conditions will cause warning messages
about USB bandwidth usage to be logged and some devices or
drivers may not work correctly.
config USB_DYNAMIC_MINORS
bool "Dynamic USB minor allocation (EXPERIMENTAL)"
depends on USB && EXPERIMENTAL
help
If you say Y here, the USB subsystem will use dynamic minor
allocation for any device that uses the USB major number.
This means that you can have more than 16 of a single type
of device (like USB printers).
If you are unsure about this, say N here.
config USB_SUSPEND
bool "USB suspend/resume (EXPERIMENTAL)"
depends on USB && PM && EXPERIMENTAL
help
If you say Y here, you can use driver calls or the sysfs
"power/state" file to suspend or resume individual USB
peripherals. There are many related features, such as
remote wakeup and driver-specific suspend processing, that
may not yet work as expected.
If you are unsure about this, say N here.
config USB_OTG
bool
depends on USB && EXPERIMENTAL
select USB_SUSPEND
default n
config USB_OTG_WHITELIST
bool "Rely on OTG Targeted Peripherals List"
depends on USB_OTG
default y
help
If you say Y here, the "otg_whitelist.h" file will be used as a
product whitelist, so USB peripherals not listed there will be
rejected during enumeration. This behavior is required by the
USB OTG specification for all devices not on your product's
"Targeted Peripherals List".
Otherwise, peripherals not listed there will only generate a
warning and enumeration will continue. That's more like what
normal Linux-USB hosts do (other than the warning), and is
convenient for many stages of product development.

16
drivers/usb/core/Makefile Normal file
View file

@ -0,0 +1,16 @@
#
# Makefile for USB Core files and filesystem
#
usbcore-objs := usb.o hub.o hcd.o urb.o message.o \
config.o file.o buffer.o sysfs.o
ifeq ($(CONFIG_PCI),y)
usbcore-objs += hcd-pci.o
endif
ifeq ($(CONFIG_USB_DEVICEFS),y)
usbcore-objs += devio.o inode.o devices.o
endif
obj-$(CONFIG_USB) += usbcore.o

154
drivers/usb/core/buffer.c Normal file
View file

@ -0,0 +1,154 @@
/*
* DMA memory management for framework level HCD code (hc_driver)
*
* This implementation plugs in through generic "usb_bus" level methods,
* and should work with all USB controllers, regardles of bus type.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
#include "hcd.h"
/*
* DMA-Coherent Buffers
*/
/* FIXME tune these based on pool statistics ... */
static const size_t pool_max [HCD_BUFFER_POOLS] = {
/* platforms without dma-friendly caches might need to
* prevent cacheline sharing...
*/
32,
128,
512,
PAGE_SIZE / 2
/* bigger --> allocate pages */
};
/* SETUP primitives */
/**
* hcd_buffer_create - initialize buffer pools
* @hcd: the bus whose buffer pools are to be initialized
* Context: !in_interrupt()
*
* Call this as part of initializing a host controller that uses the dma
* memory allocators. It initializes some pools of dma-coherent memory that
* will be shared by all drivers using that controller, or returns a negative
* errno value on error.
*
* Call hcd_buffer_destroy() to clean up after using those pools.
*/
int hcd_buffer_create (struct usb_hcd *hcd)
{
char name [16];
int i, size;
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (!(size = pool_max [i]))
continue;
snprintf (name, sizeof name, "buffer-%d", size);
hcd->pool [i] = dma_pool_create (name, hcd->self.controller,
size, size, 0);
if (!hcd->pool [i]) {
hcd_buffer_destroy (hcd);
return -ENOMEM;
}
}
return 0;
}
/**
* hcd_buffer_destroy - deallocate buffer pools
* @hcd: the bus whose buffer pools are to be destroyed
* Context: !in_interrupt()
*
* This frees the buffer pools created by hcd_buffer_create().
*/
void hcd_buffer_destroy (struct usb_hcd *hcd)
{
int i;
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
struct dma_pool *pool = hcd->pool [i];
if (pool) {
dma_pool_destroy (pool);
hcd->pool[i] = NULL;
}
}
}
/* sometimes alloc/free could use kmalloc with SLAB_DMA, for
* better sharing and to leverage mm/slab.c intelligence.
*/
void *hcd_buffer_alloc (
struct usb_bus *bus,
size_t size,
int mem_flags,
dma_addr_t *dma
)
{
struct usb_hcd *hcd = bus->hcpriv;
int i;
/* some USB hosts just use PIO */
if (!bus->controller->dma_mask) {
*dma = ~(dma_addr_t) 0;
return kmalloc (size, mem_flags);
}
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max [i])
return dma_pool_alloc (hcd->pool [i], mem_flags, dma);
}
return dma_alloc_coherent (hcd->self.controller, size, dma, 0);
}
void hcd_buffer_free (
struct usb_bus *bus,
size_t size,
void *addr,
dma_addr_t dma
)
{
struct usb_hcd *hcd = bus->hcpriv;
int i;
if (!addr)
return;
if (!bus->controller->dma_mask) {
kfree (addr);
return;
}
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max [i]) {
dma_pool_free (hcd->pool [i], addr, dma);
return;
}
}
dma_free_coherent (hcd->self.controller, size, addr, dma);
}

534
drivers/usb/core/config.c Normal file
View file

@ -0,0 +1,534 @@
#include <linux/config.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#endif
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <asm/byteorder.h>
#define USB_MAXALTSETTING 128 /* Hard limit */
#define USB_MAXENDPOINTS 30 /* Hard limit */
#define USB_MAXCONFIG 8 /* Arbitrary limit */
static inline const char *plural(int n)
{
return (n == 1 ? "" : "s");
}
static int find_next_descriptor(unsigned char *buffer, int size,
int dt1, int dt2, int *num_skipped)
{
struct usb_descriptor_header *h;
int n = 0;
unsigned char *buffer0 = buffer;
/* Find the next descriptor of type dt1 or dt2 */
while (size > 0) {
h = (struct usb_descriptor_header *) buffer;
if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2)
break;
buffer += h->bLength;
size -= h->bLength;
++n;
}
/* Store the number of descriptors skipped and return the
* number of bytes skipped */
if (num_skipped)
*num_skipped = n;
return buffer - buffer0;
}
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
int asnum, struct usb_host_interface *ifp, int num_ep,
unsigned char *buffer, int size)
{
unsigned char *buffer0 = buffer;
struct usb_endpoint_descriptor *d;
struct usb_host_endpoint *endpoint;
int n, i;
d = (struct usb_endpoint_descriptor *) buffer;
buffer += d->bLength;
size -= d->bLength;
if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)
n = USB_DT_ENDPOINT_AUDIO_SIZE;
else if (d->bLength >= USB_DT_ENDPOINT_SIZE)
n = USB_DT_ENDPOINT_SIZE;
else {
dev_warn(ddev, "config %d interface %d altsetting %d has an "
"invalid endpoint descriptor of length %d, skipping\n",
cfgno, inum, asnum, d->bLength);
goto skip_to_next_endpoint_or_interface_descriptor;
}
i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK;
if (i >= 16 || i == 0) {
dev_warn(ddev, "config %d interface %d altsetting %d has an "
"invalid endpoint with address 0x%X, skipping\n",
cfgno, inum, asnum, d->bEndpointAddress);
goto skip_to_next_endpoint_or_interface_descriptor;
}
/* Only store as many endpoints as we have room for */
if (ifp->desc.bNumEndpoints >= num_ep)
goto skip_to_next_endpoint_or_interface_descriptor;
endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
++ifp->desc.bNumEndpoints;
memcpy(&endpoint->desc, d, n);
INIT_LIST_HEAD(&endpoint->urb_list);
/* Skip over any Class Specific or Vendor Specific descriptors;
* find the next endpoint or interface descriptor */
endpoint->extra = buffer;
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
USB_DT_INTERFACE, &n);
endpoint->extralen = i;
if (n > 0)
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
n, plural(n), "endpoint");
return buffer - buffer0 + i;
skip_to_next_endpoint_or_interface_descriptor:
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
USB_DT_INTERFACE, NULL);
return buffer - buffer0 + i;
}
void usb_release_interface_cache(struct kref *ref)
{
struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref);
int j;
for (j = 0; j < intfc->num_altsetting; j++)
kfree(intfc->altsetting[j].endpoint);
kfree(intfc);
}
static int usb_parse_interface(struct device *ddev, int cfgno,
struct usb_host_config *config, unsigned char *buffer, int size,
u8 inums[], u8 nalts[])
{
unsigned char *buffer0 = buffer;
struct usb_interface_descriptor *d;
int inum, asnum;
struct usb_interface_cache *intfc;
struct usb_host_interface *alt;
int i, n;
int len, retval;
int num_ep, num_ep_orig;
d = (struct usb_interface_descriptor *) buffer;
buffer += d->bLength;
size -= d->bLength;
if (d->bLength < USB_DT_INTERFACE_SIZE)
goto skip_to_next_interface_descriptor;
/* Which interface entry is this? */
intfc = NULL;
inum = d->bInterfaceNumber;
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
if (inums[i] == inum) {
intfc = config->intf_cache[i];
break;
}
}
if (!intfc || intfc->num_altsetting >= nalts[i])
goto skip_to_next_interface_descriptor;
/* Check for duplicate altsetting entries */
asnum = d->bAlternateSetting;
for ((i = 0, alt = &intfc->altsetting[0]);
i < intfc->num_altsetting;
(++i, ++alt)) {
if (alt->desc.bAlternateSetting == asnum) {
dev_warn(ddev, "Duplicate descriptor for config %d "
"interface %d altsetting %d, skipping\n",
cfgno, inum, asnum);
goto skip_to_next_interface_descriptor;
}
}
++intfc->num_altsetting;
memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE);
/* Skip over any Class Specific or Vendor Specific descriptors;
* find the first endpoint or interface descriptor */
alt->extra = buffer;
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
USB_DT_INTERFACE, &n);
alt->extralen = i;
if (n > 0)
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
n, plural(n), "interface");
buffer += i;
size -= i;
/* Allocate space for the right(?) number of endpoints */
num_ep = num_ep_orig = alt->desc.bNumEndpoints;
alt->desc.bNumEndpoints = 0; // Use as a counter
if (num_ep > USB_MAXENDPOINTS) {
dev_warn(ddev, "too many endpoints for config %d interface %d "
"altsetting %d: %d, using maximum allowed: %d\n",
cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS);
num_ep = USB_MAXENDPOINTS;
}
len = sizeof(struct usb_host_endpoint) * num_ep;
alt->endpoint = kmalloc(len, GFP_KERNEL);
if (!alt->endpoint)
return -ENOMEM;
memset(alt->endpoint, 0, len);
/* Parse all the endpoint descriptors */
n = 0;
while (size > 0) {
if (((struct usb_descriptor_header *) buffer)->bDescriptorType
== USB_DT_INTERFACE)
break;
retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt,
num_ep, buffer, size);
if (retval < 0)
return retval;
++n;
buffer += retval;
size -= retval;
}
if (n != num_ep_orig)
dev_warn(ddev, "config %d interface %d altsetting %d has %d "
"endpoint descriptor%s, different from the interface "
"descriptor's value: %d\n",
cfgno, inum, asnum, n, plural(n), num_ep_orig);
return buffer - buffer0;
skip_to_next_interface_descriptor:
i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
USB_DT_INTERFACE, NULL);
return buffer - buffer0 + i;
}
static int usb_parse_configuration(struct device *ddev, int cfgidx,
struct usb_host_config *config, unsigned char *buffer, int size)
{
unsigned char *buffer0 = buffer;
int cfgno;
int nintf, nintf_orig;
int i, j, n;
struct usb_interface_cache *intfc;
unsigned char *buffer2;
int size2;
struct usb_descriptor_header *header;
int len, retval;
u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
config->desc.bLength < USB_DT_CONFIG_SIZE) {
dev_err(ddev, "invalid descriptor for config index %d: "
"type = 0x%X, length = %d\n", cfgidx,
config->desc.bDescriptorType, config->desc.bLength);
return -EINVAL;
}
cfgno = config->desc.bConfigurationValue;
buffer += config->desc.bLength;
size -= config->desc.bLength;
nintf = nintf_orig = config->desc.bNumInterfaces;
if (nintf > USB_MAXINTERFACES) {
dev_warn(ddev, "config %d has too many interfaces: %d, "
"using maximum allowed: %d\n",
cfgno, nintf, USB_MAXINTERFACES);
nintf = USB_MAXINTERFACES;
}
/* Go through the descriptors, checking their length and counting the
* number of altsettings for each interface */
n = 0;
for ((buffer2 = buffer, size2 = size);
size2 > 0;
(buffer2 += header->bLength, size2 -= header->bLength)) {
if (size2 < sizeof(struct usb_descriptor_header)) {
dev_warn(ddev, "config %d descriptor has %d excess "
"byte%s, ignoring\n",
cfgno, size2, plural(size2));
break;
}
header = (struct usb_descriptor_header *) buffer2;
if ((header->bLength > size2) || (header->bLength < 2)) {
dev_warn(ddev, "config %d has an invalid descriptor "
"of length %d, skipping remainder of the config\n",
cfgno, header->bLength);
break;
}
if (header->bDescriptorType == USB_DT_INTERFACE) {
struct usb_interface_descriptor *d;
int inum;
d = (struct usb_interface_descriptor *) header;
if (d->bLength < USB_DT_INTERFACE_SIZE) {
dev_warn(ddev, "config %d has an invalid "
"interface descriptor of length %d, "
"skipping\n", cfgno, d->bLength);
continue;
}
inum = d->bInterfaceNumber;
if (inum >= nintf_orig)
dev_warn(ddev, "config %d has an invalid "
"interface number: %d but max is %d\n",
cfgno, inum, nintf_orig - 1);
/* Have we already encountered this interface?
* Count its altsettings */
for (i = 0; i < n; ++i) {
if (inums[i] == inum)
break;
}
if (i < n) {
if (nalts[i] < 255)
++nalts[i];
} else if (n < USB_MAXINTERFACES) {
inums[n] = inum;
nalts[n] = 1;
++n;
}
} else if (header->bDescriptorType == USB_DT_DEVICE ||
header->bDescriptorType == USB_DT_CONFIG)
dev_warn(ddev, "config %d contains an unexpected "
"descriptor of type 0x%X, skipping\n",
cfgno, header->bDescriptorType);
} /* for ((buffer2 = buffer, size2 = size); ...) */
size = buffer2 - buffer;
config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0);
if (n != nintf)
dev_warn(ddev, "config %d has %d interface%s, different from "
"the descriptor's value: %d\n",
cfgno, n, plural(n), nintf_orig);
else if (n == 0)
dev_warn(ddev, "config %d has no interfaces?\n", cfgno);
config->desc.bNumInterfaces = nintf = n;
/* Check for missing interface numbers */
for (i = 0; i < nintf; ++i) {
for (j = 0; j < nintf; ++j) {
if (inums[j] == i)
break;
}
if (j >= nintf)
dev_warn(ddev, "config %d has no interface number "
"%d\n", cfgno, i);
}
/* Allocate the usb_interface_caches and altsetting arrays */
for (i = 0; i < nintf; ++i) {
j = nalts[i];
if (j > USB_MAXALTSETTING) {
dev_warn(ddev, "too many alternate settings for "
"config %d interface %d: %d, "
"using maximum allowed: %d\n",
cfgno, inums[i], j, USB_MAXALTSETTING);
nalts[i] = j = USB_MAXALTSETTING;
}
len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
config->intf_cache[i] = intfc = kmalloc(len, GFP_KERNEL);
if (!intfc)
return -ENOMEM;
memset(intfc, 0, len);
kref_init(&intfc->ref);
}
/* Skip over any Class Specific or Vendor Specific descriptors;
* find the first interface descriptor */
config->extra = buffer;
i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
USB_DT_INTERFACE, &n);
config->extralen = i;
if (n > 0)
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
n, plural(n), "configuration");
buffer += i;
size -= i;
/* Parse all the interface/altsetting descriptors */
while (size > 0) {
retval = usb_parse_interface(ddev, cfgno, config,
buffer, size, inums, nalts);
if (retval < 0)
return retval;
buffer += retval;
size -= retval;
}
/* Check for missing altsettings */
for (i = 0; i < nintf; ++i) {
intfc = config->intf_cache[i];
for (j = 0; j < intfc->num_altsetting; ++j) {
for (n = 0; n < intfc->num_altsetting; ++n) {
if (intfc->altsetting[n].desc.
bAlternateSetting == j)
break;
}
if (n >= intfc->num_altsetting)
dev_warn(ddev, "config %d interface %d has no "
"altsetting %d\n", cfgno, inums[i], j);
}
}
return 0;
}
// hub-only!! ... and only exported for reset/reinit path.
// otherwise used internally on disconnect/destroy path
void usb_destroy_configuration(struct usb_device *dev)
{
int c, i;
if (!dev->config)
return;
if (dev->rawdescriptors) {
for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
kfree(dev->rawdescriptors[i]);
kfree(dev->rawdescriptors);
dev->rawdescriptors = NULL;
}
for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
struct usb_host_config *cf = &dev->config[c];
kfree(cf->string);
cf->string = NULL;
for (i = 0; i < cf->desc.bNumInterfaces; i++) {
if (cf->intf_cache[i])
kref_put(&cf->intf_cache[i]->ref,
usb_release_interface_cache);
}
}
kfree(dev->config);
dev->config = NULL;
}
// hub-only!! ... and only in reset path, or usb_new_device()
// (used by real hubs and virtual root hubs)
int usb_get_configuration(struct usb_device *dev)
{
struct device *ddev = &dev->dev;
int ncfg = dev->descriptor.bNumConfigurations;
int result = -ENOMEM;
unsigned int cfgno, length;
unsigned char *buffer;
unsigned char *bigbuffer;
struct usb_config_descriptor *desc;
if (ncfg > USB_MAXCONFIG) {
dev_warn(ddev, "too many configurations: %d, "
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
}
if (ncfg < 1) {
dev_err(ddev, "no configurations\n");
return -EINVAL;
}
length = ncfg * sizeof(struct usb_host_config);
dev->config = kmalloc(length, GFP_KERNEL);
if (!dev->config)
goto err2;
memset(dev->config, 0, length);
length = ncfg * sizeof(char *);
dev->rawdescriptors = kmalloc(length, GFP_KERNEL);
if (!dev->rawdescriptors)
goto err2;
memset(dev->rawdescriptors, 0, length);
buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
if (!buffer)
goto err2;
desc = (struct usb_config_descriptor *)buffer;
for (cfgno = 0; cfgno < ncfg; cfgno++) {
/* We grab just the first descriptor so we know how long
* the whole configuration is */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
buffer, USB_DT_CONFIG_SIZE);
if (result < 0) {
dev_err(ddev, "unable to read config index %d "
"descriptor/%s\n", cfgno, "start");
goto err;
} else if (result < 4) {
dev_err(ddev, "config index %d descriptor too short "
"(expected %i, got %i)\n", cfgno,
USB_DT_CONFIG_SIZE, result);
result = -EINVAL;
goto err;
}
length = max((int) le16_to_cpu(desc->wTotalLength),
USB_DT_CONFIG_SIZE);
/* Now that we know the length, get the whole thing */
bigbuffer = kmalloc(length, GFP_KERNEL);
if (!bigbuffer) {
result = -ENOMEM;
goto err;
}
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
bigbuffer, length);
if (result < 0) {
dev_err(ddev, "unable to read config index %d "
"descriptor/%s\n", cfgno, "all");
kfree(bigbuffer);
goto err;
}
if (result < length) {
dev_warn(ddev, "config index %d descriptor too short "
"(expected %i, got %i)\n", cfgno, length, result);
length = result;
}
dev->rawdescriptors[cfgno] = bigbuffer;
result = usb_parse_configuration(&dev->dev, cfgno,
&dev->config[cfgno], bigbuffer, length);
if (result < 0) {
++cfgno;
goto err;
}
}
result = 0;
err:
kfree(buffer);
dev->descriptor.bNumConfigurations = cfgno;
err2:
if (result == -ENOMEM)
dev_err(ddev, "out of memory\n");
return result;
}

677
drivers/usb/core/devices.c Normal file
View file

@ -0,0 +1,677 @@
/*
* devices.c
* (C) Copyright 1999 Randy Dunlap.
* (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device)
* (C) Copyright 1999 Deti Fliegl (new USB architecture)
*
* 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
*
*************************************************************
*
* <mountpoint>/devices contains USB topology, device, config, class,
* interface, & endpoint data.
*
* I considered using /proc/bus/usb/devices/device# for each device
* as it is attached or detached, but I didn't like this for some
* reason -- maybe it's just too deep of a directory structure.
* I also don't like looking in multiple places to gather and view
* the data. Having only one file for ./devices also prevents race
* conditions that could arise if a program was reading device info
* for devices that are being removed (unplugged). (That is, the
* program may find a directory for devnum_12 then try to open it,
* but it was just unplugged, so the directory is now deleted.
* But programs would just have to be prepared for situations like
* this in any plug-and-play environment.)
*
* 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch>
* Converted the whole proc stuff to real
* read methods. Now not the whole device list needs to fit
* into one page, only the device list for one bus.
* Added a poll method to /proc/bus/usb/devices, to wake
* up an eventual usbd
* 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch>
* Turned into its own filesystem
* 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk>
* Converted file reading routine to dump to buffer once
* per device, not per bus
*
* $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $
*/
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/usb.h>
#include <linux/smp_lock.h>
#include <linux/usbdevice_fs.h>
#include <asm/uaccess.h>
#include "hcd.h"
#define MAX_TOPO_LEVEL 6
/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
#define ALLOW_SERIAL_NUMBER
static char *format_topo =
/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */
"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n";
static char *format_string_manufacturer =
/* S: Manufacturer=xxxx */
"S: Manufacturer=%.100s\n";
static char *format_string_product =
/* S: Product=xxxx */
"S: Product=%.100s\n";
#ifdef ALLOW_SERIAL_NUMBER
static char *format_string_serialnumber =
/* S: SerialNumber=xxxx */
"S: SerialNumber=%.100s\n";
#endif
static char *format_bandwidth =
/* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */
"B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n";
static char *format_device1 =
/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */
"D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n";
static char *format_device2 =
/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */
"P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n";
static char *format_config =
/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
"C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
static char *format_iface =
/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
"I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
static char *format_endpt =
/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */
"E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n";
/*
* Need access to the driver and USB bus lists.
* extern struct list_head usb_bus_list;
* However, these will come from functions that return ptrs to each of them.
*/
static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq);
static unsigned int conndiscevcnt = 0;
/* this struct stores the poll state for <mountpoint>/devices pollers */
struct usb_device_status {
unsigned int lastev;
};
struct class_info {
int class;
char *class_name;
};
static const struct class_info clas_info[] =
{ /* max. 5 chars. per name string */
{USB_CLASS_PER_INTERFACE, ">ifc"},
{USB_CLASS_AUDIO, "audio"},
{USB_CLASS_COMM, "comm."},
{USB_CLASS_HID, "HID"},
{USB_CLASS_HUB, "hub"},
{USB_CLASS_PHYSICAL, "PID"},
{USB_CLASS_PRINTER, "print"},
{USB_CLASS_MASS_STORAGE, "stor."},
{USB_CLASS_CDC_DATA, "data"},
{USB_CLASS_APP_SPEC, "app."},
{USB_CLASS_VENDOR_SPEC, "vend."},
{USB_CLASS_STILL_IMAGE, "still"},
{USB_CLASS_CSCID, "scard"},
{USB_CLASS_CONTENT_SEC, "c-sec"},
{-1, "unk."} /* leave as last */
};
/*****************************************************************/
void usbfs_conn_disc_event(void)
{
conndiscevcnt++;
wake_up(&deviceconndiscwq);
}
static const char *class_decode(const int class)
{
int ix;
for (ix = 0; clas_info[ix].class != -1; ix++)
if (clas_info[ix].class == class)
break;
return (clas_info[ix].class_name);
}
static char *usb_dump_endpoint_descriptor (
int speed,
char *start,
char *end,
const struct usb_endpoint_descriptor *desc
)
{
char dir, unit, *type;
unsigned interval, in, bandwidth = 1;
if (start > end)
return start;
in = (desc->bEndpointAddress & USB_DIR_IN);
dir = in ? 'I' : 'O';
if (speed == USB_SPEED_HIGH) {
switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) {
case 1 << 11: bandwidth = 2; break;
case 2 << 11: bandwidth = 3; break;
}
}
/* this isn't checking for illegal values */
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
case USB_ENDPOINT_XFER_CONTROL:
type = "Ctrl";
if (speed == USB_SPEED_HIGH) /* uframes per NAK */
interval = desc->bInterval;
else
interval = 0;
dir = 'B'; /* ctrl is bidirectional */
break;
case USB_ENDPOINT_XFER_ISOC:
type = "Isoc";
interval = 1 << (desc->bInterval - 1);
break;
case USB_ENDPOINT_XFER_BULK:
type = "Bulk";
if (speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
interval = desc->bInterval;
else
interval = 0;
break;
case USB_ENDPOINT_XFER_INT:
type = "Int.";
if (speed == USB_SPEED_HIGH) {
interval = 1 << (desc->bInterval - 1);
} else
interval = desc->bInterval;
break;
default: /* "can't happen" */
return start;
}
interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000;
if (interval % 1000)
unit = 'u';
else {
unit = 'm';
interval /= 1000;
}
start += sprintf(start, format_endpt, desc->bEndpointAddress, dir,
desc->bmAttributes, type,
(le16_to_cpu(desc->wMaxPacketSize) & 0x07ff) * bandwidth,
interval, unit);
return start;
}
static char *usb_dump_interface_descriptor(char *start, char *end,
const struct usb_interface_cache *intfc,
const struct usb_interface *iface,
int setno)
{
const struct usb_interface_descriptor *desc = &intfc->altsetting[setno].desc;
char *driver_name = "";
if (start > end)
return start;
down_read(&usb_bus_type.subsys.rwsem);
if (iface)
driver_name = (iface->dev.driver
? iface->dev.driver->name
: "(none)");
start += sprintf(start, format_iface,
desc->bInterfaceNumber,
desc->bAlternateSetting,
desc->bNumEndpoints,
desc->bInterfaceClass,
class_decode(desc->bInterfaceClass),
desc->bInterfaceSubClass,
desc->bInterfaceProtocol,
driver_name);
up_read(&usb_bus_type.subsys.rwsem);
return start;
}
static char *usb_dump_interface(
int speed,
char *start,
char *end,
const struct usb_interface_cache *intfc,
const struct usb_interface *iface,
int setno
) {
const struct usb_host_interface *desc = &intfc->altsetting[setno];
int i;
start = usb_dump_interface_descriptor(start, end, intfc, iface, setno);
for (i = 0; i < desc->desc.bNumEndpoints; i++) {
if (start > end)
return start;
start = usb_dump_endpoint_descriptor(speed,
start, end, &desc->endpoint[i].desc);
}
return start;
}
/* TBD:
* 0. TBDs
* 1. marking active interface altsettings (code lists all, but should mark
* which ones are active, if any)
*/
static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, int active)
{
if (start > end)
return start;
start += sprintf(start, format_config,
active ? '*' : ' ', /* mark active/actual/current cfg. */
desc->bNumInterfaces,
desc->bConfigurationValue,
desc->bmAttributes,
desc->bMaxPower * 2);
return start;
}
static char *usb_dump_config (
int speed,
char *start,
char *end,
const struct usb_host_config *config,
int active
)
{
int i, j;
struct usb_interface_cache *intfc;
struct usb_interface *interface;
if (start > end)
return start;
if (!config) /* getting these some in 2.3.7; none in 2.3.6 */
return start + sprintf(start, "(null Cfg. desc.)\n");
start = usb_dump_config_descriptor(start, end, &config->desc, active);
for (i = 0; i < config->desc.bNumInterfaces; i++) {
intfc = config->intf_cache[i];
interface = config->interface[i];
for (j = 0; j < intfc->num_altsetting; j++) {
if (start > end)
return start;
start = usb_dump_interface(speed,
start, end, intfc, interface, j);
}
}
return start;
}
/*
* Dump the different USB descriptors.
*/
static char *usb_dump_device_descriptor(char *start, char *end, const struct usb_device_descriptor *desc)
{
u16 bcdUSB = le16_to_cpu(desc->bcdUSB);
u16 bcdDevice = le16_to_cpu(desc->bcdDevice);
if (start > end)
return start;
start += sprintf (start, format_device1,
bcdUSB >> 8, bcdUSB & 0xff,
desc->bDeviceClass,
class_decode (desc->bDeviceClass),
desc->bDeviceSubClass,
desc->bDeviceProtocol,
desc->bMaxPacketSize0,
desc->bNumConfigurations);
if (start > end)
return start;
start += sprintf(start, format_device2,
le16_to_cpu(desc->idVendor),
le16_to_cpu(desc->idProduct),
bcdDevice >> 8, bcdDevice & 0xff);
return start;
}
/*
* Dump the different strings that this device holds.
*/
static char *usb_dump_device_strings (char *start, char *end, struct usb_device *dev)
{
if (start > end)
return start;
if (dev->manufacturer)
start += sprintf(start, format_string_manufacturer, dev->manufacturer);
if (start > end)
goto out;
if (dev->product)
start += sprintf(start, format_string_product, dev->product);
if (start > end)
goto out;
#ifdef ALLOW_SERIAL_NUMBER
if (dev->serial)
start += sprintf(start, format_string_serialnumber, dev->serial);
#endif
out:
return start;
}
static char *usb_dump_desc(char *start, char *end, struct usb_device *dev)
{
int i;
if (start > end)
return start;
start = usb_dump_device_descriptor(start, end, &dev->descriptor);
if (start > end)
return start;
start = usb_dump_device_strings (start, end, dev);
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
if (start > end)
return start;
start = usb_dump_config(dev->speed,
start, end, dev->config + i,
/* active ? */
(dev->config + i) == dev->actconfig);
}
return start;
}
#ifdef PROC_EXTRA /* TBD: may want to add this code later */
static char *usb_dump_hub_descriptor(char *start, char *end, const struct usb_hub_descriptor * desc)
{
int leng = USB_DT_HUB_NONVAR_SIZE;
unsigned char *ptr = (unsigned char *)desc;
if (start > end)
return start;
start += sprintf(start, "Interface:");
while (leng && start <= end) {
start += sprintf(start, " %02x", *ptr);
ptr++; leng--;
}
*start++ = '\n';
return start;
}
static char *usb_dump_string(char *start, char *end, const struct usb_device *dev, char *id, int index)
{
if (start > end)
return start;
start += sprintf(start, "Interface:");
if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index])
start += sprintf(start, "%s: %.100s ", id, dev->stringindex[index]);
return start;
}
#endif /* PROC_EXTRA */
/*****************************************************************/
/* This is a recursive function. Parameters:
* buffer - the user-space buffer to write data into
* nbytes - the maximum number of bytes to write
* skip_bytes - the number of bytes to skip before writing anything
* file_offset - the offset into the devices file on completion
* The caller must own the device lock.
*/
static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset,
struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count)
{
int chix;
int ret, cnt = 0;
int parent_devnum = 0;
char *pages_start, *data_end, *speed;
unsigned int length;
ssize_t total_written = 0;
/* don't bother with anything else if we're not writing any data */
if (*nbytes <= 0)
return 0;
if (level > MAX_TOPO_LEVEL)
return 0;
/* allocate 2^1 pages = 8K (on i386); should be more than enough for one device */
if (!(pages_start = (char*) __get_free_pages(GFP_KERNEL,1)))
return -ENOMEM;
if (usbdev->parent && usbdev->parent->devnum != -1)
parent_devnum = usbdev->parent->devnum;
/*
* So the root hub's parent is 0 and any device that is
* plugged into the root hub has a parent of 0.
*/
switch (usbdev->speed) {
case USB_SPEED_LOW:
speed = "1.5"; break;
case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */
case USB_SPEED_FULL:
speed = "12 "; break;
case USB_SPEED_HIGH:
speed = "480"; break;
default:
speed = "?? ";
}
data_end = pages_start + sprintf(pages_start, format_topo,
bus->busnum, level, parent_devnum,
index, count, usbdev->devnum,
speed, usbdev->maxchild);
/*
* level = topology-tier level;
* parent_devnum = parent device number;
* index = parent's connector number;
* count = device count at this level
*/
/* If this is the root hub, display the bandwidth information */
if (level == 0) {
int max;
/* high speed reserves 80%, full/low reserves 90% */
if (usbdev->speed == USB_SPEED_HIGH)
max = 800;
else
max = FRAME_TIME_MAX_USECS_ALLOC;
/* report "average" periodic allocation over a microsecond.
* the schedules are actually bursty, HCDs need to deal with
* that and just compute/report this average.
*/
data_end += sprintf(data_end, format_bandwidth,
bus->bandwidth_allocated, max,
(100 * bus->bandwidth_allocated + max / 2)
/ max,
bus->bandwidth_int_reqs,
bus->bandwidth_isoc_reqs);
}
data_end = usb_dump_desc(data_end, pages_start + (2 * PAGE_SIZE) - 256, usbdev);
if (data_end > (pages_start + (2 * PAGE_SIZE) - 256))
data_end += sprintf(data_end, "(truncated)\n");
length = data_end - pages_start;
/* if we can start copying some data to the user */
if (length > *skip_bytes) {
length -= *skip_bytes;
if (length > *nbytes)
length = *nbytes;
if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) {
free_pages((unsigned long)pages_start, 1);
return -EFAULT;
}
*nbytes -= length;
*file_offset += length;
total_written += length;
*buffer += length;
*skip_bytes = 0;
} else
*skip_bytes -= length;
free_pages((unsigned long)pages_start, 1);
/* Now look at all of this device's children. */
for (chix = 0; chix < usbdev->maxchild; chix++) {
struct usb_device *childdev = usbdev->children[chix];
if (childdev) {
down(&childdev->serialize);
ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev,
bus, level + 1, chix, ++cnt);
up(&childdev->serialize);
if (ret == -EFAULT)
return total_written;
total_written += ret;
}
}
return total_written;
}
static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
struct usb_bus *bus;
ssize_t ret, total_written = 0;
loff_t skip_bytes = *ppos;
if (*ppos < 0)
return -EINVAL;
if (nbytes <= 0)
return 0;
if (!access_ok(VERIFY_WRITE, buf, nbytes))
return -EFAULT;
down (&usb_bus_list_lock);
/* print devices for all busses */
list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */
if (!bus->root_hub)
continue;
usb_lock_device(bus->root_hub);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0);
usb_unlock_device(bus->root_hub);
if (ret < 0) {
up(&usb_bus_list_lock);
return ret;
}
total_written += ret;
}
up (&usb_bus_list_lock);
return total_written;
}
/* Kernel lock for "lastev" protection */
static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
{
struct usb_device_status *st = (struct usb_device_status *)file->private_data;
unsigned int mask = 0;
lock_kernel();
if (!st) {
st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL);
if (!st) {
unlock_kernel();
return POLLIN;
}
/* we may have dropped BKL - need to check for having lost the race */
if (file->private_data) {
kfree(st);
st = file->private_data;
goto lost_race;
}
/*
* need to prevent the module from being unloaded, since
* proc_unregister does not call the release method and
* we would have a memory leak
*/
st->lastev = conndiscevcnt;
file->private_data = st;
mask = POLLIN;
}
lost_race:
if (file->f_mode & FMODE_READ)
poll_wait(file, &deviceconndiscwq, wait);
if (st->lastev != conndiscevcnt)
mask |= POLLIN;
st->lastev = conndiscevcnt;
unlock_kernel();
return mask;
}
static int usb_device_open(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
static int usb_device_release(struct inode *inode, struct file *file)
{
if (file->private_data) {
kfree(file->private_data);
file->private_data = NULL;
}
return 0;
}
static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
{
loff_t ret;
lock_kernel();
switch (orig) {
case 0:
file->f_pos = offset;
ret = file->f_pos;
break;
case 1:
file->f_pos += offset;
ret = file->f_pos;
break;
case 2:
default:
ret = -EINVAL;
}
unlock_kernel();
return ret;
}
struct file_operations usbfs_devices_fops = {
.llseek = usb_device_lseek,
.read = usb_device_read,
.poll = usb_device_poll,
.open = usb_device_open,
.release = usb_device_release,
};

1483
drivers/usb/core/devio.c Normal file

File diff suppressed because it is too large Load diff

227
drivers/usb/core/file.c Normal file
View file

@ -0,0 +1,227 @@
/*
* drivers/usb/file.c
*
* (C) Copyright Linus Torvalds 1999
* (C) Copyright Johannes Erdfelt 1999-2001
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
more docs, etc)
* (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
#define MAX_USB_MINORS 256
static struct file_operations *usb_minors[MAX_USB_MINORS];
static DEFINE_SPINLOCK(minor_lock);
static int usb_open(struct inode * inode, struct file * file)
{
int minor = iminor(inode);
struct file_operations *c;
int err = -ENODEV;
struct file_operations *old_fops, *new_fops = NULL;
spin_lock (&minor_lock);
c = usb_minors[minor];
if (!c || !(new_fops = fops_get(c))) {
spin_unlock(&minor_lock);
return err;
}
spin_unlock(&minor_lock);
old_fops = file->f_op;
file->f_op = new_fops;
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
if (file->f_op->open)
err = file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
static struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
};
static struct class_simple *usb_class;
int usb_major_init(void)
{
int error;
error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
if (error) {
err("unable to get major %d for usb devices", USB_MAJOR);
goto out;
}
usb_class = class_simple_create(THIS_MODULE, "usb");
if (IS_ERR(usb_class)) {
err("class_simple_create failed for usb devices");
unregister_chrdev(USB_MAJOR, "usb");
goto out;
}
devfs_mk_dir("usb");
out:
return error;
}
void usb_major_cleanup(void)
{
class_simple_destroy(usb_class);
devfs_remove("usb");
unregister_chrdev(USB_MAJOR, "usb");
}
/**
* usb_register_dev - register a USB device, and ask for a minor number
* @intf: pointer to the usb_interface that is being registered
* @class_driver: pointer to the usb_class_driver for this device
*
* This should be called by all USB drivers that use the USB major number.
* If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be
* dynamically allocated out of the list of available ones. If it is not
* enabled, the minor number will be based on the next available free minor,
* starting at the class_driver->minor_base.
*
* This function also creates the devfs file for the usb device, if devfs
* is enabled, and creates a usb class device in the sysfs tree.
*
* usb_deregister_dev() must be called when the driver is done with
* the minor numbers given out by this function.
*
* Returns -EINVAL if something bad happens with trying to register a
* device, and 0 on success.
*/
int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int retval = -EINVAL;
int minor_base = class_driver->minor_base;
int minor = 0;
char name[BUS_ID_SIZE];
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
/*
* We don't care what the device tries to start at, we want to start
* at zero to pack the devices into the smallest available space with
* no holes in the minor range.
*/
minor_base = 0;
#endif
intf->minor = -1;
dbg ("looking for a minor, starting at %d", minor_base);
if (class_driver->fops == NULL)
goto exit;
spin_lock (&minor_lock);
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
if (usb_minors[minor])
continue;
usb_minors[minor] = class_driver->fops;
retval = 0;
break;
}
spin_unlock (&minor_lock);
if (retval)
goto exit;
intf->minor = minor;
/* handle the devfs registration */
snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
devfs_mk_cdev(MKDEV(USB_MAJOR, minor), class_driver->mode, name);
/* create a usb class device for this usb interface */
temp = strrchr(name, '/');
if (temp && (temp[1] != 0x00))
++temp;
else
temp = name;
intf->class_dev = class_simple_device_add(usb_class, MKDEV(USB_MAJOR, minor), &intf->dev, "%s", temp);
if (IS_ERR(intf->class_dev)) {
spin_lock (&minor_lock);
usb_minors[intf->minor] = NULL;
spin_unlock (&minor_lock);
devfs_remove (name);
retval = PTR_ERR(intf->class_dev);
}
exit:
return retval;
}
EXPORT_SYMBOL(usb_register_dev);
/**
* usb_deregister_dev - deregister a USB device's dynamic minor.
* @intf: pointer to the usb_interface that is being deregistered
* @class_driver: pointer to the usb_class_driver for this device
*
* Used in conjunction with usb_register_dev(). This function is called
* when the USB driver is finished with the minor numbers gotten from a
* call to usb_register_dev() (usually when the device is disconnected
* from the system.)
*
* This function also cleans up the devfs file for the usb device, if devfs
* is enabled, and removes the usb class device from the sysfs tree.
*
* This should be called by all drivers that use the USB major number.
*/
void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int minor_base = class_driver->minor_base;
char name[BUS_ID_SIZE];
#ifdef CONFIG_USB_DYNAMIC_MINORS
minor_base = 0;
#endif
if (intf->minor == -1)
return;
dbg ("removing %d minor", intf->minor);
spin_lock (&minor_lock);
usb_minors[intf->minor] = NULL;
spin_unlock (&minor_lock);
snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
devfs_remove (name);
class_simple_device_remove(MKDEV(USB_MAJOR, intf->minor));
intf->class_dev = NULL;
intf->minor = -1;
}
EXPORT_SYMBOL(usb_deregister_dev);

358
drivers/usb/core/hcd-pci.c Normal file
View file

@ -0,0 +1,358 @@
/*
* (C) Copyright David Brownell 2000-2002
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/usb.h>
#include "hcd.h"
/* PCI-based HCs are normal, but custom bus glue should be ok */
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_pci_probe - initialize PCI-based HCDs
* @dev: USB Host Controller being probed
* @id: pci hotplug id connecting controller to HCD framework
* Context: !in_interrupt()
*
* Allocates basic PCI resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
*/
int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
struct hc_driver *driver;
struct usb_hcd *hcd;
int retval;
if (usb_disabled())
return -ENODEV;
if (!id || !(driver = (struct hc_driver *) id->driver_data))
return -EINVAL;
if (pci_enable_device (dev) < 0)
return -ENODEV;
dev->current_state = 0;
dev->dev.power.power_state = 0;
if (!dev->irq) {
dev_err (&dev->dev,
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
pci_name(dev));
retval = -ENODEV;
goto err1;
}
hcd = usb_create_hcd (driver, &dev->dev, pci_name(dev));
if (!hcd) {
retval = -ENOMEM;
goto err1;
}
if (driver->flags & HCD_MEMORY) { // EHCI, OHCI
hcd->rsrc_start = pci_resource_start (dev, 0);
hcd->rsrc_len = pci_resource_len (dev, 0);
if (!request_mem_region (hcd->rsrc_start, hcd->rsrc_len,
driver->description)) {
dev_dbg (&dev->dev, "controller already in use\n");
retval = -EBUSY;
goto err2;
}
hcd->regs = ioremap_nocache (hcd->rsrc_start, hcd->rsrc_len);
if (hcd->regs == NULL) {
dev_dbg (&dev->dev, "error mapping memory\n");
retval = -EFAULT;
goto err3;
}
} else { // UHCI
int region;
for (region = 0; region < PCI_ROM_RESOURCE; region++) {
if (!(pci_resource_flags (dev, region) &
IORESOURCE_IO))
continue;
hcd->rsrc_start = pci_resource_start (dev, region);
hcd->rsrc_len = pci_resource_len (dev, region);
if (request_region (hcd->rsrc_start, hcd->rsrc_len,
driver->description))
break;
}
if (region == PCI_ROM_RESOURCE) {
dev_dbg (&dev->dev, "no i/o regions available\n");
retval = -EBUSY;
goto err1;
}
}
#ifdef CONFIG_PCI_NAMES
hcd->product_desc = dev->pretty_name;
#endif
pci_set_master (dev);
retval = usb_add_hcd (hcd, dev->irq, SA_SHIRQ);
if (retval != 0)
goto err4;
return retval;
err4:
if (driver->flags & HCD_MEMORY) {
iounmap (hcd->regs);
err3:
release_mem_region (hcd->rsrc_start, hcd->rsrc_len);
} else
release_region (hcd->rsrc_start, hcd->rsrc_len);
err2:
usb_put_hcd (hcd);
err1:
pci_disable_device (dev);
dev_err (&dev->dev, "init %s fail, %d\n", pci_name(dev), retval);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_probe);
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_pci_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
* Store this function in the HCD's struct pci_driver as remove().
*/
void usb_hcd_pci_remove (struct pci_dev *dev)
{
struct usb_hcd *hcd;
hcd = pci_get_drvdata(dev);
if (!hcd)
return;
usb_remove_hcd (hcd);
if (hcd->driver->flags & HCD_MEMORY) {
iounmap (hcd->regs);
release_mem_region (hcd->rsrc_start, hcd->rsrc_len);
} else {
release_region (hcd->rsrc_start, hcd->rsrc_len);
}
usb_put_hcd (hcd);
pci_disable_device(dev);
}
EXPORT_SYMBOL (usb_hcd_pci_remove);
#ifdef CONFIG_PM
static char __attribute_used__ *pci_state(u32 state)
{
switch (state) {
case 0: return "D0";
case 1: return "D1";
case 2: return "D2";
case 3: return "D3hot";
case 4: return "D3cold";
}
return NULL;
}
/**
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
* @dev: USB Host Controller being suspended
* @state: state that the controller is going into
*
* Store this function in the HCD's struct pci_driver as suspend().
*/
int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
{
struct usb_hcd *hcd;
int retval = 0;
int has_pci_pm;
hcd = pci_get_drvdata(dev);
/* even when the PCI layer rejects some of the PCI calls
* below, HCs can try global suspend and reduce DMA traffic.
* PM-sensitive HCDs may already have done this.
*/
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
if (state > 4)
state = 4;
switch (hcd->state) {
/* entry if root hub wasn't yet suspended ... from sysfs,
* without autosuspend, or if USB_SUSPEND isn't configured.
*/
case HC_STATE_RUNNING:
hcd->state = HC_STATE_QUIESCING;
retval = hcd->driver->suspend (hcd, state);
if (retval) {
dev_dbg (hcd->self.controller,
"suspend fail, retval %d\n",
retval);
break;
}
hcd->state = HC_STATE_SUSPENDED;
/* FALLTHROUGH */
/* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the
* controller and/or root hub will already have been suspended,
* but it won't be ready for a PCI resume call.
*
* FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will
* have been called, otherwise root hub timers still run ...
*/
case HC_STATE_SUSPENDED:
if (state <= dev->current_state)
break;
/* no DMA or IRQs except in D0 */
if (!dev->current_state) {
pci_save_state (dev);
pci_disable_device (dev);
free_irq (hcd->irq, hcd);
}
if (!has_pci_pm) {
dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n");
break;
}
/* POLICY: ignore D1/D2/D3hot differences;
* we know D3hot will always work.
*/
retval = pci_set_power_state (dev, state);
if (retval < 0 && state < 3) {
retval = pci_set_power_state (dev, 3);
if (retval == 0)
state = 3;
}
if (retval == 0) {
dev_dbg (hcd->self.controller, "--> PCI %s\n",
pci_state(dev->current_state));
#ifdef CONFIG_USB_SUSPEND
pci_enable_wake (dev, state, hcd->remote_wakeup);
pci_enable_wake (dev, 4, hcd->remote_wakeup);
#endif
} else if (retval < 0) {
dev_dbg (&dev->dev, "PCI %s suspend fail, %d\n",
pci_state(state), retval);
(void) usb_hcd_pci_resume (dev);
break;
}
break;
default:
dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
hcd->state);
retval = -EINVAL;
break;
}
/* update power_state **ONLY** to make sysfs happier */
if (retval == 0)
dev->dev.power.power_state = state;
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_suspend);
/**
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
* @dev: USB Host Controller being resumed
*
* Store this function in the HCD's struct pci_driver as resume().
*/
int usb_hcd_pci_resume (struct pci_dev *dev)
{
struct usb_hcd *hcd;
int retval;
int has_pci_pm;
hcd = pci_get_drvdata(dev);
if (hcd->state != HC_STATE_SUSPENDED) {
dev_dbg (hcd->self.controller,
"can't resume, not suspended!\n");
return 0;
}
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
/* D3cold resume isn't usually reported this way... */
dev_dbg(hcd->self.controller, "resume from PCI %s%s\n",
pci_state(dev->current_state),
has_pci_pm ? "" : " (legacy)");
hcd->state = HC_STATE_RESUMING;
if (has_pci_pm)
pci_set_power_state (dev, 0);
dev->dev.power.power_state = 0;
retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
hcd->driver->description, hcd);
if (retval < 0) {
dev_err (hcd->self.controller,
"can't restore IRQ after resume!\n");
return retval;
}
hcd->saw_irq = 0;
pci_restore_state (dev);
#ifdef CONFIG_USB_SUSPEND
pci_enable_wake (dev, dev->current_state, 0);
pci_enable_wake (dev, 4, 0);
#endif
retval = hcd->driver->resume (hcd);
if (!HC_IS_RUNNING (hcd->state)) {
dev_dbg (hcd->self.controller,
"resume fail, retval %d\n", retval);
usb_hc_died (hcd);
}
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_resume);
#endif /* CONFIG_PM */

1840
drivers/usb/core/hcd.c Normal file

File diff suppressed because it is too large Load diff

476
drivers/usb/core/hcd.h Normal file
View file

@ -0,0 +1,476 @@
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef __KERNEL__
/* This file contains declarations of usbcore internals that are mostly
* used or exposed by Host Controller Drivers.
*/
/*
* USB Packet IDs (PIDs)
*/
#define USB_PID_UNDEF_0 0xf0
#define USB_PID_OUT 0xe1
#define USB_PID_ACK 0xd2
#define USB_PID_DATA0 0xc3
#define USB_PID_PING 0xb4 /* USB 2.0 */
#define USB_PID_SOF 0xa5
#define USB_PID_NYET 0x96 /* USB 2.0 */
#define USB_PID_DATA2 0x87 /* USB 2.0 */
#define USB_PID_SPLIT 0x78 /* USB 2.0 */
#define USB_PID_IN 0x69
#define USB_PID_NAK 0x5a
#define USB_PID_DATA1 0x4b
#define USB_PID_PREAMBLE 0x3c /* Token mode */
#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
#define USB_PID_SETUP 0x2d
#define USB_PID_STALL 0x1e
#define USB_PID_MDATA 0x0f /* USB 2.0 */
/*-------------------------------------------------------------------------*/
/*
* USB Host Controller Driver (usb_hcd) framework
*
* Since "struct usb_bus" is so thin, you can't share much code in it.
* This framework is a layer over that, and should be more sharable.
*/
/*-------------------------------------------------------------------------*/
struct usb_hcd { /* usb_bus.hcpriv points to this */
/*
* housekeeping
*/
struct usb_bus self; /* hcd is-a bus */
const char *product_desc; /* product/vendor string */
char irq_descr[24]; /* driver + bus # */
struct timer_list rh_timer; /* drives root hub */
/*
* hardware info/state
*/
const struct hc_driver *driver; /* hw-specific hooks */
unsigned saw_irq : 1;
unsigned can_wakeup:1; /* hw supports wakeup? */
unsigned remote_wakeup:1;/* sw should use wakeup? */
unsigned rh_registered:1;/* is root hub registered? */
int irq; /* irq allocated */
void __iomem *regs; /* device memory/io */
u64 rsrc_start; /* memory/io resource start */
u64 rsrc_len; /* memory/io resource length */
#define HCD_BUFFER_POOLS 4
struct dma_pool *pool [HCD_BUFFER_POOLS];
int state;
# define __ACTIVE 0x01
# define __SUSPEND 0x04
# define __TRANSIENT 0x80
# define HC_STATE_HALT 0
# define HC_STATE_RUNNING (__ACTIVE)
# define HC_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
# define HC_STATE_RESUMING (__SUSPEND|__TRANSIENT)
# define HC_STATE_SUSPENDED (__SUSPEND)
#define HC_IS_RUNNING(state) ((state) & __ACTIVE)
#define HC_IS_SUSPENDED(state) ((state) & __SUSPEND)
/* more shared queuing code would be good; it should support
* smarter scheduling, handle transaction translators, etc;
* input size of periodic table to an interrupt scheduler.
* (ohci 32, uhci 1024, ehci 256/512/1024).
*/
/* The HC driver's private data is stored at the end of
* this structure.
*/
unsigned long hcd_priv[0]
__attribute__ ((aligned (sizeof(unsigned long))));
};
/* 2.4 does this a bit differently ... */
static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
{
return &hcd->self;
}
// urb.hcpriv is really hardware-specific
struct hcd_timeout { /* timeouts we allocate */
struct list_head timeout_list;
struct timer_list timer;
};
/*-------------------------------------------------------------------------*/
/*
* FIXME usb_operations should vanish or become hc_driver,
* when usb_bus and usb_hcd become the same thing.
*/
struct usb_operations {
int (*get_frame_number) (struct usb_device *usb_dev);
int (*submit_urb) (struct urb *urb, int mem_flags);
int (*unlink_urb) (struct urb *urb, int status);
/* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
int mem_flags,
dma_addr_t *dma);
void (*buffer_free)(struct usb_bus *bus, size_t size,
void *addr, dma_addr_t dma);
void (*disable)(struct usb_device *udev,
struct usb_host_endpoint *ep);
/* global suspend/resume of bus */
int (*hub_suspend)(struct usb_bus *);
int (*hub_resume)(struct usb_bus *);
};
/* each driver provides one of these, and hardware init support */
struct pt_regs;
struct hc_driver {
const char *description; /* "ehci-hcd" etc */
const char *product_desc; /* product/vendor string */
size_t hcd_priv_size; /* size of private data */
/* irq handler */
irqreturn_t (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);
int flags;
#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
#define HCD_USB11 0x0010 /* USB 1.1 */
#define HCD_USB2 0x0020 /* USB 2.0 */
/* called to init HCD and root hub */
int (*reset) (struct usb_hcd *hcd);
int (*start) (struct usb_hcd *hcd);
/* NOTE: these suspend/resume calls relate to the HC as
* a whole, not just the root hub; they're for bus glue.
*/
/* called after all devices were suspended */
int (*suspend) (struct usb_hcd *hcd, u32 state);
/* called before any devices get resumed */
int (*resume) (struct usb_hcd *hcd);
/* cleanly make HCD stop writing memory and doing I/O */
void (*stop) (struct usb_hcd *hcd);
/* return current frame number */
int (*get_frame_number) (struct usb_hcd *hcd);
/* manage i/o requests, device state */
int (*urb_enqueue) (struct usb_hcd *hcd,
struct usb_host_endpoint *ep,
struct urb *urb,
int mem_flags);
int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
/* hw synch, freeing endpoint resources that urb_dequeue can't */
void (*endpoint_disable)(struct usb_hcd *hcd,
struct usb_host_endpoint *ep);
/* root hub support */
int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
int (*hub_control) (struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
int (*hub_suspend)(struct usb_hcd *);
int (*hub_resume)(struct usb_hcd *);
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
};
extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
struct device *dev, char *bus_name);
extern void usb_put_hcd (struct usb_hcd *hcd);
extern int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags);
extern void usb_remove_hcd(struct usb_hcd *hcd);
#ifdef CONFIG_PCI
struct pci_dev;
struct pci_device_id;
extern int usb_hcd_pci_probe (struct pci_dev *dev,
const struct pci_device_id *id);
extern void usb_hcd_pci_remove (struct pci_dev *dev);
#ifdef CONFIG_PM
extern int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state);
extern int usb_hcd_pci_resume (struct pci_dev *dev);
#endif /* CONFIG_PM */
#endif /* CONFIG_PCI */
/* pci-ish (pdev null is ok) buffer alloc/mapping support */
int hcd_buffer_create (struct usb_hcd *hcd);
void hcd_buffer_destroy (struct usb_hcd *hcd);
void *hcd_buffer_alloc (struct usb_bus *bus, size_t size,
int mem_flags, dma_addr_t *dma);
void hcd_buffer_free (struct usb_bus *bus, size_t size,
void *addr, dma_addr_t dma);
/* generic bus glue, needed for host controllers that don't use PCI */
extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
extern void usb_hc_died (struct usb_hcd *hcd);
/* -------------------------------------------------------------------------- */
/* Enumeration is only for the hub driver, or HCD virtual root hubs */
extern struct usb_device *usb_alloc_dev(struct usb_device *parent,
struct usb_bus *, unsigned port);
extern int usb_new_device(struct usb_device *dev);
extern void usb_disconnect(struct usb_device **);
extern int usb_get_configuration(struct usb_device *dev);
extern void usb_destroy_configuration(struct usb_device *dev);
/*-------------------------------------------------------------------------*/
/*
* HCD Root Hub support
*/
#include "hub.h"
/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
#define DeviceRequest \
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
#define DeviceOutRequest \
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
#define InterfaceRequest \
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
#define EndpointRequest \
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
#define EndpointOutRequest \
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
/* class requests from the USB 2.0 hub spec, table 11-15 */
/* GetBusState and SetHubDescriptor are optional, omitted */
#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
/*-------------------------------------------------------------------------*/
/*
* Generic bandwidth allocation constants/support
*/
#define FRAME_TIME_USECS 1000L
#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
/* Trying not to use worst-case bit-stuffing
of (7/6 * 8 * bytecount) = 9.33 * bytecount */
/* bytecount = data payload byte count */
#define NS_TO_US(ns) ((ns + 500L) / 1000L)
/* convert & round nanoseconds to microseconds */
extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb,
int bustime, int isoc);
extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb,
int isoc);
/*
* Full/low speed bandwidth allocation constants/support.
*/
#define BW_HOST_DELAY 1000L /* nanoseconds */
#define BW_HUB_LS_SETUP 333L /* nanoseconds */
/* 4 full-speed bit times (est.) */
#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
/*
* Ceiling microseconds (typical) for that many bytes at high speed
* ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
* to preallocate bandwidth)
*/
#define USB2_HOST_DELAY 5 /* nsec, guess */
#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ USB2_HOST_DELAY)
#define HS_USECS_ISO(bytes) NS_TO_US ( ((38 * 8 * 2083)/1000) \
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ USB2_HOST_DELAY)
extern long usb_calc_bus_time (int speed, int is_input,
int isoc, int bytecount);
/*-------------------------------------------------------------------------*/
extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
extern int usb_hcd_register_root_hub (struct usb_device *usb_dev,
struct usb_hcd *hcd);
extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
extern void usb_set_device_state(struct usb_device *udev,
enum usb_device_state new_state);
/*-------------------------------------------------------------------------*/
/* exported only within usbcore */
extern struct list_head usb_bus_list;
extern struct semaphore usb_bus_list_lock;
extern wait_queue_head_t usb_kill_urb_queue;
extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
extern void usb_bus_put (struct usb_bus *bus);
extern int usb_find_interface_driver (struct usb_device *dev,
struct usb_interface *interface);
#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
/*
* USB device fs stuff
*/
#ifdef CONFIG_USB_DEVICEFS
/*
* these are expected to be called from the USB core/hub thread
* with the kernel lock held
*/
extern void usbfs_add_bus(struct usb_bus *bus);
extern void usbfs_remove_bus(struct usb_bus *bus);
extern void usbfs_add_device(struct usb_device *dev);
extern void usbfs_remove_device(struct usb_device *dev);
extern void usbfs_update_special (void);
extern int usbfs_init(void);
extern void usbfs_cleanup(void);
#else /* CONFIG_USB_DEVICEFS */
static inline void usbfs_add_bus(struct usb_bus *bus) {}
static inline void usbfs_remove_bus(struct usb_bus *bus) {}
static inline void usbfs_add_device(struct usb_device *dev) {}
static inline void usbfs_remove_device(struct usb_device *dev) {}
static inline void usbfs_update_special (void) {}
static inline int usbfs_init(void) { return 0; }
static inline void usbfs_cleanup(void) { }
#endif /* CONFIG_USB_DEVICEFS */
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
struct usb_mon_operations {
void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err);
void (*urb_complete)(struct usb_bus *bus, struct urb *urb);
/* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
void (*bus_add)(struct usb_bus *bus);
void (*bus_remove)(struct usb_bus *bus);
};
extern struct usb_mon_operations *mon_ops;
static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb)
{
if (bus->monitored)
(*mon_ops->urb_submit)(bus, urb);
}
static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
int error)
{
if (bus->monitored)
(*mon_ops->urb_submit_error)(bus, urb, error);
}
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb)
{
if (bus->monitored)
(*mon_ops->urb_complete)(bus, urb);
}
static inline void usbmon_notify_bus_add(struct usb_bus *bus)
{
if (mon_ops)
(*mon_ops->bus_add)(bus);
}
static inline void usbmon_notify_bus_remove(struct usb_bus *bus)
{
if (mon_ops)
(*mon_ops->bus_remove)(bus);
}
int usb_mon_register(struct usb_mon_operations *ops);
void usb_mon_deregister(void);
#else
static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {}
static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
int error) {}
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
static inline void usbmon_notify_bus_add(struct usb_bus *bus) {}
static inline void usbmon_notify_bus_remove(struct usb_bus *bus) {}
#endif /* CONFIG_USB_MON */
/*-------------------------------------------------------------------------*/
/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
// bleech -- resurfaced in 2.4.11 or 2.4.12
#define bitmap DeviceRemovable
/*-------------------------------------------------------------------------*/
/* random stuff */
#define RUN_CONTEXT (in_irq () ? "in_irq" \
: (in_interrupt () ? "in_interrupt" : "can sleep"))
#endif /* __KERNEL__ */

3057
drivers/usb/core/hub.c Normal file

File diff suppressed because it is too large Load diff

238
drivers/usb/core/hub.h Normal file
View file

@ -0,0 +1,238 @@
#ifndef __LINUX_HUB_H
#define __LINUX_HUB_H
/*
* Hub protocol and driver data structures.
*
* Some of these are known to the "virtual root hub" code
* in host controller drivers.
*/
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/compiler.h> /* likely()/unlikely() */
/*
* Hub request types
*/
#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
/*
* Hub class requests
* See USB 2.0 spec Table 11-16
*/
#define HUB_CLEAR_TT_BUFFER 8
#define HUB_RESET_TT 9
#define HUB_GET_TT_STATE 10
#define HUB_STOP_TT 11
/*
* Hub Class feature numbers
* See USB 2.0 spec Table 11-17
*/
#define C_HUB_LOCAL_POWER 0
#define C_HUB_OVER_CURRENT 1
/*
* Port feature numbers
* See USB 2.0 spec Table 11-17
*/
#define USB_PORT_FEAT_CONNECTION 0
#define USB_PORT_FEAT_ENABLE 1
#define USB_PORT_FEAT_SUSPEND 2
#define USB_PORT_FEAT_OVER_CURRENT 3
#define USB_PORT_FEAT_RESET 4
#define USB_PORT_FEAT_POWER 8
#define USB_PORT_FEAT_LOWSPEED 9
#define USB_PORT_FEAT_HIGHSPEED 10
#define USB_PORT_FEAT_C_CONNECTION 16
#define USB_PORT_FEAT_C_ENABLE 17
#define USB_PORT_FEAT_C_SUSPEND 18
#define USB_PORT_FEAT_C_OVER_CURRENT 19
#define USB_PORT_FEAT_C_RESET 20
#define USB_PORT_FEAT_TEST 21
#define USB_PORT_FEAT_INDICATOR 22
/*
* Hub Status and Hub Change results
* See USB 2.0 spec Table 11-19 and Table 11-20
*/
struct usb_port_status {
__le16 wPortStatus;
__le16 wPortChange;
} __attribute__ ((packed));
/*
* wPortStatus bit field
* See USB 2.0 spec Table 11-21
*/
#define USB_PORT_STAT_CONNECTION 0x0001
#define USB_PORT_STAT_ENABLE 0x0002
#define USB_PORT_STAT_SUSPEND 0x0004
#define USB_PORT_STAT_OVERCURRENT 0x0008
#define USB_PORT_STAT_RESET 0x0010
/* bits 5 to 7 are reserved */
#define USB_PORT_STAT_POWER 0x0100
#define USB_PORT_STAT_LOW_SPEED 0x0200
#define USB_PORT_STAT_HIGH_SPEED 0x0400
#define USB_PORT_STAT_TEST 0x0800
#define USB_PORT_STAT_INDICATOR 0x1000
/* bits 13 to 15 are reserved */
/*
* wPortChange bit field
* See USB 2.0 spec Table 11-22
* Bits 0 to 4 shown, bits 5 to 15 are reserved
*/
#define USB_PORT_STAT_C_CONNECTION 0x0001
#define USB_PORT_STAT_C_ENABLE 0x0002
#define USB_PORT_STAT_C_SUSPEND 0x0004
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
#define USB_PORT_STAT_C_RESET 0x0010
/*
* wHubCharacteristics (masks)
* See USB 2.0 spec Table 11-13, offset 3
*/
#define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */
#define HUB_CHAR_COMPOUND 0x0004 /* D2 */
#define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */
#define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */
#define HUB_CHAR_PORTIND 0x0080 /* D7 */
struct usb_hub_status {
__le16 wHubStatus;
__le16 wHubChange;
} __attribute__ ((packed));
/*
* Hub Status & Hub Change bit masks
* See USB 2.0 spec Table 11-19 and Table 11-20
* Bits 0 and 1 for wHubStatus and wHubChange
* Bits 2 to 15 are reserved for both
*/
#define HUB_STATUS_LOCAL_POWER 0x0001
#define HUB_STATUS_OVERCURRENT 0x0002
#define HUB_CHANGE_LOCAL_POWER 0x0001
#define HUB_CHANGE_OVERCURRENT 0x0002
/*
* Hub descriptor
* See USB 2.0 spec Table 11-13
*/
#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
#define USB_DT_HUB_NONVAR_SIZE 7
struct usb_hub_descriptor {
__u8 bDescLength;
__u8 bDescriptorType;
__u8 bNbrPorts;
__u16 wHubCharacteristics;
__u8 bPwrOn2PwrGood;
__u8 bHubContrCurrent;
/* add 1 bit for hub status change; round to bytes */
__u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
__u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
} __attribute__ ((packed));
/* port indicator status selectors, tables 11-7 and 11-25 */
#define HUB_LED_AUTO 0
#define HUB_LED_AMBER 1
#define HUB_LED_GREEN 2
#define HUB_LED_OFF 3
enum hub_led_mode {
INDICATOR_AUTO = 0,
INDICATOR_CYCLE,
/* software blinks for attention: software, hardware, reserved */
INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF,
INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF,
INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF
} __attribute__ ((packed));
struct usb_device;
/*
* As of USB 2.0, full/low speed devices are segregated into trees.
* One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
* The other type grows from high speed hubs when they connect to
* full/low speed devices using "Transaction Translators" (TTs).
*
* TTs should only be known to the hub driver, and high speed bus
* drivers (only EHCI for now). They affect periodic scheduling and
* sometimes control/bulk error recovery.
*/
struct usb_tt {
struct usb_device *hub; /* upstream highspeed hub */
int multi; /* true means one TT per port */
/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
spinlock_t lock;
struct list_head clear_list; /* of usb_tt_clear */
struct work_struct kevent;
};
struct usb_tt_clear {
struct list_head clear_list;
unsigned tt;
u16 devinfo;
};
extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);
struct usb_hub {
struct device *intfdev; /* the "interface" device */
struct usb_device *hdev;
struct urb *urb; /* for interrupt polling pipe */
/* buffer for urb ... with extra space in case of babble */
char (*buffer)[8];
dma_addr_t buffer_dma; /* DMA address for buffer */
union {
struct usb_hub_status hub;
struct usb_port_status port;
} *status; /* buffer for status reports */
int error; /* last reported error */
int nerrors; /* track consecutive errors */
struct list_head event_list; /* hubs w/data or errs ready */
unsigned long event_bits[1]; /* status change bitmask */
unsigned long change_bits[1]; /* ports with logical connect
status change */
unsigned long busy_bits[1]; /* ports being reset */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif
struct usb_hub_descriptor *descriptor; /* class descriptor */
struct usb_tt tt; /* Transaction Translator */
u8 power_budget; /* in 2mA units; or zero */
unsigned quiescing:1;
unsigned activating:1;
unsigned resume_root_hub:1;
unsigned has_indicators:1;
enum hub_led_mode indicator[USB_MAXCHILDREN];
struct work_struct leds;
};
/* use this for low-powered root hubs */
static inline void
hub_set_power_budget (struct usb_device *hubdev, unsigned mA)
{
struct usb_hub *hub;
hub = (struct usb_hub *)
usb_get_intfdata (hubdev->actconfig->interface[0]);
hub->power_budget = min(mA,(unsigned)500)/2;
}
#endif /* __LINUX_HUB_H */

764
drivers/usb/core/inode.c Normal file
View file

@ -0,0 +1,764 @@
/*****************************************************************************/
/*
* inode.c -- Inode/Dentry functions for the USB device file system.
*
* Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
* Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com)
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History:
* 0.1 04.01.2000 Created
* 0.2 10.12.2001 converted to use the vfs layer better
*/
/*****************************************************************************/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/usb.h>
#include <linux/namei.h>
#include <linux/usbdevice_fs.h>
#include <linux/smp_lock.h>
#include <linux/parser.h>
#include <asm/byteorder.h>
#include "usb.h"
static struct super_operations usbfs_ops;
static struct file_operations default_file_operations;
static struct inode_operations usbfs_dir_inode_operations;
static struct vfsmount *usbfs_mount;
static int usbfs_mount_count; /* = 0 */
static int ignore_mount = 0;
static struct dentry *devices_usbfs_dentry;
static int num_buses; /* = 0 */
static uid_t devuid; /* = 0 */
static uid_t busuid; /* = 0 */
static uid_t listuid; /* = 0 */
static gid_t devgid; /* = 0 */
static gid_t busgid; /* = 0 */
static gid_t listgid; /* = 0 */
static umode_t devmode = S_IWUSR | S_IRUGO;
static umode_t busmode = S_IXUGO | S_IRUGO;
static umode_t listmode = S_IRUGO;
enum {
Opt_devuid, Opt_devgid, Opt_devmode,
Opt_busuid, Opt_busgid, Opt_busmode,
Opt_listuid, Opt_listgid, Opt_listmode,
Opt_err,
};
static match_table_t tokens = {
{Opt_devuid, "devuid=%u"},
{Opt_devgid, "devgid=%u"},
{Opt_devmode, "devmode=%o"},
{Opt_busuid, "busuid=%u"},
{Opt_busgid, "busgid=%u"},
{Opt_busmode, "busmode=%o"},
{Opt_listuid, "listuid=%u"},
{Opt_listgid, "listgid=%u"},
{Opt_listmode, "listmode=%o"},
{Opt_err, NULL}
};
static int parse_options(struct super_block *s, char *data)
{
char *p;
int option;
/* (re)set to defaults. */
devuid = 0;
busuid = 0;
listuid = 0;
devgid = 0;
busgid = 0;
listgid = 0;
devmode = S_IWUSR | S_IRUGO;
busmode = S_IXUGO | S_IRUGO;
listmode = S_IRUGO;
while ((p = strsep(&data, ",")) != NULL) {
substring_t args[MAX_OPT_ARGS];
int token;
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case Opt_devuid:
if (match_int(&args[0], &option))
return -EINVAL;
devuid = option;
break;
case Opt_devgid:
if (match_int(&args[0], &option))
return -EINVAL;
devgid = option;
break;
case Opt_devmode:
if (match_octal(&args[0], &option))
return -EINVAL;
devmode = option & S_IRWXUGO;
break;
case Opt_busuid:
if (match_int(&args[0], &option))
return -EINVAL;
busuid = option;
break;
case Opt_busgid:
if (match_int(&args[0], &option))
return -EINVAL;
busgid = option;
break;
case Opt_busmode:
if (match_octal(&args[0], &option))
return -EINVAL;
busmode = option & S_IRWXUGO;
break;
case Opt_listuid:
if (match_int(&args[0], &option))
return -EINVAL;
listuid = option;
break;
case Opt_listgid:
if (match_int(&args[0], &option))
return -EINVAL;
listgid = option;
break;
case Opt_listmode:
if (match_octal(&args[0], &option))
return -EINVAL;
listmode = option & S_IRWXUGO;
break;
default:
err("usbfs: unrecognised mount option \"%s\" "
"or missing value\n", p);
return -EINVAL;
}
}
return 0;
}
static void update_special(struct dentry *special)
{
special->d_inode->i_uid = listuid;
special->d_inode->i_gid = listgid;
special->d_inode->i_mode = S_IFREG | listmode;
}
static void update_dev(struct dentry *dev)
{
dev->d_inode->i_uid = devuid;
dev->d_inode->i_gid = devgid;
dev->d_inode->i_mode = S_IFREG | devmode;
}
static void update_bus(struct dentry *bus)
{
struct dentry *dev = NULL;
bus->d_inode->i_uid = busuid;
bus->d_inode->i_gid = busgid;
bus->d_inode->i_mode = S_IFDIR | busmode;
down(&bus->d_inode->i_sem);
list_for_each_entry(dev, &bus->d_subdirs, d_child)
if (dev->d_inode)
update_dev(dev);
up(&bus->d_inode->i_sem);
}
static void update_sb(struct super_block *sb)
{
struct dentry *root = sb->s_root;
struct dentry *bus = NULL;
if (!root)
return;
down(&root->d_inode->i_sem);
list_for_each_entry(bus, &root->d_subdirs, d_child) {
if (bus->d_inode) {
switch (S_IFMT & bus->d_inode->i_mode) {
case S_IFDIR:
update_bus(bus);
break;
case S_IFREG:
update_special(bus);
break;
default:
warn("Unknown node %s mode %x found on remount!\n",bus->d_name.name,bus->d_inode->i_mode);
break;
}
}
}
up(&root->d_inode->i_sem);
}
static int remount(struct super_block *sb, int *flags, char *data)
{
/* If this is not a real mount,
* i.e. it's a simple_pin_fs from create_special_files,
* then ignore it.
*/
if (ignore_mount)
return 0;
if (parse_options(sb, data)) {
warn("usbfs: mount parameter error:");
return -EINVAL;
}
if (usbfs_mount && usbfs_mount->mnt_sb)
update_sb(usbfs_mount->mnt_sb);
return 0;
}
static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t dev)
{
struct inode *inode = new_inode(sb);
if (inode) {
inode->i_mode = mode;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
default:
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
inode->i_fop = &default_file_operations;
break;
case S_IFDIR:
inode->i_op = &usbfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inode->i_nlink++;
break;
}
}
return inode;
}
/* SMP-safe */
static int usbfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
dev_t dev)
{
struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev);
int error = -EPERM;
if (dentry->d_inode)
return -EEXIST;
if (inode) {
d_instantiate(dentry, inode);
dget(dentry);
error = 0;
}
return error;
}
static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
{
int res;
mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
res = usbfs_mknod (dir, dentry, mode, 0);
if (!res)
dir->i_nlink++;
return res;
}
static int usbfs_create (struct inode *dir, struct dentry *dentry, int mode)
{
mode = (mode & S_IALLUGO) | S_IFREG;
return usbfs_mknod (dir, dentry, mode, 0);
}
static inline int usbfs_positive (struct dentry *dentry)
{
return dentry->d_inode && !d_unhashed(dentry);
}
static int usbfs_empty (struct dentry *dentry)
{
struct list_head *list;
spin_lock(&dcache_lock);
list_for_each(list, &dentry->d_subdirs) {
struct dentry *de = list_entry(list, struct dentry, d_child);
if (usbfs_positive(de)) {
spin_unlock(&dcache_lock);
return 0;
}
}
spin_unlock(&dcache_lock);
return 1;
}
static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
down(&inode->i_sem);
dentry->d_inode->i_nlink--;
dput(dentry);
up(&inode->i_sem);
d_delete(dentry);
return 0;
}
static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
{
int error = -ENOTEMPTY;
struct inode * inode = dentry->d_inode;
down(&inode->i_sem);
dentry_unhash(dentry);
if (usbfs_empty(dentry)) {
dentry->d_inode->i_nlink -= 2;
dput(dentry);
inode->i_flags |= S_DEAD;
dir->i_nlink--;
error = 0;
}
up(&inode->i_sem);
if (!error)
d_delete(dentry);
dput(dentry);
return error;
}
/* default file operations */
static ssize_t default_read_file (struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t default_write_file (struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
return count;
}
static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
{
loff_t retval = -EINVAL;
down(&file->f_dentry->d_inode->i_sem);
switch(orig) {
case 0:
if (offset > 0) {
file->f_pos = offset;
retval = file->f_pos;
}
break;
case 1:
if ((offset + file->f_pos) > 0) {
file->f_pos += offset;
retval = file->f_pos;
}
break;
default:
break;
}
up(&file->f_dentry->d_inode->i_sem);
return retval;
}
static int default_open (struct inode *inode, struct file *file)
{
if (inode->u.generic_ip)
file->private_data = inode->u.generic_ip;
return 0;
}
static struct file_operations default_file_operations = {
.read = default_read_file,
.write = default_write_file,
.open = default_open,
.llseek = default_file_lseek,
};
static struct inode_operations usbfs_dir_inode_operations = {
.lookup = simple_lookup,
};
static struct super_operations usbfs_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
.remount_fs = remount,
};
static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
struct dentry *root;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = USBDEVICE_SUPER_MAGIC;
sb->s_op = &usbfs_ops;
sb->s_time_gran = 1;
inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0);
if (!inode) {
dbg("%s: could not get inode!",__FUNCTION__);
return -ENOMEM;
}
root = d_alloc_root(inode);
if (!root) {
dbg("%s: could not get root dentry!",__FUNCTION__);
iput(inode);
return -ENOMEM;
}
sb->s_root = root;
return 0;
}
static struct dentry * get_dentry(struct dentry *parent, const char *name)
{
struct qstr qstr;
qstr.name = name;
qstr.len = strlen(name);
qstr.hash = full_name_hash(name,qstr.len);
return lookup_hash(&qstr,parent);
}
/*
* fs_create_by_name - create a file, given a name
* @name: name of file
* @mode: type of file
* @parent: dentry of directory to create it in
* @dentry: resulting dentry of file
*
* This function handles both regular files and directories.
*/
static int fs_create_by_name (const char *name, mode_t mode,
struct dentry *parent, struct dentry **dentry)
{
int error = 0;
/* If the parent is not specified, we create it in the root.
* We need the root dentry to do this, which is in the super
* block. A pointer to that is in the struct vfsmount that we
* have around.
*/
if (!parent ) {
if (usbfs_mount && usbfs_mount->mnt_sb) {
parent = usbfs_mount->mnt_sb->s_root;
}
}
if (!parent) {
dbg("Ah! can not find a parent!");
return -EFAULT;
}
*dentry = NULL;
down(&parent->d_inode->i_sem);
*dentry = get_dentry (parent, name);
if (!IS_ERR(dentry)) {
if ((mode & S_IFMT) == S_IFDIR)
error = usbfs_mkdir (parent->d_inode, *dentry, mode);
else
error = usbfs_create (parent->d_inode, *dentry, mode);
} else
error = PTR_ERR(dentry);
up(&parent->d_inode->i_sem);
return error;
}
static struct dentry *fs_create_file (const char *name, mode_t mode,
struct dentry *parent, void *data,
struct file_operations *fops,
uid_t uid, gid_t gid)
{
struct dentry *dentry;
int error;
dbg("creating file '%s'",name);
error = fs_create_by_name (name, mode, parent, &dentry);
if (error) {
dentry = NULL;
} else {
if (dentry->d_inode) {
if (data)
dentry->d_inode->u.generic_ip = data;
if (fops)
dentry->d_inode->i_fop = fops;
dentry->d_inode->i_uid = uid;
dentry->d_inode->i_gid = gid;
}
}
return dentry;
}
static void fs_remove_file (struct dentry *dentry)
{
struct dentry *parent = dentry->d_parent;
if (!parent || !parent->d_inode)
return;
down(&parent->d_inode->i_sem);
if (usbfs_positive(dentry)) {
if (dentry->d_inode) {
if (S_ISDIR(dentry->d_inode->i_mode))
usbfs_rmdir(parent->d_inode, dentry);
else
usbfs_unlink(parent->d_inode, dentry);
dput(dentry);
}
}
up(&parent->d_inode->i_sem);
}
/* --------------------------------------------------------------------- */
static struct super_block *usb_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return get_sb_single(fs_type, flags, data, usbfs_fill_super);
}
static struct file_system_type usb_fs_type = {
.owner = THIS_MODULE,
.name = "usbfs",
.get_sb = usb_get_sb,
.kill_sb = kill_litter_super,
};
/* --------------------------------------------------------------------- */
static int create_special_files (void)
{
struct dentry *parent;
int retval;
/* the simple_pin_fs calls will call remount with no options
* without this flag that would overwrite the real mount options (if any)
*/
ignore_mount = 1;
/* create the devices special file */
retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count);
if (retval) {
err ("Unable to get usbfs mount");
goto exit;
}
ignore_mount = 0;
parent = usbfs_mount->mnt_sb->s_root;
devices_usbfs_dentry = fs_create_file ("devices",
listmode | S_IFREG, parent,
NULL, &usbfs_devices_fops,
listuid, listgid);
if (devices_usbfs_dentry == NULL) {
err ("Unable to create devices usbfs file");
retval = -ENODEV;
goto error_clean_mounts;
}
goto exit;
error_clean_mounts:
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
exit:
return retval;
}
static void remove_special_files (void)
{
if (devices_usbfs_dentry)
fs_remove_file (devices_usbfs_dentry);
devices_usbfs_dentry = NULL;
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
}
void usbfs_update_special (void)
{
struct inode *inode;
if (devices_usbfs_dentry) {
inode = devices_usbfs_dentry->d_inode;
if (inode)
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
}
void usbfs_add_bus(struct usb_bus *bus)
{
struct dentry *parent;
char name[8];
int retval;
/* create the special files if this is the first bus added */
if (num_buses == 0) {
retval = create_special_files();
if (retval)
return;
}
++num_buses;
sprintf (name, "%03d", bus->busnum);
parent = usbfs_mount->mnt_sb->s_root;
bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,
bus, NULL, busuid, busgid);
if (bus->usbfs_dentry == NULL) {
err ("error creating usbfs bus entry");
return;
}
usbfs_update_special();
usbfs_conn_disc_event();
}
void usbfs_remove_bus(struct usb_bus *bus)
{
if (bus->usbfs_dentry) {
fs_remove_file (bus->usbfs_dentry);
bus->usbfs_dentry = NULL;
}
--num_buses;
if (num_buses <= 0) {
remove_special_files();
num_buses = 0;
}
usbfs_update_special();
usbfs_conn_disc_event();
}
void usbfs_add_device(struct usb_device *dev)
{
char name[8];
int i;
int i_size;
sprintf (name, "%03d", dev->devnum);
dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
dev->bus->usbfs_dentry, dev,
&usbfs_device_file_operations,
devuid, devgid);
if (dev->usbfs_dentry == NULL) {
err ("error creating usbfs device entry");
return;
}
/* Set the size of the device's file to be
* equal to the size of the device descriptors. */
i_size = sizeof (struct usb_device_descriptor);
for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) {
struct usb_config_descriptor *config =
(struct usb_config_descriptor *)dev->rawdescriptors[i];
i_size += le16_to_cpu(config->wTotalLength);
}
if (dev->usbfs_dentry->d_inode)
dev->usbfs_dentry->d_inode->i_size = i_size;
usbfs_update_special();
usbfs_conn_disc_event();
}
void usbfs_remove_device(struct usb_device *dev)
{
struct dev_state *ds;
struct siginfo sinfo;
if (dev->usbfs_dentry) {
fs_remove_file (dev->usbfs_dentry);
dev->usbfs_dentry = NULL;
}
while (!list_empty(&dev->filelist)) {
ds = list_entry(dev->filelist.next, struct dev_state, list);
wake_up_all(&ds->wait);
list_del_init(&ds->list);
if (ds->discsignr) {
sinfo.si_signo = SIGPIPE;
sinfo.si_errno = EPIPE;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = ds->disccontext;
send_sig_info(ds->discsignr, &sinfo, ds->disctask);
}
}
usbfs_update_special();
usbfs_conn_disc_event();
}
/* --------------------------------------------------------------------- */
static struct proc_dir_entry *usbdir = NULL;
int __init usbfs_init(void)
{
int retval;
retval = usb_register(&usbfs_driver);
if (retval)
return retval;
retval = register_filesystem(&usb_fs_type);
if (retval) {
usb_deregister(&usbfs_driver);
return retval;
}
/* create mount point for usbfs */
usbdir = proc_mkdir("usb", proc_bus);
return 0;
}
void usbfs_cleanup(void)
{
usb_deregister(&usbfs_driver);
unregister_filesystem(&usb_fs_type);
if (usbdir)
remove_proc_entry("usb", proc_bus);
}

1480
drivers/usb/core/message.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
/*
* drivers/usb/core/otg_whitelist.h
*
* Copyright (C) 2004 Texas Instruments
*
* 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 OTG Whitelist is the OTG "Targeted Peripheral List". It should
* mostly use of USB_DEVICE() or USB_DEVICE_VER() entries..
*
* YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING!
*/
static struct usb_device_id whitelist_table [] = {
/* hubs are optional in OTG, but very handy ... */
{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), },
#ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */
/* FIXME actually, printers are NOT supposed to use device classes;
* they're supposed to use interface classes...
*/
{ USB_DEVICE_INFO(7, 1, 1) },
{ USB_DEVICE_INFO(7, 1, 2) },
{ USB_DEVICE_INFO(7, 1, 3) },
#endif
#ifdef CONFIG_USB_CDCETHER
/* Linux-USB CDC Ethernet gadget */
{ USB_DEVICE(0x0525, 0xa4a1), },
/* Linux-USB CDC Ethernet + RNDIS gadget */
{ USB_DEVICE(0x0525, 0xa4a2), },
#endif
#if defined(CONFIG_USB_TEST) || defined(CONFIG_USB_TEST_MODULE)
/* gadget zero, for testing */
{ USB_DEVICE(0x0525, 0xa4a0), },
#endif
{ } /* Terminating entry */
};
static int is_targeted(struct usb_device *dev)
{
struct usb_device_id *id = whitelist_table;
/* possible in developer configs only! */
if (!dev->bus->otg_port)
return 1;
/* HNP test device is _never_ targeted (see OTG spec 6.6.6) */
if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a &&
le16_to_cpu(dev->descriptor.idProduct) == 0xbadd))
return 0;
/* NOTE: can't use usb_match_id() since interface caches
* aren't set up yet. this is cut/paste from that code.
*/
for (id = whitelist_table; id->match_flags; id++) {
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
continue;
/* No need to test id->bcdDevice_lo != 0, since 0 is never
greater than any unsigned number. */
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
(id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
(id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
(id->bDeviceClass != dev->descriptor.bDeviceClass))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
(id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
continue;
return 1;
}
/* add other match criteria here ... */
/* OTG MESSAGE: report errors here, customize to match your product */
dev_err(&dev->dev, "device v%04x p%04x is not supported\n",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
#ifdef CONFIG_USB_OTG_WHITELIST
return 0;
#else
return 1;
#endif
}

318
drivers/usb/core/sysfs.c Normal file
View file

@ -0,0 +1,318 @@
/*
* drivers/usb/core/sysfs.c
*
* (C) Copyright 2002 David Brownell
* (C) Copyright 2002,2004 Greg Kroah-Hartman
* (C) Copyright 2002,2004 IBM Corp.
*
* All of the sysfs file attributes for usb devices and interfaces.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
#include "usb.h"
/* Active configuration fields */
#define usb_actconfig_show(field, multiplier, format_string) \
static ssize_t show_##field (struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
struct usb_host_config *actconfig; \
\
udev = to_usb_device (dev); \
actconfig = udev->actconfig; \
if (actconfig) \
return sprintf (buf, format_string, \
actconfig->desc.field * multiplier); \
else \
return 0; \
} \
#define usb_actconfig_attr(field, multiplier, format_string) \
usb_actconfig_show(field, multiplier, format_string) \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
usb_actconfig_attr (bNumInterfaces, 1, "%2d\n")
usb_actconfig_attr (bmAttributes, 1, "%2x\n")
usb_actconfig_attr (bMaxPower, 2, "%3dmA\n")
static ssize_t show_configuration_string(struct device *dev, char *buf)
{
struct usb_device *udev;
struct usb_host_config *actconfig;
int len;
udev = to_usb_device (dev);
actconfig = udev->actconfig;
if ((!actconfig) || (!actconfig->string))
return 0;
len = sprintf(buf, actconfig->string, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
}
static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL);
/* configuration value is always present, and r/w */
usb_actconfig_show(bConfigurationValue, 1, "%u\n");
static ssize_t
set_bConfigurationValue (struct device *dev, const char *buf, size_t count)
{
struct usb_device *udev = udev = to_usb_device (dev);
int config, value;
if (sscanf (buf, "%u", &config) != 1 || config > 255)
return -EINVAL;
usb_lock_device(udev);
value = usb_set_configuration (udev, config);
usb_unlock_device(udev);
return (value < 0) ? value : count;
}
static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR,
show_bConfigurationValue, set_bConfigurationValue);
/* String fields */
#define usb_string_attr(name) \
static ssize_t show_##name(struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
int len; \
\
udev = to_usb_device (dev); \
len = snprintf(buf, 256, "%s", udev->name); \
if (len < 0) \
return 0; \
buf[len] = '\n'; \
buf[len+1] = 0; \
return len+1; \
} \
static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
usb_string_attr(product);
usb_string_attr(manufacturer);
usb_string_attr(serial);
static ssize_t
show_speed (struct device *dev, char *buf)
{
struct usb_device *udev;
char *speed;
udev = to_usb_device (dev);
switch (udev->speed) {
case USB_SPEED_LOW:
speed = "1.5";
break;
case USB_SPEED_UNKNOWN:
case USB_SPEED_FULL:
speed = "12";
break;
case USB_SPEED_HIGH:
speed = "480";
break;
default:
speed = "unknown";
}
return sprintf (buf, "%s\n", speed);
}
static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
static ssize_t
show_devnum (struct device *dev, char *buf)
{
struct usb_device *udev;
udev = to_usb_device (dev);
return sprintf (buf, "%d\n", udev->devnum);
}
static DEVICE_ATTR(devnum, S_IRUGO, show_devnum, NULL);
static ssize_t
show_version (struct device *dev, char *buf)
{
struct usb_device *udev;
u16 bcdUSB;
udev = to_usb_device(dev);
bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
return sprintf(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff);
}
static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
static ssize_t
show_maxchild (struct device *dev, char *buf)
{
struct usb_device *udev;
udev = to_usb_device (dev);
return sprintf (buf, "%d\n", udev->maxchild);
}
static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL);
/* Descriptor fields */
#define usb_descriptor_attr_le16(field, format_string) \
static ssize_t \
show_##field (struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
\
udev = to_usb_device (dev); \
return sprintf (buf, format_string, \
le16_to_cpu(udev->descriptor.field)); \
} \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
usb_descriptor_attr_le16(idVendor, "%04x\n")
usb_descriptor_attr_le16(idProduct, "%04x\n")
usb_descriptor_attr_le16(bcdDevice, "%04x\n")
#define usb_descriptor_attr(field, format_string) \
static ssize_t \
show_##field (struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
\
udev = to_usb_device (dev); \
return sprintf (buf, format_string, udev->descriptor.field); \
} \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
usb_descriptor_attr (bDeviceClass, "%02x\n")
usb_descriptor_attr (bDeviceSubClass, "%02x\n")
usb_descriptor_attr (bDeviceProtocol, "%02x\n")
usb_descriptor_attr (bNumConfigurations, "%d\n")
static struct attribute *dev_attrs[] = {
/* current configuration's attributes */
&dev_attr_bNumInterfaces.attr,
&dev_attr_bConfigurationValue.attr,
&dev_attr_bmAttributes.attr,
&dev_attr_bMaxPower.attr,
/* device attributes */
&dev_attr_idVendor.attr,
&dev_attr_idProduct.attr,
&dev_attr_bcdDevice.attr,
&dev_attr_bDeviceClass.attr,
&dev_attr_bDeviceSubClass.attr,
&dev_attr_bDeviceProtocol.attr,
&dev_attr_bNumConfigurations.attr,
&dev_attr_speed.attr,
&dev_attr_devnum.attr,
&dev_attr_version.attr,
&dev_attr_maxchild.attr,
NULL,
};
static struct attribute_group dev_attr_grp = {
.attrs = dev_attrs,
};
void usb_create_sysfs_dev_files (struct usb_device *udev)
{
struct device *dev = &udev->dev;
sysfs_create_group(&dev->kobj, &dev_attr_grp);
if (udev->manufacturer)
device_create_file (dev, &dev_attr_manufacturer);
if (udev->product)
device_create_file (dev, &dev_attr_product);
if (udev->serial)
device_create_file (dev, &dev_attr_serial);
device_create_file (dev, &dev_attr_configuration);
}
void usb_remove_sysfs_dev_files (struct usb_device *udev)
{
struct device *dev = &udev->dev;
sysfs_remove_group(&dev->kobj, &dev_attr_grp);
if (udev->descriptor.iManufacturer)
device_remove_file(dev, &dev_attr_manufacturer);
if (udev->descriptor.iProduct)
device_remove_file(dev, &dev_attr_product);
if (udev->descriptor.iSerialNumber)
device_remove_file(dev, &dev_attr_serial);
device_remove_file (dev, &dev_attr_configuration);
}
/* Interface fields */
#define usb_intf_attr(field, format_string) \
static ssize_t \
show_##field (struct device *dev, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface (dev); \
\
return sprintf (buf, format_string, intf->cur_altsetting->desc.field); \
} \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
usb_intf_attr (bInterfaceNumber, "%02x\n")
usb_intf_attr (bAlternateSetting, "%2d\n")
usb_intf_attr (bNumEndpoints, "%02x\n")
usb_intf_attr (bInterfaceClass, "%02x\n")
usb_intf_attr (bInterfaceSubClass, "%02x\n")
usb_intf_attr (bInterfaceProtocol, "%02x\n")
static ssize_t show_interface_string(struct device *dev, char *buf)
{
struct usb_interface *intf;
struct usb_device *udev;
int len;
intf = to_usb_interface (dev);
udev = interface_to_usbdev (intf);
len = snprintf(buf, 256, "%s", intf->cur_altsetting->string);
if (len < 0)
return 0;
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
}
static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL);
static struct attribute *intf_attrs[] = {
&dev_attr_bInterfaceNumber.attr,
&dev_attr_bAlternateSetting.attr,
&dev_attr_bNumEndpoints.attr,
&dev_attr_bInterfaceClass.attr,
&dev_attr_bInterfaceSubClass.attr,
&dev_attr_bInterfaceProtocol.attr,
NULL,
};
static struct attribute_group intf_attr_grp = {
.attrs = intf_attrs,
};
void usb_create_sysfs_intf_files (struct usb_interface *intf)
{
sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
if (intf->cur_altsetting->string)
device_create_file(&intf->dev, &dev_attr_interface);
}
void usb_remove_sysfs_intf_files (struct usb_interface *intf)
{
sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
if (intf->cur_altsetting->string)
device_remove_file(&intf->dev, &dev_attr_interface);
}

511
drivers/usb/core/urb.c Normal file
View file

@ -0,0 +1,511 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/init.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
#include "hcd.h"
#define to_urb(d) container_of(d, struct urb, kref)
static void urb_destroy(struct kref *kref)
{
struct urb *urb = to_urb(kref);
kfree(urb);
}
/**
* usb_init_urb - initializes a urb so that it can be used by a USB driver
* @urb: pointer to the urb to initialize
*
* Initializes a urb so that the USB subsystem can use it properly.
*
* If a urb is created with a call to usb_alloc_urb() it is not
* necessary to call this function. Only use this if you allocate the
* space for a struct urb on your own. If you call this function, be
* careful when freeing the memory for your urb that it is no longer in
* use by the USB core.
*
* Only use this function if you _really_ understand what you are doing.
*/
void usb_init_urb(struct urb *urb)
{
if (urb) {
memset(urb, 0, sizeof(*urb));
kref_init(&urb->kref);
spin_lock_init(&urb->lock);
}
}
/**
* usb_alloc_urb - creates a new urb for a USB driver to use
* @iso_packets: number of iso packets for this urb
* @mem_flags: the type of memory to allocate, see kmalloc() for a list of
* valid options for this.
*
* Creates an urb for the USB driver to use, initializes a few internal
* structures, incrementes the usage counter, and returns a pointer to it.
*
* If no memory is available, NULL is returned.
*
* If the driver want to use this urb for interrupt, control, or bulk
* endpoints, pass '0' as the number of iso packets.
*
* The driver must call usb_free_urb() when it is finished with the urb.
*/
struct urb *usb_alloc_urb(int iso_packets, int mem_flags)
{
struct urb *urb;
urb = (struct urb *)kmalloc(sizeof(struct urb) +
iso_packets * sizeof(struct usb_iso_packet_descriptor),
mem_flags);
if (!urb) {
err("alloc_urb: kmalloc failed");
return NULL;
}
usb_init_urb(urb);
return urb;
}
/**
* usb_free_urb - frees the memory used by a urb when all users of it are finished
* @urb: pointer to the urb to free, may be NULL
*
* Must be called when a user of a urb is finished with it. When the last user
* of the urb calls this function, the memory of the urb is freed.
*
* Note: The transfer buffer associated with the urb is not freed, that must be
* done elsewhere.
*/
void usb_free_urb(struct urb *urb)
{
if (urb)
kref_put(&urb->kref, urb_destroy);
}
/**
* usb_get_urb - increments the reference count of the urb
* @urb: pointer to the urb to modify, may be NULL
*
* This must be called whenever a urb is transferred from a device driver to a
* host controller driver. This allows proper reference counting to happen
* for urbs.
*
* A pointer to the urb with the incremented reference counter is returned.
*/
struct urb * usb_get_urb(struct urb *urb)
{
if (urb)
kref_get(&urb->kref);
return urb;
}
/*-------------------------------------------------------------------*/
/**
* usb_submit_urb - issue an asynchronous transfer request for an endpoint
* @urb: pointer to the urb describing the request
* @mem_flags: the type of memory to allocate, see kmalloc() for a list
* of valid options for this.
*
* This submits a transfer request, and transfers control of the URB
* describing that request to the USB subsystem. Request completion will
* be indicated later, asynchronously, by calling the completion handler.
* The three types of completion are success, error, and unlink
* (a software-induced fault, also called "request cancelation").
*
* URBs may be submitted in interrupt context.
*
* The caller must have correctly initialized the URB before submitting
* it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are
* available to ensure that most fields are correctly initialized, for
* the particular kind of transfer, although they will not initialize
* any transfer flags.
*
* Successful submissions return 0; otherwise this routine returns a
* negative error number. If the submission is successful, the complete()
* callback from the URB will be called exactly once, when the USB core and
* Host Controller Driver (HCD) are finished with the URB. When the completion
* function is called, control of the URB is returned to the device
* driver which issued the request. The completion handler may then
* immediately free or reuse that URB.
*
* With few exceptions, USB device drivers should never access URB fields
* provided by usbcore or the HCD until its complete() is called.
* The exceptions relate to periodic transfer scheduling. For both
* interrupt and isochronous urbs, as part of successful URB submission
* urb->interval is modified to reflect the actual transfer period used
* (normally some power of two units). And for isochronous urbs,
* urb->start_frame is modified to reflect when the URB's transfers were
* scheduled to start. Not all isochronous transfer scheduling policies
* will work, but most host controller drivers should easily handle ISO
* queues going from now until 10-200 msec into the future.
*
* For control endpoints, the synchronous usb_control_msg() call is
* often used (in non-interrupt context) instead of this call.
* That is often used through convenience wrappers, for the requests
* that are standardized in the USB 2.0 specification. For bulk
* endpoints, a synchronous usb_bulk_msg() call is available.
*
* Request Queuing:
*
* URBs may be submitted to endpoints before previous ones complete, to
* minimize the impact of interrupt latencies and system overhead on data
* throughput. With that queuing policy, an endpoint's queue would never
* be empty. This is required for continuous isochronous data streams,
* and may also be required for some kinds of interrupt transfers. Such
* queuing also maximizes bandwidth utilization by letting USB controllers
* start work on later requests before driver software has finished the
* completion processing for earlier (successful) requests.
*
* As of Linux 2.6, all USB endpoint transfer queues support depths greater
* than one. This was previously a HCD-specific behavior, except for ISO
* transfers. Non-isochronous endpoint queues are inactive during cleanup
* after faults (transfer errors or cancelation).
*
* Reserved Bandwidth Transfers:
*
* Periodic transfers (interrupt or isochronous) are performed repeatedly,
* using the interval specified in the urb. Submitting the first urb to
* the endpoint reserves the bandwidth necessary to make those transfers.
* If the USB subsystem can't allocate sufficient bandwidth to perform
* the periodic request, submitting such a periodic request should fail.
*
* Device drivers must explicitly request that repetition, by ensuring that
* some URB is always on the endpoint's queue (except possibly for short
* periods during completion callacks). When there is no longer an urb
* queued, the endpoint's bandwidth reservation is canceled. This means
* drivers can use their completion handlers to ensure they keep bandwidth
* they need, by reinitializing and resubmitting the just-completed urb
* until the driver longer needs that periodic bandwidth.
*
* Memory Flags:
*
* The general rules for how to decide which mem_flags to use
* are the same as for kmalloc. There are four
* different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and
* GFP_ATOMIC.
*
* GFP_NOFS is not ever used, as it has not been implemented yet.
*
* GFP_ATOMIC is used when
* (a) you are inside a completion handler, an interrupt, bottom half,
* tasklet or timer, or
* (b) you are holding a spinlock or rwlock (does not apply to
* semaphores), or
* (c) current->state != TASK_RUNNING, this is the case only after
* you've changed it.
*
* GFP_NOIO is used in the block io path and error handling of storage
* devices.
*
* All other situations use GFP_KERNEL.
*
* Some more specific rules for mem_flags can be inferred, such as
* (1) start_xmit, timeout, and receive methods of network drivers must
* use GFP_ATOMIC (they are called with a spinlock held);
* (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also
* called with a spinlock held);
* (3) If you use a kernel thread with a network driver you must use
* GFP_NOIO, unless (b) or (c) apply;
* (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c)
* apply or your are in a storage driver's block io path;
* (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and
* (6) changing firmware on a running storage or net device uses
* GFP_NOIO, unless b) or c) apply
*
*/
int usb_submit_urb(struct urb *urb, int mem_flags)
{
int pipe, temp, max;
struct usb_device *dev;
struct usb_operations *op;
int is_out;
if (!urb || urb->hcpriv || !urb->complete)
return -EINVAL;
if (!(dev = urb->dev) ||
(dev->state < USB_STATE_DEFAULT) ||
(!dev->bus) || (dev->devnum <= 0))
return -ENODEV;
if (dev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
if (!(op = dev->bus->op) || !op->submit_urb)
return -ENODEV;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
urb->bandwidth = 0;
/* Lots of sanity checks, so HCDs can rely on clean data
* and don't need to duplicate tests
*/
pipe = urb->pipe;
temp = usb_pipetype (pipe);
is_out = usb_pipeout (pipe);
if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED)
return -ENODEV;
/* FIXME there should be a sharable lock protecting us against
* config/altsetting changes and disconnects, kicking in here.
* (here == before maxpacket, and eventually endpoint type,
* checks get made.)
*/
max = usb_maxpacket (dev, pipe, is_out);
if (max <= 0) {
dev_dbg(&dev->dev,
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
usb_pipeendpoint (pipe), is_out ? "out" : "in",
__FUNCTION__, max);
return -EMSGSIZE;
}
/* periodic transfers limit size per frame/uframe,
* but drivers only control those sizes for ISO.
* while we're checking, initialize return status.
*/
if (temp == PIPE_ISOCHRONOUS) {
int n, len;
/* "high bandwidth" mode, 1-3 packets/uframe? */
if (dev->speed == USB_SPEED_HIGH) {
int mult = 1 + ((max >> 11) & 0x03);
max &= 0x07ff;
max *= mult;
}
if (urb->number_of_packets <= 0)
return -EINVAL;
for (n = 0; n < urb->number_of_packets; n++) {
len = urb->iso_frame_desc [n].length;
if (len < 0 || len > max)
return -EMSGSIZE;
urb->iso_frame_desc [n].status = -EXDEV;
urb->iso_frame_desc [n].actual_length = 0;
}
}
/* the I/O buffer must be mapped/unmapped, except when length=0 */
if (urb->transfer_buffer_length < 0)
return -EMSGSIZE;
#ifdef DEBUG
/* stuff that drivers shouldn't do, but which shouldn't
* cause problems in HCDs if they get it wrong.
*/
{
unsigned int orig_flags = urb->transfer_flags;
unsigned int allowed;
/* enforce simple/standard policy */
allowed = URB_ASYNC_UNLINK; // affects later unlinks
allowed |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
allowed |= URB_NO_INTERRUPT;
switch (temp) {
case PIPE_BULK:
if (is_out)
allowed |= URB_ZERO_PACKET;
/* FALLTHROUGH */
case PIPE_CONTROL:
allowed |= URB_NO_FSBR; /* only affects UHCI */
/* FALLTHROUGH */
default: /* all non-iso endpoints */
if (!is_out)
allowed |= URB_SHORT_NOT_OK;
break;
case PIPE_ISOCHRONOUS:
allowed |= URB_ISO_ASAP;
break;
}
urb->transfer_flags &= allowed;
/* fail if submitter gave bogus flags */
if (urb->transfer_flags != orig_flags) {
err ("BOGUS urb flags, %x --> %x",
orig_flags, urb->transfer_flags);
return -EINVAL;
}
}
#endif
/*
* Force periodic transfer intervals to be legal values that are
* a power of two (so HCDs don't need to).
*
* FIXME want bus->{intr,iso}_sched_horizon values here. Each HC
* supports different values... this uses EHCI/UHCI defaults (and
* EHCI can use smaller non-default values).
*/
switch (temp) {
case PIPE_ISOCHRONOUS:
case PIPE_INTERRUPT:
/* too small? */
if (urb->interval <= 0)
return -EINVAL;
/* too big? */
switch (dev->speed) {
case USB_SPEED_HIGH: /* units are microframes */
// NOTE usb handles 2^15
if (urb->interval > (1024 * 8))
urb->interval = 1024 * 8;
temp = 1024 * 8;
break;
case USB_SPEED_FULL: /* units are frames/msec */
case USB_SPEED_LOW:
if (temp == PIPE_INTERRUPT) {
if (urb->interval > 255)
return -EINVAL;
// NOTE ohci only handles up to 32
temp = 128;
} else {
if (urb->interval > 1024)
urb->interval = 1024;
// NOTE usb and ohci handle up to 2^15
temp = 1024;
}
break;
default:
return -EINVAL;
}
/* power of two? */
while (temp > urb->interval)
temp >>= 1;
urb->interval = temp;
}
return op->submit_urb (urb, mem_flags);
}
/*-------------------------------------------------------------------*/
/**
* usb_unlink_urb - abort/cancel a transfer request for an endpoint
* @urb: pointer to urb describing a previously submitted request,
* may be NULL
*
* This routine cancels an in-progress request. URBs complete only
* once per submission, and may be canceled only once per submission.
* Successful cancelation means the requests's completion handler will
* be called with a status code indicating that the request has been
* canceled (rather than any other code) and will quickly be removed
* from host controller data structures.
*
* In the past, clearing the URB_ASYNC_UNLINK transfer flag for the
* URB indicated that the request was synchronous. This usage is now
* deprecated; if the flag is clear the call will be forwarded to
* usb_kill_urb() and the return value will be 0. In the future, drivers
* should call usb_kill_urb() directly for synchronous unlinking.
*
* When the URB_ASYNC_UNLINK transfer flag for the URB is set, this
* request is asynchronous. Success is indicated by returning -EINPROGRESS,
* at which time the URB will normally have been unlinked but not yet
* given back to the device driver. When it is called, the completion
* function will see urb->status == -ECONNRESET. Failure is indicated
* by any other return value. Unlinking will fail when the URB is not
* currently "linked" (i.e., it was never submitted, or it was unlinked
* before, or the hardware is already finished with it), even if the
* completion handler has not yet run.
*
* Unlinking and Endpoint Queues:
*
* Host Controller Drivers (HCDs) place all the URBs for a particular
* endpoint in a queue. Normally the queue advances as the controller
* hardware processes each request. But when an URB terminates with any
* fault (such as an error, or being unlinked) its queue stops, at least
* until that URB's completion routine returns. It is guaranteed that
* the queue will not restart until all its unlinked URBs have been fully
* retired, with their completion routines run, even if that's not until
* some time after the original completion handler returns.
*
* This means that USB device drivers can safely build deep queues for
* large or complex transfers, and clean them up reliably after any sort
* of aborted transfer by unlinking all pending URBs at the first fault.
*
* Note that an URB terminating early because a short packet was received
* will count as an error if and only if the URB_SHORT_NOT_OK flag is set.
* Also, that all unlinks performed in any URB completion handler must
* be asynchronous.
*
* Queues for isochronous endpoints are treated differently, because they
* advance at fixed rates. Such queues do not stop when an URB is unlinked.
* An unlinked URB may leave a gap in the stream of packets. It is undefined
* whether such gaps can be filled in.
*
* When a control URB terminates with an error, it is likely that the
* status stage of the transfer will not take place, even if it is merely
* a soft error resulting from a short-packet with URB_SHORT_NOT_OK set.
*/
int usb_unlink_urb(struct urb *urb)
{
if (!urb)
return -EINVAL;
if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
#ifdef CONFIG_DEBUG_KERNEL
if (printk_ratelimit()) {
printk(KERN_NOTICE "usb_unlink_urb() is deprecated for "
"synchronous unlinks. Use usb_kill_urb() instead.\n");
WARN_ON(1);
}
#endif
usb_kill_urb(urb);
return 0;
}
if (!(urb->dev && urb->dev->bus && urb->dev->bus->op))
return -ENODEV;
return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET);
}
/**
* usb_kill_urb - cancel a transfer request and wait for it to finish
* @urb: pointer to URB describing a previously submitted request,
* may be NULL
*
* This routine cancels an in-progress request. It is guaranteed that
* upon return all completion handlers will have finished and the URB
* will be totally idle and available for reuse. These features make
* this an ideal way to stop I/O in a disconnect() callback or close()
* function. If the request has not already finished or been unlinked
* the completion handler will see urb->status == -ENOENT.
*
* While the routine is running, attempts to resubmit the URB will fail
* with error -EPERM. Thus even if the URB's completion handler always
* tries to resubmit, it will not succeed and the URB will become idle.
*
* This routine may not be used in an interrupt context (such as a bottom
* half or a completion handler), or when holding a spinlock, or in other
* situations where the caller can't schedule().
*/
void usb_kill_urb(struct urb *urb)
{
if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op))
return;
spin_lock_irq(&urb->lock);
++urb->reject;
spin_unlock_irq(&urb->lock);
urb->dev->bus->op->unlink_urb(urb, -ENOENT);
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
spin_lock_irq(&urb->lock);
--urb->reject;
spin_unlock_irq(&urb->lock);
}
EXPORT_SYMBOL(usb_init_urb);
EXPORT_SYMBOL(usb_alloc_urb);
EXPORT_SYMBOL(usb_free_urb);
EXPORT_SYMBOL(usb_get_urb);
EXPORT_SYMBOL(usb_submit_urb);
EXPORT_SYMBOL(usb_unlink_urb);
EXPORT_SYMBOL(usb_kill_urb);

1573
drivers/usb/core/usb.c Normal file

File diff suppressed because it is too large Load diff

46
drivers/usb/core/usb.h Normal file
View file

@ -0,0 +1,46 @@
/* Functions local to drivers/usb/core/ */
extern void usb_create_sysfs_dev_files (struct usb_device *dev);
extern void usb_remove_sysfs_dev_files (struct usb_device *dev);
extern void usb_create_sysfs_intf_files (struct usb_interface *intf);
extern void usb_remove_sysfs_intf_files (struct usb_interface *intf);
extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr);
extern void usb_disable_interface (struct usb_device *dev,
struct usb_interface *intf);
extern void usb_release_interface_cache(struct kref *ref);
extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern void usb_lock_all_devices(void);
extern void usb_unlock_all_devices(void);
extern void usb_kick_khubd(struct usb_device *dev);
extern void usb_resume_root_hub(struct usb_device *dev);
/* for labeling diagnostics */
extern const char *usbcore_name;
/* usbfs stuff */
extern struct usb_driver usbfs_driver;
extern struct file_operations usbfs_devices_fops;
extern struct file_operations usbfs_device_file_operations;
extern void usbfs_conn_disc_event(void);
struct dev_state {
struct list_head list; /* state list */
struct usb_device *dev;
struct file *file;
spinlock_t lock; /* protects the async urb lists */
struct list_head async_pending;
struct list_head async_completed;
wait_queue_head_t wait; /* wake up if a request completed */
unsigned int discsignr;
struct task_struct *disctask;
void __user *disccontext;
unsigned long ifclaimed;
};

389
drivers/usb/gadget/Kconfig Normal file
View file

@ -0,0 +1,389 @@
#
# USB Gadget support on a system involves
# (a) a peripheral controller, and
# (b) the gadget driver using it.
#
# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
#
# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
# - Some systems have both kinds of of controller.
#
# With help from a special transceiver and a "Mini-AB" jack, systems with
# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
#
menu "USB Gadget Support"
config USB_GADGET
tristate "Support for USB Gadgets"
help
USB is a master/slave protocol, organized with one master
host (such as a PC) controlling up to 127 peripheral devices.
The USB hardware is asymmetric, which makes it easier to set up:
you can't connect a "to-the-host" connector to a peripheral.
Linux can run in the host, or in the peripheral. In both cases
you need a low level bus controller driver, and some software
talking to it. Peripheral controllers are often discrete silicon,
or are integrated with the CPU in a microcontroller. The more
familiar host side controllers have names like like "EHCI", "OHCI",
or "UHCI", and are usually integrated into southbridges on PC
motherboards.
Enable this configuration option if you want to run Linux inside
a USB peripheral device. Configure one hardware driver for your
peripheral/device side bus controller, and a "gadget driver" for
your peripheral protocol. (If you use modular gadget drivers,
you may configure more than one.)
If in doubt, say "N" and don't enable these drivers; most people
don't have this kind of hardware (except maybe inside Linux PDAs).
For more information, see <http://www.linux-usb.org/gadget> and
the kernel DocBook documentation for this API.
config USB_GADGET_DEBUG_FILES
boolean "Debugging information files"
depends on USB_GADGET && PROC_FS
help
Some of the drivers in the "gadget" framework can expose
debugging information in files such as /proc/driver/udc
(for a peripheral controller). The information in these
files may help when you're troubleshooting or bringing up a
driver on a new board. Enable these files by choosing "Y"
here. If in doubt, or to conserve kernel memory, say "N".
#
# USB Peripheral Controller Support
#
choice
prompt "USB Peripheral Controller"
depends on USB_GADGET
help
A USB device uses a controller to talk to its host.
Systems should have only one such upstream link.
Many controller drivers are platform-specific; these
often need board-specific hooks.
config USB_GADGET_NET2280
boolean "NetChip 2280"
depends on PCI
select USB_GADGET_DUALSPEED
help
NetChip 2280 is a PCI based USB peripheral controller which
supports both full and high speed USB 2.0 data transfers.
It has six configurable endpoints, as well as endpoint zero
(for control transfers) and several endpoints with dedicated
functions.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "net2280" and force all
gadget drivers to also be dynamically linked.
config USB_NET2280
tristate
depends on USB_GADGET_NET2280
default USB_GADGET
config USB_GADGET_PXA2XX
boolean "PXA 25x or IXP 4xx"
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
help
Intel's PXA 25x series XScale ARM-5TE processors include
an integrated full speed USB 1.1 device controller. The
controller in the IXP 4xx series is register-compatible.
It has fifteen fixed-function endpoints, as well as endpoint
zero (for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "pxa2xx_udc" and force all
gadget drivers to also be dynamically linked.
config USB_PXA2XX
tristate
depends on USB_GADGET_PXA2XX
default USB_GADGET
# if there's only one gadget driver, using only two bulk endpoints,
# don't waste memory for the other endpoints
config USB_PXA2XX_SMALL
depends on USB_GADGET_PXA2XX
bool
default n if USB_ETH_RNDIS
default y if USB_ZERO
default y if USB_ETH
default y if USB_G_SERIAL
config USB_GADGET_GOKU
boolean "Toshiba TC86C001 'Goku-S'"
depends on PCI
help
The Toshiba TC86C001 is a PCI device which includes controllers
for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
The device controller has three configurable (bulk or interrupt)
endpoints, plus endpoint zero (for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "goku_udc" and to force all
gadget drivers to also be dynamically linked.
config USB_GOKU
tristate
depends on USB_GADGET_GOKU
default USB_GADGET
config USB_GADGET_LH7A40X
boolean "LH7A40X"
depends on ARCH_LH7A40X
help
This driver provides USB Device Controller driver for LH7A40x
config USB_LH7A40X
tristate
depends on USB_GADGET_LH7A40X
default USB_GADGET
config USB_GADGET_OMAP
boolean "OMAP USB Device Controller"
depends on ARCH_OMAP
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
help
Many Texas Instruments OMAP processors have flexible full
speed USB device controllers, with support for up to 30
endpoints (plus endpoint zero). This driver supports the
controller in the OMAP 1611, and should work with controllers
in other OMAP processors too, given minor tweaks.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "omap_udc" and force all
gadget drivers to also be dynamically linked.
config USB_OMAP
tristate
depends on USB_GADGET_OMAP
default USB_GADGET
config USB_OTG
boolean "OTG Support"
depends on USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD
help
The most notable feature of USB OTG is support for a
"Dual-Role" device, which can act as either a device
or a host. The initial role choice can be changed
later, when two dual-role devices talk to each other.
Select this only if your OMAP board has a Mini-AB connector.
config USB_GADGET_DUMMY_HCD
boolean "Dummy HCD (DEVELOPMENT)"
depends on USB && EXPERIMENTAL
select USB_GADGET_DUALSPEED
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
side is the master; the gadget side is the slave. Gadget drivers
can be high, full, or low speed; and they have access to endpoints
like those from NET2280, PXA2xx, or SA1100 hardware.
This may help in some stages of creating a driver to embed in a
Linux device, since it lets you debug several parts of the gadget
driver without its hardware or drivers being involved.
Since such a gadget side driver needs to interoperate with a host
side Linux-USB device driver, this may help to debug both sides
of a USB protocol stack.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "dummy_hcd" and force all
gadget drivers to also be dynamically linked.
config USB_DUMMY_HCD
tristate
depends on USB_GADGET_DUMMY_HCD
default USB_GADGET
# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
# first and will be selected by default.
endchoice
config USB_GADGET_DUALSPEED
bool
depends on USB_GADGET
default n
help
Means that gadget drivers should include extra descriptors
and code to handle dual-speed controllers.
#
# USB Gadget Drivers
#
choice
tristate "USB Gadget Drivers"
depends on USB_GADGET
default USB_ETH
help
A Linux "Gadget Driver" talks to the USB Peripheral Controller
driver through the abstract "gadget" API. Some other operating
systems call these "client" drivers, of which "class drivers"
are a subset (implementing a USB device class specification).
A gadget driver implements one or more USB functions using
the peripheral hardware.
Gadget drivers are hardware-neutral, or "platform independent",
except that they sometimes must understand quirks or limitations
of the particular controllers they work with. For example, when
a controller doesn't support alternate configurations or provide
enough of the right types of endpoints, the gadget driver might
not be able work with that controller, or might need to implement
a less common variant of a device class protocol.
# this first set of drivers all depend on bulk-capable hardware.
config USB_ZERO
tristate "Gadget Zero (DEVELOPMENT)"
depends on EXPERIMENTAL
help
Gadget Zero is a two-configuration device. It either sinks and
sources bulk data; or it loops back a configurable number of
transfers. It also implements control requests, for "chapter 9"
conformance. The driver needs only two bulk-capable endpoints, so
it can work on top of most device-side usb controllers. It's
useful for testing, and is also a working example showing how
USB "gadget drivers" can be written.
Make this be the first driver you try using on top of any new
USB peripheral controller driver. Then you can use host-side
test software, like the "usbtest" driver, to put your hardware
and its driver through a basic set of functional tests.
Gadget Zero also works with the host-side "usb-skeleton" driver,
and with many kinds of host-side test software. You may need
to tweak product and vendor IDs before host software knows about
this device, and arrange to select an appropriate configuration.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_zero".
config USB_ZERO_HNPTEST
boolean "HNP Test Device"
depends on USB_ZERO && USB_OTG
help
You can configure this device to enumerate using the device
identifiers of the USB-OTG test device. That means that when
this gadget connects to another OTG device, with this one using
the "B-Peripheral" role, that device will use HNP to let this
one serve as the USB host instead (in the "B-Host" role).
config USB_ETH
tristate "Ethernet Gadget (with CDC Ethernet support)"
depends on NET
help
This driver implements Ethernet style communication, in either
of two ways:
- The "Communication Device Class" (CDC) Ethernet Control Model.
That protocol is often avoided with pure Ethernet adapters, in
favor of simpler vendor-specific hardware, but is widely
supported by firmware for smart network devices.
- On hardware can't implement that protocol, a simple CDC subset
is used, placing fewer demands on USB.
RNDIS support is a third option, more demanding than that subset.
Within the USB device, this gadget driver exposes a network device
"usbX", where X depends on what other networking devices you have.
Treat it like a two-node Ethernet link: host, and gadget.
The Linux-USB host-side "usbnet" driver interoperates with this
driver, so that deep I/O queues can be supported. On 2.4 kernels,
use "CDCEther" instead, if you're using the CDC option. That CDC
mode should also interoperate with standard CDC Ethernet class
drivers on other host operating systems.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_ether".
config USB_ETH_RNDIS
bool "RNDIS support (EXPERIMENTAL)"
depends on USB_ETH && EXPERIMENTAL
default y
help
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
and Microsoft provides redistributable binary RNDIS drivers for
older versions of Windows.
If you say "y" here, the Ethernet gadget driver will try to provide
a second device configuration, supporting RNDIS to talk to such
Microsoft USB hosts.
To make MS-Windows work with this, use Documentation/usb/linux.inf
as the "driver info file". For versions of MS-Windows older than
XP, you'll need to download drivers from Microsoft's website; a URL
is given in comments found in that info file.
config USB_GADGETFS
tristate "Gadget Filesystem (EXPERIMENTAL)"
depends on EXPERIMENTAL
help
This driver provides a filesystem based API that lets user mode
programs implement a single-configuration USB device, including
endpoint I/O and control requests that don't relate to enumeration.
All endpoints, transfer speeds, and transfer types supported by
the hardware are available, through read() and write() calls.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "gadgetfs".
config USB_FILE_STORAGE
tristate "File-backed Storage Gadget"
help
The File-backed Storage Gadget acts as a USB Mass Storage
disk drive. As its storage repository it can use a regular
file or a block device (in much the same way as the "loop"
device driver), specified as a module parameter.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_file_storage".
config USB_FILE_STORAGE_TEST
bool "File-backed Storage Gadget testing version"
depends on USB_FILE_STORAGE
default n
help
Say "y" to generate the larger testing version of the
File-backed Storage Gadget, useful for probing the
behavior of USB Mass Storage hosts. Not needed for
normal operation.
config USB_G_SERIAL
tristate "Serial Gadget (with CDC ACM support)"
help
The Serial Gadget talks to the Linux-USB generic serial driver.
This driver supports a CDC-ACM module option, which can be used
to interoperate with MS-Windows hosts or with the Linux-USB
"cdc-acm" driver.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_serial".
For more information, see Documentation/usb/gadget_serial.txt
which includes instructions and a "driver info file" needed to
make MS-Windows work with this driver.
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.
# - none yet
endchoice
endmenu

View file

@ -0,0 +1,30 @@
#
# USB peripheral controller drivers
#
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2280) += net2280.o
obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
#
# USB gadget drivers
#
g_zero-objs := zero.o usbstring.o config.o epautoconf.o
g_ether-objs := ether.o usbstring.o config.o epautoconf.o
g_serial-objs := serial.o usbstring.o config.o epautoconf.o
gadgetfs-objs := inode.o
g_file_storage-objs := file_storage.o usbstring.o config.o \
epautoconf.o
ifeq ($(CONFIG_USB_ETH_RNDIS),y)
g_ether-objs += rndis.o
endif
obj-$(CONFIG_USB_ZERO) += g_zero.o
obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o

117
drivers/usb/gadget/config.c Normal file
View file

@ -0,0 +1,117 @@
/*
* usb/gadget/config.c -- simplify building config descriptors
*
* Copyright (C) 2003 David Brownell
*
* 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
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
/**
* usb_descriptor_fillbuf - fill buffer with descriptors
* @buf: Buffer to be filled
* @buflen: Size of buf
* @src: Array of descriptor pointers, terminated by null pointer.
*
* Copies descriptors into the buffer, returning the length or a
* negative error code if they can't all be copied. Useful when
* assembling descriptors for an associated set of interfaces used
* as part of configuring a composite device; or in other cases where
* sets of descriptors need to be marshaled.
*/
int
usb_descriptor_fillbuf(void *buf, unsigned buflen,
const struct usb_descriptor_header **src)
{
u8 *dest = buf;
if (!src)
return -EINVAL;
/* fill buffer from src[] until null descriptor ptr */
for (; 0 != *src; src++) {
unsigned len = (*src)->bLength;
if (len > buflen)
return -EINVAL;
memcpy(dest, *src, len);
buflen -= len;
dest += len;
}
return dest - (u8 *)buf;
}
/**
* usb_gadget_config_buf - builts a complete configuration descriptor
* @config: Header for the descriptor, including characteristics such
* as power requirements and number of interfaces.
* @desc: Null-terminated vector of pointers to the descriptors (interface,
* endpoint, etc) defining all functions in this device configuration.
* @buf: Buffer for the resulting configuration descriptor.
* @length: Length of buffer. If this is not big enough to hold the
* entire configuration descriptor, an error code will be returned.
*
* This copies descriptors into the response buffer, building a descriptor
* for that configuration. It returns the buffer length or a negative
* status code. The config.wTotalLength field is set to match the length
* of the result, but other descriptor fields (including power usage and
* interface count) must be set by the caller.
*
* Gadget drivers could use this when constructing a config descriptor
* in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
* resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
*/
int usb_gadget_config_buf(
const struct usb_config_descriptor *config,
void *buf,
unsigned length,
const struct usb_descriptor_header **desc
)
{
struct usb_config_descriptor *cp = buf;
int len;
/* config descriptor first */
if (length < USB_DT_CONFIG_SIZE || !desc)
return -EINVAL;
*cp = *config;
/* then interface/endpoint/class/vendor/... */
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
length - USB_DT_CONFIG_SIZE, desc);
if (len < 0)
return len;
len += USB_DT_CONFIG_SIZE;
if (len > 0xffff)
return -EINVAL;
/* patch up the config descriptor */
cp->bLength = USB_DT_CONFIG_SIZE;
cp->bDescriptorType = USB_DT_CONFIG;
cp->wTotalLength = cpu_to_le16(len);
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
return len;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,310 @@
/*
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
*
* Copyright (C) 2004 David Brownell
*
* 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
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
#include "gadget_chips.h"
/* we must assign addresses for configurable endpoints (like net2280) */
static __initdata unsigned epnum;
// #define MANY_ENDPOINTS
#ifdef MANY_ENDPOINTS
/* more than 15 configurable endpoints */
static __initdata unsigned in_epnum;
#endif
/*
* This should work with endpoints from controller drivers sharing the
* same endpoint naming convention. By example:
*
* - ep1, ep2, ... address is fixed, not direction or type
* - ep1in, ep2out, ... address and direction are fixed, not type
* - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
* - ep1in-bulk, ep2out-iso, ... all three are fixed
* - ep-* ... no functionality restrictions
*
* Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
* Less common restrictions are implied by gadget_is_*().
*
* NOTE: each endpoint is unidirectional, as specified by its USB
* descriptor; and isn't specific to a configuration or altsetting.
*/
static int __init
ep_matches (
struct usb_gadget *gadget,
struct usb_ep *ep,
struct usb_endpoint_descriptor *desc
)
{
u8 type;
const char *tmp;
u16 max;
/* endpoint already claimed? */
if (0 != ep->driver_data)
return 0;
/* only support ep0 for portable CONTROL traffic */
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
/* some other naming convention */
if ('e' != ep->name[0])
return 0;
/* type-restriction: "-iso", "-bulk", or "-int".
* direction-restriction: "in", "out".
*/
if ('-' != ep->name[2]) {
tmp = strrchr (ep->name, '-');
if (tmp) {
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* bulk endpoints handle interrupt transfers,
* except the toggle-quirky iso-synch kind
*/
if ('s' == tmp[2]) // == "-iso"
return 0;
/* for now, avoid PXA "interrupt-in";
* it's documented as never using DATA1.
*/
if (gadget_is_pxa (gadget)
&& 'i' == tmp [1])
return 0;
break;
case USB_ENDPOINT_XFER_BULK:
if ('b' != tmp[1]) // != "-bulk"
return 0;
break;
case USB_ENDPOINT_XFER_ISOC:
if ('s' != tmp[2]) // != "-iso"
return 0;
}
} else {
tmp = ep->name + strlen (ep->name);
}
/* direction-restriction: "..in-..", "out-.." */
tmp--;
if (!isdigit (*tmp)) {
if (desc->bEndpointAddress & USB_DIR_IN) {
if ('n' != *tmp)
return 0;
} else {
if ('t' != *tmp)
return 0;
}
}
}
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
*/
max = 0x7ff & le16_to_cpup (&desc->wMaxPacketSize);
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* INT: limit 64 bytes full speed, 1024 high speed */
if (!gadget->is_dualspeed && max > 64)
return 0;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high speed */
if (ep->maxpacket < max)
return 0;
if (!gadget->is_dualspeed && max > 1023)
return 0;
/* BOTH: "high bandwidth" works only at high speed */
if ((desc->wMaxPacketSize & __constant_cpu_to_le16(3<<11))) {
if (!gadget->is_dualspeed)
return 0;
/* configure your hardware with enough buffering!! */
}
break;
}
/* MATCH!! */
/* report address */
if (isdigit (ep->name [2])) {
u8 num = simple_strtol (&ep->name [2], NULL, 10);
desc->bEndpointAddress |= num;
#ifdef MANY_ENDPOINTS
} else if (desc->bEndpointAddress & USB_DIR_IN) {
if (++in_epnum > 15)
return 0;
desc->bEndpointAddress = USB_DIR_IN | in_epnum;
#endif
} else {
if (++epnum > 15)
return 0;
desc->bEndpointAddress |= epnum;
}
/* report (variable) full speed bulk maxpacket */
if (USB_ENDPOINT_XFER_BULK == type) {
int size = ep->maxpacket;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
size = 64;
desc->wMaxPacketSize = cpu_to_le16(size);
}
return 1;
}
static struct usb_ep * __init
find_ep (struct usb_gadget *gadget, const char *name)
{
struct usb_ep *ep;
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
if (0 == strcmp (ep->name, name))
return ep;
}
return NULL;
}
/**
* usb_ep_autoconfig - choose an endpoint matching the descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
* initialized. For periodic transfers, the maximum packet
* size must also be initialized. This is modified on success.
*
* By choosing an endpoint to use with the specified descriptor, this
* routine simplifies writing gadget drivers that work with multiple
* USB device controllers. The endpoint would be passed later to
* usb_ep_enable(), along with some descriptor.
*
* That second descriptor won't always be the same as the first one.
* For example, isochronous endpoints can be autoconfigured for high
* bandwidth, and then used in several lower bandwidth altsettings.
* Also, high and full speed descriptors will be different.
*
* Be sure to examine and test the results of autoconfiguration on your
* hardware. This code may not make the best choices about how to use the
* USB controller, and it can't know all the restrictions that may apply.
* Some combinations of driver and hardware won't be able to autoconfigure.
*
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
* is initialized as if the endpoint were used at full speed. To prevent
* the endpoint from being returned by a later autoconfig call, claim it
* by assigning ep->driver_data to some non-null value.
*
* On failure, this returns a null endpoint descriptor.
*/
struct usb_ep * __init usb_ep_autoconfig (
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc
)
{
struct usb_ep *ep;
u8 type;
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
/* First, apply chip-specific "best usage" knowledge.
* This might make a good usb_gadget_ops hook ...
*/
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep (gadget, "ep-e");
if (ep && ep_matches (gadget, ep, desc))
return ep;
ep = find_ep (gadget, "ep-f");
if (ep && ep_matches (gadget, ep, desc))
return ep;
} else if (gadget_is_goku (gadget)) {
if (USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough */
ep = find_ep (gadget, "ep3-bulk");
if (ep && ep_matches (gadget, ep, desc))
return ep;
} else if (USB_ENDPOINT_XFER_BULK == type
&& (USB_DIR_IN & desc->bEndpointAddress)) {
/* DMA may be available */
ep = find_ep (gadget, "ep2-bulk");
if (ep && ep_matches (gadget, ep, desc))
return ep;
}
} else if (gadget_is_sh (gadget) && USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough; maybe 8 byte fifo is too */
ep = find_ep (gadget, "ep3in-bulk");
if (ep && ep_matches (gadget, ep, desc))
return ep;
} else if (gadget_is_mq11xx (gadget) && USB_ENDPOINT_XFER_INT == type) {
ep = find_ep (gadget, "ep1-bulk");
if (ep && ep_matches (gadget, ep, desc))
return ep;
}
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
if (ep_matches (gadget, ep, desc))
return ep;
}
/* Fail */
return NULL;
}
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
* @gadget: device for which autoconfig state will be reset
*
* Use this for devices where one configuration may need to assign
* endpoint resources very differently from the next one. It clears
* state such as ep->driver_data and the record of assigned endpoints
* used by usb_ep_autoconfig().
*/
void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget)
{
struct usb_ep *ep;
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
ep->driver_data = NULL;
}
#ifdef MANY_ENDPOINTS
in_epnum = 0;
#endif
epnum = 0;
}

2660
drivers/usb/gadget/ether.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,92 @@
/*
* USB device controllers have lots of quirks. Use these macros in
* gadget drivers or other code that needs to deal with them, and which
* autoconfigures instead of using early binding to the hardware.
*
* This could eventually work like the ARM mach_is_*() stuff, driven by
* some config file that gets updated as new hardware is supported.
*
* NOTE: some of these controller drivers may not be available yet.
*/
#ifdef CONFIG_USB_GADGET_NET2280
#define gadget_is_net2280(g) !strcmp("net2280", (g)->name)
#else
#define gadget_is_net2280(g) 0
#endif
#ifdef CONFIG_USB_GADGET_DUMMY_HCD
#define gadget_is_dummy(g) !strcmp("dummy_udc", (g)->name)
#else
#define gadget_is_dummy(g) 0
#endif
#ifdef CONFIG_USB_GADGET_PXA2XX
#define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name)
#else
#define gadget_is_pxa(g) 0
#endif
#ifdef CONFIG_USB_GADGET_GOKU
#define gadget_is_goku(g) !strcmp("goku_udc", (g)->name)
#else
#define gadget_is_goku(g) 0
#endif
#ifdef CONFIG_USB_GADGET_SUPERH
#define gadget_is_sh(g) !strcmp("sh_udc", (g)->name)
#else
#define gadget_is_sh(g) 0
#endif
#ifdef CONFIG_USB_GADGET_SA1100
#define gadget_is_sa1100(g) !strcmp("sa1100_udc", (g)->name)
#else
#define gadget_is_sa1100(g) 0
#endif
#ifdef CONFIG_USB_GADGET_LH7A40X
#define gadget_is_lh7a40x(g) !strcmp("lh7a40x_udc", (g)->name)
#else
#define gadget_is_lh7a40x(g) 0
#endif
#ifdef CONFIG_USB_GADGET_MQ11XX
#define gadget_is_mq11xx(g) !strcmp("mq11xx_udc", (g)->name)
#else
#define gadget_is_mq11xx(g) 0
#endif
#ifdef CONFIG_USB_GADGET_OMAP
#define gadget_is_omap(g) !strcmp("omap_udc", (g)->name)
#else
#define gadget_is_omap(g) 0
#endif
#ifdef CONFIG_USB_GADGET_N9604
#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name)
#else
#define gadget_is_n9604(g) 0
#endif
#ifdef CONFIG_USB_GADGET_PXA27X
#define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name)
#else
#define gadget_is_pxa27x(g) 0
#endif
#ifdef CONFIG_USB_GADGET_S3C2410
#define gadget_is_s3c2410(g) !strcmp("s3c2410_udc", (g)->name)
#else
#define gadget_is_s3c2410(g) 0
#endif
#ifdef CONFIG_USB_GADGET_AT91
#define gadget_is_at91(g) !strcmp("at91_udc", (g)->name)
#else
#define gadget_is_at91(g) 0
#endif
// CONFIG_USB_GADGET_SX2
// CONFIG_USB_GADGET_AU1X00
// ...

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,290 @@
/*
* Toshiba TC86C001 ("Goku-S") USB Device Controller driver
*
* Copyright (C) 2000-2002 Lineo
* by Stuart Lynne, Tom Rushworth, and Bruce Balden
* Copyright (C) 2002 Toshiba Corporation
* Copyright (C) 2003 MontaVista Software (source@mvista.com)
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
/*
* PCI BAR 0 points to these registers.
*/
struct goku_udc_regs {
/* irq management */
u32 int_status; /* 0x000 */
u32 int_enable;
#define INT_SUSPEND 0x00001 /* or resume */
#define INT_USBRESET 0x00002
#define INT_ENDPOINT0 0x00004
#define INT_SETUP 0x00008
#define INT_STATUS 0x00010
#define INT_STATUSNAK 0x00020
#define INT_EPxDATASET(n) (0x00020 << (n)) /* 0 < n < 4 */
# define INT_EP1DATASET 0x00040
# define INT_EP2DATASET 0x00080
# define INT_EP3DATASET 0x00100
#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */
# define INT_EP1NAK 0x00200
# define INT_EP2NAK 0x00400
# define INT_EP3NAK 0x00800
#define INT_SOF 0x01000
#define INT_ERR 0x02000
#define INT_MSTWRSET 0x04000
#define INT_MSTWREND 0x08000
#define INT_MSTWRTMOUT 0x10000
#define INT_MSTRDEND 0x20000
#define INT_SYSERROR 0x40000
#define INT_PWRDETECT 0x80000
#define INT_DEVWIDE (INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND)
#define INT_EP0 (INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK)
u32 dma_master;
#define MST_EOPB_DIS 0x0800
#define MST_EOPB_ENA 0x0400
#define MST_TIMEOUT_DIS 0x0200
#define MST_TIMEOUT_ENA 0x0100
#define MST_RD_EOPB 0x0080 /* write-only */
#define MST_RD_RESET 0x0040
#define MST_WR_RESET 0x0020
#define MST_RD_ENA 0x0004 /* 1:start, 0:ignore */
#define MST_WR_ENA 0x0002 /* 1:start, 0:ignore */
#define MST_CONNECTION 0x0001 /* 0 for ep1out/ep2in */
#define MST_R_BITS (MST_EOPB_DIS|MST_EOPB_ENA \
|MST_RD_ENA|MST_RD_RESET)
#define MST_W_BITS (MST_TIMEOUT_DIS|MST_TIMEOUT_ENA \
|MST_WR_ENA|MST_WR_RESET)
#define MST_RW_BITS (MST_R_BITS|MST_W_BITS \
|MST_CONNECTION)
/* these values assume (dma_master & MST_CONNECTION) == 0 */
#define UDC_MSTWR_ENDPOINT 1
#define UDC_MSTRD_ENDPOINT 2
/* dma master write */
u32 out_dma_start;
u32 out_dma_end;
u32 out_dma_current;
/* dma master read */
u32 in_dma_start;
u32 in_dma_end;
u32 in_dma_current;
u32 power_detect;
#define PW_DETECT 0x04
#define PW_RESETB 0x02
#define PW_PULLUP 0x01
u8 _reserved0 [0x1d8];
/* endpoint registers */
u32 ep_fifo [4]; /* 0x200 */
u8 _reserved1 [0x10];
u32 ep_mode [4]; /* only 1-3 valid */
u8 _reserved2 [0x10];
u32 ep_status [4];
#define EPxSTATUS_TOGGLE 0x40
#define EPxSTATUS_SUSPEND 0x20
#define EPxSTATUS_EP_MASK (0x07<<2)
# define EPxSTATUS_EP_READY (0<<2)
# define EPxSTATUS_EP_DATAIN (1<<2)
# define EPxSTATUS_EP_FULL (2<<2)
# define EPxSTATUS_EP_TX_ERR (3<<2)
# define EPxSTATUS_EP_RX_ERR (4<<2)
# define EPxSTATUS_EP_BUSY (5<<2)
# define EPxSTATUS_EP_STALL (6<<2)
# define EPxSTATUS_EP_INVALID (7<<2)
#define EPxSTATUS_FIFO_DISABLE 0x02
#define EPxSTATUS_STAGE_ERROR 0x01
u8 _reserved3 [0x10];
u32 EPxSizeLA[4];
#define PACKET_ACTIVE (1<<7)
#define DATASIZE 0x7f
u8 _reserved3a [0x10];
u32 EPxSizeLB[4]; /* only 1,2 valid */
u8 _reserved3b [0x10];
u32 EPxSizeHA[4]; /* only 1-3 valid */
u8 _reserved3c [0x10];
u32 EPxSizeHB[4]; /* only 1,2 valid */
u8 _reserved4[0x30];
/* SETUP packet contents */
u32 bRequestType; /* 0x300 */
u32 bRequest;
u32 wValueL;
u32 wValueH;
u32 wIndexL;
u32 wIndexH;
u32 wLengthL;
u32 wLengthH;
/* command interaction/handshaking */
u32 SetupRecv; /* 0x320 */
u32 CurrConfig;
u32 StdRequest;
u32 Request;
u32 DataSet;
#define DATASET_A(epnum) (1<<(2*(epnum)))
#define DATASET_B(epnum) (2<<(2*(epnum)))
#define DATASET_AB(epnum) (3<<(2*(epnum)))
u8 _reserved5[4];
u32 UsbState;
#define USBSTATE_CONFIGURED 0x04
#define USBSTATE_ADDRESSED 0x02
#define USBSTATE_DEFAULT 0x01
u32 EOP;
u32 Command; /* 0x340 */
#define COMMAND_SETDATA0 2
#define COMMAND_RESET 3
#define COMMAND_STALL 4
#define COMMAND_INVALID 5
#define COMMAND_FIFO_DISABLE 7
#define COMMAND_FIFO_ENABLE 8
#define COMMAND_INIT_DESCRIPTOR 9
#define COMMAND_FIFO_CLEAR 10 /* also stall */
#define COMMAND_STALL_CLEAR 11
#define COMMAND_EP(n) ((n) << 4)
u32 EPxSingle;
u8 _reserved6[4];
u32 EPxBCS;
u8 _reserved7[8];
u32 IntControl;
#define ICONTROL_STATUSNAK 1
u8 _reserved8[4];
u32 reqmode; // 0x360 standard request mode, low 8 bits
#define G_REQMODE_SET_INTF (1<<7)
#define G_REQMODE_GET_INTF (1<<6)
#define G_REQMODE_SET_CONF (1<<5)
#define G_REQMODE_GET_CONF (1<<4)
#define G_REQMODE_GET_DESC (1<<3)
#define G_REQMODE_SET_FEAT (1<<2)
#define G_REQMODE_CLEAR_FEAT (1<<1)
#define G_REQMODE_GET_STATUS (1<<0)
u32 ReqMode;
u8 _reserved9[0x18];
u32 PortStatus; /* 0x380 */
u8 _reserved10[8];
u32 address;
u32 buff_test;
u8 _reserved11[4];
u32 UsbReady;
u8 _reserved12[4];
u32 SetDescStall; /* 0x3a0 */
u8 _reserved13[0x45c];
/* hardware could handle limited GET_DESCRIPTOR duties */
#define DESC_LEN 0x80
u32 descriptors[DESC_LEN]; /* 0x800 */
u8 _reserved14[0x600];
} __attribute__ ((packed));
#define MAX_FIFO_SIZE 64
#define MAX_EP0_SIZE 8 /* ep0 fifo is bigger, though */
/*-------------------------------------------------------------------------*/
/* DRIVER DATA STRUCTURES and UTILITIES */
struct goku_ep {
struct usb_ep ep;
struct goku_udc *dev;
unsigned long irqs;
unsigned num:8,
dma:1,
is_in:1,
stopped:1;
/* analogous to a host-side qh */
struct list_head queue;
const struct usb_endpoint_descriptor *desc;
u32 __iomem *reg_fifo;
u32 __iomem *reg_mode;
u32 __iomem *reg_status;
};
struct goku_request {
struct usb_request req;
struct list_head queue;
unsigned mapped:1;
};
enum ep0state {
EP0_DISCONNECT, /* no host */
EP0_IDLE, /* between STATUS ack and SETUP report */
EP0_IN, EP0_OUT, /* data stage */
EP0_STATUS, /* status stage */
EP0_STALL, /* data or status stages */
EP0_SUSPEND, /* usb suspend */
};
struct goku_udc {
/* each pci device provides one gadget, several endpoints */
struct usb_gadget gadget;
spinlock_t lock;
struct goku_ep ep[4];
struct usb_gadget_driver *driver;
enum ep0state ep0state;
unsigned got_irq:1,
got_region:1,
req_config:1,
configured:1,
enabled:1;
/* pci state used to access those endpoints */
struct pci_dev *pdev;
struct goku_udc_regs __iomem *regs;
u32 int_enable;
/* statistics... */
unsigned long irqs;
};
/*-------------------------------------------------------------------------*/
#define xprintk(dev,level,fmt,args...) \
printk(level "%s %s: " fmt , driver_name , \
pci_name(dev->pdev) , ## args)
#ifdef DEBUG
#define DBG(dev,fmt,args...) \
xprintk(dev , KERN_DEBUG , fmt , ## args)
#else
#define DBG(dev,fmt,args...) \
do { } while (0)
#endif /* DEBUG */
#ifdef VERBOSE
#define VDBG DBG
#else
#define VDBG(dev,fmt,args...) \
do { } while (0)
#endif /* VERBOSE */
#define ERROR(dev,fmt,args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
#define WARN(dev,fmt,args...) \
xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)

2110
drivers/usb/gadget/inode.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,261 @@
/*
* linux/drivers/usb/gadget/lh7a40x_udc.h
* Sharp LH7A40x on-chip full speed USB device controllers
*
* Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID
* Copyright (C) 2004 Bo Henriksen, Nordic ID
*
* 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
*
*/
#ifndef __LH7A40X_H_
#define __LH7A40X_H_
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <asm/byteorder.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/hardware.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
/*
* Memory map
*/
#define USB_FA 0x80000200 // function address register
#define USB_PM 0x80000204 // power management register
#define USB_IN_INT 0x80000208 // IN interrupt register bank (EP0-EP3)
#define USB_OUT_INT 0x80000210 // OUT interrupt register bank (EP2)
#define USB_INT 0x80000218 // interrupt register bank
#define USB_IN_INT_EN 0x8000021C // IN interrupt enable register bank
#define USB_OUT_INT_EN 0x80000224 // OUT interrupt enable register bank
#define USB_INT_EN 0x8000022C // USB interrupt enable register bank
#define USB_FRM_NUM1 0x80000230 // Frame number1 register
#define USB_FRM_NUM2 0x80000234 // Frame number2 register
#define USB_INDEX 0x80000238 // index register
#define USB_IN_MAXP 0x80000240 // IN MAXP register
#define USB_IN_CSR1 0x80000244 // IN CSR1 register/EP0 CSR register
#define USB_EP0_CSR 0x80000244 // IN CSR1 register/EP0 CSR register
#define USB_IN_CSR2 0x80000248 // IN CSR2 register
#define USB_OUT_MAXP 0x8000024C // OUT MAXP register
#define USB_OUT_CSR1 0x80000250 // OUT CSR1 register
#define USB_OUT_CSR2 0x80000254 // OUT CSR2 register
#define USB_OUT_FIFO_WC1 0x80000258 // OUT FIFO write count1 register
#define USB_OUT_FIFO_WC2 0x8000025C // OUT FIFO write count2 register
#define USB_RESET 0x8000044C // USB reset register
#define USB_EP0_FIFO 0x80000280
#define USB_EP1_FIFO 0x80000284
#define USB_EP2_FIFO 0x80000288
#define USB_EP3_FIFO 0x8000028c
/*
* USB reset register
*/
#define USB_RESET_APB (1<<1) //resets USB APB control side WRITE
#define USB_RESET_IO (1<<0) //resets USB IO side WRITE
/*
* USB function address register
*/
#define USB_FA_ADDR_UPDATE (1<<7)
#define USB_FA_FUNCTION_ADDR (0x7F)
/*
* Power Management register
*/
#define PM_USB_DCP (1<<5)
#define PM_USB_ENABLE (1<<4)
#define PM_USB_RESET (1<<3)
#define PM_UC_RESUME (1<<2)
#define PM_SUSPEND_MODE (1<<1)
#define PM_ENABLE_SUSPEND (1<<0)
/*
* IN interrupt register
*/
#define USB_IN_INT_EP3 (1<<3)
#define USB_IN_INT_EP1 (1<<1)
#define USB_IN_INT_EP0 (1<<0)
/*
* OUT interrupt register
*/
#define USB_OUT_INT_EP2 (1<<2)
/*
* USB interrupt register
*/
#define USB_INT_RESET_INT (1<<2)
#define USB_INT_RESUME_INT (1<<1)
#define USB_INT_SUSPEND_INT (1<<0)
/*
* USB interrupt enable register
*/
#define USB_INT_EN_USB_RESET_INTER (1<<2)
#define USB_INT_EN_RESUME_INTER (1<<1)
#define USB_INT_EN_SUSPEND_INTER (1<<0)
/*
* INCSR1 register
*/
#define USB_IN_CSR1_CLR_DATA_TOGGLE (1<<6)
#define USB_IN_CSR1_SENT_STALL (1<<5)
#define USB_IN_CSR1_SEND_STALL (1<<4)
#define USB_IN_CSR1_FIFO_FLUSH (1<<3)
#define USB_IN_CSR1_FIFO_NOT_EMPTY (1<<1)
#define USB_IN_CSR1_IN_PKT_RDY (1<<0)
/*
* INCSR2 register
*/
#define USB_IN_CSR2_AUTO_SET (1<<7)
#define USB_IN_CSR2_USB_DMA_EN (1<<4)
/*
* OUT CSR1 register
*/
#define USB_OUT_CSR1_CLR_DATA_REG (1<<7)
#define USB_OUT_CSR1_SENT_STALL (1<<6)
#define USB_OUT_CSR1_SEND_STALL (1<<5)
#define USB_OUT_CSR1_FIFO_FLUSH (1<<4)
#define USB_OUT_CSR1_FIFO_FULL (1<<1)
#define USB_OUT_CSR1_OUT_PKT_RDY (1<<0)
/*
* OUT CSR2 register
*/
#define USB_OUT_CSR2_AUTO_CLR (1<<7)
#define USB_OUT_CSR2_USB_DMA_EN (1<<4)
/*
* EP0 CSR
*/
#define EP0_CLR_SETUP_END (1<<7) /* Clear "Setup Ends" Bit (w) */
#define EP0_CLR_OUT (1<<6) /* Clear "Out packet ready" Bit (w) */
#define EP0_SEND_STALL (1<<5) /* Send STALL Handshake (rw) */
#define EP0_SETUP_END (1<<4) /* Setup Ends (r) */
#define EP0_DATA_END (1<<3) /* Data end (rw) */
#define EP0_SENT_STALL (1<<2) /* Sent Stall Handshake (r) */
#define EP0_IN_PKT_RDY (1<<1) /* In packet ready (rw) */
#define EP0_OUT_PKT_RDY (1<<0) /* Out packet ready (r) */
/* general CSR */
#define OUT_PKT_RDY (1<<0)
#define IN_PKT_RDY (1<<0)
/*
* IN/OUT MAXP register
*/
#define USB_OUT_MAXP_MAXP (0xF)
#define USB_IN_MAXP_MAXP (0xF)
// Max packet size
//#define EP0_PACKETSIZE 0x10
#define EP0_PACKETSIZE 0x8
#define EP0_MAXPACKETSIZE 0x10
#define UDC_MAX_ENDPOINTS 4
#define WAIT_FOR_SETUP 0
#define DATA_STATE_XMIT 1
#define DATA_STATE_NEED_ZLP 2
#define WAIT_FOR_OUT_STATUS 3
#define DATA_STATE_RECV 4
/* ********************************************************************************************* */
/* IO
*/
typedef enum ep_type {
ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt
} ep_type_t;
struct lh7a40x_ep {
struct usb_ep ep;
struct lh7a40x_udc *dev;
const struct usb_endpoint_descriptor *desc;
struct list_head queue;
unsigned long pio_irqs;
u8 stopped;
u8 bEndpointAddress;
u8 bmAttributes;
ep_type_t ep_type;
u32 fifo;
u32 csr1;
u32 csr2;
};
struct lh7a40x_request {
struct usb_request req;
struct list_head queue;
};
struct lh7a40x_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct device *dev;
spinlock_t lock;
int ep0state;
struct lh7a40x_ep ep[UDC_MAX_ENDPOINTS];
unsigned char usb_address;
unsigned req_pending:1, req_std:1, req_config:1;
};
extern struct lh7a40x_udc *the_controller;
#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN)
#define ep_index(EP) ((EP)->bEndpointAddress&0xF)
#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
#endif

217
drivers/usb/gadget/ndis.h Normal file
View file

@ -0,0 +1,217 @@
/*
* ndis.h
*
* ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
*
* Thanks to the cygwin development team,
* espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
*
* THIS SOFTWARE IS NOT COPYRIGHTED
*
* This source code is offered for use in the public domain. You may
* use, modify or distribute it freely.
*
* This code is distributed in the hope that it will be useful but
* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
* DISCLAIMED. This includes but is not limited to warranties of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef _LINUX_NDIS_H
#define _LINUX_NDIS_H
#define NDIS_STATUS_MULTICAST_FULL 0xC0010009
#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A
#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B
enum NDIS_DEVICE_POWER_STATE {
NdisDeviceStateUnspecified = 0,
NdisDeviceStateD0,
NdisDeviceStateD1,
NdisDeviceStateD2,
NdisDeviceStateD3,
NdisDeviceStateMaximum
};
struct NDIS_PM_WAKE_UP_CAPABILITIES {
enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
};
/* NDIS_PNP_CAPABILITIES.Flags constants */
#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001
#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002
#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004
struct NDIS_PNP_CAPABILITIES {
u32 Flags;
struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
};
struct NDIS_PM_PACKET_PATTERN {
u32 Priority;
u32 Reserved;
u32 MaskSize;
u32 PatternOffset;
u32 PatternSize;
u32 PatternFlags;
};
/* Required Object IDs (OIDs) */
#define OID_GEN_SUPPORTED_LIST 0x00010101
#define OID_GEN_HARDWARE_STATUS 0x00010102
#define OID_GEN_MEDIA_SUPPORTED 0x00010103
#define OID_GEN_MEDIA_IN_USE 0x00010104
#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
#define OID_GEN_LINK_SPEED 0x00010107
#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
#define OID_GEN_VENDOR_ID 0x0001010C
#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D
#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
#define OID_GEN_DRIVER_VERSION 0x00010110
#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
#define OID_GEN_PROTOCOL_OPTIONS 0x00010112
#define OID_GEN_MAC_OPTIONS 0x00010113
#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
#define OID_GEN_SUPPORTED_GUIDS 0x00010117
#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
#define OID_GEN_MACHINE_NAME 0x0001021A
#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
#define OID_GEN_VLAN_ID 0x0001021C
/* Optional OIDs */
#define OID_GEN_MEDIA_CAPABILITIES 0x00010201
#define OID_GEN_PHYSICAL_MEDIUM 0x00010202
/* Required statistics OIDs */
#define OID_GEN_XMIT_OK 0x00020101
#define OID_GEN_RCV_OK 0x00020102
#define OID_GEN_XMIT_ERROR 0x00020103
#define OID_GEN_RCV_ERROR 0x00020104
#define OID_GEN_RCV_NO_BUFFER 0x00020105
/* Optional statistics OIDs */
#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207
#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209
#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
#define OID_GEN_RCV_CRC_ERROR 0x0002020D
#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
#define OID_GEN_GET_TIME_CAPS 0x0002020F
#define OID_GEN_GET_NETCARD_TIME 0x00020210
#define OID_GEN_NETCARD_LOAD 0x00020211
#define OID_GEN_DEVICE_PROFILE 0x00020212
#define OID_GEN_INIT_TIME_MS 0x00020213
#define OID_GEN_RESET_COUNTS 0x00020214
#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215
#define OID_GEN_FRIENDLY_NAME 0x00020216
#define OID_GEN_MINIPORT_INFO 0x00020217
#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218
/* IEEE 802.3 (Ethernet) OIDs */
#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
#define OID_802_3_PERMANENT_ADDRESS 0x01010101
#define OID_802_3_CURRENT_ADDRESS 0x01010102
#define OID_802_3_MULTICAST_LIST 0x01010103
#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
#define OID_802_3_MAC_OPTIONS 0x01010105
#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
#define OID_802_3_XMIT_ONE_COLLISION 0x01020102
#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
#define OID_802_3_XMIT_DEFERRED 0x01020201
#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
#define OID_802_3_RCV_OVERRUN 0x01020203
#define OID_802_3_XMIT_UNDERRUN 0x01020204
#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
/* OID_GEN_MINIPORT_INFO constants */
#define NDIS_MINIPORT_BUS_MASTER 0x00000001
#define NDIS_MINIPORT_WDM_DRIVER 0x00000002
#define NDIS_MINIPORT_SG_LIST 0x00000004
#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008
#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010
#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020
#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040
#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080
#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100
#define NDIS_MINIPORT_IS_NDIS_5 0x00000200
#define NDIS_MINIPORT_IS_CO 0x00000400
#define NDIS_MINIPORT_DESERIALIZE 0x00000800
#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000
#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000
#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000
#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000
#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000
#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000
#define NDIS_MINIPORT_HIDDEN 0x00040000
#define NDIS_MINIPORT_SWENUM 0x00080000
#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000
#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000
#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000
#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000
#define NDIS_MINIPORT_64BITS_DMA 0x01000000
#define NDIS_MEDIUM_802_3 0x00000000
#define NDIS_MEDIUM_802_5 0x00000001
#define NDIS_MEDIUM_FDDI 0x00000002
#define NDIS_MEDIUM_WAN 0x00000003
#define NDIS_MEDIUM_LOCAL_TALK 0x00000004
#define NDIS_MEDIUM_DIX 0x00000005
#define NDIS_MEDIUM_ARCENT_RAW 0x00000006
#define NDIS_MEDIUM_ARCENT_878_2 0x00000007
#define NDIS_MEDIUM_ATM 0x00000008
#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009
#define NDIS_MEDIUM_IRDA 0x0000000A
#define NDIS_MEDIUM_BPC 0x0000000B
#define NDIS_MEDIUM_CO_WAN 0x0000000C
#define NDIS_MEDIUM_1394 0x0000000D
#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
#define NDIS_PACKET_TYPE_SMT 0x00000040
#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
#define NDIS_PACKET_TYPE_GROUP 0x00000100
#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200
#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
#define NDIS_MEDIA_STATE_CONNECTED 0x00000000
#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001
#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001
#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002
#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004
#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008
#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010
#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020
#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040
#define NDIS_MAC_OPTION_RESERVED 0x80000000
#endif /* _LINUX_NDIS_H */

2967
drivers/usb/gadget/net2280.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,728 @@
/*
* NetChip 2280 high/full speed USB device controller.
* Unlike many such controllers, this one talks PCI.
*/
/*
* Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
* Copyright (C) 2003 David Brownell
*
* 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
*/
/*-------------------------------------------------------------------------*/
/* NET2280 MEMORY MAPPED REGISTERS
*
* The register layout came from the chip documentation, and the bit
* number definitions were extracted from chip specification.
*
* Use the shift operator ('<<') to build bit masks, with readl/writel
* to access the registers through PCI.
*/
/* main registers, BAR0 + 0x0000 */
struct net2280_regs {
// offset 0x0000
u32 devinit;
#define LOCAL_CLOCK_FREQUENCY 8
#define FORCE_PCI_RESET 7
#define PCI_ID 6
#define PCI_ENABLE 5
#define FIFO_SOFT_RESET 4
#define CFG_SOFT_RESET 3
#define PCI_SOFT_RESET 2
#define USB_SOFT_RESET 1
#define M8051_RESET 0
u32 eectl;
#define EEPROM_ADDRESS_WIDTH 23
#define EEPROM_CHIP_SELECT_ACTIVE 22
#define EEPROM_PRESENT 21
#define EEPROM_VALID 20
#define EEPROM_BUSY 19
#define EEPROM_CHIP_SELECT_ENABLE 18
#define EEPROM_BYTE_READ_START 17
#define EEPROM_BYTE_WRITE_START 16
#define EEPROM_READ_DATA 8
#define EEPROM_WRITE_DATA 0
u32 eeclkfreq;
u32 _unused0;
// offset 0x0010
u32 pciirqenb0; /* interrupt PCI master ... */
#define SETUP_PACKET_INTERRUPT_ENABLE 7
#define ENDPOINT_F_INTERRUPT_ENABLE 6
#define ENDPOINT_E_INTERRUPT_ENABLE 5
#define ENDPOINT_D_INTERRUPT_ENABLE 4
#define ENDPOINT_C_INTERRUPT_ENABLE 3
#define ENDPOINT_B_INTERRUPT_ENABLE 2
#define ENDPOINT_A_INTERRUPT_ENABLE 1
#define ENDPOINT_0_INTERRUPT_ENABLE 0
u32 pciirqenb1;
#define PCI_INTERRUPT_ENABLE 31
#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27
#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26
#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25
#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20
#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19
#define PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE 18
#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17
#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16
#define GPIO_INTERRUPT_ENABLE 13
#define DMA_D_INTERRUPT_ENABLE 12
#define DMA_C_INTERRUPT_ENABLE 11
#define DMA_B_INTERRUPT_ENABLE 10
#define DMA_A_INTERRUPT_ENABLE 9
#define EEPROM_DONE_INTERRUPT_ENABLE 8
#define VBUS_INTERRUPT_ENABLE 7
#define CONTROL_STATUS_INTERRUPT_ENABLE 6
#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4
#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3
#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2
#define RESUME_INTERRUPT_ENABLE 1
#define SOF_INTERRUPT_ENABLE 0
u32 cpu_irqenb0; /* ... or onboard 8051 */
#define SETUP_PACKET_INTERRUPT_ENABLE 7
#define ENDPOINT_F_INTERRUPT_ENABLE 6
#define ENDPOINT_E_INTERRUPT_ENABLE 5
#define ENDPOINT_D_INTERRUPT_ENABLE 4
#define ENDPOINT_C_INTERRUPT_ENABLE 3
#define ENDPOINT_B_INTERRUPT_ENABLE 2
#define ENDPOINT_A_INTERRUPT_ENABLE 1
#define ENDPOINT_0_INTERRUPT_ENABLE 0
u32 cpu_irqenb1;
#define CPU_INTERRUPT_ENABLE 31
#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27
#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26
#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25
#define PCI_INTA_INTERRUPT_ENABLE 24
#define PCI_PME_INTERRUPT_ENABLE 23
#define PCI_SERR_INTERRUPT_ENABLE 22
#define PCI_PERR_INTERRUPT_ENABLE 21
#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20
#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19
#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17
#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16
#define GPIO_INTERRUPT_ENABLE 13
#define DMA_D_INTERRUPT_ENABLE 12
#define DMA_C_INTERRUPT_ENABLE 11
#define DMA_B_INTERRUPT_ENABLE 10
#define DMA_A_INTERRUPT_ENABLE 9
#define EEPROM_DONE_INTERRUPT_ENABLE 8
#define VBUS_INTERRUPT_ENABLE 7
#define CONTROL_STATUS_INTERRUPT_ENABLE 6
#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4
#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3
#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2
#define RESUME_INTERRUPT_ENABLE 1
#define SOF_INTERRUPT_ENABLE 0
// offset 0x0020
u32 _unused1;
u32 usbirqenb1;
#define USB_INTERRUPT_ENABLE 31
#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27
#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26
#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25
#define PCI_INTA_INTERRUPT_ENABLE 24
#define PCI_PME_INTERRUPT_ENABLE 23
#define PCI_SERR_INTERRUPT_ENABLE 22
#define PCI_PERR_INTERRUPT_ENABLE 21
#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20
#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19
#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17
#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16
#define GPIO_INTERRUPT_ENABLE 13
#define DMA_D_INTERRUPT_ENABLE 12
#define DMA_C_INTERRUPT_ENABLE 11
#define DMA_B_INTERRUPT_ENABLE 10
#define DMA_A_INTERRUPT_ENABLE 9
#define EEPROM_DONE_INTERRUPT_ENABLE 8
#define VBUS_INTERRUPT_ENABLE 7
#define CONTROL_STATUS_INTERRUPT_ENABLE 6
#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4
#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3
#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2
#define RESUME_INTERRUPT_ENABLE 1
#define SOF_INTERRUPT_ENABLE 0
u32 irqstat0;
#define INTA_ASSERTED 12
#define SETUP_PACKET_INTERRUPT 7
#define ENDPOINT_F_INTERRUPT 6
#define ENDPOINT_E_INTERRUPT 5
#define ENDPOINT_D_INTERRUPT 4
#define ENDPOINT_C_INTERRUPT 3
#define ENDPOINT_B_INTERRUPT 2
#define ENDPOINT_A_INTERRUPT 1
#define ENDPOINT_0_INTERRUPT 0
u32 irqstat1;
#define POWER_STATE_CHANGE_INTERRUPT 27
#define PCI_ARBITER_TIMEOUT_INTERRUPT 26
#define PCI_PARITY_ERROR_INTERRUPT 25
#define PCI_INTA_INTERRUPT 24
#define PCI_PME_INTERRUPT 23
#define PCI_SERR_INTERRUPT 22
#define PCI_PERR_INTERRUPT 21
#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT 20
#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT 19
#define PCI_RETRY_ABORT_INTERRUPT 17
#define PCI_MASTER_CYCLE_DONE_INTERRUPT 16
#define GPIO_INTERRUPT 13
#define DMA_D_INTERRUPT 12
#define DMA_C_INTERRUPT 11
#define DMA_B_INTERRUPT 10
#define DMA_A_INTERRUPT 9
#define EEPROM_DONE_INTERRUPT 8
#define VBUS_INTERRUPT 7
#define CONTROL_STATUS_INTERRUPT 6
#define ROOT_PORT_RESET_INTERRUPT 4
#define SUSPEND_REQUEST_INTERRUPT 3
#define SUSPEND_REQUEST_CHANGE_INTERRUPT 2
#define RESUME_INTERRUPT 1
#define SOF_INTERRUPT 0
// offset 0x0030
u32 idxaddr;
u32 idxdata;
u32 fifoctl;
#define PCI_BASE2_RANGE 16
#define IGNORE_FIFO_AVAILABILITY 3
#define PCI_BASE2_SELECT 2
#define FIFO_CONFIGURATION_SELECT 0
u32 _unused2;
// offset 0x0040
u32 memaddr;
#define START 28
#define DIRECTION 27
#define FIFO_DIAGNOSTIC_SELECT 24
#define MEMORY_ADDRESS 0
u32 memdata0;
u32 memdata1;
u32 _unused3;
// offset 0x0050
u32 gpioctl;
#define GPIO3_LED_SELECT 12
#define GPIO3_INTERRUPT_ENABLE 11
#define GPIO2_INTERRUPT_ENABLE 10
#define GPIO1_INTERRUPT_ENABLE 9
#define GPIO0_INTERRUPT_ENABLE 8
#define GPIO3_OUTPUT_ENABLE 7
#define GPIO2_OUTPUT_ENABLE 6
#define GPIO1_OUTPUT_ENABLE 5
#define GPIO0_OUTPUT_ENABLE 4
#define GPIO3_DATA 3
#define GPIO2_DATA 2
#define GPIO1_DATA 1
#define GPIO0_DATA 0
u32 gpiostat;
#define GPIO3_INTERRUPT 3
#define GPIO2_INTERRUPT 2
#define GPIO1_INTERRUPT 1
#define GPIO0_INTERRUPT 0
} __attribute__ ((packed));
/* usb control, BAR0 + 0x0080 */
struct net2280_usb_regs {
// offset 0x0080
u32 stdrsp;
#define STALL_UNSUPPORTED_REQUESTS 31
#define SET_TEST_MODE 16
#define GET_OTHER_SPEED_CONFIGURATION 15
#define GET_DEVICE_QUALIFIER 14
#define SET_ADDRESS 13
#define ENDPOINT_SET_CLEAR_HALT 12
#define DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP 11
#define GET_STRING_DESCRIPTOR_2 10
#define GET_STRING_DESCRIPTOR_1 9
#define GET_STRING_DESCRIPTOR_0 8
#define GET_SET_INTERFACE 6
#define GET_SET_CONFIGURATION 5
#define GET_CONFIGURATION_DESCRIPTOR 4
#define GET_DEVICE_DESCRIPTOR 3
#define GET_ENDPOINT_STATUS 2
#define GET_INTERFACE_STATUS 1
#define GET_DEVICE_STATUS 0
u32 prodvendid;
#define PRODUCT_ID 16
#define VENDOR_ID 0
u32 relnum;
u32 usbctl;
#define SERIAL_NUMBER_INDEX 16
#define PRODUCT_ID_STRING_ENABLE 13
#define VENDOR_ID_STRING_ENABLE 12
#define USB_ROOT_PORT_WAKEUP_ENABLE 11
#define VBUS_PIN 10
#define TIMED_DISCONNECT 9
#define SUSPEND_IMMEDIATELY 7
#define SELF_POWERED_USB_DEVICE 6
#define REMOTE_WAKEUP_SUPPORT 5
#define PME_POLARITY 4
#define USB_DETECT_ENABLE 3
#define PME_WAKEUP_ENABLE 2
#define DEVICE_REMOTE_WAKEUP_ENABLE 1
#define SELF_POWERED_STATUS 0
// offset 0x0090
u32 usbstat;
#define HIGH_SPEED 7
#define FULL_SPEED 6
#define GENERATE_RESUME 5
#define GENERATE_DEVICE_REMOTE_WAKEUP 4
u32 xcvrdiag;
#define FORCE_HIGH_SPEED_MODE 31
#define FORCE_FULL_SPEED_MODE 30
#define USB_TEST_MODE 24
#define LINE_STATE 16
#define TRANSCEIVER_OPERATION_MODE 2
#define TRANSCEIVER_SELECT 1
#define TERMINATION_SELECT 0
u32 setup0123;
u32 setup4567;
// offset 0x0090
u32 _unused0;
u32 ouraddr;
#define FORCE_IMMEDIATE 7
#define OUR_USB_ADDRESS 0
u32 ourconfig;
} __attribute__ ((packed));
/* pci control, BAR0 + 0x0100 */
struct net2280_pci_regs {
// offset 0x0100
u32 pcimstctl;
#define PCI_ARBITER_PARK_SELECT 13
#define PCI_MULTI LEVEL_ARBITER 12
#define PCI_RETRY_ABORT_ENABLE 11
#define DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE 10
#define DMA_READ_MULTIPLE_ENABLE 9
#define DMA_READ_LINE_ENABLE 8
#define PCI_MASTER_COMMAND_SELECT 6
#define MEM_READ_OR_WRITE 0
#define IO_READ_OR_WRITE 1
#define CFG_READ_OR_WRITE 2
#define PCI_MASTER_START 5
#define PCI_MASTER_READ_WRITE 4
#define PCI_MASTER_WRITE 0
#define PCI_MASTER_READ 1
#define PCI_MASTER_BYTE_WRITE_ENABLES 0
u32 pcimstaddr;
u32 pcimstdata;
u32 pcimststat;
#define PCI_ARBITER_CLEAR 2
#define PCI_EXTERNAL_ARBITER 1
#define PCI_HOST_MODE 0
} __attribute__ ((packed));
/* dma control, BAR0 + 0x0180 ... array of four structs like this,
* for channels 0..3. see also struct net2280_dma: descriptor
* that can be loaded into some of these registers.
*/
struct net2280_dma_regs { /* [11.7] */
// offset 0x0180, 0x01a0, 0x01c0, 0x01e0,
u32 dmactl;
#define DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE 25
#define DMA_CLEAR_COUNT_ENABLE 21
#define DESCRIPTOR_POLLING_RATE 19
#define POLL_CONTINUOUS 0
#define POLL_1_USEC 1
#define POLL_100_USEC 2
#define POLL_1_MSEC 3
#define DMA_VALID_BIT_POLLING_ENABLE 18
#define DMA_VALID_BIT_ENABLE 17
#define DMA_SCATTER_GATHER_ENABLE 16
#define DMA_OUT_AUTO_START_ENABLE 4
#define DMA_PREEMPT_ENABLE 3
#define DMA_FIFO_VALIDATE 2
#define DMA_ENABLE 1
#define DMA_ADDRESS_HOLD 0
u32 dmastat;
#define DMA_SCATTER_GATHER_DONE_INTERRUPT 25
#define DMA_TRANSACTION_DONE_INTERRUPT 24
#define DMA_ABORT 1
#define DMA_START 0
u32 _unused0 [2];
// offset 0x0190, 0x01b0, 0x01d0, 0x01f0,
u32 dmacount;
#define VALID_BIT 31
#define DMA_DIRECTION 30
#define DMA_DONE_INTERRUPT_ENABLE 29
#define END_OF_CHAIN 28
#define DMA_BYTE_COUNT_MASK ((1<<24)-1)
#define DMA_BYTE_COUNT 0
u32 dmaaddr;
u32 dmadesc;
u32 _unused1;
} __attribute__ ((packed));
/* dedicated endpoint registers, BAR0 + 0x0200 */
struct net2280_dep_regs { /* [11.8] */
// offset 0x0200, 0x0210, 0x220, 0x230, 0x240
u32 dep_cfg;
// offset 0x0204, 0x0214, 0x224, 0x234, 0x244
u32 dep_rsp;
u32 _unused [2];
} __attribute__ ((packed));
/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs
* like this, for ep0 then the configurable endpoints A..F
* ep0 reserved for control; E and F have only 64 bytes of fifo
*/
struct net2280_ep_regs { /* [11.9] */
// offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0
u32 ep_cfg;
#define ENDPOINT_BYTE_COUNT 16
#define ENDPOINT_ENABLE 10
#define ENDPOINT_TYPE 8
#define ENDPOINT_DIRECTION 7
#define ENDPOINT_NUMBER 0
u32 ep_rsp;
#define SET_NAK_OUT_PACKETS 15
#define SET_EP_HIDE_STATUS_PHASE 14
#define SET_EP_FORCE_CRC_ERROR 13
#define SET_INTERRUPT_MODE 12
#define SET_CONTROL_STATUS_PHASE_HANDSHAKE 11
#define SET_NAK_OUT_PACKETS_MODE 10
#define SET_ENDPOINT_TOGGLE 9
#define SET_ENDPOINT_HALT 8
#define CLEAR_NAK_OUT_PACKETS 7
#define CLEAR_EP_HIDE_STATUS_PHASE 6
#define CLEAR_EP_FORCE_CRC_ERROR 5
#define CLEAR_INTERRUPT_MODE 4
#define CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE 3
#define CLEAR_NAK_OUT_PACKETS_MODE 2
#define CLEAR_ENDPOINT_TOGGLE 1
#define CLEAR_ENDPOINT_HALT 0
u32 ep_irqenb;
#define SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE 6
#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 5
#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3
#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2
#define DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE 1
#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0
u32 ep_stat;
#define FIFO_VALID_COUNT 24
#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 22
#define TIMEOUT 21
#define USB_STALL_SENT 20
#define USB_IN_NAK_SENT 19
#define USB_IN_ACK_RCVD 18
#define USB_OUT_PING_NAK_SENT 17
#define USB_OUT_ACK_SENT 16
#define FIFO_OVERFLOW 13
#define FIFO_UNDERFLOW 12
#define FIFO_FULL 11
#define FIFO_EMPTY 10
#define FIFO_FLUSH 9
#define SHORT_PACKET_OUT_DONE_INTERRUPT 6
#define SHORT_PACKET_TRANSFERRED_INTERRUPT 5
#define NAK_OUT_PACKETS 4
#define DATA_PACKET_RECEIVED_INTERRUPT 3
#define DATA_PACKET_TRANSMITTED_INTERRUPT 2
#define DATA_OUT_PING_TOKEN_INTERRUPT 1
#define DATA_IN_TOKEN_INTERRUPT 0
// offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0
u32 ep_avail;
u32 ep_data;
u32 _unused0 [2];
} __attribute__ ((packed));
/*-------------------------------------------------------------------------*/
#ifdef __KERNEL__
/* indexed registers [11.10] are accessed indirectly
* caller must own the device lock.
*/
static inline u32
get_idx_reg (struct net2280_regs __iomem *regs, u32 index)
{
writel (index, &regs->idxaddr);
/* NOTE: synchs device/cpu memory views */
return readl (&regs->idxdata);
}
static inline void
set_idx_reg (struct net2280_regs __iomem *regs, u32 index, u32 value)
{
writel (index, &regs->idxaddr);
writel (value, &regs->idxdata);
/* posted, may not be visible yet */
}
#endif /* __KERNEL__ */
#define REG_DIAG 0x0
#define RETRY_COUNTER 16
#define FORCE_PCI_SERR 11
#define FORCE_PCI_INTERRUPT 10
#define FORCE_USB_INTERRUPT 9
#define FORCE_CPU_INTERRUPT 8
#define ILLEGAL_BYTE_ENABLES 5
#define FAST_TIMES 4
#define FORCE_RECEIVE_ERROR 2
#define FORCE_TRANSMIT_CRC_ERROR 0
#define REG_FRAME 0x02 /* from last sof */
#define REG_CHIPREV 0x03 /* in bcd */
#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */
#define CHIPREV_1 0x0100
#define CHIPREV_1A 0x0110
#ifdef __KERNEL__
/* ep a-f highspeed and fullspeed maxpacket, addresses
* computed from ep->num
*/
#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \
(((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1))
/*-------------------------------------------------------------------------*/
/* [8.3] for scatter/gather i/o
* use struct net2280_dma_regs bitfields
*/
struct net2280_dma {
__le32 dmacount;
__le32 dmaaddr; /* the buffer */
__le32 dmadesc; /* next dma descriptor */
__le32 _reserved;
} __attribute__ ((aligned (16)));
/*-------------------------------------------------------------------------*/
/* DRIVER DATA STRUCTURES and UTILITIES */
struct net2280_ep {
struct usb_ep ep;
struct net2280_ep_regs __iomem *regs;
struct net2280_dma_regs __iomem *dma;
struct net2280_dma *dummy;
dma_addr_t td_dma; /* of dummy */
struct net2280 *dev;
unsigned long irqs;
/* analogous to a host-side qh */
struct list_head queue;
const struct usb_endpoint_descriptor *desc;
unsigned num : 8,
fifo_size : 12,
in_fifo_validate : 1,
out_overflow : 1,
stopped : 1,
is_in : 1,
is_iso : 1;
};
static inline void allow_status (struct net2280_ep *ep)
{
/* ep0 only */
writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
| (1 << CLEAR_NAK_OUT_PACKETS)
| (1 << CLEAR_NAK_OUT_PACKETS_MODE)
, &ep->regs->ep_rsp);
ep->stopped = 1;
}
/* count (<= 4) bytes in the next fifo write will be valid */
static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count)
{
writeb (count, 2 + (u8 __iomem *) &ep->regs->ep_cfg);
}
struct net2280_request {
struct usb_request req;
struct net2280_dma *td;
dma_addr_t td_dma;
struct list_head queue;
unsigned mapped : 1,
valid : 1;
};
struct net2280 {
/* each pci device provides one gadget, several endpoints */
struct usb_gadget gadget;
spinlock_t lock;
struct net2280_ep ep [7];
struct usb_gadget_driver *driver;
unsigned enabled : 1,
protocol_stall : 1,
softconnect : 1,
got_irq : 1,
region : 1;
u16 chiprev;
/* pci state used to access those endpoints */
struct pci_dev *pdev;
struct net2280_regs __iomem *regs;
struct net2280_usb_regs __iomem *usb;
struct net2280_pci_regs __iomem *pci;
struct net2280_dma_regs __iomem *dma;
struct net2280_dep_regs __iomem *dep;
struct net2280_ep_regs __iomem *epregs;
struct pci_pool *requests;
// statistics...
};
static inline void set_halt (struct net2280_ep *ep)
{
/* ep0 and bulk/intr endpoints */
writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
/* set NAK_OUT for erratum 0114 */
| ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS)
| (1 << SET_ENDPOINT_HALT)
, &ep->regs->ep_rsp);
}
static inline void clear_halt (struct net2280_ep *ep)
{
/* ep0 and bulk/intr endpoints */
writel ( (1 << CLEAR_ENDPOINT_HALT)
| (1 << CLEAR_ENDPOINT_TOGGLE)
/* unless the gadget driver left a short packet in the
* fifo, this reverses the erratum 0114 workaround.
*/
| ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS)
, &ep->regs->ep_rsp);
}
#ifdef USE_RDK_LEDS
static inline void net2280_led_init (struct net2280 *dev)
{
/* LED3 (green) is on during USB activity. note erratum 0113. */
writel ((1 << GPIO3_LED_SELECT)
| (1 << GPIO3_OUTPUT_ENABLE)
| (1 << GPIO2_OUTPUT_ENABLE)
| (1 << GPIO1_OUTPUT_ENABLE)
| (1 << GPIO0_OUTPUT_ENABLE)
, &dev->regs->gpioctl);
}
/* indicate speed with bi-color LED 0/1 */
static inline
void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed)
{
u32 val = readl (&dev->regs->gpioctl);
switch (speed) {
case USB_SPEED_HIGH: /* green */
val &= ~(1 << GPIO0_DATA);
val |= (1 << GPIO1_DATA);
break;
case USB_SPEED_FULL: /* red */
val &= ~(1 << GPIO1_DATA);
val |= (1 << GPIO0_DATA);
break;
default: /* (off/black) */
val &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA));
break;
}
writel (val, &dev->regs->gpioctl);
}
/* indicate power with LED 2 */
static inline void net2280_led_active (struct net2280 *dev, int is_active)
{
u32 val = readl (&dev->regs->gpioctl);
// FIXME this LED never seems to turn on.
if (is_active)
val |= GPIO2_DATA;
else
val &= ~GPIO2_DATA;
writel (val, &dev->regs->gpioctl);
}
static inline void net2280_led_shutdown (struct net2280 *dev)
{
/* turn off all four GPIO*_DATA bits */
writel (readl (&dev->regs->gpioctl) & ~0x0f,
&dev->regs->gpioctl);
}
#else
#define net2280_led_init(dev) do { } while (0)
#define net2280_led_speed(dev, speed) do { } while (0)
#define net2280_led_shutdown(dev) do { } while (0)
#endif
/*-------------------------------------------------------------------------*/
#define xprintk(dev,level,fmt,args...) \
printk(level "%s %s: " fmt , driver_name , \
pci_name(dev->pdev) , ## args)
#ifdef DEBUG
#undef DEBUG
#define DEBUG(dev,fmt,args...) \
xprintk(dev , KERN_DEBUG , fmt , ## args)
#else
#define DEBUG(dev,fmt,args...) \
do { } while (0)
#endif /* DEBUG */
#ifdef VERBOSE
#define VDEBUG DEBUG
#else
#define VDEBUG(dev,fmt,args...) \
do { } while (0)
#endif /* VERBOSE */
#define ERROR(dev,fmt,args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
#define WARN(dev,fmt,args...) \
xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
/*-------------------------------------------------------------------------*/
static inline void start_out_naking (struct net2280_ep *ep)
{
/* NOTE: hardware races lurk here, and PING protocol issues */
writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
/* synch with device */
readl (&ep->regs->ep_rsp);
}
#ifdef DEBUG
static inline void assert_out_naking (struct net2280_ep *ep, const char *where)
{
u32 tmp = readl (&ep->regs->ep_stat);
if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
DEBUG (ep->dev, "%s %s %08x !NAK\n",
ep->ep.name, where, tmp);
writel ((1 << SET_NAK_OUT_PACKETS),
&ep->regs->ep_rsp);
}
}
#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__FUNCTION__)
#else
#define ASSERT_OUT_NAKING(ep) do {} while (0)
#endif
static inline void stop_out_naking (struct net2280_ep *ep)
{
u32 tmp;
tmp = readl (&ep->regs->ep_stat);
if ((tmp & (1 << NAK_OUT_PACKETS)) != 0)
writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
}
#endif /* __KERNEL__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,208 @@
/*
* omap_udc.h -- for omap 3.2 udc, with OTG support
*
* 2004 (C) Texas Instruments, Inc.
* 2004 (C) David Brownell
*/
/*
* USB device/endpoint management registers
*/
#define UDC_REG(offset) __REG16(UDC_BASE + (offset))
#define UDC_REV_REG UDC_REG(0x0) /* Revision */
#define UDC_EP_NUM_REG UDC_REG(0x4) /* Which endpoint */
# define UDC_SETUP_SEL (1 << 6)
# define UDC_EP_SEL (1 << 5)
# define UDC_EP_DIR (1 << 4)
/* low 4 bits for endpoint number */
#define UDC_DATA_REG UDC_REG(0x08) /* Endpoint FIFO */
#define UDC_CTRL_REG UDC_REG(0x0C) /* Endpoint control */
# define UDC_CLR_HALT (1 << 7)
# define UDC_SET_HALT (1 << 6)
# define UDC_SET_FIFO_EN (1 << 2)
# define UDC_CLR_EP (1 << 1)
# define UDC_RESET_EP (1 << 0)
#define UDC_STAT_FLG_REG UDC_REG(0x10) /* Endpoint status */
# define UDC_NO_RXPACKET (1 << 15)
# define UDC_MISS_IN (1 << 14)
# define UDC_DATA_FLUSH (1 << 13)
# define UDC_ISO_ERR (1 << 12)
# define UDC_ISO_FIFO_EMPTY (1 << 9)
# define UDC_ISO_FIFO_FULL (1 << 8)
# define UDC_EP_HALTED (1 << 6)
# define UDC_STALL (1 << 5)
# define UDC_NAK (1 << 4)
# define UDC_ACK (1 << 3)
# define UDC_FIFO_EN (1 << 2)
# define UDC_NON_ISO_FIFO_EMPTY (1 << 1)
# define UDC_NON_ISO_FIFO_FULL (1 << 0)
#define UDC_RXFSTAT_REG UDC_REG(0x14) /* OUT bytecount */
#define UDC_SYSCON1_REG UDC_REG(0x18) /* System config 1 */
# define UDC_CFG_LOCK (1 << 8)
# define UDC_DATA_ENDIAN (1 << 7)
# define UDC_DMA_ENDIAN (1 << 6)
# define UDC_NAK_EN (1 << 4)
# define UDC_AUTODECODE_DIS (1 << 3)
# define UDC_SELF_PWR (1 << 2)
# define UDC_SOFF_DIS (1 << 1)
# define UDC_PULLUP_EN (1 << 0)
#define UDC_SYSCON2_REG UDC_REG(0x1C) /* System config 2 */
# define UDC_RMT_WKP (1 << 6)
# define UDC_STALL_CMD (1 << 5)
# define UDC_DEV_CFG (1 << 3)
# define UDC_CLR_CFG (1 << 2)
#define UDC_DEVSTAT_REG UDC_REG(0x20) /* Device status */
# define UDC_B_HNP_ENABLE (1 << 9)
# define UDC_A_HNP_SUPPORT (1 << 8)
# define UDC_A_ALT_HNP_SUPPORT (1 << 7)
# define UDC_R_WK_OK (1 << 6)
# define UDC_USB_RESET (1 << 5)
# define UDC_SUS (1 << 4)
# define UDC_CFG (1 << 3)
# define UDC_ADD (1 << 2)
# define UDC_DEF (1 << 1)
# define UDC_ATT (1 << 0)
#define UDC_SOF_REG UDC_REG(0x24) /* Start of frame */
# define UDC_FT_LOCK (1 << 12)
# define UDC_TS_OK (1 << 11)
# define UDC_TS 0x03ff
#define UDC_IRQ_EN_REG UDC_REG(0x28) /* Interrupt enable */
# define UDC_SOF_IE (1 << 7)
# define UDC_EPN_RX_IE (1 << 5)
# define UDC_EPN_TX_IE (1 << 4)
# define UDC_DS_CHG_IE (1 << 3)
# define UDC_EP0_IE (1 << 0)
#define UDC_DMA_IRQ_EN_REG UDC_REG(0x2C) /* DMA irq enable */
/* rx/tx dma channels numbered 1-3 not 0-2 */
# define UDC_TX_DONE_IE(n) (1 << (4 * (n) - 2))
# define UDC_RX_CNT_IE(n) (1 << (4 * (n) - 3))
# define UDC_RX_EOT_IE(n) (1 << (4 * (n) - 4))
#define UDC_IRQ_SRC_REG UDC_REG(0x30) /* Interrupt source */
# define UDC_TXN_DONE (1 << 10)
# define UDC_RXN_CNT (1 << 9)
# define UDC_RXN_EOT (1 << 8)
# define UDC_SOF (1 << 7)
# define UDC_EPN_RX (1 << 5)
# define UDC_EPN_TX (1 << 4)
# define UDC_DS_CHG (1 << 3)
# define UDC_SETUP (1 << 2)
# define UDC_EP0_RX (1 << 1)
# define UDC_EP0_TX (1 << 0)
# define UDC_IRQ_SRC_MASK 0x7bf
#define UDC_EPN_STAT_REG UDC_REG(0x34) /* EP irq status */
#define UDC_DMAN_STAT_REG UDC_REG(0x38) /* DMA irq status */
# define UDC_DMA_RX_SB (1 << 12)
# define UDC_DMA_RX_SRC(x) (((x)>>8) & 0xf)
# define UDC_DMA_TX_SRC(x) (((x)>>0) & 0xf)
/* DMA configuration registers: up to three channels in each direction. */
#define UDC_RXDMA_CFG_REG UDC_REG(0x40) /* 3 eps for RX DMA */
#define UDC_TXDMA_CFG_REG UDC_REG(0x44) /* 3 eps for TX DMA */
#define UDC_DATA_DMA_REG UDC_REG(0x48) /* rx/tx fifo addr */
/* rx/tx dma control, numbering channels 1-3 not 0-2 */
#define UDC_TXDMA_REG(chan) UDC_REG(0x50 - 4 + 4 * (chan))
# define UDC_TXN_EOT (1 << 15) /* bytes vs packets */
# define UDC_TXN_START (1 << 14) /* start transfer */
# define UDC_TXN_TSC 0x03ff /* units in xfer */
#define UDC_RXDMA_REG(chan) UDC_REG(0x60 - 4 + 4 * (chan))
# define UDC_RXN_STOP (1 << 15) /* enable EOT irq */
# define UDC_RXN_TC 0x00ff /* packets in xfer */
/*
* Endpoint configuration registers (used before CFG_LOCK is set)
* UDC_EP_TX_REG(0) is unused
*/
#define UDC_EP_RX_REG(endpoint) UDC_REG(0x80 + (endpoint)*4)
# define UDC_EPN_RX_VALID (1 << 15)
# define UDC_EPN_RX_DB (1 << 14)
/* buffer size in bits 13, 12 */
# define UDC_EPN_RX_ISO (1 << 11)
/* buffer pointer in low 11 bits */
#define UDC_EP_TX_REG(endpoint) UDC_REG(0xc0 + (endpoint)*4)
/* same bitfields as in RX_REG */
/*-------------------------------------------------------------------------*/
struct omap_req {
struct usb_request req;
struct list_head queue;
unsigned dma_bytes;
unsigned mapped:1;
};
struct omap_ep {
struct usb_ep ep;
struct list_head queue;
unsigned long irqs;
struct list_head iso;
const struct usb_endpoint_descriptor *desc;
char name[14];
u16 maxpacket;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned double_buf:1;
unsigned stopped:1;
unsigned fnf:1;
unsigned has_dma:1;
u8 ackwait;
u8 dma_channel;
u16 dma_counter;
int lch;
struct omap_udc *udc;
struct timer_list timer;
};
struct omap_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
spinlock_t lock;
struct omap_ep ep[32];
u16 devstat;
struct otg_transceiver *transceiver;
struct list_head iso;
unsigned softconnect:1;
unsigned vbus_active:1;
unsigned ep0_pending:1;
unsigned ep0_in:1;
unsigned ep0_set_config:1;
unsigned ep0_reset_config:1;
unsigned ep0_setup:1;
struct completion *done;
};
/*-------------------------------------------------------------------------*/
#ifdef DEBUG
#define DBG(stuff...) printk(KERN_DEBUG "udc: " stuff)
#else
#define DBG(stuff...) do{}while(0)
#endif
#ifdef VERBOSE
# define VDBG DBG
#else
# define VDBG(stuff...) do{}while(0)
#endif
#define ERR(stuff...) printk(KERN_ERR "udc: " stuff)
#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
/*-------------------------------------------------------------------------*/
#define MOD_CONF_CTRL_0_REG __REG32(MOD_CONF_CTRL_0)
#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */
#define FUNC_MUX_CTRL_0_REG __REG32(FUNC_MUX_CTRL_0)
#define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */
#define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */
#define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f)
#define HMC_1610 (OTG_SYSCON_2_REG & 0x3f)
#define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,320 @@
/*
* linux/drivers/usb/gadget/pxa2xx_udc.h
* Intel PXA2xx on-chip full speed USB device controller
*
* Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
* Copyright (C) 2003 David Brownell
*
*
* 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
*/
#ifndef __LINUX_USB_GADGET_PXA2XX_H
#define __LINUX_USB_GADGET_PXA2XX_H
#include <linux/types.h>
/*-------------------------------------------------------------------------*/
/* pxa2xx has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
#define UFNRH_SIR (1 << 7) /* SOF interrupt request */
#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */
#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */
#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */
#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */
/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
#define UDCCFR UDC_RES2 /* UDC Control Function Register */
#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */
#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */
/* latest pxa255 errata define new "must be one" bits in UDCCFR */
#define UDCCFR_MB1 (0xff & ~(UDCCFR_AREN|UDCCFR_ACM))
/*-------------------------------------------------------------------------*/
struct pxa2xx_udc;
struct pxa2xx_ep {
struct usb_ep ep;
struct pxa2xx_udc *dev;
const struct usb_endpoint_descriptor *desc;
struct list_head queue;
unsigned long pio_irqs;
unsigned long dma_irqs;
short dma;
unsigned short fifo_size;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned stopped : 1;
unsigned dma_fixup : 1;
/* UDCCS = UDC Control/Status for this EP
* UBCR = UDC Byte Count Remaining (contents of OUT fifo)
* UDDR = UDC Endpoint Data Register (the fifo)
* DRCM = DMA Request Channel Map
*/
volatile u32 *reg_udccs;
volatile u32 *reg_ubcr;
volatile u32 *reg_uddr;
#ifdef USE_DMA
volatile u32 *reg_drcmr;
#define drcmr(n) .reg_drcmr = & DRCMR ## n ,
#else
#define drcmr(n)
#endif
};
struct pxa2xx_request {
struct usb_request req;
struct list_head queue;
};
enum ep0_state {
EP0_IDLE,
EP0_IN_DATA_PHASE,
EP0_OUT_DATA_PHASE,
EP0_END_XFER,
EP0_STALL,
};
#define EP0_FIFO_SIZE ((unsigned)16)
#define BULK_FIFO_SIZE ((unsigned)64)
#define ISO_FIFO_SIZE ((unsigned)256)
#define INT_FIFO_SIZE ((unsigned)8)
struct udc_stats {
struct ep0stats {
unsigned long ops;
unsigned long bytes;
} read, write;
unsigned long irqs;
};
#ifdef CONFIG_USB_PXA2XX_SMALL
/* when memory's tight, SMALL config saves code+data. */
#undef USE_DMA
#define PXA_UDC_NUM_ENDPOINTS 3
#endif
#ifndef PXA_UDC_NUM_ENDPOINTS
#define PXA_UDC_NUM_ENDPOINTS 16
#endif
struct pxa2xx_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
enum ep0_state ep0state;
struct udc_stats stats;
unsigned got_irq : 1,
vbus : 1,
pullup : 1,
has_cfr : 1,
req_pending : 1,
req_std : 1,
req_config : 1;
#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
struct timer_list timer;
struct device *dev;
struct pxa2xx_udc_mach_info *mach;
u64 dma_mask;
struct pxa2xx_ep ep [PXA_UDC_NUM_ENDPOINTS];
};
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_ARCH_LUBBOCK
#include <asm/arch/lubbock.h>
/* lubbock can also report usb connect/disconnect irqs */
#ifdef DEBUG
#define HEX_DISPLAY(n) if (machine_is_lubbock()) { LUB_HEXLED = (n); }
#endif
#endif
/*-------------------------------------------------------------------------*/
/* LEDs are only for debug */
#ifndef HEX_DISPLAY
#define HEX_DISPLAY(n) do {} while(0)
#endif
#ifdef DEBUG
#include <asm/leds.h>
#define LED_CONNECTED_ON leds_event(led_green_on)
#define LED_CONNECTED_OFF do { \
leds_event(led_green_off); \
HEX_DISPLAY(0); \
} while(0)
#endif
#ifndef LED_CONNECTED_ON
#define LED_CONNECTED_ON do {} while(0)
#define LED_CONNECTED_OFF do {} while(0)
#endif
/*-------------------------------------------------------------------------*/
static struct pxa2xx_udc *the_controller;
/* one GPIO should be used to detect host disconnect */
static inline int is_usb_connected(void)
{
if (!the_controller->mach->udc_is_connected)
return 1;
return the_controller->mach->udc_is_connected();
}
/* one GPIO should force the host to see this device (or not) */
static inline void make_usb_disappear(void)
{
if (!the_controller->mach->udc_command)
return;
the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
}
static inline void let_usb_appear(void)
{
if (!the_controller->mach->udc_command)
return;
the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
}
/*-------------------------------------------------------------------------*/
/*
* Debugging support vanishes in non-debug builds. DBG_NORMAL should be
* mostly silent during normal use/testing, with no timing side-effects.
*/
#define DBG_NORMAL 1 /* error paths, device state transitions */
#define DBG_VERBOSE 2 /* add some success path trace info */
#define DBG_NOISY 3 /* ... even more: request level */
#define DBG_VERY_NOISY 4 /* ... even more: packet level */
#ifdef DEBUG
static const char *state_name[] = {
"EP0_IDLE",
"EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
"EP0_END_XFER", "EP0_STALL"
};
#define DMSG(stuff...) printk(KERN_DEBUG "udc: " stuff)
#ifdef VERBOSE
# define UDC_DEBUG DBG_VERBOSE
#else
# define UDC_DEBUG DBG_NORMAL
#endif
static void __attribute__ ((__unused__))
dump_udccr(const char *label)
{
u32 udccr = UDCCR;
DMSG("%s %02X =%s%s%s%s%s%s%s%s\n",
label, udccr,
(udccr & UDCCR_REM) ? " rem" : "",
(udccr & UDCCR_RSTIR) ? " rstir" : "",
(udccr & UDCCR_SRM) ? " srm" : "",
(udccr & UDCCR_SUSIR) ? " susir" : "",
(udccr & UDCCR_RESIR) ? " resir" : "",
(udccr & UDCCR_RSM) ? " rsm" : "",
(udccr & UDCCR_UDA) ? " uda" : "",
(udccr & UDCCR_UDE) ? " ude" : "");
}
static void __attribute__ ((__unused__))
dump_udccs0(const char *label)
{
u32 udccs0 = UDCCS0;
DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n",
label, state_name[the_controller->ep0state], udccs0,
(udccs0 & UDCCS0_SA) ? " sa" : "",
(udccs0 & UDCCS0_RNE) ? " rne" : "",
(udccs0 & UDCCS0_FST) ? " fst" : "",
(udccs0 & UDCCS0_SST) ? " sst" : "",
(udccs0 & UDCCS0_DRWF) ? " dwrf" : "",
(udccs0 & UDCCS0_FTF) ? " ftf" : "",
(udccs0 & UDCCS0_IPR) ? " ipr" : "",
(udccs0 & UDCCS0_OPR) ? " opr" : "");
}
static void __attribute__ ((__unused__))
dump_state(struct pxa2xx_udc *dev)
{
u32 tmp;
unsigned i;
DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
is_usb_connected() ? "host " : "disconnected",
state_name[dev->ep0state],
UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
dump_udccr("udccr");
if (dev->has_cfr) {
tmp = UDCCFR;
DMSG("udccfr %02X =%s%s\n", tmp,
(tmp & UDCCFR_AREN) ? " aren" : "",
(tmp & UDCCFR_ACM) ? " acm" : "");
}
if (!dev->driver) {
DMSG("no gadget driver bound\n");
return;
} else
DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
if (!is_usb_connected())
return;
dump_udccs0 ("udccs0");
DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n",
dev->stats.write.bytes, dev->stats.write.ops,
dev->stats.read.bytes, dev->stats.read.ops);
for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
if (dev->ep [i].desc == 0)
continue;
DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs);
}
}
#else
#define DMSG(stuff...) do{}while(0)
#define dump_udccr(x) do{}while(0)
#define dump_udccs0(x) do{}while(0)
#define dump_state(x) do{}while(0)
#define UDC_DEBUG ((unsigned)0)
#endif
#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
#endif /* __LINUX_USB_GADGET_PXA2XX_H */

1428
drivers/usb/gadget/rndis.c Normal file

File diff suppressed because it is too large Load diff

348
drivers/usb/gadget/rndis.h Normal file
View file

@ -0,0 +1,348 @@
/*
* RNDIS Definitions for Remote NDIS
*
* Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $
*
* Authors: Benedikt Spranger, Pengutronix
* Robert Schwebel, Pengutronix
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2, as published by the Free Software Foundation.
*
* This software was originally developed in conformance with
* Microsoft's Remote NDIS Specification License Agreement.
*/
#ifndef _LINUX_RNDIS_H
#define _LINUX_RNDIS_H
#include "ndis.h"
#define RNDIS_MAXIMUM_FRAME_SIZE 1518
#define RNDIS_MAX_TOTAL_SIZE 1558
/* Remote NDIS Versions */
#define RNDIS_MAJOR_VERSION 1
#define RNDIS_MINOR_VERSION 0
/* Status Values */
#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */
#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */
#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */
#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */
#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */
#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */
/* For all not specified status messages:
* RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx
*/
/* Message Set for Connectionless (802.3) Devices */
#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */
#define REMOTE_NDIS_HALT_MSG 0x00000003U
#define REMOTE_NDIS_QUERY_MSG 0x00000004U
#define REMOTE_NDIS_SET_MSG 0x00000005U
#define REMOTE_NDIS_RESET_MSG 0x00000006U
#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U
#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U
/* Message completion */
#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U
#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U
#define REMOTE_NDIS_SET_CMPLT 0x80000005U
#define REMOTE_NDIS_RESET_CMPLT 0x80000006U
#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U
/* Device Flags */
#define RNDIS_DF_CONNECTIONLESS 0x00000001U
#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U
#define RNDIS_MEDIUM_802_3 0x00000000U
/* from drivers/net/sk98lin/h/skgepnmi.h */
#define OID_PNP_CAPABILITIES 0xFD010100
#define OID_PNP_SET_POWER 0xFD010101
#define OID_PNP_QUERY_POWER 0xFD010102
#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103
#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104
#define OID_PNP_ENABLE_WAKE_UP 0xFD010106
/* supported OIDs */
static const u32 oid_supported_list [] =
{
/* the general stuff */
OID_GEN_SUPPORTED_LIST,
OID_GEN_HARDWARE_STATUS,
OID_GEN_MEDIA_SUPPORTED,
OID_GEN_MEDIA_IN_USE,
OID_GEN_MAXIMUM_FRAME_SIZE,
OID_GEN_LINK_SPEED,
OID_GEN_TRANSMIT_BLOCK_SIZE,
OID_GEN_RECEIVE_BLOCK_SIZE,
OID_GEN_VENDOR_ID,
OID_GEN_VENDOR_DESCRIPTION,
OID_GEN_VENDOR_DRIVER_VERSION,
OID_GEN_CURRENT_PACKET_FILTER,
OID_GEN_MAXIMUM_TOTAL_SIZE,
OID_GEN_MEDIA_CONNECT_STATUS,
OID_GEN_PHYSICAL_MEDIUM,
#if 0
OID_GEN_RNDIS_CONFIG_PARAMETER,
#endif
/* the statistical stuff */
OID_GEN_XMIT_OK,
OID_GEN_RCV_OK,
OID_GEN_XMIT_ERROR,
OID_GEN_RCV_ERROR,
OID_GEN_RCV_NO_BUFFER,
#ifdef RNDIS_OPTIONAL_STATS
OID_GEN_DIRECTED_BYTES_XMIT,
OID_GEN_DIRECTED_FRAMES_XMIT,
OID_GEN_MULTICAST_BYTES_XMIT,
OID_GEN_MULTICAST_FRAMES_XMIT,
OID_GEN_BROADCAST_BYTES_XMIT,
OID_GEN_BROADCAST_FRAMES_XMIT,
OID_GEN_DIRECTED_BYTES_RCV,
OID_GEN_DIRECTED_FRAMES_RCV,
OID_GEN_MULTICAST_BYTES_RCV,
OID_GEN_MULTICAST_FRAMES_RCV,
OID_GEN_BROADCAST_BYTES_RCV,
OID_GEN_BROADCAST_FRAMES_RCV,
OID_GEN_RCV_CRC_ERROR,
OID_GEN_TRANSMIT_QUEUE_LENGTH,
#endif /* RNDIS_OPTIONAL_STATS */
/* mandatory 802.3 */
/* the general stuff */
OID_802_3_PERMANENT_ADDRESS,
OID_802_3_CURRENT_ADDRESS,
OID_802_3_MULTICAST_LIST,
OID_802_3_MAC_OPTIONS,
OID_802_3_MAXIMUM_LIST_SIZE,
/* the statistical stuff */
OID_802_3_RCV_ERROR_ALIGNMENT,
OID_802_3_XMIT_ONE_COLLISION,
OID_802_3_XMIT_MORE_COLLISIONS,
#ifdef RNDIS_OPTIONAL_STATS
OID_802_3_XMIT_DEFERRED,
OID_802_3_XMIT_MAX_COLLISIONS,
OID_802_3_RCV_OVERRUN,
OID_802_3_XMIT_UNDERRUN,
OID_802_3_XMIT_HEARTBEAT_FAILURE,
OID_802_3_XMIT_TIMES_CRS_LOST,
OID_802_3_XMIT_LATE_COLLISIONS,
#endif /* RNDIS_OPTIONAL_STATS */
#ifdef RNDIS_PM
/* PM and wakeup are mandatory for USB: */
/* power management */
OID_PNP_CAPABILITIES,
OID_PNP_QUERY_POWER,
OID_PNP_SET_POWER,
/* wake up host */
OID_PNP_ENABLE_WAKE_UP,
OID_PNP_ADD_WAKE_UP_PATTERN,
OID_PNP_REMOVE_WAKE_UP_PATTERN,
#endif
};
typedef struct rndis_init_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 MajorVersion;
__le32 MinorVersion;
__le32 MaxTransferSize;
} rndis_init_msg_type;
typedef struct rndis_init_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
__le32 MajorVersion;
__le32 MinorVersion;
__le32 DeviceFlags;
__le32 Medium;
__le32 MaxPacketsPerTransfer;
__le32 MaxTransferSize;
__le32 PacketAlignmentFactor;
__le32 AFListOffset;
__le32 AFListSize;
} rndis_init_cmplt_type;
typedef struct rndis_halt_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
} rndis_halt_msg_type;
typedef struct rndis_query_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 OID;
__le32 InformationBufferLength;
__le32 InformationBufferOffset;
__le32 DeviceVcHandle;
} rndis_query_msg_type;
typedef struct rndis_query_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
__le32 InformationBufferLength;
__le32 InformationBufferOffset;
} rndis_query_cmplt_type;
typedef struct rndis_set_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 OID;
__le32 InformationBufferLength;
__le32 InformationBufferOffset;
__le32 DeviceVcHandle;
} rndis_set_msg_type;
typedef struct rndis_set_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
} rndis_set_cmplt_type;
typedef struct rndis_reset_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 Reserved;
} rndis_reset_msg_type;
typedef struct rndis_reset_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 Status;
__le32 AddressingReset;
} rndis_reset_cmplt_type;
typedef struct rndis_indicate_status_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 Status;
__le32 StatusBufferLength;
__le32 StatusBufferOffset;
} rndis_indicate_status_msg_type;
typedef struct rndis_keepalive_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
} rndis_keepalive_msg_type;
typedef struct rndis_keepalive_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
} rndis_keepalive_cmplt_type;
struct rndis_packet_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 DataOffset;
__le32 DataLength;
__le32 OOBDataOffset;
__le32 OOBDataLength;
__le32 NumOOBDataElements;
__le32 PerPacketInfoOffset;
__le32 PerPacketInfoLength;
__le32 VcHandle;
__le32 Reserved;
};
struct rndis_config_parameter
{
__le32 ParameterNameOffset;
__le32 ParameterNameLength;
__le32 ParameterType;
__le32 ParameterValueOffset;
__le32 ParameterValueLength;
};
/* implementation specific */
enum rndis_state
{
RNDIS_UNINITIALIZED,
RNDIS_INITIALIZED,
RNDIS_DATA_INITIALIZED,
};
typedef struct rndis_resp_t
{
struct list_head list;
u8 *buf;
u32 length;
int send;
} rndis_resp_t;
typedef struct rndis_params
{
u8 confignr;
int used;
enum rndis_state state;
u32 filter;
u32 medium;
u32 speed;
u32 media_state;
const u8 *host_mac;
struct net_device *dev;
struct net_device_stats *stats;
u32 vendorID;
const char *vendorDescr;
int (*ack) (struct net_device *);
struct list_head resp_queue;
} rndis_params;
/* RNDIS Message parser and other useless functions */
int rndis_msg_parser (u8 configNr, u8 *buf);
int rndis_register (int (*rndis_control_ack) (struct net_device *));
void rndis_deregister (int configNr);
int rndis_set_param_dev (u8 configNr, struct net_device *dev,
struct net_device_stats *stats);
int rndis_set_param_vendor (u8 configNr, u32 vendorID,
const char *vendorDescr);
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
void rndis_add_hdr (struct sk_buff *skb);
int rndis_rm_hdr (u8 *buf, u32 *length);
u8 *rndis_get_next_response (int configNr, u32 *length);
void rndis_free_response (int configNr, u8 *buf);
int rndis_signal_connect (int configNr);
int rndis_signal_disconnect (int configNr);
int rndis_state (int configNr);
extern void rndis_set_host_mac (int configNr, const u8 *addr);
int __init rndis_init (void);
void rndis_exit (void);
#endif /* _LINUX_RNDIS_H */

2436
drivers/usb/gadget/serial.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,136 @@
/*
* Copyright (C) 2003 David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
#include <asm/unaligned.h>
static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
{
int count = 0;
u8 c;
u16 uchar;
/* this insists on correct encodings, though not minimal ones.
* BUT it currently rejects legit 4-byte UTF-8 code points,
* which need surrogate pairs. (Unicode 3.1 can use them.)
*/
while (len != 0 && (c = (u8) *s++) != 0) {
if (unlikely(c & 0x80)) {
// 2-byte sequence:
// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
if ((c & 0xe0) == 0xc0) {
uchar = (c & 0x1f) << 6;
c = (u8) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c;
// 3-byte sequence (most CJKV characters):
// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
} else if ((c & 0xf0) == 0xe0) {
uchar = (c & 0x0f) << 12;
c = (u8) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c << 6;
c = (u8) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c;
/* no bogus surrogates */
if (0xd800 <= uchar && uchar <= 0xdfff)
goto fail;
// 4-byte sequence (surrogate pairs, currently rare):
// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
// = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
// (uuuuu = wwww + 1)
// FIXME accept the surrogate code points (only)
} else
goto fail;
} else
uchar = c;
put_unaligned (cpu_to_le16 (uchar), cp++);
count++;
len--;
}
return count;
fail:
return -1;
}
/**
* usb_gadget_get_string - fill out a string descriptor
* @table: of c strings encoded using UTF-8
* @id: string id, from low byte of wValue in get string descriptor
* @buf: at least 256 bytes
*
* Finds the UTF-8 string matching the ID, and converts it into a
* string descriptor in utf16-le.
* Returns length of descriptor (always even) or negative errno
*
* If your driver needs stings in multiple languages, you'll probably
* "switch (wIndex) { ... }" in your ep0 string descriptor logic,
* using this routine after choosing which set of UTF-8 strings to use.
* Note that US-ASCII is a strict subset of UTF-8; any string bytes with
* the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
* characters (which are also widely used in C strings).
*/
int
usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
{
struct usb_string *s;
int len;
/* descriptor 0 has the language id */
if (id == 0) {
buf [0] = 4;
buf [1] = USB_DT_STRING;
buf [2] = (u8) table->language;
buf [3] = (u8) (table->language >> 8);
return 4;
}
for (s = table->strings; s && s->s; s++)
if (s->id == id)
break;
/* unrecognized: stall. */
if (!s || !s->s)
return -EINVAL;
/* string descriptors have length, tag, then UTF16-LE text */
len = min ((size_t) 126, strlen (s->s));
memset (buf + 2, 0, 2 * len); /* zero all the bytes */
len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len);
if (len < 0)
return -EINVAL;
buf [0] = (len + 1) * 2;
buf [1] = USB_DT_STRING;
return buf [0];
}

1357
drivers/usb/gadget/zero.c Normal file

File diff suppressed because it is too large Load diff

126
drivers/usb/host/Kconfig Normal file
View file

@ -0,0 +1,126 @@
#
# USB Host Controller Drivers
#
comment "USB Host Controller Drivers"
depends on USB
config USB_EHCI_HCD
tristate "EHCI HCD (USB 2.0) support"
depends on USB && PCI
---help---
The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
"high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
If your USB host controller supports USB 2.0, you will likely want to
configure this Host Controller Driver. At this writing, the primary
implementation of EHCI is a chip from NEC, widely available in add-on
PCI cards, but implementations are in the works from other vendors
including Intel and Philips. Motherboard support is appearing.
EHCI controllers are packaged with "companion" host controllers (OHCI
or UHCI) to handle USB 1.1 devices connected to root hub ports. Ports
will connect to EHCI if it the device is high speed, otherwise they
connect to a companion controller. If you configure EHCI, you should
probably configure the OHCI (for NEC and some other vendors) USB Host
Controller Driver or UHCI (for Via motherboards) Host Controller
Driver too.
You may want to read <file:Documentation/usb/ehci.txt>.
To compile this driver as a module, choose M here: the
module will be called ehci-hcd.
config USB_EHCI_SPLIT_ISO
bool "Full speed ISO transactions (EXPERIMENTAL)"
depends on USB_EHCI_HCD && EXPERIMENTAL
default n
---help---
This code is new and hasn't been used with many different
EHCI or USB 2.0 transaction translator implementations.
It should work for ISO-OUT transfers, like audio.
config USB_EHCI_ROOT_HUB_TT
bool "Root Hub Transaction Translators (EXPERIMENTAL)"
depends on USB_EHCI_HCD && EXPERIMENTAL
---help---
Some EHCI chips have vendor-specific extensions to integrate
transaction translators, so that no OHCI or UHCI companion
controller is needed. It's safe to say "y" even if your
controller doesn't support this feature.
This supports the EHCI implementation from TransDimension Inc.
config USB_OHCI_HCD
tristate "OHCI HCD support"
depends on USB && USB_ARCH_HAS_OHCI
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
---help---
The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's
UHCI specification. If your USB host controller follows the OHCI spec,
say Y. On most non-x86 systems, and on x86 hardware that's not using a
USB controller from Intel or VIA, this is appropriate. If your host
controller doesn't use PCI, this is probably appropriate. For a PCI
based system where you're not sure, the "lspci -v" entry will list the
right "prog-if" for your USB controller(s): EHCI, OHCI, or UHCI.
To compile this driver as a module, choose M here: the
module will be called ohci-hcd.
config USB_OHCI_HCD_PPC_SOC
bool "OHCI support for on-chip PPC USB controller"
depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx)
default y
select USB_OHCI_BIG_ENDIAN
---help---
Enables support for the USB controller on the MPC52xx or
STB03xxx processor chip. If unsure, say Y.
config USB_OHCI_HCD_PCI
bool "OHCI support for PCI-bus USB controllers"
depends on USB_OHCI_HCD && PCI && (STB03xxx || PPC_MPC52xx)
default y
select USB_OHCI_LITTLE_ENDIAN
---help---
Enables support for PCI-bus plug-in USB controller cards.
If unsure, say Y.
config USB_OHCI_BIG_ENDIAN
bool
depends on USB_OHCI_HCD
default n
config USB_OHCI_LITTLE_ENDIAN
bool
depends on USB_OHCI_HCD
default n if STB03xxx || PPC_MPC52xx
default y
config USB_UHCI_HCD
tristate "UHCI HCD (most Intel and VIA) support"
depends on USB && PCI
---help---
The Universal Host Controller Interface is a standard by Intel for
accessing the USB hardware in the PC (which is also called the USB
host controller). If your USB host controller conforms to this
standard, you may want to say Y, but see below. All recent boards
with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX,
i810, i820) conform to this standard. Also all VIA PCI chipsets
(like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro
133). If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called uhci-hcd.
config USB_SL811_HCD
tristate "SL811HS HCD support"
depends on USB
default N
help
The SL811HS is a single-port USB controller that supports either
host side or peripheral side roles. Enable this option if your
board has this chip, and you want to use it as a host controller.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called sl811-hcd.

10
drivers/usb/host/Makefile Normal file
View file

@ -0,0 +1,10 @@
#
# Makefile for USB Host Controller Driver
# framework and drivers
#
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o

755
drivers/usb/host/ehci-dbg.c Normal file
View file

@ -0,0 +1,755 @@
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* this file is part of ehci-hcd.c */
#define ehci_dbg(ehci, fmt, args...) \
dev_dbg (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
#define ehci_err(ehci, fmt, args...) \
dev_err (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
#define ehci_info(ehci, fmt, args...) \
dev_info (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
#define ehci_warn(ehci, fmt, args...) \
dev_warn (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
#ifdef EHCI_VERBOSE_DEBUG
# define vdbg dbg
# define ehci_vdbg ehci_dbg
#else
# define vdbg(fmt,args...) do { } while (0)
# define ehci_vdbg(ehci, fmt, args...) do { } while (0)
#endif
#ifdef DEBUG
/* check the values in the HCSPARAMS register
* (host controller _Structural_ parameters)
* see EHCI spec, Table 2-4 for each value
*/
static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
{
u32 params = readl (&ehci->caps->hcs_params);
ehci_dbg (ehci,
"%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n",
label, params,
HCS_DEBUG_PORT (params),
HCS_INDICATOR (params) ? " ind" : "",
HCS_N_CC (params),
HCS_N_PCC (params),
HCS_PORTROUTED (params) ? "" : " ordered",
HCS_PPC (params) ? "" : " !ppc",
HCS_N_PORTS (params)
);
/* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
if (HCS_PORTROUTED (params)) {
int i;
char buf [46], tmp [7], byte;
buf[0] = 0;
for (i = 0; i < HCS_N_PORTS (params); i++) {
// FIXME MIPS won't readb() ...
byte = readb (&ehci->caps->portroute[(i>>1)]);
sprintf(tmp, "%d ",
((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
strcat(buf, tmp);
}
ehci_dbg (ehci, "%s portroute %s\n",
label, buf);
}
}
#else
static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {}
#endif
#ifdef DEBUG
/* check the values in the HCCPARAMS register
* (host controller _Capability_ parameters)
* see EHCI Spec, Table 2-5 for each value
* */
static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
{
u32 params = readl (&ehci->caps->hcc_params);
if (HCC_ISOC_CACHE (params)) {
ehci_dbg (ehci,
"%s hcc_params %04x caching frame %s%s%s\n",
label, params,
HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
HCC_CANPARK (params) ? " park" : "",
HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
} else {
ehci_dbg (ehci,
"%s hcc_params %04x thresh %d uframes %s%s%s\n",
label,
params,
HCC_ISOC_THRES (params),
HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
HCC_CANPARK (params) ? " park" : "",
HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
}
}
#else
static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
#endif
#ifdef DEBUG
static void __attribute__((__unused__))
dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
{
ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
le32_to_cpup (&qtd->hw_next),
le32_to_cpup (&qtd->hw_alt_next),
le32_to_cpup (&qtd->hw_token),
le32_to_cpup (&qtd->hw_buf [0]));
if (qtd->hw_buf [1])
ehci_dbg (ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
le32_to_cpup (&qtd->hw_buf [1]),
le32_to_cpup (&qtd->hw_buf [2]),
le32_to_cpup (&qtd->hw_buf [3]),
le32_to_cpup (&qtd->hw_buf [4]));
}
static void __attribute__((__unused__))
dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{
ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
qh, qh->hw_next, qh->hw_info1, qh->hw_info2,
qh->hw_current);
dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
}
static void __attribute__((__unused__))
dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
{
ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
ehci_dbg (ehci,
" trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_transaction[0]),
le32_to_cpu(itd->hw_transaction[1]),
le32_to_cpu(itd->hw_transaction[2]),
le32_to_cpu(itd->hw_transaction[3]),
le32_to_cpu(itd->hw_transaction[4]),
le32_to_cpu(itd->hw_transaction[5]),
le32_to_cpu(itd->hw_transaction[6]),
le32_to_cpu(itd->hw_transaction[7]));
ehci_dbg (ehci,
" buf: %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_bufp[0]),
le32_to_cpu(itd->hw_bufp[1]),
le32_to_cpu(itd->hw_bufp[2]),
le32_to_cpu(itd->hw_bufp[3]),
le32_to_cpu(itd->hw_bufp[4]),
le32_to_cpu(itd->hw_bufp[5]),
le32_to_cpu(itd->hw_bufp[6]));
ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n",
itd->index[0], itd->index[1], itd->index[2],
itd->index[3], itd->index[4], itd->index[5],
itd->index[6], itd->index[7]);
}
static void __attribute__((__unused__))
dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
{
ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
ehci_dbg (ehci,
" addr %08x sched %04x result %08x buf %08x %08x\n",
le32_to_cpu(sitd->hw_fullspeed_ep),
le32_to_cpu(sitd->hw_uframe),
le32_to_cpu(sitd->hw_results),
le32_to_cpu(sitd->hw_buf [0]),
le32_to_cpu(sitd->hw_buf [1]));
}
static int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
{
return scnprintf (buf, len,
"%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
label, label [0] ? " " : "", status,
(status & STS_ASS) ? " Async" : "",
(status & STS_PSS) ? " Periodic" : "",
(status & STS_RECL) ? " Recl" : "",
(status & STS_HALT) ? " Halt" : "",
(status & STS_IAA) ? " IAA" : "",
(status & STS_FATAL) ? " FATAL" : "",
(status & STS_FLR) ? " FLR" : "",
(status & STS_PCD) ? " PCD" : "",
(status & STS_ERR) ? " ERR" : "",
(status & STS_INT) ? " INT" : ""
);
}
static int __attribute__((__unused__))
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
{
return scnprintf (buf, len,
"%s%sintrenable %02x%s%s%s%s%s%s",
label, label [0] ? " " : "", enable,
(enable & STS_IAA) ? " IAA" : "",
(enable & STS_FATAL) ? " FATAL" : "",
(enable & STS_FLR) ? " FLR" : "",
(enable & STS_PCD) ? " PCD" : "",
(enable & STS_ERR) ? " ERR" : "",
(enable & STS_INT) ? " INT" : ""
);
}
static const char *const fls_strings [] =
{ "1024", "512", "256", "??" };
static int
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
{
return scnprintf (buf, len,
"%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s",
label, label [0] ? " " : "", command,
(command & CMD_PARK) ? "park" : "(park)",
CMD_PARK_CNT (command),
(command >> 16) & 0x3f,
(command & CMD_LRESET) ? " LReset" : "",
(command & CMD_IAAD) ? " IAAD" : "",
(command & CMD_ASE) ? " Async" : "",
(command & CMD_PSE) ? " Periodic" : "",
fls_strings [(command >> 2) & 0x3],
(command & CMD_RESET) ? " Reset" : "",
(command & CMD_RUN) ? "RUN" : "HALT"
);
}
static int
dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
{
char *sig;
/* signaling state */
switch (status & (3 << 10)) {
case 0 << 10: sig = "se0"; break;
case 1 << 10: sig = "k"; break; /* low speed */
case 2 << 10: sig = "j"; break;
default: sig = "?"; break;
}
return scnprintf (buf, len,
"%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s",
label, label [0] ? " " : "", port, status,
(status & PORT_POWER) ? " POWER" : "",
(status & PORT_OWNER) ? " OWNER" : "",
sig,
(status & PORT_RESET) ? " RESET" : "",
(status & PORT_SUSPEND) ? " SUSPEND" : "",
(status & PORT_RESUME) ? " RESUME" : "",
(status & PORT_OCC) ? " OCC" : "",
(status & PORT_OC) ? " OC" : "",
(status & PORT_PEC) ? " PEC" : "",
(status & PORT_PE) ? " PE" : "",
(status & PORT_CSC) ? " CSC" : "",
(status & PORT_CONNECT) ? " CONNECT" : ""
);
}
#else
static inline void __attribute__((__unused__))
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{}
static inline int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
{ return 0; }
static inline int __attribute__((__unused__))
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
{ return 0; }
static inline int __attribute__((__unused__))
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
{ return 0; }
static inline int __attribute__((__unused__))
dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
{ return 0; }
#endif /* DEBUG */
/* functions have the "wrong" filename when they're output... */
#define dbg_status(ehci, label, status) { \
char _buf [80]; \
dbg_status_buf (_buf, sizeof _buf, label, status); \
ehci_dbg (ehci, "%s\n", _buf); \
}
#define dbg_cmd(ehci, label, command) { \
char _buf [80]; \
dbg_command_buf (_buf, sizeof _buf, label, command); \
ehci_dbg (ehci, "%s\n", _buf); \
}
#define dbg_port(ehci, label, port, status) { \
char _buf [80]; \
dbg_port_buf (_buf, sizeof _buf, label, port, status); \
ehci_dbg (ehci, "%s\n", _buf); \
}
/*-------------------------------------------------------------------------*/
#ifdef STUB_DEBUG_FILES
static inline void create_debug_files (struct ehci_hcd *bus) { }
static inline void remove_debug_files (struct ehci_hcd *bus) { }
#else
/* troubleshooting help: expose state in driverfs */
#define speed_char(info1) ({ char tmp; \
switch (info1 & (3 << 12)) { \
case 0 << 12: tmp = 'f'; break; \
case 1 << 12: tmp = 'l'; break; \
case 2 << 12: tmp = 'h'; break; \
default: tmp = '?'; break; \
}; tmp; })
static inline char token_mark (__le32 token)
{
__u32 v = le32_to_cpu (token);
if (v & QTD_STS_ACTIVE)
return '*';
if (v & QTD_STS_HALT)
return '-';
if (!IS_SHORT_READ (v))
return ' ';
/* tries to advance through hw_alt_next */
return '/';
}
static void qh_lines (
struct ehci_hcd *ehci,
struct ehci_qh *qh,
char **nextp,
unsigned *sizep
)
{
u32 scratch;
u32 hw_curr;
struct list_head *entry;
struct ehci_qtd *td;
unsigned temp;
unsigned size = *sizep;
char *next = *nextp;
char mark;
if (qh->hw_qtd_next == EHCI_LIST_END) /* NEC does this */
mark = '@';
else
mark = token_mark (qh->hw_token);
if (mark == '/') { /* qh_alt_next controls qh advance? */
if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
mark = '#'; /* blocked */
else if (qh->hw_alt_next == EHCI_LIST_END)
mark = '.'; /* use hw_qtd_next */
/* else alt_next points to some other qtd */
}
scratch = le32_to_cpup (&qh->hw_info1);
hw_curr = (mark == '*') ? le32_to_cpup (&qh->hw_current) : 0;
temp = scnprintf (next, size,
"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
qh, scratch & 0x007f,
speed_char (scratch),
(scratch >> 8) & 0x000f,
scratch, le32_to_cpup (&qh->hw_info2),
le32_to_cpup (&qh->hw_token), mark,
(__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token)
? "data1" : "data0",
(le32_to_cpup (&qh->hw_alt_next) >> 1) & 0x0f);
size -= temp;
next += temp;
/* hc may be modifying the list as we read it ... */
list_for_each (entry, &qh->qtd_list) {
td = list_entry (entry, struct ehci_qtd, qtd_list);
scratch = le32_to_cpup (&td->hw_token);
mark = ' ';
if (hw_curr == td->qtd_dma)
mark = '*';
else if (qh->hw_qtd_next == td->qtd_dma)
mark = '+';
else if (QTD_LENGTH (scratch)) {
if (td->hw_alt_next == ehci->async->hw_alt_next)
mark = '#';
else if (td->hw_alt_next != EHCI_LIST_END)
mark = '/';
}
temp = snprintf (next, size,
"\n\t%p%c%s len=%d %08x urb %p",
td, mark, ({ char *tmp;
switch ((scratch>>8)&0x03) {
case 0: tmp = "out"; break;
case 1: tmp = "in"; break;
case 2: tmp = "setup"; break;
default: tmp = "?"; break;
} tmp;}),
(scratch >> 16) & 0x7fff,
scratch,
td->urb);
if (temp < 0)
temp = 0;
else if (size < temp)
temp = size;
size -= temp;
next += temp;
if (temp == size)
goto done;
}
temp = snprintf (next, size, "\n");
if (temp < 0)
temp = 0;
else if (size < temp)
temp = size;
size -= temp;
next += temp;
done:
*sizep = size;
*nextp = next;
}
static ssize_t
show_async (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
unsigned long flags;
unsigned temp, size;
char *next;
struct ehci_qh *qh;
*buf = 0;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
/* dumps a snapshot of the async schedule.
* usually empty except for long-term bulk reads, or head.
* one QH per line, and TDs we know about
*/
spin_lock_irqsave (&ehci->lock, flags);
for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
qh_lines (ehci, qh, &next, &size);
if (ehci->reclaim && size > 0) {
temp = scnprintf (next, size, "\nreclaim =\n");
size -= temp;
next += temp;
for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim)
qh_lines (ehci, qh, &next, &size);
}
spin_unlock_irqrestore (&ehci->lock, flags);
return strlen (buf);
}
static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
#define DBG_SCHED_LIMIT 64
static ssize_t
show_periodic (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
unsigned long flags;
union ehci_shadow p, *seen;
unsigned temp, size, seen_count;
char *next;
unsigned i;
__le32 tag;
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
return 0;
seen_count = 0;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
temp = scnprintf (next, size, "size = %d\n", ehci->periodic_size);
size -= temp;
next += temp;
/* dump a snapshot of the periodic schedule.
* iso changes, interrupt usually doesn't.
*/
spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < ehci->periodic_size; i++) {
p = ehci->pshadow [i];
if (likely (!p.ptr))
continue;
tag = Q_NEXT_TYPE (ehci->periodic [i]);
temp = scnprintf (next, size, "%4d: ", i);
size -= temp;
next += temp;
do {
switch (tag) {
case Q_TYPE_QH:
temp = scnprintf (next, size, " qh%d-%04x/%p",
p.qh->period,
le32_to_cpup (&p.qh->hw_info2)
/* uframe masks */
& 0xffff,
p.qh);
size -= temp;
next += temp;
/* don't repeat what follows this qh */
for (temp = 0; temp < seen_count; temp++) {
if (seen [temp].ptr != p.ptr)
continue;
if (p.qh->qh_next.ptr)
temp = scnprintf (next, size,
" ...");
p.ptr = NULL;
break;
}
/* show more info the first time around */
if (temp == seen_count && p.ptr) {
u32 scratch = le32_to_cpup (
&p.qh->hw_info1);
struct ehci_qtd *qtd;
char *type = "";
/* count tds, get ep direction */
temp = 0;
list_for_each_entry (qtd,
&p.qh->qtd_list,
qtd_list) {
temp++;
switch (0x03 & (le32_to_cpu (
qtd->hw_token) >> 8)) {
case 0: type = "out"; continue;
case 1: type = "in"; continue;
}
}
temp = scnprintf (next, size,
" (%c%d ep%d%s "
"[%d/%d] q%d p%d)",
speed_char (scratch),
scratch & 0x007f,
(scratch >> 8) & 0x000f, type,
p.qh->usecs, p.qh->c_usecs,
temp,
0x7ff & (scratch >> 16));
if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++].qh = p.qh;
} else
temp = 0;
if (p.qh) {
tag = Q_NEXT_TYPE (p.qh->hw_next);
p = p.qh->qh_next;
}
break;
case Q_TYPE_FSTN:
temp = scnprintf (next, size,
" fstn-%8x/%p", p.fstn->hw_prev,
p.fstn);
tag = Q_NEXT_TYPE (p.fstn->hw_next);
p = p.fstn->fstn_next;
break;
case Q_TYPE_ITD:
temp = scnprintf (next, size,
" itd/%p", p.itd);
tag = Q_NEXT_TYPE (p.itd->hw_next);
p = p.itd->itd_next;
break;
case Q_TYPE_SITD:
temp = scnprintf (next, size,
" sitd%d-%04x/%p",
p.sitd->stream->interval,
le32_to_cpup (&p.sitd->hw_uframe)
& 0x0000ffff,
p.sitd);
tag = Q_NEXT_TYPE (p.sitd->hw_next);
p = p.sitd->sitd_next;
break;
}
size -= temp;
next += temp;
} while (p.ptr);
temp = scnprintf (next, size, "\n");
size -= temp;
next += temp;
}
spin_unlock_irqrestore (&ehci->lock, flags);
kfree (seen);
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
#undef DBG_SCHED_LIMIT
static ssize_t
show_registers (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
unsigned long flags;
unsigned temp, size, i;
char *next, scratch [80];
static char fmt [] = "%*s\n";
static char label [] = "";
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
spin_lock_irqsave (&ehci->lock, flags);
if (bus->controller->power.power_state) {
size = scnprintf (next, size,
"bus %s, device %s (driver " DRIVER_VERSION ")\n"
"SUSPENDED (no register access)\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id);
goto done;
}
/* Capability Registers */
i = HC_VERSION(readl (&ehci->caps->hc_capbase));
temp = scnprintf (next, size,
"bus %s, device %s (driver " DRIVER_VERSION ")\n"
"EHCI %x.%02x, hcd state %d\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id,
i >> 8, i & 0x0ff, hcd->state);
size -= temp;
next += temp;
// FIXME interpret both types of params
i = readl (&ehci->caps->hcs_params);
temp = scnprintf (next, size, "structural params 0x%08x\n", i);
size -= temp;
next += temp;
i = readl (&ehci->caps->hcc_params);
temp = scnprintf (next, size, "capability params 0x%08x\n", i);
size -= temp;
next += temp;
/* Operational Registers */
temp = dbg_status_buf (scratch, sizeof scratch, label,
readl (&ehci->regs->status));
temp = scnprintf (next, size, fmt, temp, scratch);
size -= temp;
next += temp;
temp = dbg_command_buf (scratch, sizeof scratch, label,
readl (&ehci->regs->command));
temp = scnprintf (next, size, fmt, temp, scratch);
size -= temp;
next += temp;
temp = dbg_intr_buf (scratch, sizeof scratch, label,
readl (&ehci->regs->intr_enable));
temp = scnprintf (next, size, fmt, temp, scratch);
size -= temp;
next += temp;
temp = scnprintf (next, size, "uframe %04x\n",
readl (&ehci->regs->frame_index));
size -= temp;
next += temp;
for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) {
temp = dbg_port_buf (scratch, sizeof scratch, label, i + 1,
readl (&ehci->regs->port_status [i]));
temp = scnprintf (next, size, fmt, temp, scratch);
size -= temp;
next += temp;
}
if (ehci->reclaim) {
temp = scnprintf (next, size, "reclaim qh %p%s\n",
ehci->reclaim,
ehci->reclaim_ready ? " ready" : "");
size -= temp;
next += temp;
}
#ifdef EHCI_STATS
temp = scnprintf (next, size,
"irq normal %ld err %ld reclaim %ld (lost %ld)\n",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
ehci->stats.lost_iaa);
size -= temp;
next += temp;
temp = scnprintf (next, size, "complete %ld unlink %ld\n",
ehci->stats.complete, ehci->stats.unlink);
size -= temp;
next += temp;
#endif
done:
spin_unlock_irqrestore (&ehci->lock, flags);
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
static inline void create_debug_files (struct ehci_hcd *ehci)
{
struct class_device *cldev = &ehci_to_hcd(ehci)->self.class_dev;
class_device_create_file(cldev, &class_device_attr_async);
class_device_create_file(cldev, &class_device_attr_periodic);
class_device_create_file(cldev, &class_device_attr_registers);
}
static inline void remove_debug_files (struct ehci_hcd *ehci)
{
struct class_device *cldev = &ehci_to_hcd(ehci)->self.class_dev;
class_device_remove_file(cldev, &class_device_attr_async);
class_device_remove_file(cldev, &class_device_attr_periodic);
class_device_remove_file(cldev, &class_device_attr_registers);
}
#endif /* STUB_DEBUG_FILES */

1261
drivers/usb/host/ehci-hcd.c Normal file

File diff suppressed because it is too large Load diff

553
drivers/usb/host/ehci-hub.c Normal file
View file

@ -0,0 +1,553 @@
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* this file is part of ehci-hcd.c */
/*-------------------------------------------------------------------------*/
/*
* EHCI Root Hub ... the nonsharable stuff
*
* Registers don't need cpu_to_le32, that happens transparently
*/
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
static int ehci_hub_suspend (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int port;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
port = HCS_N_PORTS (ehci->hcs_params);
spin_lock_irq (&ehci->lock);
/* stop schedules, clean any completed work */
if (HC_IS_RUNNING(hcd->state)) {
ehci_quiesce (ehci);
hcd->state = HC_STATE_QUIESCING;
}
ehci->command = readl (&ehci->regs->command);
if (ehci->reclaim)
ehci->reclaim_ready = 1;
ehci_work(ehci, NULL);
/* suspend any active/unsuspended ports, maybe allow wakeup */
while (port--) {
u32 __iomem *reg = &ehci->regs->port_status [port];
u32 t1 = readl (reg);
u32 t2 = t1;
if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
t2 |= PORT_SUSPEND;
if (hcd->remote_wakeup)
t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E;
else
t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
if (t1 != t2) {
ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
port + 1, t1, t2);
writel (t2, reg);
}
}
/* turn off now-idle HC */
ehci_halt (ehci);
hcd->state = HC_STATE_SUSPENDED;
ehci->next_statechange = jiffies + msecs_to_jiffies(10);
spin_unlock_irq (&ehci->lock);
return 0;
}
/* caller has locked the root hub, and should reset/reinit on error */
static int ehci_hub_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
int i;
int intr_enable;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
spin_lock_irq (&ehci->lock);
/* re-init operational registers in case we lost power */
if (readl (&ehci->regs->intr_enable) == 0) {
/* at least some APM implementations will try to deliver
* IRQs right away, so delay them until we're ready.
*/
intr_enable = 1;
writel (0, &ehci->regs->segment);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
} else
intr_enable = 0;
ehci_dbg(ehci, "resume root hub%s\n",
intr_enable ? " after power loss" : "");
/* restore CMD_RUN, framelist size, and irq threshold */
writel (ehci->command, &ehci->regs->command);
/* take ports out of suspend */
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
temp = readl (&ehci->regs->port_status [i]);
temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
if (temp & PORT_SUSPEND) {
ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
temp |= PORT_RESUME;
}
writel (temp, &ehci->regs->port_status [i]);
}
i = HCS_N_PORTS (ehci->hcs_params);
mdelay (20);
while (i--) {
temp = readl (&ehci->regs->port_status [i]);
if ((temp & PORT_SUSPEND) == 0)
continue;
temp &= ~PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
ehci_vdbg (ehci, "resumed port %d\n", i + 1);
}
(void) readl (&ehci->regs->command);
/* maybe re-activate the schedule(s) */
temp = 0;
if (ehci->async->qh_next.qh)
temp |= CMD_ASE;
if (ehci->periodic_sched)
temp |= CMD_PSE;
if (temp) {
ehci->command |= temp;
writel (ehci->command, &ehci->regs->command);
}
ehci->next_statechange = jiffies + msecs_to_jiffies(5);
hcd->state = HC_STATE_RUNNING;
/* Now we can safely re-enable irqs */
if (intr_enable)
writel (INTR_MASK, &ehci->regs->intr_enable);
spin_unlock_irq (&ehci->lock);
return 0;
}
#else
#define ehci_hub_suspend NULL
#define ehci_hub_resume NULL
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static int check_reset_complete (
struct ehci_hcd *ehci,
int index,
int port_status
) {
if (!(port_status & PORT_CONNECT)) {
ehci->reset_done [index] = 0;
return port_status;
}
/* if reset finished and it's still not enabled -- handoff */
if (!(port_status & PORT_PE)) {
/* with integrated TT, there's nobody to hand it to! */
if (ehci_is_TDI(ehci)) {
ehci_dbg (ehci,
"Failed to enable port %d on root hub TT\n",
index+1);
return port_status;
}
ehci_dbg (ehci, "port %d full speed --> companion\n",
index + 1);
// what happens if HCS_N_CC(params) == 0 ?
port_status |= PORT_OWNER;
writel (port_status, &ehci->regs->port_status [index]);
} else
ehci_dbg (ehci, "port %d high speed\n", index + 1);
return port_status;
}
/*-------------------------------------------------------------------------*/
/* build "status change" packet (one or two bytes) from HC registers */
static int
ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp, status = 0;
int ports, i, retval = 1;
unsigned long flags;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (!HC_IS_RUNNING(hcd->state))
return 0;
/* init status to no-changes */
buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params);
if (ports > 7) {
buf [1] = 0;
retval++;
}
/* no hub change reports (bit 0) for now (power, ...) */
/* port N changes (bit N)? */
spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < ports; i++) {
temp = readl (&ehci->regs->port_status [i]);
if (temp & PORT_OWNER) {
/* don't report this in GetPortStatus */
if (temp & PORT_CSC) {
temp &= ~PORT_CSC;
writel (temp, &ehci->regs->port_status [i]);
}
continue;
}
if (!(temp & PORT_CONNECT))
ehci->reset_done [i] = 0;
if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0
// PORT_STAT_C_SUSPEND?
|| ((temp & PORT_RESUME) != 0
&& time_after (jiffies,
ehci->reset_done [i]))) {
if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
status = STS_PCD;
}
}
/* FIXME autosuspend idle root hubs */
spin_unlock_irqrestore (&ehci->lock, flags);
return status ? retval : 0;
}
/*-------------------------------------------------------------------------*/
static void
ehci_hub_descriptor (
struct ehci_hcd *ehci,
struct usb_hub_descriptor *desc
) {
int ports = HCS_N_PORTS (ehci->hcs_params);
u16 temp;
desc->bDescriptorType = 0x29;
desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
desc->bHubContrCurrent = 0;
desc->bNbrPorts = ports;
temp = 1 + (ports / 8);
desc->bDescLength = 7 + 2 * temp;
/* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
memset (&desc->bitmap [0], 0, temp);
memset (&desc->bitmap [temp], 0xff, temp);
temp = 0x0008; /* per-port overcurrent reporting */
if (HCS_PPC (ehci->hcs_params))
temp |= 0x0001; /* per-port power control */
#if 0
// re-enable when we support USB_PORT_FEAT_INDICATOR below.
if (HCS_INDICATOR (ehci->hcs_params))
temp |= 0x0080; /* per-port indicators (LEDs) */
#endif
desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp);
}
/*-------------------------------------------------------------------------*/
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
static int ehci_hub_control (
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports = HCS_N_PORTS (ehci->hcs_params);
u32 temp, status;
unsigned long flags;
int retval = 0;
/*
* FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
* HCS_INDICATOR may say we can change LEDs to off/amber/green.
* (track current state ourselves) ... blink for diagnostics,
* power, "this is the one", etc. EHCI spec supports this.
*/
spin_lock_irqsave (&ehci->lock, flags);
switch (typeReq) {
case ClearHubFeature:
switch (wValue) {
case C_HUB_LOCAL_POWER:
case C_HUB_OVER_CURRENT:
/* no hub-wide feature/status flags */
break;
default:
goto error;
}
break;
case ClearPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
temp = readl (&ehci->regs->port_status [wIndex]);
if (temp & PORT_OWNER)
break;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
writel (temp & ~PORT_PE,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_C_ENABLE:
writel (temp | PORT_PEC,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_SUSPEND:
if (temp & PORT_RESET)
goto error;
if (temp & PORT_SUSPEND) {
if ((temp & PORT_PE) == 0)
goto error;
/* resume signaling for 20 msec */
writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME,
&ehci->regs->port_status [wIndex]);
ehci->reset_done [wIndex] = jiffies
+ msecs_to_jiffies (20);
}
break;
case USB_PORT_FEAT_C_SUSPEND:
/* we auto-clear this feature */
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params))
writel (temp & ~PORT_POWER,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_C_CONNECTION:
writel (temp | PORT_CSC,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
writel (temp | PORT_OCC,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_C_RESET:
/* GetPortStatus clears reset */
break;
default:
goto error;
}
readl (&ehci->regs->command); /* unblock posted write */
break;
case GetHubDescriptor:
ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
buf);
break;
case GetHubStatus:
/* no hub-wide feature/status flags */
memset (buf, 0, 4);
//cpu_to_le32s ((u32 *) buf);
break;
case GetPortStatus:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
status = 0;
temp = readl (&ehci->regs->port_status [wIndex]);
// wPortChange bits
if (temp & PORT_CSC)
status |= 1 << USB_PORT_FEAT_C_CONNECTION;
if (temp & PORT_PEC)
status |= 1 << USB_PORT_FEAT_C_ENABLE;
if (temp & PORT_OCC)
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
/* whoever resumes must GetPortStatus to complete it!! */
if ((temp & PORT_RESUME)
&& time_after (jiffies,
ehci->reset_done [wIndex])) {
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
ehci->reset_done [wIndex] = 0;
/* stop resume signaling */
temp = readl (&ehci->regs->port_status [wIndex]);
writel (temp & ~PORT_RESUME,
&ehci->regs->port_status [wIndex]);
retval = handshake (
&ehci->regs->port_status [wIndex],
PORT_RESUME, 0, 2000 /* 2msec */);
if (retval != 0) {
ehci_err (ehci, "port %d resume error %d\n",
wIndex + 1, retval);
goto error;
}
temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
}
/* whoever resets must GetPortStatus to complete it!! */
if ((temp & PORT_RESET)
&& time_after (jiffies,
ehci->reset_done [wIndex])) {
status |= 1 << USB_PORT_FEAT_C_RESET;
ehci->reset_done [wIndex] = 0;
/* force reset to complete */
writel (temp & ~PORT_RESET,
&ehci->regs->port_status [wIndex]);
retval = handshake (
&ehci->regs->port_status [wIndex],
PORT_RESET, 0, 500);
if (retval != 0) {
ehci_err (ehci, "port %d reset error %d\n",
wIndex + 1, retval);
goto error;
}
/* see what we found out */
temp = check_reset_complete (ehci, wIndex,
readl (&ehci->regs->port_status [wIndex]));
}
// don't show wPortStatus if it's owned by a companion hc
if (!(temp & PORT_OWNER)) {
if (temp & PORT_CONNECT) {
status |= 1 << USB_PORT_FEAT_CONNECTION;
// status may be from integrated TT
status |= ehci_port_speed(ehci, temp);
}
if (temp & PORT_PE)
status |= 1 << USB_PORT_FEAT_ENABLE;
if (temp & (PORT_SUSPEND|PORT_RESUME))
status |= 1 << USB_PORT_FEAT_SUSPEND;
if (temp & PORT_OC)
status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
if (temp & PORT_RESET)
status |= 1 << USB_PORT_FEAT_RESET;
if (temp & PORT_POWER)
status |= 1 << USB_PORT_FEAT_POWER;
}
#ifndef EHCI_VERBOSE_DEBUG
if (status & ~0xffff) /* only if wPortChange is interesting */
#endif
dbg_port (ehci, "GetStatus", wIndex + 1, temp);
// we "know" this alignment is good, caller used kmalloc()...
*((__le32 *) buf) = cpu_to_le32 (status);
break;
case SetHubFeature:
switch (wValue) {
case C_HUB_LOCAL_POWER:
case C_HUB_OVER_CURRENT:
/* no hub-wide feature/status flags */
break;
default:
goto error;
}
break;
case SetPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
temp = readl (&ehci->regs->port_status [wIndex]);
if (temp & PORT_OWNER)
break;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0)
goto error;
if (hcd->remote_wakeup)
temp |= PORT_WAKE_BITS;
writel (temp | PORT_SUSPEND,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params))
writel (temp | PORT_POWER,
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_RESET:
if (temp & PORT_RESUME)
goto error;
/* line status bits may report this as low speed,
* which can be fine if this root hub has a
* transaction translator built in.
*/
if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
&& !ehci_is_TDI(ehci)
&& PORT_USB11 (temp)) {
ehci_dbg (ehci,
"port %d low speed --> companion\n",
wIndex + 1);
temp |= PORT_OWNER;
} else {
ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
temp |= PORT_RESET;
temp &= ~PORT_PE;
/*
* caller must wait, then call GetPortStatus
* usb 2.0 spec says 50 ms resets on root
*/
ehci->reset_done [wIndex] = jiffies
+ msecs_to_jiffies (50);
}
writel (temp, &ehci->regs->port_status [wIndex]);
break;
default:
goto error;
}
readl (&ehci->regs->command); /* unblock posted writes */
break;
default:
error:
/* "stall" on error */
retval = -EPIPE;
}
spin_unlock_irqrestore (&ehci->lock, flags);
return retval;
}

237
drivers/usb/host/ehci-mem.c Normal file
View file

@ -0,0 +1,237 @@
/*
* Copyright (c) 2001 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* this file is part of ehci-hcd.c */
/*-------------------------------------------------------------------------*/
/*
* There's basically three types of memory:
* - data used only by the HCD ... kmalloc is fine
* - async and periodic schedules, shared by HC and HCD ... these
* need to use dma_pool or dma_alloc_coherent
* - driver buffers, read/written by HC ... single shot DMA mapped
*
* There's also PCI "register" data, which is memory mapped.
* No memory seen by this driver is pageable.
*/
/*-------------------------------------------------------------------------*/
/* Allocate the key transfer structures from the previously allocated pool */
static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
{
memset (qtd, 0, sizeof *qtd);
qtd->qtd_dma = dma;
qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
qtd->hw_next = EHCI_LIST_END;
qtd->hw_alt_next = EHCI_LIST_END;
INIT_LIST_HEAD (&qtd->qtd_list);
}
static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags)
{
struct ehci_qtd *qtd;
dma_addr_t dma;
qtd = dma_pool_alloc (ehci->qtd_pool, flags, &dma);
if (qtd != NULL) {
ehci_qtd_init (qtd, dma);
}
return qtd;
}
static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
{
dma_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
}
static void qh_destroy (struct kref *kref)
{
struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref);
struct ehci_hcd *ehci = qh->ehci;
/* clean qtds first, and know this is not linked */
if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
ehci_dbg (ehci, "unused qh not empty!\n");
BUG ();
}
if (qh->dummy)
ehci_qtd_free (ehci, qh->dummy);
usb_put_dev (qh->dev);
dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
}
static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags)
{
struct ehci_qh *qh;
dma_addr_t dma;
qh = (struct ehci_qh *)
dma_pool_alloc (ehci->qh_pool, flags, &dma);
if (!qh)
return qh;
memset (qh, 0, sizeof *qh);
kref_init(&qh->kref);
qh->ehci = ehci;
qh->qh_dma = dma;
// INIT_LIST_HEAD (&qh->qh_list);
INIT_LIST_HEAD (&qh->qtd_list);
/* dummy td enables safe urb queuing */
qh->dummy = ehci_qtd_alloc (ehci, flags);
if (qh->dummy == NULL) {
ehci_dbg (ehci, "no dummy td\n");
dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
qh = NULL;
}
return qh;
}
/* to share a qh (cpu threads, or hc) */
static inline struct ehci_qh *qh_get (struct ehci_qh *qh)
{
kref_get(&qh->kref);
return qh;
}
static inline void qh_put (struct ehci_qh *qh)
{
kref_put(&qh->kref, qh_destroy);
}
/*-------------------------------------------------------------------------*/
/* The queue heads and transfer descriptors are managed from pools tied
* to each of the "per device" structures.
* This is the initialisation and cleanup code.
*/
static void ehci_mem_cleanup (struct ehci_hcd *ehci)
{
if (ehci->async)
qh_put (ehci->async);
ehci->async = NULL;
/* DMA consistent memory and pools */
if (ehci->qtd_pool)
dma_pool_destroy (ehci->qtd_pool);
ehci->qtd_pool = NULL;
if (ehci->qh_pool) {
dma_pool_destroy (ehci->qh_pool);
ehci->qh_pool = NULL;
}
if (ehci->itd_pool)
dma_pool_destroy (ehci->itd_pool);
ehci->itd_pool = NULL;
if (ehci->sitd_pool)
dma_pool_destroy (ehci->sitd_pool);
ehci->sitd_pool = NULL;
if (ehci->periodic)
dma_free_coherent (ehci_to_hcd(ehci)->self.controller,
ehci->periodic_size * sizeof (u32),
ehci->periodic, ehci->periodic_dma);
ehci->periodic = NULL;
/* shadow periodic table */
if (ehci->pshadow)
kfree (ehci->pshadow);
ehci->pshadow = NULL;
}
/* remember to add cleanup code (above) if you add anything here */
static int ehci_mem_init (struct ehci_hcd *ehci, int flags)
{
int i;
/* QTDs for control/bulk/intr transfers */
ehci->qtd_pool = dma_pool_create ("ehci_qtd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_qtd),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->qtd_pool) {
goto fail;
}
/* QHs for control/bulk/intr transfers */
ehci->qh_pool = dma_pool_create ("ehci_qh",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_qh),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->qh_pool) {
goto fail;
}
ehci->async = ehci_qh_alloc (ehci, flags);
if (!ehci->async) {
goto fail;
}
/* ITD for high speed ISO transfers */
ehci->itd_pool = dma_pool_create ("ehci_itd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_itd),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->itd_pool) {
goto fail;
}
/* SITD for full/low speed split ISO transfers */
ehci->sitd_pool = dma_pool_create ("ehci_sitd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_sitd),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->sitd_pool) {
goto fail;
}
/* Hardware periodic table */
ehci->periodic = (__le32 *)
dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller,
ehci->periodic_size * sizeof(__le32),
&ehci->periodic_dma, 0);
if (ehci->periodic == NULL) {
goto fail;
}
for (i = 0; i < ehci->periodic_size; i++)
ehci->periodic [i] = EHCI_LIST_END;
/* software shadow of hardware table */
ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags);
if (ehci->pshadow == NULL) {
goto fail;
}
memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
return 0;
fail:
ehci_dbg (ehci, "couldn't init memory\n");
ehci_mem_cleanup (ehci);
return -ENOMEM;
}

1090
drivers/usb/host/ehci-q.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

637
drivers/usb/host/ehci.h Normal file
View file

@ -0,0 +1,637 @@
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __LINUX_EHCI_HCD_H
#define __LINUX_EHCI_HCD_H
/* definitions used for the EHCI driver */
/* statistics can be kept for for tuning/monitoring */
struct ehci_stats {
/* irq usage */
unsigned long normal;
unsigned long error;
unsigned long reclaim;
unsigned long lost_iaa;
/* termination of urbs from core */
unsigned long complete;
unsigned long unlink;
};
/* ehci_hcd->lock guards shared data against other CPUs:
* ehci_hcd: async, reclaim, periodic (and shadow), ...
* usb_host_endpoint: hcpriv
* ehci_qh: qh_next, qtd_list
* ehci_qtd: qtd_list
*
* Also, hold this lock when talking to HC registers or
* when updating hw_* fields in shared qh/qtd/... structures.
*/
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
struct ehci_hcd { /* one per controller */
spinlock_t lock;
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *reclaim;
unsigned reclaim_ready : 1;
unsigned scanning : 1;
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
unsigned periodic_size;
__le32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma;
unsigned i_thresh; /* uframes HC might cache */
union ehci_shadow *pshadow; /* mirror hw periodic table */
int next_uframe; /* scan periodic, start here */
unsigned periodic_sched; /* periodic activity count */
/* per root hub port */
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */
struct dma_pool *qtd_pool; /* one or more per qh */
struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */
struct timer_list watchdog;
struct notifier_block reboot_notifier;
unsigned long actions;
unsigned stamp;
unsigned long next_statechange;
u32 command;
unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */
/* glue to PCI and HCD framework */
struct ehci_caps __iomem *caps;
struct ehci_regs __iomem *regs;
__u32 hcs_params; /* cached register copy */
/* irq statistics */
#ifdef EHCI_STATS
struct ehci_stats stats;
# define COUNT(x) do { (x)++; } while (0)
#else
# define COUNT(x) do {} while (0)
#endif
};
/* convert between an HCD pointer and the corresponding EHCI_HCD */
static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
{
return (struct ehci_hcd *) (hcd->hcd_priv);
}
static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
{
return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
}
enum ehci_timer_action {
TIMER_IO_WATCHDOG,
TIMER_IAA_WATCHDOG,
TIMER_ASYNC_SHRINK,
TIMER_ASYNC_OFF,
};
static inline void
timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action)
{
clear_bit (action, &ehci->actions);
}
static inline void
timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
{
if (!test_and_set_bit (action, &ehci->actions)) {
unsigned long t;
switch (action) {
case TIMER_IAA_WATCHDOG:
t = EHCI_IAA_JIFFIES;
break;
case TIMER_IO_WATCHDOG:
t = EHCI_IO_JIFFIES;
break;
case TIMER_ASYNC_OFF:
t = EHCI_ASYNC_JIFFIES;
break;
// case TIMER_ASYNC_SHRINK:
default:
t = EHCI_SHRINK_JIFFIES;
break;
}
t += jiffies;
// all timings except IAA watchdog can be overridden.
// async queue SHRINK often precedes IAA. while it's ready
// to go OFF neither can matter, and afterwards the IO
// watchdog stops unless there's still periodic traffic.
if (action != TIMER_IAA_WATCHDOG
&& t > ehci->watchdog.expires
&& timer_pending (&ehci->watchdog))
return;
mod_timer (&ehci->watchdog, t);
}
}
/*-------------------------------------------------------------------------*/
/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
/* Section 2.2 Host Controller Capability Registers */
struct ehci_caps {
/* these fields are specified as 8 and 16 bit registers,
* but some hosts can't perform 8 or 16 bit PCI accesses.
*/
u32 hc_capbase;
#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
u32 hcs_params; /* HCSPARAMS - offset 0x4 */
#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
u32 hcc_params; /* HCCPARAMS - offset 0x8 */
#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
u8 portroute [8]; /* nibbles for routing - offset 0xC */
} __attribute__ ((packed));
/* Section 2.3 Host Controller Operational Registers */
struct ehci_regs {
/* USBCMD: offset 0x00 */
u32 command;
/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
#define CMD_PARK (1<<11) /* enable "park" on async qh */
#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
#define CMD_ASE (1<<5) /* async schedule enable */
#define CMD_PSE (1<<4) /* periodic schedule enable */
/* 3:2 is periodic frame list size */
#define CMD_RESET (1<<1) /* reset HC not bus */
#define CMD_RUN (1<<0) /* start/stop HC */
/* USBSTS: offset 0x04 */
u32 status;
#define STS_ASS (1<<15) /* Async Schedule Status */
#define STS_PSS (1<<14) /* Periodic Schedule Status */
#define STS_RECL (1<<13) /* Reclamation */
#define STS_HALT (1<<12) /* Not running (any reason) */
/* some bits reserved */
/* these STS_* flags are also intr_enable bits (USBINTR) */
#define STS_IAA (1<<5) /* Interrupted on async advance */
#define STS_FATAL (1<<4) /* such as some PCI access errors */
#define STS_FLR (1<<3) /* frame list rolled over */
#define STS_PCD (1<<2) /* port change detect */
#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
#define STS_INT (1<<0) /* "normal" completion (short, ...) */
/* USBINTR: offset 0x08 */
u32 intr_enable;
/* FRINDEX: offset 0x0C */
u32 frame_index; /* current microframe number */
/* CTRLDSSEGMENT: offset 0x10 */
u32 segment; /* address bits 63:32 if needed */
/* PERIODICLISTBASE: offset 0x14 */
u32 frame_list; /* points to periodic list */
/* ASYNCLISTADDR: offset 0x18 */
u32 async_next; /* address of next async queue head */
u32 reserved [9];
/* CONFIGFLAG: offset 0x40 */
u32 configured_flag;
#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
/* PORTSC: offset 0x44 */
u32 port_status [0]; /* up to N_PORTS */
/* 31:23 reserved */
#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
/* 19:16 for port testing */
#define PORT_LED_OFF (0<<14)
#define PORT_LED_AMBER (1<<14)
#define PORT_LED_GREEN (2<<14)
#define PORT_LED_MASK (3<<14)
#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
#define PORT_POWER (1<<12) /* true: has power (see PPC) */
#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */
/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
/* 9 reserved */
#define PORT_RESET (1<<8) /* reset port */
#define PORT_SUSPEND (1<<7) /* suspend port */
#define PORT_RESUME (1<<6) /* resume it */
#define PORT_OCC (1<<5) /* over current change */
#define PORT_OC (1<<4) /* over current active */
#define PORT_PEC (1<<3) /* port enable change */
#define PORT_PE (1<<2) /* port enable */
#define PORT_CSC (1<<1) /* connect status change */
#define PORT_CONNECT (1<<0) /* device connected */
} __attribute__ ((packed));
/* Appendix C, Debug port ... intended for use with special "debug devices"
* that can help if there's no serial console. (nonstandard enumeration.)
*/
struct ehci_dbg_port {
u32 control;
#define DBGP_OWNER (1<<30)
#define DBGP_ENABLED (1<<28)
#define DBGP_DONE (1<<16)
#define DBGP_INUSE (1<<10)
#define DBGP_ERRCODE(x) (((x)>>7)&0x0f)
# define DBGP_ERR_BAD 1
# define DBGP_ERR_SIGNAL 2
#define DBGP_ERROR (1<<6)
#define DBGP_GO (1<<5)
#define DBGP_OUT (1<<4)
#define DBGP_LEN(x) (((x)>>0)&0x0f)
u32 pids;
#define DBGP_PID_GET(x) (((x)>>16)&0xff)
#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok));
u32 data03;
u32 data47;
u32 address;
#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep));
} __attribute__ ((packed));
/*-------------------------------------------------------------------------*/
#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
/*
* EHCI Specification 0.95 Section 3.5
* QTD: describe data transfer components (buffer, direction, ...)
* See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
*
* These are associated only with "QH" (Queue Head) structures,
* used with control, bulk, and interrupt transfers.
*/
struct ehci_qtd {
/* first part defined by EHCI spec */
__le32 hw_next; /* see EHCI 3.5.1 */
__le32 hw_alt_next; /* see EHCI 3.5.2 */
__le32 hw_token; /* see EHCI 3.5.3 */
#define QTD_TOGGLE (1 << 31) /* data toggle */
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define QTD_IOC (1 << 15) /* interrupt on complete */
#define QTD_CERR(tok) (((tok)>>10) & 0x3)
#define QTD_PID(tok) (((tok)>>8) & 0x3)
#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
#define QTD_STS_HALT (1 << 6) /* halted on error */
#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
#define QTD_STS_STS (1 << 1) /* split transaction state */
#define QTD_STS_PING (1 << 0) /* issue PING? */
__le32 hw_buf [5]; /* see EHCI 3.5.4 */
__le32 hw_buf_hi [5]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t qtd_dma; /* qtd address */
struct list_head qtd_list; /* sw qtd list */
struct urb *urb; /* qtd's urb */
size_t length; /* length of buffer */
} __attribute__ ((aligned (32)));
/* mask NakCnt+T in qh->hw_alt_next */
#define QTD_MASK __constant_cpu_to_le32 (~0x1f)
#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
/*-------------------------------------------------------------------------*/
/* type tag from {qh,itd,sitd,fstn}->hw_next */
#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
/* values for that type tag */
#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1)
#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1)
#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1)
#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1)
/* next async queue entry, or pointer to interrupt/periodic QH */
#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
/* for periodic/async schedules and qtd lists, mark end of list */
#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */
/*
* Entries in periodic shadow table are pointers to one of four kinds
* of data structure. That's dictated by the hardware; a type tag is
* encoded in the low bits of the hardware's periodic schedule. Use
* Q_NEXT_TYPE to get the tag.
*
* For entries in the async schedule, the type tag always says "qh".
*/
union ehci_shadow {
struct ehci_qh *qh; /* Q_TYPE_QH */
struct ehci_itd *itd; /* Q_TYPE_ITD */
struct ehci_sitd *sitd; /* Q_TYPE_SITD */
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
u32 *hw_next; /* (all types) */
void *ptr;
};
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.95 Section 3.6
* QH: describes control/bulk/interrupt endpoints
* See Fig 3-7 "Queue Head Structure Layout".
*
* These appear in both the async and (for interrupt) periodic schedules.
*/
struct ehci_qh {
/* first part defined by EHCI spec */
__le32 hw_next; /* see EHCI 3.6.1 */
__le32 hw_info1; /* see EHCI 3.6.2 */
#define QH_HEAD 0x00008000
__le32 hw_info2; /* see EHCI 3.6.2 */
__le32 hw_current; /* qtd list - see EHCI 3.6.4 */
/* qtd overlay (hardware parts of a struct ehci_qtd) */
__le32 hw_qtd_next;
__le32 hw_alt_next;
__le32 hw_token;
__le32 hw_buf [5];
__le32 hw_buf_hi [5];
/* the rest is HCD-private */
dma_addr_t qh_dma; /* address of qh */
union ehci_shadow qh_next; /* ptr to qh; or periodic */
struct list_head qtd_list; /* sw qtd list */
struct ehci_qtd *dummy;
struct ehci_qh *reclaim; /* next to reclaim */
struct ehci_hcd *ehci;
struct kref kref;
unsigned stamp;
u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
/* periodic schedule info */
u8 usecs; /* intr bandwidth */
u8 gap_uf; /* uframes split/csplit gap */
u8 c_usecs; /* ... split completion bw */
unsigned short period; /* polling interval */
unsigned short start; /* where polling starts */
#define NO_FRAME ((unsigned short)~0) /* pick new start */
struct usb_device *dev; /* access to TT */
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
/* description of one iso transaction (up to 3 KB data if highspeed) */
struct ehci_iso_packet {
/* These will be copied to iTD when scheduling */
u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
__le32 transaction; /* itd->hw_transaction[i] |= */
u8 cross; /* buf crosses pages */
/* for full speed OUT splits */
u32 buf1;
};
/* temporary schedule data for packets from iso urbs (both speeds)
* each packet is one logical usb transaction to the device (not TT),
* beginning at stream->next_uframe
*/
struct ehci_iso_sched {
struct list_head td_list;
unsigned span;
struct ehci_iso_packet packet [0];
};
/*
* ehci_iso_stream - groups all (s)itds for this endpoint.
* acts like a qh would, if EHCI had them for ISO.
*/
struct ehci_iso_stream {
/* first two fields match QH, but info1 == 0 */
__le32 hw_next;
__le32 hw_info1;
u32 refcount;
u8 bEndpointAddress;
u8 highspeed;
u16 depth; /* depth in uframes */
struct list_head td_list; /* queued itds/sitds */
struct list_head free_list; /* list of unused itds/sitds */
struct usb_device *udev;
struct usb_host_endpoint *ep;
/* output of (re)scheduling */
unsigned long start; /* jiffies */
unsigned long rescheduled;
int next_uframe;
__le32 splits;
/* the rest is derived from the endpoint descriptor,
* trusting urb->interval == f(epdesc->bInterval) and
* including the extra info for hw_bufp[0..2]
*/
u8 interval;
u8 usecs, c_usecs;
u16 maxp;
u16 raw_mask;
unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */
__le32 buf0;
__le32 buf1;
__le32 buf2;
/* this is used to initialize sITD's tt info */
__le32 address;
};
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.95 Section 3.3
* Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
*
* Schedule records for high speed iso xfers
*/
struct ehci_itd {
/* first part defined by EHCI spec */
__le32 hw_next; /* see EHCI 3.3.1 */
__le32 hw_transaction [8]; /* see EHCI 3.3.2 */
#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
#define ITD_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
__le32 hw_bufp [7]; /* see EHCI 3.3.3 */
__le32 hw_bufp_hi [7]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t itd_dma; /* for this itd */
union ehci_shadow itd_next; /* ptr to periodic q entry */
struct urb *urb;
struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head itd_list; /* list of stream's itds */
/* any/all hw_transactions here may be used by that urb */
unsigned frame; /* where scheduled */
unsigned pg;
unsigned index[8]; /* in urb->iso_frame_desc */
u8 usecs[8];
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.95 Section 3.4
* siTD, aka split-transaction isochronous Transfer Descriptor
* ... describe full speed iso xfers through TT in hubs
* see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
*/
struct ehci_sitd {
/* first part defined by EHCI spec */
__le32 hw_next;
/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
__le32 hw_fullspeed_ep; /* EHCI table 3-9 */
__le32 hw_uframe; /* EHCI table 3-10 */
__le32 hw_results; /* EHCI table 3-11 */
#define SITD_IOC (1 << 31) /* interrupt on completion */
#define SITD_PAGE (1 << 30) /* buffer 0/1 */
#define SITD_LENGTH(x) (0x3ff & ((x)>>16))
#define SITD_STS_ACTIVE (1 << 7) /* HC may execute this */
#define SITD_STS_ERR (1 << 6) /* error from TT */
#define SITD_STS_DBE (1 << 5) /* data buffer error (in HC) */
#define SITD_STS_BABBLE (1 << 4) /* device was babbling */
#define SITD_STS_XACT (1 << 3) /* illegal IN response */
#define SITD_STS_MMF (1 << 2) /* incomplete split transaction */
#define SITD_STS_STS (1 << 1) /* split transaction state */
#define SITD_ACTIVE __constant_cpu_to_le32(SITD_STS_ACTIVE)
__le32 hw_buf [2]; /* EHCI table 3-12 */
__le32 hw_backpointer; /* EHCI table 3-13 */
__le32 hw_buf_hi [2]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t sitd_dma;
union ehci_shadow sitd_next; /* ptr to periodic q entry */
struct urb *urb;
struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head sitd_list; /* list of stream's sitds */
unsigned frame;
unsigned index;
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.96 Section 3.7
* Periodic Frame Span Traversal Node (FSTN)
*
* Manages split interrupt transactions (using TT) that span frame boundaries
* into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN
* makes the HC jump (back) to a QH to scan for fs/ls QH completions until
* it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
*/
struct ehci_fstn {
__le32 hw_next; /* any periodic q entry */
__le32 hw_prev; /* qh or EHCI_LIST_END */
/* the rest is HCD-private */
dma_addr_t fstn_dma;
union ehci_shadow fstn_next; /* ptr to periodic q entry */
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT
/*
* Some EHCI controllers have a Transaction Translator built into the
* root hub. This is a non-standard feature. Each controller will need
* to add code to the following inline functions, and call them as
* needed (mostly in root hub code).
*/
#define ehci_is_TDI(e) ((e)->is_tdi_rh_tt)
/* Returns the speed of a device attached to a port on the root hub. */
static inline unsigned int
ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
{
if (ehci_is_TDI(ehci)) {
switch ((portsc>>26)&3) {
case 0:
return 0;
case 1:
return (1<<USB_PORT_FEAT_LOWSPEED);
case 2:
default:
return (1<<USB_PORT_FEAT_HIGHSPEED);
}
}
return (1<<USB_PORT_FEAT_HIGHSPEED);
}
#else
#define ehci_is_TDI(e) (0)
#define ehci_port_speed(ehci, portsc) (1<<USB_PORT_FEAT_HIGHSPEED)
#endif
/*-------------------------------------------------------------------------*/
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
/*-------------------------------------------------------------------------*/
#endif /* __LINUX_EHCI_HCD_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,289 @@
#ifndef __LINUX_ETRAX_USB_H
#define __LINUX_ETRAX_USB_H
#include <linux/types.h>
#include <linux/list.h>
typedef struct USB_IN_Desc {
volatile __u16 sw_len;
volatile __u16 command;
volatile unsigned long next;
volatile unsigned long buf;
volatile __u16 hw_len;
volatile __u16 status;
} USB_IN_Desc_t;
typedef struct USB_SB_Desc {
volatile __u16 sw_len;
volatile __u16 command;
volatile unsigned long next;
volatile unsigned long buf;
__u32 dummy;
} USB_SB_Desc_t;
typedef struct USB_EP_Desc {
volatile __u16 hw_len;
volatile __u16 command;
volatile unsigned long sub;
volatile unsigned long next;
__u32 dummy;
} USB_EP_Desc_t;
struct virt_root_hub {
int devnum;
void *urb;
void *int_addr;
int send;
int interval;
int numports;
struct timer_list rh_int_timer;
volatile __u16 wPortChange_1;
volatile __u16 wPortChange_2;
volatile __u16 prev_wPortStatus_1;
volatile __u16 prev_wPortStatus_2;
};
struct etrax_usb_intr_traffic {
int sleeping;
int error;
struct wait_queue *wq;
};
typedef struct etrax_usb_hc {
struct usb_bus *bus;
struct virt_root_hub rh;
struct etrax_usb_intr_traffic intr;
} etrax_hc_t;
typedef enum {
STARTED,
NOT_STARTED,
UNLINK,
TRANSFER_DONE,
WAITING_FOR_DESCR_INTR
} etrax_usb_urb_state_t;
typedef struct etrax_usb_urb_priv {
/* The first_sb field is used for freeing all SB descriptors belonging
to an urb. The corresponding ep descriptor's sub pointer cannot be
used for this since the DMA advances the sub pointer as it processes
the sb list. */
USB_SB_Desc_t *first_sb;
/* The last_sb field referes to the last SB descriptor that belongs to
this urb. This is important to know so we can free the SB descriptors
that ranges between first_sb and last_sb. */
USB_SB_Desc_t *last_sb;
/* The rx_offset field is used in ctrl and bulk traffic to keep track
of the offset in the urb's transfer_buffer where incoming data should be
copied to. */
__u32 rx_offset;
/* Counter used in isochronous transfers to keep track of the
number of packets received/transmitted. */
__u32 isoc_packet_counter;
/* This field is used to pass information about the urb's current state between
the various interrupt handlers (thus marked volatile). */
volatile etrax_usb_urb_state_t urb_state;
/* Connection between the submitted urb and ETRAX epid number */
__u8 epid;
/* The rx_data_list field is used for periodic traffic, to hold
received data for later processing in the the complete_urb functions,
where the data us copied to the urb's transfer_buffer. Basically, we
use this intermediate storage because we don't know when it's safe to
reuse the transfer_buffer (FIXME?). */
struct list_head rx_data_list;
} etrax_urb_priv_t;
/* This struct is for passing data from the top half to the bottom half. */
typedef struct usb_interrupt_registers
{
etrax_hc_t *hc;
__u32 r_usb_epid_attn;
__u8 r_usb_status;
__u16 r_usb_rh_port_status_1;
__u16 r_usb_rh_port_status_2;
__u32 r_usb_irq_mask_read;
__u32 r_usb_fm_number;
struct work_struct usb_bh;
} usb_interrupt_registers_t;
/* This struct is for passing data from the isoc top half to the isoc bottom half. */
typedef struct usb_isoc_complete_data
{
struct urb *urb;
struct work_struct usb_bh;
} usb_isoc_complete_data_t;
/* This struct holds data we get from the rx descriptors for DMA channel 9
for periodic traffic (intr and isoc). */
typedef struct rx_data
{
void *data;
int length;
struct list_head list;
} rx_data_t;
typedef struct urb_entry
{
struct urb *urb;
struct list_head list;
} urb_entry_t;
/* ---------------------------------------------------------------------------
Virtual Root HUB
------------------------------------------------------------------------- */
/* destination of request */
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
/* Our Vendor Specific feature */
#define RH_REMOVE_EP 0x00
#define RH_ACK 0x01
#define RH_REQ_ERR -1
#define RH_NACK 0x00
/* Field definitions for */
#define USB_IN_command__eol__BITNR 0 /* command macros */
#define USB_IN_command__eol__WIDTH 1
#define USB_IN_command__eol__no 0
#define USB_IN_command__eol__yes 1
#define USB_IN_command__intr__BITNR 3
#define USB_IN_command__intr__WIDTH 1
#define USB_IN_command__intr__no 0
#define USB_IN_command__intr__yes 1
#define USB_IN_status__eop__BITNR 1 /* status macros. */
#define USB_IN_status__eop__WIDTH 1
#define USB_IN_status__eop__no 0
#define USB_IN_status__eop__yes 1
#define USB_IN_status__eot__BITNR 5
#define USB_IN_status__eot__WIDTH 1
#define USB_IN_status__eot__no 0
#define USB_IN_status__eot__yes 1
#define USB_IN_status__error__BITNR 6
#define USB_IN_status__error__WIDTH 1
#define USB_IN_status__error__no 0
#define USB_IN_status__error__yes 1
#define USB_IN_status__nodata__BITNR 7
#define USB_IN_status__nodata__WIDTH 1
#define USB_IN_status__nodata__no 0
#define USB_IN_status__nodata__yes 1
#define USB_IN_status__epid__BITNR 8
#define USB_IN_status__epid__WIDTH 5
#define USB_EP_command__eol__BITNR 0
#define USB_EP_command__eol__WIDTH 1
#define USB_EP_command__eol__no 0
#define USB_EP_command__eol__yes 1
#define USB_EP_command__eof__BITNR 1
#define USB_EP_command__eof__WIDTH 1
#define USB_EP_command__eof__no 0
#define USB_EP_command__eof__yes 1
#define USB_EP_command__intr__BITNR 3
#define USB_EP_command__intr__WIDTH 1
#define USB_EP_command__intr__no 0
#define USB_EP_command__intr__yes 1
#define USB_EP_command__enable__BITNR 4
#define USB_EP_command__enable__WIDTH 1
#define USB_EP_command__enable__no 0
#define USB_EP_command__enable__yes 1
#define USB_EP_command__hw_valid__BITNR 5
#define USB_EP_command__hw_valid__WIDTH 1
#define USB_EP_command__hw_valid__no 0
#define USB_EP_command__hw_valid__yes 1
#define USB_EP_command__epid__BITNR 8
#define USB_EP_command__epid__WIDTH 5
#define USB_SB_command__eol__BITNR 0 /* command macros. */
#define USB_SB_command__eol__WIDTH 1
#define USB_SB_command__eol__no 0
#define USB_SB_command__eol__yes 1
#define USB_SB_command__eot__BITNR 1
#define USB_SB_command__eot__WIDTH 1
#define USB_SB_command__eot__no 0
#define USB_SB_command__eot__yes 1
#define USB_SB_command__intr__BITNR 3
#define USB_SB_command__intr__WIDTH 1
#define USB_SB_command__intr__no 0
#define USB_SB_command__intr__yes 1
#define USB_SB_command__tt__BITNR 4
#define USB_SB_command__tt__WIDTH 2
#define USB_SB_command__tt__zout 0
#define USB_SB_command__tt__in 1
#define USB_SB_command__tt__out 2
#define USB_SB_command__tt__setup 3
#define USB_SB_command__rem__BITNR 8
#define USB_SB_command__rem__WIDTH 6
#define USB_SB_command__full__BITNR 6
#define USB_SB_command__full__WIDTH 1
#define USB_SB_command__full__no 0
#define USB_SB_command__full__yes 1
#endif

View file

@ -0,0 +1,284 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
*
* Bus Glue for AMD Alchemy Au1xxx
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Rusell King et al.
*
* Modified for LH7A404 from ohci-sa1111.c
* by Durgesh Pattamatta <pattamattad@sharpsec.com>
* Modified for AMD Alchemy Au1xxx
* by Matt Porter <mporter@kernel.crashing.org>
*
* This file is licenced under the GPL.
*/
#include <asm/mach-au1x00/au1000.h>
#define USBH_ENABLE_BE (1<<0)
#define USBH_ENABLE_C (1<<1)
#define USBH_ENABLE_E (1<<2)
#define USBH_ENABLE_CE (1<<3)
#define USBH_ENABLE_RD (1<<4)
#ifdef __LITTLE_ENDIAN
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
#elif __BIG_ENDIAN
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE)
#else
#error not byte order defined
#endif
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void au1xxx_start_hc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": starting Au1xxx OHCI USB Controller\n");
/* enable host controller */
au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
udelay(1000);
au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG);
udelay(1000);
/* wait for reset complete (read register twice; see au1500 errata) */
while (au_readl(USB_HOST_CONFIG),
!(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
udelay(1000);
printk(KERN_DEBUG __FILE__
": Clock to USB host has been enabled \n");
}
static void au1xxx_stop_hc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": stopping Au1xxx OHCI USB Controller\n");
/* Disable clock */
au_writel(readl((void *)USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_au1xxx_probe - initialize Au1xxx-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
int usb_hcd_au1xxx_probe (const struct hc_driver *driver,
struct platform_device *dev)
{
int retval;
struct usb_hcd *hcd;
if(dev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug ("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
hcd = usb_create_hcd(driver, &dev->dev, "au1xxx");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
retval = -ENOMEM;
goto err2;
}
au1xxx_start_hc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
if (retval == 0)
return retval;
au1xxx_stop_hc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_au1xxx_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_au1xxx_remove (struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
au1xxx_stop_hc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_au1xxx_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
ohci_dbg (ohci, "ohci_au1xxx_start, ohci:%p", ohci);
if ((ret = ohci_init (ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_au1xxx_hc_driver = {
.description = hcd_name,
.product_desc = "Au1xxx OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_au1xxx_start,
#ifdef CONFIG_PM
/* suspend: ohci_au1xxx_suspend, -- tbd */
/* resume: ohci_au1xxx_resume, -- tbd */
#endif /*CONFIG_PM*/
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_au1xxx_drv_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int ret;
pr_debug ("In ohci_hcd_au1xxx_drv_probe");
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev);
return ret;
}
static int ohci_hcd_au1xxx_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
usb_hcd_au1xxx_remove(hcd, pdev);
return 0;
}
/*TBD*/
/*static int ohci_hcd_au1xxx_drv_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
return 0;
}
static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
return 0;
}
*/
static struct device_driver ohci_hcd_au1xxx_driver = {
.name = "au1xxx-ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_au1xxx_drv_probe,
.remove = ohci_hcd_au1xxx_drv_remove,
/*.suspend = ohci_hcd_au1xxx_drv_suspend, */
/*.resume = ohci_hcd_au1xxx_drv_resume, */
};
static int __init ohci_hcd_au1xxx_init (void)
{
pr_debug (DRIVER_INFO " (Au1xxx)");
pr_debug ("block sizes: ed %d td %d\n",
sizeof (struct ed), sizeof (struct td));
return driver_register(&ohci_hcd_au1xxx_driver);
}
static void __exit ohci_hcd_au1xxx_cleanup (void)
{
driver_unregister(&ohci_hcd_au1xxx_driver);
}
module_init (ohci_hcd_au1xxx_init);
module_exit (ohci_hcd_au1xxx_cleanup);

707
drivers/usb/host/ohci-dbg.c Normal file
View file

@ -0,0 +1,707 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
/*-------------------------------------------------------------------------*/
#ifdef DEBUG
#define edstring(ed_type) ({ char *temp; \
switch (ed_type) { \
case PIPE_CONTROL: temp = "ctrl"; break; \
case PIPE_BULK: temp = "bulk"; break; \
case PIPE_INTERRUPT: temp = "intr"; break; \
default: temp = "isoc"; break; \
}; temp;})
#define pipestring(pipe) edstring(usb_pipetype(pipe))
/* debug| print the main components of an URB
* small: 0) header + data packets 1) just header
*/
static void __attribute__((unused))
urb_print (struct urb * urb, char * str, int small)
{
unsigned int pipe= urb->pipe;
if (!urb->dev || !urb->dev->bus) {
dbg("%s URB: no dev", str);
return;
}
#ifndef OHCI_VERBOSE_DEBUG
if (urb->status != 0)
#endif
dbg("%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d",
str,
urb,
usb_pipedevice (pipe),
usb_pipeendpoint (pipe),
usb_pipeout (pipe)? "out" : "in",
pipestring (pipe),
urb->transfer_flags,
urb->actual_length,
urb->transfer_buffer_length,
urb->status);
#ifdef OHCI_VERBOSE_DEBUG
if (!small) {
int i, len;
if (usb_pipecontrol (pipe)) {
printk (KERN_DEBUG __FILE__ ": setup(8):");
for (i = 0; i < 8 ; i++)
printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
printk ("\n");
}
if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
printk (KERN_DEBUG __FILE__ ": data(%d/%d):",
urb->actual_length,
urb->transfer_buffer_length);
len = usb_pipeout (pipe)?
urb->transfer_buffer_length: urb->actual_length;
for (i = 0; i < 16 && i < len; i++)
printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
printk ("%s stat:%d\n", i < len? "...": "", urb->status);
}
}
#endif
}
#define ohci_dbg_sw(ohci, next, size, format, arg...) \
do { \
if (next) { \
unsigned s_len; \
s_len = scnprintf (*next, *size, format, ## arg ); \
*size -= s_len; *next += s_len; \
} else \
ohci_dbg(ohci,format, ## arg ); \
} while (0);
static void ohci_dump_intr_mask (
struct ohci_hcd *ohci,
char *label,
u32 mask,
char **next,
unsigned *size)
{
ohci_dbg_sw (ohci, next, size, "%s 0x%08x%s%s%s%s%s%s%s%s%s\n",
label,
mask,
(mask & OHCI_INTR_MIE) ? " MIE" : "",
(mask & OHCI_INTR_OC) ? " OC" : "",
(mask & OHCI_INTR_RHSC) ? " RHSC" : "",
(mask & OHCI_INTR_FNO) ? " FNO" : "",
(mask & OHCI_INTR_UE) ? " UE" : "",
(mask & OHCI_INTR_RD) ? " RD" : "",
(mask & OHCI_INTR_SF) ? " SF" : "",
(mask & OHCI_INTR_WDH) ? " WDH" : "",
(mask & OHCI_INTR_SO) ? " SO" : ""
);
}
static void maybe_print_eds (
struct ohci_hcd *ohci,
char *label,
u32 value,
char **next,
unsigned *size)
{
if (value)
ohci_dbg_sw (ohci, next, size, "%s %08x\n", label, value);
}
static char *hcfs2string (int state)
{
switch (state) {
case OHCI_USB_RESET: return "reset";
case OHCI_USB_RESUME: return "resume";
case OHCI_USB_OPER: return "operational";
case OHCI_USB_SUSPEND: return "suspend";
}
return "?";
}
// dump control and status registers
static void
ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
{
struct ohci_regs __iomem *regs = controller->regs;
u32 temp;
temp = ohci_readl (controller, &regs->revision) & 0xff;
ohci_dbg_sw (controller, next, size,
"OHCI %d.%d, %s legacy support registers\n",
0x03 & (temp >> 4), (temp & 0x0f),
(temp & 0x0100) ? "with" : "NO");
temp = ohci_readl (controller, &regs->control);
ohci_dbg_sw (controller, next, size,
"control 0x%03x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n",
temp,
(temp & OHCI_CTRL_RWE) ? " RWE" : "",
(temp & OHCI_CTRL_RWC) ? " RWC" : "",
(temp & OHCI_CTRL_IR) ? " IR" : "",
hcfs2string (temp & OHCI_CTRL_HCFS),
(temp & OHCI_CTRL_BLE) ? " BLE" : "",
(temp & OHCI_CTRL_CLE) ? " CLE" : "",
(temp & OHCI_CTRL_IE) ? " IE" : "",
(temp & OHCI_CTRL_PLE) ? " PLE" : "",
temp & OHCI_CTRL_CBSR
);
temp = ohci_readl (controller, &regs->cmdstatus);
ohci_dbg_sw (controller, next, size,
"cmdstatus 0x%05x SOC=%d%s%s%s%s\n", temp,
(temp & OHCI_SOC) >> 16,
(temp & OHCI_OCR) ? " OCR" : "",
(temp & OHCI_BLF) ? " BLF" : "",
(temp & OHCI_CLF) ? " CLF" : "",
(temp & OHCI_HCR) ? " HCR" : ""
);
ohci_dump_intr_mask (controller, "intrstatus",
ohci_readl (controller, &regs->intrstatus),
next, size);
ohci_dump_intr_mask (controller, "intrenable",
ohci_readl (controller, &regs->intrenable),
next, size);
// intrdisable always same as intrenable
maybe_print_eds (controller, "ed_periodcurrent",
ohci_readl (controller, &regs->ed_periodcurrent),
next, size);
maybe_print_eds (controller, "ed_controlhead",
ohci_readl (controller, &regs->ed_controlhead),
next, size);
maybe_print_eds (controller, "ed_controlcurrent",
ohci_readl (controller, &regs->ed_controlcurrent),
next, size);
maybe_print_eds (controller, "ed_bulkhead",
ohci_readl (controller, &regs->ed_bulkhead),
next, size);
maybe_print_eds (controller, "ed_bulkcurrent",
ohci_readl (controller, &regs->ed_bulkcurrent),
next, size);
maybe_print_eds (controller, "donehead",
ohci_readl (controller, &regs->donehead), next, size);
/* broken fminterval means traffic won't flow! */
ohci_dbg (controller, "fminterval %08x\n",
ohci_readl (controller, &regs->fminterval));
}
#define dbg_port_sw(hc,num,value,next,size) \
ohci_dbg_sw (hc, next, size, \
"roothub.portstatus [%d] " \
"0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
num, temp, \
(temp & RH_PS_PRSC) ? " PRSC" : "", \
(temp & RH_PS_OCIC) ? " OCIC" : "", \
(temp & RH_PS_PSSC) ? " PSSC" : "", \
(temp & RH_PS_PESC) ? " PESC" : "", \
(temp & RH_PS_CSC) ? " CSC" : "", \
\
(temp & RH_PS_LSDA) ? " LSDA" : "", \
(temp & RH_PS_PPS) ? " PPS" : "", \
(temp & RH_PS_PRS) ? " PRS" : "", \
(temp & RH_PS_POCI) ? " POCI" : "", \
(temp & RH_PS_PSS) ? " PSS" : "", \
\
(temp & RH_PS_PES) ? " PES" : "", \
(temp & RH_PS_CCS) ? " CCS" : "" \
);
static void
ohci_dump_roothub (
struct ohci_hcd *controller,
int verbose,
char **next,
unsigned *size)
{
u32 temp, ndp, i;
temp = roothub_a (controller);
if (temp == ~(u32)0)
return;
ndp = (temp & RH_A_NDP);
if (verbose) {
ohci_dbg_sw (controller, next, size,
"roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp,
((temp & RH_A_POTPGT) >> 24) & 0xff,
(temp & RH_A_NOCP) ? " NOCP" : "",
(temp & RH_A_OCPM) ? " OCPM" : "",
(temp & RH_A_DT) ? " DT" : "",
(temp & RH_A_NPS) ? " NPS" : "",
(temp & RH_A_PSM) ? " PSM" : "",
ndp
);
temp = roothub_b (controller);
ohci_dbg_sw (controller, next, size,
"roothub.b %08x PPCM=%04x DR=%04x\n",
temp,
(temp & RH_B_PPCM) >> 16,
(temp & RH_B_DR)
);
temp = roothub_status (controller);
ohci_dbg_sw (controller, next, size,
"roothub.status %08x%s%s%s%s%s%s\n",
temp,
(temp & RH_HS_CRWE) ? " CRWE" : "",
(temp & RH_HS_OCIC) ? " OCIC" : "",
(temp & RH_HS_LPSC) ? " LPSC" : "",
(temp & RH_HS_DRWE) ? " DRWE" : "",
(temp & RH_HS_OCI) ? " OCI" : "",
(temp & RH_HS_LPS) ? " LPS" : ""
);
}
for (i = 0; i < ndp; i++) {
temp = roothub_portstatus (controller, i);
dbg_port_sw (controller, i, temp, next, size);
}
}
static void ohci_dump (struct ohci_hcd *controller, int verbose)
{
ohci_dbg (controller, "OHCI controller state\n");
// dumps some of the state we know about
ohci_dump_status (controller, NULL, NULL);
if (controller->hcca)
ohci_dbg (controller,
"hcca frame #%04x\n", ohci_frame_no(controller));
ohci_dump_roothub (controller, 1, NULL, NULL);
}
static const char data0 [] = "DATA0";
static const char data1 [] = "DATA1";
static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label,
const struct td *td)
{
u32 tmp = hc32_to_cpup (ohci, &td->hwINFO);
ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x\n",
label, td,
(tmp & TD_DONE) ? " (DONE)" : "",
td->urb, td->index,
hc32_to_cpup (ohci, &td->hwNextTD));
if ((tmp & TD_ISO) == 0) {
const char *toggle, *pid;
u32 cbp, be;
switch (tmp & TD_T) {
case TD_T_DATA0: toggle = data0; break;
case TD_T_DATA1: toggle = data1; break;
case TD_T_TOGGLE: toggle = "(CARRY)"; break;
default: toggle = "(?)"; break;
}
switch (tmp & TD_DP) {
case TD_DP_SETUP: pid = "SETUP"; break;
case TD_DP_IN: pid = "IN"; break;
case TD_DP_OUT: pid = "OUT"; break;
default: pid = "(bad pid)"; break;
}
ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s\n", tmp,
TD_CC_GET(tmp), /* EC, */ toggle,
(tmp & TD_DI) >> 21, pid,
(tmp & TD_R) ? "R" : "");
cbp = hc32_to_cpup (ohci, &td->hwCBP);
be = hc32_to_cpup (ohci, &td->hwBE);
ohci_dbg (ohci, " cbp %08x be %08x (len %d)\n", cbp, be,
cbp ? (be + 1 - cbp) : 0);
} else {
unsigned i;
ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x\n", tmp,
TD_CC_GET(tmp),
(tmp >> 24) & 0x07,
(tmp & TD_DI) >> 21,
tmp & 0x0000ffff);
ohci_dbg (ohci, " bp0 %08x be %08x\n",
hc32_to_cpup (ohci, &td->hwCBP) & ~0x0fff,
hc32_to_cpup (ohci, &td->hwBE));
for (i = 0; i < MAXPSW; i++) {
u16 psw = ohci_hwPSW (ohci, td, i);
int cc = (psw >> 12) & 0x0f;
ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d\n", i,
psw, cc,
(cc >= 0x0e) ? "OFFSET" : "SIZE",
psw & 0x0fff);
}
}
}
/* caller MUST own hcd spinlock if verbose is set! */
static void __attribute__((unused))
ohci_dump_ed (const struct ohci_hcd *ohci, const char *label,
const struct ed *ed, int verbose)
{
u32 tmp = hc32_to_cpu (ohci, ed->hwINFO);
char *type = "";
ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x\n",
label,
ed, ed->state, edstring (ed->type),
hc32_to_cpup (ohci, &ed->hwNextED));
switch (tmp & (ED_IN|ED_OUT)) {
case ED_OUT: type = "-OUT"; break;
case ED_IN: type = "-IN"; break;
/* else from TDs ... control */
}
ohci_dbg (ohci,
" info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d\n", tmp,
0x03ff & (tmp >> 16),
(tmp & ED_DEQUEUE) ? " DQ" : "",
(tmp & ED_ISO) ? " ISO" : "",
(tmp & ED_SKIP) ? " SKIP" : "",
(tmp & ED_LOWSPEED) ? " LOW" : "",
0x000f & (tmp >> 7),
type,
0x007f & tmp);
tmp = hc32_to_cpup (ohci, &ed->hwHeadP);
ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s\n",
tmp,
(tmp & ED_C) ? data1 : data0,
(tmp & ED_H) ? " HALT" : "",
hc32_to_cpup (ohci, &ed->hwTailP),
verbose ? "" : " (not listing)");
if (verbose) {
struct list_head *tmp;
/* use ed->td_list because HC concurrently modifies
* hwNextTD as it accumulates ed_donelist.
*/
list_for_each (tmp, &ed->td_list) {
struct td *td;
td = list_entry (tmp, struct td, td_list);
ohci_dump_td (ohci, " ->", td);
}
}
}
#else
static inline void ohci_dump (struct ohci_hcd *controller, int verbose) {}
#undef OHCI_VERBOSE_DEBUG
#endif /* DEBUG */
/*-------------------------------------------------------------------------*/
#ifdef STUB_DEBUG_FILES
static inline void create_debug_files (struct ohci_hcd *bus) { }
static inline void remove_debug_files (struct ohci_hcd *bus) { }
#else
static ssize_t
show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
{
unsigned temp, size = count;
if (!ed)
return 0;
/* print first --> last */
while (ed->ed_prev)
ed = ed->ed_prev;
/* dump a snapshot of the bulk or control schedule */
while (ed) {
u32 info = hc32_to_cpu (ohci, ed->hwINFO);
u32 headp = hc32_to_cpu (ohci, ed->hwHeadP);
struct list_head *entry;
struct td *td;
temp = scnprintf (buf, size,
"ed/%p %cs dev%d ep%d%s max %d %08x%s%s %s",
ed,
(info & ED_LOWSPEED) ? 'l' : 'f',
info & 0x7f,
(info >> 7) & 0xf,
(info & ED_IN) ? "in" : "out",
0x03ff & (info >> 16),
info,
(info & ED_SKIP) ? " s" : "",
(headp & ED_H) ? " H" : "",
(headp & ED_C) ? data1 : data0);
size -= temp;
buf += temp;
list_for_each (entry, &ed->td_list) {
u32 cbp, be;
td = list_entry (entry, struct td, td_list);
info = hc32_to_cpup (ohci, &td->hwINFO);
cbp = hc32_to_cpup (ohci, &td->hwCBP);
be = hc32_to_cpup (ohci, &td->hwBE);
temp = scnprintf (buf, size,
"\n\ttd %p %s %d cc=%x urb %p (%08x)",
td,
({ char *pid;
switch (info & TD_DP) {
case TD_DP_SETUP: pid = "setup"; break;
case TD_DP_IN: pid = "in"; break;
case TD_DP_OUT: pid = "out"; break;
default: pid = "(?)"; break;
} pid;}),
cbp ? (be + 1 - cbp) : 0,
TD_CC_GET (info), td->urb, info);
size -= temp;
buf += temp;
}
temp = scnprintf (buf, size, "\n");
size -= temp;
buf += temp;
ed = ed->ed_next;
}
return count - size;
}
static ssize_t
show_async (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
size_t temp;
unsigned long flags;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ohci = hcd_to_ohci(hcd);
/* display control and bulk lists together, for simplicity */
spin_lock_irqsave (&ohci->lock, flags);
temp = show_list (ohci, buf, PAGE_SIZE, ohci->ed_controltail);
temp += show_list (ohci, buf + temp, PAGE_SIZE - temp, ohci->ed_bulktail);
spin_unlock_irqrestore (&ohci->lock, flags);
return temp;
}
static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
#define DBG_SCHED_LIMIT 64
static ssize_t
show_periodic (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct ed **seen, *ed;
unsigned long flags;
unsigned temp, size, seen_count;
char *next;
unsigned i;
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
return 0;
seen_count = 0;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ohci = hcd_to_ohci(hcd);
next = buf;
size = PAGE_SIZE;
temp = scnprintf (next, size, "size = %d\n", NUM_INTS);
size -= temp;
next += temp;
/* dump a snapshot of the periodic schedule (and load) */
spin_lock_irqsave (&ohci->lock, flags);
for (i = 0; i < NUM_INTS; i++) {
if (!(ed = ohci->periodic [i]))
continue;
temp = scnprintf (next, size, "%2d [%3d]:", i, ohci->load [i]);
size -= temp;
next += temp;
do {
temp = scnprintf (next, size, " ed%d/%p",
ed->interval, ed);
size -= temp;
next += temp;
for (temp = 0; temp < seen_count; temp++) {
if (seen [temp] == ed)
break;
}
/* show more info the first time around */
if (temp == seen_count) {
u32 info = hc32_to_cpu (ohci, ed->hwINFO);
struct list_head *entry;
unsigned qlen = 0;
/* qlen measured here in TDs, not urbs */
list_for_each (entry, &ed->td_list)
qlen++;
temp = scnprintf (next, size,
" (%cs dev%d ep%d%s-%s qlen %u"
" max %d %08x%s%s)",
(info & ED_LOWSPEED) ? 'l' : 'f',
info & 0x7f,
(info >> 7) & 0xf,
(info & ED_IN) ? "in" : "out",
(info & ED_ISO) ? "iso" : "int",
qlen,
0x03ff & (info >> 16),
info,
(info & ED_SKIP) ? " K" : "",
(ed->hwHeadP &
cpu_to_hc32(ohci, ED_H)) ?
" H" : "");
size -= temp;
next += temp;
if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++] = ed;
ed = ed->ed_next;
} else {
/* we've seen it and what's after */
temp = 0;
ed = NULL;
}
} while (ed);
temp = scnprintf (next, size, "\n");
size -= temp;
next += temp;
}
spin_unlock_irqrestore (&ohci->lock, flags);
kfree (seen);
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
#undef DBG_SCHED_LIMIT
static ssize_t
show_registers (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct ohci_regs __iomem *regs;
unsigned long flags;
unsigned temp, size;
char *next;
u32 rdata;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ohci = hcd_to_ohci(hcd);
regs = ohci->regs;
next = buf;
size = PAGE_SIZE;
spin_lock_irqsave (&ohci->lock, flags);
/* dump driver info, then registers in spec order */
ohci_dbg_sw (ohci, &next, &size,
"bus %s, device %s\n"
"%s\n"
"%s version " DRIVER_VERSION "\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id,
hcd->product_desc,
hcd_name);
if (bus->controller->power.power_state) {
size -= scnprintf (next, size,
"SUSPENDED (no register access)\n");
goto done;
}
ohci_dump_status(ohci, &next, &size);
/* hcca */
if (ohci->hcca)
ohci_dbg_sw (ohci, &next, &size,
"hcca frame 0x%04x\n", ohci_frame_no(ohci));
/* other registers mostly affect frame timings */
rdata = ohci_readl (ohci, &regs->fminterval);
temp = scnprintf (next, size,
"fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n",
rdata, (rdata >> 31) ? "FIT " : "",
(rdata >> 16) & 0xefff, rdata & 0xffff);
size -= temp;
next += temp;
rdata = ohci_readl (ohci, &regs->fmremaining);
temp = scnprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n",
rdata, (rdata >> 31) ? "FRT " : "",
rdata & 0x3fff);
size -= temp;
next += temp;
rdata = ohci_readl (ohci, &regs->periodicstart);
temp = scnprintf (next, size, "periodicstart 0x%04x\n",
rdata & 0x3fff);
size -= temp;
next += temp;
rdata = ohci_readl (ohci, &regs->lsthresh);
temp = scnprintf (next, size, "lsthresh 0x%04x\n",
rdata & 0x3fff);
size -= temp;
next += temp;
/* roothub */
ohci_dump_roothub (ohci, 1, &next, &size);
done:
spin_unlock_irqrestore (&ohci->lock, flags);
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
static inline void create_debug_files (struct ohci_hcd *ohci)
{
struct class_device *cldev = &ohci_to_hcd(ohci)->self.class_dev;
class_device_create_file(cldev, &class_device_attr_async);
class_device_create_file(cldev, &class_device_attr_periodic);
class_device_create_file(cldev, &class_device_attr_registers);
ohci_dbg (ohci, "created debug files\n");
}
static inline void remove_debug_files (struct ohci_hcd *ohci)
{
struct class_device *cldev = &ohci_to_hcd(ohci)->self.class_dev;
class_device_remove_file(cldev, &class_device_attr_async);
class_device_remove_file(cldev, &class_device_attr_periodic);
class_device_remove_file(cldev, &class_device_attr_registers);
}
#endif
/*-------------------------------------------------------------------------*/

925
drivers/usb/host/ohci-hcd.c Normal file
View file

@ -0,0 +1,925 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
* [ (C) Copyright 1999 Linus Torvalds ]
* [ (C) Copyright 1999 Gregory P. Smith]
*
*
* OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller
* interfaces (though some non-x86 Intel chips use it). It supports
* smarter hardware than UHCI. A download link for the spec available
* through the http://www.usb.org website.
*
* History:
*
* 2004/03/24 LH7A404 support (Durgesh Pattamatta & Marc Singer)
* 2004/02/04 use generic dma_* functions instead of pci_* (dsaxena@plexity.net)
* 2003/02/24 show registers in sysfs (Kevin Brosius)
*
* 2002/09/03 get rid of ed hashtables, rework periodic scheduling and
* bandwidth accounting; if debugging, show schedules in driverfs
* 2002/07/19 fixes to management of ED and schedule state.
* 2002/06/09 SA-1111 support (Christopher Hoover)
* 2002/06/01 remember frame when HC won't see EDs any more; use that info
* to fix urb unlink races caused by interrupt latency assumptions;
* minor ED field and function naming updates
* 2002/01/18 package as a patch for 2.5.3; this should match the
* 2.4.17 kernel modulo some bugs being fixed.
*
* 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes
* from post-2.4.5 patches.
* 2001/09/20 URB_ZERO_PACKET support; hcca_dma portability, OPTi warning
* 2001/09/07 match PCI PM changes, errnos from Linus' tree
* 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify;
* pbook pci quirks gone (please fix pbook pci sw!) (db)
*
* 2001/04/08 Identify version on module load (gb)
* 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
pci_map_single (db)
* 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
* 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
*
* 2000/09/26 fixed races in removing the private portion of the urb
* 2000/09/07 disable bulk and control lists when unlinking the last
* endpoint descriptor in order to avoid unrecoverable errors on
* the Lucent chips. (rwc@sgi)
* 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
* urb unlink probs, indentation fixes
* 2000/08/11 various oops fixes mostly affecting iso and cleanup from
* device unplugs.
* 2000/06/28 use PCI hotplug framework, for better power management
* and for Cardbus support (David Brownell)
* 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling
* when the controller loses power; handle UE; cleanup; ...
*
* v5.2 1999/12/07 URB 3rd preview,
* v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
* v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume
* i386: HUB, Keyboard, Mouse, Printer
*
* v4.3 1999/10/27 multiple HCs, bulk_request
* v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
* v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
* v4.0 1999/08/18
* v3.0 1999/06/25
* v2.1 1999/05/09 code clean up
* v2.0 1999/05/04
* v1.0 1999/04/27 initial release
*
* This file is licenced under the GPL.
*/
#include <linux/config.h>
#ifdef CONFIG_USB_DEBUG
# define DEBUG
#else
# undef DEBUG
#endif
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.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> /* for in_interrupt () */
#include <linux/usb.h>
#include <linux/usb_otg.h>
#include "../core/hcd.h"
#include <linux/dma-mapping.h>
#include <linux/dmapool.h> /* needed by ohci-mem.c when no PCI */
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#define DRIVER_VERSION "2004 Nov 08"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
/*-------------------------------------------------------------------------*/
// #define OHCI_VERBOSE_DEBUG /* not always helpful */
/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT \
(OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
#ifdef __hppa__
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
#define IR_DISABLE
#endif
#ifdef CONFIG_ARCH_OMAP
/* OMAP doesn't support IR (no SMM; not needed) */
#define IR_DISABLE
#endif
/*-------------------------------------------------------------------------*/
static const char hcd_name [] = "ohci_hcd";
#include "ohci.h"
static void ohci_dump (struct ohci_hcd *ohci, int verbose);
static int ohci_init (struct ohci_hcd *ohci);
static void ohci_stop (struct usb_hcd *hcd);
#include "ohci-hub.c"
#include "ohci-dbg.c"
#include "ohci-mem.c"
#include "ohci-q.c"
/*
* On architectures with edge-triggered interrupts we must never return
* IRQ_NONE.
*/
#if defined(CONFIG_SA1111) /* ... or other edge-triggered systems */
#define IRQ_NOTMINE IRQ_HANDLED
#else
#define IRQ_NOTMINE IRQ_NONE
#endif
/* Some boards misreport power switching/overcurrent */
static int distrust_firmware = 1;
module_param (distrust_firmware, bool, 0);
MODULE_PARM_DESC (distrust_firmware,
"true to distrust firmware power/overcurrent setup");
/* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */
static int no_handshake = 0;
module_param (no_handshake, bool, 0);
MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
/*-------------------------------------------------------------------------*/
/*
* queue up an urb for anything except the root hub
*/
static int ohci_urb_enqueue (
struct usb_hcd *hcd,
struct usb_host_endpoint *ep,
struct urb *urb,
int mem_flags
) {
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct ed *ed;
urb_priv_t *urb_priv;
unsigned int pipe = urb->pipe;
int i, size = 0;
unsigned long flags;
int retval = 0;
#ifdef OHCI_VERBOSE_DEBUG
urb_print (urb, "SUB", usb_pipein (pipe));
#endif
/* every endpoint has a ed, locate and maybe (re)initialize it */
if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval)))
return -ENOMEM;
/* for the private part of the URB we need the number of TDs (size) */
switch (ed->type) {
case PIPE_CONTROL:
/* td_submit_urb() doesn't yet handle these */
if (urb->transfer_buffer_length > 4096)
return -EMSGSIZE;
/* 1 TD for setup, 1 for ACK, plus ... */
size = 2;
/* FALLTHROUGH */
// case PIPE_INTERRUPT:
// case PIPE_BULK:
default:
/* one TD for every 4096 Bytes (can be upto 8K) */
size += urb->transfer_buffer_length / 4096;
/* ... and for any remaining bytes ... */
if ((urb->transfer_buffer_length % 4096) != 0)
size++;
/* ... and maybe a zero length packet to wrap it up */
if (size == 0)
size++;
else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
&& (urb->transfer_buffer_length
% usb_maxpacket (urb->dev, pipe,
usb_pipeout (pipe))) == 0)
size++;
break;
case PIPE_ISOCHRONOUS: /* number of packets from URB */
size = urb->number_of_packets;
break;
}
/* allocate the private part of the URB */
urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),
mem_flags);
if (!urb_priv)
return -ENOMEM;
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
INIT_LIST_HEAD (&urb_priv->pending);
urb_priv->length = size;
urb_priv->ed = ed;
/* allocate the TDs (deferring hash chain updates) */
for (i = 0; i < size; i++) {
urb_priv->td [i] = td_alloc (ohci, mem_flags);
if (!urb_priv->td [i]) {
urb_priv->length = i;
urb_free_priv (ohci, urb_priv);
return -ENOMEM;
}
}
spin_lock_irqsave (&ohci->lock, flags);
/* don't submit to a dead HC */
if (!HC_IS_RUNNING(hcd->state)) {
retval = -ENODEV;
goto fail;
}
/* in case of unlink-during-submit */
spin_lock (&urb->lock);
if (urb->status != -EINPROGRESS) {
spin_unlock (&urb->lock);
urb->hcpriv = urb_priv;
finish_urb (ohci, urb, NULL);
retval = 0;
goto fail;
}
/* schedule the ed if needed */
if (ed->state == ED_IDLE) {
retval = ed_schedule (ohci, ed);
if (retval < 0)
goto fail0;
if (ed->type == PIPE_ISOCHRONOUS) {
u16 frame = ohci_frame_no(ohci);
/* delay a few frames before the first TD */
frame += max_t (u16, 8, ed->interval);
frame &= ~(ed->interval - 1);
frame |= ed->branch;
urb->start_frame = frame;
/* yes, only URB_ISO_ASAP is supported, and
* urb->start_frame is never used as input.
*/
}
} else if (ed->type == PIPE_ISOCHRONOUS)
urb->start_frame = ed->last_iso + ed->interval;
/* fill the TDs and link them to the ed; and
* enable that part of the schedule, if needed
* and update count of queued periodic urbs
*/
urb->hcpriv = urb_priv;
td_submit_urb (ohci, urb);
fail0:
spin_unlock (&urb->lock);
fail:
if (retval)
urb_free_priv (ohci, urb_priv);
spin_unlock_irqrestore (&ohci->lock, flags);
return retval;
}
/*
* decouple the URB from the HC queues (TDs, urb_priv); it's
* already marked using urb->status. reporting is always done
* asynchronously, and we might be dealing with an urb that's
* partially transferred, or an ED with other urbs being unlinked.
*/
static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
#ifdef OHCI_VERBOSE_DEBUG
urb_print (urb, "UNLINK", 1);
#endif
spin_lock_irqsave (&ohci->lock, flags);
if (HC_IS_RUNNING(hcd->state)) {
urb_priv_t *urb_priv;
/* Unless an IRQ completed the unlink while it was being
* handed to us, flag it for unlink and giveback, and force
* some upcoming INTR_SF to call finish_unlinks()
*/
urb_priv = urb->hcpriv;
if (urb_priv) {
if (urb_priv->ed->state == ED_OPER)
start_ed_unlink (ohci, urb_priv->ed);
}
} else {
/*
* with HC dead, we won't respect hc queue pointers
* any more ... just clean up every urb's memory.
*/
if (urb->hcpriv)
finish_urb (ohci, urb, NULL);
}
spin_unlock_irqrestore (&ohci->lock, flags);
return 0;
}
/*-------------------------------------------------------------------------*/
/* frees config/altsetting state for endpoints,
* including ED memory, dummy TD, and bulk/intr data toggle
*/
static void
ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
struct ed *ed = ep->hcpriv;
unsigned limit = 1000;
/* ASSERT: any requests/urbs are being unlinked */
/* ASSERT: nobody can be submitting urbs for this any more */
if (!ed)
return;
rescan:
spin_lock_irqsave (&ohci->lock, flags);
if (!HC_IS_RUNNING (hcd->state)) {
sanitize:
ed->state = ED_IDLE;
finish_unlinks (ohci, 0, NULL);
}
switch (ed->state) {
case ED_UNLINK: /* wait for hw to finish? */
/* major IRQ delivery trouble loses INTR_SF too... */
if (limit-- == 0) {
ohci_warn (ohci, "IRQ INTR_SF lossage\n");
goto sanitize;
}
spin_unlock_irqrestore (&ohci->lock, flags);
set_current_state (TASK_UNINTERRUPTIBLE);
schedule_timeout (1);
goto rescan;
case ED_IDLE: /* fully unlinked */
if (list_empty (&ed->td_list)) {
td_free (ohci, ed->dummy);
ed_free (ohci, ed);
break;
}
/* else FALL THROUGH */
default:
/* caller was supposed to have unlinked any requests;
* that's not our job. can't recover; must leak ed.
*/
ohci_err (ohci, "leak ed %p (#%02x) state %d%s\n",
ed, ep->desc.bEndpointAddress, ed->state,
list_empty (&ed->td_list) ? "" : " (has tds)");
td_free (ohci, ed->dummy);
break;
}
ep->hcpriv = NULL;
spin_unlock_irqrestore (&ohci->lock, flags);
return;
}
static int ohci_get_frame (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
return ohci_frame_no(ohci);
}
static void ohci_usb_reset (struct ohci_hcd *ohci)
{
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
ohci->hc_control &= OHCI_CTRL_RWC;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
}
/*-------------------------------------------------------------------------*
* HC functions
*-------------------------------------------------------------------------*/
/* init memory, and kick BIOS/SMM off */
static int ohci_init (struct ohci_hcd *ohci)
{
int ret;
disable (ohci);
ohci->regs = ohci_to_hcd(ohci)->regs;
ohci->next_statechange = jiffies;
#ifndef IR_DISABLE
/* SMM owns the HC? not for long! */
if (!no_handshake && ohci_readl (ohci,
&ohci->regs->control) & OHCI_CTRL_IR) {
u32 temp;
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
/* this timeout is arbitrary. we make it long, so systems
* depending on usb keyboards may be usable even if the
* BIOS/SMM code seems pretty broken.
*/
temp = 500; /* arbitrary: five seconds */
ohci_writel (ohci, OHCI_INTR_OC, &ohci->regs->intrenable);
ohci_writel (ohci, OHCI_OCR, &ohci->regs->cmdstatus);
while (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_IR) {
msleep (10);
if (--temp == 0) {
ohci_err (ohci, "USB HC takeover failed!"
" (BIOS/SMM bug)\n");
return -EBUSY;
}
}
ohci_usb_reset (ohci);
}
#endif
/* Disable HC interrupts */
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
// flush the writes
(void) ohci_readl (ohci, &ohci->regs->control);
if (ohci->hcca)
return 0;
ohci->hcca = dma_alloc_coherent (ohci_to_hcd(ohci)->self.controller,
sizeof *ohci->hcca, &ohci->hcca_dma, 0);
if (!ohci->hcca)
return -ENOMEM;
if ((ret = ohci_mem_init (ohci)) < 0)
ohci_stop (ohci_to_hcd(ohci));
return ret;
}
/*-------------------------------------------------------------------------*/
/* Start an OHCI controller, set the BUS operational
* resets USB and controller
* enable interrupts
* connect the virtual root hub
*/
static int ohci_run (struct ohci_hcd *ohci)
{
u32 mask, temp;
struct usb_device *udev;
struct usb_bus *bus;
int first = ohci->fminterval == 0;
disable (ohci);
/* boot firmware should have set this up (5.1.1.3.1) */
if (first) {
temp = ohci_readl (ohci, &ohci->regs->fminterval);
ohci->fminterval = temp & 0x3fff;
if (ohci->fminterval != FI)
ohci_dbg (ohci, "fminterval delta %d\n",
ohci->fminterval - FI);
ohci->fminterval |= FSMP (ohci->fminterval) << 16;
/* also: power/overcurrent flags in roothub.a */
}
/* Reset USB nearly "by the book". RemoteWakeupConnected
* saved if boot firmware (BIOS/SMM/...) told us it's connected
* (for OHCI integrated on mainboard, it normally is)
*/
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
ohci->hc_control);
if (ohci->hc_control & OHCI_CTRL_RWC
&& !(ohci->flags & OHCI_QUIRK_AMD756))
ohci_to_hcd(ohci)->can_wakeup = 1;
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_OPER:
temp = 0;
break;
case OHCI_USB_SUSPEND:
case OHCI_USB_RESUME:
ohci->hc_control &= OHCI_CTRL_RWC;
ohci->hc_control |= OHCI_USB_RESUME;
temp = 10 /* msec wait */;
break;
// case OHCI_USB_RESET:
default:
ohci->hc_control &= OHCI_CTRL_RWC;
ohci->hc_control |= OHCI_USB_RESET;
temp = 50 /* msec wait */;
break;
}
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
// flush the writes
(void) ohci_readl (ohci, &ohci->regs->control);
msleep(temp);
temp = roothub_a (ohci);
if (!(temp & RH_A_NPS)) {
unsigned ports = temp & RH_A_NDP;
/* power down each port */
for (temp = 0; temp < ports; temp++)
ohci_writel (ohci, RH_PS_LSDA,
&ohci->regs->roothub.portstatus [temp]);
}
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
/* 2msec timelimit here means no irqs/preempt */
spin_lock_irq (&ohci->lock);
retry:
/* HC Reset requires max 10 us delay */
ohci_writel (ohci, OHCI_HCR, &ohci->regs->cmdstatus);
temp = 30; /* ... allow extra time */
while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
if (--temp == 0) {
spin_unlock_irq (&ohci->lock);
ohci_err (ohci, "USB HC reset timed out!\n");
return -1;
}
udelay (1);
}
/* now we're in the SUSPEND state ... must go OPERATIONAL
* within 2msec else HC enters RESUME
*
* ... but some hardware won't init fmInterval "by the book"
* (SiS, OPTi ...), so reset again instead. SiS doesn't need
* this if we write fmInterval after we're OPERATIONAL.
* Unclear about ALi, ServerWorks, and others ... this could
* easily be a longstanding bug in chip init on Linux.
*/
if (ohci->flags & OHCI_QUIRK_INITRESET) {
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
}
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
/* a reset clears this */
ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
periodic_reinit (ohci);
/* some OHCI implementations are finicky about how they init.
* bogus values here mean not even enumeration could work.
*/
if ((ohci_readl (ohci, &ohci->regs->fminterval) & 0x3fff0000) == 0
|| !ohci_readl (ohci, &ohci->regs->periodicstart)) {
if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {
ohci->flags |= OHCI_QUIRK_INITRESET;
ohci_dbg (ohci, "enabling initreset quirk\n");
goto retry;
}
spin_unlock_irq (&ohci->lock);
ohci_err (ohci, "init err (%08x %04x)\n",
ohci_readl (ohci, &ohci->regs->fminterval),
ohci_readl (ohci, &ohci->regs->periodicstart));
return -EOVERFLOW;
}
/* start controller operations */
ohci->hc_control &= OHCI_CTRL_RWC;
ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
ohci_to_hcd(ohci)->state = HC_STATE_RUNNING;
/* wake on ConnectStatusChange, matching external hubs */
ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_INIT;
ohci_writel (ohci, mask, &ohci->regs->intrstatus);
ohci_writel (ohci, mask, &ohci->regs->intrenable);
/* handle root hub init quirks ... */
temp = roothub_a (ohci);
temp &= ~(RH_A_PSM | RH_A_OCPM);
if (ohci->flags & OHCI_QUIRK_SUPERIO) {
/* NSC 87560 and maybe others */
temp |= RH_A_NOCP;
temp &= ~(RH_A_POTPGT | RH_A_NPS);
ohci_writel (ohci, temp, &ohci->regs->roothub.a);
} else if ((ohci->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
/* hub power always on; required for AMD-756 and some
* Mac platforms. ganged overcurrent reporting, if any.
*/
temp |= RH_A_NPS;
ohci_writel (ohci, temp, &ohci->regs->roothub.a);
}
ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status);
ohci_writel (ohci, (temp & RH_A_NPS) ? 0 : RH_B_PPCM,
&ohci->regs->roothub.b);
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
spin_unlock_irq (&ohci->lock);
// POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((temp >> 23) & 0x1fe);
bus = &ohci_to_hcd(ohci)->self;
ohci_to_hcd(ohci)->state = HC_STATE_RUNNING;
ohci_dump (ohci, 1);
udev = bus->root_hub;
if (udev) {
return 0;
}
/* connect the virtual root hub */
udev = usb_alloc_dev (NULL, bus, 0);
if (!udev) {
disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
return -ENOMEM;
}
udev->speed = USB_SPEED_FULL;
if (usb_hcd_register_root_hub (udev, ohci_to_hcd(ohci)) != 0) {
usb_put_dev (udev);
disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
return -ENODEV;
}
if (ohci->power_budget)
hub_set_power_budget(udev, ohci->power_budget);
create_debug_files (ohci);
return 0;
}
/*-------------------------------------------------------------------------*/
/* an interrupt happens */
static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct ohci_regs __iomem *regs = ohci->regs;
int ints;
/* we can eliminate a (slow) ohci_readl()
if _only_ WDH caused this irq */
if ((ohci->hcca->done_head != 0)
&& ! (hc32_to_cpup (ohci, &ohci->hcca->done_head)
& 0x01)) {
ints = OHCI_INTR_WDH;
/* cardbus/... hardware gone before remove() */
} else if ((ints = ohci_readl (ohci, &regs->intrstatus)) == ~(u32)0) {
disable (ohci);
ohci_dbg (ohci, "device removed!\n");
return IRQ_HANDLED;
/* interrupt for some other device? */
} else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
return IRQ_NOTMINE;
}
if (ints & OHCI_INTR_UE) {
disable (ohci);
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
// e.g. due to PCI Master/Target Abort
ohci_dump (ohci, 1);
ohci_usb_reset (ohci);
}
if (ints & OHCI_INTR_RD) {
ohci_vdbg (ohci, "resume detect\n");
if (hcd->state != HC_STATE_QUIESCING)
schedule_work(&ohci->rh_resume);
}
if (ints & OHCI_INTR_WDH) {
if (HC_IS_RUNNING(hcd->state))
ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);
spin_lock (&ohci->lock);
dl_done_list (ohci, ptregs);
spin_unlock (&ohci->lock);
if (HC_IS_RUNNING(hcd->state))
ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrenable);
}
/* could track INTR_SO to reduce available PCI/... bandwidth */
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled
* when there's still unlinking to be done (next frame).
*/
spin_lock (&ohci->lock);
if (ohci->ed_rm_list)
finish_unlinks (ohci, ohci_frame_no(ohci), ptregs);
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
&& HC_IS_RUNNING(hcd->state))
ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable);
spin_unlock (&ohci->lock);
if (HC_IS_RUNNING(hcd->state)) {
ohci_writel (ohci, ints, &regs->intrstatus);
ohci_writel (ohci, OHCI_INTR_MIE, &regs->intrenable);
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
}
return IRQ_HANDLED;
}
/*-------------------------------------------------------------------------*/
static void ohci_stop (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
hcd->state);
ohci_dump (ohci, 1);
flush_scheduled_work();
ohci_usb_reset (ohci);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
remove_debug_files (ohci);
ohci_mem_cleanup (ohci);
if (ohci->hcca) {
dma_free_coherent (hcd->self.controller,
sizeof *ohci->hcca,
ohci->hcca, ohci->hcca_dma);
ohci->hcca = NULL;
ohci->hcca_dma = 0;
}
}
/*-------------------------------------------------------------------------*/
/* must not be called from interrupt context */
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
static int ohci_restart (struct ohci_hcd *ohci)
{
int temp;
int i;
struct urb_priv *priv;
struct usb_device *root = ohci_to_hcd(ohci)->self.root_hub;
/* mark any devices gone, so they do nothing till khubd disconnects.
* recycle any "live" eds/tds (and urbs) right away.
* later, khubd disconnect processing will recycle the other state,
* (either as disconnect/reconnect, or maybe someday as a reset).
*/
spin_lock_irq(&ohci->lock);
disable (ohci);
for (i = 0; i < root->maxchild; i++) {
if (root->children [i])
usb_set_device_state (root->children[i],
USB_STATE_NOTATTACHED);
}
if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
struct urb *urb = priv->td[0]->urb;
struct ed *ed = priv->ed;
switch (ed->state) {
case ED_OPER:
ed->state = ED_UNLINK;
ed->hwINFO |= cpu_to_hc32(ohci, ED_DEQUEUE);
ed_deschedule (ohci, ed);
ed->ed_next = ohci->ed_rm_list;
ed->ed_prev = NULL;
ohci->ed_rm_list = ed;
/* FALLTHROUGH */
case ED_UNLINK:
break;
default:
ohci_dbg(ohci, "bogus ed %p state %d\n",
ed, ed->state);
}
spin_lock (&urb->lock);
urb->status = -ESHUTDOWN;
spin_unlock (&urb->lock);
}
finish_unlinks (ohci, 0, NULL);
spin_unlock_irq(&ohci->lock);
/* paranoia, in case that didn't work: */
/* empty the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
/* no EDs to remove */
ohci->ed_rm_list = NULL;
/* empty control and bulk lists */
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
if ((temp = ohci_run (ohci)) < 0) {
ohci_err (ohci, "can't restart, %d\n", temp);
return temp;
} else {
/* here we "know" root ports should always stay powered,
* and that if we try to turn them back on the root hub
* will respond to CSC processing.
*/
i = roothub_a (ohci) & RH_A_NDP;
while (i--)
ohci_writel (ohci, RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]);
ohci_dbg (ohci, "restart complete\n");
}
return 0;
}
#endif
/*-------------------------------------------------------------------------*/
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_INFO);
MODULE_LICENSE ("GPL");
#ifdef CONFIG_PCI
#include "ohci-pci.c"
#endif
#ifdef CONFIG_SA1111
#include "ohci-sa1111.c"
#endif
#ifdef CONFIG_ARCH_OMAP
#include "ohci-omap.c"
#endif
#ifdef CONFIG_ARCH_LH7A404
#include "ohci-lh7a404.c"
#endif
#ifdef CONFIG_PXA27x
#include "ohci-pxa27x.c"
#endif
#ifdef CONFIG_SOC_AU1X00
#include "ohci-au1xxx.c"
#endif
#ifdef CONFIG_USB_OHCI_HCD_PPC_SOC
#include "ohci-ppc-soc.c"
#endif
#if !(defined(CONFIG_PCI) \
|| defined(CONFIG_SA1111) \
|| defined(CONFIG_ARCH_OMAP) \
|| defined (CONFIG_ARCH_LH7A404) \
|| defined (CONFIG_PXA27x) \
|| defined (CONFIG_SOC_AU1X00) \
|| defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
)
#error "missing bus glue for ohci-hcd"
#endif

643
drivers/usb/host/ohci-hub.c Normal file
View file

@ -0,0 +1,643 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under GPL
*/
/*-------------------------------------------------------------------------*/
/*
* OHCI Root Hub ... the nonsharable stuff
*/
#define dbg_port(hc,label,num,value) \
ohci_dbg (hc, \
"%s roothub.portstatus [%d] " \
"= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
label, num, temp, \
(temp & RH_PS_PRSC) ? " PRSC" : "", \
(temp & RH_PS_OCIC) ? " OCIC" : "", \
(temp & RH_PS_PSSC) ? " PSSC" : "", \
(temp & RH_PS_PESC) ? " PESC" : "", \
(temp & RH_PS_CSC) ? " CSC" : "", \
\
(temp & RH_PS_LSDA) ? " LSDA" : "", \
(temp & RH_PS_PPS) ? " PPS" : "", \
(temp & RH_PS_PRS) ? " PRS" : "", \
(temp & RH_PS_POCI) ? " POCI" : "", \
(temp & RH_PS_PSS) ? " PSS" : "", \
\
(temp & RH_PS_PES) ? " PES" : "", \
(temp & RH_PS_CCS) ? " CCS" : "" \
);
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
static void dl_done_list (struct ohci_hcd *, struct pt_regs *);
static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *);
static int ohci_restart (struct ohci_hcd *ohci);
static int ohci_hub_suspend (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int status = 0;
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_RESUME:
ohci_dbg (ohci, "resume/suspend?\n");
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci->hc_control |= OHCI_USB_RESET;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
/* FALL THROUGH */
case OHCI_USB_RESET:
status = -EBUSY;
ohci_dbg (ohci, "needs reinit!\n");
goto done;
case OHCI_USB_SUSPEND:
ohci_dbg (ohci, "already suspended\n");
goto done;
}
ohci_dbg (ohci, "suspend root hub\n");
/* First stop any processing */
hcd->state = HC_STATE_QUIESCING;
if (ohci->hc_control & OHCI_SCHED_ENABLES) {
int limit;
ohci->hc_control &= ~OHCI_SCHED_ENABLES;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus);
/* sched disables take effect on the next frame,
* then the last WDH could take 6+ msec
*/
ohci_dbg (ohci, "stopping schedules ...\n");
limit = 2000;
while (limit > 0) {
udelay (250);
limit =- 250;
if (ohci_readl (ohci, &ohci->regs->intrstatus)
& OHCI_INTR_SF)
break;
}
dl_done_list (ohci, NULL);
mdelay (7);
}
dl_done_list (ohci, NULL);
finish_unlinks (ohci, ohci_frame_no(ohci), NULL);
ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
&ohci->regs->intrstatus);
/* maybe resume can wake root hub */
if (hcd->remote_wakeup)
ohci->hc_control |= OHCI_CTRL_RWE;
else
ohci->hc_control &= ~OHCI_CTRL_RWE;
/* Suspend hub */
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci->hc_control |= OHCI_USB_SUSPEND;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
/* no resumes until devices finish suspending */
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
done:
if (status == 0)
hcd->state = HC_STATE_SUSPENDED;
spin_unlock_irqrestore (&ohci->lock, flags);
return status;
}
static inline struct ed *find_head (struct ed *ed)
{
/* for bulk and control lists */
while (ed->ed_prev)
ed = ed->ed_prev;
return ed;
}
/* caller has locked the root hub */
static int ohci_hub_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
u32 temp, enables;
int status = -EINPROGRESS;
if (time_before (jiffies, ohci->next_statechange))
msleep(5);
spin_lock_irq (&ohci->lock);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
/* this can happen after suspend-to-disk */
if (hcd->state == HC_STATE_RESUMING) {
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
ohci->hc_control);
status = -EBUSY;
/* this happens when pmcore resumes HC then root */
} else {
ohci_dbg (ohci, "duplicate resume\n");
status = 0;
}
} else switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_SUSPEND:
ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
ohci->hc_control |= OHCI_USB_RESUME;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
ohci_dbg (ohci, "resume root hub\n");
break;
case OHCI_USB_RESUME:
/* HCFS changes sometime after INTR_RD */
ohci_info (ohci, "wakeup\n");
break;
case OHCI_USB_OPER:
ohci_dbg (ohci, "already resumed\n");
status = 0;
break;
default: /* RESET, we lost power */
ohci_dbg (ohci, "root hub hardware reset\n");
status = -EBUSY;
}
spin_unlock_irq (&ohci->lock);
if (status == -EBUSY) {
(void) ohci_init (ohci);
return ohci_restart (ohci);
}
if (status != -EINPROGRESS)
return status;
temp = roothub_a (ohci) & RH_A_NDP;
enables = 0;
while (temp--) {
u32 stat = ohci_readl (ohci,
&ohci->regs->roothub.portstatus [temp]);
/* force global, not selective, resume */
if (!(stat & RH_PS_PSS))
continue;
ohci_writel (ohci, RH_PS_POCI,
&ohci->regs->roothub.portstatus [temp]);
}
/* Some controllers (lucent erratum) need extra-long delays */
hcd->state = HC_STATE_RESUMING;
mdelay (20 /* usb 11.5.1.10 */ + 15);
temp = ohci_readl (ohci, &ohci->regs->control);
temp &= OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
ohci_err (ohci, "controller won't resume\n");
return -EBUSY;
}
/* disable old schedule state, reinit from scratch */
ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
ohci_writel (ohci, 0, &ohci->regs->ed_controlcurrent);
ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
ohci_writel (ohci, 0, &ohci->regs->ed_bulkcurrent);
ohci_writel (ohci, 0, &ohci->regs->ed_periodcurrent);
ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
/* Sometimes PCI D3 suspend trashes frame timings ... */
periodic_reinit (ohci);
/* interrupts might have been disabled */
ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
if (ohci->ed_rm_list)
ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
&ohci->regs->intrstatus);
/* Then re-enable operations */
ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
msleep (3);
temp = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (hcd->can_wakeup)
temp |= OHCI_CTRL_RWC;
ohci->hc_control = temp;
ohci_writel (ohci, temp, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
/* TRSMRCY */
msleep (10);
/* keep it alive for ~5x suspend + resume costs */
ohci->next_statechange = jiffies + msecs_to_jiffies (250);
/* maybe turn schedules back on */
enables = 0;
temp = 0;
if (!ohci->ed_rm_list) {
if (ohci->ed_controltail) {
ohci_writel (ohci,
find_head (ohci->ed_controltail)->dma,
&ohci->regs->ed_controlhead);
enables |= OHCI_CTRL_CLE;
temp |= OHCI_CLF;
}
if (ohci->ed_bulktail) {
ohci_writel (ohci, find_head (ohci->ed_bulktail)->dma,
&ohci->regs->ed_bulkhead);
enables |= OHCI_CTRL_BLE;
temp |= OHCI_BLF;
}
}
if (hcd->self.bandwidth_isoc_reqs || hcd->self.bandwidth_int_reqs)
enables |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
if (enables) {
ohci_dbg (ohci, "restarting schedules ... %08x\n", enables);
ohci->hc_control |= enables;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
if (temp)
ohci_writel (ohci, temp, &ohci->regs->cmdstatus);
(void) ohci_readl (ohci, &ohci->regs->control);
}
hcd->state = HC_STATE_RUNNING;
return 0;
}
static void ohci_rh_resume (void *_hcd)
{
struct usb_hcd *hcd = _hcd;
usb_lock_device (hcd->self.root_hub);
(void) ohci_hub_resume (hcd);
usb_unlock_device (hcd->self.root_hub);
}
#else
static void ohci_rh_resume (void *_hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (_hcd);
ohci_dbg(ohci, "rh_resume ??\n");
}
#endif /* CONFIG_USB_SUSPEND || CONFIG_PM */
/*-------------------------------------------------------------------------*/
/* build "status change" packet (one or two bytes) from HC registers */
static int
ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ports, i, changed = 0, length = 1;
int can_suspend = hcd->can_wakeup;
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
/* handle autosuspended root: finish resuming before
* letting khubd or root hub timer see state changes.
*/
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER
|| !HC_IS_RUNNING(hcd->state)) {
can_suspend = 0;
goto done;
}
ports = roothub_a (ohci) & RH_A_NDP;
if (ports > MAX_ROOT_PORTS) {
ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports,
ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */
goto done;
}
/* init status */
if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
buf [0] = changed = 1;
else
buf [0] = 0;
if (ports > 7) {
buf [1] = 0;
length++;
}
/* look at each port */
for (i = 0; i < ports; i++) {
u32 status = roothub_portstatus (ohci, i);
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
| RH_PS_OCIC | RH_PS_PRSC)) {
changed = 1;
if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
continue;
}
/* can suspend if no ports are enabled; or if all all
* enabled ports are suspended AND remote wakeup is on.
*/
if (!(status & RH_PS_CCS))
continue;
if ((status & RH_PS_PSS) && hcd->remote_wakeup)
continue;
can_suspend = 0;
}
done:
spin_unlock_irqrestore (&ohci->lock, flags);
#ifdef CONFIG_PM
/* save power by suspending idle root hubs;
* INTR_RD wakes us when there's work
* NOTE: if we can do this, we don't need a root hub timer!
*/
if (can_suspend
&& !changed
&& !ohci->ed_rm_list
&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
& ohci->hc_control)
== OHCI_USB_OPER
&& time_after (jiffies, ohci->next_statechange)
&& usb_trylock_device (hcd->self.root_hub)
) {
ohci_vdbg (ohci, "autosuspend\n");
(void) ohci_hub_suspend (hcd);
hcd->state = HC_STATE_RUNNING;
usb_unlock_device (hcd->self.root_hub);
}
#endif
return changed ? length : 0;
}
/*-------------------------------------------------------------------------*/
static void
ohci_hub_descriptor (
struct ohci_hcd *ohci,
struct usb_hub_descriptor *desc
) {
u32 rh = roothub_a (ohci);
int ports = rh & RH_A_NDP;
u16 temp;
desc->bDescriptorType = 0x29;
desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
desc->bHubContrCurrent = 0;
desc->bNbrPorts = ports;
temp = 1 + (ports / 8);
desc->bDescLength = 7 + 2 * temp;
temp = 0;
if (rh & RH_A_NPS) /* no power switching? */
temp |= 0x0002;
if (rh & RH_A_PSM) /* per-port power switching? */
temp |= 0x0001;
if (rh & RH_A_NOCP) /* no overcurrent reporting? */
temp |= 0x0010;
else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
temp |= 0x0008;
desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ohci, temp);
/* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
rh = roothub_b (ohci);
desc->bitmap [0] = rh & RH_B_DR;
if (ports > 7) {
desc->bitmap [1] = (rh & RH_B_DR) >> 8;
desc->bitmap [2] = desc->bitmap [3] = 0xff;
} else
desc->bitmap [1] = 0xff;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_OTG
static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
u32 status;
if (!port)
return -EINVAL;
port--;
/* start port reset before HNP protocol times out */
status = ohci_readl(ohci, &ohci->regs->roothub.portstatus [port]);
if (!(status & RH_PS_CCS))
return -ENODEV;
/* khubd will finish the reset later */
ohci_writel(ohci, RH_PS_PRS, &ohci->regs->roothub.portstatus [port]);
return 0;
}
static void start_hnp(struct ohci_hcd *ohci);
#else
#define ohci_start_port_reset NULL
#endif
/*-------------------------------------------------------------------------*/
/* See usb 7.1.7.5: root hubs must issue at least 50 msec reset signaling,
* not necessarily continuous ... to guard against resume signaling.
* The short timeout is safe for non-root hubs, and is backward-compatible
* with earlier Linux hosts.
*/
#ifdef CONFIG_USB_SUSPEND
#define PORT_RESET_MSEC 50
#else
#define PORT_RESET_MSEC 10
#endif
/* this timer value might be vendor-specific ... */
#define PORT_RESET_HW_MSEC 10
/* wrap-aware logic morphed from <linux/jiffies.h> */
#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
/* called from some task, normally khubd */
static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port)
{
__hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port];
u32 temp;
u16 now = ohci_readl(ohci, &ohci->regs->fmnumber);
u16 reset_done = now + PORT_RESET_MSEC;
/* build a "continuous enough" reset signal, with up to
* 3msec gap between pulses. scheduler HZ==100 must work;
* this might need to be deadline-scheduled.
*/
do {
/* spin until any current reset finishes */
for (;;) {
temp = ohci_readl (ohci, portstat);
if (!(temp & RH_PS_PRS))
break;
udelay (500);
}
if (!(temp & RH_PS_CCS))
break;
if (temp & RH_PS_PRSC)
ohci_writel (ohci, RH_PS_PRSC, portstat);
/* start the next reset, sleep till it's probably done */
ohci_writel (ohci, RH_PS_PRS, portstat);
msleep(PORT_RESET_HW_MSEC);
now = ohci_readl(ohci, &ohci->regs->fmnumber);
} while (tick_before(now, reset_done));
/* caller synchronizes using PRSC */
}
static int ohci_hub_control (
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength
) {
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ports = hcd_to_bus (hcd)->root_hub->maxchild;
u32 temp;
int retval = 0;
switch (typeReq) {
case ClearHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
ohci_writel (ohci, RH_HS_OCIC,
&ohci->regs->roothub.status);
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case ClearPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
temp = RH_PS_CCS;
break;
case USB_PORT_FEAT_C_ENABLE:
temp = RH_PS_PESC;
break;
case USB_PORT_FEAT_SUSPEND:
temp = RH_PS_POCI;
if ((ohci->hc_control & OHCI_CTRL_HCFS)
!= OHCI_USB_OPER)
schedule_work (&ohci->rh_resume);
break;
case USB_PORT_FEAT_C_SUSPEND:
temp = RH_PS_PSSC;
break;
case USB_PORT_FEAT_POWER:
temp = RH_PS_LSDA;
break;
case USB_PORT_FEAT_C_CONNECTION:
temp = RH_PS_CSC;
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
temp = RH_PS_OCIC;
break;
case USB_PORT_FEAT_C_RESET:
temp = RH_PS_PRSC;
break;
default:
goto error;
}
ohci_writel (ohci, temp,
&ohci->regs->roothub.portstatus [wIndex]);
// ohci_readl (ohci, &ohci->regs->roothub.portstatus [wIndex]);
break;
case GetHubDescriptor:
ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
break;
case GetHubStatus:
temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
*(__le32 *) buf = cpu_to_le32 (temp);
break;
case GetPortStatus:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
temp = roothub_portstatus (ohci, wIndex);
*(__le32 *) buf = cpu_to_le32 (temp);
#ifndef OHCI_VERBOSE_DEBUG
if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
#endif
dbg_port (ohci, "GetStatus", wIndex, temp);
break;
case SetHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
// FIXME: this can be cleared, yes?
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case SetPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
#ifdef CONFIG_USB_OTG
if (hcd->self.otg_port == (wIndex + 1)
&& hcd->self.b_hnp_enable)
start_hnp(ohci);
else
#endif
ohci_writel (ohci, RH_PS_PSS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
case USB_PORT_FEAT_POWER:
ohci_writel (ohci, RH_PS_PPS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
case USB_PORT_FEAT_RESET:
root_port_reset (ohci, wIndex);
break;
default:
goto error;
}
break;
default:
error:
/* "protocol stall" on error */
retval = -EPIPE;
}
return retval;
}

View file

@ -0,0 +1,266 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
*
* Bus Glue for Sharp LH7A404
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Rusell King et al.
*
* Modified for LH7A404 from ohci-sa1111.c
* by Durgesh Pattamatta <pattamattad@sharpsec.com>
*
* This file is licenced under the GPL.
*/
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/hardware.h>
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void lh7a404_start_hc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": starting LH7A404 OHCI USB Controller\n");
/*
* Now, carefully enable the USB clock, and take
* the USB host controller out of reset.
*/
CSC_PWRCNT |= CSC_PWRCNT_USBH_EN; /* Enable clock */
udelay(1000);
USBH_CMDSTATUS = OHCI_HCR;
printk(KERN_DEBUG __FILE__
": Clock to USB host has been enabled \n");
}
static void lh7a404_stop_hc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": stopping LH7A404 OHCI USB Controller\n");
CSC_PWRCNT &= ~CSC_PWRCNT_USBH_EN; /* Disable clock */
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_lh7a404_probe - initialize LH7A404-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
int usb_hcd_lh7a404_probe (const struct hc_driver *driver,
struct platform_device *dev)
{
int retval;
struct usb_hcd *hcd;
if (dev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
hcd = usb_create_hcd(driver, &dev->dev, "lh7a404");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
retval = -ENOMEM;
goto err2;
}
lh7a404_start_hc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
if (retval == 0)
return retval;
lh7a404_stop_hc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_lh7a404_remove - shutdown processing for LH7A404-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_lh7a404_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_lh7a404_remove (struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
lh7a404_stop_hc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_lh7a404_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci);
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_lh7a404_hc_driver = {
.description = hcd_name,
.product_desc = "LH7A404 OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_lh7a404_start,
#ifdef CONFIG_PM
/* suspend: ohci_lh7a404_suspend, -- tbd */
/* resume: ohci_lh7a404_resume, -- tbd */
#endif /*CONFIG_PM*/
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_lh7a404_drv_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int ret;
pr_debug ("In ohci_hcd_lh7a404_drv_probe");
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_lh7a404_probe(&ohci_lh7a404_hc_driver, pdev);
return ret;
}
static int ohci_hcd_lh7a404_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
usb_hcd_lh7a404_remove(hcd, pdev);
return 0;
}
/*TBD*/
/*static int ohci_hcd_lh7a404_drv_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
return 0;
}
static int ohci_hcd_lh7a404_drv_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
return 0;
}
*/
static struct device_driver ohci_hcd_lh7a404_driver = {
.name = "lh7a404-ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_lh7a404_drv_probe,
.remove = ohci_hcd_lh7a404_drv_remove,
/*.suspend = ohci_hcd_lh7a404_drv_suspend, */
/*.resume = ohci_hcd_lh7a404_drv_resume, */
};
static int __init ohci_hcd_lh7a404_init (void)
{
pr_debug (DRIVER_INFO " (LH7A404)");
pr_debug ("block sizes: ed %d td %d\n",
sizeof (struct ed), sizeof (struct td));
return driver_register(&ohci_hcd_lh7a404_driver);
}
static void __exit ohci_hcd_lh7a404_cleanup (void)
{
driver_unregister(&ohci_hcd_lh7a404_driver);
}
module_init (ohci_hcd_lh7a404_init);
module_exit (ohci_hcd_lh7a404_cleanup);

139
drivers/usb/host/ohci-mem.c Normal file
View file

@ -0,0 +1,139 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
/*-------------------------------------------------------------------------*/
/*
* There's basically three types of memory:
* - data used only by the HCD ... kmalloc is fine
* - async and periodic schedules, shared by HC and HCD ... these
* need to use dma_pool or dma_alloc_coherent
* - driver buffers, read/written by HC ... the hcd glue or the
* device driver provides us with dma addresses
*
* There's also PCI "register" data, which is memory mapped.
* No memory seen by this driver is pagable.
*/
/*-------------------------------------------------------------------------*/
static void ohci_hcd_init (struct ohci_hcd *ohci)
{
ohci->next_statechange = jiffies;
spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending);
INIT_WORK (&ohci->rh_resume, ohci_rh_resume, ohci_to_hcd(ohci));
}
/*-------------------------------------------------------------------------*/
static int ohci_mem_init (struct ohci_hcd *ohci)
{
ohci->td_cache = dma_pool_create ("ohci_td",
ohci_to_hcd(ohci)->self.controller,
sizeof (struct td),
32 /* byte alignment */,
0 /* no page-crossing issues */);
if (!ohci->td_cache)
return -ENOMEM;
ohci->ed_cache = dma_pool_create ("ohci_ed",
ohci_to_hcd(ohci)->self.controller,
sizeof (struct ed),
16 /* byte alignment */,
0 /* no page-crossing issues */);
if (!ohci->ed_cache) {
dma_pool_destroy (ohci->td_cache);
return -ENOMEM;
}
return 0;
}
static void ohci_mem_cleanup (struct ohci_hcd *ohci)
{
if (ohci->td_cache) {
dma_pool_destroy (ohci->td_cache);
ohci->td_cache = NULL;
}
if (ohci->ed_cache) {
dma_pool_destroy (ohci->ed_cache);
ohci->ed_cache = NULL;
}
}
/*-------------------------------------------------------------------------*/
/* ohci "done list" processing needs this mapping */
static inline struct td *
dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
{
struct td *td;
td_dma &= TD_MASK;
td = hc->td_hash [TD_HASH_FUNC(td_dma)];
while (td && td->td_dma != td_dma)
td = td->td_hash;
return td;
}
/* TDs ... */
static struct td *
td_alloc (struct ohci_hcd *hc, int mem_flags)
{
dma_addr_t dma;
struct td *td;
td = dma_pool_alloc (hc->td_cache, mem_flags, &dma);
if (td) {
/* in case hc fetches it, make it look dead */
memset (td, 0, sizeof *td);
td->hwNextTD = cpu_to_hc32 (hc, dma);
td->td_dma = dma;
/* hashed in td_fill */
}
return td;
}
static void
td_free (struct ohci_hcd *hc, struct td *td)
{
struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
while (*prev && *prev != td)
prev = &(*prev)->td_hash;
if (*prev)
*prev = td->td_hash;
else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0)
ohci_dbg (hc, "no hash for td %p\n", td);
dma_pool_free (hc->td_cache, td, td->td_dma);
}
/*-------------------------------------------------------------------------*/
/* EDs ... */
static struct ed *
ed_alloc (struct ohci_hcd *hc, int mem_flags)
{
dma_addr_t dma;
struct ed *ed;
ed = dma_pool_alloc (hc->ed_cache, mem_flags, &dma);
if (ed) {
memset (ed, 0, sizeof (*ed));
INIT_LIST_HEAD (&ed->td_list);
ed->dma = dma;
}
return ed;
}
static void
ed_free (struct ohci_hcd *hc, struct ed *ed)
{
dma_pool_free (hc->ed_cache, ed, ed->dma);
}

View file

@ -0,0 +1,560 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2005 David Brownell
* (C) Copyright 2002 Hewlett-Packard Company
*
* OMAP Bus Glue
*
* Modified for OMAP by Tony Lindgren <tony@atomide.com>
* Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc.
* and on ohci-sa1111.c by Christopher Hoover <ch@hpl.hp.com>
*
* This file is licenced under the GPL.
*/
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/arch/hardware.h>
#include <asm/arch/mux.h>
#include <asm/arch/irqs.h>
#include <asm/arch/gpio.h>
#include <asm/arch/fpga.h>
#include <asm/arch/usb.h>
#include <asm/hardware/clock.h>
/* OMAP-1510 OHCI has its own MMU for DMA */
#define OMAP1510_LB_MEMSIZE 32 /* Should be same as SDRAM size */
#define OMAP1510_LB_CLOCK_DIV 0xfffec10c
#define OMAP1510_LB_MMU_CTL 0xfffec208
#define OMAP1510_LB_MMU_LCK 0xfffec224
#define OMAP1510_LB_MMU_LD_TLB 0xfffec228
#define OMAP1510_LB_MMU_CAM_H 0xfffec22c
#define OMAP1510_LB_MMU_CAM_L 0xfffec230
#define OMAP1510_LB_MMU_RAM_H 0xfffec234
#define OMAP1510_LB_MMU_RAM_L 0xfffec238
#ifndef CONFIG_ARCH_OMAP
#error "This file is OMAP bus glue. CONFIG_OMAP must be defined."
#endif
#ifdef CONFIG_TPS65010
#include <asm/arch/tps65010.h>
#else
#define LOW 0
#define HIGH 1
#define GPIO1 1
static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
{
return 0;
}
#endif
extern int usb_disabled(void);
extern int ocpi_enable(void);
static struct clk *usb_host_ck;
static void omap_ohci_clock_power(int on)
{
if (on) {
clk_enable(usb_host_ck);
/* guesstimate for T5 == 1x 32K clock + APLL lock time */
udelay(100);
} else {
clk_disable(usb_host_ck);
}
}
/*
* Board specific gang-switched transceiver power on/off.
* NOTE: OSK supplies power from DC, not battery.
*/
static int omap_ohci_transceiver_power(int on)
{
if (on) {
if (machine_is_omap_innovator() && cpu_is_omap1510())
fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
| ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk())
tps65010_set_gpio_out_value(GPIO1, LOW);
} else {
if (machine_is_omap_innovator() && cpu_is_omap1510())
fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
& ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk())
tps65010_set_gpio_out_value(GPIO1, HIGH);
}
return 0;
}
/*
* OMAP-1510 specific Local Bus clock on/off
*/
static int omap_1510_local_bus_power(int on)
{
if (on) {
omap_writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL);
udelay(200);
} else {
omap_writel(0, OMAP1510_LB_MMU_CTL);
}
return 0;
}
/*
* OMAP-1510 specific Local Bus initialization
* NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
* See also arch/mach-omap/memory.h for __virt_to_dma() and
* __dma_to_virt() which need to match with the physical
* Local Bus address below.
*/
static int omap_1510_local_bus_init(void)
{
unsigned int tlb;
unsigned long lbaddr, physaddr;
omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
OMAP1510_LB_CLOCK_DIV);
/* Configure the Local Bus MMU table */
for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) {
lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
physaddr = tlb * 0x00100000 + PHYS_OFFSET;
omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
OMAP1510_LB_MMU_CAM_L);
omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
omap_writel(tlb << 4, OMAP1510_LB_MMU_LCK);
omap_writel(0x1, OMAP1510_LB_MMU_LD_TLB);
}
/* Enable the walking table */
omap_writel(omap_readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL);
udelay(200);
return 0;
}
#ifdef CONFIG_USB_OTG
static void start_hnp(struct ohci_hcd *ohci)
{
const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1;
unsigned long flags;
otg_start_hnp(ohci->transceiver);
local_irq_save(flags);
ohci->transceiver->state = OTG_STATE_A_SUSPEND;
writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]);
OTG_CTRL_REG &= ~OTG_A_BUSREQ;
local_irq_restore(flags);
}
#endif
/*-------------------------------------------------------------------------*/
static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
{
struct omap_usb_config *config = pdev->dev.platform_data;
int need_transceiver = (config->otg != 0);
int ret;
dev_dbg(&pdev->dev, "starting USB Controller\n");
if (config->otg) {
ohci_to_hcd(ohci)->self.otg_port = config->otg;
/* default/minimum OTG power budget: 8 mA */
ohci->power_budget = 8;
}
/* boards can use OTG transceivers in non-OTG modes */
need_transceiver = need_transceiver
|| machine_is_omap_h2() || machine_is_omap_h3();
if (cpu_is_omap16xx())
ocpi_enable();
#ifdef CONFIG_ARCH_OMAP_OTG
if (need_transceiver) {
ohci->transceiver = otg_get_transceiver();
if (ohci->transceiver) {
int status = otg_set_host(ohci->transceiver,
&ohci_to_hcd(ohci)->self);
dev_dbg(&pdev->dev, "init %s transceiver, status %d\n",
ohci->transceiver->label, status);
if (status) {
if (ohci->transceiver)
put_device(ohci->transceiver->dev);
return status;
}
} else {
dev_err(&pdev->dev, "can't find transceiver\n");
return -ENODEV;
}
}
#endif
omap_ohci_clock_power(1);
if (cpu_is_omap1510()) {
omap_1510_local_bus_power(1);
omap_1510_local_bus_init();
}
if ((ret = ohci_init(ohci)) < 0)
return ret;
/* board-specific power switching and overcurrent support */
if (machine_is_omap_osk() || machine_is_omap_innovator()) {
u32 rh = roothub_a (ohci);
/* power switching (ganged by default) */
rh &= ~RH_A_NPS;
/* TPS2045 switch for internal transceiver (port 1) */
if (machine_is_omap_osk()) {
ohci->power_budget = 250;
rh &= ~RH_A_NOCP;
/* gpio9 for overcurrent detction */
omap_cfg_reg(W8_1610_GPIO9);
omap_request_gpio(9);
omap_set_gpio_direction(9, 1 /* IN */);
/* for paranoia's sake: disable USB.PUEN */
omap_cfg_reg(W4_USB_HIGHZ);
}
ohci_writel(ohci, rh, &ohci->regs->roothub.a);
distrust_firmware = 0;
}
/* FIXME khubd hub requests should manage power switching */
omap_ohci_transceiver_power(1);
/* board init will have already handled HMC and mux setup.
* any external transceiver should already be initialized
* too, so all configured ports use the right signaling now.
*/
return 0;
}
static void omap_stop_hc(struct platform_device *pdev)
{
dev_dbg(&pdev->dev, "stopping USB Controller\n");
omap_ohci_clock_power(0);
}
/*-------------------------------------------------------------------------*/
void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_omap_probe - initialize OMAP-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*/
int usb_hcd_omap_probe (const struct hc_driver *driver,
struct platform_device *pdev)
{
int retval;
struct usb_hcd *hcd = 0;
struct ohci_hcd *ohci;
if (pdev->num_resources != 2) {
printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
pdev->num_resources);
return -ENODEV;
}
if (pdev->resource[0].flags != IORESOURCE_MEM
|| pdev->resource[1].flags != IORESOURCE_IRQ) {
printk(KERN_ERR "hcd probe: invalid resource type\n");
return -ENODEV;
}
usb_host_ck = clk_get(0, "usb_hhc_ck");
if (IS_ERR(usb_host_ck))
return PTR_ERR(usb_host_ck);
hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
if (!hcd) {
retval = -ENOMEM;
goto err0;
}
hcd->rsrc_start = pdev->resource[0].start;
hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dev_dbg(&pdev->dev, "request_mem_region failed\n");
retval = -EBUSY;
goto err1;
}
hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start);
ohci = hcd_to_ohci(hcd);
ohci_hcd_init(ohci);
retval = omap_start_hc(ohci, pdev);
if (retval < 0)
goto err2;
retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), SA_INTERRUPT);
if (retval == 0)
return retval;
omap_stop_hc(pdev);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
err0:
clk_put(usb_host_ck);
return retval;
}
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_omap_remove - shutdown processing for OMAP-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_omap_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
{
usb_remove_hcd(hcd);
if (machine_is_omap_osk())
omap_free_gpio(9);
omap_stop_hc(pdev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
clk_put(usb_host_ck);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_omap_start (struct usb_hcd *hcd)
{
struct omap_usb_config *config;
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
config = hcd->self.controller->platform_data;
if (config->otg || config->rwc)
writel(OHCI_CTRL_RWC, &ohci->regs->control);
if ((ret = ohci_run (ohci)) < 0) {
dev_err(hcd->self.controller, "can't start\n");
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_omap_hc_driver = {
.description = hcd_name,
.product_desc = "OMAP OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_omap_start,
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_omap_drv_probe(struct device *dev)
{
return usb_hcd_omap_probe(&ohci_omap_hc_driver,
to_platform_device(dev));
}
static int ohci_hcd_omap_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
usb_hcd_omap_remove(hcd, pdev);
if (ohci->transceiver) {
(void) otg_set_host(ohci->transceiver, 0);
put_device(ohci->transceiver->dev);
}
dev_set_drvdata(dev, NULL);
return 0;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/* states match PCI usage, always suspending the root hub except that
* 4 ~= D3cold (ACPI D3) with clock off (resume sees reset).
*/
static int ohci_omap_suspend(struct device *dev, u32 state, u32 level)
{
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
int status = -EINVAL;
if (level != SUSPEND_POWER_DOWN)
return 0;
if (state <= dev->power.power_state)
return 0;
dev_dbg(dev, "suspend to %d\n", state);
down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
status = ohci_hub_suspend(ohci_to_hcd(ohci));
if (status == 0) {
if (state >= 4) {
omap_ohci_clock_power(0);
ohci_to_hcd(ohci)->self.root_hub->state =
USB_STATE_SUSPENDED;
state = 4;
}
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
dev->power.power_state = state;
}
up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
return status;
}
static int ohci_omap_resume(struct device *dev, u32 level)
{
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
int status = 0;
if (level != RESUME_POWER_ON)
return 0;
switch (dev->power.power_state) {
case 0:
break;
case 4:
if (time_before(jiffies, ohci->next_statechange))
msleep(5);
ohci->next_statechange = jiffies;
omap_ohci_clock_power(1);
/* FALLTHROUGH */
default:
dev_dbg(dev, "resume from %d\n", dev->power.power_state);
#ifdef CONFIG_USB_SUSPEND
/* get extra cleanup even if remote wakeup isn't in use */
status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub);
#else
down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
status = ohci_hub_resume(ohci_to_hcd(ohci));
up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
#endif
if (status == 0)
dev->power.power_state = 0;
break;
}
return status;
}
#endif
/*-------------------------------------------------------------------------*/
/*
* Driver definition to register with the OMAP bus
*/
static struct device_driver ohci_hcd_omap_driver = {
.name = "ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_omap_drv_probe,
.remove = ohci_hcd_omap_drv_remove,
#ifdef CONFIG_PM
.suspend = ohci_omap_suspend,
.resume = ohci_omap_resume,
#endif
};
static int __init ohci_hcd_omap_init (void)
{
printk (KERN_DEBUG "%s: " DRIVER_INFO " (OMAP)\n", hcd_name);
if (usb_disabled())
return -ENODEV;
pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td));
return driver_register(&ohci_hcd_omap_driver);
}
static void __exit ohci_hcd_omap_cleanup (void)
{
driver_unregister(&ohci_hcd_omap_driver);
}
module_init (ohci_hcd_omap_init);
module_exit (ohci_hcd_omap_cleanup);

264
drivers/usb/host/ohci-pci.c Normal file
View file

@ -0,0 +1,264 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
* [ (C) Copyright 1999 Linus Torvalds ]
* [ (C) Copyright 1999 Gregory P. Smith]
*
* PCI Bus Glue
*
* This file is licenced under the GPL.
*/
#ifdef CONFIG_PMAC_PBOOK
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pci-bridge.h>
#include <asm/prom.h>
#ifndef CONFIG_PM
# define CONFIG_PM
#endif
#endif
#ifndef CONFIG_PCI
#error "This file is PCI bus glue. CONFIG_PCI must be defined."
#endif
/*-------------------------------------------------------------------------*/
static int
ohci_pci_reset (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
ohci_hcd_init (ohci);
return ohci_init (ohci);
}
static int __devinit
ohci_pci_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) {
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
/* AMD 756, for most chips (early revs), corrupts register
* values on read ... so enable the vendor workaround.
*/
if (pdev->vendor == PCI_VENDOR_ID_AMD
&& pdev->device == 0x740c) {
ohci->flags = OHCI_QUIRK_AMD756;
ohci_info (ohci, "AMD756 erratum 4 workaround\n");
// also somewhat erratum 10 (suspend/resume issues)
}
/* FIXME for some of the early AMD 760 southbridges, OHCI
* won't work at all. blacklist them.
*/
/* Apple's OHCI driver has a lot of bizarre workarounds
* for this chip. Evidently control and bulk lists
* can get confused. (B&W G3 models, and ...)
*/
else if (pdev->vendor == PCI_VENDOR_ID_OPTI
&& pdev->device == 0xc861) {
ohci_info (ohci,
"WARNING: OPTi workarounds unavailable\n");
}
/* Check for NSC87560. We have to look at the bridge (fn1) to
* identify the USB (fn2). This quirk might apply to more or
* even all NSC stuff.
*/
else if (pdev->vendor == PCI_VENDOR_ID_NS) {
struct pci_dev *b;
b = pci_find_slot (pdev->bus->number,
PCI_DEVFN (PCI_SLOT (pdev->devfn), 1));
if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO
&& b->vendor == PCI_VENDOR_ID_NS) {
ohci->flags |= OHCI_QUIRK_SUPERIO;
ohci_info (ohci, "Using NSC SuperIO setup\n");
}
}
}
/* NOTE: there may have already been a first reset, to
* keep bios/smm irqs from making trouble
*/
if ((ret = ohci_run (ohci)) < 0) {
ohci_err (ohci, "can't start\n");
ohci_stop (hcd);
return ret;
}
return 0;
}
#ifdef CONFIG_PM
static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
/* suspend root hub, hoping it keeps power during suspend */
if (time_before (jiffies, ohci->next_statechange))
msleep (100);
#ifdef CONFIG_USB_SUSPEND
(void) usb_suspend_device (hcd->self.root_hub, state);
#else
usb_lock_device (hcd->self.root_hub);
(void) ohci_hub_suspend (hcd);
usb_unlock_device (hcd->self.root_hub);
#endif
/* let things settle down a bit */
msleep (100);
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac) {
struct device_node *of_node;
/* Disable USB PAD & cell clock */
of_node = pci_device_to_OF_node (to_pci_dev(hcd->self.controller));
if (of_node)
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
}
#endif /* CONFIG_PMAC_PBOOK */
return 0;
}
static int ohci_pci_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int retval = 0;
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac) {
struct device_node *of_node;
/* Re-enable USB PAD & cell clock */
of_node = pci_device_to_OF_node (to_pci_dev(hcd->self.controller));
if (of_node)
pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);
}
#endif /* CONFIG_PMAC_PBOOK */
/* resume root hub */
if (time_before (jiffies, ohci->next_statechange))
msleep (100);
#ifdef CONFIG_USB_SUSPEND
/* get extra cleanup even if remote wakeup isn't in use */
retval = usb_resume_device (hcd->self.root_hub);
#else
usb_lock_device (hcd->self.root_hub);
retval = ohci_hub_resume (hcd);
usb_unlock_device (hcd->self.root_hub);
#endif
return retval;
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_pci_hc_driver = {
.description = hcd_name,
.product_desc = "OHCI Host Controller",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_MEMORY | HCD_USB11,
/*
* basic lifecycle operations
*/
.reset = ohci_pci_reset,
.start = ohci_pci_start,
#ifdef CONFIG_PM
.suspend = ohci_pci_suspend,
.resume = ohci_pci_resume,
#endif
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
/*-------------------------------------------------------------------------*/
static const struct pci_device_id pci_ids [] = { {
/* handle any USB OHCI controller */
PCI_DEVICE_CLASS((PCI_CLASS_SERIAL_USB << 8) | 0x10, ~0),
.driver_data = (unsigned long) &ohci_pci_hc_driver,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE (pci, pci_ids);
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver ohci_pci_driver = {
.name = (char *) hcd_name,
.id_table = pci_ids,
.probe = usb_hcd_pci_probe,
.remove = usb_hcd_pci_remove,
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.resume = usb_hcd_pci_resume,
#endif
};
static int __init ohci_hcd_pci_init (void)
{
printk (KERN_DEBUG "%s: " DRIVER_INFO " (PCI)\n", hcd_name);
if (usb_disabled())
return -ENODEV;
pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td));
return pci_register_driver (&ohci_pci_driver);
}
module_init (ohci_hcd_pci_init);
/*-------------------------------------------------------------------------*/
static void __exit ohci_hcd_pci_cleanup (void)
{
pci_unregister_driver (&ohci_pci_driver);
}
module_exit (ohci_hcd_pci_cleanup);

View file

@ -0,0 +1,234 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
* (C) Copyright 2003-2005 MontaVista Software Inc.
*
* Bus Glue for PPC On-Chip OHCI driver
* Tested on Freescale MPC5200 and IBM STB04xxx
*
* Modified by Dale Farnsworth <dale@farnsworth.org> from ohci-sa1111.c
*
* This file is licenced under the GPL.
*/
#include <asm/usb.h>
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_ppc_soc_probe - initialize On-Chip HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
*/
static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
struct platform_device *pdev)
{
int retval;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct resource *res;
int irq;
struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
pr_debug("initializing PPC-SOC USB Controller\n");
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
pr_debug(__FILE__ ": no irq\n");
return -ENODEV;
}
irq = res->start;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
pr_debug(__FILE__ ": no reg addr\n");
return -ENODEV;
}
hcd = usb_create_hcd(driver, &pdev->dev, "PPC-SOC USB");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = res->start;
hcd->rsrc_len = res->end - res->start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug(__FILE__ ": request_mem_region failed\n");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug(__FILE__ ": ioremap failed\n");
retval = -ENOMEM;
goto err2;
}
if (pd->start && (retval = pd->start(pdev)))
goto err3;
ohci = hcd_to_ohci(hcd);
ohci->flags |= OHCI_BIG_ENDIAN;
ohci_hcd_init(ohci);
retval = usb_add_hcd(hcd, irq, SA_INTERRUPT);
if (retval == 0)
return retval;
pr_debug("Removing PPC-SOC USB Controller\n");
if (pd && pd->stop)
pd->stop(pdev);
err3:
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_ppc_soc_remove - shutdown processing for On-Chip HCDs
* @pdev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_ppc_soc_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
static void usb_hcd_ppc_soc_remove(struct usb_hcd *hcd,
struct platform_device *pdev)
{
struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
usb_remove_hcd(hcd);
pr_debug("stopping PPC-SOC USB Controller\n");
if (pd && pd->stop)
pd->stop(pdev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_hcd_put(hcd);
}
static int __devinit
ohci_ppc_soc_start(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
int ret;
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run(ohci)) < 0) {
err("can't start %s", ohci_to_hcd(ohci)->self.bus_name);
ohci_stop(hcd);
return ret;
}
return 0;
}
static const struct hc_driver ohci_ppc_soc_hc_driver = {
.description = hcd_name,
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_ppc_soc_start,
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
static int ohci_hcd_ppc_soc_drv_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int ret;
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_ppc_soc_probe(&ohci_ppc_soc_hc_driver, pdev);
return ret;
}
static int ohci_hcd_ppc_soc_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
usb_hcd_ppc_soc_remove(hcd, pdev);
return 0;
}
static struct device_driver ohci_hcd_ppc_soc_driver = {
.name = "ppc-soc-ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_ppc_soc_drv_probe,
.remove = ohci_hcd_ppc_soc_drv_remove,
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
/*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/
/*.resume = ohci_hcd_ppc_soc_drv_resume,*/
#endif
};
static int __init ohci_hcd_ppc_soc_init(void)
{
pr_debug(DRIVER_INFO " (PPC SOC)\n");
pr_debug("block sizes: ed %d td %d\n", sizeof(struct ed),
sizeof(struct td));
return driver_register(&ohci_hcd_ppc_soc_driver);
}
static void __exit ohci_hcd_ppc_soc_cleanup(void)
{
driver_unregister(&ohci_hcd_ppc_soc_driver);
}
module_init(ohci_hcd_ppc_soc_init);
module_exit(ohci_hcd_ppc_soc_cleanup);

View file

@ -0,0 +1,383 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
*
* Bus Glue for pxa27x
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Russell King et al.
*
* Modified for LH7A404 from ohci-sa1111.c
* by Durgesh Pattamatta <pattamattad@sharpsec.com>
*
* Modified for pxa27x from ohci-lh7a404.c
* by Nick Bane <nick@cecomputing.co.uk> 26-8-2004
*
* This file is licenced under the GPL.
*/
#include <linux/device.h>
#include <asm/mach-types.h>
#include <asm/hardware.h>
#include <asm/arch/pxa-regs.h>
#define PMM_NPS_MODE 1
#define PMM_GLOBAL_MODE 2
#define PMM_PERPORT_MODE 3
#define PXA_UHC_MAX_PORTNUM 3
#define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 )
static int pxa27x_ohci_pmm_state;
/*
PMM_NPS_MODE -- PMM Non-power switching mode
Ports are powered continuously.
PMM_GLOBAL_MODE -- PMM global switching mode
All ports are powered at the same time.
PMM_PERPORT_MODE -- PMM per port switching mode
Ports are powered individually.
*/
static int pxa27x_ohci_select_pmm( int mode )
{
pxa27x_ohci_pmm_state = mode;
switch ( mode ) {
case PMM_NPS_MODE:
UHCRHDA |= RH_A_NPS;
break;
case PMM_GLOBAL_MODE:
UHCRHDA &= ~(RH_A_NPS & RH_A_PSM);
break;
case PMM_PERPORT_MODE:
UHCRHDA &= ~(RH_A_NPS);
UHCRHDA |= RH_A_PSM;
/* Set port power control mask bits, only 3 ports. */
UHCRHDB |= (0x7<<17);
break;
default:
printk( KERN_ERR
"Invalid mode %d, set to non-power switch mode.\n",
mode );
pxa27x_ohci_pmm_state = PMM_NPS_MODE;
UHCRHDA |= RH_A_NPS;
}
return 0;
}
/*
If you select PMM_PERPORT_MODE, you should set the port power
*/
static int pxa27x_ohci_set_port_power( int port )
{
if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
&& (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
UHCRHPS(port) |= 0x100;
return 0;
}
return -1;
}
/*
If you select PMM_PERPORT_MODE, you should set the port power
*/
static int pxa27x_ohci_clear_port_power( int port )
{
if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
&& (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
UHCRHPS(port) |= 0x200;
return 0;
}
return -1;
}
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void pxa27x_start_hc(struct platform_device *dev)
{
pxa_set_cken(CKEN10_USBHOST, 1);
UHCHR |= UHCHR_FHR;
udelay(11);
UHCHR &= ~UHCHR_FHR;
UHCHR |= UHCHR_FSBIR;
while (UHCHR & UHCHR_FSBIR)
cpu_relax();
/* This could be properly abstracted away through the
device data the day more machines are supported and
their differences can be figured out correctly. */
if (machine_is_mainstone()) {
/* setup Port1 GPIO pin. */
pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */
pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */
/* Set the Power Control Polarity Low and Power Sense
Polarity Low to active low. Supply power to USB ports. */
UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE);
}
UHCHR &= ~UHCHR_SSE;
UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE);
}
static void pxa27x_stop_hc(struct platform_device *dev)
{
UHCHR |= UHCHR_FHR;
udelay(11);
UHCHR &= ~UHCHR_FHR;
UHCCOMS |= 1;
udelay(10);
pxa_set_cken(CKEN10_USBHOST, 0);
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
int usb_hcd_pxa27x_probe (const struct hc_driver *driver,
struct platform_device *dev)
{
int retval;
struct usb_hcd *hcd;
if (dev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug ("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
hcd = usb_create_hcd (driver, &dev->dev, "pxa27x");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
retval = -ENOMEM;
goto err2;
}
pxa27x_start_hc(dev);
/* Select Power Management Mode */
pxa27x_ohci_select_pmm( PMM_PERPORT_MODE );
/* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */
if (pxa27x_ohci_set_port_power(1) < 0)
printk(KERN_ERR "Setting port 1 power failed.\n");
if (pxa27x_ohci_clear_port_power(2) < 0)
printk(KERN_ERR "Setting port 2 power failed.\n");
if (pxa27x_ohci_clear_port_power(3) < 0)
printk(KERN_ERR "Setting port 3 power failed.\n");
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
if (retval == 0)
return retval;
pxa27x_stop_hc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_pxa27x_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
pxa27x_stop_hc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_pxa27x_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_pxa27x_hc_driver = {
.description = hcd_name,
.product_desc = "PXA27x OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_pxa27x_start,
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_pxa27x_drv_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int ret;
pr_debug ("In ohci_hcd_pxa27x_drv_probe");
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev);
return ret;
}
static int ohci_hcd_pxa27x_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
usb_hcd_pxa27x_remove(hcd, pdev);
return 0;
}
static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, u32 state, u32 level)
{
// struct platform_device *pdev = to_platform_device(dev);
// struct usb_hcd *hcd = dev_get_drvdata(dev);
printk("%s: not implemented yet\n", __FUNCTION__);
return 0;
}
static int ohci_hcd_pxa27x_drv_resume(struct device *dev, u32 state)
{
// struct platform_device *pdev = to_platform_device(dev);
// struct usb_hcd *hcd = dev_get_drvdata(dev);
printk("%s: not implemented yet\n", __FUNCTION__);
return 0;
}
static struct device_driver ohci_hcd_pxa27x_driver = {
.name = "pxa27x-ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_pxa27x_drv_probe,
.remove = ohci_hcd_pxa27x_drv_remove,
.suspend = ohci_hcd_pxa27x_drv_suspend,
.resume = ohci_hcd_pxa27x_drv_resume,
};
static int __init ohci_hcd_pxa27x_init (void)
{
pr_debug (DRIVER_INFO " (pxa27x)");
pr_debug ("block sizes: ed %d td %d\n",
sizeof (struct ed), sizeof (struct td));
return driver_register(&ohci_hcd_pxa27x_driver);
}
static void __exit ohci_hcd_pxa27x_cleanup (void)
{
driver_unregister(&ohci_hcd_pxa27x_driver);
}
module_init (ohci_hcd_pxa27x_init);
module_exit (ohci_hcd_pxa27x_cleanup);

1107
drivers/usb/host/ohci-q.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,289 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
*
* SA1111 Bus Glue
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Rusell King et al.
*
* This file is licenced under the GPL.
*/
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/assabet.h>
#include <asm/arch/badge4.h>
#include <asm/hardware/sa1111.h>
#ifndef CONFIG_SA1111
#error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined."
#endif
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void sa1111_start_hc(struct sa1111_dev *dev)
{
unsigned int usb_rst = 0;
printk(KERN_DEBUG __FILE__
": starting SA-1111 OHCI USB Controller\n");
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4()) {
badge4_set_5V(BADGE4_5V_USB, 1);
}
#endif
if (machine_is_xp860() ||
machine_has_neponset() ||
machine_is_pfs168() ||
machine_is_badge4())
usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
/*
* Configure the power sense and control lines. Place the USB
* host controller in reset.
*/
sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
dev->mapbase + SA1111_USB_RESET);
/*
* Now, carefully enable the USB clock, and take
* the USB host controller out of reset.
*/
sa1111_enable_device(dev);
udelay(11);
sa1111_writel(usb_rst, dev->mapbase + SA1111_USB_RESET);
}
static void sa1111_stop_hc(struct sa1111_dev *dev)
{
unsigned int usb_rst;
printk(KERN_DEBUG __FILE__
": stopping SA-1111 OHCI USB Controller\n");
/*
* Put the USB host controller into reset.
*/
usb_rst = sa1111_readl(dev->mapbase + SA1111_USB_RESET);
sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
dev->mapbase + SA1111_USB_RESET);
/*
* Stop the USB clock.
*/
sa1111_disable_device(dev);
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4()) {
/* Disable power to the USB bus */
badge4_set_5V(BADGE4_5V_USB, 0);
}
#endif
}
/*-------------------------------------------------------------------------*/
#if 0
static void dump_hci_status(struct usb_hcd *hcd, const char *label)
{
unsigned long status = sa1111_readl(hcd->regs + SA1111_USB_STATUS);
dbg ("%s USB_STATUS = { %s%s%s%s%s}", label,
((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""),
((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "),
((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "),
((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : ""));
}
#endif
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_sa1111_probe - initialize SA-1111-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
*/
int usb_hcd_sa1111_probe (const struct hc_driver *driver,
struct sa1111_dev *dev)
{
struct usb_hcd *hcd;
int retval;
hcd = usb_create_hcd (driver, &dev->dev, "sa1111");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->res.start;
hcd->rsrc_len = dev->res.end - dev->res.start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dbg("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = dev->mapbase;
sa1111_start_hc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->irq[1], SA_INTERRUPT);
if (retval == 0)
return retval;
sa1111_stop_hc(dev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_sa1111_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_sa1111_remove (struct usb_hcd *hcd, struct sa1111_dev *dev)
{
usb_remove_hcd(hcd);
sa1111_stop_hc(dev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_sa1111_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_sa1111_hc_driver = {
.description = hcd_name,
.product_desc = "SA-1111 OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_sa1111_start,
#ifdef CONFIG_PM
/* suspend: ohci_sa1111_suspend, -- tbd */
/* resume: ohci_sa1111_resume, -- tbd */
#endif
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_sa1111_drv_probe(struct sa1111_dev *dev)
{
int ret;
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, dev);
return ret;
}
static int ohci_hcd_sa1111_drv_remove(struct sa1111_dev *dev)
{
struct usb_hcd *hcd = sa1111_get_drvdata(dev);
usb_hcd_sa1111_remove(hcd, dev);
return 0;
}
static struct sa1111_driver ohci_hcd_sa1111_driver = {
.drv = {
.name = "sa1111-ohci",
},
.devid = SA1111_DEVID_USB,
.probe = ohci_hcd_sa1111_drv_probe,
.remove = ohci_hcd_sa1111_drv_remove,
};
static int __init ohci_hcd_sa1111_init (void)
{
dbg (DRIVER_INFO " (SA-1111)");
dbg ("block sizes: ed %d td %d",
sizeof (struct ed), sizeof (struct td));
return sa1111_driver_register(&ohci_hcd_sa1111_driver);
}
static void __exit ohci_hcd_sa1111_cleanup (void)
{
sa1111_driver_unregister(&ohci_hcd_sa1111_driver);
}
module_init (ohci_hcd_sa1111_init);
module_exit (ohci_hcd_sa1111_cleanup);

636
drivers/usb/host/ohci.h Normal file
View file

@ -0,0 +1,636 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
/*
* __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
* __leXX (normally) or __beXX (given OHCI_BIG_ENDIAN), depending on the
* host controller implementation.
*/
typedef __u32 __bitwise __hc32;
typedef __u16 __bitwise __hc16;
/*
* OHCI Endpoint Descriptor (ED) ... holds TD queue
* See OHCI spec, section 4.2
*
* This is a "Queue Head" for those transfers, which is why
* both EHCI and UHCI call similar structures a "QH".
*/
struct ed {
/* first fields are hardware-specified */
__hc32 hwINFO; /* endpoint config bitmap */
/* info bits defined by hcd */
#define ED_DEQUEUE (1 << 27)
/* info bits defined by the hardware */
#define ED_ISO (1 << 15)
#define ED_SKIP (1 << 14)
#define ED_LOWSPEED (1 << 13)
#define ED_OUT (0x01 << 11)
#define ED_IN (0x02 << 11)
__hc32 hwTailP; /* tail of TD list */
__hc32 hwHeadP; /* head of TD list (hc r/w) */
#define ED_C (0x02) /* toggle carry */
#define ED_H (0x01) /* halted */
__hc32 hwNextED; /* next ED in list */
/* rest are purely for the driver's use */
dma_addr_t dma; /* addr of ED */
struct td *dummy; /* next TD to activate */
/* host's view of schedule */
struct ed *ed_next; /* on schedule or rm_list */
struct ed *ed_prev; /* for non-interrupt EDs */
struct list_head td_list; /* "shadow list" of our TDs */
/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
* usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
*/
u8 state; /* ED_{IDLE,UNLINK,OPER} */
#define ED_IDLE 0x00 /* NOT linked to HC */
#define ED_UNLINK 0x01 /* being unlinked from hc */
#define ED_OPER 0x02 /* IS linked to hc */
u8 type; /* PIPE_{BULK,...} */
/* periodic scheduling params (for intr and iso) */
u8 branch;
u16 interval;
u16 load;
u16 last_iso; /* iso only */
/* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick;
} __attribute__ ((aligned(16)));
#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */
/*
* OHCI Transfer Descriptor (TD) ... one per transfer segment
* See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt)
* and 4.3.2 (iso)
*/
struct td {
/* first fields are hardware-specified */
__hc32 hwINFO; /* transfer info bitmask */
/* hwINFO bits for both general and iso tds: */
#define TD_CC 0xf0000000 /* condition code */
#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
//#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
#define TD_DI 0x00E00000 /* frames before interrupt */
#define TD_DI_SET(X) (((X) & 0x07)<< 21)
/* these two bits are available for definition/use by HCDs in both
* general and iso tds ... others are available for only one type
*/
#define TD_DONE 0x00020000 /* retired to donelist */
#define TD_ISO 0x00010000 /* copy of ED_ISO */
/* hwINFO bits for general tds: */
#define TD_EC 0x0C000000 /* error count */
#define TD_T 0x03000000 /* data toggle state */
#define TD_T_DATA0 0x02000000 /* DATA0 */
#define TD_T_DATA1 0x03000000 /* DATA1 */
#define TD_T_TOGGLE 0x00000000 /* uses ED_C */
#define TD_DP 0x00180000 /* direction/pid */
#define TD_DP_SETUP 0x00000000 /* SETUP pid */
#define TD_DP_IN 0x00100000 /* IN pid */
#define TD_DP_OUT 0x00080000 /* OUT pid */
/* 0x00180000 rsvd */
#define TD_R 0x00040000 /* round: short packets OK? */
/* (no hwINFO #defines yet for iso tds) */
__hc32 hwCBP; /* Current Buffer Pointer (or 0) */
__hc32 hwNextTD; /* Next TD Pointer */
__hc32 hwBE; /* Memory Buffer End Pointer */
/* PSW is only for ISO. Only 1 PSW entry is used, but on
* big-endian PPC hardware that's the second entry.
*/
#define MAXPSW 2
__hc16 hwPSW [MAXPSW];
/* rest are purely for the driver's use */
__u8 index;
struct ed *ed;
struct td *td_hash; /* dma-->td hashtable */
struct td *next_dl_td;
struct urb *urb;
dma_addr_t td_dma; /* addr of this TD */
dma_addr_t data_dma; /* addr of data it points to */
struct list_head td_list; /* "shadow list", TDs on same ED */
} __attribute__ ((aligned(32))); /* c/b/i need 16; only iso needs 32 */
#define TD_MASK ((u32)~0x1f) /* strip hw status in low addr bits */
/*
* Hardware transfer status codes -- CC from td->hwINFO or td->hwPSW
*/
#define TD_CC_NOERROR 0x00
#define TD_CC_CRC 0x01
#define TD_CC_BITSTUFFING 0x02
#define TD_CC_DATATOGGLEM 0x03
#define TD_CC_STALL 0x04
#define TD_DEVNOTRESP 0x05
#define TD_PIDCHECKFAIL 0x06
#define TD_UNEXPECTEDPID 0x07
#define TD_DATAOVERRUN 0x08
#define TD_DATAUNDERRUN 0x09
/* 0x0A, 0x0B reserved for hardware */
#define TD_BUFFEROVERRUN 0x0C
#define TD_BUFFERUNDERRUN 0x0D
/* 0x0E, 0x0F reserved for HCD */
#define TD_NOTACCESSED 0x0F
/* map OHCI TD status codes (CC) to errno values */
static const int cc_to_error [16] = {
/* No Error */ 0,
/* CRC Error */ -EILSEQ,
/* Bit Stuff */ -EPROTO,
/* Data Togg */ -EILSEQ,
/* Stall */ -EPIPE,
/* DevNotResp */ -ETIMEDOUT,
/* PIDCheck */ -EPROTO,
/* UnExpPID */ -EPROTO,
/* DataOver */ -EOVERFLOW,
/* DataUnder */ -EREMOTEIO,
/* (for hw) */ -EIO,
/* (for hw) */ -EIO,
/* BufferOver */ -ECOMM,
/* BuffUnder */ -ENOSR,
/* (for HCD) */ -EALREADY,
/* (for HCD) */ -EALREADY
};
/*
* The HCCA (Host Controller Communications Area) is a 256 byte
* structure defined section 4.4.1 of the OHCI spec. The HC is
* told the base address of it. It must be 256-byte aligned.
*/
struct ohci_hcca {
#define NUM_INTS 32
__hc32 int_table [NUM_INTS]; /* periodic schedule */
/*
* OHCI defines u16 frame_no, followed by u16 zero pad.
* Since some processors can't do 16 bit bus accesses,
* portable access must be a 32 bits wide.
*/
__hc32 frame_no; /* current frame number */
__hc32 done_head; /* info returned for an interrupt */
u8 reserved_for_hc [116];
u8 what [4]; /* spec only identifies 252 bytes :) */
} __attribute__ ((aligned(256)));
/*
* This is the structure of the OHCI controller's memory mapped I/O region.
* You must use readl() and writel() (in <asm/io.h>) to access these fields!!
* Layout is in section 7 (and appendix B) of the spec.
*/
struct ohci_regs {
/* control and status registers (section 7.1) */
__hc32 revision;
__hc32 control;
__hc32 cmdstatus;
__hc32 intrstatus;
__hc32 intrenable;
__hc32 intrdisable;
/* memory pointers (section 7.2) */
__hc32 hcca;
__hc32 ed_periodcurrent;
__hc32 ed_controlhead;
__hc32 ed_controlcurrent;
__hc32 ed_bulkhead;
__hc32 ed_bulkcurrent;
__hc32 donehead;
/* frame counters (section 7.3) */
__hc32 fminterval;
__hc32 fmremaining;
__hc32 fmnumber;
__hc32 periodicstart;
__hc32 lsthresh;
/* Root hub ports (section 7.4) */
struct ohci_roothub_regs {
__hc32 a;
__hc32 b;
__hc32 status;
#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports (RH_A_NDP) */
__hc32 portstatus [MAX_ROOT_PORTS];
} roothub;
/* and optional "legacy support" registers (appendix B) at 0x0100 */
} __attribute__ ((aligned(32)));
/* OHCI CONTROL AND STATUS REGISTER MASKS */
/*
* HcControl (control) register masks
*/
#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
/* pre-shifted values for HCFS */
# define OHCI_USB_RESET (0 << 6)
# define OHCI_USB_RESUME (1 << 6)
# define OHCI_USB_OPER (2 << 6)
# define OHCI_USB_SUSPEND (3 << 6)
/*
* HcCommandStatus (cmdstatus) register masks
*/
#define OHCI_HCR (1 << 0) /* host controller reset */
#define OHCI_CLF (1 << 1) /* control list filled */
#define OHCI_BLF (1 << 2) /* bulk list filled */
#define OHCI_OCR (1 << 3) /* ownership change request */
#define OHCI_SOC (3 << 16) /* scheduling overrun count */
/*
* masks used with interrupt registers:
* HcInterruptStatus (intrstatus)
* HcInterruptEnable (intrenable)
* HcInterruptDisable (intrdisable)
*/
#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
#define OHCI_INTR_SF (1 << 2) /* start frame */
#define OHCI_INTR_RD (1 << 3) /* resume detect */
#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
#define OHCI_INTR_OC (1 << 30) /* ownership change */
#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
/* OHCI ROOT HUB REGISTER MASKS */
/* roothub.portstatus [i] bits */
#define RH_PS_CCS 0x00000001 /* current connect status */
#define RH_PS_PES 0x00000002 /* port enable status*/
#define RH_PS_PSS 0x00000004 /* port suspend status */
#define RH_PS_POCI 0x00000008 /* port over current indicator */
#define RH_PS_PRS 0x00000010 /* port reset status */
#define RH_PS_PPS 0x00000100 /* port power status */
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
#define RH_PS_CSC 0x00010000 /* connect status change */
#define RH_PS_PESC 0x00020000 /* port enable status change */
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
#define RH_PS_PRSC 0x00100000 /* port reset status change */
/* roothub.status bits */
#define RH_HS_LPS 0x00000001 /* local power status */
#define RH_HS_OCI 0x00000002 /* over current indicator */
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
#define RH_HS_LPSC 0x00010000 /* local power status change */
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
/* roothub.b masks */
#define RH_B_DR 0x0000ffff /* device removable flags */
#define RH_B_PPCM 0xffff0000 /* port power control mask */
/* roothub.a masks */
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
#define RH_A_PSM (1 << 8) /* power switching mode */
#define RH_A_NPS (1 << 9) /* no power switching */
#define RH_A_DT (1 << 10) /* device type (mbz) */
#define RH_A_OCPM (1 << 11) /* over current protection mode */
#define RH_A_NOCP (1 << 12) /* no over current protection */
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
/* hcd-private per-urb state */
typedef struct urb_priv {
struct ed *ed;
u16 length; // # tds in this request
u16 td_cnt; // tds already serviced
struct list_head pending;
struct td *td [0]; // all TDs in this request
} urb_priv_t;
#define TD_HASH_SIZE 64 /* power'o'two */
// sizeof (struct td) ~= 64 == 2^6 ...
#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
/*
* This is the full ohci controller description
*
* Note how the "proper" USB information is just
* a subset of what the full implementation needs. (Linus)
*/
struct ohci_hcd {
spinlock_t lock;
/*
* I/O memory used to communicate with the HC (dma-consistent)
*/
struct ohci_regs __iomem *regs;
/*
* main memory used to communicate with the HC (dma-consistent).
* hcd adds to schedule for a live hc any time, but removals finish
* only at the start of the next frame.
*/
struct ohci_hcca *hcca;
dma_addr_t hcca_dma;
struct ed *ed_rm_list; /* to be removed */
struct ed *ed_bulktail; /* last in bulk list */
struct ed *ed_controltail; /* last in ctrl list */
struct ed *periodic [NUM_INTS]; /* shadow int_table */
/*
* OTG controllers and transceivers need software interaction;
* other external transceivers should be software-transparent
*/
struct otg_transceiver *transceiver;
unsigned power_budget;
/*
* memory management for queue data structures
*/
struct dma_pool *td_cache;
struct dma_pool *ed_cache;
struct td *td_hash [TD_HASH_SIZE];
struct list_head pending;
/*
* driver state
*/
int load [NUM_INTS];
u32 hc_control; /* copy of hc control reg */
unsigned long next_statechange; /* suspend/resume */
u32 fminterval; /* saved register */
struct work_struct rh_resume;
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */
#define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */
#define OHCI_BIG_ENDIAN 0x08 /* big endian HC */
// there are also chip quirks/bugs in init logic
};
/* convert between an hcd pointer and the corresponding ohci_hcd */
static inline struct ohci_hcd *hcd_to_ohci (struct usb_hcd *hcd)
{
return (struct ohci_hcd *) (hcd->hcd_priv);
}
static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci)
{
return container_of ((void *) ohci, struct usb_hcd, hcd_priv);
}
/*-------------------------------------------------------------------------*/
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
#define ohci_dbg(ohci, fmt, args...) \
dev_dbg (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
#define ohci_err(ohci, fmt, args...) \
dev_err (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
#define ohci_info(ohci, fmt, args...) \
dev_info (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
#define ohci_warn(ohci, fmt, args...) \
dev_warn (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
#ifdef OHCI_VERBOSE_DEBUG
# define ohci_vdbg ohci_dbg
#else
# define ohci_vdbg(ohci, fmt, args...) do { } while (0)
#endif
/*-------------------------------------------------------------------------*/
/*
* While most USB host controllers implement their registers and
* in-memory communication descriptors in little-endian format,
* a minority (notably the IBM STB04XXX and the Motorola MPC5200
* processors) implement them in big endian format.
*
* This attempts to support either format at compile time without a
* runtime penalty, or both formats with the additional overhead
* of checking a flag bit.
*/
#ifdef CONFIG_USB_OHCI_BIG_ENDIAN
#ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN
#define big_endian(ohci) (ohci->flags & OHCI_BIG_ENDIAN) /* either */
#else
#define big_endian(ohci) 1 /* only big endian */
#endif
/*
* Big-endian read/write functions are arch-specific.
* Other arches can be added if/when they're needed.
*/
#if defined(CONFIG_PPC)
#define readl_be(addr) in_be32((__force unsigned *)addr)
#define writel_be(val, addr) out_be32((__force unsigned *)addr, val)
#endif
static inline unsigned int ohci_readl (const struct ohci_hcd *ohci,
__hc32 __iomem * regs)
{
return big_endian(ohci) ? readl_be (regs) : readl ((__force u32 *)regs);
}
static inline void ohci_writel (const struct ohci_hcd *ohci,
const unsigned int val, __hc32 __iomem *regs)
{
big_endian(ohci) ? writel_be (val, regs) :
writel (val, (__force u32 *)regs);
}
#else /* !CONFIG_USB_OHCI_BIG_ENDIAN */
#define big_endian(ohci) 0 /* only little endian */
#ifdef CONFIG_ARCH_LH7A404
/* Marc Singer: at the time this code was written, the LH7A404
* had a problem reading the USB host registers. This
* implementation of the ohci_readl function performs the read
* twice as a work-around.
*/
static inline unsigned int
ohci_readl (const struct ohci_hcd *ohci, const __hc32 *regs)
{
*(volatile __force unsigned int*) regs;
return *(volatile __force unsigned int*) regs;
}
#else
/* Standard version of ohci_readl uses standard, platform
* specific implementation. */
static inline unsigned int
ohci_readl (const struct ohci_hcd *ohci, __hc32 __iomem * regs)
{
return readl(regs);
}
#endif
static inline void ohci_writel (const struct ohci_hcd *ohci,
const unsigned int val, __hc32 __iomem *regs)
{
writel (val, regs);
}
#endif /* !CONFIG_USB_OHCI_BIG_ENDIAN */
/*-------------------------------------------------------------------------*/
/* cpu to ohci */
static inline __hc16 cpu_to_hc16 (const struct ohci_hcd *ohci, const u16 x)
{
return big_endian(ohci) ? (__force __hc16)cpu_to_be16(x) : (__force __hc16)cpu_to_le16(x);
}
static inline __hc16 cpu_to_hc16p (const struct ohci_hcd *ohci, const u16 *x)
{
return big_endian(ohci) ? cpu_to_be16p(x) : cpu_to_le16p(x);
}
static inline __hc32 cpu_to_hc32 (const struct ohci_hcd *ohci, const u32 x)
{
return big_endian(ohci) ? (__force __hc32)cpu_to_be32(x) : (__force __hc32)cpu_to_le32(x);
}
static inline __hc32 cpu_to_hc32p (const struct ohci_hcd *ohci, const u32 *x)
{
return big_endian(ohci) ? cpu_to_be32p(x) : cpu_to_le32p(x);
}
/* ohci to cpu */
static inline u16 hc16_to_cpu (const struct ohci_hcd *ohci, const __hc16 x)
{
return big_endian(ohci) ? be16_to_cpu((__force __be16)x) : le16_to_cpu((__force __le16)x);
}
static inline u16 hc16_to_cpup (const struct ohci_hcd *ohci, const __hc16 *x)
{
return big_endian(ohci) ? be16_to_cpup((__force __be16 *)x) : le16_to_cpup((__force __le16 *)x);
}
static inline u32 hc32_to_cpu (const struct ohci_hcd *ohci, const __hc32 x)
{
return big_endian(ohci) ? be32_to_cpu((__force __be32)x) : le32_to_cpu((__force __le32)x);
}
static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x)
{
return big_endian(ohci) ? be32_to_cpup((__force __be32 *)x) : le32_to_cpup((__force __le32 *)x);
}
/*-------------------------------------------------------------------------*/
/* HCCA frame number is 16 bits, but is accessed as 32 bits since not all
* hardware handles 16 bit reads. That creates a different confusion on
* some big-endian SOC implementations. Same thing happens with PSW access.
*/
#ifdef CONFIG_STB03xxx
#define OHCI_BE_FRAME_NO_SHIFT 16
#else
#define OHCI_BE_FRAME_NO_SHIFT 0
#endif
static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
{
u32 tmp;
if (big_endian(ohci)) {
tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no);
tmp >>= OHCI_BE_FRAME_NO_SHIFT;
} else
tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no);
return (u16)tmp;
}
static inline __hc16 *ohci_hwPSWp(const struct ohci_hcd *ohci,
const struct td *td, int index)
{
return (__hc16 *)(big_endian(ohci) ?
&td->hwPSW[index ^ 1] : &td->hwPSW[index]);
}
static inline u16 ohci_hwPSW(const struct ohci_hcd *ohci,
const struct td *td, int index)
{
return hc16_to_cpup(ohci, ohci_hwPSWp(ohci, td, index));
}
/*-------------------------------------------------------------------------*/
static inline void disable (struct ohci_hcd *ohci)
{
ohci_to_hcd(ohci)->state = HC_STATE_HALT;
}
#define FI 0x2edf /* 12000 bits per frame (-1) */
#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
#define FIT (1 << 31)
#define LSTHRESH 0x628 /* lowspeed bit threshold */
static void periodic_reinit (struct ohci_hcd *ohci)
{
u32 fi = ohci->fminterval & 0x03fff;
u32 fit = ohci_readl(ohci, &ohci->regs->fminterval) & FIT;
ohci_writel (ohci, (fit ^ FIT) | ohci->fminterval,
&ohci->regs->fminterval);
ohci_writel (ohci, ((9 * fi) / 10) & 0x3fff,
&ohci->regs->periodicstart);
}
/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
* The erratum (#4) description is incorrect. AMD's workaround waits
* till some bits (mostly reserved) are clear; ok for all revs.
*/
#define read_roothub(hc, register, mask) ({ \
u32 temp = ohci_readl (hc, &hc->regs->roothub.register); \
if (temp == -1) \
disable (hc); \
else if (hc->flags & OHCI_QUIRK_AMD756) \
while (temp & mask) \
temp = ohci_readl (hc, &hc->regs->roothub.register); \
temp; })
static u32 roothub_a (struct ohci_hcd *hc)
{ return read_roothub (hc, a, 0xfc0fe000); }
static inline u32 roothub_b (struct ohci_hcd *hc)
{ return ohci_readl (hc, &hc->regs->roothub.b); }
static inline u32 roothub_status (struct ohci_hcd *hc)
{ return ohci_readl (hc, &hc->regs->roothub.status); }
static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
{ return read_roothub (hc, portstatus [i], 0xffe0fce0); }

1851
drivers/usb/host/sl811-hcd.c Normal file

File diff suppressed because it is too large Load diff

266
drivers/usb/host/sl811.h Normal file
View file

@ -0,0 +1,266 @@
/*
* SL811HS register declarations and HCD data structures
*
* Copyright (C) 2004 Psion Teklogix
* Copyright (C) 2004 David Brownell
* Copyright (C) 2001 Cypress Semiconductor Inc.
*/
/*
* SL811HS has transfer registers, and control registers. In host/master
* mode one set of registers is used; in peripheral/slave mode, another.
* - SL11H only has some "A" transfer registers from 0x00-0x04
* - SL811HS also has "B" registers from 0x08-0x0c
* - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30
*/
#define SL811_EP_A(base) ((base) + 0)
#define SL811_EP_B(base) ((base) + 8)
#define SL811_HOST_BUF 0x00
#define SL811_PERIPH_EP0 0x00
#define SL811_PERIPH_EP1 0x10
#define SL811_PERIPH_EP2 0x20
#define SL811_PERIPH_EP3 0x30
/* TRANSFER REGISTERS: host and peripheral sides are similar
* except for the control models (master vs slave).
*/
#define SL11H_HOSTCTLREG 0
# define SL11H_HCTLMASK_ARM 0x01
# define SL11H_HCTLMASK_ENABLE 0x02
# define SL11H_HCTLMASK_IN 0x00
# define SL11H_HCTLMASK_OUT 0x04
# define SL11H_HCTLMASK_ISOCH 0x10
# define SL11H_HCTLMASK_AFTERSOF 0x20
# define SL11H_HCTLMASK_TOGGLE 0x40
# define SL11H_HCTLMASK_PREAMBLE 0x80
#define SL11H_BUFADDRREG 1
#define SL11H_BUFLNTHREG 2
#define SL11H_PKTSTATREG 3 /* read */
# define SL11H_STATMASK_ACK 0x01
# define SL11H_STATMASK_ERROR 0x02
# define SL11H_STATMASK_TMOUT 0x04
# define SL11H_STATMASK_SEQ 0x08
# define SL11H_STATMASK_SETUP 0x10
# define SL11H_STATMASK_OVF 0x20
# define SL11H_STATMASK_NAK 0x40
# define SL11H_STATMASK_STALL 0x80
#define SL11H_PIDEPREG 3 /* write */
# define SL_SETUP 0xd0
# define SL_IN 0x90
# define SL_OUT 0x10
# define SL_SOF 0x50
# define SL_PREAMBLE 0xc0
# define SL_NAK 0xa0
# define SL_STALL 0xe0
# define SL_DATA0 0x30
# define SL_DATA1 0xb0
#define SL11H_XFERCNTREG 4 /* read */
#define SL11H_DEVADDRREG 4 /* write */
/* CONTROL REGISTERS: host and peripheral are very different.
*/
#define SL11H_CTLREG1 5
# define SL11H_CTL1MASK_SOF_ENA 0x01
# define SL11H_CTL1MASK_FORCE 0x18
# define SL11H_CTL1MASK_NORMAL 0x00
# define SL11H_CTL1MASK_SE0 0x08 /* reset */
# define SL11H_CTL1MASK_J 0x10
# define SL11H_CTL1MASK_K 0x18 /* resume */
# define SL11H_CTL1MASK_LSPD 0x20
# define SL11H_CTL1MASK_SUSPEND 0x40
#define SL11H_IRQ_ENABLE 6
# define SL11H_INTMASK_DONE_A 0x01
# define SL11H_INTMASK_DONE_B 0x02
# define SL11H_INTMASK_SOFINTR 0x10
# define SL11H_INTMASK_INSRMV 0x20 /* to/from SE0 */
# define SL11H_INTMASK_RD 0x40
# define SL11H_INTMASK_DP 0x80 /* only in INTSTATREG */
#define SL11S_ADDRESS 7
/* 0x08-0x0c are for the B buffer (not in SL11) */
#define SL11H_IRQ_STATUS 0x0D /* write to ack */
#define SL11H_HWREVREG 0x0E /* read */
# define SL11H_HWRMASK_HWREV 0xF0
#define SL11H_SOFLOWREG 0x0E /* write */
#define SL11H_SOFTMRREG 0x0F /* read */
/* a write to this register enables SL811HS features.
* HOST flag presumably overrides the chip input signal?
*/
#define SL811HS_CTLREG2 0x0F
# define SL811HS_CTL2MASK_SOF_MASK 0x3F
# define SL811HS_CTL2MASK_DSWAP 0x40
# define SL811HS_CTL2MASK_HOST 0x80
#define SL811HS_CTL2_INIT (SL811HS_CTL2MASK_HOST | 0x2e)
/* DATA BUFFERS: registers from 0x10..0xff are for data buffers;
* that's 240 bytes, which we'll split evenly between A and B sides.
* Only ISO can use more than 64 bytes per packet.
* (The SL11S has 0x40..0xff for buffers.)
*/
#define H_MAXPACKET 120 /* bytes in A or B fifos */
#define SL11H_DATA_START 0x10
#define SL811HS_PACKET_BUF(is_a) ((is_a) \
? SL11H_DATA_START \
: (SL11H_DATA_START + H_MAXPACKET))
/*-------------------------------------------------------------------------*/
#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
struct sl811 {
spinlock_t lock;
void __iomem *addr_reg;
void __iomem *data_reg;
struct sl811_platform_data *board;
struct proc_dir_entry *pde;
unsigned long stat_insrmv;
unsigned long stat_wake;
unsigned long stat_sof;
unsigned long stat_a;
unsigned long stat_b;
unsigned long stat_lost;
unsigned long stat_overrun;
/* sw model */
struct timer_list timer;
struct sl811h_ep *next_periodic;
struct sl811h_ep *next_async;
struct sl811h_ep *active_a;
unsigned long jiffies_a;
struct sl811h_ep *active_b;
unsigned long jiffies_b;
u32 port1;
u8 ctrl1, ctrl2, irq_enable;
u16 frame;
/* async schedule: control, bulk */
struct list_head async;
/* periodic schedule: interrupt, iso */
u16 load[PERIODIC_SIZE];
struct sl811h_ep *periodic[PERIODIC_SIZE];
unsigned periodic_count;
};
static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd)
{
return (struct sl811 *) (hcd->hcd_priv);
}
static inline struct usb_hcd *sl811_to_hcd(struct sl811 *sl811)
{
return container_of((void *) sl811, struct usb_hcd, hcd_priv);
}
struct sl811h_ep {
struct usb_host_endpoint *hep;
struct usb_device *udev;
u8 defctrl;
u8 maxpacket;
u8 epnum;
u8 nextpid;
u16 error_count;
u16 nak_count;
u16 length; /* of current packet */
/* periodic schedule */
u16 period;
u16 branch;
u16 load;
struct sl811h_ep *next;
/* async schedule */
struct list_head schedule;
};
/*-------------------------------------------------------------------------*/
/* These register utilities should work for the SL811S register API too
* NOTE: caller must hold sl811->lock.
*/
static inline u8 sl811_read(struct sl811 *sl811, int reg)
{
writeb(reg, sl811->addr_reg);
return readb(sl811->data_reg);
}
static inline void sl811_write(struct sl811 *sl811, int reg, u8 val)
{
writeb(reg, sl811->addr_reg);
writeb(val, sl811->data_reg);
}
static inline void
sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count)
{
const u8 *data;
void __iomem *data_reg;
if (!count)
return;
writeb(addr, sl811->addr_reg);
data = buf;
data_reg = sl811->data_reg;
do {
writeb(*data++, data_reg);
} while (--count);
}
static inline void
sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count)
{
u8 *data;
void __iomem *data_reg;
if (!count)
return;
writeb(addr, sl811->addr_reg);
data = buf;
data_reg = sl811->data_reg;
do {
*data++ = readb(data_reg);
} while (--count);
}
/*-------------------------------------------------------------------------*/
#ifdef DEBUG
#define DBG(stuff...) printk(KERN_DEBUG "sl811: " stuff)
#else
#define DBG(stuff...) do{}while(0)
#endif
#ifdef VERBOSE
# define VDBG DBG
#else
# define VDBG(stuff...) do{}while(0)
#endif
#ifdef PACKET_TRACE
# define PACKET VDBG
#else
# define PACKET(stuff...) do{}while(0)
#endif
#define ERR(stuff...) printk(KERN_ERR "sl811: " stuff)
#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff)
#define INFO(stuff...) printk(KERN_INFO "sl811: " stuff)

View file

@ -0,0 +1,587 @@
/*
* UHCI-specific debugging code. Invaluable when something
* goes wrong, but don't get in my face.
*
* Kernel visible pointers are surrounded in []'s and bus
* visible pointers are surrounded in ()'s
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2001 Johannes Erdfelt
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/smp_lock.h>
#include <asm/io.h>
#include "uhci-hcd.h"
static struct dentry *uhci_debugfs_root = NULL;
/* Handle REALLY large printk's so we don't overflow buffers */
static inline void lprintk(char *buf)
{
char *p;
/* Just write one line at a time */
while (buf) {
p = strchr(buf, '\n');
if (p)
*p = 0;
printk(KERN_DEBUG "%s\n", buf);
buf = p;
if (buf)
buf++;
}
}
static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
{
char *out = buf;
char *spid;
u32 status, token;
/* Try to make sure there's enough memory */
if (len < 160)
return 0;
status = td_status(td);
out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, le32_to_cpu(td->link));
out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
((status >> 27) & 3),
(status & TD_CTRL_SPD) ? "SPD " : "",
(status & TD_CTRL_LS) ? "LS " : "",
(status & TD_CTRL_IOC) ? "IOC " : "",
(status & TD_CTRL_ACTIVE) ? "Active " : "",
(status & TD_CTRL_STALLED) ? "Stalled " : "",
(status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
(status & TD_CTRL_BABBLE) ? "Babble " : "",
(status & TD_CTRL_NAK) ? "NAK " : "",
(status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
(status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
status & 0x7ff);
token = td_token(td);
switch (uhci_packetid(token)) {
case USB_PID_SETUP:
spid = "SETUP";
break;
case USB_PID_OUT:
spid = "OUT";
break;
case USB_PID_IN:
spid = "IN";
break;
default:
spid = "?";
break;
}
out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
token >> 21,
((token >> 19) & 1),
(token >> 15) & 15,
(token >> 8) & 127,
(token & 0xff),
spid);
out += sprintf(out, "(buf=%08x)\n", le32_to_cpu(td->buffer));
return out - buf;
}
static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
{
char *out = buf;
struct urb_priv *urbp;
struct list_head *head, *tmp;
struct uhci_td *td;
int i = 0, checked = 0, prevactive = 0;
__le32 element = qh_element(qh);
/* Try to make sure there's enough memory */
if (len < 80 * 6)
return 0;
out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
qh, le32_to_cpu(qh->link), le32_to_cpu(element));
if (element & UHCI_PTR_QH)
out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
if (element & UHCI_PTR_DEPTH)
out += sprintf(out, "%*s Depth traverse\n", space, "");
if (element & cpu_to_le32(8))
out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, "");
if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
out += sprintf(out, "%*s Element is NULL (bug?)\n", space, "");
if (!qh->urbp) {
out += sprintf(out, "%*s urbp == NULL\n", space, "");
goto out;
}
urbp = qh->urbp;
head = &urbp->td_list;
tmp = head->next;
td = list_entry(tmp, struct uhci_td, list);
if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
out += sprintf(out, "%*s Element != First TD\n", space, "");
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
tmp = tmp->next;
out += sprintf(out, "%*s%d: ", space + 2, "", i++);
out += uhci_show_td(td, out, len - (out - buf), 0);
if (i > 10 && !checked && prevactive && tmp != head &&
debug <= 2) {
struct list_head *ntmp = tmp;
struct uhci_td *ntd = td;
int active = 1, ni = i;
checked = 1;
while (ntmp != head && ntmp->next != head && active) {
ntd = list_entry(ntmp, struct uhci_td, list);
ntmp = ntmp->next;
active = td_status(ntd) & TD_CTRL_ACTIVE;
ni++;
}
if (active && ni > i) {
out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i);
tmp = ntmp;
td = ntd;
i = ni;
}
}
prevactive = td_status(td) & TD_CTRL_ACTIVE;
}
if (list_empty(&urbp->queue_list) || urbp->queued)
goto out;
out += sprintf(out, "%*sQueued QH's:\n", -space, "--");
head = &urbp->queue_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
queue_list);
tmp = tmp->next;
out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
}
out:
return out - buf;
}
#define show_frame_num() \
if (!shown) { \
shown = 1; \
out += sprintf(out, "- Frame %d\n", i); \
}
#ifdef CONFIG_PROC_FS
static const char *qh_names[] = {
"skel_int128_qh", "skel_int64_qh",
"skel_int32_qh", "skel_int16_qh",
"skel_int8_qh", "skel_int4_qh",
"skel_int2_qh", "skel_int1_qh",
"skel_ls_control_qh", "skel_fs_control_qh",
"skel_bulk_qh", "skel_term_qh"
};
#define show_qh_name() \
if (!shown) { \
shown = 1; \
out += sprintf(out, "- %s\n", qh_names[i]); \
}
static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
{
char *out = buf;
/* Try to make sure there's enough memory */
if (len < 160)
return 0;
out += sprintf(out, " stat%d = %04x %s%s%s%s%s%s%s%s%s%s\n",
port,
status,
(status & USBPORTSC_SUSP) ? " Suspend" : "",
(status & USBPORTSC_OCC) ? " OverCurrentChange" : "",
(status & USBPORTSC_OC) ? " OverCurrent" : "",
(status & USBPORTSC_PR) ? " Reset" : "",
(status & USBPORTSC_LSDA) ? " LowSpeed" : "",
(status & USBPORTSC_RD) ? " ResumeDetect" : "",
(status & USBPORTSC_PEC) ? " EnableChange" : "",
(status & USBPORTSC_PE) ? " Enabled" : "",
(status & USBPORTSC_CSC) ? " ConnectChange" : "",
(status & USBPORTSC_CCS) ? " Connected" : "");
return out - buf;
}
static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
{
char *out = buf;
unsigned long io_addr = uhci->io_addr;
unsigned short usbcmd, usbstat, usbint, usbfrnum;
unsigned int flbaseadd;
unsigned char sof;
unsigned short portsc1, portsc2;
/* Try to make sure there's enough memory */
if (len < 80 * 6)
return 0;
usbcmd = inw(io_addr + 0);
usbstat = inw(io_addr + 2);
usbint = inw(io_addr + 4);
usbfrnum = inw(io_addr + 6);
flbaseadd = inl(io_addr + 8);
sof = inb(io_addr + 12);
portsc1 = inw(io_addr + 16);
portsc2 = inw(io_addr + 18);
out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n",
usbcmd,
(usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
(usbcmd & USBCMD_CF) ? "CF " : "",
(usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
(usbcmd & USBCMD_FGR) ? "FGR " : "",
(usbcmd & USBCMD_EGSM) ? "EGSM " : "",
(usbcmd & USBCMD_GRESET) ? "GRESET " : "",
(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
(usbcmd & USBCMD_RS) ? "RS " : "");
out += sprintf(out, " usbstat = %04x %s%s%s%s%s%s\n",
usbstat,
(usbstat & USBSTS_HCH) ? "HCHalted " : "",
(usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
(usbstat & USBSTS_HSE) ? "HostSystemError " : "",
(usbstat & USBSTS_RD) ? "ResumeDetect " : "",
(usbstat & USBSTS_ERROR) ? "USBError " : "",
(usbstat & USBSTS_USBINT) ? "USBINT " : "");
out += sprintf(out, " usbint = %04x\n", usbint);
out += sprintf(out, " usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1,
0xfff & (4*(unsigned int)usbfrnum));
out += sprintf(out, " flbaseadd = %08x\n", flbaseadd);
out += sprintf(out, " sof = %02x\n", sof);
out += uhci_show_sc(1, portsc1, out, len - (out - buf));
out += uhci_show_sc(2, portsc2, out, len - (out - buf));
return out - buf;
}
static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *buf, int len)
{
struct list_head *tmp;
char *out = buf;
int count = 0;
if (len < 200)
return 0;
out += sprintf(out, "urb_priv [%p] ", urbp);
out += sprintf(out, "urb [%p] ", urbp->urb);
out += sprintf(out, "qh [%p] ", urbp->qh);
out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
switch (usb_pipetype(urbp->urb->pipe)) {
case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO "); break;
case PIPE_INTERRUPT: out += sprintf(out, "INT "); break;
case PIPE_BULK: out += sprintf(out, "BLK "); break;
case PIPE_CONTROL: out += sprintf(out, "CTL "); break;
}
out += sprintf(out, "%s", (urbp->fsbr ? "FSBR " : ""));
out += sprintf(out, "%s", (urbp->fsbr_timeout ? "FSBR_TO " : ""));
if (urbp->urb->status != -EINPROGRESS)
out += sprintf(out, "Status=%d ", urbp->urb->status);
//out += sprintf(out, "Inserttime=%lx ",urbp->inserttime);
//out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime);
count = 0;
list_for_each(tmp, &urbp->td_list)
count++;
out += sprintf(out, "TDs=%d ",count);
if (urbp->queued)
out += sprintf(out, "queued\n");
else {
count = 0;
list_for_each(tmp, &urbp->queue_list)
count++;
out += sprintf(out, "queued URBs=%d\n", count);
}
return out - buf;
}
static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
{
char *out = buf;
struct list_head *head, *tmp;
int count;
out += sprintf(out, "Main list URBs:");
if (list_empty(&uhci->urb_list))
out += sprintf(out, " Empty\n");
else {
out += sprintf(out, "\n");
count = 0;
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
out += sprintf(out, " %d: ", ++count);
out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
tmp = tmp->next;
}
}
out += sprintf(out, "Remove list URBs:");
if (list_empty(&uhci->urb_remove_list))
out += sprintf(out, " Empty\n");
else {
out += sprintf(out, "\n");
count = 0;
head = &uhci->urb_remove_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
out += sprintf(out, " %d: ", ++count);
out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
tmp = tmp->next;
}
}
out += sprintf(out, "Complete list URBs:");
if (list_empty(&uhci->complete_list))
out += sprintf(out, " Empty\n");
else {
out += sprintf(out, "\n");
count = 0;
head = &uhci->complete_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
out += sprintf(out, " %d: ", ++count);
out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
tmp = tmp->next;
}
}
return out - buf;
}
static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
{
unsigned long flags;
char *out = buf;
int i, j;
struct uhci_qh *qh;
struct uhci_td *td;
struct list_head *tmp, *head;
spin_lock_irqsave(&uhci->lock, flags);
out += sprintf(out, "HC status\n");
out += uhci_show_status(uhci, out, len - (out - buf));
out += sprintf(out, "Frame List\n");
for (i = 0; i < UHCI_NUMFRAMES; ++i) {
int shown = 0;
td = uhci->fl->frame_cpu[i];
if (!td)
continue;
if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
show_frame_num();
out += sprintf(out, " frame list does not match td->dma_handle!\n");
}
show_frame_num();
head = &td->fl_list;
tmp = head;
do {
td = list_entry(tmp, struct uhci_td, fl_list);
tmp = tmp->next;
out += uhci_show_td(td, out, len - (out - buf), 4);
} while (tmp != head);
}
out += sprintf(out, "Skeleton QH's\n");
for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
int shown = 0;
qh = uhci->skelqh[i];
if (debug > 1) {
show_qh_name();
out += uhci_show_qh(qh, out, len - (out - buf), 4);
}
/* Last QH is the Terminating QH, it's different */
if (i == UHCI_NUM_SKELQH - 1) {
if (qh->link != UHCI_PTR_TERM)
out += sprintf(out, " bandwidth reclamation on!\n");
if (qh_element(qh) != cpu_to_le32(uhci->term_td->dma_handle))
out += sprintf(out, " skel_term_qh element is not set to term_td!\n");
continue;
}
j = (i < 7) ? 7 : i+1; /* Next skeleton */
if (list_empty(&qh->list)) {
if (i < UHCI_NUM_SKELQH - 1) {
if (qh->link !=
(cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) {
show_qh_name();
out += sprintf(out, " skeleton QH not linked to next skeleton QH!\n");
}
}
continue;
}
show_qh_name();
head = &qh->list;
tmp = head->next;
while (tmp != head) {
qh = list_entry(tmp, struct uhci_qh, list);
tmp = tmp->next;
out += uhci_show_qh(qh, out, len - (out - buf), 4);
}
if (i < UHCI_NUM_SKELQH - 1) {
if (qh->link !=
(cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH))
out += sprintf(out, " last QH not linked to next skeleton!\n");
}
}
if (debug > 2)
out += uhci_show_lists(uhci, out, len - (out - buf));
spin_unlock_irqrestore(&uhci->lock, flags);
return out - buf;
}
#define MAX_OUTPUT (64 * 1024)
struct uhci_debug {
int size;
char *data;
struct uhci_hcd *uhci;
};
static int uhci_debug_open(struct inode *inode, struct file *file)
{
struct uhci_hcd *uhci = inode->u.generic_ip;
struct uhci_debug *up;
int ret = -ENOMEM;
lock_kernel();
up = kmalloc(sizeof(*up), GFP_KERNEL);
if (!up)
goto out;
up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
if (!up->data) {
kfree(up);
goto out;
}
up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
file->private_data = up;
ret = 0;
out:
unlock_kernel();
return ret;
}
static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
{
struct uhci_debug *up;
loff_t new = -1;
lock_kernel();
up = file->private_data;
switch (whence) {
case 0:
new = off;
break;
case 1:
new = file->f_pos + off;
break;
}
if (new < 0 || new > up->size) {
unlock_kernel();
return -EINVAL;
}
unlock_kernel();
return (file->f_pos = new);
}
static ssize_t uhci_debug_read(struct file *file, char __user *buf,
size_t nbytes, loff_t *ppos)
{
struct uhci_debug *up = file->private_data;
return simple_read_from_buffer(buf, nbytes, ppos, up->data, up->size);
}
static int uhci_debug_release(struct inode *inode, struct file *file)
{
struct uhci_debug *up = file->private_data;
kfree(up->data);
kfree(up);
return 0;
}
static struct file_operations uhci_debug_operations = {
.open = uhci_debug_open,
.llseek = uhci_debug_lseek,
.read = uhci_debug_read,
.release = uhci_debug_release,
};
#else /* CONFIG_DEBUG_FS */
#define uhci_debug_operations (* (struct file_operations *) NULL)
#endif

919
drivers/usb/host/uhci-hcd.c Normal file
View file

@ -0,0 +1,919 @@
/*
* Universal Host Controller Interface driver for USB.
*
* Maintainer: Alan Stern <stern@rowland.harvard.edu>
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
* (C) Copyright 1999 Randy Dunlap
* (C) Copyright 1999 Georg Acher, acher@in.tum.de
* (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
* (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
* (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
* (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
*
* Intel documents this fairly well, and as far as I know there
* are no royalties or anything like that, but even so there are
* people who decided that they want to do the same thing in a
* completely different way.
*
* WARNING! The USB documentation is downright evil. Most of it
* is just crap, written by a committee. You're better off ignoring
* most of it, the important stuff is:
* - the low-level protocol (fairly simple but lots of small details)
* - working around the horridness of the rest
*/
#include <linux/config.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.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/unistd.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#include <linux/pm.h>
#include <linux/dmapool.h>
#include <linux/dma-mapping.h>
#include <linux/usb.h>
#include <linux/bitops.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include "../core/hcd.h"
#include "uhci-hcd.h"
/*
* Version Information
*/
#define DRIVER_VERSION "v2.2"
#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \
Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \
Alan Stern"
#define DRIVER_DESC "USB Universal Host Controller Interface driver"
/*
* debug = 0, no debugging messages
* debug = 1, dump failed URB's except for stalls
* debug = 2, dump all failed URB's (including stalls)
* show all queues in /debug/uhci/[pci_addr]
* debug = 3, show all TD's in URB's when dumping
*/
#ifdef DEBUG
static int debug = 1;
#else
static int debug = 0;
#endif
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level");
static char *errbuf;
#define ERRBUF_LEN (32 * 1024)
static kmem_cache_t *uhci_up_cachep; /* urb_priv */
static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
static void hc_state_transitions(struct uhci_hcd *uhci);
/* If a transfer is still active after this much time, turn off FSBR */
#define IDLE_TIMEOUT msecs_to_jiffies(50)
#define FSBR_DELAY msecs_to_jiffies(50)
/* When we timeout an idle transfer for FSBR, we'll switch it over to */
/* depth first traversal. We'll do it in groups of this number of TD's */
/* to make sure it doesn't hog all of the bandwidth */
#define DEPTH_INTERVAL 5
#include "uhci-hub.c"
#include "uhci-debug.c"
#include "uhci-q.c"
static int init_stall_timer(struct usb_hcd *hcd);
static void stall_callback(unsigned long ptr)
{
struct usb_hcd *hcd = (struct usb_hcd *)ptr;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
struct urb_priv *up;
unsigned long flags;
spin_lock_irqsave(&uhci->lock, flags);
uhci_scan_schedule(uhci, NULL);
list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
spin_lock(&u->lock);
/* Check if the FSBR timed out */
if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
uhci_fsbr_timeout(uhci, u);
spin_unlock(&u->lock);
}
/* Really disable FSBR */
if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
uhci->fsbrtimeout = 0;
uhci->skel_term_qh->link = UHCI_PTR_TERM;
}
/* Poll for and perform state transitions */
hc_state_transitions(uhci);
if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))
uhci_check_ports(uhci);
init_stall_timer(hcd);
spin_unlock_irqrestore(&uhci->lock, flags);
}
static int init_stall_timer(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
init_timer(&uhci->stall_timer);
uhci->stall_timer.function = stall_callback;
uhci->stall_timer.data = (unsigned long)hcd;
uhci->stall_timer.expires = jiffies + msecs_to_jiffies(100);
add_timer(&uhci->stall_timer);
return 0;
}
static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long io_addr = uhci->io_addr;
unsigned short status;
/*
* Read the interrupt status, and write it back to clear the
* interrupt cause. Contrary to the UHCI specification, the
* "HC Halted" status bit is persistent: it is RO, not R/WC.
*/
status = inw(io_addr + USBSTS);
if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */
return IRQ_NONE;
outw(status, io_addr + USBSTS); /* Clear it */
if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
if (status & USBSTS_HSE)
dev_err(uhci_dev(uhci), "host system error, "
"PCI problems?\n");
if (status & USBSTS_HCPE)
dev_err(uhci_dev(uhci), "host controller process "
"error, something bad happened!\n");
if ((status & USBSTS_HCH) && uhci->state > 0) {
dev_err(uhci_dev(uhci), "host controller halted, "
"very bad!\n");
/* FIXME: Reset the controller, fix the offending TD */
}
}
if (status & USBSTS_RD)
uhci->resume_detect = 1;
spin_lock(&uhci->lock);
uhci_scan_schedule(uhci, regs);
spin_unlock(&uhci->lock);
return IRQ_HANDLED;
}
static void reset_hc(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
/* Turn off PIRQ, SMI, and all interrupts. This also turns off
* the BIOS's USB Legacy Support.
*/
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
outw(0, uhci->io_addr + USBINTR);
/* Global reset for 50ms */
uhci->state = UHCI_RESET;
outw(USBCMD_GRESET, io_addr + USBCMD);
msleep(50);
outw(0, io_addr + USBCMD);
/* Another 10ms delay */
msleep(10);
uhci->resume_detect = 0;
uhci->is_stopped = UHCI_IS_STOPPED;
}
static void suspend_hc(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
uhci->state = UHCI_SUSPENDED;
uhci->resume_detect = 0;
outw(USBCMD_EGSM, io_addr + USBCMD);
/* FIXME: Wait for the controller to actually stop */
uhci_get_current_frame_number(uhci);
uhci->is_stopped = UHCI_IS_STOPPED;
uhci_scan_schedule(uhci, NULL);
}
static void wakeup_hc(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
switch (uhci->state) {
case UHCI_SUSPENDED: /* Start the resume */
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
/* Global resume for >= 20ms */
outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD);
uhci->state = UHCI_RESUMING_1;
uhci->state_end = jiffies + msecs_to_jiffies(20);
uhci->is_stopped = 0;
break;
case UHCI_RESUMING_1: /* End global resume */
uhci->state = UHCI_RESUMING_2;
outw(0, io_addr + USBCMD);
/* Falls through */
case UHCI_RESUMING_2: /* Wait for EOP to be sent */
if (inw(io_addr + USBCMD) & USBCMD_FGR)
break;
/* Run for at least 1 second, and
* mark it configured with a 64-byte max packet */
uhci->state = UHCI_RUNNING_GRACE;
uhci->state_end = jiffies + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP,
io_addr + USBCMD);
break;
case UHCI_RUNNING_GRACE: /* Now allowed to suspend */
uhci->state = UHCI_RUNNING;
break;
default:
break;
}
}
static int ports_active(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
int connection = 0;
int i;
for (i = 0; i < uhci->rh_numports; i++)
connection |= (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_CCS);
return connection;
}
static int suspend_allowed(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
int i;
if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL)
return 1;
/* Some of Intel's USB controllers have a bug that causes false
* resume indications if any port has an over current condition.
* To prevent problems, we will not allow a global suspend if
* any ports are OC.
*
* Some motherboards using Intel's chipsets (but not using all
* the USB ports) appear to hardwire the over current inputs active
* to disable the USB ports.
*/
/* check for over current condition on any port */
for (i = 0; i < uhci->rh_numports; i++) {
if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)
return 0;
}
return 1;
}
static void hc_state_transitions(struct uhci_hcd *uhci)
{
switch (uhci->state) {
case UHCI_RUNNING:
/* global suspend if nothing connected for 1 second */
if (!ports_active(uhci) && suspend_allowed(uhci)) {
uhci->state = UHCI_SUSPENDING_GRACE;
uhci->state_end = jiffies + HZ;
}
break;
case UHCI_SUSPENDING_GRACE:
if (ports_active(uhci))
uhci->state = UHCI_RUNNING;
else if (time_after_eq(jiffies, uhci->state_end))
suspend_hc(uhci);
break;
case UHCI_SUSPENDED:
/* wakeup if requested by a device */
if (uhci->resume_detect)
wakeup_hc(uhci);
break;
case UHCI_RESUMING_1:
case UHCI_RESUMING_2:
case UHCI_RUNNING_GRACE:
if (time_after_eq(jiffies, uhci->state_end))
wakeup_hc(uhci);
break;
default:
break;
}
}
/*
* Store the current frame number in uhci->frame_number if the controller
* is runnning
*/
static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
{
if (!uhci->is_stopped)
uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
}
static int start_hc(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
int timeout = 10;
/*
* Reset the HC - this will force us to get a
* new notification of any already connected
* ports due to the virtual disconnect that it
* implies.
*/
outw(USBCMD_HCRESET, io_addr + USBCMD);
while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
if (--timeout < 0) {
dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n");
return -ETIMEDOUT;
}
msleep(1);
}
/* Mark controller as running before we enable interrupts */
uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
/* Turn on PIRQ and all interrupts */
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
USBLEGSUP_DEFAULT);
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
io_addr + USBINTR);
/* Start at frame 0 */
outw(0, io_addr + USBFRNUM);
outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
/* Run and mark it configured with a 64-byte max packet */
uhci->state = UHCI_RUNNING_GRACE;
uhci->state_end = jiffies + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
uhci->is_stopped = 0;
return 0;
}
/*
* De-allocate all resources
*/
static void release_uhci(struct uhci_hcd *uhci)
{
int i;
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
if (uhci->term_td) {
uhci_free_td(uhci, uhci->term_td);
uhci->term_td = NULL;
}
if (uhci->qh_pool) {
dma_pool_destroy(uhci->qh_pool);
uhci->qh_pool = NULL;
}
if (uhci->td_pool) {
dma_pool_destroy(uhci->td_pool);
uhci->td_pool = NULL;
}
if (uhci->fl) {
dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
}
if (uhci->dentry) {
debugfs_remove(uhci->dentry);
uhci->dentry = NULL;
}
}
static int uhci_reset(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
uhci->io_addr = (unsigned long) hcd->rsrc_start;
/* Kick BIOS off this hardware and reset, so we won't get
* interrupts from any previous setup.
*/
reset_hc(uhci);
return 0;
}
/*
* Allocate a frame list, and then setup the skeleton
*
* The hardware doesn't really know any difference
* in the queues, but the order does matter for the
* protocols higher up. The order is:
*
* - any isochronous events handled before any
* of the queues. We don't do that here, because
* we'll create the actual TD entries on demand.
* - The first queue is the interrupt queue.
* - The second queue is the control queue, split into low- and full-speed
* - The third queue is bulk queue.
* - The fourth queue is the bandwidth reclamation queue, which loops back
* to the full-speed control queue.
*/
static int uhci_start(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
int i, port;
unsigned io_size;
dma_addr_t dma_handle;
struct usb_device *udev;
struct dentry *dentry;
io_size = (unsigned) hcd->rsrc_len;
dentry = debugfs_create_file(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci, &uhci_debug_operations);
if (!dentry) {
dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
retval = -ENOMEM;
goto err_create_debug_entry;
}
uhci->dentry = dentry;
uhci->fsbr = 0;
uhci->fsbrtimeout = 0;
spin_lock_init(&uhci->lock);
INIT_LIST_HEAD(&uhci->qh_remove_list);
INIT_LIST_HEAD(&uhci->td_remove_list);
INIT_LIST_HEAD(&uhci->urb_remove_list);
INIT_LIST_HEAD(&uhci->urb_list);
INIT_LIST_HEAD(&uhci->complete_list);
init_waitqueue_head(&uhci->waitqh);
uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
&dma_handle, 0);
if (!uhci->fl) {
dev_err(uhci_dev(uhci), "unable to allocate "
"consistent memory for frame list\n");
goto err_alloc_fl;
}
memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
uhci->fl->dma_handle = dma_handle;
uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),
sizeof(struct uhci_td), 16, 0);
if (!uhci->td_pool) {
dev_err(uhci_dev(uhci), "unable to create td dma_pool\n");
goto err_create_td_pool;
}
uhci->qh_pool = dma_pool_create("uhci_qh", uhci_dev(uhci),
sizeof(struct uhci_qh), 16, 0);
if (!uhci->qh_pool) {
dev_err(uhci_dev(uhci), "unable to create qh dma_pool\n");
goto err_create_qh_pool;
}
/* Initialize the root hub */
/* UHCI specs says devices must have 2 ports, but goes on to say */
/* they may have more but give no way to determine how many they */
/* have. However, according to the UHCI spec, Bit 7 is always set */
/* to 1. So we try to use this to our advantage */
for (port = 0; port < (io_size - 0x10) / 2; port++) {
unsigned int portstatus;
portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
if (!(portstatus & 0x0080))
break;
}
if (debug)
dev_info(uhci_dev(uhci), "detected %d ports\n", port);
/* This is experimental so anything less than 2 or greater than 8 is */
/* something weird and we'll ignore it */
if (port < 2 || port > UHCI_RH_MAXCHILD) {
dev_info(uhci_dev(uhci), "port count misdetected? "
"forcing to 2 ports\n");
port = 2;
}
uhci->rh_numports = port;
udev = usb_alloc_dev(NULL, &hcd->self, 0);
if (!udev) {
dev_err(uhci_dev(uhci), "unable to allocate root hub\n");
goto err_alloc_root_hub;
}
uhci->term_td = uhci_alloc_td(uhci, udev);
if (!uhci->term_td) {
dev_err(uhci_dev(uhci), "unable to allocate terminating TD\n");
goto err_alloc_term_td;
}
for (i = 0; i < UHCI_NUM_SKELQH; i++) {
uhci->skelqh[i] = uhci_alloc_qh(uhci, udev);
if (!uhci->skelqh[i]) {
dev_err(uhci_dev(uhci), "unable to allocate QH\n");
goto err_alloc_skelqh;
}
}
/*
* 8 Interrupt queues; link all higher int queues to int1,
* then link int1 to control and control to bulk
*/
uhci->skel_int128_qh->link =
uhci->skel_int64_qh->link =
uhci->skel_int32_qh->link =
uhci->skel_int16_qh->link =
uhci->skel_int8_qh->link =
uhci->skel_int4_qh->link =
uhci->skel_int2_qh->link =
cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_fs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
/* This dummy TD is to work around a bug in Intel PIIX controllers */
uhci_fill_td(uhci->term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);
uhci->skel_term_qh->link = UHCI_PTR_TERM;
uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);
/*
* Fill the frame list: make all entries point to the proper
* interrupt queue.
*
* The interrupt queues will be interleaved as evenly as possible.
* There's not much to be done about period-1 interrupts; they have
* to occur in every frame. But we can schedule period-2 interrupts
* in odd-numbered frames, period-4 interrupts in frames congruent
* to 2 (mod 4), and so on. This way each frame only has two
* interrupt QHs, which will help spread out bandwidth utilization.
*/
for (i = 0; i < UHCI_NUMFRAMES; i++) {
int irq;
/*
* ffs (Find First bit Set) does exactly what we need:
* 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[6],
* 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[5], etc.
* ffs > 6 => not on any high-period queue, so use
* skel_int1_qh = skelqh[7].
* Add UHCI_NUMFRAMES to insure at least one bit is set.
*/
irq = 6 - (int) __ffs(i + UHCI_NUMFRAMES);
if (irq < 0)
irq = 7;
/* Only place we don't use the frame list routines */
uhci->fl->frame[i] = UHCI_PTR_QH |
cpu_to_le32(uhci->skelqh[irq]->dma_handle);
}
/*
* Some architectures require a full mb() to enforce completion of
* the memory writes above before the I/O transfers in start_hc().
*/
mb();
if ((retval = start_hc(uhci)) != 0)
goto err_alloc_skelqh;
init_stall_timer(hcd);
udev->speed = USB_SPEED_FULL;
if (usb_hcd_register_root_hub(udev, hcd) != 0) {
dev_err(uhci_dev(uhci), "unable to start root hub\n");
retval = -ENOMEM;
goto err_start_root_hub;
}
return 0;
/*
* error exits:
*/
err_start_root_hub:
reset_hc(uhci);
del_timer_sync(&uhci->stall_timer);
err_alloc_skelqh:
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
uhci_free_td(uhci, uhci->term_td);
uhci->term_td = NULL;
err_alloc_term_td:
usb_put_dev(udev);
err_alloc_root_hub:
dma_pool_destroy(uhci->qh_pool);
uhci->qh_pool = NULL;
err_create_qh_pool:
dma_pool_destroy(uhci->td_pool);
uhci->td_pool = NULL;
err_create_td_pool:
dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
err_alloc_fl:
debugfs_remove(uhci->dentry);
uhci->dentry = NULL;
err_create_debug_entry:
return retval;
}
static void uhci_stop(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
del_timer_sync(&uhci->stall_timer);
reset_hc(uhci);
spin_lock_irq(&uhci->lock);
uhci_scan_schedule(uhci, NULL);
spin_unlock_irq(&uhci->lock);
release_uhci(uhci);
}
#ifdef CONFIG_PM
static int uhci_suspend(struct usb_hcd *hcd, u32 state)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
spin_lock_irq(&uhci->lock);
/* Don't try to suspend broken motherboards, reset instead */
if (suspend_allowed(uhci))
suspend_hc(uhci);
else {
spin_unlock_irq(&uhci->lock);
reset_hc(uhci);
spin_lock_irq(&uhci->lock);
uhci_scan_schedule(uhci, NULL);
}
spin_unlock_irq(&uhci->lock);
return 0;
}
static int uhci_resume(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int rc;
pci_set_master(to_pci_dev(uhci_dev(uhci)));
spin_lock_irq(&uhci->lock);
if (uhci->state == UHCI_SUSPENDED) {
/*
* Some systems don't maintain the UHCI register values
* during a PM suspend/resume cycle, so reinitialize
* the Frame Number, Framelist Base Address, Interrupt
* Enable, and Legacy Support registers.
*/
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
0);
outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC |
USBINTR_SP, uhci->io_addr + USBINTR);
uhci->resume_detect = 1;
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
USBLEGSUP_DEFAULT);
} else {
spin_unlock_irq(&uhci->lock);
reset_hc(uhci);
if ((rc = start_hc(uhci)) != 0)
return rc;
spin_lock_irq(&uhci->lock);
}
hcd->state = HC_STATE_RUNNING;
spin_unlock_irq(&uhci->lock);
return 0;
}
#endif
/* Wait until all the URBs for a particular device/endpoint are gone */
static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
struct usb_host_endpoint *ep)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
wait_event_interruptible(uhci->waitqh, list_empty(&ep->urb_list));
}
static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int frame_number;
unsigned long flags;
/* Minimize latency by avoiding the spinlock */
local_irq_save(flags);
rmb();
frame_number = (uhci->is_stopped ? uhci->frame_number :
inw(uhci->io_addr + USBFRNUM));
local_irq_restore(flags);
return frame_number;
}
static const char hcd_name[] = "uhci_hcd";
static const struct hc_driver uhci_driver = {
.description = hcd_name,
.product_desc = "UHCI Host Controller",
.hcd_priv_size = sizeof(struct uhci_hcd),
/* Generic hardware linkage */
.irq = uhci_irq,
.flags = HCD_USB11,
/* Basic lifecycle operations */
.reset = uhci_reset,
.start = uhci_start,
#ifdef CONFIG_PM
.suspend = uhci_suspend,
.resume = uhci_resume,
#endif
.stop = uhci_stop,
.urb_enqueue = uhci_urb_enqueue,
.urb_dequeue = uhci_urb_dequeue,
.endpoint_disable = uhci_hcd_endpoint_disable,
.get_frame_number = uhci_hcd_get_frame_number,
.hub_status_data = uhci_hub_status_data,
.hub_control = uhci_hub_control,
};
static const struct pci_device_id uhci_pci_ids[] = { {
/* handle any USB UHCI controller */
PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x00), ~0),
.driver_data = (unsigned long) &uhci_driver,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
static struct pci_driver uhci_pci_driver = {
.name = (char *)hcd_name,
.id_table = uhci_pci_ids,
.probe = usb_hcd_pci_probe,
.remove = usb_hcd_pci_remove,
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.resume = usb_hcd_pci_resume,
#endif /* PM */
};
static int __init uhci_hcd_init(void)
{
int retval = -ENOMEM;
printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "\n");
if (usb_disabled())
return -ENODEV;
if (debug) {
errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
if (!errbuf)
goto errbuf_failed;
}
uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
if (!uhci_debugfs_root)
goto debug_failed;
uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
sizeof(struct urb_priv), 0, 0, NULL, NULL);
if (!uhci_up_cachep)
goto up_failed;
retval = pci_register_driver(&uhci_pci_driver);
if (retval)
goto init_failed;
return 0;
init_failed:
if (kmem_cache_destroy(uhci_up_cachep))
warn("not all urb_priv's were freed!");
up_failed:
debugfs_remove(uhci_debugfs_root);
debug_failed:
if (errbuf)
kfree(errbuf);
errbuf_failed:
return retval;
}
static void __exit uhci_hcd_cleanup(void)
{
pci_unregister_driver(&uhci_pci_driver);
if (kmem_cache_destroy(uhci_up_cachep))
warn("not all urb_priv's were freed!");
debugfs_remove(uhci_debugfs_root);
if (errbuf)
kfree(errbuf);
}
module_init(uhci_hcd_init);
module_exit(uhci_hcd_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

454
drivers/usb/host/uhci-hcd.h Normal file
View file

@ -0,0 +1,454 @@
#ifndef __LINUX_UHCI_HCD_H
#define __LINUX_UHCI_HCD_H
#include <linux/list.h>
#include <linux/usb.h>
#define usb_packetid(pipe) (usb_pipein(pipe) ? USB_PID_IN : USB_PID_OUT)
#define PIPE_DEVEP_MASK 0x0007ff00
/*
* Universal Host Controller Interface data structures and defines
*/
/* Command register */
#define USBCMD 0
#define USBCMD_RS 0x0001 /* Run/Stop */
#define USBCMD_HCRESET 0x0002 /* Host reset */
#define USBCMD_GRESET 0x0004 /* Global reset */
#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
#define USBCMD_FGR 0x0010 /* Force Global Resume */
#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
/* Status register */
#define USBSTS 2
#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
#define USBSTS_RD 0x0004 /* Resume Detect */
#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */
#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */
#define USBSTS_HCH 0x0020 /* HC Halted */
/* Interrupt enable register */
#define USBINTR 4
#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
#define USBFRNUM 6
#define USBFLBASEADD 8
#define USBSOF 12
/* USB port status and control registers */
#define USBPORTSC1 16
#define USBPORTSC2 18
#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */
#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
#define USBPORTSC_PE 0x0004 /* Port Enable */
#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
#define USBPORTSC_DPLUS 0x0010 /* D+ high (line status) */
#define USBPORTSC_DMINUS 0x0020 /* D- high (line status) */
#define USBPORTSC_RD 0x0040 /* Resume Detect */
#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */
#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
#define USBPORTSC_PR 0x0200 /* Port Reset */
/* OC and OCC from Intel 430TX and later (not UHCI 1.1d spec) */
#define USBPORTSC_OC 0x0400 /* Over Current condition */
#define USBPORTSC_OCC 0x0800 /* Over Current Change R/WC */
#define USBPORTSC_SUSP 0x1000 /* Suspend */
#define USBPORTSC_RES2 0x2000 /* reserved, write zeroes */
#define USBPORTSC_RES3 0x4000 /* reserved, write zeroes */
#define USBPORTSC_RES4 0x8000 /* reserved, write zeroes */
/* Legacy support register */
#define USBLEGSUP 0xc0
#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */
#define UHCI_PTR_BITS cpu_to_le32(0x000F)
#define UHCI_PTR_TERM cpu_to_le32(0x0001)
#define UHCI_PTR_QH cpu_to_le32(0x0002)
#define UHCI_PTR_DEPTH cpu_to_le32(0x0004)
#define UHCI_PTR_BREADTH cpu_to_le32(0x0000)
#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
struct uhci_frame_list {
__le32 frame[UHCI_NUMFRAMES];
void *frame_cpu[UHCI_NUMFRAMES];
dma_addr_t dma_handle;
};
struct urb_priv;
/*
* One role of a QH is to hold a queue of TDs for some endpoint. Each QH is
* used with one URB, and qh->element (updated by the HC) is either:
* - the next unprocessed TD for the URB, or
* - UHCI_PTR_TERM (when there's no more traffic for this endpoint), or
* - the QH for the next URB queued to the same endpoint.
*
* The other role of a QH is to serve as a "skeleton" framelist entry, so we
* can easily splice a QH for some endpoint into the schedule at the right
* place. Then qh->element is UHCI_PTR_TERM.
*
* In the frame list, qh->link maintains a list of QHs seen by the HC:
* skel1 --> ep1-qh --> ep2-qh --> ... --> skel2 --> ...
*/
struct uhci_qh {
/* Hardware fields */
__le32 link; /* Next queue */
__le32 element; /* Queue element pointer */
/* Software fields */
dma_addr_t dma_handle;
struct usb_device *dev;
struct urb_priv *urbp;
struct list_head list; /* P: uhci->frame_list_lock */
struct list_head remove_list; /* P: uhci->remove_list_lock */
} __attribute__((aligned(16)));
/*
* We need a special accessor for the element pointer because it is
* subject to asynchronous updates by the controller
*/
static __le32 inline qh_element(struct uhci_qh *qh) {
__le32 element = qh->element;
barrier();
return element;
}
/*
* for TD <status>:
*/
#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */
#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */
#define TD_CTRL_C_ERR_SHIFT 27
#define TD_CTRL_LS (1 << 26) /* Low Speed Device */
#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */
#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */
#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */
#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */
#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */
#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */
#define TD_CTRL_NAK (1 << 19) /* NAK Received */
#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */
#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */
#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */
#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT)
#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xF60000)
#define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
/*
* for TD <info>: (a.k.a. Token)
*/
#define td_token(td) le32_to_cpu((td)->token)
#define TD_TOKEN_DEVADDR_SHIFT 8
#define TD_TOKEN_TOGGLE_SHIFT 19
#define TD_TOKEN_TOGGLE (1 << 19)
#define TD_TOKEN_EXPLEN_SHIFT 21
#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */
#define TD_TOKEN_PID_MASK 0xFF
#define uhci_explen(len) ((len) << TD_TOKEN_EXPLEN_SHIFT)
#define uhci_expected_length(token) ((((token) >> 21) + 1) & TD_TOKEN_EXPLEN_MASK)
#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
#define uhci_endpoint(token) (((token) >> 15) & 0xf)
#define uhci_devaddr(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7f)
#define uhci_devep(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7ff)
#define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
/*
* The documentation says "4 words for hardware, 4 words for software".
*
* That's silly, the hardware doesn't care. The hardware only cares that
* the hardware words are 16-byte aligned, and we can have any amount of
* sw space after the TD entry as far as I can tell.
*
* But let's just go with the documentation, at least for 32-bit machines.
* On 64-bit machines we probably want to take advantage of the fact that
* hw doesn't really care about the size of the sw-only area.
*
* Alas, not anymore, we have more than 4 words for software, woops.
* Everything still works tho, surprise! -jerdfelt
*
* td->link points to either another TD (not necessarily for the same urb or
* even the same endpoint), or nothing (PTR_TERM), or a QH (for queued urbs)
*/
struct uhci_td {
/* Hardware fields */
__le32 link;
__le32 status;
__le32 token;
__le32 buffer;
/* Software fields */
dma_addr_t dma_handle;
struct usb_device *dev;
struct urb *urb;
struct list_head list; /* P: urb->lock */
struct list_head remove_list; /* P: uhci->td_remove_list_lock */
int frame; /* for iso: what frame? */
struct list_head fl_list; /* P: uhci->frame_list_lock */
} __attribute__((aligned(16)));
/*
* We need a special accessor for the control/status word because it is
* subject to asynchronous updates by the controller
*/
static u32 inline td_status(struct uhci_td *td) {
__le32 status = td->status;
barrier();
return le32_to_cpu(status);
}
/*
* The UHCI driver places Interrupt, Control and Bulk into QH's both
* to group together TD's for one transfer, and also to faciliate queuing
* of URB's. To make it easy to insert entries into the schedule, we have
* a skeleton of QH's for each predefined Interrupt latency, low-speed
* control, full-speed control and terminating QH (see explanation for
* the terminating QH below).
*
* When we want to add a new QH, we add it to the end of the list for the
* skeleton QH.
*
* For instance, the queue can look like this:
*
* skel int128 QH
* dev 1 interrupt QH
* dev 5 interrupt QH
* skel int64 QH
* skel int32 QH
* ...
* skel int1 QH
* skel low-speed control QH
* dev 5 control QH
* skel full-speed control QH
* skel bulk QH
* dev 1 bulk QH
* dev 2 bulk QH
* skel terminating QH
*
* The terminating QH is used for 2 reasons:
* - To place a terminating TD which is used to workaround a PIIX bug
* (see Intel errata for explanation)
* - To loop back to the full-speed control queue for full-speed bandwidth
* reclamation
*
* Isochronous transfers are stored before the start of the skeleton
* schedule and don't use QH's. While the UHCI spec doesn't forbid the
* use of QH's for Isochronous, it doesn't use them either. Since we don't
* need to use them either, we follow the spec diagrams in hope that it'll
* be more compatible with future UHCI implementations.
*/
#define UHCI_NUM_SKELQH 12
#define skel_int128_qh skelqh[0]
#define skel_int64_qh skelqh[1]
#define skel_int32_qh skelqh[2]
#define skel_int16_qh skelqh[3]
#define skel_int8_qh skelqh[4]
#define skel_int4_qh skelqh[5]
#define skel_int2_qh skelqh[6]
#define skel_int1_qh skelqh[7]
#define skel_ls_control_qh skelqh[8]
#define skel_fs_control_qh skelqh[9]
#define skel_bulk_qh skelqh[10]
#define skel_term_qh skelqh[11]
/*
* Search tree for determining where <interval> fits in the skelqh[]
* skeleton.
*
* An interrupt request should be placed into the slowest skelqh[]
* which meets the interval/period/frequency requirement.
* An interrupt request is allowed to be faster than <interval> but not slower.
*
* For a given <interval>, this function returns the appropriate/matching
* skelqh[] index value.
*/
static inline int __interval_to_skel(int interval)
{
if (interval < 16) {
if (interval < 4) {
if (interval < 2)
return 7; /* int1 for 0-1 ms */
return 6; /* int2 for 2-3 ms */
}
if (interval < 8)
return 5; /* int4 for 4-7 ms */
return 4; /* int8 for 8-15 ms */
}
if (interval < 64) {
if (interval < 32)
return 3; /* int16 for 16-31 ms */
return 2; /* int32 for 32-63 ms */
}
if (interval < 128)
return 1; /* int64 for 64-127 ms */
return 0; /* int128 for 128-255 ms (Max.) */
}
/*
* Device states for the host controller.
*
* To prevent "bouncing" in the presence of electrical noise,
* we insist on a 1-second "grace" period, before switching to
* the RUNNING or SUSPENDED states, during which the state is
* not allowed to change.
*
* The resume process is divided into substates in order to avoid
* potentially length delays during the timer handler.
*
* States in which the host controller is halted must have values <= 0.
*/
enum uhci_state {
UHCI_RESET,
UHCI_RUNNING_GRACE, /* Before RUNNING */
UHCI_RUNNING, /* The normal state */
UHCI_SUSPENDING_GRACE, /* Before SUSPENDED */
UHCI_SUSPENDED = -10, /* When no devices are attached */
UHCI_RESUMING_1,
UHCI_RESUMING_2
};
/*
* This describes the full uhci information.
*
* Note how the "proper" USB information is just
* a subset of what the full implementation needs.
*/
struct uhci_hcd {
/* debugfs */
struct dentry *dentry;
/* Grabbed from PCI */
unsigned long io_addr;
struct dma_pool *qh_pool;
struct dma_pool *td_pool;
struct usb_bus *bus;
struct uhci_td *term_td; /* Terminating TD, see UHCI bug */
struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */
spinlock_t lock;
struct uhci_frame_list *fl; /* P: uhci->lock */
int fsbr; /* Full-speed bandwidth reclamation */
unsigned long fsbrtimeout; /* FSBR delay */
enum uhci_state state; /* FIXME: needs a spinlock */
unsigned long state_end; /* Time of next transition */
unsigned int frame_number; /* As of last check */
unsigned int is_stopped;
#define UHCI_IS_STOPPED 9999 /* Larger than a frame # */
unsigned int scan_in_progress:1; /* Schedule scan is running */
unsigned int need_rescan:1; /* Redo the schedule scan */
unsigned int resume_detect:1; /* Need a Global Resume */
/* Support for port suspend/resume/reset */
unsigned long port_c_suspend; /* Bit-arrays of ports */
unsigned long suspended_ports;
unsigned long resuming_ports;
unsigned long ports_timeout; /* Time to stop signalling */
/* Main list of URB's currently controlled by this HC */
struct list_head urb_list; /* P: uhci->lock */
/* List of QH's that are done, but waiting to be unlinked (race) */
struct list_head qh_remove_list; /* P: uhci->lock */
unsigned int qh_remove_age; /* Age in frames */
/* List of TD's that are done, but waiting to be freed (race) */
struct list_head td_remove_list; /* P: uhci->lock */
unsigned int td_remove_age; /* Age in frames */
/* List of asynchronously unlinked URB's */
struct list_head urb_remove_list; /* P: uhci->lock */
unsigned int urb_remove_age; /* Age in frames */
/* List of URB's awaiting completion callback */
struct list_head complete_list; /* P: uhci->lock */
int rh_numports;
struct timer_list stall_timer;
wait_queue_head_t waitqh; /* endpoint_disable waiters */
};
/* Convert between a usb_hcd pointer and the corresponding uhci_hcd */
static inline struct uhci_hcd *hcd_to_uhci(struct usb_hcd *hcd)
{
return (struct uhci_hcd *) (hcd->hcd_priv);
}
static inline struct usb_hcd *uhci_to_hcd(struct uhci_hcd *uhci)
{
return container_of((void *) uhci, struct usb_hcd, hcd_priv);
}
#define uhci_dev(u) (uhci_to_hcd(u)->self.controller)
struct urb_priv {
struct list_head urb_list;
struct urb *urb;
struct uhci_qh *qh; /* QH for this URB */
struct list_head td_list; /* P: urb->lock */
unsigned fsbr : 1; /* URB turned on FSBR */
unsigned fsbr_timeout : 1; /* URB timed out on FSBR */
unsigned queued : 1; /* QH was queued (not linked in) */
unsigned short_control_packet : 1; /* If we get a short packet during */
/* a control transfer, retrigger */
/* the status phase */
unsigned long inserttime; /* In jiffies */
unsigned long fsbrtime; /* In jiffies */
struct list_head queue_list; /* P: uhci->frame_list_lock */
};
/*
* Locking in uhci.c
*
* Almost everything relating to the hardware schedule and processing
* of URBs is protected by uhci->lock. urb->status is protected by
* urb->lock; that's the one exception.
*
* To prevent deadlocks, never lock uhci->lock while holding urb->lock.
* The safe order of locking is:
*
* #1 uhci->lock
* #2 urb->lock
*/
#endif

299
drivers/usb/host/uhci-hub.c Normal file
View file

@ -0,0 +1,299 @@
/*
* Universal Host Controller Interface driver for USB.
*
* Maintainer: Alan Stern <stern@rowland.harvard.edu>
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
* (C) Copyright 1999 Randy Dunlap
* (C) Copyright 1999 Georg Acher, acher@in.tum.de
* (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
* (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
* (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
*/
static __u8 root_hub_hub_des[] =
{
0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */
0x02, /* __u8 bNbrPorts; */
0x0a, /* __u16 wHubCharacteristics; */
0x00, /* (per-port OC, no power switching) */
0x01, /* __u8 bPwrOn2pwrGood; 2ms */
0x00, /* __u8 bHubContrCurrent; 0 mA */
0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
};
#define UHCI_RH_MAXCHILD 7
/* must write as zeroes */
#define WZ_BITS (USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4)
/* status change bits: nonzero writes will clear */
#define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int port;
*buf = 0;
for (port = 0; port < uhci->rh_numports; ++port) {
if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) ||
test_bit(port, &uhci->port_c_suspend))
*buf |= (1 << (port + 1));
}
if (*buf && uhci->state == UHCI_SUSPENDED)
uhci->resume_detect = 1;
return !!*buf;
}
#define OK(x) len = (x); break
#define CLR_RH_PORTSTAT(x) \
status = inw(port_addr); \
status &= ~(RWC_BITS|WZ_BITS); \
status &= ~(x); \
status |= RWC_BITS & (x); \
outw(status, port_addr)
#define SET_RH_PORTSTAT(x) \
status = inw(port_addr); \
status |= (x); \
status &= ~(RWC_BITS|WZ_BITS); \
outw(status, port_addr)
/* UHCI controllers don't automatically stop resume signalling after 20 msec,
* so we have to poll and check timeouts in order to take care of it.
*/
static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
unsigned long port_addr)
{
int status;
if (test_bit(port, &uhci->suspended_ports)) {
CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
clear_bit(port, &uhci->suspended_ports);
clear_bit(port, &uhci->resuming_ports);
set_bit(port, &uhci->port_c_suspend);
/* The controller won't actually turn off the RD bit until
* it has had a chance to send a low-speed EOP sequence,
* which takes 3 bit times (= 2 microseconds). We'll delay
* slightly longer for good luck. */
udelay(4);
}
}
static void uhci_check_ports(struct uhci_hcd *uhci)
{
unsigned int port;
unsigned long port_addr;
int status;
for (port = 0; port < uhci->rh_numports; ++port) {
port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
status = inw(port_addr);
if (unlikely(status & USBPORTSC_PR)) {
if (time_after_eq(jiffies, uhci->ports_timeout)) {
CLR_RH_PORTSTAT(USBPORTSC_PR);
udelay(10);
/* If the port was enabled before, turning
* reset on caused a port enable change.
* Turning reset off causes a port connect
* status change. Clear these changes. */
CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC);
SET_RH_PORTSTAT(USBPORTSC_PE);
}
}
if (unlikely(status & USBPORTSC_RD)) {
if (!test_bit(port, &uhci->resuming_ports)) {
/* Port received a wakeup request */
set_bit(port, &uhci->resuming_ports);
uhci->ports_timeout = jiffies +
msecs_to_jiffies(20);
} else if (time_after_eq(jiffies,
uhci->ports_timeout)) {
uhci_finish_suspend(uhci, port, port_addr);
}
}
}
}
/* size of returned buffer is part of USB spec */
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int status, lstatus, retval = 0, len = 0;
unsigned int port = wIndex - 1;
unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
u16 wPortChange, wPortStatus;
unsigned long flags;
spin_lock_irqsave(&uhci->lock, flags);
switch (typeReq) {
case GetHubStatus:
*(__le32 *)buf = cpu_to_le32(0);
OK(4); /* hub power */
case GetPortStatus:
if (port >= uhci->rh_numports)
goto err;
uhci_check_ports(uhci);
status = inw(port_addr);
/* Intel controllers report the OverCurrent bit active on.
* VIA controllers report it active off, so we'll adjust the
* bit value. (It's not standardized in the UHCI spec.)
*/
if (to_pci_dev(hcd->self.controller)->vendor ==
PCI_VENDOR_ID_VIA)
status ^= USBPORTSC_OC;
/* UHCI doesn't support C_RESET (always false) */
wPortChange = lstatus = 0;
if (status & USBPORTSC_CSC)
wPortChange |= USB_PORT_STAT_C_CONNECTION;
if (status & USBPORTSC_PEC)
wPortChange |= USB_PORT_STAT_C_ENABLE;
if (status & USBPORTSC_OCC)
wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
if (test_bit(port, &uhci->port_c_suspend)) {
wPortChange |= USB_PORT_STAT_C_SUSPEND;
lstatus |= 1;
}
if (test_bit(port, &uhci->suspended_ports))
lstatus |= 2;
if (test_bit(port, &uhci->resuming_ports))
lstatus |= 4;
/* UHCI has no power switching (always on) */
wPortStatus = USB_PORT_STAT_POWER;
if (status & USBPORTSC_CCS)
wPortStatus |= USB_PORT_STAT_CONNECTION;
if (status & USBPORTSC_PE) {
wPortStatus |= USB_PORT_STAT_ENABLE;
if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
wPortStatus |= USB_PORT_STAT_SUSPEND;
}
if (status & USBPORTSC_OC)
wPortStatus |= USB_PORT_STAT_OVERCURRENT;
if (status & USBPORTSC_PR)
wPortStatus |= USB_PORT_STAT_RESET;
if (status & USBPORTSC_LSDA)
wPortStatus |= USB_PORT_STAT_LOW_SPEED;
if (wPortChange)
dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
wIndex, status, lstatus);
*(__le16 *)buf = cpu_to_le16(wPortStatus);
*(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
OK(4);
case SetHubFeature: /* We don't implement these */
case ClearHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
case C_HUB_LOCAL_POWER:
OK(0);
default:
goto err;
}
break;
case SetPortFeature:
if (port >= uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
set_bit(port, &uhci->suspended_ports);
SET_RH_PORTSTAT(USBPORTSC_SUSP);
OK(0);
case USB_PORT_FEAT_RESET:
SET_RH_PORTSTAT(USBPORTSC_PR);
/* Reset terminates Resume signalling */
uhci_finish_suspend(uhci, port, port_addr);
/* USB v2.0 7.1.7.5 */
uhci->ports_timeout = jiffies + msecs_to_jiffies(50);
OK(0);
case USB_PORT_FEAT_POWER:
/* UHCI has no power switching */
OK(0);
default:
goto err;
}
break;
case ClearPortFeature:
if (port >= uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PE);
/* Disable terminates Resume signalling */
uhci_finish_suspend(uhci, port, port_addr);
OK(0);
case USB_PORT_FEAT_C_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PEC);
OK(0);
case USB_PORT_FEAT_SUSPEND:
if (test_bit(port, &uhci->suspended_ports) &&
!test_and_set_bit(port,
&uhci->resuming_ports)) {
SET_RH_PORTSTAT(USBPORTSC_RD);
/* The controller won't allow RD to be set
* if the port is disabled. When this happens
* just skip the Resume signalling.
*/
if (!(inw(port_addr) & USBPORTSC_RD))
uhci_finish_suspend(uhci, port,
port_addr);
else
/* USB v2.0 7.1.7.7 */
uhci->ports_timeout = jiffies +
msecs_to_jiffies(20);
}
OK(0);
case USB_PORT_FEAT_C_SUSPEND:
clear_bit(port, &uhci->port_c_suspend);
OK(0);
case USB_PORT_FEAT_POWER:
/* UHCI has no power switching */
goto err;
case USB_PORT_FEAT_C_CONNECTION:
CLR_RH_PORTSTAT(USBPORTSC_CSC);
OK(0);
case USB_PORT_FEAT_C_OVER_CURRENT:
CLR_RH_PORTSTAT(USBPORTSC_OCC);
OK(0);
case USB_PORT_FEAT_C_RESET:
/* this driver won't report these */
OK(0);
default:
goto err;
}
break;
case GetHubDescriptor:
len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
memcpy(buf, root_hub_hub_des, len);
if (len > 2)
buf[2] = uhci->rh_numports;
OK(len);
default:
err:
retval = -EPIPE;
}
spin_unlock_irqrestore(&uhci->lock, flags);
return retval;
}

1539
drivers/usb/host/uhci-q.c Normal file

File diff suppressed because it is too large Load diff

30
drivers/usb/image/Kconfig Normal file
View file

@ -0,0 +1,30 @@
#
# USB Imageing devices configuration
#
comment "USB Imaging devices"
depends on USB
config USB_MDC800
tristate "USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)"
depends on USB && EXPERIMENTAL
---help---
Say Y here if you want to connect this type of still camera to
your computer's USB port. This driver can be used with gphoto 0.4.3
and higher (look at <http://www.gphoto.org/>).
To use it create a device node with "mknod /dev/mustek c 180 32" and
configure it in your software.
To compile this driver as a module, choose M here: the
module will be called mdc800.
config USB_MICROTEK
tristate "Microtek X6USB scanner support"
depends on USB && SCSI
help
Say Y here if you want support for the Microtek X6USB and
possibly the Phantom 336CX, Phantom C6 and ScanMaker V6U(S)L.
Support for anything but the X6 is experimental.
Please report failures and successes.
The scanner will appear as a scsi generic device to the rest
of the system. Scsi support is required.
This driver can be compiled as a module, called microtek.

View file

@ -0,0 +1,6 @@
#
# Makefile for USB Image drivers
#
obj-$(CONFIG_USB_MDC800) += mdc800.o
obj-$(CONFIG_USB_MICROTEK) += microtek.o

1063
drivers/usb/image/mdc800.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,862 @@
/* Driver for Microtek Scanmaker X6 USB scanner, and possibly others.
*
* (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
* (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
*
* Parts shamelessly stolen from usb-storage and copyright by their
* authors. Thanks to Matt Dharm for giving us permission!
*
* This driver implements a SCSI host controller driver and a USB
* device driver. To avoid confusion, all the USB related stuff is
* prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_.
*
* Microtek (www.microtek.com) did not release the specifications for
* their USB protocol to us, so we had to reverse engineer them. We
* don't know for which models they are valid.
*
* The X6 USB has three bulk endpoints, one output (0x1) down which
* commands and outgoing data are sent, and two input: 0x82 from which
* normal data is read from the scanner (in packets of maximum 32
* bytes) and from which the status byte is read, and 0x83 from which
* the results of a scan (or preview) are read in up to 64 * 1024 byte
* chunks by the Windows driver. We don't know how much it is possible
* to read at a time from 0x83.
*
* It seems possible to read (with URB transfers) everything from 0x82
* in one go, without bothering to read in 32 byte chunks.
*
* There seems to be an optimisation of a further READ implicit if
* you simply read from 0x83.
*
* Guessed protocol:
*
* Send raw SCSI command to EP 0x1
*
* If there is data to receive:
* If the command was READ datatype=image:
* Read a lot of data from EP 0x83
* Else:
* Read data from EP 0x82
* Else:
* If there is data to transmit:
* Write it to EP 0x1
*
* Read status byte from EP 0x82
*
* References:
*
* The SCSI command set for the scanner is available from
* ftp://ftp.microtek.com/microtek/devpack/
*
* Microtek NV sent us a more up to date version of the document. If
* you want it, just send mail.
*
* Status:
*
* Untested with multiple scanners.
* Untested on SMP.
* Untested on a bigendian machine.
*
* History:
*
* 20000417 starting history
* 20000417 fixed load oops
* 20000417 fixed unload oops
* 20000419 fixed READ IMAGE detection
* 20000424 started conversion to use URBs
* 20000502 handled short transfers as errors
* 20000513 rename and organisation of functions (john)
* 20000513 added IDs for all products supported by Windows driver (john)
* 20000514 Rewrote mts_scsi_queuecommand to use URBs (john)
* 20000514 Version 0.0.8j
* 20000514 Fix reporting of non-existant devices to SCSI layer (john)
* 20000514 Added MTS_DEBUG_INT (john)
* 20000514 Changed "usb-microtek" to "microtek" for consistency (john)
* 20000514 Stupid bug fixes (john)
* 20000514 Version 0.0.9j
* 20000515 Put transfer context and URB in mts_desc (john)
* 20000515 Added prelim turn off debugging support (john)
* 20000515 Version 0.0.10j
* 20000515 Fixed up URB allocation (clear URB on alloc) (john)
* 20000515 Version 0.0.11j
* 20000516 Removed unnecessary spinlock in mts_transfer_context (john)
* 20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john)
* 20000516 Implemented (badly) scsi_abort (john)
* 20000516 Version 0.0.12j
* 20000517 Hopefully removed mts_remove_nolock quasideadlock (john)
* 20000517 Added mts_debug_dump to print ll USB info (john)
* 20000518 Tweaks and documentation updates (john)
* 20000518 Version 0.0.13j
* 20000518 Cleaned up abort handling (john)
* 20000523 Removed scsi_command and various scsi_..._resets (john)
* 20000523 Added unlink URB on scsi_abort, now OHCI supports it (john)
* 20000523 Fixed last tiresome compile warning (john)
* 20000523 Version 0.0.14j (though version 0.1 has come out?)
* 20000602 Added primitive reset
* 20000602 Version 0.2.0
* 20000603 various cosmetic changes
* 20000603 Version 0.2.1
* 20000620 minor cosmetic changes
* 20000620 Version 0.2.2
* 20000822 Hopefully fixed deadlock in mts_remove_nolock()
* 20000822 Fixed minor race in mts_transfer_cleanup()
* 20000822 Fixed deadlock on submission error in queuecommand
* 20000822 Version 0.2.3
* 20000913 Reduced module size if debugging is off
* 20000913 Version 0.2.4
* 20010210 New abort logic
* 20010210 Version 0.3.0
* 20010217 Merged scatter/gather
* 20010218 Version 0.4.0
* 20010218 Cosmetic fixes
* 20010218 Version 0.4.1
* 20010306 Abort while using scatter/gather
* 20010306 Version 0.4.2
* 20010311 Remove all timeouts and tidy up generally (john)
* 20010320 check return value of scsi_register()
* 20010320 Version 0.4.3
* 20010408 Identify version on module load.
* 20011003 Fix multiple requests
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
#include <linux/proc_fs.h>
#include <asm/atomic.h>
#include <linux/blkdev.h>
#include "../../scsi/scsi.h"
#include <scsi/scsi_host.h>
#include "microtek.h"
/*
* Version Information
*/
#define DRIVER_VERSION "v0.4.3"
#define DRIVER_AUTHOR "John Fremlin <[email protected]>, Oliver Neukum <[email protected]>"
#define DRIVER_DESC "Microtek Scanmaker X6 USB scanner driver"
/* Should we do debugging? */
//#define MTS_DO_DEBUG
/* USB layer driver interface */
static int mts_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id);
static void mts_usb_disconnect(struct usb_interface *intf);
static struct usb_device_id mts_usb_ids [];
static struct usb_driver mts_usb_driver = {
.owner = THIS_MODULE,
.name = "microtekX6",
.probe = mts_usb_probe,
.disconnect = mts_usb_disconnect,
.id_table = mts_usb_ids,
};
/* Internal driver stuff */
#define MTS_VERSION "0.4.3"
#define MTS_NAME "microtek usb (rev " MTS_VERSION "): "
#define MTS_WARNING(x...) \
printk( KERN_WARNING MTS_NAME x )
#define MTS_ERROR(x...) \
printk( KERN_ERR MTS_NAME x )
#define MTS_INT_ERROR(x...) \
MTS_ERROR(x)
#define MTS_MESSAGE(x...) \
printk( KERN_INFO MTS_NAME x )
#if defined MTS_DO_DEBUG
#define MTS_DEBUG(x...) \
printk( KERN_DEBUG MTS_NAME x )
#define MTS_DEBUG_GOT_HERE() \
MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ )
#define MTS_DEBUG_INT() \
do { MTS_DEBUG_GOT_HERE(); \
MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
mts_debug_dump(context->instance);\
} while(0)
#else
#define MTS_NUL_STATEMENT do { } while(0)
#define MTS_DEBUG(x...) MTS_NUL_STATEMENT
#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT
#define MTS_DEBUG_INT() MTS_NUL_STATEMENT
#endif
#define MTS_INT_INIT()\
struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; \
MTS_DEBUG_INT();\
#ifdef MTS_DO_DEBUG
static inline void mts_debug_dump(struct mts_desc* desc) {
MTS_DEBUG("desc at 0x%x: toggle = %02x%02x\n",
(int)desc,
(int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0]
);
MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n",
usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
usb_rcvbulkpipe(desc->usb_dev,desc->ep_response),
usb_rcvbulkpipe(desc->usb_dev,desc->ep_image)
);
}
static inline void mts_show_command(Scsi_Cmnd *srb)
{
char *what = NULL;
switch (srb->cmnd[0]) {
case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
case REZERO_UNIT: what = "REZERO_UNIT"; break;
case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
case READ_6: what = "READ_6"; break;
case WRITE_6: what = "WRITE_6"; break;
case SEEK_6: what = "SEEK_6"; break;
case READ_REVERSE: what = "READ_REVERSE"; break;
case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
case SPACE: what = "SPACE"; break;
case INQUIRY: what = "INQUIRY"; break;
case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
case MODE_SELECT: what = "MODE_SELECT"; break;
case RESERVE: what = "RESERVE"; break;
case RELEASE: what = "RELEASE"; break;
case COPY: what = "COPY"; break;
case ERASE: what = "ERASE"; break;
case MODE_SENSE: what = "MODE_SENSE"; break;
case START_STOP: what = "START_STOP"; break;
case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
case SET_WINDOW: what = "SET_WINDOW"; break;
case READ_CAPACITY: what = "READ_CAPACITY"; break;
case READ_10: what = "READ_10"; break;
case WRITE_10: what = "WRITE_10"; break;
case SEEK_10: what = "SEEK_10"; break;
case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
case VERIFY: what = "VERIFY"; break;
case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
case SEARCH_LOW: what = "SEARCH_LOW"; break;
case SET_LIMITS: what = "SET_LIMITS"; break;
case READ_POSITION: what = "READ_POSITION"; break;
case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
case COMPARE: what = "COMPARE"; break;
case COPY_VERIFY: what = "COPY_VERIFY"; break;
case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
case READ_BUFFER: what = "READ_BUFFER"; break;
case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
case READ_LONG: what = "READ_LONG"; break;
case WRITE_LONG: what = "WRITE_LONG"; break;
case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
case WRITE_SAME: what = "WRITE_SAME"; break;
case READ_TOC: what = "READ_TOC"; break;
case LOG_SELECT: what = "LOG_SELECT"; break;
case LOG_SENSE: what = "LOG_SENSE"; break;
case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
case READ_12: what = "READ_12"; break;
case WRITE_12: what = "WRITE_12"; break;
case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
default:
MTS_DEBUG("can't decode command\n");
goto out;
break;
}
MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len);
out:
MTS_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
}
#else
static inline void mts_show_command(Scsi_Cmnd * dummy)
{
}
static inline void mts_debug_dump(struct mts_desc* dummy)
{
}
#endif
static inline void mts_urb_abort(struct mts_desc* desc) {
MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
usb_kill_urb( desc->urb );
}
static int mts_scsi_abort (Scsi_Cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
MTS_DEBUG_GOT_HERE();
mts_urb_abort(desc);
return SCSI_ABORT_PENDING;
}
static int mts_scsi_host_reset (Scsi_Cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int result, rc;
MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
if (rc < 0)
return FAILED;
result = usb_reset_device(desc->usb_dev);;
if (rc)
usb_unlock_device(desc->usb_dev);
return result ? FAILED : SUCCESS;
}
static
int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback );
static void mts_transfer_cleanup( struct urb *transfer );
static void mts_do_sg(struct urb * transfer, struct pt_regs *regs);
inline static
void mts_int_submit_urb (struct urb* transfer,
int pipe,
void* data,
unsigned length,
usb_complete_t callback )
/* Interrupt context! */
/* Holding transfer->context->lock! */
{
int res;
MTS_INT_INIT();
usb_fill_bulk_urb(transfer,
context->instance->usb_dev,
pipe,
data,
length,
callback,
context
);
transfer->status = 0;
res = usb_submit_urb( transfer, GFP_ATOMIC );
if ( unlikely(res) ) {
MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
context->srb->result = DID_ERROR << 16;
mts_transfer_cleanup(transfer);
}
return;
}
static void mts_transfer_cleanup( struct urb *transfer )
/* Interrupt context! */
{
MTS_INT_INIT();
if ( likely(context->final_callback != NULL) )
context->final_callback(context->srb);
}
static void mts_transfer_done( struct urb *transfer, struct pt_regs *regs )
{
MTS_INT_INIT();
context->srb->result &= MTS_SCSI_ERR_MASK;
context->srb->result |= (unsigned)context->status<<1;
mts_transfer_cleanup(transfer);
return;
}
static void mts_get_status( struct urb *transfer )
/* Interrupt context! */
{
MTS_INT_INIT();
mts_int_submit_urb(transfer,
usb_rcvbulkpipe(context->instance->usb_dev,
context->instance->ep_response),
&context->status,
1,
mts_transfer_done );
}
static void mts_data_done( struct urb* transfer, struct pt_regs *regs )
/* Interrupt context! */
{
MTS_INT_INIT();
if ( context->data_length != transfer->actual_length ) {
context->srb->resid = context->data_length - transfer->actual_length;
} else if ( unlikely(transfer->status) ) {
context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
}
mts_get_status(transfer);
return;
}
static void mts_command_done( struct urb *transfer, struct pt_regs *regs )
/* Interrupt context! */
{
MTS_INT_INIT();
if ( unlikely(transfer->status) ) {
if (transfer->status == -ENOENT) {
/* We are being killed */
MTS_DEBUG_GOT_HERE();
context->srb->result = DID_ABORT<<16;
} else {
/* A genuine error has occurred */
MTS_DEBUG_GOT_HERE();
context->srb->result = DID_ERROR<<16;
}
mts_transfer_cleanup(transfer);
return;
}
if (context->srb->cmnd[0] == REQUEST_SENSE) {
mts_int_submit_urb(transfer,
context->data_pipe,
context->srb->sense_buffer,
context->data_length,
mts_data_done);
} else { if ( context->data ) {
mts_int_submit_urb(transfer,
context->data_pipe,
context->data,
context->data_length,
context->srb->use_sg ? mts_do_sg : mts_data_done);
} else {
mts_get_status(transfer);
}
}
return;
}
static void mts_do_sg (struct urb* transfer, struct pt_regs *regs)
{
struct scatterlist * sg;
MTS_INT_INIT();
MTS_DEBUG("Processing fragment %d of %d\n", context->fragment,context->srb->use_sg);
if (unlikely(transfer->status)) {
context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
mts_transfer_cleanup(transfer);
}
sg = context->srb->buffer;
context->fragment++;
mts_int_submit_urb(transfer,
context->data_pipe,
page_address(sg[context->fragment].page) +
sg[context->fragment].offset,
sg[context->fragment].length,
context->fragment + 1 == context->srb->use_sg ? mts_data_done : mts_do_sg);
return;
}
static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
static const u8 mts_read_image_sig_len = 4;
static const unsigned char mts_direction[256/8] = {
0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
static void
mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
{
int pipe;
struct scatterlist * sg;
MTS_DEBUG_GOT_HERE();
desc->context.instance = desc;
desc->context.srb = srb;
desc->context.fragment = 0;
if (!srb->use_sg) {
if ( !srb->bufflen ){
desc->context.data = NULL;
desc->context.data_length = 0;
return;
} else {
desc->context.data = srb->buffer;
desc->context.data_length = srb->bufflen;
MTS_DEBUG("length = %d or %d\n",
srb->request_bufflen, srb->bufflen);
}
} else {
MTS_DEBUG("Using scatter/gather\n");
sg = srb->buffer;
desc->context.data = page_address(sg[0].page) + sg[0].offset;
desc->context.data_length = sg[0].length;
}
/* can't rely on srb->sc_data_direction */
/* Brutally ripped from usb-storage */
if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len )
) { pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image);
MTS_DEBUG( "transfering from desc->ep_image == %d\n",
(int)desc->ep_image );
} else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) {
pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response);
MTS_DEBUG( "transfering from desc->ep_response == %d\n",
(int)desc->ep_response);
} else {
MTS_DEBUG("transfering to desc->ep_out == %d\n",
(int)desc->ep_out);
pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out);
}
desc->context.data_pipe = pipe;
}
static
int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback )
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int err = 0;
int res;
MTS_DEBUG_GOT_HERE();
mts_show_command(srb);
mts_debug_dump(desc);
if ( srb->device->lun || srb->device->id || srb->device->channel ) {
MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel );
MTS_DEBUG("this device doesn't exist\n");
srb->result = DID_BAD_TARGET << 16;
if(likely(callback != NULL))
callback(srb);
goto out;
}
usb_fill_bulk_urb(desc->urb,
desc->usb_dev,
usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
srb->cmnd,
srb->cmd_len,
mts_command_done,
&desc->context
);
mts_build_transfer_context( srb, desc );
desc->context.final_callback = callback;
/* here we need ATOMIC as we are called with the iolock */
res=usb_submit_urb(desc->urb, GFP_ATOMIC);
if(unlikely(res)){
MTS_ERROR("error %d submitting URB\n",(int)res);
srb->result = DID_ERROR << 16;
if(likely(callback != NULL))
callback(srb);
}
out:
return err;
}
static Scsi_Host_Template mts_scsi_host_template = {
.module = THIS_MODULE,
.name = "microtekX6",
.proc_name = "microtekX6",
.queuecommand = mts_scsi_queuecommand,
.eh_abort_handler = mts_scsi_abort,
.eh_host_reset_handler = mts_scsi_host_reset,
.sg_tablesize = SG_ALL,
.can_queue = 1,
.this_id = -1,
.cmd_per_lun = 1,
.use_clustering = 1,
.emulated = 1,
};
struct vendor_product
{
char* name;
enum
{
mts_sup_unknown=0,
mts_sup_alpha,
mts_sup_full
}
support_status;
} ;
/* These are taken from the msmUSB.inf file on the Windows driver CD */
const static struct vendor_product mts_supported_products[] =
{
{ "Phantom 336CX", mts_sup_unknown},
{ "Phantom 336CX", mts_sup_unknown},
{ "Scanmaker X6", mts_sup_alpha},
{ "Phantom C6", mts_sup_unknown},
{ "Phantom 336CX", mts_sup_unknown},
{ "ScanMaker V6USL", mts_sup_unknown},
{ "ScanMaker V6USL", mts_sup_unknown},
{ "Scanmaker V6UL", mts_sup_unknown},
{ "Scanmaker V6UPL", mts_sup_alpha},
};
/* The entries of microtek_table must correspond, line-by-line to
the entries of mts_supported_products[]. */
static struct usb_device_id mts_usb_ids [] =
{
{ USB_DEVICE(0x4ce, 0x0300) },
{ USB_DEVICE(0x5da, 0x0094) },
{ USB_DEVICE(0x5da, 0x0099) },
{ USB_DEVICE(0x5da, 0x009a) },
{ USB_DEVICE(0x5da, 0x00a0) },
{ USB_DEVICE(0x5da, 0x00a3) },
{ USB_DEVICE(0x5da, 0x80a3) },
{ USB_DEVICE(0x5da, 0x80ac) },
{ USB_DEVICE(0x5da, 0x00b6) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, mts_usb_ids);
static int mts_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int i;
int ep_out = -1;
int ep_in_set[3]; /* this will break if we have more than three endpoints
which is why we check */
int *ep_in_current = ep_in_set;
int err_retval = -ENOMEM;
struct mts_desc * new_desc;
struct vendor_product const* p;
struct usb_device *dev = interface_to_usbdev (intf);
/* the current altsetting on the interface we're probing */
struct usb_host_interface *altsetting;
MTS_DEBUG_GOT_HERE();
MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
le16_to_cpu(dev->descriptor.idProduct),
le16_to_cpu(dev->descriptor.idVendor) );
MTS_DEBUG_GOT_HERE();
p = &mts_supported_products[id - mts_usb_ids];
MTS_DEBUG_GOT_HERE();
MTS_DEBUG( "found model %s\n", p->name );
if ( p->support_status != mts_sup_full )
MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n",
p->name );
/* the current altsetting on the interface we're probing */
altsetting = intf->cur_altsetting;
/* Check if the config is sane */
if ( altsetting->desc.bNumEndpoints != MTS_EP_TOTAL ) {
MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
(int)MTS_EP_TOTAL, (int)altsetting->desc.bNumEndpoints );
return -ENODEV;
}
for( i = 0; i < altsetting->desc.bNumEndpoints; i++ ) {
if ((altsetting->endpoint[i].desc.bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
(int)altsetting->endpoint[i].desc.bEndpointAddress );
} else {
if (altsetting->endpoint[i].desc.bEndpointAddress &
USB_DIR_IN)
*ep_in_current++
= altsetting->endpoint[i].desc.bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
else {
if ( ep_out != -1 ) {
MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
return -ENODEV;
}
ep_out = altsetting->endpoint[i].desc.bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
}
}
}
if ( ep_out == -1 ) {
MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
return -ENODEV;
}
new_desc = kmalloc(sizeof(struct mts_desc), GFP_KERNEL);
if (!new_desc)
goto out;
memset(new_desc, 0, sizeof(*new_desc));
new_desc->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!new_desc->urb)
goto out_kfree;
new_desc->usb_dev = dev;
new_desc->usb_intf = intf;
init_MUTEX(&new_desc->lock);
/* endpoints */
new_desc->ep_out = ep_out;
new_desc->ep_response = ep_in_set[0];
new_desc->ep_image = ep_in_set[1];
if ( new_desc->ep_out != MTS_EP_OUT )
MTS_WARNING( "will this work? Command EP is not usually %d\n",
(int)new_desc->ep_out );
if ( new_desc->ep_response != MTS_EP_RESPONSE )
MTS_WARNING( "will this work? Response EP is not usually %d\n",
(int)new_desc->ep_response );
if ( new_desc->ep_image != MTS_EP_IMAGE )
MTS_WARNING( "will this work? Image data EP is not usually %d\n",
(int)new_desc->ep_image );
new_desc->host = scsi_host_alloc(&mts_scsi_host_template,
sizeof(new_desc));
if (!new_desc->host)
goto out_free_urb;
new_desc->host->hostdata[0] = (unsigned long)new_desc;
if (scsi_add_host(new_desc->host, NULL)) {
err_retval = -EIO;
goto out_free_urb;
}
scsi_scan_host(new_desc->host);
usb_set_intfdata(intf, new_desc);
return 0;
out_free_urb:
usb_free_urb(new_desc->urb);
out_kfree:
kfree(new_desc);
out:
return err_retval;
}
static void mts_usb_disconnect (struct usb_interface *intf)
{
struct mts_desc *desc = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
usb_kill_urb(desc->urb);
scsi_remove_host(desc->host);
scsi_host_put(desc->host);
usb_free_urb(desc->urb);
kfree(desc);
}
static int __init microtek_drv_init(void)
{
return usb_register(&mts_usb_driver);
}
static void __exit microtek_drv_exit(void)
{
usb_deregister(&mts_usb_driver);
}
module_init(microtek_drv_init);
module_exit(microtek_drv_exit);
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,55 @@
/*
* Driver for Microtek Scanmaker X6 USB scanner and possibly others.
*
* (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
* (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
*
* See microtek.c for history
*
*/
typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *);
struct mts_transfer_context
{
struct mts_desc* instance;
mts_scsi_cmnd_callback final_callback;
Scsi_Cmnd *srb;
void* data;
unsigned data_length;
int data_pipe;
int fragment;
u8 status; /* status returned from ep_response after command completion */
};
struct mts_desc {
struct mts_desc *next;
struct mts_desc *prev;
struct usb_device *usb_dev;
struct usb_interface *usb_intf;
/* Endpoint addresses */
u8 ep_out;
u8 ep_response;
u8 ep_image;
struct Scsi_Host * host;
struct semaphore lock;
struct urb *urb;
struct mts_transfer_context context;
};
#define MTS_EP_OUT 0x1
#define MTS_EP_RESPONSE 0x2
#define MTS_EP_IMAGE 0x3
#define MTS_EP_TOTAL 0x3
#define MTS_SCSI_ERR_MASK ~0x3fu

237
drivers/usb/input/Kconfig Normal file
View file

@ -0,0 +1,237 @@
#
# USB Input driver configuration
#
comment "USB Input Devices"
depends on USB
config USB_HID
tristate "USB Human Interface Device (full HID) support"
depends on USB
---help---
Say Y here if you want full HID support to connect keyboards,
mice, joysticks, graphic tablets, or any other HID based devices
to your computer via USB. You also need to select HID Input layer
support (below) if you want to use keyboards, mice, joysticks and
the like ... as well as Uninterruptible Power Supply (UPS) and
monitor control devices.
You can't use this driver and the HIDBP (Boot Protocol) keyboard
and mouse drivers at the same time. More information is available:
<file:Documentation/input/input.txt>.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called usbhid.
comment "Input core support is needed for USB HID input layer or HIDBP support"
depends on USB_HID && INPUT=n
config USB_HIDINPUT
bool "HID input layer support"
default y
depends on INPUT && USB_HID
help
Say Y here if you want to use a USB keyboard, mouse or joystick,
or any other HID input device.
If unsure, say Y.
config HID_FF
bool "Force feedback support (EXPERIMENTAL)"
depends on USB_HIDINPUT && EXPERIMENTAL
help
Say Y here is you want force feedback support for a few HID devices.
See below for a list of supported devices.
See <file:Documentation/input/ff.txt> for a description of the force
feedback API.
If unsure, say N.
config HID_PID
bool "PID Devices (Microsoft Sidewinder Force Feedback 2)"
depends on HID_FF
help
Say Y here if you have a PID-compliant joystick and wish to enable force
feedback for it. The Microsoft Sidewinder Force Feedback 2 is one such
device.
config LOGITECH_FF
bool "Logitech WingMan *3D support"
depends on HID_FF
help
Say Y here if you have one of these devices:
- Logitech WingMan Cordless RumblePad
- Logitech WingMan Force 3D
and if you want to enable force feedback for them.
Note: if you say N here, this device will still be supported, but without
force feedback.
config THRUSTMASTER_FF
bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
depends on HID_FF && EXPERIMENTAL
help
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
and want to enable force feedback support for it.
Note: if you say N here, this device will still be supported, but without
force feedback.
config USB_HIDDEV
bool "/dev/hiddev raw HID device support"
depends on USB_HID
help
Say Y here if you want to support HID devices (from the USB
specification standpoint) that aren't strictly user interface
devices, like monitor controls and Uninterruptable Power Supplies.
This module supports these devices separately using a separate
event interface on /dev/usb/hiddevX (char 180:96 to 180:111).
If unsure, say Y.
menu "USB HID Boot Protocol drivers"
depends on USB!=n && USB_HID!=y
config USB_KBD
tristate "USB HIDBP Keyboard (simple Boot) support"
depends on USB && INPUT
---help---
Say Y here only if you are absolutely sure that you don't want
to use the generic HID driver for your USB keyboard and prefer
to use the keyboard in its limited Boot Protocol mode instead.
This is almost certainly not what you want. This is mostly
useful for embedded applications or simple keyboards.
To compile this driver as a module, choose M here: the
module will be called usbkbd.
If even remotely unsure, say N.
config USB_MOUSE
tristate "USB HIDBP Mouse (simple Boot) support"
depends on USB && INPUT
---help---
Say Y here only if you are absolutely sure that you don't want
to use the generic HID driver for your USB mouse and prefer
to use the mouse in its limited Boot Protocol mode instead.
This is almost certainly not what you want. This is mostly
useful for embedded applications or simple mice.
To compile this driver as a module, choose M here: the
module will be called usbmouse.
If even remotely unsure, say N.
endmenu
config USB_AIPTEK
tristate "Aiptek 6000U/8000U tablet support"
depends on USB && INPUT
help
Say Y here if you want to use the USB version of the Aiptek 6000U
or Aiptek 8000U tablet. Make sure to say Y to "Mouse support"
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
(CONFIG_INPUT_EVDEV) as well.
To compile this driver as a module, choose M here: the
module will be called aiptek.
config USB_WACOM
tristate "Wacom Intuos/Graphire tablet support"
depends on USB && INPUT
help
Say Y here if you want to use the USB version of the Wacom Intuos
or Graphire tablet. Make sure to say Y to "Mouse support"
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
(CONFIG_INPUT_EVDEV) as well.
To compile this driver as a module, choose M here: the
module will be called wacom.
config USB_KBTAB
tristate "KB Gear JamStudio tablet support"
depends on USB && INPUT
help
Say Y here if you want to use the USB version of the KB Gear
JamStudio tablet. Make sure to say Y to "Mouse support"
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
(CONFIG_INPUT_EVDEV) as well.
To compile this driver as a module, choose M here: the
module will be called kbtab.
config USB_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB && INPUT
---help---
Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
These are aluminum dials which can measure clockwise and anticlockwise
rotation. The dial also acts as a pushbutton. The base contains an LED
which can be instructed to pulse or to switch to a particular intensity.
You can download userspace tools from
<http://sowerbutts.com/powermate/>.
To compile this driver as a module, choose M here: the
module will be called powermate.
config USB_MTOUCH
tristate "MicroTouch USB Touchscreen Driver"
depends on USB && INPUT
---help---
Say Y here if you want to use a MicroTouch (Now 3M) USB
Touchscreen controller.
See <file:Documentation/usb/mtouch.txt> for additional information.
To compile this driver as a module, choose M here: the
module will be called mtouchusb.
config USB_EGALAX
tristate "eGalax TouchKit USB Touchscreen Driver"
depends on USB && INPUT
---help---
Say Y here if you want to use a eGalax TouchKit USB
Touchscreen controller.
The driver has been tested on a Xenarc 700TSV monitor
with eGalax touchscreen.
Have a look at <http://linux.chapter7.ch/touchkit/> for
a usage description and the required user-space stuff.
To compile this driver as a module, choose M here: the
module will be called touchkitusb.
config USB_XPAD
tristate "X-Box gamepad support"
depends on USB && INPUT
---help---
Say Y here if you want to use the X-Box pad with your computer.
Make sure to say Y to "Joystick support" (CONFIG_INPUT_JOYDEV)
and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well.
For information about how to connect the X-Box pad to USB, see
<file:Documentation/input/xpad.txt>.
To compile this driver as a module, choose M here: the
module will be called xpad.
config USB_ATI_REMOTE
tristate "ATI / X10 USB RF remote control"
depends on USB && INPUT
---help---
Say Y here if you want to use an ATI or X10 "Lola" USB remote control.
These are RF remotes with USB receivers.
The ATI remote comes with many of ATI's All-In-Wonder video cards.
The X10 "Lola" remote is available at:
<http://www.x10.com/products/lola_sg1.htm>
This driver provides mouse pointer, left and right mouse buttons,
and maps all the other remote buttons to keypress events.
To compile this driver as a module, choose M here: the module will be
called ati_remote.

View file

@ -0,0 +1,39 @@
#
# Makefile for the USB input drivers
#
# Multipart objects.
usbhid-objs := hid-core.o
# Optional parts of multipart objects.
ifeq ($(CONFIG_USB_HIDDEV),y)
usbhid-objs += hiddev.o
endif
ifeq ($(CONFIG_USB_HIDINPUT),y)
usbhid-objs += hid-input.o
endif
ifeq ($(CONFIG_HID_PID),y)
usbhid-objs += pid.o
endif
ifeq ($(CONFIG_LOGITECH_FF),y)
usbhid-objs += hid-lgff.o
endif
ifeq ($(CONFIG_THRUSTMASTER_FF),y)
usbhid-objs += hid-tmff.o
endif
ifeq ($(CONFIG_HID_FF),y)
usbhid-objs += hid-ff.o
endif
obj-$(CONFIG_USB_AIPTEK) += aiptek.o
obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_USB_HID) += usbhid.o
obj-$(CONFIG_USB_KBD) += usbkbd.o
obj-$(CONFIG_USB_KBTAB) += kbtab.o
obj-$(CONFIG_USB_MOUSE) += usbmouse.o
obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o
obj-$(CONFIG_USB_EGALAX) += touchkitusb.o
obj-$(CONFIG_USB_POWERMATE) += powermate.o
obj-$(CONFIG_USB_WACOM) += wacom.o
obj-$(CONFIG_USB_XPAD) += xpad.o

Some files were not shown because too many files have changed in this diff Show more