Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
6e277408
Commit
6e277408
authored
Jun 06, 2002
by
Greg Kroah-Hartman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
USB: split some pci specific pieces out of hcd.c into a separate file.
parent
12822f40
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
419 additions
and
394 deletions
+419
-394
drivers/usb/core/Makefile
drivers/usb/core/Makefile
+5
-1
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd-pci.c
+401
-0
drivers/usb/core/hcd.c
drivers/usb/core/hcd.c
+8
-393
drivers/usb/core/hcd.h
drivers/usb/core/hcd.h
+5
-0
No files found.
drivers/usb/core/Makefile
View file @
6e277408
...
...
@@ -2,11 +2,15 @@
# 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
\
config.o
ifeq
($(CONFIG_PCI),y)
usbcore-objs
+=
hcd-pci.o
endif
ifeq
($(CONFIG_USB_DEVICEFS),y)
usbcore-objs
+=
devio.o inode.o drivers.o devices.o
endif
...
...
drivers/usb/core/hcd-pci.c
0 → 100644
View file @
6e277408
/*
* (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
);
}
drivers/usb/core/hcd.c
View file @
6e277408
...
...
@@ -23,24 +23,12 @@
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/uts.h>
/* for UTS_SYSNAME */
#include <asm/byteorder.h>
#ifdef CONFIG_USB_DEBUG
...
...
@@ -52,12 +40,6 @@
#include <linux/usb.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
...
...
@@ -110,9 +92,9 @@ static struct usb_busmap busmap;
DECLARE_MUTEX
(
usb_bus_list_lock
);
/* exported only for usbfs */
/* used when updating hcd data */
s
tatic
s
pinlock_t
hcd_data_lock
=
SPIN_LOCK_UNLOCKED
;
spinlock_t
hcd_data_lock
=
SPIN_LOCK_UNLOCKED
;
st
atic
st
ruct
usb_operations
hcd_operations
;
struct
usb_operations
hcd_operations
;
/*-------------------------------------------------------------------------*/
...
...
@@ -288,7 +270,7 @@ static int ascii2utf (char *s, u8 *utf, int utfmax)
/*
* rh_string - provides manufacturer, product and serial strings for root hub
* @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
* @data: return packet in UTF-16 LE
* @len: length of the return packet
...
...
@@ -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
;
...
...
@@ -590,7 +572,7 @@ void usb_bus_put (struct usb_bus *bus)
/*-------------------------------------------------------------------------*/
/* 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
));
...
...
@@ -922,327 +904,6 @@ void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc)
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)
/*-------------------------------------------------------------------------*/
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
)
{
unsigned
long
flags
;
...
...
@@ -1677,7 +1307,7 @@ static int hcd_unlink_urb (struct urb *urb)
spin_unlock_irqrestore
(
&
urb
->
lock
,
flags
);
if
(
urb
==
(
struct
urb
*
)
hcd
->
rh_timer
.
data
)
{
rh_status_dequeue
(
hcd
,
urb
);
usb_
rh_status_dequeue
(
hcd
,
urb
);
retval
=
0
;
}
else
{
retval
=
hcd
->
driver
->
urb_dequeue
(
hcd
,
urb
);
...
...
@@ -1751,7 +1381,7 @@ static int hcd_free_dev (struct usb_device *udev)
return
0
;
}
st
atic
st
ruct
usb_operations
hcd_operations
=
{
struct
usb_operations
hcd_operations
=
{
allocate:
hcd_alloc_dev
,
get_frame_number:
hcd_get_frame_number
,
submit_urb:
hcd_submit_urb
,
...
...
@@ -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
* @hcd: host controller returning the URB
...
...
drivers/usb/core/hcd.h
View file @
6e277408
...
...
@@ -161,6 +161,11 @@ struct hc_driver {
};
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
struct
pci_dev
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment