mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (148 commits) USB: serial: fix stalled writes USB: remove fake "address-of" expressions USB: fix thread-unsafe anchor utiliy routines USB: usbtest: support test device with only one iso-in or iso-out endpoint USB: usbtest: avoid to free coherent buffer in atomic context USB: xhci: Set DMA mask for host. USB: xhci: Don't flush doorbell writes. USB: xhci: Reduce reads and writes of interrupter registers. USB: xhci: Make xhci_set_hc_event_deq() static. USB: xhci: Minimize HW event ring dequeue pointer writes. USB: xhci: Make xhci_handle_event() static. USB: xhci: Remove unnecessary reads of IRQ_PENDING register. USB: xhci: Performance - move xhci_work() into xhci_irq() USB: xhci: Performance - move interrupt handlers into xhci-ring.c USB: xhci: Performance - move functions that find ep ring. USB:: fix linux/usb.h kernel-doc warnings USB: add USB serial ssu100 driver USB: usb-storage: implement autosuspend USB: ehci: fix remove of ehci debugfs dir USB: Add USB 2.0 to ssb ohci driver ...
This commit is contained in:
commit
9895850b23
@ -7,3 +7,15 @@ Description:
|
||||
0 -> resumed
|
||||
|
||||
(_UDC_ is the name of the USB Device Controller driver)
|
||||
|
||||
What: /sys/devices/platform/_UDC_/gadget/gadget-lunX/nofua
|
||||
Date: July 2010
|
||||
Contact: Andy Shevchenko <andy.shevchenko@gmail.com>
|
||||
Description:
|
||||
Show or set the reaction on the FUA (Force Unit Access) bit in
|
||||
the SCSI WRITE(10,12) commands when a gadget in USB Mass
|
||||
Storage mode.
|
||||
|
||||
Possible values are:
|
||||
1 -> ignore the FUA flag
|
||||
0 -> obey the FUA flag
|
||||
|
@ -9,7 +9,7 @@ compatible with the USB 1.1 standard. It defines three transfer speeds:
|
||||
- "Low Speed" 1.5 Mbit/sec
|
||||
|
||||
USB 1.1 only addressed full speed and low speed. High speed devices
|
||||
can be used on USB 1.1 systems, but they slow down to USB 1.1 speeds.
|
||||
can be used on USB 1.1 systems, but they slow down to USB 1.1 speeds.
|
||||
|
||||
USB 1.1 devices may also be used on USB 2.0 systems. When plugged
|
||||
into an EHCI controller, they are given to a USB 1.1 "companion"
|
||||
|
150
Documentation/usb/gadget_multi.txt
Normal file
150
Documentation/usb/gadget_multi.txt
Normal file
@ -0,0 +1,150 @@
|
||||
-*- org -*-
|
||||
|
||||
* Overview
|
||||
|
||||
The Multifunction Composite Gadget (or g_multi) is a composite gadget
|
||||
that makes extensive use of the composite framework to provide
|
||||
a... multifunction gadget.
|
||||
|
||||
In it's standard configuration it provides a single USB configuration
|
||||
with RNDIS[1] (that is Ethernet), USB CDC[2] ACM (that is serial) and
|
||||
USB Mass Storage functions.
|
||||
|
||||
A CDC ECM (Ethernet) function may be turned on via a Kconfig option
|
||||
and RNDIS can be turned off. If they are both enabled the gadget will
|
||||
have two configurations -- one with RNDIS and another with CDC ECM[3].
|
||||
|
||||
Please not that if you use non-standard configuration (that is enable
|
||||
CDC ECM) you may need to change vendor and/or product ID.
|
||||
|
||||
* Host drivers
|
||||
|
||||
To make use of the gadget one needs to make it work on host side --
|
||||
without that there's no hope of achieving anything with the gadget.
|
||||
As one might expect, things one need to do very from system to system.
|
||||
|
||||
** Linux host drivers
|
||||
|
||||
Since the gadget uses standard composite framework and appears as such
|
||||
to Linux host it does not need any additional drivers on Linux host
|
||||
side. All the functions are handled by respective drivers developed
|
||||
for them.
|
||||
|
||||
This is also true for two configuration set-up with RNDIS
|
||||
configuration being the first one. Linux host will use the second
|
||||
configuration with CDC ECM which should work better under Linux.
|
||||
|
||||
** Windows host drivers
|
||||
|
||||
For the gadget two work under Windows two conditions have to be met:
|
||||
|
||||
*** Detecting as composite gadget
|
||||
|
||||
First of all, Windows need to detect the gadget as an USB composite
|
||||
gadget which on its own have some conditions[4]. If they are met,
|
||||
Windows lets USB Generic Parent Driver[5] handle the device which then
|
||||
tries to much drivers for each individual interface (sort of, don't
|
||||
get into too many details).
|
||||
|
||||
The good news is: you do not have to worry about most of the
|
||||
conditions!
|
||||
|
||||
The only thing to worry is that the gadget has to have a single
|
||||
configuration so a dual RNDIS and CDC ECM gadget won't work unless you
|
||||
create a proper INF -- and of course, if you do submit it!
|
||||
|
||||
*** Installing drivers for each function
|
||||
|
||||
The other, trickier thing is making Windows install drivers for each
|
||||
individual function.
|
||||
|
||||
For mass storage it is trivial since Windows detect it's an interface
|
||||
implementing USB Mass Storage class and selects appropriate driver.
|
||||
|
||||
Things are harder with RDNIS and CDC ACM.
|
||||
|
||||
**** RNDIS
|
||||
|
||||
To make Windows select RNDIS drivers for the first function in the
|
||||
gadget, one needs to use the [[file:linux.inf]] file provided with this
|
||||
document. It "attaches" Window's RNDIS driver to the first interface
|
||||
of the gadget.
|
||||
|
||||
Please note, that while testing we encountered some issues[6] when
|
||||
RNDIS was not the first interface. You do not need to worry abut it
|
||||
unless you are trying to develop your own gadget in which case watch
|
||||
out for this bug.
|
||||
|
||||
**** CDC ACM
|
||||
|
||||
Similarly, [[file:linux-cdc-acm.inf]] is provided for CDC ACM.
|
||||
|
||||
**** Customising the gadget
|
||||
|
||||
If you intend to hack the g_multi gadget be advised that rearranging
|
||||
functions will obviously change interface numbers for each of the
|
||||
functionality. As an effect provided INFs won't work since they have
|
||||
interface numbers hard-coded in them (it's not hard to change those
|
||||
though[7]).
|
||||
|
||||
This also means, that after experimenting with g_multi and changing
|
||||
provided functions one should change gadget's vendor and/or product ID
|
||||
so there will be no collision with other customised gadgets or the
|
||||
original gadget.
|
||||
|
||||
Failing to comply may cause brain damage after wondering for hours why
|
||||
things don't work as intended before realising Windows have cached
|
||||
some drivers information (changing USB port may sometimes help plus
|
||||
you might try using USBDeview[8] to remove the phantom device).
|
||||
|
||||
**** INF testing
|
||||
|
||||
Provided INF files have been tested on Windows XP SP3, Windows Vista
|
||||
and Windows 7, all 32-bit versions. It should work on 64-bit versions
|
||||
as well. It most likely won't work on Windows prior to Windows XP
|
||||
SP2.
|
||||
|
||||
** Other systems
|
||||
|
||||
At this moment, drivers for any other systems have not been tested.
|
||||
Knowing how MacOS is based on BSD and BSD is an Open Source it is
|
||||
believed that it should (read: "I have no idea whether it will") work
|
||||
out-of-the-box.
|
||||
|
||||
For more exotic systems I have even less to say...
|
||||
|
||||
Any testing and drivers *are* *welcome*!
|
||||
|
||||
* Authors
|
||||
|
||||
This document has been written by Michal Nazarewicz
|
||||
([[mailto:mina86@mina86.com]]). INF files have been hacked with
|
||||
support of Marek Szyprowski ([[mailto:m.szyprowski@samsung.com]]) and
|
||||
Xiaofan Chen ([[mailto:xiaofanc@gmail.com]]) basing on the MS RNDIS
|
||||
template[9], Microchip's CDC ACM INF file and David Brownell's
|
||||
([[mailto:dbrownell@users.sourceforge.net]]) original INF files.
|
||||
|
||||
* Footnotes
|
||||
|
||||
[1] Remote Network Driver Interface Specification,
|
||||
[[http://msdn.microsoft.com/en-us/library/ee484414.aspx]].
|
||||
|
||||
[2] Communications Device Class Abstract Control Model, spec for this
|
||||
and other USB classes can be found at
|
||||
[[http://www.usb.org/developers/devclass_docs/]].
|
||||
|
||||
[3] CDC Ethernet Control Model.
|
||||
|
||||
[4] [[http://msdn.microsoft.com/en-us/library/ff537109(v=VS.85).aspx]]
|
||||
|
||||
[5] [[http://msdn.microsoft.com/en-us/library/ff539234(v=VS.85).aspx]]
|
||||
|
||||
[6] To put it in some other nice words, Windows failed to respond to
|
||||
any user input.
|
||||
|
||||
[7] You may find [[http://www.cygnal.org/ubb/Forum9/HTML/001050.html]]
|
||||
useful.
|
||||
|
||||
[8] http://www.nirsoft.net/utils/usb_devices_view.html
|
||||
|
||||
[9] [[http://msdn.microsoft.com/en-us/library/ff570620.aspx]]
|
@ -151,88 +151,23 @@ instructions below to install the host side driver.
|
||||
|
||||
Installing the Windows Host ACM Driver
|
||||
--------------------------------------
|
||||
To use the Windows ACM driver you must have the files "gserial.inf"
|
||||
and "usbser.sys" together in a folder on the Windows machine.
|
||||
|
||||
The "gserial.inf" file is given here.
|
||||
|
||||
-------------------- CUT HERE --------------------
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
Class=Ports
|
||||
ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
|
||||
Provider=%LINUX%
|
||||
DriverVer=08/17/2004,0.0.2.0
|
||||
; Copyright (C) 2004 Al Borchers (alborchers@steinerpoint.com)
|
||||
|
||||
[Manufacturer]
|
||||
%LINUX%=GSerialDeviceList
|
||||
|
||||
[GSerialDeviceList]
|
||||
%GSERIAL%=GSerialInstall, USB\VID_0525&PID_A4A7
|
||||
|
||||
[DestinationDirs]
|
||||
DefaultDestDir=10,System32\Drivers
|
||||
|
||||
[GSerialInstall]
|
||||
CopyFiles=GSerialCopyFiles
|
||||
AddReg=GSerialAddReg
|
||||
|
||||
[GSerialCopyFiles]
|
||||
usbser.sys
|
||||
|
||||
[GSerialAddReg]
|
||||
HKR,,DevLoader,,*ntkern
|
||||
HKR,,NTMPDriver,,usbser.sys
|
||||
HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
|
||||
|
||||
[GSerialInstall.Services]
|
||||
AddService = usbser,0x0002,GSerialService
|
||||
|
||||
[GSerialService]
|
||||
DisplayName = %GSERIAL_DISPLAY_NAME%
|
||||
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
|
||||
StartType = 3 ; SERVICE_DEMAND_START
|
||||
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
|
||||
ServiceBinary = %10%\System32\Drivers\usbser.sys
|
||||
LoadOrderGroup = Base
|
||||
|
||||
[Strings]
|
||||
LINUX = "Linux"
|
||||
GSERIAL = "Gadget Serial"
|
||||
GSERIAL_DISPLAY_NAME = "USB Gadget Serial Driver"
|
||||
-------------------- CUT HERE --------------------
|
||||
|
||||
The "usbser.sys" file comes with various versions of Windows.
|
||||
For example, it can be found on Windows XP typically in
|
||||
|
||||
C:\WINDOWS\Driver Cache\i386\driver.cab
|
||||
|
||||
Or it can be found on the Windows 98SE CD in the "win98" folder
|
||||
in the "DRIVER11.CAB" through "DRIVER20.CAB" cab files. You will
|
||||
need the DOS "expand" program, the Cygwin "cabextract" program, or
|
||||
a similar program to unpack these cab files and extract "usbser.sys".
|
||||
|
||||
For example, to extract "usbser.sys" into the current directory
|
||||
on Windows XP, open a DOS window and run a command like
|
||||
|
||||
expand C:\WINDOWS\Driver~1\i386\driver.cab -F:usbser.sys .
|
||||
|
||||
(Thanks to Nishant Kamat for pointing out this DOS command.)
|
||||
To use the Windows ACM driver you must have the "linux-cdc-acm.inf"
|
||||
file (provided along this document) which supports all recent versions
|
||||
of Windows.
|
||||
|
||||
When the gadget serial driver is loaded and the USB device connected
|
||||
to the Windows host with a USB cable, Windows should recognize the
|
||||
gadget serial device and ask for a driver. Tell Windows to find the
|
||||
driver in the folder that contains "gserial.inf" and "usbser.sys".
|
||||
driver in the folder that contains the "linux-cdc-acm.inf" file.
|
||||
|
||||
For example, on Windows XP, when the gadget serial device is first
|
||||
plugged in, the "Found New Hardware Wizard" starts up. Select
|
||||
"Install from a list or specific location (Advanced)", then on
|
||||
the next screen select "Include this location in the search" and
|
||||
enter the path or browse to the folder containing "gserial.inf" and
|
||||
"usbser.sys". Windows will complain that the Gadget Serial driver
|
||||
has not passed Windows Logo testing, but select "Continue anyway"
|
||||
and finish the driver installation.
|
||||
"Install from a list or specific location (Advanced)", then on the
|
||||
next screen select "Include this location in the search" and enter the
|
||||
path or browse to the folder containing the "linux-cdc-acm.inf" file.
|
||||
Windows will complain that the Gadget Serial driver has not passed
|
||||
Windows Logo testing, but select "Continue anyway" and finish the
|
||||
driver installation.
|
||||
|
||||
On Windows XP, in the "Device Manager" (under "Control Panel",
|
||||
"System", "Hardware") expand the "Ports (COM & LPT)" entry and you
|
||||
@ -345,5 +280,3 @@ you should be able to send data back and forth between the gadget
|
||||
side and host side systems. Anything you type on the terminal
|
||||
window on the gadget side should appear in the terminal window on
|
||||
the host side and vice versa.
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ immediately usable. That means the system must do many things, including:
|
||||
|
||||
- Bind a driver to that device. Bus frameworks do that using a
|
||||
device driver's probe() routine.
|
||||
|
||||
|
||||
- Tell other subsystems to configure the new device. Print
|
||||
queues may need to be enabled, networks brought up, disk
|
||||
partitions mounted, and so on. In some cases these will
|
||||
@ -84,7 +84,7 @@ USB MODUTILS SUPPORT
|
||||
Current versions of module-init-tools will create a "modules.usbmap" file
|
||||
which contains the entries from each driver's MODULE_DEVICE_TABLE. Such
|
||||
files can be used by various user mode policy agents to make sure all the
|
||||
right driver modules get loaded, either at boot time or later.
|
||||
right driver modules get loaded, either at boot time or later.
|
||||
|
||||
See <linux/usb.h> for full information about such table entries; or look
|
||||
at existing drivers. Each table entry describes one or more criteria to
|
||||
|
107
Documentation/usb/linux-cdc-acm.inf
Normal file
107
Documentation/usb/linux-cdc-acm.inf
Normal file
@ -0,0 +1,107 @@
|
||||
; Windows USB CDC ACM Setup File
|
||||
|
||||
; Based on INF template which was:
|
||||
; Copyright (c) 2000 Microsoft Corporation
|
||||
; Copyright (c) 2007 Microchip Technology Inc.
|
||||
; likely to be covered by the MLPL as found at:
|
||||
; <http://msdn.microsoft.com/en-us/cc300389.aspx#MLPL>.
|
||||
; For use only on Windows operating systems.
|
||||
|
||||
[Version]
|
||||
Signature="$Windows NT$"
|
||||
Class=Ports
|
||||
ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
|
||||
Provider=%Linux%
|
||||
DriverVer=11/15/2007,5.1.2600.0
|
||||
|
||||
[Manufacturer]
|
||||
%Linux%=DeviceList, NTamd64
|
||||
|
||||
[DestinationDirs]
|
||||
DefaultDestDir=12
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; Windows 2000/XP/Vista-32bit Sections
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
[DriverInstall.nt]
|
||||
include=mdmcpq.inf
|
||||
CopyFiles=DriverCopyFiles.nt
|
||||
AddReg=DriverInstall.nt.AddReg
|
||||
|
||||
[DriverCopyFiles.nt]
|
||||
usbser.sys,,,0x20
|
||||
|
||||
[DriverInstall.nt.AddReg]
|
||||
HKR,,DevLoader,,*ntkern
|
||||
HKR,,NTMPDriver,,USBSER.sys
|
||||
HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
|
||||
|
||||
[DriverInstall.nt.Services]
|
||||
AddService=usbser, 0x00000002, DriverService.nt
|
||||
|
||||
[DriverService.nt]
|
||||
DisplayName=%SERVICE%
|
||||
ServiceType=1
|
||||
StartType=3
|
||||
ErrorControl=1
|
||||
ServiceBinary=%12%\USBSER.sys
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; Vista-64bit Sections
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
[DriverInstall.NTamd64]
|
||||
include=mdmcpq.inf
|
||||
CopyFiles=DriverCopyFiles.NTamd64
|
||||
AddReg=DriverInstall.NTamd64.AddReg
|
||||
|
||||
[DriverCopyFiles.NTamd64]
|
||||
USBSER.sys,,,0x20
|
||||
|
||||
[DriverInstall.NTamd64.AddReg]
|
||||
HKR,,DevLoader,,*ntkern
|
||||
HKR,,NTMPDriver,,USBSER.sys
|
||||
HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
|
||||
|
||||
[DriverInstall.NTamd64.Services]
|
||||
AddService=usbser, 0x00000002, DriverService.NTamd64
|
||||
|
||||
[DriverService.NTamd64]
|
||||
DisplayName=%SERVICE%
|
||||
ServiceType=1
|
||||
StartType=3
|
||||
ErrorControl=1
|
||||
ServiceBinary=%12%\USBSER.sys
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; Vendor and Product ID Definitions
|
||||
;------------------------------------------------------------------------------
|
||||
; When developing your USB device, the VID and PID used in the PC side
|
||||
; application program and the firmware on the microcontroller must match.
|
||||
; Modify the below line to use your VID and PID. Use the format as shown
|
||||
; below.
|
||||
; Note: One INF file can be used for multiple devices with different
|
||||
; VID and PIDs. For each supported device, append
|
||||
; ",USB\VID_xxxx&PID_yyyy" to the end of the line.
|
||||
;------------------------------------------------------------------------------
|
||||
[SourceDisksFiles]
|
||||
[SourceDisksNames]
|
||||
[DeviceList]
|
||||
%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02
|
||||
|
||||
[DeviceList.NTamd64]
|
||||
%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; String Definitions
|
||||
;------------------------------------------------------------------------------
|
||||
;Modify these strings to customize your device
|
||||
;------------------------------------------------------------------------------
|
||||
[Strings]
|
||||
Linux = "Linux Developer Community"
|
||||
DESCRIPTION = "Gadget Serial"
|
||||
SERVICE = "USB RS-232 Emulation Driver"
|
@ -1,200 +1,66 @@
|
||||
; MS-Windows driver config matching some basic modes of the
|
||||
; Linux-USB Ethernet/RNDIS gadget firmware:
|
||||
;
|
||||
; - RNDIS plus CDC Ethernet ... this may be familiar as a DOCSIS
|
||||
; cable modem profile, and supports most non-Microsoft USB hosts
|
||||
;
|
||||
; - RNDIS plus CDC Subset ... used by hardware that incapable of
|
||||
; full CDC Ethernet support.
|
||||
;
|
||||
; Microsoft only directly supports RNDIS drivers, and bundled them into XP.
|
||||
; The Microsoft "Remote NDIS USB Driver Kit" is currently found at:
|
||||
; http://www.microsoft.com/whdc/device/network/ndis/rmndis.mspx
|
||||
|
||||
; Based on template INF file found at
|
||||
; <http://msdn.microsoft.com/en-us/library/ff570620.aspx>
|
||||
; which was:
|
||||
; Copyright (c) Microsoft Corporation
|
||||
; and released under the MLPL as found at:
|
||||
; <http://msdn.microsoft.com/en-us/cc300389.aspx#MLPL>.
|
||||
; For use only on Windows operating systems.
|
||||
|
||||
[Version]
|
||||
Signature = "$CHICAGO$"
|
||||
Signature = "$Windows NT$"
|
||||
Class = Net
|
||||
ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318}
|
||||
Provider = %Linux%
|
||||
Compatible = 1
|
||||
MillenniumPreferred = .ME
|
||||
DriverVer = 03/30/2004,0.0.0.0
|
||||
; catalog file would be used by WHQL
|
||||
;CatalogFile = Linux.cat
|
||||
DriverVer = 06/21/2006,6.0.6000.16384
|
||||
|
||||
[Manufacturer]
|
||||
%Linux% = LinuxDevices,NT.5.1
|
||||
%Linux% = LinuxDevices,NTx86,NTamd64,NTia64
|
||||
|
||||
[LinuxDevices]
|
||||
; NetChip IDs, used by both firmware modes
|
||||
%LinuxDevice% = RNDIS, USB\VID_0525&PID_a4a2
|
||||
; Decoration for x86 architecture
|
||||
[LinuxDevices.NTx86]
|
||||
%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00
|
||||
|
||||
[LinuxDevices.NT.5.1]
|
||||
%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2
|
||||
; Decoration for x64 architecture
|
||||
[LinuxDevices.NTamd64]
|
||||
%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00
|
||||
|
||||
; Decoration for ia64 architecture
|
||||
[LinuxDevices.NTia64]
|
||||
%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00
|
||||
|
||||
;@@@ This is the common setting for setup
|
||||
[ControlFlags]
|
||||
ExcludeFromSelect=*
|
||||
|
||||
; Windows 98, Windows 98 Second Edition specific sections --------
|
||||
|
||||
[RNDIS]
|
||||
DeviceID = usb8023
|
||||
MaxInstance = 512
|
||||
DriverVer = 03/30/2004,0.0.0.0
|
||||
AddReg = RNDIS_AddReg_98, RNDIS_AddReg_Common
|
||||
|
||||
[RNDIS_AddReg_98]
|
||||
HKR, , DevLoader, 0, *ndis
|
||||
HKR, , DeviceVxDs, 0, usb8023.sys
|
||||
HKR, NDIS, LogDriverName, 0, "usb8023"
|
||||
HKR, NDIS, MajorNdisVersion, 1, 5
|
||||
HKR, NDIS, MinorNdisVersion, 1, 0
|
||||
HKR, Ndi\Interfaces, DefUpper, 0, "ndis3,ndis4,ndis5"
|
||||
HKR, Ndi\Interfaces, DefLower, 0, "ethernet"
|
||||
HKR, Ndi\Interfaces, UpperRange, 0, "ndis3,ndis4,ndis5"
|
||||
HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
|
||||
HKR, Ndi\Install, ndis3, 0, "RNDIS_Install_98"
|
||||
HKR, Ndi\Install, ndis4, 0, "RNDIS_Install_98"
|
||||
HKR, Ndi\Install, ndis5, 0, "RNDIS_Install_98"
|
||||
HKR, Ndi, DeviceId, 0, "USB\VID_0525&PID_a4a2"
|
||||
|
||||
[RNDIS_Install_98]
|
||||
CopyFiles=RNDIS_CopyFiles_98
|
||||
|
||||
[RNDIS_CopyFiles_98]
|
||||
usb8023.sys, usb8023w.sys, , 0
|
||||
rndismp.sys, rndismpw.sys, , 0
|
||||
|
||||
; Windows Millennium Edition specific sections --------------------
|
||||
|
||||
[RNDIS.ME]
|
||||
DeviceID = usb8023
|
||||
MaxInstance = 512
|
||||
DriverVer = 03/30/2004,0.0.0.0
|
||||
AddReg = RNDIS_AddReg_ME, RNDIS_AddReg_Common
|
||||
Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI
|
||||
BusType = 15
|
||||
|
||||
[RNDIS_AddReg_ME]
|
||||
HKR, , DevLoader, 0, *ndis
|
||||
HKR, , DeviceVxDs, 0, usb8023.sys
|
||||
HKR, NDIS, LogDriverName, 0, "usb8023"
|
||||
HKR, NDIS, MajorNdisVersion, 1, 5
|
||||
HKR, NDIS, MinorNdisVersion, 1, 0
|
||||
HKR, Ndi\Interfaces, DefUpper, 0, "ndis3,ndis4,ndis5"
|
||||
HKR, Ndi\Interfaces, DefLower, 0, "ethernet"
|
||||
HKR, Ndi\Interfaces, UpperRange, 0, "ndis3,ndis4,ndis5"
|
||||
HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
|
||||
HKR, Ndi\Install, ndis3, 0, "RNDIS_Install_ME"
|
||||
HKR, Ndi\Install, ndis4, 0, "RNDIS_Install_ME"
|
||||
HKR, Ndi\Install, ndis5, 0, "RNDIS_Install_ME"
|
||||
HKR, Ndi, DeviceId, 0, "USB\VID_0525&PID_a4a2"
|
||||
|
||||
[RNDIS_Install_ME]
|
||||
CopyFiles=RNDIS_CopyFiles_ME
|
||||
|
||||
[RNDIS_CopyFiles_ME]
|
||||
usb8023.sys, usb8023m.sys, , 0
|
||||
rndismp.sys, rndismpm.sys, , 0
|
||||
|
||||
; Windows 2000 specific sections ---------------------------------
|
||||
|
||||
[RNDIS.NT]
|
||||
Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI
|
||||
BusType = 15
|
||||
DriverVer = 03/30/2004,0.0.0.0
|
||||
AddReg = RNDIS_AddReg_NT, RNDIS_AddReg_Common
|
||||
CopyFiles = RNDIS_CopyFiles_NT
|
||||
|
||||
[RNDIS.NT.Services]
|
||||
AddService = USB_RNDIS, 2, RNDIS_ServiceInst_NT, RNDIS_EventLog
|
||||
|
||||
[RNDIS_CopyFiles_NT]
|
||||
; no rename of files on Windows 2000, use the 'k' names as is
|
||||
usb8023k.sys, , , 0
|
||||
rndismpk.sys, , , 0
|
||||
|
||||
[RNDIS_ServiceInst_NT]
|
||||
DisplayName = %ServiceDisplayName%
|
||||
ServiceType = 1
|
||||
StartType = 3
|
||||
ErrorControl = 1
|
||||
ServiceBinary = %12%\usb8023k.sys
|
||||
LoadOrderGroup = NDIS
|
||||
AddReg = RNDIS_WMI_AddReg_NT
|
||||
|
||||
[RNDIS_WMI_AddReg_NT]
|
||||
HKR, , MofImagePath, 0x00020000, "System32\drivers\rndismpk.sys"
|
||||
|
||||
; Windows XP specific sections -----------------------------------
|
||||
|
||||
; DDInstall section
|
||||
; References the in-build Netrndis.inf
|
||||
[RNDIS.NT.5.1]
|
||||
Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI
|
||||
BusType = 15
|
||||
DriverVer = 03/30/2004,0.0.0.0
|
||||
AddReg = RNDIS_AddReg_NT, RNDIS_AddReg_Common
|
||||
; no copyfiles - the files are already in place
|
||||
Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI
|
||||
BusType = 15
|
||||
; NEVER REMOVE THE FOLLOWING REFERENCE FOR NETRNDIS.INF
|
||||
include = netrndis.inf
|
||||
needs = Usb_Rndis.ndi
|
||||
AddReg = Rndis_AddReg_Vista
|
||||
|
||||
; DDInstal.Services section
|
||||
[RNDIS.NT.5.1.Services]
|
||||
AddService = USB_RNDIS, 2, RNDIS_ServiceInst_51, RNDIS_EventLog
|
||||
include = netrndis.inf
|
||||
needs = Usb_Rndis.ndi.Services
|
||||
|
||||
[RNDIS_ServiceInst_51]
|
||||
DisplayName = %ServiceDisplayName%
|
||||
ServiceType = 1
|
||||
StartType = 3
|
||||
ErrorControl = 1
|
||||
ServiceBinary = %12%\usb8023.sys
|
||||
LoadOrderGroup = NDIS
|
||||
AddReg = RNDIS_WMI_AddReg_51
|
||||
; Optional registry settings. You can modify as needed.
|
||||
[RNDIS_AddReg_Vista]
|
||||
HKR, NDI\params\VistaProperty, ParamDesc, 0, %Vista_Property%
|
||||
HKR, NDI\params\VistaProperty, type, 0, "edit"
|
||||
HKR, NDI\params\VistaProperty, LimitText, 0, "12"
|
||||
HKR, NDI\params\VistaProperty, UpperCase, 0, "1"
|
||||
HKR, NDI\params\VistaProperty, default, 0, " "
|
||||
HKR, NDI\params\VistaProperty, optional, 0, "1"
|
||||
|
||||
[RNDIS_WMI_AddReg_51]
|
||||
HKR, , MofImagePath, 0x00020000, "System32\drivers\rndismp.sys"
|
||||
|
||||
; Windows 2000 and Windows XP common sections --------------------
|
||||
|
||||
[RNDIS_AddReg_NT]
|
||||
HKR, Ndi, Service, 0, "USB_RNDIS"
|
||||
HKR, Ndi\Interfaces, UpperRange, 0, "ndis5"
|
||||
HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
|
||||
|
||||
[RNDIS_EventLog]
|
||||
AddReg = RNDIS_EventLog_AddReg
|
||||
|
||||
[RNDIS_EventLog_AddReg]
|
||||
HKR, , EventMessageFile, 0x00020000, "%%SystemRoot%%\System32\netevent.dll"
|
||||
HKR, , TypesSupported, 0x00010001, 7
|
||||
|
||||
; Common Sections -------------------------------------------------
|
||||
|
||||
[RNDIS_AddReg_Common]
|
||||
HKR, NDI\params\NetworkAddress, ParamDesc, 0, %NetworkAddress%
|
||||
HKR, NDI\params\NetworkAddress, type, 0, "edit"
|
||||
HKR, NDI\params\NetworkAddress, LimitText, 0, "12"
|
||||
HKR, NDI\params\NetworkAddress, UpperCase, 0, "1"
|
||||
HKR, NDI\params\NetworkAddress, default, 0, " "
|
||||
HKR, NDI\params\NetworkAddress, optional, 0, "1"
|
||||
|
||||
[SourceDisksNames]
|
||||
1=%SourceDisk%,,1
|
||||
|
||||
[SourceDisksFiles]
|
||||
usb8023m.sys=1
|
||||
rndismpm.sys=1
|
||||
usb8023w.sys=1
|
||||
rndismpw.sys=1
|
||||
usb8023k.sys=1
|
||||
rndismpk.sys=1
|
||||
|
||||
[DestinationDirs]
|
||||
RNDIS_CopyFiles_98 = 10, system32/drivers
|
||||
RNDIS_CopyFiles_ME = 10, system32/drivers
|
||||
RNDIS_CopyFiles_NT = 12
|
||||
; No sys copyfiles - the sys files are already in-build
|
||||
; (part of the operating system).
|
||||
; We do not support XP SP1-, 2003 SP1-, ME, 9x.
|
||||
|
||||
[Strings]
|
||||
ServiceDisplayName = "USB Remote NDIS Network Device Driver"
|
||||
NetworkAddress = "Network Address"
|
||||
Linux = "Linux Developer Community"
|
||||
LinuxDevice = "Linux USB Ethernet/RNDIS Gadget"
|
||||
SourceDisk = "Ethernet/RNDIS Gadget Driver Install Disk"
|
||||
|
||||
Vista_Property = "Optional Vista Property"
|
||||
|
@ -551,9 +551,9 @@ static void __init armadillo5x0_init(void)
|
||||
/* USB */
|
||||
#if defined(CONFIG_USB_ULPI)
|
||||
usbotg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
|
||||
mxc_register_device(&mxc_otg_host, &usbotg_pdata);
|
||||
mxc_register_device(&mxc_usbh2, &usbh2_pdata);
|
||||
|
@ -245,9 +245,9 @@ static struct mxc_usbh_platform_data usbh2_pdata = {
|
||||
static void lilly1131_usb_init(void)
|
||||
{
|
||||
usbotg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
|
||||
mxc_register_device(&mxc_usbh1, &usbh1_pdata);
|
||||
mxc_register_device(&mxc_usbh2, &usbh2_pdata);
|
||||
|
@ -256,7 +256,7 @@ static void __init mxc_board_init(void)
|
||||
#if defined(CONFIG_USB_ULPI)
|
||||
/* USB */
|
||||
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
|
||||
mxc_register_device(&mxc_usbh2, &usbh2_pdata);
|
||||
#endif
|
||||
|
@ -412,7 +412,7 @@ static struct mxc_usbh_platform_data usbh2_pdata = {
|
||||
static int __init moboard_usbh2_init(void)
|
||||
{
|
||||
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
|
||||
return mxc_register_device(&mxc_usbh2, &usbh2_pdata);
|
||||
}
|
||||
|
@ -654,13 +654,13 @@ static void __init mxc_board_init(void)
|
||||
#if defined(CONFIG_USB_ULPI)
|
||||
if (otg_mode_host) {
|
||||
otg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
|
||||
mxc_register_device(&mxc_otg_host, &otg_pdata);
|
||||
}
|
||||
|
||||
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
|
||||
mxc_register_device(&mxc_usbh2, &usbh2_pdata);
|
||||
#endif
|
||||
|
@ -378,7 +378,7 @@ static void __init mxc_board_init(void)
|
||||
#if defined(CONFIG_USB_ULPI)
|
||||
if (otg_mode_host) {
|
||||
otg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
|
||||
mxc_register_device(&mxc_otg_host, &otg_pdata);
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ static struct mxc_usbh_platform_data otg_host_pdata = {
|
||||
static int __init smartbot_otg_host_init(void)
|
||||
{
|
||||
otg_host_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
|
||||
USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
|
||||
return mxc_register_device(&mxc_otg_host, &otg_host_pdata);
|
||||
}
|
||||
|
@ -226,6 +226,7 @@
|
||||
|
||||
#define S3C_DIEPMSK S3C_HSOTG_REG(0x810)
|
||||
|
||||
#define S3C_DIEPMSK_TxFIFOEmpty (1 << 7)
|
||||
#define S3C_DIEPMSK_INEPNakEffMsk (1 << 6)
|
||||
#define S3C_DIEPMSK_INTknEPMisMsk (1 << 5)
|
||||
#define S3C_DIEPMSK_INTknTXFEmpMsk (1 << 4)
|
||||
@ -371,6 +372,7 @@
|
||||
|
||||
#define S3C_DIEPDMA(_a) S3C_HSOTG_REG(0x914 + ((_a) * 0x20))
|
||||
#define S3C_DOEPDMA(_a) S3C_HSOTG_REG(0xB14 + ((_a) * 0x20))
|
||||
#define S3C_DTXFSTS(_a) S3C_HSOTG_REG(0x918 + ((_a) * 0x20))
|
||||
|
||||
#define S3C_EPFIFO(_a) S3C_HSOTG_REG(0x1000 + ((_a) * 0x1000))
|
||||
|
||||
|
@ -215,7 +215,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
|
||||
vhci = hcd_to_vhci(hcd);
|
||||
|
||||
spin_lock_irqsave(&vhci->lock, flags);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
usbip_dbg_vhci_rh("hw accessible flag in on?\n");
|
||||
goto done;
|
||||
}
|
||||
@ -269,7 +269,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
|
||||
u32 prev_port_status[VHCI_NPORTS];
|
||||
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
|
||||
if (!HCD_HW_ACCESSIBLE(hcd))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/*
|
||||
@ -1041,7 +1041,7 @@ static int vhci_bus_resume(struct usb_hcd *hcd)
|
||||
dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
|
||||
|
||||
spin_lock_irq(&vhci->lock);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
rc = -ESHUTDOWN;
|
||||
} else {
|
||||
/* vhci->rh_state = DUMMY_RH_RUNNING;
|
||||
|
@ -41,7 +41,7 @@ obj-$(CONFIG_USB_MICROTEK) += image/
|
||||
obj-$(CONFIG_USB_SERIAL) += serial/
|
||||
|
||||
obj-$(CONFIG_USB) += misc/
|
||||
obj-y += early/
|
||||
obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
|
||||
|
||||
obj-$(CONFIG_USB_ATM) += atm/
|
||||
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
|
||||
|
@ -564,7 +564,7 @@ static void cxacru_timeout_kill(unsigned long data)
|
||||
}
|
||||
|
||||
static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,
|
||||
int* actual_length)
|
||||
int *actual_length)
|
||||
{
|
||||
struct timer_list timer;
|
||||
|
||||
@ -952,7 +952,7 @@ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
|
||||
put_unaligned(cpu_to_le32(addr), (__le32 *)(buf + offb));
|
||||
offb += 4;
|
||||
addr += l;
|
||||
if(l)
|
||||
if (l)
|
||||
memcpy(buf + offb, data + offd, l);
|
||||
if (l < stride)
|
||||
memset(buf + offb + l, 0, stride - l);
|
||||
@ -967,7 +967,7 @@ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
|
||||
}
|
||||
offb = 0;
|
||||
}
|
||||
} while(offd < size);
|
||||
} while (offd < size);
|
||||
dbg("sent fw %#x", fw);
|
||||
|
||||
ret = 0;
|
||||
@ -1043,8 +1043,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
|
||||
if (instance->modem_type->boot_rom_patch) {
|
||||
val = cpu_to_le32(BR_ADDR);
|
||||
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0);
|
||||
}
|
||||
if (ret) {
|
||||
@ -1068,7 +1067,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
|
||||
}
|
||||
|
||||
static int cxacru_find_firmware(struct cxacru_data *instance,
|
||||
char* phase, const struct firmware **fw_p)
|
||||
char *phase, const struct firmware **fw_p)
|
||||
{
|
||||
struct usbatm_data *usbatm = instance->usbatm;
|
||||
struct device *dev = &usbatm->usb_intf->dev;
|
||||
|
@ -753,11 +753,13 @@ static struct usb_driver speedtch_usb_driver = {
|
||||
.id_table = speedtch_usb_ids
|
||||
};
|
||||
|
||||
static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_interfaces) {
|
||||
static void speedtch_release_interfaces(struct usb_device *usb_dev,
|
||||
int num_interfaces)
|
||||
{
|
||||
struct usb_interface *cur_intf;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < num_interfaces; i++)
|
||||
for (i = 0; i < num_interfaces; i++)
|
||||
if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) {
|
||||
usb_set_intfdata(cur_intf, NULL);
|
||||
usb_driver_release_interface(&speedtch_usb_driver, cur_intf);
|
||||
@ -792,7 +794,7 @@ static int speedtch_bind(struct usbatm_data *usbatm,
|
||||
|
||||
/* claim all interfaces */
|
||||
|
||||
for (i=0; i < num_interfaces; i++) {
|
||||
for (i = 0; i < num_interfaces; i++) {
|
||||
cur_intf = usb_ifnum_to_if(usb_dev, i);
|
||||
|
||||
if ((i != ifnum) && cur_intf) {
|
||||
@ -842,7 +844,7 @@ static int speedtch_bind(struct usbatm_data *usbatm,
|
||||
|
||||
use_isoc = 0; /* fall back to bulk if endpoint not found */
|
||||
|
||||
for (i=0; i<desc->desc.bNumEndpoints; i++) {
|
||||
for (i = 0; i < desc->desc.bNumEndpoints; i++) {
|
||||
const struct usb_endpoint_descriptor *endpoint_desc = &desc->endpoint[i].desc;
|
||||
|
||||
if ((endpoint_desc->bEndpointAddress == target_address)) {
|
||||
|
@ -67,6 +67,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
@ -2436,7 +2437,6 @@ UEA_ATTR(firmid, 0);
|
||||
|
||||
/* Retrieve the device End System Identifier (MAC) */
|
||||
|
||||
#define htoi(x) (isdigit(x) ? x-'0' : toupper(x)-'A'+10)
|
||||
static int uea_getesi(struct uea_softc *sc, u_char * esi)
|
||||
{
|
||||
unsigned char mac_str[2 * ETH_ALEN + 1];
|
||||
@ -2447,7 +2447,8 @@ static int uea_getesi(struct uea_softc *sc, u_char * esi)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < ETH_ALEN; i++)
|
||||
esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]);
|
||||
esi[i] = hex_to_bin(mac_str[2 * i]) * 16 +
|
||||
hex_to_bin(mac_str[2 * i + 1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -84,8 +84,8 @@
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
static int usbatm_print_packet(const unsigned char *data, int len);
|
||||
#define PACKETDEBUG(arg...) usbatm_print_packet (arg)
|
||||
#define vdbg(arg...) dbg (arg)
|
||||
#define PACKETDEBUG(arg...) usbatm_print_packet(arg)
|
||||
#define vdbg(arg...) dbg(arg)
|
||||
#else
|
||||
#define PACKETDEBUG(arg...)
|
||||
#define vdbg(arg...)
|
||||
@ -273,8 +273,7 @@ static void usbatm_complete(struct urb *urb)
|
||||
|
||||
if (unlikely(status) &&
|
||||
(!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
|
||||
status != -EILSEQ ))
|
||||
{
|
||||
status != -EILSEQ)) {
|
||||
if (status == -ESHUTDOWN)
|
||||
return;
|
||||
|
||||
@ -494,7 +493,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
|
||||
ptr += data_len;
|
||||
__skb_pull(skb, data_len);
|
||||
|
||||
if(!left)
|
||||
if (!left)
|
||||
continue;
|
||||
|
||||
memset(ptr, 0, left);
|
||||
@ -506,7 +505,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
|
||||
trailer[2] = ctrl->len >> 8;
|
||||
trailer[3] = ctrl->len;
|
||||
|
||||
ctrl->crc = ~ crc32_be(ctrl->crc, ptr, left - 4);
|
||||
ctrl->crc = ~crc32_be(ctrl->crc, ptr, left - 4);
|
||||
|
||||
trailer[4] = ctrl->crc >> 24;
|
||||
trailer[5] = ctrl->crc >> 16;
|
||||
@ -516,8 +515,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
|
||||
target[3] |= 0x2; /* adjust PTI */
|
||||
|
||||
ctrl->len = 0; /* tag this skb finished */
|
||||
}
|
||||
else
|
||||
} else
|
||||
ctrl->crc = crc32_be(ctrl->crc, ptr, left);
|
||||
}
|
||||
|
||||
@ -1146,7 +1144,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
|
||||
instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->bulk_out);
|
||||
|
||||
/* tx buffer size must be a positive multiple of the stride */
|
||||
instance->tx_channel.buf_size = max (instance->tx_channel.stride,
|
||||
instance->tx_channel.buf_size = max(instance->tx_channel.stride,
|
||||
snd_buf_bytes - (snd_buf_bytes % instance->tx_channel.stride));
|
||||
|
||||
/* rx buffer size must be a positive multiple of the endpoint maxpacket */
|
||||
@ -1159,7 +1157,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
|
||||
goto fail_unbind;
|
||||
}
|
||||
|
||||
num_packets = max (1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */
|
||||
num_packets = max(1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */
|
||||
|
||||
if (num_packets * maxpacket > UDSL_MAX_BUF_SIZE)
|
||||
num_packets--;
|
||||
@ -1262,7 +1260,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
|
||||
usb_free_urb(instance->urbs[i]);
|
||||
}
|
||||
|
||||
kfree (instance);
|
||||
kfree(instance);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -1390,9 +1388,8 @@ static int usbatm_print_packet(const unsigned char *data, int len)
|
||||
for (i = 0; i < len;) {
|
||||
buffer[0] = '\0';
|
||||
sprintf(buffer, "%.3d :", i);
|
||||
for (j = 0; (j < 16) && (i < len); j++, i++) {
|
||||
for (j = 0; (j < 16) && (i < len); j++, i++)
|
||||
sprintf(buffer, "%s %2.2x", buffer, data[i]);
|
||||
}
|
||||
dbg("%s", buffer);
|
||||
}
|
||||
return i;
|
||||
|
@ -48,7 +48,7 @@
|
||||
dev_warn(&(instance)->usb_intf->dev, \
|
||||
"failed assertion '%s' at line %d", \
|
||||
__stringify(x), __LINE__); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define usb_err(instance, format, arg...) \
|
||||
@ -59,7 +59,7 @@
|
||||
dev_warn(&(instance)->usb_intf->dev , format , ## arg)
|
||||
#ifdef DEBUG
|
||||
#define usb_dbg(instance, format, arg...) \
|
||||
dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg)
|
||||
dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg)
|
||||
#else
|
||||
#define usb_dbg(instance, format, arg...) \
|
||||
do {} while (0)
|
||||
@ -104,21 +104,21 @@ struct usbatm_data;
|
||||
/*
|
||||
* Assuming all methods exist and succeed, they are called in this order:
|
||||
*
|
||||
* bind, heavy_init, atm_start, ..., atm_stop, unbind
|
||||
* bind, heavy_init, atm_start, ..., atm_stop, unbind
|
||||
*/
|
||||
|
||||
struct usbatm_driver {
|
||||
const char *driver_name;
|
||||
|
||||
/* init device ... can sleep, or cause probe() failure */
|
||||
int (*bind) (struct usbatm_data *, struct usb_interface *,
|
||||
int (*bind) (struct usbatm_data *, struct usb_interface *,
|
||||
const struct usb_device_id *id);
|
||||
|
||||
/* additional device initialization that is too slow to be done in probe() */
|
||||
int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
|
||||
int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
|
||||
|
||||
/* cleanup device ... can sleep, but can't fail */
|
||||
void (*unbind) (struct usbatm_data *, struct usb_interface *);
|
||||
void (*unbind) (struct usbatm_data *, struct usb_interface *);
|
||||
|
||||
/* init ATM device ... can sleep, or cause ATM initialization failure */
|
||||
int (*atm_start) (struct usbatm_data *, struct atm_dev *);
|
||||
@ -126,9 +126,9 @@ struct usbatm_driver {
|
||||
/* cleanup ATM device ... can sleep, but can't fail */
|
||||
void (*atm_stop) (struct usbatm_data *, struct atm_dev *);
|
||||
|
||||
int bulk_in; /* bulk rx endpoint */
|
||||
int isoc_in; /* isochronous rx endpoint */
|
||||
int bulk_out; /* bulk tx endpoint */
|
||||
int bulk_in; /* bulk rx endpoint */
|
||||
int isoc_in; /* isochronous rx endpoint */
|
||||
int bulk_out; /* bulk tx endpoint */
|
||||
|
||||
unsigned rx_padding;
|
||||
unsigned tx_padding;
|
||||
@ -156,7 +156,7 @@ struct usbatm_channel {
|
||||
struct usbatm_data {
|
||||
/******************
|
||||
* public fields *
|
||||
******************/
|
||||
******************/
|
||||
|
||||
/* mini driver */
|
||||
struct usbatm_driver *driver;
|
||||
@ -174,7 +174,7 @@ struct usbatm_data {
|
||||
|
||||
/********************************
|
||||
* private fields - do not use *
|
||||
********************************/
|
||||
********************************/
|
||||
|
||||
struct kref refcount;
|
||||
struct mutex serialize;
|
||||
|
@ -49,13 +49,13 @@ static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX];
|
||||
static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1];
|
||||
static struct usb_driver xusbatm_usb_driver;
|
||||
|
||||
static struct usb_interface *xusbatm_find_intf (struct usb_device *usb_dev, int altsetting, u8 ep)
|
||||
static struct usb_interface *xusbatm_find_intf(struct usb_device *usb_dev, int altsetting, u8 ep)
|
||||
{
|
||||
struct usb_host_interface *alt;
|
||||
struct usb_interface *intf;
|
||||
int i, j;
|
||||
|
||||
for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++)
|
||||
for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++)
|
||||
if ((intf = usb_dev->actconfig->interface[i]) && (alt = usb_altnum_to_altsetting(intf, altsetting)))
|
||||
for (j = 0; j < alt->desc.bNumEndpoints; j++)
|
||||
if (alt->endpoint[j].desc.bEndpointAddress == ep)
|
||||
@ -63,7 +63,7 @@ static struct usb_interface *xusbatm_find_intf (struct usb_device *usb_dev, int
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int xusbatm_capture_intf (struct usbatm_data *usbatm, struct usb_device *usb_dev,
|
||||
static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *usb_dev,
|
||||
struct usb_interface *intf, int altsetting, int claim)
|
||||
{
|
||||
int ifnum = intf->altsetting->desc.bInterfaceNumber;
|
||||
@ -80,7 +80,7 @@ static int xusbatm_capture_intf (struct usbatm_data *usbatm, struct usb_device *
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xusbatm_release_intf (struct usb_device *usb_dev, struct usb_interface *intf, int claimed)
|
||||
static void xusbatm_release_intf(struct usb_device *usb_dev, struct usb_interface *intf, int claimed)
|
||||
{
|
||||
if (claimed) {
|
||||
usb_set_intfdata(intf, NULL);
|
||||
@ -147,7 +147,7 @@ static void xusbatm_unbind(struct usbatm_data *usbatm,
|
||||
|
||||
usb_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
|
||||
for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
|
||||
struct usb_interface *cur_intf = usb_dev->actconfig->interface[i];
|
||||
|
||||
if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) {
|
||||
|
@ -264,7 +264,7 @@ static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
|
||||
if (unlikely(hcd->state == HC_STATE_HALT))
|
||||
return;
|
||||
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
|
||||
if (!HCD_HW_ACCESSIBLE(hcd))
|
||||
return;
|
||||
|
||||
/* Handle Start of frame events */
|
||||
@ -282,7 +282,7 @@ static int c67x00_hcd_start(struct usb_hcd *hcd)
|
||||
{
|
||||
hcd->uses_new_polling = 1;
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
hcd->poll_rh = 1;
|
||||
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H
|
||||
* ->lock locks what interrupt accesses.
|
||||
*/
|
||||
struct usblp {
|
||||
struct usb_device *dev; /* USB device */
|
||||
struct usb_device *dev; /* USB device */
|
||||
struct mutex wmut;
|
||||
struct mutex mut;
|
||||
spinlock_t lock; /* locks rcomplete, wcomplete */
|
||||
@ -169,7 +169,8 @@ struct usblp {
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
static void usblp_dump(struct usblp *usblp) {
|
||||
static void usblp_dump(struct usblp *usblp)
|
||||
{
|
||||
int p;
|
||||
|
||||
dbg("usblp=0x%p", usblp);
|
||||
@ -216,8 +217,8 @@ static const struct quirk_printer_struct quirk_printers[] = {
|
||||
{ 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
|
||||
{ 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
|
||||
{ 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
|
||||
{ 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
|
||||
{ 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
|
||||
{ 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
|
||||
{ 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
|
||||
{ 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
|
||||
{ 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
|
||||
{ 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
|
||||
@ -254,9 +255,8 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i
|
||||
/* High byte has the interface index.
|
||||
Low byte has the alternate setting.
|
||||
*/
|
||||
if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS)) {
|
||||
index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting;
|
||||
}
|
||||
if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS))
|
||||
index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting;
|
||||
|
||||
retval = usb_control_msg(usblp->dev,
|
||||
dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
|
||||
@ -372,7 +372,7 @@ static int usblp_check_status(struct usblp *usblp, int err)
|
||||
return newerr;
|
||||
}
|
||||
|
||||
static int handle_bidir (struct usblp *usblp)
|
||||
static int handle_bidir(struct usblp *usblp)
|
||||
{
|
||||
if (usblp->bidir && usblp->used) {
|
||||
if (usblp_submit_read(usblp) < 0)
|
||||
@ -395,14 +395,13 @@ static int usblp_open(struct inode *inode, struct file *file)
|
||||
if (minor < 0)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock (&usblp_mutex);
|
||||
mutex_lock(&usblp_mutex);
|
||||
|
||||
retval = -ENODEV;
|
||||
intf = usb_find_interface(&usblp_driver, minor);
|
||||
if (!intf) {
|
||||
if (!intf)
|
||||
goto out;
|
||||
}
|
||||
usblp = usb_get_intfdata (intf);
|
||||
usblp = usb_get_intfdata(intf);
|
||||
if (!usblp || !usblp->dev || !usblp->present)
|
||||
goto out;
|
||||
|
||||
@ -433,18 +432,18 @@ static int usblp_open(struct inode *inode, struct file *file)
|
||||
retval = -EIO;
|
||||
}
|
||||
out:
|
||||
mutex_unlock (&usblp_mutex);
|
||||
mutex_unlock(&usblp_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void usblp_cleanup (struct usblp *usblp)
|
||||
static void usblp_cleanup(struct usblp *usblp)
|
||||
{
|
||||
printk(KERN_INFO "usblp%d: removed\n", usblp->minor);
|
||||
|
||||
kfree(usblp->readbuf);
|
||||
kfree (usblp->device_id_string);
|
||||
kfree (usblp->statusbuf);
|
||||
kfree (usblp);
|
||||
kfree(usblp->device_id_string);
|
||||
kfree(usblp->statusbuf);
|
||||
kfree(usblp);
|
||||
}
|
||||
|
||||
static void usblp_unlink_urbs(struct usblp *usblp)
|
||||
@ -458,14 +457,14 @@ static int usblp_release(struct inode *inode, struct file *file)
|
||||
|
||||
usblp->flags &= ~LP_ABORT;
|
||||
|
||||
mutex_lock (&usblp_mutex);
|
||||
mutex_lock(&usblp_mutex);
|
||||
usblp->used = 0;
|
||||
if (usblp->present) {
|
||||
usblp_unlink_urbs(usblp);
|
||||
usb_autopm_put_interface(usblp->intf);
|
||||
} else /* finish cleanup from disconnect */
|
||||
usblp_cleanup (usblp);
|
||||
mutex_unlock (&usblp_mutex);
|
||||
} else /* finish cleanup from disconnect */
|
||||
usblp_cleanup(usblp);
|
||||
mutex_unlock(&usblp_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -495,190 +494,190 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
int twoints[2];
|
||||
int retval = 0;
|
||||
|
||||
mutex_lock (&usblp->mut);
|
||||
mutex_lock(&usblp->mut);
|
||||
if (!usblp->present) {
|
||||
retval = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dbg("usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)", cmd, _IOC_TYPE(cmd),
|
||||
_IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd) );
|
||||
_IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));
|
||||
|
||||
if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */
|
||||
|
||||
switch (_IOC_NR(cmd)) {
|
||||
|
||||
case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
|
||||
if (_IOC_DIR(cmd) != _IOC_READ) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
|
||||
if (_IOC_DIR(cmd) != _IOC_READ) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
length = usblp_cache_device_id_string(usblp);
|
||||
if (length < 0) {
|
||||
retval = length;
|
||||
goto done;
|
||||
}
|
||||
if (length > _IOC_SIZE(cmd))
|
||||
length = _IOC_SIZE(cmd); /* truncate */
|
||||
length = usblp_cache_device_id_string(usblp);
|
||||
if (length < 0) {
|
||||
retval = length;
|
||||
goto done;
|
||||
}
|
||||
if (length > _IOC_SIZE(cmd))
|
||||
length = _IOC_SIZE(cmd); /* truncate */
|
||||
|
||||
if (copy_to_user((void __user *) arg,
|
||||
usblp->device_id_string,
|
||||
(unsigned long) length)) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
if (copy_to_user((void __user *) arg,
|
||||
usblp->device_id_string,
|
||||
(unsigned long) length)) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
case IOCNR_GET_PROTOCOLS:
|
||||
if (_IOC_DIR(cmd) != _IOC_READ ||
|
||||
_IOC_SIZE(cmd) < sizeof(twoints)) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
case IOCNR_GET_PROTOCOLS:
|
||||
if (_IOC_DIR(cmd) != _IOC_READ ||
|
||||
_IOC_SIZE(cmd) < sizeof(twoints)) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
twoints[0] = usblp->current_protocol;
|
||||
twoints[1] = 0;
|
||||
for (i = USBLP_FIRST_PROTOCOL;
|
||||
i <= USBLP_LAST_PROTOCOL; i++) {
|
||||
if (usblp->protocol[i].alt_setting >= 0)
|
||||
twoints[1] |= (1<<i);
|
||||
}
|
||||
twoints[0] = usblp->current_protocol;
|
||||
twoints[1] = 0;
|
||||
for (i = USBLP_FIRST_PROTOCOL;
|
||||
i <= USBLP_LAST_PROTOCOL; i++) {
|
||||
if (usblp->protocol[i].alt_setting >= 0)
|
||||
twoints[1] |= (1<<i);
|
||||
}
|
||||
|
||||
if (copy_to_user((void __user *)arg,
|
||||
(unsigned char *)twoints,
|
||||
sizeof(twoints))) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
if (copy_to_user((void __user *)arg,
|
||||
(unsigned char *)twoints,
|
||||
sizeof(twoints))) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
case IOCNR_SET_PROTOCOL:
|
||||
if (_IOC_DIR(cmd) != _IOC_WRITE) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
case IOCNR_SET_PROTOCOL:
|
||||
if (_IOC_DIR(cmd) != _IOC_WRITE) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (arg == -10) {
|
||||
usblp_dump(usblp);
|
||||
break;
|
||||
}
|
||||
if (arg == -10) {
|
||||
usblp_dump(usblp);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
usblp_unlink_urbs(usblp);
|
||||
retval = usblp_set_protocol(usblp, arg);
|
||||
if (retval < 0) {
|
||||
usblp_set_protocol(usblp,
|
||||
usblp->current_protocol);
|
||||
}
|
||||
break;
|
||||
usblp_unlink_urbs(usblp);
|
||||
retval = usblp_set_protocol(usblp, arg);
|
||||
if (retval < 0) {
|
||||
usblp_set_protocol(usblp,
|
||||
usblp->current_protocol);
|
||||
}
|
||||
break;
|
||||
|
||||
case IOCNR_HP_SET_CHANNEL:
|
||||
if (_IOC_DIR(cmd) != _IOC_WRITE ||
|
||||
le16_to_cpu(usblp->dev->descriptor.idVendor) != 0x03F0 ||
|
||||
usblp->quirks & USBLP_QUIRK_BIDIR) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
case IOCNR_HP_SET_CHANNEL:
|
||||
if (_IOC_DIR(cmd) != _IOC_WRITE ||
|
||||
le16_to_cpu(usblp->dev->descriptor.idVendor) != 0x03F0 ||
|
||||
usblp->quirks & USBLP_QUIRK_BIDIR) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = usblp_hp_channel_change_request(usblp,
|
||||
arg, &newChannel);
|
||||
if (err < 0) {
|
||||
dev_err(&usblp->dev->dev,
|
||||
"usblp%d: error = %d setting "
|
||||
"HP channel\n",
|
||||
usblp->minor, err);
|
||||
retval = -EIO;
|
||||
goto done;
|
||||
}
|
||||
err = usblp_hp_channel_change_request(usblp,
|
||||
arg, &newChannel);
|
||||
if (err < 0) {
|
||||
dev_err(&usblp->dev->dev,
|
||||
"usblp%d: error = %d setting "
|
||||
"HP channel\n",
|
||||
usblp->minor, err);
|
||||
retval = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dbg("usblp%d requested/got HP channel %ld/%d",
|
||||
usblp->minor, arg, newChannel);
|
||||
break;
|
||||
dbg("usblp%d requested/got HP channel %ld/%d",
|
||||
usblp->minor, arg, newChannel);
|
||||
break;
|
||||
|
||||
case IOCNR_GET_BUS_ADDRESS:
|
||||
if (_IOC_DIR(cmd) != _IOC_READ ||
|
||||
_IOC_SIZE(cmd) < sizeof(twoints)) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
case IOCNR_GET_BUS_ADDRESS:
|
||||
if (_IOC_DIR(cmd) != _IOC_READ ||
|
||||
_IOC_SIZE(cmd) < sizeof(twoints)) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
twoints[0] = usblp->dev->bus->busnum;
|
||||
twoints[1] = usblp->dev->devnum;
|
||||
if (copy_to_user((void __user *)arg,
|
||||
(unsigned char *)twoints,
|
||||
sizeof(twoints))) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
twoints[0] = usblp->dev->bus->busnum;
|
||||
twoints[1] = usblp->dev->devnum;
|
||||
if (copy_to_user((void __user *)arg,
|
||||
(unsigned char *)twoints,
|
||||
sizeof(twoints))) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dbg("usblp%d is bus=%d, device=%d",
|
||||
usblp->minor, twoints[0], twoints[1]);
|
||||
break;
|
||||
dbg("usblp%d is bus=%d, device=%d",
|
||||
usblp->minor, twoints[0], twoints[1]);
|
||||
break;
|
||||
|
||||
case IOCNR_GET_VID_PID:
|
||||
if (_IOC_DIR(cmd) != _IOC_READ ||
|
||||
_IOC_SIZE(cmd) < sizeof(twoints)) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
case IOCNR_GET_VID_PID:
|
||||
if (_IOC_DIR(cmd) != _IOC_READ ||
|
||||
_IOC_SIZE(cmd) < sizeof(twoints)) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
twoints[0] = le16_to_cpu(usblp->dev->descriptor.idVendor);
|
||||
twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct);
|
||||
if (copy_to_user((void __user *)arg,
|
||||
(unsigned char *)twoints,
|
||||
sizeof(twoints))) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
twoints[0] = le16_to_cpu(usblp->dev->descriptor.idVendor);
|
||||
twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct);
|
||||
if (copy_to_user((void __user *)arg,
|
||||
(unsigned char *)twoints,
|
||||
sizeof(twoints))) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
|
||||
usblp->minor, twoints[0], twoints[1]);
|
||||
break;
|
||||
dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
|
||||
usblp->minor, twoints[0], twoints[1]);
|
||||
break;
|
||||
|
||||
case IOCNR_SOFT_RESET:
|
||||
if (_IOC_DIR(cmd) != _IOC_NONE) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
retval = usblp_reset(usblp);
|
||||
break;
|
||||
default:
|
||||
retval = -ENOTTY;
|
||||
case IOCNR_SOFT_RESET:
|
||||
if (_IOC_DIR(cmd) != _IOC_NONE) {
|
||||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
retval = usblp_reset(usblp);
|
||||
break;
|
||||
default:
|
||||
retval = -ENOTTY;
|
||||
}
|
||||
else /* old-style ioctl value */
|
||||
switch (cmd) {
|
||||
|
||||
case LPGETSTATUS:
|
||||
if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
|
||||
if (printk_ratelimit())
|
||||
printk(KERN_ERR "usblp%d:"
|
||||
"failed reading printer status (%d)\n",
|
||||
usblp->minor, retval);
|
||||
retval = -EIO;
|
||||
goto done;
|
||||
}
|
||||
status = *usblp->statusbuf;
|
||||
if (copy_to_user ((void __user *)arg, &status, sizeof(int)))
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
case LPGETSTATUS:
|
||||
if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
|
||||
if (printk_ratelimit())
|
||||
printk(KERN_ERR "usblp%d:"
|
||||
"failed reading printer status (%d)\n",
|
||||
usblp->minor, retval);
|
||||
retval = -EIO;
|
||||
goto done;
|
||||
}
|
||||
status = *usblp->statusbuf;
|
||||
if (copy_to_user((void __user *)arg, &status, sizeof(int)))
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
|
||||
case LPABORT:
|
||||
if (arg)
|
||||
usblp->flags |= LP_ABORT;
|
||||
else
|
||||
usblp->flags &= ~LP_ABORT;
|
||||
break;
|
||||
case LPABORT:
|
||||
if (arg)
|
||||
usblp->flags |= LP_ABORT;
|
||||
else
|
||||
usblp->flags &= ~LP_ABORT;
|
||||
break;
|
||||
|
||||
default:
|
||||
retval = -ENOTTY;
|
||||
default:
|
||||
retval = -ENOTTY;
|
||||
}
|
||||
|
||||
done:
|
||||
mutex_unlock (&usblp->mut);
|
||||
mutex_unlock(&usblp->mut);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -840,7 +839,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, lo
|
||||
}
|
||||
|
||||
done:
|
||||
mutex_unlock (&usblp->mut);
|
||||
mutex_unlock(&usblp->mut);
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -1023,7 +1022,7 @@ raise_urb:
|
||||
* while you are sending print data, and you don't try to query the
|
||||
* printer status every couple of milliseconds, you will probably be OK.
|
||||
*/
|
||||
static unsigned int usblp_quirks (__u16 vendor, __u16 product)
|
||||
static unsigned int usblp_quirks(__u16 vendor, __u16 product)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -1031,7 +1030,7 @@ static unsigned int usblp_quirks (__u16 vendor, __u16 product)
|
||||
if (vendor == quirk_printers[i].vendorId &&
|
||||
product == quirk_printers[i].productId)
|
||||
return quirk_printers[i].quirks;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1061,7 +1060,7 @@ static struct usb_class_driver usblp_class = {
|
||||
static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usblp *usblp = usb_get_intfdata (intf);
|
||||
struct usblp *usblp = usb_get_intfdata(intf);
|
||||
|
||||
if (usblp->device_id_string[0] == 0 &&
|
||||
usblp->device_id_string[1] == 0)
|
||||
@ -1075,7 +1074,7 @@ static DEVICE_ATTR(ieee1284_id, S_IRUGO, usblp_show_ieee1284_id, NULL);
|
||||
static int usblp_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev (intf);
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usblp *usblp;
|
||||
int protocol;
|
||||
int retval;
|
||||
@ -1089,7 +1088,7 @@ static int usblp_probe(struct usb_interface *intf,
|
||||
}
|
||||
usblp->dev = dev;
|
||||
mutex_init(&usblp->wmut);
|
||||
mutex_init (&usblp->mut);
|
||||
mutex_init(&usblp->mut);
|
||||
spin_lock_init(&usblp->lock);
|
||||
init_waitqueue_head(&usblp->rwait);
|
||||
init_waitqueue_head(&usblp->wwait);
|
||||
@ -1153,7 +1152,7 @@ static int usblp_probe(struct usb_interface *intf,
|
||||
usblp_check_status(usblp, 0);
|
||||
#endif
|
||||
|
||||
usb_set_intfdata (intf, usblp);
|
||||
usb_set_intfdata(intf, usblp);
|
||||
|
||||
usblp->present = 1;
|
||||
|
||||
@ -1177,7 +1176,7 @@ static int usblp_probe(struct usb_interface *intf,
|
||||
return 0;
|
||||
|
||||
abort_intfdata:
|
||||
usb_set_intfdata (intf, NULL);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
|
||||
abort:
|
||||
kfree(usblp->readbuf);
|
||||
@ -1340,35 +1339,35 @@ static int usblp_cache_device_id_string(struct usblp *usblp)
|
||||
|
||||
static void usblp_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usblp *usblp = usb_get_intfdata (intf);
|
||||
struct usblp *usblp = usb_get_intfdata(intf);
|
||||
|
||||
usb_deregister_dev(intf, &usblp_class);
|
||||
|
||||
if (!usblp || !usblp->dev) {
|
||||
dev_err(&intf->dev, "bogus disconnect\n");
|
||||
BUG ();
|
||||
BUG();
|
||||
}
|
||||
|
||||
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
|
||||
|
||||
mutex_lock (&usblp_mutex);
|
||||
mutex_lock (&usblp->mut);
|
||||
mutex_lock(&usblp_mutex);
|
||||
mutex_lock(&usblp->mut);
|
||||
usblp->present = 0;
|
||||
wake_up(&usblp->wwait);
|
||||
wake_up(&usblp->rwait);
|
||||
usb_set_intfdata (intf, NULL);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
usblp_unlink_urbs(usblp);
|
||||
mutex_unlock (&usblp->mut);
|
||||
mutex_unlock(&usblp->mut);
|
||||
|
||||
if (!usblp->used)
|
||||
usblp_cleanup (usblp);
|
||||
mutex_unlock (&usblp_mutex);
|
||||
usblp_cleanup(usblp);
|
||||
mutex_unlock(&usblp_mutex);
|
||||
}
|
||||
|
||||
static int usblp_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct usblp *usblp = usb_get_intfdata (intf);
|
||||
struct usblp *usblp = usb_get_intfdata(intf);
|
||||
|
||||
usblp_unlink_urbs(usblp);
|
||||
#if 0 /* XXX Do we want this? What if someone is reading, should we fail? */
|
||||
@ -1382,10 +1381,10 @@ static int usblp_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
|
||||
static int usblp_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct usblp *usblp = usb_get_intfdata (intf);
|
||||
struct usblp *usblp = usb_get_intfdata(intf);
|
||||
int r;
|
||||
|
||||
r = handle_bidir (usblp);
|
||||
r = handle_bidir(usblp);
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -1401,7 +1400,7 @@ static const struct usb_device_id usblp_ids[] = {
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, usblp_ids);
|
||||
MODULE_DEVICE_TABLE(usb, usblp_ids);
|
||||
|
||||
static struct usb_driver usblp_driver = {
|
||||
.name = "usblp",
|
||||
@ -1426,8 +1425,8 @@ static void __exit usblp_exit(void)
|
||||
module_init(usblp_init);
|
||||
module_exit(usblp_exit);
|
||||
|
||||
MODULE_AUTHOR( DRIVER_AUTHOR );
|
||||
MODULE_DESCRIPTION( DRIVER_DESC );
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
module_param(proto_bias, int, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(proto_bias, "Favourite protocol number");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1668,13 +1668,10 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
|
||||
default:
|
||||
if (intf->dev.driver)
|
||||
driver = to_usb_driver(intf->dev.driver);
|
||||
if (driver == NULL || driver->ioctl == NULL) {
|
||||
if (driver == NULL || driver->unlocked_ioctl == NULL) {
|
||||
retval = -ENOTTY;
|
||||
} else {
|
||||
/* keep API that guarantees BKL */
|
||||
lock_kernel();
|
||||
retval = driver->ioctl(intf, ctl->ioctl_code, buf);
|
||||
unlock_kernel();
|
||||
retval = driver->unlocked_ioctl(intf, ctl->ioctl_code, buf);
|
||||
if (retval == -ENOIOCTLCMD)
|
||||
retval = -ENOTTY;
|
||||
}
|
||||
|
@ -1742,9 +1742,8 @@ static int usb_runtime_suspend(struct device *dev)
|
||||
}
|
||||
|
||||
/* Prevent the parent from suspending immediately after */
|
||||
else if (udev->parent) {
|
||||
else if (udev->parent)
|
||||
udev->parent->last_busy = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
/* Runtime suspend for a USB interface doesn't mean anything. */
|
||||
@ -1786,21 +1785,19 @@ static int usb_runtime_idle(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops usb_bus_pm_ops = {
|
||||
static const struct dev_pm_ops usb_bus_pm_ops = {
|
||||
.runtime_suspend = usb_runtime_suspend,
|
||||
.runtime_resume = usb_runtime_resume,
|
||||
.runtime_idle = usb_runtime_idle,
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#define usb_bus_pm_ops (*(struct dev_pm_ops *) NULL)
|
||||
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
|
||||
struct bus_type usb_bus_type = {
|
||||
.name = "usb",
|
||||
.match = usb_device_match,
|
||||
.uevent = usb_uevent,
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
.pm = &usb_bus_pm_ops,
|
||||
#endif
|
||||
};
|
||||
|
@ -96,16 +96,21 @@ static ssize_t show_ep_interval(struct device *dev,
|
||||
|
||||
switch (usb_endpoint_type(ep->desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
if (ep->udev->speed == USB_SPEED_HIGH) /* uframes per NAK */
|
||||
if (ep->udev->speed == USB_SPEED_HIGH)
|
||||
/* uframes per NAK */
|
||||
interval = ep->desc->bInterval;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
interval = 1 << (ep->desc->bInterval - 1);
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (ep->udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
|
||||
if (ep->udev->speed == USB_SPEED_HIGH && !in)
|
||||
/* uframes per NAK */
|
||||
interval = ep->desc->bInterval;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
if (ep->udev->speed == USB_SPEED_HIGH)
|
||||
interval = 1 << (ep->desc->bInterval - 1);
|
||||
|
@ -105,8 +105,10 @@ int usb_choose_configuration(struct usb_device *udev)
|
||||
/* When the first config's first interface is one of Microsoft's
|
||||
* pet nonstandard Ethernet-over-USB protocols, ignore it unless
|
||||
* this kernel has enabled the necessary host side driver.
|
||||
* But: Don't ignore it if it's the only config.
|
||||
*/
|
||||
if (i == 0 && desc && (is_rndis(desc) || is_activesync(desc))) {
|
||||
if (i == 0 && num_configs > 1 && desc &&
|
||||
(is_rndis(desc) || is_activesync(desc))) {
|
||||
#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)
|
||||
continue;
|
||||
#else
|
||||
|
@ -66,10 +66,7 @@ static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
* vice versa.
|
||||
*/
|
||||
companion = NULL;
|
||||
for (;;) {
|
||||
companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion);
|
||||
if (!companion)
|
||||
break;
|
||||
for_each_pci_dev(companion) {
|
||||
if (companion->bus != pdev->bus ||
|
||||
PCI_SLOT(companion->devfn) != slot)
|
||||
continue;
|
||||
@ -250,6 +247,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
if (retval != 0)
|
||||
goto err4;
|
||||
set_hs_companion(dev, hcd);
|
||||
|
||||
if (pci_dev_run_wake(dev))
|
||||
pm_runtime_put_noidle(&dev->dev);
|
||||
return retval;
|
||||
|
||||
err4:
|
||||
@ -292,6 +292,17 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
|
||||
if (!hcd)
|
||||
return;
|
||||
|
||||
if (pci_dev_run_wake(dev))
|
||||
pm_runtime_get_noresume(&dev->dev);
|
||||
|
||||
/* Fake an interrupt request in order to give the driver a chance
|
||||
* to test whether the controller hardware has been removed (e.g.,
|
||||
* cardbus physical eject).
|
||||
*/
|
||||
local_irq_disable();
|
||||
usb_hcd_irq(0, hcd);
|
||||
local_irq_enable();
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
if (hcd->driver->flags & HCD_MEMORY) {
|
||||
iounmap(hcd->regs);
|
||||
@ -317,12 +328,34 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev)
|
||||
if (!hcd)
|
||||
return;
|
||||
|
||||
if (hcd->driver->shutdown)
|
||||
if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
|
||||
hcd->driver->shutdown)
|
||||
hcd->driver->shutdown(hcd);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#ifdef CONFIG_PM_OPS
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
static void powermac_set_asic(struct pci_dev *pci_dev, int enable)
|
||||
{
|
||||
/* Enanble or disable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node(pci_dev);
|
||||
if (of_node)
|
||||
pmac_call_feature(PMAC_FTR_USB_ENABLE,
|
||||
of_node, 0, enable);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable)
|
||||
{}
|
||||
|
||||
#endif /* CONFIG_PPC_PMAC */
|
||||
|
||||
static int check_root_hub_suspended(struct device *dev)
|
||||
{
|
||||
@ -337,7 +370,7 @@ static int check_root_hub_suspended(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hcd_pci_suspend(struct device *dev)
|
||||
static int suspend_common(struct device *dev, bool do_wakeup)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
@ -352,13 +385,21 @@ static int hcd_pci_suspend(struct device *dev)
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* We might already be suspended (runtime PM -- not yet written) */
|
||||
if (pci_dev->current_state != PCI_D0)
|
||||
return retval;
|
||||
|
||||
if (hcd->driver->pci_suspend) {
|
||||
retval = hcd->driver->pci_suspend(hcd);
|
||||
/* Optimization: Don't suspend if a root-hub wakeup is
|
||||
* pending and it would cause the HCD to wake up anyway.
|
||||
*/
|
||||
if (do_wakeup && HCD_WAKEUP_PENDING(hcd))
|
||||
return -EBUSY;
|
||||
retval = hcd->driver->pci_suspend(hcd, do_wakeup);
|
||||
suspend_report_result(hcd->driver->pci_suspend, retval);
|
||||
|
||||
/* Check again in case wakeup raced with pci_suspend */
|
||||
if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
|
||||
if (hcd->driver->pci_resume)
|
||||
hcd->driver->pci_resume(hcd, false);
|
||||
retval = -EBUSY;
|
||||
}
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
@ -374,6 +415,48 @@ static int hcd_pci_suspend(struct device *dev)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int resume_common(struct device *dev, int event)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
int retval;
|
||||
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
dev_dbg(dev, "can't resume, not suspended!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
retval = pci_enable_device(pci_dev);
|
||||
if (retval < 0) {
|
||||
dev_err(dev, "can't re-enable after resume, %d!\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pci_set_master(pci_dev);
|
||||
|
||||
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
|
||||
if (hcd->driver->pci_resume) {
|
||||
if (event != PM_EVENT_AUTO_RESUME)
|
||||
wait_for_companions(pci_dev, hcd);
|
||||
|
||||
retval = hcd->driver->pci_resume(hcd,
|
||||
event == PM_EVENT_RESTORE);
|
||||
if (retval) {
|
||||
dev_err(dev, "PCI post-resume error %d!\n", retval);
|
||||
usb_hc_died(hcd);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int hcd_pci_suspend(struct device *dev)
|
||||
{
|
||||
return suspend_common(dev, device_may_wakeup(dev));
|
||||
}
|
||||
|
||||
static int hcd_pci_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
@ -408,16 +491,7 @@ static int hcd_pci_suspend_noirq(struct device *dev)
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
/* Disable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node(pci_dev);
|
||||
if (of_node)
|
||||
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
|
||||
}
|
||||
#endif
|
||||
powermac_set_asic(pci_dev, 0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -425,69 +499,63 @@ static int hcd_pci_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
/* Reenable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node(pci_dev);
|
||||
if (of_node)
|
||||
pmac_call_feature(PMAC_FTR_USB_ENABLE,
|
||||
of_node, 0, 1);
|
||||
}
|
||||
#endif
|
||||
powermac_set_asic(pci_dev, 1);
|
||||
|
||||
/* Go back to D0 and disable remote wakeup */
|
||||
pci_back_from_sleep(pci_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resume_common(struct device *dev, bool hibernated)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
int retval;
|
||||
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
dev_dbg(dev, "can't resume, not suspended!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
retval = pci_enable_device(pci_dev);
|
||||
if (retval < 0) {
|
||||
dev_err(dev, "can't re-enable after resume, %d!\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pci_set_master(pci_dev);
|
||||
|
||||
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
|
||||
if (hcd->driver->pci_resume) {
|
||||
/* This call should be made only during system resume,
|
||||
* not during runtime resume.
|
||||
*/
|
||||
wait_for_companions(pci_dev, hcd);
|
||||
|
||||
retval = hcd->driver->pci_resume(hcd, hibernated);
|
||||
if (retval) {
|
||||
dev_err(dev, "PCI post-resume error %d!\n", retval);
|
||||
usb_hc_died(hcd);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hcd_pci_resume(struct device *dev)
|
||||
{
|
||||
return resume_common(dev, false);
|
||||
return resume_common(dev, PM_EVENT_RESUME);
|
||||
}
|
||||
|
||||
static int hcd_pci_restore(struct device *dev)
|
||||
{
|
||||
return resume_common(dev, true);
|
||||
return resume_common(dev, PM_EVENT_RESTORE);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define hcd_pci_suspend NULL
|
||||
#define hcd_pci_suspend_noirq NULL
|
||||
#define hcd_pci_resume_noirq NULL
|
||||
#define hcd_pci_resume NULL
|
||||
#define hcd_pci_restore NULL
|
||||
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int hcd_pci_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = suspend_common(dev, true);
|
||||
if (retval == 0)
|
||||
powermac_set_asic(to_pci_dev(dev), 0);
|
||||
dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hcd_pci_runtime_resume(struct device *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
powermac_set_asic(to_pci_dev(dev), 1);
|
||||
retval = resume_common(dev, PM_EVENT_AUTO_RESUME);
|
||||
dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define hcd_pci_runtime_suspend NULL
|
||||
#define hcd_pci_runtime_resume NULL
|
||||
|
||||
#endif /* CONFIG_PM_RUNTIME */
|
||||
|
||||
const struct dev_pm_ops usb_hcd_pci_pm_ops = {
|
||||
.suspend = hcd_pci_suspend,
|
||||
.suspend_noirq = hcd_pci_suspend_noirq,
|
||||
@ -501,7 +569,9 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = {
|
||||
.poweroff_noirq = hcd_pci_suspend_noirq,
|
||||
.restore_noirq = hcd_pci_resume_noirq,
|
||||
.restore = hcd_pci_restore,
|
||||
.runtime_suspend = hcd_pci_runtime_suspend,
|
||||
.runtime_resume = hcd_pci_runtime_resume,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
|
||||
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
#endif /* CONFIG_PM_OPS */
|
||||
|
@ -667,7 +667,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
|
||||
unsigned long flags;
|
||||
char buffer[6]; /* Any root hubs with > 31 ports? */
|
||||
|
||||
if (unlikely(!hcd->rh_registered))
|
||||
if (unlikely(!hcd->rh_pollable))
|
||||
return;
|
||||
if (!hcd->uses_new_polling && !hcd->status_urb)
|
||||
return;
|
||||
@ -679,7 +679,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
|
||||
spin_lock_irqsave(&hcd_root_hub_lock, flags);
|
||||
urb = hcd->status_urb;
|
||||
if (urb) {
|
||||
hcd->poll_pending = 0;
|
||||
clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
|
||||
hcd->status_urb = NULL;
|
||||
urb->actual_length = length;
|
||||
memcpy(urb->transfer_buffer, buffer, length);
|
||||
@ -690,7 +690,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
|
||||
spin_lock(&hcd_root_hub_lock);
|
||||
} else {
|
||||
length = 0;
|
||||
hcd->poll_pending = 1;
|
||||
set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
|
||||
}
|
||||
@ -699,7 +699,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
|
||||
* exceed that limit if HZ is 100. The math is more clunky than
|
||||
* maybe expected, this is to make sure that all timers for USB devices
|
||||
* fire at the same time to give the CPU a break inbetween */
|
||||
if (hcd->uses_new_polling ? hcd->poll_rh :
|
||||
if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) :
|
||||
(length == 0 && hcd->status_urb != NULL))
|
||||
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
|
||||
}
|
||||
@ -736,7 +736,7 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
|
||||
mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
|
||||
|
||||
/* If a status change has already occurred, report it ASAP */
|
||||
else if (hcd->poll_pending)
|
||||
else if (HCD_POLL_PENDING(hcd))
|
||||
mod_timer(&hcd->rh_timer, jiffies);
|
||||
retval = 0;
|
||||
done:
|
||||
@ -1150,8 +1150,7 @@ int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
|
||||
* finish unlinking the initial failed usb_set_address()
|
||||
* or device descriptor fetch.
|
||||
*/
|
||||
if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) &&
|
||||
!is_root_hub(urb->dev)) {
|
||||
if (!HCD_SAW_IRQ(hcd) && !is_root_hub(urb->dev)) {
|
||||
dev_warn(hcd->self.controller, "Unlink after no-IRQ? "
|
||||
"Controller is probably using the wrong IRQ.\n");
|
||||
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
@ -1219,6 +1218,11 @@ static int hcd_alloc_coherent(struct usb_bus *bus,
|
||||
{
|
||||
unsigned char *vaddr;
|
||||
|
||||
if (*vaddr_handle == NULL) {
|
||||
WARN_ON_ONCE(1);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr),
|
||||
mem_flags, dma_handle);
|
||||
if (!vaddr)
|
||||
@ -1941,6 +1945,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
|
||||
|
||||
dev_dbg(&rhdev->dev, "usb %s%s\n",
|
||||
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
|
||||
clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
|
||||
if (!hcd->driver->bus_resume)
|
||||
return -ENOENT;
|
||||
if (hcd->state == HC_STATE_RUNNING)
|
||||
@ -1994,8 +1999,10 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave (&hcd_root_hub_lock, flags);
|
||||
if (hcd->rh_registered)
|
||||
if (hcd->rh_registered) {
|
||||
set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
|
||||
queue_work(pm_wq, &hcd->wakeup_work);
|
||||
}
|
||||
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
|
||||
@ -2063,8 +2070,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
|
||||
if (unlikely(hcd->state == HC_STATE_HALT ||
|
||||
!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
|
||||
if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) {
|
||||
rc = IRQ_NONE;
|
||||
} else if (hcd->driver->irq(hcd) == IRQ_NONE) {
|
||||
rc = IRQ_NONE;
|
||||
@ -2079,6 +2085,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
|
||||
local_irq_restore(flags);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_irq);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -2098,7 +2105,7 @@ void usb_hc_died (struct usb_hcd *hcd)
|
||||
|
||||
spin_lock_irqsave (&hcd_root_hub_lock, flags);
|
||||
if (hcd->rh_registered) {
|
||||
hcd->poll_rh = 0;
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
||||
/* make khubd clean up old urbs and devices */
|
||||
usb_set_device_state (hcd->self.root_hub,
|
||||
@ -2217,6 +2224,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
retval = -ENOMEM;
|
||||
goto err_allocate_root_hub;
|
||||
}
|
||||
hcd->self.root_hub = rhdev;
|
||||
|
||||
switch (hcd->driver->flags & HCD_MASK) {
|
||||
case HCD_USB11:
|
||||
@ -2229,9 +2237,8 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
rhdev->speed = USB_SPEED_SUPER;
|
||||
break;
|
||||
default:
|
||||
goto err_allocate_root_hub;
|
||||
goto err_set_rh_speed;
|
||||
}
|
||||
hcd->self.root_hub = rhdev;
|
||||
|
||||
/* wakeup flag init defaults to "everything works" for root hubs,
|
||||
* but drivers can override it in reset() if needed, along with
|
||||
@ -2246,6 +2253,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
dev_err(hcd->self.controller, "can't setup\n");
|
||||
goto err_hcd_driver_setup;
|
||||
}
|
||||
hcd->rh_pollable = 1;
|
||||
|
||||
/* NOTE: root hub and controller capabilities may not be the same */
|
||||
if (device_can_wakeup(hcd->self.controller)
|
||||
@ -2300,23 +2308,38 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
retval);
|
||||
goto error_create_attr_group;
|
||||
}
|
||||
if (hcd->uses_new_polling && hcd->poll_rh)
|
||||
if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
|
||||
usb_hcd_poll_rh_status(hcd);
|
||||
return retval;
|
||||
|
||||
error_create_attr_group:
|
||||
if (HC_IS_RUNNING(hcd->state))
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
spin_lock_irq(&hcd_root_hub_lock);
|
||||
hcd->rh_registered = 0;
|
||||
spin_unlock_irq(&hcd_root_hub_lock);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
cancel_work_sync(&hcd->wakeup_work);
|
||||
#endif
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
usb_disconnect(&hcd->self.root_hub);
|
||||
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
err_register_root_hub:
|
||||
hcd->rh_pollable = 0;
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
del_timer_sync(&hcd->rh_timer);
|
||||
hcd->driver->stop(hcd);
|
||||
hcd->state = HC_STATE_HALT;
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
del_timer_sync(&hcd->rh_timer);
|
||||
err_hcd_driver_start:
|
||||
if (hcd->irq >= 0)
|
||||
free_irq(irqnum, hcd);
|
||||
err_request_irq:
|
||||
err_hcd_driver_setup:
|
||||
hcd->self.root_hub = NULL;
|
||||
usb_put_dev(rhdev);
|
||||
err_set_rh_speed:
|
||||
usb_put_dev(hcd->self.root_hub);
|
||||
err_allocate_root_hub:
|
||||
usb_deregister_bus(&hcd->self);
|
||||
err_register_bus:
|
||||
@ -2335,8 +2358,13 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
|
||||
*/
|
||||
void usb_remove_hcd(struct usb_hcd *hcd)
|
||||
{
|
||||
struct usb_device *rhdev = hcd->self.root_hub;
|
||||
|
||||
dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
|
||||
|
||||
usb_get_dev(rhdev);
|
||||
sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group);
|
||||
|
||||
if (HC_IS_RUNNING (hcd->state))
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
|
||||
@ -2349,19 +2377,30 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
||||
cancel_work_sync(&hcd->wakeup_work);
|
||||
#endif
|
||||
|
||||
sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group);
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
usb_disconnect(&hcd->self.root_hub);
|
||||
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
|
||||
/* Prevent any more root-hub status calls from the timer.
|
||||
* The HCD might still restart the timer (if a port status change
|
||||
* interrupt occurs), but usb_hcd_poll_rh_status() won't invoke
|
||||
* the hub_status_data() callback.
|
||||
*/
|
||||
hcd->rh_pollable = 0;
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
del_timer_sync(&hcd->rh_timer);
|
||||
|
||||
hcd->driver->stop(hcd);
|
||||
hcd->state = HC_STATE_HALT;
|
||||
|
||||
hcd->poll_rh = 0;
|
||||
/* In case the HCD restarted the timer, stop it again. */
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
del_timer_sync(&hcd->rh_timer);
|
||||
|
||||
if (hcd->irq >= 0)
|
||||
free_irq(hcd->irq, hcd);
|
||||
|
||||
usb_put_dev(hcd->self.root_hub);
|
||||
usb_deregister_bus(&hcd->self);
|
||||
hcd_buffer_destroy(hcd);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
@ -1294,6 +1295,7 @@ descriptor_error:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* No BKL needed */
|
||||
static int
|
||||
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
|
||||
{
|
||||
@ -1801,7 +1803,6 @@ int usb_new_device(struct usb_device *udev)
|
||||
pm_runtime_set_active(&udev->dev);
|
||||
pm_runtime_enable(&udev->dev);
|
||||
|
||||
usb_detect_quirks(udev);
|
||||
err = usb_enumerate_device(udev); /* Read descriptors */
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
@ -2880,7 +2881,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
|
||||
/* notify HCD that we have a device connected and addressed */
|
||||
if (hcd->driver->update_device)
|
||||
hcd->driver->update_device(hcd, udev);
|
||||
fail:
|
||||
if (retval) {
|
||||
hub_port_disable(hub, port1, 0);
|
||||
@ -3111,6 +3114,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
if (status < 0)
|
||||
goto loop;
|
||||
|
||||
usb_detect_quirks(udev);
|
||||
if (udev->quirks & USB_QUIRK_DELAY_INIT)
|
||||
msleep(1000);
|
||||
|
||||
/* consecutive bus-powered hubs aren't reliable; they can
|
||||
* violate the voltage drop budget. if the new child has
|
||||
* a "powered" LED, users should notice we didn't enable it
|
||||
@ -3463,7 +3470,7 @@ static struct usb_driver hub_driver = {
|
||||
.reset_resume = hub_reset_resume,
|
||||
.pre_reset = hub_pre_reset,
|
||||
.post_reset = hub_post_reset,
|
||||
.ioctl = hub_ioctl,
|
||||
.unlocked_ioctl = hub_ioctl,
|
||||
.id_table = hub_id_table,
|
||||
.supports_autosuspend = 1,
|
||||
};
|
||||
|
@ -265,13 +265,9 @@ static int remount(struct super_block *sb, int *flags, char *data)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lock_kernel();
|
||||
|
||||
if (usbfs_mount && usbfs_mount->mnt_sb)
|
||||
update_sb(usbfs_mount->mnt_sb);
|
||||
|
||||
unlock_kernel();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,9 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* Creative SB Audigy 2 NX */
|
||||
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech Harmony 700-series */
|
||||
{ USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
|
||||
/* Philips PSC805 audio device */
|
||||
{ USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
|
@ -137,6 +137,16 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_anchor_urb);
|
||||
|
||||
/* Callers must hold anchor->lock */
|
||||
static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
|
||||
{
|
||||
urb->anchor = NULL;
|
||||
list_del(&urb->anchor_list);
|
||||
usb_put_urb(urb);
|
||||
if (list_empty(&anchor->urb_list))
|
||||
wake_up(&anchor->wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_unanchor_urb - unanchors an URB
|
||||
* @urb: pointer to the urb to anchor
|
||||
@ -156,17 +166,14 @@ void usb_unanchor_urb(struct urb *urb)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
if (unlikely(anchor != urb->anchor)) {
|
||||
/* we've lost the race to another thread */
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
return;
|
||||
}
|
||||
urb->anchor = NULL;
|
||||
list_del(&urb->anchor_list);
|
||||
/*
|
||||
* At this point, we could be competing with another thread which
|
||||
* has the same intention. To protect the urb from being unanchored
|
||||
* twice, only the winner of the race gets the job.
|
||||
*/
|
||||
if (likely(anchor == urb->anchor))
|
||||
__usb_unanchor_urb(urb, anchor);
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
usb_put_urb(urb);
|
||||
if (list_empty(&anchor->urb_list))
|
||||
wake_up(&anchor->wait);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unanchor_urb);
|
||||
|
||||
@ -749,20 +756,11 @@ EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
|
||||
void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
|
||||
{
|
||||
struct urb *victim;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
while (!list_empty(&anchor->urb_list)) {
|
||||
victim = list_entry(anchor->urb_list.prev, struct urb,
|
||||
anchor_list);
|
||||
usb_get_urb(victim);
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
/* this will unanchor the URB */
|
||||
while ((victim = usb_get_from_anchor(anchor)) != NULL) {
|
||||
usb_unlink_urb(victim);
|
||||
usb_put_urb(victim);
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
|
||||
|
||||
@ -799,12 +797,11 @@ struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
|
||||
victim = list_entry(anchor->urb_list.next, struct urb,
|
||||
anchor_list);
|
||||
usb_get_urb(victim);
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
usb_unanchor_urb(victim);
|
||||
__usb_unanchor_urb(victim, anchor);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
victim = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
|
||||
return victim;
|
||||
}
|
||||
@ -826,12 +823,7 @@ void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
|
||||
while (!list_empty(&anchor->urb_list)) {
|
||||
victim = list_entry(anchor->urb_list.prev, struct urb,
|
||||
anchor_list);
|
||||
usb_get_urb(victim);
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
/* this may free the URB */
|
||||
usb_unanchor_urb(victim);
|
||||
usb_put_urb(victim);
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
__usb_unanchor_urb(victim, anchor);
|
||||
}
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
}
|
||||
|
@ -317,10 +317,6 @@ static const struct dev_pm_ops usb_device_pm_ops = {
|
||||
.restore = usb_dev_restore,
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#define usb_device_pm_ops (*(struct dev_pm_ops *) NULL)
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
||||
@ -338,7 +334,9 @@ struct device_type usb_device_type = {
|
||||
.release = usb_release_dev,
|
||||
.uevent = usb_dev_uevent,
|
||||
.devnode = usb_devnode,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &usb_device_pm_ops,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
@ -714,6 +714,7 @@ config USB_GADGETFS
|
||||
config USB_FUNCTIONFS
|
||||
tristate "Function Filesystem (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
|
||||
help
|
||||
The Function Filesystem (FunctioFS) lets one create USB
|
||||
composite functions in user space in the same way as GadgetFS
|
||||
@ -722,31 +723,31 @@ config USB_FUNCTIONFS
|
||||
implemented in kernel space (for instance Ethernet, serial or
|
||||
mass storage) and other are implemented in user space.
|
||||
|
||||
If you say "y" or "m" here you will be able what kind of
|
||||
configurations the gadget will provide.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build
|
||||
a dynamically linked module called "g_ffs".
|
||||
|
||||
config USB_FUNCTIONFS_ETH
|
||||
bool "Include CDC ECM (Ethernet) function"
|
||||
bool "Include configuration with CDC ECM (Ethernet)"
|
||||
depends on USB_FUNCTIONFS && NET
|
||||
help
|
||||
Include an CDC ECM (Ethernet) funcion in the CDC ECM (Funcion)
|
||||
Filesystem. If you also say "y" to the RNDIS query below the
|
||||
gadget will have two configurations.
|
||||
Include a configuration with CDC ECM funcion (Ethernet) and the
|
||||
Funcion Filesystem.
|
||||
|
||||
config USB_FUNCTIONFS_RNDIS
|
||||
bool "Include RNDIS (Ethernet) function"
|
||||
bool "Include configuration with RNDIS (Ethernet)"
|
||||
depends on USB_FUNCTIONFS && NET
|
||||
help
|
||||
Include an RNDIS (Ethernet) funcion in the Funcion Filesystem.
|
||||
If you also say "y" to the CDC ECM query above the gadget will
|
||||
have two configurations.
|
||||
Include a configuration with RNDIS funcion (Ethernet) and the Filesystem.
|
||||
|
||||
config USB_FUNCTIONFS_GENERIC
|
||||
bool "Include 'pure' configuration"
|
||||
depends on USB_FUNCTIONFS && (USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
|
||||
depends on USB_FUNCTIONFS
|
||||
help
|
||||
Include a configuration with FunctionFS and no Ethernet
|
||||
configuration.
|
||||
Include a configuration with the Function Filesystem alone with
|
||||
no Ethernet interface.
|
||||
|
||||
config USB_FILE_STORAGE
|
||||
tristate "File-backed Storage Gadget"
|
||||
@ -863,6 +864,7 @@ config USB_G_NOKIA
|
||||
config USB_G_MULTI
|
||||
tristate "Multifunction Composite Gadget (EXPERIMENTAL)"
|
||||
depends on BLOCK && NET
|
||||
select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
|
||||
help
|
||||
The Multifunction Composite Gadget provides Ethernet (RNDIS
|
||||
and/or CDC Ethernet), mass storage and ACM serial link
|
||||
@ -913,6 +915,34 @@ config USB_G_HID
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_hid".
|
||||
|
||||
config USB_G_DBGP
|
||||
tristate "EHCI Debug Device Gadget"
|
||||
help
|
||||
This gadget emulates an EHCI Debug device. This is useful when you want
|
||||
to interact with an EHCI Debug Port.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_dbgp".
|
||||
|
||||
if USB_G_DBGP
|
||||
choice
|
||||
prompt "EHCI Debug Device mode"
|
||||
default USB_G_DBGP_SERIAL
|
||||
|
||||
config USB_G_DBGP_PRINTK
|
||||
depends on USB_G_DBGP
|
||||
bool "printk"
|
||||
help
|
||||
Directly printk() received data. No interaction.
|
||||
|
||||
config USB_G_DBGP_SERIAL
|
||||
depends on USB_G_DBGP
|
||||
bool "serial"
|
||||
help
|
||||
Userland can interact using /dev/ttyGSxxx.
|
||||
endchoice
|
||||
endif
|
||||
|
||||
# put drivers that need isochronous transfer support (for audio
|
||||
# or video class gadget drivers), or specific hardware, here.
|
||||
config USB_G_WEBCAM
|
||||
|
@ -44,6 +44,7 @@ g_printer-objs := printer.o
|
||||
g_cdc-objs := cdc2.o
|
||||
g_multi-objs := multi.o
|
||||
g_hid-objs := hid.o
|
||||
g_dbgp-objs := dbgp.o
|
||||
g_nokia-objs := nokia.o
|
||||
g_webcam-objs := webcam.o
|
||||
|
||||
@ -52,7 +53,6 @@ obj-$(CONFIG_USB_AUDIO) += g_audio.o
|
||||
obj-$(CONFIG_USB_ETH) += g_ether.o
|
||||
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
|
||||
obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
|
||||
obj-$(CONFIG_USB_ETH_FUNCTIONFS) += g_eth_ffs.o
|
||||
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
|
||||
obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
|
||||
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
|
||||
@ -60,6 +60,7 @@ obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
|
||||
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
|
||||
obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
|
||||
obj-$(CONFIG_USB_G_HID) += g_hid.o
|
||||
obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
|
||||
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
|
||||
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
|
||||
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
|
||||
|
@ -89,7 +89,7 @@ static const struct usb_descriptor_header *otg_desc[] = {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init audio_do_config(struct usb_configuration *c)
|
||||
static int __ref audio_do_config(struct usb_configuration *c)
|
||||
{
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
@ -113,7 +113,7 @@ static struct usb_configuration audio_config_driver = {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init audio_bind(struct usb_composite_dev *cdev)
|
||||
static int __ref audio_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
int status;
|
||||
|
@ -129,7 +129,7 @@ static u8 hostaddr[ETH_ALEN];
|
||||
/*
|
||||
* We _always_ have both CDC ECM and CDC ACM functions.
|
||||
*/
|
||||
static int __init cdc_do_config(struct usb_configuration *c)
|
||||
static int __ref cdc_do_config(struct usb_configuration *c)
|
||||
{
|
||||
int status;
|
||||
|
||||
@ -159,7 +159,7 @@ static struct usb_configuration cdc_config_driver = {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init cdc_bind(struct usb_composite_dev *cdev)
|
||||
static int __ref cdc_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
|
@ -673,20 +673,83 @@ static int get_string(struct usb_composite_dev *cdev,
|
||||
* string IDs. Drivers for functions, configurations, or gadgets will
|
||||
* then store that ID in the appropriate descriptors and string table.
|
||||
*
|
||||
* All string identifier should be allocated using this routine, to
|
||||
* ensure that for example different functions don't wrongly assign
|
||||
* different meanings to the same identifier.
|
||||
* All string identifier should be allocated using this,
|
||||
* @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure
|
||||
* that for example different functions don't wrongly assign different
|
||||
* meanings to the same identifier.
|
||||
*/
|
||||
int usb_string_id(struct usb_composite_dev *cdev)
|
||||
{
|
||||
if (cdev->next_string_id < 254) {
|
||||
/* string id 0 is reserved */
|
||||
/* string id 0 is reserved by USB spec for list of
|
||||
* supported languages */
|
||||
/* 255 reserved as well? -- mina86 */
|
||||
cdev->next_string_id++;
|
||||
return cdev->next_string_id;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_string_ids() - allocate unused string IDs in batch
|
||||
* @cdev: the device whose string descriptor IDs are being allocated
|
||||
* @str: an array of usb_string objects to assign numbers to
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* @usb_string_ids() is called from bind() callbacks to allocate
|
||||
* string IDs. Drivers for functions, configurations, or gadgets will
|
||||
* then copy IDs from the string table to the appropriate descriptors
|
||||
* and string table for other languages.
|
||||
*
|
||||
* All string identifier should be allocated using this,
|
||||
* @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
|
||||
* example different functions don't wrongly assign different meanings
|
||||
* to the same identifier.
|
||||
*/
|
||||
int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
|
||||
{
|
||||
int next = cdev->next_string_id;
|
||||
|
||||
for (; str->s; ++str) {
|
||||
if (unlikely(next >= 254))
|
||||
return -ENODEV;
|
||||
str->id = ++next;
|
||||
}
|
||||
|
||||
cdev->next_string_id = next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_string_ids_n() - allocate unused string IDs in batch
|
||||
* @cdev: the device whose string descriptor IDs are being allocated
|
||||
* @n: number of string IDs to allocate
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns the first requested ID. This ID and next @n-1 IDs are now
|
||||
* valid IDs. At least providind that @n is non zore because if it
|
||||
* is, returns last requested ID which is now very useful information.
|
||||
*
|
||||
* @usb_string_ids_n() is called from bind() callbacks to allocate
|
||||
* string IDs. Drivers for functions, configurations, or gadgets will
|
||||
* then store that ID in the appropriate descriptors and string table.
|
||||
*
|
||||
* All string identifier should be allocated using this,
|
||||
* @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
|
||||
* example different functions don't wrongly assign different meanings
|
||||
* to the same identifier.
|
||||
*/
|
||||
int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
|
||||
{
|
||||
unsigned next = c->next_string_id;
|
||||
if (unlikely(n > 254 || (unsigned)next + n > 254))
|
||||
return -ENODEV;
|
||||
c->next_string_id += n;
|
||||
return next + 1;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
@ -893,6 +956,8 @@ static void composite_disconnect(struct usb_gadget *gadget)
|
||||
spin_lock_irqsave(&cdev->lock, flags);
|
||||
if (cdev->config)
|
||||
reset_config(cdev);
|
||||
if (composite->disconnect)
|
||||
composite->disconnect(cdev);
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
}
|
||||
|
||||
|
434
drivers/usb/gadget/dbgp.c
Normal file
434
drivers/usb/gadget/dbgp.c
Normal file
@ -0,0 +1,434 @@
|
||||
/*
|
||||
* dbgp.c -- EHCI Debug Port device gadget
|
||||
*
|
||||
* Copyright (C) 2010 Stephane Duverger
|
||||
*
|
||||
* Released under the GPLv2.
|
||||
*
|
||||
*/
|
||||
|
||||
/* verbose messages */
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* See comments in "zero.c" */
|
||||
#include "epautoconf.c"
|
||||
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
#include "u_serial.c"
|
||||
#endif
|
||||
|
||||
#define DRIVER_VENDOR_ID 0x0525 /* NetChip */
|
||||
#define DRIVER_PRODUCT_ID 0xc0de /* undefined */
|
||||
|
||||
#define USB_DEBUG_MAX_PACKET_SIZE 8
|
||||
#define DBGP_REQ_EP0_LEN 128
|
||||
#define DBGP_REQ_LEN 512
|
||||
|
||||
static struct dbgp {
|
||||
struct usb_gadget *gadget;
|
||||
struct usb_request *req;
|
||||
struct usb_ep *i_ep;
|
||||
struct usb_ep *o_ep;
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
struct gserial *serial;
|
||||
#endif
|
||||
} dbgp;
|
||||
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = __constant_cpu_to_le16(0x0200),
|
||||
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID),
|
||||
.idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID),
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
static struct usb_debug_descriptor dbg_desc = {
|
||||
.bLength = sizeof dbg_desc,
|
||||
.bDescriptorType = USB_DT_DEBUG,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor i_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor o_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_G_DBGP_PRINTK
|
||||
static int dbgp_consume(char *buf, unsigned len)
|
||||
{
|
||||
char c;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
c = buf[len-1];
|
||||
if (c != 0)
|
||||
buf[len-1] = 0;
|
||||
|
||||
printk(KERN_NOTICE "%s%c", buf, c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __disable_ep(struct usb_ep *ep)
|
||||
{
|
||||
if (ep && ep->driver_data == dbgp.gadget) {
|
||||
usb_ep_disable(ep);
|
||||
ep->driver_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void dbgp_disable_ep(void)
|
||||
{
|
||||
__disable_ep(dbgp.i_ep);
|
||||
__disable_ep(dbgp.o_ep);
|
||||
}
|
||||
|
||||
static void dbgp_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
int stp;
|
||||
int err = 0;
|
||||
int status = req->status;
|
||||
|
||||
if (ep == dbgp.i_ep) {
|
||||
stp = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
stp = 2;
|
||||
goto release_req;
|
||||
}
|
||||
|
||||
dbgp_consume(req->buf, req->actual);
|
||||
|
||||
req->length = DBGP_REQ_LEN;
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
stp = 3;
|
||||
goto release_req;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
release_req:
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(dbgp.o_ep, req);
|
||||
dbgp_disable_ep();
|
||||
fail:
|
||||
dev_dbg(&dbgp.gadget->dev,
|
||||
"complete: failure (%d:%d) ==> %d\n", stp, err, status);
|
||||
}
|
||||
|
||||
static int dbgp_enable_ep_req(struct usb_ep *ep)
|
||||
{
|
||||
int err, stp;
|
||||
struct usb_request *req;
|
||||
|
||||
req = usb_ep_alloc_request(ep, GFP_KERNEL);
|
||||
if (!req) {
|
||||
err = -ENOMEM;
|
||||
stp = 1;
|
||||
goto fail_1;
|
||||
}
|
||||
|
||||
req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL);
|
||||
if (!req->buf) {
|
||||
err = -ENOMEM;
|
||||
stp = 2;
|
||||
goto fail_2;
|
||||
}
|
||||
|
||||
req->complete = dbgp_complete;
|
||||
req->length = DBGP_REQ_LEN;
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
stp = 3;
|
||||
goto fail_3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_3:
|
||||
kfree(req->buf);
|
||||
fail_2:
|
||||
usb_ep_free_request(dbgp.o_ep, req);
|
||||
fail_1:
|
||||
dev_dbg(&dbgp.gadget->dev,
|
||||
"enable ep req: failure (%d:%d)\n", stp, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
int err = usb_ep_enable(ep, desc);
|
||||
ep->driver_data = dbgp.gadget;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dbgp_enable_ep(void)
|
||||
{
|
||||
int err, stp;
|
||||
|
||||
err = __enable_ep(dbgp.i_ep, &i_desc);
|
||||
if (err < 0) {
|
||||
stp = 1;
|
||||
goto fail_1;
|
||||
}
|
||||
|
||||
err = __enable_ep(dbgp.o_ep, &o_desc);
|
||||
if (err < 0) {
|
||||
stp = 2;
|
||||
goto fail_2;
|
||||
}
|
||||
|
||||
err = dbgp_enable_ep_req(dbgp.o_ep);
|
||||
if (err < 0) {
|
||||
stp = 3;
|
||||
goto fail_3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_3:
|
||||
__disable_ep(dbgp.o_ep);
|
||||
fail_2:
|
||||
__disable_ep(dbgp.i_ep);
|
||||
fail_1:
|
||||
dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void dbgp_disconnect(struct usb_gadget *gadget)
|
||||
{
|
||||
#ifdef CONFIG_USB_G_DBGP_PRINTK
|
||||
dbgp_disable_ep();
|
||||
#else
|
||||
gserial_disconnect(dbgp.serial);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void dbgp_unbind(struct usb_gadget *gadget)
|
||||
{
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
kfree(dbgp.serial);
|
||||
#endif
|
||||
if (dbgp.req) {
|
||||
kfree(dbgp.req->buf);
|
||||
usb_ep_free_request(gadget->ep0, dbgp.req);
|
||||
}
|
||||
|
||||
gadget->ep0->driver_data = NULL;
|
||||
}
|
||||
|
||||
static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
|
||||
{
|
||||
int stp;
|
||||
|
||||
usb_ep_autoconfig_reset(gadget);
|
||||
|
||||
dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc);
|
||||
if (!dbgp.i_ep) {
|
||||
stp = 1;
|
||||
goto fail_1;
|
||||
}
|
||||
|
||||
dbgp.i_ep->driver_data = gadget;
|
||||
i_desc.wMaxPacketSize =
|
||||
__constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
|
||||
|
||||
dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc);
|
||||
if (!dbgp.o_ep) {
|
||||
dbgp.i_ep->driver_data = NULL;
|
||||
stp = 2;
|
||||
goto fail_2;
|
||||
}
|
||||
|
||||
dbgp.o_ep->driver_data = gadget;
|
||||
o_desc.wMaxPacketSize =
|
||||
__constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
|
||||
|
||||
dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress & 0x7f;
|
||||
dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress & 0x7f;
|
||||
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
dbgp.serial->in = dbgp.i_ep;
|
||||
dbgp.serial->out = dbgp.o_ep;
|
||||
|
||||
dbgp.serial->in_desc = &i_desc;
|
||||
dbgp.serial->out_desc = &o_desc;
|
||||
|
||||
if (gserial_setup(gadget, 1) < 0) {
|
||||
stp = 3;
|
||||
goto fail_3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_3:
|
||||
dbgp.o_ep->driver_data = NULL;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
fail_2:
|
||||
dbgp.i_ep->driver_data = NULL;
|
||||
fail_1:
|
||||
dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int __init dbgp_bind(struct usb_gadget *gadget)
|
||||
{
|
||||
int err, stp;
|
||||
|
||||
dbgp.gadget = gadget;
|
||||
|
||||
dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
|
||||
if (!dbgp.req) {
|
||||
err = -ENOMEM;
|
||||
stp = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL);
|
||||
if (!dbgp.req->buf) {
|
||||
err = -ENOMEM;
|
||||
stp = 2;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dbgp.req->length = DBGP_REQ_EP0_LEN;
|
||||
gadget->ep0->driver_data = gadget;
|
||||
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
|
||||
if (!dbgp.serial) {
|
||||
stp = 3;
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
err = dbgp_configure_endpoints(gadget);
|
||||
if (err < 0) {
|
||||
stp = 4;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dev_dbg(&dbgp.gadget->dev, "bind: success\n");
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err);
|
||||
dbgp_unbind(gadget);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void dbgp_setup_complete(struct usb_ep *ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n",
|
||||
req->status, req->actual, req->length);
|
||||
}
|
||||
|
||||
static int dbgp_setup(struct usb_gadget *gadget,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_request *req = dbgp.req;
|
||||
u8 request = ctrl->bRequest;
|
||||
u16 value = le16_to_cpu(ctrl->wValue);
|
||||
u16 length = le16_to_cpu(ctrl->wLength);
|
||||
int err = 0;
|
||||
void *data;
|
||||
u16 len;
|
||||
|
||||
gadget->ep0->driver_data = gadget;
|
||||
|
||||
if (request == USB_REQ_GET_DESCRIPTOR) {
|
||||
switch (value>>8) {
|
||||
case USB_DT_DEVICE:
|
||||
dev_dbg(&dbgp.gadget->dev, "setup: desc device\n");
|
||||
len = sizeof device_desc;
|
||||
data = &device_desc;
|
||||
break;
|
||||
case USB_DT_DEBUG:
|
||||
dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n");
|
||||
len = sizeof dbg_desc;
|
||||
data = &dbg_desc;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
} else if (request == USB_REQ_SET_FEATURE &&
|
||||
value == USB_DEVICE_DEBUG_MODE) {
|
||||
len = 0;
|
||||
data = NULL;
|
||||
dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n");
|
||||
#ifdef CONFIG_USB_G_DBGP_PRINTK
|
||||
err = dbgp_enable_ep();
|
||||
#else
|
||||
err = gserial_connect(dbgp.serial, 0);
|
||||
#endif
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
} else
|
||||
goto fail;
|
||||
|
||||
if (len >= 0) {
|
||||
req->length = min(length, len);
|
||||
req->zero = len < req->length;
|
||||
if (data && req->length)
|
||||
memcpy(req->buf, data, req->length);
|
||||
|
||||
req->complete = dbgp_setup_complete;
|
||||
return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
fail:
|
||||
dev_dbg(&dbgp.gadget->dev,
|
||||
"setup: failure req %x v %x\n", request, value);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct usb_gadget_driver dbgp_driver = {
|
||||
.function = "dbgp",
|
||||
.speed = USB_SPEED_HIGH,
|
||||
.bind = dbgp_bind,
|
||||
.unbind = dbgp_unbind,
|
||||
.setup = dbgp_setup,
|
||||
.disconnect = dbgp_disconnect,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "dbgp"
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dbgp_init(void)
|
||||
{
|
||||
return usb_gadget_register_driver(&dbgp_driver);
|
||||
}
|
||||
|
||||
static void __exit dbgp_exit(void)
|
||||
{
|
||||
usb_gadget_unregister_driver(&dbgp_driver);
|
||||
#ifdef CONFIG_USB_G_DBGP_SERIAL
|
||||
gserial_cleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Stephane Duverger");
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(dbgp_init);
|
||||
module_exit(dbgp_exit);
|
@ -1542,7 +1542,7 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
|
||||
dum = hcd_to_dummy (hcd);
|
||||
|
||||
spin_lock_irqsave (&dum->lock, flags);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
|
||||
if (!HCD_HW_ACCESSIBLE(hcd))
|
||||
goto done;
|
||||
|
||||
if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
|
||||
@ -1588,7 +1588,7 @@ static int dummy_hub_control (
|
||||
int retval = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
|
||||
if (!HCD_HW_ACCESSIBLE(hcd))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
dum = hcd_to_dummy (hcd);
|
||||
@ -1739,7 +1739,7 @@ static int dummy_bus_resume (struct usb_hcd *hcd)
|
||||
dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__);
|
||||
|
||||
spin_lock_irq (&dum->lock);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
rc = -ESHUTDOWN;
|
||||
} else {
|
||||
dum->rh_state = DUMMY_RH_RUNNING;
|
||||
|
@ -237,7 +237,7 @@ static u8 hostaddr[ETH_ALEN];
|
||||
* the first one present. That's to make Microsoft's drivers happy,
|
||||
* and to follow DOCSIS 1.0 (cable modem standard).
|
||||
*/
|
||||
static int __init rndis_do_config(struct usb_configuration *c)
|
||||
static int __ref rndis_do_config(struct usb_configuration *c)
|
||||
{
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
@ -270,7 +270,7 @@ MODULE_PARM_DESC(use_eem, "use CDC EEM mode");
|
||||
/*
|
||||
* We _always_ have an ECM, CDC Subset, or EEM configuration.
|
||||
*/
|
||||
static int __init eth_do_config(struct usb_configuration *c)
|
||||
static int __ref eth_do_config(struct usb_configuration *c)
|
||||
{
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
@ -297,7 +297,7 @@ static struct usb_configuration eth_config_driver = {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init eth_bind(struct usb_composite_dev *cdev)
|
||||
static int __ref eth_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
|
@ -714,9 +714,7 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
|
||||
struct ffs_function *func = ffs->func;
|
||||
ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
|
||||
} else if (gadget->ops->ioctl) {
|
||||
lock_kernel();
|
||||
ret = gadget->ops->ioctl(gadget, code, value);
|
||||
unlock_kernel();
|
||||
} else {
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
@ -1377,7 +1375,8 @@ static void ffs_data_reset(struct ffs_data *ffs)
|
||||
|
||||
static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
|
||||
{
|
||||
unsigned i, count;
|
||||
struct usb_gadget_strings **lang;
|
||||
int first_id;
|
||||
|
||||
ENTER();
|
||||
|
||||
@ -1385,7 +1384,9 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
|
||||
|| test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
|
||||
return -EBADFD;
|
||||
|
||||
ffs_data_get(ffs);
|
||||
first_id = usb_string_ids_n(cdev, ffs->strings_count);
|
||||
if (unlikely(first_id < 0))
|
||||
return first_id;
|
||||
|
||||
ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
|
||||
if (unlikely(!ffs->ep0req))
|
||||
@ -1393,25 +1394,16 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
|
||||
ffs->ep0req->complete = ffs_ep0_complete;
|
||||
ffs->ep0req->context = ffs;
|
||||
|
||||
/* Get strings identifiers */
|
||||
for (count = ffs->strings_count, i = 0; i < count; ++i) {
|
||||
struct usb_gadget_strings **lang;
|
||||
|
||||
int id = usb_string_id(cdev);
|
||||
if (unlikely(id < 0)) {
|
||||
usb_ep_free_request(cdev->gadget->ep0, ffs->ep0req);
|
||||
ffs->ep0req = NULL;
|
||||
return id;
|
||||
}
|
||||
|
||||
lang = ffs->stringtabs;
|
||||
do {
|
||||
(*lang)->strings[i].id = id;
|
||||
++lang;
|
||||
} while (*lang);
|
||||
lang = ffs->stringtabs;
|
||||
for (lang = ffs->stringtabs; *lang; ++lang) {
|
||||
struct usb_string *str = (*lang)->strings;
|
||||
int id = first_id;
|
||||
for (; str->s; ++id, ++str)
|
||||
str->id = id;
|
||||
}
|
||||
|
||||
ffs->gadget = cdev->gadget;
|
||||
ffs_data_get(ffs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1480,9 +1472,9 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
|
||||
}
|
||||
|
||||
|
||||
static int functionfs_add(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *c,
|
||||
struct ffs_data *ffs)
|
||||
static int functionfs_bind_config(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *c,
|
||||
struct ffs_data *ffs)
|
||||
{
|
||||
struct ffs_function *func;
|
||||
int ret;
|
||||
|
@ -142,7 +142,7 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
|
||||
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t *ptr)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)file->private_data;
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
char *tmp_buff = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
@ -200,7 +200,7 @@ static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *offp)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)file->private_data;
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
ssize_t status = -ENOMEM;
|
||||
|
||||
if (!access_ok(VERIFY_READ, buffer, count))
|
||||
@ -257,7 +257,7 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
|
||||
|
||||
static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct f_hidg *hidg = (struct f_hidg *)file->private_data;
|
||||
struct f_hidg *hidg = file->private_data;
|
||||
unsigned int ret = 0;
|
||||
|
||||
poll_wait(file, &hidg->read_queue, wait);
|
||||
|
@ -324,7 +324,7 @@ static void loopback_disable(struct usb_function *f)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init loopback_bind_config(struct usb_configuration *c)
|
||||
static int __ref loopback_bind_config(struct usb_configuration *c)
|
||||
{
|
||||
struct f_loopback *loop;
|
||||
int status;
|
||||
@ -346,7 +346,7 @@ static int __init loopback_bind_config(struct usb_configuration *c)
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct usb_configuration loopback_driver = {
|
||||
static struct usb_configuration loopback_driver = {
|
||||
.label = "loopback",
|
||||
.strings = loopback_strings,
|
||||
.bind = loopback_bind_config,
|
||||
|
@ -316,6 +316,27 @@ static const char fsg_string_interface[] = "Mass Storage";
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
struct fsg_dev;
|
||||
struct fsg_common;
|
||||
|
||||
/* FSF callback functions */
|
||||
struct fsg_operations {
|
||||
/* Callback function to call when thread exits. If no
|
||||
* callback is set or it returns value lower then zero MSF
|
||||
* will force eject all LUNs it operates on (including those
|
||||
* marked as non-removable or with prevent_medium_removal flag
|
||||
* set). */
|
||||
int (*thread_exits)(struct fsg_common *common);
|
||||
|
||||
/* Called prior to ejection. Negative return means error,
|
||||
* zero means to continue with ejection, positive means not to
|
||||
* eject. */
|
||||
int (*pre_eject)(struct fsg_common *common,
|
||||
struct fsg_lun *lun, int num);
|
||||
/* Called after ejection. Negative return means error, zero
|
||||
* or positive is just a success. */
|
||||
int (*post_eject)(struct fsg_common *common,
|
||||
struct fsg_lun *lun, int num);
|
||||
};
|
||||
|
||||
|
||||
/* Data shared by all the FSG instances. */
|
||||
@ -333,7 +354,6 @@ struct fsg_common {
|
||||
struct usb_ep *ep0; /* Copy of gadget->ep0 */
|
||||
struct usb_request *ep0req; /* Copy of cdev->req */
|
||||
unsigned int ep0_req_tag;
|
||||
const char *ep0req_name;
|
||||
|
||||
struct fsg_buffhd *next_buffhd_to_fill;
|
||||
struct fsg_buffhd *next_buffhd_to_drain;
|
||||
@ -369,8 +389,8 @@ struct fsg_common {
|
||||
struct completion thread_notifier;
|
||||
struct task_struct *thread_task;
|
||||
|
||||
/* Callback function to call when thread exits. */
|
||||
int (*thread_exits)(struct fsg_common *common);
|
||||
/* Callback functions. */
|
||||
const struct fsg_operations *ops;
|
||||
/* Gadget's private data. */
|
||||
void *private_data;
|
||||
|
||||
@ -394,12 +414,8 @@ struct fsg_config {
|
||||
const char *lun_name_format;
|
||||
const char *thread_name;
|
||||
|
||||
/* Callback function to call when thread exits. If no
|
||||
* callback is set or it returns value lower then zero MSF
|
||||
* will force eject all LUNs it operates on (including those
|
||||
* marked as non-removable or with prevent_medium_removal flag
|
||||
* set). */
|
||||
int (*thread_exits)(struct fsg_common *common);
|
||||
/* Callback functions. */
|
||||
const struct fsg_operations *ops;
|
||||
/* Gadget's private data. */
|
||||
void *private_data;
|
||||
|
||||
@ -435,6 +451,7 @@ static inline int __fsg_is_set(struct fsg_common *common,
|
||||
if (common->fsg)
|
||||
return 1;
|
||||
ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -623,8 +640,6 @@ static int fsg_setup(struct usb_function *f,
|
||||
|
||||
/* Respond with data/status */
|
||||
req->length = min((u16)1, w_length);
|
||||
fsg->common->ep0req_name =
|
||||
ctrl->bRequestType & USB_DIR_IN ? "ep0-in" : "ep0-out";
|
||||
return ep0_queue(fsg->common);
|
||||
}
|
||||
|
||||
@ -1395,43 +1410,55 @@ static int do_start_stop(struct fsg_common *common)
|
||||
} else if (!curlun->removable) {
|
||||
curlun->sense_data = SS_INVALID_COMMAND;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
loej = common->cmnd[4] & 0x02;
|
||||
start = common->cmnd[4] & 0x01;
|
||||
|
||||
/* eject code from file_storage.c:do_start_stop() */
|
||||
|
||||
if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
|
||||
(common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
|
||||
} else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
|
||||
(common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
|
||||
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
/* Are we allowed to unload the media? */
|
||||
if (curlun->prevent_medium_removal) {
|
||||
LDBG(curlun, "unload attempt prevented\n");
|
||||
curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (loej) { /* Simulate an unload/eject */
|
||||
up_read(&common->filesem);
|
||||
down_write(&common->filesem);
|
||||
fsg_lun_close(curlun);
|
||||
up_write(&common->filesem);
|
||||
down_read(&common->filesem);
|
||||
}
|
||||
} else {
|
||||
loej = common->cmnd[4] & 0x02;
|
||||
start = common->cmnd[4] & 0x01;
|
||||
|
||||
/* Our emulation doesn't support mounting; the medium is
|
||||
* available for use as soon as it is loaded. */
|
||||
/* Our emulation doesn't support mounting; the medium is
|
||||
* available for use as soon as it is loaded. */
|
||||
if (start) {
|
||||
if (!fsg_lun_is_open(curlun)) {
|
||||
curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* Are we allowed to unload the media? */
|
||||
if (curlun->prevent_medium_removal) {
|
||||
LDBG(curlun, "unload attempt prevented\n");
|
||||
curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!loej)
|
||||
return 0;
|
||||
|
||||
/* Simulate an unload/eject */
|
||||
if (common->ops && common->ops->pre_eject) {
|
||||
int r = common->ops->pre_eject(common, curlun,
|
||||
curlun - common->luns);
|
||||
if (unlikely(r < 0))
|
||||
return r;
|
||||
else if (r)
|
||||
return 0;
|
||||
}
|
||||
|
||||
up_read(&common->filesem);
|
||||
down_write(&common->filesem);
|
||||
fsg_lun_close(curlun);
|
||||
up_write(&common->filesem);
|
||||
down_read(&common->filesem);
|
||||
|
||||
return common->ops && common->ops->post_eject
|
||||
? min(0, common->ops->post_eject(common, curlun,
|
||||
curlun - common->luns))
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
@ -2610,7 +2637,8 @@ static int fsg_main_thread(void *common_)
|
||||
common->thread_task = NULL;
|
||||
spin_unlock_irq(&common->lock);
|
||||
|
||||
if (!common->thread_exits || common->thread_exits(common) < 0) {
|
||||
if (!common->ops || !common->ops->thread_exits
|
||||
|| common->ops->thread_exits(common) < 0) {
|
||||
struct fsg_lun *curlun = common->luns;
|
||||
unsigned i = common->nluns;
|
||||
|
||||
@ -2686,6 +2714,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
||||
common->free_storage_on_release = 0;
|
||||
}
|
||||
|
||||
common->ops = cfg->ops;
|
||||
common->private_data = cfg->private_data;
|
||||
|
||||
common->gadget = gadget;
|
||||
@ -2807,7 +2836,6 @@ buffhds_first_it:
|
||||
|
||||
|
||||
/* Tell the thread to start working */
|
||||
common->thread_exits = cfg->thread_exits;
|
||||
common->thread_task =
|
||||
kthread_create(fsg_main_thread, common,
|
||||
OR(cfg->thread_name, "file-storage"));
|
||||
@ -2990,9 +3018,9 @@ static struct usb_gadget_strings *fsg_strings_array[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int fsg_add(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *c,
|
||||
struct fsg_common *common)
|
||||
static int fsg_bind_config(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *c,
|
||||
struct fsg_common *common)
|
||||
{
|
||||
struct fsg_dev *fsg;
|
||||
int rc;
|
||||
@ -3024,6 +3052,13 @@ static int fsg_add(struct usb_composite_dev *cdev,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int __deprecated __maybe_unused
|
||||
fsg_add(struct usb_composite_dev *cdev,
|
||||
struct usb_configuration *c,
|
||||
struct fsg_common *common)
|
||||
{
|
||||
return fsg_bind_config(cdev, c, common);
|
||||
}
|
||||
|
||||
|
||||
/************************* Module parameters *************************/
|
||||
@ -3096,8 +3131,8 @@ fsg_config_from_params(struct fsg_config *cfg,
|
||||
cfg->product_name = 0;
|
||||
cfg->release = 0xffff;
|
||||
|
||||
cfg->thread_exits = 0;
|
||||
cfg->private_data = 0;
|
||||
cfg->ops = NULL;
|
||||
cfg->private_data = NULL;
|
||||
|
||||
/* Finalise */
|
||||
cfg->can_stall = params->stall;
|
||||
|
@ -404,7 +404,7 @@ static void sourcesink_disable(struct usb_function *f)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init sourcesink_bind_config(struct usb_configuration *c)
|
||||
static int __ref sourcesink_bind_config(struct usb_configuration *c)
|
||||
{
|
||||
struct f_sourcesink *ss;
|
||||
int status;
|
||||
|
@ -56,7 +56,7 @@
|
||||
* following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03),
|
||||
* UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by
|
||||
* the optional "protocol" module parameter. In addition, the default
|
||||
* Vendor ID, Product ID, and release number can be overridden.
|
||||
* Vendor ID, Product ID, release number and serial number can be overridden.
|
||||
*
|
||||
* There is support for multiple logical units (LUNs), each of which has
|
||||
* its own backing file. The number of LUNs can be set using the optional
|
||||
@ -93,6 +93,8 @@
|
||||
* removable Default false, boolean for removable media
|
||||
* luns=N Default N = number of filenames, number of
|
||||
* LUNs to support
|
||||
* nofua=b[,b...] Default false, booleans for ignore FUA flag
|
||||
* in SCSI WRITE(10,12) commands
|
||||
* stall Default determined according to the type of
|
||||
* USB device controller (usually true),
|
||||
* boolean to permit the driver to halt
|
||||
@ -106,17 +108,18 @@
|
||||
* vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID
|
||||
* product=0xPPPP Default 0xa4a5 (FSG), USB Product ID
|
||||
* release=0xRRRR Override the USB release number (bcdDevice)
|
||||
* serial=HHHH... Override serial number (string of hex chars)
|
||||
* buflen=N Default N=16384, buffer size used (will be
|
||||
* rounded down to a multiple of
|
||||
* PAGE_CACHE_SIZE)
|
||||
*
|
||||
* If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro",
|
||||
* "removable", "luns", "stall", and "cdrom" options are available; default
|
||||
* values are used for everything else.
|
||||
* "removable", "luns", "nofua", "stall", and "cdrom" options are available;
|
||||
* default values are used for everything else.
|
||||
*
|
||||
* The pathnames of the backing files and the ro settings are available in
|
||||
* the attribute files "file" and "ro" in the lun<n> subdirectory of the
|
||||
* gadget's sysfs directory. If the "removable" option is set, writing to
|
||||
* the attribute files "file", "nofua", and "ro" in the lun<n> subdirectory of
|
||||
* the gadget's sysfs directory. If the "removable" option is set, writing to
|
||||
* these files will simulate ejecting/loading the medium (writing an empty
|
||||
* line means eject) and adjusting a write-enable tab. Changes to the ro
|
||||
* setting are not allowed when the medium is loaded or if CD-ROM emulation
|
||||
@ -270,6 +273,8 @@
|
||||
|
||||
#define DRIVER_DESC "File-backed Storage Gadget"
|
||||
#define DRIVER_NAME "g_file_storage"
|
||||
/* DRIVER_VERSION must be at least 6 characters long, as it is used
|
||||
* to generate a fallback serial number. */
|
||||
#define DRIVER_VERSION "20 November 2008"
|
||||
|
||||
static char fsg_string_manufacturer[64];
|
||||
@ -301,8 +306,10 @@ MODULE_LICENSE("Dual BSD/GPL");
|
||||
static struct {
|
||||
char *file[FSG_MAX_LUNS];
|
||||
int ro[FSG_MAX_LUNS];
|
||||
int nofua[FSG_MAX_LUNS];
|
||||
unsigned int num_filenames;
|
||||
unsigned int num_ros;
|
||||
unsigned int num_nofuas;
|
||||
unsigned int nluns;
|
||||
|
||||
int removable;
|
||||
@ -314,6 +321,7 @@ static struct {
|
||||
unsigned short vendor;
|
||||
unsigned short product;
|
||||
unsigned short release;
|
||||
char *serial;
|
||||
unsigned int buflen;
|
||||
|
||||
int transport_type;
|
||||
@ -341,6 +349,10 @@ MODULE_PARM_DESC(file, "names of backing files or devices");
|
||||
module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO);
|
||||
MODULE_PARM_DESC(ro, "true to force read-only");
|
||||
|
||||
module_param_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas,
|
||||
S_IRUGO);
|
||||
MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit");
|
||||
|
||||
module_param_named(luns, mod_data.nluns, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(luns, "number of LUNs");
|
||||
|
||||
@ -353,6 +365,8 @@ MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
|
||||
module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk");
|
||||
|
||||
module_param_named(serial, mod_data.serial, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(serial, "USB serial number");
|
||||
|
||||
/* In the non-TEST version, only the module parameters listed above
|
||||
* are available. */
|
||||
@ -1272,7 +1286,8 @@ static int do_write(struct fsg_dev *fsg)
|
||||
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (fsg->cmnd[1] & 0x08) { // FUA
|
||||
/* FUA */
|
||||
if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) {
|
||||
spin_lock(&curlun->filp->f_lock);
|
||||
curlun->filp->f_flags |= O_DSYNC;
|
||||
spin_unlock(&curlun->filp->f_lock);
|
||||
@ -3126,6 +3141,7 @@ static int fsg_main_thread(void *fsg_)
|
||||
|
||||
/* The write permissions and store_xxx pointers are set in fsg_bind() */
|
||||
static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
|
||||
static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL);
|
||||
static DEVICE_ATTR(file, 0444, fsg_show_file, NULL);
|
||||
|
||||
|
||||
@ -3197,6 +3213,7 @@ static int __init check_parameters(struct fsg_dev *fsg)
|
||||
{
|
||||
int prot;
|
||||
int gcnum;
|
||||
int i;
|
||||
|
||||
/* Store the default values */
|
||||
mod_data.transport_type = USB_PR_BULK;
|
||||
@ -3272,13 +3289,65 @@ static int __init check_parameters(struct fsg_dev *fsg)
|
||||
ERROR(fsg, "invalid buflen\n");
|
||||
return -ETOOSMALL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_FILE_STORAGE_TEST */
|
||||
|
||||
/* Serial string handling.
|
||||
* On a real device, the serial string would be loaded
|
||||
* from permanent storage. */
|
||||
if (mod_data.serial) {
|
||||
const char *ch;
|
||||
unsigned len = 0;
|
||||
|
||||
/* Sanity check :
|
||||
* The CB[I] specification limits the serial string to
|
||||
* 12 uppercase hexadecimal characters.
|
||||
* BBB need at least 12 uppercase hexadecimal characters,
|
||||
* with a maximum of 126. */
|
||||
for (ch = mod_data.serial; *ch; ++ch) {
|
||||
++len;
|
||||
if ((*ch < '0' || *ch > '9') &&
|
||||
(*ch < 'A' || *ch > 'F')) { /* not uppercase hex */
|
||||
WARNING(fsg,
|
||||
"Invalid serial string character: %c; "
|
||||
"Failing back to default\n",
|
||||
*ch);
|
||||
goto fill_serial;
|
||||
}
|
||||
}
|
||||
if (len > 126 ||
|
||||
(mod_data.transport_type == USB_PR_BULK && len < 12) ||
|
||||
(mod_data.transport_type != USB_PR_BULK && len > 12)) {
|
||||
WARNING(fsg,
|
||||
"Invalid serial string length; "
|
||||
"Failing back to default\n");
|
||||
goto fill_serial;
|
||||
}
|
||||
fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial;
|
||||
} else {
|
||||
WARNING(fsg,
|
||||
"Userspace failed to provide serial number; "
|
||||
"Failing back to default\n");
|
||||
fill_serial:
|
||||
/* Serial number not specified or invalid, make our own.
|
||||
* We just encode it from the driver version string,
|
||||
* 12 characters to comply with both CB[I] and BBB spec.
|
||||
* Warning : Two devices running the same kernel will have
|
||||
* the same fallback serial number. */
|
||||
for (i = 0; i < 12; i += 2) {
|
||||
unsigned char c = DRIVER_VERSION[i / 2];
|
||||
|
||||
if (!c)
|
||||
break;
|
||||
sprintf(&fsg_string_serial[i], "%02X", c);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
static int __ref fsg_bind(struct usb_gadget *gadget)
|
||||
{
|
||||
struct fsg_dev *fsg = the_fsg;
|
||||
int rc;
|
||||
@ -3305,6 +3374,10 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
}
|
||||
}
|
||||
|
||||
/* Only for removable media? */
|
||||
dev_attr_nofua.attr.mode = 0644;
|
||||
dev_attr_nofua.store = fsg_store_nofua;
|
||||
|
||||
/* Find out how many LUNs there should be */
|
||||
i = mod_data.nluns;
|
||||
if (i == 0)
|
||||
@ -3330,6 +3403,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
curlun->ro = mod_data.cdrom || mod_data.ro[i];
|
||||
curlun->initially_ro = curlun->ro;
|
||||
curlun->removable = mod_data.removable;
|
||||
curlun->nofua = mod_data.nofua[i];
|
||||
curlun->dev.release = lun_release;
|
||||
curlun->dev.parent = &gadget->dev;
|
||||
curlun->dev.driver = &fsg_driver.driver;
|
||||
@ -3343,6 +3417,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
}
|
||||
if ((rc = device_create_file(&curlun->dev,
|
||||
&dev_attr_ro)) != 0 ||
|
||||
(rc = device_create_file(&curlun->dev,
|
||||
&dev_attr_nofua)) != 0 ||
|
||||
(rc = device_create_file(&curlun->dev,
|
||||
&dev_attr_file)) != 0) {
|
||||
device_unregister(&curlun->dev);
|
||||
@ -3447,16 +3523,6 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
gadget->name);
|
||||
|
||||
/* On a real device, serial[] would be loaded from permanent
|
||||
* storage. We just encode it from the driver version string. */
|
||||
for (i = 0; i < sizeof fsg_string_serial - 2; i += 2) {
|
||||
unsigned char c = DRIVER_VERSION[i / 2];
|
||||
|
||||
if (!c)
|
||||
break;
|
||||
sprintf(&fsg_string_serial[i], "%02X", c);
|
||||
}
|
||||
|
||||
fsg->thread_task = kthread_create(fsg_main_thread, fsg,
|
||||
"file-storage-gadget");
|
||||
if (IS_ERR(fsg->thread_task)) {
|
||||
@ -3478,8 +3544,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
if (IS_ERR(p))
|
||||
p = NULL;
|
||||
}
|
||||
LINFO(curlun, "ro=%d, file: %s\n",
|
||||
curlun->ro, (p ? p : "(error)"));
|
||||
LINFO(curlun, "ro=%d, nofua=%d, file: %s\n",
|
||||
curlun->ro, curlun->nofua, (p ? p : "(error)"));
|
||||
}
|
||||
}
|
||||
kfree(pathbuf);
|
||||
|
@ -32,12 +32,13 @@
|
||||
# include "u_ether.c"
|
||||
|
||||
static u8 gfs_hostaddr[ETH_ALEN];
|
||||
#else
|
||||
# if !defined CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
# define CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
# ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
# endif
|
||||
#else
|
||||
# define gether_cleanup() do { } while (0)
|
||||
# define gether_setup(gadget, hostaddr) ((int)0)
|
||||
# define gfs_hostaddr NULL
|
||||
#endif
|
||||
|
||||
#include "f_fs.c"
|
||||
@ -107,15 +108,7 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = {
|
||||
enum {
|
||||
GFS_STRING_MANUFACTURER_IDX,
|
||||
GFS_STRING_PRODUCT_IDX,
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
GFS_STRING_RNDIS_CONFIG_IDX,
|
||||
#endif
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
GFS_STRING_ECM_CONFIG_IDX,
|
||||
#endif
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
GFS_STRING_GENERIC_CONFIG_IDX,
|
||||
#endif
|
||||
GFS_STRING_FIRST_CONFIG_IDX,
|
||||
};
|
||||
|
||||
static char gfs_manufacturer[50];
|
||||
@ -126,13 +119,13 @@ static struct usb_string gfs_strings[] = {
|
||||
[GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer,
|
||||
[GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc,
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
[GFS_STRING_RNDIS_CONFIG_IDX].s = "FunctionFS + RNDIS",
|
||||
{ .s = "FunctionFS + RNDIS" },
|
||||
#endif
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
[GFS_STRING_ECM_CONFIG_IDX].s = "FunctionFS + ECM",
|
||||
{ .s = "FunctionFS + ECM" },
|
||||
#endif
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
[GFS_STRING_GENERIC_CONFIG_IDX].s = "FunctionFS",
|
||||
{ .s = "FunctionFS" },
|
||||
#endif
|
||||
{ } /* end of list */
|
||||
};
|
||||
@ -146,59 +139,33 @@ static struct usb_gadget_strings *gfs_dev_strings[] = {
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct gfs_configuration {
|
||||
struct usb_configuration c;
|
||||
int (*eth)(struct usb_configuration *c, u8 *ethaddr);
|
||||
} gfs_configurations[] = {
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
static int gfs_do_rndis_config(struct usb_configuration *c);
|
||||
|
||||
static struct usb_configuration gfs_rndis_config_driver = {
|
||||
.label = "FunctionFS + RNDIS",
|
||||
.bind = gfs_do_rndis_config,
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
# define gfs_add_rndis_config(cdev) \
|
||||
usb_add_config(cdev, &gfs_rndis_config_driver)
|
||||
#else
|
||||
# define gfs_add_rndis_config(cdev) 0
|
||||
{
|
||||
.eth = rndis_bind_config,
|
||||
},
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
static int gfs_do_ecm_config(struct usb_configuration *c);
|
||||
|
||||
static struct usb_configuration gfs_ecm_config_driver = {
|
||||
.label = "FunctionFS + ECM",
|
||||
.bind = gfs_do_ecm_config,
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
# define gfs_add_ecm_config(cdev) \
|
||||
usb_add_config(cdev, &gfs_ecm_config_driver)
|
||||
#else
|
||||
# define gfs_add_ecm_config(cdev) 0
|
||||
{
|
||||
.eth = eth_bind_config,
|
||||
},
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
static int gfs_do_generic_config(struct usb_configuration *c);
|
||||
|
||||
static struct usb_configuration gfs_generic_config_driver = {
|
||||
.label = "FunctionFS",
|
||||
.bind = gfs_do_generic_config,
|
||||
.bConfigurationValue = 2,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
# define gfs_add_generic_config(cdev) \
|
||||
usb_add_config(cdev, &gfs_generic_config_driver)
|
||||
#else
|
||||
# define gfs_add_generic_config(cdev) 0
|
||||
{
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
static int gfs_bind(struct usb_composite_dev *cdev);
|
||||
static int gfs_unbind(struct usb_composite_dev *cdev);
|
||||
static int gfs_do_config(struct usb_configuration *c);
|
||||
|
||||
static struct usb_composite_driver gfs_driver = {
|
||||
.name = gfs_short_name,
|
||||
@ -267,7 +234,7 @@ static int functionfs_check_dev_callback(const char *dev_name)
|
||||
|
||||
static int gfs_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
ENTER();
|
||||
|
||||
@ -284,57 +251,32 @@ static int gfs_bind(struct usb_composite_dev *cdev)
|
||||
snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
cdev->gadget->name);
|
||||
ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret;
|
||||
gfs_dev_desc.iManufacturer = ret;
|
||||
|
||||
ret = usb_string_id(cdev);
|
||||
ret = usb_string_ids_tab(cdev, gfs_strings);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret;
|
||||
gfs_dev_desc.iProduct = ret;
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_RNDIS_CONFIG_IDX].id = ret;
|
||||
gfs_rndis_config_driver.iConfiguration = ret;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_ECM_CONFIG_IDX].id = ret;
|
||||
gfs_ecm_config_driver.iConfiguration = ret;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
ret = usb_string_id(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
gfs_strings[GFS_STRING_GENERIC_CONFIG_IDX].id = ret;
|
||||
gfs_generic_config_driver.iConfiguration = ret;
|
||||
#endif
|
||||
gfs_dev_desc.iManufacturer = gfs_strings[GFS_STRING_MANUFACTURER_IDX].id;
|
||||
gfs_dev_desc.iProduct = gfs_strings[GFS_STRING_PRODUCT_IDX].id;
|
||||
|
||||
ret = functionfs_bind(gfs_ffs_data, cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
|
||||
ret = gfs_add_rndis_config(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_unbind;
|
||||
for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
|
||||
struct gfs_configuration *c = gfs_configurations + i;
|
||||
|
||||
ret = gfs_add_ecm_config(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_unbind;
|
||||
ret = GFS_STRING_FIRST_CONFIG_IDX + i;
|
||||
c->c.label = gfs_strings[ret].s;
|
||||
c->c.iConfiguration = gfs_strings[ret].id;
|
||||
c->c.bind = gfs_do_config;
|
||||
c->c.bConfigurationValue = 1 + i;
|
||||
c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER;
|
||||
|
||||
ret = gfs_add_generic_config(cdev);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_unbind;
|
||||
ret = usb_add_config(cdev, &c->c);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_unbind;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -368,10 +310,10 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
|
||||
}
|
||||
|
||||
|
||||
static int __gfs_do_config(struct usb_configuration *c,
|
||||
int (*eth)(struct usb_configuration *c, u8 *ethaddr),
|
||||
u8 *ethaddr)
|
||||
static int gfs_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct gfs_configuration *gc =
|
||||
container_of(c, struct gfs_configuration, c);
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!gfs_ffs_data))
|
||||
@ -382,13 +324,13 @@ static int __gfs_do_config(struct usb_configuration *c,
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
if (eth) {
|
||||
ret = eth(c, ethaddr);
|
||||
if (gc->eth) {
|
||||
ret = gc->eth(c, gfs_hostaddr);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = functionfs_add(c->cdev, c, gfs_ffs_data);
|
||||
ret = functionfs_bind_config(c->cdev, c, gfs_ffs_data);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
@ -406,32 +348,12 @@ static int __gfs_do_config(struct usb_configuration *c,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
static int gfs_do_rndis_config(struct usb_configuration *c)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
return __gfs_do_config(c, rndis_bind_config, gfs_hostaddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
static int gfs_do_ecm_config(struct usb_configuration *c)
|
||||
static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
{
|
||||
ENTER();
|
||||
|
||||
return __gfs_do_config(c,
|
||||
can_support_ecm(c->cdev->gadget)
|
||||
? ecm_bind_config : geth_bind_config,
|
||||
gfs_hostaddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
|
||||
static int gfs_do_generic_config(struct usb_configuration *c)
|
||||
{
|
||||
ENTER();
|
||||
|
||||
return __gfs_do_config(c, NULL, NULL);
|
||||
return can_support_ecm(c->cdev->gadget)
|
||||
? ecm_bind_config(c, ethaddr)
|
||||
: geth_bind_config(c, ethaddr);
|
||||
}
|
||||
#endif
|
||||
|
@ -1157,7 +1157,7 @@ fail:
|
||||
/*
|
||||
* Creates an output endpoint, and initializes output ports.
|
||||
*/
|
||||
static int __init gmidi_bind(struct usb_gadget *gadget)
|
||||
static int __ref gmidi_bind(struct usb_gadget *gadget)
|
||||
{
|
||||
struct gmidi_device *dev;
|
||||
struct usb_ep *in_ep, *out_ep;
|
||||
|
@ -127,7 +127,7 @@ static struct usb_gadget_strings *dev_strings[] = {
|
||||
|
||||
/****************************** Configurations ******************************/
|
||||
|
||||
static int __init do_config(struct usb_configuration *c)
|
||||
static int __ref do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct hidg_func_node *e;
|
||||
int func = 0, status = 0;
|
||||
@ -156,7 +156,7 @@ static struct usb_configuration config_driver = {
|
||||
|
||||
/****************************** Gadget Bind ******************************/
|
||||
|
||||
static int __init hid_bind(struct usb_composite_dev *cdev)
|
||||
static int __ref hid_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct list_head *tmp;
|
||||
|
@ -1299,11 +1299,9 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
|
||||
struct usb_gadget *gadget = dev->gadget;
|
||||
long ret = -ENOTTY;
|
||||
|
||||
if (gadget->ops->ioctl) {
|
||||
lock_kernel();
|
||||
if (gadget->ops->ioctl)
|
||||
ret = gadget->ops->ioctl (gadget, code, value);
|
||||
unlock_kernel();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1867,13 +1865,9 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
|
||||
buf += 4;
|
||||
length -= 4;
|
||||
|
||||
kbuf = kmalloc (length, GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user (kbuf, buf, length)) {
|
||||
kfree (kbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
kbuf = memdup_user(buf, length);
|
||||
if (IS_ERR(kbuf))
|
||||
return PTR_ERR(kbuf);
|
||||
|
||||
spin_lock_irq (&dev->lock);
|
||||
value = -EINVAL;
|
||||
|
@ -842,9 +842,9 @@ static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
||||
VDBG(dev, "req->mapped = 0\n");
|
||||
}
|
||||
|
||||
DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n",
|
||||
_ep->name,
|
||||
_req, _req->length, _req->buf, _req->dma);
|
||||
DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08llx\n",
|
||||
_ep->name,
|
||||
_req, _req->length, _req->buf, (unsigned long long)_req->dma);
|
||||
|
||||
_req->status = -EINPROGRESS;
|
||||
_req->actual = 0;
|
||||
|
@ -141,9 +141,14 @@ static int msg_thread_exits(struct fsg_common *common)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init msg_do_config(struct usb_configuration *c)
|
||||
static int __ref msg_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct fsg_common *common;
|
||||
static const struct fsg_operations ops = {
|
||||
.thread_exits = msg_thread_exits,
|
||||
};
|
||||
static struct fsg_common common;
|
||||
|
||||
struct fsg_common *retp;
|
||||
struct fsg_config config;
|
||||
int ret;
|
||||
|
||||
@ -153,13 +158,14 @@ static int __init msg_do_config(struct usb_configuration *c)
|
||||
}
|
||||
|
||||
fsg_config_from_params(&config, &mod_data);
|
||||
config.thread_exits = msg_thread_exits;
|
||||
common = fsg_common_init(0, c->cdev, &config);
|
||||
if (IS_ERR(common))
|
||||
return PTR_ERR(common);
|
||||
config.ops = &ops;
|
||||
|
||||
ret = fsg_add(c->cdev, c, common);
|
||||
fsg_common_put(common);
|
||||
retp = fsg_common_init(&common, c->cdev, &config);
|
||||
if (IS_ERR(retp))
|
||||
return PTR_ERR(retp);
|
||||
|
||||
ret = fsg_bind_config(c->cdev, c, &common);
|
||||
fsg_common_put(&common);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -176,7 +182,7 @@ static struct usb_configuration msg_config_driver = {
|
||||
/****************************** Gadget Bind ******************************/
|
||||
|
||||
|
||||
static int __init msg_bind(struct usb_composite_dev *cdev)
|
||||
static int __ref msg_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
int status;
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
|
||||
#if defined USB_ETH_RNDIS
|
||||
@ -35,14 +36,13 @@
|
||||
|
||||
|
||||
#define DRIVER_DESC "Multifunction Composite Gadget"
|
||||
#define DRIVER_VERSION "2009/07/21"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Michal Nazarewicz");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */
|
||||
#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/***************************** All the files... *****************************/
|
||||
|
||||
/*
|
||||
* kbuild is not very cooperative with respect to linking separately
|
||||
@ -57,6 +57,8 @@
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
|
||||
#include "f_mass_storage.c"
|
||||
|
||||
#include "u_serial.c"
|
||||
#include "f_acm.c"
|
||||
|
||||
@ -68,13 +70,24 @@
|
||||
#endif
|
||||
#include "u_ether.c"
|
||||
|
||||
#undef DBG /* u_ether.c has broken idea about macros */
|
||||
#undef VDBG /* so clean up after it */
|
||||
#undef ERROR
|
||||
#undef INFO
|
||||
#include "f_mass_storage.c"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/***************************** Device Descriptor ****************************/
|
||||
|
||||
#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */
|
||||
#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */
|
||||
|
||||
|
||||
enum {
|
||||
__MULTI_NO_CONFIG,
|
||||
#ifdef CONFIG_USB_G_MULTI_RNDIS
|
||||
MULTI_RNDIS_CONFIG_NUM,
|
||||
#endif
|
||||
#ifdef CONFIG_USB_G_MULTI_CDC
|
||||
MULTI_CDC_CONFIG_NUM,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
@ -82,80 +95,82 @@ static struct usb_device_descriptor device_desc = {
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
|
||||
/* .bDeviceClass = USB_CLASS_COMM, */
|
||||
/* .bDeviceSubClass = 0, */
|
||||
/* .bDeviceProtocol = 0, */
|
||||
.bDeviceClass = 0xEF,
|
||||
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
|
||||
.bDeviceSubClass = 2,
|
||||
.bDeviceProtocol = 1,
|
||||
/* .bMaxPacketSize0 = f(hardware) */
|
||||
|
||||
/* Vendor and product id can be overridden by module parameters. */
|
||||
.idVendor = cpu_to_le16(MULTI_VENDOR_NUM),
|
||||
.idProduct = cpu_to_le16(MULTI_PRODUCT_NUM),
|
||||
/* .bcdDevice = f(hardware) */
|
||||
/* .iManufacturer = DYNAMIC */
|
||||
/* .iProduct = DYNAMIC */
|
||||
/* NO SERIAL NUMBER */
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
static struct usb_otg_descriptor otg_descriptor = {
|
||||
.bLength = sizeof otg_descriptor,
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
|
||||
/* REVISIT SRP-only hardware is possible, although
|
||||
* it would not be called "OTG" ...
|
||||
*/
|
||||
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header *otg_desc[] = {
|
||||
(struct usb_descriptor_header *) &otg_descriptor,
|
||||
(struct usb_descriptor_header *) &(struct usb_otg_descriptor){
|
||||
.bLength = sizeof(struct usb_otg_descriptor),
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
|
||||
/*
|
||||
* REVISIT SRP-only hardware is possible, although
|
||||
* it would not be called "OTG" ...
|
||||
*/
|
||||
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
|
||||
},
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
enum {
|
||||
MULTI_STRING_MANUFACTURER_IDX,
|
||||
MULTI_STRING_PRODUCT_IDX,
|
||||
#ifdef CONFIG_USB_G_MULTI_RNDIS
|
||||
MULTI_STRING_RNDIS_CONFIG_IDX,
|
||||
#endif
|
||||
#ifdef CONFIG_USB_G_MULTI_CDC
|
||||
MULTI_STRING_CDC_CONFIG_IDX,
|
||||
#endif
|
||||
};
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
[MULTI_STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[MULTI_STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
#ifdef CONFIG_USB_G_MULTI_RNDIS
|
||||
[MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS",
|
||||
#endif
|
||||
#ifdef CONFIG_USB_G_MULTI_CDC
|
||||
[MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM",
|
||||
#endif
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings stringtab_dev = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_dev,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *dev_strings[] = {
|
||||
&stringtab_dev,
|
||||
&(struct usb_gadget_strings){
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_dev,
|
||||
},
|
||||
NULL,
|
||||
};
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
|
||||
|
||||
|
||||
/****************************** Configurations ******************************/
|
||||
|
||||
static struct fsg_module_parameters mod_data = {
|
||||
.stall = 1
|
||||
};
|
||||
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
|
||||
static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
|
||||
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
|
||||
|
||||
static struct fsg_common *fsg_common;
|
||||
static struct fsg_common fsg_common;
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
|
||||
|
||||
/********** RNDIS **********/
|
||||
|
||||
#ifdef USB_ETH_RNDIS
|
||||
|
||||
static int __init rndis_do_config(struct usb_configuration *c)
|
||||
static __ref int rndis_do_config(struct usb_configuration *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -172,26 +187,42 @@ static int __init rndis_do_config(struct usb_configuration *c)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = fsg_add(c->cdev, c, fsg_common);
|
||||
ret = fsg_bind_config(c->cdev, c, &fsg_common);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_configuration rndis_config_driver = {
|
||||
.label = "Multifunction Composite (RNDIS + MS + ACM)",
|
||||
.bind = rndis_do_config,
|
||||
.bConfigurationValue = 2,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
static int rndis_config_register(struct usb_composite_dev *cdev)
|
||||
{
|
||||
static struct usb_configuration config = {
|
||||
.bind = rndis_do_config,
|
||||
.bConfigurationValue = MULTI_RNDIS_CONFIG_NUM,
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
|
||||
config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s;
|
||||
config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id;
|
||||
|
||||
return usb_add_config(cdev, &config);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int rndis_config_register(struct usb_composite_dev *cdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/********** CDC ECM **********/
|
||||
|
||||
#ifdef CONFIG_USB_G_MULTI_CDC
|
||||
|
||||
static int __init cdc_do_config(struct usb_configuration *c)
|
||||
static __ref int cdc_do_config(struct usb_configuration *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -208,20 +239,33 @@ static int __init cdc_do_config(struct usb_configuration *c)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = fsg_add(c->cdev, c, fsg_common);
|
||||
ret = fsg_bind_config(c->cdev, c, &fsg_common);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_configuration cdc_config_driver = {
|
||||
.label = "Multifunction Composite (CDC + MS + ACM)",
|
||||
.bind = cdc_do_config,
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
static int cdc_config_register(struct usb_composite_dev *cdev)
|
||||
{
|
||||
static struct usb_configuration config = {
|
||||
.bind = cdc_do_config,
|
||||
.bConfigurationValue = MULTI_CDC_CONFIG_NUM,
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
|
||||
config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s;
|
||||
config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id;
|
||||
|
||||
return usb_add_config(cdev, &config);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int cdc_config_register(struct usb_composite_dev *cdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -230,7 +274,7 @@ static struct usb_configuration cdc_config_driver = {
|
||||
/****************************** Gadget Bind ******************************/
|
||||
|
||||
|
||||
static int __init multi_bind(struct usb_composite_dev *cdev)
|
||||
static int __ref multi_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
int status, gcnum;
|
||||
@ -252,67 +296,56 @@ static int __init multi_bind(struct usb_composite_dev *cdev)
|
||||
goto fail0;
|
||||
|
||||
/* set up mass storage function */
|
||||
fsg_common = fsg_common_from_params(0, cdev, &mod_data);
|
||||
if (IS_ERR(fsg_common)) {
|
||||
status = PTR_ERR(fsg_common);
|
||||
goto fail1;
|
||||
{
|
||||
void *retp;
|
||||
retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
|
||||
if (IS_ERR(retp)) {
|
||||
status = PTR_ERR(retp);
|
||||
goto fail1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* set bcdDevice */
|
||||
gcnum = usb_gadget_controller_number(gadget);
|
||||
if (gcnum >= 0)
|
||||
if (gcnum >= 0) {
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
|
||||
else {
|
||||
/* We assume that can_support_ecm() tells the truth;
|
||||
* but if the controller isn't recognized at all then
|
||||
* that assumption is a bit more likely to be wrong.
|
||||
*/
|
||||
WARNING(cdev, "controller '%s' not recognized\n",
|
||||
gadget->name);
|
||||
} else {
|
||||
WARNING(cdev, "controller '%s' not recognized\n", gadget->name);
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
|
||||
}
|
||||
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
*/
|
||||
|
||||
/* device descriptor strings: manufacturer, product */
|
||||
/* allocate string descriptor numbers */
|
||||
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
gadget->name);
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail2;
|
||||
strings_dev[STRING_MANUFACTURER_IDX].id = status;
|
||||
device_desc.iManufacturer = status;
|
||||
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
if (unlikely(status < 0))
|
||||
goto fail2;
|
||||
strings_dev[STRING_PRODUCT_IDX].id = status;
|
||||
device_desc.iProduct = status;
|
||||
|
||||
#ifdef USB_ETH_RNDIS
|
||||
/* register our first configuration */
|
||||
status = usb_add_config(cdev, &rndis_config_driver);
|
||||
if (status < 0)
|
||||
device_desc.iManufacturer =
|
||||
strings_dev[MULTI_STRING_MANUFACTURER_IDX].id;
|
||||
device_desc.iProduct =
|
||||
strings_dev[MULTI_STRING_PRODUCT_IDX].id;
|
||||
|
||||
/* register configurations */
|
||||
status = rndis_config_register(cdev);
|
||||
if (unlikely(status < 0))
|
||||
goto fail2;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_G_MULTI_CDC
|
||||
/* register our second configuration */
|
||||
status = usb_add_config(cdev, &cdc_config_driver);
|
||||
if (status < 0)
|
||||
status = cdc_config_register(cdev);
|
||||
if (unlikely(status < 0))
|
||||
goto fail2;
|
||||
#endif
|
||||
|
||||
dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
|
||||
fsg_common_put(fsg_common);
|
||||
/* we're done */
|
||||
dev_info(&gadget->dev, DRIVER_DESC "\n");
|
||||
fsg_common_put(&fsg_common);
|
||||
return 0;
|
||||
|
||||
|
||||
/* error recovery */
|
||||
fail2:
|
||||
fsg_common_put(fsg_common);
|
||||
fsg_common_put(&fsg_common);
|
||||
fail1:
|
||||
gserial_cleanup();
|
||||
fail0:
|
||||
@ -339,18 +372,15 @@ static struct usb_composite_driver multi_driver = {
|
||||
.unbind = __exit_p(multi_unbind),
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Michal Nazarewicz");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int __init g_multi_init(void)
|
||||
static int __init multi_init(void)
|
||||
{
|
||||
return usb_composite_register(&multi_driver);
|
||||
}
|
||||
module_init(g_multi_init);
|
||||
module_init(multi_init);
|
||||
|
||||
static void __exit g_multi_cleanup(void)
|
||||
static void __exit multi_exit(void)
|
||||
{
|
||||
usb_composite_unregister(&multi_driver);
|
||||
}
|
||||
module_exit(g_multi_cleanup);
|
||||
module_exit(multi_exit);
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timer.h>
|
||||
@ -70,6 +70,7 @@
|
||||
#define DRIVER_DESC "Printer Gadget"
|
||||
#define DRIVER_VERSION "2007 OCT 06"
|
||||
|
||||
static DEFINE_MUTEX(printer_mutex);
|
||||
static const char shortname [] = "printer";
|
||||
static const char driver_desc [] = DRIVER_DESC;
|
||||
|
||||
@ -476,7 +477,7 @@ printer_open(struct inode *inode, struct file *fd)
|
||||
unsigned long flags;
|
||||
int ret = -EBUSY;
|
||||
|
||||
lock_kernel();
|
||||
mutex_lock(&printer_mutex);
|
||||
dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
@ -492,7 +493,7 @@ printer_open(struct inode *inode, struct file *fd)
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
DBG(dev, "printer_open returned %x\n", ret);
|
||||
unlock_kernel();
|
||||
mutex_unlock(&printer_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1346,7 +1347,7 @@ printer_unbind(struct usb_gadget *gadget)
|
||||
set_gadget_data(gadget, NULL);
|
||||
}
|
||||
|
||||
static int __init
|
||||
static int __ref
|
||||
printer_bind(struct usb_gadget *gadget)
|
||||
{
|
||||
struct printer_dev *dev;
|
||||
|
@ -12,6 +12,8 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
@ -23,6 +25,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
@ -33,6 +36,7 @@
|
||||
#include <plat/regs-usb-hsotg.h>
|
||||
#include <mach/regs-sys.h>
|
||||
#include <plat/udc-hs.h>
|
||||
#include <plat/cpu.h>
|
||||
|
||||
#define DMA_ADDR_INVALID (~((dma_addr_t)0))
|
||||
|
||||
@ -91,7 +95,9 @@ struct s3c_hsotg_req;
|
||||
* For periodic IN endpoints, we have fifo_size and fifo_load to try
|
||||
* and keep track of the amount of data in the periodic FIFO for each
|
||||
* of these as we don't have a status register that tells us how much
|
||||
* is in each of them.
|
||||
* is in each of them. (note, this may actually be useless information
|
||||
* as in shared-fifo mode periodic in acts like a single-frame packet
|
||||
* buffer than a fifo)
|
||||
*/
|
||||
struct s3c_hsotg_ep {
|
||||
struct usb_ep ep;
|
||||
@ -128,6 +134,7 @@ struct s3c_hsotg_ep {
|
||||
* @regs: The memory area mapped for accessing registers.
|
||||
* @regs_res: The resource that was allocated when claiming register space.
|
||||
* @irq: The IRQ number we are using
|
||||
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
|
||||
* @debug_root: root directrory for debugfs.
|
||||
* @debug_file: main status file for debugfs.
|
||||
* @debug_fifo: FIFO status file for debugfs.
|
||||
@ -145,6 +152,9 @@ struct s3c_hsotg {
|
||||
void __iomem *regs;
|
||||
struct resource *regs_res;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
|
||||
unsigned int dedicated_fifos:1;
|
||||
|
||||
struct dentry *debug_root;
|
||||
struct dentry *debug_file;
|
||||
@ -310,11 +320,11 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
|
||||
hsotg->regs + S3C_GNPTXFSIZ);
|
||||
*/
|
||||
|
||||
/* set FIFO sizes to 2048/0x1C0 */
|
||||
/* set FIFO sizes to 2048/1024 */
|
||||
|
||||
writel(2048, hsotg->regs + S3C_GRXFSIZ);
|
||||
writel(S3C_GNPTXFSIZ_NPTxFStAddr(2048) |
|
||||
S3C_GNPTXFSIZ_NPTxFDep(0x1C0),
|
||||
S3C_GNPTXFSIZ_NPTxFDep(1024),
|
||||
hsotg->regs + S3C_GNPTXFSIZ);
|
||||
|
||||
/* arange all the rest of the TX FIFOs, as some versions of this
|
||||
@ -464,7 +474,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
||||
if (to_write == 0)
|
||||
return 0;
|
||||
|
||||
if (periodic) {
|
||||
if (periodic && !hsotg->dedicated_fifos) {
|
||||
u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index));
|
||||
int size_left;
|
||||
int size_done;
|
||||
@ -474,6 +484,14 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
||||
|
||||
size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
|
||||
|
||||
/* if shared fifo, we cannot write anything until the
|
||||
* previous data has been completely sent.
|
||||
*/
|
||||
if (hs_ep->fifo_load != 0) {
|
||||
s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n",
|
||||
__func__, size_left,
|
||||
hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size);
|
||||
@ -494,6 +512,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
||||
s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
|
||||
return -ENOSPC;
|
||||
}
|
||||
} else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
|
||||
can_write = readl(hsotg->regs + S3C_DTXFSTS(hs_ep->index));
|
||||
|
||||
can_write &= 0xffff;
|
||||
can_write *= 4;
|
||||
} else {
|
||||
if (S3C_GNPTXSTS_NPTxQSpcAvail_GET(gnptxsts) == 0) {
|
||||
dev_dbg(hsotg->dev,
|
||||
@ -505,6 +528,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
||||
}
|
||||
|
||||
can_write = S3C_GNPTXSTS_NPTxFSpcAvail_GET(gnptxsts);
|
||||
can_write *= 4; /* fifo size is in 32bit quantities. */
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n",
|
||||
@ -517,6 +541,17 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
||||
if (can_write > 512)
|
||||
can_write = 512;
|
||||
|
||||
/* limit the write to one max-packet size worth of data, but allow
|
||||
* the transfer to return that it did not run out of fifo space
|
||||
* doing it. */
|
||||
if (to_write > hs_ep->ep.maxpacket) {
|
||||
to_write = hs_ep->ep.maxpacket;
|
||||
|
||||
s3c_hsotg_en_gsint(hsotg,
|
||||
periodic ? S3C_GINTSTS_PTxFEmp :
|
||||
S3C_GINTSTS_NPTxFEmp);
|
||||
}
|
||||
|
||||
/* see if we can write data */
|
||||
|
||||
if (to_write > can_write) {
|
||||
@ -579,12 +614,10 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
|
||||
maxsize = S3C_DxEPTSIZ_XferSize_LIMIT + 1;
|
||||
maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1;
|
||||
} else {
|
||||
maxsize = 64+64;
|
||||
if (hs_ep->dir_in) {
|
||||
/* maxsize = S3C_DIEPTSIZ0_XferSize_LIMIT + 1; */
|
||||
maxsize = 64+64+1;
|
||||
maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1;
|
||||
} else {
|
||||
maxsize = 0x3f;
|
||||
maxpkt = 2;
|
||||
}
|
||||
}
|
||||
@ -1353,6 +1386,9 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||
read_ptr = hs_req->req.actual;
|
||||
max_req = hs_req->req.length - read_ptr;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
|
||||
__func__, to_read, max_req, read_ptr, hs_req->req.length);
|
||||
|
||||
if (to_read > max_req) {
|
||||
/* more data appeared than we where willing
|
||||
* to deal with in this request.
|
||||
@ -1362,9 +1398,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
|
||||
__func__, to_read, max_req, read_ptr, hs_req->req.length);
|
||||
|
||||
hs_ep->total_data += to_read;
|
||||
hs_req->req.actual += to_read;
|
||||
to_read = DIV_ROUND_UP(to_read, 4);
|
||||
@ -1433,9 +1466,11 @@ static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
|
||||
int epnum, bool was_setup)
|
||||
{
|
||||
u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum));
|
||||
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum];
|
||||
struct s3c_hsotg_req *hs_req = hs_ep->req;
|
||||
struct usb_request *req = &hs_req->req;
|
||||
unsigned size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
|
||||
int result = 0;
|
||||
|
||||
if (!hs_req) {
|
||||
@ -1444,9 +1479,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
|
||||
}
|
||||
|
||||
if (using_dma(hsotg)) {
|
||||
u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum));
|
||||
unsigned size_done;
|
||||
unsigned size_left;
|
||||
|
||||
/* Calculate the size of the transfer by checking how much
|
||||
* is left in the endpoint size register and then working it
|
||||
@ -1456,14 +1489,18 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
|
||||
* so may overshoot/undershoot the transfer.
|
||||
*/
|
||||
|
||||
size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
|
||||
|
||||
size_done = hs_ep->size_loaded - size_left;
|
||||
size_done += hs_ep->last_load;
|
||||
|
||||
req->actual = size_done;
|
||||
}
|
||||
|
||||
/* if there is more request to do, schedule new transfer */
|
||||
if (req->actual < req->length && size_left == 0) {
|
||||
s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->actual < req->length && req->short_not_ok) {
|
||||
dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n",
|
||||
__func__, req->actual, req->length);
|
||||
@ -1758,7 +1795,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
|
||||
if (dir_in) {
|
||||
s3c_hsotg_complete_in(hsotg, hs_ep);
|
||||
|
||||
if (idx == 0)
|
||||
if (idx == 0 && !hs_ep->req)
|
||||
s3c_hsotg_enqueue_setup(hsotg);
|
||||
} else if (using_dma(hsotg)) {
|
||||
/* We're using DMA, we need to fire an OutDone here
|
||||
@ -1818,6 +1855,15 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
|
||||
__func__, idx);
|
||||
clear |= S3C_DIEPMSK_INTknEPMisMsk;
|
||||
}
|
||||
|
||||
/* FIFO has space or is empty (see GAHBCFG) */
|
||||
if (hsotg->dedicated_fifos &&
|
||||
ints & S3C_DIEPMSK_TxFIFOEmpty) {
|
||||
dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n",
|
||||
__func__, idx);
|
||||
s3c_hsotg_trytx(hsotg, hs_ep);
|
||||
clear |= S3C_DIEPMSK_TxFIFOEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
writel(clear, hsotg->regs + epint_reg);
|
||||
@ -2071,17 +2117,12 @@ irq_retry:
|
||||
kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true);
|
||||
|
||||
/* it seems after a reset we can end up with a situation
|
||||
* where the TXFIFO still has data in it... try flushing
|
||||
* it to remove anything that may still be in it.
|
||||
* where the TXFIFO still has data in it... the docs
|
||||
* suggest resetting all the fifos, so use the init_fifo
|
||||
* code to relayout and flush the fifos.
|
||||
*/
|
||||
|
||||
if (1) {
|
||||
writel(S3C_GRSTCTL_TxFNum(0) | S3C_GRSTCTL_TxFFlsh,
|
||||
hsotg->regs + S3C_GRSTCTL);
|
||||
|
||||
dev_info(hsotg->dev, "GNPTXSTS=%08x\n",
|
||||
readl(hsotg->regs + S3C_GNPTXSTS));
|
||||
}
|
||||
s3c_hsotg_init_fifo(hsotg);
|
||||
|
||||
s3c_hsotg_enqueue_setup(hsotg);
|
||||
|
||||
@ -2274,6 +2315,12 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
|
||||
break;
|
||||
}
|
||||
|
||||
/* if the hardware has dedicated fifos, we must give each IN EP
|
||||
* a unique tx-fifo even if it is non-periodic.
|
||||
*/
|
||||
if (dir_in && hsotg->dedicated_fifos)
|
||||
epctrl |= S3C_DxEPCTL_TxFNum(index);
|
||||
|
||||
/* for non control endpoints, set PID to D0 */
|
||||
if (index)
|
||||
epctrl |= S3C_DxEPCTL_SetD0PID;
|
||||
@ -2563,7 +2610,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
||||
|
||||
writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
|
||||
S3C_DIEPMSK_INTknEPMisMsk |
|
||||
S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk,
|
||||
S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk |
|
||||
((hsotg->dedicated_fifos) ? S3C_DIEPMSK_TxFIFOEmpty : 0),
|
||||
hsotg->regs + S3C_DIEPMSK);
|
||||
|
||||
/* don't need XferCompl, we get that from RXFIFO in slave mode. In
|
||||
@ -2732,7 +2780,7 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
||||
*/
|
||||
|
||||
ptxfifo = readl(hsotg->regs + S3C_DPTXFSIZn(epnum));
|
||||
hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo);
|
||||
hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo) * 4;
|
||||
|
||||
/* if we're using dma, we need to set the next-endpoint pointer
|
||||
* to be something valid.
|
||||
@ -2753,13 +2801,33 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
||||
*/
|
||||
static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg)
|
||||
{
|
||||
u32 osc;
|
||||
struct clk *xusbxti;
|
||||
u32 pwr, osc;
|
||||
|
||||
writel(0, S3C_PHYPWR);
|
||||
pwr = readl(S3C_PHYPWR);
|
||||
pwr &= ~0x19;
|
||||
writel(pwr, S3C_PHYPWR);
|
||||
mdelay(1);
|
||||
|
||||
osc = hsotg->plat->is_osc ? S3C_PHYCLK_EXT_OSC : 0;
|
||||
|
||||
xusbxti = clk_get(hsotg->dev, "xusbxti");
|
||||
if (xusbxti && !IS_ERR(xusbxti)) {
|
||||
switch (clk_get_rate(xusbxti)) {
|
||||
case 12*MHZ:
|
||||
osc |= S3C_PHYCLK_CLKSEL_12M;
|
||||
break;
|
||||
case 24*MHZ:
|
||||
osc |= S3C_PHYCLK_CLKSEL_24M;
|
||||
break;
|
||||
default:
|
||||
case 48*MHZ:
|
||||
/* default reference clock */
|
||||
break;
|
||||
}
|
||||
clk_put(xusbxti);
|
||||
}
|
||||
|
||||
writel(osc | 0x10, S3C_PHYCLK);
|
||||
|
||||
/* issue a full set of resets to the otg and core */
|
||||
@ -2772,6 +2840,8 @@ static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg)
|
||||
|
||||
static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
|
||||
{
|
||||
u32 cfg4;
|
||||
|
||||
/* unmask subset of endpoint interrupts */
|
||||
|
||||
writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
|
||||
@ -2807,6 +2877,14 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
|
||||
|
||||
writel(using_dma(hsotg) ? S3C_GAHBCFG_DMAEn : 0x0,
|
||||
hsotg->regs + S3C_GAHBCFG);
|
||||
|
||||
/* check hardware configuration */
|
||||
|
||||
cfg4 = readl(hsotg->regs + 0x50);
|
||||
hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
|
||||
|
||||
dev_info(hsotg->dev, "%s fifos\n",
|
||||
hsotg->dedicated_fifos ? "dedicated" : "shared");
|
||||
}
|
||||
|
||||
static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
|
||||
@ -3181,13 +3259,20 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
|
||||
hsotg->dev = dev;
|
||||
hsotg->plat = plat;
|
||||
|
||||
hsotg->clk = clk_get(&pdev->dev, "otg");
|
||||
if (IS_ERR(hsotg->clk)) {
|
||||
dev_err(dev, "cannot get otg clock\n");
|
||||
ret = -EINVAL;
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, hsotg);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "cannot find register resource 0\n");
|
||||
ret = -EINVAL;
|
||||
goto err_mem;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
hsotg->regs_res = request_mem_region(res->start, resource_size(res),
|
||||
@ -3195,7 +3280,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
|
||||
if (!hsotg->regs_res) {
|
||||
dev_err(dev, "cannot reserve registers\n");
|
||||
ret = -ENOENT;
|
||||
goto err_mem;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
hsotg->regs = ioremap(res->start, resource_size(res));
|
||||
@ -3248,6 +3333,8 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
|
||||
|
||||
/* reset the system */
|
||||
|
||||
clk_enable(hsotg->clk);
|
||||
|
||||
s3c_hsotg_gate(pdev, true);
|
||||
|
||||
s3c_hsotg_otgreset(hsotg);
|
||||
@ -3271,7 +3358,8 @@ err_regs:
|
||||
err_regs_res:
|
||||
release_resource(hsotg->regs_res);
|
||||
kfree(hsotg->regs_res);
|
||||
|
||||
err_clk:
|
||||
clk_put(hsotg->clk);
|
||||
err_mem:
|
||||
kfree(hsotg);
|
||||
return ret;
|
||||
@ -3293,6 +3381,9 @@ static int __devexit s3c_hsotg_remove(struct platform_device *pdev)
|
||||
|
||||
s3c_hsotg_gate(pdev, false);
|
||||
|
||||
clk_disable(hsotg->clk);
|
||||
clk_put(hsotg->clk);
|
||||
|
||||
kfree(hsotg);
|
||||
return 0;
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init serial_bind_config(struct usb_configuration *c)
|
||||
static int __ref serial_bind_config(struct usb_configuration *c)
|
||||
{
|
||||
unsigned i;
|
||||
int status = 0;
|
||||
@ -161,7 +161,7 @@ static struct usb_configuration serial_config_driver = {
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
|
||||
static int __init gs_bind(struct usb_composite_dev *cdev)
|
||||
static int __ref gs_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
|
@ -57,10 +57,12 @@
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
|
||||
/* Thanks to NetChip Technologies for donating this product ID.
|
||||
/*
|
||||
* Thanks to NetChip Technologies for donating this product ID.
|
||||
*
|
||||
* DO NOT REUSE THESE IDs with any other driver!! Ever!!
|
||||
* Instead: allocate your own, using normal USB-IF procedures. */
|
||||
* Instead: allocate your own, using normal USB-IF procedures.
|
||||
*/
|
||||
#define FSG_VENDOR_ID 0x0525 /* NetChip */
|
||||
#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
|
||||
|
||||
@ -84,14 +86,27 @@
|
||||
#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
|
||||
#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
|
||||
|
||||
/* Keep those macros in sync with thos in
|
||||
* include/linux/ubs/composite.h or else GCC will complain. If they
|
||||
/*
|
||||
* Keep those macros in sync with those in
|
||||
* include/linux/usb/composite.h or else GCC will complain. If they
|
||||
* are identical (the same names of arguments, white spaces in the
|
||||
* same places) GCC will allow redefinition otherwise (even if some
|
||||
* white space is removed or added) warning will be issued. No
|
||||
* checking if those symbols is defined is performed because warning
|
||||
* is desired when those macros were defined by someone else to mean
|
||||
* something else. */
|
||||
* white space is removed or added) warning will be issued.
|
||||
*
|
||||
* Those macros are needed here because File Storage Gadget does not
|
||||
* include the composite.h header. For composite gadgets those macros
|
||||
* are redundant since composite.h is included any way.
|
||||
*
|
||||
* One could check whether those macros are already defined (which
|
||||
* would indicate composite.h had been included) or not (which would
|
||||
* indicate we were in FSG) but this is not done because a warning is
|
||||
* desired if definitions here differ from the ones in composite.h.
|
||||
*
|
||||
* We want the definitions to match and be the same in File Storage
|
||||
* Gadget as well as Mass Storage Function (and so composite gadgets
|
||||
* using MSF). If someone changes them in composite.h it will produce
|
||||
* a warning in this file when building MSF.
|
||||
*/
|
||||
#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args)
|
||||
#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args)
|
||||
#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args)
|
||||
@ -269,6 +284,7 @@ struct fsg_lun {
|
||||
unsigned int prevent_medium_removal:1;
|
||||
unsigned int registered:1;
|
||||
unsigned int info_valid:1;
|
||||
unsigned int nofua:1;
|
||||
|
||||
u32 sense_data;
|
||||
u32 sense_data_info;
|
||||
@ -313,9 +329,11 @@ struct fsg_buffhd {
|
||||
enum fsg_buffer_state state;
|
||||
struct fsg_buffhd *next;
|
||||
|
||||
/* The NetChip 2280 is faster, and handles some protocol faults
|
||||
/*
|
||||
* The NetChip 2280 is faster, and handles some protocol faults
|
||||
* better, if we don't submit any short bulk-out read requests.
|
||||
* So we will record the intended request length here. */
|
||||
* So we will record the intended request length here.
|
||||
*/
|
||||
unsigned int bulk_out_intended_length;
|
||||
|
||||
struct usb_request *inreq;
|
||||
@ -395,8 +413,10 @@ fsg_intf_desc = {
|
||||
.iInterface = FSG_STRING_INTERFACE,
|
||||
};
|
||||
|
||||
/* Three full-speed endpoint descriptors: bulk-in, bulk-out,
|
||||
* and interrupt-in. */
|
||||
/*
|
||||
* Three full-speed endpoint descriptors: bulk-in, bulk-out, and
|
||||
* interrupt-in.
|
||||
*/
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_fs_bulk_in_desc = {
|
||||
@ -459,7 +479,7 @@ static struct usb_descriptor_header *fsg_fs_function[] = {
|
||||
*
|
||||
* That means alternate endpoint descriptors (bigger packets)
|
||||
* and a "device qualifier" ... plus more construction options
|
||||
* for the config descriptor.
|
||||
* for the configuration descriptor.
|
||||
*/
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_hs_bulk_in_desc = {
|
||||
@ -547,8 +567,10 @@ static struct usb_gadget_strings fsg_stringtab = {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* If the next two routines are called while the gadget is registered,
|
||||
* the caller must own fsg->filesem for writing. */
|
||||
/*
|
||||
* If the next two routines are called while the gadget is registered,
|
||||
* the caller must own fsg->filesem for writing.
|
||||
*/
|
||||
|
||||
static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||
{
|
||||
@ -587,8 +609,10 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If we can't read the file, it's no good.
|
||||
* If we can't write the file, use it read-only. */
|
||||
/*
|
||||
* If we can't read the file, it's no good.
|
||||
* If we can't write the file, use it read-only.
|
||||
*/
|
||||
if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) {
|
||||
LINFO(curlun, "file not readable: %s\n", filename);
|
||||
goto out;
|
||||
@ -646,8 +670,10 @@ static void fsg_lun_close(struct fsg_lun *curlun)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Sync the file data, don't bother with the metadata.
|
||||
* This code was copied from fs/buffer.c:sys_fdatasync(). */
|
||||
/*
|
||||
* Sync the file data, don't bother with the metadata.
|
||||
* This code was copied from fs/buffer.c:sys_fdatasync().
|
||||
*/
|
||||
static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
|
||||
{
|
||||
struct file *filp = curlun->filp;
|
||||
@ -689,6 +715,14 @@ static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
|
||||
: curlun->initially_ro);
|
||||
}
|
||||
|
||||
static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", curlun->nofua);
|
||||
}
|
||||
|
||||
static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -723,26 +757,47 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
|
||||
ssize_t rc = count;
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
struct rw_semaphore *filesem = dev_get_drvdata(dev);
|
||||
int i;
|
||||
unsigned long ro;
|
||||
|
||||
if (sscanf(buf, "%d", &i) != 1)
|
||||
if (strict_strtoul(buf, 2, &ro))
|
||||
return -EINVAL;
|
||||
|
||||
/* Allow the write-enable status to change only while the backing file
|
||||
* is closed. */
|
||||
/*
|
||||
* Allow the write-enable status to change only while the
|
||||
* backing file is closed.
|
||||
*/
|
||||
down_read(filesem);
|
||||
if (fsg_lun_is_open(curlun)) {
|
||||
LDBG(curlun, "read-only status change prevented\n");
|
||||
rc = -EBUSY;
|
||||
} else {
|
||||
curlun->ro = !!i;
|
||||
curlun->initially_ro = !!i;
|
||||
curlun->ro = ro;
|
||||
curlun->initially_ro = ro;
|
||||
LDBG(curlun, "read-only status set to %d\n", curlun->ro);
|
||||
}
|
||||
up_read(filesem);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t fsg_store_nofua(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
unsigned long nofua;
|
||||
|
||||
if (strict_strtoul(buf, 2, &nofua))
|
||||
return -EINVAL;
|
||||
|
||||
/* Sync data when switching from async mode to sync */
|
||||
if (!nofua && curlun->nofua)
|
||||
fsg_lun_fsync_sub(curlun);
|
||||
|
||||
curlun->nofua = nofua;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
|
@ -704,17 +704,6 @@ static char *host_addr;
|
||||
module_param(host_addr, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
|
||||
|
||||
|
||||
static u8 __init nibble(unsigned char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
c = toupper(c);
|
||||
if (isxdigit(c))
|
||||
return 10 + c - 'A';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_ether_addr(const char *str, u8 *dev_addr)
|
||||
{
|
||||
if (str) {
|
||||
@ -725,8 +714,8 @@ static int get_ether_addr(const char *str, u8 *dev_addr)
|
||||
|
||||
if ((*str == '.') || (*str == ':'))
|
||||
str++;
|
||||
num = nibble(*str++) << 4;
|
||||
num |= (nibble(*str++));
|
||||
num = hex_to_bin(*str++) << 4;
|
||||
num |= hex_to_bin(*str++);
|
||||
dev_addr [i] = num;
|
||||
}
|
||||
if (is_valid_ether_addr(dev_addr))
|
||||
|
@ -18,6 +18,7 @@
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -308,7 +308,7 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
|
||||
* USB configuration
|
||||
*/
|
||||
|
||||
static int __init
|
||||
static int __ref
|
||||
webcam_config_bind(struct usb_configuration *c)
|
||||
{
|
||||
return uvc_bind_config(c, uvc_control_cls, uvc_fs_streaming_cls,
|
||||
@ -330,7 +330,7 @@ webcam_unbind(struct usb_composite_dev *cdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init
|
||||
static int __ref
|
||||
webcam_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -264,7 +264,7 @@ static void zero_resume(struct usb_composite_dev *cdev)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init zero_bind(struct usb_composite_dev *cdev)
|
||||
static int __ref zero_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
|
@ -72,8 +72,9 @@ config USB_EHCI_ROOT_HUB_TT
|
||||
from ARC, and has since changed hands a few times.
|
||||
|
||||
config USB_EHCI_TT_NEWSCHED
|
||||
bool "Improved Transaction Translator scheduling (EXPERIMENTAL)"
|
||||
depends on USB_EHCI_HCD && EXPERIMENTAL
|
||||
bool "Improved Transaction Translator scheduling"
|
||||
depends on USB_EHCI_HCD
|
||||
default y
|
||||
---help---
|
||||
This changes the periodic scheduling code to fill more of the low
|
||||
and full speed bandwidth available from the Transaction Translator
|
||||
@ -84,9 +85,11 @@ config USB_EHCI_TT_NEWSCHED
|
||||
If you have multiple periodic low/fullspeed devices connected to a
|
||||
highspeed USB hub which is connected to a highspeed USB Host
|
||||
Controller, and some of those devices will not work correctly
|
||||
(possibly due to "ENOSPC" or "-28" errors), say Y.
|
||||
(possibly due to "ENOSPC" or "-28" errors), say Y. Conversely, if
|
||||
you have only one such device and it doesn't work, you could try
|
||||
saying N.
|
||||
|
||||
If unsure, say N.
|
||||
If unsure, say Y.
|
||||
|
||||
config USB_EHCI_BIG_ENDIAN_MMIO
|
||||
bool
|
||||
|
@ -228,7 +228,7 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
|
||||
* the root hub is either suspended or stopped.
|
||||
*/
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
ehci_prepare_ports_for_controller_suspend(ehci);
|
||||
ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
|
||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||
|
||||
|
@ -98,13 +98,18 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
|
||||
HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
|
||||
} else {
|
||||
ehci_dbg (ehci,
|
||||
"%s hcc_params %04x thresh %d uframes %s%s%s\n",
|
||||
"%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n",
|
||||
label,
|
||||
params,
|
||||
HCC_ISOC_THRES(params),
|
||||
HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
|
||||
HCC_CANPARK(params) ? " park" : "",
|
||||
HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
|
||||
HCC_64BIT_ADDR(params) ? " 64 bit addr" : "",
|
||||
HCC_LPM(params) ? " LPM" : "",
|
||||
HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "",
|
||||
HCC_HW_PREFETCH(params) ? " hw prefetch" : "",
|
||||
HCC_32FRAME_PERIODIC_LIST(params) ?
|
||||
" 32 peridic list" : "");
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -191,8 +196,9 @@ static int __maybe_unused
|
||||
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
"%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
|
||||
"%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s",
|
||||
label, label [0] ? " " : "", status,
|
||||
(status & STS_PPCE_MASK) ? " PPCE" : "",
|
||||
(status & STS_ASS) ? " Async" : "",
|
||||
(status & STS_PSS) ? " Periodic" : "",
|
||||
(status & STS_RECL) ? " Recl" : "",
|
||||
@ -210,8 +216,9 @@ static int __maybe_unused
|
||||
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
"%s%sintrenable %02x%s%s%s%s%s%s",
|
||||
"%s%sintrenable %02x%s%s%s%s%s%s%s",
|
||||
label, label [0] ? " " : "", enable,
|
||||
(enable & STS_PPCE_MASK) ? " PPCE" : "",
|
||||
(enable & STS_IAA) ? " IAA" : "",
|
||||
(enable & STS_FATAL) ? " FATAL" : "",
|
||||
(enable & STS_FLR) ? " FLR" : "",
|
||||
@ -228,9 +235,15 @@ static int
|
||||
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
"%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s",
|
||||
"%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s "
|
||||
"period=%s%s %s",
|
||||
label, label [0] ? " " : "", command,
|
||||
(command & CMD_PARK) ? "park" : "(park)",
|
||||
(command & CMD_HIRD) ? " HIRD" : "",
|
||||
(command & CMD_PPCEE) ? " PPCEE" : "",
|
||||
(command & CMD_FSP) ? " FSP" : "",
|
||||
(command & CMD_ASPE) ? " ASPE" : "",
|
||||
(command & CMD_PSPE) ? " PSPE" : "",
|
||||
(command & CMD_PARK) ? " park" : "(park)",
|
||||
CMD_PARK_CNT (command),
|
||||
(command >> 16) & 0x3f,
|
||||
(command & CMD_LRESET) ? " LReset" : "",
|
||||
@ -257,11 +270,22 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
}
|
||||
|
||||
return scnprintf (buf, len,
|
||||
"%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s",
|
||||
"%s%sport:%d status %06x %d %s%s%s%s%s%s "
|
||||
"sig=%s%s%s%s%s%s%s%s%s%s%s",
|
||||
label, label [0] ? " " : "", port, status,
|
||||
status>>25,/*device address */
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ?
|
||||
" ACK" : "",
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ?
|
||||
" NYET" : "",
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ?
|
||||
" STALL" : "",
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ?
|
||||
" ERR" : "",
|
||||
(status & PORT_POWER) ? " POWER" : "",
|
||||
(status & PORT_OWNER) ? " OWNER" : "",
|
||||
sig,
|
||||
(status & PORT_LPM) ? " LPM" : "",
|
||||
(status & PORT_RESET) ? " RESET" : "",
|
||||
(status & PORT_SUSPEND) ? " SUSPEND" : "",
|
||||
(status & PORT_RESUME) ? " RESUME" : "",
|
||||
@ -330,6 +354,13 @@ static int debug_async_open(struct inode *, struct file *);
|
||||
static int debug_periodic_open(struct inode *, struct file *);
|
||||
static int debug_registers_open(struct inode *, struct file *);
|
||||
static int debug_async_open(struct inode *, struct file *);
|
||||
static int debug_lpm_open(struct inode *, struct file *);
|
||||
static ssize_t debug_lpm_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos);
|
||||
static ssize_t debug_lpm_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos);
|
||||
static int debug_lpm_close(struct inode *inode, struct file *file);
|
||||
|
||||
static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
|
||||
static int debug_close(struct inode *, struct file *);
|
||||
|
||||
@ -351,6 +382,13 @@ static const struct file_operations debug_registers_fops = {
|
||||
.read = debug_output,
|
||||
.release = debug_close,
|
||||
};
|
||||
static const struct file_operations debug_lpm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = debug_lpm_open,
|
||||
.read = debug_lpm_read,
|
||||
.write = debug_lpm_write,
|
||||
.release = debug_lpm_close,
|
||||
};
|
||||
|
||||
static struct dentry *ehci_debug_root;
|
||||
|
||||
@ -674,7 +712,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
size = scnprintf (next, size,
|
||||
"bus %s, device %s\n"
|
||||
"%s\n"
|
||||
@ -917,51 +955,127 @@ static int debug_registers_open(struct inode *inode, struct file *file)
|
||||
return file->private_data ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static int debug_lpm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int debug_lpm_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t debug_lpm_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
/* TODO: show lpm stats */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
char buf[50];
|
||||
size_t len;
|
||||
u32 temp;
|
||||
unsigned long port;
|
||||
u32 __iomem *portsc ;
|
||||
u32 params;
|
||||
|
||||
hcd = bus_to_hcd(file->private_data);
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
|
||||
len = min(count, sizeof(buf) - 1);
|
||||
if (copy_from_user(buf, user_buf, len))
|
||||
return -EFAULT;
|
||||
buf[len] = '\0';
|
||||
if (len > 0 && buf[len - 1] == '\n')
|
||||
buf[len - 1] = '\0';
|
||||
|
||||
if (strncmp(buf, "enable", 5) == 0) {
|
||||
if (strict_strtoul(buf + 7, 10, &port))
|
||||
return -EINVAL;
|
||||
params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
if (port > HCS_N_PORTS(params)) {
|
||||
ehci_dbg(ehci, "ERR: LPM on bad port %lu\n", port);
|
||||
return -ENODEV;
|
||||
}
|
||||
portsc = &ehci->regs->port_status[port-1];
|
||||
temp = ehci_readl(ehci, portsc);
|
||||
if (!(temp & PORT_DEV_ADDR)) {
|
||||
ehci_dbg(ehci, "LPM: no device attached\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
temp |= PORT_LPM;
|
||||
ehci_writel(ehci, temp, portsc);
|
||||
printk(KERN_INFO "force enable LPM for port %lu\n", port);
|
||||
} else if (strncmp(buf, "hird=", 5) == 0) {
|
||||
unsigned long hird;
|
||||
if (strict_strtoul(buf + 5, 16, &hird))
|
||||
return -EINVAL;
|
||||
printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird);
|
||||
temp = ehci_readl(ehci, &ehci->regs->command);
|
||||
temp &= ~CMD_HIRD;
|
||||
temp |= hird << 24;
|
||||
ehci_writel(ehci, temp, &ehci->regs->command);
|
||||
} else if (strncmp(buf, "disable", 7) == 0) {
|
||||
if (strict_strtoul(buf + 8, 10, &port))
|
||||
return -EINVAL;
|
||||
params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
if (port > HCS_N_PORTS(params)) {
|
||||
ehci_dbg(ehci, "ERR: LPM off bad port %lu\n", port);
|
||||
return -ENODEV;
|
||||
}
|
||||
portsc = &ehci->regs->port_status[port-1];
|
||||
temp = ehci_readl(ehci, portsc);
|
||||
if (!(temp & PORT_DEV_ADDR)) {
|
||||
ehci_dbg(ehci, "ERR: no device attached\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
temp &= ~PORT_LPM;
|
||||
ehci_writel(ehci, temp, portsc);
|
||||
printk(KERN_INFO "disabled LPM for port %lu\n", port);
|
||||
} else
|
||||
return -EOPNOTSUPP;
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline void create_debug_files (struct ehci_hcd *ehci)
|
||||
{
|
||||
struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
|
||||
|
||||
ehci->debug_dir = debugfs_create_dir(bus->bus_name, ehci_debug_root);
|
||||
if (!ehci->debug_dir)
|
||||
goto dir_error;
|
||||
return;
|
||||
|
||||
ehci->debug_async = debugfs_create_file("async", S_IRUGO,
|
||||
ehci->debug_dir, bus,
|
||||
&debug_async_fops);
|
||||
if (!ehci->debug_async)
|
||||
goto async_error;
|
||||
if (!debugfs_create_file("async", S_IRUGO, ehci->debug_dir, bus,
|
||||
&debug_async_fops))
|
||||
goto file_error;
|
||||
|
||||
ehci->debug_periodic = debugfs_create_file("periodic", S_IRUGO,
|
||||
ehci->debug_dir, bus,
|
||||
&debug_periodic_fops);
|
||||
if (!ehci->debug_periodic)
|
||||
goto periodic_error;
|
||||
if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus,
|
||||
&debug_periodic_fops))
|
||||
goto file_error;
|
||||
|
||||
if (!debugfs_create_file("registers", S_IRUGO, ehci->debug_dir, bus,
|
||||
&debug_registers_fops))
|
||||
goto file_error;
|
||||
|
||||
if (!debugfs_create_file("lpm", S_IRUGO|S_IWUGO, ehci->debug_dir, bus,
|
||||
&debug_lpm_fops))
|
||||
goto file_error;
|
||||
|
||||
ehci->debug_registers = debugfs_create_file("registers", S_IRUGO,
|
||||
ehci->debug_dir, bus,
|
||||
&debug_registers_fops);
|
||||
if (!ehci->debug_registers)
|
||||
goto registers_error;
|
||||
return;
|
||||
|
||||
registers_error:
|
||||
debugfs_remove(ehci->debug_periodic);
|
||||
periodic_error:
|
||||
debugfs_remove(ehci->debug_async);
|
||||
async_error:
|
||||
debugfs_remove(ehci->debug_dir);
|
||||
dir_error:
|
||||
ehci->debug_periodic = NULL;
|
||||
ehci->debug_async = NULL;
|
||||
ehci->debug_dir = NULL;
|
||||
file_error:
|
||||
debugfs_remove_recursive(ehci->debug_dir);
|
||||
}
|
||||
|
||||
static inline void remove_debug_files (struct ehci_hcd *ehci)
|
||||
{
|
||||
debugfs_remove(ehci->debug_registers);
|
||||
debugfs_remove(ehci->debug_periodic);
|
||||
debugfs_remove(ehci->debug_async);
|
||||
debugfs_remove(ehci->debug_dir);
|
||||
debugfs_remove_recursive(ehci->debug_dir);
|
||||
}
|
||||
|
||||
#endif /* STUB_DEBUG_FILES */
|
||||
|
@ -313,7 +313,8 @@ static int ehci_fsl_drv_suspend(struct device *dev)
|
||||
struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
|
||||
void __iomem *non_ehci = hcd->regs;
|
||||
|
||||
ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd));
|
||||
ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
|
||||
device_may_wakeup(dev));
|
||||
if (!fsl_deep_sleep())
|
||||
return 0;
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
@ -78,7 +79,13 @@ static const char hcd_name [] = "ehci_hcd";
|
||||
#define EHCI_TUNE_RL_TT 0
|
||||
#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
|
||||
#define EHCI_TUNE_MULT_TT 1
|
||||
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
|
||||
/*
|
||||
* Some drivers think it's safe to schedule isochronous transfers more than
|
||||
* 256 ms into the future (partly as a result of an old bug in the scheduling
|
||||
* code). In an attempt to avoid trouble, we will use a minimum scheduling
|
||||
* length of 512 frames instead of 256.
|
||||
*/
|
||||
#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */
|
||||
|
||||
#define EHCI_IAA_MSECS 10 /* arbitrary */
|
||||
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
|
||||
@ -100,6 +107,11 @@ static int ignore_oc = 0;
|
||||
module_param (ignore_oc, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
|
||||
|
||||
/* for link power management(LPM) feature */
|
||||
static unsigned int hird;
|
||||
module_param(hird, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n");
|
||||
|
||||
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -304,6 +316,7 @@ static void end_unlink_async(struct ehci_hcd *ehci);
|
||||
static void ehci_work(struct ehci_hcd *ehci);
|
||||
|
||||
#include "ehci-hub.c"
|
||||
#include "ehci-lpm.c"
|
||||
#include "ehci-mem.c"
|
||||
#include "ehci-q.c"
|
||||
#include "ehci-sched.c"
|
||||
@ -577,6 +590,11 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
|
||||
log2_irq_thresh = 0;
|
||||
temp = 1 << (16 + log2_irq_thresh);
|
||||
if (HCC_PER_PORT_CHANGE_EVENT(hcc_params)) {
|
||||
ehci->has_ppcd = 1;
|
||||
ehci_dbg(ehci, "enable per-port change event\n");
|
||||
temp |= CMD_PPCEE;
|
||||
}
|
||||
if (HCC_CANPARK(hcc_params)) {
|
||||
/* HW default park == 3, on hardware that supports it (like
|
||||
* NVidia and ALI silicon), maximizes throughput on the async
|
||||
@ -603,10 +621,22 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||
default: BUG();
|
||||
}
|
||||
}
|
||||
if (HCC_LPM(hcc_params)) {
|
||||
/* support link power management EHCI 1.1 addendum */
|
||||
ehci_dbg(ehci, "support lpm\n");
|
||||
ehci->has_lpm = 1;
|
||||
if (hird > 0xf) {
|
||||
ehci_dbg(ehci, "hird %d invalid, use default 0",
|
||||
hird);
|
||||
hird = 0;
|
||||
}
|
||||
temp |= hird << 24;
|
||||
}
|
||||
ehci->command = temp;
|
||||
|
||||
/* Accept arbitrarily long scatter-gather lists */
|
||||
hcd->self.sg_tablesize = ~0;
|
||||
if (!(hcd->driver->flags & HCD_LOCAL_MEM))
|
||||
hcd->self.sg_tablesize = ~0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -619,7 +649,6 @@ static int ehci_run (struct usb_hcd *hcd)
|
||||
u32 hcc_params;
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
hcd->poll_rh = 0;
|
||||
|
||||
/* EHCI spec section 4.1 */
|
||||
if ((retval = ehci_reset(ehci)) != 0) {
|
||||
@ -764,6 +793,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
||||
/* remote wakeup [4.3.1] */
|
||||
if (status & STS_PCD) {
|
||||
unsigned i = HCS_N_PORTS (ehci->hcs_params);
|
||||
u32 ppcd = 0;
|
||||
|
||||
/* kick root hub later */
|
||||
pcd_status = status;
|
||||
@ -772,9 +802,18 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
||||
if (!(cmd & CMD_RUN))
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
|
||||
/* get per-port change detect bits */
|
||||
if (ehci->has_ppcd)
|
||||
ppcd = status >> 16;
|
||||
|
||||
while (i--) {
|
||||
int pstatus = ehci_readl(ehci,
|
||||
&ehci->regs->port_status [i]);
|
||||
int pstatus;
|
||||
|
||||
/* leverage per-port change bits feature */
|
||||
if (ehci->has_ppcd && !(ppcd & (1 << i)))
|
||||
continue;
|
||||
pstatus = ehci_readl(ehci,
|
||||
&ehci->regs->port_status[i]);
|
||||
|
||||
if (pstatus & PORT_OWNER)
|
||||
continue;
|
||||
|
@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
||||
}
|
||||
|
||||
static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
bool suspending)
|
||||
bool suspending, bool do_wakeup)
|
||||
{
|
||||
int port;
|
||||
u32 temp;
|
||||
@ -117,8 +117,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
* when the controller is suspended or resumed. In all other
|
||||
* cases they don't need to be changed.
|
||||
*/
|
||||
if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup ||
|
||||
device_may_wakeup(ehci_to_hcd(ehci)->self.controller))
|
||||
if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup)
|
||||
return;
|
||||
|
||||
/* clear phy low-power mode before changing wakeup flags */
|
||||
@ -167,6 +166,10 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Does the root hub have a port wakeup pending? */
|
||||
if (!suspending && (ehci_readl(ehci, &ehci->regs->status) & STS_PCD))
|
||||
usb_hcd_resume_root_hub(ehci_to_hcd(ehci));
|
||||
}
|
||||
|
||||
static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||
@ -316,7 +319,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||
if (time_before (jiffies, ehci->next_statechange))
|
||||
msleep(5);
|
||||
spin_lock_irq (&ehci->lock);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
@ -603,6 +606,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
|
||||
u32 mask;
|
||||
int ports, i, retval = 1;
|
||||
unsigned long flags;
|
||||
u32 ppcd = 0;
|
||||
|
||||
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
|
||||
if (!HC_IS_RUNNING(hcd->state))
|
||||
@ -632,7 +636,15 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
|
||||
|
||||
/* port N changes (bit N)? */
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
|
||||
/* get per-port change detect bits */
|
||||
if (ehci->has_ppcd)
|
||||
ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16;
|
||||
|
||||
for (i = 0; i < ports; i++) {
|
||||
/* leverage per-port change bits feature */
|
||||
if (ehci->has_ppcd && !(ppcd & (1 << i)))
|
||||
continue;
|
||||
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
|
||||
|
||||
/*
|
||||
@ -790,6 +802,11 @@ static int ehci_hub_control (
|
||||
status_reg);
|
||||
break;
|
||||
case USB_PORT_FEAT_C_CONNECTION:
|
||||
if (ehci->has_lpm) {
|
||||
/* clear PORTSC bits on disconnect */
|
||||
temp &= ~PORT_LPM;
|
||||
temp &= ~PORT_DEV_ADDR;
|
||||
}
|
||||
ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC,
|
||||
status_reg);
|
||||
break;
|
||||
|
83
drivers/usb/host/ehci-lpm.c
Normal file
83
drivers/usb/host/ehci-lpm.c
Normal file
@ -0,0 +1,83 @@
|
||||
/* ehci-lpm.c EHCI HCD LPM support code
|
||||
* Copyright (c) 2008 - 2010, Intel Corporation.
|
||||
* Author: Jacob Pan <jacob.jun.pan@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* this file is part of ehci-hcd.c */
|
||||
static int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num)
|
||||
{
|
||||
u32 __iomem portsc;
|
||||
|
||||
ehci_dbg(ehci, "set dev address %d for port %d\n", dev_addr, port_num);
|
||||
if (port_num > HCS_N_PORTS(ehci->hcs_params)) {
|
||||
ehci_dbg(ehci, "invalid port number %d\n", port_num);
|
||||
return -ENODEV;
|
||||
}
|
||||
portsc = ehci_readl(ehci, &ehci->regs->port_status[port_num-1]);
|
||||
portsc &= ~PORT_DEV_ADDR;
|
||||
portsc |= dev_addr<<25;
|
||||
ehci_writel(ehci, portsc, &ehci->regs->port_status[port_num-1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* this function is used to check if the device support LPM
|
||||
* if yes, mark the PORTSC register with PORT_LPM bit
|
||||
*/
|
||||
static int ehci_lpm_check(struct ehci_hcd *ehci, int port)
|
||||
{
|
||||
u32 __iomem *portsc ;
|
||||
u32 val32;
|
||||
int retval;
|
||||
|
||||
portsc = &ehci->regs->port_status[port-1];
|
||||
val32 = ehci_readl(ehci, portsc);
|
||||
if (!(val32 & PORT_DEV_ADDR)) {
|
||||
ehci_dbg(ehci, "LPM: no device attached\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
val32 |= PORT_LPM;
|
||||
ehci_writel(ehci, val32, portsc);
|
||||
msleep(5);
|
||||
val32 |= PORT_SUSPEND;
|
||||
ehci_dbg(ehci, "Sending LPM 0x%08x to port %d\n", val32, port);
|
||||
ehci_writel(ehci, val32, portsc);
|
||||
/* wait for ACK */
|
||||
msleep(10);
|
||||
retval = handshake(ehci, &ehci->regs->port_status[port-1], PORT_SSTS,
|
||||
PORTSC_SUSPEND_STS_ACK, 125);
|
||||
dbg_port(ehci, "LPM", port, val32);
|
||||
if (retval != -ETIMEDOUT) {
|
||||
ehci_dbg(ehci, "LPM: device ACK for LPM\n");
|
||||
val32 |= PORT_LPM;
|
||||
/*
|
||||
* now device should be in L1 sleep, let's wake up the device
|
||||
* so that we can complete enumeration.
|
||||
*/
|
||||
ehci_writel(ehci, val32, portsc);
|
||||
msleep(10);
|
||||
val32 |= PORT_RESUME;
|
||||
ehci_writel(ehci, val32, portsc);
|
||||
} else {
|
||||
ehci_dbg(ehci, "LPM: device does not ACK, disable LPM %d\n",
|
||||
retval);
|
||||
val32 &= ~PORT_LPM;
|
||||
retval = -ETIMEDOUT;
|
||||
ehci_writel(ehci, val32, portsc);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
@ -38,6 +38,7 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/ulpi.h>
|
||||
#include <plat/usb.h>
|
||||
|
||||
/*
|
||||
@ -236,6 +237,35 @@ static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void omap_ehci_soft_phy_reset(struct ehci_hcd_omap *omap, u8 port)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
||||
unsigned reg = 0;
|
||||
|
||||
reg = ULPI_FUNC_CTRL_RESET
|
||||
/* FUNCTION_CTRL_SET register */
|
||||
| (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT)
|
||||
/* Write */
|
||||
| (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT)
|
||||
/* PORTn */
|
||||
| ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT)
|
||||
/* start ULPI access*/
|
||||
| (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT);
|
||||
|
||||
ehci_omap_writel(omap->ehci_base, EHCI_INSNREG05_ULPI, reg);
|
||||
|
||||
/* Wait for ULPI access completion */
|
||||
while ((ehci_omap_readl(omap->ehci_base, EHCI_INSNREG05_ULPI)
|
||||
& (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_dbg(omap->dev, "phy reset operation timed out\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* omap_start_ehc
|
||||
* - Start the TI USBHOST controller
|
||||
*/
|
||||
@ -425,6 +455,12 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
gpio_set_value(omap->reset_gpio_port[1], 1);
|
||||
}
|
||||
|
||||
/* Soft reset the PHY using PHY reset command over ULPI */
|
||||
if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY)
|
||||
omap_ehci_soft_phy_reset(omap, 0);
|
||||
if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY)
|
||||
omap_ehci_soft_phy_reset(omap, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
err_sys_status:
|
||||
|
@ -114,6 +114,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||
break;
|
||||
case PCI_VENDOR_ID_INTEL:
|
||||
ehci->need_io_watchdog = 0;
|
||||
ehci->fs_i_thresh = 1;
|
||||
if (pdev->device == 0x27cc) {
|
||||
ehci->broken_periodic = 1;
|
||||
ehci_info(ehci, "using broken periodic workaround\n");
|
||||
@ -277,7 +278,7 @@ done:
|
||||
* Also they depend on separate root hub suspend/resume.
|
||||
*/
|
||||
|
||||
static int ehci_pci_suspend(struct usb_hcd *hcd)
|
||||
static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
unsigned long flags;
|
||||
@ -291,7 +292,7 @@ static int ehci_pci_suspend(struct usb_hcd *hcd)
|
||||
* the root hub is either suspended or stopped.
|
||||
*/
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
ehci_prepare_ports_for_controller_suspend(ehci);
|
||||
ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
|
||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||
|
||||
@ -361,6 +362,22 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int rc = 0;
|
||||
|
||||
if (!udev->parent) /* udev is root hub itself, impossible */
|
||||
rc = -1;
|
||||
/* we only support lpm device connected to root hub yet */
|
||||
if (ehci->has_lpm && !udev->parent->parent) {
|
||||
rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum);
|
||||
if (!rc)
|
||||
rc = ehci_lpm_check(ehci, udev->portnum);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_pci_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "EHCI Host Controller",
|
||||
@ -407,6 +424,11 @@ static const struct hc_driver ehci_pci_hc_driver = {
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
/*
|
||||
* call back when device connected and addressed
|
||||
*/
|
||||
.update_device = ehci_update_device,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
|
@ -1126,8 +1126,7 @@ submit_async (
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
|
||||
&ehci_to_hcd(ehci)->flags))) {
|
||||
if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
|
||||
rc = -ESHUTDOWN;
|
||||
goto done;
|
||||
}
|
||||
|
@ -880,8 +880,7 @@ static int intr_submit (
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
|
||||
&ehci_to_hcd(ehci)->flags))) {
|
||||
if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
|
||||
status = -ESHUTDOWN;
|
||||
goto done_not_linked;
|
||||
}
|
||||
@ -1075,15 +1074,6 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
|
||||
if (stream->ep)
|
||||
stream->ep->hcpriv = NULL;
|
||||
|
||||
if (stream->rescheduled) {
|
||||
ehci_info (ehci, "ep%d%s-iso rescheduled "
|
||||
"%lu times in %lu seconds\n",
|
||||
stream->bEndpointAddress, is_in ? "in" : "out",
|
||||
stream->rescheduled,
|
||||
((jiffies - stream->start)/HZ)
|
||||
);
|
||||
}
|
||||
|
||||
kfree(stream);
|
||||
}
|
||||
}
|
||||
@ -1396,30 +1386,25 @@ iso_stream_schedule (
|
||||
struct ehci_iso_stream *stream
|
||||
)
|
||||
{
|
||||
u32 now, next, start, period;
|
||||
u32 now, next, start, period, span;
|
||||
int status;
|
||||
unsigned mod = ehci->periodic_size << 3;
|
||||
struct ehci_iso_sched *sched = urb->hcpriv;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
if (sched->span > (mod - SCHEDULE_SLOP)) {
|
||||
period = urb->interval;
|
||||
span = sched->span;
|
||||
if (!stream->highspeed) {
|
||||
period <<= 3;
|
||||
span <<= 3;
|
||||
}
|
||||
|
||||
if (span > mod - SCHEDULE_SLOP) {
|
||||
ehci_dbg (ehci, "iso request %p too long\n", urb);
|
||||
status = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((stream->depth + sched->span) > mod) {
|
||||
ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n",
|
||||
urb, stream->depth, sched->span, mod);
|
||||
status = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
period = urb->interval;
|
||||
if (!stream->highspeed)
|
||||
period <<= 3;
|
||||
|
||||
now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
|
||||
now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1);
|
||||
|
||||
/* Typical case: reuse current schedule, stream is still active.
|
||||
* Hopefully there are no gaps from the host falling behind
|
||||
@ -1427,34 +1412,35 @@ iso_stream_schedule (
|
||||
* slot in the schedule, implicitly assuming URB_ISO_ASAP.
|
||||
*/
|
||||
if (likely (!list_empty (&stream->td_list))) {
|
||||
pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
|
||||
start = stream->next_uframe;
|
||||
u32 excess;
|
||||
|
||||
/* For high speed devices, allow scheduling within the
|
||||
* isochronous scheduling threshold. For full speed devices,
|
||||
* don't. (Work around for Intel ICH9 bug.)
|
||||
* isochronous scheduling threshold. For full speed devices
|
||||
* and Intel PCI-based controllers, don't (work around for
|
||||
* Intel ICH9 bug).
|
||||
*/
|
||||
if (!stream->highspeed &&
|
||||
pdev->vendor == PCI_VENDOR_ID_INTEL)
|
||||
if (!stream->highspeed && ehci->fs_i_thresh)
|
||||
next = now + ehci->i_thresh;
|
||||
else
|
||||
next = now;
|
||||
|
||||
/* Fell behind (by up to twice the slop amount)? */
|
||||
if (((start - next) & (mod - 1)) >=
|
||||
mod - 2 * SCHEDULE_SLOP)
|
||||
start += period * DIV_ROUND_UP(
|
||||
(next - start) & (mod - 1),
|
||||
period);
|
||||
|
||||
/* Tried to schedule too far into the future? */
|
||||
if (unlikely(((start - now) & (mod - 1)) + sched->span
|
||||
>= mod - 2 * SCHEDULE_SLOP)) {
|
||||
/* Fell behind (by up to twice the slop amount)?
|
||||
* We decide based on the time of the last currently-scheduled
|
||||
* slot, not the time of the next available slot.
|
||||
*/
|
||||
excess = (stream->next_uframe - period - next) & (mod - 1);
|
||||
if (excess >= mod - 2 * SCHEDULE_SLOP)
|
||||
start = next + excess - mod + period *
|
||||
DIV_ROUND_UP(mod - excess, period);
|
||||
else
|
||||
start = next + excess + period;
|
||||
if (start - now >= mod) {
|
||||
ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
|
||||
urb, start - now - period, period,
|
||||
mod);
|
||||
status = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
stream->next_uframe = start;
|
||||
goto ready;
|
||||
}
|
||||
|
||||
/* need to schedule; when's the next (u)frame we could start?
|
||||
@ -1463,51 +1449,60 @@ iso_stream_schedule (
|
||||
* can also help high bandwidth if the dma and irq loads don't
|
||||
* jump until after the queue is primed.
|
||||
*/
|
||||
start = SCHEDULE_SLOP + (now & ~0x07);
|
||||
start %= mod;
|
||||
stream->next_uframe = start;
|
||||
else {
|
||||
start = SCHEDULE_SLOP + (now & ~0x07);
|
||||
|
||||
/* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
|
||||
/* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
|
||||
|
||||
/* find a uframe slot with enough bandwidth */
|
||||
for (; start < (stream->next_uframe + period); start++) {
|
||||
int enough_space;
|
||||
/* find a uframe slot with enough bandwidth */
|
||||
next = start + period;
|
||||
for (; start < next; start++) {
|
||||
|
||||
/* check schedule: enough space? */
|
||||
if (stream->highspeed)
|
||||
enough_space = itd_slot_ok (ehci, mod, start,
|
||||
stream->usecs, period);
|
||||
else {
|
||||
if ((start % 8) >= 6)
|
||||
continue;
|
||||
enough_space = sitd_slot_ok (ehci, mod, stream,
|
||||
start, sched, period);
|
||||
/* check schedule: enough space? */
|
||||
if (stream->highspeed) {
|
||||
if (itd_slot_ok(ehci, mod, start,
|
||||
stream->usecs, period))
|
||||
break;
|
||||
} else {
|
||||
if ((start % 8) >= 6)
|
||||
continue;
|
||||
if (sitd_slot_ok(ehci, mod, stream,
|
||||
start, sched, period))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* schedule it here if there's enough bandwidth */
|
||||
if (enough_space) {
|
||||
stream->next_uframe = start % mod;
|
||||
goto ready;
|
||||
/* no room in the schedule */
|
||||
if (start == next) {
|
||||
ehci_dbg(ehci, "iso resched full %p (now %d max %d)\n",
|
||||
urb, now, now + mod);
|
||||
status = -ENOSPC;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* no room in the schedule */
|
||||
ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n",
|
||||
list_empty (&stream->td_list) ? "" : "re",
|
||||
urb, now, now + mod);
|
||||
status = -ENOSPC;
|
||||
/* Tried to schedule too far into the future? */
|
||||
if (unlikely(start - now + span - period
|
||||
>= mod - 2 * SCHEDULE_SLOP)) {
|
||||
ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
|
||||
urb, start - now, span - period,
|
||||
mod - 2 * SCHEDULE_SLOP);
|
||||
status = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
iso_sched_free (stream, sched);
|
||||
urb->hcpriv = NULL;
|
||||
return status;
|
||||
stream->next_uframe = start & (mod - 1);
|
||||
|
||||
ready:
|
||||
/* report high speed start in uframes; full speed, in frames */
|
||||
urb->start_frame = stream->next_uframe;
|
||||
if (!stream->highspeed)
|
||||
urb->start_frame >>= 3;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
iso_sched_free(stream, sched);
|
||||
urb->hcpriv = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -1602,7 +1597,7 @@ itd_link_urb (
|
||||
struct ehci_iso_sched *iso_sched = urb->hcpriv;
|
||||
struct ehci_itd *itd;
|
||||
|
||||
next_uframe = stream->next_uframe % mod;
|
||||
next_uframe = stream->next_uframe & (mod - 1);
|
||||
|
||||
if (unlikely (list_empty(&stream->td_list))) {
|
||||
ehci_to_hcd(ehci)->self.bandwidth_allocated
|
||||
@ -1613,7 +1608,6 @@ itd_link_urb (
|
||||
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
|
||||
urb->interval,
|
||||
next_uframe >> 3, next_uframe & 0x7);
|
||||
stream->start = jiffies;
|
||||
}
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
|
||||
|
||||
@ -1639,14 +1633,13 @@ itd_link_urb (
|
||||
itd_patch(ehci, itd, iso_sched, packet, uframe);
|
||||
|
||||
next_uframe += stream->interval;
|
||||
stream->depth += stream->interval;
|
||||
next_uframe %= mod;
|
||||
next_uframe &= mod - 1;
|
||||
packet++;
|
||||
|
||||
/* link completed itds into the schedule */
|
||||
if (((next_uframe >> 3) != frame)
|
||||
|| packet == urb->number_of_packets) {
|
||||
itd_link (ehci, frame % ehci->periodic_size, itd);
|
||||
itd_link(ehci, frame & (ehci->periodic_size - 1), itd);
|
||||
itd = NULL;
|
||||
}
|
||||
}
|
||||
@ -1695,7 +1688,6 @@ itd_complete (
|
||||
|
||||
t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]);
|
||||
itd->hw_transaction [uframe] = 0;
|
||||
stream->depth -= stream->interval;
|
||||
|
||||
/* report transfer status */
|
||||
if (unlikely (t & ISO_ERRS)) {
|
||||
@ -1815,8 +1807,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
|
||||
|
||||
/* schedule ... need to lock */
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
|
||||
&ehci_to_hcd(ehci)->flags))) {
|
||||
if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
|
||||
status = -ESHUTDOWN;
|
||||
goto done_not_linked;
|
||||
}
|
||||
@ -2024,9 +2015,8 @@ sitd_link_urb (
|
||||
"sched devp %s ep%d%s-iso [%d] %dms/%04x\n",
|
||||
urb->dev->devpath, stream->bEndpointAddress & 0x0f,
|
||||
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
|
||||
(next_uframe >> 3) % ehci->periodic_size,
|
||||
(next_uframe >> 3) & (ehci->periodic_size - 1),
|
||||
stream->interval, hc32_to_cpu(ehci, stream->splits));
|
||||
stream->start = jiffies;
|
||||
}
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
|
||||
|
||||
@ -2047,13 +2037,12 @@ sitd_link_urb (
|
||||
sitd->urb = urb;
|
||||
|
||||
sitd_patch(ehci, stream, sitd, sched, packet);
|
||||
sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
|
||||
sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1),
|
||||
sitd);
|
||||
|
||||
next_uframe += stream->interval << 3;
|
||||
stream->depth += stream->interval << 3;
|
||||
}
|
||||
stream->next_uframe = next_uframe % mod;
|
||||
stream->next_uframe = next_uframe & (mod - 1);
|
||||
|
||||
/* don't need that schedule data any more */
|
||||
iso_sched_free (stream, sched);
|
||||
@ -2111,7 +2100,6 @@ sitd_complete (
|
||||
desc->actual_length = desc->length - SITD_LENGTH(t);
|
||||
urb->actual_length += desc->actual_length;
|
||||
}
|
||||
stream->depth -= stream->interval << 3;
|
||||
|
||||
/* handle completion now? */
|
||||
if ((urb_index + 1) != urb->number_of_packets)
|
||||
@ -2201,8 +2189,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
|
||||
|
||||
/* schedule ... need to lock */
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
|
||||
&ehci_to_hcd(ehci)->flags))) {
|
||||
if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
|
||||
status = -ESHUTDOWN;
|
||||
goto done_not_linked;
|
||||
}
|
||||
@ -2263,7 +2250,7 @@ scan_periodic (struct ehci_hcd *ehci)
|
||||
now_uframe = ehci->next_uframe;
|
||||
if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
|
||||
clock = ehci_readl(ehci, &ehci->regs->frame_index);
|
||||
clock_frame = (clock >> 3) % ehci->periodic_size;
|
||||
clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
|
||||
} else {
|
||||
clock = now_uframe + mod - 1;
|
||||
clock_frame = -1;
|
||||
@ -2272,7 +2259,7 @@ scan_periodic (struct ehci_hcd *ehci)
|
||||
free_cached_lists(ehci);
|
||||
ehci->clock_frame = clock_frame;
|
||||
}
|
||||
clock %= mod;
|
||||
clock &= mod - 1;
|
||||
clock_frame = clock >> 3;
|
||||
|
||||
for (;;) {
|
||||
@ -2361,7 +2348,7 @@ restart:
|
||||
* frame is current.
|
||||
*/
|
||||
if (((frame == clock_frame) ||
|
||||
(((frame + 1) % ehci->periodic_size)
|
||||
(((frame + 1) & (ehci->periodic_size - 1))
|
||||
== clock_frame))
|
||||
&& live
|
||||
&& (q.sitd->hw_results &
|
||||
@ -2428,7 +2415,8 @@ restart:
|
||||
|| ehci->periodic_sched == 0)
|
||||
break;
|
||||
ehci->next_uframe = now_uframe;
|
||||
now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
|
||||
now = ehci_readl(ehci, &ehci->regs->frame_index) &
|
||||
(mod - 1);
|
||||
if (now_uframe == now)
|
||||
break;
|
||||
|
||||
@ -2441,7 +2429,7 @@ restart:
|
||||
}
|
||||
} else {
|
||||
now_uframe++;
|
||||
now_uframe %= mod;
|
||||
now_uframe &= mod - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +130,7 @@ struct ehci_hcd { /* one per controller */
|
||||
unsigned has_amcc_usb23:1;
|
||||
unsigned need_io_watchdog:1;
|
||||
unsigned broken_periodic:1;
|
||||
unsigned fs_i_thresh:1; /* Intel iso scheduling */
|
||||
|
||||
/* required for usb32 quirk */
|
||||
#define OHCI_CTRL_HCFS (3 << 6)
|
||||
@ -140,7 +141,8 @@ struct ehci_hcd { /* one per controller */
|
||||
#define OHCI_HCCTRL_LEN 0x4
|
||||
__hc32 *ohci_hcctrl_reg;
|
||||
unsigned has_hostpc:1;
|
||||
|
||||
unsigned has_lpm:1; /* support link power management */
|
||||
unsigned has_ppcd:1; /* support per-port change bits */
|
||||
u8 sbrn; /* packed release number */
|
||||
|
||||
/* irq statistics */
|
||||
@ -154,9 +156,6 @@ struct ehci_hcd { /* one per controller */
|
||||
/* debug files */
|
||||
#ifdef DEBUG
|
||||
struct dentry *debug_dir;
|
||||
struct dentry *debug_async;
|
||||
struct dentry *debug_periodic;
|
||||
struct dentry *debug_registers;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -401,15 +400,12 @@ struct ehci_iso_stream {
|
||||
u32 refcount;
|
||||
u8 bEndpointAddress;
|
||||
u8 highspeed;
|
||||
u16 depth; /* depth in uframes */
|
||||
struct list_head td_list; /* queued itds/sitds */
|
||||
struct list_head free_list; /* list of unused itds/sitds */
|
||||
struct usb_device *udev;
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
/* output of (re)scheduling */
|
||||
unsigned long start; /* jiffies */
|
||||
unsigned long rescheduled;
|
||||
int next_uframe;
|
||||
__hc32 splits;
|
||||
|
||||
@ -538,11 +534,11 @@ struct ehci_fstn {
|
||||
|
||||
/* Prepare the PORTSC wakeup flags during controller suspend/resume */
|
||||
|
||||
#define ehci_prepare_ports_for_controller_suspend(ehci) \
|
||||
ehci_adjust_port_wakeup_flags(ehci, true);
|
||||
#define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \
|
||||
ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup);
|
||||
|
||||
#define ehci_prepare_ports_for_controller_resume(ehci) \
|
||||
ehci_adjust_port_wakeup_flags(ehci, false);
|
||||
#define ehci_prepare_ports_for_controller_resume(ehci) \
|
||||
ehci_adjust_port_wakeup_flags(ehci, false, false);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -159,7 +159,7 @@ static int hwahc_op_start(struct usb_hcd *usb_hcd)
|
||||
goto error_set_cluster_id;
|
||||
|
||||
usb_hcd->uses_new_polling = 1;
|
||||
usb_hcd->poll_rh = 1;
|
||||
set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags);
|
||||
usb_hcd->state = HC_STATE_RUNNING;
|
||||
result = 0;
|
||||
out:
|
||||
@ -776,7 +776,7 @@ static int hwahc_probe(struct usb_interface *usb_iface,
|
||||
goto error_alloc;
|
||||
}
|
||||
usb_hcd->wireless = 1;
|
||||
usb_hcd->flags |= HCD_FLAG_SAW_IRQ;
|
||||
set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags);
|
||||
wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
||||
hwahc_init(hwahc);
|
||||
|
@ -1521,7 +1521,7 @@ static int imx21_hc_reset(struct usb_hcd *hcd)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
spin_unlock_irq(&imx21->lock);
|
||||
schedule_timeout(1);
|
||||
schedule_timeout_uninterruptible(1);
|
||||
spin_lock_irq(&imx21->lock);
|
||||
}
|
||||
spin_unlock_irqrestore(&imx21->lock, flags);
|
||||
|
@ -8,29 +8,7 @@
|
||||
/*
|
||||
* Platform specific compile time options
|
||||
*/
|
||||
#if defined(CONFIG_ARCH_KARO)
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#include <asm/arch/karo.h>
|
||||
|
||||
#define USE_32BIT 1
|
||||
|
||||
|
||||
/* These options are mutually eclusive */
|
||||
#define USE_PLATFORM_DELAY 1
|
||||
#define USE_NDELAY 0
|
||||
/*
|
||||
* MAX_ROOT_PORTS: Number of downstream ports
|
||||
*
|
||||
* The chip has two USB ports, one of which can be configured as
|
||||
* an USB device port, so the value of this constant is implementation
|
||||
* specific.
|
||||
*/
|
||||
#define MAX_ROOT_PORTS 2
|
||||
#define DUMMY_DELAY_ACCESS do {} while (0)
|
||||
|
||||
/* insert platform specific definitions for other machines here */
|
||||
#elif defined(CONFIG_BLACKFIN)
|
||||
#if defined(CONFIG_BLACKFIN)
|
||||
|
||||
#include <linux/io.h>
|
||||
#define USE_32BIT 0
|
||||
|
@ -482,7 +482,6 @@ static int isp1760_run(struct usb_hcd *hcd)
|
||||
u32 chipid;
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
hcd->poll_rh = 0;
|
||||
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
isp1760_enable_interrupts(hcd);
|
||||
@ -1450,7 +1449,7 @@ static int isp1760_prepare_enqueue(struct isp1760_hcd *priv, struct urb *urb,
|
||||
epnum = urb->ep->desc.bEndpointAddress;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &priv_to_hcd(priv)->flags)) {
|
||||
if (!HCD_HW_ACCESSIBLE(priv_to_hcd(priv))) {
|
||||
rc = -ESHUTDOWN;
|
||||
goto done;
|
||||
}
|
||||
|
@ -645,7 +645,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
||||
hcd->product_desc,
|
||||
hcd_name);
|
||||
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
size -= scnprintf (next, size,
|
||||
"SUSPENDED (no register access)\n");
|
||||
goto done;
|
||||
@ -687,7 +687,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
||||
next += temp;
|
||||
|
||||
temp = scnprintf (next, size, "hub poll timer %s\n",
|
||||
ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
|
||||
HCD_POLL_RH(ohci_to_hcd(ohci)) ? "ON" : "off");
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
|
@ -212,7 +212,7 @@ static int ohci_urb_enqueue (
|
||||
spin_lock_irqsave (&ohci->lock, flags);
|
||||
|
||||
/* don't submit to a dead HC */
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
retval = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
@ -685,7 +685,7 @@ retry:
|
||||
}
|
||||
|
||||
/* use rhsc irqs after khubd is fully initialized */
|
||||
hcd->poll_rh = 1;
|
||||
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
hcd->uses_new_polling = 1;
|
||||
|
||||
/* start controller operations */
|
||||
@ -822,7 +822,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
||||
else if (ints & OHCI_INTR_RD) {
|
||||
ohci_vdbg(ohci, "resume detect\n");
|
||||
ohci_writel(ohci, OHCI_INTR_RD, ®s->intrstatus);
|
||||
hcd->poll_rh = 1;
|
||||
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
if (ohci->autostop) {
|
||||
spin_lock (&ohci->lock);
|
||||
ohci_rh_resume (ohci);
|
||||
|
@ -284,7 +284,7 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
|
||||
|
||||
spin_lock_irq (&ohci->lock);
|
||||
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
|
||||
if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
|
||||
rc = -ESHUTDOWN;
|
||||
else
|
||||
rc = ohci_rh_suspend (ohci, 0);
|
||||
@ -302,7 +302,7 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
|
||||
|
||||
spin_lock_irq (&ohci->lock);
|
||||
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
|
||||
if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
|
||||
rc = -ESHUTDOWN;
|
||||
else
|
||||
rc = ohci_rh_resume (ohci);
|
||||
@ -355,6 +355,11 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd)
|
||||
ohci_readl(ohci, &ohci->regs->intrenable);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
/* Does the root hub have a port wakeup pending? */
|
||||
if (ohci_readl(ohci, &ohci->regs->intrstatus) &
|
||||
(OHCI_INTR_RD | OHCI_INTR_RHSC))
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
}
|
||||
|
||||
/* Carry out polling-, autostop-, and autoresume-related state changes */
|
||||
@ -364,7 +369,7 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
|
||||
int poll_rh = 1;
|
||||
int rhsc_enable;
|
||||
|
||||
/* Some broken controllers never turn off RHCS in the interrupt
|
||||
/* Some broken controllers never turn off RHSC in the interrupt
|
||||
* status register. For their sake we won't re-enable RHSC
|
||||
* interrupts if the interrupt bit is already active.
|
||||
*/
|
||||
@ -489,7 +494,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave (&ohci->lock, flags);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
|
||||
if (!HCD_HW_ACCESSIBLE(hcd))
|
||||
goto done;
|
||||
|
||||
/* undocumented erratum seen on at least rev D */
|
||||
@ -533,8 +538,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
|
||||
}
|
||||
}
|
||||
|
||||
hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed,
|
||||
any_connected, rhsc_status);
|
||||
if (ohci_root_hub_state_changes(ohci, changed,
|
||||
any_connected, rhsc_status))
|
||||
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
else
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore (&ohci->lock, flags);
|
||||
@ -701,7 +710,7 @@ static int ohci_hub_control (
|
||||
u32 temp;
|
||||
int retval = 0;
|
||||
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
|
||||
if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
|
||||
return -ESHUTDOWN;
|
||||
|
||||
switch (typeReq) {
|
||||
|
@ -392,7 +392,7 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ohci_pci_suspend(struct usb_hcd *hcd)
|
||||
static int ohci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
unsigned long flags;
|
||||
|
@ -93,8 +93,11 @@ static void ssb_ohci_detach(struct ssb_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = ssb_get_drvdata(dev);
|
||||
|
||||
if (hcd->driver->shutdown)
|
||||
hcd->driver->shutdown(hcd);
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
ssb_device_disable(dev, 0);
|
||||
}
|
||||
@ -106,10 +109,52 @@ static int ssb_ohci_attach(struct ssb_device *dev)
|
||||
int err = -ENOMEM;
|
||||
u32 tmp, flags = 0;
|
||||
|
||||
if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
|
||||
flags |= SSB_OHCI_TMSLOW_HOSTMODE;
|
||||
if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
|
||||
dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ssb_device_enable(dev, flags);
|
||||
if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) {
|
||||
/* Put the device into host-mode. */
|
||||
flags |= SSB_OHCI_TMSLOW_HOSTMODE;
|
||||
ssb_device_enable(dev, flags);
|
||||
} else if (dev->id.coreid == SSB_DEV_USB20_HOST) {
|
||||
/*
|
||||
* USB 2.0 special considerations:
|
||||
*
|
||||
* In addition to the standard SSB reset sequence, the Host
|
||||
* Control Register must be programmed to bring the USB core
|
||||
* and various phy components out of reset.
|
||||
*/
|
||||
ssb_device_enable(dev, 0);
|
||||
ssb_write32(dev, 0x200, 0x7ff);
|
||||
|
||||
/* Change Flush control reg */
|
||||
tmp = ssb_read32(dev, 0x400);
|
||||
tmp &= ~8;
|
||||
ssb_write32(dev, 0x400, tmp);
|
||||
tmp = ssb_read32(dev, 0x400);
|
||||
|
||||
/* Change Shim control reg */
|
||||
tmp = ssb_read32(dev, 0x304);
|
||||
tmp &= ~0x100;
|
||||
ssb_write32(dev, 0x304, tmp);
|
||||
tmp = ssb_read32(dev, 0x304);
|
||||
|
||||
udelay(1);
|
||||
|
||||
/* Work around for 5354 failures */
|
||||
if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) {
|
||||
/* Change syn01 reg */
|
||||
tmp = 0x00fe00fe;
|
||||
ssb_write32(dev, 0x894, tmp);
|
||||
|
||||
/* Change syn03 reg */
|
||||
tmp = ssb_read32(dev, 0x89c);
|
||||
tmp |= 0x1;
|
||||
ssb_write32(dev, 0x89c, tmp);
|
||||
}
|
||||
} else
|
||||
ssb_device_enable(dev, 0);
|
||||
|
||||
hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
|
||||
dev_name(dev->dev));
|
||||
@ -200,6 +245,7 @@ static int ssb_ohci_resume(struct ssb_device *dev)
|
||||
static const struct ssb_device_id ssb_ohci_table[] = {
|
||||
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
|
||||
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
|
||||
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
|
||||
SSB_DEVTABLE_END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ssb, ssb_ohci_table);
|
||||
|
@ -1641,8 +1641,7 @@ static int submit_async(struct oxu_hcd *oxu, struct urb *urb,
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave(&oxu->lock, flags);
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
|
||||
&oxu_to_hcd(oxu)->flags))) {
|
||||
if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) {
|
||||
rc = -ESHUTDOWN;
|
||||
goto done;
|
||||
}
|
||||
@ -2209,8 +2208,7 @@ static int intr_submit(struct oxu_hcd *oxu, struct urb *urb,
|
||||
|
||||
spin_lock_irqsave(&oxu->lock, flags);
|
||||
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
|
||||
&oxu_to_hcd(oxu)->flags))) {
|
||||
if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) {
|
||||
status = -ESHUTDOWN;
|
||||
goto done;
|
||||
}
|
||||
@ -2715,7 +2713,6 @@ static int oxu_run(struct usb_hcd *hcd)
|
||||
u32 temp, hcc_params;
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
hcd->poll_rh = 0;
|
||||
|
||||
/* EHCI spec section 4.1 */
|
||||
retval = ehci_reset(oxu);
|
||||
|
@ -813,8 +813,11 @@ static int sl811h_urb_enqueue(
|
||||
#endif
|
||||
|
||||
/* avoid all allocations within spinlocks */
|
||||
if (!hep->hcpriv)
|
||||
if (!hep->hcpriv) {
|
||||
ep = kzalloc(sizeof *ep, mem_flags);
|
||||
if (ep == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&sl811->lock, flags);
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
#include "uhci-hcd.h"
|
||||
|
||||
#define uhci_debug_operations (* (const struct file_operations *) NULL)
|
||||
static struct dentry *uhci_debugfs_root;
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -495,18 +494,16 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct uhci_hcd *uhci = inode->i_private;
|
||||
struct uhci_debug *up;
|
||||
int ret = -ENOMEM;
|
||||
unsigned long flags;
|
||||
|
||||
lock_kernel();
|
||||
up = kmalloc(sizeof(*up), GFP_KERNEL);
|
||||
if (!up)
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
|
||||
if (!up->data) {
|
||||
kfree(up);
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
up->size = 0;
|
||||
@ -517,10 +514,7 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
|
||||
|
||||
file->private_data = up;
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
unlock_kernel();
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
|
||||
@ -528,9 +522,9 @@ static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
|
||||
struct uhci_debug *up;
|
||||
loff_t new = -1;
|
||||
|
||||
lock_kernel();
|
||||
up = file->private_data;
|
||||
|
||||
/* XXX: atomic 64bit seek access, but that needs to be fixed in the VFS */
|
||||
switch (whence) {
|
||||
case 0:
|
||||
new = off;
|
||||
@ -539,11 +533,10 @@ static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
|
||||
new = file->f_pos + off;
|
||||
break;
|
||||
}
|
||||
if (new < 0 || new > up->size) {
|
||||
unlock_kernel();
|
||||
|
||||
if (new < 0 || new > up->size)
|
||||
return -EINVAL;
|
||||
}
|
||||
unlock_kernel();
|
||||
|
||||
return (file->f_pos = new);
|
||||
}
|
||||
|
||||
@ -564,7 +557,6 @@ static int uhci_debug_release(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef uhci_debug_operations
|
||||
static const struct file_operations uhci_debug_operations = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = uhci_debug_open,
|
||||
@ -572,6 +564,7 @@ static const struct file_operations uhci_debug_operations = {
|
||||
.read = uhci_debug_read,
|
||||
.release = uhci_debug_release,
|
||||
};
|
||||
#define UHCI_DEBUG_OPS
|
||||
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
|
@ -140,7 +140,7 @@ static void finish_reset(struct uhci_hcd *uhci)
|
||||
uhci->rh_state = UHCI_RH_RESET;
|
||||
uhci->is_stopped = UHCI_IS_STOPPED;
|
||||
uhci_to_hcd(uhci)->state = HC_STATE_HALT;
|
||||
uhci_to_hcd(uhci)->poll_rh = 0;
|
||||
clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
|
||||
|
||||
uhci->dead = 0; /* Full reset resurrects the controller */
|
||||
}
|
||||
@ -176,6 +176,8 @@ static void check_and_reset_hc(struct uhci_hcd *uhci)
|
||||
*/
|
||||
static void configure_hc(struct uhci_hcd *uhci)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
|
||||
|
||||
/* Set the frame length to the default: 1 ms exactly */
|
||||
outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF);
|
||||
|
||||
@ -191,8 +193,11 @@ static void configure_hc(struct uhci_hcd *uhci)
|
||||
mb();
|
||||
|
||||
/* Enable PIRQ */
|
||||
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
|
||||
USBLEGSUP_DEFAULT);
|
||||
pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
|
||||
|
||||
/* Disable platform-specific non-PME# wakeup */
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL)
|
||||
pci_write_config_byte(pdev, USBRES_INTEL, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -344,7 +349,10 @@ __acquires(uhci->lock)
|
||||
/* If interrupts don't work and remote wakeup is enabled then
|
||||
* the suspended root hub needs to be polled.
|
||||
*/
|
||||
uhci_to_hcd(uhci)->poll_rh = (!int_enable && wakeup_enable);
|
||||
if (!int_enable && wakeup_enable)
|
||||
set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
|
||||
else
|
||||
clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
|
||||
|
||||
uhci_scan_schedule(uhci);
|
||||
uhci_fsbr_off(uhci);
|
||||
@ -363,7 +371,7 @@ static void start_rh(struct uhci_hcd *uhci)
|
||||
uhci->io_addr + USBINTR);
|
||||
mb();
|
||||
uhci->rh_state = UHCI_RH_RUNNING;
|
||||
uhci_to_hcd(uhci)->poll_rh = 1;
|
||||
set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
|
||||
}
|
||||
|
||||
static void wakeup_rh(struct uhci_hcd *uhci)
|
||||
@ -589,7 +597,7 @@ static int uhci_start(struct usb_hcd *hcd)
|
||||
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
||||
int retval = -EBUSY;
|
||||
int i;
|
||||
struct dentry *dentry;
|
||||
struct dentry __maybe_unused *dentry;
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
|
||||
@ -599,18 +607,16 @@ static int uhci_start(struct usb_hcd *hcd)
|
||||
INIT_LIST_HEAD(&uhci->idle_qh_list);
|
||||
init_waitqueue_head(&uhci->waitqh);
|
||||
|
||||
if (DEBUG_CONFIGURED) {
|
||||
dentry = debugfs_create_file(hcd->self.bus_name,
|
||||
S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
|
||||
uhci, &uhci_debug_operations);
|
||||
if (!dentry) {
|
||||
dev_err(uhci_dev(uhci), "couldn't create uhci "
|
||||
"debugfs entry\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_create_debug_entry;
|
||||
}
|
||||
uhci->dentry = dentry;
|
||||
#ifdef UHCI_DEBUG_OPS
|
||||
dentry = debugfs_create_file(hcd->self.bus_name,
|
||||
S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
|
||||
uhci, &uhci_debug_operations);
|
||||
if (!dentry) {
|
||||
dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
uhci->dentry = dentry;
|
||||
#endif
|
||||
|
||||
uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
|
||||
UHCI_NUMFRAMES * sizeof(*uhci->frame),
|
||||
@ -691,7 +697,9 @@ static int uhci_start(struct usb_hcd *hcd)
|
||||
|
||||
configure_hc(uhci);
|
||||
uhci->is_initialized = 1;
|
||||
spin_lock_irq(&uhci->lock);
|
||||
start_rh(uhci);
|
||||
spin_unlock_irq(&uhci->lock);
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -722,7 +730,6 @@ err_alloc_frame_cpu:
|
||||
err_alloc_frame:
|
||||
debugfs_remove(uhci->dentry);
|
||||
|
||||
err_create_debug_entry:
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -731,7 +738,7 @@ static void uhci_stop(struct usb_hcd *hcd)
|
||||
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
||||
|
||||
spin_lock_irq(&uhci->lock);
|
||||
if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && !uhci->dead)
|
||||
if (HCD_HW_ACCESSIBLE(hcd) && !uhci->dead)
|
||||
uhci_hc_died(uhci);
|
||||
uhci_scan_schedule(uhci);
|
||||
spin_unlock_irq(&uhci->lock);
|
||||
@ -748,7 +755,7 @@ static int uhci_rh_suspend(struct usb_hcd *hcd)
|
||||
int rc = 0;
|
||||
|
||||
spin_lock_irq(&uhci->lock);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
|
||||
if (!HCD_HW_ACCESSIBLE(hcd))
|
||||
rc = -ESHUTDOWN;
|
||||
else if (uhci->dead)
|
||||
; /* Dead controllers tell no tales */
|
||||
@ -775,7 +782,7 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
|
||||
int rc = 0;
|
||||
|
||||
spin_lock_irq(&uhci->lock);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
|
||||
if (!HCD_HW_ACCESSIBLE(hcd))
|
||||
rc = -ESHUTDOWN;
|
||||
else if (!uhci->dead)
|
||||
wakeup_rh(uhci);
|
||||
@ -783,15 +790,16 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int uhci_pci_suspend(struct usb_hcd *hcd)
|
||||
static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
|
||||
{
|
||||
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
|
||||
int rc = 0;
|
||||
|
||||
dev_dbg(uhci_dev(uhci), "%s\n", __func__);
|
||||
|
||||
spin_lock_irq(&uhci->lock);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
|
||||
if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
|
||||
goto done_okay; /* Already suspended or dead */
|
||||
|
||||
if (uhci->rh_state > UHCI_RH_SUSPENDED) {
|
||||
@ -803,11 +811,15 @@ static int uhci_pci_suspend(struct usb_hcd *hcd)
|
||||
/* All PCI host controllers are required to disable IRQ generation
|
||||
* at the source, so we must turn off PIRQ.
|
||||
*/
|
||||
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
|
||||
mb();
|
||||
hcd->poll_rh = 0;
|
||||
pci_write_config_word(pdev, USBLEGSUP, 0);
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
||||
/* FIXME: Enable non-PME# remote wakeup? */
|
||||
/* Enable platform-specific non-PME# wakeup */
|
||||
if (do_wakeup) {
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL)
|
||||
pci_write_config_byte(pdev, USBRES_INTEL,
|
||||
USBPORT1EN | USBPORT2EN);
|
||||
}
|
||||
|
||||
done_okay:
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
@ -826,7 +838,6 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||
* even if the controller was dead.
|
||||
*/
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
mb();
|
||||
|
||||
spin_lock_irq(&uhci->lock);
|
||||
|
||||
@ -834,8 +845,6 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||
if (hibernated)
|
||||
uhci_hc_died(uhci);
|
||||
|
||||
/* FIXME: Disable non-PME# remote wakeup? */
|
||||
|
||||
/* The firmware or a boot kernel may have changed the controller
|
||||
* settings during a system wakeup. Check it and reconfigure
|
||||
* to avoid problems.
|
||||
@ -845,22 +854,20 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||
/* If the controller was dead before, it's back alive now */
|
||||
configure_hc(uhci);
|
||||
|
||||
if (uhci->rh_state == UHCI_RH_RESET) {
|
||||
|
||||
/* The controller had to be reset */
|
||||
/* Tell the core if the controller had to be reset */
|
||||
if (uhci->rh_state == UHCI_RH_RESET)
|
||||
usb_root_hub_lost_power(hcd->self.root_hub);
|
||||
suspend_rh(uhci, UHCI_RH_SUSPENDED);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&uhci->lock);
|
||||
|
||||
/* If interrupts don't work and remote wakeup is enabled then
|
||||
* the suspended root hub needs to be polled.
|
||||
*/
|
||||
if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) {
|
||||
hcd->poll_rh = 1;
|
||||
usb_hcd_poll_rh_status(hcd);
|
||||
}
|
||||
if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup)
|
||||
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
||||
/* Does the root hub have a port wakeup pending? */
|
||||
usb_hcd_poll_rh_status(hcd);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -67,12 +67,17 @@
|
||||
#define USBPORTSC_RES3 0x4000 /* reserved, write zeroes */
|
||||
#define USBPORTSC_RES4 0x8000 /* reserved, write zeroes */
|
||||
|
||||
/* Legacy support register */
|
||||
/* PCI legacy support register */
|
||||
#define USBLEGSUP 0xc0
|
||||
#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
|
||||
#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
|
||||
#define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
|
||||
|
||||
/* PCI Intel-specific resume-enable register */
|
||||
#define USBRES_INTEL 0xc4
|
||||
#define USBPORT1EN 0x01
|
||||
#define USBPORT2EN 0x02
|
||||
|
||||
#define UHCI_PTR_BITS cpu_to_le32(0x000F)
|
||||
#define UHCI_PTR_TERM cpu_to_le32(0x0001)
|
||||
#define UHCI_PTR_QH cpu_to_le32(0x0002)
|
||||
|
@ -190,7 +190,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
||||
spin_lock_irqsave(&uhci->lock, flags);
|
||||
|
||||
uhci_scan_schedule(uhci);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
|
||||
if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
|
||||
goto done;
|
||||
uhci_check_ports(uhci);
|
||||
|
||||
@ -200,7 +200,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
||||
case UHCI_RH_SUSPENDING:
|
||||
case UHCI_RH_SUSPENDED:
|
||||
/* if port change, ask to be resumed */
|
||||
if (status)
|
||||
if (status || uhci->resuming_ports)
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
break;
|
||||
|
||||
@ -246,7 +246,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 wPortChange, wPortStatus;
|
||||
unsigned long flags;
|
||||
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
|
||||
if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
spin_lock_irqsave(&uhci->lock, flags);
|
||||
|
@ -565,7 +565,7 @@ static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
qh->unlink_frame = uhci->frame_number;
|
||||
|
||||
/* Force an interrupt so we know when the QH is fully unlinked */
|
||||
if (list_empty(&uhci->skel_unlink_qh->node))
|
||||
if (list_empty(&uhci->skel_unlink_qh->node) || uhci->is_stopped)
|
||||
uhci_set_next_interrupt(uhci);
|
||||
|
||||
/* Move the QH from its old list to the end of the unlinking list */
|
||||
@ -1667,7 +1667,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
qh->advance_jiffies = jiffies;
|
||||
goto done;
|
||||
}
|
||||
ret = 0;
|
||||
ret = uhci->is_stopped;
|
||||
}
|
||||
|
||||
/* The queue hasn't advanced; check for timeout */
|
||||
|
@ -68,7 +68,7 @@ static int whc_start(struct usb_hcd *usb_hcd)
|
||||
whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN);
|
||||
|
||||
usb_hcd->uses_new_polling = 1;
|
||||
usb_hcd->poll_rh = 1;
|
||||
set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags);
|
||||
usb_hcd->state = HC_STATE_RUNNING;
|
||||
|
||||
out:
|
||||
|
@ -475,7 +475,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
|
||||
|| (prev_end & (WHCI_PAGE_SIZE-1))
|
||||
|| (dma_addr & (WHCI_PAGE_SIZE-1))
|
||||
|| std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) {
|
||||
if (std->len % qset->max_packet != 0)
|
||||
if (std && std->len % qset->max_packet != 0)
|
||||
return -EINVAL;
|
||||
std = qset_new_std(whc, qset, urb, mem_flags);
|
||||
if (std == NULL) {
|
||||
|
@ -391,49 +391,6 @@ struct xhci_ring *xhci_stream_id_to_ring(
|
||||
return ep->stream_info->stream_rings[stream_id];
|
||||
}
|
||||
|
||||
struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
unsigned int stream_id)
|
||||
{
|
||||
struct xhci_virt_ep *ep;
|
||||
|
||||
ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
/* Common case: no streams */
|
||||
if (!(ep->ep_state & EP_HAS_STREAMS))
|
||||
return ep->ring;
|
||||
|
||||
if (stream_id == 0) {
|
||||
xhci_warn(xhci,
|
||||
"WARN: Slot ID %u, ep index %u has streams, "
|
||||
"but URB has no stream ID.\n",
|
||||
slot_id, ep_index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (stream_id < ep->stream_info->num_streams)
|
||||
return ep->stream_info->stream_rings[stream_id];
|
||||
|
||||
xhci_warn(xhci,
|
||||
"WARN: Slot ID %u, ep index %u has "
|
||||
"stream IDs 1 to %u allocated, "
|
||||
"but stream ID %u is requested.\n",
|
||||
slot_id, ep_index,
|
||||
ep->stream_info->num_streams - 1,
|
||||
stream_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the right ring for the given URB.
|
||||
* If the endpoint supports streams, boundary check the URB's stream ID.
|
||||
* If the endpoint doesn't support streams, return the singular endpoint ring.
|
||||
*/
|
||||
struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
struct urb *urb)
|
||||
{
|
||||
return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
|
||||
xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
|
||||
static int xhci_test_radix_tree(struct xhci_hcd *xhci,
|
||||
unsigned int num_streams,
|
||||
@ -1112,8 +1069,18 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
|
||||
|
||||
/* Set up the endpoint ring */
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
xhci_ring_alloc(xhci, 1, true, mem_flags);
|
||||
/*
|
||||
* Isochronous endpoint ring needs bigger size because one isoc URB
|
||||
* carries multiple packets and it will insert multiple tds to the
|
||||
* ring.
|
||||
* This should be replaced with dynamic ring resizing in the future.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc))
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
xhci_ring_alloc(xhci, 8, true, mem_flags);
|
||||
else
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
xhci_ring_alloc(xhci, 1, true, mem_flags);
|
||||
if (!virt_dev->eps[ep_index].new_ring) {
|
||||
/* Attempt to use the ring cache */
|
||||
if (virt_dev->num_rings_cached == 0)
|
||||
@ -1124,6 +1091,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
|
||||
virt_dev->num_rings_cached--;
|
||||
xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring);
|
||||
}
|
||||
virt_dev->eps[ep_index].skip = false;
|
||||
ep_ring = virt_dev->eps[ep_index].new_ring;
|
||||
ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state;
|
||||
|
||||
@ -1389,6 +1357,22 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
|
||||
return command;
|
||||
}
|
||||
|
||||
void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv)
|
||||
{
|
||||
int last;
|
||||
|
||||
if (!urb_priv)
|
||||
return;
|
||||
|
||||
last = urb_priv->length - 1;
|
||||
if (last >= 0) {
|
||||
int i;
|
||||
for (i = 0; i <= last; i++)
|
||||
kfree(urb_priv->td[i]);
|
||||
}
|
||||
kfree(urb_priv);
|
||||
}
|
||||
|
||||
void xhci_free_command(struct xhci_hcd *xhci,
|
||||
struct xhci_command *command)
|
||||
{
|
||||
@ -1588,7 +1572,7 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags)
|
||||
unsigned int num_tests;
|
||||
int i, ret;
|
||||
|
||||
num_tests = sizeof(simple_test_vector) / sizeof(simple_test_vector[0]);
|
||||
num_tests = ARRAY_SIZE(simple_test_vector);
|
||||
for (i = 0; i < num_tests; i++) {
|
||||
ret = xhci_test_trb_in_td(xhci,
|
||||
xhci->event_ring->first_seg,
|
||||
@ -1601,7 +1585,7 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags)
|
||||
return ret;
|
||||
}
|
||||
|
||||
num_tests = sizeof(complex_test_vector) / sizeof(complex_test_vector[0]);
|
||||
num_tests = ARRAY_SIZE(complex_test_vector);
|
||||
for (i = 0; i < num_tests; i++) {
|
||||
ret = xhci_test_trb_in_td(xhci,
|
||||
complex_test_vector[i].input_seg,
|
||||
@ -1617,6 +1601,29 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
|
||||
{
|
||||
u64 temp;
|
||||
dma_addr_t deq;
|
||||
|
||||
deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
|
||||
xhci->event_ring->dequeue);
|
||||
if (deq == 0 && !in_interrupt())
|
||||
xhci_warn(xhci, "WARN something wrong with SW event ring "
|
||||
"dequeue ptr.\n");
|
||||
/* Update HC event ring dequeue pointer */
|
||||
temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
|
||||
temp &= ERST_PTR_MASK;
|
||||
/* Don't clear the EHB bit (which is RW1C) because
|
||||
* there might be more events to service.
|
||||
*/
|
||||
temp &= ~ERST_EHB;
|
||||
xhci_dbg(xhci, "// Write event ring dequeue pointer, "
|
||||
"preserving EHB bit\n");
|
||||
xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
|
||||
&xhci->ir_set->erst_dequeue);
|
||||
}
|
||||
|
||||
|
||||
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
{
|
||||
|
@ -53,6 +53,7 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
int retval;
|
||||
u32 temp;
|
||||
|
||||
hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2;
|
||||
|
||||
@ -93,6 +94,14 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
|
||||
return retval;
|
||||
xhci_dbg(xhci, "Reset complete\n");
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
|
||||
if (HCC_64BIT_ADDR(temp)) {
|
||||
xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
|
||||
dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64));
|
||||
} else {
|
||||
dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
|
||||
}
|
||||
|
||||
xhci_dbg(xhci, "Calling HCD init\n");
|
||||
/* Initialize HCD and host controller data structures. */
|
||||
retval = xhci_init(hcd);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,7 @@
|
||||
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/module.h>
|
||||
@ -171,22 +172,84 @@ int xhci_reset(struct xhci_hcd *xhci)
|
||||
return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/* Set up MSI-X table for entry 0 (may claim other entries later) */
|
||||
static int xhci_setup_msix(struct xhci_hcd *xhci)
|
||||
/*
|
||||
* Free IRQs
|
||||
* free all IRQs request
|
||||
*/
|
||||
static void xhci_free_irq(struct xhci_hcd *xhci)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
|
||||
xhci->msix_count = 0;
|
||||
/* XXX: did I do this right? ixgbe does kcalloc for more than one */
|
||||
xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL);
|
||||
/* return if using legacy interrupt */
|
||||
if (xhci_to_hcd(xhci)->irq >= 0)
|
||||
return;
|
||||
|
||||
if (xhci->msix_entries) {
|
||||
for (i = 0; i < xhci->msix_count; i++)
|
||||
if (xhci->msix_entries[i].vector)
|
||||
free_irq(xhci->msix_entries[i].vector,
|
||||
xhci_to_hcd(xhci));
|
||||
} else if (pdev->irq >= 0)
|
||||
free_irq(pdev->irq, xhci_to_hcd(xhci));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up MSI
|
||||
*/
|
||||
static int xhci_setup_msi(struct xhci_hcd *xhci)
|
||||
{
|
||||
int ret;
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
|
||||
ret = pci_enable_msi(pdev);
|
||||
if (ret) {
|
||||
xhci_err(xhci, "failed to allocate MSI entry\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq,
|
||||
0, "xhci_hcd", xhci_to_hcd(xhci));
|
||||
if (ret) {
|
||||
xhci_err(xhci, "disable MSI interrupt\n");
|
||||
pci_disable_msi(pdev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up MSI-X
|
||||
*/
|
||||
static int xhci_setup_msix(struct xhci_hcd *xhci)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
|
||||
/*
|
||||
* calculate number of msi-x vectors supported.
|
||||
* - HCS_MAX_INTRS: the max number of interrupts the host can handle,
|
||||
* with max number of interrupters based on the xhci HCSPARAMS1.
|
||||
* - num_online_cpus: maximum msi-x vectors per CPUs core.
|
||||
* Add additional 1 vector to ensure always available interrupt.
|
||||
*/
|
||||
xhci->msix_count = min(num_online_cpus() + 1,
|
||||
HCS_MAX_INTRS(xhci->hcs_params1));
|
||||
|
||||
xhci->msix_entries =
|
||||
kmalloc((sizeof(struct msix_entry))*xhci->msix_count,
|
||||
GFP_KERNEL);
|
||||
if (!xhci->msix_entries) {
|
||||
xhci_err(xhci, "Failed to allocate MSI-X entries\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
xhci->msix_entries[0].entry = 0;
|
||||
|
||||
for (i = 0; i < xhci->msix_count; i++) {
|
||||
xhci->msix_entries[i].entry = i;
|
||||
xhci->msix_entries[i].vector = 0;
|
||||
}
|
||||
|
||||
ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count);
|
||||
if (ret) {
|
||||
@ -194,20 +257,19 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
|
||||
goto free_entries;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass the xhci pointer value as the request_irq "cookie".
|
||||
* If more irqs are added, this will need to be unique for each one.
|
||||
*/
|
||||
ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0,
|
||||
"xHCI", xhci_to_hcd(xhci));
|
||||
if (ret) {
|
||||
xhci_err(xhci, "Failed to allocate MSI-X interrupt\n");
|
||||
goto disable_msix;
|
||||
for (i = 0; i < xhci->msix_count; i++) {
|
||||
ret = request_irq(xhci->msix_entries[i].vector,
|
||||
(irq_handler_t)xhci_msi_irq,
|
||||
0, "xhci_hcd", xhci_to_hcd(xhci));
|
||||
if (ret)
|
||||
goto disable_msix;
|
||||
}
|
||||
xhci_dbg(xhci, "Finished setting up MSI-X\n");
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
|
||||
disable_msix:
|
||||
xhci_err(xhci, "disable MSI-X interrupt\n");
|
||||
xhci_free_irq(xhci);
|
||||
pci_disable_msix(pdev);
|
||||
free_entries:
|
||||
kfree(xhci->msix_entries);
|
||||
@ -215,21 +277,23 @@ free_entries:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* XXX: code duplication; can xhci_setup_msix call this? */
|
||||
/* Free any IRQs and disable MSI-X */
|
||||
static void xhci_cleanup_msix(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
if (!xhci->msix_entries)
|
||||
return;
|
||||
|
||||
free_irq(xhci->msix_entries[0].vector, xhci);
|
||||
pci_disable_msix(pdev);
|
||||
kfree(xhci->msix_entries);
|
||||
xhci->msix_entries = NULL;
|
||||
xhci_dbg(xhci, "Finished cleaning up MSI-X\n");
|
||||
xhci_free_irq(xhci);
|
||||
|
||||
if (xhci->msix_entries) {
|
||||
pci_disable_msix(pdev);
|
||||
kfree(xhci->msix_entries);
|
||||
xhci->msix_entries = NULL;
|
||||
} else {
|
||||
pci_disable_msi(pdev);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize memory for HCD and xHC (one-time init).
|
||||
@ -257,100 +321,8 @@ int xhci_init(struct usb_hcd *hcd)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called in interrupt context when there might be work
|
||||
* queued on the event ring
|
||||
*
|
||||
* xhci->lock must be held by caller.
|
||||
*/
|
||||
static void xhci_work(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
u64 temp_64;
|
||||
|
||||
/*
|
||||
* Clear the op reg interrupt status first,
|
||||
* so we can receive interrupts from other MSI-X interrupters.
|
||||
* Write 1 to clear the interrupt status.
|
||||
*/
|
||||
temp = xhci_readl(xhci, &xhci->op_regs->status);
|
||||
temp |= STS_EINT;
|
||||
xhci_writel(xhci, temp, &xhci->op_regs->status);
|
||||
/* FIXME when MSI-X is supported and there are multiple vectors */
|
||||
/* Clear the MSI-X event interrupt status */
|
||||
|
||||
/* Acknowledge the interrupt */
|
||||
temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
|
||||
temp |= 0x3;
|
||||
xhci_writel(xhci, temp, &xhci->ir_set->irq_pending);
|
||||
/* Flush posted writes */
|
||||
xhci_readl(xhci, &xhci->ir_set->irq_pending);
|
||||
|
||||
if (xhci->xhc_state & XHCI_STATE_DYING)
|
||||
xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
|
||||
"Shouldn't IRQs be disabled?\n");
|
||||
else
|
||||
/* FIXME this should be a delayed service routine
|
||||
* that clears the EHB.
|
||||
*/
|
||||
xhci_handle_event(xhci);
|
||||
|
||||
/* Clear the event handler busy flag (RW1C); the event ring should be empty. */
|
||||
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
|
||||
xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue);
|
||||
/* Flush posted writes -- FIXME is this necessary? */
|
||||
xhci_readl(xhci, &xhci->ir_set->irq_pending);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* xHCI spec says we can get an interrupt, and if the HC has an error condition,
|
||||
* we might get bad data out of the event ring. Section 4.10.2.7 has a list of
|
||||
* indicators of an event TRB error, but we check the status *first* to be safe.
|
||||
*/
|
||||
irqreturn_t xhci_irq(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
u32 temp, temp2;
|
||||
union xhci_trb *trb;
|
||||
|
||||
spin_lock(&xhci->lock);
|
||||
trb = xhci->event_ring->dequeue;
|
||||
/* Check if the xHC generated the interrupt, or the irq is shared */
|
||||
temp = xhci_readl(xhci, &xhci->op_regs->status);
|
||||
temp2 = xhci_readl(xhci, &xhci->ir_set->irq_pending);
|
||||
if (temp == 0xffffffff && temp2 == 0xffffffff)
|
||||
goto hw_died;
|
||||
|
||||
if (!(temp & STS_EINT) && !ER_IRQ_PENDING(temp2)) {
|
||||
spin_unlock(&xhci->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
xhci_dbg(xhci, "op reg status = %08x\n", temp);
|
||||
xhci_dbg(xhci, "ir set irq_pending = %08x\n", temp2);
|
||||
xhci_dbg(xhci, "Event ring dequeue ptr:\n");
|
||||
xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n",
|
||||
(unsigned long long)xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb),
|
||||
lower_32_bits(trb->link.segment_ptr),
|
||||
upper_32_bits(trb->link.segment_ptr),
|
||||
(unsigned int) trb->link.intr_target,
|
||||
(unsigned int) trb->link.control);
|
||||
|
||||
if (temp & STS_FATAL) {
|
||||
xhci_warn(xhci, "WARNING: Host System Error\n");
|
||||
xhci_halt(xhci);
|
||||
hw_died:
|
||||
xhci_to_hcd(xhci)->state = HC_STATE_HALT;
|
||||
spin_unlock(&xhci->lock);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
xhci_work(xhci);
|
||||
spin_unlock(&xhci->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
|
||||
void xhci_event_ring_work(unsigned long arg)
|
||||
@ -423,21 +395,36 @@ int xhci_run(struct usb_hcd *hcd)
|
||||
{
|
||||
u32 temp;
|
||||
u64 temp_64;
|
||||
u32 ret;
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
void (*doorbell)(struct xhci_hcd *) = NULL;
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
hcd->poll_rh = 0;
|
||||
|
||||
xhci_dbg(xhci, "xhci_run\n");
|
||||
#if 0 /* FIXME: MSI not setup yet */
|
||||
/* Do this at the very last minute */
|
||||
ret = xhci_setup_msix(xhci);
|
||||
if (!ret)
|
||||
return ret;
|
||||
/* unregister the legacy interrupt */
|
||||
if (hcd->irq)
|
||||
free_irq(hcd->irq, hcd);
|
||||
hcd->irq = -1;
|
||||
|
||||
ret = xhci_setup_msix(xhci);
|
||||
if (ret)
|
||||
/* fall back to msi*/
|
||||
ret = xhci_setup_msi(xhci);
|
||||
|
||||
if (ret) {
|
||||
/* fall back to legacy interrupt*/
|
||||
ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
|
||||
hcd->irq_descr, hcd);
|
||||
if (ret) {
|
||||
xhci_err(xhci, "request interrupt %d failed\n",
|
||||
pdev->irq);
|
||||
return ret;
|
||||
}
|
||||
hcd->irq = pdev->irq;
|
||||
}
|
||||
|
||||
return -ENOSYS;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
|
||||
init_timer(&xhci->event_ring_timer);
|
||||
xhci->event_ring_timer.data = (unsigned long) xhci;
|
||||
@ -495,7 +482,6 @@ int xhci_run(struct usb_hcd *hcd)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
xhci_dbg(xhci, "// @%p = 0x%x\n", &xhci->op_regs->command, temp);
|
||||
if (doorbell)
|
||||
(*doorbell)(xhci);
|
||||
if (xhci->quirks & XHCI_NEC_HOST)
|
||||
@ -522,11 +508,9 @@ void xhci_stop(struct usb_hcd *hcd)
|
||||
spin_lock_irq(&xhci->lock);
|
||||
xhci_halt(xhci);
|
||||
xhci_reset(xhci);
|
||||
xhci_cleanup_msix(xhci);
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
|
||||
#if 0 /* No MSI yet */
|
||||
xhci_cleanup_msix(xhci);
|
||||
#endif
|
||||
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
|
||||
/* Tell the event ring poll function not to reschedule */
|
||||
xhci->zombie = 1;
|
||||
@ -560,11 +544,8 @@ void xhci_shutdown(struct usb_hcd *hcd)
|
||||
|
||||
spin_lock_irq(&xhci->lock);
|
||||
xhci_halt(xhci);
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
|
||||
#if 0
|
||||
xhci_cleanup_msix(xhci);
|
||||
#endif
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
|
||||
xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n",
|
||||
xhci_readl(xhci, &xhci->op_regs->status));
|
||||
@ -720,7 +701,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
unsigned int slot_id, ep_index;
|
||||
|
||||
struct urb_priv *urb_priv;
|
||||
int size, i;
|
||||
|
||||
if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0)
|
||||
return -EINVAL;
|
||||
@ -734,12 +716,36 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
if (!in_interrupt())
|
||||
xhci_dbg(xhci, "urb submitted during PCI suspend\n");
|
||||
ret = -ESHUTDOWN;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(&urb->ep->desc))
|
||||
size = urb->number_of_packets;
|
||||
else
|
||||
size = 1;
|
||||
|
||||
urb_priv = kzalloc(sizeof(struct urb_priv) +
|
||||
size * sizeof(struct xhci_td *), mem_flags);
|
||||
if (!urb_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags);
|
||||
if (!urb_priv->td[i]) {
|
||||
urb_priv->length = i;
|
||||
xhci_urb_free_priv(xhci, urb_priv);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
urb_priv->length = size;
|
||||
urb_priv->td_cnt = 0;
|
||||
urb->hcpriv = urb_priv;
|
||||
|
||||
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
|
||||
/* Check to see if the max packet size for the default control
|
||||
* endpoint changed during FS device enumeration
|
||||
@ -788,11 +794,18 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
|
||||
slot_id, ep_index);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
if (xhci->xhc_state & XHCI_STATE_DYING)
|
||||
goto dying;
|
||||
ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb,
|
||||
slot_id, ep_index);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
}
|
||||
exit:
|
||||
return ret;
|
||||
dying:
|
||||
xhci_urb_free_priv(xhci, urb_priv);
|
||||
urb->hcpriv = NULL;
|
||||
xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
|
||||
"non-responsive xHCI host.\n",
|
||||
urb->ep->desc.bEndpointAddress, urb);
|
||||
@ -800,6 +813,47 @@ dying:
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
/* Get the right ring for the given URB.
|
||||
* If the endpoint supports streams, boundary check the URB's stream ID.
|
||||
* If the endpoint doesn't support streams, return the singular endpoint ring.
|
||||
*/
|
||||
static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
struct urb *urb)
|
||||
{
|
||||
unsigned int slot_id;
|
||||
unsigned int ep_index;
|
||||
unsigned int stream_id;
|
||||
struct xhci_virt_ep *ep;
|
||||
|
||||
slot_id = urb->dev->slot_id;
|
||||
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
|
||||
stream_id = urb->stream_id;
|
||||
ep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
/* Common case: no streams */
|
||||
if (!(ep->ep_state & EP_HAS_STREAMS))
|
||||
return ep->ring;
|
||||
|
||||
if (stream_id == 0) {
|
||||
xhci_warn(xhci,
|
||||
"WARN: Slot ID %u, ep index %u has streams, "
|
||||
"but URB has no stream ID.\n",
|
||||
slot_id, ep_index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (stream_id < ep->stream_info->num_streams)
|
||||
return ep->stream_info->stream_rings[stream_id];
|
||||
|
||||
xhci_warn(xhci,
|
||||
"WARN: Slot ID %u, ep index %u has "
|
||||
"stream IDs 1 to %u allocated, "
|
||||
"but stream ID %u is requested.\n",
|
||||
slot_id, ep_index,
|
||||
ep->stream_info->num_streams - 1,
|
||||
stream_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the URB's TD from the endpoint ring. This may cause the HC to stop
|
||||
* USB transfers, potentially stopping in the middle of a TRB buffer. The HC
|
||||
@ -834,9 +888,10 @@ dying:
|
||||
int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int ret, i;
|
||||
u32 temp;
|
||||
struct xhci_hcd *xhci;
|
||||
struct urb_priv *urb_priv;
|
||||
struct xhci_td *td;
|
||||
unsigned int ep_index;
|
||||
struct xhci_ring *ep_ring;
|
||||
@ -851,12 +906,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
temp = xhci_readl(xhci, &xhci->op_regs->status);
|
||||
if (temp == 0xffffffff) {
|
||||
xhci_dbg(xhci, "HW died, freeing TD.\n");
|
||||
td = (struct xhci_td *) urb->hcpriv;
|
||||
urb_priv = urb->hcpriv;
|
||||
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN);
|
||||
kfree(td);
|
||||
xhci_urb_free_priv(xhci, urb_priv);
|
||||
return ret;
|
||||
}
|
||||
if (xhci->xhc_state & XHCI_STATE_DYING) {
|
||||
@ -884,9 +939,14 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
|
||||
xhci_dbg(xhci, "Endpoint ring:\n");
|
||||
xhci_debug_ring(xhci, ep_ring);
|
||||
td = (struct xhci_td *) urb->hcpriv;
|
||||
|
||||
list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
|
||||
urb_priv = urb->hcpriv;
|
||||
|
||||
for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
|
||||
td = urb_priv->td[i];
|
||||
list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
|
||||
}
|
||||
|
||||
/* Queue a stop endpoint command, but only if this is
|
||||
* the first cancellation to be handled.
|
||||
*/
|
||||
|
@ -720,6 +720,14 @@ struct xhci_virt_ep {
|
||||
struct timer_list stop_cmd_timer;
|
||||
int stop_cmds_pending;
|
||||
struct xhci_hcd *xhci;
|
||||
/*
|
||||
* Sometimes the xHC can not process isochronous endpoint ring quickly
|
||||
* enough, and it will miss some isoc tds on the ring and generate
|
||||
* a Missed Service Error Event.
|
||||
* Set skip flag when receive a Missed Service Error Event and
|
||||
* process the missed tds on the endpoint ring.
|
||||
*/
|
||||
bool skip;
|
||||
};
|
||||
|
||||
struct xhci_virt_device {
|
||||
@ -911,6 +919,9 @@ struct xhci_event_cmd {
|
||||
/* Control transfer TRB specific fields */
|
||||
#define TRB_DIR_IN (1<<16)
|
||||
|
||||
/* Isochronous TRB specific fields */
|
||||
#define TRB_SIA (1<<31)
|
||||
|
||||
struct xhci_generic_trb {
|
||||
u32 field[4];
|
||||
};
|
||||
@ -1082,6 +1093,12 @@ struct xhci_scratchpad {
|
||||
dma_addr_t *sp_dma_buffers;
|
||||
};
|
||||
|
||||
struct urb_priv {
|
||||
int length;
|
||||
int td_cnt;
|
||||
struct xhci_td *td[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* Each segment table entry is 4*32bits long. 1K seems like an ok size:
|
||||
* (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
|
||||
@ -1130,7 +1147,7 @@ struct xhci_hcd {
|
||||
int page_size;
|
||||
/* Valid values are 12 to 20, inclusive */
|
||||
int page_shift;
|
||||
/* only one MSI vector for now, but might need more later */
|
||||
/* msi-x vectors */
|
||||
int msix_count;
|
||||
struct msix_entry *msix_entries;
|
||||
/* data structures */
|
||||
@ -1327,11 +1344,6 @@ void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_ring *xhci_dma_to_transfer_ring(
|
||||
struct xhci_virt_ep *ep,
|
||||
u64 address);
|
||||
struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
struct urb *urb);
|
||||
struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
unsigned int stream_id);
|
||||
struct xhci_ring *xhci_stream_id_to_ring(
|
||||
struct xhci_virt_device *dev,
|
||||
unsigned int ep_index,
|
||||
@ -1339,6 +1351,7 @@ struct xhci_ring *xhci_stream_id_to_ring(
|
||||
struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
|
||||
bool allocate_in_ctx, bool allocate_completion,
|
||||
gfp_t mem_flags);
|
||||
void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv);
|
||||
void xhci_free_command(struct xhci_hcd *xhci,
|
||||
struct xhci_command *command);
|
||||
|
||||
@ -1358,6 +1371,7 @@ void xhci_stop(struct usb_hcd *hcd);
|
||||
void xhci_shutdown(struct usb_hcd *hcd);
|
||||
int xhci_get_frame(struct usb_hcd *hcd);
|
||||
irqreturn_t xhci_irq(struct usb_hcd *hcd);
|
||||
irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd);
|
||||
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
|
||||
void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev);
|
||||
int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
@ -1386,8 +1400,6 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
|
||||
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
|
||||
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
|
||||
void *xhci_setup_one_noop(struct xhci_hcd *xhci);
|
||||
void xhci_handle_event(struct xhci_hcd *xhci);
|
||||
void xhci_set_hc_event_deq(struct xhci_hcd *xhci);
|
||||
int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id);
|
||||
int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||
u32 slot_id);
|
||||
@ -1401,6 +1413,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
|
||||
int slot_id, unsigned int ep_index);
|
||||
int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
|
||||
int slot_id, unsigned int ep_index);
|
||||
int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
struct urb *urb, int slot_id, unsigned int ep_index);
|
||||
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||
u32 slot_id, bool command_must_succeed);
|
||||
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user