Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
This commit is contained in:
Коммит
1f419cadff
|
@ -291,7 +291,7 @@
|
|||
|
||||
!Edrivers/usb/core/hcd.c
|
||||
!Edrivers/usb/core/hcd-pci.c
|
||||
!Edrivers/usb/core/buffer.c
|
||||
!Idrivers/usb/core/buffer.c
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
|
|
|
@ -2,7 +2,6 @@ Driver documentation for yealink usb-p1k phones
|
|||
|
||||
0. Status
|
||||
~~~~~~~~~
|
||||
|
||||
The p1k is a relatively cheap usb 1.1 phone with:
|
||||
- keyboard full support, yealink.ko / input event API
|
||||
- LCD full support, yealink.ko / sysfs API
|
||||
|
@ -17,9 +16,8 @@ For vendor documentation see http://www.yealink.com
|
|||
|
||||
1. Compilation (stand alone version)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Currently only kernel 2.6.x.y versions are supported.
|
||||
In order to build the yealink.ko module do:
|
||||
In order to build the yealink.ko module do
|
||||
|
||||
make
|
||||
|
||||
|
@ -28,6 +26,21 @@ the Makefile is pointing to the location where your kernel sources
|
|||
are located, default /usr/src/linux.
|
||||
|
||||
|
||||
1.1 Troubleshooting
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Q: Module yealink compiled and installed without any problem but phone
|
||||
is not initialized and does not react to any actions.
|
||||
A: If you see something like:
|
||||
hiddev0: USB HID v1.00 Device [Yealink Network Technology Ltd. VOIP USB Phone
|
||||
in dmesg, it means that the hid driver has grabbed the device first. Try to
|
||||
load module yealink before any other usb hid driver. Please see the
|
||||
instructions provided by your distribution on module configuration.
|
||||
|
||||
Q: Phone is working now (displays version and accepts keypad input) but I can't
|
||||
find the sysfs files.
|
||||
A: The sysfs files are located on the particular usb endpoint. On most
|
||||
distributions you can do: "find /sys/ -name get_icons" for a hint.
|
||||
|
||||
|
||||
2. keyboard features
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -1517,8 +1517,6 @@ running once the system is up.
|
|||
uart6850= [HW,OSS]
|
||||
Format: <io>,<irq>
|
||||
|
||||
usb-handoff [HW] Enable early USB BIOS -> OS handoff
|
||||
|
||||
usbhid.mousepoll=
|
||||
[USBHID] The interval which mice are to be polled at.
|
||||
|
||||
|
|
20
MAINTAINERS
20
MAINTAINERS
|
@ -116,12 +116,6 @@ M: ajk@iehk.rwth-aachen.de
|
|||
L: linux-hams@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
YEALINK PHONE DRIVER
|
||||
P: Henk Vergonet
|
||||
M: Henk.Vergonet@gmail.com
|
||||
L: usbb2k-api-dev@nongnu.org
|
||||
S: Maintained
|
||||
|
||||
8139CP 10/100 FAST ETHERNET DRIVER
|
||||
P: Jeff Garzik
|
||||
M: jgarzik@pobox.com
|
||||
|
@ -2495,14 +2489,6 @@ L: linux-kernel@vger.kernel.org
|
|||
L: linux-usb-devel@lists.sourceforge.net
|
||||
S: Supported
|
||||
|
||||
USB BLUETOOTH TTY CONVERTER DRIVER
|
||||
P: Greg Kroah-Hartman
|
||||
M: greg@kroah.com
|
||||
L: linux-usb-users@lists.sourceforge.net
|
||||
L: linux-usb-devel@lists.sourceforge.net
|
||||
S: Maintained
|
||||
W: http://www.kroah.com/linux-usb/
|
||||
|
||||
USB CDC ETHERNET DRIVER
|
||||
P: Greg Kroah-Hartman
|
||||
M: greg@kroah.com
|
||||
|
@ -2863,6 +2849,12 @@ M: jpr@f6fbb.org
|
|||
L: linux-hams@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
YEALINK PHONE DRIVER
|
||||
P: Henk Vergonet
|
||||
M: Henk.Vergonet@gmail.com
|
||||
L: usbb2k-api-dev@nongnu.org
|
||||
S: Maintained
|
||||
|
||||
YMFPCI YAMAHA PCI SOUND (Use ALSA instead)
|
||||
P: Pete Zaitcev
|
||||
M: zaitcev@yahoo.com
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# Rewritten to use lists instead of if-statements.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PCI) += pci/
|
||||
obj-$(CONFIG_PCI) += pci/ usb/
|
||||
obj-$(CONFIG_PARISC) += parisc/
|
||||
obj-y += video/
|
||||
obj-$(CONFIG_ACPI) += acpi/
|
||||
|
|
|
@ -30,23 +30,6 @@ LIST_HEAD(dpm_off_irq);
|
|||
DECLARE_MUTEX(dpm_sem);
|
||||
DECLARE_MUTEX(dpm_list_sem);
|
||||
|
||||
/*
|
||||
* PM Reference Counting.
|
||||
*/
|
||||
|
||||
static inline void device_pm_hold(struct device * dev)
|
||||
{
|
||||
if (dev)
|
||||
atomic_inc(&dev->power.pm_users);
|
||||
}
|
||||
|
||||
static inline void device_pm_release(struct device * dev)
|
||||
{
|
||||
if (dev)
|
||||
atomic_dec(&dev->power.pm_users);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* device_pm_set_parent - Specify power dependency.
|
||||
* @dev: Device who needs power.
|
||||
|
@ -62,10 +45,8 @@ static inline void device_pm_release(struct device * dev)
|
|||
|
||||
void device_pm_set_parent(struct device * dev, struct device * parent)
|
||||
{
|
||||
struct device * old_parent = dev->power.pm_parent;
|
||||
device_pm_release(old_parent);
|
||||
dev->power.pm_parent = parent;
|
||||
device_pm_hold(parent);
|
||||
put_device(dev->power.pm_parent);
|
||||
dev->power.pm_parent = get_device(parent);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_pm_set_parent);
|
||||
|
||||
|
@ -75,7 +56,6 @@ int device_pm_add(struct device * dev)
|
|||
|
||||
pr_debug("PM: Adding info for %s:%s\n",
|
||||
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
|
||||
atomic_set(&dev->power.pm_users, 0);
|
||||
down(&dpm_list_sem);
|
||||
list_add_tail(&dev->power.entry, &dpm_active);
|
||||
device_pm_set_parent(dev, dev->parent);
|
||||
|
@ -91,7 +71,7 @@ void device_pm_remove(struct device * dev)
|
|||
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
|
||||
down(&dpm_list_sem);
|
||||
dpm_sysfs_remove(dev);
|
||||
device_pm_release(dev->power.pm_parent);
|
||||
put_device(dev->power.pm_parent);
|
||||
list_del_init(&dev->power.entry);
|
||||
up(&dpm_list_sem);
|
||||
}
|
||||
|
|
|
@ -67,9 +67,6 @@ extern int suspend_device(struct device *, pm_message_t);
|
|||
* runtime.c
|
||||
*/
|
||||
|
||||
extern int dpm_runtime_suspend(struct device *, pm_message_t);
|
||||
extern void dpm_runtime_resume(struct device *);
|
||||
|
||||
#else /* CONFIG_PM */
|
||||
|
||||
|
||||
|
@ -82,14 +79,4 @@ static inline void device_pm_remove(struct device * dev)
|
|||
|
||||
}
|
||||
|
||||
static inline int dpm_runtime_suspend(struct device * dev, pm_message_t state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void dpm_runtime_resume(struct device * dev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -36,6 +36,7 @@ void dpm_runtime_resume(struct device * dev)
|
|||
runtime_resume(dev);
|
||||
up(&dpm_sem);
|
||||
}
|
||||
EXPORT_SYMBOL(dpm_runtime_resume);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -1512,7 +1512,7 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|||
scmd->nsg = 1;
|
||||
sg = &scmd->sgv[0];
|
||||
sg->page = virt_to_page(sc->top_sense);
|
||||
sg->offset = (unsigned int)sc->top_sense & (PAGE_SIZE-1);
|
||||
sg->offset = (unsigned long)sc->top_sense & (PAGE_SIZE-1);
|
||||
sg->length = UB_SENSE_SIZE;
|
||||
scmd->len = UB_SENSE_SIZE;
|
||||
scmd->lun = cmd->lun;
|
||||
|
@ -1891,7 +1891,7 @@ static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
|
|||
cmd->nsg = 1;
|
||||
sg = &cmd->sgv[0];
|
||||
sg->page = virt_to_page(p);
|
||||
sg->offset = (unsigned int)p & (PAGE_SIZE-1);
|
||||
sg->offset = (unsigned long)p & (PAGE_SIZE-1);
|
||||
sg->length = 8;
|
||||
cmd->len = 8;
|
||||
cmd->lun = lun;
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
*
|
||||
* Copyright (c) 1999 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Init/reset quirks for USB host controllers should be in the
|
||||
* USB quirks file, where their drivers can access reuse it.
|
||||
*
|
||||
* The bridge optimization stuff has been removed. If you really
|
||||
* have a silly BIOS which is unable to set your host bridge right,
|
||||
* use the PowerTweak utility (see http://powertweak.sourceforge.net).
|
||||
|
@ -644,28 +647,6 @@ static void quirk_via_irq(struct pci_dev *dev)
|
|||
}
|
||||
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_via_irq);
|
||||
|
||||
/*
|
||||
* PIIX3 USB: We have to disable USB interrupts that are
|
||||
* hardwired to PIRQD# and may be shared with an
|
||||
* external device.
|
||||
*
|
||||
* Legacy Support Register (LEGSUP):
|
||||
* bit13: USB PIRQ Enable (USBPIRQDEN),
|
||||
* bit4: Trap/SMI On IRQ Enable (USBSMIEN).
|
||||
*
|
||||
* We mask out all r/wc bits, too.
|
||||
*/
|
||||
static void __devinit quirk_piix3_usb(struct pci_dev *dev)
|
||||
{
|
||||
u16 legsup;
|
||||
|
||||
pci_read_config_word(dev, 0xc0, &legsup);
|
||||
legsup &= 0x50ef;
|
||||
pci_write_config_word(dev, 0xc0, legsup);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_2, quirk_piix3_usb );
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_2, quirk_piix3_usb );
|
||||
|
||||
/*
|
||||
* VIA VT82C598 has its device ID settable and many BIOSes
|
||||
* set it to the ID of VT82C597 for backward compatibility.
|
||||
|
@ -1039,234 +1020,6 @@ static void __init quirk_sis_96x_smbus(struct pci_dev *dev)
|
|||
pci_read_config_byte(dev, 0x77, &val);
|
||||
}
|
||||
|
||||
|
||||
#define UHCI_USBLEGSUP 0xc0 /* legacy support */
|
||||
#define UHCI_USBCMD 0 /* command register */
|
||||
#define UHCI_USBSTS 2 /* status register */
|
||||
#define UHCI_USBINTR 4 /* interrupt register */
|
||||
#define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
|
||||
#define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
|
||||
#define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */
|
||||
#define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */
|
||||
#define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */
|
||||
|
||||
#define OHCI_CONTROL 0x04
|
||||
#define OHCI_CMDSTATUS 0x08
|
||||
#define OHCI_INTRSTATUS 0x0c
|
||||
#define OHCI_INTRENABLE 0x10
|
||||
#define OHCI_INTRDISABLE 0x14
|
||||
#define OHCI_OCR (1 << 3) /* ownership change request */
|
||||
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
|
||||
#define OHCI_INTR_OC (1 << 30) /* ownership change */
|
||||
|
||||
#define EHCI_HCC_PARAMS 0x08 /* extended capabilities */
|
||||
#define EHCI_USBCMD 0 /* command register */
|
||||
#define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
|
||||
#define EHCI_USBSTS 4 /* status register */
|
||||
#define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */
|
||||
#define EHCI_USBINTR 8 /* interrupt register */
|
||||
#define EHCI_USBLEGSUP 0 /* legacy support register */
|
||||
#define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */
|
||||
#define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */
|
||||
#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
|
||||
#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */
|
||||
|
||||
int usb_early_handoff __devinitdata = 0;
|
||||
static int __init usb_handoff_early(char *str)
|
||||
{
|
||||
usb_early_handoff = 1;
|
||||
return 0;
|
||||
}
|
||||
__setup("usb-handoff", usb_handoff_early);
|
||||
|
||||
static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
|
||||
{
|
||||
unsigned long base = 0;
|
||||
int wait_time, delta;
|
||||
u16 val, sts;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_ROM_RESOURCE; i++)
|
||||
if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
|
||||
base = pci_resource_start(pdev, i);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!base)
|
||||
return;
|
||||
|
||||
/*
|
||||
* stop controller
|
||||
*/
|
||||
sts = inw(base + UHCI_USBSTS);
|
||||
val = inw(base + UHCI_USBCMD);
|
||||
val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE);
|
||||
outw(val, base + UHCI_USBCMD);
|
||||
|
||||
/*
|
||||
* wait while it stops if it was running
|
||||
*/
|
||||
if ((sts & UHCI_USBSTS_HALTED) == 0)
|
||||
{
|
||||
wait_time = 1000;
|
||||
delta = 100;
|
||||
|
||||
do {
|
||||
outw(0x1f, base + UHCI_USBSTS);
|
||||
udelay(delta);
|
||||
wait_time -= delta;
|
||||
val = inw(base + UHCI_USBSTS);
|
||||
if (val & UHCI_USBSTS_HALTED)
|
||||
break;
|
||||
} while (wait_time > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* disable interrupts & legacy support
|
||||
*/
|
||||
outw(0, base + UHCI_USBINTR);
|
||||
outw(0x1f, base + UHCI_USBSTS);
|
||||
pci_read_config_word(pdev, UHCI_USBLEGSUP, &val);
|
||||
if (val & 0xbf)
|
||||
pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT);
|
||||
|
||||
}
|
||||
|
||||
static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
int wait_time;
|
||||
|
||||
base = ioremap_nocache(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
if (base == NULL) return;
|
||||
|
||||
if (readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
|
||||
wait_time = 500; /* 0.5 seconds */
|
||||
writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);
|
||||
writel(OHCI_OCR, base + OHCI_CMDSTATUS);
|
||||
while (wait_time > 0 &&
|
||||
readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
|
||||
wait_time -= 10;
|
||||
msleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* disable interrupts
|
||||
*/
|
||||
writel(~(u32)0, base + OHCI_INTRDISABLE);
|
||||
writel(~(u32)0, base + OHCI_INTRSTATUS);
|
||||
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
|
||||
{
|
||||
int wait_time, delta;
|
||||
void __iomem *base, *op_reg_base;
|
||||
u32 hcc_params, val, temp;
|
||||
u8 cap_length;
|
||||
|
||||
base = ioremap_nocache(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
if (base == NULL) return;
|
||||
|
||||
cap_length = readb(base);
|
||||
op_reg_base = base + cap_length;
|
||||
hcc_params = readl(base + EHCI_HCC_PARAMS);
|
||||
hcc_params = (hcc_params >> 8) & 0xff;
|
||||
if (hcc_params) {
|
||||
pci_read_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGSUP,
|
||||
&val);
|
||||
if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) {
|
||||
/*
|
||||
* Ok, BIOS is in smm mode, try to hand off...
|
||||
*/
|
||||
pci_read_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGCTLSTS,
|
||||
&temp);
|
||||
pci_write_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGCTLSTS,
|
||||
temp | EHCI_USBLEGCTLSTS_SOOE);
|
||||
val |= EHCI_USBLEGSUP_OS;
|
||||
pci_write_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGSUP,
|
||||
val);
|
||||
|
||||
wait_time = 500;
|
||||
do {
|
||||
msleep(10);
|
||||
wait_time -= 10;
|
||||
pci_read_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGSUP,
|
||||
&val);
|
||||
} while (wait_time && (val & EHCI_USBLEGSUP_BIOS));
|
||||
if (!wait_time) {
|
||||
/*
|
||||
* well, possibly buggy BIOS...
|
||||
*/
|
||||
printk(KERN_WARNING "EHCI early BIOS handoff "
|
||||
"failed (BIOS bug ?)\n");
|
||||
pci_write_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGSUP,
|
||||
EHCI_USBLEGSUP_OS);
|
||||
pci_write_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGCTLSTS,
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* halt EHCI & disable its interrupts in any case
|
||||
*/
|
||||
val = readl(op_reg_base + EHCI_USBSTS);
|
||||
if ((val & EHCI_USBSTS_HALTED) == 0) {
|
||||
val = readl(op_reg_base + EHCI_USBCMD);
|
||||
val &= ~EHCI_USBCMD_RUN;
|
||||
writel(val, op_reg_base + EHCI_USBCMD);
|
||||
|
||||
wait_time = 2000;
|
||||
delta = 100;
|
||||
do {
|
||||
writel(0x3f, op_reg_base + EHCI_USBSTS);
|
||||
udelay(delta);
|
||||
wait_time -= delta;
|
||||
val = readl(op_reg_base + EHCI_USBSTS);
|
||||
if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) {
|
||||
break;
|
||||
}
|
||||
} while (wait_time > 0);
|
||||
}
|
||||
writel(0, op_reg_base + EHCI_USBINTR);
|
||||
writel(0x3f, op_reg_base + EHCI_USBSTS);
|
||||
|
||||
iounmap(base);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
|
||||
{
|
||||
if (!usb_early_handoff)
|
||||
return;
|
||||
|
||||
if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x00)) { /* UHCI */
|
||||
quirk_usb_handoff_uhci(pdev);
|
||||
} else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10)) { /* OHCI */
|
||||
quirk_usb_handoff_ohci(pdev);
|
||||
} else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x20)) { /* EHCI */
|
||||
quirk_usb_disable_ehci(pdev);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff);
|
||||
|
||||
/*
|
||||
* ... This is further complicated by the fact that some SiS96x south
|
||||
* bridges pretend to be 85C503/5513 instead. In that case see if we
|
||||
|
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_USB) += core/
|
|||
|
||||
obj-$(CONFIG_USB_MON) += mon/
|
||||
|
||||
obj-$(CONFIG_PCI) += host/
|
||||
obj-$(CONFIG_USB_EHCI_HCD) += host/
|
||||
obj-$(CONFIG_USB_ISP116X_HCD) += host/
|
||||
obj-$(CONFIG_USB_OHCI_HCD) += host/
|
||||
|
@ -17,7 +18,6 @@ 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/
|
||||
|
||||
|
|
|
@ -28,29 +28,6 @@ config USB_AUDIO
|
|||
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 && OBSOLETE_OSS_USB_DRIVER
|
||||
|
|
|
@ -5,6 +5,5 @@
|
|||
|
||||
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
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -827,11 +827,10 @@ skip_normal_probe:
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) {
|
||||
dev_dbg(&intf->dev, "out of memory (acm kmalloc)\n");
|
||||
if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
|
||||
dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
|
||||
goto alloc_fail;
|
||||
}
|
||||
memset(acm, 0, sizeof(struct acm));
|
||||
|
||||
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
|
||||
readsize = le16_to_cpu(epread->wMaxPacketSize);
|
||||
|
|
|
@ -844,9 +844,8 @@ static struct file_operations usblp_fops = {
|
|||
};
|
||||
|
||||
static struct usb_class_driver usblp_class = {
|
||||
.name = "usb/lp%d",
|
||||
.name = "lp%d",
|
||||
.fops = &usblp_fops,
|
||||
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
|
||||
.minor_base = USBLP_MINOR_BASE,
|
||||
};
|
||||
|
||||
|
|
|
@ -61,14 +61,17 @@ config USB_DYNAMIC_MINORS
|
|||
If you are unsure about this, say N here.
|
||||
|
||||
config USB_SUSPEND
|
||||
bool "USB suspend/resume (EXPERIMENTAL)"
|
||||
bool "USB selective suspend/resume and wakeup (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.
|
||||
peripherals.
|
||||
|
||||
Also, USB "remote wakeup" signaling is supported, whereby some
|
||||
USB devices (like keyboards and network adapters) can wake up
|
||||
their parent hub. That wakeup cascades up the USB tree, and
|
||||
could wake the system from states like suspend-to-RAM.
|
||||
|
||||
If you are unsure about this, say N here.
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
|
||||
usbcore-objs := usb.o hub.o hcd.o urb.o message.o \
|
||||
config.o file.o buffer.o sysfs.o devio.o
|
||||
config.o file.o buffer.o sysfs.o devio.o notify.o
|
||||
|
||||
ifeq ($(CONFIG_PCI),y)
|
||||
usbcore-objs += hcd-pci.o
|
||||
|
|
|
@ -112,8 +112,12 @@ 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);
|
||||
for (j = 0; j < intfc->num_altsetting; j++) {
|
||||
struct usb_host_interface *alt = &intfc->altsetting[j];
|
||||
|
||||
kfree(alt->endpoint);
|
||||
kfree(alt->string);
|
||||
}
|
||||
kfree(intfc);
|
||||
}
|
||||
|
||||
|
@ -188,10 +192,9 @@ static int usb_parse_interface(struct device *ddev, int cfgno,
|
|||
}
|
||||
|
||||
len = sizeof(struct usb_host_endpoint) * num_ep;
|
||||
alt->endpoint = kmalloc(len, GFP_KERNEL);
|
||||
alt->endpoint = kzalloc(len, GFP_KERNEL);
|
||||
if (!alt->endpoint)
|
||||
return -ENOMEM;
|
||||
memset(alt->endpoint, 0, len);
|
||||
|
||||
/* Parse all the endpoint descriptors */
|
||||
n = 0;
|
||||
|
@ -353,10 +356,9 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
|
|||
}
|
||||
|
||||
len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
|
||||
config->intf_cache[i] = intfc = kmalloc(len, GFP_KERNEL);
|
||||
config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL);
|
||||
if (!intfc)
|
||||
return -ENOMEM;
|
||||
memset(intfc, 0, len);
|
||||
kref_init(&intfc->ref);
|
||||
}
|
||||
|
||||
|
@ -422,8 +424,6 @@ void usb_destroy_configuration(struct usb_device *dev)
|
|||
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,
|
||||
|
@ -459,16 +459,14 @@ int usb_get_configuration(struct usb_device *dev)
|
|||
}
|
||||
|
||||
length = ncfg * sizeof(struct usb_host_config);
|
||||
dev->config = kmalloc(length, GFP_KERNEL);
|
||||
dev->config = kzalloc(length, GFP_KERNEL);
|
||||
if (!dev->config)
|
||||
goto err2;
|
||||
memset(dev->config, 0, length);
|
||||
|
||||
length = ncfg * sizeof(char *);
|
||||
dev->rawdescriptors = kmalloc(length, GFP_KERNEL);
|
||||
dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
|
||||
if (!dev->rawdescriptors)
|
||||
goto err2;
|
||||
memset(dev->rawdescriptors, 0, length);
|
||||
|
||||
buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
@ -209,10 +210,10 @@ err:
|
|||
static struct async *alloc_async(unsigned int numisoframes)
|
||||
{
|
||||
unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor);
|
||||
struct async *as = kmalloc(assize, GFP_KERNEL);
|
||||
struct async *as = kzalloc(assize, GFP_KERNEL);
|
||||
|
||||
if (!as)
|
||||
return NULL;
|
||||
memset(as, 0, assize);
|
||||
as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL);
|
||||
if (!as->urb) {
|
||||
kfree(as);
|
||||
|
@ -279,6 +280,28 @@ static inline struct async *async_getpending(struct dev_state *ps, void __user *
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void snoop_urb(struct urb *urb, void __user *userurb)
|
||||
{
|
||||
int j;
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
|
||||
if (!usbfs_snoop)
|
||||
return;
|
||||
|
||||
if (urb->pipe & USB_DIR_IN)
|
||||
dev_info(&urb->dev->dev, "direction=IN\n");
|
||||
else
|
||||
dev_info(&urb->dev->dev, "direction=OUT\n");
|
||||
dev_info(&urb->dev->dev, "userurb=%p\n", userurb);
|
||||
dev_info(&urb->dev->dev, "transfer_buffer_length=%d\n",
|
||||
urb->transfer_buffer_length);
|
||||
dev_info(&urb->dev->dev, "actual_length=%d\n", urb->actual_length);
|
||||
dev_info(&urb->dev->dev, "data: ");
|
||||
for (j = 0; j < urb->transfer_buffer_length; ++j)
|
||||
printk ("%02x ", data[j]);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
static void async_completed(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct async *as = (struct async *)urb->context;
|
||||
|
@ -296,7 +319,9 @@ static void async_completed(struct urb *urb, struct pt_regs *regs)
|
|||
kill_proc_info_as_uid(as->signr, &sinfo, as->pid, as->uid,
|
||||
as->euid);
|
||||
}
|
||||
wake_up(&ps->wait);
|
||||
snoop(&urb->dev->dev, "urb complete\n");
|
||||
snoop_urb(urb, as->userurb);
|
||||
wake_up(&ps->wait);
|
||||
}
|
||||
|
||||
static void destroy_async (struct dev_state *ps, struct list_head *list)
|
||||
|
@ -493,6 +518,23 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct usb_device *usbdev_lookup_minor(int minor)
|
||||
{
|
||||
struct class_device *class_dev;
|
||||
struct usb_device *dev = NULL;
|
||||
|
||||
down(&usb_device_class->sem);
|
||||
list_for_each_entry(class_dev, &usb_device_class->children, node) {
|
||||
if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
|
||||
dev = class_dev->class_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
up(&usb_device_class->sem);
|
||||
|
||||
return dev;
|
||||
};
|
||||
|
||||
/*
|
||||
* file operations
|
||||
*/
|
||||
|
@ -601,7 +643,7 @@ static int proc_control(struct dev_state *ps, void __user *arg)
|
|||
if (usbfs_snoop) {
|
||||
dev_info(&dev->dev, "control read: data ");
|
||||
for (j = 0; j < i; ++j)
|
||||
printk ("%02x ", (unsigned char)(tbuf)[j]);
|
||||
printk("%02x ", (unsigned char)(tbuf)[j]);
|
||||
printk("\n");
|
||||
}
|
||||
if (copy_to_user(ctrl.data, tbuf, i)) {
|
||||
|
@ -624,7 +666,7 @@ static int proc_control(struct dev_state *ps, void __user *arg)
|
|||
if (usbfs_snoop) {
|
||||
dev_info(&dev->dev, "control write: data: ");
|
||||
for (j = 0; j < ctrl.wLength; ++j)
|
||||
printk ("%02x ", (unsigned char)(tbuf)[j]);
|
||||
printk("%02x ", (unsigned char)(tbuf)[j]);
|
||||
printk("\n");
|
||||
}
|
||||
usb_unlock_device(dev);
|
||||
|
@ -649,7 +691,7 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
|
|||
unsigned int tmo, len1, pipe;
|
||||
int len2;
|
||||
unsigned char *tbuf;
|
||||
int i, ret;
|
||||
int i, j, ret;
|
||||
|
||||
if (copy_from_user(&bulk, arg, sizeof(bulk)))
|
||||
return -EFAULT;
|
||||
|
@ -674,10 +716,18 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
|
|||
kfree(tbuf);
|
||||
return -EINVAL;
|
||||
}
|
||||
snoop(&dev->dev, "bulk read: len=0x%02x timeout=%04d\n",
|
||||
bulk.len, bulk.timeout);
|
||||
usb_unlock_device(dev);
|
||||
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
|
||||
usb_lock_device(dev);
|
||||
if (!i && len2) {
|
||||
if (usbfs_snoop) {
|
||||
dev_info(&dev->dev, "bulk read: data ");
|
||||
for (j = 0; j < len2; ++j)
|
||||
printk("%02x ", (unsigned char)(tbuf)[j]);
|
||||
printk("\n");
|
||||
}
|
||||
if (copy_to_user(bulk.data, tbuf, len2)) {
|
||||
kfree(tbuf);
|
||||
return -EFAULT;
|
||||
|
@ -690,6 +740,14 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
|
|||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
snoop(&dev->dev, "bulk write: len=0x%02x timeout=%04d\n",
|
||||
bulk.len, bulk.timeout);
|
||||
if (usbfs_snoop) {
|
||||
dev_info(&dev->dev, "bulk write: data: ");
|
||||
for (j = 0; j < len1; ++j)
|
||||
printk("%02x ", (unsigned char)(tbuf)[j]);
|
||||
printk("\n");
|
||||
}
|
||||
usb_unlock_device(dev);
|
||||
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
|
||||
usb_lock_device(dev);
|
||||
|
@ -835,7 +893,6 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg)
|
|||
return status;
|
||||
}
|
||||
|
||||
|
||||
static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
|
||||
void __user *arg)
|
||||
|
@ -896,6 +953,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
kfree(dr);
|
||||
return -EFAULT;
|
||||
}
|
||||
snoop(&ps->dev->dev, "control urb\n");
|
||||
break;
|
||||
|
||||
case USBDEVFS_URB_TYPE_BULK:
|
||||
|
@ -910,6 +968,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
return -EINVAL;
|
||||
if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
|
||||
return -EFAULT;
|
||||
snoop(&ps->dev->dev, "bulk urb\n");
|
||||
break;
|
||||
|
||||
case USBDEVFS_URB_TYPE_ISO:
|
||||
|
@ -939,6 +998,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
return -EINVAL;
|
||||
}
|
||||
uurb->buffer_length = totlen;
|
||||
snoop(&ps->dev->dev, "iso urb\n");
|
||||
break;
|
||||
|
||||
case USBDEVFS_URB_TYPE_INTERRUPT:
|
||||
|
@ -954,6 +1014,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
return -EINVAL;
|
||||
if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
|
||||
return -EFAULT;
|
||||
snoop(&ps->dev->dev, "interrupt urb\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1003,6 +1064,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
snoop(&as->urb->dev->dev, "submit urb\n");
|
||||
snoop_urb(as->urb, as->userurb);
|
||||
async_newpending(as);
|
||||
if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) {
|
||||
dev_printk(KERN_DEBUG, &ps->dev->dev, "usbfs: usb_submit_urb returned %d\n", ret);
|
||||
|
@ -1238,23 +1301,20 @@ static int proc_releaseinterface(struct dev_state *ps, void __user *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int proc_ioctl (struct dev_state *ps, void __user *arg)
|
||||
static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
|
||||
{
|
||||
struct usbdevfs_ioctl ctrl;
|
||||
int size;
|
||||
void *buf = NULL;
|
||||
int retval = 0;
|
||||
struct usb_interface *intf = NULL;
|
||||
struct usb_driver *driver = NULL;
|
||||
|
||||
/* get input parameters and alloc buffer */
|
||||
if (copy_from_user(&ctrl, arg, sizeof (ctrl)))
|
||||
return -EFAULT;
|
||||
if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) {
|
||||
/* alloc buffer */
|
||||
if ((size = _IOC_SIZE (ctl->ioctl_code)) > 0) {
|
||||
if ((buf = kmalloc (size, GFP_KERNEL)) == NULL)
|
||||
return -ENOMEM;
|
||||
if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) {
|
||||
if (copy_from_user (buf, ctrl.data, size)) {
|
||||
if ((_IOC_DIR(ctl->ioctl_code) & _IOC_WRITE)) {
|
||||
if (copy_from_user (buf, ctl->data, size)) {
|
||||
kfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
@ -1270,9 +1330,9 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
|
|||
|
||||
if (ps->dev->state != USB_STATE_CONFIGURED)
|
||||
retval = -EHOSTUNREACH;
|
||||
else if (!(intf = usb_ifnum_to_if (ps->dev, ctrl.ifno)))
|
||||
else if (!(intf = usb_ifnum_to_if (ps->dev, ctl->ifno)))
|
||||
retval = -EINVAL;
|
||||
else switch (ctrl.ioctl_code) {
|
||||
else switch (ctl->ioctl_code) {
|
||||
|
||||
/* disconnect kernel driver from interface */
|
||||
case USBDEVFS_DISCONNECT:
|
||||
|
@ -1304,7 +1364,7 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
|
|||
if (driver == NULL || driver->ioctl == NULL) {
|
||||
retval = -ENOTTY;
|
||||
} else {
|
||||
retval = driver->ioctl (intf, ctrl.ioctl_code, buf);
|
||||
retval = driver->ioctl (intf, ctl->ioctl_code, buf);
|
||||
if (retval == -ENOIOCTLCMD)
|
||||
retval = -ENOTTY;
|
||||
}
|
||||
|
@ -1313,15 +1373,42 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
|
|||
|
||||
/* cleanup and return */
|
||||
if (retval >= 0
|
||||
&& (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0
|
||||
&& (_IOC_DIR (ctl->ioctl_code) & _IOC_READ) != 0
|
||||
&& size > 0
|
||||
&& copy_to_user (ctrl.data, buf, size) != 0)
|
||||
&& copy_to_user (ctl->data, buf, size) != 0)
|
||||
retval = -EFAULT;
|
||||
|
||||
kfree(buf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int proc_ioctl_default(struct dev_state *ps, void __user *arg)
|
||||
{
|
||||
struct usbdevfs_ioctl ctrl;
|
||||
|
||||
if (copy_from_user(&ctrl, arg, sizeof (ctrl)))
|
||||
return -EFAULT;
|
||||
return proc_ioctl(ps, &ctrl);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static int proc_ioctl_compat(struct dev_state *ps, void __user *arg)
|
||||
{
|
||||
struct usbdevfs_ioctl32 __user *uioc;
|
||||
struct usbdevfs_ioctl ctrl;
|
||||
u32 udata;
|
||||
|
||||
uioc = compat_ptr(arg);
|
||||
if (get_user(ctrl.ifno, &uioc->ifno) ||
|
||||
get_user(ctrl.ioctl_code, &uioc->ioctl_code) ||
|
||||
__get_user(udata, &uioc->data))
|
||||
return -EFAULT;
|
||||
ctrl.data = compat_ptr(udata);
|
||||
|
||||
return proc_ioctl(ps, &ctrl);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NOTE: All requests here that have interface numbers as parameters
|
||||
* are assuming that somehow the configuration has been prevented from
|
||||
|
@ -1422,6 +1509,10 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
|
|||
ret = proc_reapurbnonblock_compat(ps, p);
|
||||
break;
|
||||
|
||||
case USBDEVFS_IOCTL32:
|
||||
snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__);
|
||||
ret = proc_ioctl_compat(ps, p);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case USBDEVFS_DISCARDURB:
|
||||
|
@ -1456,7 +1547,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
|
|||
|
||||
case USBDEVFS_IOCTL:
|
||||
snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__);
|
||||
ret = proc_ioctl(ps, p);
|
||||
ret = proc_ioctl_default(ps, p);
|
||||
break;
|
||||
}
|
||||
usb_unlock_device(dev);
|
||||
|
@ -1488,24 +1579,7 @@ struct file_operations usbfs_device_file_operations = {
|
|||
.release = usbdev_release,
|
||||
};
|
||||
|
||||
struct usb_device *usbdev_lookup_minor(int minor)
|
||||
{
|
||||
struct class_device *class_dev;
|
||||
struct usb_device *dev = NULL;
|
||||
|
||||
down(&usb_device_class->sem);
|
||||
list_for_each_entry(class_dev, &usb_device_class->children, node) {
|
||||
if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
|
||||
dev = class_dev->class_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
up(&usb_device_class->sem);
|
||||
|
||||
return dev;
|
||||
};
|
||||
|
||||
void usbdev_add(struct usb_device *dev)
|
||||
static void usbdev_add(struct usb_device *dev)
|
||||
{
|
||||
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
|
||||
|
||||
|
@ -1516,11 +1590,29 @@ void usbdev_add(struct usb_device *dev)
|
|||
dev->class_dev->class_data = dev;
|
||||
}
|
||||
|
||||
void usbdev_remove(struct usb_device *dev)
|
||||
static void usbdev_remove(struct usb_device *dev)
|
||||
{
|
||||
class_device_unregister(dev->class_dev);
|
||||
}
|
||||
|
||||
static int usbdev_notify(struct notifier_block *self, unsigned long action,
|
||||
void *dev)
|
||||
{
|
||||
switch (action) {
|
||||
case USB_DEVICE_ADD:
|
||||
usbdev_add(dev);
|
||||
break;
|
||||
case USB_DEVICE_REMOVE:
|
||||
usbdev_remove(dev);
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block usbdev_nb = {
|
||||
.notifier_call = usbdev_notify,
|
||||
};
|
||||
|
||||
static struct cdev usb_device_cdev = {
|
||||
.kobj = {.name = "usb_device", },
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -1540,24 +1632,32 @@ int __init usbdev_init(void)
|
|||
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
if (retval) {
|
||||
err("unable to get usb_device major %d", USB_DEVICE_MAJOR);
|
||||
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
goto out;
|
||||
goto error_cdev;
|
||||
}
|
||||
usb_device_class = class_create(THIS_MODULE, "usb_device");
|
||||
if (IS_ERR(usb_device_class)) {
|
||||
err("unable to register usb_device class");
|
||||
retval = PTR_ERR(usb_device_class);
|
||||
usb_device_class = NULL;
|
||||
cdev_del(&usb_device_cdev);
|
||||
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
goto error_class;
|
||||
}
|
||||
|
||||
usb_register_notify(&usbdev_nb);
|
||||
|
||||
out:
|
||||
return retval;
|
||||
|
||||
error_class:
|
||||
usb_device_class = NULL;
|
||||
cdev_del(&usb_device_cdev);
|
||||
|
||||
error_cdev:
|
||||
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
goto out;
|
||||
}
|
||||
|
||||
void usbdev_cleanup(void)
|
||||
{
|
||||
usb_unregister_notify(&usbdev_nb);
|
||||
class_destroy(usb_device_class);
|
||||
cdev_del(&usb_device_cdev);
|
||||
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/devfs_fs_kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
|
@ -88,8 +87,6 @@ int usb_major_init(void)
|
|||
goto out;
|
||||
}
|
||||
|
||||
devfs_mk_dir("usb");
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
@ -97,7 +94,6 @@ out:
|
|||
void usb_major_cleanup(void)
|
||||
{
|
||||
class_destroy(usb_class);
|
||||
devfs_remove("usb");
|
||||
unregister_chrdev(USB_MAJOR, "usb");
|
||||
}
|
||||
|
||||
|
@ -112,8 +108,7 @@ void usb_major_cleanup(void)
|
|||
* 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.
|
||||
* This function also 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.
|
||||
|
@ -162,11 +157,8 @@ int usb_register_dev(struct usb_interface *intf,
|
|||
|
||||
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 */
|
||||
snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
|
||||
temp = strrchr(name, '/');
|
||||
if (temp && (temp[1] != 0x00))
|
||||
++temp;
|
||||
|
@ -179,7 +171,6 @@ int usb_register_dev(struct usb_interface *intf,
|
|||
spin_lock (&minor_lock);
|
||||
usb_minors[intf->minor] = NULL;
|
||||
spin_unlock (&minor_lock);
|
||||
devfs_remove (name);
|
||||
retval = PTR_ERR(intf->class_dev);
|
||||
}
|
||||
exit:
|
||||
|
@ -197,9 +188,8 @@ EXPORT_SYMBOL(usb_register_dev);
|
|||
* 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 function also 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,
|
||||
|
@ -222,7 +212,6 @@ void usb_deregister_dev(struct usb_interface *intf,
|
|||
spin_unlock (&minor_lock);
|
||||
|
||||
snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
|
||||
devfs_remove (name);
|
||||
class_device_destroy(usb_class, MKDEV(USB_MAJOR, intf->minor));
|
||||
intf->class_dev = NULL;
|
||||
intf->minor = -1;
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
|
||||
|
@ -197,6 +199,26 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
|
|||
|
||||
hcd = pci_get_drvdata(dev);
|
||||
|
||||
/* Root hub suspend should have stopped all downstream traffic,
|
||||
* and all bus master traffic. And done so for both the interface
|
||||
* and the stub usb_device (which we check here). But maybe it
|
||||
* didn't; writing sysfs power/state files ignores such rules...
|
||||
*
|
||||
* We must ignore the FREEZE vs SUSPEND distinction here, because
|
||||
* otherwise the swsusp will save (and restore) garbage state.
|
||||
*/
|
||||
if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON)
|
||||
return -EBUSY;
|
||||
|
||||
if (hcd->driver->suspend) {
|
||||
retval = hcd->driver->suspend(hcd, message);
|
||||
if (retval) {
|
||||
dev_dbg (&dev->dev, "PCI pre-suspend fail, %d\n",
|
||||
retval);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME until the generic PM interfaces change a lot more, this
|
||||
* can't use PCI D1 and D2 states. For example, the confusion
|
||||
* between messages and states will need to vanish, and messages
|
||||
|
@ -215,31 +237,13 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
|
|||
*/
|
||||
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
||||
|
||||
switch (hcd->state) {
|
||||
|
||||
/* entry if root hub wasn't yet suspended ... from sysfs,
|
||||
* without autosuspend, or if USB_SUSPEND isn't configured.
|
||||
/* Downstream ports from this root hub should already be quiesced, so
|
||||
* there will be no DMA activity. Now we can shut down the upstream
|
||||
* link (except maybe for PME# resume signaling) and enter some PCI
|
||||
* low power state, if the hardware allows.
|
||||
*/
|
||||
case HC_STATE_RUNNING:
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
retval = hcd->driver->suspend (hcd, message);
|
||||
if (retval) {
|
||||
dev_dbg (hcd->self.controller,
|
||||
"suspend fail, retval %d\n",
|
||||
retval);
|
||||
break;
|
||||
}
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
/* FALLTHROUGH */
|
||||
if (hcd->state == HC_STATE_SUSPENDED) {
|
||||
|
||||
/* 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:
|
||||
/* no DMA or IRQs except when HC is active */
|
||||
if (dev->current_state == PCI_D0) {
|
||||
pci_save_state (dev);
|
||||
|
@ -248,7 +252,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
|
|||
|
||||
if (!has_pci_pm) {
|
||||
dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n");
|
||||
break;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* NOTE: dev->current_state becomes nonzero only here, and
|
||||
|
@ -259,28 +263,29 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
|
|||
retval = pci_set_power_state (dev, PCI_D3hot);
|
||||
if (retval == 0) {
|
||||
dev_dbg (hcd->self.controller, "--> PCI D3\n");
|
||||
retval = pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup);
|
||||
if (retval)
|
||||
break;
|
||||
retval = pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup);
|
||||
} else if (retval < 0) {
|
||||
|
||||
/* Ignore these return values. We rely on pci code to
|
||||
* reject requests the hardware can't implement, rather
|
||||
* than coding the same thing.
|
||||
*/
|
||||
(void) pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup);
|
||||
(void) pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup);
|
||||
} else {
|
||||
dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n",
|
||||
retval);
|
||||
(void) usb_hcd_pci_resume (dev);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
} else {
|
||||
dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
|
||||
hcd->state);
|
||||
WARN_ON(1);
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* update power_state **ONLY** to make sysfs happier */
|
||||
done:
|
||||
if (retval == 0)
|
||||
dev->dev.power.power_state = message;
|
||||
dev->dev.power.power_state = PMSG_SUSPEND;
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL (usb_hcd_pci_suspend);
|
||||
|
@ -336,20 +341,9 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
|
|||
dev->current_state);
|
||||
}
|
||||
#endif
|
||||
retval = pci_enable_wake (dev, dev->current_state, 0);
|
||||
if (retval) {
|
||||
dev_err(hcd->self.controller,
|
||||
"can't enable_wake to %d, %d!\n",
|
||||
dev->current_state, retval);
|
||||
return retval;
|
||||
}
|
||||
retval = pci_enable_wake (dev, PCI_D3cold, 0);
|
||||
if (retval) {
|
||||
dev_err(hcd->self.controller,
|
||||
"can't enable_wake to %d, %d!\n",
|
||||
PCI_D3cold, retval);
|
||||
return retval;
|
||||
}
|
||||
/* yes, ignore these results too... */
|
||||
(void) pci_enable_wake (dev, dev->current_state, 0);
|
||||
(void) pci_enable_wake (dev, PCI_D3cold, 0);
|
||||
} else {
|
||||
/* Same basic cases: clean (powered/not), dirty */
|
||||
dev_dbg(hcd->self.controller, "PCI legacy resume\n");
|
||||
|
@ -371,17 +365,17 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
|
|||
|
||||
dev->dev.power.power_state = PMSG_ON;
|
||||
|
||||
hcd->state = HC_STATE_RESUMING;
|
||||
hcd->saw_irq = 0;
|
||||
|
||||
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);
|
||||
if (hcd->driver->resume) {
|
||||
retval = hcd->driver->resume(hcd);
|
||||
if (retval) {
|
||||
dev_err (hcd->self.controller,
|
||||
"PCI post-resume error %d!\n", retval);
|
||||
usb_hc_died (hcd);
|
||||
}
|
||||
}
|
||||
|
||||
retval = pci_enable_device(dev);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL (usb_hcd_pci_resume);
|
||||
|
|
|
@ -130,7 +130,7 @@ static const u8 usb2_rh_dev_descriptor [18] = {
|
|||
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
||||
0x00, /* __u8 bDeviceSubClass; */
|
||||
0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/
|
||||
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
|
||||
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
|
||||
|
||||
0x00, 0x00, /* __le16 idVendor; */
|
||||
0x00, 0x00, /* __le16 idProduct; */
|
||||
|
@ -153,7 +153,7 @@ static const u8 usb11_rh_dev_descriptor [18] = {
|
|||
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
||||
0x00, /* __u8 bDeviceSubClass; */
|
||||
0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */
|
||||
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
|
||||
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
|
||||
|
||||
0x00, 0x00, /* __le16 idVendor; */
|
||||
0x00, 0x00, /* __le16 idProduct; */
|
||||
|
@ -458,22 +458,18 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
|
|||
|
||||
default:
|
||||
/* non-generic request */
|
||||
if (HC_IS_SUSPENDED (hcd->state))
|
||||
status = -EAGAIN;
|
||||
else {
|
||||
switch (typeReq) {
|
||||
case GetHubStatus:
|
||||
case GetPortStatus:
|
||||
len = 4;
|
||||
break;
|
||||
case GetHubDescriptor:
|
||||
len = sizeof (struct usb_hub_descriptor);
|
||||
break;
|
||||
}
|
||||
status = hcd->driver->hub_control (hcd,
|
||||
typeReq, wValue, wIndex,
|
||||
tbuf, wLength);
|
||||
switch (typeReq) {
|
||||
case GetHubStatus:
|
||||
case GetPortStatus:
|
||||
len = 4;
|
||||
break;
|
||||
case GetHubDescriptor:
|
||||
len = sizeof (struct usb_hub_descriptor);
|
||||
break;
|
||||
}
|
||||
status = hcd->driver->hub_control (hcd,
|
||||
typeReq, wValue, wIndex,
|
||||
tbuf, wLength);
|
||||
break;
|
||||
error:
|
||||
/* "protocol stall" on error */
|
||||
|
@ -487,7 +483,7 @@ error:
|
|||
"CTRL: TypeReq=0x%x val=0x%x "
|
||||
"idx=0x%x len=%d ==> %d\n",
|
||||
typeReq, wValue, wIndex,
|
||||
wLength, urb->status);
|
||||
wLength, status);
|
||||
}
|
||||
}
|
||||
if (len) {
|
||||
|
@ -748,10 +744,9 @@ struct usb_bus *usb_alloc_bus (struct usb_operations *op)
|
|||
{
|
||||
struct usb_bus *bus;
|
||||
|
||||
bus = kmalloc (sizeof *bus, GFP_KERNEL);
|
||||
bus = kzalloc (sizeof *bus, GFP_KERNEL);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
memset(bus, 0, sizeof(struct usb_bus));
|
||||
usb_bus_init (bus);
|
||||
bus->op = op;
|
||||
return bus;
|
||||
|
@ -796,8 +791,7 @@ static int usb_register_bus(struct usb_bus *bus)
|
|||
list_add (&bus->bus_list, &usb_bus_list);
|
||||
up (&usb_bus_list_lock);
|
||||
|
||||
usbfs_add_bus (bus);
|
||||
usbmon_notify_bus_add (bus);
|
||||
usb_notify_add_bus(bus);
|
||||
|
||||
dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);
|
||||
return 0;
|
||||
|
@ -824,8 +818,7 @@ static void usb_deregister_bus (struct usb_bus *bus)
|
|||
list_del (&bus->bus_list);
|
||||
up (&usb_bus_list_lock);
|
||||
|
||||
usbmon_notify_bus_remove (bus);
|
||||
usbfs_remove_bus (bus);
|
||||
usb_notify_remove_bus(bus);
|
||||
|
||||
clear_bit (bus->busnum, busmap.busmap);
|
||||
|
||||
|
@ -1143,10 +1136,20 @@ static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
|
|||
else switch (hcd->state) {
|
||||
case HC_STATE_RUNNING:
|
||||
case HC_STATE_RESUMING:
|
||||
doit:
|
||||
usb_get_dev (urb->dev);
|
||||
list_add_tail (&urb->urb_list, &ep->urb_list);
|
||||
status = 0;
|
||||
break;
|
||||
case HC_STATE_SUSPENDED:
|
||||
/* HC upstream links (register access, wakeup signaling) can work
|
||||
* even when the downstream links (and DMA etc) are quiesced; let
|
||||
* usbcore talk to the root hub.
|
||||
*/
|
||||
if (hcd->self.controller->power.power_state.event == PM_EVENT_ON
|
||||
&& urb->dev->parent == NULL)
|
||||
goto doit;
|
||||
/* FALL THROUGH */
|
||||
default:
|
||||
status = -ESHUTDOWN;
|
||||
break;
|
||||
|
@ -1294,12 +1297,6 @@ static int hcd_unlink_urb (struct urb *urb, int status)
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* running ~= hc unlink handshake works (irq, timer, etc)
|
||||
* halted ~= no unlink handshake is needed
|
||||
* suspended, resuming == should never happen
|
||||
*/
|
||||
WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT);
|
||||
|
||||
/* insist the urb is still queued */
|
||||
list_for_each(tmp, &ep->urb_list) {
|
||||
if (tmp == &urb->urb_list)
|
||||
|
@ -1431,28 +1428,92 @@ rescan:
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int hcd_hub_suspend (struct usb_bus *bus)
|
||||
int hcd_bus_suspend (struct usb_bus *bus)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int status;
|
||||
|
||||
hcd = container_of (bus, struct usb_hcd, self);
|
||||
if (hcd->driver->hub_suspend)
|
||||
return hcd->driver->hub_suspend (hcd);
|
||||
return 0;
|
||||
if (!hcd->driver->bus_suspend)
|
||||
return -ENOENT;
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
status = hcd->driver->bus_suspend (hcd);
|
||||
if (status == 0)
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
else
|
||||
dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
|
||||
"suspend", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hcd_hub_resume (struct usb_bus *bus)
|
||||
int hcd_bus_resume (struct usb_bus *bus)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int status;
|
||||
|
||||
hcd = container_of (bus, struct usb_hcd, self);
|
||||
if (hcd->driver->hub_resume)
|
||||
return hcd->driver->hub_resume (hcd);
|
||||
return 0;
|
||||
if (!hcd->driver->bus_resume)
|
||||
return -ENOENT;
|
||||
if (hcd->state == HC_STATE_RUNNING)
|
||||
return 0;
|
||||
hcd->state = HC_STATE_RESUMING;
|
||||
status = hcd->driver->bus_resume (hcd);
|
||||
if (status == 0)
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
else {
|
||||
dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
|
||||
"resume", status);
|
||||
usb_hc_died(hcd);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* usb_hcd_suspend_root_hub - HCD autosuspends downstream ports
|
||||
* @hcd: host controller for this root hub
|
||||
*
|
||||
* This call arranges that usb_hcd_resume_root_hub() is safe to call later;
|
||||
* that the HCD's root hub polling is deactivated; and that the root's hub
|
||||
* driver is suspended. HCDs may call this to autosuspend when their root
|
||||
* hub's downstream ports are all inactive: unpowered, disconnected,
|
||||
* disabled, or suspended.
|
||||
*
|
||||
* The HCD will autoresume on device connect change detection (using SRP
|
||||
* or a D+/D- pullup). The HCD also autoresumes on remote wakeup signaling
|
||||
* from any ports that are suspended (if that is enabled). In most cases,
|
||||
* overcurrent signaling (on powered ports) will also start autoresume.
|
||||
*
|
||||
* Always called with IRQs blocked.
|
||||
*/
|
||||
void usb_hcd_suspend_root_hub (struct usb_hcd *hcd)
|
||||
{
|
||||
struct urb *urb;
|
||||
|
||||
spin_lock (&hcd_root_hub_lock);
|
||||
usb_suspend_root_hub (hcd->self.root_hub);
|
||||
|
||||
/* force status urb to complete/unlink while suspended */
|
||||
if (hcd->status_urb) {
|
||||
urb = hcd->status_urb;
|
||||
urb->status = -ECONNRESET;
|
||||
urb->hcpriv = NULL;
|
||||
urb->actual_length = 0;
|
||||
|
||||
del_timer (&hcd->rh_timer);
|
||||
hcd->poll_pending = 0;
|
||||
hcd->status_urb = NULL;
|
||||
} else
|
||||
urb = NULL;
|
||||
spin_unlock (&hcd_root_hub_lock);
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
|
||||
if (urb)
|
||||
usb_hcd_giveback_urb (hcd, urb, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub);
|
||||
|
||||
/**
|
||||
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
|
||||
* @hcd: host controller for this root hub
|
||||
|
@ -1460,7 +1521,7 @@ static int hcd_hub_resume (struct usb_bus *bus)
|
|||
* The USB host controller calls this function when its root hub is
|
||||
* suspended (with the remote wakeup feature enabled) and a remote
|
||||
* wakeup request is received. It queues a request for khubd to
|
||||
* resume the root hub.
|
||||
* resume the root hub (that is, manage its downstream ports again).
|
||||
*/
|
||||
void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
|
||||
{
|
||||
|
@ -1471,14 +1532,10 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
|
|||
usb_resume_root_hub (hcd->self.root_hub);
|
||||
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
|
||||
}
|
||||
|
||||
#else
|
||||
void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
|
||||
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_USB_OTG
|
||||
|
@ -1530,10 +1587,6 @@ static struct usb_operations usb_hcd_operations = {
|
|||
.buffer_alloc = hcd_buffer_alloc,
|
||||
.buffer_free = hcd_buffer_free,
|
||||
.disable = hcd_endpoint_disable,
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
.hub_suspend = hcd_hub_suspend,
|
||||
.hub_resume = hcd_hub_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
|
@ -154,10 +154,6 @@ struct usb_operations {
|
|||
|
||||
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 */
|
||||
|
@ -182,12 +178,12 @@ struct hc_driver {
|
|||
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.
|
||||
* a whole, not just the root hub; they're for PCI bus glue.
|
||||
*/
|
||||
/* called after all devices were suspended */
|
||||
/* called after suspending the hub, before entering D3 etc */
|
||||
int (*suspend) (struct usb_hcd *hcd, pm_message_t message);
|
||||
|
||||
/* called before any devices get resumed */
|
||||
/* called after entering D0 (etc), before resuming the hub */
|
||||
int (*resume) (struct usb_hcd *hcd);
|
||||
|
||||
/* cleanly make HCD stop writing memory and doing I/O */
|
||||
|
@ -212,8 +208,8 @@ struct hc_driver {
|
|||
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 (*bus_suspend)(struct usb_hcd *);
|
||||
int (*bus_resume)(struct usb_hcd *);
|
||||
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
|
||||
void (*hub_irq_enable)(struct usb_hcd *);
|
||||
/* Needed only if port-change IRQs are level-triggered */
|
||||
|
@ -355,8 +351,6 @@ extern long usb_calc_bus_time (int speed, int is_input,
|
|||
|
||||
extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -378,6 +372,33 @@ extern int usb_find_interface_driver (struct usb_device *dev,
|
|||
|
||||
#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd);
|
||||
extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
|
||||
extern int hcd_bus_suspend (struct usb_bus *bus);
|
||||
extern int hcd_bus_resume (struct usb_bus *bus);
|
||||
#else
|
||||
static inline void usb_hcd_suspend_root_hub(struct usb_hcd *hcd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int hcd_bus_suspend(struct usb_bus *bus)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hcd_bus_resume (struct usb_bus *bus)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*
|
||||
* USB device fs stuff
|
||||
*/
|
||||
|
@ -388,23 +409,13 @@ extern int usb_find_interface_driver (struct usb_device *dev,
|
|||
* 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) { }
|
||||
|
||||
|
@ -419,8 +430,6 @@ struct usb_mon_operations {
|
|||
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;
|
||||
|
@ -443,18 +452,6 @@ 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);
|
||||
|
@ -465,8 +462,6 @@ 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 */
|
||||
|
||||
|
|
|
@ -436,9 +436,10 @@ static void hub_power_on(struct usb_hub *hub)
|
|||
{
|
||||
int port1;
|
||||
unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
|
||||
u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
|
||||
|
||||
/* if hub supports power switching, enable power on each port */
|
||||
if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
|
||||
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
|
||||
dev_dbg(hub->intfdev, "enabling power on all ports\n");
|
||||
for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
|
||||
set_port_feature(hub->hdev, port1,
|
||||
|
@ -449,10 +450,18 @@ static void hub_power_on(struct usb_hub *hub)
|
|||
msleep(max(pgood_delay, (unsigned) 100));
|
||||
}
|
||||
|
||||
static inline void __hub_quiesce(struct usb_hub *hub)
|
||||
{
|
||||
/* (nonblocking) khubd and related activity won't re-trigger */
|
||||
hub->quiescing = 1;
|
||||
hub->activating = 0;
|
||||
hub->resume_root_hub = 0;
|
||||
}
|
||||
|
||||
static void hub_quiesce(struct usb_hub *hub)
|
||||
{
|
||||
/* stop khubd and related activity */
|
||||
hub->quiescing = 1;
|
||||
/* (blocking) stop khubd and related activity */
|
||||
__hub_quiesce(hub);
|
||||
usb_kill_urb(hub->urb);
|
||||
if (hub->has_indicators)
|
||||
cancel_delayed_work(&hub->leds);
|
||||
|
@ -466,6 +475,7 @@ static void hub_activate(struct usb_hub *hub)
|
|||
|
||||
hub->quiescing = 0;
|
||||
hub->activating = 1;
|
||||
hub->resume_root_hub = 0;
|
||||
status = usb_submit_urb(hub->urb, GFP_NOIO);
|
||||
if (status < 0)
|
||||
dev_err(hub->intfdev, "activate --> %d\n", status);
|
||||
|
@ -516,6 +526,7 @@ static int hub_configure(struct usb_hub *hub,
|
|||
struct usb_device *hdev = hub->hdev;
|
||||
struct device *hub_dev = hub->intfdev;
|
||||
u16 hubstatus, hubchange;
|
||||
u16 wHubCharacteristics;
|
||||
unsigned int pipe;
|
||||
int maxp, ret;
|
||||
char *message;
|
||||
|
@ -561,9 +572,9 @@ static int hub_configure(struct usb_hub *hub,
|
|||
dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
|
||||
(hdev->maxchild == 1) ? "" : "s");
|
||||
|
||||
le16_to_cpus(&hub->descriptor->wHubCharacteristics);
|
||||
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
|
||||
|
||||
if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) {
|
||||
if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
|
||||
int i;
|
||||
char portstr [USB_MAXCHILDREN + 1];
|
||||
|
||||
|
@ -576,7 +587,7 @@ static int hub_configure(struct usb_hub *hub,
|
|||
} else
|
||||
dev_dbg(hub_dev, "standalone hub\n");
|
||||
|
||||
switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) {
|
||||
switch (wHubCharacteristics & HUB_CHAR_LPSM) {
|
||||
case 0x00:
|
||||
dev_dbg(hub_dev, "ganged power switching\n");
|
||||
break;
|
||||
|
@ -589,7 +600,7 @@ static int hub_configure(struct usb_hub *hub,
|
|||
break;
|
||||
}
|
||||
|
||||
switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) {
|
||||
switch (wHubCharacteristics & HUB_CHAR_OCPM) {
|
||||
case 0x00:
|
||||
dev_dbg(hub_dev, "global over-current protection\n");
|
||||
break;
|
||||
|
@ -629,7 +640,7 @@ static int hub_configure(struct usb_hub *hub,
|
|||
}
|
||||
|
||||
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
|
||||
switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) {
|
||||
switch (wHubCharacteristics & HUB_CHAR_TTTT) {
|
||||
case HUB_TTTT_8_BITS:
|
||||
if (hdev->descriptor.bDeviceProtocol != 0) {
|
||||
hub->tt.think_time = 666;
|
||||
|
@ -659,7 +670,7 @@ static int hub_configure(struct usb_hub *hub,
|
|||
}
|
||||
|
||||
/* probe() zeroes hub->indicator[] */
|
||||
if (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND) {
|
||||
if (wHubCharacteristics & HUB_CHAR_PORTIND) {
|
||||
hub->has_indicators = 1;
|
||||
dev_dbg(hub_dev, "Port indicators are supported\n");
|
||||
}
|
||||
|
@ -704,7 +715,7 @@ static int hub_configure(struct usb_hub *hub,
|
|||
(hubstatus & HUB_STATUS_LOCAL_POWER)
|
||||
? "lost (inactive)" : "good");
|
||||
|
||||
if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) == 0)
|
||||
if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0)
|
||||
dev_dbg(hub_dev, "%sover-current condition exists\n",
|
||||
(hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
|
||||
|
||||
|
@ -854,14 +865,12 @@ descriptor_error:
|
|||
/* We found a hub */
|
||||
dev_info (&intf->dev, "USB hub found\n");
|
||||
|
||||
hub = kmalloc(sizeof(*hub), GFP_KERNEL);
|
||||
hub = kzalloc(sizeof(*hub), GFP_KERNEL);
|
||||
if (!hub) {
|
||||
dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(hub, 0, sizeof(*hub));
|
||||
|
||||
INIT_LIST_HEAD(&hub->event_list);
|
||||
hub->intfdev = &intf->dev;
|
||||
hub->hdev = hdev;
|
||||
|
@ -1117,14 +1126,14 @@ void usb_disconnect(struct usb_device **pdev)
|
|||
*/
|
||||
usb_disable_device(udev, 0);
|
||||
|
||||
usb_notify_remove_device(udev);
|
||||
|
||||
/* Free the device number, remove the /proc/bus/usb entry and
|
||||
* the sysfs attributes, and delete the parent's children[]
|
||||
* (or root_hub) pointer.
|
||||
*/
|
||||
dev_dbg (&udev->dev, "unregistering device\n");
|
||||
release_address(udev);
|
||||
usbfs_remove_device(udev);
|
||||
usbdev_remove(udev);
|
||||
usb_remove_sysfs_dev_files(udev);
|
||||
|
||||
/* Avoid races with recursively_mark_NOTATTACHED() */
|
||||
|
@ -1195,21 +1204,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
|
|||
{}
|
||||
#endif
|
||||
|
||||
static void get_string(struct usb_device *udev, char **string, int index)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if (!index)
|
||||
return;
|
||||
buf = kmalloc(256, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return;
|
||||
if (usb_string(udev, index, buf, 256) > 0)
|
||||
*string = buf;
|
||||
else
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_OTG
|
||||
#include "otg_whitelist.h"
|
||||
|
@ -1248,9 +1242,10 @@ int usb_new_device(struct usb_device *udev)
|
|||
}
|
||||
|
||||
/* read the standard strings and cache them if present */
|
||||
get_string(udev, &udev->product, udev->descriptor.iProduct);
|
||||
get_string(udev, &udev->manufacturer, udev->descriptor.iManufacturer);
|
||||
get_string(udev, &udev->serial, udev->descriptor.iSerialNumber);
|
||||
udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
|
||||
udev->manufacturer = usb_cache_string(udev,
|
||||
udev->descriptor.iManufacturer);
|
||||
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
|
||||
|
||||
/* Tell the world! */
|
||||
dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "
|
||||
|
@ -1322,11 +1317,9 @@ int usb_new_device(struct usb_device *udev)
|
|||
* (Includes HNP test device.)
|
||||
*/
|
||||
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
|
||||
static int __usb_suspend_device (struct usb_device *,
|
||||
int port1, pm_message_t state);
|
||||
err = __usb_suspend_device(udev,
|
||||
udev->bus->otg_port,
|
||||
PMSG_SUSPEND);
|
||||
static int __usb_suspend_device(struct usb_device *,
|
||||
int port1);
|
||||
err = __usb_suspend_device(udev, udev->bus->otg_port);
|
||||
if (err < 0)
|
||||
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
|
||||
}
|
||||
|
@ -1362,10 +1355,8 @@ int usb_new_device(struct usb_device *udev)
|
|||
}
|
||||
|
||||
/* USB device state == configured ... usable */
|
||||
usb_notify_add_device(udev);
|
||||
|
||||
/* add a /proc/bus/usb entry */
|
||||
usbdev_add(udev);
|
||||
usbfs_add_device(udev);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
@ -1516,7 +1507,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
|
|||
/* FIXME let caller ask to power down the port:
|
||||
* - some devices won't enumerate without a VBUS power cycle
|
||||
* - SRP saves power that way
|
||||
* - usb_suspend_device(dev, PMSG_SUSPEND)
|
||||
* - ... new call, TBD ...
|
||||
* That's easy if this hub can switch power per-port, and
|
||||
* khubd reactivates the port later (timer, SRP, etc).
|
||||
* Powerdown must be optional, because of reset/DFU.
|
||||
|
@ -1598,11 +1589,14 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
|
|||
* Other than re-initializing the hub (plug/unplug, except for root hubs),
|
||||
* Linux (2.6) currently has NO mechanisms to initiate that: no khubd
|
||||
* timer, no SRP, no requests through sysfs.
|
||||
*
|
||||
* If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
|
||||
* the root hub for their bus goes into global suspend ... so we don't
|
||||
* (falsely) update the device power state to say it suspended.
|
||||
*/
|
||||
static int __usb_suspend_device (struct usb_device *udev, int port1,
|
||||
pm_message_t state)
|
||||
static int __usb_suspend_device (struct usb_device *udev, int port1)
|
||||
{
|
||||
int status;
|
||||
int status = 0;
|
||||
|
||||
/* caller owns the udev device lock */
|
||||
if (port1 < 0)
|
||||
|
@ -1613,95 +1607,39 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* suspend interface drivers; if this is a hub, it
|
||||
* suspends the child devices
|
||||
*/
|
||||
/* all interfaces must already be suspended */
|
||||
if (udev->actconfig) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
||||
struct usb_interface *intf;
|
||||
struct usb_driver *driver;
|
||||
|
||||
intf = udev->actconfig->interface[i];
|
||||
if (state.event <= intf->dev.power.power_state.event)
|
||||
continue;
|
||||
if (!intf->dev.driver)
|
||||
continue;
|
||||
driver = to_usb_driver(intf->dev.driver);
|
||||
|
||||
if (driver->suspend) {
|
||||
status = driver->suspend(intf, state);
|
||||
if (intf->dev.power.power_state.event != state.event
|
||||
|| status)
|
||||
dev_err(&intf->dev,
|
||||
"suspend %d fail, code %d\n",
|
||||
state.event, status);
|
||||
}
|
||||
|
||||
/* only drivers with suspend() can ever resume();
|
||||
* and after power loss, even they won't.
|
||||
* bus_rescan_devices() can rebind drivers later.
|
||||
*
|
||||
* FIXME the PM core self-deadlocks when unbinding
|
||||
* drivers during suspend/resume ... everything grabs
|
||||
* dpm_sem (not a spinlock, ugh). we want to unbind,
|
||||
* since we know every driver's probe/disconnect works
|
||||
* even for drivers that can't suspend.
|
||||
*/
|
||||
if (!driver->suspend || state.event > PM_EVENT_FREEZE) {
|
||||
#if 1
|
||||
dev_warn(&intf->dev, "resume is unsafe!\n");
|
||||
#else
|
||||
down_write(&usb_bus_type.rwsem);
|
||||
device_release_driver(&intf->dev);
|
||||
up_write(&usb_bus_type.rwsem);
|
||||
#endif
|
||||
if (is_active(intf)) {
|
||||
dev_dbg(&intf->dev, "nyet suspended\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME this needs port power off call paths too, to help force
|
||||
* USB into the "generic" PM model. At least for devices on
|
||||
* ports that aren't using ganged switching (usually root hubs).
|
||||
*
|
||||
* NOTE: SRP-capable links should adopt more aggressive poweroff
|
||||
* policies (when HNP doesn't apply) once we have mechanisms to
|
||||
* turn power back on! (Likely not before 2.7...)
|
||||
/* we only change a device's upstream USB link.
|
||||
* root hubs have no upstream USB link.
|
||||
*/
|
||||
if (state.event > PM_EVENT_FREEZE) {
|
||||
dev_warn(&udev->dev, "no poweroff yet, suspending instead\n");
|
||||
}
|
||||
|
||||
/* "global suspend" of the HC-to-USB interface (root hub), or
|
||||
* "selective suspend" of just one hub-device link.
|
||||
*/
|
||||
if (!udev->parent) {
|
||||
struct usb_bus *bus = udev->bus;
|
||||
if (bus && bus->op->hub_suspend) {
|
||||
status = bus->op->hub_suspend (bus);
|
||||
if (status == 0) {
|
||||
dev_dbg(&udev->dev, "usb suspend\n");
|
||||
usb_set_device_state(udev,
|
||||
USB_STATE_SUSPENDED);
|
||||
}
|
||||
} else
|
||||
status = -EOPNOTSUPP;
|
||||
} else
|
||||
if (udev->parent)
|
||||
status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
|
||||
udev);
|
||||
|
||||
if (status == 0)
|
||||
udev->dev.power.power_state = state;
|
||||
udev->dev.power.power_state = PMSG_SUSPEND;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
#endif
|
||||
|
||||
/*
|
||||
* usb_suspend_device - suspend a usb device
|
||||
* @udev: device that's no longer in active use
|
||||
* @state: PMSG_SUSPEND to suspend
|
||||
* Context: must be able to sleep; device not locked
|
||||
* Context: must be able to sleep; device not locked; pm locks held
|
||||
*
|
||||
* Suspends a USB device that isn't in active use, conserving power.
|
||||
* Devices may wake out of a suspend, if anything important happens,
|
||||
|
@ -1709,37 +1647,50 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
|
|||
* suspend by the host, using usb_resume_device(). It's also routine
|
||||
* to disconnect devices while they are suspended.
|
||||
*
|
||||
* This only affects the USB hardware for a device; its interfaces
|
||||
* (and, for hubs, child devices) must already have been suspended.
|
||||
*
|
||||
* Suspending OTG devices may trigger HNP, if that's been enabled
|
||||
* between a pair of dual-role devices. That will change roles, such
|
||||
* as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
|
||||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
int usb_suspend_device(struct usb_device *udev, pm_message_t state)
|
||||
int usb_suspend_device(struct usb_device *udev)
|
||||
{
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
int port1, status;
|
||||
|
||||
port1 = locktree(udev);
|
||||
if (port1 < 0)
|
||||
return port1;
|
||||
|
||||
status = __usb_suspend_device(udev, port1, state);
|
||||
status = __usb_suspend_device(udev, port1);
|
||||
usb_unlock_device(udev);
|
||||
return status;
|
||||
#else
|
||||
/* NOTE: udev->state unchanged, it's not lying ... */
|
||||
udev->dev.power.power_state = PMSG_SUSPEND;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_suspend_device);
|
||||
|
||||
/*
|
||||
* If the USB "suspend" state is in use (rather than "global suspend"),
|
||||
* many devices will be individually taken out of suspend state using
|
||||
* special" resume" signaling. These routines kick in shortly after
|
||||
* hardware resume signaling is finished, either because of selective
|
||||
* resume (by host) or remote wakeup (by device) ... now see what changed
|
||||
* in the tree that's rooted at this device.
|
||||
*/
|
||||
static int finish_port_resume(struct usb_device *udev)
|
||||
static int finish_device_resume(struct usb_device *udev)
|
||||
{
|
||||
int status;
|
||||
u16 devstatus;
|
||||
|
||||
/* caller owns the udev device lock */
|
||||
dev_dbg(&udev->dev, "usb resume\n");
|
||||
dev_dbg(&udev->dev, "finish resume\n");
|
||||
|
||||
/* usb ch9 identifies four variants of SUSPENDED, based on what
|
||||
* state the device resumes to. Linux currently won't see the
|
||||
|
@ -1749,7 +1700,6 @@ static int finish_port_resume(struct usb_device *udev)
|
|||
usb_set_device_state(udev, udev->actconfig
|
||||
? USB_STATE_CONFIGURED
|
||||
: USB_STATE_ADDRESS);
|
||||
udev->dev.power.power_state = PMSG_ON;
|
||||
|
||||
/* 10.5.4.5 says be sure devices in the tree are still there.
|
||||
* For now let's assume the device didn't go crazy on resume,
|
||||
|
@ -1762,9 +1712,11 @@ static int finish_port_resume(struct usb_device *udev)
|
|||
status);
|
||||
else if (udev->actconfig) {
|
||||
unsigned i;
|
||||
int (*resume)(struct device *);
|
||||
|
||||
le16_to_cpus(&devstatus);
|
||||
if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
|
||||
if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)
|
||||
&& udev->parent) {
|
||||
status = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
|
@ -1780,33 +1732,11 @@ static int finish_port_resume(struct usb_device *udev)
|
|||
}
|
||||
|
||||
/* resume interface drivers; if this is a hub, it
|
||||
* resumes the child devices
|
||||
* may have a child resume event to deal with soon
|
||||
*/
|
||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
||||
struct usb_interface *intf;
|
||||
struct usb_driver *driver;
|
||||
|
||||
intf = udev->actconfig->interface[i];
|
||||
if (intf->dev.power.power_state.event == PM_EVENT_ON)
|
||||
continue;
|
||||
if (!intf->dev.driver) {
|
||||
/* FIXME maybe force to alt 0 */
|
||||
continue;
|
||||
}
|
||||
driver = to_usb_driver(intf->dev.driver);
|
||||
|
||||
/* bus_rescan_devices() may rebind drivers */
|
||||
if (!driver->resume)
|
||||
continue;
|
||||
|
||||
/* can we do better than just logging errors? */
|
||||
status = driver->resume(intf);
|
||||
if (intf->dev.power.power_state.event != PM_EVENT_ON
|
||||
|| status)
|
||||
dev_dbg(&intf->dev,
|
||||
"resume fail, state %d code %d\n",
|
||||
intf->dev.power.power_state.event, status);
|
||||
}
|
||||
resume = udev->dev.bus->resume;
|
||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++)
|
||||
(void) resume(&udev->actconfig->interface[i]->dev);
|
||||
status = 0;
|
||||
|
||||
} else if (udev->devnum <= 0) {
|
||||
|
@ -1816,6 +1746,8 @@ static int finish_port_resume(struct usb_device *udev)
|
|||
return status;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
static int
|
||||
hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
||||
{
|
||||
|
@ -1861,7 +1793,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
|||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
if (udev)
|
||||
status = finish_port_resume(udev);
|
||||
status = finish_device_resume(udev);
|
||||
}
|
||||
}
|
||||
if (status < 0)
|
||||
|
@ -1870,12 +1802,12 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
|||
return status;
|
||||
}
|
||||
|
||||
static int hub_resume (struct usb_interface *intf);
|
||||
#endif
|
||||
|
||||
/**
|
||||
/*
|
||||
* usb_resume_device - re-activate a suspended usb device
|
||||
* @udev: device to re-activate
|
||||
* Context: must be able to sleep; device not locked
|
||||
* Context: must be able to sleep; device not locked; pm locks held
|
||||
*
|
||||
* This will re-activate the suspended device, increasing power usage
|
||||
* while letting drivers communicate again with its endpoints.
|
||||
|
@ -1893,35 +1825,22 @@ int usb_resume_device(struct usb_device *udev)
|
|||
if (port1 < 0)
|
||||
return port1;
|
||||
|
||||
/* "global resume" of the HC-to-USB interface (root hub), or
|
||||
* selective resume of one hub-to-device port
|
||||
*/
|
||||
if (!udev->parent) {
|
||||
struct usb_bus *bus = udev->bus;
|
||||
if (bus && bus->op->hub_resume) {
|
||||
status = bus->op->hub_resume (bus);
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
/* selective resume of one downstream hub-to-device port */
|
||||
if (udev->parent) {
|
||||
if (udev->state == USB_STATE_SUSPENDED) {
|
||||
// NOTE swsusp may bork us, device state being wrong...
|
||||
// NOTE this fails if parent is also suspended...
|
||||
status = hub_port_resume(hdev_to_hub(udev->parent),
|
||||
port1, udev);
|
||||
} else
|
||||
status = -EOPNOTSUPP;
|
||||
if (status == 0) {
|
||||
dev_dbg(&udev->dev, "usb resume\n");
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
usb_set_device_state (udev, USB_STATE_CONFIGURED);
|
||||
udev->dev.power.power_state = PMSG_ON;
|
||||
status = hub_resume (udev
|
||||
->actconfig->interface[0]);
|
||||
}
|
||||
} else if (udev->state == USB_STATE_SUSPENDED) {
|
||||
// NOTE this fails if parent is also suspended...
|
||||
status = hub_port_resume(hdev_to_hub(udev->parent),
|
||||
port1, udev);
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
if (status < 0) {
|
||||
status = 0;
|
||||
} else
|
||||
#endif
|
||||
status = finish_device_resume(udev);
|
||||
if (status < 0)
|
||||
dev_dbg(&udev->dev, "can't resume, status %d\n",
|
||||
status);
|
||||
}
|
||||
|
||||
usb_unlock_device(udev);
|
||||
|
||||
|
@ -1938,6 +1857,8 @@ static int remote_wakeup(struct usb_device *udev)
|
|||
{
|
||||
int status = 0;
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
/* don't repeat RESUME sequence if this device
|
||||
* was already woken up by some other task
|
||||
*/
|
||||
|
@ -1946,38 +1867,52 @@ static int remote_wakeup(struct usb_device *udev)
|
|||
dev_dbg(&udev->dev, "RESUME (wakeup)\n");
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
status = finish_port_resume(udev);
|
||||
status = finish_device_resume(udev);
|
||||
}
|
||||
up(&udev->serialize);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hub_suspend(struct usb_interface *intf, pm_message_t state)
|
||||
static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||
{
|
||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
unsigned port1;
|
||||
int status;
|
||||
|
||||
/* stop khubd and related activity */
|
||||
hub_quiesce(hub);
|
||||
|
||||
/* then suspend every port */
|
||||
/* fail if children aren't already suspended */
|
||||
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||
struct usb_device *udev;
|
||||
|
||||
udev = hdev->children [port1-1];
|
||||
if (!udev)
|
||||
continue;
|
||||
down(&udev->serialize);
|
||||
status = __usb_suspend_device(udev, port1, state);
|
||||
up(&udev->serialize);
|
||||
if (status < 0)
|
||||
dev_dbg(&intf->dev, "suspend port %d --> %d\n",
|
||||
port1, status);
|
||||
if (udev && (udev->dev.power.power_state.event
|
||||
== PM_EVENT_ON
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|| udev->state != USB_STATE_SUSPENDED
|
||||
#endif
|
||||
)) {
|
||||
dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
intf->dev.power.power_state = state;
|
||||
/* "global suspend" of the downstream HC-to-USB interface */
|
||||
if (!hdev->parent) {
|
||||
struct usb_bus *bus = hdev->bus;
|
||||
if (bus) {
|
||||
int status = hcd_bus_suspend (bus);
|
||||
|
||||
if (status != 0) {
|
||||
dev_dbg(&hdev->dev, "'global' suspend %d\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
} else
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* stop khubd and related activity */
|
||||
hub_quiesce(hub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1985,11 +1920,35 @@ static int hub_resume(struct usb_interface *intf)
|
|||
{
|
||||
struct usb_device *hdev = interface_to_usbdev(intf);
|
||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||
unsigned port1;
|
||||
int status;
|
||||
|
||||
if (intf->dev.power.power_state.event == PM_EVENT_ON)
|
||||
return 0;
|
||||
/* "global resume" of the downstream HC-to-USB interface */
|
||||
if (!hdev->parent) {
|
||||
struct usb_bus *bus = hdev->bus;
|
||||
if (bus) {
|
||||
status = hcd_bus_resume (bus);
|
||||
if (status) {
|
||||
dev_dbg(&intf->dev, "'global' resume %d\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
} else
|
||||
return -EOPNOTSUPP;
|
||||
if (status == 0) {
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
hub_activate(hub);
|
||||
|
||||
/* REVISIT: this recursion probably shouldn't exist. Remove
|
||||
* this code sometime, after retesting with different root and
|
||||
* external hubs.
|
||||
*/
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
{
|
||||
unsigned port1;
|
||||
|
||||
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||
struct usb_device *udev;
|
||||
|
@ -2015,7 +1974,7 @@ static int hub_resume(struct usb_interface *intf)
|
|||
if (portstat & USB_PORT_STAT_SUSPEND)
|
||||
status = hub_port_resume(hub, port1, udev);
|
||||
else {
|
||||
status = finish_port_resume(udev);
|
||||
status = finish_device_resume(udev);
|
||||
if (status < 0) {
|
||||
dev_dbg(&intf->dev, "resume port %d --> %d\n",
|
||||
port1, status);
|
||||
|
@ -2024,13 +1983,23 @@ static int hub_resume(struct usb_interface *intf)
|
|||
}
|
||||
up(&udev->serialize);
|
||||
}
|
||||
intf->dev.power.power_state = PMSG_ON;
|
||||
|
||||
hub->resume_root_hub = 0;
|
||||
hub_activate(hub);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_suspend_root_hub(struct usb_device *hdev)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
|
||||
/* This also makes any led blinker stop retriggering. We're called
|
||||
* from irq, so the blinker might still be scheduled. Caller promises
|
||||
* that the root hub status URB will be canceled.
|
||||
*/
|
||||
__hub_quiesce(hub);
|
||||
mark_quiesced(to_usb_interface(hub->intfdev));
|
||||
}
|
||||
|
||||
void usb_resume_root_hub(struct usb_device *hdev)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
|
@ -2039,28 +2008,6 @@ void usb_resume_root_hub(struct usb_device *hdev)
|
|||
kick_khubd(hub);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_USB_SUSPEND */
|
||||
|
||||
int usb_suspend_device(struct usb_device *udev, pm_message_t state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_resume_device(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define hub_suspend NULL
|
||||
#define hub_resume NULL
|
||||
#define remote_wakeup(x) 0
|
||||
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
|
||||
EXPORT_SYMBOL(usb_suspend_device);
|
||||
EXPORT_SYMBOL(usb_resume_device);
|
||||
|
||||
|
||||
|
||||
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
|
||||
*
|
||||
|
@ -2469,6 +2416,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
|||
{
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
struct device *hub_dev = hub->intfdev;
|
||||
u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
|
||||
int status, i;
|
||||
|
||||
dev_dbg (hub_dev,
|
||||
|
@ -2506,8 +2454,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
|||
if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
|
||||
|
||||
/* maybe switch power back on (e.g. root hub was reset) */
|
||||
if ((hub->descriptor->wHubCharacteristics
|
||||
& HUB_CHAR_LPSM) < 2
|
||||
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
|
||||
&& !(portstatus & (1 << USB_PORT_FEAT_POWER)))
|
||||
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
|
||||
|
||||
|
@ -2686,21 +2633,28 @@ static void hub_events(void)
|
|||
intf = to_usb_interface(hub->intfdev);
|
||||
hub_dev = &intf->dev;
|
||||
|
||||
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
|
||||
i = hub->resume_root_hub;
|
||||
|
||||
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x%s\n",
|
||||
hdev->state, hub->descriptor
|
||||
? hub->descriptor->bNbrPorts
|
||||
: 0,
|
||||
/* NOTE: expects max 15 ports... */
|
||||
(u16) hub->change_bits[0],
|
||||
(u16) hub->event_bits[0]);
|
||||
(u16) hub->event_bits[0],
|
||||
i ? ", resume root" : "");
|
||||
|
||||
usb_get_intf(intf);
|
||||
i = hub->resume_root_hub;
|
||||
spin_unlock_irq(&hub_event_lock);
|
||||
|
||||
/* Is this is a root hub wanting to be resumed? */
|
||||
if (i)
|
||||
usb_resume_device(hdev);
|
||||
/* Is this is a root hub wanting to reactivate the downstream
|
||||
* ports? If so, be sure the interface resumes even if its
|
||||
* stub "device" node was never suspended.
|
||||
*/
|
||||
if (i) {
|
||||
dpm_runtime_resume(&hdev->dev);
|
||||
dpm_runtime_resume(&intf->dev);
|
||||
}
|
||||
|
||||
/* Lock the device, then check to see if we were
|
||||
* disconnected while waiting for the lock to succeed. */
|
||||
|
|
|
@ -131,7 +131,7 @@ struct usb_hub_descriptor {
|
|||
__u8 bDescLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bNbrPorts;
|
||||
__u16 wHubCharacteristics;
|
||||
__le16 wHubCharacteristics;
|
||||
__u8 bPwrOn2PwrGood;
|
||||
__u8 bHubContrCurrent;
|
||||
/* add 1 bit for hub status change; round to bytes */
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
@ -619,7 +620,7 @@ void usbfs_update_special (void)
|
|||
}
|
||||
}
|
||||
|
||||
void usbfs_add_bus(struct usb_bus *bus)
|
||||
static void usbfs_add_bus(struct usb_bus *bus)
|
||||
{
|
||||
struct dentry *parent;
|
||||
char name[8];
|
||||
|
@ -642,12 +643,9 @@ void usbfs_add_bus(struct usb_bus *bus)
|
|||
err ("error creating usbfs bus entry");
|
||||
return;
|
||||
}
|
||||
|
||||
usbfs_update_special();
|
||||
usbfs_conn_disc_event();
|
||||
}
|
||||
|
||||
void usbfs_remove_bus(struct usb_bus *bus)
|
||||
static void usbfs_remove_bus(struct usb_bus *bus)
|
||||
{
|
||||
if (bus->usbfs_dentry) {
|
||||
fs_remove_file (bus->usbfs_dentry);
|
||||
|
@ -659,12 +657,9 @@ void usbfs_remove_bus(struct usb_bus *bus)
|
|||
remove_special_files();
|
||||
num_buses = 0;
|
||||
}
|
||||
|
||||
usbfs_update_special();
|
||||
usbfs_conn_disc_event();
|
||||
}
|
||||
|
||||
void usbfs_add_device(struct usb_device *dev)
|
||||
static void usbfs_add_device(struct usb_device *dev)
|
||||
{
|
||||
char name[8];
|
||||
int i;
|
||||
|
@ -690,12 +685,9 @@ void usbfs_add_device(struct usb_device *dev)
|
|||
}
|
||||
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)
|
||||
static void usbfs_remove_device(struct usb_device *dev)
|
||||
{
|
||||
struct dev_state *ds;
|
||||
struct siginfo sinfo;
|
||||
|
@ -716,10 +708,33 @@ void usbfs_remove_device(struct usb_device *dev)
|
|||
kill_proc_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)
|
||||
{
|
||||
switch (action) {
|
||||
case USB_DEVICE_ADD:
|
||||
usbfs_add_device(dev);
|
||||
break;
|
||||
case USB_DEVICE_REMOVE:
|
||||
usbfs_remove_device(dev);
|
||||
break;
|
||||
case USB_BUS_ADD:
|
||||
usbfs_add_bus(dev);
|
||||
break;
|
||||
case USB_BUS_REMOVE:
|
||||
usbfs_remove_bus(dev);
|
||||
}
|
||||
|
||||
usbfs_update_special();
|
||||
usbfs_conn_disc_event();
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block usbfs_nb = {
|
||||
.notifier_call = usbfs_notify,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct proc_dir_entry *usbdir = NULL;
|
||||
|
@ -732,6 +747,8 @@ int __init usbfs_init(void)
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
usb_register_notify(&usbfs_nb);
|
||||
|
||||
/* create mount point for usbfs */
|
||||
usbdir = proc_mkdir("usb", proc_bus);
|
||||
|
||||
|
@ -740,6 +757,7 @@ int __init usbfs_init(void)
|
|||
|
||||
void usbfs_cleanup(void)
|
||||
{
|
||||
usb_unregister_notify(&usbfs_nb);
|
||||
unregister_filesystem(&usb_fs_type);
|
||||
if (usbdir)
|
||||
remove_proc_entry("usb", proc_bus);
|
||||
|
|
|
@ -187,21 +187,37 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u
|
|||
* If a thread in your driver uses this call, make sure your disconnect()
|
||||
* method can wait for it to complete. Since you don't have a handle on
|
||||
* the URB used, you can't cancel the request.
|
||||
*
|
||||
* Because there is no usb_interrupt_msg() and no USBDEVFS_INTERRUPT
|
||||
* ioctl, users are forced to abuse this routine by using it to submit
|
||||
* URBs for interrupt endpoints. We will take the liberty of creating
|
||||
* an interrupt URB (with the default interval) if the target is an
|
||||
* interrupt endpoint.
|
||||
*/
|
||||
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
|
||||
void *data, int len, int *actual_length, int timeout)
|
||||
{
|
||||
struct urb *urb;
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
if (len < 0)
|
||||
ep = (usb_pipein(pipe) ? usb_dev->ep_in : usb_dev->ep_out)
|
||||
[usb_pipeendpoint(pipe)];
|
||||
if (!ep || len < 0)
|
||||
return -EINVAL;
|
||||
|
||||
urb=usb_alloc_urb(0, GFP_KERNEL);
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
|
||||
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
|
||||
usb_api_blocking_completion, NULL);
|
||||
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
|
||||
USB_ENDPOINT_XFER_INT) {
|
||||
pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
|
||||
usb_fill_int_urb(urb, usb_dev, pipe, data, len,
|
||||
usb_api_blocking_completion, NULL,
|
||||
ep->desc.bInterval);
|
||||
} else
|
||||
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
|
||||
usb_api_blocking_completion, NULL);
|
||||
|
||||
return usb_start_wait_urb(urb, timeout, actual_length);
|
||||
}
|
||||
|
@ -771,6 +787,31 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
|
|||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_cache_string - read a string descriptor and cache it for later use
|
||||
* @udev: the device whose string descriptor is being read
|
||||
* @index: the descriptor index
|
||||
*
|
||||
* Returns a pointer to a kmalloc'ed buffer containing the descriptor string,
|
||||
* or NULL if the index is 0 or the string could not be read.
|
||||
*/
|
||||
char *usb_cache_string(struct usb_device *udev, int index)
|
||||
{
|
||||
char *buf;
|
||||
char *smallbuf = NULL;
|
||||
int len;
|
||||
|
||||
if (index > 0 && (buf = kmalloc(256, GFP_KERNEL)) != NULL) {
|
||||
if ((len = usb_string(udev, index, buf, 256)) > 0) {
|
||||
if ((smallbuf = kmalloc(++len, GFP_KERNEL)) == NULL)
|
||||
return buf;
|
||||
memcpy(smallbuf, buf, len);
|
||||
}
|
||||
kfree(buf);
|
||||
}
|
||||
return smallbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
|
||||
* @dev: the device whose device descriptor is being updated
|
||||
|
@ -992,8 +1033,6 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
|
|||
dev_dbg (&dev->dev, "unregistering interface %s\n",
|
||||
interface->dev.bus_id);
|
||||
usb_remove_sysfs_intf_files(interface);
|
||||
kfree(interface->cur_altsetting->string);
|
||||
interface->cur_altsetting->string = NULL;
|
||||
device_del (&interface->dev);
|
||||
}
|
||||
|
||||
|
@ -1133,6 +1172,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
*/
|
||||
|
||||
/* prevent submissions using previous endpoint settings */
|
||||
if (device_is_registered(&iface->dev))
|
||||
usb_remove_sysfs_intf_files(iface);
|
||||
usb_disable_interface(dev, iface);
|
||||
|
||||
iface->cur_altsetting = alt;
|
||||
|
@ -1168,6 +1209,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
* (Likewise, EP0 never "halts" on well designed devices.)
|
||||
*/
|
||||
usb_enable_interface(dev, iface);
|
||||
if (device_is_registered(&iface->dev))
|
||||
usb_create_sysfs_intf_files(iface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1217,10 +1260,8 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
USB_REQ_SET_CONFIGURATION, 0,
|
||||
config->desc.bConfigurationValue, 0,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (retval < 0) {
|
||||
usb_set_device_state(dev, USB_STATE_ADDRESS);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
|
||||
dev->toggle[0] = dev->toggle[1] = 0;
|
||||
|
||||
|
@ -1229,6 +1270,8 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
struct usb_interface *intf = config->interface[i];
|
||||
struct usb_host_interface *alt;
|
||||
|
||||
if (device_is_registered(&intf->dev))
|
||||
usb_remove_sysfs_intf_files(intf);
|
||||
alt = usb_altnum_to_altsetting(intf, 0);
|
||||
|
||||
/* No altsetting 0? We'll assume the first altsetting.
|
||||
|
@ -1241,6 +1284,8 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
|
||||
intf->cur_altsetting = alt;
|
||||
usb_enable_interface(dev, intf);
|
||||
if (device_is_registered(&intf->dev))
|
||||
usb_create_sysfs_intf_files(intf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1328,7 +1373,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
|
|||
}
|
||||
|
||||
for (; n < nintf; ++n) {
|
||||
new_interfaces[n] = kmalloc(
|
||||
new_interfaces[n] = kzalloc(
|
||||
sizeof(struct usb_interface),
|
||||
GFP_KERNEL);
|
||||
if (!new_interfaces[n]) {
|
||||
|
@ -1369,7 +1414,6 @@ free_interfaces:
|
|||
struct usb_host_interface *alt;
|
||||
|
||||
cp->interface[i] = intf = new_interfaces[i];
|
||||
memset(intf, 0, sizeof(*intf));
|
||||
intfc = cp->intf_cache[i];
|
||||
intf->altsetting = intfc->altsetting;
|
||||
intf->num_altsetting = intfc->num_altsetting;
|
||||
|
@ -1393,6 +1437,7 @@ free_interfaces:
|
|||
intf->dev.dma_mask = dev->dev.dma_mask;
|
||||
intf->dev.release = release_interface;
|
||||
device_initialize (&intf->dev);
|
||||
mark_quiesced(intf);
|
||||
sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
|
||||
dev->bus->busnum, dev->devpath,
|
||||
configuration,
|
||||
|
@ -1400,12 +1445,9 @@ free_interfaces:
|
|||
}
|
||||
kfree(new_interfaces);
|
||||
|
||||
if ((cp->desc.iConfiguration) &&
|
||||
(cp->string == NULL)) {
|
||||
cp->string = kmalloc(256, GFP_KERNEL);
|
||||
if (cp->string)
|
||||
usb_string(dev, cp->desc.iConfiguration, cp->string, 256);
|
||||
}
|
||||
if (cp->string == NULL)
|
||||
cp->string = usb_cache_string(dev,
|
||||
cp->desc.iConfiguration);
|
||||
|
||||
/* Now that all the interfaces are set up, register them
|
||||
* to trigger binding of drivers to interfaces. probe()
|
||||
|
@ -1415,13 +1457,12 @@ free_interfaces:
|
|||
*/
|
||||
for (i = 0; i < nintf; ++i) {
|
||||
struct usb_interface *intf = cp->interface[i];
|
||||
struct usb_interface_descriptor *desc;
|
||||
struct usb_host_interface *alt = intf->cur_altsetting;
|
||||
|
||||
desc = &intf->altsetting [0].desc;
|
||||
dev_dbg (&dev->dev,
|
||||
"adding %s (config #%d, interface %d)\n",
|
||||
intf->dev.bus_id, configuration,
|
||||
desc->bInterfaceNumber);
|
||||
alt->desc.bInterfaceNumber);
|
||||
ret = device_add (&intf->dev);
|
||||
if (ret != 0) {
|
||||
dev_err(&dev->dev,
|
||||
|
@ -1430,13 +1471,6 @@ free_interfaces:
|
|||
ret);
|
||||
continue;
|
||||
}
|
||||
if ((intf->cur_altsetting->desc.iInterface) &&
|
||||
(intf->cur_altsetting->string == NULL)) {
|
||||
intf->cur_altsetting->string = kmalloc(256, GFP_KERNEL);
|
||||
if (intf->cur_altsetting->string)
|
||||
usb_string(dev, intf->cur_altsetting->desc.iInterface,
|
||||
intf->cur_altsetting->string, 256);
|
||||
}
|
||||
usb_create_sysfs_intf_files (intf);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* All the USB notify logic
|
||||
*
|
||||
* (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
|
||||
*
|
||||
* notifier functions originally based on those in kernel/sys.c
|
||||
* but fixed up to not be so broken.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/notifier.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
#define DEBUG
|
||||
#else
|
||||
#undef DEBUG
|
||||
#endif
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
|
||||
static struct notifier_block *usb_notifier_list;
|
||||
static DECLARE_MUTEX(usb_notifier_lock);
|
||||
|
||||
static void usb_notifier_chain_register(struct notifier_block **list,
|
||||
struct notifier_block *n)
|
||||
{
|
||||
down(&usb_notifier_lock);
|
||||
while (*list) {
|
||||
if (n->priority > (*list)->priority)
|
||||
break;
|
||||
list = &((*list)->next);
|
||||
}
|
||||
n->next = *list;
|
||||
*list = n;
|
||||
up(&usb_notifier_lock);
|
||||
}
|
||||
|
||||
static void usb_notifier_chain_unregister(struct notifier_block **nl,
|
||||
struct notifier_block *n)
|
||||
{
|
||||
down(&usb_notifier_lock);
|
||||
while ((*nl)!=NULL) {
|
||||
if ((*nl)==n) {
|
||||
*nl = n->next;
|
||||
goto exit;
|
||||
}
|
||||
nl=&((*nl)->next);
|
||||
}
|
||||
exit:
|
||||
up(&usb_notifier_lock);
|
||||
}
|
||||
|
||||
static int usb_notifier_call_chain(struct notifier_block **n,
|
||||
unsigned long val, void *v)
|
||||
{
|
||||
int ret=NOTIFY_DONE;
|
||||
struct notifier_block *nb = *n;
|
||||
|
||||
down(&usb_notifier_lock);
|
||||
while (nb) {
|
||||
ret = nb->notifier_call(nb,val,v);
|
||||
if (ret&NOTIFY_STOP_MASK) {
|
||||
goto exit;
|
||||
}
|
||||
nb = nb->next;
|
||||
}
|
||||
exit:
|
||||
up(&usb_notifier_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_register_notify - register a notifier callback whenever a usb change happens
|
||||
* @nb: pointer to the notifier block for the callback events.
|
||||
*
|
||||
* These changes are either USB devices or busses being added or removed.
|
||||
*/
|
||||
void usb_register_notify(struct notifier_block *nb)
|
||||
{
|
||||
usb_notifier_chain_register(&usb_notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_register_notify);
|
||||
|
||||
/**
|
||||
* usb_unregister_notify - unregister a notifier callback
|
||||
* @nb: pointer to the notifier block for the callback events.
|
||||
*
|
||||
* usb_register_notifier() must have been previously called for this function
|
||||
* to work properly.
|
||||
*/
|
||||
void usb_unregister_notify(struct notifier_block *nb)
|
||||
{
|
||||
usb_notifier_chain_unregister(&usb_notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unregister_notify);
|
||||
|
||||
|
||||
void usb_notify_add_device(struct usb_device *udev)
|
||||
{
|
||||
usb_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev);
|
||||
}
|
||||
|
||||
void usb_notify_remove_device(struct usb_device *udev)
|
||||
{
|
||||
usb_notifier_call_chain(&usb_notifier_list, USB_DEVICE_REMOVE, udev);
|
||||
}
|
||||
|
||||
void usb_notify_add_bus(struct usb_bus *ubus)
|
||||
{
|
||||
usb_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus);
|
||||
}
|
||||
|
||||
void usb_notify_remove_bus(struct usb_bus *ubus)
|
||||
{
|
||||
usb_notifier_call_chain(&usb_notifier_list, USB_BUS_REMOVE, ubus);
|
||||
}
|
|
@ -22,9 +22,207 @@
|
|||
|
||||
#include "usb.h"
|
||||
|
||||
/* endpoint stuff */
|
||||
struct ep_object {
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
struct usb_device *udev;
|
||||
struct kobject kobj;
|
||||
};
|
||||
#define to_ep_object(_kobj) \
|
||||
container_of(_kobj, struct ep_object, kobj)
|
||||
|
||||
struct ep_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct usb_device *,
|
||||
struct usb_endpoint_descriptor *, char *);
|
||||
};
|
||||
#define to_ep_attribute(_attr) \
|
||||
container_of(_attr, struct ep_attribute, attr)
|
||||
|
||||
#define EP_ATTR(_name) \
|
||||
struct ep_attribute ep_##_name = { \
|
||||
.attr = {.name = #_name, .owner = THIS_MODULE, \
|
||||
.mode = S_IRUGO}, \
|
||||
.show = show_ep_##_name}
|
||||
|
||||
#define usb_ep_attr(field, format_string) \
|
||||
static ssize_t show_ep_##field(struct usb_device *udev, \
|
||||
struct usb_endpoint_descriptor *desc, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, format_string, desc->field); \
|
||||
} \
|
||||
static EP_ATTR(field);
|
||||
|
||||
usb_ep_attr(bLength, "%02x\n")
|
||||
usb_ep_attr(bEndpointAddress, "%02x\n")
|
||||
usb_ep_attr(bmAttributes, "%02x\n")
|
||||
usb_ep_attr(bInterval, "%02x\n")
|
||||
|
||||
static ssize_t show_ep_wMaxPacketSize(struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *desc, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%04x\n",
|
||||
le16_to_cpu(desc->wMaxPacketSize) & 0x07ff);
|
||||
}
|
||||
static EP_ATTR(wMaxPacketSize);
|
||||
|
||||
static ssize_t show_ep_type(struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *desc, char *buf)
|
||||
{
|
||||
char *type = "unknown";
|
||||
|
||||
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
type = "Control";
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
type = "Isoc";
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
type = "Bulk";
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
type = "Interrupt";
|
||||
break;
|
||||
}
|
||||
return sprintf(buf, "%s\n", type);
|
||||
}
|
||||
static EP_ATTR(type);
|
||||
|
||||
static ssize_t show_ep_interval(struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *desc, char *buf)
|
||||
{
|
||||
char unit;
|
||||
unsigned interval = 0;
|
||||
unsigned in;
|
||||
|
||||
in = (desc->bEndpointAddress & USB_DIR_IN);
|
||||
|
||||
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
if (udev->speed == USB_SPEED_HIGH) /* uframes per NAK */
|
||||
interval = desc->bInterval;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
interval = 1 << (desc->bInterval - 1);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
|
||||
interval = desc->bInterval;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
if (udev->speed == USB_SPEED_HIGH)
|
||||
interval = 1 << (desc->bInterval - 1);
|
||||
else
|
||||
interval = desc->bInterval;
|
||||
break;
|
||||
}
|
||||
interval *= (udev->speed == USB_SPEED_HIGH) ? 125 : 1000;
|
||||
if (interval % 1000)
|
||||
unit = 'u';
|
||||
else {
|
||||
unit = 'm';
|
||||
interval /= 1000;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d%cs\n", interval, unit);
|
||||
}
|
||||
static EP_ATTR(interval);
|
||||
|
||||
static ssize_t show_ep_direction(struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *desc, char *buf)
|
||||
{
|
||||
char *direction;
|
||||
|
||||
if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
|
||||
USB_ENDPOINT_XFER_CONTROL)
|
||||
direction = "both";
|
||||
else if (desc->bEndpointAddress & USB_DIR_IN)
|
||||
direction = "in";
|
||||
else
|
||||
direction = "out";
|
||||
return sprintf(buf, "%s\n", direction);
|
||||
}
|
||||
static EP_ATTR(direction);
|
||||
|
||||
static struct attribute *ep_attrs[] = {
|
||||
&ep_bLength.attr,
|
||||
&ep_bEndpointAddress.attr,
|
||||
&ep_bmAttributes.attr,
|
||||
&ep_bInterval.attr,
|
||||
&ep_wMaxPacketSize.attr,
|
||||
&ep_type.attr,
|
||||
&ep_interval.attr,
|
||||
&ep_direction.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void ep_object_release(struct kobject *kobj)
|
||||
{
|
||||
kfree(to_ep_object(kobj));
|
||||
}
|
||||
|
||||
static ssize_t ep_object_show(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ep_object *ep_obj = to_ep_object(kobj);
|
||||
struct ep_attribute *ep_attr = to_ep_attribute(attr);
|
||||
|
||||
return (ep_attr->show)(ep_obj->udev, ep_obj->desc, buf);
|
||||
}
|
||||
|
||||
static struct sysfs_ops ep_object_sysfs_ops = {
|
||||
.show = ep_object_show,
|
||||
};
|
||||
|
||||
static struct kobj_type ep_object_ktype = {
|
||||
.release = ep_object_release,
|
||||
.sysfs_ops = &ep_object_sysfs_ops,
|
||||
.default_attrs = ep_attrs,
|
||||
};
|
||||
|
||||
static void usb_create_ep_files(struct kobject *parent,
|
||||
struct usb_host_endpoint *endpoint,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
struct ep_object *ep_obj;
|
||||
struct kobject *kobj;
|
||||
|
||||
ep_obj = kzalloc(sizeof(struct ep_object), GFP_KERNEL);
|
||||
if (!ep_obj)
|
||||
return;
|
||||
|
||||
ep_obj->desc = &endpoint->desc;
|
||||
ep_obj->udev = udev;
|
||||
|
||||
kobj = &ep_obj->kobj;
|
||||
kobject_set_name(kobj, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
kobj->parent = parent;
|
||||
kobj->ktype = &ep_object_ktype;
|
||||
|
||||
/* Don't use kobject_register, because it generates a hotplug event */
|
||||
kobject_init(kobj);
|
||||
if (kobject_add(kobj) == 0)
|
||||
endpoint->kobj = kobj;
|
||||
else
|
||||
kobject_put(kobj);
|
||||
}
|
||||
|
||||
static void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
|
||||
{
|
||||
|
||||
if (endpoint->kobj) {
|
||||
kobject_del(endpoint->kobj);
|
||||
kobject_put(endpoint->kobj);
|
||||
endpoint->kobj = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Active configuration fields */
|
||||
#define usb_actconfig_show(field, multiplier, format_string) \
|
||||
static ssize_t show_##field (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
static ssize_t show_##field (struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct usb_device *udev; \
|
||||
struct usb_host_config *actconfig; \
|
||||
|
@ -46,22 +244,17 @@ 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, struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_configuration_string(struct device *dev,
|
||||
struct device_attribute *attr, 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;
|
||||
return sprintf(buf, "%s\n", actconfig->string);
|
||||
}
|
||||
static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL);
|
||||
|
||||
|
@ -69,7 +262,8 @@ static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL);
|
|||
usb_actconfig_show(bConfigurationValue, 1, "%u\n");
|
||||
|
||||
static ssize_t
|
||||
set_bConfigurationValue (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
set_bConfigurationValue (struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = udev = to_usb_device (dev);
|
||||
int config, value;
|
||||
|
@ -87,18 +281,13 @@ static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR,
|
|||
|
||||
/* String fields */
|
||||
#define usb_string_attr(name) \
|
||||
static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
static ssize_t show_##name(struct device *dev, \
|
||||
struct device_attribute *attr, 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; \
|
||||
return sprintf(buf, "%s\n", udev->name); \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
|
||||
|
||||
|
@ -167,7 +356,8 @@ 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, struct device_attribute *attr, char *buf) \
|
||||
show_##field (struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct usb_device *udev; \
|
||||
\
|
||||
|
@ -183,7 +373,8 @@ usb_descriptor_attr_le16(bcdDevice, "%04x\n")
|
|||
|
||||
#define usb_descriptor_attr(field, format_string) \
|
||||
static ssize_t \
|
||||
show_##field (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
show_##field (struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct usb_device *udev; \
|
||||
\
|
||||
|
@ -236,19 +427,21 @@ void usb_create_sysfs_dev_files (struct usb_device *udev)
|
|||
if (udev->serial)
|
||||
device_create_file (dev, &dev_attr_serial);
|
||||
device_create_file (dev, &dev_attr_configuration);
|
||||
usb_create_ep_files(&dev->kobj, &udev->ep0, udev);
|
||||
}
|
||||
|
||||
void usb_remove_sysfs_dev_files (struct usb_device *udev)
|
||||
{
|
||||
struct device *dev = &udev->dev;
|
||||
|
||||
usb_remove_ep_files(&udev->ep0);
|
||||
sysfs_remove_group(&dev->kobj, &dev_attr_grp);
|
||||
|
||||
if (udev->descriptor.iManufacturer)
|
||||
if (udev->manufacturer)
|
||||
device_remove_file(dev, &dev_attr_manufacturer);
|
||||
if (udev->descriptor.iProduct)
|
||||
if (udev->product)
|
||||
device_remove_file(dev, &dev_attr_product);
|
||||
if (udev->descriptor.iSerialNumber)
|
||||
if (udev->serial)
|
||||
device_remove_file(dev, &dev_attr_serial);
|
||||
device_remove_file (dev, &dev_attr_configuration);
|
||||
}
|
||||
|
@ -256,11 +449,13 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev)
|
|||
/* Interface fields */
|
||||
#define usb_intf_attr(field, format_string) \
|
||||
static ssize_t \
|
||||
show_##field (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
show_##field (struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct usb_interface *intf = to_usb_interface (dev); \
|
||||
\
|
||||
return sprintf (buf, format_string, intf->cur_altsetting->desc.field); \
|
||||
return sprintf (buf, format_string, \
|
||||
intf->cur_altsetting->desc.field); \
|
||||
} \
|
||||
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
|
||||
|
||||
|
@ -271,7 +466,8 @@ 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, struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_interface_string(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf;
|
||||
struct usb_device *udev;
|
||||
|
@ -288,34 +484,28 @@ static ssize_t show_interface_string(struct device *dev, struct device_attribute
|
|||
}
|
||||
static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL);
|
||||
|
||||
static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_modalias(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf;
|
||||
struct usb_device *udev;
|
||||
int len;
|
||||
struct usb_host_interface *alt;
|
||||
|
||||
intf = to_usb_interface(dev);
|
||||
udev = interface_to_usbdev(intf);
|
||||
alt = intf->cur_altsetting;
|
||||
|
||||
len = sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct),
|
||||
le16_to_cpu(udev->descriptor.bcdDevice),
|
||||
udev->descriptor.bDeviceClass,
|
||||
udev->descriptor.bDeviceSubClass,
|
||||
udev->descriptor.bDeviceProtocol);
|
||||
buf += len;
|
||||
|
||||
if (udev->descriptor.bDeviceClass == 0) {
|
||||
struct usb_host_interface *alt = intf->cur_altsetting;
|
||||
|
||||
return len + sprintf(buf, "%02Xisc%02Xip%02X\n",
|
||||
alt->desc.bInterfaceClass,
|
||||
alt->desc.bInterfaceSubClass,
|
||||
alt->desc.bInterfaceProtocol);
|
||||
} else {
|
||||
return len + sprintf(buf, "*isc*ip*\n");
|
||||
}
|
||||
return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
|
||||
"ic%02Xisc%02Xip%02X\n",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct),
|
||||
le16_to_cpu(udev->descriptor.bcdDevice),
|
||||
udev->descriptor.bDeviceClass,
|
||||
udev->descriptor.bDeviceSubClass,
|
||||
udev->descriptor.bDeviceProtocol,
|
||||
alt->desc.bInterfaceClass,
|
||||
alt->desc.bInterfaceSubClass,
|
||||
alt->desc.bInterfaceProtocol);
|
||||
}
|
||||
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
|
||||
|
||||
|
@ -333,20 +523,47 @@ static struct attribute_group intf_attr_grp = {
|
|||
.attrs = intf_attrs,
|
||||
};
|
||||
|
||||
static inline void usb_create_intf_ep_files(struct usb_interface *intf,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_interface *iface_desc;
|
||||
int i;
|
||||
|
||||
iface_desc = intf->cur_altsetting;
|
||||
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
|
||||
usb_create_ep_files(&intf->dev.kobj, &iface_desc->endpoint[i],
|
||||
udev);
|
||||
}
|
||||
|
||||
static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_host_interface *iface_desc;
|
||||
int i;
|
||||
|
||||
iface_desc = intf->cur_altsetting;
|
||||
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
|
||||
usb_remove_ep_files(&iface_desc->endpoint[i]);
|
||||
}
|
||||
|
||||
void usb_create_sysfs_intf_files (struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *alt = intf->cur_altsetting;
|
||||
|
||||
sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
|
||||
|
||||
if (intf->cur_altsetting->string)
|
||||
if (alt->string == NULL)
|
||||
alt->string = usb_cache_string(udev, alt->desc.iInterface);
|
||||
if (alt->string)
|
||||
device_create_file(&intf->dev, &dev_attr_interface);
|
||||
|
||||
usb_create_intf_ep_files(intf, udev);
|
||||
}
|
||||
|
||||
void usb_remove_sysfs_intf_files (struct usb_interface *intf)
|
||||
{
|
||||
usb_remove_intf_ep_files(intf);
|
||||
sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
|
||||
|
||||
if (intf->cur_altsetting->string)
|
||||
device_remove_file(&intf->dev, &dev_attr_interface);
|
||||
|
||||
}
|
||||
|
|
|
@ -237,7 +237,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||
(dev->state < USB_STATE_DEFAULT) ||
|
||||
(!dev->bus) || (dev->devnum <= 0))
|
||||
return -ENODEV;
|
||||
if (dev->state == USB_STATE_SUSPENDED)
|
||||
if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
|
||||
|| dev->state == USB_STATE_SUSPENDED)
|
||||
return -EHOSTUNREACH;
|
||||
if (!(op = dev->bus->op) || !op->submit_urb)
|
||||
return -ENODEV;
|
||||
|
|
|
@ -107,10 +107,19 @@ static int usb_probe_interface(struct device *dev)
|
|||
id = usb_match_id (intf, driver->id_table);
|
||||
if (id) {
|
||||
dev_dbg (dev, "%s - got id\n", __FUNCTION__);
|
||||
|
||||
/* Interface "power state" doesn't correspond to any hardware
|
||||
* state whatsoever. We use it to record when it's bound to
|
||||
* a driver that may start I/0: it's not frozen/quiesced.
|
||||
*/
|
||||
mark_active(intf);
|
||||
intf->condition = USB_INTERFACE_BINDING;
|
||||
error = driver->probe (intf, id);
|
||||
intf->condition = error ? USB_INTERFACE_UNBOUND :
|
||||
USB_INTERFACE_BOUND;
|
||||
if (error) {
|
||||
mark_quiesced(intf);
|
||||
intf->condition = USB_INTERFACE_UNBOUND;
|
||||
} else
|
||||
intf->condition = USB_INTERFACE_BOUND;
|
||||
}
|
||||
|
||||
return error;
|
||||
|
@ -136,6 +145,7 @@ static int usb_unbind_interface(struct device *dev)
|
|||
0);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
intf->condition = USB_INTERFACE_UNBOUND;
|
||||
mark_quiesced(intf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -299,6 +309,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||
dev->driver = &driver->driver;
|
||||
usb_set_intfdata(iface, priv);
|
||||
iface->condition = USB_INTERFACE_BOUND;
|
||||
mark_active(iface);
|
||||
|
||||
/* if interface was already added, bind now; else let
|
||||
* the future device_add() bind it, bypassing probe()
|
||||
|
@ -345,6 +356,7 @@ void usb_driver_release_interface(struct usb_driver *driver,
|
|||
dev->driver = NULL;
|
||||
usb_set_intfdata(iface, NULL);
|
||||
iface->condition = USB_INTERFACE_UNBOUND;
|
||||
mark_quiesced(iface);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -557,6 +569,7 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp,
|
|||
{
|
||||
struct usb_interface *intf;
|
||||
struct usb_device *usb_dev;
|
||||
struct usb_host_interface *alt;
|
||||
int i = 0;
|
||||
int length = 0;
|
||||
|
||||
|
@ -573,7 +586,8 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp,
|
|||
|
||||
intf = to_usb_interface(dev);
|
||||
usb_dev = interface_to_usbdev (intf);
|
||||
|
||||
alt = intf->cur_altsetting;
|
||||
|
||||
if (usb_dev->devnum < 0) {
|
||||
pr_debug ("usb %s: already deleted?\n", dev->bus_id);
|
||||
return -ENODEV;
|
||||
|
@ -615,46 +629,27 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp,
|
|||
usb_dev->descriptor.bDeviceProtocol))
|
||||
return -ENOMEM;
|
||||
|
||||
if (usb_dev->descriptor.bDeviceClass == 0) {
|
||||
struct usb_host_interface *alt = intf->cur_altsetting;
|
||||
if (add_hotplug_env_var(envp, num_envp, &i,
|
||||
buffer, buffer_size, &length,
|
||||
"INTERFACE=%d/%d/%d",
|
||||
alt->desc.bInterfaceClass,
|
||||
alt->desc.bInterfaceSubClass,
|
||||
alt->desc.bInterfaceProtocol))
|
||||
return -ENOMEM;
|
||||
|
||||
/* 2.4 only exposed interface zero. in 2.5, hotplug
|
||||
* agents are called for all interfaces, and can use
|
||||
* $DEVPATH/bInterfaceNumber if necessary.
|
||||
*/
|
||||
if (add_hotplug_env_var(envp, num_envp, &i,
|
||||
buffer, buffer_size, &length,
|
||||
"INTERFACE=%d/%d/%d",
|
||||
alt->desc.bInterfaceClass,
|
||||
alt->desc.bInterfaceSubClass,
|
||||
alt->desc.bInterfaceProtocol))
|
||||
return -ENOMEM;
|
||||
|
||||
if (add_hotplug_env_var(envp, num_envp, &i,
|
||||
buffer, buffer_size, &length,
|
||||
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
|
||||
le16_to_cpu(usb_dev->descriptor.idVendor),
|
||||
le16_to_cpu(usb_dev->descriptor.idProduct),
|
||||
le16_to_cpu(usb_dev->descriptor.bcdDevice),
|
||||
usb_dev->descriptor.bDeviceClass,
|
||||
usb_dev->descriptor.bDeviceSubClass,
|
||||
usb_dev->descriptor.bDeviceProtocol,
|
||||
alt->desc.bInterfaceClass,
|
||||
alt->desc.bInterfaceSubClass,
|
||||
alt->desc.bInterfaceProtocol))
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
if (add_hotplug_env_var(envp, num_envp, &i,
|
||||
buffer, buffer_size, &length,
|
||||
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic*isc*ip*",
|
||||
le16_to_cpu(usb_dev->descriptor.idVendor),
|
||||
le16_to_cpu(usb_dev->descriptor.idProduct),
|
||||
le16_to_cpu(usb_dev->descriptor.bcdDevice),
|
||||
usb_dev->descriptor.bDeviceClass,
|
||||
usb_dev->descriptor.bDeviceSubClass,
|
||||
usb_dev->descriptor.bDeviceProtocol))
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (add_hotplug_env_var(envp, num_envp, &i,
|
||||
buffer, buffer_size, &length,
|
||||
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
|
||||
le16_to_cpu(usb_dev->descriptor.idVendor),
|
||||
le16_to_cpu(usb_dev->descriptor.idProduct),
|
||||
le16_to_cpu(usb_dev->descriptor.bcdDevice),
|
||||
usb_dev->descriptor.bDeviceClass,
|
||||
usb_dev->descriptor.bDeviceSubClass,
|
||||
usb_dev->descriptor.bDeviceProtocol,
|
||||
alt->desc.bInterfaceClass,
|
||||
alt->desc.bInterfaceSubClass,
|
||||
alt->desc.bInterfaceProtocol))
|
||||
return -ENOMEM;
|
||||
|
||||
envp[i] = NULL;
|
||||
|
||||
|
@ -709,12 +704,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
|
|||
{
|
||||
struct usb_device *dev;
|
||||
|
||||
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
|
||||
bus = usb_bus_get(bus);
|
||||
if (!bus) {
|
||||
kfree(dev);
|
||||
|
@ -1402,13 +1395,30 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
|
|||
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
static int verify_suspended(struct device *dev, void *unused)
|
||||
{
|
||||
return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
|
||||
}
|
||||
|
||||
static int usb_generic_suspend(struct device *dev, pm_message_t message)
|
||||
{
|
||||
struct usb_interface *intf;
|
||||
struct usb_driver *driver;
|
||||
struct usb_interface *intf;
|
||||
struct usb_driver *driver;
|
||||
int status;
|
||||
|
||||
if (dev->driver == &usb_generic_driver)
|
||||
return usb_suspend_device (to_usb_device(dev), message);
|
||||
/* USB devices enter SUSPEND state through their hubs, but can be
|
||||
* marked for FREEZE as soon as their children are already idled.
|
||||
* But those semantics are useless, so we equate the two (sigh).
|
||||
*/
|
||||
if (dev->driver == &usb_generic_driver) {
|
||||
if (dev->power.power_state.event == message.event)
|
||||
return 0;
|
||||
/* we need to rule out bogus requests through sysfs */
|
||||
status = device_for_each_child(dev, NULL, verify_suspended);
|
||||
if (status)
|
||||
return status;
|
||||
return usb_suspend_device (to_usb_device(dev));
|
||||
}
|
||||
|
||||
if ((dev->driver == NULL) ||
|
||||
(dev->driver_data == &usb_generic_driver_data))
|
||||
|
@ -1417,23 +1427,44 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message)
|
|||
intf = to_usb_interface(dev);
|
||||
driver = to_usb_driver(dev->driver);
|
||||
|
||||
/* there's only one USB suspend state */
|
||||
if (intf->dev.power.power_state.event)
|
||||
/* with no hardware, USB interfaces only use FREEZE and ON states */
|
||||
if (!is_active(intf))
|
||||
return 0;
|
||||
|
||||
if (driver->suspend)
|
||||
return driver->suspend(intf, message);
|
||||
return 0;
|
||||
if (driver->suspend && driver->resume) {
|
||||
status = driver->suspend(intf, message);
|
||||
if (status)
|
||||
dev_err(dev, "%s error %d\n", "suspend", status);
|
||||
else
|
||||
mark_quiesced(intf);
|
||||
} else {
|
||||
// FIXME else if there's no suspend method, disconnect...
|
||||
dev_warn(dev, "no %s?\n", "suspend");
|
||||
status = 0;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int usb_generic_resume(struct device *dev)
|
||||
{
|
||||
struct usb_interface *intf;
|
||||
struct usb_driver *driver;
|
||||
struct usb_interface *intf;
|
||||
struct usb_driver *driver;
|
||||
struct usb_device *udev;
|
||||
int status;
|
||||
|
||||
/* devices resume through their hub */
|
||||
if (dev->driver == &usb_generic_driver)
|
||||
if (dev->power.power_state.event == PM_EVENT_ON)
|
||||
return 0;
|
||||
|
||||
/* mark things as "on" immediately, no matter what errors crop up */
|
||||
dev->power.power_state.event = PM_EVENT_ON;
|
||||
|
||||
/* devices resume through their hubs */
|
||||
if (dev->driver == &usb_generic_driver) {
|
||||
udev = to_usb_device(dev);
|
||||
if (udev->state == USB_STATE_NOTATTACHED)
|
||||
return 0;
|
||||
return usb_resume_device (to_usb_device(dev));
|
||||
}
|
||||
|
||||
if ((dev->driver == NULL) ||
|
||||
(dev->driver_data == &usb_generic_driver_data))
|
||||
|
@ -1442,8 +1473,22 @@ static int usb_generic_resume(struct device *dev)
|
|||
intf = to_usb_interface(dev);
|
||||
driver = to_usb_driver(dev->driver);
|
||||
|
||||
if (driver->resume)
|
||||
return driver->resume(intf);
|
||||
udev = interface_to_usbdev(intf);
|
||||
if (udev->state == USB_STATE_NOTATTACHED)
|
||||
return 0;
|
||||
|
||||
/* if driver was suspended, it has a resume method;
|
||||
* however, sysfs can wrongly mark things as suspended
|
||||
* (on the "no suspend method" FIXME path above)
|
||||
*/
|
||||
if (driver->resume) {
|
||||
status = driver->resume(intf);
|
||||
if (status) {
|
||||
dev_err(dev, "%s error %d\n", "resume", status);
|
||||
mark_quiesced(intf);
|
||||
}
|
||||
} else
|
||||
dev_warn(dev, "no %s?\n", "resume");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,12 +13,14 @@ 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 char *usb_cache_string(struct usb_device *udev, int index);
|
||||
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_suspend_root_hub(struct usb_device *hdev);
|
||||
extern void usb_resume_root_hub(struct usb_device *dev);
|
||||
|
||||
extern int usb_hub_init(void);
|
||||
|
@ -28,6 +30,28 @@ extern void usb_major_cleanup(void);
|
|||
extern int usb_host_init(void);
|
||||
extern void usb_host_cleanup(void);
|
||||
|
||||
extern int usb_suspend_device(struct usb_device *dev);
|
||||
extern int usb_resume_device(struct usb_device *dev);
|
||||
|
||||
|
||||
/* Interfaces and their "power state" are owned by usbcore */
|
||||
|
||||
static inline void mark_active(struct usb_interface *f)
|
||||
{
|
||||
f->dev.power.power_state.event = PM_EVENT_ON;
|
||||
}
|
||||
|
||||
static inline void mark_quiesced(struct usb_interface *f)
|
||||
{
|
||||
f->dev.power.power_state.event = PM_EVENT_FREEZE;
|
||||
}
|
||||
|
||||
static inline int is_active(struct usb_interface *f)
|
||||
{
|
||||
return f->dev.power.power_state.event == PM_EVENT_ON;
|
||||
}
|
||||
|
||||
|
||||
/* for labeling diagnostics */
|
||||
extern const char *usbcore_name;
|
||||
|
||||
|
@ -39,9 +63,6 @@ extern void usbfs_conn_disc_event(void);
|
|||
|
||||
extern int usbdev_init(void);
|
||||
extern void usbdev_cleanup(void);
|
||||
extern void usbdev_add(struct usb_device *dev);
|
||||
extern void usbdev_remove(struct usb_device *dev);
|
||||
extern struct usb_device *usbdev_lookup_minor(int minor);
|
||||
|
||||
struct dev_state {
|
||||
struct list_head list; /* state list */
|
||||
|
@ -58,3 +79,9 @@ struct dev_state {
|
|||
unsigned long ifclaimed;
|
||||
};
|
||||
|
||||
/* internal notify stuff */
|
||||
extern void usb_notify_add_device(struct usb_device *udev);
|
||||
extern void usb_notify_remove_device(struct usb_device *udev);
|
||||
extern void usb_notify_add_bus(struct usb_bus *ubus);
|
||||
extern void usb_notify_remove_bus(struct usb_bus *ubus);
|
||||
|
||||
|
|
|
@ -967,6 +967,7 @@ static int dummy_udc_resume (struct device *dev)
|
|||
|
||||
static struct device_driver dummy_udc_driver = {
|
||||
.name = (char *) gadget_name,
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = dummy_udc_probe,
|
||||
.remove = dummy_udc_remove,
|
||||
|
@ -1751,7 +1752,7 @@ static int dummy_hub_control (
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int dummy_hub_suspend (struct usb_hcd *hcd)
|
||||
static int dummy_bus_suspend (struct usb_hcd *hcd)
|
||||
{
|
||||
struct dummy *dum = hcd_to_dummy (hcd);
|
||||
|
||||
|
@ -1762,7 +1763,7 @@ static int dummy_hub_suspend (struct usb_hcd *hcd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_hub_resume (struct usb_hcd *hcd)
|
||||
static int dummy_bus_resume (struct usb_hcd *hcd)
|
||||
{
|
||||
struct dummy *dum = hcd_to_dummy (hcd);
|
||||
|
||||
|
@ -1894,8 +1895,8 @@ static const struct hc_driver dummy_hcd = {
|
|||
|
||||
.hub_status_data = dummy_hub_status,
|
||||
.hub_control = dummy_hub_control,
|
||||
.hub_suspend = dummy_hub_suspend,
|
||||
.hub_resume = dummy_hub_resume,
|
||||
.bus_suspend = dummy_bus_suspend,
|
||||
.bus_resume = dummy_bus_resume,
|
||||
};
|
||||
|
||||
static int dummy_hcd_probe (struct device *dev)
|
||||
|
@ -1936,13 +1937,6 @@ static int dummy_hcd_suspend (struct device *dev, pm_message_t state)
|
|||
dev_dbg (dev, "%s\n", __FUNCTION__);
|
||||
hcd = dev_get_drvdata (dev);
|
||||
|
||||
#ifndef CONFIG_USB_SUSPEND
|
||||
/* Otherwise this would never happen */
|
||||
usb_lock_device (hcd->self.root_hub);
|
||||
dummy_hub_suspend (hcd);
|
||||
usb_unlock_device (hcd->self.root_hub);
|
||||
#endif
|
||||
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1955,19 +1949,13 @@ static int dummy_hcd_resume (struct device *dev)
|
|||
hcd = dev_get_drvdata (dev);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
|
||||
#ifndef CONFIG_USB_SUSPEND
|
||||
/* Otherwise this would never happen */
|
||||
usb_lock_device (hcd->self.root_hub);
|
||||
dummy_hub_resume (hcd);
|
||||
usb_unlock_device (hcd->self.root_hub);
|
||||
#endif
|
||||
|
||||
usb_hcd_poll_rh_status (hcd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_driver dummy_hcd_driver = {
|
||||
.name = (char *) driver_name,
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = dummy_hcd_probe,
|
||||
.remove = dummy_hcd_remove,
|
||||
|
|
|
@ -2533,6 +2533,7 @@ static struct usb_gadget_driver eth_driver = {
|
|||
|
||||
.driver = {
|
||||
.name = (char *) shortname,
|
||||
.owner = THIS_MODULE,
|
||||
// .shutdown = ...
|
||||
// .suspend = ...
|
||||
// .resume = ...
|
||||
|
|
|
@ -224,6 +224,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -669,7 +670,6 @@ struct fsg_dev {
|
|||
wait_queue_head_t thread_wqh;
|
||||
int thread_wakeup_needed;
|
||||
struct completion thread_notifier;
|
||||
int thread_pid;
|
||||
struct task_struct *thread_task;
|
||||
sigset_t thread_signal_mask;
|
||||
|
||||
|
@ -1084,7 +1084,6 @@ static void wakeup_thread(struct fsg_dev *fsg)
|
|||
static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct task_struct *thread_task;
|
||||
|
||||
/* Do nothing if a higher-priority exception is already in progress.
|
||||
* If a lower-or-equal priority exception is in progress, preempt it
|
||||
|
@ -1093,9 +1092,9 @@ static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state)
|
|||
if (fsg->state <= new_state) {
|
||||
fsg->exception_req_tag = fsg->ep0_req_tag;
|
||||
fsg->state = new_state;
|
||||
thread_task = fsg->thread_task;
|
||||
if (thread_task)
|
||||
send_sig_info(SIGUSR1, SEND_SIG_FORCED, thread_task);
|
||||
if (fsg->thread_task)
|
||||
send_sig_info(SIGUSR1, SEND_SIG_FORCED,
|
||||
fsg->thread_task);
|
||||
}
|
||||
spin_unlock_irqrestore(&fsg->lock, flags);
|
||||
}
|
||||
|
@ -3383,11 +3382,6 @@ static int fsg_main_thread(void *fsg_)
|
|||
{
|
||||
struct fsg_dev *fsg = (struct fsg_dev *) fsg_;
|
||||
|
||||
fsg->thread_task = current;
|
||||
|
||||
/* Release all our userspace resources */
|
||||
daemonize("file-storage-gadget");
|
||||
|
||||
/* Allow the thread to be killed by a signal, but set the signal mask
|
||||
* to block everything but INT, TERM, KILL, and USR1. */
|
||||
siginitsetinv(&fsg->thread_signal_mask, sigmask(SIGINT) |
|
||||
|
@ -3400,9 +3394,6 @@ static int fsg_main_thread(void *fsg_)
|
|||
* that expects a __user pointer and it will work okay. */
|
||||
set_fs(get_ds());
|
||||
|
||||
/* Wait for the gadget registration to finish up */
|
||||
wait_for_completion(&fsg->thread_notifier);
|
||||
|
||||
/* The main loop */
|
||||
while (fsg->state != FSG_STATE_TERMINATED) {
|
||||
if (exception_in_progress(fsg) || signal_pending(current)) {
|
||||
|
@ -3440,8 +3431,9 @@ static int fsg_main_thread(void *fsg_)
|
|||
spin_unlock_irq(&fsg->lock);
|
||||
}
|
||||
|
||||
spin_lock_irq(&fsg->lock);
|
||||
fsg->thread_task = NULL;
|
||||
flush_signals(current);
|
||||
spin_unlock_irq(&fsg->lock);
|
||||
|
||||
/* In case we are exiting because of a signal, unregister the
|
||||
* gadget driver and close the backing file. */
|
||||
|
@ -3831,12 +3823,11 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||
|
||||
/* Create the LUNs, open their backing files, and register the
|
||||
* LUN devices in sysfs. */
|
||||
fsg->luns = kmalloc(i * sizeof(struct lun), GFP_KERNEL);
|
||||
fsg->luns = kzalloc(i * sizeof(struct lun), GFP_KERNEL);
|
||||
if (!fsg->luns) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memset(fsg->luns, 0, i * sizeof(struct lun));
|
||||
fsg->nluns = i;
|
||||
|
||||
for (i = 0; i < fsg->nluns; ++i) {
|
||||
|
@ -3959,10 +3950,12 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||
sprintf(&serial[i], "%02X", c);
|
||||
}
|
||||
|
||||
if ((rc = kernel_thread(fsg_main_thread, fsg, (CLONE_VM | CLONE_FS |
|
||||
CLONE_FILES))) < 0)
|
||||
fsg->thread_task = kthread_create(fsg_main_thread, fsg,
|
||||
"file-storage-gadget");
|
||||
if (IS_ERR(fsg->thread_task)) {
|
||||
rc = PTR_ERR(fsg->thread_task);
|
||||
goto out;
|
||||
fsg->thread_pid = rc;
|
||||
}
|
||||
|
||||
INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
|
||||
INFO(fsg, "Number of LUNs=%d\n", fsg->nluns);
|
||||
|
@ -3994,7 +3987,12 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||
DBG(fsg, "removable=%d, stall=%d, buflen=%u\n",
|
||||
mod_data.removable, mod_data.can_stall,
|
||||
mod_data.buflen);
|
||||
DBG(fsg, "I/O thread pid: %d\n", fsg->thread_pid);
|
||||
DBG(fsg, "I/O thread pid: %d\n", fsg->thread_task->pid);
|
||||
|
||||
set_bit(REGISTERED, &fsg->atomic_bitflags);
|
||||
|
||||
/* Tell the thread to start working */
|
||||
wake_up_process(fsg->thread_task);
|
||||
return 0;
|
||||
|
||||
autoconf_fail:
|
||||
|
@ -4046,6 +4044,7 @@ static struct usb_gadget_driver fsg_driver = {
|
|||
|
||||
.driver = {
|
||||
.name = (char *) shortname,
|
||||
.owner = THIS_MODULE,
|
||||
// .release = ...
|
||||
// .suspend = ...
|
||||
// .resume = ...
|
||||
|
@ -4057,10 +4056,9 @@ static int __init fsg_alloc(void)
|
|||
{
|
||||
struct fsg_dev *fsg;
|
||||
|
||||
fsg = kmalloc(sizeof *fsg, GFP_KERNEL);
|
||||
fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
|
||||
if (!fsg)
|
||||
return -ENOMEM;
|
||||
memset(fsg, 0, sizeof *fsg);
|
||||
spin_lock_init(&fsg->lock);
|
||||
init_rwsem(&fsg->filesem);
|
||||
init_waitqueue_head(&fsg->thread_wqh);
|
||||
|
@ -4086,15 +4084,9 @@ static int __init fsg_init(void)
|
|||
if ((rc = fsg_alloc()) != 0)
|
||||
return rc;
|
||||
fsg = the_fsg;
|
||||
if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) {
|
||||
if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0)
|
||||
fsg_free(fsg);
|
||||
return rc;
|
||||
}
|
||||
set_bit(REGISTERED, &fsg->atomic_bitflags);
|
||||
|
||||
/* Tell the thread to start working */
|
||||
complete(&fsg->thread_notifier);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
module_init(fsg_init);
|
||||
|
||||
|
|
|
@ -1970,6 +1970,7 @@ MODULE_DEVICE_TABLE (pci, pci_ids);
|
|||
static struct pci_driver goku_pci_driver = {
|
||||
.name = (char *) driver_name,
|
||||
.id_table = pci_ids,
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
.probe = goku_probe,
|
||||
.remove = goku_remove,
|
||||
|
|
|
@ -2140,6 +2140,7 @@ static int lh7a40x_udc_remove(struct device *_dev)
|
|||
|
||||
static struct device_driver udc_driver = {
|
||||
.name = (char *)driver_name,
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = lh7a40x_udc_probe,
|
||||
.remove = lh7a40x_udc_remove
|
||||
|
|
|
@ -2948,6 +2948,7 @@ MODULE_DEVICE_TABLE (pci, pci_ids);
|
|||
static struct pci_driver net2280_pci_driver = {
|
||||
.name = (char *) driver_name,
|
||||
.id_table = pci_ids,
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
.probe = net2280_probe,
|
||||
.remove = net2280_remove,
|
||||
|
|
|
@ -691,7 +691,7 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req)
|
|||
}
|
||||
|
||||
static void
|
||||
finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status)
|
||||
finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one)
|
||||
{
|
||||
u16 count;
|
||||
|
||||
|
@ -699,6 +699,8 @@ finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status)
|
|||
ep->dma_counter = (u16) (req->req.dma + req->req.actual);
|
||||
count = dma_dest_len(ep, req->req.dma + req->req.actual);
|
||||
count += req->req.actual;
|
||||
if (one)
|
||||
count--;
|
||||
if (count <= req->req.length)
|
||||
req->req.actual = count;
|
||||
|
||||
|
@ -747,7 +749,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src)
|
|||
if (!list_empty(&ep->queue)) {
|
||||
req = container_of(ep->queue.next,
|
||||
struct omap_req, queue);
|
||||
finish_out_dma(ep, req, 0);
|
||||
finish_out_dma(ep, req, 0, dman_stat & UDC_DMA_RX_SB);
|
||||
}
|
||||
UDC_IRQ_SRC_REG = UDC_RXN_EOT;
|
||||
|
||||
|
@ -925,7 +927,7 @@ static void dma_channel_release(struct omap_ep *ep)
|
|||
while (UDC_RXDMA_CFG_REG & mask)
|
||||
udelay(10);
|
||||
if (req)
|
||||
finish_out_dma(ep, req, -ECONNRESET);
|
||||
finish_out_dma(ep, req, -ECONNRESET, 0);
|
||||
}
|
||||
omap_free_dma(ep->lch);
|
||||
ep->dma_channel = 0;
|
||||
|
@ -1786,8 +1788,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
|
|||
udc->driver->suspend(&udc->gadget);
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
if (udc->transceiver)
|
||||
otg_set_suspend(udc->transceiver, 1);
|
||||
} else {
|
||||
VDBG("resume\n");
|
||||
if (udc->transceiver)
|
||||
otg_set_suspend(udc->transceiver, 0);
|
||||
if (udc->gadget.speed == USB_SPEED_FULL
|
||||
&& udc->driver->resume) {
|
||||
spin_unlock(&udc->lock);
|
||||
|
@ -2943,6 +2949,7 @@ static int omap_udc_resume(struct device *dev)
|
|||
|
||||
static struct device_driver udc_driver = {
|
||||
.name = (char *) driver_name,
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = omap_udc_probe,
|
||||
.remove = __exit_p(omap_udc_remove),
|
||||
|
|
|
@ -2631,6 +2631,7 @@ static int pxa2xx_udc_resume(struct device *dev)
|
|||
|
||||
static struct device_driver udc_driver = {
|
||||
.name = "pxa2xx-udc",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = pxa2xx_udc_probe,
|
||||
.shutdown = pxa2xx_udc_shutdown,
|
||||
|
|
|
@ -1302,6 +1302,7 @@ static struct usb_gadget_driver zero_driver = {
|
|||
|
||||
.driver = {
|
||||
.name = (char *) shortname,
|
||||
.owner = THIS_MODULE,
|
||||
// .shutdown = ...
|
||||
// .suspend = ...
|
||||
// .resume = ...
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#
|
||||
# Makefile for USB Host Controller Driver
|
||||
# framework and drivers
|
||||
# Makefile for USB Host Controller Drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PCI) += pci-quirks.o
|
||||
|
||||
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
|
||||
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
|
||||
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
|
||||
|
|
|
@ -182,6 +182,9 @@ static int ehci_halt (struct ehci_hcd *ehci)
|
|||
{
|
||||
u32 temp = readl (&ehci->regs->status);
|
||||
|
||||
/* disable any irqs left enabled by previous code */
|
||||
writel (0, &ehci->regs->intr_enable);
|
||||
|
||||
if ((temp & STS_HALT) != 0)
|
||||
return 0;
|
||||
|
||||
|
@ -297,50 +300,17 @@ static void ehci_watchdog (unsigned long param)
|
|||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
|
||||
/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
|
||||
* off the controller (maybe it can boot from highspeed USB disks).
|
||||
/* Reboot notifiers kick in for silicon on any bus (not just pci, etc).
|
||||
* This forcibly disables dma and IRQs, helping kexec and other cases
|
||||
* where the next system software may expect clean state.
|
||||
*/
|
||||
static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
|
||||
|
||||
/* always say Linux will own the hardware */
|
||||
pci_write_config_byte(pdev, where + 3, 1);
|
||||
|
||||
/* maybe wait a while for BIOS to respond */
|
||||
if (cap & (1 << 16)) {
|
||||
int msec = 5000;
|
||||
|
||||
do {
|
||||
msleep(10);
|
||||
msec -= 10;
|
||||
pci_read_config_dword(pdev, where, &cap);
|
||||
} while ((cap & (1 << 16)) && msec);
|
||||
if (cap & (1 << 16)) {
|
||||
ehci_err(ehci, "BIOS handoff failed (%d, %08x)\n",
|
||||
where, cap);
|
||||
// some BIOS versions seem buggy...
|
||||
// return 1;
|
||||
ehci_warn (ehci, "continuing after BIOS bug...\n");
|
||||
/* disable all SMIs, and clear "BIOS owns" flag */
|
||||
pci_write_config_dword(pdev, where + 4, 0);
|
||||
pci_write_config_byte(pdev, where + 2, 0);
|
||||
} else
|
||||
ehci_dbg(ehci, "BIOS handoff succeeded\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int
|
||||
ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
|
||||
{
|
||||
struct ehci_hcd *ehci;
|
||||
|
||||
ehci = container_of (self, struct ehci_hcd, reboot_notifier);
|
||||
(void) ehci_halt (ehci);
|
||||
|
||||
/* make BIOS/etc use companion controller during reboot */
|
||||
writel (0, &ehci->regs->configured_flag);
|
||||
|
@ -363,156 +333,90 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
|
|||
msleep(20);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* called by khubd or root hub init threads */
|
||||
|
||||
static int ehci_hc_reset (struct usb_hcd *hcd)
|
||||
/*
|
||||
* ehci_work is called from some interrupts, timers, and so on.
|
||||
* it calls driver completion functions, after dropping ehci->lock.
|
||||
*/
|
||||
static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
u32 temp;
|
||||
unsigned count = 256/4;
|
||||
timer_action_done (ehci, TIMER_IO_WATCHDOG);
|
||||
if (ehci->reclaim_ready)
|
||||
end_unlink_async (ehci, regs);
|
||||
|
||||
spin_lock_init (&ehci->lock);
|
||||
/* another CPU may drop ehci->lock during a schedule scan while
|
||||
* it reports urb completions. this flag guards against bogus
|
||||
* attempts at re-entrant schedule scanning.
|
||||
*/
|
||||
if (ehci->scanning)
|
||||
return;
|
||||
ehci->scanning = 1;
|
||||
scan_async (ehci, regs);
|
||||
if (ehci->next_uframe != -1)
|
||||
scan_periodic (ehci, regs);
|
||||
ehci->scanning = 0;
|
||||
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH (readl (&ehci->caps->hc_capbase));
|
||||
dbg_hcs_params (ehci, "reset");
|
||||
dbg_hcc_params (ehci, "reset");
|
||||
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = readl (&ehci->caps->hcs_params);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
if (hcd->self.controller->bus == &pci_bus_type) {
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
|
||||
switch (pdev->vendor) {
|
||||
case PCI_VENDOR_ID_TDI:
|
||||
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
|
||||
ehci->is_tdi_rh_tt = 1;
|
||||
tdi_reset (ehci);
|
||||
}
|
||||
break;
|
||||
case PCI_VENDOR_ID_AMD:
|
||||
/* AMD8111 EHCI doesn't work, according to AMD errata */
|
||||
if (pdev->device == 0x7463) {
|
||||
ehci_info (ehci, "ignoring AMD8111 (errata)\n");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
case PCI_VENDOR_ID_NVIDIA:
|
||||
/* NVidia reports that certain chips don't handle
|
||||
* QH, ITD, or SITD addresses above 2GB. (But TD,
|
||||
* data buffer, and periodic schedule are normal.)
|
||||
*/
|
||||
switch (pdev->device) {
|
||||
case 0x003c: /* MCP04 */
|
||||
case 0x005b: /* CK804 */
|
||||
case 0x00d8: /* CK8 */
|
||||
case 0x00e8: /* CK8S */
|
||||
if (pci_set_consistent_dma_mask(pdev,
|
||||
DMA_31BIT_MASK) < 0)
|
||||
ehci_warn (ehci, "can't enable NVidia "
|
||||
"workaround for >2GB RAM\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* optional debug port, normally in the first BAR */
|
||||
temp = pci_find_capability (pdev, 0x0a);
|
||||
if (temp) {
|
||||
pci_read_config_dword(pdev, temp, &temp);
|
||||
temp >>= 16;
|
||||
if ((temp & (3 << 13)) == (1 << 13)) {
|
||||
temp &= 0x1fff;
|
||||
ehci->debug = hcd->regs + temp;
|
||||
temp = readl (&ehci->debug->control);
|
||||
ehci_info (ehci, "debug port %d%s\n",
|
||||
HCS_DEBUG_PORT(ehci->hcs_params),
|
||||
(temp & DBGP_ENABLED)
|
||||
? " IN USE"
|
||||
: "");
|
||||
if (!(temp & DBGP_ENABLED))
|
||||
ehci->debug = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
|
||||
} else
|
||||
temp = 0;
|
||||
|
||||
/* EHCI 0.96 and later may have "extended capabilities" */
|
||||
while (temp && count--) {
|
||||
u32 cap;
|
||||
|
||||
pci_read_config_dword (to_pci_dev(hcd->self.controller),
|
||||
temp, &cap);
|
||||
ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
|
||||
switch (cap & 0xff) {
|
||||
case 1: /* BIOS/SMM/... handoff */
|
||||
if (bios_handoff (ehci, temp, cap) != 0)
|
||||
return -EOPNOTSUPP;
|
||||
break;
|
||||
case 0: /* illegal reserved capability */
|
||||
ehci_warn (ehci, "illegal capability!\n");
|
||||
cap = 0;
|
||||
/* FALLTHROUGH */
|
||||
default: /* unknown */
|
||||
break;
|
||||
}
|
||||
temp = (cap >> 8) & 0xff;
|
||||
}
|
||||
if (!count) {
|
||||
ehci_err (ehci, "bogus capabilities ... PCI problems!\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (ehci_is_TDI(ehci))
|
||||
ehci_reset (ehci);
|
||||
#endif
|
||||
|
||||
ehci_port_power (ehci, 0);
|
||||
|
||||
/* at least the Genesys GL880S needs fixup here */
|
||||
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
|
||||
temp &= 0x0f;
|
||||
if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) {
|
||||
ehci_dbg (ehci, "bogus port configuration: "
|
||||
"cc=%d x pcc=%d < ports=%d\n",
|
||||
HCS_N_CC(ehci->hcs_params),
|
||||
HCS_N_PCC(ehci->hcs_params),
|
||||
HCS_N_PORTS(ehci->hcs_params));
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
if (hcd->self.controller->bus == &pci_bus_type) {
|
||||
struct pci_dev *pdev;
|
||||
|
||||
pdev = to_pci_dev(hcd->self.controller);
|
||||
switch (pdev->vendor) {
|
||||
case 0x17a0: /* GENESYS */
|
||||
/* GL880S: should be PORTS=2 */
|
||||
temp |= (ehci->hcs_params & ~0xf);
|
||||
ehci->hcs_params = temp;
|
||||
break;
|
||||
case PCI_VENDOR_ID_NVIDIA:
|
||||
/* NF4: should be PCC=10 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* force HC to halt state */
|
||||
return ehci_halt (ehci);
|
||||
/* the IO watchdog guards against hardware or driver bugs that
|
||||
* misplace IRQs, and should let us run completely without IRQs.
|
||||
* such lossage has been observed on both VT6202 and VT8235.
|
||||
*/
|
||||
if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) &&
|
||||
(ehci->async->qh_next.ptr != NULL ||
|
||||
ehci->periodic_sched != 0))
|
||||
timer_action (ehci, TIMER_IO_WATCHDOG);
|
||||
}
|
||||
|
||||
static int ehci_start (struct usb_hcd *hcd)
|
||||
static void ehci_stop (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
|
||||
ehci_dbg (ehci, "stop\n");
|
||||
|
||||
/* Turn off port power on all root hub ports. */
|
||||
ehci_port_power (ehci, 0);
|
||||
|
||||
/* no more interrupts ... */
|
||||
del_timer_sync (&ehci->watchdog);
|
||||
|
||||
spin_lock_irq(&ehci->lock);
|
||||
if (HC_IS_RUNNING (hcd->state))
|
||||
ehci_quiesce (ehci);
|
||||
|
||||
ehci_reset (ehci);
|
||||
writel (0, &ehci->regs->intr_enable);
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
|
||||
/* let companion controllers work when we aren't */
|
||||
writel (0, &ehci->regs->configured_flag);
|
||||
unregister_reboot_notifier (&ehci->reboot_notifier);
|
||||
|
||||
remove_debug_files (ehci);
|
||||
|
||||
/* root hub is shut down separately (first, when possible) */
|
||||
spin_lock_irq (&ehci->lock);
|
||||
if (ehci->async)
|
||||
ehci_work (ehci, NULL);
|
||||
spin_unlock_irq (&ehci->lock);
|
||||
ehci_mem_cleanup (ehci);
|
||||
|
||||
#ifdef EHCI_STATS
|
||||
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
|
||||
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
|
||||
ehci->stats.lost_iaa);
|
||||
ehci_dbg (ehci, "complete %ld unlink %ld\n",
|
||||
ehci->stats.complete, ehci->stats.unlink);
|
||||
#endif
|
||||
|
||||
dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
|
||||
}
|
||||
|
||||
static int ehci_run (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
u32 temp;
|
||||
int retval;
|
||||
u32 hcc_params;
|
||||
u8 sbrn = 0;
|
||||
int first;
|
||||
|
||||
/* skip some things on restart paths */
|
||||
|
@ -551,27 +455,6 @@ static int ehci_start (struct usb_hcd *hcd)
|
|||
}
|
||||
writel (ehci->periodic_dma, &ehci->regs->frame_list);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
if (hcd->self.controller->bus == &pci_bus_type) {
|
||||
struct pci_dev *pdev;
|
||||
u16 port_wake;
|
||||
|
||||
pdev = to_pci_dev(hcd->self.controller);
|
||||
|
||||
/* Serial Bus Release Number is at PCI 0x60 offset */
|
||||
pci_read_config_byte(pdev, 0x60, &sbrn);
|
||||
|
||||
/* port wake capability, reported by boot firmware */
|
||||
pci_read_config_word(pdev, 0x62, &port_wake);
|
||||
hcd->can_wakeup = (port_wake & 1) != 0;
|
||||
|
||||
/* help hc dma work well with cachelines */
|
||||
retval = pci_set_mwi(pdev);
|
||||
if (retval)
|
||||
ehci_dbg(ehci, "unable to enable MWI - not fatal.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* dedicate a qh for the async ring head, since we couldn't unlink
|
||||
* a 'real' qh without stopping the async schedule [4.8]. use it
|
||||
|
@ -667,7 +550,7 @@ static int ehci_start (struct usb_hcd *hcd)
|
|||
temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
|
||||
ehci_info (ehci,
|
||||
"USB %x.%x %s, EHCI %x.%02x, driver %s\n",
|
||||
((sbrn & 0xf0)>>4), (sbrn & 0x0f),
|
||||
((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
|
||||
first ? "initialized" : "restarted",
|
||||
temp >> 8, temp & 0xff, DRIVER_VERSION);
|
||||
|
||||
|
@ -679,188 +562,6 @@ static int ehci_start (struct usb_hcd *hcd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* always called by thread; normally rmmod */
|
||||
|
||||
static void ehci_stop (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
|
||||
ehci_dbg (ehci, "stop\n");
|
||||
|
||||
/* Turn off port power on all root hub ports. */
|
||||
ehci_port_power (ehci, 0);
|
||||
|
||||
/* no more interrupts ... */
|
||||
del_timer_sync (&ehci->watchdog);
|
||||
|
||||
spin_lock_irq(&ehci->lock);
|
||||
if (HC_IS_RUNNING (hcd->state))
|
||||
ehci_quiesce (ehci);
|
||||
|
||||
ehci_reset (ehci);
|
||||
writel (0, &ehci->regs->intr_enable);
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
|
||||
/* let companion controllers work when we aren't */
|
||||
writel (0, &ehci->regs->configured_flag);
|
||||
unregister_reboot_notifier (&ehci->reboot_notifier);
|
||||
|
||||
remove_debug_files (ehci);
|
||||
|
||||
/* root hub is shut down separately (first, when possible) */
|
||||
spin_lock_irq (&ehci->lock);
|
||||
if (ehci->async)
|
||||
ehci_work (ehci, NULL);
|
||||
spin_unlock_irq (&ehci->lock);
|
||||
ehci_mem_cleanup (ehci);
|
||||
|
||||
#ifdef EHCI_STATS
|
||||
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
|
||||
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
|
||||
ehci->stats.lost_iaa);
|
||||
ehci_dbg (ehci, "complete %ld unlink %ld\n",
|
||||
ehci->stats.complete, ehci->stats.unlink);
|
||||
#endif
|
||||
|
||||
dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
|
||||
}
|
||||
|
||||
static int ehci_get_frame (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* suspend/resume, section 4.3 */
|
||||
|
||||
/* These routines rely on the bus (pci, platform, etc)
|
||||
* to handle powerdown and wakeup, and currently also on
|
||||
* transceivers that don't need any software attention to set up
|
||||
* the right sort of wakeup.
|
||||
*/
|
||||
|
||||
static int ehci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
|
||||
if (time_before (jiffies, ehci->next_statechange))
|
||||
msleep (100);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
(void) usb_suspend_device (hcd->self.root_hub, message);
|
||||
#else
|
||||
usb_lock_device (hcd->self.root_hub);
|
||||
(void) ehci_hub_suspend (hcd);
|
||||
usb_unlock_device (hcd->self.root_hub);
|
||||
#endif
|
||||
|
||||
// save (PCI) FLADJ in case of Vaux power loss
|
||||
// ... we'd only use it to handle clock skew
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_resume (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
unsigned port;
|
||||
struct usb_device *root = hcd->self.root_hub;
|
||||
int retval = -EINVAL;
|
||||
|
||||
// maybe restore (PCI) FLADJ
|
||||
|
||||
if (time_before (jiffies, ehci->next_statechange))
|
||||
msleep (100);
|
||||
|
||||
/* If any port is suspended (or owned by the companion),
|
||||
* we know we can/must resume the HC (and mustn't reset it).
|
||||
*/
|
||||
for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) {
|
||||
u32 status;
|
||||
port--;
|
||||
status = readl (&ehci->regs->port_status [port]);
|
||||
if (!(status & PORT_POWER))
|
||||
continue;
|
||||
if (status & (PORT_SUSPEND | PORT_OWNER)) {
|
||||
down (&hcd->self.root_hub->serialize);
|
||||
retval = ehci_hub_resume (hcd);
|
||||
up (&hcd->self.root_hub->serialize);
|
||||
break;
|
||||
}
|
||||
if (!root->children [port])
|
||||
continue;
|
||||
dbg_port (ehci, __FUNCTION__, port + 1, status);
|
||||
usb_set_device_state (root->children[port],
|
||||
USB_STATE_NOTATTACHED);
|
||||
}
|
||||
|
||||
/* Else reset, to cope with power loss or flush-to-storage
|
||||
* style "resume" having activated BIOS during reboot.
|
||||
*/
|
||||
if (port == 0) {
|
||||
(void) ehci_halt (ehci);
|
||||
(void) ehci_reset (ehci);
|
||||
(void) ehci_hc_reset (hcd);
|
||||
|
||||
/* emptying the schedule aborts any urbs */
|
||||
spin_lock_irq (&ehci->lock);
|
||||
if (ehci->reclaim)
|
||||
ehci->reclaim_ready = 1;
|
||||
ehci_work (ehci, NULL);
|
||||
spin_unlock_irq (&ehci->lock);
|
||||
|
||||
/* restart; khubd will disconnect devices */
|
||||
retval = ehci_start (hcd);
|
||||
|
||||
/* here we "know" root ports should always stay powered;
|
||||
* but some controllers may lose all power.
|
||||
*/
|
||||
ehci_port_power (ehci, 1);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* ehci_work is called from some interrupts, timers, and so on.
|
||||
* it calls driver completion functions, after dropping ehci->lock.
|
||||
*/
|
||||
static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
|
||||
{
|
||||
timer_action_done (ehci, TIMER_IO_WATCHDOG);
|
||||
if (ehci->reclaim_ready)
|
||||
end_unlink_async (ehci, regs);
|
||||
|
||||
/* another CPU may drop ehci->lock during a schedule scan while
|
||||
* it reports urb completions. this flag guards against bogus
|
||||
* attempts at re-entrant schedule scanning.
|
||||
*/
|
||||
if (ehci->scanning)
|
||||
return;
|
||||
ehci->scanning = 1;
|
||||
scan_async (ehci, regs);
|
||||
if (ehci->next_uframe != -1)
|
||||
scan_periodic (ehci, regs);
|
||||
ehci->scanning = 0;
|
||||
|
||||
/* the IO watchdog guards against hardware or driver bugs that
|
||||
* misplace IRQs, and should let us run completely without IRQs.
|
||||
* such lossage has been observed on both VT6202 and VT8235.
|
||||
*/
|
||||
if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) &&
|
||||
(ehci->async->qh_next.ptr != NULL ||
|
||||
ehci->periodic_sched != 0))
|
||||
timer_action (ehci, TIMER_IO_WATCHDOG);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
|
||||
|
@ -1171,106 +872,24 @@ done:
|
|||
return;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static const struct hc_driver ehci_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "EHCI Host Controller",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ehci_hc_reset,
|
||||
.start = ehci_start,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ehci_suspend,
|
||||
.resume = ehci_resume,
|
||||
#endif
|
||||
.stop = ehci_stop,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.hub_suspend = ehci_hub_suspend,
|
||||
.hub_resume = ehci_hub_resume,
|
||||
};
|
||||
static int ehci_get_frame (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* EHCI 1.0 doesn't require PCI */
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
|
||||
/* PCI driver selection metadata; PCI hotplugging uses this */
|
||||
static const struct pci_device_id pci_ids [] = { {
|
||||
/* handle any USB 2.0 EHCI controller */
|
||||
PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0),
|
||||
.driver_data = (unsigned long) &ehci_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 ehci_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
|
||||
};
|
||||
|
||||
#endif /* PCI */
|
||||
|
||||
|
||||
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
|
||||
|
||||
MODULE_DESCRIPTION (DRIVER_INFO);
|
||||
MODULE_AUTHOR (DRIVER_AUTHOR);
|
||||
MODULE_LICENSE ("GPL");
|
||||
|
||||
static int __init init (void)
|
||||
{
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
#ifdef CONFIG_PCI
|
||||
#include "ehci-pci.c"
|
||||
#endif
|
||||
|
||||
pr_debug ("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
|
||||
hcd_name,
|
||||
sizeof (struct ehci_qh), sizeof (struct ehci_qtd),
|
||||
sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
|
||||
|
||||
return pci_register_driver (&ehci_pci_driver);
|
||||
}
|
||||
module_init (init);
|
||||
|
||||
static void __exit cleanup (void)
|
||||
{
|
||||
pci_unregister_driver (&ehci_pci_driver);
|
||||
}
|
||||
module_exit (cleanup);
|
||||
#if !defined(CONFIG_PCI)
|
||||
#error "missing bus glue for ehci-hcd"
|
||||
#endif
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ehci_hub_suspend (struct usb_hcd *hcd)
|
||||
static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
int port;
|
||||
|
@ -83,7 +83,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
|
|||
|
||||
|
||||
/* caller has locked the root hub, and should reset/reinit on error */
|
||||
static int ehci_hub_resume (struct usb_hcd *hcd)
|
||||
static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
u32 temp;
|
||||
|
@ -159,8 +159,8 @@ static int ehci_hub_resume (struct usb_hcd *hcd)
|
|||
|
||||
#else
|
||||
|
||||
#define ehci_hub_suspend NULL
|
||||
#define ehci_hub_resume NULL
|
||||
#define ehci_bus_suspend NULL
|
||||
#define ehci_bus_resume NULL
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* EHCI HCD (Host Controller Driver) PCI Bus Glue.
|
||||
*
|
||||
* Copyright (c) 2000-2004 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 CONFIG_PCI
|
||||
#error "This file is PCI bus glue. CONFIG_PCI must be defined."
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
|
||||
* off the controller (maybe it can boot from highspeed USB disks).
|
||||
*/
|
||||
static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
|
||||
|
||||
/* always say Linux will own the hardware */
|
||||
pci_write_config_byte(pdev, where + 3, 1);
|
||||
|
||||
/* maybe wait a while for BIOS to respond */
|
||||
if (cap & (1 << 16)) {
|
||||
int msec = 5000;
|
||||
|
||||
do {
|
||||
msleep(10);
|
||||
msec -= 10;
|
||||
pci_read_config_dword(pdev, where, &cap);
|
||||
} while ((cap & (1 << 16)) && msec);
|
||||
if (cap & (1 << 16)) {
|
||||
ehci_err(ehci, "BIOS handoff failed (%d, %08x)\n",
|
||||
where, cap);
|
||||
// some BIOS versions seem buggy...
|
||||
// return 1;
|
||||
ehci_warn (ehci, "continuing after BIOS bug...\n");
|
||||
/* disable all SMIs, and clear "BIOS owns" flag */
|
||||
pci_write_config_dword(pdev, where + 4, 0);
|
||||
pci_write_config_byte(pdev, where + 2, 0);
|
||||
} else
|
||||
ehci_dbg(ehci, "BIOS handoff succeeded\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called by khubd or root hub init threads */
|
||||
static int ehci_pci_reset (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
u32 temp;
|
||||
unsigned count = 256/4;
|
||||
|
||||
spin_lock_init (&ehci->lock);
|
||||
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH (readl (&ehci->caps->hc_capbase));
|
||||
dbg_hcs_params (ehci, "reset");
|
||||
dbg_hcc_params (ehci, "reset");
|
||||
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = readl (&ehci->caps->hcs_params);
|
||||
|
||||
if (hcd->self.controller->bus == &pci_bus_type) {
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
|
||||
switch (pdev->vendor) {
|
||||
case PCI_VENDOR_ID_TDI:
|
||||
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
|
||||
ehci->is_tdi_rh_tt = 1;
|
||||
tdi_reset (ehci);
|
||||
}
|
||||
break;
|
||||
case PCI_VENDOR_ID_AMD:
|
||||
/* AMD8111 EHCI doesn't work, according to AMD errata */
|
||||
if (pdev->device == 0x7463) {
|
||||
ehci_info (ehci, "ignoring AMD8111 (errata)\n");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
case PCI_VENDOR_ID_NVIDIA:
|
||||
/* NVidia reports that certain chips don't handle
|
||||
* QH, ITD, or SITD addresses above 2GB. (But TD,
|
||||
* data buffer, and periodic schedule are normal.)
|
||||
*/
|
||||
switch (pdev->device) {
|
||||
case 0x003c: /* MCP04 */
|
||||
case 0x005b: /* CK804 */
|
||||
case 0x00d8: /* CK8 */
|
||||
case 0x00e8: /* CK8S */
|
||||
if (pci_set_consistent_dma_mask(pdev,
|
||||
DMA_31BIT_MASK) < 0)
|
||||
ehci_warn (ehci, "can't enable NVidia "
|
||||
"workaround for >2GB RAM\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* optional debug port, normally in the first BAR */
|
||||
temp = pci_find_capability (pdev, 0x0a);
|
||||
if (temp) {
|
||||
pci_read_config_dword(pdev, temp, &temp);
|
||||
temp >>= 16;
|
||||
if ((temp & (3 << 13)) == (1 << 13)) {
|
||||
temp &= 0x1fff;
|
||||
ehci->debug = hcd->regs + temp;
|
||||
temp = readl (&ehci->debug->control);
|
||||
ehci_info (ehci, "debug port %d%s\n",
|
||||
HCS_DEBUG_PORT(ehci->hcs_params),
|
||||
(temp & DBGP_ENABLED)
|
||||
? " IN USE"
|
||||
: "");
|
||||
if (!(temp & DBGP_ENABLED))
|
||||
ehci->debug = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
|
||||
} else
|
||||
temp = 0;
|
||||
|
||||
/* EHCI 0.96 and later may have "extended capabilities" */
|
||||
while (temp && count--) {
|
||||
u32 cap;
|
||||
|
||||
pci_read_config_dword (to_pci_dev(hcd->self.controller),
|
||||
temp, &cap);
|
||||
ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
|
||||
switch (cap & 0xff) {
|
||||
case 1: /* BIOS/SMM/... handoff */
|
||||
if (bios_handoff (ehci, temp, cap) != 0)
|
||||
return -EOPNOTSUPP;
|
||||
break;
|
||||
case 0: /* illegal reserved capability */
|
||||
ehci_warn (ehci, "illegal capability!\n");
|
||||
cap = 0;
|
||||
/* FALLTHROUGH */
|
||||
default: /* unknown */
|
||||
break;
|
||||
}
|
||||
temp = (cap >> 8) & 0xff;
|
||||
}
|
||||
if (!count) {
|
||||
ehci_err (ehci, "bogus capabilities ... PCI problems!\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (ehci_is_TDI(ehci))
|
||||
ehci_reset (ehci);
|
||||
|
||||
ehci_port_power (ehci, 0);
|
||||
|
||||
/* at least the Genesys GL880S needs fixup here */
|
||||
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
|
||||
temp &= 0x0f;
|
||||
if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) {
|
||||
ehci_dbg (ehci, "bogus port configuration: "
|
||||
"cc=%d x pcc=%d < ports=%d\n",
|
||||
HCS_N_CC(ehci->hcs_params),
|
||||
HCS_N_PCC(ehci->hcs_params),
|
||||
HCS_N_PORTS(ehci->hcs_params));
|
||||
|
||||
if (hcd->self.controller->bus == &pci_bus_type) {
|
||||
struct pci_dev *pdev;
|
||||
|
||||
pdev = to_pci_dev(hcd->self.controller);
|
||||
switch (pdev->vendor) {
|
||||
case 0x17a0: /* GENESYS */
|
||||
/* GL880S: should be PORTS=2 */
|
||||
temp |= (ehci->hcs_params & ~0xf);
|
||||
ehci->hcs_params = temp;
|
||||
break;
|
||||
case PCI_VENDOR_ID_NVIDIA:
|
||||
/* NF4: should be PCC=10 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* force HC to halt state */
|
||||
return ehci_halt (ehci);
|
||||
}
|
||||
|
||||
static int ehci_pci_start (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
int result = 0;
|
||||
|
||||
if (hcd->self.controller->bus == &pci_bus_type) {
|
||||
struct pci_dev *pdev;
|
||||
u16 port_wake;
|
||||
|
||||
pdev = to_pci_dev(hcd->self.controller);
|
||||
|
||||
/* Serial Bus Release Number is at PCI 0x60 offset */
|
||||
pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
|
||||
|
||||
/* port wake capability, reported by boot firmware */
|
||||
pci_read_config_word(pdev, 0x62, &port_wake);
|
||||
hcd->can_wakeup = (port_wake & 1) != 0;
|
||||
|
||||
/* help hc dma work well with cachelines */
|
||||
result = pci_set_mwi(pdev);
|
||||
if (result)
|
||||
ehci_dbg(ehci, "unable to enable MWI - not fatal.\n");
|
||||
}
|
||||
|
||||
return ehci_run (hcd);
|
||||
}
|
||||
|
||||
/* always called by thread; normally rmmod */
|
||||
|
||||
static void ehci_pci_stop (struct usb_hcd *hcd)
|
||||
{
|
||||
ehci_stop (hcd);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* suspend/resume, section 4.3 */
|
||||
|
||||
/* These routines rely on the bus (pci, platform, etc)
|
||||
* to handle powerdown and wakeup, and currently also on
|
||||
* transceivers that don't need any software attention to set up
|
||||
* the right sort of wakeup.
|
||||
*/
|
||||
|
||||
static int ehci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
|
||||
if (time_before (jiffies, ehci->next_statechange))
|
||||
msleep (100);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
(void) usb_suspend_device (hcd->self.root_hub);
|
||||
#else
|
||||
usb_lock_device (hcd->self.root_hub);
|
||||
(void) ehci_bus_suspend (hcd);
|
||||
usb_unlock_device (hcd->self.root_hub);
|
||||
#endif
|
||||
|
||||
// save (PCI) FLADJ in case of Vaux power loss
|
||||
// ... we'd only use it to handle clock skew
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_pci_resume (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
unsigned port;
|
||||
struct usb_device *root = hcd->self.root_hub;
|
||||
int retval = -EINVAL;
|
||||
|
||||
// maybe restore (PCI) FLADJ
|
||||
|
||||
if (time_before (jiffies, ehci->next_statechange))
|
||||
msleep (100);
|
||||
|
||||
/* If any port is suspended (or owned by the companion),
|
||||
* we know we can/must resume the HC (and mustn't reset it).
|
||||
*/
|
||||
for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) {
|
||||
u32 status;
|
||||
port--;
|
||||
status = readl (&ehci->regs->port_status [port]);
|
||||
if (!(status & PORT_POWER))
|
||||
continue;
|
||||
if (status & (PORT_SUSPEND | PORT_OWNER)) {
|
||||
down (&hcd->self.root_hub->serialize);
|
||||
retval = ehci_bus_resume (hcd);
|
||||
up (&hcd->self.root_hub->serialize);
|
||||
break;
|
||||
}
|
||||
if (!root->children [port])
|
||||
continue;
|
||||
dbg_port (ehci, __FUNCTION__, port + 1, status);
|
||||
usb_set_device_state (root->children[port],
|
||||
USB_STATE_NOTATTACHED);
|
||||
}
|
||||
|
||||
/* Else reset, to cope with power loss or flush-to-storage
|
||||
* style "resume" having activated BIOS during reboot.
|
||||
*/
|
||||
if (port == 0) {
|
||||
(void) ehci_halt (ehci);
|
||||
(void) ehci_reset (ehci);
|
||||
(void) ehci_pci_reset (hcd);
|
||||
|
||||
/* emptying the schedule aborts any urbs */
|
||||
spin_lock_irq (&ehci->lock);
|
||||
if (ehci->reclaim)
|
||||
ehci->reclaim_ready = 1;
|
||||
ehci_work (ehci, NULL);
|
||||
spin_unlock_irq (&ehci->lock);
|
||||
|
||||
/* restart; khubd will disconnect devices */
|
||||
retval = ehci_run (hcd);
|
||||
|
||||
/* here we "know" root ports should always stay powered;
|
||||
* but some controllers may lose all power.
|
||||
*/
|
||||
ehci_port_power (ehci, 1);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct hc_driver ehci_pci_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "EHCI Host Controller",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ehci_pci_reset,
|
||||
.start = ehci_pci_start,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ehci_pci_suspend,
|
||||
.resume = ehci_pci_resume,
|
||||
#endif
|
||||
.stop = ehci_pci_stop,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* PCI driver selection metadata; PCI hotplugging uses this */
|
||||
static const struct pci_device_id pci_ids [] = { {
|
||||
/* handle any USB 2.0 EHCI controller */
|
||||
PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0),
|
||||
.driver_data = (unsigned long) &ehci_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 ehci_pci_driver = {
|
||||
.name = (char *) hcd_name,
|
||||
.id_table = pci_ids,
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
.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 ehci_hcd_pci_init (void)
|
||||
{
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
pr_debug ("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
|
||||
hcd_name,
|
||||
sizeof (struct ehci_qh), sizeof (struct ehci_qtd),
|
||||
sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
|
||||
|
||||
return pci_register_driver (&ehci_pci_driver);
|
||||
}
|
||||
module_init (ehci_hcd_pci_init);
|
||||
|
||||
static void __exit ehci_hcd_pci_cleanup (void)
|
||||
{
|
||||
pci_unregister_driver (&ehci_pci_driver);
|
||||
}
|
||||
module_exit (ehci_hcd_pci_cleanup);
|
|
@ -97,6 +97,7 @@ struct ehci_hcd { /* one per controller */
|
|||
#else
|
||||
# define COUNT(x) do {} while (0)
|
||||
#endif
|
||||
u8 sbrn; /* packed release number */
|
||||
};
|
||||
|
||||
/* convert between an HCD pointer and the corresponding EHCI_HCD */
|
||||
|
|
|
@ -638,7 +638,7 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
|
|||
+ msecs_to_jiffies(20) + 1);
|
||||
if (intstat & HCINT_RD) {
|
||||
DBG("---- remote wakeup\n");
|
||||
schedule_work(&isp116x->rh_resume);
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
irqstat &= ~HCuPINT_OPR;
|
||||
|
@ -1160,7 +1160,7 @@ static int isp116x_hub_control(struct usb_hcd *hcd,
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int isp116x_hub_suspend(struct usb_hcd *hcd)
|
||||
static int isp116x_bus_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
unsigned long flags;
|
||||
|
@ -1200,7 +1200,7 @@ static int isp116x_hub_suspend(struct usb_hcd *hcd)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int isp116x_hub_resume(struct usb_hcd *hcd)
|
||||
static int isp116x_bus_resume(struct usb_hcd *hcd)
|
||||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
u32 val;
|
||||
|
@ -1263,21 +1263,11 @@ static int isp116x_hub_resume(struct usb_hcd *hcd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void isp116x_rh_resume(void *_hcd)
|
||||
{
|
||||
struct usb_hcd *hcd = _hcd;
|
||||
|
||||
usb_resume_device(hcd->self.root_hub);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define isp116x_hub_suspend NULL
|
||||
#define isp116x_hub_resume NULL
|
||||
|
||||
static void isp116x_rh_resume(void *_hcd)
|
||||
{
|
||||
}
|
||||
#define isp116x_bus_suspend NULL
|
||||
#define isp116x_bus_resume NULL
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1636,8 +1626,8 @@ static struct hc_driver isp116x_hc_driver = {
|
|||
|
||||
.hub_status_data = isp116x_hub_status_data,
|
||||
.hub_control = isp116x_hub_control,
|
||||
.hub_suspend = isp116x_hub_suspend,
|
||||
.hub_resume = isp116x_hub_resume,
|
||||
.bus_suspend = isp116x_bus_suspend,
|
||||
.bus_resume = isp116x_bus_resume,
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
@ -1732,7 +1722,6 @@ static int __init isp116x_probe(struct device *dev)
|
|||
isp116x->addr_reg = addr_reg;
|
||||
spin_lock_init(&isp116x->lock);
|
||||
INIT_LIST_HEAD(&isp116x->async);
|
||||
INIT_WORK(&isp116x->rh_resume, isp116x_rh_resume, hcd);
|
||||
isp116x->board = dev->platform_data;
|
||||
|
||||
if (!isp116x->board) {
|
||||
|
@ -1777,16 +1766,10 @@ static int __init isp116x_probe(struct device *dev)
|
|||
static int isp116x_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
int ret = 0;
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
|
||||
VDBG("%s: state %x\n", __func__, state);
|
||||
|
||||
ret = usb_suspend_device(hcd->self.root_hub, state);
|
||||
if (!ret) {
|
||||
dev->power.power_state = state;
|
||||
INFO("%s suspended\n", hcd_name);
|
||||
} else
|
||||
ERR("%s suspend failed\n", hcd_name);
|
||||
dev->power.power_state = state;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1797,15 +1780,11 @@ static int isp116x_suspend(struct device *dev, pm_message_t state)
|
|||
static int isp116x_resume(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
|
||||
VDBG("%s: state %x\n", __func__, dev->power.power_state);
|
||||
|
||||
ret = usb_resume_device(hcd->self.root_hub);
|
||||
if (!ret) {
|
||||
dev->power.power_state = PMSG_ON;
|
||||
VDBG("%s resumed\n", (char *)hcd_name);
|
||||
}
|
||||
dev->power.power_state = PMSG_ON;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -253,7 +253,6 @@ static const int cc_to_error[16] = {
|
|||
|
||||
struct isp116x {
|
||||
spinlock_t lock;
|
||||
struct work_struct rh_resume;
|
||||
|
||||
void __iomem *addr_reg;
|
||||
void __iomem *data_reg;
|
||||
|
|
|
@ -214,6 +214,11 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
|
|||
*/
|
||||
.hub_status_data = ohci_hub_status_data,
|
||||
.hub_control = ohci_hub_control,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -259,6 +264,7 @@ static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
|
|||
|
||||
static struct device_driver ohci_hcd_au1xxx_driver = {
|
||||
.name = "au1xxx-ohci",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = ohci_hcd_au1xxx_drv_probe,
|
||||
.remove = ohci_hcd_au1xxx_drv_remove,
|
||||
|
|
|
@ -193,10 +193,6 @@ ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
|
|||
|
||||
maybe_print_eds (controller, "donehead",
|
||||
ohci_readl (controller, ®s->donehead), next, size);
|
||||
|
||||
/* broken fminterval means traffic won't flow! */
|
||||
ohci_dbg (controller, "fminterval %08x\n",
|
||||
ohci_readl (controller, ®s->fminterval));
|
||||
}
|
||||
|
||||
#define dbg_port_sw(hc,num,value,next,size) \
|
||||
|
|
|
@ -723,7 +723,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
|
|||
ohci_vdbg (ohci, "resume detect\n");
|
||||
ohci_writel (ohci, OHCI_INTR_RD, ®s->intrstatus);
|
||||
if (hcd->state != HC_STATE_QUIESCING)
|
||||
schedule_work(&ohci->rh_resume);
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
}
|
||||
|
||||
if (ints & OHCI_INTR_WDH) {
|
||||
|
@ -791,7 +791,7 @@ static void ohci_stop (struct usb_hcd *hcd)
|
|||
|
||||
/* must not be called from interrupt context */
|
||||
|
||||
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ohci_restart (struct ohci_hcd *ohci)
|
||||
{
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#define OHCI_SCHED_ENABLES \
|
||||
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
|
||||
|
@ -45,7 +45,7 @@ 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)
|
||||
static int ohci_bus_suspend (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
int status = 0;
|
||||
|
@ -73,7 +73,6 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
|
|||
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;
|
||||
|
||||
|
@ -108,7 +107,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
|
|||
else
|
||||
ohci->hc_control &= ~OHCI_CTRL_RWE;
|
||||
|
||||
/* Suspend hub */
|
||||
/* Suspend hub ... this is the "global (to this bus) suspend" mode,
|
||||
* which doesn't imply ports will first be individually suspended.
|
||||
*/
|
||||
ohci->hc_control &= ~OHCI_CTRL_HCFS;
|
||||
ohci->hc_control |= OHCI_USB_SUSPEND;
|
||||
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
|
||||
|
@ -118,8 +119,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
|
|||
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
|
||||
|
||||
done:
|
||||
/* external suspend vs self autosuspend ... same effect */
|
||||
if (status == 0)
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
usb_hcd_suspend_root_hub(hcd);
|
||||
spin_unlock_irqrestore (&ohci->lock, flags);
|
||||
return status;
|
||||
}
|
||||
|
@ -133,7 +135,7 @@ static inline struct ed *find_head (struct ed *ed)
|
|||
}
|
||||
|
||||
/* caller has locked the root hub */
|
||||
static int ohci_hub_resume (struct usb_hcd *hcd)
|
||||
static int ohci_bus_resume (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
u32 temp, enables;
|
||||
|
@ -146,7 +148,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
|
|||
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 */
|
||||
/* this can happen after resuming a swsusp snapshot */
|
||||
if (hcd->state == HC_STATE_RESUMING) {
|
||||
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
|
||||
ohci->hc_control);
|
||||
|
@ -169,11 +171,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
|
|||
ohci_info (ohci, "wakeup\n");
|
||||
break;
|
||||
case OHCI_USB_OPER:
|
||||
ohci_dbg (ohci, "already resumed\n");
|
||||
status = 0;
|
||||
/* this can happen after resuming a swsusp snapshot */
|
||||
ohci_dbg (ohci, "snapshot resume? reinit\n");
|
||||
status = -EBUSY;
|
||||
break;
|
||||
default: /* RESET, we lost power */
|
||||
ohci_dbg (ohci, "root hub hardware reset\n");
|
||||
ohci_dbg (ohci, "lost power\n");
|
||||
status = -EBUSY;
|
||||
}
|
||||
spin_unlock_irq (&ohci->lock);
|
||||
|
@ -198,8 +201,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
|
|||
}
|
||||
|
||||
/* Some controllers (lucent erratum) need extra-long delays */
|
||||
hcd->state = HC_STATE_RESUMING;
|
||||
mdelay (20 /* usb 11.5.1.10 */ + 15);
|
||||
msleep (20 /* usb 11.5.1.10 */ + 12 /* 32 msec counter */ + 1);
|
||||
|
||||
temp = ohci_readl (ohci, &ohci->regs->control);
|
||||
temp &= OHCI_CTRL_HCFS;
|
||||
|
@ -273,28 +275,10 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
|
|||
(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 */
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -367,7 +351,6 @@ done:
|
|||
#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
|
||||
|
@ -379,8 +362,7 @@ done:
|
|||
&& usb_trylock_device (hcd->self.root_hub)
|
||||
) {
|
||||
ohci_vdbg (ohci, "autosuspend\n");
|
||||
(void) ohci_hub_suspend (hcd);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
(void) ohci_bus_suspend (hcd);
|
||||
usb_unlock_device (hcd->self.root_hub);
|
||||
}
|
||||
#endif
|
||||
|
@ -554,7 +536,7 @@ static int ohci_hub_control (
|
|||
temp = RH_PS_POCI;
|
||||
if ((ohci->hc_control & OHCI_CTRL_HCFS)
|
||||
!= OHCI_USB_OPER)
|
||||
schedule_work (&ohci->rh_resume);
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
break;
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
temp = RH_PS_PSSC;
|
||||
|
|
|
@ -193,6 +193,11 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
|
|||
*/
|
||||
.hub_status_data = ohci_hub_status_data,
|
||||
.hub_control = ohci_hub_control,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -239,6 +244,7 @@ static int ohci_hcd_lh7a404_drv_resume(struct device *dev)
|
|||
|
||||
static struct device_driver ohci_hcd_lh7a404_driver = {
|
||||
.name = "lh7a404-ohci",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = ohci_hcd_lh7a404_drv_probe,
|
||||
.remove = ohci_hcd_lh7a404_drv_remove,
|
||||
|
|
|
@ -28,7 +28,6 @@ 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));
|
||||
ohci->reboot_notifier.notifier_call = ohci_reboot;
|
||||
}
|
||||
|
||||
|
|
|
@ -420,9 +420,9 @@ static const struct hc_driver ohci_omap_hc_driver = {
|
|||
*/
|
||||
.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,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
@ -458,41 +458,29 @@ static int ohci_hcd_omap_drv_remove(struct device *dev)
|
|||
static int ohci_omap_suspend(struct device *dev, pm_message_t message)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
|
||||
int status = -EINVAL;
|
||||
|
||||
down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
|
||||
status = ohci_hub_suspend(ohci_to_hcd(ohci));
|
||||
if (status == 0) {
|
||||
omap_ohci_clock_power(0);
|
||||
ohci_to_hcd(ohci)->self.root_hub->state =
|
||||
USB_STATE_SUSPENDED;
|
||||
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
|
||||
dev->power.power_state = PMSG_SUSPEND;
|
||||
}
|
||||
up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
|
||||
return status;
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
omap_ohci_clock_power(0);
|
||||
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
|
||||
dev->power.power_state = PMSG_SUSPEND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ohci_omap_resume(struct device *dev)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
|
||||
int status = 0;
|
||||
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
omap_ohci_clock_power(1);
|
||||
#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 = PMSG_ON;
|
||||
return status;
|
||||
dev->power.power_state = PMSG_ON;
|
||||
usb_hcd_resume_root_hub(dev_get_drvdata(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -504,6 +492,7 @@ static int ohci_omap_resume(struct device *dev)
|
|||
*/
|
||||
static struct device_driver ohci_hcd_omap_driver = {
|
||||
.name = "ohci",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = ohci_hcd_omap_drv_probe,
|
||||
.remove = ohci_hcd_omap_drv_remove,
|
||||
|
|
|
@ -112,23 +112,13 @@ ohci_pci_start (struct usb_hcd *hcd)
|
|||
|
||||
static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
/* root hub was already suspended */
|
||||
|
||||
/* 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, message);
|
||||
#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);
|
||||
|
||||
/* FIXME these PMAC things get called in the wrong places. ASIC
|
||||
* clocks should be turned off AFTER entering D3, and on BEFORE
|
||||
* trying to enter D0. Evidently the PCI layer doesn't currently
|
||||
* provide the right sort of platform hooks for this ...
|
||||
*/
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
if (_machine == _MACH_Pmac) {
|
||||
struct device_node *of_node;
|
||||
|
@ -145,9 +135,6 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
|||
|
||||
static int ohci_pci_resume (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
int retval = 0;
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
if (_machine == _MACH_Pmac) {
|
||||
struct device_node *of_node;
|
||||
|
@ -159,19 +146,8 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
|
|||
}
|
||||
#endif /* CONFIG_PPC_PMAC */
|
||||
|
||||
/* 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;
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
@ -218,9 +194,9 @@ static const struct hc_driver ohci_pci_hc_driver = {
|
|||
*/
|
||||
.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,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
@ -240,6 +216,7 @@ MODULE_DEVICE_TABLE (pci, pci_ids);
|
|||
static struct pci_driver ohci_pci_driver = {
|
||||
.name = (char *) hcd_name,
|
||||
.id_table = pci_ids,
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
.probe = usb_hcd_pci_probe,
|
||||
.remove = usb_hcd_pci_remove,
|
||||
|
|
|
@ -163,9 +163,9 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
|
|||
*/
|
||||
.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,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
@ -193,10 +193,11 @@ static int ohci_hcd_ppc_soc_drv_remove(struct device *dev)
|
|||
|
||||
static struct device_driver ohci_hcd_ppc_soc_driver = {
|
||||
.name = "ppc-soc-ohci",
|
||||
.owner = THIS_MODULE,
|
||||
.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)
|
||||
#ifdef CONFIG_PM
|
||||
/*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/
|
||||
/*.resume = ohci_hcd_ppc_soc_drv_resume,*/
|
||||
#endif
|
||||
|
|
|
@ -278,10 +278,11 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
|
|||
*/
|
||||
.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,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
|
@ -448,11 +448,11 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
|
|||
*/
|
||||
.hub_status_data = ohci_s3c2410_hub_status_data,
|
||||
.hub_control = ohci_s3c2410_hub_control,
|
||||
|
||||
#if defined(CONFIG_USB_SUSPEND) && 0
|
||||
.hub_suspend = ohci_hub_suspend,
|
||||
.hub_resume = ohci_hub_resume,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
/* device driver */
|
||||
|
@ -474,6 +474,7 @@ static int ohci_hcd_s3c2410_drv_remove(struct device *dev)
|
|||
|
||||
static struct device_driver ohci_hcd_s3c2410_driver = {
|
||||
.name = "s3c2410-ohci",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &platform_bus_type,
|
||||
.probe = ohci_hcd_s3c2410_drv_probe,
|
||||
.remove = ohci_hcd_s3c2410_drv_remove,
|
||||
|
|
|
@ -235,10 +235,11 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
|
|||
*/
|
||||
.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,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
|
@ -389,7 +389,6 @@ struct ohci_hcd {
|
|||
unsigned long next_statechange; /* suspend/resume */
|
||||
u32 fminterval; /* saved register */
|
||||
|
||||
struct work_struct rh_resume;
|
||||
struct notifier_block reboot_notifier;
|
||||
|
||||
unsigned long flags; /* for HC bugs */
|
||||
|
|
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
* This file contains code to reset and initialize USB host controllers.
|
||||
* Some of it includes work-arounds for PCI hardware and BIOS quirks.
|
||||
* It may need to run early during booting -- before USB would normally
|
||||
* initialize -- to ensure that Linux doesn't use any legacy modes.
|
||||
*
|
||||
* Copyright (c) 1999 Martin Mares <mj@ucw.cz>
|
||||
* (and others)
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
#define DEBUG
|
||||
#else
|
||||
#undef DEBUG
|
||||
#endif
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
|
||||
#define UHCI_USBLEGSUP 0xc0 /* legacy support */
|
||||
#define UHCI_USBCMD 0 /* command register */
|
||||
#define UHCI_USBINTR 4 /* interrupt register */
|
||||
#define UHCI_USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
|
||||
#define UHCI_USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
|
||||
#define UHCI_USBCMD_RUN 0x0001 /* RUN/STOP bit */
|
||||
#define UHCI_USBCMD_HCRESET 0x0002 /* Host Controller reset */
|
||||
#define UHCI_USBCMD_EGSM 0x0008 /* Global Suspend Mode */
|
||||
#define UHCI_USBCMD_CONFIGURE 0x0040 /* Config Flag */
|
||||
#define UHCI_USBINTR_RESUME 0x0002 /* Resume interrupt enable */
|
||||
|
||||
#define OHCI_CONTROL 0x04
|
||||
#define OHCI_CMDSTATUS 0x08
|
||||
#define OHCI_INTRSTATUS 0x0c
|
||||
#define OHCI_INTRENABLE 0x10
|
||||
#define OHCI_INTRDISABLE 0x14
|
||||
#define OHCI_OCR (1 << 3) /* ownership change request */
|
||||
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
|
||||
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
|
||||
#define OHCI_INTR_OC (1 << 30) /* ownership change */
|
||||
|
||||
#define EHCI_HCC_PARAMS 0x08 /* extended capabilities */
|
||||
#define EHCI_USBCMD 0 /* command register */
|
||||
#define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
|
||||
#define EHCI_USBSTS 4 /* status register */
|
||||
#define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */
|
||||
#define EHCI_USBINTR 8 /* interrupt register */
|
||||
#define EHCI_USBLEGSUP 0 /* legacy support register */
|
||||
#define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */
|
||||
#define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */
|
||||
#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
|
||||
#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */
|
||||
|
||||
|
||||
/*
|
||||
* Make sure the controller is completely inactive, unable to
|
||||
* generate interrupts or do DMA.
|
||||
*/
|
||||
void uhci_reset_hc(struct pci_dev *pdev, unsigned long base)
|
||||
{
|
||||
/* Turn off PIRQ enable and SMI enable. (This also turns off the
|
||||
* BIOS's USB Legacy Support.) Turn off all the R/WC bits too.
|
||||
*/
|
||||
pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_RWC);
|
||||
|
||||
/* 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(UHCI_USBCMD_HCRESET, base + UHCI_USBCMD);
|
||||
mb();
|
||||
udelay(5);
|
||||
if (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET)
|
||||
dev_warn(&pdev->dev, "HCRESET not completed yet!\n");
|
||||
|
||||
/* Just to be safe, disable interrupt requests and
|
||||
* make sure the controller is stopped.
|
||||
*/
|
||||
outw(0, base + UHCI_USBINTR);
|
||||
outw(0, base + UHCI_USBCMD);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uhci_reset_hc);
|
||||
|
||||
/*
|
||||
* Initialize a controller that was newly discovered or has just been
|
||||
* resumed. In either case we can't be sure of its previous state.
|
||||
*
|
||||
* Returns: 1 if the controller was reset, 0 otherwise.
|
||||
*/
|
||||
int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
|
||||
{
|
||||
u16 legsup;
|
||||
unsigned int cmd, intr;
|
||||
|
||||
/*
|
||||
* When restarting a suspended controller, we expect all the
|
||||
* settings to be the same as we left them:
|
||||
*
|
||||
* PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
|
||||
* Controller is stopped and configured with EGSM set;
|
||||
* No interrupts enabled except possibly Resume Detect.
|
||||
*
|
||||
* If any of these conditions are violated we do a complete reset.
|
||||
*/
|
||||
pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup);
|
||||
if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) {
|
||||
dev_dbg(&pdev->dev, "%s: legsup = 0x%04x\n",
|
||||
__FUNCTION__, legsup);
|
||||
goto reset_needed;
|
||||
}
|
||||
|
||||
cmd = inw(base + UHCI_USBCMD);
|
||||
if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) ||
|
||||
!(cmd & UHCI_USBCMD_EGSM)) {
|
||||
dev_dbg(&pdev->dev, "%s: cmd = 0x%04x\n",
|
||||
__FUNCTION__, cmd);
|
||||
goto reset_needed;
|
||||
}
|
||||
|
||||
intr = inw(base + UHCI_USBINTR);
|
||||
if (intr & (~UHCI_USBINTR_RESUME)) {
|
||||
dev_dbg(&pdev->dev, "%s: intr = 0x%04x\n",
|
||||
__FUNCTION__, intr);
|
||||
goto reset_needed;
|
||||
}
|
||||
return 0;
|
||||
|
||||
reset_needed:
|
||||
dev_dbg(&pdev->dev, "Performing full reset\n");
|
||||
uhci_reset_hc(pdev, base);
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
|
||||
|
||||
static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
|
||||
{
|
||||
unsigned long base = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_ROM_RESOURCE; i++)
|
||||
if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
|
||||
base = pci_resource_start(pdev, i);
|
||||
break;
|
||||
}
|
||||
|
||||
if (base)
|
||||
uhci_check_and_reset_hc(pdev, base);
|
||||
}
|
||||
|
||||
static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
int wait_time;
|
||||
u32 control;
|
||||
|
||||
base = ioremap_nocache(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
if (base == NULL) return;
|
||||
|
||||
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
|
||||
#ifndef __hppa__
|
||||
control = readl(base + OHCI_CONTROL);
|
||||
if (control & OHCI_CTRL_IR) {
|
||||
wait_time = 500; /* arbitrary; 5 seconds */
|
||||
writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);
|
||||
writel(OHCI_OCR, base + OHCI_CMDSTATUS);
|
||||
while (wait_time > 0 &&
|
||||
readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
|
||||
wait_time -= 10;
|
||||
msleep(10);
|
||||
}
|
||||
if (wait_time <= 0)
|
||||
printk(KERN_WARNING "%s %s: early BIOS handoff "
|
||||
"failed (BIOS bug ?)\n",
|
||||
pdev->dev.bus_id, "OHCI");
|
||||
|
||||
/* reset controller, preserving RWC */
|
||||
writel(control & OHCI_CTRL_RWC, base + OHCI_CONTROL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* disable interrupts
|
||||
*/
|
||||
writel(~(u32)0, base + OHCI_INTRDISABLE);
|
||||
writel(~(u32)0, base + OHCI_INTRSTATUS);
|
||||
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
|
||||
{
|
||||
int wait_time, delta;
|
||||
void __iomem *base, *op_reg_base;
|
||||
u32 hcc_params, val, temp;
|
||||
u8 cap_length;
|
||||
|
||||
base = ioremap_nocache(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
if (base == NULL) return;
|
||||
|
||||
cap_length = readb(base);
|
||||
op_reg_base = base + cap_length;
|
||||
hcc_params = readl(base + EHCI_HCC_PARAMS);
|
||||
hcc_params = (hcc_params >> 8) & 0xff;
|
||||
if (hcc_params) {
|
||||
pci_read_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGSUP,
|
||||
&val);
|
||||
if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) {
|
||||
/*
|
||||
* Ok, BIOS is in smm mode, try to hand off...
|
||||
*/
|
||||
pci_read_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGCTLSTS,
|
||||
&temp);
|
||||
pci_write_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGCTLSTS,
|
||||
temp | EHCI_USBLEGCTLSTS_SOOE);
|
||||
val |= EHCI_USBLEGSUP_OS;
|
||||
pci_write_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGSUP,
|
||||
val);
|
||||
|
||||
wait_time = 500;
|
||||
do {
|
||||
msleep(10);
|
||||
wait_time -= 10;
|
||||
pci_read_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGSUP,
|
||||
&val);
|
||||
} while (wait_time && (val & EHCI_USBLEGSUP_BIOS));
|
||||
if (!wait_time) {
|
||||
/*
|
||||
* well, possibly buggy BIOS...
|
||||
*/
|
||||
printk(KERN_WARNING "%s %s: early BIOS handoff "
|
||||
"failed (BIOS bug ?)\n",
|
||||
pdev->dev.bus_id, "EHCI");
|
||||
pci_write_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGSUP,
|
||||
EHCI_USBLEGSUP_OS);
|
||||
pci_write_config_dword(pdev,
|
||||
hcc_params + EHCI_USBLEGCTLSTS,
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* halt EHCI & disable its interrupts in any case
|
||||
*/
|
||||
val = readl(op_reg_base + EHCI_USBSTS);
|
||||
if ((val & EHCI_USBSTS_HALTED) == 0) {
|
||||
val = readl(op_reg_base + EHCI_USBCMD);
|
||||
val &= ~EHCI_USBCMD_RUN;
|
||||
writel(val, op_reg_base + EHCI_USBCMD);
|
||||
|
||||
wait_time = 2000;
|
||||
delta = 100;
|
||||
do {
|
||||
writel(0x3f, op_reg_base + EHCI_USBSTS);
|
||||
udelay(delta);
|
||||
wait_time -= delta;
|
||||
val = readl(op_reg_base + EHCI_USBSTS);
|
||||
if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) {
|
||||
break;
|
||||
}
|
||||
} while (wait_time > 0);
|
||||
}
|
||||
writel(0, op_reg_base + EHCI_USBINTR);
|
||||
writel(0x3f, op_reg_base + EHCI_USBSTS);
|
||||
|
||||
iounmap(base);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
|
||||
{
|
||||
if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI)
|
||||
quirk_usb_handoff_uhci(pdev);
|
||||
else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI)
|
||||
quirk_usb_handoff_ohci(pdev);
|
||||
else if (pdev->class == PCI_CLASS_SERIAL_USB_EHCI)
|
||||
quirk_usb_disable_ehci(pdev);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff);
|
|
@ -1363,7 +1363,7 @@ error:
|
|||
#ifdef CONFIG_PM
|
||||
|
||||
static int
|
||||
sl811h_hub_suspend(struct usb_hcd *hcd)
|
||||
sl811h_bus_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
// SOFs off
|
||||
DBG("%s\n", __FUNCTION__);
|
||||
|
@ -1371,7 +1371,7 @@ sl811h_hub_suspend(struct usb_hcd *hcd)
|
|||
}
|
||||
|
||||
static int
|
||||
sl811h_hub_resume(struct usb_hcd *hcd)
|
||||
sl811h_bus_resume(struct usb_hcd *hcd)
|
||||
{
|
||||
// SOFs on
|
||||
DBG("%s\n", __FUNCTION__);
|
||||
|
@ -1380,8 +1380,8 @@ sl811h_hub_resume(struct usb_hcd *hcd)
|
|||
|
||||
#else
|
||||
|
||||
#define sl811h_hub_suspend NULL
|
||||
#define sl811h_hub_resume NULL
|
||||
#define sl811h_bus_suspend NULL
|
||||
#define sl811h_bus_resume NULL
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1623,8 +1623,8 @@ static struct hc_driver sl811h_hc_driver = {
|
|||
*/
|
||||
.hub_status_data = sl811h_hub_status_data,
|
||||
.hub_control = sl811h_hub_control,
|
||||
.hub_suspend = sl811h_hub_suspend,
|
||||
.hub_resume = sl811h_hub_resume,
|
||||
.bus_suspend = sl811h_bus_suspend,
|
||||
.bus_resume = sl811h_bus_resume,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -1791,7 +1791,7 @@ sl811h_suspend(struct device *dev, pm_message_t state)
|
|||
int retval = 0;
|
||||
|
||||
if (state.event == PM_EVENT_FREEZE)
|
||||
retval = sl811h_hub_suspend(hcd);
|
||||
retval = sl811h_bus_suspend(hcd);
|
||||
else if (state.event == PM_EVENT_SUSPEND)
|
||||
port_power(sl811, 0);
|
||||
if (retval == 0)
|
||||
|
@ -1816,7 +1816,7 @@ sl811h_resume(struct device *dev)
|
|||
}
|
||||
|
||||
dev->power.power_state = PMSG_ON;
|
||||
return sl811h_hub_resume(hcd);
|
||||
return sl811h_bus_resume(hcd);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -1831,6 +1831,7 @@ sl811h_resume(struct device *dev)
|
|||
struct device_driver sl811h_driver = {
|
||||
.name = (char *) hcd_name,
|
||||
.bus = &platform_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
.probe = sl811h_probe,
|
||||
.remove = __devexit_p(sl811h_remove),
|
||||
|
|
|
@ -348,7 +348,6 @@ static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *bu
|
|||
|
||||
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;
|
||||
|
@ -446,11 +445,11 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
|
|||
out += sprintf(out, "Frame List\n");
|
||||
for (i = 0; i < UHCI_NUMFRAMES; ++i) {
|
||||
int shown = 0;
|
||||
td = uhci->fl->frame_cpu[i];
|
||||
td = uhci->frame_cpu[i];
|
||||
if (!td)
|
||||
continue;
|
||||
|
||||
if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
|
||||
if (td->dma_handle != (dma_addr_t)uhci->frame[i]) {
|
||||
show_frame_num();
|
||||
out += sprintf(out, " frame list does not match td->dma_handle!\n");
|
||||
}
|
||||
|
|
|
@ -101,37 +101,16 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
|
|||
#include "uhci-q.c"
|
||||
#include "uhci-hub.c"
|
||||
|
||||
extern void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
|
||||
extern int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
|
||||
|
||||
/*
|
||||
* Make sure the controller is completely inactive, unable to
|
||||
* generate interrupts or do DMA.
|
||||
* Finish up a host controller reset and update the recorded state.
|
||||
*/
|
||||
static void reset_hc(struct uhci_hcd *uhci)
|
||||
static void finish_reset(struct uhci_hcd *uhci)
|
||||
{
|
||||
int port;
|
||||
|
||||
/* Turn off PIRQ enable and SMI enable. (This also turns off the
|
||||
* BIOS's USB Legacy Support.) Turn off all the R/WC bits too.
|
||||
*/
|
||||
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
|
||||
USBLEGSUP_RWC);
|
||||
|
||||
/* 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, uhci->io_addr + USBCMD);
|
||||
mb();
|
||||
udelay(5);
|
||||
if (inw(uhci->io_addr + USBCMD) & USBCMD_HCRESET)
|
||||
dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n");
|
||||
|
||||
/* Just to be safe, disable interrupt requests and
|
||||
* make sure the controller is stopped.
|
||||
*/
|
||||
outw(0, uhci->io_addr + USBINTR);
|
||||
outw(0, uhci->io_addr + USBCMD);
|
||||
|
||||
/* HCRESET doesn't affect the Suspend, Reset, and Resume Detect
|
||||
* bits in the port status and control registers.
|
||||
* We have to clear them by hand.
|
||||
|
@ -153,7 +132,8 @@ static void reset_hc(struct uhci_hcd *uhci)
|
|||
*/
|
||||
static void hc_died(struct uhci_hcd *uhci)
|
||||
{
|
||||
reset_hc(uhci);
|
||||
uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr);
|
||||
finish_reset(uhci);
|
||||
uhci->hc_inaccessible = 1;
|
||||
}
|
||||
|
||||
|
@ -163,44 +143,8 @@ static void hc_died(struct uhci_hcd *uhci)
|
|||
*/
|
||||
static void check_and_reset_hc(struct uhci_hcd *uhci)
|
||||
{
|
||||
u16 legsup;
|
||||
unsigned int cmd, intr;
|
||||
|
||||
/*
|
||||
* When restarting a suspended controller, we expect all the
|
||||
* settings to be the same as we left them:
|
||||
*
|
||||
* PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
|
||||
* Controller is stopped and configured with EGSM set;
|
||||
* No interrupts enabled except possibly Resume Detect.
|
||||
*
|
||||
* If any of these conditions are violated we do a complete reset.
|
||||
*/
|
||||
pci_read_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, &legsup);
|
||||
if (legsup & ~(USBLEGSUP_RO | USBLEGSUP_RWC)) {
|
||||
dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n",
|
||||
__FUNCTION__, legsup);
|
||||
goto reset_needed;
|
||||
}
|
||||
|
||||
cmd = inw(uhci->io_addr + USBCMD);
|
||||
if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) {
|
||||
dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n",
|
||||
__FUNCTION__, cmd);
|
||||
goto reset_needed;
|
||||
}
|
||||
|
||||
intr = inw(uhci->io_addr + USBINTR);
|
||||
if (intr & (~USBINTR_RESUME)) {
|
||||
dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n",
|
||||
__FUNCTION__, intr);
|
||||
goto reset_needed;
|
||||
}
|
||||
return;
|
||||
|
||||
reset_needed:
|
||||
dev_dbg(uhci_dev(uhci), "Performing full reset\n");
|
||||
reset_hc(uhci);
|
||||
if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr))
|
||||
finish_reset(uhci);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -212,13 +156,13 @@ static void configure_hc(struct uhci_hcd *uhci)
|
|||
outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF);
|
||||
|
||||
/* Store the frame list base address */
|
||||
outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);
|
||||
outl(uhci->frame_dma_handle, uhci->io_addr + USBFLBASEADD);
|
||||
|
||||
/* Set the current frame number */
|
||||
outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
|
||||
|
||||
/* Mark controller as running before we enable interrupts */
|
||||
uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
|
||||
/* Mark controller as not halted before we enable interrupts */
|
||||
uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED;
|
||||
mb();
|
||||
|
||||
/* Enable PIRQ */
|
||||
|
@ -319,6 +263,7 @@ __acquires(uhci->lock)
|
|||
|
||||
static void start_rh(struct uhci_hcd *uhci)
|
||||
{
|
||||
uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
|
||||
uhci->is_stopped = 0;
|
||||
smp_wmb();
|
||||
|
||||
|
@ -437,36 +382,21 @@ 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;
|
||||
}
|
||||
uhci_free_qh(uhci, uhci->skelqh[i]);
|
||||
|
||||
if (uhci->term_td) {
|
||||
uhci_free_td(uhci, uhci->term_td);
|
||||
uhci->term_td = NULL;
|
||||
}
|
||||
uhci_free_td(uhci, uhci->term_td);
|
||||
|
||||
if (uhci->qh_pool) {
|
||||
dma_pool_destroy(uhci->qh_pool);
|
||||
uhci->qh_pool = NULL;
|
||||
}
|
||||
dma_pool_destroy(uhci->qh_pool);
|
||||
|
||||
if (uhci->td_pool) {
|
||||
dma_pool_destroy(uhci->td_pool);
|
||||
uhci->td_pool = NULL;
|
||||
}
|
||||
dma_pool_destroy(uhci->td_pool);
|
||||
|
||||
if (uhci->fl) {
|
||||
dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
|
||||
uhci->fl, uhci->fl->dma_handle);
|
||||
uhci->fl = NULL;
|
||||
}
|
||||
kfree(uhci->frame_cpu);
|
||||
|
||||
if (uhci->dentry) {
|
||||
debugfs_remove(uhci->dentry);
|
||||
uhci->dentry = NULL;
|
||||
}
|
||||
dma_free_coherent(uhci_dev(uhci),
|
||||
UHCI_NUMFRAMES * sizeof(*uhci->frame),
|
||||
uhci->frame, uhci->frame_dma_handle);
|
||||
|
||||
debugfs_remove(uhci->dentry);
|
||||
}
|
||||
|
||||
static int uhci_reset(struct usb_hcd *hcd)
|
||||
|
@ -545,7 +475,6 @@ static int uhci_start(struct usb_hcd *hcd)
|
|||
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
||||
int retval = -EBUSY;
|
||||
int i;
|
||||
dma_addr_t dma_handle;
|
||||
struct dentry *dentry;
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
|
@ -579,17 +508,23 @@ static int uhci_start(struct usb_hcd *hcd)
|
|||
|
||||
init_waitqueue_head(&uhci->waitqh);
|
||||
|
||||
uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
|
||||
&dma_handle, 0);
|
||||
if (!uhci->fl) {
|
||||
uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
|
||||
UHCI_NUMFRAMES * sizeof(*uhci->frame),
|
||||
&uhci->frame_dma_handle, 0);
|
||||
if (!uhci->frame) {
|
||||
dev_err(uhci_dev(uhci), "unable to allocate "
|
||||
"consistent memory for frame list\n");
|
||||
goto err_alloc_fl;
|
||||
goto err_alloc_frame;
|
||||
}
|
||||
memset(uhci->frame, 0, UHCI_NUMFRAMES * sizeof(*uhci->frame));
|
||||
|
||||
memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
|
||||
|
||||
uhci->fl->dma_handle = dma_handle;
|
||||
uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu),
|
||||
GFP_KERNEL);
|
||||
if (!uhci->frame_cpu) {
|
||||
dev_err(uhci_dev(uhci), "unable to allocate "
|
||||
"memory for frame pointers\n");
|
||||
goto err_alloc_frame_cpu;
|
||||
}
|
||||
|
||||
uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),
|
||||
sizeof(struct uhci_td), 16, 0);
|
||||
|
@ -672,7 +607,7 @@ static int uhci_start(struct usb_hcd *hcd)
|
|||
irq = 7;
|
||||
|
||||
/* Only place we don't use the frame list routines */
|
||||
uhci->fl->frame[i] = UHCI_PTR_QH |
|
||||
uhci->frame[i] = UHCI_PTR_QH |
|
||||
cpu_to_le32(uhci->skelqh[irq]->dma_handle);
|
||||
}
|
||||
|
||||
|
@ -690,31 +625,29 @@ static int uhci_start(struct usb_hcd *hcd)
|
|||
* error exits:
|
||||
*/
|
||||
err_alloc_skelqh:
|
||||
for (i = 0; i < UHCI_NUM_SKELQH; i++)
|
||||
if (uhci->skelqh[i]) {
|
||||
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:
|
||||
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;
|
||||
kfree(uhci->frame_cpu);
|
||||
|
||||
err_alloc_fl:
|
||||
err_alloc_frame_cpu:
|
||||
dma_free_coherent(uhci_dev(uhci),
|
||||
UHCI_NUMFRAMES * sizeof(*uhci->frame),
|
||||
uhci->frame, uhci->frame_dma_handle);
|
||||
|
||||
err_alloc_frame:
|
||||
debugfs_remove(uhci->dentry);
|
||||
uhci->dentry = NULL;
|
||||
|
||||
err_create_debug_entry:
|
||||
return retval;
|
||||
|
@ -726,7 +659,7 @@ static void uhci_stop(struct usb_hcd *hcd)
|
|||
|
||||
spin_lock_irq(&uhci->lock);
|
||||
if (!uhci->hc_inaccessible)
|
||||
reset_hc(uhci);
|
||||
hc_died(uhci);
|
||||
uhci_scan_schedule(uhci, NULL);
|
||||
spin_unlock_irq(&uhci->lock);
|
||||
|
||||
|
@ -774,14 +707,8 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
|
|||
if (uhci->hc_inaccessible) /* Dead or already suspended */
|
||||
goto done;
|
||||
|
||||
#ifndef CONFIG_USB_SUSPEND
|
||||
/* Otherwise this would never happen */
|
||||
suspend_rh(uhci, UHCI_RH_SUSPENDED);
|
||||
#endif
|
||||
|
||||
if (uhci->rh_state > UHCI_RH_SUSPENDED) {
|
||||
dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n");
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
rc = -EBUSY;
|
||||
goto done;
|
||||
};
|
||||
|
@ -820,10 +747,6 @@ static int uhci_resume(struct usb_hcd *hcd)
|
|||
check_and_reset_hc(uhci);
|
||||
configure_hc(uhci);
|
||||
|
||||
#ifndef CONFIG_USB_SUSPEND
|
||||
/* Otherwise this would never happen */
|
||||
wakeup_rh(uhci);
|
||||
#endif
|
||||
if (uhci->rh_state == UHCI_RH_RESET)
|
||||
suspend_rh(uhci, UHCI_RH_SUSPENDED);
|
||||
|
||||
|
@ -881,8 +804,8 @@ static const struct hc_driver uhci_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.suspend = uhci_suspend,
|
||||
.resume = uhci_resume,
|
||||
.hub_suspend = uhci_rh_suspend,
|
||||
.hub_resume = uhci_rh_resume,
|
||||
.bus_suspend = uhci_rh_suspend,
|
||||
.bus_resume = uhci_rh_resume,
|
||||
#endif
|
||||
.stop = uhci_stop,
|
||||
|
||||
|
@ -908,6 +831,7 @@ MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
|
|||
static struct pci_driver uhci_pci_driver = {
|
||||
.name = (char *)hcd_name,
|
||||
.id_table = uhci_pci_ids,
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
.probe = usb_hcd_pci_probe,
|
||||
.remove = usb_hcd_pci_remove,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#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
|
||||
*/
|
||||
|
@ -82,15 +83,10 @@
|
|||
#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;
|
||||
/*
|
||||
* Queue Headers
|
||||
*/
|
||||
|
||||
/*
|
||||
* One role of a QH is to hold a queue of TDs for some endpoint. Each QH is
|
||||
|
@ -116,13 +112,13 @@ struct uhci_qh {
|
|||
|
||||
struct urb_priv *urbp;
|
||||
|
||||
struct list_head list; /* P: uhci->frame_list_lock */
|
||||
struct list_head remove_list; /* P: uhci->remove_list_lock */
|
||||
struct list_head list;
|
||||
struct list_head remove_list;
|
||||
} __attribute__((aligned(16)));
|
||||
|
||||
/*
|
||||
* We need a special accessor for the element pointer because it is
|
||||
* subject to asynchronous updates by the controller
|
||||
* subject to asynchronous updates by the controller.
|
||||
*/
|
||||
static __le32 inline qh_element(struct uhci_qh *qh) {
|
||||
__le32 element = qh->element;
|
||||
|
@ -131,6 +127,11 @@ static __le32 inline qh_element(struct uhci_qh *qh) {
|
|||
return element;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transfer Descriptors
|
||||
*/
|
||||
|
||||
/*
|
||||
* for TD <status>:
|
||||
*/
|
||||
|
@ -183,17 +184,10 @@ static __le32 inline qh_element(struct uhci_qh *qh) {
|
|||
*
|
||||
* 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
|
||||
* sw space after the TD entry.
|
||||
*
|
||||
* 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)
|
||||
* even the same endpoint), or nothing (PTR_TERM), or a QH (for queued urbs).
|
||||
*/
|
||||
struct uhci_td {
|
||||
/* Hardware fields */
|
||||
|
@ -205,18 +199,16 @@ struct uhci_td {
|
|||
/* Software fields */
|
||||
dma_addr_t dma_handle;
|
||||
|
||||
struct urb *urb;
|
||||
|
||||
struct list_head list; /* P: urb->lock */
|
||||
struct list_head remove_list; /* P: uhci->td_remove_list_lock */
|
||||
struct list_head list;
|
||||
struct list_head remove_list;
|
||||
|
||||
int frame; /* for iso: what frame? */
|
||||
struct list_head fl_list; /* P: uhci->frame_list_lock */
|
||||
struct list_head fl_list;
|
||||
} __attribute__((aligned(16)));
|
||||
|
||||
/*
|
||||
* We need a special accessor for the control/status word because it is
|
||||
* subject to asynchronous updates by the controller
|
||||
* subject to asynchronous updates by the controller.
|
||||
*/
|
||||
static u32 inline td_status(struct uhci_td *td) {
|
||||
__le32 status = td->status;
|
||||
|
@ -226,6 +218,10 @@ static u32 inline td_status(struct uhci_td *td) {
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Skeleton Queue Headers
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -256,15 +252,15 @@ static u32 inline td_status(struct uhci_td *td) {
|
|||
*
|
||||
* 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)
|
||||
* (see Intel errata for explanation), and
|
||||
* - To loop back to the full-speed control queue for full-speed bandwidth
|
||||
* reclamation
|
||||
* 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.
|
||||
* use of QH's for Isochronous, it doesn't use them either. And the spec
|
||||
* says that queues never advance on an error completion status, which
|
||||
* makes them totally unsuitable for Isochronous transfers.
|
||||
*/
|
||||
|
||||
#define UHCI_NUM_SKELQH 12
|
||||
|
@ -314,8 +310,13 @@ static inline int __interval_to_skel(int interval)
|
|||
return 0; /* int128 for 128-255 ms (Max.) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* States for the root hub.
|
||||
* The UHCI controller and root hub
|
||||
*/
|
||||
|
||||
/*
|
||||
* States for the root hub:
|
||||
*
|
||||
* To prevent "bouncing" in the presence of electrical noise,
|
||||
* when there are no devices attached we delay for 1 second in the
|
||||
|
@ -326,7 +327,7 @@ static inline int __interval_to_skel(int interval)
|
|||
*/
|
||||
enum uhci_rh_state {
|
||||
/* In the following states the HC must be halted.
|
||||
* These two must come first */
|
||||
* These two must come first. */
|
||||
UHCI_RH_RESET,
|
||||
UHCI_RH_SUSPENDED,
|
||||
|
||||
|
@ -338,13 +339,13 @@ enum uhci_rh_state {
|
|||
UHCI_RH_SUSPENDING,
|
||||
|
||||
/* In the following states it's an error if the HC is halted.
|
||||
* These two must come last */
|
||||
* These two must come last. */
|
||||
UHCI_RH_RUNNING, /* The normal state */
|
||||
UHCI_RH_RUNNING_NODEVS, /* Running with no devices attached */
|
||||
};
|
||||
|
||||
/*
|
||||
* This describes the full uhci information.
|
||||
* The full UHCI controller information:
|
||||
*/
|
||||
struct uhci_hcd {
|
||||
|
||||
|
@ -361,7 +362,11 @@ struct uhci_hcd {
|
|||
struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */
|
||||
|
||||
spinlock_t lock;
|
||||
struct uhci_frame_list *fl; /* P: uhci->lock */
|
||||
|
||||
dma_addr_t frame_dma_handle; /* Hardware frame list */
|
||||
__le32 *frame;
|
||||
void **frame_cpu; /* CPU's frame list */
|
||||
|
||||
int fsbr; /* Full-speed bandwidth reclamation */
|
||||
unsigned long fsbrtimeout; /* FSBR delay */
|
||||
|
||||
|
@ -385,22 +390,22 @@ struct uhci_hcd {
|
|||
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 */
|
||||
struct list_head urb_list;
|
||||
|
||||
/* List of QH's that are done, but waiting to be unlinked (race) */
|
||||
struct list_head qh_remove_list; /* P: uhci->lock */
|
||||
struct list_head qh_remove_list;
|
||||
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 */
|
||||
struct list_head td_remove_list;
|
||||
unsigned int td_remove_age; /* Age in frames */
|
||||
|
||||
/* List of asynchronously unlinked URB's */
|
||||
struct list_head urb_remove_list; /* P: uhci->lock */
|
||||
struct list_head urb_remove_list;
|
||||
unsigned int urb_remove_age; /* Age in frames */
|
||||
|
||||
/* List of URB's awaiting completion callback */
|
||||
struct list_head complete_list; /* P: uhci->lock */
|
||||
struct list_head complete_list;
|
||||
|
||||
int rh_numports; /* Number of root-hub ports */
|
||||
|
||||
|
@ -419,13 +424,17 @@ static inline struct usb_hcd *uhci_to_hcd(struct uhci_hcd *uhci)
|
|||
|
||||
#define uhci_dev(u) (uhci_to_hcd(u)->self.controller)
|
||||
|
||||
|
||||
/*
|
||||
* Private per-URB data
|
||||
*/
|
||||
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 */
|
||||
struct list_head td_list;
|
||||
|
||||
unsigned fsbr : 1; /* URB turned on FSBR */
|
||||
unsigned fsbr_timeout : 1; /* URB timed out on FSBR */
|
||||
|
@ -434,12 +443,12 @@ struct urb_priv {
|
|||
/* 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 */
|
||||
struct list_head queue_list;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Locking in uhci.c
|
||||
*
|
||||
|
@ -459,6 +468,5 @@ struct urb_priv {
|
|||
|
||||
#define PCI_VENDOR_ID_GENESYS 0x17a0
|
||||
#define PCI_DEVICE_ID_GL880S_UHCI 0x8083
|
||||
#define PCI_DEVICE_ID_GL880S_EHCI 0x8084
|
||||
|
||||
#endif
|
||||
|
|
|
@ -89,10 +89,10 @@ static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td,
|
|||
td->frame = framenum;
|
||||
|
||||
/* Is there a TD already mapped there? */
|
||||
if (uhci->fl->frame_cpu[framenum]) {
|
||||
if (uhci->frame_cpu[framenum]) {
|
||||
struct uhci_td *ftd, *ltd;
|
||||
|
||||
ftd = uhci->fl->frame_cpu[framenum];
|
||||
ftd = uhci->frame_cpu[framenum];
|
||||
ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
|
||||
|
||||
list_add_tail(&td->fl_list, &ftd->fl_list);
|
||||
|
@ -101,29 +101,32 @@ static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td,
|
|||
wmb();
|
||||
ltd->link = cpu_to_le32(td->dma_handle);
|
||||
} else {
|
||||
td->link = uhci->fl->frame[framenum];
|
||||
td->link = uhci->frame[framenum];
|
||||
wmb();
|
||||
uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle);
|
||||
uhci->fl->frame_cpu[framenum] = td;
|
||||
uhci->frame[framenum] = cpu_to_le32(td->dma_handle);
|
||||
uhci->frame_cpu[framenum] = td;
|
||||
}
|
||||
}
|
||||
|
||||
static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
|
||||
static inline void uhci_remove_td_frame_list(struct uhci_hcd *uhci,
|
||||
struct uhci_td *td)
|
||||
{
|
||||
/* If it's not inserted, don't remove it */
|
||||
if (td->frame == -1 && list_empty(&td->fl_list))
|
||||
if (td->frame == -1) {
|
||||
WARN_ON(!list_empty(&td->fl_list));
|
||||
return;
|
||||
}
|
||||
|
||||
if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
|
||||
if (uhci->frame_cpu[td->frame] == td) {
|
||||
if (list_empty(&td->fl_list)) {
|
||||
uhci->fl->frame[td->frame] = td->link;
|
||||
uhci->fl->frame_cpu[td->frame] = NULL;
|
||||
uhci->frame[td->frame] = td->link;
|
||||
uhci->frame_cpu[td->frame] = NULL;
|
||||
} else {
|
||||
struct uhci_td *ntd;
|
||||
|
||||
ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
|
||||
uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
|
||||
uhci->fl->frame_cpu[td->frame] = ntd;
|
||||
uhci->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
|
||||
uhci->frame_cpu[td->frame] = ntd;
|
||||
}
|
||||
} else {
|
||||
struct uhci_td *ptd;
|
||||
|
@ -132,13 +135,20 @@ static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
|
|||
ptd->link = td->link;
|
||||
}
|
||||
|
||||
wmb();
|
||||
td->link = UHCI_PTR_TERM;
|
||||
|
||||
list_del_init(&td->fl_list);
|
||||
td->frame = -1;
|
||||
}
|
||||
|
||||
static void unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb)
|
||||
{
|
||||
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
|
||||
struct uhci_td *td;
|
||||
|
||||
list_for_each_entry(td, &urbp->td_list, list)
|
||||
uhci_remove_td_frame_list(uhci, td);
|
||||
wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Inserts a td list into qh.
|
||||
*/
|
||||
|
@ -443,7 +453,6 @@ static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *u
|
|||
|
||||
memset((void *)urbp, 0, sizeof(*urbp));
|
||||
|
||||
urbp->inserttime = jiffies;
|
||||
urbp->fsbrtime = jiffies;
|
||||
urbp->urb = urb;
|
||||
|
||||
|
@ -462,8 +471,6 @@ static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
|
|||
{
|
||||
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
|
||||
|
||||
td->urb = urb;
|
||||
|
||||
list_add_tail(&td->list, &urbp->td_list);
|
||||
}
|
||||
|
||||
|
@ -473,8 +480,6 @@ static void uhci_remove_td_from_urb(struct uhci_td *td)
|
|||
return;
|
||||
|
||||
list_del_init(&td->list);
|
||||
|
||||
td->urb = NULL;
|
||||
}
|
||||
|
||||
static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
|
||||
|
@ -503,7 +508,6 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
|
|||
|
||||
list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
|
||||
uhci_remove_td_from_urb(td);
|
||||
uhci_remove_td(uhci, td);
|
||||
list_add(&td->remove_list, &uhci->td_remove_list);
|
||||
}
|
||||
|
||||
|
@ -1073,6 +1077,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
|||
struct uhci_td *td;
|
||||
int i, ret, frame;
|
||||
int status, destination;
|
||||
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
|
||||
|
||||
status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
|
||||
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
|
||||
|
@ -1081,11 +1086,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
frame = urb->start_frame;
|
||||
for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
|
||||
if (!urb->iso_frame_desc[i].length)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < urb->number_of_packets; i++) {
|
||||
td = uhci_alloc_td(uhci);
|
||||
if (!td)
|
||||
return -ENOMEM;
|
||||
|
@ -1096,8 +1097,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
|||
|
||||
if (i + 1 >= urb->number_of_packets)
|
||||
td->status |= cpu_to_le32(TD_CTRL_IOC);
|
||||
}
|
||||
|
||||
frame = urb->start_frame;
|
||||
list_for_each_entry(td, &urbp->td_list, list) {
|
||||
uhci_insert_td_frame_list(uhci, td, frame);
|
||||
frame += urb->interval;
|
||||
}
|
||||
|
||||
return -EINPROGRESS;
|
||||
|
@ -1110,7 +1115,7 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
|||
int status;
|
||||
int i, ret = 0;
|
||||
|
||||
urb->actual_length = 0;
|
||||
urb->actual_length = urb->error_count = 0;
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(td, &urbp->td_list, list) {
|
||||
|
@ -1134,6 +1139,7 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
|||
|
||||
i++;
|
||||
}
|
||||
unlink_isochronous_tds(uhci, urb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1366,6 +1372,8 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
|||
goto done;
|
||||
list_del_init(&urbp->urb_list);
|
||||
|
||||
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
|
||||
unlink_isochronous_tds(uhci, urb);
|
||||
uhci_unlink_generic(uhci, urb);
|
||||
|
||||
uhci_get_current_frame_number(uhci);
|
||||
|
|
|
@ -425,9 +425,8 @@ static void mdc800_usb_download_notify (struct urb *urb, struct pt_regs *res)
|
|||
static struct usb_driver mdc800_usb_driver;
|
||||
static struct file_operations mdc800_device_ops;
|
||||
static struct usb_class_driver mdc800_class = {
|
||||
.name = "usb/mdc800%d",
|
||||
.name = "mdc800%d",
|
||||
.fops = &mdc800_device_ops,
|
||||
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
|
||||
.minor_base = MDC800_DEVICE_MINOR_BASE,
|
||||
};
|
||||
|
||||
|
@ -976,13 +975,13 @@ static struct usb_driver mdc800_usb_driver =
|
|||
Init and Cleanup this driver (Main Functions)
|
||||
*************************************************************************/
|
||||
|
||||
#define try(A) if (!(A)) goto cleanup_on_fail;
|
||||
|
||||
static int __init usb_mdc800_init (void)
|
||||
{
|
||||
int retval = -ENODEV;
|
||||
/* Allocate Memory */
|
||||
try (mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL));
|
||||
mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL);
|
||||
if (!mdc800)
|
||||
goto cleanup_on_fail;
|
||||
|
||||
memset(mdc800, 0, sizeof(struct mdc800_data));
|
||||
mdc800->dev = NULL;
|
||||
|
@ -998,13 +997,25 @@ static int __init usb_mdc800_init (void)
|
|||
mdc800->downloaded = 0;
|
||||
mdc800->written = 0;
|
||||
|
||||
try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL));
|
||||
try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL));
|
||||
try (mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL));
|
||||
mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL);
|
||||
if (!mdc800->irq_urb_buffer)
|
||||
goto cleanup_on_fail;
|
||||
mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL);
|
||||
if (!mdc800->write_urb_buffer)
|
||||
goto cleanup_on_fail;
|
||||
mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL);
|
||||
if (!mdc800->download_urb_buffer)
|
||||
goto cleanup_on_fail;
|
||||
|
||||
try (mdc800->irq_urb=usb_alloc_urb (0, GFP_KERNEL));
|
||||
try (mdc800->download_urb=usb_alloc_urb (0, GFP_KERNEL));
|
||||
try (mdc800->write_urb=usb_alloc_urb (0, GFP_KERNEL));
|
||||
mdc800->irq_urb=usb_alloc_urb (0, GFP_KERNEL);
|
||||
if (!mdc800->irq_urb)
|
||||
goto cleanup_on_fail;
|
||||
mdc800->download_urb=usb_alloc_urb (0, GFP_KERNEL);
|
||||
if (!mdc800->download_urb)
|
||||
goto cleanup_on_fail;
|
||||
mdc800->write_urb=usb_alloc_urb (0, GFP_KERNEL);
|
||||
if (!mdc800->write_urb)
|
||||
goto cleanup_on_fail;
|
||||
|
||||
/* Register the driver */
|
||||
retval = usb_register(&mdc800_usb_driver);
|
||||
|
|
|
@ -773,11 +773,10 @@ static int mts_usb_probe(struct usb_interface *intf,
|
|||
}
|
||||
|
||||
|
||||
new_desc = kmalloc(sizeof(struct mts_desc), GFP_KERNEL);
|
||||
new_desc = kzalloc(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;
|
||||
|
|
|
@ -2154,7 +2154,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|||
* input_handles associated with this input device.
|
||||
* What identifies an evdev input_handler is that it begins
|
||||
* with 'event', continues with a digit, and that in turn
|
||||
* is mapped to /{devfs}/input/eventN.
|
||||
* is mapped to input/eventN.
|
||||
*/
|
||||
list_for_each_safe(node, next, &inputdev->h_list) {
|
||||
inputhandle = to_handle(node);
|
||||
|
|
|
@ -1784,6 +1784,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
|||
hid->urbctrl->transfer_dma = hid->ctrlbuf_dma;
|
||||
hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
||||
|
||||
/* May be needed for some devices */
|
||||
usb_clear_halt(hid->dev, hid->urbin->pipe);
|
||||
|
||||
return hid;
|
||||
|
||||
fail:
|
||||
|
@ -1887,7 +1890,6 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
struct hid_device *hid = usb_get_intfdata (intf);
|
||||
|
||||
usb_kill_urb(hid->urbin);
|
||||
intf->dev.power.power_state = PMSG_SUSPEND;
|
||||
dev_dbg(&intf->dev, "suspend\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -1897,7 +1899,6 @@ static int hid_resume(struct usb_interface *intf)
|
|||
struct hid_device *hid = usb_get_intfdata (intf);
|
||||
int status;
|
||||
|
||||
intf->dev.power.power_state = PMSG_ON;
|
||||
if (hid->open)
|
||||
status = usb_submit_urb(hid->urbin, GFP_NOIO);
|
||||
else
|
||||
|
|
|
@ -732,9 +732,8 @@ static struct file_operations hiddev_fops = {
|
|||
};
|
||||
|
||||
static struct usb_class_driver hiddev_class = {
|
||||
.name = "usb/hid/hiddev%d",
|
||||
.name = "hiddev%d",
|
||||
.fops = &hiddev_fops,
|
||||
.mode = S_IFCHR | S_IRUGO | S_IWUSR,
|
||||
.minor_base = HIDDEV_MINOR_BASE,
|
||||
};
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ struct seg7_conversion_map {
|
|||
|
||||
static inline int map_to_seg7(struct seg7_conversion_map *map, int c)
|
||||
{
|
||||
return c & 0x7f ? map->table[c] : -EINVAL;
|
||||
return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL;
|
||||
}
|
||||
|
||||
#define SEG7_CONVERSION_MAP(_name, _map) \
|
||||
|
|
|
@ -75,7 +75,9 @@ struct touchkit_usb {
|
|||
|
||||
static struct usb_device_id touchkit_devices[] = {
|
||||
{USB_DEVICE(0x3823, 0x0001)},
|
||||
{USB_DEVICE(0x0123, 0x0001)},
|
||||
{USB_DEVICE(0x0eef, 0x0001)},
|
||||
{USB_DEVICE(0x0eef, 0x0002)},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -707,9 +707,8 @@ static struct file_operations dabusb_fops =
|
|||
};
|
||||
|
||||
static struct usb_class_driver dabusb_class = {
|
||||
.name = "usb/dabusb%d",
|
||||
.name = "dabusb%d",
|
||||
.fops = &dabusb_fops,
|
||||
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
|
||||
.minor_base = DABUSB_MINOR,
|
||||
};
|
||||
|
||||
|
|
|
@ -1873,9 +1873,8 @@ static struct file_operations auerswald_fops =
|
|||
};
|
||||
|
||||
static struct usb_class_driver auerswald_class = {
|
||||
.name = "usb/auer%d",
|
||||
.name = "auer%d",
|
||||
.fops = &auerswald_fops,
|
||||
.mode = S_IFCHR | S_IRUGO | S_IWUGO,
|
||||
.minor_base = AUER_MINOR_BASE,
|
||||
};
|
||||
|
||||
|
|
|
@ -105,11 +105,10 @@ static struct file_operations idmouse_fops = {
|
|||
.release = idmouse_release,
|
||||
};
|
||||
|
||||
/* class driver information for devfs */
|
||||
/* class driver information */
|
||||
static struct usb_class_driver idmouse_class = {
|
||||
.name = "usb/idmouse%d",
|
||||
.name = "idmouse%d",
|
||||
.fops = &idmouse_fops,
|
||||
.mode = S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH, /* filemode (char, 444) */
|
||||
.minor_base = USB_IDMOUSE_MINOR_BASE,
|
||||
};
|
||||
|
||||
|
|
|
@ -271,12 +271,11 @@ static struct file_operations tower_fops = {
|
|||
|
||||
/*
|
||||
* usb class driver info in order to get a minor number from the usb core,
|
||||
* and to have the device registered with devfs and the driver core
|
||||
* and to have the device registered with the driver core
|
||||
*/
|
||||
static struct usb_class_driver tower_class = {
|
||||
.name = "usb/legousbtower%d",
|
||||
.name = "legousbtower%d",
|
||||
.fops = &tower_fops,
|
||||
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
|
||||
.minor_base = LEGO_USB_TOWER_MINOR_BASE,
|
||||
};
|
||||
|
||||
|
|
|
@ -443,9 +443,8 @@ file_operations usb_rio_fops = {
|
|||
};
|
||||
|
||||
static struct usb_class_driver usb_rio_class = {
|
||||
.name = "usb/rio500%d",
|
||||
.name = "rio500%d",
|
||||
.fops = &usb_rio_fops,
|
||||
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
|
||||
.minor_base = RIO_MINOR,
|
||||
};
|
||||
|
||||
|
|
|
@ -2440,7 +2440,7 @@ int
|
|||
sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init)
|
||||
{
|
||||
int ret = 0, slot = sisusb->font_slot, i;
|
||||
struct font_desc *myfont;
|
||||
const struct font_desc *myfont;
|
||||
u8 *tempbuf;
|
||||
u16 *tempbufb;
|
||||
size_t written;
|
||||
|
@ -3239,12 +3239,7 @@ static struct file_operations usb_sisusb_fops = {
|
|||
};
|
||||
|
||||
static struct usb_class_driver usb_sisusb_class = {
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13)
|
||||
.name = "usb/sisusbvga%d",
|
||||
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
|
||||
#else
|
||||
.name = "sisusbvga%d",
|
||||
#endif
|
||||
.fops = &usb_sisusb_fops,
|
||||
.minor_base = SISUSB_MINOR
|
||||
};
|
||||
|
|
|
@ -251,13 +251,12 @@ static struct file_operations lcd_fops = {
|
|||
};
|
||||
|
||||
/*
|
||||
* * usb class driver info in order to get a minor number from the usb core,
|
||||
* * and to have the device registered with devfs and the driver core
|
||||
* */
|
||||
* usb class driver info in order to get a minor number from the usb core,
|
||||
* and to have the device registered with the driver core
|
||||
*/
|
||||
static struct usb_class_driver lcd_class = {
|
||||
.name = "usb/lcd%d",
|
||||
.name = "lcd%d",
|
||||
.fops = &lcd_fops,
|
||||
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
|
||||
.minor_base = USBLCD_MINOR,
|
||||
};
|
||||
|
||||
|
|
|
@ -983,6 +983,7 @@ test_ctrl_queue (struct usbtest_dev *dev, struct usbtest_param *param)
|
|||
reqp->number = i % NUM_SUBCASES;
|
||||
reqp->expected = expected;
|
||||
u->setup_packet = (char *) &reqp->setup;
|
||||
u->transfer_flags |= URB_NO_SETUP_DMA_MAP;
|
||||
|
||||
u->context = &context;
|
||||
u->complete = ctrl_complete;
|
||||
|
@ -1948,21 +1949,11 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id)
|
|||
|
||||
static int usbtest_suspend (struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct usbtest_dev *dev = usb_get_intfdata (intf);
|
||||
|
||||
down (&dev->sem);
|
||||
intf->dev.power.power_state = PMSG_SUSPEND;
|
||||
up (&dev->sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbtest_resume (struct usb_interface *intf)
|
||||
{
|
||||
struct usbtest_dev *dev = usb_get_intfdata (intf);
|
||||
|
||||
down (&dev->sem);
|
||||
intf->dev.power.power_state = PMSG_ON;
|
||||
up (&dev->sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#include "usb_mon.h"
|
||||
#include "../core/hcd.h"
|
||||
|
@ -205,6 +206,23 @@ static void mon_bus_remove(struct usb_bus *ubus)
|
|||
up(&mon_lock);
|
||||
}
|
||||
|
||||
static int mon_notify(struct notifier_block *self, unsigned long action,
|
||||
void *dev)
|
||||
{
|
||||
switch (action) {
|
||||
case USB_BUS_ADD:
|
||||
mon_bus_add(dev);
|
||||
break;
|
||||
case USB_BUS_REMOVE:
|
||||
mon_bus_remove(dev);
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block mon_nb = {
|
||||
.notifier_call = mon_notify,
|
||||
};
|
||||
|
||||
/*
|
||||
* Ops
|
||||
*/
|
||||
|
@ -212,8 +230,6 @@ static struct usb_mon_operations mon_ops_0 = {
|
|||
.urb_submit = mon_submit,
|
||||
.urb_submit_error = mon_submit_error,
|
||||
.urb_complete = mon_complete,
|
||||
.bus_add = mon_bus_add,
|
||||
.bus_remove = mon_bus_remove,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -329,6 +345,8 @@ static int __init mon_init(void)
|
|||
}
|
||||
// MOD_INC_USE_COUNT(which_module?);
|
||||
|
||||
usb_register_notify(&mon_nb);
|
||||
|
||||
down(&usb_bus_list_lock);
|
||||
list_for_each_entry (ubus, &usb_bus_list, bus_list) {
|
||||
mon_bus_init(mondir, ubus);
|
||||
|
@ -342,6 +360,7 @@ static void __exit mon_exit(void)
|
|||
struct mon_bus *mbus;
|
||||
struct list_head *p;
|
||||
|
||||
usb_unregister_notify(&mon_nb);
|
||||
usb_mon_deregister();
|
||||
|
||||
down(&mon_lock);
|
||||
|
|
|
@ -294,7 +294,7 @@ config USB_NET_ZAURUS
|
|||
This also supports some related device firmware, as used in some
|
||||
PDAs from Olympus and some cell phones from Motorola.
|
||||
|
||||
If you install an alternate ROM image, such as the Linux 2.6 based
|
||||
If you install an alternate image, such as the Linux 2.6 based
|
||||
versions of OpenZaurus, you should no longer need to support this
|
||||
protocol. Only the "eth-fd" or "net_fd" drivers in these devices
|
||||
really need this non-conformant variant of CDC Ethernet (or in
|
||||
|
|
|
@ -469,7 +469,7 @@ static int kaweth_reset(struct kaweth_device *kaweth)
|
|||
0,
|
||||
KAWETH_CONTROL_TIMEOUT);
|
||||
|
||||
udelay(10000);
|
||||
mdelay(10);
|
||||
|
||||
kaweth_dbg("kaweth_reset() returns %d.",result);
|
||||
|
||||
|
|
|
@ -1384,7 +1384,6 @@ static int pegasus_suspend (struct usb_interface *intf, pm_message_t message)
|
|||
usb_kill_urb(pegasus->rx_urb);
|
||||
usb_kill_urb(pegasus->intr_urb);
|
||||
}
|
||||
intf->dev.power.power_state = PMSG_SUSPEND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1392,7 +1391,6 @@ static int pegasus_resume (struct usb_interface *intf)
|
|||
{
|
||||
struct pegasus *pegasus = usb_get_intfdata(intf);
|
||||
|
||||
intf->dev.power.power_state = PMSG_ON;
|
||||
netif_device_attach (pegasus->net);
|
||||
if (netif_running(pegasus->net)) {
|
||||
pegasus->rx_urb->status = 0;
|
||||
|
|
|
@ -181,6 +181,8 @@ PEGASUS_DEV( "Accton USB 10/100 Ethernet Adapter", VENDOR_ACCTON, 0x1046,
|
|||
DEFAULT_GPIO_RESET )
|
||||
PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_ACCTON, 0x5046,
|
||||
DEFAULT_GPIO_RESET | PEGASUS_II )
|
||||
PEGASUS_DEV( "Philips USB 10/100 Ethernet", VENDOR_ACCTON, 0xb004,
|
||||
DEFAULT_GPIO_RESET | PEGASUS_II )
|
||||
PEGASUS_DEV( "ADMtek ADM8511 \"Pegasus II\" USB Ethernet",
|
||||
VENDOR_ADMTEK, 0x8511,
|
||||
DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA )
|
||||
|
|
|
@ -909,6 +909,7 @@ static void rtl8150_disconnect(struct usb_interface *intf)
|
|||
usb_set_intfdata(intf, NULL);
|
||||
if (dev) {
|
||||
set_bit(RTL8150_UNPLUG, &dev->flags);
|
||||
tasklet_disable(&dev->tl);
|
||||
unregister_netdev(dev->netdev);
|
||||
unlink_all_urbs(dev);
|
||||
free_all_urbs(dev);
|
||||
|
|
|
@ -1185,7 +1185,6 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
|
|||
netif_device_detach (dev->net);
|
||||
(void) unlink_urbs (dev, &dev->rxq);
|
||||
(void) unlink_urbs (dev, &dev->txq);
|
||||
intf->dev.power.power_state = PMSG_SUSPEND;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_suspend);
|
||||
|
@ -1194,7 +1193,6 @@ int usbnet_resume (struct usb_interface *intf)
|
|||
{
|
||||
struct usbnet *dev = usb_get_intfdata(intf);
|
||||
|
||||
intf->dev.power.power_state = PMSG_ON;
|
||||
netif_device_attach (dev->net);
|
||||
tasklet_schedule (&dev->bh);
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,730 @@
|
|||
This is the contents of some of the drivers/usb/serial/ files that had old
|
||||
changelog comments. They were quite old, and out of date, and we don't keep
|
||||
them anymore, so I've put them here, away from the source files, in case
|
||||
people still care to see them.
|
||||
|
||||
- Greg Kroah-Hartman <greg@kroah.com> October 20, 2005
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
usb-serial.h Change Log comments:
|
||||
|
||||
(03/26/2002) gkh
|
||||
removed the port->tty check from port_paranoia_check() due to serial
|
||||
consoles not having a tty device assigned to them.
|
||||
|
||||
(12/03/2001) gkh
|
||||
removed active from the port structure.
|
||||
added documentation to the usb_serial_device_type structure
|
||||
|
||||
(10/10/2001) gkh
|
||||
added vendor and product to serial structure. Needed to determine device
|
||||
owner when the device is disconnected.
|
||||
|
||||
(05/30/2001) gkh
|
||||
added sem to port structure and removed port_lock
|
||||
|
||||
(10/05/2000) gkh
|
||||
Added interrupt_in_endpointAddress and bulk_in_endpointAddress to help
|
||||
fix bug with urb->dev not being set properly, now that the usb core
|
||||
needs it.
|
||||
|
||||
(09/11/2000) gkh
|
||||
Added usb_serial_debug_data function to help get rid of #DEBUG in the
|
||||
drivers.
|
||||
|
||||
(08/28/2000) gkh
|
||||
Added port_lock to port structure.
|
||||
|
||||
(08/08/2000) gkh
|
||||
Added open_count to port structure.
|
||||
|
||||
(07/23/2000) gkh
|
||||
Added bulk_out_endpointAddress to port structure.
|
||||
|
||||
(07/19/2000) gkh, pberger, and borchers
|
||||
Modifications to allow usb-serial drivers to be modules.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
usb-serial.c Change Log comments:
|
||||
|
||||
(12/10/2002) gkh
|
||||
Split the ports off into their own struct device, and added a
|
||||
usb-serial bus driver.
|
||||
|
||||
(11/19/2002) gkh
|
||||
removed a few #ifdefs for the generic code and cleaned up the failure
|
||||
logic in initialization.
|
||||
|
||||
(10/02/2002) gkh
|
||||
moved the console code to console.c and out of this file.
|
||||
|
||||
(06/05/2002) gkh
|
||||
moved location of startup() call in serial_probe() until after all
|
||||
of the port information and endpoints are initialized. This makes
|
||||
things easier for some drivers.
|
||||
|
||||
(04/10/2002) gkh
|
||||
added serial_read_proc function which creates a
|
||||
/proc/tty/driver/usb-serial file.
|
||||
|
||||
(03/27/2002) gkh
|
||||
Got USB serial console code working properly and merged into the main
|
||||
version of the tree. Thanks to Randy Dunlap for the initial version
|
||||
of this code, and for pushing me to finish it up.
|
||||
The USB serial console works with any usb serial driver device.
|
||||
|
||||
(03/21/2002) gkh
|
||||
Moved all manipulation of port->open_count into the core. Now the
|
||||
individual driver's open and close functions are called only when the
|
||||
first open() and last close() is called. Making the drivers a bit
|
||||
smaller and simpler.
|
||||
Fixed a bug if a driver didn't have the owner field set.
|
||||
|
||||
(02/26/2002) gkh
|
||||
Moved all locking into the main serial_* functions, instead of having
|
||||
the individual drivers have to grab the port semaphore. This should
|
||||
reduce races.
|
||||
Reworked the MOD_INC logic a bit to always increment and decrement, even
|
||||
if the generic driver is being used.
|
||||
|
||||
(10/10/2001) gkh
|
||||
usb_serial_disconnect() now sets the serial->dev pointer is to NULL to
|
||||
help prevent child drivers from accessing the device since it is now
|
||||
gone.
|
||||
|
||||
(09/13/2001) gkh
|
||||
Moved generic driver initialize after we have registered with the USB
|
||||
core. Thanks to Randy Dunlap for pointing this problem out.
|
||||
|
||||
(07/03/2001) gkh
|
||||
Fixed module paramater size. Thanks to John Brockmeyer for the pointer.
|
||||
Fixed vendor and product getting defined through the MODULE_PARM macro
|
||||
if the Generic driver wasn't compiled in.
|
||||
Fixed problem with generic_shutdown() not being called for drivers that
|
||||
don't have a shutdown() function.
|
||||
|
||||
(06/06/2001) gkh
|
||||
added evil hack that is needed for the prolific pl2303 device due to the
|
||||
crazy way its endpoints are set up.
|
||||
|
||||
(05/30/2001) gkh
|
||||
switched from using spinlock to a semaphore, which fixes lots of problems.
|
||||
|
||||
(04/08/2001) gb
|
||||
Identify version on module load.
|
||||
|
||||
2001_02_05 gkh
|
||||
Fixed buffer overflows bug with the generic serial driver. Thanks to
|
||||
Todd Squires <squirest@ct0.com> for fixing this.
|
||||
|
||||
(01/10/2001) gkh
|
||||
Fixed bug where the generic serial adaptor grabbed _any_ device that was
|
||||
offered to it.
|
||||
|
||||
(12/12/2000) gkh
|
||||
Removed MOD_INC and MOD_DEC from poll and disconnect functions, and
|
||||
moved them to the serial_open and serial_close functions.
|
||||
Also fixed bug with there not being a MOD_DEC for the generic driver
|
||||
(thanks to Gary Brubaker for finding this.)
|
||||
|
||||
(11/29/2000) gkh
|
||||
Small NULL pointer initialization cleanup which saves a bit of disk image
|
||||
|
||||
(11/01/2000) Adam J. Richter
|
||||
instead of using idVendor/idProduct pairs, usb serial drivers
|
||||
now identify their hardware interest with usb_device_id tables,
|
||||
which they usually have anyhow for use with MODULE_DEVICE_TABLE.
|
||||
|
||||
(10/05/2000) gkh
|
||||
Fixed bug with urb->dev not being set properly, now that the usb
|
||||
core needs it.
|
||||
|
||||
(09/11/2000) gkh
|
||||
Removed DEBUG #ifdefs with call to usb_serial_debug_data
|
||||
|
||||
(08/28/2000) gkh
|
||||
Added port_lock to port structure.
|
||||
Added locks for SMP safeness to generic driver
|
||||
Fixed the ability to open a generic device's port more than once.
|
||||
|
||||
(07/23/2000) gkh
|
||||
Added bulk_out_endpointAddress to port structure.
|
||||
|
||||
(07/19/2000) gkh, pberger, and borchers
|
||||
Modifications to allow usb-serial drivers to be modules.
|
||||
|
||||
(07/03/2000) gkh
|
||||
Added more debugging to serial_ioctl call
|
||||
|
||||
(06/25/2000) gkh
|
||||
Changed generic_write_bulk_callback to not call wake_up_interruptible
|
||||
directly, but to have port_softint do it at a safer time.
|
||||
|
||||
(06/23/2000) gkh
|
||||
Cleaned up debugging statements in a quest to find UHCI timeout bug.
|
||||
|
||||
(05/22/2000) gkh
|
||||
Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be
|
||||
removed from the individual device source files.
|
||||
|
||||
(05/03/2000) gkh
|
||||
Added the Digi Acceleport driver from Al Borchers and Peter Berger.
|
||||
|
||||
(05/02/2000) gkh
|
||||
Changed devfs and tty register code to work properly now. This was based on
|
||||
the ACM driver changes by Vojtech Pavlik.
|
||||
|
||||
(04/27/2000) Ryan VanderBijl
|
||||
Put calls to *_paranoia_checks into one function.
|
||||
|
||||
(04/23/2000) gkh
|
||||
Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports.
|
||||
Moved when the startup code printed out the devices that are supported.
|
||||
|
||||
(04/19/2000) gkh
|
||||
Added driver for ZyXEL omni.net lcd plus ISDN TA
|
||||
Made startup info message specify which drivers were compiled in.
|
||||
|
||||
(04/03/2000) gkh
|
||||
Changed the probe process to remove the module unload races.
|
||||
Changed where the tty layer gets initialized to have devfs work nicer.
|
||||
Added initial devfs support.
|
||||
|
||||
(03/26/2000) gkh
|
||||
Split driver up into device specific pieces.
|
||||
|
||||
(03/19/2000) gkh
|
||||
Fixed oops that could happen when device was removed while a program
|
||||
was talking to the device.
|
||||
Removed the static urbs and now all urbs are created and destroyed
|
||||
dynamically.
|
||||
Reworked the internal interface. Now everything is based on the
|
||||
usb_serial_port structure instead of the larger usb_serial structure.
|
||||
This fixes the bug that a multiport device could not have more than
|
||||
one port open at one time.
|
||||
|
||||
(03/17/2000) gkh
|
||||
Added config option for debugging messages.
|
||||
Added patch for keyspan pda from Brian Warner.
|
||||
|
||||
(03/06/2000) gkh
|
||||
Added the keyspan pda code from Brian Warner <warner@lothar.com>
|
||||
Moved a bunch of the port specific stuff into its own structure. This
|
||||
is in anticipation of the true multiport devices (there's a bug if you
|
||||
try to access more than one port of any multiport device right now)
|
||||
|
||||
(02/21/2000) gkh
|
||||
Made it so that any serial devices only have to specify which functions
|
||||
they want to overload from the generic function calls (great,
|
||||
inheritance in C, in a driver, just what I wanted...)
|
||||
Added support for set_termios and ioctl function calls. No drivers take
|
||||
advantage of this yet.
|
||||
Removed the #ifdef MODULE, now there is no module specific code.
|
||||
Cleaned up a few comments in usb-serial.h that were wrong (thanks again
|
||||
to Miles Lott).
|
||||
Small fix to get_free_serial.
|
||||
|
||||
(02/14/2000) gkh
|
||||
Removed the Belkin and Peracom functionality from the driver due to
|
||||
the lack of support from the vendor, and me not wanting people to
|
||||
accidenatly buy the device, expecting it to work with Linux.
|
||||
Added read_bulk_callback and write_bulk_callback to the type structure
|
||||
for the needs of the FTDI and WhiteHEAT driver.
|
||||
Changed all reverences to FTDI to FTDI_SIO at the request of Bill
|
||||
Ryder.
|
||||
Changed the output urb size back to the max endpoint size to make
|
||||
the ftdi_sio driver have it easier, and due to the fact that it didn't
|
||||
really increase the speed any.
|
||||
|
||||
(02/11/2000) gkh
|
||||
Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a
|
||||
patch from Miles Lott (milos@insync.net).
|
||||
Fixed bug with not restoring the minor range that a device grabs, if
|
||||
the startup function fails (thanks Miles for finding this).
|
||||
|
||||
(02/05/2000) gkh
|
||||
Added initial framework for the Keyspan PDA serial converter so that
|
||||
Brian Warner has a place to put his code.
|
||||
Made the ezusb specific functions generic enough that different
|
||||
devices can use them (whiteheat and keyspan_pda both need them).
|
||||
Split out a whole bunch of structure and other stuff to a separate
|
||||
usb-serial.h file.
|
||||
Made the Visor connection messages a little more understandable, now
|
||||
that Miles Lott (milos@insync.net) has gotten the Generic channel to
|
||||
work. Also made them always show up in the log file.
|
||||
|
||||
(01/25/2000) gkh
|
||||
Added initial framework for FTDI serial converter so that Bill Ryder
|
||||
has a place to put his code.
|
||||
Added the vendor specific info from Handspring. Now we can print out
|
||||
informational debug messages as well as understand what is happening.
|
||||
|
||||
(01/23/2000) gkh
|
||||
Fixed problem of crash when trying to open a port that didn't have a
|
||||
device assigned to it. Made the minor node finding a little smarter,
|
||||
now it looks to find a continuous space for the new device.
|
||||
|
||||
(01/21/2000) gkh
|
||||
Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net)
|
||||
Fixed get_serial_by_minor which was all messed up for multi port
|
||||
devices. Fixed multi port problem for generic devices. Now the number
|
||||
of ports is determined by the number of bulk out endpoints for the
|
||||
generic device.
|
||||
|
||||
(01/19/2000) gkh
|
||||
Removed lots of cruft that was around from the old (pre urb) driver
|
||||
interface.
|
||||
Made the serial_table dynamic. This should save lots of memory when
|
||||
the number of minor nodes goes up to 256.
|
||||
Added initial support for devices that have more than one port.
|
||||
Added more debugging comments for the Visor, and added a needed
|
||||
set_configuration call.
|
||||
|
||||
(01/17/2000) gkh
|
||||
Fixed the WhiteHEAT firmware (my processing tool had a bug)
|
||||
and added new debug loader firmware for it.
|
||||
Removed the put_char function as it isn't really needed.
|
||||
Added visor startup commands as found by the Win98 dump.
|
||||
|
||||
(01/13/2000) gkh
|
||||
Fixed the vendor id for the generic driver to the one I meant it to be.
|
||||
|
||||
(01/12/2000) gkh
|
||||
Forget the version numbering...that's pretty useless...
|
||||
Made the driver able to be compiled so that the user can select which
|
||||
converter they want to use. This allows people who only want the Visor
|
||||
support to not pay the memory size price of the WhiteHEAT.
|
||||
Fixed bug where the generic driver (idVendor=0000 and idProduct=0000)
|
||||
grabbed the root hub. Not good.
|
||||
|
||||
version 0.4.0 (01/10/2000) gkh
|
||||
Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT
|
||||
device. Added startup function to allow firmware to be downloaded to
|
||||
a device if it needs to be.
|
||||
Added firmware download logic to the WhiteHEAT device.
|
||||
Started to add #defines to split up the different drivers for potential
|
||||
configuration option.
|
||||
|
||||
version 0.3.1 (12/30/99) gkh
|
||||
Fixed problems with urb for bulk out.
|
||||
Added initial support for multiple sets of endpoints. This enables
|
||||
the Handspring Visor to be attached successfully. Only the first
|
||||
bulk in / bulk out endpoint pair is being used right now.
|
||||
|
||||
version 0.3.0 (12/27/99) gkh
|
||||
Added initial support for the Handspring Visor based on a patch from
|
||||
Miles Lott (milos@sneety.insync.net)
|
||||
Cleaned up the code a bunch and converted over to using urbs only.
|
||||
|
||||
version 0.2.3 (12/21/99) gkh
|
||||
Added initial support for the Connect Tech WhiteHEAT converter.
|
||||
Incremented the number of ports in expectation of getting the
|
||||
WhiteHEAT to work properly (4 ports per connection).
|
||||
Added notification on insertion and removal of what port the
|
||||
device is/was connected to (and what kind of device it was).
|
||||
|
||||
version 0.2.2 (12/16/99) gkh
|
||||
Changed major number to the new allocated number. We're legal now!
|
||||
|
||||
version 0.2.1 (12/14/99) gkh
|
||||
Fixed bug that happens when device node is opened when there isn't a
|
||||
device attached to it. Thanks to marek@webdesign.no for noticing this.
|
||||
|
||||
version 0.2.0 (11/10/99) gkh
|
||||
Split up internals to make it easier to add different types of serial
|
||||
converters to the code.
|
||||
Added a "generic" driver that gets it's vendor and product id
|
||||
from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net)
|
||||
for the idea and sample code (from the usb scanner driver.)
|
||||
Cleared up any licensing questions by releasing it under the GNU GPL.
|
||||
|
||||
version 0.1.2 (10/25/99) gkh
|
||||
Fixed bug in detecting device.
|
||||
|
||||
version 0.1.1 (10/05/99) gkh
|
||||
Changed the major number to not conflict with anything else.
|
||||
|
||||
version 0.1 (09/28/99) gkh
|
||||
Can recognize the two different devices and start up a read from
|
||||
device when asked to. Writes also work. No control signals yet, this
|
||||
all is vendor specific data (i.e. no spec), also no control for
|
||||
different baud rates or other bit settings.
|
||||
Currently we are using the same devid as the acm driver. This needs
|
||||
to change.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
visor.c Change Log comments:
|
||||
|
||||
(06/03/2003) Judd Montgomery <judd at jpilot.org>
|
||||
Added support for module parameter options for untested/unknown
|
||||
devices.
|
||||
|
||||
(03/09/2003) gkh
|
||||
Added support for the Sony Clie NZ90V device. Thanks to Martin Brachtl
|
||||
<brachtl@redgrep.cz> for the information.
|
||||
|
||||
(03/05/2003) gkh
|
||||
Think Treo support is now working.
|
||||
|
||||
(04/03/2002) gkh
|
||||
Added support for the Sony OS 4.1 devices. Thanks to Hiroyuki ARAKI
|
||||
<hiro@zob.ne.jp> for the information.
|
||||
|
||||
(03/27/2002) gkh
|
||||
Removed assumptions that port->tty was always valid (is not true
|
||||
for usb serial console devices.)
|
||||
|
||||
(03/23/2002) gkh
|
||||
Added support for the Palm i705 device, thanks to Thomas Riemer
|
||||
<tom@netmech.com> for the information.
|
||||
|
||||
(03/21/2002) gkh
|
||||
Added support for the Palm m130 device, thanks to Udo Eisenbarth
|
||||
<udo.eisenbarth@web.de> for the information.
|
||||
|
||||
(02/27/2002) gkh
|
||||
Reworked the urb handling logic. We have no more pool, but dynamically
|
||||
allocate the urb and the transfer buffer on the fly. In testing this
|
||||
does not incure any measurable overhead. This also relies on the fact
|
||||
that we have proper reference counting logic for urbs.
|
||||
|
||||
(02/21/2002) SilaS
|
||||
Added initial support for the Palm m515 devices.
|
||||
|
||||
(02/14/2002) gkh
|
||||
Added support for the Clie S-360 device.
|
||||
|
||||
(12/18/2001) gkh
|
||||
Added better Clie support for 3.5 devices. Thanks to Geoffrey Levand
|
||||
for the patch.
|
||||
|
||||
(11/11/2001) gkh
|
||||
Added support for the m125 devices, and added check to prevent oopses
|
||||
for Clié devices that lie about the number of ports they have.
|
||||
|
||||
(08/30/2001) gkh
|
||||
Added support for the Clie devices, both the 3.5 and 4.0 os versions.
|
||||
Many thanks to Daniel Burke, and Bryan Payne for helping with this.
|
||||
|
||||
(08/23/2001) gkh
|
||||
fixed a few potential bugs pointed out by Oliver Neukum.
|
||||
|
||||
(05/30/2001) gkh
|
||||
switched from using spinlock to a semaphore, which fixes lots of problems.
|
||||
|
||||
(05/28/2000) gkh
|
||||
Added initial support for the Palm m500 and Palm m505 devices.
|
||||
|
||||
(04/08/2001) gb
|
||||
Identify version on module load.
|
||||
|
||||
(01/21/2000) gkh
|
||||
Added write_room and chars_in_buffer, as they were previously using the
|
||||
generic driver versions which is all wrong now that we are using an urb
|
||||
pool. Thanks to Wolfgang Grandegger for pointing this out to me.
|
||||
Removed count assignment in the write function, which was not needed anymore
|
||||
either. Thanks to Al Borchers for pointing this out.
|
||||
|
||||
(12/12/2000) gkh
|
||||
Moved MOD_DEC to end of visor_close to be nicer, as the final write
|
||||
message can sleep.
|
||||
|
||||
(11/12/2000) gkh
|
||||
Fixed bug with data being dropped on the floor by forcing tty->low_latency
|
||||
to be on. Hopefully this fixes the OHCI issue!
|
||||
|
||||
(11/01/2000) Adam J. Richter
|
||||
usb_device_id table support
|
||||
|
||||
(10/05/2000) gkh
|
||||
Fixed bug with urb->dev not being set properly, now that the usb
|
||||
core needs it.
|
||||
|
||||
(09/11/2000) gkh
|
||||
Got rid of always calling kmalloc for every urb we wrote out to the
|
||||
device.
|
||||
Added visor_read_callback so we can keep track of bytes in and out for
|
||||
those people who like to know the speed of their device.
|
||||
Removed DEBUG #ifdefs with call to usb_serial_debug_data
|
||||
|
||||
(09/06/2000) gkh
|
||||
Fixed oops in visor_exit. Need to uncomment usb_unlink_urb call _after_
|
||||
the host controller drivers set urb->dev = NULL when the urb is finished.
|
||||
|
||||
(08/28/2000) gkh
|
||||
Added locks for SMP safeness.
|
||||
|
||||
(08/08/2000) gkh
|
||||
Fixed endian problem in visor_startup.
|
||||
Fixed MOD_INC and MOD_DEC logic and the ability to open a port more
|
||||
than once.
|
||||
|
||||
(07/23/2000) gkh
|
||||
Added pool of write urbs to speed up transfers to the visor.
|
||||
|
||||
(07/19/2000) gkh
|
||||
Added module_init and module_exit functions to handle the fact that this
|
||||
driver is a loadable module now.
|
||||
|
||||
(07/03/2000) gkh
|
||||
Added visor_set_ioctl and visor_set_termios functions (they don't do much
|
||||
of anything, but are good for debugging.)
|
||||
|
||||
(06/25/2000) gkh
|
||||
Fixed bug in visor_unthrottle that should help with the disconnect in PPP
|
||||
bug that people have been reporting.
|
||||
|
||||
(06/23/2000) gkh
|
||||
Cleaned up debugging statements in a quest to find UHCI timeout bug.
|
||||
|
||||
(04/27/2000) Ryan VanderBijl
|
||||
Fixed memory leak in visor_close
|
||||
|
||||
(03/26/2000) gkh
|
||||
Split driver up into device specific pieces.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
pl2303.c Change Log comments:
|
||||
|
||||
2002_Mar_26 gkh
|
||||
allowed driver to work properly if there is no tty assigned to a port
|
||||
(this happens for serial console devices.)
|
||||
|
||||
2001_Oct_06 gkh
|
||||
Added RTS and DTR line control. Thanks to joe@bndlg.de for parts of it.
|
||||
|
||||
2001_Sep_19 gkh
|
||||
Added break support.
|
||||
|
||||
2001_Aug_30 gkh
|
||||
fixed oops in write_bulk_callback.
|
||||
|
||||
2001_Aug_28 gkh
|
||||
reworked buffer logic to be like other usb-serial drivers. Hopefully
|
||||
removing some reported problems.
|
||||
|
||||
2001_Jun_06 gkh
|
||||
finished porting to 2.4 format.
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
io_edgeport.c Change Log comments:
|
||||
|
||||
2003_04_03 al borchers
|
||||
- fixed a bug (that shows up with dosemu) where the tty struct is
|
||||
used in a callback after it has been freed
|
||||
|
||||
2.3 2002_03_08 greg kroah-hartman
|
||||
- fixed bug when multiple devices were attached at the same time.
|
||||
|
||||
2.2 2001_11_14 greg kroah-hartman
|
||||
- fixed bug in edge_close that kept the port from being used more
|
||||
than once.
|
||||
- fixed memory leak on device removal.
|
||||
- fixed potential double free of memory when command urb submitting
|
||||
failed.
|
||||
- other small cleanups when the device is removed
|
||||
|
||||
2.1 2001_07_09 greg kroah-hartman
|
||||
- added support for TIOCMBIS and TIOCMBIC.
|
||||
|
||||
(04/08/2001) gb
|
||||
- Identify version on module load.
|
||||
|
||||
2.0 2001_03_05 greg kroah-hartman
|
||||
- reworked entire driver to fit properly in with the other usb-serial
|
||||
drivers. Occasional oopses still happen, but it's a good start.
|
||||
|
||||
1.2.3 (02/23/2001) greg kroah-hartman
|
||||
- changed device table to work properly for 2.4.x final format.
|
||||
- fixed problem with dropping data at high data rates.
|
||||
|
||||
1.2.2 (11/27/2000) greg kroah-hartman
|
||||
- cleaned up more NTisms.
|
||||
- Added device table for 2.4.0-test11
|
||||
|
||||
1.2.1 (11/08/2000) greg kroah-hartman
|
||||
- Started to clean up NTisms.
|
||||
- Fixed problem with dev field of urb for kernels >= 2.4.0-test9
|
||||
|
||||
1.2 (10/17/2000) David Iacovelli
|
||||
Remove all EPIC code and GPL source
|
||||
Fix RELEVANT_IFLAG macro to include flow control
|
||||
changes port configuration changes.
|
||||
Fix redefinition of SERIAL_MAGIC
|
||||
Change all timeout values to 5 seconds
|
||||
Tried to fix the UHCI multiple urb submission, but failed miserably.
|
||||
it seems to work fine with OHCI.
|
||||
( Greg take a look at the #if 0 at end of WriteCmdUsb() we must
|
||||
find a way to work arount this UHCI bug )
|
||||
|
||||
1.1 (10/11/2000) David Iacovelli
|
||||
Fix XON/XOFF flow control to support both IXON and IXOFF
|
||||
|
||||
0.9.27 (06/30/2000) David Iacovelli
|
||||
Added transmit queue and now allocate urb for command writes.
|
||||
|
||||
0.9.26 (06/29/2000) David Iacovelli
|
||||
Add support for 80251 based edgeport
|
||||
|
||||
0.9.25 (06/27/2000) David Iacovelli
|
||||
Do not close the port if it has multiple opens.
|
||||
|
||||
0.9.24 (05/26/2000) David Iacovelli
|
||||
Add IOCTLs to support RXTX and JAVA POS
|
||||
and first cut at running BlackBox Demo
|
||||
|
||||
0.9.23 (05/24/2000) David Iacovelli
|
||||
Add IOCTLs to support RXTX and JAVA POS
|
||||
|
||||
0.9.22 (05/23/2000) David Iacovelli
|
||||
fixed bug in enumeration. If epconfig turns on mapping by
|
||||
path after a device is already plugged in, we now update
|
||||
the mapping correctly
|
||||
|
||||
0.9.21 (05/16/2000) David Iacovelli
|
||||
Added BlockUntilChaseResp() to also wait for txcredits
|
||||
Updated the way we allocate and handle write URBs
|
||||
Add debug code to dump buffers
|
||||
|
||||
0.9.20 (05/01/2000) David Iacovelli
|
||||
change driver to use usb/tts/
|
||||
|
||||
0.9.19 (05/01/2000) David Iacovelli
|
||||
Update code to compile if DEBUG is off
|
||||
|
||||
0.9.18 (04/28/2000) David Iacovelli
|
||||
cleanup and test tty_register with devfs
|
||||
|
||||
0.9.17 (04/27/2000) greg kroah-hartman
|
||||
changed tty_register around to be like the way it
|
||||
was before, but now it works properly with devfs.
|
||||
|
||||
0.9.16 (04/26/2000) david iacovelli
|
||||
Fixed bug in GetProductInfo()
|
||||
|
||||
0.9.15 (04/25/2000) david iacovelli
|
||||
Updated enumeration
|
||||
|
||||
0.9.14 (04/24/2000) david iacovelli
|
||||
Removed all config/status IOCTLS and
|
||||
converted to using /proc/edgeport
|
||||
still playing with devfs
|
||||
|
||||
0.9.13 (04/24/2000) david iacovelli
|
||||
Removed configuration based on ttyUSB0
|
||||
Added support for configuration using /prod/edgeport
|
||||
first attempt at using devfs (not working yet!)
|
||||
Added IOCTL to GetProductInfo()
|
||||
Added support for custom baud rates
|
||||
Add support for random port numbers
|
||||
|
||||
0.9.12 (04/18/2000) david iacovelli
|
||||
added additional configuration IOCTLs
|
||||
use ttyUSB0 for configuration
|
||||
|
||||
0.9.11 (04/17/2000) greg kroah-hartman
|
||||
fixed module initialization race conditions.
|
||||
made all urbs dynamically allocated.
|
||||
made driver devfs compatible. now it only registers the tty device
|
||||
when the device is actually plugged in.
|
||||
|
||||
0.9.10 (04/13/2000) greg kroah-hartman
|
||||
added proc interface framework.
|
||||
|
||||
0.9.9 (04/13/2000) david iacovelli
|
||||
added enumeration code and ioctls to configure the device
|
||||
|
||||
0.9.8 (04/12/2000) david iacovelli
|
||||
Change interrupt read start when device is plugged in
|
||||
and stop when device is removed
|
||||
process interrupt reads when all ports are closed
|
||||
(keep value of rxBytesAvail consistent with the edgeport)
|
||||
set the USB_BULK_QUEUE flag so that we can shove a bunch
|
||||
of urbs at once down the pipe
|
||||
|
||||
0.9.7 (04/10/2000) david iacovelli
|
||||
start to add enumeration code.
|
||||
generate serial number for epic devices
|
||||
add support for kdb
|
||||
|
||||
0.9.6 (03/30/2000) david iacovelli
|
||||
add IOCTL to get string, manufacture, and boot descriptors
|
||||
|
||||
0.9.5 (03/14/2000) greg kroah-hartman
|
||||
more error checking added to SerialOpen to try to fix UHCI open problem
|
||||
|
||||
0.9.4 (03/09/2000) greg kroah-hartman
|
||||
added more error checking to handle oops when data is hanging
|
||||
around and tty is abruptly closed.
|
||||
|
||||
0.9.3 (03/09/2000) david iacovelli
|
||||
Add epic support for xon/xoff chars
|
||||
play with performance
|
||||
|
||||
0.9.2 (03/08/2000) greg kroah-hartman
|
||||
changed most "info" calls to "dbg"
|
||||
implemented flow control properly in the termios call
|
||||
|
||||
0.9.1 (03/08/2000) david iacovelli
|
||||
added EPIC support
|
||||
enabled bootloader update
|
||||
|
||||
0.9 (03/08/2000) greg kroah-hartman
|
||||
Release to IO networks.
|
||||
Integrated changes that David made
|
||||
made getting urbs for writing SMP safe
|
||||
|
||||
0.8 (03/07/2000) greg kroah-hartman
|
||||
Release to IO networks.
|
||||
Fixed problems that were seen in code by David.
|
||||
Now both Edgeport/4 and Edgeport/2 works properly.
|
||||
Changed most of the functions to use port instead of serial.
|
||||
|
||||
0.7 (02/27/2000) greg kroah-hartman
|
||||
Milestone 3 release.
|
||||
Release to IO Networks
|
||||
ioctl for waiting on line change implemented.
|
||||
ioctl for getting statistics implemented.
|
||||
multiport support working.
|
||||
lsr and msr registers are now handled properly.
|
||||
change break now hooked up and working.
|
||||
support for all known Edgeport devices.
|
||||
|
||||
0.6 (02/22/2000) greg kroah-hartman
|
||||
Release to IO networks.
|
||||
CHASE is implemented correctly when port is closed.
|
||||
SerialOpen now blocks correctly until port is fully opened.
|
||||
|
||||
0.5 (02/20/2000) greg kroah-hartman
|
||||
Release to IO networks.
|
||||
Known problems:
|
||||
modem status register changes are not sent on to the user
|
||||
CHASE is not implemented when the port is closed.
|
||||
|
||||
0.4 (02/16/2000) greg kroah-hartman
|
||||
Second cut at the CeBit demo.
|
||||
Doesn't leak memory on every write to the port
|
||||
Still small leaks on startup.
|
||||
Added support for Edgeport/2 and Edgeport/8
|
||||
|
||||
0.3 (02/15/2000) greg kroah-hartman
|
||||
CeBit demo release.
|
||||
Force the line settings to 4800, 8, 1, e for the demo.
|
||||
Warning! This version leaks memory like crazy!
|
||||
|
||||
0.2 (01/30/2000) greg kroah-hartman
|
||||
Milestone 1 release.
|
||||
Device is found by USB subsystem, enumerated, fimware is downloaded
|
||||
and the descriptors are printed to the debug log, config is set, and
|
||||
green light starts to blink. Open port works, and data can be sent
|
||||
and received at the default settings of the UART. Loopback connector
|
||||
and debug log confirms this.
|
||||
|
||||
0.1 (01/23/2000) greg kroah-hartman
|
||||
Initial release to help IO Networks try to set up their test system.
|
||||
Edgeport4 is recognized, firmware is downloaded, config is set so
|
||||
device blinks green light every 3 sec. Port is bound, but opening,
|
||||
closing, and sending data do not work properly.
|
||||
|
||||
|
|
@ -394,6 +394,15 @@ config USB_SERIAL_MCT_U232
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called mct_u232.
|
||||
|
||||
config USB_SERIAL_NOKIA_DKU2
|
||||
tristate "USB Nokia DKU2 Driver"
|
||||
depends on USB_SERIAL
|
||||
help
|
||||
Say Y here if you want to use a Nokia DKU2 device.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nokia_dku2.
|
||||
|
||||
config USB_SERIAL_PL2303
|
||||
tristate "USB Prolific 2303 Single Port Serial Driver"
|
||||
depends on USB_SERIAL
|
||||
|
|
|
@ -31,6 +31,7 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
|
|||
obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o
|
||||
obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o
|
||||
obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o
|
||||
obj-$(CONFIG_USB_SERIAL_NOKIA_DKU2) += nokia_dku2.o
|
||||
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
|
||||
obj-$(CONFIG_USB_SERIAL_OPTION) += option.o
|
||||
obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o
|
||||
|
|
|
@ -30,9 +30,11 @@ static struct usb_driver airprime_driver = {
|
|||
.id_table = id_table,
|
||||
};
|
||||
|
||||
static struct usb_serial_device_type airprime_device = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "airprime",
|
||||
static struct usb_serial_driver airprime_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "airprime",
|
||||
},
|
||||
.id_table = id_table,
|
||||
.num_interrupt_in = NUM_DONT_CARE,
|
||||
.num_bulk_in = NUM_DONT_CARE,
|
||||
|
|
|
@ -121,10 +121,12 @@ static struct usb_driver belkin_driver = {
|
|||
};
|
||||
|
||||
/* All of the device info needed for the serial converters */
|
||||
static struct usb_serial_device_type belkin_device = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "Belkin / Peracom / GoHubs USB Serial Adapter",
|
||||
.short_name = "belkin",
|
||||
static struct usb_serial_driver belkin_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "belkin",
|
||||
},
|
||||
.description = "Belkin / Peracom / GoHubs USB Serial Adapter",
|
||||
.id_table = id_table_combined,
|
||||
.num_interrupt_in = 1,
|
||||
.num_bulk_in = 1,
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
static int usb_serial_device_match (struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct usb_serial_device_type *driver;
|
||||
struct usb_serial_driver *driver;
|
||||
const struct usb_serial_port *port;
|
||||
|
||||
/*
|
||||
|
@ -44,7 +44,7 @@ struct bus_type usb_serial_bus_type = {
|
|||
|
||||
static int usb_serial_device_probe (struct device *dev)
|
||||
{
|
||||
struct usb_serial_device_type *driver;
|
||||
struct usb_serial_driver *driver;
|
||||
struct usb_serial_port *port;
|
||||
int retval = 0;
|
||||
int minor;
|
||||
|
@ -57,13 +57,13 @@ static int usb_serial_device_probe (struct device *dev)
|
|||
|
||||
driver = port->serial->type;
|
||||
if (driver->port_probe) {
|
||||
if (!try_module_get(driver->owner)) {
|
||||
if (!try_module_get(driver->driver.owner)) {
|
||||
dev_err(dev, "module get failed, exiting\n");
|
||||
retval = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
retval = driver->port_probe (port);
|
||||
module_put(driver->owner);
|
||||
module_put(driver->driver.owner);
|
||||
if (retval)
|
||||
goto exit;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ static int usb_serial_device_probe (struct device *dev)
|
|||
tty_register_device (usb_serial_tty_driver, minor, dev);
|
||||
dev_info(&port->serial->dev->dev,
|
||||
"%s converter now attached to ttyUSB%d\n",
|
||||
driver->name, minor);
|
||||
driver->description, minor);
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
|
@ -80,7 +80,7 @@ exit:
|
|||
|
||||
static int usb_serial_device_remove (struct device *dev)
|
||||
{
|
||||
struct usb_serial_device_type *driver;
|
||||
struct usb_serial_driver *driver;
|
||||
struct usb_serial_port *port;
|
||||
int retval = 0;
|
||||
int minor;
|
||||
|
@ -92,43 +92,38 @@ static int usb_serial_device_remove (struct device *dev)
|
|||
|
||||
driver = port->serial->type;
|
||||
if (driver->port_remove) {
|
||||
if (!try_module_get(driver->owner)) {
|
||||
if (!try_module_get(driver->driver.owner)) {
|
||||
dev_err(dev, "module get failed, exiting\n");
|
||||
retval = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
retval = driver->port_remove (port);
|
||||
module_put(driver->owner);
|
||||
module_put(driver->driver.owner);
|
||||
}
|
||||
exit:
|
||||
minor = port->number;
|
||||
tty_unregister_device (usb_serial_tty_driver, minor);
|
||||
dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
|
||||
driver->name, minor);
|
||||
driver->description, minor);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int usb_serial_bus_register(struct usb_serial_device_type *device)
|
||||
int usb_serial_bus_register(struct usb_serial_driver *driver)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (device->short_name)
|
||||
device->driver.name = (char *)device->short_name;
|
||||
else
|
||||
device->driver.name = (char *)device->name;
|
||||
device->driver.bus = &usb_serial_bus_type;
|
||||
device->driver.probe = usb_serial_device_probe;
|
||||
device->driver.remove = usb_serial_device_remove;
|
||||
device->driver.owner = device->owner;
|
||||
driver->driver.bus = &usb_serial_bus_type;
|
||||
driver->driver.probe = usb_serial_device_probe;
|
||||
driver->driver.remove = usb_serial_device_remove;
|
||||
|
||||
retval = driver_register(&device->driver);
|
||||
retval = driver_register(&driver->driver);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void usb_serial_bus_deregister(struct usb_serial_device_type *device)
|
||||
void usb_serial_bus_deregister(struct usb_serial_driver *driver)
|
||||
{
|
||||
driver_unregister (&device->driver);
|
||||
driver_unregister(&driver->driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -67,15 +67,17 @@ MODULE_DEVICE_TABLE (usb, id_table);
|
|||
|
||||
static struct usb_driver cp2101_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "CP2101",
|
||||
.name = "cp2101",
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = id_table,
|
||||
};
|
||||
|
||||
static struct usb_serial_device_type cp2101_device = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "CP2101",
|
||||
static struct usb_serial_driver cp2101_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cp2101",
|
||||
},
|
||||
.id_table = id_table,
|
||||
.num_interrupt_in = 0,
|
||||
.num_bulk_in = 0,
|
||||
|
|
|
@ -83,10 +83,12 @@ static struct usb_driver cyberjack_driver = {
|
|||
.id_table = id_table,
|
||||
};
|
||||
|
||||
static struct usb_serial_device_type cyberjack_device = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "Reiner SCT Cyberjack USB card reader",
|
||||
.short_name = "cyberjack",
|
||||
static struct usb_serial_driver cyberjack_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cyberjack",
|
||||
},
|
||||
.description = "Reiner SCT Cyberjack USB card reader",
|
||||
.id_table = id_table,
|
||||
.num_interrupt_in = 1,
|
||||
.num_bulk_in = 1,
|
||||
|
|
|
@ -176,10 +176,12 @@ static unsigned int cypress_buf_put(struct cypress_buf *cb, const char *buf, u
|
|||
static unsigned int cypress_buf_get(struct cypress_buf *cb, char *buf, unsigned int count);
|
||||
|
||||
|
||||
static struct usb_serial_device_type cypress_earthmate_device = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "DeLorme Earthmate USB",
|
||||
.short_name = "earthmate",
|
||||
static struct usb_serial_driver cypress_earthmate_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "earthmate",
|
||||
},
|
||||
.description = "DeLorme Earthmate USB",
|
||||
.id_table = id_table_earthmate,
|
||||
.num_interrupt_in = 1,
|
||||
.num_interrupt_out = 1,
|
||||
|
@ -203,10 +205,12 @@ static struct usb_serial_device_type cypress_earthmate_device = {
|
|||
.write_int_callback = cypress_write_int_callback,
|
||||
};
|
||||
|
||||
static struct usb_serial_device_type cypress_hidcom_device = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "HID->COM RS232 Adapter",
|
||||
.short_name = "cyphidcom",
|
||||
static struct usb_serial_driver cypress_hidcom_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cyphidcom",
|
||||
},
|
||||
.description = "HID->COM RS232 Adapter",
|
||||
.id_table = id_table_cyphidcomrs232,
|
||||
.num_interrupt_in = 1,
|
||||
.num_interrupt_out = 1,
|
||||
|
|
|
@ -503,10 +503,12 @@ static struct usb_driver digi_driver = {
|
|||
|
||||
/* device info needed for the Digi serial converter */
|
||||
|
||||
static struct usb_serial_device_type digi_acceleport_2_device = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "Digi 2 port USB adapter",
|
||||
.short_name = "digi_2",
|
||||
static struct usb_serial_driver digi_acceleport_2_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "digi_2",
|
||||
},
|
||||
.description = "Digi 2 port USB adapter",
|
||||
.id_table = id_table_2,
|
||||
.num_interrupt_in = 0,
|
||||
.num_bulk_in = 4,
|
||||
|
@ -530,10 +532,12 @@ static struct usb_serial_device_type digi_acceleport_2_device = {
|
|||
.shutdown = digi_shutdown,
|
||||
};
|
||||
|
||||
static struct usb_serial_device_type digi_acceleport_4_device = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "Digi 4 port USB adapter",
|
||||
.short_name = "digi_4",
|
||||
static struct usb_serial_driver digi_acceleport_4_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "digi_4",
|
||||
},
|
||||
.description = "Digi 4 port USB adapter",
|
||||
.id_table = id_table_4,
|
||||
.num_interrupt_in = 0,
|
||||
.num_bulk_in = 5,
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче