Commit 6e277408 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

USB: split some pci specific pieces out of hcd.c into a separate file.

parent 12822f40
...@@ -2,11 +2,15 @@ ...@@ -2,11 +2,15 @@
# Makefile for USB Core files and filesystem # Makefile for USB Core files and filesystem
# #
export-objs := usb.o hcd.o urb.o message.o config.o export-objs := usb.o hcd.o hcd-pci.o urb.o message.o config.o
usbcore-objs := usb.o usb-debug.o hub.o hcd.o urb.o message.o \ usbcore-objs := usb.o usb-debug.o hub.o hcd.o urb.o message.o \
config.o config.o
ifeq ($(CONFIG_PCI),y)
usbcore-objs += hcd-pci.o
endif
ifeq ($(CONFIG_USB_DEVICEFS),y) ifeq ($(CONFIG_USB_DEVICEFS),y)
usbcore-objs += devio.o inode.o drivers.o devices.o usbcore-objs += devio.o inode.o drivers.o devices.o
endif endif
......
/*
* (C) Copyright Linus Torvalds 1999
* (C) Copyright Johannes Erdfelt 1999-2001
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2002
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
#include "hcd.h"
/* PCI-based HCs are normal, but custom bus glue should be ok */
static void hcd_irq (int irq, void *__hcd, struct pt_regs *r);
static void hc_died (struct usb_hcd *hcd);
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_pci_probe - initialize PCI-based HCDs
* @dev: USB Host Controller being probed
* @id: pci hotplug id connecting controller to HCD framework
* Context: !in_interrupt()
*
* Allocates basic PCI resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
*/
int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
struct hc_driver *driver;
unsigned long resource, len;
void *base;
u8 latency, limit;
struct usb_hcd *hcd;
int retval, region;
char buf [8], *bufp = buf;
if (!id || !(driver = (struct hc_driver *) id->driver_data))
return -EINVAL;
if (pci_enable_device (dev) < 0)
return -ENODEV;
if (!dev->irq) {
err ("Found HC with no IRQ. Check BIOS/PCI %s setup!",
dev->slot_name);
return -ENODEV;
}
if (driver->flags & HCD_MEMORY) { // EHCI, OHCI
region = 0;
resource = pci_resource_start (dev, 0);
len = pci_resource_len (dev, 0);
if (!request_mem_region (resource, len, driver->description)) {
dbg ("controller already in use");
return -EBUSY;
}
base = ioremap_nocache (resource, len);
if (base == NULL) {
dbg ("error mapping memory");
retval = -EFAULT;
clean_1:
release_mem_region (resource, len);
err ("init %s fail, %d", dev->slot_name, retval);
return retval;
}
} else { // UHCI
resource = len = 0;
for (region = 0; region < PCI_ROM_RESOURCE; region++) {
if (!(pci_resource_flags (dev, region) & IORESOURCE_IO))
continue;
resource = pci_resource_start (dev, region);
len = pci_resource_len (dev, region);
if (request_region (resource, len,
driver->description))
break;
}
if (region == PCI_ROM_RESOURCE) {
dbg ("no i/o regions available");
return -EBUSY;
}
base = (void *) resource;
}
// driver->start(), later on, will transfer device from
// control by SMM/BIOS to control by Linux (if needed)
pci_set_master (dev);
hcd = driver->hcd_alloc ();
if (hcd == NULL){
dbg ("hcd alloc fail");
retval = -ENOMEM;
clean_2:
if (driver->flags & HCD_MEMORY) {
iounmap (base);
goto clean_1;
} else {
release_region (resource, len);
err ("init %s fail, %d", dev->slot_name, retval);
return retval;
}
}
pci_set_drvdata(dev, hcd);
hcd->driver = driver;
hcd->description = driver->description;
hcd->pdev = dev;
info ("%s @ %s, %s", hcd->description, dev->slot_name, dev->name);
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
if (latency) {
pci_read_config_byte (dev, PCI_MAX_LAT, &limit);
if (limit && limit < latency) {
dbg ("PCI latency reduced to max %d", limit);
pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit);
}
}
#ifndef __sparc__
sprintf (buf, "%d", dev->irq);
#else
bufp = __irq_itoa(dev->irq);
#endif
if (request_irq (dev->irq, hcd_irq, SA_SHIRQ, hcd->description, hcd)
!= 0) {
err ("request interrupt %s failed", bufp);
retval = -EBUSY;
driver->hcd_free (hcd);
goto clean_2;
}
hcd->irq = dev->irq;
hcd->regs = base;
hcd->region = region;
info ("irq %s, %s %p", bufp,
(driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
base);
usb_init_bus (&hcd->self);
hcd->self.op = &hcd_operations;
hcd->self.hcpriv = (void *) hcd;
hcd->self.bus_name = dev->slot_name;
hcd->product_desc = dev->name;
INIT_LIST_HEAD (&hcd->dev_list);
usb_register_bus (&hcd->self);
if ((retval = driver->start (hcd)) < 0)
usb_hcd_pci_remove (dev);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_probe);
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_pci_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
* Store this function in the HCD's struct pci_driver as remove().
*/
void usb_hcd_pci_remove (struct pci_dev *dev)
{
struct usb_hcd *hcd;
struct usb_device *hub;
hcd = pci_get_drvdata(dev);
if (!hcd)
return;
info ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
if (in_interrupt ()) BUG ();
hub = hcd->self.root_hub;
hcd->state = USB_STATE_QUIESCING;
dbg ("%s: roothub graceful disconnect", hcd->self.bus_name);
usb_disconnect (&hub);
hcd->driver->stop (hcd);
hcd->state = USB_STATE_HALT;
free_irq (hcd->irq, hcd);
if (hcd->driver->flags & HCD_MEMORY) {
iounmap (hcd->regs);
release_mem_region (pci_resource_start (dev, 0),
pci_resource_len (dev, 0));
} else {
release_region (pci_resource_start (dev, hcd->region),
pci_resource_len (dev, hcd->region));
}
usb_deregister_bus (&hcd->self);
if (atomic_read (&hcd->self.refcnt) != 1)
err ("usb_hcd_pci_remove %s, count != 1", hcd->self.bus_name);
hcd->driver->hcd_free (hcd);
}
EXPORT_SYMBOL (usb_hcd_pci_remove);
#ifdef CONFIG_PM
/*
* Some "sleep" power levels imply updating struct usb_driver
* to include a callback asking hcds to do their bit by checking
* if all the drivers can suspend. Gets involved with remote wakeup.
*
* If there are pending urbs, then HCs will need to access memory,
* causing extra power drain. New sleep()/wakeup() PM calls might
* be needed, beyond PCI suspend()/resume(). The root hub timer
* still be accessing memory though ...
*
* FIXME: USB should have some power budgeting support working with
* all kinds of hubs.
*
* FIXME: This assumes only D0->D3 suspend and D3->D0 resume.
* D1 and D2 states should do something, yes?
*
* FIXME: Should provide generic enable_wake(), calling pci_enable_wake()
* for all supported states, so that USB remote wakeup can work for any
* devices that support it (and are connected via powered hubs).
*
* FIXME: resume doesn't seem to work right any more...
*/
// 2.4 kernels have issued concurrent resumes (w/APM)
// we defend against that error; PCI doesn't yet.
/**
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
* @dev: USB Host Controller being suspended
*
* Store this function in the HCD's struct pci_driver as suspend().
*/
int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
{
struct usb_hcd *hcd;
int retval;
hcd = pci_get_drvdata(dev);
info ("suspend %s to state %d", hcd->self.bus_name, state);
pci_save_state (dev, hcd->pci_state);
// FIXME for all connected devices, leaf-to-root:
// driver->suspend()
// proposed "new 2.5 driver model" will automate that
/* driver may want to disable DMA etc */
retval = hcd->driver->suspend (hcd, state);
hcd->state = USB_STATE_SUSPENDED;
pci_set_power_state (dev, state);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_suspend);
/**
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
* @dev: USB Host Controller being resumed
*
* Store this function in the HCD's struct pci_driver as resume().
*/
int usb_hcd_pci_resume (struct pci_dev *dev)
{
struct usb_hcd *hcd;
int retval;
hcd = pci_get_drvdata(dev);
info ("resume %s", hcd->self.bus_name);
/* guard against multiple resumes (APM bug?) */
atomic_inc (&hcd->resume_count);
if (atomic_read (&hcd->resume_count) != 1) {
err ("concurrent PCI resumes for %s", hcd->self.bus_name);
retval = 0;
goto done;
}
retval = -EBUSY;
if (hcd->state != USB_STATE_SUSPENDED) {
dbg ("can't resume, not suspended!");
goto done;
}
hcd->state = USB_STATE_RESUMING;
pci_set_power_state (dev, 0);
pci_restore_state (dev, hcd->pci_state);
retval = hcd->driver->resume (hcd);
if (!HCD_IS_RUNNING (hcd->state)) {
dbg ("resume %s failure, retval %d", hcd->self.bus_name, retval);
hc_died (hcd);
// FIXME: recover, reset etc.
} else {
// FIXME for all connected devices, root-to-leaf:
// driver->resume ();
// proposed "new 2.5 driver model" will automate that
}
done:
atomic_dec (&hcd->resume_count);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_resume);
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static void hcd_irq (int irq, void *__hcd, struct pt_regs * r)
{
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
return;
hcd->driver->irq (hcd);
if (hcd->state != start && hcd->state == USB_STATE_HALT)
hc_died (hcd);
}
/*-------------------------------------------------------------------------*/
static void hc_died (struct usb_hcd *hcd)
{
struct list_head *devlist, *urblist;
struct hcd_dev *dev;
struct urb *urb;
unsigned long flags;
/* flag every pending urb as done */
spin_lock_irqsave (&hcd_data_lock, flags);
list_for_each (devlist, &hcd->dev_list) {
dev = list_entry (devlist, struct hcd_dev, dev_list);
list_for_each (urblist, &dev->urb_list) {
urb = list_entry (urblist, struct urb, urb_list);
dbg ("shutdown %s urb %p pipe %x, current status %d",
hcd->self.bus_name, urb, urb->pipe, urb->status);
if (urb->status == -EINPROGRESS)
urb->status = -ESHUTDOWN;
}
}
urb = (struct urb *) hcd->rh_timer.data;
if (urb)
urb->status = -ESHUTDOWN;
spin_unlock_irqrestore (&hcd_data_lock, flags);
if (urb)
usb_rh_status_dequeue (hcd, urb);
hcd->driver->stop (hcd);
}
...@@ -23,24 +23,12 @@ ...@@ -23,24 +23,12 @@
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/uts.h> /* for UTS_SYSNAME */ #include <linux/uts.h> /* for UTS_SYSNAME */
#include <asm/byteorder.h>
#ifdef CONFIG_USB_DEBUG #ifdef CONFIG_USB_DEBUG
...@@ -52,12 +40,6 @@ ...@@ -52,12 +40,6 @@
#include <linux/usb.h> #include <linux/usb.h>
#include "hcd.h" #include "hcd.h"
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
// #define USB_BANDWIDTH_MESSAGES // #define USB_BANDWIDTH_MESSAGES
...@@ -110,9 +92,9 @@ static struct usb_busmap busmap; ...@@ -110,9 +92,9 @@ static struct usb_busmap busmap;
DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */ DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */
/* used when updating hcd data */ /* used when updating hcd data */
static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED; spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
static struct usb_operations hcd_operations; struct usb_operations hcd_operations;
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -288,7 +270,7 @@ static int ascii2utf (char *s, u8 *utf, int utfmax) ...@@ -288,7 +270,7 @@ static int ascii2utf (char *s, u8 *utf, int utfmax)
/* /*
* rh_string - provides manufacturer, product and serial strings for root hub * rh_string - provides manufacturer, product and serial strings for root hub
* @id: the string ID number (1: serial number, 2: product, 3: vendor) * @id: the string ID number (1: serial number, 2: product, 3: vendor)
* @pci_desc: PCI device descriptor for the relevant HC * @hcd: the host controller for this root hub
* @type: string describing our driver * @type: string describing our driver
* @data: return packet in UTF-16 LE * @data: return packet in UTF-16 LE
* @len: length of the return packet * @len: length of the return packet
...@@ -559,7 +541,7 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) ...@@ -559,7 +541,7 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb) void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
{ {
unsigned long flags; unsigned long flags;
...@@ -590,7 +572,7 @@ void usb_bus_put (struct usb_bus *bus) ...@@ -590,7 +572,7 @@ void usb_bus_put (struct usb_bus *bus)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* shared initialization code */ /* shared initialization code */
static void usb_init_bus (struct usb_bus *bus) void usb_init_bus (struct usb_bus *bus)
{ {
memset (&bus->devmap, 0, sizeof(struct usb_devmap)); memset (&bus->devmap, 0, sizeof(struct usb_devmap));
...@@ -922,327 +904,6 @@ void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc) ...@@ -922,327 +904,6 @@ void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc)
EXPORT_SYMBOL (usb_release_bandwidth); EXPORT_SYMBOL (usb_release_bandwidth);
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PCI
/* PCI-based HCs are normal, but custom bus glue should be ok */
static void hcd_irq (int irq, void *__hcd, struct pt_regs *r);
static void hc_died (struct usb_hcd *hcd);
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_pci_probe - initialize PCI-based HCDs
* @dev: USB Host Controller being probed
* @id: pci hotplug id connecting controller to HCD framework
* Context: !in_interrupt()
*
* Allocates basic PCI resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
*/
int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
struct hc_driver *driver;
unsigned long resource, len;
void *base;
u8 latency, limit;
struct usb_hcd *hcd;
int retval, region;
char buf [8], *bufp = buf;
if (!id || !(driver = (struct hc_driver *) id->driver_data))
return -EINVAL;
if (pci_enable_device (dev) < 0)
return -ENODEV;
if (!dev->irq) {
err ("Found HC with no IRQ. Check BIOS/PCI %s setup!",
dev->slot_name);
return -ENODEV;
}
if (driver->flags & HCD_MEMORY) { // EHCI, OHCI
region = 0;
resource = pci_resource_start (dev, 0);
len = pci_resource_len (dev, 0);
if (!request_mem_region (resource, len, driver->description)) {
dbg ("controller already in use");
return -EBUSY;
}
base = ioremap_nocache (resource, len);
if (base == NULL) {
dbg ("error mapping memory");
retval = -EFAULT;
clean_1:
release_mem_region (resource, len);
err ("init %s fail, %d", dev->slot_name, retval);
return retval;
}
} else { // UHCI
resource = len = 0;
for (region = 0; region < PCI_ROM_RESOURCE; region++) {
if (!(pci_resource_flags (dev, region) & IORESOURCE_IO))
continue;
resource = pci_resource_start (dev, region);
len = pci_resource_len (dev, region);
if (request_region (resource, len,
driver->description))
break;
}
if (region == PCI_ROM_RESOURCE) {
dbg ("no i/o regions available");
return -EBUSY;
}
base = (void *) resource;
}
// driver->start(), later on, will transfer device from
// control by SMM/BIOS to control by Linux (if needed)
pci_set_master (dev);
hcd = driver->hcd_alloc ();
if (hcd == NULL){
dbg ("hcd alloc fail");
retval = -ENOMEM;
clean_2:
if (driver->flags & HCD_MEMORY) {
iounmap (base);
goto clean_1;
} else {
release_region (resource, len);
err ("init %s fail, %d", dev->slot_name, retval);
return retval;
}
}
pci_set_drvdata(dev, hcd);
hcd->driver = driver;
hcd->description = driver->description;
hcd->pdev = dev;
info ("%s @ %s, %s", hcd->description, dev->slot_name, dev->name);
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
if (latency) {
pci_read_config_byte (dev, PCI_MAX_LAT, &limit);
if (limit && limit < latency) {
dbg ("PCI latency reduced to max %d", limit);
pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit);
}
}
#ifndef __sparc__
sprintf (buf, "%d", dev->irq);
#else
bufp = __irq_itoa(dev->irq);
#endif
if (request_irq (dev->irq, hcd_irq, SA_SHIRQ, hcd->description, hcd)
!= 0) {
err ("request interrupt %s failed", bufp);
retval = -EBUSY;
driver->hcd_free (hcd);
goto clean_2;
}
hcd->irq = dev->irq;
hcd->regs = base;
hcd->region = region;
info ("irq %s, %s %p", bufp,
(driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
base);
usb_init_bus (&hcd->self);
hcd->self.op = &hcd_operations;
hcd->self.hcpriv = (void *) hcd;
hcd->self.bus_name = dev->slot_name;
hcd->product_desc = dev->name;
INIT_LIST_HEAD (&hcd->dev_list);
usb_register_bus (&hcd->self);
if ((retval = driver->start (hcd)) < 0)
usb_hcd_pci_remove (dev);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_probe);
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_pci_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
* Store this function in the HCD's struct pci_driver as remove().
*/
void usb_hcd_pci_remove (struct pci_dev *dev)
{
struct usb_hcd *hcd;
struct usb_device *hub;
hcd = pci_get_drvdata(dev);
if (!hcd)
return;
info ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
if (in_interrupt ()) BUG ();
hub = hcd->self.root_hub;
hcd->state = USB_STATE_QUIESCING;
dbg ("%s: roothub graceful disconnect", hcd->self.bus_name);
usb_disconnect (&hub);
hcd->driver->stop (hcd);
hcd->state = USB_STATE_HALT;
free_irq (hcd->irq, hcd);
if (hcd->driver->flags & HCD_MEMORY) {
iounmap (hcd->regs);
release_mem_region (pci_resource_start (dev, 0),
pci_resource_len (dev, 0));
} else {
release_region (pci_resource_start (dev, hcd->region),
pci_resource_len (dev, hcd->region));
}
usb_deregister_bus (&hcd->self);
if (atomic_read (&hcd->self.refcnt) != 1)
err ("usb_hcd_pci_remove %s, count != 1", hcd->self.bus_name);
hcd->driver->hcd_free (hcd);
}
EXPORT_SYMBOL (usb_hcd_pci_remove);
#ifdef CONFIG_PM
/*
* Some "sleep" power levels imply updating struct usb_driver
* to include a callback asking hcds to do their bit by checking
* if all the drivers can suspend. Gets involved with remote wakeup.
*
* If there are pending urbs, then HCs will need to access memory,
* causing extra power drain. New sleep()/wakeup() PM calls might
* be needed, beyond PCI suspend()/resume(). The root hub timer
* still be accessing memory though ...
*
* FIXME: USB should have some power budgeting support working with
* all kinds of hubs.
*
* FIXME: This assumes only D0->D3 suspend and D3->D0 resume.
* D1 and D2 states should do something, yes?
*
* FIXME: Should provide generic enable_wake(), calling pci_enable_wake()
* for all supported states, so that USB remote wakeup can work for any
* devices that support it (and are connected via powered hubs).
*
* FIXME: resume doesn't seem to work right any more...
*/
// 2.4 kernels have issued concurrent resumes (w/APM)
// we defend against that error; PCI doesn't yet.
/**
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
* @dev: USB Host Controller being suspended
*
* Store this function in the HCD's struct pci_driver as suspend().
*/
int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
{
struct usb_hcd *hcd;
int retval;
hcd = pci_get_drvdata(dev);
info ("suspend %s to state %d", hcd->self.bus_name, state);
pci_save_state (dev, hcd->pci_state);
// FIXME for all connected devices, leaf-to-root:
// driver->suspend()
// proposed "new 2.5 driver model" will automate that
/* driver may want to disable DMA etc */
retval = hcd->driver->suspend (hcd, state);
hcd->state = USB_STATE_SUSPENDED;
pci_set_power_state (dev, state);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_suspend);
/**
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
* @dev: USB Host Controller being resumed
*
* Store this function in the HCD's struct pci_driver as resume().
*/
int usb_hcd_pci_resume (struct pci_dev *dev)
{
struct usb_hcd *hcd;
int retval;
hcd = pci_get_drvdata(dev);
info ("resume %s", hcd->self.bus_name);
/* guard against multiple resumes (APM bug?) */
atomic_inc (&hcd->resume_count);
if (atomic_read (&hcd->resume_count) != 1) {
err ("concurrent PCI resumes for %s", hcd->self.bus_name);
retval = 0;
goto done;
}
retval = -EBUSY;
if (hcd->state != USB_STATE_SUSPENDED) {
dbg ("can't resume, not suspended!");
goto done;
}
hcd->state = USB_STATE_RESUMING;
pci_set_power_state (dev, 0);
pci_restore_state (dev, hcd->pci_state);
retval = hcd->driver->resume (hcd);
if (!HCD_IS_RUNNING (hcd->state)) {
dbg ("resume %s failure, retval %d", hcd->self.bus_name, retval);
hc_died (hcd);
// FIXME: recover, reset etc.
} else {
// FIXME for all connected devices, root-to-leaf:
// driver->resume ();
// proposed "new 2.5 driver model" will automate that
}
done:
atomic_dec (&hcd->resume_count);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_resume);
#endif /* CONFIG_PM */
#endif
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* /*
...@@ -1285,37 +946,6 @@ static int hcd_alloc_dev (struct usb_device *udev) ...@@ -1285,37 +946,6 @@ static int hcd_alloc_dev (struct usb_device *udev)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void hc_died (struct usb_hcd *hcd)
{
struct list_head *devlist, *urblist;
struct hcd_dev *dev;
struct urb *urb;
unsigned long flags;
/* flag every pending urb as done */
spin_lock_irqsave (&hcd_data_lock, flags);
list_for_each (devlist, &hcd->dev_list) {
dev = list_entry (devlist, struct hcd_dev, dev_list);
list_for_each (urblist, &dev->urb_list) {
urb = list_entry (urblist, struct urb, urb_list);
dbg ("shutdown %s urb %p pipe %x, current status %d",
hcd->self.bus_name, urb, urb->pipe, urb->status);
if (urb->status == -EINPROGRESS)
urb->status = -ESHUTDOWN;
}
}
urb = (struct urb *) hcd->rh_timer.data;
if (urb)
urb->status = -ESHUTDOWN;
spin_unlock_irqrestore (&hcd_data_lock, flags);
if (urb)
rh_status_dequeue (hcd, urb);
hcd->driver->stop (hcd);
}
/*-------------------------------------------------------------------------*/
static void urb_unlink (struct urb *urb) static void urb_unlink (struct urb *urb)
{ {
unsigned long flags; unsigned long flags;
...@@ -1677,7 +1307,7 @@ static int hcd_unlink_urb (struct urb *urb) ...@@ -1677,7 +1307,7 @@ static int hcd_unlink_urb (struct urb *urb)
spin_unlock_irqrestore (&urb->lock, flags); spin_unlock_irqrestore (&urb->lock, flags);
if (urb == (struct urb *) hcd->rh_timer.data) { if (urb == (struct urb *) hcd->rh_timer.data) {
rh_status_dequeue (hcd, urb); usb_rh_status_dequeue (hcd, urb);
retval = 0; retval = 0;
} else { } else {
retval = hcd->driver->urb_dequeue (hcd, urb); retval = hcd->driver->urb_dequeue (hcd, urb);
...@@ -1751,7 +1381,7 @@ static int hcd_free_dev (struct usb_device *udev) ...@@ -1751,7 +1381,7 @@ static int hcd_free_dev (struct usb_device *udev)
return 0; return 0;
} }
static struct usb_operations hcd_operations = { struct usb_operations hcd_operations = {
allocate: hcd_alloc_dev, allocate: hcd_alloc_dev,
get_frame_number: hcd_get_frame_number, get_frame_number: hcd_get_frame_number,
submit_urb: hcd_submit_urb, submit_urb: hcd_submit_urb,
...@@ -1761,21 +1391,6 @@ static struct usb_operations hcd_operations = { ...@@ -1761,21 +1391,6 @@ static struct usb_operations hcd_operations = {
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void hcd_irq (int irq, void *__hcd, struct pt_regs * r)
{
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
return;
hcd->driver->irq (hcd);
if (hcd->state != start && hcd->state == USB_STATE_HALT)
hc_died (hcd);
}
/*-------------------------------------------------------------------------*/
/** /**
* usb_hcd_giveback_urb - return URB from HCD to device driver * usb_hcd_giveback_urb - return URB from HCD to device driver
* @hcd: host controller returning the URB * @hcd: host controller returning the URB
......
...@@ -161,6 +161,11 @@ struct hc_driver { ...@@ -161,6 +161,11 @@ struct hc_driver {
}; };
extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb); extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb);
extern void usb_init_bus (struct usb_bus *bus);
extern void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb);
extern spinlock_t hcd_data_lock;
extern struct usb_operations hcd_operations;
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
struct pci_dev; struct pci_dev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment