forked from Minki/linux
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (142 commits) USB: fix race in autosuspend reschedule atmel_usba_udc: Keep track of the device status USB: Nikon D40X unusual_devs entry USB: serial core should respect driver requirements USB: documentation for USB power management USB: skip autosuspended devices during system resume USB: mutual exclusion for EHCI init and port resets USB: allow usbstorage to have LUNS greater than 2Tb USB: Adding support for SHARP WS011SH to ipaq.c USB: add atmel_usba_udc driver USB: ohci SSB bus glue USB: ehci build fixes on au1xxx, ppc-soc USB: add runtime frame_no quirk for big-endian OHCI USB: funsoft: Fix termios USB: visor: termios bits USB: unusual_devs entry for Nikon DSC D2Xs USB: re-remove <linux/usb_sl811.h> USB: move <linux/usb_gadget.h> to <linux/usb/gadget.h> USB: Export URB statistics for powertop USB: serial gadget: Disable endpoints on unload ...
This commit is contained in:
commit
117494a1b6
92
Documentation/usb/authorization.txt
Normal file
92
Documentation/usb/authorization.txt
Normal file
@ -0,0 +1,92 @@
|
||||
|
||||
Authorizing (or not) your USB devices to connect to the system
|
||||
|
||||
(C) 2007 Inaky Perez-Gonzalez <inaky@linux.intel.com> Intel Corporation
|
||||
|
||||
This feature allows you to control if a USB device can be used (or
|
||||
not) in a system. This feature will allow you to implement a lock-down
|
||||
of USB devices, fully controlled by user space.
|
||||
|
||||
As of now, when a USB device is connected it is configured and
|
||||
it's interfaces inmediately made available to the users. With this
|
||||
modification, only if root authorizes the device to be configured will
|
||||
then it be possible to use it.
|
||||
|
||||
Usage:
|
||||
|
||||
Authorize a device to connect:
|
||||
|
||||
$ echo 1 > /sys/usb/devices/DEVICE/authorized
|
||||
|
||||
Deauthorize a device:
|
||||
|
||||
$ echo 0 > /sys/usb/devices/DEVICE/authorized
|
||||
|
||||
Set new devices connected to hostX to be deauthorized by default (ie:
|
||||
lock down):
|
||||
|
||||
$ echo 0 > /sys/bus/devices/usbX/authorized_default
|
||||
|
||||
Remove the lock down:
|
||||
|
||||
$ echo 1 > /sys/bus/devices/usbX/authorized_default
|
||||
|
||||
By default, Wired USB devices are authorized by default to
|
||||
connect. Wireless USB hosts deauthorize by default all new connected
|
||||
devices (this is so because we need to do an authentication phase
|
||||
before authorizing).
|
||||
|
||||
|
||||
Example system lockdown (lame)
|
||||
-----------------------
|
||||
|
||||
Imagine you want to implement a lockdown so only devices of type XYZ
|
||||
can be connected (for example, it is a kiosk machine with a visible
|
||||
USB port):
|
||||
|
||||
boot up
|
||||
rc.local ->
|
||||
|
||||
for host in /sys/bus/devices/usb*
|
||||
do
|
||||
echo 0 > $host/authorized_default
|
||||
done
|
||||
|
||||
Hookup an script to udev, for new USB devices
|
||||
|
||||
if device_is_my_type $DEV
|
||||
then
|
||||
echo 1 > $device_path/authorized
|
||||
done
|
||||
|
||||
|
||||
Now, device_is_my_type() is where the juice for a lockdown is. Just
|
||||
checking if the class, type and protocol match something is the worse
|
||||
security verification you can make (or the best, for someone willing
|
||||
to break it). If you need something secure, use crypto and Certificate
|
||||
Authentication or stuff like that. Something simple for an storage key
|
||||
could be:
|
||||
|
||||
function device_is_my_type()
|
||||
{
|
||||
echo 1 > authorized # temporarily authorize it
|
||||
# FIXME: make sure none can mount it
|
||||
mount DEVICENODE /mntpoint
|
||||
sum=$(md5sum /mntpoint/.signature)
|
||||
if [ $sum = $(cat /etc/lockdown/keysum) ]
|
||||
then
|
||||
echo "We are good, connected"
|
||||
umount /mntpoint
|
||||
# Other stuff so others can use it
|
||||
else
|
||||
echo 0 > authorized
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
Of course, this is lame, you'd want to do a real certificate
|
||||
verification stuff with PKI, so you don't depend on a shared secret,
|
||||
etc, but you get the idea. Anybody with access to a device gadget kit
|
||||
can fake descriptors and device info. Don't trust that. You are
|
||||
welcome.
|
||||
|
517
Documentation/usb/power-management.txt
Normal file
517
Documentation/usb/power-management.txt
Normal file
@ -0,0 +1,517 @@
|
||||
Power Management for USB
|
||||
|
||||
Alan Stern <stern@rowland.harvard.edu>
|
||||
|
||||
October 5, 2007
|
||||
|
||||
|
||||
|
||||
What is Power Management?
|
||||
-------------------------
|
||||
|
||||
Power Management (PM) is the practice of saving energy by suspending
|
||||
parts of a computer system when they aren't being used. While a
|
||||
component is "suspended" it is in a nonfunctional low-power state; it
|
||||
might even be turned off completely. A suspended component can be
|
||||
"resumed" (returned to a functional full-power state) when the kernel
|
||||
needs to use it. (There also are forms of PM in which components are
|
||||
placed in a less functional but still usable state instead of being
|
||||
suspended; an example would be reducing the CPU's clock rate. This
|
||||
document will not discuss those other forms.)
|
||||
|
||||
When the parts being suspended include the CPU and most of the rest of
|
||||
the system, we speak of it as a "system suspend". When a particular
|
||||
device is turned off while the system as a whole remains running, we
|
||||
call it a "dynamic suspend" (also known as a "runtime suspend" or
|
||||
"selective suspend"). This document concentrates mostly on how
|
||||
dynamic PM is implemented in the USB subsystem, although system PM is
|
||||
covered to some extent (see Documentation/power/*.txt for more
|
||||
information about system PM).
|
||||
|
||||
Note: Dynamic PM support for USB is present only if the kernel was
|
||||
built with CONFIG_USB_SUSPEND enabled. System PM support is present
|
||||
only if the kernel was built with CONFIG_SUSPEND or CONFIG_HIBERNATION
|
||||
enabled.
|
||||
|
||||
|
||||
What is Remote Wakeup?
|
||||
----------------------
|
||||
|
||||
When a device has been suspended, it generally doesn't resume until
|
||||
the computer tells it to. Likewise, if the entire computer has been
|
||||
suspended, it generally doesn't resume until the user tells it to, say
|
||||
by pressing a power button or opening the cover.
|
||||
|
||||
However some devices have the capability of resuming by themselves, or
|
||||
asking the kernel to resume them, or even telling the entire computer
|
||||
to resume. This capability goes by several names such as "Wake On
|
||||
LAN"; we will refer to it generically as "remote wakeup". When a
|
||||
device is enabled for remote wakeup and it is suspended, it may resume
|
||||
itself (or send a request to be resumed) in response to some external
|
||||
event. Examples include a suspended keyboard resuming when a key is
|
||||
pressed, or a suspended USB hub resuming when a device is plugged in.
|
||||
|
||||
|
||||
When is a USB device idle?
|
||||
--------------------------
|
||||
|
||||
A device is idle whenever the kernel thinks it's not busy doing
|
||||
anything important and thus is a candidate for being suspended. The
|
||||
exact definition depends on the device's driver; drivers are allowed
|
||||
to declare that a device isn't idle even when there's no actual
|
||||
communication taking place. (For example, a hub isn't considered idle
|
||||
unless all the devices plugged into that hub are already suspended.)
|
||||
In addition, a device isn't considered idle so long as a program keeps
|
||||
its usbfs file open, whether or not any I/O is going on.
|
||||
|
||||
If a USB device has no driver, its usbfs file isn't open, and it isn't
|
||||
being accessed through sysfs, then it definitely is idle.
|
||||
|
||||
|
||||
Forms of dynamic PM
|
||||
-------------------
|
||||
|
||||
Dynamic suspends can occur in two ways: manual and automatic.
|
||||
"Manual" means that the user has told the kernel to suspend a device,
|
||||
whereas "automatic" means that the kernel has decided all by itself to
|
||||
suspend a device. Automatic suspend is called "autosuspend" for
|
||||
short. In general, a device won't be autosuspended unless it has been
|
||||
idle for some minimum period of time, the so-called idle-delay time.
|
||||
|
||||
Of course, nothing the kernel does on its own initiative should
|
||||
prevent the computer or its devices from working properly. If a
|
||||
device has been autosuspended and a program tries to use it, the
|
||||
kernel will automatically resume the device (autoresume). For the
|
||||
same reason, an autosuspended device will usually have remote wakeup
|
||||
enabled, if the device supports remote wakeup.
|
||||
|
||||
It is worth mentioning that many USB drivers don't support
|
||||
autosuspend. In fact, at the time of this writing (Linux 2.6.23) the
|
||||
only drivers which do support it are the hub driver, kaweth, asix,
|
||||
usblp, usblcd, and usb-skeleton (which doesn't count). If a
|
||||
non-supporting driver is bound to a device, the device won't be
|
||||
autosuspended. In effect, the kernel pretends the device is never
|
||||
idle.
|
||||
|
||||
We can categorize power management events in two broad classes:
|
||||
external and internal. External events are those triggered by some
|
||||
agent outside the USB stack: system suspend/resume (triggered by
|
||||
userspace), manual dynamic suspend/resume (also triggered by
|
||||
userspace), and remote wakeup (triggered by the device). Internal
|
||||
events are those triggered within the USB stack: autosuspend and
|
||||
autoresume.
|
||||
|
||||
|
||||
The user interface for dynamic PM
|
||||
---------------------------------
|
||||
|
||||
The user interface for controlling dynamic PM is located in the power/
|
||||
subdirectory of each USB device's sysfs directory, that is, in
|
||||
/sys/bus/usb/devices/.../power/ where "..." is the device's ID. The
|
||||
relevant attribute files are: wakeup, level, and autosuspend.
|
||||
|
||||
power/wakeup
|
||||
|
||||
This file is empty if the device does not support
|
||||
remote wakeup. Otherwise the file contains either the
|
||||
word "enabled" or the word "disabled", and you can
|
||||
write those words to the file. The setting determines
|
||||
whether or not remote wakeup will be enabled when the
|
||||
device is next suspended. (If the setting is changed
|
||||
while the device is suspended, the change won't take
|
||||
effect until the following suspend.)
|
||||
|
||||
power/level
|
||||
|
||||
This file contains one of three words: "on", "auto",
|
||||
or "suspend". You can write those words to the file
|
||||
to change the device's setting.
|
||||
|
||||
"on" means that the device should be resumed and
|
||||
autosuspend is not allowed. (Of course, system
|
||||
suspends are still allowed.)
|
||||
|
||||
"auto" is the normal state in which the kernel is
|
||||
allowed to autosuspend and autoresume the device.
|
||||
|
||||
"suspend" means that the device should remain
|
||||
suspended, and autoresume is not allowed. (But remote
|
||||
wakeup may still be allowed, since it is controlled
|
||||
separately by the power/wakeup attribute.)
|
||||
|
||||
power/autosuspend
|
||||
|
||||
This file contains an integer value, which is the
|
||||
number of seconds the device should remain idle before
|
||||
the kernel will autosuspend it (the idle-delay time).
|
||||
The default is 2. 0 means to autosuspend as soon as
|
||||
the device becomes idle, and -1 means never to
|
||||
autosuspend. You can write a number to the file to
|
||||
change the autosuspend idle-delay time.
|
||||
|
||||
Writing "-1" to power/autosuspend and writing "on" to power/level do
|
||||
essentially the same thing -- they both prevent the device from being
|
||||
autosuspended. Yes, this is a redundancy in the API.
|
||||
|
||||
(In 2.6.21 writing "0" to power/autosuspend would prevent the device
|
||||
from being autosuspended; the behavior was changed in 2.6.22. The
|
||||
power/autosuspend attribute did not exist prior to 2.6.21, and the
|
||||
power/level attribute did not exist prior to 2.6.22.)
|
||||
|
||||
|
||||
Changing the default idle-delay time
|
||||
------------------------------------
|
||||
|
||||
The default autosuspend idle-delay time is controlled by a module
|
||||
parameter in usbcore. You can specify the value when usbcore is
|
||||
loaded. For example, to set it to 5 seconds instead of 2 you would
|
||||
do:
|
||||
|
||||
modprobe usbcore autosuspend=5
|
||||
|
||||
Equivalently, you could add to /etc/modprobe.conf a line saying:
|
||||
|
||||
options usbcore autosuspend=5
|
||||
|
||||
Some distributions load the usbcore module very early during the boot
|
||||
process, by means of a program or script running from an initramfs
|
||||
image. To alter the parameter value you would have to rebuild that
|
||||
image.
|
||||
|
||||
If usbcore is compiled into the kernel rather than built as a loadable
|
||||
module, you can add
|
||||
|
||||
usbcore.autosuspend=5
|
||||
|
||||
to the kernel's boot command line.
|
||||
|
||||
Finally, the parameter value can be changed while the system is
|
||||
running. If you do:
|
||||
|
||||
echo 5 >/sys/module/usbcore/parameters/autosuspend
|
||||
|
||||
then each new USB device will have its autosuspend idle-delay
|
||||
initialized to 5. (The idle-delay values for already existing devices
|
||||
will not be affected.)
|
||||
|
||||
Setting the initial default idle-delay to -1 will prevent any
|
||||
autosuspend of any USB device. This is a simple alternative to
|
||||
disabling CONFIG_USB_SUSPEND and rebuilding the kernel, and it has the
|
||||
added benefit of allowing you to enable autosuspend for selected
|
||||
devices.
|
||||
|
||||
|
||||
Warnings
|
||||
--------
|
||||
|
||||
The USB specification states that all USB devices must support power
|
||||
management. Nevertheless, the sad fact is that many devices do not
|
||||
support it very well. You can suspend them all right, but when you
|
||||
try to resume them they disconnect themselves from the USB bus or
|
||||
they stop working entirely. This seems to be especially prevalent
|
||||
among printers and scanners, but plenty of other types of device have
|
||||
the same deficiency.
|
||||
|
||||
For this reason, by default the kernel disables autosuspend (the
|
||||
power/level attribute is initialized to "on") for all devices other
|
||||
than hubs. Hubs, at least, appear to be reasonably well-behaved in
|
||||
this regard.
|
||||
|
||||
(In 2.6.21 and 2.6.22 this wasn't the case. Autosuspend was enabled
|
||||
by default for almost all USB devices. A number of people experienced
|
||||
problems as a result.)
|
||||
|
||||
This means that non-hub devices won't be autosuspended unless the user
|
||||
or a program explicitly enables it. As of this writing there aren't
|
||||
any widespread programs which will do this; we hope that in the near
|
||||
future device managers such as HAL will take on this added
|
||||
responsibility. In the meantime you can always carry out the
|
||||
necessary operations by hand or add them to a udev script. You can
|
||||
also change the idle-delay time; 2 seconds is not the best choice for
|
||||
every device.
|
||||
|
||||
Sometimes it turns out that even when a device does work okay with
|
||||
autosuspend there are still problems. For example, there are
|
||||
experimental patches adding autosuspend support to the usbhid driver,
|
||||
which manages keyboards and mice, among other things. Tests with a
|
||||
number of keyboards showed that typing on a suspended keyboard, while
|
||||
causing the keyboard to do a remote wakeup all right, would
|
||||
nonetheless frequently result in lost keystrokes. Tests with mice
|
||||
showed that some of them would issue a remote-wakeup request in
|
||||
response to button presses but not to motion, and some in response to
|
||||
neither.
|
||||
|
||||
The kernel will not prevent you from enabling autosuspend on devices
|
||||
that can't handle it. It is even possible in theory to damage a
|
||||
device by suspending it at the wrong time -- for example, suspending a
|
||||
USB hard disk might cause it to spin down without parking the heads.
|
||||
(Highly unlikely, but possible.) Take care.
|
||||
|
||||
|
||||
The driver interface for Power Management
|
||||
-----------------------------------------
|
||||
|
||||
The requirements for a USB driver to support external power management
|
||||
are pretty modest; the driver need only define
|
||||
|
||||
.suspend
|
||||
.resume
|
||||
.reset_resume
|
||||
|
||||
methods in its usb_driver structure, and the reset_resume method is
|
||||
optional. The methods' jobs are quite simple:
|
||||
|
||||
The suspend method is called to warn the driver that the
|
||||
device is going to be suspended. If the driver returns a
|
||||
negative error code, the suspend will be aborted. Normally
|
||||
the driver will return 0, in which case it must cancel all
|
||||
outstanding URBs (usb_kill_urb()) and not submit any more.
|
||||
|
||||
The resume method is called to tell the driver that the
|
||||
device has been resumed and the driver can return to normal
|
||||
operation. URBs may once more be submitted.
|
||||
|
||||
The reset_resume method is called to tell the driver that
|
||||
the device has been resumed and it also has been reset.
|
||||
The driver should redo any necessary device initialization,
|
||||
since the device has probably lost most or all of its state
|
||||
(although the interfaces will be in the same altsettings as
|
||||
before the suspend).
|
||||
|
||||
The reset_resume method is used by the USB Persist facility (see
|
||||
Documentation/usb/persist.txt) and it can also be used under certain
|
||||
circumstances when CONFIG_USB_PERSIST is not enabled. Currently, if a
|
||||
device is reset during a resume and the driver does not have a
|
||||
reset_resume method, the driver won't receive any notification about
|
||||
the resume. Later kernels will call the driver's disconnect method;
|
||||
2.6.23 doesn't do this.
|
||||
|
||||
USB drivers are bound to interfaces, so their suspend and resume
|
||||
methods get called when the interfaces are suspended or resumed. In
|
||||
principle one might want to suspend some interfaces on a device (i.e.,
|
||||
force the drivers for those interface to stop all activity) without
|
||||
suspending the other interfaces. The USB core doesn't allow this; all
|
||||
interfaces are suspended when the device itself is suspended and all
|
||||
interfaces are resumed when the device is resumed. It isn't possible
|
||||
to suspend or resume some but not all of a device's interfaces. The
|
||||
closest you can come is to unbind the interfaces' drivers.
|
||||
|
||||
|
||||
The driver interface for autosuspend and autoresume
|
||||
---------------------------------------------------
|
||||
|
||||
To support autosuspend and autoresume, a driver should implement all
|
||||
three of the methods listed above. In addition, a driver indicates
|
||||
that it supports autosuspend by setting the .supports_autosuspend flag
|
||||
in its usb_driver structure. It is then responsible for informing the
|
||||
USB core whenever one of its interfaces becomes busy or idle. The
|
||||
driver does so by calling these three functions:
|
||||
|
||||
int usb_autopm_get_interface(struct usb_interface *intf);
|
||||
void usb_autopm_put_interface(struct usb_interface *intf);
|
||||
int usb_autopm_set_interface(struct usb_interface *intf);
|
||||
|
||||
The functions work by maintaining a counter in the usb_interface
|
||||
structure. When intf->pm_usage_count is > 0 then the interface is
|
||||
deemed to be busy, and the kernel will not autosuspend the interface's
|
||||
device. When intf->pm_usage_count is <= 0 then the interface is
|
||||
considered to be idle, and the kernel may autosuspend the device.
|
||||
|
||||
(There is a similar pm_usage_count field in struct usb_device,
|
||||
associated with the device itself rather than any of its interfaces.
|
||||
This field is used only by the USB core.)
|
||||
|
||||
The driver owns intf->pm_usage_count; it can modify the value however
|
||||
and whenever it likes. A nice aspect of the usb_autopm_* routines is
|
||||
that the changes they make are protected by the usb_device structure's
|
||||
PM mutex (udev->pm_mutex); however drivers may change pm_usage_count
|
||||
without holding the mutex.
|
||||
|
||||
usb_autopm_get_interface() increments pm_usage_count and
|
||||
attempts an autoresume if the new value is > 0 and the
|
||||
device is suspended.
|
||||
|
||||
usb_autopm_put_interface() decrements pm_usage_count and
|
||||
attempts an autosuspend if the new value is <= 0 and the
|
||||
device isn't suspended.
|
||||
|
||||
usb_autopm_set_interface() leaves pm_usage_count alone.
|
||||
It attempts an autoresume if the value is > 0 and the device
|
||||
is suspended, and it attempts an autosuspend if the value is
|
||||
<= 0 and the device isn't suspended.
|
||||
|
||||
There also are a couple of utility routines drivers can use:
|
||||
|
||||
usb_autopm_enable() sets pm_usage_cnt to 1 and then calls
|
||||
usb_autopm_set_interface(), which will attempt an autoresume.
|
||||
|
||||
usb_autopm_disable() sets pm_usage_cnt to 0 and then calls
|
||||
usb_autopm_set_interface(), which will attempt an autosuspend.
|
||||
|
||||
The conventional usage pattern is that a driver calls
|
||||
usb_autopm_get_interface() in its open routine and
|
||||
usb_autopm_put_interface() in its close or release routine. But
|
||||
other patterns are possible.
|
||||
|
||||
The autosuspend attempts mentioned above will often fail for one
|
||||
reason or another. For example, the power/level attribute might be
|
||||
set to "on", or another interface in the same device might not be
|
||||
idle. This is perfectly normal. If the reason for failure was that
|
||||
the device hasn't been idle for long enough, a delayed workqueue
|
||||
routine is automatically set up to carry out the operation when the
|
||||
autosuspend idle-delay has expired.
|
||||
|
||||
Autoresume attempts also can fail. This will happen if power/level is
|
||||
set to "suspend" or if the device doesn't manage to resume properly.
|
||||
Unlike autosuspend, there's no delay for an autoresume.
|
||||
|
||||
|
||||
Other parts of the driver interface
|
||||
-----------------------------------
|
||||
|
||||
Sometimes a driver needs to make sure that remote wakeup is enabled
|
||||
during autosuspend. For example, there's not much point
|
||||
autosuspending a keyboard if the user can't cause the keyboard to do a
|
||||
remote wakeup by typing on it. If the driver sets
|
||||
intf->needs_remote_wakeup to 1, the kernel won't autosuspend the
|
||||
device if remote wakeup isn't available or has been disabled through
|
||||
the power/wakeup attribute. (If the device is already autosuspended,
|
||||
though, setting this flag won't cause the kernel to autoresume it.
|
||||
Normally a driver would set this flag in its probe method, at which
|
||||
time the device is guaranteed not to be autosuspended.)
|
||||
|
||||
The usb_autopm_* routines have to run in a sleepable process context;
|
||||
they must not be called from an interrupt handler or while holding a
|
||||
spinlock. In fact, the entire autosuspend mechanism is not well geared
|
||||
toward interrupt-driven operation. However there is one thing a
|
||||
driver can do in an interrupt handler:
|
||||
|
||||
usb_mark_last_busy(struct usb_device *udev);
|
||||
|
||||
This sets udev->last_busy to the current time. udev->last_busy is the
|
||||
field used for idle-delay calculations; updating it will cause any
|
||||
pending autosuspend to be moved back. The usb_autopm_* routines will
|
||||
also set the last_busy field to the current time.
|
||||
|
||||
Calling urb_mark_last_busy() from within an URB completion handler is
|
||||
subject to races: The kernel may have just finished deciding the
|
||||
device has been idle for long enough but not yet gotten around to
|
||||
calling the driver's suspend method. The driver would have to be
|
||||
responsible for synchronizing its suspend method with its URB
|
||||
completion handler and causing the autosuspend to fail with -EBUSY if
|
||||
an URB had completed too recently.
|
||||
|
||||
External suspend calls should never be allowed to fail in this way,
|
||||
only autosuspend calls. The driver can tell them apart by checking
|
||||
udev->auto_pm; this flag will be set to 1 for internal PM events
|
||||
(autosuspend or autoresume) and 0 for external PM events.
|
||||
|
||||
Many of the ingredients in the autosuspend framework are oriented
|
||||
towards interfaces: The usb_interface structure contains the
|
||||
pm_usage_cnt field, and the usb_autopm_* routines take an interface
|
||||
pointer as their argument. But somewhat confusingly, a few of the
|
||||
pieces (usb_mark_last_busy() and udev->auto_pm) use the usb_device
|
||||
structure instead. Drivers need to keep this straight; they can call
|
||||
interface_to_usbdev() to find the device structure for a given
|
||||
interface.
|
||||
|
||||
|
||||
Locking requirements
|
||||
--------------------
|
||||
|
||||
All three suspend/resume methods are always called while holding the
|
||||
usb_device's PM mutex. For external events -- but not necessarily for
|
||||
autosuspend or autoresume -- the device semaphore (udev->dev.sem) will
|
||||
also be held. This implies that external suspend/resume events are
|
||||
mutually exclusive with calls to probe, disconnect, pre_reset, and
|
||||
post_reset; the USB core guarantees that this is true of internal
|
||||
suspend/resume events as well.
|
||||
|
||||
If a driver wants to block all suspend/resume calls during some
|
||||
critical section, it can simply acquire udev->pm_mutex.
|
||||
Alternatively, if the critical section might call some of the
|
||||
usb_autopm_* routines, the driver can avoid deadlock by doing:
|
||||
|
||||
down(&udev->dev.sem);
|
||||
rc = usb_autopm_get_interface(intf);
|
||||
|
||||
and at the end of the critical section:
|
||||
|
||||
if (!rc)
|
||||
usb_autopm_put_interface(intf);
|
||||
up(&udev->dev.sem);
|
||||
|
||||
Holding the device semaphore will block all external PM calls, and the
|
||||
usb_autopm_get_interface() will prevent any internal PM calls, even if
|
||||
it fails. (Exercise: Why?)
|
||||
|
||||
The rules for locking order are:
|
||||
|
||||
Never acquire any device semaphore while holding any PM mutex.
|
||||
|
||||
Never acquire udev->pm_mutex while holding the PM mutex for
|
||||
a device that isn't a descendant of udev.
|
||||
|
||||
In other words, PM mutexes should only be acquired going up the device
|
||||
tree, and they should be acquired only after locking all the device
|
||||
semaphores you need to hold. These rules don't matter to drivers very
|
||||
much; they usually affect just the USB core.
|
||||
|
||||
Still, drivers do need to be careful. For example, many drivers use a
|
||||
private mutex to synchronize their normal I/O activities with their
|
||||
disconnect method. Now if the driver supports autosuspend then it
|
||||
must call usb_autopm_put_interface() from somewhere -- maybe from its
|
||||
close method. It should make the call while holding the private mutex,
|
||||
since a driver shouldn't call any of the usb_autopm_* functions for an
|
||||
interface from which it has been unbound.
|
||||
|
||||
But the usb_autpm_* routines always acquire the device's PM mutex, and
|
||||
consequently the locking order has to be: private mutex first, PM
|
||||
mutex second. Since the suspend method is always called with the PM
|
||||
mutex held, it mustn't try to acquire the private mutex. It has to
|
||||
synchronize with the driver's I/O activities in some other way.
|
||||
|
||||
|
||||
Interaction between dynamic PM and system PM
|
||||
--------------------------------------------
|
||||
|
||||
Dynamic power management and system power management can interact in
|
||||
a couple of ways.
|
||||
|
||||
Firstly, a device may already be manually suspended or autosuspended
|
||||
when a system suspend occurs. Since system suspends are supposed to
|
||||
be as transparent as possible, the device should remain suspended
|
||||
following the system resume. The 2.6.23 kernel obeys this principle
|
||||
for manually suspended devices but not for autosuspended devices; they
|
||||
do get resumed when the system wakes up. (Presumably they will be
|
||||
autosuspended again after their idle-delay time expires.) In later
|
||||
kernels this behavior will be fixed.
|
||||
|
||||
(There is an exception. If a device would undergo a reset-resume
|
||||
instead of a normal resume, and the device is enabled for remote
|
||||
wakeup, then the reset-resume takes place even if the device was
|
||||
already suspended when the system suspend began. The justification is
|
||||
that a reset-resume is a kind of remote-wakeup event. Or to put it
|
||||
another way, a device which needs a reset won't be able to generate
|
||||
normal remote-wakeup signals, so it ought to be resumed immediately.)
|
||||
|
||||
Secondly, a dynamic power-management event may occur as a system
|
||||
suspend is underway. The window for this is short, since system
|
||||
suspends don't take long (a few seconds usually), but it can happen.
|
||||
For example, a suspended device may send a remote-wakeup signal while
|
||||
the system is suspending. The remote wakeup may succeed, which would
|
||||
cause the system suspend to abort. If the remote wakeup doesn't
|
||||
succeed, it may still remain active and thus cause the system to
|
||||
resume as soon as the system suspend is complete. Or the remote
|
||||
wakeup may fail and get lost. Which outcome occurs depends on timing
|
||||
and on the hardware and firmware design.
|
||||
|
||||
More interestingly, a device might undergo a manual resume or
|
||||
autoresume during system suspend. With current kernels this shouldn't
|
||||
happen, because manual resumes must be initiated by userspace and
|
||||
autoresumes happen in response to I/O requests, but all user processes
|
||||
and I/O should be quiescent during a system suspend -- thanks to the
|
||||
freezer. However there are plans to do away with the freezer, which
|
||||
would mean these things would become possible. If and when this comes
|
||||
about, the USB core will carefully arrange matters so that either type
|
||||
of resume will block until the entire system has resumed.
|
@ -428,6 +428,17 @@ Options supported:
|
||||
See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
|
||||
information on this driver.
|
||||
|
||||
Winchiphead CH341 Driver
|
||||
|
||||
This driver is for the Winchiphead CH341 USB-RS232 Converter. This chip
|
||||
also implements an IEEE 1284 parallel port, I2C and SPI, but that is not
|
||||
supported by the driver. The protocol was analyzed from the behaviour
|
||||
of the Windows driver, no datasheet is available at present.
|
||||
The manufacturer's website: http://www.winchiphead.com/.
|
||||
For any questions or problems with this driver, please contact
|
||||
frank@kingswood-consulting.co.uk.
|
||||
|
||||
|
||||
Generic Serial driver
|
||||
|
||||
If your device is not one of the above listed devices, compatible with
|
||||
|
@ -34,9 +34,12 @@ if usbmon is built into the kernel.
|
||||
Verify that bus sockets are present.
|
||||
|
||||
# ls /sys/kernel/debug/usbmon
|
||||
1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u
|
||||
0s 0t 0u 1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u
|
||||
#
|
||||
|
||||
Now you can choose to either use the sockets numbered '0' (to capture packets on
|
||||
all buses), and skip to step #3, or find the bus used by your device with step #2.
|
||||
|
||||
2. Find which bus connects to the desired device
|
||||
|
||||
Run "cat /proc/bus/usb/devices", and find the T-line which corresponds to
|
||||
@ -56,6 +59,10 @@ Bus=03 means it's bus 3.
|
||||
|
||||
# cat /sys/kernel/debug/usbmon/3u > /tmp/1.mon.out
|
||||
|
||||
to listen on a single bus, otherwise, to listen on all buses, type:
|
||||
|
||||
# cat /sys/kernel/debug/usbmon/0u > /tmp/1.mon.out
|
||||
|
||||
This process will be reading until killed. Naturally, the output can be
|
||||
redirected to a desirable location. This is preferred, because it is going
|
||||
to be quite long.
|
||||
|
@ -677,6 +677,13 @@ P: Haavard Skinnemoen
|
||||
M: hskinnemoen@atmel.com
|
||||
S: Supported
|
||||
|
||||
ATMEL USBA UDC DRIVER
|
||||
P: Haavard Skinnemoen
|
||||
M: hskinnemoen@atmel.com
|
||||
L: kernel@avr32linux.org
|
||||
W: http://avr32linux.org/twiki/bin/view/Main/AtmelUsbDeviceDriver
|
||||
S: Supported
|
||||
|
||||
ATMEL WIRELESS DRIVER
|
||||
P: Simon Kelley
|
||||
M: simon@thekelleys.org.uk
|
||||
|
@ -40,7 +40,7 @@
|
||||
#include <linux/pata_platform.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb_sl811.h>
|
||||
#include <linux/usb/sl811.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/bfin5xx_spi.h>
|
||||
#include <asm/reboot.h>
|
||||
|
@ -40,7 +40,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/bfin5xx_spi.h>
|
||||
#include <linux/usb_sl811.h>
|
||||
#include <linux/usb/sl811.h>
|
||||
|
||||
#include <linux/spi/ad7877.h>
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
||||
#include <linux/pata_platform.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb_sl811.h>
|
||||
#include <linux/usb/sl811.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/bfin5xx_spi.h>
|
||||
#include <asm/reboot.h>
|
||||
|
@ -28,27 +28,7 @@ obj-$(CONFIG_USB_MICROTEK) += image/
|
||||
|
||||
obj-$(CONFIG_USB_SERIAL) += serial/
|
||||
|
||||
obj-$(CONFIG_USB_ADUTUX) += misc/
|
||||
obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
|
||||
obj-$(CONFIG_USB_AUERSWALD) += misc/
|
||||
obj-$(CONFIG_USB_BERRY_CHARGE) += misc/
|
||||
obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/
|
||||
obj-$(CONFIG_USB_CYTHERM) += misc/
|
||||
obj-$(CONFIG_USB_EMI26) += misc/
|
||||
obj-$(CONFIG_USB_EMI62) += misc/
|
||||
obj-$(CONFIG_USB_FTDI_ELAN) += misc/
|
||||
obj-$(CONFIG_USB_IDMOUSE) += misc/
|
||||
obj-$(CONFIG_USB_LCD) += misc/
|
||||
obj-$(CONFIG_USB_LD) += misc/
|
||||
obj-$(CONFIG_USB_LED) += misc/
|
||||
obj-$(CONFIG_USB_LEGOTOWER) += misc/
|
||||
obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
|
||||
obj-$(CONFIG_USB_RIO500) += misc/
|
||||
obj-$(CONFIG_USB_SISUSBVGA) += misc/
|
||||
obj-$(CONFIG_USB_TEST) += misc/
|
||||
obj-$(CONFIG_USB_TRANCEVIBRATOR)+= misc/
|
||||
obj-$(CONFIG_USB_USS720) += misc/
|
||||
obj-$(CONFIG_USB_IOWARRIOR) += misc/
|
||||
obj-$(CONFIG_USB) += misc/
|
||||
|
||||
obj-$(CONFIG_USB_ATM) += atm/
|
||||
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
|
||||
|
@ -482,7 +482,9 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
|
||||
int rbuflen = ((rsize - 1) / stride + 1) * CMD_PACKET_SIZE;
|
||||
|
||||
if (wbuflen > PAGE_SIZE || rbuflen > PAGE_SIZE) {
|
||||
dbg("too big transfer requested");
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "requested transfer size too large (%d, %d)\n",
|
||||
wbuflen, rbuflen);
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
@ -493,8 +495,9 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
|
||||
init_completion(&instance->rcv_done);
|
||||
ret = usb_submit_urb(instance->rcv_urb, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
dbg("submitting read urb for cm %#x failed", cm);
|
||||
ret = ret;
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "submit of read urb for cm %#x failed (%d)\n",
|
||||
cm, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -510,27 +513,29 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
|
||||
init_completion(&instance->snd_done);
|
||||
ret = usb_submit_urb(instance->snd_urb, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
dbg("submitting write urb for cm %#x failed", cm);
|
||||
ret = ret;
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "submit of write urb for cm %#x failed (%d)\n",
|
||||
cm, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL);
|
||||
if (ret < 0) {
|
||||
dbg("sending cm %#x failed", cm);
|
||||
ret = ret;
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "send of cm %#x failed (%d)\n", cm, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen);
|
||||
if (ret < 0) {
|
||||
dbg("receiving cm %#x failed", cm);
|
||||
ret = ret;
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "receive of cm %#x failed (%d)\n", cm, ret);
|
||||
goto fail;
|
||||
}
|
||||
if (actlen % CMD_PACKET_SIZE || !actlen) {
|
||||
dbg("response is not a positive multiple of %d: %#x",
|
||||
CMD_PACKET_SIZE, actlen);
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "invalid response length to cm %#x: %d\n",
|
||||
cm, actlen);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
@ -538,12 +543,16 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
|
||||
/* check the return status and copy the data to the output buffer, if needed */
|
||||
for (offb = offd = 0; offd < rsize && offb < actlen; offb += CMD_PACKET_SIZE) {
|
||||
if (rbuf[offb] != cm) {
|
||||
dbg("wrong cm %#x in response", rbuf[offb]);
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "wrong cm %#x in response to cm %#x\n",
|
||||
rbuf[offb], cm);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
if (rbuf[offb + 1] != CM_STATUS_SUCCESS) {
|
||||
dbg("response failed: %#x", rbuf[offb + 1]);
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "response to cm %#x failed: %#x\n",
|
||||
cm, rbuf[offb + 1]);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
@ -582,14 +591,18 @@ static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_requ
|
||||
for (offb = 0; offb < len; ) {
|
||||
int l = le32_to_cpu(buf[offb++]);
|
||||
if (l > stride || l > (len - offb) / 2) {
|
||||
dbg("wrong data length %#x in response", l);
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "invalid data length from cm %#x: %d\n",
|
||||
cm, l);
|
||||
ret = -EIO;
|
||||
goto cleanup;
|
||||
}
|
||||
while (l--) {
|
||||
offd = le32_to_cpu(buf[offb++]);
|
||||
if (offd >= size) {
|
||||
dbg("wrong index %#x in response", offd);
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "wrong index #%x in response to cm #%x\n",
|
||||
offd, cm);
|
||||
ret = -EIO;
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -251,7 +251,6 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
|
||||
{
|
||||
unsigned char *buffer;
|
||||
struct usbatm_data *usbatm = instance->usbatm;
|
||||
struct usb_interface *intf;
|
||||
struct usb_device *usb_dev = usbatm->usb_dev;
|
||||
int actual_length;
|
||||
int ret = 0;
|
||||
@ -265,7 +264,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
|
||||
if (!usb_ifnum_to_if(usb_dev, 2)) {
|
||||
ret = -ENODEV;
|
||||
usb_dbg(usbatm, "%s: interface not found!\n", __func__);
|
||||
goto out_free;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,7 @@
|
||||
* v0.12 - add hpoj.sourceforge.net ioctls (David Paschal)
|
||||
* v0.13 - alloc space for statusbuf (<status> not on stack);
|
||||
* use usb_buffer_alloc() for read buf & write buf;
|
||||
* none - Maintained in Linux kernel after v0.13
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -69,7 +70,6 @@
|
||||
#define USBLP_DEVICE_ID_SIZE 1024
|
||||
|
||||
/* ioctls: */
|
||||
#define LPGETSTATUS 0x060b /* same as in drivers/char/lp.c */
|
||||
#define IOCNR_GET_DEVICE_ID 1
|
||||
#define IOCNR_GET_PROTOCOLS 2
|
||||
#define IOCNR_SET_PROTOCOL 3
|
||||
@ -115,7 +115,7 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H
|
||||
#define USBLP_MINORS 16
|
||||
#define USBLP_MINOR_BASE 0
|
||||
|
||||
#define USBLP_WRITE_TIMEOUT (5000) /* 5 seconds */
|
||||
#define USBLP_CTL_TIMEOUT 5000 /* 5 seconds */
|
||||
|
||||
#define USBLP_FIRST_PROTOCOL 1
|
||||
#define USBLP_LAST_PROTOCOL 3
|
||||
@ -159,10 +159,12 @@ struct usblp {
|
||||
int wstatus; /* bytes written or error */
|
||||
int rstatus; /* bytes ready or error */
|
||||
unsigned int quirks; /* quirks flags */
|
||||
unsigned int flags; /* mode flags */
|
||||
unsigned char used; /* True if open */
|
||||
unsigned char present; /* True if not disconnected */
|
||||
unsigned char bidir; /* interface is bidirectional */
|
||||
unsigned char sleeping; /* interface is suspended */
|
||||
unsigned char no_paper; /* Paper Out happened */
|
||||
unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */
|
||||
/* first 2 bytes are (big-endian) length */
|
||||
};
|
||||
@ -259,7 +261,7 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i
|
||||
|
||||
retval = usb_control_msg(usblp->dev,
|
||||
dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
|
||||
request, type | dir | recip, value, index, buf, len, USBLP_WRITE_TIMEOUT);
|
||||
request, type | dir | recip, value, index, buf, len, USBLP_CTL_TIMEOUT);
|
||||
dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d",
|
||||
request, !!dir, recip, value, index, len, retval);
|
||||
return retval < 0 ? retval : 0;
|
||||
@ -325,13 +327,11 @@ static void usblp_bulk_write(struct urb *urb)
|
||||
usblp->wstatus = status;
|
||||
else
|
||||
usblp->wstatus = urb->actual_length;
|
||||
usblp->no_paper = 0;
|
||||
usblp->wcomplete = 1;
|
||||
wake_up(&usblp->wwait);
|
||||
spin_unlock(&usblp->lock);
|
||||
|
||||
/* XXX Use usb_setup_bulk_urb when available. Talk to Marcel. */
|
||||
kfree(urb->transfer_buffer);
|
||||
urb->transfer_buffer = NULL; /* Not refcounted, so to be safe... */
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
|
||||
@ -346,16 +346,17 @@ static int usblp_check_status(struct usblp *usblp, int err)
|
||||
unsigned char status, newerr = 0;
|
||||
int error;
|
||||
|
||||
error = usblp_read_status (usblp, usblp->statusbuf);
|
||||
if (error < 0) {
|
||||
mutex_lock(&usblp->mut);
|
||||
if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) {
|
||||
mutex_unlock(&usblp->mut);
|
||||
if (printk_ratelimit())
|
||||
printk(KERN_ERR
|
||||
"usblp%d: error %d reading printer status\n",
|
||||
usblp->minor, error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = *usblp->statusbuf;
|
||||
mutex_unlock(&usblp->mut);
|
||||
|
||||
if (~status & LP_PERRORP)
|
||||
newerr = 3;
|
||||
@ -411,18 +412,10 @@ static int usblp_open(struct inode *inode, struct file *file)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* TODO: need to implement LP_ABORTOPEN + O_NONBLOCK as in drivers/char/lp.c ???
|
||||
* This is #if 0-ed because we *don't* want to fail an open
|
||||
* just because the printer is off-line.
|
||||
* We do not implement LP_ABORTOPEN/LPABORTOPEN for two reasons:
|
||||
* - We do not want persistent state which close(2) does not clear
|
||||
* - It is not used anyway, according to CUPS people
|
||||
*/
|
||||
#if 0
|
||||
if ((retval = usblp_check_status(usblp, 0))) {
|
||||
retval = retval > 1 ? -EIO : -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
#else
|
||||
retval = 0;
|
||||
#endif
|
||||
|
||||
retval = usb_autopm_get_interface(intf);
|
||||
if (retval < 0)
|
||||
@ -463,6 +456,8 @@ static int usblp_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct usblp *usblp = file->private_data;
|
||||
|
||||
usblp->flags &= ~LP_ABORT;
|
||||
|
||||
mutex_lock (&usblp_mutex);
|
||||
usblp->used = 0;
|
||||
if (usblp->present) {
|
||||
@ -485,8 +480,8 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait
|
||||
poll_wait(file, &usblp->rwait, wait);
|
||||
poll_wait(file, &usblp->wwait, wait);
|
||||
spin_lock_irqsave(&usblp->lock, flags);
|
||||
ret = ((!usblp->bidir || !usblp->rcomplete) ? 0 : POLLIN | POLLRDNORM)
|
||||
| (!usblp->wcomplete ? 0 : POLLOUT | POLLWRNORM);
|
||||
ret = ((usblp->bidir && usblp->rcomplete) ? POLLIN | POLLRDNORM : 0) |
|
||||
((usblp->no_paper || usblp->wcomplete) ? POLLOUT | POLLWRNORM : 0);
|
||||
spin_unlock_irqrestore(&usblp->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
@ -675,6 +670,13 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
|
||||
case LPABORT:
|
||||
if (arg)
|
||||
usblp->flags |= LP_ABORT;
|
||||
else
|
||||
usblp->flags &= ~LP_ABORT;
|
||||
break;
|
||||
|
||||
default:
|
||||
retval = -ENOTTY;
|
||||
}
|
||||
@ -684,10 +686,30 @@ done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct urb *usblp_new_writeurb(struct usblp *usblp, int transfer_length)
|
||||
{
|
||||
struct urb *urb;
|
||||
char *writebuf;
|
||||
|
||||
if ((writebuf = kmalloc(transfer_length, GFP_KERNEL)) == NULL)
|
||||
return NULL;
|
||||
if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) {
|
||||
kfree(writebuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
usb_fill_bulk_urb(urb, usblp->dev,
|
||||
usb_sndbulkpipe(usblp->dev,
|
||||
usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress),
|
||||
writebuf, transfer_length, usblp_bulk_write, usblp);
|
||||
urb->transfer_flags |= URB_FREE_BUFFER;
|
||||
|
||||
return urb;
|
||||
}
|
||||
|
||||
static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct usblp *usblp = file->private_data;
|
||||
char *writebuf;
|
||||
struct urb *writeurb;
|
||||
int rv;
|
||||
int transfer_length;
|
||||
@ -708,17 +730,11 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
|
||||
transfer_length = USBLP_BUF_SIZE;
|
||||
|
||||
rv = -ENOMEM;
|
||||
if ((writebuf = kmalloc(USBLP_BUF_SIZE, GFP_KERNEL)) == NULL)
|
||||
goto raise_buf;
|
||||
if ((writeurb = usb_alloc_urb(0, GFP_KERNEL)) == NULL)
|
||||
if ((writeurb = usblp_new_writeurb(usblp, transfer_length)) == NULL)
|
||||
goto raise_urb;
|
||||
usb_fill_bulk_urb(writeurb, usblp->dev,
|
||||
usb_sndbulkpipe(usblp->dev,
|
||||
usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress),
|
||||
writebuf, transfer_length, usblp_bulk_write, usblp);
|
||||
usb_anchor_urb(writeurb, &usblp->urbs);
|
||||
|
||||
if (copy_from_user(writebuf,
|
||||
if (copy_from_user(writeurb->transfer_buffer,
|
||||
buffer + writecount, transfer_length)) {
|
||||
rv = -EFAULT;
|
||||
goto raise_badaddr;
|
||||
@ -730,6 +746,7 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
|
||||
if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) {
|
||||
usblp->wstatus = 0;
|
||||
spin_lock_irq(&usblp->lock);
|
||||
usblp->no_paper = 0;
|
||||
usblp->wcomplete = 1;
|
||||
wake_up(&usblp->wwait);
|
||||
spin_unlock_irq(&usblp->lock);
|
||||
@ -747,12 +764,17 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
|
||||
/* Presume that it's going to complete well. */
|
||||
writecount += transfer_length;
|
||||
}
|
||||
if (rv == -ENOSPC) {
|
||||
spin_lock_irq(&usblp->lock);
|
||||
usblp->no_paper = 1; /* Mark for poll(2) */
|
||||
spin_unlock_irq(&usblp->lock);
|
||||
writecount += transfer_length;
|
||||
}
|
||||
/* Leave URB dangling, to be cleaned on close. */
|
||||
goto collect_error;
|
||||
}
|
||||
|
||||
if (usblp->wstatus < 0) {
|
||||
usblp_check_status(usblp, 0);
|
||||
rv = -EIO;
|
||||
goto collect_error;
|
||||
}
|
||||
@ -771,8 +793,6 @@ raise_badaddr:
|
||||
usb_unanchor_urb(writeurb);
|
||||
usb_free_urb(writeurb);
|
||||
raise_urb:
|
||||
kfree(writebuf);
|
||||
raise_buf:
|
||||
raise_wait:
|
||||
collect_error: /* Out of raise sequence */
|
||||
mutex_unlock(&usblp->wmut);
|
||||
@ -838,32 +858,36 @@ done:
|
||||
* when O_NONBLOCK is set. So, applications setting O_NONBLOCK must use
|
||||
* select(2) or poll(2) to wait for the buffer to drain before closing.
|
||||
* Alternatively, set blocking mode with fcntl and issue a zero-size write.
|
||||
*
|
||||
* Old v0.13 code had a non-functional timeout for wait_event(). Someone forgot
|
||||
* to check the return code for timeout expiration, so it had no effect.
|
||||
* Apparently, it was intended to check for error conditons, such as out
|
||||
* of paper. It is going to return when we settle things with CUPS. XXX
|
||||
*/
|
||||
static int usblp_wwait(struct usblp *usblp, int nonblock)
|
||||
{
|
||||
DECLARE_WAITQUEUE(waita, current);
|
||||
int rc;
|
||||
int err = 0;
|
||||
|
||||
add_wait_queue(&usblp->wwait, &waita);
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (mutex_lock_interruptible(&usblp->mut)) {
|
||||
rc = -EINTR;
|
||||
break;
|
||||
}
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if ((rc = usblp_wtest(usblp, nonblock)) < 0) {
|
||||
mutex_unlock(&usblp->mut);
|
||||
break;
|
||||
}
|
||||
rc = usblp_wtest(usblp, nonblock);
|
||||
mutex_unlock(&usblp->mut);
|
||||
if (rc == 0)
|
||||
if (rc <= 0)
|
||||
break;
|
||||
schedule();
|
||||
|
||||
if (usblp->flags & LP_ABORT) {
|
||||
if (schedule_timeout(msecs_to_jiffies(5000)) == 0) {
|
||||
err = usblp_check_status(usblp, err);
|
||||
if (err == 1) { /* Paper out */
|
||||
rc = -ENOSPC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&usblp->wwait, &waita);
|
||||
|
@ -507,18 +507,30 @@ void usb_destroy_configuration(struct usb_device *dev)
|
||||
}
|
||||
|
||||
|
||||
// hub-only!! ... and only in reset path, or usb_new_device()
|
||||
// (used by real hubs and virtual root hubs)
|
||||
/*
|
||||
* Get the USB config descriptors, cache and parse'em
|
||||
*
|
||||
* hub-only!! ... and only in reset path, or usb_new_device()
|
||||
* (used by real hubs and virtual root hubs)
|
||||
*
|
||||
* NOTE: if this is a WUSB device and is not authorized, we skip the
|
||||
* whole thing. A non-authorized USB device has no
|
||||
* configurations.
|
||||
*/
|
||||
int usb_get_configuration(struct usb_device *dev)
|
||||
{
|
||||
struct device *ddev = &dev->dev;
|
||||
int ncfg = dev->descriptor.bNumConfigurations;
|
||||
int result = -ENOMEM;
|
||||
int result = 0;
|
||||
unsigned int cfgno, length;
|
||||
unsigned char *buffer;
|
||||
unsigned char *bigbuffer;
|
||||
struct usb_config_descriptor *desc;
|
||||
|
||||
cfgno = 0;
|
||||
if (dev->authorized == 0) /* Not really an error */
|
||||
goto out_not_authorized;
|
||||
result = -ENOMEM;
|
||||
if (ncfg > USB_MAXCONFIG) {
|
||||
dev_warn(ddev, "too many configurations: %d, "
|
||||
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
|
||||
@ -545,14 +557,15 @@ int usb_get_configuration(struct usb_device *dev)
|
||||
goto err2;
|
||||
desc = (struct usb_config_descriptor *)buffer;
|
||||
|
||||
for (cfgno = 0; cfgno < ncfg; cfgno++) {
|
||||
result = 0;
|
||||
for (; cfgno < ncfg; cfgno++) {
|
||||
/* We grab just the first descriptor so we know how long
|
||||
* the whole configuration is */
|
||||
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
|
||||
buffer, USB_DT_CONFIG_SIZE);
|
||||
if (result < 0) {
|
||||
dev_err(ddev, "unable to read config index %d "
|
||||
"descriptor/%s\n", cfgno, "start");
|
||||
"descriptor/%s: %d\n", cfgno, "start", result);
|
||||
dev_err(ddev, "chopping to %d config(s)\n", cfgno);
|
||||
dev->descriptor.bNumConfigurations = cfgno;
|
||||
break;
|
||||
@ -599,6 +612,7 @@ int usb_get_configuration(struct usb_device *dev)
|
||||
|
||||
err:
|
||||
kfree(buffer);
|
||||
out_not_authorized:
|
||||
dev->descriptor.bNumConfigurations = cfgno;
|
||||
err2:
|
||||
if (result == -ENOMEM)
|
||||
|
@ -71,6 +71,7 @@ struct async {
|
||||
void __user *userbuffer;
|
||||
void __user *userurb;
|
||||
struct urb *urb;
|
||||
int status;
|
||||
u32 secid;
|
||||
};
|
||||
|
||||
@ -289,10 +290,8 @@ static void snoop_urb(struct urb *urb, void __user *userurb)
|
||||
if (!usbfs_snoop)
|
||||
return;
|
||||
|
||||
if (urb->pipe & USB_DIR_IN)
|
||||
dev_info(&urb->dev->dev, "direction=IN\n");
|
||||
else
|
||||
dev_info(&urb->dev->dev, "direction=OUT\n");
|
||||
dev_info(&urb->dev->dev, "direction=%s\n",
|
||||
usb_urb_dir_in(urb) ? "IN" : "OUT");
|
||||
dev_info(&urb->dev->dev, "userurb=%p\n", userurb);
|
||||
dev_info(&urb->dev->dev, "transfer_buffer_length=%d\n",
|
||||
urb->transfer_buffer_length);
|
||||
@ -312,9 +311,10 @@ static void async_completed(struct urb *urb)
|
||||
spin_lock(&ps->lock);
|
||||
list_move_tail(&as->asynclist, &ps->async_completed);
|
||||
spin_unlock(&ps->lock);
|
||||
as->status = urb->status;
|
||||
if (as->signr) {
|
||||
sinfo.si_signo = as->signr;
|
||||
sinfo.si_errno = as->urb->status;
|
||||
sinfo.si_errno = as->status;
|
||||
sinfo.si_code = SI_ASYNCIO;
|
||||
sinfo.si_addr = as->userurb;
|
||||
kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid,
|
||||
@ -910,6 +910,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
struct usb_ctrlrequest *dr = NULL;
|
||||
unsigned int u, totlen, isofrmlen;
|
||||
int ret, ifnum = -1;
|
||||
int is_in;
|
||||
|
||||
if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK|
|
||||
URB_NO_FSBR|URB_ZERO_PACKET))
|
||||
@ -924,16 +925,18 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
if ((ret = checkintf(ps, ifnum)))
|
||||
return ret;
|
||||
}
|
||||
if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0)
|
||||
ep = ps->dev->ep_in [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
|
||||
else
|
||||
ep = ps->dev->ep_out [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
|
||||
if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) {
|
||||
is_in = 1;
|
||||
ep = ps->dev->ep_in[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
|
||||
} else {
|
||||
is_in = 0;
|
||||
ep = ps->dev->ep_out[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
|
||||
}
|
||||
if (!ep)
|
||||
return -ENOENT;
|
||||
switch(uurb->type) {
|
||||
case USBDEVFS_URB_TYPE_CONTROL:
|
||||
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
|
||||
!= USB_ENDPOINT_XFER_CONTROL)
|
||||
if (!usb_endpoint_xfer_control(&ep->desc))
|
||||
return -EINVAL;
|
||||
/* min 8 byte setup packet, max 8 byte setup plus an arbitrary data stage */
|
||||
if (uurb->buffer_length < 8 || uurb->buffer_length > (8 + MAX_USBFS_BUFFER_SIZE))
|
||||
@ -952,23 +955,32 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
kfree(dr);
|
||||
return ret;
|
||||
}
|
||||
uurb->endpoint = (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK);
|
||||
uurb->number_of_packets = 0;
|
||||
uurb->buffer_length = le16_to_cpup(&dr->wLength);
|
||||
uurb->buffer += 8;
|
||||
if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) {
|
||||
if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
|
||||
is_in = 1;
|
||||
uurb->endpoint |= USB_DIR_IN;
|
||||
} else {
|
||||
is_in = 0;
|
||||
uurb->endpoint &= ~USB_DIR_IN;
|
||||
}
|
||||
if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
|
||||
uurb->buffer, uurb->buffer_length)) {
|
||||
kfree(dr);
|
||||
return -EFAULT;
|
||||
}
|
||||
snoop(&ps->dev->dev, "control urb: bRequest=%02x "
|
||||
"bRrequestType=%02x wValue=%04x "
|
||||
"wIndex=%04x wLength=%04x\n",
|
||||
dr->bRequest, dr->bRequestType, dr->wValue,
|
||||
dr->wIndex, dr->wLength);
|
||||
dr->bRequest, dr->bRequestType,
|
||||
__le16_to_cpup(&dr->wValue),
|
||||
__le16_to_cpup(&dr->wIndex),
|
||||
__le16_to_cpup(&dr->wLength));
|
||||
break;
|
||||
|
||||
case USBDEVFS_URB_TYPE_BULK:
|
||||
switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
switch (usb_endpoint_type(&ep->desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
return -EINVAL;
|
||||
@ -977,7 +989,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
uurb->number_of_packets = 0;
|
||||
if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
|
||||
if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
|
||||
uurb->buffer, uurb->buffer_length))
|
||||
return -EFAULT;
|
||||
snoop(&ps->dev->dev, "bulk urb\n");
|
||||
break;
|
||||
@ -986,8 +999,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
/* arbitrary limit */
|
||||
if (uurb->number_of_packets < 1 || uurb->number_of_packets > 128)
|
||||
return -EINVAL;
|
||||
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
|
||||
!= USB_ENDPOINT_XFER_ISOC)
|
||||
if (!usb_endpoint_xfer_isoc(&ep->desc))
|
||||
return -EINVAL;
|
||||
isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets;
|
||||
if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
|
||||
@ -1014,12 +1026,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
|
||||
case USBDEVFS_URB_TYPE_INTERRUPT:
|
||||
uurb->number_of_packets = 0;
|
||||
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
|
||||
!= USB_ENDPOINT_XFER_INT)
|
||||
if (!usb_endpoint_xfer_int(&ep->desc))
|
||||
return -EINVAL;
|
||||
if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
|
||||
if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
|
||||
uurb->buffer, uurb->buffer_length))
|
||||
return -EFAULT;
|
||||
snoop(&ps->dev->dev, "interrupt urb\n");
|
||||
break;
|
||||
@ -1039,8 +1051,11 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
return -ENOMEM;
|
||||
}
|
||||
as->urb->dev = ps->dev;
|
||||
as->urb->pipe = (uurb->type << 30) | __create_pipe(ps->dev, uurb->endpoint & 0xf) | (uurb->endpoint & USB_DIR_IN);
|
||||
as->urb->transfer_flags = uurb->flags;
|
||||
as->urb->pipe = (uurb->type << 30) |
|
||||
__create_pipe(ps->dev, uurb->endpoint & 0xf) |
|
||||
(uurb->endpoint & USB_DIR_IN);
|
||||
as->urb->transfer_flags = uurb->flags |
|
||||
(is_in ? URB_DIR_IN : URB_DIR_OUT);
|
||||
as->urb->transfer_buffer_length = uurb->buffer_length;
|
||||
as->urb->setup_packet = (unsigned char*)dr;
|
||||
as->urb->start_frame = uurb->start_frame;
|
||||
@ -1070,13 +1085,13 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
as->uid = current->uid;
|
||||
as->euid = current->euid;
|
||||
security_task_getsecid(current, &as->secid);
|
||||
if (!(uurb->endpoint & USB_DIR_IN)) {
|
||||
if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) {
|
||||
if (!is_in) {
|
||||
if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
|
||||
as->urb->transfer_buffer_length)) {
|
||||
free_async(as);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
snoop(&as->urb->dev->dev, "submit urb\n");
|
||||
snoop_urb(as->urb, as->userurb);
|
||||
async_newpending(as);
|
||||
if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) {
|
||||
@ -1119,14 +1134,14 @@ static int processcompl(struct async *as, void __user * __user *arg)
|
||||
if (as->userbuffer)
|
||||
if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length))
|
||||
return -EFAULT;
|
||||
if (put_user(urb->status, &userurb->status))
|
||||
if (put_user(as->status, &userurb->status))
|
||||
return -EFAULT;
|
||||
if (put_user(urb->actual_length, &userurb->actual_length))
|
||||
return -EFAULT;
|
||||
if (put_user(urb->error_count, &userurb->error_count))
|
||||
return -EFAULT;
|
||||
|
||||
if (usb_pipeisoc(urb->pipe)) {
|
||||
if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
|
||||
for (i = 0; i < urb->number_of_packets; i++) {
|
||||
if (put_user(urb->iso_frame_desc[i].actual_length,
|
||||
&userurb->iso_frame_desc[i].actual_length))
|
||||
@ -1233,14 +1248,14 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
|
||||
if (as->userbuffer)
|
||||
if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length))
|
||||
return -EFAULT;
|
||||
if (put_user(urb->status, &userurb->status))
|
||||
if (put_user(as->status, &userurb->status))
|
||||
return -EFAULT;
|
||||
if (put_user(urb->actual_length, &userurb->actual_length))
|
||||
return -EFAULT;
|
||||
if (put_user(urb->error_count, &userurb->error_count))
|
||||
return -EFAULT;
|
||||
|
||||
if (usb_pipeisoc(urb->pipe)) {
|
||||
if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
|
||||
for (i = 0; i < urb->number_of_packets; i++) {
|
||||
if (put_user(urb->iso_frame_desc[i].actual_length,
|
||||
&userurb->iso_frame_desc[i].actual_length))
|
||||
|
@ -202,6 +202,11 @@ static int usb_probe_interface(struct device *dev)
|
||||
intf = to_usb_interface(dev);
|
||||
udev = interface_to_usbdev(intf);
|
||||
|
||||
if (udev->authorized == 0) {
|
||||
dev_err(&intf->dev, "Device is not authorized for usage\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
id = usb_match_id(intf, driver->id_table);
|
||||
if (!id)
|
||||
id = usb_match_dynamic_id(intf, driver);
|
||||
@ -945,11 +950,11 @@ done:
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
/* Internal routine to check whether we may autosuspend a device. */
|
||||
static int autosuspend_check(struct usb_device *udev)
|
||||
static int autosuspend_check(struct usb_device *udev, int reschedule)
|
||||
{
|
||||
int i;
|
||||
struct usb_interface *intf;
|
||||
unsigned long suspend_time;
|
||||
unsigned long suspend_time, j;
|
||||
|
||||
/* For autosuspend, fail fast if anything is in use or autosuspend
|
||||
* is disabled. Also fail if any interfaces require remote wakeup
|
||||
@ -991,20 +996,20 @@ static int autosuspend_check(struct usb_device *udev)
|
||||
}
|
||||
|
||||
/* If everything is okay but the device hasn't been idle for long
|
||||
* enough, queue a delayed autosuspend request.
|
||||
* enough, queue a delayed autosuspend request. If the device
|
||||
* _has_ been idle for long enough and the reschedule flag is set,
|
||||
* likewise queue a delayed (1 second) autosuspend request.
|
||||
*/
|
||||
if (time_after(suspend_time, jiffies)) {
|
||||
j = jiffies;
|
||||
if (time_before(j, suspend_time))
|
||||
reschedule = 1;
|
||||
else
|
||||
suspend_time = j + HZ;
|
||||
if (reschedule) {
|
||||
if (!timer_pending(&udev->autosuspend.timer)) {
|
||||
|
||||
/* The value of jiffies may change between the
|
||||
* time_after() comparison above and the subtraction
|
||||
* below. That's okay; the system behaves sanely
|
||||
* when a timer is registered for the present moment
|
||||
* or for the past.
|
||||
*/
|
||||
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
|
||||
round_jiffies_relative(suspend_time - jiffies));
|
||||
}
|
||||
round_jiffies_relative(suspend_time - j));
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
return 0;
|
||||
@ -1012,7 +1017,7 @@ static int autosuspend_check(struct usb_device *udev)
|
||||
|
||||
#else
|
||||
|
||||
static inline int autosuspend_check(struct usb_device *udev)
|
||||
static inline int autosuspend_check(struct usb_device *udev, int reschedule)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -1069,7 +1074,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
||||
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
|
||||
|
||||
if (udev->auto_pm) {
|
||||
status = autosuspend_check(udev);
|
||||
status = autosuspend_check(udev, 0);
|
||||
if (status < 0)
|
||||
goto done;
|
||||
}
|
||||
@ -1083,15 +1088,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (status == 0) {
|
||||
|
||||
/* Non-root devices don't need to do anything for FREEZE
|
||||
* or PRETHAW. */
|
||||
if (udev->parent && (msg.event == PM_EVENT_FREEZE ||
|
||||
msg.event == PM_EVENT_PRETHAW))
|
||||
goto done;
|
||||
if (status == 0)
|
||||
status = usb_suspend_device(udev, msg);
|
||||
}
|
||||
|
||||
/* If the suspend failed, resume interfaces that did get suspended */
|
||||
if (status != 0) {
|
||||
@ -1102,12 +1100,24 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
||||
|
||||
/* Try another autosuspend when the interfaces aren't busy */
|
||||
if (udev->auto_pm)
|
||||
autosuspend_check(udev);
|
||||
autosuspend_check(udev, status == -EBUSY);
|
||||
|
||||
/* If the suspend succeeded, propagate it up the tree */
|
||||
/* If the suspend succeeded then prevent any more URB submissions,
|
||||
* flush any outstanding URBs, and propagate the suspend up the tree.
|
||||
*/
|
||||
} else {
|
||||
cancel_delayed_work(&udev->autosuspend);
|
||||
if (parent)
|
||||
udev->can_submit = 0;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
|
||||
usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
|
||||
}
|
||||
|
||||
/* If this is just a FREEZE or a PRETHAW, udev might
|
||||
* not really be suspended. Only true suspends get
|
||||
* propagated up the device tree.
|
||||
*/
|
||||
if (parent && udev->state == USB_STATE_SUSPENDED)
|
||||
usb_autosuspend_device(parent);
|
||||
}
|
||||
|
||||
@ -1156,6 +1166,7 @@ static int usb_resume_both(struct usb_device *udev)
|
||||
status = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
udev->can_submit = 1;
|
||||
|
||||
/* Propagate the resume up the tree, if necessary */
|
||||
if (udev->state == USB_STATE_SUSPENDED) {
|
||||
@ -1529,9 +1540,21 @@ int usb_external_resume_device(struct usb_device *udev)
|
||||
|
||||
static int usb_suspend(struct device *dev, pm_message_t message)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
if (!is_usb_device(dev)) /* Ignore PM for interfaces */
|
||||
return 0;
|
||||
return usb_external_suspend_device(to_usb_device(dev), message);
|
||||
udev = to_usb_device(dev);
|
||||
|
||||
/* If udev is already suspended, we can skip this suspend and
|
||||
* we should also skip the upcoming system resume. */
|
||||
if (udev->state == USB_STATE_SUSPENDED) {
|
||||
udev->skip_sys_resume = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
udev->skip_sys_resume = 0;
|
||||
return usb_external_suspend_device(udev, message);
|
||||
}
|
||||
|
||||
static int usb_resume(struct device *dev)
|
||||
@ -1542,13 +1565,14 @@ static int usb_resume(struct device *dev)
|
||||
return 0;
|
||||
udev = to_usb_device(dev);
|
||||
|
||||
/* If autoresume is disabled then we also want to prevent resume
|
||||
* during system wakeup. However, a "persistent-device" reset-resume
|
||||
* after power loss counts as a wakeup event. So allow a
|
||||
* reset-resume to occur if remote wakeup is enabled. */
|
||||
if (udev->autoresume_disabled) {
|
||||
/* If udev->skip_sys_resume is set then udev was already suspended
|
||||
* when the system suspend started, so we don't want to resume
|
||||
* udev during this system wakeup. However a reset-resume counts
|
||||
* as a wakeup event, so allow a reset-resume to occur if remote
|
||||
* wakeup is enabled. */
|
||||
if (udev->skip_sys_resume) {
|
||||
if (!(udev->reset_resume && udev->do_remote_wakeup))
|
||||
return -EPERM;
|
||||
return -EHOSTUNREACH;
|
||||
}
|
||||
return usb_external_resume_device(udev);
|
||||
}
|
||||
|
@ -267,7 +267,6 @@ static void ep_device_release(struct device *dev)
|
||||
{
|
||||
struct ep_device *ep_dev = to_ep_device(dev);
|
||||
|
||||
dev_dbg(dev, "%s called for %s\n", __FUNCTION__, dev->bus_id);
|
||||
endpoint_free_minor(ep_dev);
|
||||
kfree(ep_dev);
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ static int is_activesync(struct usb_interface_descriptor *desc)
|
||||
&& desc->bInterfaceProtocol == 1;
|
||||
}
|
||||
|
||||
static int choose_configuration(struct usb_device *udev)
|
||||
int usb_choose_configuration(struct usb_device *udev)
|
||||
{
|
||||
int i;
|
||||
int num_configs;
|
||||
@ -161,17 +161,20 @@ static int generic_probe(struct usb_device *udev)
|
||||
/* Choose and set the configuration. This registers the interfaces
|
||||
* with the driver core and lets interface drivers bind to them.
|
||||
*/
|
||||
c = choose_configuration(udev);
|
||||
if (c >= 0) {
|
||||
err = usb_set_configuration(udev, c);
|
||||
if (err) {
|
||||
dev_err(&udev->dev, "can't set config #%d, error %d\n",
|
||||
if (udev->authorized == 0)
|
||||
dev_err(&udev->dev, "Device is not authorized for usage\n");
|
||||
else {
|
||||
c = usb_choose_configuration(udev);
|
||||
if (c >= 0) {
|
||||
err = usb_set_configuration(udev, c);
|
||||
if (err) {
|
||||
dev_err(&udev->dev, "can't set config #%d, error %d\n",
|
||||
c, err);
|
||||
/* This need not be fatal. The user can try to
|
||||
* set other configurations. */
|
||||
/* This need not be fatal. The user can try to
|
||||
* set other configurations. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* USB device state == configured ... usable */
|
||||
usb_notify_add_device(udev);
|
||||
|
||||
@ -203,8 +206,13 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
*/
|
||||
if (!udev->parent)
|
||||
rc = hcd_bus_suspend(udev);
|
||||
|
||||
/* Non-root devices don't need to do anything for FREEZE or PRETHAW */
|
||||
else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
|
||||
rc = 0;
|
||||
else
|
||||
rc = usb_port_suspend(udev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -356,10 +356,18 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
|
||||
const u8 *bufp = tbuf;
|
||||
int len = 0;
|
||||
int patch_wakeup = 0;
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
int status;
|
||||
int n;
|
||||
|
||||
might_sleep();
|
||||
|
||||
spin_lock_irq(&hcd_root_hub_lock);
|
||||
status = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
spin_unlock_irq(&hcd_root_hub_lock);
|
||||
if (status)
|
||||
return status;
|
||||
urb->hcpriv = hcd; /* Indicate it's queued */
|
||||
|
||||
cmd = (struct usb_ctrlrequest *) urb->setup_packet;
|
||||
typeReq = (cmd->bRequestType << 8) | cmd->bRequest;
|
||||
wValue = le16_to_cpu (cmd->wValue);
|
||||
@ -523,13 +531,18 @@ error:
|
||||
}
|
||||
|
||||
/* any errors get returned through the urb completion */
|
||||
local_irq_save (flags);
|
||||
spin_lock (&urb->lock);
|
||||
if (urb->status == -EINPROGRESS)
|
||||
urb->status = status;
|
||||
spin_unlock (&urb->lock);
|
||||
usb_hcd_giveback_urb (hcd, urb);
|
||||
local_irq_restore (flags);
|
||||
spin_lock_irq(&hcd_root_hub_lock);
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
|
||||
/* This peculiar use of spinlocks echoes what real HC drivers do.
|
||||
* Avoiding calls to local_irq_disable/enable makes the code
|
||||
* RT-friendly.
|
||||
*/
|
||||
spin_unlock(&hcd_root_hub_lock);
|
||||
usb_hcd_giveback_urb(hcd, urb, status);
|
||||
spin_lock(&hcd_root_hub_lock);
|
||||
|
||||
spin_unlock_irq(&hcd_root_hub_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -559,31 +572,23 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
|
||||
if (length > 0) {
|
||||
|
||||
/* try to complete the status urb */
|
||||
local_irq_save (flags);
|
||||
spin_lock(&hcd_root_hub_lock);
|
||||
spin_lock_irqsave(&hcd_root_hub_lock, flags);
|
||||
urb = hcd->status_urb;
|
||||
if (urb) {
|
||||
spin_lock(&urb->lock);
|
||||
if (urb->status == -EINPROGRESS) {
|
||||
hcd->poll_pending = 0;
|
||||
hcd->status_urb = NULL;
|
||||
urb->status = 0;
|
||||
urb->hcpriv = NULL;
|
||||
urb->actual_length = length;
|
||||
memcpy(urb->transfer_buffer, buffer, length);
|
||||
} else /* urb has been unlinked */
|
||||
length = 0;
|
||||
spin_unlock(&urb->lock);
|
||||
} else
|
||||
length = 0;
|
||||
spin_unlock(&hcd_root_hub_lock);
|
||||
hcd->poll_pending = 0;
|
||||
hcd->status_urb = NULL;
|
||||
urb->actual_length = length;
|
||||
memcpy(urb->transfer_buffer, buffer, length);
|
||||
|
||||
/* local irqs are always blocked in completions */
|
||||
if (length > 0)
|
||||
usb_hcd_giveback_urb (hcd, urb);
|
||||
else
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
spin_unlock(&hcd_root_hub_lock);
|
||||
usb_hcd_giveback_urb(hcd, urb, 0);
|
||||
spin_lock(&hcd_root_hub_lock);
|
||||
} else {
|
||||
length = 0;
|
||||
hcd->poll_pending = 1;
|
||||
local_irq_restore (flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
|
||||
}
|
||||
|
||||
/* The USB 2.0 spec says 256 ms. This is close enough and won't
|
||||
@ -611,33 +616,35 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
|
||||
int len = 1 + (urb->dev->maxchild / 8);
|
||||
|
||||
spin_lock_irqsave (&hcd_root_hub_lock, flags);
|
||||
if (urb->status != -EINPROGRESS) /* already unlinked */
|
||||
retval = urb->status;
|
||||
else if (hcd->status_urb || urb->transfer_buffer_length < len) {
|
||||
if (hcd->status_urb || urb->transfer_buffer_length < len) {
|
||||
dev_dbg (hcd->self.controller, "not queuing rh status urb\n");
|
||||
retval = -EINVAL;
|
||||
} else {
|
||||
hcd->status_urb = urb;
|
||||
urb->hcpriv = hcd; /* indicate it's queued */
|
||||
|
||||
if (!hcd->uses_new_polling)
|
||||
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)
|
||||
mod_timer (&hcd->rh_timer, jiffies);
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
hcd->status_urb = urb;
|
||||
urb->hcpriv = hcd; /* indicate it's queued */
|
||||
if (!hcd->uses_new_polling)
|
||||
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)
|
||||
mod_timer(&hcd->rh_timer, jiffies);
|
||||
retval = 0;
|
||||
done:
|
||||
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
if (usb_pipeint (urb->pipe))
|
||||
if (usb_endpoint_xfer_int(&urb->ep->desc))
|
||||
return rh_queue_status (hcd, urb);
|
||||
if (usb_pipecontrol (urb->pipe))
|
||||
if (usb_endpoint_xfer_control(&urb->ep->desc))
|
||||
return rh_call_control (hcd, urb);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -647,32 +654,96 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
|
||||
/* Unlinks of root-hub control URBs are legal, but they don't do anything
|
||||
* since these URBs always execute synchronously.
|
||||
*/
|
||||
static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
|
||||
static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
|
||||
spin_lock_irqsave(&hcd_root_hub_lock, flags);
|
||||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
if (usb_endpoint_num(&urb->ep->desc) == 0) { /* Control URB */
|
||||
; /* Do nothing */
|
||||
|
||||
} else { /* Status URB */
|
||||
if (!hcd->uses_new_polling)
|
||||
del_timer (&hcd->rh_timer);
|
||||
local_irq_save (flags);
|
||||
spin_lock (&hcd_root_hub_lock);
|
||||
if (urb == hcd->status_urb) {
|
||||
hcd->status_urb = NULL;
|
||||
urb->hcpriv = NULL;
|
||||
} else
|
||||
urb = NULL; /* wasn't fully queued */
|
||||
spin_unlock (&hcd_root_hub_lock);
|
||||
if (urb)
|
||||
usb_hcd_giveback_urb (hcd, urb);
|
||||
local_irq_restore (flags);
|
||||
}
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
|
||||
return 0;
|
||||
spin_unlock(&hcd_root_hub_lock);
|
||||
usb_hcd_giveback_urb(hcd, urb, status);
|
||||
spin_lock(&hcd_root_hub_lock);
|
||||
}
|
||||
}
|
||||
done:
|
||||
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Show & store the current value of authorized_default
|
||||
*/
|
||||
static ssize_t usb_host_authorized_default_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_device *rh_usb_dev = to_usb_device(dev);
|
||||
struct usb_bus *usb_bus = rh_usb_dev->bus;
|
||||
struct usb_hcd *usb_hcd;
|
||||
|
||||
if (usb_bus == NULL) /* FIXME: not sure if this case is possible */
|
||||
return -ENODEV;
|
||||
usb_hcd = bus_to_hcd(usb_bus);
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default);
|
||||
}
|
||||
|
||||
static ssize_t usb_host_authorized_default_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
unsigned val;
|
||||
struct usb_device *rh_usb_dev = to_usb_device(dev);
|
||||
struct usb_bus *usb_bus = rh_usb_dev->bus;
|
||||
struct usb_hcd *usb_hcd;
|
||||
|
||||
if (usb_bus == NULL) /* FIXME: not sure if this case is possible */
|
||||
return -ENODEV;
|
||||
usb_hcd = bus_to_hcd(usb_bus);
|
||||
result = sscanf(buf, "%u\n", &val);
|
||||
if (result == 1) {
|
||||
usb_hcd->authorized_default = val? 1 : 0;
|
||||
result = size;
|
||||
}
|
||||
else
|
||||
result = -EINVAL;
|
||||
return result;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(authorized_default, 0644,
|
||||
usb_host_authorized_default_show,
|
||||
usb_host_authorized_default_store);
|
||||
|
||||
|
||||
/* Group all the USB bus attributes */
|
||||
static struct attribute *usb_bus_attrs[] = {
|
||||
&dev_attr_authorized_default.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group usb_bus_attr_group = {
|
||||
.name = NULL, /* we want them in the same directory */
|
||||
.attrs = usb_bus_attrs,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct class *usb_host_class;
|
||||
@ -726,27 +797,23 @@ static void usb_bus_init (struct usb_bus *bus)
|
||||
*/
|
||||
static int usb_register_bus(struct usb_bus *bus)
|
||||
{
|
||||
int result = -E2BIG;
|
||||
int busnum;
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
|
||||
if (busnum < USB_MAXBUS) {
|
||||
set_bit (busnum, busmap.busmap);
|
||||
bus->busnum = busnum;
|
||||
} else {
|
||||
if (busnum >= USB_MAXBUS) {
|
||||
printk (KERN_ERR "%s: too many buses\n", usbcore_name);
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
return -E2BIG;
|
||||
goto error_find_busnum;
|
||||
}
|
||||
|
||||
set_bit (busnum, busmap.busmap);
|
||||
bus->busnum = busnum;
|
||||
bus->class_dev = class_device_create(usb_host_class, NULL, MKDEV(0,0),
|
||||
bus->controller, "usb_host%d", busnum);
|
||||
if (IS_ERR(bus->class_dev)) {
|
||||
clear_bit(busnum, busmap.busmap);
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
return PTR_ERR(bus->class_dev);
|
||||
}
|
||||
|
||||
bus->controller, "usb_host%d",
|
||||
busnum);
|
||||
result = PTR_ERR(bus->class_dev);
|
||||
if (IS_ERR(bus->class_dev))
|
||||
goto error_create_class_dev;
|
||||
class_set_devdata(bus->class_dev, bus);
|
||||
|
||||
/* Add it to the local list of buses */
|
||||
@ -755,8 +822,15 @@ static int usb_register_bus(struct usb_bus *bus)
|
||||
|
||||
usb_notify_add_bus(bus);
|
||||
|
||||
dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);
|
||||
dev_info (bus->controller, "new USB bus registered, assigned bus "
|
||||
"number %d\n", bus->busnum);
|
||||
return 0;
|
||||
|
||||
error_create_class_dev:
|
||||
clear_bit(busnum, busmap.busmap);
|
||||
error_find_busnum:
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -908,103 +982,145 @@ EXPORT_SYMBOL (usb_calc_bus_time);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void urb_unlink(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* clear all state linking urb to this dev (and hcd) */
|
||||
spin_lock_irqsave(&hcd_urb_list_lock, flags);
|
||||
list_del_init (&urb->urb_list);
|
||||
spin_unlock_irqrestore(&hcd_urb_list_lock, flags);
|
||||
|
||||
if (hcd->self.uses_dma && !is_root_hub(urb->dev)) {
|
||||
if (usb_pipecontrol (urb->pipe)
|
||||
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
|
||||
dma_unmap_single (hcd->self.controller, urb->setup_dma,
|
||||
sizeof (struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
if (urb->transfer_buffer_length != 0
|
||||
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
|
||||
dma_unmap_single (hcd->self.controller,
|
||||
urb->transfer_dma,
|
||||
urb->transfer_buffer_length,
|
||||
usb_pipein (urb->pipe)
|
||||
? DMA_FROM_DEVICE
|
||||
: DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
/* may be called in any context with a valid urb->dev usecount
|
||||
* caller surrenders "ownership" of urb
|
||||
* expects usb_submit_urb() to have sanity checked and conditioned all
|
||||
* inputs in the urb
|
||||
/**
|
||||
* usb_hcd_link_urb_to_ep - add an URB to its endpoint queue
|
||||
* @hcd: host controller to which @urb was submitted
|
||||
* @urb: URB being submitted
|
||||
*
|
||||
* Host controller drivers should call this routine in their enqueue()
|
||||
* method. The HCD's private spinlock must be held and interrupts must
|
||||
* be disabled. The actions carried out here are required for URB
|
||||
* submission, as well as for endpoint shutdown and for usb_kill_urb.
|
||||
*
|
||||
* Returns 0 for no error, otherwise a negative error code (in which case
|
||||
* the enqueue() method must fail). If no error occurs but enqueue() fails
|
||||
* anyway, it must call usb_hcd_unlink_urb_from_ep() before releasing
|
||||
* the private spinlock and returning.
|
||||
*/
|
||||
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
|
||||
int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
int status;
|
||||
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
|
||||
struct usb_host_endpoint *ep;
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
if (!hcd)
|
||||
return -ENODEV;
|
||||
spin_lock(&hcd_urb_list_lock);
|
||||
|
||||
usbmon_urb_submit(&hcd->self, urb);
|
||||
|
||||
/*
|
||||
* Atomically queue the urb, first to our records, then to the HCD.
|
||||
* Access to urb->status is controlled by urb->lock ... changes on
|
||||
* i/o completion (normal or fault) or unlinking.
|
||||
*/
|
||||
|
||||
// FIXME: verify that quiescing hc works right (RH cleans up)
|
||||
|
||||
spin_lock_irqsave(&hcd_urb_list_lock, flags);
|
||||
ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)
|
||||
[usb_pipeendpoint(urb->pipe)];
|
||||
if (unlikely (!ep))
|
||||
status = -ENOENT;
|
||||
else if (unlikely (urb->reject))
|
||||
status = -EPERM;
|
||||
else switch (hcd->state) {
|
||||
case HC_STATE_RUNNING:
|
||||
case HC_STATE_RESUMING:
|
||||
list_add_tail (&urb->urb_list, &ep->urb_list);
|
||||
status = 0;
|
||||
break;
|
||||
default:
|
||||
status = -ESHUTDOWN;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&hcd_urb_list_lock, flags);
|
||||
if (status) {
|
||||
INIT_LIST_HEAD (&urb->urb_list);
|
||||
usbmon_urb_submit_error(&hcd->self, urb, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* increment urb's reference count as part of giving it to the HCD
|
||||
* (which now controls it). HCD guarantees that it either returns
|
||||
* an error or calls giveback(), but not both.
|
||||
*/
|
||||
urb = usb_get_urb (urb);
|
||||
atomic_inc (&urb->use_count);
|
||||
|
||||
if (is_root_hub(urb->dev)) {
|
||||
/* NOTE: requirement on hub callers (usbfs and the hub
|
||||
* driver, for now) that URBs' urb->transfer_buffer be
|
||||
* valid and usb_buffer_{sync,unmap}() not be needed, since
|
||||
* they could clobber root hub response data.
|
||||
*/
|
||||
status = rh_urb_enqueue (hcd, urb);
|
||||
/* Check that the URB isn't being killed */
|
||||
if (unlikely(urb->reject)) {
|
||||
rc = -EPERM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* lower level hcd code should use *_dma exclusively,
|
||||
if (unlikely(!urb->ep->enabled)) {
|
||||
rc = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (unlikely(!urb->dev->can_submit)) {
|
||||
rc = -EHOSTUNREACH;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the host controller's state and add the URB to the
|
||||
* endpoint's queue.
|
||||
*/
|
||||
switch (hcd->state) {
|
||||
case HC_STATE_RUNNING:
|
||||
case HC_STATE_RESUMING:
|
||||
urb->unlinked = 0;
|
||||
list_add_tail(&urb->urb_list, &urb->ep->urb_list);
|
||||
break;
|
||||
default:
|
||||
rc = -ESHUTDOWN;
|
||||
goto done;
|
||||
}
|
||||
done:
|
||||
spin_unlock(&hcd_urb_list_lock);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_link_urb_to_ep);
|
||||
|
||||
/**
|
||||
* usb_hcd_check_unlink_urb - check whether an URB may be unlinked
|
||||
* @hcd: host controller to which @urb was submitted
|
||||
* @urb: URB being checked for unlinkability
|
||||
* @status: error code to store in @urb if the unlink succeeds
|
||||
*
|
||||
* Host controller drivers should call this routine in their dequeue()
|
||||
* method. The HCD's private spinlock must be held and interrupts must
|
||||
* be disabled. The actions carried out here are required for making
|
||||
* sure than an unlink is valid.
|
||||
*
|
||||
* Returns 0 for no error, otherwise a negative error code (in which case
|
||||
* the dequeue() method must fail). The possible error codes are:
|
||||
*
|
||||
* -EIDRM: @urb was not submitted or has already completed.
|
||||
* The completion function may not have been called yet.
|
||||
*
|
||||
* -EBUSY: @urb has already been unlinked.
|
||||
*/
|
||||
int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
|
||||
int status)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
|
||||
/* insist the urb is still queued */
|
||||
list_for_each(tmp, &urb->ep->urb_list) {
|
||||
if (tmp == &urb->urb_list)
|
||||
break;
|
||||
}
|
||||
if (tmp != &urb->urb_list)
|
||||
return -EIDRM;
|
||||
|
||||
/* Any status except -EINPROGRESS means something already started to
|
||||
* unlink this URB from the hardware. So there's no more work to do.
|
||||
*/
|
||||
if (urb->unlinked)
|
||||
return -EBUSY;
|
||||
urb->unlinked = status;
|
||||
|
||||
/* IRQ setup can easily be broken so that USB controllers
|
||||
* never get completion IRQs ... maybe even the ones we need to
|
||||
* 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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_check_unlink_urb);
|
||||
|
||||
/**
|
||||
* usb_hcd_unlink_urb_from_ep - remove an URB from its endpoint queue
|
||||
* @hcd: host controller to which @urb was submitted
|
||||
* @urb: URB being unlinked
|
||||
*
|
||||
* Host controller drivers should call this routine before calling
|
||||
* usb_hcd_giveback_urb(). The HCD's private spinlock must be held and
|
||||
* interrupts must be disabled. The actions carried out here are required
|
||||
* for URB completion.
|
||||
*/
|
||||
void usb_hcd_unlink_urb_from_ep(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
/* clear all state linking urb to this dev (and hcd) */
|
||||
spin_lock(&hcd_urb_list_lock);
|
||||
list_del_init(&urb->urb_list);
|
||||
spin_unlock(&hcd_urb_list_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep);
|
||||
|
||||
static void map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
/* Map the URB's buffers for DMA access.
|
||||
* Lower level HCD code should use *_dma exclusively,
|
||||
* unless it uses pio or talks to another transport.
|
||||
*/
|
||||
if (hcd->self.uses_dma) {
|
||||
if (usb_pipecontrol (urb->pipe)
|
||||
if (hcd->self.uses_dma && !is_root_hub(urb->dev)) {
|
||||
if (usb_endpoint_xfer_control(&urb->ep->desc)
|
||||
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
|
||||
urb->setup_dma = dma_map_single (
|
||||
hcd->self.controller,
|
||||
@ -1017,20 +1133,75 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
|
||||
hcd->self.controller,
|
||||
urb->transfer_buffer,
|
||||
urb->transfer_buffer_length,
|
||||
usb_pipein (urb->pipe)
|
||||
usb_urb_dir_in(urb)
|
||||
? DMA_FROM_DEVICE
|
||||
: DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);
|
||||
done:
|
||||
if (unlikely (status)) {
|
||||
urb_unlink(hcd, urb);
|
||||
atomic_dec (&urb->use_count);
|
||||
if (urb->reject)
|
||||
wake_up (&usb_kill_urb_queue);
|
||||
static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
if (hcd->self.uses_dma && !is_root_hub(urb->dev)) {
|
||||
if (usb_endpoint_xfer_control(&urb->ep->desc)
|
||||
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
|
||||
dma_unmap_single(hcd->self.controller, urb->setup_dma,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
if (urb->transfer_buffer_length != 0
|
||||
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
|
||||
dma_unmap_single(hcd->self.controller,
|
||||
urb->transfer_dma,
|
||||
urb->transfer_buffer_length,
|
||||
usb_urb_dir_in(urb)
|
||||
? DMA_FROM_DEVICE
|
||||
: DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* may be called in any context with a valid urb->dev usecount
|
||||
* caller surrenders "ownership" of urb
|
||||
* expects usb_submit_urb() to have sanity checked and conditioned all
|
||||
* inputs in the urb
|
||||
*/
|
||||
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
|
||||
{
|
||||
int status;
|
||||
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
|
||||
|
||||
/* increment urb's reference count as part of giving it to the HCD
|
||||
* (which will control it). HCD guarantees that it either returns
|
||||
* an error or calls giveback(), but not both.
|
||||
*/
|
||||
usb_get_urb(urb);
|
||||
atomic_inc(&urb->use_count);
|
||||
atomic_inc(&urb->dev->urbnum);
|
||||
usbmon_urb_submit(&hcd->self, urb);
|
||||
|
||||
/* NOTE requirements on root-hub callers (usbfs and the hub
|
||||
* driver, for now): URBs' urb->transfer_buffer must be
|
||||
* valid and usb_buffer_{sync,unmap}() not be needed, since
|
||||
* they could clobber root hub response data. Also, control
|
||||
* URBs must be submitted in process context with interrupts
|
||||
* enabled.
|
||||
*/
|
||||
map_urb_for_dma(hcd, urb);
|
||||
if (is_root_hub(urb->dev))
|
||||
status = rh_urb_enqueue(hcd, urb);
|
||||
else
|
||||
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
|
||||
|
||||
if (unlikely(status)) {
|
||||
usbmon_urb_submit_error(&hcd->self, urb, status);
|
||||
usb_put_urb (urb);
|
||||
unmap_urb_for_dma(hcd, urb);
|
||||
urb->hcpriv = NULL;
|
||||
INIT_LIST_HEAD(&urb->urb_list);
|
||||
atomic_dec(&urb->use_count);
|
||||
atomic_dec(&urb->dev->urbnum);
|
||||
if (urb->reject)
|
||||
wake_up(&usb_kill_urb_queue);
|
||||
usb_put_urb(urb);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@ -1042,24 +1213,19 @@ done:
|
||||
* soon as practical. we've already set up the urb's return status,
|
||||
* but we can't know if the callback completed already.
|
||||
*/
|
||||
static int
|
||||
unlink1 (struct usb_hcd *hcd, struct urb *urb)
|
||||
static int unlink1(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
int value;
|
||||
|
||||
if (is_root_hub(urb->dev))
|
||||
value = usb_rh_urb_dequeue (hcd, urb);
|
||||
value = usb_rh_urb_dequeue(hcd, urb, status);
|
||||
else {
|
||||
|
||||
/* The only reason an HCD might fail this call is if
|
||||
* it has not yet fully queued the urb to begin with.
|
||||
* Such failures should be harmless. */
|
||||
value = hcd->driver->urb_dequeue (hcd, urb);
|
||||
value = hcd->driver->urb_dequeue(hcd, urb, status);
|
||||
}
|
||||
|
||||
if (value != 0)
|
||||
dev_dbg (hcd->self.controller, "dequeue %p --> %d\n",
|
||||
urb, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -1071,88 +1237,17 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
|
||||
*/
|
||||
int usb_hcd_unlink_urb (struct urb *urb, int status)
|
||||
{
|
||||
struct usb_host_endpoint *ep;
|
||||
struct usb_hcd *hcd = NULL;
|
||||
struct device *sys = NULL;
|
||||
unsigned long flags;
|
||||
struct list_head *tmp;
|
||||
int retval;
|
||||
struct usb_hcd *hcd;
|
||||
int retval;
|
||||
|
||||
if (!urb)
|
||||
return -EINVAL;
|
||||
if (!urb->dev || !urb->dev->bus)
|
||||
return -ENODEV;
|
||||
ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)
|
||||
[usb_pipeendpoint(urb->pipe)];
|
||||
if (!ep)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* we contend for urb->status with the hcd core,
|
||||
* which changes it while returning the urb.
|
||||
*
|
||||
* Caller guaranteed that the urb pointer hasn't been freed, and
|
||||
* that it was submitted. But as a rule it can't know whether or
|
||||
* not it's already been unlinked ... so we respect the reversed
|
||||
* lock sequence needed for the usb_hcd_giveback_urb() code paths
|
||||
* (urb lock, then hcd_urb_list_lock) in case some other CPU is now
|
||||
* unlinking it.
|
||||
*/
|
||||
spin_lock_irqsave (&urb->lock, flags);
|
||||
spin_lock(&hcd_urb_list_lock);
|
||||
|
||||
sys = &urb->dev->dev;
|
||||
hcd = bus_to_hcd(urb->dev->bus);
|
||||
if (hcd == NULL) {
|
||||
retval = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
retval = unlink1(hcd, urb, status);
|
||||
|
||||
/* insist the urb is still queued */
|
||||
list_for_each(tmp, &ep->urb_list) {
|
||||
if (tmp == &urb->urb_list)
|
||||
break;
|
||||
}
|
||||
if (tmp != &urb->urb_list) {
|
||||
retval = -EIDRM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Any status except -EINPROGRESS means something already started to
|
||||
* unlink this URB from the hardware. So there's no more work to do.
|
||||
*/
|
||||
if (urb->status != -EINPROGRESS) {
|
||||
retval = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* IRQ setup can easily be broken so that USB controllers
|
||||
* never get completion IRQs ... maybe even the ones we need to
|
||||
* 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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
urb->status = status;
|
||||
|
||||
spin_unlock(&hcd_urb_list_lock);
|
||||
spin_unlock_irqrestore (&urb->lock, flags);
|
||||
|
||||
retval = unlink1 (hcd, urb);
|
||||
if (retval == 0)
|
||||
retval = -EINPROGRESS;
|
||||
return retval;
|
||||
|
||||
done:
|
||||
spin_unlock(&hcd_urb_list_lock);
|
||||
spin_unlock_irqrestore (&urb->lock, flags);
|
||||
if (retval != -EIDRM && sys && sys->driver)
|
||||
dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);
|
||||
else if (retval != -EIDRM && retval != -EBUSY)
|
||||
dev_dbg(&urb->dev->dev, "hcd_unlink_urb %p fail %d\n",
|
||||
urb, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -1162,6 +1257,7 @@ done:
|
||||
* usb_hcd_giveback_urb - return URB from HCD to device driver
|
||||
* @hcd: host controller returning the URB
|
||||
* @urb: urb being returned to the USB device driver.
|
||||
* @status: completion status code for the URB.
|
||||
* Context: in_interrupt()
|
||||
*
|
||||
* This hands the URB from HCD to its USB device driver, using its
|
||||
@ -1169,14 +1265,27 @@ done:
|
||||
* (and is done using urb->hcpriv). It also released all HCD locks;
|
||||
* the device driver won't cause problems if it frees, modifies,
|
||||
* or resubmits this URB.
|
||||
*
|
||||
* If @urb was unlinked, the value of @status will be overridden by
|
||||
* @urb->unlinked. Erroneous short transfers are detected in case
|
||||
* the HCD hasn't checked for them.
|
||||
*/
|
||||
void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
|
||||
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
urb_unlink(hcd, urb);
|
||||
usbmon_urb_complete (&hcd->self, urb);
|
||||
urb->hcpriv = NULL;
|
||||
if (unlikely(urb->unlinked))
|
||||
status = urb->unlinked;
|
||||
else if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
|
||||
urb->actual_length < urb->transfer_buffer_length &&
|
||||
!status))
|
||||
status = -EREMOTEIO;
|
||||
|
||||
unmap_urb_for_dma(hcd, urb);
|
||||
usbmon_urb_complete(&hcd->self, urb, status);
|
||||
usb_unanchor_urb(urb);
|
||||
|
||||
/* pass ownership to the completion handler */
|
||||
urb->status = status;
|
||||
urb->complete (urb);
|
||||
atomic_dec (&urb->use_count);
|
||||
if (unlikely (urb->reject))
|
||||
@ -1187,78 +1296,61 @@ EXPORT_SYMBOL (usb_hcd_giveback_urb);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* disables the endpoint: cancels any pending urbs, then synchronizes with
|
||||
* the hcd to make sure all endpoint state is gone from hardware, and then
|
||||
* waits until the endpoint's queue is completely drained. use for
|
||||
* set_configuration, set_interface, driver removal, physical disconnect.
|
||||
*
|
||||
* example: a qh stored in ep->hcpriv, holding state related to endpoint
|
||||
* type, maxpacket size, toggle, halt status, and scheduling.
|
||||
/* Cancel all URBs pending on this endpoint and wait for the endpoint's
|
||||
* queue to drain completely. The caller must first insure that no more
|
||||
* URBs can be submitted for this endpoint.
|
||||
*/
|
||||
void usb_hcd_endpoint_disable (struct usb_device *udev,
|
||||
void usb_hcd_flush_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct urb *urb;
|
||||
|
||||
if (!ep)
|
||||
return;
|
||||
might_sleep();
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
local_irq_disable ();
|
||||
|
||||
/* ep is already gone from udev->ep_{in,out}[]; no more submits */
|
||||
/* No more submits can occur */
|
||||
rescan:
|
||||
spin_lock(&hcd_urb_list_lock);
|
||||
spin_lock_irq(&hcd_urb_list_lock);
|
||||
list_for_each_entry (urb, &ep->urb_list, urb_list) {
|
||||
int tmp;
|
||||
int is_in;
|
||||
|
||||
/* the urb may already have been unlinked */
|
||||
if (urb->status != -EINPROGRESS)
|
||||
if (urb->unlinked)
|
||||
continue;
|
||||
usb_get_urb (urb);
|
||||
is_in = usb_urb_dir_in(urb);
|
||||
spin_unlock(&hcd_urb_list_lock);
|
||||
|
||||
spin_lock (&urb->lock);
|
||||
tmp = urb->status;
|
||||
if (tmp == -EINPROGRESS)
|
||||
urb->status = -ESHUTDOWN;
|
||||
spin_unlock (&urb->lock);
|
||||
/* kick hcd */
|
||||
unlink1(hcd, urb, -ESHUTDOWN);
|
||||
dev_dbg (hcd->self.controller,
|
||||
"shutdown urb %p ep%d%s%s\n",
|
||||
urb, usb_endpoint_num(&ep->desc),
|
||||
is_in ? "in" : "out",
|
||||
({ char *s;
|
||||
|
||||
/* kick hcd unless it's already returning this */
|
||||
if (tmp == -EINPROGRESS) {
|
||||
tmp = urb->pipe;
|
||||
unlink1 (hcd, urb);
|
||||
dev_dbg (hcd->self.controller,
|
||||
"shutdown urb %p pipe %08x ep%d%s%s\n",
|
||||
urb, tmp, usb_pipeendpoint (tmp),
|
||||
(tmp & USB_DIR_IN) ? "in" : "out",
|
||||
({ char *s; \
|
||||
switch (usb_pipetype (tmp)) { \
|
||||
case PIPE_CONTROL: s = ""; break; \
|
||||
case PIPE_BULK: s = "-bulk"; break; \
|
||||
case PIPE_INTERRUPT: s = "-intr"; break; \
|
||||
default: s = "-iso"; break; \
|
||||
}; s;}));
|
||||
}
|
||||
switch (usb_endpoint_type(&ep->desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
s = ""; break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
s = "-bulk"; break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
s = "-intr"; break;
|
||||
default:
|
||||
s = "-iso"; break;
|
||||
};
|
||||
s;
|
||||
}));
|
||||
usb_put_urb (urb);
|
||||
|
||||
/* list contents may have changed */
|
||||
goto rescan;
|
||||
}
|
||||
spin_unlock(&hcd_urb_list_lock);
|
||||
local_irq_enable ();
|
||||
spin_unlock_irq(&hcd_urb_list_lock);
|
||||
|
||||
/* synchronize with the hardware, so old configuration state
|
||||
* clears out immediately (and will be freed).
|
||||
*/
|
||||
might_sleep ();
|
||||
if (hcd->driver->endpoint_disable)
|
||||
hcd->driver->endpoint_disable (hcd, ep);
|
||||
|
||||
/* Wait until the endpoint queue is completely empty. Most HCDs
|
||||
* will have done this already in their endpoint_disable method,
|
||||
* but some might not. And there could be root-hub control URBs
|
||||
* still pending since they aren't affected by the HCDs'
|
||||
* endpoint_disable methods.
|
||||
*/
|
||||
/* Wait until the endpoint queue is completely empty */
|
||||
while (!list_empty (&ep->urb_list)) {
|
||||
spin_lock_irq(&hcd_urb_list_lock);
|
||||
|
||||
@ -1278,6 +1370,25 @@ rescan:
|
||||
}
|
||||
}
|
||||
|
||||
/* Disables the endpoint: synchronizes with the hcd to make sure all
|
||||
* endpoint state is gone from hardware. usb_hcd_flush_endpoint() must
|
||||
* have been called previously. Use for set_configuration, set_interface,
|
||||
* driver removal, physical disconnect.
|
||||
*
|
||||
* example: a qh stored in ep->hcpriv, holding state related to endpoint
|
||||
* type, maxpacket size, toggle, halt status, and scheduling.
|
||||
*/
|
||||
void usb_hcd_disable_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
might_sleep();
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
if (hcd->driver->endpoint_disable)
|
||||
hcd->driver->endpoint_disable(hcd, ep);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* called in any context */
|
||||
@ -1525,7 +1636,6 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
|
||||
hcd->driver = driver;
|
||||
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
|
||||
"USB Host Controller";
|
||||
|
||||
return hcd;
|
||||
}
|
||||
EXPORT_SYMBOL (usb_create_hcd);
|
||||
@ -1570,6 +1680,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
|
||||
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
|
||||
|
||||
hcd->authorized_default = hcd->wireless? 0 : 1;
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
/* HC is in reset state, but accessible. Now do the one-time init,
|
||||
@ -1646,10 +1757,20 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
if ((retval = register_root_hub(hcd)) != 0)
|
||||
goto err_register_root_hub;
|
||||
|
||||
retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);
|
||||
if (retval < 0) {
|
||||
printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n",
|
||||
retval);
|
||||
goto error_create_attr_group;
|
||||
}
|
||||
if (hcd->uses_new_polling && hcd->poll_rh)
|
||||
usb_hcd_poll_rh_status(hcd);
|
||||
return retval;
|
||||
|
||||
error_create_attr_group:
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
usb_disconnect(&hcd->self.root_hub);
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
err_register_root_hub:
|
||||
hcd->driver->stop(hcd);
|
||||
err_hcd_driver_start:
|
||||
@ -1691,6 +1812,7 @@ 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);
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
/* This file contains declarations of usbcore internals that are mostly
|
||||
* used or exposed by Host Controller Drivers.
|
||||
*/
|
||||
@ -51,6 +53,12 @@
|
||||
*
|
||||
* Since "struct usb_bus" is so thin, you can't share much code in it.
|
||||
* This framework is a layer over that, and should be more sharable.
|
||||
*
|
||||
* @authorized_default: Specifies if new devices are authorized to
|
||||
* connect by default or they require explicit
|
||||
* user space authorization; this bit is settable
|
||||
* through /sys/class/usb_host/X/authorized_default.
|
||||
* For the rest is RO, so we don't lock to r/w it.
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -90,6 +98,7 @@ struct usb_hcd {
|
||||
unsigned poll_rh:1; /* poll for rh status? */
|
||||
unsigned poll_pending:1; /* status has changed? */
|
||||
unsigned wireless:1; /* Wireless USB HCD */
|
||||
unsigned authorized_default:1;
|
||||
|
||||
int irq; /* irq allocated */
|
||||
void __iomem *regs; /* device memory/io */
|
||||
@ -182,11 +191,10 @@ struct hc_driver {
|
||||
int (*get_frame_number) (struct usb_hcd *hcd);
|
||||
|
||||
/* manage i/o requests, device state */
|
||||
int (*urb_enqueue) (struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *ep,
|
||||
struct urb *urb,
|
||||
gfp_t mem_flags);
|
||||
int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
|
||||
int (*urb_enqueue)(struct usb_hcd *hcd,
|
||||
struct urb *urb, gfp_t mem_flags);
|
||||
int (*urb_dequeue)(struct usb_hcd *hcd,
|
||||
struct urb *urb, int status);
|
||||
|
||||
/* hw synch, freeing endpoint resources that urb_dequeue can't */
|
||||
void (*endpoint_disable)(struct usb_hcd *hcd,
|
||||
@ -204,10 +212,18 @@ struct hc_driver {
|
||||
/* Needed only if port-change IRQs are level-triggered */
|
||||
};
|
||||
|
||||
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
|
||||
extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
|
||||
int status);
|
||||
extern void usb_hcd_unlink_urb_from_ep(struct usb_hcd *hcd, struct urb *urb);
|
||||
|
||||
extern int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags);
|
||||
extern int usb_hcd_unlink_urb (struct urb *urb, int status);
|
||||
extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb);
|
||||
extern void usb_hcd_endpoint_disable (struct usb_device *udev,
|
||||
extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb,
|
||||
int status);
|
||||
extern void usb_hcd_flush_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep);
|
||||
extern void usb_hcd_disable_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep);
|
||||
extern int usb_hcd_get_frame_number (struct usb_device *udev);
|
||||
|
||||
@ -402,7 +418,7 @@ static inline void usbfs_cleanup(void) { }
|
||||
struct usb_mon_operations {
|
||||
void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
|
||||
void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err);
|
||||
void (*urb_complete)(struct usb_bus *bus, struct urb *urb);
|
||||
void (*urb_complete)(struct usb_bus *bus, struct urb *urb, int status);
|
||||
/* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
|
||||
};
|
||||
|
||||
@ -421,10 +437,11 @@ static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
|
||||
(*mon_ops->urb_submit_error)(bus, urb, error);
|
||||
}
|
||||
|
||||
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb)
|
||||
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
|
||||
int status)
|
||||
{
|
||||
if (bus->monitored)
|
||||
(*mon_ops->urb_complete)(bus, urb);
|
||||
(*mon_ops->urb_complete)(bus, urb, status);
|
||||
}
|
||||
|
||||
int usb_mon_register(struct usb_mon_operations *ops);
|
||||
@ -435,7 +452,8 @@ void usb_mon_deregister(void);
|
||||
static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {}
|
||||
static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
|
||||
int error) {}
|
||||
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
|
||||
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
|
||||
int status) {}
|
||||
|
||||
#endif /* CONFIG_USB_MON */
|
||||
|
||||
@ -454,5 +472,9 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
|
||||
: (in_interrupt () ? "in_interrupt" : "can sleep"))
|
||||
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
/* This rwsem is for use only by the hub driver and ehci-hcd.
|
||||
* Nobody else should touch it.
|
||||
*/
|
||||
extern struct rw_semaphore ehci_cf_port_reset_rwsem;
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
@ -125,6 +125,12 @@ MODULE_PARM_DESC(use_both_schemes,
|
||||
"try the other device initialization scheme if the "
|
||||
"first one fails");
|
||||
|
||||
/* Mutual exclusion for EHCI CF initialization. This interferes with
|
||||
* port reset on some companion controllers.
|
||||
*/
|
||||
DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
|
||||
EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
|
||||
|
||||
|
||||
static inline char *portspeed(int portstatus)
|
||||
{
|
||||
@ -347,11 +353,11 @@ void usb_kick_khubd(struct usb_device *hdev)
|
||||
static void hub_irq(struct urb *urb)
|
||||
{
|
||||
struct usb_hub *hub = urb->context;
|
||||
int status;
|
||||
int status = urb->status;
|
||||
int i;
|
||||
unsigned long bits;
|
||||
|
||||
switch (urb->status) {
|
||||
switch (status) {
|
||||
case -ENOENT: /* synchronous unlink */
|
||||
case -ECONNRESET: /* async unlink */
|
||||
case -ESHUTDOWN: /* hardware going away */
|
||||
@ -359,10 +365,10 @@ static void hub_irq(struct urb *urb)
|
||||
|
||||
default: /* presumably an error */
|
||||
/* Cause a hub reset after 10 consecutive errors */
|
||||
dev_dbg (hub->intfdev, "transfer --> %d\n", urb->status);
|
||||
dev_dbg (hub->intfdev, "transfer --> %d\n", status);
|
||||
if ((++hub->nerrors < 10) || hub->error)
|
||||
goto resubmit;
|
||||
hub->error = urb->status;
|
||||
hub->error = status;
|
||||
/* FALL THROUGH */
|
||||
|
||||
/* let khubd handle things */
|
||||
@ -1220,54 +1226,14 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* usb_new_device - perform initial device setup (usbcore-internal)
|
||||
* usb_configure_device_otg - FIXME (usbcore-internal)
|
||||
* @udev: newly addressed device (in ADDRESS state)
|
||||
*
|
||||
* This is called with devices which have been enumerated, but not yet
|
||||
* configured. The device descriptor is available, but not descriptors
|
||||
* for any device configuration. The caller must have locked either
|
||||
* the parent hub (if udev is a normal device) or else the
|
||||
* usb_bus_list_lock (if udev is a root hub). The parent's pointer to
|
||||
* udev has already been installed, but udev is not yet visible through
|
||||
* sysfs or other filesystem code.
|
||||
*
|
||||
* It will return if the device is configured properly or not. Zero if
|
||||
* the interface was registered with the driver core; else a negative
|
||||
* errno value.
|
||||
*
|
||||
* This call is synchronous, and may not be used in an interrupt context.
|
||||
*
|
||||
* Only the hub driver or root-hub registrar should ever call this.
|
||||
* Do configuration for On-The-Go devices
|
||||
*/
|
||||
int usb_new_device(struct usb_device *udev)
|
||||
static int usb_configure_device_otg(struct usb_device *udev)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Determine quirks */
|
||||
usb_detect_quirks(udev);
|
||||
|
||||
err = usb_get_configuration(udev);
|
||||
if (err < 0) {
|
||||
dev_err(&udev->dev, "can't read configurations, error %d\n",
|
||||
err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* read the standard strings and cache them if present */
|
||||
udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
|
||||
udev->manufacturer = usb_cache_string(udev,
|
||||
udev->descriptor.iManufacturer);
|
||||
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
|
||||
|
||||
/* Tell the world! */
|
||||
dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "
|
||||
"SerialNumber=%d\n",
|
||||
udev->descriptor.iManufacturer,
|
||||
udev->descriptor.iProduct,
|
||||
udev->descriptor.iSerialNumber);
|
||||
show_string(udev, "Product", udev->product);
|
||||
show_string(udev, "Manufacturer", udev->manufacturer);
|
||||
show_string(udev, "SerialNumber", udev->serial);
|
||||
int err = 0;
|
||||
|
||||
#ifdef CONFIG_USB_OTG
|
||||
/*
|
||||
@ -1329,8 +1295,82 @@ int usb_new_device(struct usb_device *udev)
|
||||
err = -ENOTSUPP;
|
||||
goto fail;
|
||||
}
|
||||
fail:
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usb_configure_device - Detect and probe device intfs/otg (usbcore-internal)
|
||||
* @udev: newly addressed device (in ADDRESS state)
|
||||
*
|
||||
* This is only called by usb_new_device() and usb_authorize_device()
|
||||
* and FIXME -- all comments that apply to them apply here wrt to
|
||||
* environment.
|
||||
*
|
||||
* If the device is WUSB and not authorized, we don't attempt to read
|
||||
* the string descriptors, as they will be errored out by the device
|
||||
* until it has been authorized.
|
||||
*/
|
||||
static int usb_configure_device(struct usb_device *udev)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (udev->config == NULL) {
|
||||
err = usb_get_configuration(udev);
|
||||
if (err < 0) {
|
||||
dev_err(&udev->dev, "can't read configurations, error %d\n",
|
||||
err);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (udev->wusb == 1 && udev->authorized == 0) {
|
||||
udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
|
||||
udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
|
||||
udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
|
||||
}
|
||||
else {
|
||||
/* read the standard strings and cache them if present */
|
||||
udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
|
||||
udev->manufacturer = usb_cache_string(udev,
|
||||
udev->descriptor.iManufacturer);
|
||||
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
|
||||
}
|
||||
err = usb_configure_device_otg(udev);
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usb_new_device - perform initial device setup (usbcore-internal)
|
||||
* @udev: newly addressed device (in ADDRESS state)
|
||||
*
|
||||
* This is called with devices which have been enumerated, but not yet
|
||||
* configured. The device descriptor is available, but not descriptors
|
||||
* for any device configuration. The caller must have locked either
|
||||
* the parent hub (if udev is a normal device) or else the
|
||||
* usb_bus_list_lock (if udev is a root hub). The parent's pointer to
|
||||
* udev has already been installed, but udev is not yet visible through
|
||||
* sysfs or other filesystem code.
|
||||
*
|
||||
* It will return if the device is configured properly or not. Zero if
|
||||
* the interface was registered with the driver core; else a negative
|
||||
* errno value.
|
||||
*
|
||||
* This call is synchronous, and may not be used in an interrupt context.
|
||||
*
|
||||
* Only the hub driver or root-hub registrar should ever call this.
|
||||
*/
|
||||
int usb_new_device(struct usb_device *udev)
|
||||
{
|
||||
int err;
|
||||
|
||||
usb_detect_quirks(udev); /* Determine quirks */
|
||||
err = usb_configure_device(udev); /* detect & probe dev/intfs */
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
/* export the usbdev device-node for libusb */
|
||||
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
|
||||
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
|
||||
@ -1346,19 +1386,106 @@ int usb_new_device(struct usb_device *udev)
|
||||
err = device_add(&udev->dev);
|
||||
if (err) {
|
||||
dev_err(&udev->dev, "can't device_add, error %d\n", err);
|
||||
if (udev->parent)
|
||||
usb_autosuspend_device(udev->parent);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
exit:
|
||||
/* Tell the world! */
|
||||
dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "
|
||||
"SerialNumber=%d\n",
|
||||
udev->descriptor.iManufacturer,
|
||||
udev->descriptor.iProduct,
|
||||
udev->descriptor.iSerialNumber);
|
||||
show_string(udev, "Product", udev->product);
|
||||
show_string(udev, "Manufacturer", udev->manufacturer);
|
||||
show_string(udev, "SerialNumber", udev->serial);
|
||||
return err;
|
||||
|
||||
fail:
|
||||
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
|
||||
goto exit;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Similar to usb_disconnect()
|
||||
*
|
||||
* We share a lock (that we have) with device_del(), so we need to
|
||||
* defer its call.
|
||||
*/
|
||||
int usb_deauthorize_device(struct usb_device *usb_dev)
|
||||
{
|
||||
unsigned cnt;
|
||||
usb_lock_device(usb_dev);
|
||||
if (usb_dev->authorized == 0)
|
||||
goto out_unauthorized;
|
||||
usb_dev->authorized = 0;
|
||||
usb_set_configuration(usb_dev, -1);
|
||||
usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
|
||||
usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
|
||||
usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
|
||||
kfree(usb_dev->config);
|
||||
usb_dev->config = NULL;
|
||||
for (cnt = 0; cnt < usb_dev->descriptor.bNumConfigurations; cnt++)
|
||||
kfree(usb_dev->rawdescriptors[cnt]);
|
||||
usb_dev->descriptor.bNumConfigurations = 0;
|
||||
kfree(usb_dev->rawdescriptors);
|
||||
out_unauthorized:
|
||||
usb_unlock_device(usb_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int usb_authorize_device(struct usb_device *usb_dev)
|
||||
{
|
||||
int result = 0, c;
|
||||
usb_lock_device(usb_dev);
|
||||
if (usb_dev->authorized == 1)
|
||||
goto out_authorized;
|
||||
kfree(usb_dev->product);
|
||||
usb_dev->product = NULL;
|
||||
kfree(usb_dev->manufacturer);
|
||||
usb_dev->manufacturer = NULL;
|
||||
kfree(usb_dev->serial);
|
||||
usb_dev->serial = NULL;
|
||||
result = usb_autoresume_device(usb_dev);
|
||||
if (result < 0) {
|
||||
dev_err(&usb_dev->dev,
|
||||
"can't autoresume for authorization: %d\n", result);
|
||||
goto error_autoresume;
|
||||
}
|
||||
result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
|
||||
if (result < 0) {
|
||||
dev_err(&usb_dev->dev, "can't re-read device descriptor for "
|
||||
"authorization: %d\n", result);
|
||||
goto error_device_descriptor;
|
||||
}
|
||||
usb_dev->authorized = 1;
|
||||
result = usb_configure_device(usb_dev);
|
||||
if (result < 0)
|
||||
goto error_configure;
|
||||
/* Choose and set the configuration. This registers the interfaces
|
||||
* with the driver core and lets interface drivers bind to them.
|
||||
*/
|
||||
c = usb_choose_configuration(usb_dev);
|
||||
if (c >= 0) {
|
||||
result = usb_set_configuration(usb_dev, c);
|
||||
if (result) {
|
||||
dev_err(&usb_dev->dev,
|
||||
"can't set config #%d, error %d\n", c, result);
|
||||
/* This need not be fatal. The user can try to
|
||||
* set other configurations. */
|
||||
}
|
||||
}
|
||||
dev_info(&usb_dev->dev, "authorized to connect\n");
|
||||
error_configure:
|
||||
error_device_descriptor:
|
||||
error_autoresume:
|
||||
out_authorized:
|
||||
usb_unlock_device(usb_dev); // complements locktree
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static int hub_port_status(struct usb_hub *hub, int port1,
|
||||
u16 *status, u16 *change)
|
||||
{
|
||||
@ -1460,6 +1587,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
{
|
||||
int i, status;
|
||||
|
||||
/* Block EHCI CF initialization during the port reset.
|
||||
* Some companion controllers don't like it when they mix.
|
||||
*/
|
||||
down_read(&ehci_cf_port_reset_rwsem);
|
||||
|
||||
/* Reset the port */
|
||||
for (i = 0; i < PORT_RESET_TRIES; i++) {
|
||||
status = set_port_feature(hub->hdev,
|
||||
@ -1481,6 +1613,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
case 0:
|
||||
/* TRSTRCY = 10 ms; plus some extra */
|
||||
msleep(10 + 40);
|
||||
udev->devnum = 0; /* Device now at address 0 */
|
||||
/* FALL THROUGH */
|
||||
case -ENOTCONN:
|
||||
case -ENODEV:
|
||||
@ -1490,7 +1623,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
usb_set_device_state(udev, status
|
||||
? USB_STATE_NOTATTACHED
|
||||
: USB_STATE_DEFAULT);
|
||||
return status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dev_dbg (hub->intfdev,
|
||||
@ -1503,6 +1636,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
"Cannot enable port %i. Maybe the USB cable is bad?\n",
|
||||
port1);
|
||||
|
||||
done:
|
||||
up_read(&ehci_cf_port_reset_rwsem);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1833,14 +1968,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||
struct usb_device *udev;
|
||||
|
||||
udev = hdev->children [port1-1];
|
||||
if (udev && msg.event == PM_EVENT_SUSPEND &&
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
udev->state != USB_STATE_SUSPENDED
|
||||
#else
|
||||
udev->dev.power.power_state.event
|
||||
== PM_EVENT_ON
|
||||
#endif
|
||||
) {
|
||||
if (udev && udev->can_submit) {
|
||||
if (!hdev->auto_pm)
|
||||
dev_dbg(&intf->dev, "port %d nyet suspended\n",
|
||||
port1);
|
||||
@ -1999,26 +2127,27 @@ static void ep0_reinit(struct usb_device *udev)
|
||||
{
|
||||
usb_disable_endpoint(udev, 0 + USB_DIR_IN);
|
||||
usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
|
||||
udev->ep_in[0] = udev->ep_out[0] = &udev->ep0;
|
||||
usb_enable_endpoint(udev, &udev->ep0);
|
||||
}
|
||||
|
||||
#define usb_sndaddr0pipe() (PIPE_CONTROL << 30)
|
||||
#define usb_rcvaddr0pipe() ((PIPE_CONTROL << 30) | USB_DIR_IN)
|
||||
|
||||
static int hub_set_address(struct usb_device *udev)
|
||||
static int hub_set_address(struct usb_device *udev, int devnum)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (udev->devnum == 0)
|
||||
if (devnum <= 1)
|
||||
return -EINVAL;
|
||||
if (udev->state == USB_STATE_ADDRESS)
|
||||
return 0;
|
||||
if (udev->state != USB_STATE_DEFAULT)
|
||||
return -EINVAL;
|
||||
retval = usb_control_msg(udev, usb_sndaddr0pipe(),
|
||||
USB_REQ_SET_ADDRESS, 0, udev->devnum, 0,
|
||||
USB_REQ_SET_ADDRESS, 0, devnum, 0,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (retval == 0) {
|
||||
udev->devnum = devnum; /* Device now using proper address */
|
||||
usb_set_device_state(udev, USB_STATE_ADDRESS);
|
||||
ep0_reinit(udev);
|
||||
}
|
||||
@ -2045,6 +2174,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
unsigned delay = HUB_SHORT_RESET_TIME;
|
||||
enum usb_device_speed oldspeed = udev->speed;
|
||||
char *speed, *type;
|
||||
int devnum = udev->devnum;
|
||||
|
||||
/* root hub ports have a slightly longer reset period
|
||||
* (from USB 2.0 spec, section 7.1.7.5)
|
||||
@ -2074,7 +2204,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
goto fail;
|
||||
}
|
||||
oldspeed = udev->speed;
|
||||
|
||||
|
||||
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
|
||||
* it's fixed size except for full speed devices.
|
||||
* For Wireless USB devices, ep0 max packet is always 512 (tho
|
||||
@ -2115,7 +2245,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
dev_info (&udev->dev,
|
||||
"%s %s speed %sUSB device using %s and address %d\n",
|
||||
(udev->config) ? "reset" : "new", speed, type,
|
||||
udev->bus->controller->driver->name, udev->devnum);
|
||||
udev->bus->controller->driver->name, devnum);
|
||||
|
||||
/* Set up TT records, if needed */
|
||||
if (hdev->tt) {
|
||||
@ -2202,7 +2332,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
}
|
||||
|
||||
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
|
||||
retval = hub_set_address(udev);
|
||||
retval = hub_set_address(udev, devnum);
|
||||
if (retval >= 0)
|
||||
break;
|
||||
msleep(200);
|
||||
@ -2210,7 +2340,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
if (retval < 0) {
|
||||
dev_err(&udev->dev,
|
||||
"device not accepting address %d, error %d\n",
|
||||
udev->devnum, retval);
|
||||
devnum, retval);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -2263,8 +2393,10 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
retval = 0;
|
||||
|
||||
fail:
|
||||
if (retval)
|
||||
if (retval) {
|
||||
hub_port_disable(hub, port1, 0);
|
||||
udev->devnum = devnum; /* for disconnect processing */
|
||||
}
|
||||
mutex_unlock(&usb_address0_mutex);
|
||||
return retval;
|
||||
}
|
||||
@ -2699,9 +2831,9 @@ static void hub_events(void)
|
||||
clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
|
||||
if (hubstatus & HUB_STATUS_LOCAL_POWER)
|
||||
/* FIXME: Is this always true? */
|
||||
hub->limited_power = 0;
|
||||
else
|
||||
hub->limited_power = 1;
|
||||
else
|
||||
hub->limited_power = 0;
|
||||
}
|
||||
if (hubchange & HUB_CHANGE_OVERCURRENT) {
|
||||
dev_dbg (hub_dev, "overcurrent change\n");
|
||||
|
@ -59,8 +59,8 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
|
||||
dev_dbg(&urb->dev->dev,
|
||||
"%s timed out on ep%d%s len=%d/%d\n",
|
||||
current->comm,
|
||||
usb_pipeendpoint(urb->pipe),
|
||||
usb_pipein(urb->pipe) ? "in" : "out",
|
||||
usb_endpoint_num(&urb->ep->desc),
|
||||
usb_urb_dir_in(urb) ? "in" : "out",
|
||||
urb->actual_length,
|
||||
urb->transfer_buffer_length);
|
||||
} else
|
||||
@ -250,7 +250,8 @@ static void sg_clean (struct usb_sg_request *io)
|
||||
io->urbs = NULL;
|
||||
}
|
||||
if (io->dev->dev.dma_mask != NULL)
|
||||
usb_buffer_unmap_sg (io->dev, io->pipe, io->sg, io->nents);
|
||||
usb_buffer_unmap_sg (io->dev, usb_pipein(io->pipe),
|
||||
io->sg, io->nents);
|
||||
io->dev = NULL;
|
||||
}
|
||||
|
||||
@ -278,8 +279,8 @@ static void sg_complete (struct urb *urb)
|
||||
dev_err (io->dev->bus->controller,
|
||||
"dev %s ep%d%s scatterlist error %d/%d\n",
|
||||
io->dev->devpath,
|
||||
usb_pipeendpoint (urb->pipe),
|
||||
usb_pipein (urb->pipe) ? "in" : "out",
|
||||
usb_endpoint_num(&urb->ep->desc),
|
||||
usb_urb_dir_in(urb) ? "in" : "out",
|
||||
status, io->status);
|
||||
// BUG ();
|
||||
}
|
||||
@ -379,7 +380,8 @@ int usb_sg_init (
|
||||
*/
|
||||
dma = (dev->dev.dma_mask != NULL);
|
||||
if (dma)
|
||||
io->entries = usb_buffer_map_sg (dev, pipe, sg, nents);
|
||||
io->entries = usb_buffer_map_sg(dev, usb_pipein(pipe),
|
||||
sg, nents);
|
||||
else
|
||||
io->entries = nents;
|
||||
|
||||
@ -1013,8 +1015,11 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
|
||||
ep = dev->ep_in[epnum];
|
||||
dev->ep_in[epnum] = NULL;
|
||||
}
|
||||
if (ep && dev->bus)
|
||||
usb_hcd_endpoint_disable(dev, ep);
|
||||
if (ep) {
|
||||
ep->enabled = 0;
|
||||
usb_hcd_flush_endpoint(dev, ep);
|
||||
usb_hcd_disable_endpoint(dev, ep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1096,23 +1101,21 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
|
||||
* Resets the endpoint toggle, and sets dev->ep_{in,out} pointers.
|
||||
* For control endpoints, both the input and output sides are handled.
|
||||
*/
|
||||
static void
|
||||
usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
|
||||
void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
|
||||
{
|
||||
unsigned int epaddr = ep->desc.bEndpointAddress;
|
||||
unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
|
||||
int is_control;
|
||||
int epnum = usb_endpoint_num(&ep->desc);
|
||||
int is_out = usb_endpoint_dir_out(&ep->desc);
|
||||
int is_control = usb_endpoint_xfer_control(&ep->desc);
|
||||
|
||||
is_control = ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
|
||||
== USB_ENDPOINT_XFER_CONTROL);
|
||||
if (usb_endpoint_out(epaddr) || is_control) {
|
||||
if (is_out || is_control) {
|
||||
usb_settoggle(dev, epnum, 1, 0);
|
||||
dev->ep_out[epnum] = ep;
|
||||
}
|
||||
if (!usb_endpoint_out(epaddr) || is_control) {
|
||||
if (!is_out || is_control) {
|
||||
usb_settoggle(dev, epnum, 0, 0);
|
||||
dev->ep_in[epnum] = ep;
|
||||
}
|
||||
ep->enabled = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1171,6 +1174,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
||||
struct usb_host_interface *alt;
|
||||
int ret;
|
||||
int manual = 0;
|
||||
int changed;
|
||||
|
||||
if (dev->state == USB_STATE_SUSPENDED)
|
||||
return -EHOSTUNREACH;
|
||||
@ -1210,7 +1214,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
||||
*/
|
||||
|
||||
/* prevent submissions using previous endpoint settings */
|
||||
if (device_is_registered(&iface->dev))
|
||||
changed = (iface->cur_altsetting != alt);
|
||||
if (changed && device_is_registered(&iface->dev))
|
||||
usb_remove_sysfs_intf_files(iface);
|
||||
usb_disable_interface(dev, iface);
|
||||
|
||||
@ -1247,7 +1252,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
||||
* (Likewise, EP0 never "halts" on well designed devices.)
|
||||
*/
|
||||
usb_enable_interface(dev, iface);
|
||||
if (device_is_registered(&iface->dev))
|
||||
if (changed && device_is_registered(&iface->dev))
|
||||
usb_create_sysfs_intf_files(iface);
|
||||
|
||||
return 0;
|
||||
@ -1328,7 +1333,7 @@ int usb_reset_configuration(struct usb_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_release_interface(struct device *dev)
|
||||
static void usb_release_interface(struct device *dev)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_interface_cache *intfc =
|
||||
@ -1481,6 +1486,9 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
|
||||
* channels are available independently; and choosing between open
|
||||
* standard device protocols (like CDC) or proprietary ones.
|
||||
*
|
||||
* Note that a non-authorized device (dev->authorized == 0) will only
|
||||
* be put in unconfigured mode.
|
||||
*
|
||||
* Note that USB has an additional level of device configurability,
|
||||
* associated with interfaces. That configurability is accessed using
|
||||
* usb_set_interface().
|
||||
@ -1502,7 +1510,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
|
||||
struct usb_interface **new_interfaces = NULL;
|
||||
int n, nintf;
|
||||
|
||||
if (configuration == -1)
|
||||
if (dev->authorized == 0 || configuration == -1)
|
||||
configuration = 0;
|
||||
else {
|
||||
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
|
||||
|
@ -32,52 +32,6 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
{ USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
/* HP 5300/5370C scanner */
|
||||
{ USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 },
|
||||
/* Hewlett-Packard PhotoSmart 720 / PhotoSmart 935 (storage) */
|
||||
{ USB_DEVICE(0x03f0, 0x4002), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* SGS Thomson Microelectronics 4in1 card reader */
|
||||
{ USB_DEVICE(0x0483, 0x0321), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* Acer Peripherals Inc. (now BenQ Corp.) Prisa 640BU */
|
||||
{ USB_DEVICE(0x04a5, 0x207e), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Benq S2W 3300U */
|
||||
{ USB_DEVICE(0x04a5, 0x20b0), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Canon, Inc. CanoScan N1240U/LiDE30 */
|
||||
{ USB_DEVICE(0x04a9, 0x220e), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Canon, Inc. CanoScan N650U/N656U */
|
||||
{ USB_DEVICE(0x04a9, 0x2206), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Canon, Inc. CanoScan 1220U */
|
||||
{ USB_DEVICE(0x04a9, 0x2207), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Canon, Inc. CanoScan N670U/N676U/LiDE 20 */
|
||||
{ USB_DEVICE(0x04a9, 0x220d), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* old Cannon scanner */
|
||||
{ USB_DEVICE(0x04a9, 0x2220), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Seiko Epson Corp. Perfection 1200 */
|
||||
{ USB_DEVICE(0x04b8, 0x0104), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Seiko Epson Corp. Perfection 660 */
|
||||
{ USB_DEVICE(0x04b8, 0x0114), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Epson Perfection 1260 Photo */
|
||||
{ USB_DEVICE(0x04b8, 0x011d), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Seiko Epson Corp - Perfection 1670 */
|
||||
{ USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* EPSON Perfection 2480 */
|
||||
{ USB_DEVICE(0x04b8, 0x0121), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Seiko Epson Corp.*/
|
||||
{ USB_DEVICE(0x04b8, 0x0122), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Samsung ML-2010 printer */
|
||||
{ USB_DEVICE(0x04e8, 0x326c), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Samsung ML-2510 Series printer */
|
||||
{ USB_DEVICE(0x04e8, 0x327e), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Elsa MicroLink 56k (V.250) */
|
||||
{ USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Ultima Electronics Corp.*/
|
||||
{ USB_DEVICE(0x05d8, 0x4005), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* Genesys USB-to-IDE */
|
||||
{ USB_DEVICE(0x0503, 0x0702), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* USB Graphical LCD - EEH Datalink GmbH */
|
||||
{ USB_DEVICE(0x060c, 0x04eb), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* INTEL VALUE SSD */
|
||||
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
@ -85,44 +39,15 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* M-Systems Flash Disk Pioneers */
|
||||
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Agfa Snapscan1212u */
|
||||
{ USB_DEVICE(0x06bd, 0x2061), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Seagate RSS LLC */
|
||||
{ USB_DEVICE(0x0bc2, 0x3000), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Umax [hex] Astra 3400U */
|
||||
{ USB_DEVICE(0x1606, 0x0060), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* Philips PSC805 audio device */
|
||||
{ USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Alcor multi-card reader */
|
||||
{ USB_DEVICE(0x058f, 0x6366), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* Canon EOS 5D in PC Connection mode */
|
||||
{ USB_DEVICE(0x04a9, 0x3101), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* RIM Blackberry */
|
||||
{ USB_DEVICE(0x0fca, 0x0001), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
{ USB_DEVICE(0x0fca, 0x0004), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
{ USB_DEVICE(0x0fca, 0x0006), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* Apple iPhone */
|
||||
{ USB_DEVICE(0x05ac, 0x1290), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
/* SKYMEDI USB_DRIVE */
|
||||
{ USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
{ } /* terminating entry must be last */
|
||||
};
|
||||
|
||||
static void usb_autosuspend_quirk(struct usb_device *udev)
|
||||
{
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
/* disable autosuspend, but allow the user to re-enable it via sysfs */
|
||||
udev->autosuspend_disabled = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct usb_device_id *find_id(struct usb_device *udev)
|
||||
{
|
||||
const struct usb_device_id *id = usb_quirk_list;
|
||||
@ -149,13 +74,9 @@ void usb_detect_quirks(struct usb_device *udev)
|
||||
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
|
||||
udev->quirks);
|
||||
|
||||
/* do any special quirk handling here if needed */
|
||||
if (udev->quirks & USB_QUIRK_NO_AUTOSUSPEND)
|
||||
usb_autosuspend_quirk(udev);
|
||||
|
||||
/* By default, disable autosuspend for all non-hubs */
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
if (udev->descriptor.bDeviceClass != USB_CLASS_HUB)
|
||||
udev->autosuspend_delay = -1;
|
||||
udev->autosuspend_disabled = 1;
|
||||
#endif
|
||||
}
|
||||
|
@ -169,6 +169,16 @@ show_quirks(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
}
|
||||
static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
|
||||
|
||||
static ssize_t
|
||||
show_urbnum(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
return sprintf(buf, "%d\n", atomic_read(&udev->urbnum));
|
||||
}
|
||||
static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL);
|
||||
|
||||
|
||||
#if defined(CONFIG_USB_PERSIST) || defined(CONFIG_USB_SUSPEND)
|
||||
static const char power_group[] = "power";
|
||||
@ -413,6 +423,44 @@ usb_descriptor_attr(bDeviceProtocol, "%02x\n")
|
||||
usb_descriptor_attr(bNumConfigurations, "%d\n")
|
||||
usb_descriptor_attr(bMaxPacketSize0, "%d\n")
|
||||
|
||||
|
||||
|
||||
/* show if the device is authorized (1) or not (0) */
|
||||
static ssize_t usb_dev_authorized_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_device *usb_dev = to_usb_device(dev);
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", usb_dev->authorized);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Authorize a device to be used in the system
|
||||
*
|
||||
* Writing a 0 deauthorizes the device, writing a 1 authorizes it.
|
||||
*/
|
||||
static ssize_t usb_dev_authorized_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
struct usb_device *usb_dev = to_usb_device(dev);
|
||||
unsigned val;
|
||||
result = sscanf(buf, "%u\n", &val);
|
||||
if (result != 1)
|
||||
result = -EINVAL;
|
||||
else if (val == 0)
|
||||
result = usb_deauthorize_device(usb_dev);
|
||||
else
|
||||
result = usb_authorize_device(usb_dev);
|
||||
return result < 0? result : size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(authorized, 0644,
|
||||
usb_dev_authorized_show, usb_dev_authorized_store);
|
||||
|
||||
|
||||
static struct attribute *dev_attrs[] = {
|
||||
/* current configuration's attributes */
|
||||
&dev_attr_configuration.attr,
|
||||
@ -420,6 +468,7 @@ static struct attribute *dev_attrs[] = {
|
||||
&dev_attr_bConfigurationValue.attr,
|
||||
&dev_attr_bmAttributes.attr,
|
||||
&dev_attr_bMaxPower.attr,
|
||||
&dev_attr_urbnum.attr,
|
||||
/* device attributes */
|
||||
&dev_attr_idVendor.attr,
|
||||
&dev_attr_idProduct.attr,
|
||||
@ -435,6 +484,7 @@ static struct attribute *dev_attrs[] = {
|
||||
&dev_attr_version.attr,
|
||||
&dev_attr_maxchild.attr,
|
||||
&dev_attr_quirks.attr,
|
||||
&dev_attr_authorized.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group dev_attr_grp = {
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
#include "hcd.h"
|
||||
@ -38,7 +39,6 @@ void usb_init_urb(struct urb *urb)
|
||||
if (urb) {
|
||||
memset(urb, 0, sizeof(*urb));
|
||||
kref_init(&urb->kref);
|
||||
spin_lock_init(&urb->lock);
|
||||
INIT_LIST_HEAD(&urb->anchor_list);
|
||||
}
|
||||
}
|
||||
@ -277,44 +277,58 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
|
||||
*/
|
||||
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
{
|
||||
int pipe, temp, max;
|
||||
struct usb_device *dev;
|
||||
int is_out;
|
||||
int xfertype, max;
|
||||
struct usb_device *dev;
|
||||
struct usb_host_endpoint *ep;
|
||||
int is_out;
|
||||
|
||||
if (!urb || urb->hcpriv || !urb->complete)
|
||||
return -EINVAL;
|
||||
if (!(dev = urb->dev) ||
|
||||
(dev->state < USB_STATE_DEFAULT) ||
|
||||
(!dev->bus) || (dev->devnum <= 0))
|
||||
if (!(dev = urb->dev) || dev->state < USB_STATE_DEFAULT)
|
||||
return -ENODEV;
|
||||
if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
|
||||
|| dev->state == USB_STATE_SUSPENDED)
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
/* For now, get the endpoint from the pipe. Eventually drivers
|
||||
* will be required to set urb->ep directly and we will eliminate
|
||||
* urb->pipe.
|
||||
*/
|
||||
ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)
|
||||
[usb_pipeendpoint(urb->pipe)];
|
||||
if (!ep)
|
||||
return -ENOENT;
|
||||
|
||||
urb->ep = ep;
|
||||
urb->status = -EINPROGRESS;
|
||||
urb->actual_length = 0;
|
||||
|
||||
/* Lots of sanity checks, so HCDs can rely on clean data
|
||||
* and don't need to duplicate tests
|
||||
*/
|
||||
pipe = urb->pipe;
|
||||
temp = usb_pipetype(pipe);
|
||||
is_out = usb_pipeout(pipe);
|
||||
xfertype = usb_endpoint_type(&ep->desc);
|
||||
if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
|
||||
struct usb_ctrlrequest *setup =
|
||||
(struct usb_ctrlrequest *) urb->setup_packet;
|
||||
|
||||
if (!usb_pipecontrol(pipe) && dev->state < USB_STATE_CONFIGURED)
|
||||
if (!setup)
|
||||
return -ENOEXEC;
|
||||
is_out = !(setup->bRequestType & USB_DIR_IN) ||
|
||||
!setup->wLength;
|
||||
} else {
|
||||
is_out = usb_endpoint_dir_out(&ep->desc);
|
||||
}
|
||||
|
||||
/* Cache the direction for later use */
|
||||
urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) |
|
||||
(is_out ? URB_DIR_OUT : URB_DIR_IN);
|
||||
|
||||
if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
|
||||
dev->state < USB_STATE_CONFIGURED)
|
||||
return -ENODEV;
|
||||
|
||||
/* FIXME there should be a sharable lock protecting us against
|
||||
* config/altsetting changes and disconnects, kicking in here.
|
||||
* (here == before maxpacket, and eventually endpoint type,
|
||||
* checks get made.)
|
||||
*/
|
||||
|
||||
max = usb_maxpacket(dev, pipe, is_out);
|
||||
max = le16_to_cpu(ep->desc.wMaxPacketSize);
|
||||
if (max <= 0) {
|
||||
dev_dbg(&dev->dev,
|
||||
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
|
||||
usb_pipeendpoint(pipe), is_out ? "out" : "in",
|
||||
usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
|
||||
__FUNCTION__, max);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
@ -323,7 +337,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
* but drivers only control those sizes for ISO.
|
||||
* while we're checking, initialize return status.
|
||||
*/
|
||||
if (temp == PIPE_ISOCHRONOUS) {
|
||||
if (xfertype == USB_ENDPOINT_XFER_ISOC) {
|
||||
int n, len;
|
||||
|
||||
/* "high bandwidth" mode, 1-3 packets/uframe? */
|
||||
@ -358,20 +372,20 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
|
||||
/* enforce simple/standard policy */
|
||||
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
|
||||
URB_NO_INTERRUPT);
|
||||
switch (temp) {
|
||||
case PIPE_BULK:
|
||||
URB_NO_INTERRUPT | URB_DIR_MASK);
|
||||
switch (xfertype) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (is_out)
|
||||
allowed |= URB_ZERO_PACKET;
|
||||
/* FALLTHROUGH */
|
||||
case PIPE_CONTROL:
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
allowed |= URB_NO_FSBR; /* only affects UHCI */
|
||||
/* FALLTHROUGH */
|
||||
default: /* all non-iso endpoints */
|
||||
if (!is_out)
|
||||
allowed |= URB_SHORT_NOT_OK;
|
||||
break;
|
||||
case PIPE_ISOCHRONOUS:
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
allowed |= URB_ISO_ASAP;
|
||||
break;
|
||||
}
|
||||
@ -393,9 +407,9 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
* supports different values... this uses EHCI/UHCI defaults (and
|
||||
* EHCI can use smaller non-default values).
|
||||
*/
|
||||
switch (temp) {
|
||||
case PIPE_ISOCHRONOUS:
|
||||
case PIPE_INTERRUPT:
|
||||
switch (xfertype) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
/* too small? */
|
||||
if (urb->interval <= 0)
|
||||
return -EINVAL;
|
||||
@ -405,29 +419,27 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
// NOTE usb handles 2^15
|
||||
if (urb->interval > (1024 * 8))
|
||||
urb->interval = 1024 * 8;
|
||||
temp = 1024 * 8;
|
||||
max = 1024 * 8;
|
||||
break;
|
||||
case USB_SPEED_FULL: /* units are frames/msec */
|
||||
case USB_SPEED_LOW:
|
||||
if (temp == PIPE_INTERRUPT) {
|
||||
if (xfertype == USB_ENDPOINT_XFER_INT) {
|
||||
if (urb->interval > 255)
|
||||
return -EINVAL;
|
||||
// NOTE ohci only handles up to 32
|
||||
temp = 128;
|
||||
max = 128;
|
||||
} else {
|
||||
if (urb->interval > 1024)
|
||||
urb->interval = 1024;
|
||||
// NOTE usb and ohci handle up to 2^15
|
||||
temp = 1024;
|
||||
max = 1024;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
/* power of two? */
|
||||
while (temp > urb->interval)
|
||||
temp >>= 1;
|
||||
urb->interval = temp;
|
||||
/* Round down to a power of 2, no more than max */
|
||||
urb->interval = min(max, 1 << ilog2(urb->interval));
|
||||
}
|
||||
|
||||
return usb_hcd_submit_urb(urb, mem_flags);
|
||||
@ -496,8 +508,10 @@ int usb_unlink_urb(struct urb *urb)
|
||||
{
|
||||
if (!urb)
|
||||
return -EINVAL;
|
||||
if (!(urb->dev && urb->dev->bus))
|
||||
if (!urb->dev)
|
||||
return -ENODEV;
|
||||
if (!urb->ep)
|
||||
return -EIDRM;
|
||||
return usb_hcd_unlink_urb(urb, -ECONNRESET);
|
||||
}
|
||||
|
||||
@ -523,19 +537,21 @@ int usb_unlink_urb(struct urb *urb)
|
||||
*/
|
||||
void usb_kill_urb(struct urb *urb)
|
||||
{
|
||||
static DEFINE_MUTEX(reject_mutex);
|
||||
|
||||
might_sleep();
|
||||
if (!(urb && urb->dev && urb->dev->bus))
|
||||
if (!(urb && urb->dev && urb->ep))
|
||||
return;
|
||||
spin_lock_irq(&urb->lock);
|
||||
mutex_lock(&reject_mutex);
|
||||
++urb->reject;
|
||||
spin_unlock_irq(&urb->lock);
|
||||
mutex_unlock(&reject_mutex);
|
||||
|
||||
usb_hcd_unlink_urb(urb, -ENOENT);
|
||||
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
|
||||
|
||||
spin_lock_irq(&urb->lock);
|
||||
mutex_lock(&reject_mutex);
|
||||
--urb->reject;
|
||||
spin_unlock_irq(&urb->lock);
|
||||
mutex_unlock(&reject_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,6 +223,15 @@ static void ksuspend_usb_cleanup(void)
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
||||
/* Returns 1 if @usb_bus is WUSB, 0 otherwise */
|
||||
static unsigned usb_bus_is_wusb(struct usb_bus *bus)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of(bus, struct usb_hcd, self);
|
||||
return hcd->wireless;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usb_alloc_dev - usb device constructor (usbcore-internal)
|
||||
* @parent: hub to which device is connected; null to allocate a root hub
|
||||
@ -239,6 +248,8 @@ struct usb_device *
|
||||
usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
|
||||
{
|
||||
struct usb_device *dev;
|
||||
struct usb_hcd *usb_hcd = container_of(bus, struct usb_hcd, self);
|
||||
unsigned root_hub = 0;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
@ -255,12 +266,14 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
|
||||
dev->dev.dma_mask = bus->controller->dma_mask;
|
||||
set_dev_node(&dev->dev, dev_to_node(bus->controller));
|
||||
dev->state = USB_STATE_ATTACHED;
|
||||
atomic_set(&dev->urbnum, 0);
|
||||
|
||||
INIT_LIST_HEAD(&dev->ep0.urb_list);
|
||||
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
|
||||
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
|
||||
/* ep0 maxpacket comes later, from device descriptor */
|
||||
dev->ep_in[0] = dev->ep_out[0] = &dev->ep0;
|
||||
usb_enable_endpoint(dev, &dev->ep0);
|
||||
dev->can_submit = 1;
|
||||
|
||||
/* Save readable and stable topology id, distinguishing devices
|
||||
* by location for diagnostics, tools, driver model, etc. The
|
||||
@ -275,6 +288,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
|
||||
|
||||
dev->dev.parent = bus->controller;
|
||||
sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum);
|
||||
root_hub = 1;
|
||||
} else {
|
||||
/* match any labeling on the hubs; it's one-based */
|
||||
if (parent->devpath[0] == '0')
|
||||
@ -301,6 +315,12 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
|
||||
INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
|
||||
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
|
||||
#endif
|
||||
if (root_hub) /* Root hub always ok [and always wired] */
|
||||
dev->authorized = 1;
|
||||
else {
|
||||
dev->authorized = usb_hcd->authorized_default;
|
||||
dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
@ -748,7 +768,7 @@ void usb_buffer_unmap(struct urb *urb)
|
||||
/**
|
||||
* usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
|
||||
* @dev: device to which the scatterlist will be mapped
|
||||
* @pipe: endpoint defining the mapping direction
|
||||
* @is_in: mapping transfer direction
|
||||
* @sg: the scatterlist to map
|
||||
* @nents: the number of entries in the scatterlist
|
||||
*
|
||||
@ -771,14 +791,13 @@ void usb_buffer_unmap(struct urb *urb)
|
||||
*
|
||||
* Reverse the effect of this call with usb_buffer_unmap_sg().
|
||||
*/
|
||||
int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
|
||||
int usb_buffer_map_sg(const struct usb_device *dev, int is_in,
|
||||
struct scatterlist *sg, int nents)
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
struct device *controller;
|
||||
|
||||
if (!dev
|
||||
|| usb_pipecontrol(pipe)
|
||||
|| !(bus = dev->bus)
|
||||
|| !(controller = bus->controller)
|
||||
|| !controller->dma_mask)
|
||||
@ -786,7 +805,7 @@ int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
|
||||
|
||||
// FIXME generic api broken like pci, can't report errors
|
||||
return dma_map_sg(controller, sg, nents,
|
||||
usb_pipein(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
/* XXX DISABLED, no users currently. If you wish to re-enable this
|
||||
@ -799,14 +818,14 @@ int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
|
||||
/**
|
||||
* usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)
|
||||
* @dev: device to which the scatterlist will be mapped
|
||||
* @pipe: endpoint defining the mapping direction
|
||||
* @is_in: mapping transfer direction
|
||||
* @sg: the scatterlist to synchronize
|
||||
* @n_hw_ents: the positive return value from usb_buffer_map_sg
|
||||
*
|
||||
* Use this when you are re-using a scatterlist's data buffers for
|
||||
* another USB request.
|
||||
*/
|
||||
void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
|
||||
void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
|
||||
struct scatterlist *sg, int n_hw_ents)
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
@ -819,20 +838,20 @@ void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
|
||||
return;
|
||||
|
||||
dma_sync_sg(controller, sg, n_hw_ents,
|
||||
usb_pipein(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
|
||||
* @dev: device to which the scatterlist will be mapped
|
||||
* @pipe: endpoint defining the mapping direction
|
||||
* @is_in: mapping transfer direction
|
||||
* @sg: the scatterlist to unmap
|
||||
* @n_hw_ents: the positive return value from usb_buffer_map_sg
|
||||
*
|
||||
* Reverses the effect of usb_buffer_map_sg().
|
||||
*/
|
||||
void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
|
||||
void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
|
||||
struct scatterlist *sg, int n_hw_ents)
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
@ -845,7 +864,7 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
|
||||
return;
|
||||
|
||||
dma_unmap_sg(controller, sg, n_hw_ents,
|
||||
usb_pipein(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
/* format to disable USB on kernel command line is: nousb */
|
||||
|
@ -8,17 +8,22 @@ extern int usb_create_ep_files(struct device *parent, struct usb_host_endpoint *
|
||||
struct usb_device *udev);
|
||||
extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
|
||||
|
||||
extern void usb_enable_endpoint(struct usb_device *dev,
|
||||
struct usb_host_endpoint *ep);
|
||||
extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr);
|
||||
extern void usb_disable_interface (struct usb_device *dev,
|
||||
struct usb_interface *intf);
|
||||
extern void usb_release_interface_cache(struct kref *ref);
|
||||
extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
|
||||
extern int usb_deauthorize_device (struct usb_device *);
|
||||
extern int usb_authorize_device (struct usb_device *);
|
||||
extern void usb_detect_quirks(struct usb_device *udev);
|
||||
|
||||
extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||
unsigned int size);
|
||||
extern char *usb_cache_string(struct usb_device *udev, int index);
|
||||
extern int usb_set_configuration(struct usb_device *dev, int configuration);
|
||||
extern int usb_choose_configuration(struct usb_device *udev);
|
||||
|
||||
extern void usb_kick_khubd(struct usb_device *dev);
|
||||
extern int usb_match_device(struct usb_device *dev,
|
||||
|
@ -67,6 +67,17 @@ config USB_GADGET_DEBUG_FILES
|
||||
driver on a new board. Enable these files by choosing "Y"
|
||||
here. If in doubt, or to conserve kernel memory, say "N".
|
||||
|
||||
config USB_GADGET_DEBUG_FS
|
||||
boolean "Debugging information files in debugfs"
|
||||
depends on USB_GADGET && DEBUG_FS
|
||||
help
|
||||
Some of the drivers in the "gadget" framework can expose
|
||||
debugging information in files under /sys/kernel/debug/.
|
||||
The information in these files may help when you're
|
||||
troubleshooting or bringing up a driver on a new board.
|
||||
Enable these files by choosing "Y" here. If in doubt, or
|
||||
to conserve kernel memory, say "N".
|
||||
|
||||
config USB_GADGET_SELECTED
|
||||
boolean
|
||||
|
||||
@ -103,6 +114,20 @@ config USB_AMD5536UDC
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_ATMEL_USBA
|
||||
boolean "Atmel USBA"
|
||||
select USB_GADGET_DUALSPEED
|
||||
depends on AVR32
|
||||
help
|
||||
USBA is the integrated high-speed USB Device controller on
|
||||
the AT32AP700x processors from Atmel.
|
||||
|
||||
config USB_ATMEL_USBA
|
||||
tristate
|
||||
depends on USB_GADGET_ATMEL_USBA
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_FSL_USB2
|
||||
boolean "Freescale Highspeed USB DR Peripheral Controller"
|
||||
depends on MPC834x || PPC_MPC831x
|
||||
@ -228,7 +253,6 @@ config USB_LH7A40X
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
|
||||
config USB_GADGET_OMAP
|
||||
boolean "OMAP USB Device Controller"
|
||||
depends on ARCH_OMAP
|
||||
|
@ -14,6 +14,7 @@ obj-$(CONFIG_USB_OMAP) += omap_udc.o
|
||||
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
|
||||
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
|
||||
obj-$(CONFIG_USB_AT91) += at91_udc.o
|
||||
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
|
||||
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
|
||||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
|
||||
|
@ -69,7 +69,7 @@
|
||||
|
||||
/* gadget stack */
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* udc specific */
|
||||
#include "amd5536udc.h"
|
||||
@ -3244,7 +3244,6 @@ static int udc_pci_probe(
|
||||
retval = -ENOMEM;
|
||||
goto finished;
|
||||
}
|
||||
memset(dev, 0, sizeof(struct udc));
|
||||
|
||||
/* pci setup */
|
||||
if (pci_enable_device(pdev) < 0) {
|
||||
@ -3286,14 +3285,12 @@ static int udc_pci_probe(
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
/* chip revision */
|
||||
dev->chiprev = 0;
|
||||
/* chip revision for Hs AMD5536 */
|
||||
dev->chiprev = pdev->revision;
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_set_mwi(pdev);
|
||||
|
||||
/* chip rev for Hs AMD5536 */
|
||||
pci_read_config_byte(pdev, PCI_REVISION_ID, (u8 *) &dev->chiprev);
|
||||
/* init dma pools */
|
||||
if (use_dma) {
|
||||
retval = init_dma_pools(dev);
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/hardware.h>
|
||||
|
2077
drivers/usb/gadget/atmel_usba_udc.c
Normal file
2077
drivers/usb/gadget/atmel_usba_udc.c
Normal file
File diff suppressed because it is too large
Load Diff
352
drivers/usb/gadget/atmel_usba_udc.h
Normal file
352
drivers/usb/gadget/atmel_usba_udc.h
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Driver for the Atmel USBA high speed USB device controller
|
||||
*
|
||||
* Copyright (C) 2005-2007 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef __LINUX_USB_GADGET_USBA_UDC_H__
|
||||
#define __LINUX_USB_GADGET_USBA_UDC_H__
|
||||
|
||||
/* USB register offsets */
|
||||
#define USBA_CTRL 0x0000
|
||||
#define USBA_FNUM 0x0004
|
||||
#define USBA_INT_ENB 0x0010
|
||||
#define USBA_INT_STA 0x0014
|
||||
#define USBA_INT_CLR 0x0018
|
||||
#define USBA_EPT_RST 0x001c
|
||||
#define USBA_TST 0x00e0
|
||||
|
||||
/* USB endpoint register offsets */
|
||||
#define USBA_EPT_CFG 0x0000
|
||||
#define USBA_EPT_CTL_ENB 0x0004
|
||||
#define USBA_EPT_CTL_DIS 0x0008
|
||||
#define USBA_EPT_CTL 0x000c
|
||||
#define USBA_EPT_SET_STA 0x0014
|
||||
#define USBA_EPT_CLR_STA 0x0018
|
||||
#define USBA_EPT_STA 0x001c
|
||||
|
||||
/* USB DMA register offsets */
|
||||
#define USBA_DMA_NXT_DSC 0x0000
|
||||
#define USBA_DMA_ADDRESS 0x0004
|
||||
#define USBA_DMA_CONTROL 0x0008
|
||||
#define USBA_DMA_STATUS 0x000c
|
||||
|
||||
/* Bitfields in CTRL */
|
||||
#define USBA_DEV_ADDR_OFFSET 0
|
||||
#define USBA_DEV_ADDR_SIZE 7
|
||||
#define USBA_FADDR_EN (1 << 7)
|
||||
#define USBA_EN_USBA (1 << 8)
|
||||
#define USBA_DETACH (1 << 9)
|
||||
#define USBA_REMOTE_WAKE_UP (1 << 10)
|
||||
|
||||
/* Bitfields in FNUM */
|
||||
#define USBA_MICRO_FRAME_NUM_OFFSET 0
|
||||
#define USBA_MICRO_FRAME_NUM_SIZE 3
|
||||
#define USBA_FRAME_NUMBER_OFFSET 3
|
||||
#define USBA_FRAME_NUMBER_SIZE 11
|
||||
#define USBA_FRAME_NUM_ERROR (1 << 31)
|
||||
|
||||
/* Bitfields in INT_ENB/INT_STA/INT_CLR */
|
||||
#define USBA_HIGH_SPEED (1 << 0)
|
||||
#define USBA_DET_SUSPEND (1 << 1)
|
||||
#define USBA_MICRO_SOF (1 << 2)
|
||||
#define USBA_SOF (1 << 3)
|
||||
#define USBA_END_OF_RESET (1 << 4)
|
||||
#define USBA_WAKE_UP (1 << 5)
|
||||
#define USBA_END_OF_RESUME (1 << 6)
|
||||
#define USBA_UPSTREAM_RESUME (1 << 7)
|
||||
#define USBA_EPT_INT_OFFSET 8
|
||||
#define USBA_EPT_INT_SIZE 16
|
||||
#define USBA_DMA_INT_OFFSET 24
|
||||
#define USBA_DMA_INT_SIZE 8
|
||||
|
||||
/* Bitfields in EPT_RST */
|
||||
#define USBA_RST_OFFSET 0
|
||||
#define USBA_RST_SIZE 16
|
||||
|
||||
/* Bitfields in USBA_TST */
|
||||
#define USBA_SPEED_CFG_OFFSET 0
|
||||
#define USBA_SPEED_CFG_SIZE 2
|
||||
#define USBA_TST_J_MODE (1 << 2)
|
||||
#define USBA_TST_K_MODE (1 << 3)
|
||||
#define USBA_TST_PKT_MODE (1 << 4)
|
||||
#define USBA_OPMODE2 (1 << 5)
|
||||
|
||||
/* Bitfields in EPT_CFG */
|
||||
#define USBA_EPT_SIZE_OFFSET 0
|
||||
#define USBA_EPT_SIZE_SIZE 3
|
||||
#define USBA_EPT_DIR_IN (1 << 3)
|
||||
#define USBA_EPT_TYPE_OFFSET 4
|
||||
#define USBA_EPT_TYPE_SIZE 2
|
||||
#define USBA_BK_NUMBER_OFFSET 6
|
||||
#define USBA_BK_NUMBER_SIZE 2
|
||||
#define USBA_NB_TRANS_OFFSET 8
|
||||
#define USBA_NB_TRANS_SIZE 2
|
||||
#define USBA_EPT_MAPPED (1 << 31)
|
||||
|
||||
/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */
|
||||
#define USBA_EPT_ENABLE (1 << 0)
|
||||
#define USBA_AUTO_VALID (1 << 1)
|
||||
#define USBA_INTDIS_DMA (1 << 3)
|
||||
#define USBA_NYET_DIS (1 << 4)
|
||||
#define USBA_DATAX_RX (1 << 6)
|
||||
#define USBA_MDATA_RX (1 << 7)
|
||||
/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */
|
||||
#define USBA_BUSY_BANK_IE (1 << 18)
|
||||
|
||||
/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */
|
||||
#define USBA_FORCE_STALL (1 << 5)
|
||||
#define USBA_TOGGLE_CLR (1 << 6)
|
||||
#define USBA_TOGGLE_SEQ_OFFSET 6
|
||||
#define USBA_TOGGLE_SEQ_SIZE 2
|
||||
#define USBA_ERR_OVFLW (1 << 8)
|
||||
#define USBA_RX_BK_RDY (1 << 9)
|
||||
#define USBA_KILL_BANK (1 << 9)
|
||||
#define USBA_TX_COMPLETE (1 << 10)
|
||||
#define USBA_TX_PK_RDY (1 << 11)
|
||||
#define USBA_ISO_ERR_TRANS (1 << 11)
|
||||
#define USBA_RX_SETUP (1 << 12)
|
||||
#define USBA_ISO_ERR_FLOW (1 << 12)
|
||||
#define USBA_STALL_SENT (1 << 13)
|
||||
#define USBA_ISO_ERR_CRC (1 << 13)
|
||||
#define USBA_ISO_ERR_NBTRANS (1 << 13)
|
||||
#define USBA_NAK_IN (1 << 14)
|
||||
#define USBA_ISO_ERR_FLUSH (1 << 14)
|
||||
#define USBA_NAK_OUT (1 << 15)
|
||||
#define USBA_CURRENT_BANK_OFFSET 16
|
||||
#define USBA_CURRENT_BANK_SIZE 2
|
||||
#define USBA_BUSY_BANKS_OFFSET 18
|
||||
#define USBA_BUSY_BANKS_SIZE 2
|
||||
#define USBA_BYTE_COUNT_OFFSET 20
|
||||
#define USBA_BYTE_COUNT_SIZE 11
|
||||
#define USBA_SHORT_PACKET (1 << 31)
|
||||
|
||||
/* Bitfields in DMA_CONTROL */
|
||||
#define USBA_DMA_CH_EN (1 << 0)
|
||||
#define USBA_DMA_LINK (1 << 1)
|
||||
#define USBA_DMA_END_TR_EN (1 << 2)
|
||||
#define USBA_DMA_END_BUF_EN (1 << 3)
|
||||
#define USBA_DMA_END_TR_IE (1 << 4)
|
||||
#define USBA_DMA_END_BUF_IE (1 << 5)
|
||||
#define USBA_DMA_DESC_LOAD_IE (1 << 6)
|
||||
#define USBA_DMA_BURST_LOCK (1 << 7)
|
||||
#define USBA_DMA_BUF_LEN_OFFSET 16
|
||||
#define USBA_DMA_BUF_LEN_SIZE 16
|
||||
|
||||
/* Bitfields in DMA_STATUS */
|
||||
#define USBA_DMA_CH_ACTIVE (1 << 1)
|
||||
#define USBA_DMA_END_TR_ST (1 << 4)
|
||||
#define USBA_DMA_END_BUF_ST (1 << 5)
|
||||
#define USBA_DMA_DESC_LOAD_ST (1 << 6)
|
||||
|
||||
/* Constants for SPEED_CFG */
|
||||
#define USBA_SPEED_CFG_NORMAL 0
|
||||
#define USBA_SPEED_CFG_FORCE_HIGH 2
|
||||
#define USBA_SPEED_CFG_FORCE_FULL 3
|
||||
|
||||
/* Constants for EPT_SIZE */
|
||||
#define USBA_EPT_SIZE_8 0
|
||||
#define USBA_EPT_SIZE_16 1
|
||||
#define USBA_EPT_SIZE_32 2
|
||||
#define USBA_EPT_SIZE_64 3
|
||||
#define USBA_EPT_SIZE_128 4
|
||||
#define USBA_EPT_SIZE_256 5
|
||||
#define USBA_EPT_SIZE_512 6
|
||||
#define USBA_EPT_SIZE_1024 7
|
||||
|
||||
/* Constants for EPT_TYPE */
|
||||
#define USBA_EPT_TYPE_CONTROL 0
|
||||
#define USBA_EPT_TYPE_ISO 1
|
||||
#define USBA_EPT_TYPE_BULK 2
|
||||
#define USBA_EPT_TYPE_INT 3
|
||||
|
||||
/* Constants for BK_NUMBER */
|
||||
#define USBA_BK_NUMBER_ZERO 0
|
||||
#define USBA_BK_NUMBER_ONE 1
|
||||
#define USBA_BK_NUMBER_DOUBLE 2
|
||||
#define USBA_BK_NUMBER_TRIPLE 3
|
||||
|
||||
/* Bit manipulation macros */
|
||||
#define USBA_BF(name, value) \
|
||||
(((value) & ((1 << USBA_##name##_SIZE) - 1)) \
|
||||
<< USBA_##name##_OFFSET)
|
||||
#define USBA_BFEXT(name, value) \
|
||||
(((value) >> USBA_##name##_OFFSET) \
|
||||
& ((1 << USBA_##name##_SIZE) - 1))
|
||||
#define USBA_BFINS(name, value, old) \
|
||||
(((old) & ~(((1 << USBA_##name##_SIZE) - 1) \
|
||||
<< USBA_##name##_OFFSET)) \
|
||||
| USBA_BF(name, value))
|
||||
|
||||
/* Register access macros */
|
||||
#define usba_readl(udc, reg) \
|
||||
__raw_readl((udc)->regs + USBA_##reg)
|
||||
#define usba_writel(udc, reg, value) \
|
||||
__raw_writel((value), (udc)->regs + USBA_##reg)
|
||||
#define usba_ep_readl(ep, reg) \
|
||||
__raw_readl((ep)->ep_regs + USBA_EPT_##reg)
|
||||
#define usba_ep_writel(ep, reg, value) \
|
||||
__raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
|
||||
#define usba_dma_readl(ep, reg) \
|
||||
__raw_readl((ep)->dma_regs + USBA_DMA_##reg)
|
||||
#define usba_dma_writel(ep, reg, value) \
|
||||
__raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
|
||||
|
||||
/* Calculate base address for a given endpoint or DMA controller */
|
||||
#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
|
||||
#define USBA_DMA_BASE(x) (0x300 + (x) * 0x10)
|
||||
#define USBA_FIFO_BASE(x) ((x) << 16)
|
||||
|
||||
/* Synth parameters */
|
||||
#define USBA_NR_ENDPOINTS 7
|
||||
|
||||
#define EP0_FIFO_SIZE 64
|
||||
#define EP0_EPT_SIZE USBA_EPT_SIZE_64
|
||||
#define EP0_NR_BANKS 1
|
||||
|
||||
/*
|
||||
* REVISIT: Try to eliminate this value. Can we rely on req->mapped to
|
||||
* provide this information?
|
||||
*/
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
#define FIFO_IOMEM_ID 0
|
||||
#define CTRL_IOMEM_ID 1
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG_ERR 0x0001 /* report all error returns */
|
||||
#define DBG_HW 0x0002 /* debug hardware initialization */
|
||||
#define DBG_GADGET 0x0004 /* calls to/from gadget driver */
|
||||
#define DBG_INT 0x0008 /* interrupts */
|
||||
#define DBG_BUS 0x0010 /* report changes in bus state */
|
||||
#define DBG_QUEUE 0x0020 /* debug request queue processing */
|
||||
#define DBG_FIFO 0x0040 /* debug FIFO contents */
|
||||
#define DBG_DMA 0x0080 /* debug DMA handling */
|
||||
#define DBG_REQ 0x0100 /* print out queued request length */
|
||||
#define DBG_ALL 0xffff
|
||||
#define DBG_NONE 0x0000
|
||||
|
||||
#define DEBUG_LEVEL (DBG_ERR)
|
||||
#define DBG(level, fmt, ...) \
|
||||
do { \
|
||||
if ((level) & DEBUG_LEVEL) \
|
||||
printk(KERN_DEBUG "udc: " fmt, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DBG(level, fmt...)
|
||||
#endif
|
||||
|
||||
enum usba_ctrl_state {
|
||||
WAIT_FOR_SETUP,
|
||||
DATA_STAGE_IN,
|
||||
DATA_STAGE_OUT,
|
||||
STATUS_STAGE_IN,
|
||||
STATUS_STAGE_OUT,
|
||||
STATUS_STAGE_ADDR,
|
||||
STATUS_STAGE_TEST,
|
||||
};
|
||||
/*
|
||||
EP_STATE_IDLE,
|
||||
EP_STATE_SETUP,
|
||||
EP_STATE_IN_DATA,
|
||||
EP_STATE_OUT_DATA,
|
||||
EP_STATE_SET_ADDR_STATUS,
|
||||
EP_STATE_RX_STATUS,
|
||||
EP_STATE_TX_STATUS,
|
||||
EP_STATE_HALT,
|
||||
*/
|
||||
|
||||
struct usba_dma_desc {
|
||||
dma_addr_t next;
|
||||
dma_addr_t addr;
|
||||
u32 ctrl;
|
||||
};
|
||||
|
||||
struct usba_ep {
|
||||
int state;
|
||||
void __iomem *ep_regs;
|
||||
void __iomem *dma_regs;
|
||||
void __iomem *fifo;
|
||||
struct usb_ep ep;
|
||||
struct usba_udc *udc;
|
||||
|
||||
struct list_head queue;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
|
||||
u16 fifo_size;
|
||||
u8 nr_banks;
|
||||
u8 index;
|
||||
unsigned int can_dma:1;
|
||||
unsigned int can_isoc:1;
|
||||
unsigned int is_isoc:1;
|
||||
unsigned int is_in:1;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FS
|
||||
u32 last_dma_status;
|
||||
struct dentry *debugfs_dir;
|
||||
struct dentry *debugfs_queue;
|
||||
struct dentry *debugfs_dma_status;
|
||||
struct dentry *debugfs_state;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct usba_request {
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
|
||||
u32 ctrl;
|
||||
|
||||
unsigned int submitted:1;
|
||||
unsigned int last_transaction:1;
|
||||
unsigned int using_dma:1;
|
||||
unsigned int mapped:1;
|
||||
};
|
||||
|
||||
struct usba_udc {
|
||||
/* Protect hw registers from concurrent modifications */
|
||||
spinlock_t lock;
|
||||
|
||||
void __iomem *regs;
|
||||
void __iomem *fifo;
|
||||
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct platform_device *pdev;
|
||||
int irq;
|
||||
int vbus_pin;
|
||||
struct clk *pclk;
|
||||
struct clk *hclk;
|
||||
|
||||
u16 devstatus;
|
||||
|
||||
u16 test_mode;
|
||||
int vbus_prev;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FS
|
||||
struct dentry *debugfs_root;
|
||||
struct dentry *debugfs_regs;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct usba_ep *to_usba_ep(struct usb_ep *ep)
|
||||
{
|
||||
return container_of(ep, struct usba_ep, ep);
|
||||
}
|
||||
|
||||
static inline struct usba_request *to_usba_req(struct usb_request *req)
|
||||
{
|
||||
return container_of(req, struct usba_request, req);
|
||||
}
|
||||
|
||||
static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget)
|
||||
{
|
||||
return container_of(gadget, struct usba_udc, gadget);
|
||||
}
|
||||
|
||||
#define ep_is_control(ep) ((ep)->index == 0)
|
||||
#define ep_is_idle(ep) ((ep)->state == EP_STATE_IDLE)
|
||||
|
||||
#endif /* __LINUX_USB_GADGET_USBA_UDC_H */
|
@ -25,7 +25,7 @@
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
|
||||
/**
|
||||
|
@ -46,7 +46,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
@ -962,13 +962,13 @@ static struct platform_driver dummy_udc_driver = {
|
||||
|
||||
static int dummy_urb_enqueue (
|
||||
struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *ep,
|
||||
struct urb *urb,
|
||||
gfp_t mem_flags
|
||||
) {
|
||||
struct dummy *dum;
|
||||
struct urbp *urbp;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
if (!urb->transfer_buffer && urb->transfer_buffer_length)
|
||||
return -EINVAL;
|
||||
@ -980,6 +980,11 @@ static int dummy_urb_enqueue (
|
||||
|
||||
dum = hcd_to_dummy (hcd);
|
||||
spin_lock_irqsave (&dum->lock, flags);
|
||||
rc = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (rc) {
|
||||
kfree(urbp);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!dum->udev) {
|
||||
dum->udev = urb->dev;
|
||||
@ -996,36 +1001,35 @@ static int dummy_urb_enqueue (
|
||||
if (!timer_pending (&dum->timer))
|
||||
mod_timer (&dum->timer, jiffies + 1);
|
||||
|
||||
spin_unlock_irqrestore (&dum->lock, flags);
|
||||
return 0;
|
||||
done:
|
||||
spin_unlock_irqrestore(&dum->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
|
||||
static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
struct dummy *dum;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
/* giveback happens automatically in timer callback,
|
||||
* so make sure the callback happens */
|
||||
dum = hcd_to_dummy (hcd);
|
||||
spin_lock_irqsave (&dum->lock, flags);
|
||||
if (dum->rh_state != DUMMY_RH_RUNNING && !list_empty(&dum->urbp_list))
|
||||
mod_timer (&dum->timer, jiffies);
|
||||
spin_unlock_irqrestore (&dum->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void maybe_set_status (struct urb *urb, int status)
|
||||
{
|
||||
spin_lock (&urb->lock);
|
||||
if (urb->status == -EINPROGRESS)
|
||||
urb->status = status;
|
||||
spin_unlock (&urb->lock);
|
||||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (!rc && dum->rh_state != DUMMY_RH_RUNNING &&
|
||||
!list_empty(&dum->urbp_list))
|
||||
mod_timer (&dum->timer, jiffies);
|
||||
|
||||
spin_unlock_irqrestore (&dum->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* transfer up to a frame's worth; caller must own lock */
|
||||
static int
|
||||
transfer (struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit)
|
||||
transfer(struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit,
|
||||
int *status)
|
||||
{
|
||||
struct dummy_request *req;
|
||||
|
||||
@ -1088,24 +1092,20 @@ top:
|
||||
*
|
||||
* partially filling a buffer optionally blocks queue advances
|
||||
* (so completion handlers can clean up the queue) but we don't
|
||||
* need to emulate such data-in-flight. so we only show part
|
||||
* of the URB_SHORT_NOT_OK effect: completion status.
|
||||
* need to emulate such data-in-flight.
|
||||
*/
|
||||
if (is_short) {
|
||||
if (host_len == dev_len) {
|
||||
req->req.status = 0;
|
||||
maybe_set_status (urb, 0);
|
||||
*status = 0;
|
||||
} else if (to_host) {
|
||||
req->req.status = 0;
|
||||
if (dev_len > host_len)
|
||||
maybe_set_status (urb, -EOVERFLOW);
|
||||
*status = -EOVERFLOW;
|
||||
else
|
||||
maybe_set_status (urb,
|
||||
(urb->transfer_flags
|
||||
& URB_SHORT_NOT_OK)
|
||||
? -EREMOTEIO : 0);
|
||||
*status = 0;
|
||||
} else if (!to_host) {
|
||||
maybe_set_status (urb, 0);
|
||||
*status = 0;
|
||||
if (host_len > dev_len)
|
||||
req->req.status = -EOVERFLOW;
|
||||
else
|
||||
@ -1119,9 +1119,8 @@ top:
|
||||
req->req.status = 0;
|
||||
if (urb->transfer_buffer_length == urb->actual_length
|
||||
&& !(urb->transfer_flags
|
||||
& URB_ZERO_PACKET)) {
|
||||
maybe_set_status (urb, 0);
|
||||
}
|
||||
& URB_ZERO_PACKET))
|
||||
*status = 0;
|
||||
}
|
||||
|
||||
/* device side completion --> continuable */
|
||||
@ -1137,7 +1136,7 @@ top:
|
||||
}
|
||||
|
||||
/* host side completion --> terminate */
|
||||
if (urb->status != -EINPROGRESS)
|
||||
if (*status != -EINPROGRESS)
|
||||
break;
|
||||
|
||||
/* rescan to continue with any other queued i/o */
|
||||
@ -1248,12 +1247,12 @@ restart:
|
||||
u8 address;
|
||||
struct dummy_ep *ep = NULL;
|
||||
int type;
|
||||
int status = -EINPROGRESS;
|
||||
|
||||
urb = urbp->urb;
|
||||
if (urb->status != -EINPROGRESS) {
|
||||
/* likely it was just unlinked */
|
||||
if (urb->unlinked)
|
||||
goto return_urb;
|
||||
} else if (dum->rh_state != DUMMY_RH_RUNNING)
|
||||
else if (dum->rh_state != DUMMY_RH_RUNNING)
|
||||
continue;
|
||||
type = usb_pipetype (urb->pipe);
|
||||
|
||||
@ -1274,7 +1273,7 @@ restart:
|
||||
dev_dbg (dummy_dev(dum),
|
||||
"no ep configured for urb %p\n",
|
||||
urb);
|
||||
maybe_set_status (urb, -EPROTO);
|
||||
status = -EPROTO;
|
||||
goto return_urb;
|
||||
}
|
||||
|
||||
@ -1289,7 +1288,7 @@ restart:
|
||||
/* NOTE: must not be iso! */
|
||||
dev_dbg (dummy_dev(dum), "ep %s halted, urb %p\n",
|
||||
ep->ep.name, urb);
|
||||
maybe_set_status (urb, -EPIPE);
|
||||
status = -EPIPE;
|
||||
goto return_urb;
|
||||
}
|
||||
/* FIXME make sure both ends agree on maxpacket */
|
||||
@ -1307,7 +1306,7 @@ restart:
|
||||
w_value = le16_to_cpu(setup.wValue);
|
||||
if (le16_to_cpu(setup.wLength) !=
|
||||
urb->transfer_buffer_length) {
|
||||
maybe_set_status (urb, -EOVERFLOW);
|
||||
status = -EOVERFLOW;
|
||||
goto return_urb;
|
||||
}
|
||||
|
||||
@ -1337,7 +1336,7 @@ restart:
|
||||
if (setup.bRequestType != Dev_Request)
|
||||
break;
|
||||
dum->address = w_value;
|
||||
maybe_set_status (urb, 0);
|
||||
status = 0;
|
||||
dev_dbg (udc_dev(dum), "set_address = %d\n",
|
||||
w_value);
|
||||
value = 0;
|
||||
@ -1364,7 +1363,7 @@ restart:
|
||||
if (value == 0) {
|
||||
dum->devstatus |=
|
||||
(1 << w_value);
|
||||
maybe_set_status (urb, 0);
|
||||
status = 0;
|
||||
}
|
||||
|
||||
} else if (setup.bRequestType == Ep_Request) {
|
||||
@ -1376,7 +1375,7 @@ restart:
|
||||
}
|
||||
ep2->halted = 1;
|
||||
value = 0;
|
||||
maybe_set_status (urb, 0);
|
||||
status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
@ -1386,7 +1385,7 @@ restart:
|
||||
dum->devstatus &= ~(1 <<
|
||||
USB_DEVICE_REMOTE_WAKEUP);
|
||||
value = 0;
|
||||
maybe_set_status (urb, 0);
|
||||
status = 0;
|
||||
break;
|
||||
default:
|
||||
value = -EOPNOTSUPP;
|
||||
@ -1401,7 +1400,7 @@ restart:
|
||||
}
|
||||
ep2->halted = 0;
|
||||
value = 0;
|
||||
maybe_set_status (urb, 0);
|
||||
status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
@ -1438,7 +1437,7 @@ restart:
|
||||
urb->actual_length = min (2,
|
||||
urb->transfer_buffer_length);
|
||||
value = 0;
|
||||
maybe_set_status (urb, 0);
|
||||
status = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1465,7 +1464,7 @@ restart:
|
||||
dev_dbg (udc_dev(dum),
|
||||
"setup --> %d\n",
|
||||
value);
|
||||
maybe_set_status (urb, -EPIPE);
|
||||
status = -EPIPE;
|
||||
urb->actual_length = 0;
|
||||
}
|
||||
|
||||
@ -1482,7 +1481,7 @@ restart:
|
||||
* report random errors, to debug drivers.
|
||||
*/
|
||||
limit = max (limit, periodic_bytes (dum, ep));
|
||||
maybe_set_status (urb, -ENOSYS);
|
||||
status = -ENOSYS;
|
||||
break;
|
||||
|
||||
case PIPE_INTERRUPT:
|
||||
@ -1496,23 +1495,23 @@ restart:
|
||||
default:
|
||||
treat_control_like_bulk:
|
||||
ep->last_io = jiffies;
|
||||
total = transfer (dum, urb, ep, limit);
|
||||
total = transfer(dum, urb, ep, limit, &status);
|
||||
break;
|
||||
}
|
||||
|
||||
/* incomplete transfer? */
|
||||
if (urb->status == -EINPROGRESS)
|
||||
if (status == -EINPROGRESS)
|
||||
continue;
|
||||
|
||||
return_urb:
|
||||
urb->hcpriv = NULL;
|
||||
list_del (&urbp->urbp_list);
|
||||
kfree (urbp);
|
||||
if (ep)
|
||||
ep->already_seen = ep->setup_stage = 0;
|
||||
|
||||
usb_hcd_unlink_urb_from_ep(dummy_to_hcd(dum), urb);
|
||||
spin_unlock (&dum->lock);
|
||||
usb_hcd_giveback_urb (dummy_to_hcd(dum), urb);
|
||||
usb_hcd_giveback_urb(dummy_to_hcd(dum), urb, status);
|
||||
spin_lock (&dum->lock);
|
||||
|
||||
goto restart;
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
|
@ -19,40 +19,18 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
// #define DEBUG 1
|
||||
// #define VERBOSE
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
|
||||
#include <linux/random.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
@ -356,15 +334,15 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR);
|
||||
#define qlen(gadget) \
|
||||
(DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1))
|
||||
|
||||
/* also defer IRQs on highspeed TX */
|
||||
#define TX_DELAY qmult
|
||||
|
||||
static inline int BITRATE(struct usb_gadget *g)
|
||||
{
|
||||
return (g->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS;
|
||||
}
|
||||
|
||||
#else /* full speed (low speed doesn't do bulk) */
|
||||
|
||||
#define qmult 1
|
||||
|
||||
#define DEVSPEED USB_SPEED_FULL
|
||||
|
||||
#define qlen(gadget) DEFAULT_QLEN
|
||||
@ -390,7 +368,7 @@ static inline int BITRATE(struct usb_gadget *g)
|
||||
do { } while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef VERBOSE
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define VDEBUG DEBUG
|
||||
#else
|
||||
#define VDEBUG(dev,fmt,args...) \
|
||||
@ -830,8 +808,6 @@ static const struct usb_descriptor_header *fs_rndis_function [] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
|
||||
/*
|
||||
* usb 2.0 devices need to expose both high speed and full speed
|
||||
* descriptors, unless they only run at full speed.
|
||||
@ -934,18 +910,15 @@ static const struct usb_descriptor_header *hs_rndis_function [] = {
|
||||
|
||||
|
||||
/* maxpacket and other transfer characteristics vary by speed. */
|
||||
#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
|
||||
|
||||
#else
|
||||
|
||||
/* if there's no high speed support, maxpacket doesn't change. */
|
||||
#define ep_desc(g,hs,fs) (((void)(g)), (fs))
|
||||
|
||||
static inline void __init hs_subset_descriptors(void)
|
||||
static inline struct usb_endpoint_descriptor *
|
||||
ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
|
||||
struct usb_endpoint_descriptor *fs)
|
||||
{
|
||||
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
|
||||
return hs;
|
||||
return fs;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_USB_GADGET_DUALSPEED */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -989,22 +962,19 @@ static struct usb_gadget_strings stringtab = {
|
||||
* complications: class descriptors, and an altsetting.
|
||||
*/
|
||||
static int
|
||||
config_buf (enum usb_device_speed speed,
|
||||
u8 *buf, u8 type,
|
||||
unsigned index, int is_otg)
|
||||
config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index, int is_otg)
|
||||
{
|
||||
int len;
|
||||
const struct usb_config_descriptor *config;
|
||||
const struct usb_descriptor_header **function;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
int hs = (speed == USB_SPEED_HIGH);
|
||||
int hs = 0;
|
||||
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
hs = !hs;
|
||||
if (gadget_is_dualspeed(g)) {
|
||||
hs = (g->speed == USB_SPEED_HIGH);
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
hs = !hs;
|
||||
}
|
||||
#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function)
|
||||
#else
|
||||
#define which_fn(t) (fs_ ## t ## _function)
|
||||
#endif
|
||||
|
||||
if (index >= device_desc.bNumConfigurations)
|
||||
return -EINVAL;
|
||||
@ -1217,7 +1187,7 @@ eth_set_config (struct eth_dev *dev, unsigned number, gfp_t gfp_flags)
|
||||
if (number)
|
||||
eth_reset_config (dev);
|
||||
usb_gadget_vbus_draw(dev->gadget,
|
||||
dev->gadget->is_otg ? 8 : 100);
|
||||
gadget_is_otg(dev->gadget) ? 8 : 100);
|
||||
} else {
|
||||
char *speed;
|
||||
unsigned power;
|
||||
@ -1399,24 +1369,22 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
value = min (wLength, (u16) sizeof device_desc);
|
||||
memcpy (req->buf, &device_desc, value);
|
||||
break;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
case USB_DT_DEVICE_QUALIFIER:
|
||||
if (!gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
value = min (wLength, (u16) sizeof dev_qualifier);
|
||||
memcpy (req->buf, &dev_qualifier, value);
|
||||
break;
|
||||
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
if (!gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
// FALLTHROUGH
|
||||
#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
||||
case USB_DT_CONFIG:
|
||||
value = config_buf (gadget->speed, req->buf,
|
||||
value = config_buf(gadget, req->buf,
|
||||
wValue >> 8,
|
||||
wValue & 0xff,
|
||||
gadget->is_otg);
|
||||
gadget_is_otg(gadget));
|
||||
if (value >= 0)
|
||||
value = min (wLength, (u16) value);
|
||||
break;
|
||||
@ -1585,12 +1553,12 @@ done_set_intf:
|
||||
&& rndis_control_intf.bInterfaceNumber
|
||||
== wIndex) {
|
||||
u8 *buf;
|
||||
u32 n;
|
||||
|
||||
/* return the result */
|
||||
buf = rndis_get_next_response (dev->rndis_config,
|
||||
&value);
|
||||
buf = rndis_get_next_response(dev->rndis_config, &n);
|
||||
if (buf) {
|
||||
memcpy (req->buf, buf, value);
|
||||
memcpy(req->buf, buf, n);
|
||||
req->complete = rndis_response_complete;
|
||||
rndis_free_response(dev->rndis_config, buf);
|
||||
}
|
||||
@ -1989,8 +1957,20 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
/*
|
||||
* this freelist can be empty if an interrupt triggered disconnect()
|
||||
* and reconfigured the gadget (shutting down this queue) after the
|
||||
* network stack decided to xmit but before we got the spinlock.
|
||||
*/
|
||||
if (list_empty(&dev->tx_reqs)) {
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
req = container_of (dev->tx_reqs.next, struct usb_request, list);
|
||||
list_del (&req->list);
|
||||
|
||||
/* temporarily stop TX queue when the freelist empties */
|
||||
if (list_empty (&dev->tx_reqs))
|
||||
netif_stop_queue (net);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
@ -2026,12 +2006,11 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
|
||||
|
||||
req->length = length;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
/* throttle highspeed IRQ rate back slightly */
|
||||
req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
|
||||
? ((atomic_read (&dev->tx_qlen) % TX_DELAY) != 0)
|
||||
: 0;
|
||||
#endif
|
||||
if (gadget_is_dualspeed(dev->gadget))
|
||||
req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
|
||||
? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
|
||||
: 0;
|
||||
|
||||
retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);
|
||||
switch (retval) {
|
||||
@ -2188,8 +2167,7 @@ static int eth_stop (struct net_device *net)
|
||||
}
|
||||
|
||||
if (rndis_active(dev)) {
|
||||
rndis_set_param_medium (dev->rndis_config,
|
||||
NDIS_MEDIUM_802_3, 0);
|
||||
rndis_set_param_medium(dev->rndis_config, NDIS_MEDIUM_802_3, 0);
|
||||
(void) rndis_signal_disconnect (dev->rndis_config);
|
||||
}
|
||||
|
||||
@ -2443,26 +2421,28 @@ autoconf_fail:
|
||||
if (rndis)
|
||||
device_desc.bNumConfigurations = 2;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
if (rndis)
|
||||
dev_qualifier.bNumConfigurations = 2;
|
||||
else if (!cdc)
|
||||
dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC;
|
||||
if (gadget_is_dualspeed(gadget)) {
|
||||
if (rndis)
|
||||
dev_qualifier.bNumConfigurations = 2;
|
||||
else if (!cdc)
|
||||
dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC;
|
||||
|
||||
/* assumes ep0 uses the same value for both speeds ... */
|
||||
dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
|
||||
/* assumes ep0 uses the same value for both speeds ... */
|
||||
dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
|
||||
|
||||
/* and that all endpoints are dual-speed */
|
||||
hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
|
||||
hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
|
||||
/* and that all endpoints are dual-speed */
|
||||
hs_source_desc.bEndpointAddress =
|
||||
fs_source_desc.bEndpointAddress;
|
||||
hs_sink_desc.bEndpointAddress =
|
||||
fs_sink_desc.bEndpointAddress;
|
||||
#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
|
||||
if (status_ep)
|
||||
hs_status_desc.bEndpointAddress =
|
||||
fs_status_desc.bEndpointAddress;
|
||||
if (status_ep)
|
||||
hs_status_desc.bEndpointAddress =
|
||||
fs_status_desc.bEndpointAddress;
|
||||
#endif
|
||||
#endif /* DUALSPEED */
|
||||
}
|
||||
|
||||
if (gadget->is_otg) {
|
||||
if (gadget_is_otg(gadget)) {
|
||||
otg_descriptor.bmAttributes |= USB_OTG_HNP,
|
||||
eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
eth_config.bMaxPower = 4;
|
||||
@ -2598,12 +2578,11 @@ fail0:
|
||||
if (rndis_set_param_dev (dev->rndis_config, dev->net,
|
||||
&dev->stats, &dev->cdc_filter))
|
||||
goto fail0;
|
||||
if (rndis_set_param_vendor (dev->rndis_config, vendorID,
|
||||
manufacturer))
|
||||
if (rndis_set_param_vendor(dev->rndis_config, vendorID,
|
||||
manufacturer))
|
||||
goto fail0;
|
||||
if (rndis_set_param_medium (dev->rndis_config,
|
||||
NDIS_MEDIUM_802_3,
|
||||
0))
|
||||
if (rndis_set_param_medium(dev->rndis_config,
|
||||
NDIS_MEDIUM_802_3, 0))
|
||||
goto fail0;
|
||||
INFO (dev, "RNDIS ready\n");
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* file_storage.c -- File-backed USB Storage Gadget, for USB development
|
||||
*
|
||||
* Copyright (C) 2003-2005 Alan Stern
|
||||
* Copyright (C) 2003-2007 Alan Stern
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -217,17 +217,11 @@
|
||||
*/
|
||||
|
||||
|
||||
#undef DEBUG
|
||||
#undef VERBOSE
|
||||
#undef DUMP_MSGS
|
||||
/* #define VERBOSE_DEBUG */
|
||||
/* #define DUMP_MSGS */
|
||||
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/delay.h>
|
||||
@ -235,18 +229,10 @@
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
@ -254,7 +240,7 @@
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
@ -263,7 +249,7 @@
|
||||
|
||||
#define DRIVER_DESC "File-backed Storage Gadget"
|
||||
#define DRIVER_NAME "g_file_storage"
|
||||
#define DRIVER_VERSION "28 November 2005"
|
||||
#define DRIVER_VERSION "7 August 2007"
|
||||
|
||||
static const char longname[] = DRIVER_DESC;
|
||||
static const char shortname[] = DRIVER_NAME;
|
||||
@ -289,57 +275,48 @@ MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define xprintk(f,level,fmt,args...) \
|
||||
dev_printk(level , &(f)->gadget->dev , fmt , ## args)
|
||||
#define yprintk(l,level,fmt,args...) \
|
||||
dev_printk(level , &(l)->dev , fmt , ## args)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fsg,fmt,args...) \
|
||||
xprintk(fsg , KERN_DEBUG , fmt , ## args)
|
||||
#define LDBG(lun,fmt,args...) \
|
||||
yprintk(lun , KERN_DEBUG , fmt , ## args)
|
||||
dev_dbg(&(lun)->dev , fmt , ## args)
|
||||
#define MDBG(fmt,args...) \
|
||||
printk(KERN_DEBUG DRIVER_NAME ": " fmt , ## args)
|
||||
#else
|
||||
#define DBG(fsg,fmt,args...) \
|
||||
do { } while (0)
|
||||
#define LDBG(lun,fmt,args...) \
|
||||
do { } while (0)
|
||||
#define MDBG(fmt,args...) \
|
||||
do { } while (0)
|
||||
#undef VERBOSE
|
||||
#undef VERBOSE_DEBUG
|
||||
#undef DUMP_MSGS
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define VDBG DBG
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define VLDBG LDBG
|
||||
#else
|
||||
#define VDBG(fsg,fmt,args...) \
|
||||
do { } while (0)
|
||||
#define VLDBG(lun,fmt,args...) \
|
||||
do { } while (0)
|
||||
#endif /* VERBOSE */
|
||||
#endif /* VERBOSE_DEBUG */
|
||||
|
||||
#define ERROR(fsg,fmt,args...) \
|
||||
xprintk(fsg , KERN_ERR , fmt , ## args)
|
||||
#define LERROR(lun,fmt,args...) \
|
||||
yprintk(lun , KERN_ERR , fmt , ## args)
|
||||
|
||||
#define WARN(fsg,fmt,args...) \
|
||||
xprintk(fsg , KERN_WARNING , fmt , ## args)
|
||||
dev_err(&(lun)->dev , fmt , ## args)
|
||||
#define LWARN(lun,fmt,args...) \
|
||||
yprintk(lun , KERN_WARNING , fmt , ## args)
|
||||
|
||||
#define INFO(fsg,fmt,args...) \
|
||||
xprintk(fsg , KERN_INFO , fmt , ## args)
|
||||
dev_warn(&(lun)->dev , fmt , ## args)
|
||||
#define LINFO(lun,fmt,args...) \
|
||||
yprintk(lun , KERN_INFO , fmt , ## args)
|
||||
dev_info(&(lun)->dev , fmt , ## args)
|
||||
|
||||
#define MINFO(fmt,args...) \
|
||||
printk(KERN_INFO DRIVER_NAME ": " fmt , ## args)
|
||||
|
||||
#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)
|
||||
#define WARN(d, fmt, args...) \
|
||||
dev_warn(&(d)->gadget->dev , fmt , ## args)
|
||||
#define INFO(d, fmt, args...) \
|
||||
dev_info(&(d)->gadget->dev , fmt , ## args)
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -350,8 +327,8 @@ MODULE_LICENSE("Dual BSD/GPL");
|
||||
static struct {
|
||||
char *file[MAX_LUNS];
|
||||
int ro[MAX_LUNS];
|
||||
int num_filenames;
|
||||
int num_ros;
|
||||
unsigned int num_filenames;
|
||||
unsigned int num_ros;
|
||||
unsigned int nluns;
|
||||
|
||||
int removable;
|
||||
@ -578,7 +555,7 @@ struct lun {
|
||||
|
||||
#define backing_file_is_open(curlun) ((curlun)->filp != NULL)
|
||||
|
||||
static inline struct lun *dev_to_lun(struct device *dev)
|
||||
static struct lun *dev_to_lun(struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct lun, dev);
|
||||
}
|
||||
@ -711,13 +688,13 @@ struct fsg_dev {
|
||||
|
||||
typedef void (*fsg_routine_t)(struct fsg_dev *);
|
||||
|
||||
static int inline exception_in_progress(struct fsg_dev *fsg)
|
||||
static int exception_in_progress(struct fsg_dev *fsg)
|
||||
{
|
||||
return (fsg->state > FSG_STATE_IDLE);
|
||||
}
|
||||
|
||||
/* Make bulk-out requests be divisible by the maxpacket size */
|
||||
static void inline set_bulk_out_req_length(struct fsg_dev *fsg,
|
||||
static void set_bulk_out_req_length(struct fsg_dev *fsg,
|
||||
struct fsg_buffhd *bh, unsigned int length)
|
||||
{
|
||||
unsigned int rem;
|
||||
@ -743,50 +720,36 @@ static void close_all_backing_files(struct fsg_dev *fsg);
|
||||
static void dump_msg(struct fsg_dev *fsg, const char *label,
|
||||
const u8 *buf, unsigned int length)
|
||||
{
|
||||
unsigned int start, num, i;
|
||||
char line[52], *p;
|
||||
|
||||
if (length >= 512)
|
||||
return;
|
||||
DBG(fsg, "%s, length %u:\n", label, length);
|
||||
|
||||
start = 0;
|
||||
while (length > 0) {
|
||||
num = min(length, 16u);
|
||||
p = line;
|
||||
for (i = 0; i < num; ++i) {
|
||||
if (i == 8)
|
||||
*p++ = ' ';
|
||||
sprintf(p, " %02x", buf[i]);
|
||||
p += 3;
|
||||
}
|
||||
*p = 0;
|
||||
printk(KERN_DEBUG "%6x: %s\n", start, line);
|
||||
buf += num;
|
||||
start += num;
|
||||
length -= num;
|
||||
if (length < 512) {
|
||||
DBG(fsg, "%s, length %u:\n", label, length);
|
||||
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
|
||||
16, 1, buf, length, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void inline dump_cdb(struct fsg_dev *fsg)
|
||||
static void dump_cdb(struct fsg_dev *fsg)
|
||||
{}
|
||||
|
||||
#else
|
||||
|
||||
static void inline dump_msg(struct fsg_dev *fsg, const char *label,
|
||||
static void dump_msg(struct fsg_dev *fsg, const char *label,
|
||||
const u8 *buf, unsigned int length)
|
||||
{}
|
||||
|
||||
static void inline dump_cdb(struct fsg_dev *fsg)
|
||||
{
|
||||
int i;
|
||||
char cmdbuf[3*MAX_COMMAND_SIZE + 1];
|
||||
#ifdef VERBOSE_DEBUG
|
||||
|
||||
for (i = 0; i < fsg->cmnd_size; ++i)
|
||||
sprintf(cmdbuf + i*3, " %02x", fsg->cmnd[i]);
|
||||
VDBG(fsg, "SCSI CDB: %s\n", cmdbuf);
|
||||
static void dump_cdb(struct fsg_dev *fsg)
|
||||
{
|
||||
print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,
|
||||
16, 1, fsg->cmnd, fsg->cmnd_size, 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void dump_cdb(struct fsg_dev *fsg)
|
||||
{}
|
||||
|
||||
#endif /* VERBOSE_DEBUG */
|
||||
#endif /* DUMP_MSGS */
|
||||
|
||||
|
||||
@ -809,24 +772,24 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
|
||||
|
||||
/* Routines for unaligned data access */
|
||||
|
||||
static u16 inline get_be16(u8 *buf)
|
||||
static u16 get_be16(u8 *buf)
|
||||
{
|
||||
return ((u16) buf[0] << 8) | ((u16) buf[1]);
|
||||
}
|
||||
|
||||
static u32 inline get_be32(u8 *buf)
|
||||
static u32 get_be32(u8 *buf)
|
||||
{
|
||||
return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) |
|
||||
((u32) buf[2] << 8) | ((u32) buf[3]);
|
||||
}
|
||||
|
||||
static void inline put_be16(u8 *buf, u16 val)
|
||||
static void put_be16(u8 *buf, u16 val)
|
||||
{
|
||||
buf[0] = val >> 8;
|
||||
buf[1] = val;
|
||||
}
|
||||
|
||||
static void inline put_be32(u8 *buf, u32 val)
|
||||
static void put_be32(u8 *buf, u32 val)
|
||||
{
|
||||
buf[0] = val >> 24;
|
||||
buf[1] = val >> 16;
|
||||
@ -950,8 +913,6 @@ static const struct usb_descriptor_header *fs_function[] = {
|
||||
#define FS_FUNCTION_PRE_EP_ENTRIES 2
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
|
||||
/*
|
||||
* USB 2.0 devices need to expose both high speed and full speed
|
||||
* descriptors, unless they only run at full speed.
|
||||
@ -1014,14 +975,14 @@ static const struct usb_descriptor_header *hs_function[] = {
|
||||
#define HS_FUNCTION_PRE_EP_ENTRIES 2
|
||||
|
||||
/* Maxpacket and other transfer characteristics vary by speed. */
|
||||
#define ep_desc(g,fs,hs) (((g)->speed==USB_SPEED_HIGH) ? (hs) : (fs))
|
||||
|
||||
#else
|
||||
|
||||
/* If there's no high speed support, always use the full-speed descriptor. */
|
||||
#define ep_desc(g,fs,hs) fs
|
||||
|
||||
#endif /* !CONFIG_USB_GADGET_DUALSPEED */
|
||||
static struct usb_endpoint_descriptor *
|
||||
ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
|
||||
struct usb_endpoint_descriptor *hs)
|
||||
{
|
||||
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
|
||||
return hs;
|
||||
return fs;
|
||||
}
|
||||
|
||||
|
||||
/* The CBI specification limits the serial string to 12 uppercase hexadecimal
|
||||
@ -1053,26 +1014,22 @@ static struct usb_gadget_strings stringtab = {
|
||||
static int populate_config_buf(struct usb_gadget *gadget,
|
||||
u8 *buf, u8 type, unsigned index)
|
||||
{
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
enum usb_device_speed speed = gadget->speed;
|
||||
#endif
|
||||
int len;
|
||||
const struct usb_descriptor_header **function;
|
||||
|
||||
if (index > 0)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;
|
||||
if (speed == USB_SPEED_HIGH)
|
||||
if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH)
|
||||
function = hs_function;
|
||||
else
|
||||
#endif
|
||||
function = fs_function;
|
||||
|
||||
/* for now, don't advertise srp-only devices */
|
||||
if (!gadget->is_otg)
|
||||
if (!gadget_is_otg(gadget))
|
||||
function++;
|
||||
|
||||
len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function);
|
||||
@ -1394,10 +1351,9 @@ static int standard_setup_req(struct fsg_dev *fsg,
|
||||
value = sizeof device_desc;
|
||||
memcpy(req->buf, &device_desc, value);
|
||||
break;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
case USB_DT_DEVICE_QUALIFIER:
|
||||
VDBG(fsg, "get device qualifier\n");
|
||||
if (!fsg->gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(fsg->gadget))
|
||||
break;
|
||||
value = sizeof dev_qualifier;
|
||||
memcpy(req->buf, &dev_qualifier, value);
|
||||
@ -1405,15 +1361,12 @@ static int standard_setup_req(struct fsg_dev *fsg,
|
||||
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
VDBG(fsg, "get other-speed config descriptor\n");
|
||||
if (!fsg->gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(fsg->gadget))
|
||||
break;
|
||||
goto get_config;
|
||||
#endif
|
||||
case USB_DT_CONFIG:
|
||||
VDBG(fsg, "get configuration descriptor\n");
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
get_config:
|
||||
#endif
|
||||
get_config:
|
||||
value = populate_config_buf(fsg->gadget,
|
||||
req->buf,
|
||||
w_value >> 8,
|
||||
@ -1646,7 +1599,8 @@ static int do_read(struct fsg_dev *fsg)
|
||||
/* Wait for the next buffer to become available */
|
||||
bh = fsg->next_buffhd_to_fill;
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
if ((rc = sleep_thread(fsg)) != 0)
|
||||
rc = sleep_thread(fsg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1885,7 +1839,8 @@ static int do_write(struct fsg_dev *fsg)
|
||||
}
|
||||
|
||||
/* Wait for something to happen */
|
||||
if ((rc = sleep_thread(fsg)) != 0)
|
||||
rc = sleep_thread(fsg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2369,7 +2324,8 @@ static int pad_with_zeros(struct fsg_dev *fsg)
|
||||
|
||||
/* Wait for the next buffer to be free */
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
if ((rc = sleep_thread(fsg)) != 0)
|
||||
rc = sleep_thread(fsg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2429,7 +2385,8 @@ static int throw_away_data(struct fsg_dev *fsg)
|
||||
}
|
||||
|
||||
/* Otherwise wait for something to happen */
|
||||
if ((rc = sleep_thread(fsg)) != 0)
|
||||
rc = sleep_thread(fsg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
@ -2551,7 +2508,8 @@ static int send_status(struct fsg_dev *fsg)
|
||||
/* Wait for the next buffer to become available */
|
||||
bh = fsg->next_buffhd_to_fill;
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
if ((rc = sleep_thread(fsg)) != 0)
|
||||
rc = sleep_thread(fsg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2771,9 +2729,10 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
/* Wait for the next buffer to become available for data or status */
|
||||
bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill;
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
if ((rc = sleep_thread(fsg)) != 0)
|
||||
rc = sleep_thread(fsg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
fsg->phase_error = 0;
|
||||
fsg->short_packet_received = 0;
|
||||
|
||||
@ -3005,7 +2964,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
|
||||
/* Is the CBW meaningful? */
|
||||
if (cbw->Lun >= MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
|
||||
cbw->Length < 6 || cbw->Length > MAX_COMMAND_SIZE) {
|
||||
cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
|
||||
DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
|
||||
"cmdlen %u\n",
|
||||
cbw->Lun, cbw->Flags, cbw->Length);
|
||||
@ -3045,9 +3004,10 @@ static int get_next_command(struct fsg_dev *fsg)
|
||||
/* Wait for the next buffer to become available */
|
||||
bh = fsg->next_buffhd_to_fill;
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
if ((rc = sleep_thread(fsg)) != 0)
|
||||
rc = sleep_thread(fsg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Queue a request to read a Bulk-only CBW */
|
||||
set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN);
|
||||
@ -3061,9 +3021,10 @@ static int get_next_command(struct fsg_dev *fsg)
|
||||
|
||||
/* Wait for the CBW to arrive */
|
||||
while (bh->state != BUF_STATE_FULL) {
|
||||
if ((rc = sleep_thread(fsg)) != 0)
|
||||
rc = sleep_thread(fsg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
smp_rmb();
|
||||
rc = received_cbw(fsg, bh);
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
@ -3072,9 +3033,10 @@ static int get_next_command(struct fsg_dev *fsg)
|
||||
|
||||
/* Wait for the next command to arrive */
|
||||
while (fsg->cbbuf_cmnd_size == 0) {
|
||||
if ((rc = sleep_thread(fsg)) != 0)
|
||||
rc = sleep_thread(fsg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is the previous status interrupt request still busy?
|
||||
* The host is allowed to skip reading the status,
|
||||
@ -3595,7 +3557,8 @@ static ssize_t show_ro(struct device *dev, struct device_attribute *attr, char *
|
||||
return sprintf(buf, "%d\n", curlun->ro);
|
||||
}
|
||||
|
||||
static ssize_t show_file(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_file(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct lun *curlun = dev_to_lun(dev);
|
||||
struct fsg_dev *fsg = dev_get_drvdata(dev);
|
||||
@ -3604,8 +3567,8 @@ static ssize_t show_file(struct device *dev, struct device_attribute *attr, char
|
||||
|
||||
down_read(&fsg->filesem);
|
||||
if (backing_file_is_open(curlun)) { // Get the complete pathname
|
||||
p = d_path(curlun->filp->f_path.dentry, curlun->filp->f_path.mnt,
|
||||
buf, PAGE_SIZE - 1);
|
||||
p = d_path(curlun->filp->f_path.dentry,
|
||||
curlun->filp->f_path.mnt, buf, PAGE_SIZE - 1);
|
||||
if (IS_ERR(p))
|
||||
rc = PTR_ERR(p);
|
||||
else {
|
||||
@ -3623,7 +3586,8 @@ static ssize_t show_file(struct device *dev, struct device_attribute *attr, char
|
||||
}
|
||||
|
||||
|
||||
static ssize_t store_ro(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
static ssize_t store_ro(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
ssize_t rc = count;
|
||||
struct lun *curlun = dev_to_lun(dev);
|
||||
@ -3647,7 +3611,8 @@ static ssize_t store_ro(struct device *dev, struct device_attribute *attr, const
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t store_file(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
static ssize_t store_file(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct lun *curlun = dev_to_lun(dev);
|
||||
struct fsg_dev *fsg = dev_get_drvdata(dev);
|
||||
@ -3859,7 +3824,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
/* Find out how many LUNs there should be */
|
||||
i = mod_data.nluns;
|
||||
if (i == 0)
|
||||
i = max(mod_data.num_filenames, 1);
|
||||
i = max(mod_data.num_filenames, 1u);
|
||||
if (i > MAX_LUNS) {
|
||||
ERROR(fsg, "invalid number of LUNs: %d\n", i);
|
||||
rc = -EINVAL;
|
||||
@ -3944,22 +3909,24 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
intf_desc.bInterfaceProtocol = mod_data.transport_type;
|
||||
fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
|
||||
if (gadget_is_dualspeed(gadget)) {
|
||||
hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
|
||||
|
||||
/* Assume ep0 uses the same maxpacket value for both speeds */
|
||||
dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
|
||||
/* Assume ep0 uses the same maxpacket value for both speeds */
|
||||
dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
|
||||
|
||||
/* Assume that all endpoint addresses are the same for both speeds */
|
||||
hs_bulk_in_desc.bEndpointAddress = fs_bulk_in_desc.bEndpointAddress;
|
||||
hs_bulk_out_desc.bEndpointAddress = fs_bulk_out_desc.bEndpointAddress;
|
||||
hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress;
|
||||
#endif
|
||||
|
||||
if (gadget->is_otg) {
|
||||
otg_desc.bmAttributes |= USB_OTG_HNP;
|
||||
/* Assume endpoint addresses are the same for both speeds */
|
||||
hs_bulk_in_desc.bEndpointAddress =
|
||||
fs_bulk_in_desc.bEndpointAddress;
|
||||
hs_bulk_out_desc.bEndpointAddress =
|
||||
fs_bulk_out_desc.bEndpointAddress;
|
||||
hs_intr_in_desc.bEndpointAddress =
|
||||
fs_intr_in_desc.bEndpointAddress;
|
||||
}
|
||||
|
||||
if (gadget_is_otg(gadget))
|
||||
otg_desc.bmAttributes |= USB_OTG_HNP;
|
||||
|
||||
rc = -ENOMEM;
|
||||
|
||||
/* Allocate the request and buffer for endpoint 0 */
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -1090,14 +1090,11 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
|
||||
*/
|
||||
static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||
{
|
||||
#ifdef CONFIG_USB_OTG
|
||||
struct fsl_udc *udc;
|
||||
|
||||
udc = container_of(gadget, struct fsl_udc, gadget);
|
||||
|
||||
if (udc->transceiver)
|
||||
return otg_set_power(udc->transceiver, mA);
|
||||
#endif
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
@ -1120,7 +1117,7 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* defined in usb_gadget.h */
|
||||
/* defined in gadget.h */
|
||||
static struct usb_gadget_ops fsl_gadget_ops = {
|
||||
.get_frame = fsl_get_frame,
|
||||
.wakeup = fsl_wakeup,
|
||||
@ -1321,7 +1318,7 @@ static void setup_received_irq(struct fsl_udc *udc,
|
||||
| USB_TYPE_STANDARD)) {
|
||||
/* Note: The driver has not include OTG support yet.
|
||||
* This will be set when OTG support is added */
|
||||
if (!udc->gadget.is_otg)
|
||||
if (!gadget_is_otg(udc->gadget))
|
||||
break;
|
||||
else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE)
|
||||
udc->gadget.b_hnp_enable = 1;
|
||||
@ -1330,6 +1327,8 @@ static void setup_received_irq(struct fsl_udc *udc,
|
||||
else if (setup->bRequest ==
|
||||
USB_DEVICE_A_ALT_HNP_SUPPORT)
|
||||
udc->gadget.a_alt_hnp_support = 1;
|
||||
else
|
||||
break;
|
||||
rc = 0;
|
||||
} else
|
||||
break;
|
||||
@ -1840,10 +1839,8 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
||||
if (!driver || driver != udc_controller->driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_USB_OTG
|
||||
if (udc_controller->transceiver)
|
||||
(void)otg_set_peripheral(udc_controller->transceiver, 0);
|
||||
#endif
|
||||
|
||||
/* stop DR, disable intr */
|
||||
dr_controller_stop(udc_controller);
|
||||
|
@ -18,17 +18,11 @@
|
||||
* http://www.usb.org/developers/devclass_docs/midi10.pdf
|
||||
*/
|
||||
|
||||
#define DEBUG 1
|
||||
// #define VERBOSE
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <sound/core.h>
|
||||
@ -36,7 +30,7 @@
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/audio.h>
|
||||
#include <linux/usb/midi.h>
|
||||
|
||||
@ -139,30 +133,16 @@ struct gmidi_device {
|
||||
static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req);
|
||||
|
||||
|
||||
#define xprintk(d,level,fmt,args...) \
|
||||
dev_printk(level , &(d)->gadget->dev , fmt , ## args)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(dev,fmt,args...) \
|
||||
xprintk(dev , KERN_DEBUG , fmt , ## args)
|
||||
#else
|
||||
#define DBG(dev,fmt,args...) \
|
||||
do { } while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define VDBG DBG
|
||||
#else
|
||||
#define VDBG(dev,fmt,args...) \
|
||||
do { } while (0)
|
||||
#endif /* VERBOSE */
|
||||
|
||||
#define ERROR(dev,fmt,args...) \
|
||||
xprintk(dev , KERN_ERR , fmt , ## args)
|
||||
#define WARN(dev,fmt,args...) \
|
||||
xprintk(dev , KERN_WARNING , fmt , ## args)
|
||||
#define INFO(dev,fmt,args...) \
|
||||
xprintk(dev , KERN_INFO , fmt , ## args)
|
||||
#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)
|
||||
#define WARN(d, fmt, args...) \
|
||||
dev_warn(&(d)->gadget->dev , fmt , ## args)
|
||||
#define INFO(d, fmt, args...) \
|
||||
dev_info(&(d)->gadget->dev , fmt , ## args)
|
||||
|
||||
|
||||
static unsigned buflen = 256;
|
||||
@ -425,7 +405,7 @@ static int config_buf(struct usb_gadget *gadget,
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct usb_request* alloc_ep_req(struct usb_ep *ep, unsigned length)
|
||||
static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
|
||||
{
|
||||
struct usb_request *req;
|
||||
|
||||
@ -455,7 +435,7 @@ static const uint8_t gmidi_cin_length[] = {
|
||||
* Receives a chunk of MIDI data.
|
||||
*/
|
||||
static void gmidi_read_data(struct usb_ep *ep, int cable,
|
||||
uint8_t* data, int length)
|
||||
uint8_t *data, int length)
|
||||
{
|
||||
struct gmidi_device *dev = ep->driver_data;
|
||||
/* cable is ignored, because for now we only have one. */
|
||||
@ -541,7 +521,7 @@ static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags)
|
||||
{
|
||||
int err = 0;
|
||||
struct usb_request *req;
|
||||
struct usb_ep* ep;
|
||||
struct usb_ep *ep;
|
||||
unsigned i;
|
||||
|
||||
err = usb_ep_enable(dev->in_ep, &bulk_in_desc);
|
||||
@ -628,7 +608,7 @@ gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags)
|
||||
|
||||
if (gadget_is_sa1100(gadget) && dev->config) {
|
||||
/* tx fifo is full, but we can't clear it...*/
|
||||
INFO(dev, "can't change configurations\n");
|
||||
ERROR(dev, "can't change configurations\n");
|
||||
return -ESPIPE;
|
||||
}
|
||||
gmidi_reset_config(dev);
|
||||
@ -843,7 +823,7 @@ static void gmidi_disconnect(struct usb_gadget *gadget)
|
||||
static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget)
|
||||
{
|
||||
struct gmidi_device *dev = get_gadget_data(gadget);
|
||||
struct snd_card* card;
|
||||
struct snd_card *card;
|
||||
|
||||
DBG(dev, "unbind\n");
|
||||
|
||||
@ -867,12 +847,12 @@ static int gmidi_snd_free(struct snd_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gmidi_transmit_packet(struct usb_request* req, uint8_t p0,
|
||||
static void gmidi_transmit_packet(struct usb_request *req, uint8_t p0,
|
||||
uint8_t p1, uint8_t p2, uint8_t p3)
|
||||
{
|
||||
unsigned length = req->length;
|
||||
u8 *buf = (u8 *)req->buf + length;
|
||||
|
||||
uint8_t* buf = (uint8_t*)req->buf + length;
|
||||
buf[0] = p0;
|
||||
buf[1] = p1;
|
||||
buf[2] = p2;
|
||||
@ -883,8 +863,8 @@ static void gmidi_transmit_packet(struct usb_request* req, uint8_t p0,
|
||||
/*
|
||||
* Converts MIDI commands to USB MIDI packets.
|
||||
*/
|
||||
static void gmidi_transmit_byte(struct usb_request* req,
|
||||
struct gmidi_in_port* port, uint8_t b)
|
||||
static void gmidi_transmit_byte(struct usb_request *req,
|
||||
struct gmidi_in_port *port, uint8_t b)
|
||||
{
|
||||
uint8_t p0 = port->cable;
|
||||
|
||||
@ -981,10 +961,10 @@ static void gmidi_transmit_byte(struct usb_request* req,
|
||||
}
|
||||
}
|
||||
|
||||
static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req)
|
||||
static void gmidi_transmit(struct gmidi_device *dev, struct usb_request *req)
|
||||
{
|
||||
struct usb_ep* ep = dev->in_ep;
|
||||
struct gmidi_in_port* port = &dev->in_port;
|
||||
struct usb_ep *ep = dev->in_ep;
|
||||
struct gmidi_in_port *port = &dev->in_port;
|
||||
|
||||
if (!ep) {
|
||||
return;
|
||||
@ -1020,14 +1000,14 @@ static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req)
|
||||
|
||||
static void gmidi_in_tasklet(unsigned long data)
|
||||
{
|
||||
struct gmidi_device* dev = (struct gmidi_device*)data;
|
||||
struct gmidi_device *dev = (struct gmidi_device *)data;
|
||||
|
||||
gmidi_transmit(dev, NULL);
|
||||
}
|
||||
|
||||
static int gmidi_in_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct gmidi_device* dev = substream->rmidi->private_data;
|
||||
struct gmidi_device *dev = substream->rmidi->private_data;
|
||||
|
||||
VDBG(dev, "gmidi_in_open\n");
|
||||
dev->in_substream = substream;
|
||||
@ -1037,13 +1017,15 @@ static int gmidi_in_open(struct snd_rawmidi_substream *substream)
|
||||
|
||||
static int gmidi_in_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct gmidi_device *dev = substream->rmidi->private_data;
|
||||
|
||||
VDBG(dev, "gmidi_in_close\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)
|
||||
{
|
||||
struct gmidi_device* dev = substream->rmidi->private_data;
|
||||
struct gmidi_device *dev = substream->rmidi->private_data;
|
||||
|
||||
VDBG(dev, "gmidi_in_trigger %d\n", up);
|
||||
dev->in_port.active = up;
|
||||
@ -1054,7 +1036,7 @@ static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)
|
||||
|
||||
static int gmidi_out_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct gmidi_device* dev = substream->rmidi->private_data;
|
||||
struct gmidi_device *dev = substream->rmidi->private_data;
|
||||
|
||||
VDBG(dev, "gmidi_out_open\n");
|
||||
dev->out_substream = substream;
|
||||
@ -1063,13 +1045,15 @@ static int gmidi_out_open(struct snd_rawmidi_substream *substream)
|
||||
|
||||
static int gmidi_out_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct gmidi_device *dev = substream->rmidi->private_data;
|
||||
|
||||
VDBG(dev, "gmidi_out_close\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up)
|
||||
{
|
||||
struct gmidi_device* dev = substream->rmidi->private_data;
|
||||
struct gmidi_device *dev = substream->rmidi->private_data;
|
||||
|
||||
VDBG(dev, "gmidi_out_trigger %d\n", up);
|
||||
if (up) {
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
|
@ -20,8 +20,7 @@
|
||||
*/
|
||||
|
||||
|
||||
// #define DEBUG /* data to help fault diagnosis */
|
||||
// #define VERBOSE /* extra debug messages (success too) */
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
@ -38,7 +37,7 @@
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <linux/usb/gadgetfs.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
|
||||
/*
|
||||
@ -253,7 +252,7 @@ static const char *CHIP;
|
||||
do { } while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef VERBOSE
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define VDEBUG DBG
|
||||
#else
|
||||
#define VDEBUG(dev,fmt,args...) \
|
||||
@ -1010,11 +1009,12 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
|
||||
/* assume that was SET_CONFIGURATION */
|
||||
if (dev->current_config) {
|
||||
unsigned power;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
if (dev->gadget->speed == USB_SPEED_HIGH)
|
||||
|
||||
if (gadget_is_dualspeed(dev->gadget)
|
||||
&& (dev->gadget->speed
|
||||
== USB_SPEED_HIGH))
|
||||
power = dev->hs_config->bMaxPower;
|
||||
else
|
||||
#endif
|
||||
power = dev->config->bMaxPower;
|
||||
usb_gadget_vbus_draw(dev->gadget, 2 * power);
|
||||
}
|
||||
@ -1355,24 +1355,21 @@ static int
|
||||
config_buf (struct dev_data *dev, u8 type, unsigned index)
|
||||
{
|
||||
int len;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
int hs;
|
||||
#endif
|
||||
int hs = 0;
|
||||
|
||||
/* only one configuration */
|
||||
if (index > 0)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
hs = (dev->gadget->speed == USB_SPEED_HIGH);
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
hs = !hs;
|
||||
if (gadget_is_dualspeed(dev->gadget)) {
|
||||
hs = (dev->gadget->speed == USB_SPEED_HIGH);
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
hs = !hs;
|
||||
}
|
||||
if (hs) {
|
||||
dev->req->buf = dev->hs_config;
|
||||
len = le16_to_cpu(dev->hs_config->wTotalLength);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
} else {
|
||||
dev->req->buf = dev->config;
|
||||
len = le16_to_cpu(dev->config->wTotalLength);
|
||||
}
|
||||
@ -1393,13 +1390,13 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
spin_lock (&dev->lock);
|
||||
dev->setup_abort = 0;
|
||||
if (dev->state == STATE_DEV_UNCONNECTED) {
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
if (gadget->speed == USB_SPEED_HIGH && dev->hs_config == NULL) {
|
||||
if (gadget_is_dualspeed(gadget)
|
||||
&& gadget->speed == USB_SPEED_HIGH
|
||||
&& dev->hs_config == NULL) {
|
||||
spin_unlock(&dev->lock);
|
||||
ERROR (dev, "no high speed config??\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
||||
|
||||
dev->state = STATE_DEV_CONNECTED;
|
||||
dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket;
|
||||
@ -1469,13 +1466,12 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
// user mode expected to disable endpoints
|
||||
} else {
|
||||
u8 config, power;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
if (gadget->speed == USB_SPEED_HIGH) {
|
||||
|
||||
if (gadget_is_dualspeed(gadget)
|
||||
&& gadget->speed == USB_SPEED_HIGH) {
|
||||
config = dev->hs_config->bConfigurationValue;
|
||||
power = dev->hs_config->bMaxPower;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
} else {
|
||||
config = dev->config->bConfigurationValue;
|
||||
power = dev->config->bMaxPower;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@
|
||||
#include <asm/hardware.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/*
|
||||
* Memory map
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "m66592-udc.h"
|
||||
|
||||
|
@ -62,7 +62,7 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk.h>
|
||||
@ -1241,19 +1241,15 @@ static void pullup_enable(struct omap_udc *udc)
|
||||
udc->gadget.dev.parent->power.power_state = PMSG_ON;
|
||||
udc->gadget.dev.power.power_state = PMSG_ON;
|
||||
UDC_SYSCON1_REG |= UDC_PULLUP_EN;
|
||||
#ifndef CONFIG_USB_OTG
|
||||
if (!cpu_is_omap15xx())
|
||||
if (!gadget_is_otg(udc->gadget) && !cpu_is_omap15xx())
|
||||
OTG_CTRL_REG |= OTG_BSESSVLD;
|
||||
#endif
|
||||
UDC_IRQ_EN_REG = UDC_DS_CHG_IE;
|
||||
}
|
||||
|
||||
static void pullup_disable(struct omap_udc *udc)
|
||||
{
|
||||
#ifndef CONFIG_USB_OTG
|
||||
if (!cpu_is_omap15xx())
|
||||
if (!gadget_is_otg(udc->gadget) && !cpu_is_omap15xx())
|
||||
OTG_CTRL_REG &= ~OTG_BSESSVLD;
|
||||
#endif
|
||||
UDC_IRQ_EN_REG = UDC_DS_CHG_IE;
|
||||
UDC_SYSCON1_REG &= ~UDC_PULLUP_EN;
|
||||
}
|
||||
@ -1390,7 +1386,7 @@ static void update_otg(struct omap_udc *udc)
|
||||
{
|
||||
u16 devstat;
|
||||
|
||||
if (!udc->gadget.is_otg)
|
||||
if (!gadget_is_otg(udc->gadget))
|
||||
return;
|
||||
|
||||
if (OTG_CTRL_REG & OTG_ID)
|
||||
|
@ -54,7 +54,7 @@
|
||||
#include <asm/hardware.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include <asm/mach/udc_pxa2xx.h>
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
|
@ -17,34 +17,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
@ -89,30 +70,29 @@
|
||||
#define GS_DEFAULT_PARITY USB_CDC_NO_PARITY
|
||||
#define GS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS
|
||||
|
||||
/* select highspeed/fullspeed, hiding highspeed if not configured */
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
#define GS_SPEED_SELECT(is_hs,hs,fs) ((is_hs) ? (hs) : (fs))
|
||||
#else
|
||||
#define GS_SPEED_SELECT(is_hs,hs,fs) (fs)
|
||||
#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
||||
/* maxpacket and other transfer characteristics vary by speed. */
|
||||
static inline struct usb_endpoint_descriptor *
|
||||
choose_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
|
||||
struct usb_endpoint_descriptor *fs)
|
||||
{
|
||||
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
|
||||
return hs;
|
||||
return fs;
|
||||
}
|
||||
|
||||
|
||||
/* debug settings */
|
||||
#ifdef GS_DEBUG
|
||||
#ifdef DEBUG
|
||||
static int debug = 1;
|
||||
#else
|
||||
#define debug 0
|
||||
#endif
|
||||
|
||||
#define gs_debug(format, arg...) \
|
||||
do { if (debug) printk(KERN_DEBUG format, ## arg); } while(0)
|
||||
#define gs_debug_level(level, format, arg...) \
|
||||
do { if (debug>=level) printk(KERN_DEBUG format, ## arg); } while(0)
|
||||
|
||||
#else
|
||||
|
||||
#define gs_debug(format, arg...) \
|
||||
do { } while(0)
|
||||
#define gs_debug_level(level, format, arg...) \
|
||||
do { } while(0)
|
||||
|
||||
#endif /* GS_DEBUG */
|
||||
|
||||
/* Thanks to NetChip Technologies for donating this product ID.
|
||||
*
|
||||
@ -147,10 +127,10 @@ struct gs_req_entry {
|
||||
|
||||
/* the port structure holds info for each port, one for each minor number */
|
||||
struct gs_port {
|
||||
struct gs_dev *port_dev; /* pointer to device struct */
|
||||
struct gs_dev *port_dev; /* pointer to device struct */
|
||||
struct tty_struct *port_tty; /* pointer to tty struct */
|
||||
spinlock_t port_lock;
|
||||
int port_num;
|
||||
int port_num;
|
||||
int port_open_count;
|
||||
int port_in_use; /* open/close in progress */
|
||||
wait_queue_head_t port_write_wait;/* waiting to write */
|
||||
@ -188,7 +168,7 @@ static void __exit gs_module_exit(void);
|
||||
/* tty driver */
|
||||
static int gs_open(struct tty_struct *tty, struct file *file);
|
||||
static void gs_close(struct tty_struct *tty, struct file *file);
|
||||
static int gs_write(struct tty_struct *tty,
|
||||
static int gs_write(struct tty_struct *tty,
|
||||
const unsigned char *buf, int count);
|
||||
static void gs_put_char(struct tty_struct *tty, unsigned char ch);
|
||||
static void gs_flush_chars(struct tty_struct *tty);
|
||||
@ -222,7 +202,7 @@ static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req);
|
||||
static void gs_disconnect(struct usb_gadget *gadget);
|
||||
static int gs_set_config(struct gs_dev *dev, unsigned config);
|
||||
static void gs_reset_config(struct gs_dev *dev);
|
||||
static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed,
|
||||
static int gs_build_config_buf(u8 *buf, struct usb_gadget *g,
|
||||
u8 type, unsigned int index, int is_otg);
|
||||
|
||||
static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len,
|
||||
@ -415,18 +395,18 @@ static const struct usb_cdc_header_desc gs_header_desc = {
|
||||
};
|
||||
|
||||
static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = {
|
||||
.bLength = sizeof(gs_call_mgmt_descriptor),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
|
||||
.bmCapabilities = 0,
|
||||
.bDataInterface = 1, /* index of data interface */
|
||||
.bLength = sizeof(gs_call_mgmt_descriptor),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
|
||||
.bmCapabilities = 0,
|
||||
.bDataInterface = 1, /* index of data interface */
|
||||
};
|
||||
|
||||
static struct usb_cdc_acm_descriptor gs_acm_descriptor = {
|
||||
.bLength = sizeof(gs_acm_descriptor),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_ACM_TYPE,
|
||||
.bmCapabilities = 0,
|
||||
.bLength = sizeof(gs_acm_descriptor),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_ACM_TYPE,
|
||||
.bmCapabilities = 0,
|
||||
};
|
||||
|
||||
static const struct usb_cdc_union_desc gs_union_desc = {
|
||||
@ -436,7 +416,7 @@ static const struct usb_cdc_union_desc gs_union_desc = {
|
||||
.bMasterInterface0 = 0, /* index of control interface */
|
||||
.bSlaveInterface0 = 1, /* index of data interface */
|
||||
};
|
||||
|
||||
|
||||
static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
@ -482,7 +462,6 @@ static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
static struct usb_endpoint_descriptor gs_highspeed_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
@ -536,15 +515,13 @@ static const struct usb_descriptor_header *gs_acm_highspeed_function[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
||||
|
||||
|
||||
/* Module */
|
||||
MODULE_DESCRIPTION(GS_LONG_NAME);
|
||||
MODULE_AUTHOR("Al Borchers");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#ifdef GS_DEBUG
|
||||
#ifdef DEBUG
|
||||
module_param(debug, int, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on");
|
||||
#endif
|
||||
@ -915,7 +892,8 @@ static void gs_put_char(struct tty_struct *tty, unsigned char ch)
|
||||
return;
|
||||
}
|
||||
|
||||
gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p, %p, %p\n", port->port_num, tty, ch, __builtin_return_address(0), __builtin_return_address(1), __builtin_return_address(2));
|
||||
gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
|
||||
port->port_num, tty, ch, __builtin_return_address(0));
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
|
||||
@ -1116,7 +1094,11 @@ static int gs_send(struct gs_dev *dev)
|
||||
len = gs_send_packet(dev, req->buf, ep->maxpacket);
|
||||
|
||||
if (len > 0) {
|
||||
gs_debug_level(3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2));
|
||||
gs_debug_level(3, "gs_send: len=%d, 0x%2.2x "
|
||||
"0x%2.2x 0x%2.2x ...\n", len,
|
||||
*((unsigned char *)req->buf),
|
||||
*((unsigned char *)req->buf+1),
|
||||
*((unsigned char *)req->buf+2));
|
||||
list_del(&req_entry->re_entry);
|
||||
req->length = len;
|
||||
spin_unlock_irqrestore(&dev->dev_lock, flags);
|
||||
@ -1269,7 +1251,7 @@ static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
|
||||
switch(req->status) {
|
||||
case 0:
|
||||
/* normal completion */
|
||||
/* normal completion */
|
||||
gs_recv_packet(dev, req->buf, req->actual);
|
||||
requeue:
|
||||
req->length = ep->maxpacket;
|
||||
@ -1406,23 +1388,24 @@ static int __init gs_bind(struct usb_gadget *gadget)
|
||||
? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC;
|
||||
gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
gs_qualifier_desc.bDeviceClass = use_acm
|
||||
? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC;
|
||||
/* assume ep0 uses the same packet size for both speeds */
|
||||
gs_qualifier_desc.bMaxPacketSize0 = gs_device_desc.bMaxPacketSize0;
|
||||
/* assume endpoints are dual-speed */
|
||||
gs_highspeed_notify_desc.bEndpointAddress =
|
||||
gs_fullspeed_notify_desc.bEndpointAddress;
|
||||
gs_highspeed_in_desc.bEndpointAddress =
|
||||
gs_fullspeed_in_desc.bEndpointAddress;
|
||||
gs_highspeed_out_desc.bEndpointAddress =
|
||||
gs_fullspeed_out_desc.bEndpointAddress;
|
||||
#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
||||
if (gadget_is_dualspeed(gadget)) {
|
||||
gs_qualifier_desc.bDeviceClass = use_acm
|
||||
? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC;
|
||||
/* assume ep0 uses the same packet size for both speeds */
|
||||
gs_qualifier_desc.bMaxPacketSize0 =
|
||||
gs_device_desc.bMaxPacketSize0;
|
||||
/* assume endpoints are dual-speed */
|
||||
gs_highspeed_notify_desc.bEndpointAddress =
|
||||
gs_fullspeed_notify_desc.bEndpointAddress;
|
||||
gs_highspeed_in_desc.bEndpointAddress =
|
||||
gs_fullspeed_in_desc.bEndpointAddress;
|
||||
gs_highspeed_out_desc.bEndpointAddress =
|
||||
gs_fullspeed_out_desc.bEndpointAddress;
|
||||
}
|
||||
|
||||
usb_gadget_set_selfpowered(gadget);
|
||||
|
||||
if (gadget->is_otg) {
|
||||
if (gadget_is_otg(gadget)) {
|
||||
gs_otg_descriptor.bmAttributes |= USB_OTG_HNP,
|
||||
gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
@ -1487,6 +1470,12 @@ static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget)
|
||||
dev->dev_ctrl_req = NULL;
|
||||
}
|
||||
gs_free_ports(dev);
|
||||
if (dev->dev_notify_ep)
|
||||
usb_ep_disable(dev->dev_notify_ep);
|
||||
if (dev->dev_in_ep)
|
||||
usb_ep_disable(dev->dev_in_ep);
|
||||
if (dev->dev_out_ep)
|
||||
usb_ep_disable(dev->dev_out_ep);
|
||||
kfree(dev);
|
||||
set_gadget_data(gadget, NULL);
|
||||
}
|
||||
@ -1570,9 +1559,8 @@ static int gs_setup_standard(struct usb_gadget *gadget,
|
||||
memcpy(req->buf, &gs_device_desc, ret);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
case USB_DT_DEVICE_QUALIFIER:
|
||||
if (!gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
ret = min(wLength,
|
||||
(u16)sizeof(struct usb_qualifier_descriptor));
|
||||
@ -1580,14 +1568,13 @@ static int gs_setup_standard(struct usb_gadget *gadget,
|
||||
break;
|
||||
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
if (!gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
/* fall through */
|
||||
#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
||||
case USB_DT_CONFIG:
|
||||
ret = gs_build_config_buf(req->buf, gadget->speed,
|
||||
ret = gs_build_config_buf(req->buf, gadget,
|
||||
wValue >> 8, wValue & 0xff,
|
||||
gadget->is_otg);
|
||||
gadget_is_otg(gadget));
|
||||
if (ret >= 0)
|
||||
ret = min(wLength, (u16)ret);
|
||||
break;
|
||||
@ -1827,8 +1814,7 @@ static int gs_set_config(struct gs_dev *dev, unsigned config)
|
||||
|
||||
if (EP_NOTIFY_NAME
|
||||
&& strcmp(ep->name, EP_NOTIFY_NAME) == 0) {
|
||||
ep_desc = GS_SPEED_SELECT(
|
||||
gadget->speed == USB_SPEED_HIGH,
|
||||
ep_desc = choose_ep_desc(gadget,
|
||||
&gs_highspeed_notify_desc,
|
||||
&gs_fullspeed_notify_desc);
|
||||
ret = usb_ep_enable(ep,ep_desc);
|
||||
@ -1844,9 +1830,8 @@ static int gs_set_config(struct gs_dev *dev, unsigned config)
|
||||
}
|
||||
|
||||
else if (strcmp(ep->name, EP_IN_NAME) == 0) {
|
||||
ep_desc = GS_SPEED_SELECT(
|
||||
gadget->speed == USB_SPEED_HIGH,
|
||||
&gs_highspeed_in_desc,
|
||||
ep_desc = choose_ep_desc(gadget,
|
||||
&gs_highspeed_in_desc,
|
||||
&gs_fullspeed_in_desc);
|
||||
ret = usb_ep_enable(ep,ep_desc);
|
||||
if (ret == 0) {
|
||||
@ -1861,8 +1846,7 @@ static int gs_set_config(struct gs_dev *dev, unsigned config)
|
||||
}
|
||||
|
||||
else if (strcmp(ep->name, EP_OUT_NAME) == 0) {
|
||||
ep_desc = GS_SPEED_SELECT(
|
||||
gadget->speed == USB_SPEED_HIGH,
|
||||
ep_desc = choose_ep_desc(gadget,
|
||||
&gs_highspeed_out_desc,
|
||||
&gs_fullspeed_out_desc);
|
||||
ret = usb_ep_enable(ep,ep_desc);
|
||||
@ -1981,11 +1965,11 @@ static void gs_reset_config(struct gs_dev *dev)
|
||||
* Builds the config descriptors in the given buffer and returns the
|
||||
* length, or a negative error number.
|
||||
*/
|
||||
static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed,
|
||||
static int gs_build_config_buf(u8 *buf, struct usb_gadget *g,
|
||||
u8 type, unsigned int index, int is_otg)
|
||||
{
|
||||
int len;
|
||||
int high_speed;
|
||||
int high_speed = 0;
|
||||
const struct usb_config_descriptor *config_desc;
|
||||
const struct usb_descriptor_header **function;
|
||||
|
||||
@ -1993,20 +1977,22 @@ static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed,
|
||||
return -EINVAL;
|
||||
|
||||
/* other speed switches high and full speed */
|
||||
high_speed = (speed == USB_SPEED_HIGH);
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
high_speed = !high_speed;
|
||||
if (gadget_is_dualspeed(g)) {
|
||||
high_speed = (g->speed == USB_SPEED_HIGH);
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
high_speed = !high_speed;
|
||||
}
|
||||
|
||||
if (use_acm) {
|
||||
config_desc = &gs_acm_config_desc;
|
||||
function = GS_SPEED_SELECT(high_speed,
|
||||
gs_acm_highspeed_function,
|
||||
gs_acm_fullspeed_function);
|
||||
function = high_speed
|
||||
? gs_acm_highspeed_function
|
||||
: gs_acm_fullspeed_function;
|
||||
} else {
|
||||
config_desc = &gs_bulk_config_desc;
|
||||
function = GS_SPEED_SELECT(high_speed,
|
||||
gs_bulk_highspeed_function,
|
||||
gs_bulk_fullspeed_function);
|
||||
function = high_speed
|
||||
? gs_bulk_highspeed_function
|
||||
: gs_bulk_fullspeed_function;
|
||||
}
|
||||
|
||||
/* for now, don't advertise srp-only devices */
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
|
@ -1,38 +1,22 @@
|
||||
/*
|
||||
* zero.c -- Gadget Zero, for USB development
|
||||
*
|
||||
* Copyright (C) 2003-2004 David Brownell
|
||||
* Copyright (C) 2003-2007 David Brownell
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation, either version 2 of that License or (at your option) any
|
||||
* later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
@ -57,40 +41,28 @@
|
||||
* Many drivers will only have one configuration, letting them be much
|
||||
* simpler if they also don't support high speed operation (like this
|
||||
* driver does).
|
||||
*
|
||||
* Why is *this* driver using two configurations, rather than setting up
|
||||
* two interfaces with different functions? To help verify that multiple
|
||||
* configuration infrastucture is working correctly; also, so that it can
|
||||
* work with low capability USB controllers without four bulk endpoints.
|
||||
*/
|
||||
|
||||
#define DEBUG 1
|
||||
// #define VERBOSE
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define DRIVER_VERSION "St Patrick's Day 2004"
|
||||
#define DRIVER_VERSION "Lughnasadh, 2007"
|
||||
|
||||
static const char shortname [] = "zero";
|
||||
static const char longname [] = "Gadget Zero";
|
||||
@ -131,30 +103,16 @@ struct zero_dev {
|
||||
struct timer_list resume;
|
||||
};
|
||||
|
||||
#define xprintk(d,level,fmt,args...) \
|
||||
dev_printk(level , &(d)->gadget->dev , fmt , ## args)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(dev,fmt,args...) \
|
||||
xprintk(dev , KERN_DEBUG , fmt , ## args)
|
||||
#else
|
||||
#define DBG(dev,fmt,args...) \
|
||||
do { } while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define VDBG DBG
|
||||
#else
|
||||
#define VDBG(dev,fmt,args...) \
|
||||
do { } while (0)
|
||||
#endif /* VERBOSE */
|
||||
|
||||
#define ERROR(dev,fmt,args...) \
|
||||
xprintk(dev , KERN_ERR , fmt , ## args)
|
||||
#define WARN(dev,fmt,args...) \
|
||||
xprintk(dev , KERN_WARNING , fmt , ## args)
|
||||
#define INFO(dev,fmt,args...) \
|
||||
xprintk(dev , KERN_INFO , fmt , ## args)
|
||||
#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)
|
||||
#define WARN(d, fmt, args...) \
|
||||
dev_warn(&(d)->gadget->dev , fmt , ## args)
|
||||
#define INFO(d, fmt, args...) \
|
||||
dev_info(&(d)->gadget->dev , fmt , ## args)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -326,8 +284,6 @@ static const struct usb_descriptor_header *fs_loopback_function [] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
|
||||
/*
|
||||
* usb 2.0 devices need to expose both high speed and full speed
|
||||
* descriptors, unless they only run at full speed.
|
||||
@ -383,17 +339,20 @@ static const struct usb_descriptor_header *hs_loopback_function [] = {
|
||||
};
|
||||
|
||||
/* maxpacket and other transfer characteristics vary by speed. */
|
||||
#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
|
||||
static inline struct usb_endpoint_descriptor *
|
||||
ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
|
||||
struct usb_endpoint_descriptor *fs)
|
||||
{
|
||||
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
|
||||
return hs;
|
||||
return fs;
|
||||
}
|
||||
|
||||
#else
|
||||
static char manufacturer[50];
|
||||
|
||||
/* if there's no high speed support, maxpacket doesn't change. */
|
||||
#define ep_desc(g,hs,fs) fs
|
||||
/* default serial number takes at least two packets */
|
||||
static char serial[] = "0123456789.0123456789.0123456789";
|
||||
|
||||
#endif /* !CONFIG_USB_GADGET_DUALSPEED */
|
||||
|
||||
static char manufacturer [50];
|
||||
static char serial [40];
|
||||
|
||||
/* static strings, in UTF-8 */
|
||||
static struct usb_string strings [] = {
|
||||
@ -435,30 +394,29 @@ config_buf (struct usb_gadget *gadget,
|
||||
int is_source_sink;
|
||||
int len;
|
||||
const struct usb_descriptor_header **function;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
int hs = (gadget->speed == USB_SPEED_HIGH);
|
||||
#endif
|
||||
int hs = 0;
|
||||
|
||||
/* two configurations will always be index 0 and index 1 */
|
||||
if (index > 1)
|
||||
return -EINVAL;
|
||||
is_source_sink = loopdefault ? (index == 1) : (index == 0);
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
hs = !hs;
|
||||
if (gadget_is_dualspeed(gadget)) {
|
||||
hs = (gadget->speed == USB_SPEED_HIGH);
|
||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||
hs = !hs;
|
||||
}
|
||||
if (hs)
|
||||
function = is_source_sink
|
||||
? hs_source_sink_function
|
||||
: hs_loopback_function;
|
||||
else
|
||||
#endif
|
||||
function = is_source_sink
|
||||
? fs_source_sink_function
|
||||
: fs_loopback_function;
|
||||
|
||||
/* for now, don't advertise srp-only devices */
|
||||
if (!gadget->is_otg)
|
||||
if (!gadget_is_otg(gadget))
|
||||
function++;
|
||||
|
||||
len = usb_gadget_config_buf (is_source_sink
|
||||
@ -498,6 +456,19 @@ static void free_ep_req (struct usb_ep *ep, struct usb_request *req)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripherals,
|
||||
* this just sinks bulk packets OUT to the peripheral and sources them IN
|
||||
* to the host, optionally with specific data patterns.
|
||||
*
|
||||
* In terms of control messaging, this supports all the standard requests
|
||||
* plus two that support control-OUT tests.
|
||||
*
|
||||
* Note that because this doesn't queue more than one request at a time,
|
||||
* some other function must be used to test queueing logic. The network
|
||||
* link (g_ether) is probably the best option for that.
|
||||
*/
|
||||
|
||||
/* optionally require specific source/sink data patterns */
|
||||
|
||||
static int
|
||||
@ -534,12 +505,7 @@ check_read_data (
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
reinit_write_data (
|
||||
struct zero_dev *dev,
|
||||
struct usb_ep *ep,
|
||||
struct usb_request *req
|
||||
)
|
||||
static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
unsigned i;
|
||||
u8 *buf = req->buf;
|
||||
@ -566,16 +532,16 @@ static void source_sink_complete (struct usb_ep *ep, struct usb_request *req)
|
||||
|
||||
switch (status) {
|
||||
|
||||
case 0: /* normal completion? */
|
||||
case 0: /* normal completion? */
|
||||
if (ep == dev->out_ep) {
|
||||
check_read_data (dev, ep, req);
|
||||
memset (req->buf, 0x55, req->length);
|
||||
} else
|
||||
reinit_write_data (dev, ep, req);
|
||||
reinit_write_data(ep, req);
|
||||
break;
|
||||
|
||||
/* this endpoint is normally active while we're configured */
|
||||
case -ECONNABORTED: /* hardware forced ep reset */
|
||||
case -ECONNABORTED: /* hardware forced ep reset */
|
||||
case -ECONNRESET: /* request dequeued */
|
||||
case -ESHUTDOWN: /* disconnect from host */
|
||||
VDBG (dev, "%s gone (%d), %d/%d\n", ep->name, status,
|
||||
@ -607,8 +573,7 @@ static void source_sink_complete (struct usb_ep *ep, struct usb_request *req)
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_request *
|
||||
source_sink_start_ep (struct usb_ep *ep, gfp_t gfp_flags)
|
||||
static struct usb_request *source_sink_start_ep(struct usb_ep *ep)
|
||||
{
|
||||
struct usb_request *req;
|
||||
int status;
|
||||
@ -621,11 +586,11 @@ source_sink_start_ep (struct usb_ep *ep, gfp_t gfp_flags)
|
||||
req->complete = source_sink_complete;
|
||||
|
||||
if (strcmp (ep->name, EP_IN_NAME) == 0)
|
||||
reinit_write_data (ep->driver_data, ep, req);
|
||||
reinit_write_data(ep, req);
|
||||
else
|
||||
memset (req->buf, 0x55, req->length);
|
||||
|
||||
status = usb_ep_queue (ep, req, gfp_flags);
|
||||
status = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (status) {
|
||||
struct zero_dev *dev = ep->driver_data;
|
||||
|
||||
@ -637,8 +602,7 @@ source_sink_start_ep (struct usb_ep *ep, gfp_t gfp_flags)
|
||||
return req;
|
||||
}
|
||||
|
||||
static int
|
||||
set_source_sink_config (struct zero_dev *dev, gfp_t gfp_flags)
|
||||
static int set_source_sink_config(struct zero_dev *dev)
|
||||
{
|
||||
int result = 0;
|
||||
struct usb_ep *ep;
|
||||
@ -653,8 +617,7 @@ set_source_sink_config (struct zero_dev *dev, gfp_t gfp_flags)
|
||||
result = usb_ep_enable (ep, d);
|
||||
if (result == 0) {
|
||||
ep->driver_data = dev;
|
||||
if (source_sink_start_ep(ep, gfp_flags)
|
||||
!= NULL) {
|
||||
if (source_sink_start_ep(ep) != NULL) {
|
||||
dev->in_ep = ep;
|
||||
continue;
|
||||
}
|
||||
@ -668,8 +631,7 @@ set_source_sink_config (struct zero_dev *dev, gfp_t gfp_flags)
|
||||
result = usb_ep_enable (ep, d);
|
||||
if (result == 0) {
|
||||
ep->driver_data = dev;
|
||||
if (source_sink_start_ep(ep, gfp_flags)
|
||||
!= NULL) {
|
||||
if (source_sink_start_ep(ep) != NULL) {
|
||||
dev->out_ep = ep;
|
||||
continue;
|
||||
}
|
||||
@ -701,7 +663,7 @@ static void loopback_complete (struct usb_ep *ep, struct usb_request *req)
|
||||
|
||||
switch (status) {
|
||||
|
||||
case 0: /* normal completion? */
|
||||
case 0: /* normal completion? */
|
||||
if (ep == dev->out_ep) {
|
||||
/* loop this OUT packet back IN to the host */
|
||||
req->zero = (req->actual < req->length);
|
||||
@ -735,7 +697,7 @@ static void loopback_complete (struct usb_ep *ep, struct usb_request *req)
|
||||
* rely on the hardware driver to clean up on disconnect or
|
||||
* endpoint disable.
|
||||
*/
|
||||
case -ECONNABORTED: /* hardware forced ep reset */
|
||||
case -ECONNABORTED: /* hardware forced ep reset */
|
||||
case -ECONNRESET: /* request dequeued */
|
||||
case -ESHUTDOWN: /* disconnect from host */
|
||||
free_ep_req (ep, req);
|
||||
@ -743,8 +705,7 @@ static void loopback_complete (struct usb_ep *ep, struct usb_request *req)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
set_loopback_config (struct zero_dev *dev, gfp_t gfp_flags)
|
||||
static int set_loopback_config(struct zero_dev *dev)
|
||||
{
|
||||
int result = 0;
|
||||
struct usb_ep *ep;
|
||||
@ -844,8 +805,7 @@ static void zero_reset_config (struct zero_dev *dev)
|
||||
* code can do, perhaps by disallowing more than one configuration or
|
||||
* by limiting configuration choices (like the pxa2xx).
|
||||
*/
|
||||
static int
|
||||
zero_set_config (struct zero_dev *dev, unsigned number, gfp_t gfp_flags)
|
||||
static int zero_set_config(struct zero_dev *dev, unsigned number)
|
||||
{
|
||||
int result = 0;
|
||||
struct usb_gadget *gadget = dev->gadget;
|
||||
@ -855,17 +815,17 @@ zero_set_config (struct zero_dev *dev, unsigned number, gfp_t gfp_flags)
|
||||
|
||||
if (gadget_is_sa1100 (gadget) && dev->config) {
|
||||
/* tx fifo is full, but we can't clear it...*/
|
||||
INFO (dev, "can't change configurations\n");
|
||||
ERROR(dev, "can't change configurations\n");
|
||||
return -ESPIPE;
|
||||
}
|
||||
zero_reset_config (dev);
|
||||
|
||||
switch (number) {
|
||||
case CONFIG_SOURCE_SINK:
|
||||
result = set_source_sink_config (dev, gfp_flags);
|
||||
result = set_source_sink_config(dev);
|
||||
break;
|
||||
case CONFIG_LOOPBACK:
|
||||
result = set_loopback_config (dev, gfp_flags);
|
||||
result = set_loopback_config(dev);
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
@ -885,7 +845,7 @@ zero_set_config (struct zero_dev *dev, unsigned number, gfp_t gfp_flags)
|
||||
case USB_SPEED_LOW: speed = "low"; break;
|
||||
case USB_SPEED_FULL: speed = "full"; break;
|
||||
case USB_SPEED_HIGH: speed = "high"; break;
|
||||
default: speed = "?"; break;
|
||||
default: speed = "?"; break;
|
||||
}
|
||||
|
||||
dev->config = number;
|
||||
@ -938,19 +898,17 @@ zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
value = min (w_length, (u16) sizeof device_desc);
|
||||
memcpy (req->buf, &device_desc, value);
|
||||
break;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
case USB_DT_DEVICE_QUALIFIER:
|
||||
if (!gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
value = min (w_length, (u16) sizeof dev_qualifier);
|
||||
memcpy (req->buf, &dev_qualifier, value);
|
||||
break;
|
||||
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
if (!gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
// FALLTHROUGH
|
||||
#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
||||
case USB_DT_CONFIG:
|
||||
value = config_buf (gadget, req->buf,
|
||||
w_value >> 8,
|
||||
@ -984,7 +942,7 @@ zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
else
|
||||
VDBG (dev, "HNP inactive\n");
|
||||
spin_lock (&dev->lock);
|
||||
value = zero_set_config (dev, w_value, GFP_ATOMIC);
|
||||
value = zero_set_config(dev, w_value);
|
||||
spin_unlock (&dev->lock);
|
||||
break;
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
@ -1013,7 +971,7 @@ zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
* use this "reset the config" shortcut.
|
||||
*/
|
||||
zero_reset_config (dev);
|
||||
zero_set_config (dev, config, GFP_ATOMIC);
|
||||
zero_set_config(dev, config);
|
||||
value = 0;
|
||||
}
|
||||
spin_unlock (&dev->lock);
|
||||
@ -1163,7 +1121,7 @@ autoconf_fail:
|
||||
}
|
||||
EP_IN_NAME = ep->name;
|
||||
ep->driver_data = ep; /* claim */
|
||||
|
||||
|
||||
ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
|
||||
if (!ep)
|
||||
goto autoconf_fail;
|
||||
@ -1207,16 +1165,18 @@ autoconf_fail:
|
||||
|
||||
device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
/* assume ep0 uses the same value for both speeds ... */
|
||||
dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
|
||||
if (gadget_is_dualspeed(gadget)) {
|
||||
/* assume ep0 uses the same value for both speeds ... */
|
||||
dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
|
||||
|
||||
/* and that all endpoints are dual-speed */
|
||||
hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
|
||||
hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
|
||||
#endif
|
||||
/* and that all endpoints are dual-speed */
|
||||
hs_source_desc.bEndpointAddress =
|
||||
fs_source_desc.bEndpointAddress;
|
||||
hs_sink_desc.bEndpointAddress =
|
||||
fs_sink_desc.bEndpointAddress;
|
||||
}
|
||||
|
||||
if (gadget->is_otg) {
|
||||
if (gadget_is_otg(gadget)) {
|
||||
otg_descriptor.bmAttributes |= USB_OTG_HNP,
|
||||
source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
@ -1294,23 +1254,18 @@ static struct usb_gadget_driver zero_driver = {
|
||||
.suspend = zero_suspend,
|
||||
.resume = zero_resume,
|
||||
|
||||
.driver = {
|
||||
.driver = {
|
||||
.name = (char *) shortname,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_AUTHOR ("David Brownell");
|
||||
MODULE_LICENSE ("Dual BSD/GPL");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
static int __init init (void)
|
||||
{
|
||||
/* a real value would likely come through some id prom
|
||||
* or module option. this one takes at least two packets.
|
||||
*/
|
||||
strlcpy (serial, "0123456789.0123456789.0123456789", sizeof serial);
|
||||
|
||||
return usb_gadget_register_driver (&zero_driver);
|
||||
}
|
||||
module_init (init);
|
||||
|
@ -154,6 +154,19 @@ config USB_OHCI_HCD_PCI
|
||||
Enables support for PCI-bus plug-in USB controller cards.
|
||||
If unsure, say Y.
|
||||
|
||||
config USB_OHCI_HCD_SSB
|
||||
bool "OHCI support for Broadcom SSB OHCI core"
|
||||
depends on USB_OHCI_HCD && SSB && EXPERIMENTAL
|
||||
default n
|
||||
---help---
|
||||
Support for the Sonics Silicon Backplane (SSB) attached
|
||||
Broadcom USB OHCI core.
|
||||
|
||||
This device is present in some embedded devices with
|
||||
Broadcom based SSB bus.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config USB_OHCI_BIG_ENDIAN_DESC
|
||||
bool
|
||||
depends on USB_OHCI_HCD
|
||||
|
@ -220,10 +220,8 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
#ifdef CONFIG_PM
|
||||
.hub_suspend = ehci_hub_suspend,
|
||||
.hub_resume = ehci_hub_resume,
|
||||
#endif
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -570,10 +570,18 @@ static int ehci_run (struct usb_hcd *hcd)
|
||||
* are explicitly handed to companion controller(s), so no TT is
|
||||
* involved with the root hub. (Except where one is integrated,
|
||||
* and there's no companion controller unless maybe for USB OTG.)
|
||||
*
|
||||
* Turning on the CF flag will transfer ownership of all ports
|
||||
* from the companions to the EHCI controller. If any of the
|
||||
* companions are in the middle of a port reset at the time, it
|
||||
* could cause trouble. Write-locking ehci_cf_port_reset_rwsem
|
||||
* guarantees that no resets are in progress.
|
||||
*/
|
||||
down_write(&ehci_cf_port_reset_rwsem);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
||||
up_write(&ehci_cf_port_reset_rwsem);
|
||||
|
||||
temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
ehci_info (ehci,
|
||||
@ -719,7 +727,6 @@ dead:
|
||||
*/
|
||||
static int ehci_urb_enqueue (
|
||||
struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *ep,
|
||||
struct urb *urb,
|
||||
gfp_t mem_flags
|
||||
) {
|
||||
@ -734,12 +741,12 @@ static int ehci_urb_enqueue (
|
||||
default:
|
||||
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
|
||||
return -ENOMEM;
|
||||
return submit_async (ehci, ep, urb, &qtd_list, mem_flags);
|
||||
return submit_async(ehci, urb, &qtd_list, mem_flags);
|
||||
|
||||
case PIPE_INTERRUPT:
|
||||
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
|
||||
return -ENOMEM;
|
||||
return intr_submit (ehci, ep, urb, &qtd_list, mem_flags);
|
||||
return intr_submit(ehci, urb, &qtd_list, mem_flags);
|
||||
|
||||
case PIPE_ISOCHRONOUS:
|
||||
if (urb->dev->speed == USB_SPEED_HIGH)
|
||||
@ -777,13 +784,18 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
* completions normally happen asynchronously
|
||||
*/
|
||||
|
||||
static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
|
||||
static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
struct ehci_qh *qh;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
switch (usb_pipetype (urb->pipe)) {
|
||||
// case PIPE_CONTROL:
|
||||
// case PIPE_BULK:
|
||||
@ -838,7 +850,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
|
||||
}
|
||||
done:
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -58,8 +58,6 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
|
||||
if (!retval)
|
||||
ehci_dbg(ehci, "MWI active\n");
|
||||
|
||||
ehci_port_power(ehci, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -156,8 +154,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ehci_is_TDI(ehci))
|
||||
ehci_reset(ehci);
|
||||
ehci_reset(ehci);
|
||||
|
||||
/* at least the Genesys GL880S needs fixup here */
|
||||
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
|
||||
|
@ -160,10 +160,8 @@ static const struct hc_driver ehci_ppc_soc_hc_driver = {
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
#ifdef CONFIG_PM
|
||||
.hub_suspend = ehci_hub_suspend,
|
||||
.hub_resume = ehci_hub_resume,
|
||||
#endif
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
};
|
||||
|
||||
static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev)
|
||||
|
@ -47,7 +47,7 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
ehci_port_power(ehci, 0);
|
||||
ehci_reset(ehci);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -139,63 +139,65 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void qtd_copy_status (
|
||||
static int qtd_copy_status (
|
||||
struct ehci_hcd *ehci,
|
||||
struct urb *urb,
|
||||
size_t length,
|
||||
u32 token
|
||||
)
|
||||
{
|
||||
int status = -EINPROGRESS;
|
||||
|
||||
/* count IN/OUT bytes, not SETUP (even short packets) */
|
||||
if (likely (QTD_PID (token) != 2))
|
||||
urb->actual_length += length - QTD_LENGTH (token);
|
||||
|
||||
/* don't modify error codes */
|
||||
if (unlikely (urb->status != -EINPROGRESS))
|
||||
return;
|
||||
if (unlikely(urb->unlinked))
|
||||
return status;
|
||||
|
||||
/* force cleanup after short read; not always an error */
|
||||
if (unlikely (IS_SHORT_READ (token)))
|
||||
urb->status = -EREMOTEIO;
|
||||
status = -EREMOTEIO;
|
||||
|
||||
/* serious "can't proceed" faults reported by the hardware */
|
||||
if (token & QTD_STS_HALT) {
|
||||
if (token & QTD_STS_BABBLE) {
|
||||
/* FIXME "must" disable babbling device's port too */
|
||||
urb->status = -EOVERFLOW;
|
||||
status = -EOVERFLOW;
|
||||
} else if (token & QTD_STS_MMF) {
|
||||
/* fs/ls interrupt xfer missed the complete-split */
|
||||
urb->status = -EPROTO;
|
||||
status = -EPROTO;
|
||||
} else if (token & QTD_STS_DBE) {
|
||||
urb->status = (QTD_PID (token) == 1) /* IN ? */
|
||||
status = (QTD_PID (token) == 1) /* IN ? */
|
||||
? -ENOSR /* hc couldn't read data */
|
||||
: -ECOMM; /* hc couldn't write data */
|
||||
} else if (token & QTD_STS_XACT) {
|
||||
/* timeout, bad crc, wrong PID, etc; retried */
|
||||
if (QTD_CERR (token))
|
||||
urb->status = -EPIPE;
|
||||
status = -EPIPE;
|
||||
else {
|
||||
ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n",
|
||||
urb->dev->devpath,
|
||||
usb_pipeendpoint (urb->pipe),
|
||||
usb_pipein (urb->pipe) ? "in" : "out");
|
||||
urb->status = -EPROTO;
|
||||
status = -EPROTO;
|
||||
}
|
||||
/* CERR nonzero + no errors + halt --> stall */
|
||||
} else if (QTD_CERR (token))
|
||||
urb->status = -EPIPE;
|
||||
status = -EPIPE;
|
||||
else /* unknown */
|
||||
urb->status = -EPROTO;
|
||||
status = -EPROTO;
|
||||
|
||||
ehci_vdbg (ehci,
|
||||
"dev%d ep%d%s qtd token %08x --> status %d\n",
|
||||
usb_pipedevice (urb->pipe),
|
||||
usb_pipeendpoint (urb->pipe),
|
||||
usb_pipein (urb->pipe) ? "in" : "out",
|
||||
token, urb->status);
|
||||
token, status);
|
||||
|
||||
/* if async CSPLIT failed, try cleaning out the TT buffer */
|
||||
if (urb->status != -EPIPE
|
||||
if (status != -EPIPE
|
||||
&& urb->dev->tt && !usb_pipeint (urb->pipe)
|
||||
&& ((token & QTD_STS_MMF) != 0
|
||||
|| QTD_CERR(token) == 0)
|
||||
@ -212,10 +214,12 @@ static void qtd_copy_status (
|
||||
usb_hub_tt_clear_buffer (urb->dev, urb->pipe);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb)
|
||||
ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
|
||||
__releases(ehci->lock)
|
||||
__acquires(ehci->lock)
|
||||
{
|
||||
@ -231,25 +235,13 @@ __acquires(ehci->lock)
|
||||
qh_put (qh);
|
||||
}
|
||||
|
||||
spin_lock (&urb->lock);
|
||||
urb->hcpriv = NULL;
|
||||
switch (urb->status) {
|
||||
case -EINPROGRESS: /* success */
|
||||
urb->status = 0;
|
||||
default: /* fault */
|
||||
COUNT (ehci->stats.complete);
|
||||
break;
|
||||
case -EREMOTEIO: /* fault or normal */
|
||||
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
|
||||
urb->status = 0;
|
||||
COUNT (ehci->stats.complete);
|
||||
break;
|
||||
case -ECONNRESET: /* canceled */
|
||||
case -ENOENT:
|
||||
COUNT (ehci->stats.unlink);
|
||||
break;
|
||||
if (unlikely(urb->unlinked)) {
|
||||
COUNT(ehci->stats.unlink);
|
||||
} else {
|
||||
if (likely(status == -EINPROGRESS))
|
||||
status = 0;
|
||||
COUNT(ehci->stats.complete);
|
||||
}
|
||||
spin_unlock (&urb->lock);
|
||||
|
||||
#ifdef EHCI_URB_TRACE
|
||||
ehci_dbg (ehci,
|
||||
@ -257,13 +249,14 @@ __acquires(ehci->lock)
|
||||
__FUNCTION__, urb->dev->devpath, urb,
|
||||
usb_pipeendpoint (urb->pipe),
|
||||
usb_pipein (urb->pipe) ? "in" : "out",
|
||||
urb->status,
|
||||
status,
|
||||
urb->actual_length, urb->transfer_buffer_length);
|
||||
#endif
|
||||
|
||||
/* complete() can reenter this HCD */
|
||||
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
|
||||
spin_unlock (&ehci->lock);
|
||||
usb_hcd_giveback_urb (ehci_to_hcd(ehci), urb);
|
||||
usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status);
|
||||
spin_lock (&ehci->lock);
|
||||
}
|
||||
|
||||
@ -283,6 +276,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{
|
||||
struct ehci_qtd *last = NULL, *end = qh->dummy;
|
||||
struct list_head *entry, *tmp;
|
||||
int last_status = -EINPROGRESS;
|
||||
int stopped;
|
||||
unsigned count = 0;
|
||||
int do_status = 0;
|
||||
@ -311,6 +305,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
struct ehci_qtd *qtd;
|
||||
struct urb *urb;
|
||||
u32 token = 0;
|
||||
int qtd_status;
|
||||
|
||||
qtd = list_entry (entry, struct ehci_qtd, qtd_list);
|
||||
urb = qtd->urb;
|
||||
@ -318,11 +313,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
/* clean up any state from previous QTD ...*/
|
||||
if (last) {
|
||||
if (likely (last->urb != urb)) {
|
||||
ehci_urb_done (ehci, last->urb);
|
||||
ehci_urb_done(ehci, last->urb, last_status);
|
||||
count++;
|
||||
}
|
||||
ehci_qtd_free (ehci, last);
|
||||
last = NULL;
|
||||
last_status = -EINPROGRESS;
|
||||
}
|
||||
|
||||
/* ignore urbs submitted during completions we reported */
|
||||
@ -358,13 +354,14 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
stopped = 1;
|
||||
|
||||
if (unlikely (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)))
|
||||
urb->status = -ESHUTDOWN;
|
||||
last_status = -ESHUTDOWN;
|
||||
|
||||
/* ignore active urbs unless some previous qtd
|
||||
* for the urb faulted (including short read) or
|
||||
* its urb was canceled. we may patch qh or qtds.
|
||||
*/
|
||||
if (likely (urb->status == -EINPROGRESS))
|
||||
if (likely(last_status == -EINPROGRESS &&
|
||||
!urb->unlinked))
|
||||
continue;
|
||||
|
||||
/* issue status after short control reads */
|
||||
@ -392,11 +389,14 @@ halt:
|
||||
}
|
||||
|
||||
/* remove it from the queue */
|
||||
spin_lock (&urb->lock);
|
||||
qtd_copy_status (ehci, urb, qtd->length, token);
|
||||
do_status = (urb->status == -EREMOTEIO)
|
||||
&& usb_pipecontrol (urb->pipe);
|
||||
spin_unlock (&urb->lock);
|
||||
qtd_status = qtd_copy_status(ehci, urb, qtd->length, token);
|
||||
if (unlikely(qtd_status == -EREMOTEIO)) {
|
||||
do_status = (!urb->unlinked &&
|
||||
usb_pipecontrol(urb->pipe));
|
||||
qtd_status = 0;
|
||||
}
|
||||
if (likely(last_status == -EINPROGRESS))
|
||||
last_status = qtd_status;
|
||||
|
||||
if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
|
||||
last = list_entry (qtd->qtd_list.prev,
|
||||
@ -409,7 +409,7 @@ halt:
|
||||
|
||||
/* last urb's completion might still need calling */
|
||||
if (likely (last != NULL)) {
|
||||
ehci_urb_done (ehci, last->urb);
|
||||
ehci_urb_done(ehci, last->urb, last_status);
|
||||
count++;
|
||||
ehci_qtd_free (ehci, last);
|
||||
}
|
||||
@ -913,7 +913,6 @@ static struct ehci_qh *qh_append_tds (
|
||||
static int
|
||||
submit_async (
|
||||
struct ehci_hcd *ehci,
|
||||
struct usb_host_endpoint *ep,
|
||||
struct urb *urb,
|
||||
struct list_head *qtd_list,
|
||||
gfp_t mem_flags
|
||||
@ -922,10 +921,10 @@ submit_async (
|
||||
int epnum;
|
||||
unsigned long flags;
|
||||
struct ehci_qh *qh = NULL;
|
||||
int rc = 0;
|
||||
int rc;
|
||||
|
||||
qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
|
||||
epnum = ep->desc.bEndpointAddress;
|
||||
epnum = urb->ep->desc.bEndpointAddress;
|
||||
|
||||
#ifdef EHCI_URB_TRACE
|
||||
ehci_dbg (ehci,
|
||||
@ -933,7 +932,7 @@ submit_async (
|
||||
__FUNCTION__, urb->dev->devpath, urb,
|
||||
epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
|
||||
urb->transfer_buffer_length,
|
||||
qtd, ep->hcpriv);
|
||||
qtd, urb->ep->hcpriv);
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
@ -942,9 +941,13 @@ submit_async (
|
||||
rc = -ESHUTDOWN;
|
||||
goto done;
|
||||
}
|
||||
rc = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
|
||||
if (unlikely(rc))
|
||||
goto done;
|
||||
|
||||
qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv);
|
||||
qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv);
|
||||
if (unlikely(qh == NULL)) {
|
||||
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
|
||||
rc = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
@ -797,7 +797,6 @@ done:
|
||||
|
||||
static int intr_submit (
|
||||
struct ehci_hcd *ehci,
|
||||
struct usb_host_endpoint *ep,
|
||||
struct urb *urb,
|
||||
struct list_head *qtd_list,
|
||||
gfp_t mem_flags
|
||||
@ -805,23 +804,26 @@ static int intr_submit (
|
||||
unsigned epnum;
|
||||
unsigned long flags;
|
||||
struct ehci_qh *qh;
|
||||
int status = 0;
|
||||
int status;
|
||||
struct list_head empty;
|
||||
|
||||
/* get endpoint and transfer/schedule data */
|
||||
epnum = ep->desc.bEndpointAddress;
|
||||
epnum = urb->ep->desc.bEndpointAddress;
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
|
||||
&ehci_to_hcd(ehci)->flags))) {
|
||||
status = -ESHUTDOWN;
|
||||
goto done;
|
||||
goto done_not_linked;
|
||||
}
|
||||
status = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
|
||||
if (unlikely(status))
|
||||
goto done_not_linked;
|
||||
|
||||
/* get qh and force any scheduling errors */
|
||||
INIT_LIST_HEAD (&empty);
|
||||
qh = qh_append_tds (ehci, urb, &empty, epnum, &ep->hcpriv);
|
||||
qh = qh_append_tds(ehci, urb, &empty, epnum, &urb->ep->hcpriv);
|
||||
if (qh == NULL) {
|
||||
status = -ENOMEM;
|
||||
goto done;
|
||||
@ -832,13 +834,16 @@ static int intr_submit (
|
||||
}
|
||||
|
||||
/* then queue the urb's tds to the qh */
|
||||
qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv);
|
||||
qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv);
|
||||
BUG_ON (qh == NULL);
|
||||
|
||||
/* ... update usbfs periodic stats */
|
||||
ehci_to_hcd(ehci)->self.bandwidth_int_reqs++;
|
||||
|
||||
done:
|
||||
if (unlikely(status))
|
||||
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
|
||||
done_not_linked:
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
if (status)
|
||||
qtd_list_free (ehci, urb, qtd_list);
|
||||
@ -1622,7 +1627,7 @@ itd_complete (
|
||||
|
||||
/* give urb back to the driver ... can be out-of-order */
|
||||
dev = urb->dev;
|
||||
ehci_urb_done (ehci, urb);
|
||||
ehci_urb_done(ehci, urb, 0);
|
||||
urb = NULL;
|
||||
|
||||
/* defer stopping schedule; completion can submit */
|
||||
@ -1686,12 +1691,19 @@ 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)))
|
||||
&ehci_to_hcd(ehci)->flags))) {
|
||||
status = -ESHUTDOWN;
|
||||
else
|
||||
status = iso_stream_schedule (ehci, urb, stream);
|
||||
goto done_not_linked;
|
||||
}
|
||||
status = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
|
||||
if (unlikely(status))
|
||||
goto done_not_linked;
|
||||
status = iso_stream_schedule(ehci, urb, stream);
|
||||
if (likely (status == 0))
|
||||
itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
|
||||
else
|
||||
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
|
||||
done_not_linked:
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
|
||||
done:
|
||||
@ -1988,7 +2000,7 @@ sitd_complete (
|
||||
|
||||
/* give urb back to the driver */
|
||||
dev = urb->dev;
|
||||
ehci_urb_done (ehci, urb);
|
||||
ehci_urb_done(ehci, urb, 0);
|
||||
urb = NULL;
|
||||
|
||||
/* defer stopping schedule; completion can submit */
|
||||
@ -2049,12 +2061,19 @@ 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)))
|
||||
&ehci_to_hcd(ehci)->flags))) {
|
||||
status = -ESHUTDOWN;
|
||||
else
|
||||
status = iso_stream_schedule (ehci, urb, stream);
|
||||
goto done_not_linked;
|
||||
}
|
||||
status = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
|
||||
if (unlikely(status))
|
||||
goto done_not_linked;
|
||||
status = iso_stream_schedule(ehci, urb, stream);
|
||||
if (status == 0)
|
||||
sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
|
||||
else
|
||||
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
|
||||
done_not_linked:
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
|
||||
done:
|
||||
|
@ -277,12 +277,11 @@ static void preproc_atl_queue(struct isp116x *isp116x)
|
||||
processed urbs.
|
||||
*/
|
||||
static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep,
|
||||
struct urb *urb)
|
||||
struct urb *urb, int status)
|
||||
__releases(isp116x->lock) __acquires(isp116x->lock)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
urb->hcpriv = NULL;
|
||||
ep->error_count = 0;
|
||||
|
||||
if (usb_pipecontrol(urb->pipe))
|
||||
@ -290,8 +289,9 @@ __releases(isp116x->lock) __acquires(isp116x->lock)
|
||||
|
||||
urb_dbg(urb, "Finish");
|
||||
|
||||
usb_hcd_unlink_urb_from_ep(isp116x_to_hcd(isp116x), urb);
|
||||
spin_unlock(&isp116x->lock);
|
||||
usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb);
|
||||
usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb, status);
|
||||
spin_lock(&isp116x->lock);
|
||||
|
||||
/* take idle endpoints out of the schedule */
|
||||
@ -445,12 +445,7 @@ static void postproc_atl_queue(struct isp116x *isp116x)
|
||||
if (PTD_GET_ACTIVE(ptd)
|
||||
|| (cc != TD_CC_NOERROR && cc < 0x0E))
|
||||
break;
|
||||
if ((urb->transfer_flags & URB_SHORT_NOT_OK) &&
|
||||
urb->actual_length <
|
||||
urb->transfer_buffer_length)
|
||||
status = -EREMOTEIO;
|
||||
else
|
||||
status = 0;
|
||||
status = 0;
|
||||
ep->nextpid = 0;
|
||||
break;
|
||||
default:
|
||||
@ -458,14 +453,8 @@ static void postproc_atl_queue(struct isp116x *isp116x)
|
||||
}
|
||||
|
||||
done:
|
||||
if (status != -EINPROGRESS) {
|
||||
spin_lock(&urb->lock);
|
||||
if (urb->status == -EINPROGRESS)
|
||||
urb->status = status;
|
||||
spin_unlock(&urb->lock);
|
||||
}
|
||||
if (urb->status != -EINPROGRESS)
|
||||
finish_request(isp116x, ep, urb);
|
||||
if (status != -EINPROGRESS || urb->unlinked)
|
||||
finish_request(isp116x, ep, urb, status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,7 +662,7 @@ static int balance(struct isp116x *isp116x, u16 period, u16 load)
|
||||
/*-----------------------------------------------------------------*/
|
||||
|
||||
static int isp116x_urb_enqueue(struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *hep, struct urb *urb,
|
||||
struct urb *urb,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
@ -682,6 +671,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
|
||||
int is_out = !usb_pipein(pipe);
|
||||
int type = usb_pipetype(pipe);
|
||||
int epnum = usb_pipeendpoint(pipe);
|
||||
struct usb_host_endpoint *hep = urb->ep;
|
||||
struct isp116x_ep *ep = NULL;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
@ -705,7 +695,12 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
|
||||
if (!HC_IS_RUNNING(hcd->state)) {
|
||||
kfree(ep);
|
||||
ret = -ENODEV;
|
||||
goto fail;
|
||||
goto fail_not_linked;
|
||||
}
|
||||
ret = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (ret) {
|
||||
kfree(ep);
|
||||
goto fail_not_linked;
|
||||
}
|
||||
|
||||
if (hep->hcpriv)
|
||||
@ -808,16 +803,13 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
|
||||
}
|
||||
}
|
||||
|
||||
/* in case of unlink-during-submit */
|
||||
if (urb->status != -EINPROGRESS) {
|
||||
finish_request(isp116x, ep, urb);
|
||||
ret = 0;
|
||||
goto fail;
|
||||
}
|
||||
urb->hcpriv = hep;
|
||||
start_atl_transfers(isp116x);
|
||||
|
||||
fail:
|
||||
if (ret)
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
fail_not_linked:
|
||||
spin_unlock_irqrestore(&isp116x->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
@ -825,20 +817,21 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
|
||||
/*
|
||||
Dequeue URBs.
|
||||
*/
|
||||
static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
||||
static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
|
||||
int status)
|
||||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
struct usb_host_endpoint *hep;
|
||||
struct isp116x_ep *ep, *ep_act;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
spin_lock_irqsave(&isp116x->lock, flags);
|
||||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
hep = urb->hcpriv;
|
||||
/* URB already unlinked (or never linked)? */
|
||||
if (!hep) {
|
||||
spin_unlock_irqrestore(&isp116x->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
ep = hep->hcpriv;
|
||||
WARN_ON(hep != ep->hep);
|
||||
|
||||
@ -855,10 +848,10 @@ static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
||||
}
|
||||
|
||||
if (urb)
|
||||
finish_request(isp116x, ep, urb);
|
||||
|
||||
finish_request(isp116x, ep, urb, status);
|
||||
done:
|
||||
spin_unlock_irqrestore(&isp116x->lock, flags);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void isp116x_endpoint_disable(struct usb_hcd *hcd,
|
||||
|
@ -24,7 +24,7 @@
|
||||
* small: 0) header + data packets 1) just header
|
||||
*/
|
||||
static void __maybe_unused
|
||||
urb_print (struct urb * urb, char * str, int small)
|
||||
urb_print(struct urb * urb, char * str, int small, int status)
|
||||
{
|
||||
unsigned int pipe= urb->pipe;
|
||||
|
||||
@ -34,7 +34,7 @@ urb_print (struct urb * urb, char * str, int small)
|
||||
}
|
||||
|
||||
#ifndef OHCI_VERBOSE_DEBUG
|
||||
if (urb->status != 0)
|
||||
if (status != 0)
|
||||
#endif
|
||||
dbg("%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d",
|
||||
str,
|
||||
@ -46,7 +46,7 @@ urb_print (struct urb * urb, char * str, int small)
|
||||
urb->transfer_flags,
|
||||
urb->actual_length,
|
||||
urb->transfer_buffer_length,
|
||||
urb->status);
|
||||
status);
|
||||
|
||||
#ifdef OHCI_VERBOSE_DEBUG
|
||||
if (!small) {
|
||||
@ -66,7 +66,7 @@ urb_print (struct urb * urb, char * str, int small)
|
||||
urb->transfer_buffer_length: urb->actual_length;
|
||||
for (i = 0; i < 16 && i < len; i++)
|
||||
printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
|
||||
printk ("%s stat:%d\n", i < len? "...": "", urb->status);
|
||||
printk ("%s stat:%d\n", i < len? "...": "", status);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -81,7 +81,6 @@ static void ohci_dump (struct ohci_hcd *ohci, int verbose);
|
||||
static int ohci_init (struct ohci_hcd *ohci);
|
||||
static void ohci_stop (struct usb_hcd *hcd);
|
||||
static int ohci_restart (struct ohci_hcd *ohci);
|
||||
static void ohci_quirk_nec_worker (struct work_struct *work);
|
||||
|
||||
#include "ohci-hub.c"
|
||||
#include "ohci-dbg.c"
|
||||
@ -118,7 +117,6 @@ MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
|
||||
*/
|
||||
static int ohci_urb_enqueue (
|
||||
struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *ep,
|
||||
struct urb *urb,
|
||||
gfp_t mem_flags
|
||||
) {
|
||||
@ -131,11 +129,11 @@ static int ohci_urb_enqueue (
|
||||
int retval = 0;
|
||||
|
||||
#ifdef OHCI_VERBOSE_DEBUG
|
||||
urb_print (urb, "SUB", usb_pipein (pipe));
|
||||
urb_print(urb, "SUB", usb_pipein(pipe), -EINPROGRESS);
|
||||
#endif
|
||||
|
||||
/* every endpoint has a ed, locate and maybe (re)initialize it */
|
||||
if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval)))
|
||||
if (! (ed = ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval)))
|
||||
return -ENOMEM;
|
||||
|
||||
/* for the private part of the URB we need the number of TDs (size) */
|
||||
@ -200,22 +198,17 @@ static int ohci_urb_enqueue (
|
||||
retval = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* in case of unlink-during-submit */
|
||||
spin_lock (&urb->lock);
|
||||
if (urb->status != -EINPROGRESS) {
|
||||
spin_unlock (&urb->lock);
|
||||
urb->hcpriv = urb_priv;
|
||||
finish_urb (ohci, urb);
|
||||
retval = 0;
|
||||
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (retval)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* schedule the ed if needed */
|
||||
if (ed->state == ED_IDLE) {
|
||||
retval = ed_schedule (ohci, ed);
|
||||
if (retval < 0)
|
||||
goto fail0;
|
||||
if (retval < 0) {
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
goto fail;
|
||||
}
|
||||
if (ed->type == PIPE_ISOCHRONOUS) {
|
||||
u16 frame = ohci_frame_no(ohci);
|
||||
|
||||
@ -239,8 +232,6 @@ static int ohci_urb_enqueue (
|
||||
urb->hcpriv = urb_priv;
|
||||
td_submit_urb (ohci, urb);
|
||||
|
||||
fail0:
|
||||
spin_unlock (&urb->lock);
|
||||
fail:
|
||||
if (retval)
|
||||
urb_free_priv (ohci, urb_priv);
|
||||
@ -249,22 +240,26 @@ fail:
|
||||
}
|
||||
|
||||
/*
|
||||
* decouple the URB from the HC queues (TDs, urb_priv); it's
|
||||
* already marked using urb->status. reporting is always done
|
||||
* decouple the URB from the HC queues (TDs, urb_priv).
|
||||
* reporting is always done
|
||||
* asynchronously, and we might be dealing with an urb that's
|
||||
* partially transferred, or an ED with other urbs being unlinked.
|
||||
*/
|
||||
static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
|
||||
static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
#ifdef OHCI_VERBOSE_DEBUG
|
||||
urb_print (urb, "UNLINK", 1);
|
||||
urb_print(urb, "UNLINK", 1, status);
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave (&ohci->lock, flags);
|
||||
if (HC_IS_RUNNING(hcd->state)) {
|
||||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (rc) {
|
||||
; /* Do nothing */
|
||||
} else if (HC_IS_RUNNING(hcd->state)) {
|
||||
urb_priv_t *urb_priv;
|
||||
|
||||
/* Unless an IRQ completed the unlink while it was being
|
||||
@ -282,10 +277,10 @@ static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
|
||||
* any more ... just clean up every urb's memory.
|
||||
*/
|
||||
if (urb->hcpriv)
|
||||
finish_urb (ohci, urb);
|
||||
finish_urb(ohci, urb, status);
|
||||
}
|
||||
spin_unlock_irqrestore (&ohci->lock, flags);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -314,6 +309,8 @@ rescan:
|
||||
if (!HC_IS_RUNNING (hcd->state)) {
|
||||
sanitize:
|
||||
ed->state = ED_IDLE;
|
||||
if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
|
||||
ohci->eds_scheduled--;
|
||||
finish_unlinks (ohci, 0);
|
||||
}
|
||||
|
||||
@ -321,7 +318,12 @@ sanitize:
|
||||
case ED_UNLINK: /* wait for hw to finish? */
|
||||
/* major IRQ delivery trouble loses INTR_SF too... */
|
||||
if (limit-- == 0) {
|
||||
ohci_warn (ohci, "IRQ INTR_SF lossage\n");
|
||||
ohci_warn(ohci, "ED unlink timeout\n");
|
||||
if (quirk_zfmicro(ohci)) {
|
||||
ohci_warn(ohci, "Attempting ZF TD recovery\n");
|
||||
ohci->ed_to_check = ed;
|
||||
ohci->zf_delay = 2;
|
||||
}
|
||||
goto sanitize;
|
||||
}
|
||||
spin_unlock_irqrestore (&ohci->lock, flags);
|
||||
@ -379,6 +381,93 @@ ohci_shutdown (struct usb_hcd *hcd)
|
||||
(void) ohci_readl (ohci, &ohci->regs->control);
|
||||
}
|
||||
|
||||
static int check_ed(struct ohci_hcd *ohci, struct ed *ed)
|
||||
{
|
||||
return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0
|
||||
&& (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK)
|
||||
== (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK)
|
||||
&& !list_empty(&ed->td_list);
|
||||
}
|
||||
|
||||
/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes
|
||||
* an interrupt TD but neglects to add it to the donelist. On systems with
|
||||
* this chipset, we need to periodically check the state of the queues to look
|
||||
* for such "lost" TDs.
|
||||
*/
|
||||
static void unlink_watchdog_func(unsigned long _ohci)
|
||||
{
|
||||
long flags;
|
||||
unsigned max;
|
||||
unsigned seen_count = 0;
|
||||
unsigned i;
|
||||
struct ed **seen = NULL;
|
||||
struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci;
|
||||
|
||||
spin_lock_irqsave(&ohci->lock, flags);
|
||||
max = ohci->eds_scheduled;
|
||||
if (!max)
|
||||
goto done;
|
||||
|
||||
if (ohci->ed_to_check)
|
||||
goto out;
|
||||
|
||||
seen = kcalloc(max, sizeof *seen, GFP_ATOMIC);
|
||||
if (!seen)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < NUM_INTS; i++) {
|
||||
struct ed *ed = ohci->periodic[i];
|
||||
|
||||
while (ed) {
|
||||
unsigned temp;
|
||||
|
||||
/* scan this branch of the periodic schedule tree */
|
||||
for (temp = 0; temp < seen_count; temp++) {
|
||||
if (seen[temp] == ed) {
|
||||
/* we've checked it and what's after */
|
||||
ed = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ed)
|
||||
break;
|
||||
seen[seen_count++] = ed;
|
||||
if (!check_ed(ohci, ed)) {
|
||||
ed = ed->ed_next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* HC's TD list is empty, but HCD sees at least one
|
||||
* TD that's not been sent through the donelist.
|
||||
*/
|
||||
ohci->ed_to_check = ed;
|
||||
ohci->zf_delay = 2;
|
||||
|
||||
/* The HC may wait until the next frame to report the
|
||||
* TD as done through the donelist and INTR_WDH. (We
|
||||
* just *assume* it's not a multi-TD interrupt URB;
|
||||
* those could defer the IRQ more than one frame, using
|
||||
* DI...) Check again after the next INTR_SF.
|
||||
*/
|
||||
ohci_writel(ohci, OHCI_INTR_SF,
|
||||
&ohci->regs->intrstatus);
|
||||
ohci_writel(ohci, OHCI_INTR_SF,
|
||||
&ohci->regs->intrenable);
|
||||
|
||||
/* flush those writes */
|
||||
(void) ohci_readl(ohci, &ohci->regs->control);
|
||||
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
kfree(seen);
|
||||
if (ohci->eds_scheduled)
|
||||
mod_timer(&ohci->unlink_watchdog, round_jiffies_relative(HZ));
|
||||
done:
|
||||
spin_unlock_irqrestore(&ohci->lock, flags);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* HC functions
|
||||
*-------------------------------------------------------------------------*/
|
||||
@ -616,6 +705,15 @@ retry:
|
||||
mdelay ((temp >> 23) & 0x1fe);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
|
||||
if (quirk_zfmicro(ohci)) {
|
||||
/* Create timer to watch for bad queue state on ZF Micro */
|
||||
setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func,
|
||||
(unsigned long) ohci);
|
||||
|
||||
ohci->eds_scheduled = 0;
|
||||
ohci->ed_to_check = NULL;
|
||||
}
|
||||
|
||||
ohci_dump (ohci, 1);
|
||||
|
||||
return 0;
|
||||
@ -629,10 +727,11 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
struct ohci_regs __iomem *regs = ohci->regs;
|
||||
int ints;
|
||||
int ints;
|
||||
|
||||
/* we can eliminate a (slow) ohci_readl()
|
||||
if _only_ WDH caused this irq */
|
||||
* if _only_ WDH caused this irq
|
||||
*/
|
||||
if ((ohci->hcca->done_head != 0)
|
||||
&& ! (hc32_to_cpup (ohci, &ohci->hcca->done_head)
|
||||
& 0x01)) {
|
||||
@ -651,7 +750,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
||||
|
||||
if (ints & OHCI_INTR_UE) {
|
||||
// e.g. due to PCI Master/Target Abort
|
||||
if (ohci->flags & OHCI_QUIRK_NEC) {
|
||||
if (quirk_nec(ohci)) {
|
||||
/* Workaround for a silicon bug in some NEC chips used
|
||||
* in Apple's PowerBooks. Adapted from Darwin code.
|
||||
*/
|
||||
@ -713,6 +812,31 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
||||
ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrenable);
|
||||
}
|
||||
|
||||
if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) {
|
||||
spin_lock(&ohci->lock);
|
||||
if (ohci->ed_to_check) {
|
||||
struct ed *ed = ohci->ed_to_check;
|
||||
|
||||
if (check_ed(ohci, ed)) {
|
||||
/* HC thinks the TD list is empty; HCD knows
|
||||
* at least one TD is outstanding
|
||||
*/
|
||||
if (--ohci->zf_delay == 0) {
|
||||
struct td *td = list_entry(
|
||||
ed->td_list.next,
|
||||
struct td, td_list);
|
||||
ohci_warn(ohci,
|
||||
"Reclaiming orphan TD %p\n",
|
||||
td);
|
||||
takeback_td(ohci, td);
|
||||
ohci->ed_to_check = NULL;
|
||||
}
|
||||
} else
|
||||
ohci->ed_to_check = NULL;
|
||||
}
|
||||
spin_unlock(&ohci->lock);
|
||||
}
|
||||
|
||||
/* could track INTR_SO to reduce available PCI/... bandwidth */
|
||||
|
||||
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled
|
||||
@ -721,7 +845,9 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
||||
spin_lock (&ohci->lock);
|
||||
if (ohci->ed_rm_list)
|
||||
finish_unlinks (ohci, ohci_frame_no(ohci));
|
||||
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
|
||||
if ((ints & OHCI_INTR_SF) != 0
|
||||
&& !ohci->ed_rm_list
|
||||
&& !ohci->ed_to_check
|
||||
&& HC_IS_RUNNING(hcd->state))
|
||||
ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable);
|
||||
spin_unlock (&ohci->lock);
|
||||
@ -751,6 +877,9 @@ static void ohci_stop (struct usb_hcd *hcd)
|
||||
free_irq(hcd->irq, hcd);
|
||||
hcd->irq = -1;
|
||||
|
||||
if (quirk_zfmicro(ohci))
|
||||
del_timer(&ohci->unlink_watchdog);
|
||||
|
||||
remove_debug_files (ohci);
|
||||
ohci_mem_cleanup (ohci);
|
||||
if (ohci->hcca) {
|
||||
@ -798,9 +927,8 @@ static int ohci_restart (struct ohci_hcd *ohci)
|
||||
ed, ed->state);
|
||||
}
|
||||
|
||||
spin_lock (&urb->lock);
|
||||
urb->status = -ESHUTDOWN;
|
||||
spin_unlock (&urb->lock);
|
||||
if (!urb->unlinked)
|
||||
urb->unlinked = -ESHUTDOWN;
|
||||
}
|
||||
finish_unlinks (ohci, 0);
|
||||
spin_unlock_irq(&ohci->lock);
|
||||
@ -828,27 +956,6 @@ static int ohci_restart (struct ohci_hcd *ohci)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* NEC workaround */
|
||||
static void ohci_quirk_nec_worker(struct work_struct *work)
|
||||
{
|
||||
struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work);
|
||||
int status;
|
||||
|
||||
status = ohci_init(ohci);
|
||||
if (status != 0) {
|
||||
ohci_err(ohci, "Restarting NEC controller failed "
|
||||
"in ohci_init, %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = ohci_restart(ohci);
|
||||
if (status != 0)
|
||||
ohci_err(ohci, "Restarting NEC controller failed "
|
||||
"in ohci_restart, %d\n", status);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
|
||||
|
||||
MODULE_AUTHOR (DRIVER_AUTHOR);
|
||||
@ -926,11 +1033,17 @@ MODULE_LICENSE ("GPL");
|
||||
#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_OHCI_HCD_SSB
|
||||
#include "ohci-ssb.c"
|
||||
#define SSB_OHCI_DRIVER ssb_ohci_driver
|
||||
#endif
|
||||
|
||||
#if !defined(PCI_DRIVER) && \
|
||||
!defined(PLATFORM_DRIVER) && \
|
||||
!defined(OF_PLATFORM_DRIVER) && \
|
||||
!defined(SA1111_DRIVER) && \
|
||||
!defined(PS3_SYSTEM_BUS_DRIVER)
|
||||
!defined(PS3_SYSTEM_BUS_DRIVER) && \
|
||||
!defined(SSB_OHCI_DRIVER)
|
||||
#error "missing bus glue for ohci-hcd"
|
||||
#endif
|
||||
|
||||
@ -975,10 +1088,20 @@ static int __init ohci_hcd_mod_init(void)
|
||||
goto error_pci;
|
||||
#endif
|
||||
|
||||
#ifdef SSB_OHCI_DRIVER
|
||||
retval = ssb_driver_register(&SSB_OHCI_DRIVER);
|
||||
if (retval)
|
||||
goto error_ssb;
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
|
||||
/* Error path */
|
||||
#ifdef SSB_OHCI_DRIVER
|
||||
error_ssb:
|
||||
#endif
|
||||
#ifdef PCI_DRIVER
|
||||
pci_unregister_driver(&PCI_DRIVER);
|
||||
error_pci:
|
||||
#endif
|
||||
#ifdef SA1111_DRIVER
|
||||
@ -1003,6 +1126,9 @@ module_init(ohci_hcd_mod_init);
|
||||
|
||||
static void __exit ohci_hcd_mod_exit(void)
|
||||
{
|
||||
#ifdef SSB_OHCI_DRIVER
|
||||
ssb_driver_unregister(&SSB_OHCI_DRIVER);
|
||||
#endif
|
||||
#ifdef PCI_DRIVER
|
||||
pci_unregister_driver(&PCI_DRIVER);
|
||||
#endif
|
||||
|
@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
|
||||
ohci->next_statechange = jiffies;
|
||||
spin_lock_init (&ohci->lock);
|
||||
INIT_LIST_HEAD (&ohci->pending);
|
||||
INIT_WORK (&ohci->nec_work, ohci_quirk_nec_worker);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -84,7 +84,7 @@ static int ohci_quirk_zfmicro(struct usb_hcd *hcd)
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
|
||||
ohci->flags |= OHCI_QUIRK_ZFMICRO;
|
||||
ohci_dbg (ohci, "enabled Compaq ZFMicro chipset quirk\n");
|
||||
ohci_dbg(ohci, "enabled Compaq ZFMicro chipset quirks\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -113,11 +113,31 @@ static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
|
||||
|
||||
/* Check for NEC chip and apply quirk for allegedly lost interrupts.
|
||||
*/
|
||||
|
||||
static void ohci_quirk_nec_worker(struct work_struct *work)
|
||||
{
|
||||
struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work);
|
||||
int status;
|
||||
|
||||
status = ohci_init(ohci);
|
||||
if (status != 0) {
|
||||
ohci_err(ohci, "Restarting NEC controller failed in %s, %d\n",
|
||||
"ohci_init", status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = ohci_restart(ohci);
|
||||
if (status != 0)
|
||||
ohci_err(ohci, "Restarting NEC controller failed in %s, %d\n",
|
||||
"ohci_restart", status);
|
||||
}
|
||||
|
||||
static int ohci_quirk_nec(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
|
||||
ohci->flags |= OHCI_QUIRK_NEC;
|
||||
INIT_WORK(&ohci->nec_work, ohci_quirk_nec_worker);
|
||||
ohci_dbg (ohci, "enabled NEC chipset lost interrupt quirk\n");
|
||||
|
||||
return 0;
|
||||
|
@ -134,8 +134,11 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
|
||||
}
|
||||
|
||||
ohci = hcd_to_ohci(hcd);
|
||||
if (is_bigendian)
|
||||
if (is_bigendian) {
|
||||
ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC;
|
||||
if (of_device_is_compatible(dn, "mpc5200-ohci"))
|
||||
ohci->flags |= OHCI_QUIRK_FRAME_NO;
|
||||
}
|
||||
|
||||
ohci_hcd_init(ohci);
|
||||
|
||||
|
@ -73,6 +73,11 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
|
||||
|
||||
ohci = hcd_to_ohci(hcd);
|
||||
ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC;
|
||||
|
||||
#ifdef CONFIG_PPC_MPC52xx
|
||||
/* MPC52xx doesn't need frame_no shift */
|
||||
ohci->flags |= OHCI_QUIRK_FRAME_NO;
|
||||
#endif
|
||||
ohci_hcd_init(ohci);
|
||||
|
||||
retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
|
||||
|
@ -36,29 +36,15 @@ static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
|
||||
* PRECONDITION: ohci lock held, irqs blocked.
|
||||
*/
|
||||
static void
|
||||
finish_urb (struct ohci_hcd *ohci, struct urb *urb)
|
||||
finish_urb(struct ohci_hcd *ohci, struct urb *urb, int status)
|
||||
__releases(ohci->lock)
|
||||
__acquires(ohci->lock)
|
||||
{
|
||||
// ASSERT (urb->hcpriv != 0);
|
||||
|
||||
urb_free_priv (ohci, urb->hcpriv);
|
||||
urb->hcpriv = NULL;
|
||||
|
||||
spin_lock (&urb->lock);
|
||||
if (likely (urb->status == -EINPROGRESS))
|
||||
urb->status = 0;
|
||||
/* report short control reads right even though the data TD always
|
||||
* has TD_R set. (much simpler, but creates the 1-td limit.)
|
||||
*/
|
||||
if (unlikely (urb->transfer_flags & URB_SHORT_NOT_OK)
|
||||
&& unlikely (usb_pipecontrol (urb->pipe))
|
||||
&& urb->actual_length < urb->transfer_buffer_length
|
||||
&& usb_pipein (urb->pipe)
|
||||
&& urb->status == 0) {
|
||||
urb->status = -EREMOTEIO;
|
||||
}
|
||||
spin_unlock (&urb->lock);
|
||||
if (likely(status == -EINPROGRESS))
|
||||
status = 0;
|
||||
|
||||
switch (usb_pipetype (urb->pipe)) {
|
||||
case PIPE_ISOCHRONOUS:
|
||||
@ -70,12 +56,13 @@ __acquires(ohci->lock)
|
||||
}
|
||||
|
||||
#ifdef OHCI_VERBOSE_DEBUG
|
||||
urb_print (urb, "RET", usb_pipeout (urb->pipe));
|
||||
urb_print(urb, "RET", usb_pipeout (urb->pipe), status);
|
||||
#endif
|
||||
|
||||
/* urb->complete() can reenter this HCD */
|
||||
usb_hcd_unlink_urb_from_ep(ohci_to_hcd(ohci), urb);
|
||||
spin_unlock (&ohci->lock);
|
||||
usb_hcd_giveback_urb (ohci_to_hcd(ohci), urb);
|
||||
usb_hcd_giveback_urb(ohci_to_hcd(ohci), urb, status);
|
||||
spin_lock (&ohci->lock);
|
||||
|
||||
/* stop periodic dma if it's not needed */
|
||||
@ -179,6 +166,10 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
|
||||
ed->ed_prev = NULL;
|
||||
ed->ed_next = NULL;
|
||||
ed->hwNextED = 0;
|
||||
if (quirk_zfmicro(ohci)
|
||||
&& (ed->type == PIPE_INTERRUPT)
|
||||
&& !(ohci->eds_scheduled++))
|
||||
mod_timer(&ohci->unlink_watchdog, round_jiffies_relative(HZ));
|
||||
wmb ();
|
||||
|
||||
/* we care about rm_list when setting CLE/BLE in case the HC was at
|
||||
@ -708,19 +699,18 @@ static void td_submit_urb (
|
||||
* Done List handling functions
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
/* calculate transfer length/status and update the urb
|
||||
* PRECONDITION: irqsafe (only for urb->status locking)
|
||||
*/
|
||||
static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)
|
||||
/* calculate transfer length/status and update the urb */
|
||||
static int td_done(struct ohci_hcd *ohci, struct urb *urb, struct td *td)
|
||||
{
|
||||
u32 tdINFO = hc32_to_cpup (ohci, &td->hwINFO);
|
||||
int cc = 0;
|
||||
int status = -EINPROGRESS;
|
||||
|
||||
list_del (&td->td_list);
|
||||
|
||||
/* ISO ... drivers see per-TD length/status */
|
||||
if (tdINFO & TD_ISO) {
|
||||
u16 tdPSW = ohci_hwPSW (ohci, td, 0);
|
||||
u16 tdPSW = ohci_hwPSW(ohci, td, 0);
|
||||
int dlen = 0;
|
||||
|
||||
/* NOTE: assumes FC in tdINFO == 0, and that
|
||||
@ -729,7 +719,7 @@ static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)
|
||||
|
||||
cc = (tdPSW >> 12) & 0xF;
|
||||
if (tdINFO & TD_CC) /* hc didn't touch? */
|
||||
return;
|
||||
return status;
|
||||
|
||||
if (usb_pipeout (urb->pipe))
|
||||
dlen = urb->iso_frame_desc [td->index].length;
|
||||
@ -762,12 +752,8 @@ static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)
|
||||
if (cc == TD_DATAUNDERRUN
|
||||
&& !(urb->transfer_flags & URB_SHORT_NOT_OK))
|
||||
cc = TD_CC_NOERROR;
|
||||
if (cc != TD_CC_NOERROR && cc < 0x0E) {
|
||||
spin_lock (&urb->lock);
|
||||
if (urb->status == -EINPROGRESS)
|
||||
urb->status = cc_to_error [cc];
|
||||
spin_unlock (&urb->lock);
|
||||
}
|
||||
if (cc != TD_CC_NOERROR && cc < 0x0E)
|
||||
status = cc_to_error[cc];
|
||||
|
||||
/* count all non-empty packets except control SETUP packet */
|
||||
if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) {
|
||||
@ -786,14 +772,15 @@ static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)
|
||||
urb->actual_length,
|
||||
urb->transfer_buffer_length);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static inline struct td *
|
||||
ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev)
|
||||
static void ed_halted(struct ohci_hcd *ohci, struct td *td, int cc)
|
||||
{
|
||||
struct urb *urb = td->urb;
|
||||
urb_priv_t *urb_priv = urb->hcpriv;
|
||||
struct ed *ed = td->ed;
|
||||
struct list_head *tmp = td->td_list.next;
|
||||
__hc32 toggle = ed->hwHeadP & cpu_to_hc32 (ohci, ED_C);
|
||||
@ -805,13 +792,12 @@ ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev)
|
||||
wmb ();
|
||||
ed->hwHeadP &= ~cpu_to_hc32 (ohci, ED_H);
|
||||
|
||||
/* put any later tds from this urb onto the donelist, after 'td',
|
||||
* order won't matter here: no errors, and nothing was transferred.
|
||||
* also patch the ed so it looks as if those tds completed normally.
|
||||
/* Get rid of all later tds from this urb. We don't have
|
||||
* to be careful: no errors and nothing was transferred.
|
||||
* Also patch the ed so it looks as if those tds completed normally.
|
||||
*/
|
||||
while (tmp != &ed->td_list) {
|
||||
struct td *next;
|
||||
__hc32 info;
|
||||
|
||||
next = list_entry (tmp, struct td, td_list);
|
||||
tmp = next->td_list.next;
|
||||
@ -826,14 +812,9 @@ ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev)
|
||||
* then we need to leave the control STATUS packet queued
|
||||
* and clear ED_SKIP.
|
||||
*/
|
||||
info = next->hwINFO;
|
||||
info |= cpu_to_hc32 (ohci, TD_DONE);
|
||||
info &= ~cpu_to_hc32 (ohci, TD_CC);
|
||||
next->hwINFO = info;
|
||||
|
||||
next->next_dl_td = rev;
|
||||
rev = next;
|
||||
|
||||
list_del(&next->td_list);
|
||||
urb_priv->td_cnt++;
|
||||
ed->hwHeadP = next->hwNextTD | toggle;
|
||||
}
|
||||
|
||||
@ -859,8 +840,6 @@ ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev)
|
||||
hc32_to_cpu (ohci, td->hwINFO),
|
||||
cc, cc_to_error [cc]);
|
||||
}
|
||||
|
||||
return rev;
|
||||
}
|
||||
|
||||
/* replies to the request have to be on a FIFO basis so
|
||||
@ -897,7 +876,7 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
|
||||
*/
|
||||
if (cc != TD_CC_NOERROR
|
||||
&& (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H)))
|
||||
td_rev = ed_halted (ohci, td, cc, td_rev);
|
||||
ed_halted(ohci, td, cc);
|
||||
|
||||
td->next_dl_td = td_rev;
|
||||
td_rev = td;
|
||||
@ -940,8 +919,12 @@ skip_ed:
|
||||
TD_MASK;
|
||||
|
||||
/* INTR_WDH may need to clean up first */
|
||||
if (td->td_dma != head)
|
||||
goto skip_ed;
|
||||
if (td->td_dma != head) {
|
||||
if (ed == ohci->ed_to_check)
|
||||
ohci->ed_to_check = NULL;
|
||||
else
|
||||
goto skip_ed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -974,7 +957,7 @@ rescan_this:
|
||||
urb = td->urb;
|
||||
urb_priv = td->urb->hcpriv;
|
||||
|
||||
if (urb->status == -EINPROGRESS) {
|
||||
if (!urb->unlinked) {
|
||||
prev = &td->hwNextTD;
|
||||
continue;
|
||||
}
|
||||
@ -990,7 +973,7 @@ rescan_this:
|
||||
/* if URB is done, clean up */
|
||||
if (urb_priv->td_cnt == urb_priv->length) {
|
||||
modified = completed = 1;
|
||||
finish_urb (ohci, urb);
|
||||
finish_urb(ohci, urb, 0);
|
||||
}
|
||||
}
|
||||
if (completed && !list_empty (&ed->td_list))
|
||||
@ -998,6 +981,8 @@ rescan_this:
|
||||
|
||||
/* ED's now officially unlinked, hc doesn't see */
|
||||
ed->state = ED_IDLE;
|
||||
if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
|
||||
ohci->eds_scheduled--;
|
||||
ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H);
|
||||
ed->hwNextED = 0;
|
||||
wmb ();
|
||||
@ -1021,7 +1006,7 @@ rescan_this:
|
||||
|
||||
if (ohci->ed_controltail) {
|
||||
command |= OHCI_CLF;
|
||||
if (ohci->flags & OHCI_QUIRK_ZFMICRO)
|
||||
if (quirk_zfmicro(ohci))
|
||||
mdelay(1);
|
||||
if (!(ohci->hc_control & OHCI_CTRL_CLE)) {
|
||||
control |= OHCI_CTRL_CLE;
|
||||
@ -1031,7 +1016,7 @@ rescan_this:
|
||||
}
|
||||
if (ohci->ed_bulktail) {
|
||||
command |= OHCI_BLF;
|
||||
if (ohci->flags & OHCI_QUIRK_ZFMICRO)
|
||||
if (quirk_zfmicro(ohci))
|
||||
mdelay(1);
|
||||
if (!(ohci->hc_control & OHCI_CTRL_BLE)) {
|
||||
control |= OHCI_CTRL_BLE;
|
||||
@ -1043,13 +1028,13 @@ rescan_this:
|
||||
/* CLE/BLE to enable, CLF/BLF to (maybe) kickstart */
|
||||
if (control) {
|
||||
ohci->hc_control |= control;
|
||||
if (ohci->flags & OHCI_QUIRK_ZFMICRO)
|
||||
if (quirk_zfmicro(ohci))
|
||||
mdelay(1);
|
||||
ohci_writel (ohci, ohci->hc_control,
|
||||
&ohci->regs->control);
|
||||
}
|
||||
if (command) {
|
||||
if (ohci->flags & OHCI_QUIRK_ZFMICRO)
|
||||
if (quirk_zfmicro(ohci))
|
||||
mdelay(1);
|
||||
ohci_writel (ohci, command, &ohci->regs->cmdstatus);
|
||||
}
|
||||
@ -1060,12 +1045,61 @@ rescan_this:
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Used to take back a TD from the host controller. This would normally be
|
||||
* called from within dl_done_list, however it may be called directly if the
|
||||
* HC no longer sees the TD and it has not appeared on the donelist (after
|
||||
* two frames). This bug has been observed on ZF Micro systems.
|
||||
*/
|
||||
static void takeback_td(struct ohci_hcd *ohci, struct td *td)
|
||||
{
|
||||
struct urb *urb = td->urb;
|
||||
urb_priv_t *urb_priv = urb->hcpriv;
|
||||
struct ed *ed = td->ed;
|
||||
int status;
|
||||
|
||||
/* update URB's length and status from TD */
|
||||
status = td_done(ohci, urb, td);
|
||||
urb_priv->td_cnt++;
|
||||
|
||||
/* If all this urb's TDs are done, call complete() */
|
||||
if (urb_priv->td_cnt == urb_priv->length)
|
||||
finish_urb(ohci, urb, status);
|
||||
|
||||
/* clean schedule: unlink EDs that are no longer busy */
|
||||
if (list_empty(&ed->td_list)) {
|
||||
if (ed->state == ED_OPER)
|
||||
start_ed_unlink(ohci, ed);
|
||||
|
||||
/* ... reenabling halted EDs only after fault cleanup */
|
||||
} else if ((ed->hwINFO & cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE))
|
||||
== cpu_to_hc32(ohci, ED_SKIP)) {
|
||||
td = list_entry(ed->td_list.next, struct td, td_list);
|
||||
if (!(td->hwINFO & cpu_to_hc32(ohci, TD_DONE))) {
|
||||
ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP);
|
||||
/* ... hc may need waking-up */
|
||||
switch (ed->type) {
|
||||
case PIPE_CONTROL:
|
||||
ohci_writel(ohci, OHCI_CLF,
|
||||
&ohci->regs->cmdstatus);
|
||||
break;
|
||||
case PIPE_BULK:
|
||||
ohci_writel(ohci, OHCI_BLF,
|
||||
&ohci->regs->cmdstatus);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process normal completions (error or success) and clean the schedules.
|
||||
*
|
||||
* This is the main path for handing urbs back to drivers. The only other
|
||||
* path is finish_unlinks(), which unlinks URBs using ed_rm_list, instead of
|
||||
* scanning the (re-reversed) donelist as this does.
|
||||
* normal path is finish_unlinks(), which unlinks URBs using ed_rm_list,
|
||||
* instead of scanning the (re-reversed) donelist as this does. There's
|
||||
* an abnormal path too, handling a quirk in some Compaq silicon: URBs
|
||||
* with TDs that appear to be orphaned are directly reclaimed.
|
||||
*/
|
||||
static void
|
||||
dl_done_list (struct ohci_hcd *ohci)
|
||||
@ -1074,44 +1108,7 @@ dl_done_list (struct ohci_hcd *ohci)
|
||||
|
||||
while (td) {
|
||||
struct td *td_next = td->next_dl_td;
|
||||
struct urb *urb = td->urb;
|
||||
urb_priv_t *urb_priv = urb->hcpriv;
|
||||
struct ed *ed = td->ed;
|
||||
|
||||
/* update URB's length and status from TD */
|
||||
td_done (ohci, urb, td);
|
||||
urb_priv->td_cnt++;
|
||||
|
||||
/* If all this urb's TDs are done, call complete() */
|
||||
if (urb_priv->td_cnt == urb_priv->length)
|
||||
finish_urb (ohci, urb);
|
||||
|
||||
/* clean schedule: unlink EDs that are no longer busy */
|
||||
if (list_empty (&ed->td_list)) {
|
||||
if (ed->state == ED_OPER)
|
||||
start_ed_unlink (ohci, ed);
|
||||
|
||||
/* ... reenabling halted EDs only after fault cleanup */
|
||||
} else if ((ed->hwINFO & cpu_to_hc32 (ohci,
|
||||
ED_SKIP | ED_DEQUEUE))
|
||||
== cpu_to_hc32 (ohci, ED_SKIP)) {
|
||||
td = list_entry (ed->td_list.next, struct td, td_list);
|
||||
if (!(td->hwINFO & cpu_to_hc32 (ohci, TD_DONE))) {
|
||||
ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP);
|
||||
/* ... hc may need waking-up */
|
||||
switch (ed->type) {
|
||||
case PIPE_CONTROL:
|
||||
ohci_writel (ohci, OHCI_CLF,
|
||||
&ohci->regs->cmdstatus);
|
||||
break;
|
||||
case PIPE_BULK:
|
||||
ohci_writel (ohci, OHCI_BLF,
|
||||
&ohci->regs->cmdstatus);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
takeback_td(ohci, td);
|
||||
td = td_next;
|
||||
}
|
||||
}
|
||||
|
247
drivers/usb/host/ohci-ssb.c
Normal file
247
drivers/usb/host/ohci-ssb.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Sonics Silicon Backplane
|
||||
* Broadcom USB-core OHCI driver
|
||||
*
|
||||
* Copyright 2007 Michael Buesch <mb@bu3sch.de>
|
||||
*
|
||||
* Derived from the OHCI-PCI driver
|
||||
* Copyright 1999 Roman Weissgaerber
|
||||
* Copyright 2000-2002 David Brownell
|
||||
* Copyright 1999 Linus Torvalds
|
||||
* Copyright 1999 Gregory P. Smith
|
||||
*
|
||||
* Derived from the USBcore related parts of Broadcom-SB
|
||||
* Copyright 2005 Broadcom Corporation
|
||||
*
|
||||
* Licensed under the GNU/GPL. See COPYING for details.
|
||||
*/
|
||||
#include <linux/ssb/ssb.h>
|
||||
|
||||
|
||||
#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29)
|
||||
|
||||
struct ssb_ohci_device {
|
||||
struct ohci_hcd ohci; /* _must_ be at the beginning. */
|
||||
|
||||
u32 enable_flags;
|
||||
};
|
||||
|
||||
static inline
|
||||
struct ssb_ohci_device *hcd_to_ssb_ohci(struct usb_hcd *hcd)
|
||||
{
|
||||
return (struct ssb_ohci_device *)(hcd->hcd_priv);
|
||||
}
|
||||
|
||||
|
||||
static int ssb_ohci_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
|
||||
struct ohci_hcd *ohci = &ohcidev->ohci;
|
||||
int err;
|
||||
|
||||
ohci_hcd_init(ohci);
|
||||
err = ohci_init(ohci);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ssb_ohci_start(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
|
||||
struct ohci_hcd *ohci = &ohcidev->ohci;
|
||||
int err;
|
||||
|
||||
err = ohci_run(ohci);
|
||||
if (err < 0) {
|
||||
ohci_err(ohci, "can't start\n");
|
||||
ohci_stop(hcd);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ssb_ohci_hcd_suspend(struct usb_hcd *hcd, pm_message_t message)
|
||||
{
|
||||
struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
|
||||
struct ohci_hcd *ohci = &ohcidev->ohci;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ohci->lock, flags);
|
||||
|
||||
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
|
||||
ohci_readl(ohci, &ohci->regs->intrdisable); /* commit write */
|
||||
|
||||
/* make sure snapshot being resumed re-enumerates everything */
|
||||
if (message.event == PM_EVENT_PRETHAW)
|
||||
ohci_usb_reset(ohci);
|
||||
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
spin_unlock_irqrestore(&ohci->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssb_ohci_hcd_resume(struct usb_hcd *hcd)
|
||||
{
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct hc_driver ssb_ohci_hc_driver = {
|
||||
.description = "ssb-usb-ohci",
|
||||
.product_desc = "SSB OHCI Controller",
|
||||
.hcd_priv_size = sizeof(struct ssb_ohci_device),
|
||||
|
||||
.irq = ohci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB11,
|
||||
|
||||
.reset = ssb_ohci_reset,
|
||||
.start = ssb_ohci_start,
|
||||
.stop = ohci_stop,
|
||||
.shutdown = ohci_shutdown,
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ssb_ohci_hcd_suspend,
|
||||
.resume = ssb_ohci_hcd_resume,
|
||||
#endif
|
||||
|
||||
.urb_enqueue = ohci_urb_enqueue,
|
||||
.urb_dequeue = ohci_urb_dequeue,
|
||||
.endpoint_disable = ohci_endpoint_disable,
|
||||
|
||||
.get_frame_number = ohci_get_frame,
|
||||
|
||||
.hub_status_data = ohci_hub_status_data,
|
||||
.hub_control = ohci_hub_control,
|
||||
.hub_irq_enable = ohci_rhsc_enable,
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
static void ssb_ohci_detach(struct ssb_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = ssb_get_drvdata(dev);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
usb_put_hcd(hcd);
|
||||
ssb_device_disable(dev, 0);
|
||||
}
|
||||
|
||||
static int ssb_ohci_attach(struct ssb_device *dev)
|
||||
{
|
||||
struct ssb_ohci_device *ohcidev;
|
||||
struct usb_hcd *hcd;
|
||||
int err = -ENOMEM;
|
||||
u32 tmp, flags = 0;
|
||||
|
||||
if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
|
||||
flags |= SSB_OHCI_TMSLOW_HOSTMODE;
|
||||
|
||||
ssb_device_enable(dev, flags);
|
||||
|
||||
hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
|
||||
dev->dev->bus_id);
|
||||
if (!hcd)
|
||||
goto err_dev_disable;
|
||||
ohcidev = hcd_to_ssb_ohci(hcd);
|
||||
ohcidev->enable_flags = flags;
|
||||
|
||||
tmp = ssb_read32(dev, SSB_ADMATCH0);
|
||||
hcd->rsrc_start = ssb_admatch_base(tmp);
|
||||
hcd->rsrc_len = ssb_admatch_size(tmp);
|
||||
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs)
|
||||
goto err_put_hcd;
|
||||
err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED);
|
||||
if (err)
|
||||
goto err_iounmap;
|
||||
|
||||
ssb_set_drvdata(dev, hcd);
|
||||
|
||||
return err;
|
||||
|
||||
err_iounmap:
|
||||
iounmap(hcd->regs);
|
||||
err_put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
err_dev_disable:
|
||||
ssb_device_disable(dev, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ssb_ohci_probe(struct ssb_device *dev,
|
||||
const struct ssb_device_id *id)
|
||||
{
|
||||
int err;
|
||||
u16 chipid_top;
|
||||
|
||||
/* USBcores are only connected on embedded devices. */
|
||||
chipid_top = (dev->bus->chip_id & 0xFF00);
|
||||
if (chipid_top != 0x4700 && chipid_top != 0x5300)
|
||||
return -ENODEV;
|
||||
|
||||
/* TODO: Probably need checks here; is the core connected? */
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
/* We currently always attach SSB_DEV_USB11_HOSTDEV
|
||||
* as HOST OHCI. If we want to attach it as Client device,
|
||||
* we must branch here and call into the (yet to
|
||||
* be written) Client mode driver. Same for remove(). */
|
||||
|
||||
err = ssb_ohci_attach(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ssb_ohci_remove(struct ssb_device *dev)
|
||||
{
|
||||
ssb_ohci_detach(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state)
|
||||
{
|
||||
ssb_device_disable(dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssb_ohci_resume(struct ssb_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = ssb_get_drvdata(dev);
|
||||
struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
|
||||
|
||||
ssb_device_enable(dev, ohcidev->enable_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_PM */
|
||||
#define ssb_ohci_suspend NULL
|
||||
#define ssb_ohci_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
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_DEVTABLE_END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ssb, ssb_ohci_table);
|
||||
|
||||
static struct ssb_driver ssb_ohci_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = ssb_ohci_table,
|
||||
.probe = ssb_ohci_probe,
|
||||
.remove = ssb_ohci_remove,
|
||||
.suspend = ssb_ohci_suspend,
|
||||
.resume = ssb_ohci_resume,
|
||||
};
|
@ -398,11 +398,38 @@ struct ohci_hcd {
|
||||
#define OHCI_QUIRK_BE_MMIO 0x10 /* BE registers */
|
||||
#define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/
|
||||
#define OHCI_QUIRK_NEC 0x40 /* lost interrupts */
|
||||
#define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */
|
||||
// there are also chip quirks/bugs in init logic
|
||||
|
||||
struct work_struct nec_work; /* Worker for NEC quirk */
|
||||
|
||||
/* Needed for ZF Micro quirk */
|
||||
struct timer_list unlink_watchdog;
|
||||
unsigned eds_scheduled;
|
||||
struct ed *ed_to_check;
|
||||
unsigned zf_delay;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static inline int quirk_nec(struct ohci_hcd *ohci)
|
||||
{
|
||||
return ohci->flags & OHCI_QUIRK_NEC;
|
||||
}
|
||||
static inline int quirk_zfmicro(struct ohci_hcd *ohci)
|
||||
{
|
||||
return ohci->flags & OHCI_QUIRK_ZFMICRO;
|
||||
}
|
||||
#else
|
||||
static inline int quirk_nec(struct ohci_hcd *ohci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int quirk_zfmicro(struct ohci_hcd *ohci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* convert between an hcd pointer and the corresponding ohci_hcd */
|
||||
static inline struct ohci_hcd *hcd_to_ohci (struct usb_hcd *hcd)
|
||||
{
|
||||
@ -607,15 +634,12 @@ static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x)
|
||||
/* HCCA frame number is 16 bits, but is accessed as 32 bits since not all
|
||||
* hardware handles 16 bit reads. That creates a different confusion on
|
||||
* some big-endian SOC implementations. Same thing happens with PSW access.
|
||||
*
|
||||
* FIXME: Deal with that as a runtime quirk when STB03xxx is ported over
|
||||
* to arch/powerpc
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_STB03xxx
|
||||
#define OHCI_BE_FRAME_NO_SHIFT 16
|
||||
#ifdef CONFIG_PPC_MPC52xx
|
||||
#define big_endian_frame_no_quirk(ohci) (ohci->flags & OHCI_QUIRK_FRAME_NO)
|
||||
#else
|
||||
#define OHCI_BE_FRAME_NO_SHIFT 0
|
||||
#define big_endian_frame_no_quirk(ohci) 0
|
||||
#endif
|
||||
|
||||
static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
|
||||
@ -623,7 +647,8 @@ static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
|
||||
u32 tmp;
|
||||
if (big_endian_desc(ohci)) {
|
||||
tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no);
|
||||
tmp >>= OHCI_BE_FRAME_NO_SHIFT;
|
||||
if (!big_endian_frame_no_quirk(ohci))
|
||||
tmp >>= 16;
|
||||
} else
|
||||
tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no);
|
||||
|
||||
|
@ -782,10 +782,12 @@ static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address)
|
||||
kfree(td);
|
||||
|
||||
if (urb) {
|
||||
urb->status = -ENODEV;
|
||||
urb->hcpriv = NULL;
|
||||
usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597),
|
||||
urb);
|
||||
|
||||
spin_unlock(&r8a66597->lock);
|
||||
usb_hcd_giveback_urb(r8a66597_to_hcd(r8a66597), urb);
|
||||
usb_hcd_giveback_urb(r8a66597_to_hcd(r8a66597), urb,
|
||||
-ENODEV);
|
||||
spin_lock(&r8a66597->lock);
|
||||
}
|
||||
break;
|
||||
@ -832,7 +834,7 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
|
||||
info.pipenum = get_empty_pipenum(r8a66597, ep);
|
||||
info.address = get_urb_to_r8a66597_addr(r8a66597, urb);
|
||||
info.epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
info.maxpacket = ep->wMaxPacketSize;
|
||||
info.maxpacket = le16_to_cpu(ep->wMaxPacketSize);
|
||||
info.type = get_r8a66597_type(ep->bmAttributes
|
||||
& USB_ENDPOINT_XFERTYPE_MASK);
|
||||
info.bufnum = get_bufnum(info.pipenum);
|
||||
@ -923,7 +925,7 @@ static void prepare_setup_packet(struct r8a66597 *r8a66597,
|
||||
r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
r8a66597_write(r8a66597, p[i], setup_addr);
|
||||
r8a66597_write(r8a66597, cpu_to_le16(p[i]), setup_addr);
|
||||
setup_addr += 2;
|
||||
}
|
||||
r8a66597_write(r8a66597, SUREQ, DCPCTR);
|
||||
@ -1032,6 +1034,15 @@ static void prepare_status_packet(struct r8a66597 *r8a66597,
|
||||
pipe_start(r8a66597, td->pipe);
|
||||
}
|
||||
|
||||
static int is_set_address(unsigned char *setup_packet)
|
||||
{
|
||||
if (((setup_packet[0] & USB_TYPE_MASK) == USB_TYPE_STANDARD) &&
|
||||
setup_packet[1] == USB_REQ_SET_ADDRESS)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this function must be called with interrupt disabled */
|
||||
static int start_transfer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
|
||||
{
|
||||
@ -1039,7 +1050,7 @@ static int start_transfer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
|
||||
|
||||
switch (td->type) {
|
||||
case USB_PID_SETUP:
|
||||
if (td->urb->setup_packet[1] == USB_REQ_SET_ADDRESS) {
|
||||
if (is_set_address(td->urb->setup_packet)) {
|
||||
td->set_address = 1;
|
||||
td->urb->setup_packet[2] = alloc_usb_address(r8a66597,
|
||||
td->urb);
|
||||
@ -1106,8 +1117,9 @@ static void set_td_timer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
|
||||
}
|
||||
|
||||
/* this function must be called with interrupt disabled */
|
||||
static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td,
|
||||
u16 pipenum, struct urb *urb)
|
||||
static void finish_request(struct r8a66597 *r8a66597, struct r8a66597_td *td,
|
||||
u16 pipenum, struct urb *urb, int status)
|
||||
__releases(r8a66597->lock) __acquires(r8a66597->lock)
|
||||
{
|
||||
int restart = 0;
|
||||
struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
|
||||
@ -1115,7 +1127,7 @@ static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td,
|
||||
r8a66597->timeout_map &= ~(1 << pipenum);
|
||||
|
||||
if (likely(td)) {
|
||||
if (td->set_address && urb->status != 0)
|
||||
if (td->set_address && (status != 0 || urb->unlinked))
|
||||
r8a66597->address_map &= ~(1 << urb->setup_packet[2]);
|
||||
|
||||
pipe_toggle_save(r8a66597, td->pipe, urb);
|
||||
@ -1130,9 +1142,9 @@ static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td,
|
||||
if (usb_pipeisoc(urb->pipe))
|
||||
urb->start_frame = r8a66597_get_frame(hcd);
|
||||
|
||||
urb->hcpriv = NULL;
|
||||
usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597), urb);
|
||||
spin_unlock(&r8a66597->lock);
|
||||
usb_hcd_giveback_urb(hcd, urb);
|
||||
usb_hcd_giveback_urb(hcd, urb, status);
|
||||
spin_lock(&r8a66597->lock);
|
||||
}
|
||||
|
||||
@ -1146,14 +1158,6 @@ static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td,
|
||||
}
|
||||
}
|
||||
|
||||
/* this function must be called with interrupt disabled */
|
||||
static void finish_request(struct r8a66597 *r8a66597, struct r8a66597_td *td,
|
||||
u16 pipenum, struct urb *urb)
|
||||
__releases(r8a66597->lock) __acquires(r8a66597->lock)
|
||||
{
|
||||
done(r8a66597, td, pipenum, urb);
|
||||
}
|
||||
|
||||
static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
{
|
||||
u16 tmp;
|
||||
@ -1162,6 +1166,7 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
|
||||
struct urb *urb;
|
||||
int finish = 0;
|
||||
int status = 0;
|
||||
|
||||
if (unlikely(!td))
|
||||
return;
|
||||
@ -1170,17 +1175,15 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
fifo_change_from_pipe(r8a66597, td->pipe);
|
||||
tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
|
||||
if (unlikely((tmp & FRDY) == 0)) {
|
||||
urb->status = -EPIPE;
|
||||
pipe_stop(r8a66597, td->pipe);
|
||||
pipe_irq_disable(r8a66597, pipenum);
|
||||
err("in fifo not ready (%d)", pipenum);
|
||||
finish_request(r8a66597, td, pipenum, td->urb);
|
||||
finish_request(r8a66597, td, pipenum, td->urb, -EPIPE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* prepare parameters */
|
||||
rcv_len = tmp & DTLN;
|
||||
bufsize = td->maxpacket;
|
||||
if (usb_pipeisoc(urb->pipe)) {
|
||||
buf = (u16 *)(urb->transfer_buffer +
|
||||
urb->iso_frame_desc[td->iso_cnt].offset);
|
||||
@ -1189,29 +1192,31 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
buf = (void *)urb->transfer_buffer + urb->actual_length;
|
||||
urb_len = urb->transfer_buffer_length - urb->actual_length;
|
||||
}
|
||||
if (rcv_len < bufsize)
|
||||
size = min(rcv_len, urb_len);
|
||||
else
|
||||
size = min(bufsize, urb_len);
|
||||
bufsize = min(urb_len, (int) td->maxpacket);
|
||||
if (rcv_len <= bufsize) {
|
||||
size = rcv_len;
|
||||
} else {
|
||||
size = bufsize;
|
||||
status = -EOVERFLOW;
|
||||
finish = 1;
|
||||
}
|
||||
|
||||
/* update parameters */
|
||||
urb->actual_length += size;
|
||||
if (rcv_len == 0)
|
||||
td->zero_packet = 1;
|
||||
if ((size % td->maxpacket) > 0) {
|
||||
if (rcv_len < bufsize) {
|
||||
td->short_packet = 1;
|
||||
if (urb->transfer_buffer_length != urb->actual_length &&
|
||||
urb->transfer_flags & URB_SHORT_NOT_OK)
|
||||
td->urb->status = -EREMOTEIO;
|
||||
}
|
||||
if (usb_pipeisoc(urb->pipe)) {
|
||||
urb->iso_frame_desc[td->iso_cnt].actual_length = size;
|
||||
urb->iso_frame_desc[td->iso_cnt].status = 0;
|
||||
urb->iso_frame_desc[td->iso_cnt].status = status;
|
||||
td->iso_cnt++;
|
||||
finish = 0;
|
||||
}
|
||||
|
||||
/* check transfer finish */
|
||||
if (check_transfer_finish(td, urb)) {
|
||||
if (finish || check_transfer_finish(td, urb)) {
|
||||
pipe_stop(r8a66597, td->pipe);
|
||||
pipe_irq_disable(r8a66597, pipenum);
|
||||
finish = 1;
|
||||
@ -1226,11 +1231,8 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
buf, size);
|
||||
}
|
||||
|
||||
if (finish && pipenum != 0) {
|
||||
if (td->urb->status == -EINPROGRESS)
|
||||
td->urb->status = 0;
|
||||
finish_request(r8a66597, td, pipenum, urb);
|
||||
}
|
||||
if (finish && pipenum != 0)
|
||||
finish_request(r8a66597, td, pipenum, urb, status);
|
||||
}
|
||||
|
||||
static void packet_write(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
@ -1248,11 +1250,10 @@ static void packet_write(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
fifo_change_from_pipe(r8a66597, td->pipe);
|
||||
tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
|
||||
if (unlikely((tmp & FRDY) == 0)) {
|
||||
urb->status = -EPIPE;
|
||||
pipe_stop(r8a66597, td->pipe);
|
||||
pipe_irq_disable(r8a66597, pipenum);
|
||||
err("out write fifo not ready. (%d)", pipenum);
|
||||
finish_request(r8a66597, td, pipenum, td->urb);
|
||||
finish_request(r8a66597, td, pipenum, urb, -EPIPE);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1297,7 +1298,7 @@ static void packet_write(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
}
|
||||
|
||||
|
||||
static void check_next_phase(struct r8a66597 *r8a66597)
|
||||
static void check_next_phase(struct r8a66597 *r8a66597, int status)
|
||||
{
|
||||
struct r8a66597_td *td = r8a66597_get_td(r8a66597, 0);
|
||||
struct urb *urb;
|
||||
@ -1310,49 +1311,41 @@ static void check_next_phase(struct r8a66597 *r8a66597)
|
||||
switch (td->type) {
|
||||
case USB_PID_IN:
|
||||
case USB_PID_OUT:
|
||||
if (urb->status != -EINPROGRESS) {
|
||||
finish = 1;
|
||||
break;
|
||||
}
|
||||
if (check_transfer_finish(td, urb))
|
||||
td->type = USB_PID_ACK;
|
||||
break;
|
||||
case USB_PID_SETUP:
|
||||
if (urb->status != -EINPROGRESS)
|
||||
finish = 1;
|
||||
else if (urb->transfer_buffer_length == urb->actual_length) {
|
||||
if (urb->transfer_buffer_length == urb->actual_length)
|
||||
td->type = USB_PID_ACK;
|
||||
urb->status = 0;
|
||||
} else if (usb_pipeout(urb->pipe))
|
||||
else if (usb_pipeout(urb->pipe))
|
||||
td->type = USB_PID_OUT;
|
||||
else
|
||||
td->type = USB_PID_IN;
|
||||
break;
|
||||
case USB_PID_ACK:
|
||||
finish = 1;
|
||||
if (urb->status == -EINPROGRESS)
|
||||
urb->status = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (finish)
|
||||
finish_request(r8a66597, td, 0, urb);
|
||||
if (finish || status != 0 || urb->unlinked)
|
||||
finish_request(r8a66597, td, 0, urb, status);
|
||||
else
|
||||
start_transfer(r8a66597, td);
|
||||
}
|
||||
|
||||
static void set_urb_error(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
static int get_urb_error(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
{
|
||||
struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
|
||||
|
||||
if (td && td->urb) {
|
||||
if (td) {
|
||||
u16 pid = r8a66597_read(r8a66597, td->pipe->pipectr) & PID;
|
||||
|
||||
if (pid == PID_NAK)
|
||||
td->urb->status = -ECONNRESET;
|
||||
return -ECONNRESET;
|
||||
else
|
||||
td->urb->status = -EPIPE;
|
||||
return -EPIPE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void irq_pipe_ready(struct r8a66597 *r8a66597)
|
||||
@ -1371,7 +1364,7 @@ static void irq_pipe_ready(struct r8a66597 *r8a66597)
|
||||
packet_read(r8a66597, 0);
|
||||
else
|
||||
pipe_irq_disable(r8a66597, 0);
|
||||
check_next_phase(r8a66597);
|
||||
check_next_phase(r8a66597, 0);
|
||||
}
|
||||
|
||||
for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
|
||||
@ -1405,7 +1398,7 @@ static void irq_pipe_empty(struct r8a66597 *r8a66597)
|
||||
td = r8a66597_get_td(r8a66597, 0);
|
||||
if (td && td->type != USB_PID_OUT)
|
||||
disable_irq_empty(r8a66597, 0);
|
||||
check_next_phase(r8a66597);
|
||||
check_next_phase(r8a66597, 0);
|
||||
}
|
||||
|
||||
for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
|
||||
@ -1420,9 +1413,8 @@ static void irq_pipe_empty(struct r8a66597 *r8a66597)
|
||||
if ((tmp & INBUFM) == 0) {
|
||||
disable_irq_empty(r8a66597, pipenum);
|
||||
pipe_irq_disable(r8a66597, pipenum);
|
||||
if (td->urb->status == -EINPROGRESS)
|
||||
td->urb->status = 0;
|
||||
finish_request(r8a66597, td, pipenum, td->urb);
|
||||
finish_request(r8a66597, td, pipenum, td->urb,
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1433,15 +1425,16 @@ static void irq_pipe_nrdy(struct r8a66597 *r8a66597)
|
||||
u16 check;
|
||||
u16 pipenum;
|
||||
u16 mask;
|
||||
int status;
|
||||
|
||||
mask = r8a66597_read(r8a66597, NRDYSTS)
|
||||
& r8a66597_read(r8a66597, NRDYENB);
|
||||
r8a66597_write(r8a66597, ~mask, NRDYSTS);
|
||||
if (mask & NRDY0) {
|
||||
cfifo_change(r8a66597, 0);
|
||||
set_urb_error(r8a66597, 0);
|
||||
status = get_urb_error(r8a66597, 0);
|
||||
pipe_irq_disable(r8a66597, 0);
|
||||
check_next_phase(r8a66597);
|
||||
check_next_phase(r8a66597, status);
|
||||
}
|
||||
|
||||
for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
|
||||
@ -1452,10 +1445,10 @@ static void irq_pipe_nrdy(struct r8a66597 *r8a66597)
|
||||
if (unlikely(!td))
|
||||
continue;
|
||||
|
||||
set_urb_error(r8a66597, pipenum);
|
||||
status = get_urb_error(r8a66597, pipenum);
|
||||
pipe_irq_disable(r8a66597, pipenum);
|
||||
pipe_stop(r8a66597, td->pipe);
|
||||
finish_request(r8a66597, td, pipenum, td->urb);
|
||||
finish_request(r8a66597, td, pipenum, td->urb, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1475,6 +1468,7 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
|
||||
u16 intsts0, intsts1, intsts2;
|
||||
u16 intenb0, intenb1, intenb2;
|
||||
u16 mask0, mask1, mask2;
|
||||
int status;
|
||||
|
||||
spin_lock(&r8a66597->lock);
|
||||
|
||||
@ -1518,12 +1512,12 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
|
||||
}
|
||||
if (mask1 & SIGN) {
|
||||
r8a66597_write(r8a66597, ~SIGN, INTSTS1);
|
||||
set_urb_error(r8a66597, 0);
|
||||
check_next_phase(r8a66597);
|
||||
status = get_urb_error(r8a66597, 0);
|
||||
check_next_phase(r8a66597, status);
|
||||
}
|
||||
if (mask1 & SACK) {
|
||||
r8a66597_write(r8a66597, ~SACK, INTSTS1);
|
||||
check_next_phase(r8a66597);
|
||||
check_next_phase(r8a66597, 0);
|
||||
}
|
||||
}
|
||||
if (mask0) {
|
||||
@ -1722,21 +1716,25 @@ static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597,
|
||||
}
|
||||
|
||||
static int r8a66597_urb_enqueue(struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *hep,
|
||||
struct urb *urb,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct usb_host_endpoint *hep = urb->ep;
|
||||
struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
|
||||
struct r8a66597_td *td = NULL;
|
||||
int ret = 0, request = 0;
|
||||
int ret, request = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&r8a66597->lock, flags);
|
||||
if (!get_urb_to_r8a66597_dev(r8a66597, urb)) {
|
||||
ret = -ENODEV;
|
||||
goto error;
|
||||
goto error_not_linked;
|
||||
}
|
||||
|
||||
ret = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (ret)
|
||||
goto error_not_linked;
|
||||
|
||||
if (!hep->hcpriv) {
|
||||
hep->hcpriv = kzalloc(sizeof(struct r8a66597_pipe),
|
||||
GFP_ATOMIC);
|
||||
@ -1761,15 +1759,7 @@ static int r8a66597_urb_enqueue(struct usb_hcd *hcd,
|
||||
if (list_empty(&r8a66597->pipe_queue[td->pipenum]))
|
||||
request = 1;
|
||||
list_add_tail(&td->queue, &r8a66597->pipe_queue[td->pipenum]);
|
||||
|
||||
spin_lock(&urb->lock);
|
||||
if (urb->status != -EINPROGRESS) {
|
||||
spin_unlock(&urb->lock);
|
||||
ret = -EPIPE;
|
||||
goto error;
|
||||
}
|
||||
urb->hcpriv = td;
|
||||
spin_unlock(&urb->lock);
|
||||
|
||||
if (request) {
|
||||
ret = start_transfer(r8a66597, td);
|
||||
@ -1781,26 +1771,36 @@ static int r8a66597_urb_enqueue(struct usb_hcd *hcd,
|
||||
set_td_timer(r8a66597, td);
|
||||
|
||||
error:
|
||||
if (ret)
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
error_not_linked:
|
||||
spin_unlock_irqrestore(&r8a66597->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
||||
static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
|
||||
int status)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
|
||||
struct r8a66597_td *td;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
spin_lock_irqsave(&r8a66597->lock, flags);
|
||||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
if (urb->hcpriv) {
|
||||
td = urb->hcpriv;
|
||||
pipe_stop(r8a66597, td->pipe);
|
||||
pipe_irq_disable(r8a66597, td->pipenum);
|
||||
disable_irq_empty(r8a66597, td->pipenum);
|
||||
done(r8a66597, td, td->pipenum, urb);
|
||||
finish_request(r8a66597, td, td->pipenum, urb, status);
|
||||
}
|
||||
done:
|
||||
spin_unlock_irqrestore(&r8a66597->lock, flags);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void r8a66597_endpoint_disable(struct usb_hcd *hcd,
|
||||
@ -1830,7 +1830,7 @@ static void r8a66597_endpoint_disable(struct usb_hcd *hcd,
|
||||
td = r8a66597_get_td(r8a66597, pipenum);
|
||||
if (td)
|
||||
urb = td->urb;
|
||||
done(r8a66597, td, pipenum, urb);
|
||||
finish_request(r8a66597, td, pipenum, urb, -ESHUTDOWN);
|
||||
kfree(hep->hcpriv);
|
||||
hep->hcpriv = NULL;
|
||||
spin_unlock_irqrestore(&r8a66597->lock, flags);
|
||||
@ -2027,7 +2027,7 @@ static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
case GetPortStatus:
|
||||
if (wIndex > R8A66597_MAX_ROOT_HUB)
|
||||
goto error;
|
||||
*(u32 *)buf = rh->port;
|
||||
*(u32 *)buf = cpu_to_le32(rh->port);
|
||||
break;
|
||||
case SetPortFeature:
|
||||
if (wIndex > R8A66597_MAX_ROOT_HUB)
|
||||
@ -2126,8 +2126,8 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
|
||||
struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
|
||||
|
||||
del_timer_sync(&r8a66597->rh_timer);
|
||||
iounmap((void *)r8a66597->reg);
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap((void *)r8a66597->reg);
|
||||
usb_put_hcd(hcd);
|
||||
return 0;
|
||||
}
|
||||
|
@ -435,14 +435,9 @@ static void finish_request(
|
||||
if (usb_pipecontrol(urb->pipe))
|
||||
ep->nextpid = USB_PID_SETUP;
|
||||
|
||||
spin_lock(&urb->lock);
|
||||
if (urb->status == -EINPROGRESS)
|
||||
urb->status = status;
|
||||
urb->hcpriv = NULL;
|
||||
spin_unlock(&urb->lock);
|
||||
|
||||
usb_hcd_unlink_urb_from_ep(sl811_to_hcd(sl811), urb);
|
||||
spin_unlock(&sl811->lock);
|
||||
usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb);
|
||||
usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb, status);
|
||||
spin_lock(&sl811->lock);
|
||||
|
||||
/* leave active endpoints in the schedule */
|
||||
@ -538,35 +533,21 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank)
|
||||
bank + SL11H_XFERCNTREG);
|
||||
if (len > ep->length) {
|
||||
len = ep->length;
|
||||
urb->status = -EOVERFLOW;
|
||||
urbstat = -EOVERFLOW;
|
||||
}
|
||||
urb->actual_length += len;
|
||||
sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0),
|
||||
buf, len);
|
||||
usb_dotoggle(udev, ep->epnum, 0);
|
||||
if (urb->actual_length == urb->transfer_buffer_length)
|
||||
urbstat = 0;
|
||||
else if (len < ep->maxpacket) {
|
||||
if (urb->transfer_flags & URB_SHORT_NOT_OK)
|
||||
urbstat = -EREMOTEIO;
|
||||
if (urbstat == -EINPROGRESS &&
|
||||
(len < ep->maxpacket ||
|
||||
urb->actual_length ==
|
||||
urb->transfer_buffer_length)) {
|
||||
if (usb_pipecontrol(urb->pipe))
|
||||
ep->nextpid = USB_PID_ACK;
|
||||
else
|
||||
urbstat = 0;
|
||||
}
|
||||
if (usb_pipecontrol(urb->pipe)
|
||||
&& (urbstat == -EREMOTEIO
|
||||
|| urbstat == 0)) {
|
||||
|
||||
/* NOTE if the status stage STALLs (why?),
|
||||
* this reports the wrong urb status.
|
||||
*/
|
||||
spin_lock(&urb->lock);
|
||||
if (urb->status == -EINPROGRESS)
|
||||
urb->status = urbstat;
|
||||
spin_unlock(&urb->lock);
|
||||
|
||||
urb = NULL;
|
||||
ep->nextpid = USB_PID_ACK;
|
||||
}
|
||||
break;
|
||||
case USB_PID_SETUP:
|
||||
// PACKET("...ACK/setup_%02x qh%p\n", bank, ep);
|
||||
@ -605,7 +586,7 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank)
|
||||
bank, status, ep, urbstat);
|
||||
}
|
||||
|
||||
if (urb && (urbstat != -EINPROGRESS || urb->status != -EINPROGRESS))
|
||||
if (urbstat != -EINPROGRESS || urb->unlinked)
|
||||
finish_request(sl811, ep, urb, urbstat);
|
||||
}
|
||||
|
||||
@ -807,7 +788,6 @@ static int balance(struct sl811 *sl811, u16 period, u16 load)
|
||||
|
||||
static int sl811h_urb_enqueue(
|
||||
struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *hep,
|
||||
struct urb *urb,
|
||||
gfp_t mem_flags
|
||||
) {
|
||||
@ -820,7 +800,8 @@ static int sl811h_urb_enqueue(
|
||||
struct sl811h_ep *ep = NULL;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
int retval = 0;
|
||||
int retval;
|
||||
struct usb_host_endpoint *hep = urb->ep;
|
||||
|
||||
#ifdef DISABLE_ISO
|
||||
if (type == PIPE_ISOCHRONOUS)
|
||||
@ -838,7 +819,12 @@ static int sl811h_urb_enqueue(
|
||||
|| !HC_IS_RUNNING(hcd->state)) {
|
||||
retval = -ENODEV;
|
||||
kfree(ep);
|
||||
goto fail;
|
||||
goto fail_not_linked;
|
||||
}
|
||||
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (retval) {
|
||||
kfree(ep);
|
||||
goto fail_not_linked;
|
||||
}
|
||||
|
||||
if (hep->hcpriv) {
|
||||
@ -951,37 +937,31 @@ static int sl811h_urb_enqueue(
|
||||
sofirq_on(sl811);
|
||||
}
|
||||
|
||||
/* in case of unlink-during-submit */
|
||||
spin_lock(&urb->lock);
|
||||
if (urb->status != -EINPROGRESS) {
|
||||
spin_unlock(&urb->lock);
|
||||
finish_request(sl811, ep, urb, 0);
|
||||
retval = 0;
|
||||
goto fail;
|
||||
}
|
||||
urb->hcpriv = hep;
|
||||
spin_unlock(&urb->lock);
|
||||
|
||||
start_transfer(sl811);
|
||||
sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
|
||||
fail:
|
||||
if (retval)
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
fail_not_linked:
|
||||
spin_unlock_irqrestore(&sl811->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
||||
static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
struct sl811 *sl811 = hcd_to_sl811(hcd);
|
||||
struct usb_host_endpoint *hep;
|
||||
unsigned long flags;
|
||||
struct sl811h_ep *ep;
|
||||
int retval = 0;
|
||||
int retval;
|
||||
|
||||
spin_lock_irqsave(&sl811->lock, flags);
|
||||
hep = urb->hcpriv;
|
||||
if (!hep)
|
||||
retval = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (retval)
|
||||
goto fail;
|
||||
|
||||
hep = urb->hcpriv;
|
||||
ep = hep->hcpriv;
|
||||
if (ep) {
|
||||
/* finish right away if this urb can't be active ...
|
||||
@ -1029,8 +1009,8 @@ static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
||||
VDBG("dequeue, urb %p active %s; wait4irq\n", urb,
|
||||
(sl811->active_a == ep) ? "A" : "B");
|
||||
} else
|
||||
fail:
|
||||
retval = -EINVAL;
|
||||
fail:
|
||||
spin_unlock_irqrestore(&sl811->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
@ -51,7 +51,6 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
@ -184,7 +183,7 @@ struct u132_ring {
|
||||
struct u132 {
|
||||
struct kref kref;
|
||||
struct list_head u132_list;
|
||||
struct semaphore sw_lock;
|
||||
struct mutex sw_lock;
|
||||
struct semaphore scheduler_lock;
|
||||
struct u132_platform_data *board;
|
||||
struct platform_device *platform_dev;
|
||||
@ -493,20 +492,20 @@ static void u132_hcd_monitor_work(struct work_struct *work)
|
||||
return;
|
||||
} else {
|
||||
int retval;
|
||||
down(&u132->sw_lock);
|
||||
mutex_lock(&u132->sw_lock);
|
||||
retval = read_roothub_info(u132);
|
||||
if (retval) {
|
||||
struct usb_hcd *hcd = u132_to_hcd(u132);
|
||||
u132_disable(u132);
|
||||
u132->going = 1;
|
||||
up(&u132->sw_lock);
|
||||
mutex_unlock(&u132->sw_lock);
|
||||
usb_hc_died(hcd);
|
||||
ftdi_elan_gone_away(u132->platform_dev);
|
||||
u132_monitor_put_kref(u132);
|
||||
return;
|
||||
} else {
|
||||
u132_monitor_requeue_work(u132, 500);
|
||||
up(&u132->sw_lock);
|
||||
mutex_unlock(&u132->sw_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -519,9 +518,8 @@ static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp,
|
||||
unsigned long irqs;
|
||||
struct usb_hcd *hcd = u132_to_hcd(u132);
|
||||
urb->error_count = 0;
|
||||
urb->status = status;
|
||||
urb->hcpriv = NULL;
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
endp->queue_next += 1;
|
||||
if (ENDP_QUEUE_SIZE > --endp->queue_size) {
|
||||
endp->active = 0;
|
||||
@ -543,7 +541,7 @@ static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp,
|
||||
u132_ring_queue_work(u132, ring, 0);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_endp_put_kref(u132, endp);
|
||||
usb_hcd_giveback_urb(hcd, urb);
|
||||
usb_hcd_giveback_urb(hcd, urb, status);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -559,9 +557,8 @@ static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp,
|
||||
unsigned long irqs;
|
||||
struct usb_hcd *hcd = u132_to_hcd(u132);
|
||||
urb->error_count = 0;
|
||||
urb->status = status;
|
||||
urb->hcpriv = NULL;
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
endp->queue_next += 1;
|
||||
if (ENDP_QUEUE_SIZE > --endp->queue_size) {
|
||||
endp->active = 0;
|
||||
@ -576,7 +573,7 @@ static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp,
|
||||
endp->active = 0;
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
|
||||
kfree(urbq);
|
||||
} usb_hcd_giveback_urb(hcd, urb);
|
||||
} usb_hcd_giveback_urb(hcd, urb, status);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -646,12 +643,12 @@ static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
struct u132_ring *ring = endp->ring;
|
||||
u8 *u = urb->transfer_buffer + urb->actual_length;
|
||||
u8 *b = buf;
|
||||
@ -717,10 +714,10 @@ static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf,
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -745,12 +742,12 @@ static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
struct u132_ring *ring = endp->ring;
|
||||
urb->actual_length += len;
|
||||
endp->toggle_bits = toggle_bits;
|
||||
@ -769,10 +766,10 @@ static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf,
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -798,12 +795,12 @@ static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
struct u132_ring *ring = endp->ring;
|
||||
u8 *u = urb->transfer_buffer + urb->actual_length;
|
||||
u8 *b = buf;
|
||||
@ -872,10 +869,10 @@ static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf,
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -899,20 +896,20 @@ static void u132_hcd_configure_empty_sent(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -937,12 +934,12 @@ static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
struct u132_ring *ring = endp->ring;
|
||||
u8 *u = urb->transfer_buffer;
|
||||
u8 *b = buf;
|
||||
@ -981,10 +978,10 @@ static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf,
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1008,20 +1005,20 @@ static void u132_hcd_configure_empty_recv(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1046,12 +1043,12 @@ static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
if (usb_pipein(urb->pipe)) {
|
||||
int retval;
|
||||
struct u132_ring *ring = endp->ring;
|
||||
@ -1078,10 +1075,10 @@ static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf,
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1107,22 +1104,22 @@ static void u132_hcd_enumeration_empty_recv(void *data, struct urb *urb,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
u132->addr[0].address = 0;
|
||||
endp->usb_addr = udev->usb_addr;
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1146,12 +1143,12 @@ static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
int retval;
|
||||
struct u132_ring *ring = endp->ring;
|
||||
up(&u132->scheduler_lock);
|
||||
@ -1163,10 +1160,10 @@ static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, retval);
|
||||
return;
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1190,20 +1187,20 @@ static void u132_hcd_initial_empty_sent(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1228,12 +1225,12 @@ static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
int retval;
|
||||
struct u132_ring *ring = endp->ring;
|
||||
u8 *u = urb->transfer_buffer;
|
||||
@ -1252,10 +1249,10 @@ static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, retval);
|
||||
return;
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1280,12 +1277,12 @@ static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
|
||||
return;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
|
||||
return;
|
||||
} else if (urb->status == -EINPROGRESS) {
|
||||
} else if (!urb->unlinked) {
|
||||
int retval;
|
||||
struct u132_ring *ring = endp->ring;
|
||||
up(&u132->scheduler_lock);
|
||||
@ -1297,10 +1294,10 @@ static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf,
|
||||
u132_hcd_giveback_urb(u132, endp, urb, retval);
|
||||
return;
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
|
||||
"s=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p "
|
||||
"unlinked=%d\n", urb, urb->unlinked);
|
||||
up(&u132->scheduler_lock);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_giveback_urb(u132, endp, urb, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1805,10 +1802,10 @@ static void u132_hcd_stop(struct usb_hcd *hcd)
|
||||
dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
|
||||
"ed\n", hcd);
|
||||
} else {
|
||||
down(&u132->sw_lock);
|
||||
mutex_lock(&u132->sw_lock);
|
||||
msleep(100);
|
||||
u132_power(u132, 0);
|
||||
up(&u132->sw_lock);
|
||||
mutex_unlock(&u132->sw_lock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1830,7 +1827,7 @@ static int u132_hcd_start(struct usb_hcd *hcd)
|
||||
(pdev->dev.platform_data))->vendor;
|
||||
u16 device = ((struct u132_platform_data *)
|
||||
(pdev->dev.platform_data))->device;
|
||||
down(&u132->sw_lock);
|
||||
mutex_lock(&u132->sw_lock);
|
||||
msleep(10);
|
||||
if (vendor == PCI_VENDOR_ID_AMD && device == 0x740c) {
|
||||
u132->flags = OHCI_QUIRK_AMD756;
|
||||
@ -1845,7 +1842,7 @@ static int u132_hcd_start(struct usb_hcd *hcd)
|
||||
u132->going = 1;
|
||||
}
|
||||
msleep(100);
|
||||
up(&u132->sw_lock);
|
||||
mutex_unlock(&u132->sw_lock);
|
||||
return retval;
|
||||
} else {
|
||||
dev_err(&u132->platform_dev->dev, "platform_device missing\n");
|
||||
@ -1865,32 +1862,44 @@ static int u132_hcd_reset(struct usb_hcd *hcd)
|
||||
return -ESHUTDOWN;
|
||||
} else {
|
||||
int retval;
|
||||
down(&u132->sw_lock);
|
||||
mutex_lock(&u132->sw_lock);
|
||||
retval = u132_init(u132);
|
||||
if (retval) {
|
||||
u132_disable(u132);
|
||||
u132->going = 1;
|
||||
}
|
||||
up(&u132->sw_lock);
|
||||
mutex_unlock(&u132->sw_lock);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
static int create_endpoint_and_queue_int(struct u132 *u132,
|
||||
struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
|
||||
struct u132_udev *udev, struct urb *urb,
|
||||
struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct u132_ring *ring;
|
||||
unsigned long irqs;
|
||||
u8 endp_number = ++u132->num_endpoints;
|
||||
struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
|
||||
kmalloc(sizeof(struct u132_endp), mem_flags);
|
||||
int rc;
|
||||
u8 endp_number;
|
||||
struct u132_endp *endp = kmalloc(sizeof(struct u132_endp), mem_flags);
|
||||
|
||||
if (!endp) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&endp->queue_lock.slock);
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
rc = usb_hcd_link_urb_to_ep(u132_to_hcd(u132), urb);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
|
||||
kfree(endp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
endp_number = ++u132->num_endpoints;
|
||||
urb->ep->hcpriv = u132->endp[endp_number - 1] = endp;
|
||||
INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler);
|
||||
spin_lock_init(&endp->queue_lock.slock);
|
||||
INIT_LIST_HEAD(&endp->urb_more);
|
||||
ring = endp->ring = &u132->ring[0];
|
||||
if (ring->curr_endp) {
|
||||
@ -1906,7 +1915,7 @@ static int create_endpoint_and_queue_int(struct u132 *u132,
|
||||
endp->delayed = 0;
|
||||
endp->endp_number = endp_number;
|
||||
endp->u132 = u132;
|
||||
endp->hep = hep;
|
||||
endp->hep = urb->ep;
|
||||
endp->pipetype = usb_pipetype(urb->pipe);
|
||||
u132_endp_init_kref(u132, endp);
|
||||
if (usb_pipein(urb->pipe)) {
|
||||
@ -1925,7 +1934,6 @@ static int create_endpoint_and_queue_int(struct u132 *u132,
|
||||
u132_udev_get_kref(u132, udev);
|
||||
}
|
||||
urb->hcpriv = u132;
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
endp->delayed = 1;
|
||||
endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
|
||||
endp->udev_number = address;
|
||||
@ -1940,8 +1948,8 @@ static int create_endpoint_and_queue_int(struct u132 *u132,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queue_int_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
|
||||
struct usb_host_endpoint *hep, struct urb *urb,
|
||||
static int queue_int_on_old_endpoint(struct u132 *u132,
|
||||
struct u132_udev *udev, struct urb *urb,
|
||||
struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
|
||||
u8 usb_endp, u8 address)
|
||||
{
|
||||
@ -1965,21 +1973,33 @@ static int queue_int_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
|
||||
}
|
||||
|
||||
static int create_endpoint_and_queue_bulk(struct u132 *u132,
|
||||
struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
|
||||
struct u132_udev *udev, struct urb *urb,
|
||||
struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
int ring_number;
|
||||
struct u132_ring *ring;
|
||||
unsigned long irqs;
|
||||
u8 endp_number = ++u132->num_endpoints;
|
||||
struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
|
||||
kmalloc(sizeof(struct u132_endp), mem_flags);
|
||||
int rc;
|
||||
u8 endp_number;
|
||||
struct u132_endp *endp = kmalloc(sizeof(struct u132_endp), mem_flags);
|
||||
|
||||
if (!endp) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&endp->queue_lock.slock);
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
rc = usb_hcd_link_urb_to_ep(u132_to_hcd(u132), urb);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
|
||||
kfree(endp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
endp_number = ++u132->num_endpoints;
|
||||
urb->ep->hcpriv = u132->endp[endp_number - 1] = endp;
|
||||
INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler);
|
||||
spin_lock_init(&endp->queue_lock.slock);
|
||||
INIT_LIST_HEAD(&endp->urb_more);
|
||||
endp->dequeueing = 0;
|
||||
endp->edset_flush = 0;
|
||||
@ -1987,7 +2007,7 @@ static int create_endpoint_and_queue_bulk(struct u132 *u132,
|
||||
endp->delayed = 0;
|
||||
endp->endp_number = endp_number;
|
||||
endp->u132 = u132;
|
||||
endp->hep = hep;
|
||||
endp->hep = urb->ep;
|
||||
endp->pipetype = usb_pipetype(urb->pipe);
|
||||
u132_endp_init_kref(u132, endp);
|
||||
if (usb_pipein(urb->pipe)) {
|
||||
@ -2016,7 +2036,6 @@ static int create_endpoint_and_queue_bulk(struct u132 *u132,
|
||||
}
|
||||
ring->length += 1;
|
||||
urb->hcpriv = u132;
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
endp->udev_number = address;
|
||||
endp->usb_addr = usb_addr;
|
||||
endp->usb_endp = usb_endp;
|
||||
@ -2030,7 +2049,7 @@ static int create_endpoint_and_queue_bulk(struct u132 *u132,
|
||||
}
|
||||
|
||||
static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
|
||||
struct usb_host_endpoint *hep, struct urb *urb,
|
||||
struct urb *urb,
|
||||
struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
|
||||
u8 usb_endp, u8 address)
|
||||
{
|
||||
@ -2052,19 +2071,32 @@ static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
|
||||
}
|
||||
|
||||
static int create_endpoint_and_queue_control(struct u132 *u132,
|
||||
struct usb_host_endpoint *hep, struct urb *urb,
|
||||
struct urb *urb,
|
||||
struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct u132_ring *ring;
|
||||
u8 endp_number = ++u132->num_endpoints;
|
||||
struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
|
||||
kmalloc(sizeof(struct u132_endp), mem_flags);
|
||||
unsigned long irqs;
|
||||
int rc;
|
||||
u8 endp_number;
|
||||
struct u132_endp *endp = kmalloc(sizeof(struct u132_endp), mem_flags);
|
||||
|
||||
if (!endp) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&endp->queue_lock.slock);
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
rc = usb_hcd_link_urb_to_ep(u132_to_hcd(u132), urb);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
|
||||
kfree(endp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
endp_number = ++u132->num_endpoints;
|
||||
urb->ep->hcpriv = u132->endp[endp_number - 1] = endp;
|
||||
INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler);
|
||||
spin_lock_init(&endp->queue_lock.slock);
|
||||
INIT_LIST_HEAD(&endp->urb_more);
|
||||
ring = endp->ring = &u132->ring[0];
|
||||
if (ring->curr_endp) {
|
||||
@ -2080,11 +2112,10 @@ static int create_endpoint_and_queue_control(struct u132 *u132,
|
||||
endp->delayed = 0;
|
||||
endp->endp_number = endp_number;
|
||||
endp->u132 = u132;
|
||||
endp->hep = hep;
|
||||
endp->hep = urb->ep;
|
||||
u132_endp_init_kref(u132, endp);
|
||||
u132_endp_get_kref(u132, endp);
|
||||
if (usb_addr == 0) {
|
||||
unsigned long irqs;
|
||||
u8 address = u132->addr[usb_addr].address;
|
||||
struct u132_udev *udev = &u132->udev[address];
|
||||
endp->udev_number = address;
|
||||
@ -2098,7 +2129,6 @@ static int create_endpoint_and_queue_control(struct u132 *u132,
|
||||
udev->endp_number_in[usb_endp] = endp_number;
|
||||
udev->endp_number_out[usb_endp] = endp_number;
|
||||
urb->hcpriv = u132;
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
endp->queue_size = 1;
|
||||
endp->queue_last = 0;
|
||||
endp->queue_next = 0;
|
||||
@ -2107,7 +2137,6 @@ static int create_endpoint_and_queue_control(struct u132 *u132,
|
||||
u132_endp_queue_work(u132, endp, 0);
|
||||
return 0;
|
||||
} else { /*(usb_addr > 0) */
|
||||
unsigned long irqs;
|
||||
u8 address = u132->addr[usb_addr].address;
|
||||
struct u132_udev *udev = &u132->udev[address];
|
||||
endp->udev_number = address;
|
||||
@ -2121,7 +2150,6 @@ static int create_endpoint_and_queue_control(struct u132 *u132,
|
||||
udev->endp_number_in[usb_endp] = endp_number;
|
||||
udev->endp_number_out[usb_endp] = endp_number;
|
||||
urb->hcpriv = u132;
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
endp->queue_size = 1;
|
||||
endp->queue_last = 0;
|
||||
endp->queue_next = 0;
|
||||
@ -2133,7 +2161,7 @@ static int create_endpoint_and_queue_control(struct u132 *u132,
|
||||
}
|
||||
|
||||
static int queue_control_on_old_endpoint(struct u132 *u132,
|
||||
struct usb_host_endpoint *hep, struct urb *urb,
|
||||
struct urb *urb,
|
||||
struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
|
||||
u8 usb_endp)
|
||||
{
|
||||
@ -2233,8 +2261,8 @@ static int queue_control_on_old_endpoint(struct u132 *u132,
|
||||
}
|
||||
}
|
||||
|
||||
static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
|
||||
struct urb *urb, gfp_t mem_flags)
|
||||
static int u132_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct u132 *u132 = hcd_to_u132(hcd);
|
||||
if (irqs_disabled()) {
|
||||
@ -2249,8 +2277,8 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
|
||||
, u132->going);
|
||||
return -ENODEV;
|
||||
} else if (u132->going > 0) {
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed urb="
|
||||
"%p status=%d\n", urb, urb->status);
|
||||
dev_err(&u132->platform_dev->dev, "device is being removed "
|
||||
"urb=%p\n", urb);
|
||||
return -ESHUTDOWN;
|
||||
} else {
|
||||
u8 usb_addr = usb_pipedevice(urb->pipe);
|
||||
@ -2259,16 +2287,24 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
|
||||
if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
|
||||
u8 address = u132->addr[usb_addr].address;
|
||||
struct u132_udev *udev = &u132->udev[address];
|
||||
struct u132_endp *endp = hep->hcpriv;
|
||||
struct u132_endp *endp = urb->ep->hcpriv;
|
||||
urb->actual_length = 0;
|
||||
if (endp) {
|
||||
unsigned long irqs;
|
||||
int retval;
|
||||
spin_lock_irqsave(&endp->queue_lock.slock,
|
||||
irqs);
|
||||
retval = queue_int_on_old_endpoint(u132, udev,
|
||||
hep, urb, usb_dev, endp, usb_addr,
|
||||
usb_endp, address);
|
||||
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (retval == 0) {
|
||||
retval = queue_int_on_old_endpoint(
|
||||
u132, udev, urb,
|
||||
usb_dev, endp,
|
||||
usb_addr, usb_endp,
|
||||
address);
|
||||
if (retval)
|
||||
usb_hcd_unlink_urb_from_ep(
|
||||
hcd, urb);
|
||||
}
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock,
|
||||
irqs);
|
||||
if (retval) {
|
||||
@ -2283,8 +2319,8 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
|
||||
return -EINVAL;
|
||||
} else { /*(endp == NULL) */
|
||||
return create_endpoint_and_queue_int(u132, udev,
|
||||
hep, urb, usb_dev, usb_addr, usb_endp,
|
||||
address, mem_flags);
|
||||
urb, usb_dev, usb_addr,
|
||||
usb_endp, address, mem_flags);
|
||||
}
|
||||
} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
|
||||
dev_err(&u132->platform_dev->dev, "the hardware does no"
|
||||
@ -2293,16 +2329,24 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
|
||||
} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
|
||||
u8 address = u132->addr[usb_addr].address;
|
||||
struct u132_udev *udev = &u132->udev[address];
|
||||
struct u132_endp *endp = hep->hcpriv;
|
||||
struct u132_endp *endp = urb->ep->hcpriv;
|
||||
urb->actual_length = 0;
|
||||
if (endp) {
|
||||
unsigned long irqs;
|
||||
int retval;
|
||||
spin_lock_irqsave(&endp->queue_lock.slock,
|
||||
irqs);
|
||||
retval = queue_bulk_on_old_endpoint(u132, udev,
|
||||
hep, urb, usb_dev, endp, usb_addr,
|
||||
usb_endp, address);
|
||||
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (retval == 0) {
|
||||
retval = queue_bulk_on_old_endpoint(
|
||||
u132, udev, urb,
|
||||
usb_dev, endp,
|
||||
usb_addr, usb_endp,
|
||||
address);
|
||||
if (retval)
|
||||
usb_hcd_unlink_urb_from_ep(
|
||||
hcd, urb);
|
||||
}
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock,
|
||||
irqs);
|
||||
if (retval) {
|
||||
@ -2315,10 +2359,10 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
|
||||
return -EINVAL;
|
||||
} else
|
||||
return create_endpoint_and_queue_bulk(u132,
|
||||
udev, hep, urb, usb_dev, usb_addr,
|
||||
udev, urb, usb_dev, usb_addr,
|
||||
usb_endp, address, mem_flags);
|
||||
} else {
|
||||
struct u132_endp *endp = hep->hcpriv;
|
||||
struct u132_endp *endp = urb->ep->hcpriv;
|
||||
u16 urb_size = 8;
|
||||
u8 *b = urb->setup_packet;
|
||||
int i = 0;
|
||||
@ -2341,9 +2385,16 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
|
||||
int retval;
|
||||
spin_lock_irqsave(&endp->queue_lock.slock,
|
||||
irqs);
|
||||
retval = queue_control_on_old_endpoint(u132,
|
||||
hep, urb, usb_dev, endp, usb_addr,
|
||||
usb_endp);
|
||||
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (retval == 0) {
|
||||
retval = queue_control_on_old_endpoint(
|
||||
u132, urb, usb_dev,
|
||||
endp, usb_addr,
|
||||
usb_endp);
|
||||
if (retval)
|
||||
usb_hcd_unlink_urb_from_ep(
|
||||
hcd, urb);
|
||||
}
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock,
|
||||
irqs);
|
||||
if (retval) {
|
||||
@ -2356,7 +2407,7 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
|
||||
return -EINVAL;
|
||||
} else
|
||||
return create_endpoint_and_queue_control(u132,
|
||||
hep, urb, usb_dev, usb_addr, usb_endp,
|
||||
urb, usb_dev, usb_addr, usb_endp,
|
||||
mem_flags);
|
||||
}
|
||||
}
|
||||
@ -2375,8 +2426,7 @@ static int dequeue_from_overflow_chain(struct u132 *u132,
|
||||
list_del(scan);
|
||||
endp->queue_size -= 1;
|
||||
urb->error_count = 0;
|
||||
urb->hcpriv = NULL;
|
||||
usb_hcd_giveback_urb(hcd, urb);
|
||||
usb_hcd_giveback_urb(hcd, urb, 0);
|
||||
return 0;
|
||||
} else
|
||||
continue;
|
||||
@ -2391,10 +2441,17 @@ static int dequeue_from_overflow_chain(struct u132 *u132,
|
||||
}
|
||||
|
||||
static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
|
||||
struct urb *urb)
|
||||
struct urb *urb, int status)
|
||||
{
|
||||
unsigned long irqs;
|
||||
int rc;
|
||||
|
||||
spin_lock_irqsave(&endp->queue_lock.slock, irqs);
|
||||
rc = usb_hcd_check_unlink_urb(u132_to_hcd(u132), urb, status);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
|
||||
return rc;
|
||||
}
|
||||
if (endp->queue_size == 0) {
|
||||
dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]"
|
||||
"=%p ring[%d] %c%c usb_endp=%d usb_addr=%d\n", urb,
|
||||
@ -2410,11 +2467,10 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
|
||||
endp->edset_flush = 1;
|
||||
u132_endp_queue_work(u132, endp, 0);
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
|
||||
urb->hcpriv = NULL;
|
||||
return 0;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
|
||||
u132_hcd_abandon_urb(u132, endp, urb, urb->status);
|
||||
u132_hcd_abandon_urb(u132, endp, urb, status);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
@ -2439,6 +2495,8 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
|
||||
}
|
||||
if (urb_slot) {
|
||||
struct usb_hcd *hcd = u132_to_hcd(u132);
|
||||
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
endp->queue_size -= 1;
|
||||
if (list_empty(&endp->urb_more)) {
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock,
|
||||
@ -2453,8 +2511,7 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
|
||||
irqs);
|
||||
kfree(urbq);
|
||||
} urb->error_count = 0;
|
||||
urb->hcpriv = NULL;
|
||||
usb_hcd_giveback_urb(hcd, urb);
|
||||
usb_hcd_giveback_urb(hcd, urb, status);
|
||||
return 0;
|
||||
} else if (list_empty(&endp->urb_more)) {
|
||||
dev_err(&u132->platform_dev->dev, "urb=%p not found in "
|
||||
@ -2468,7 +2525,10 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
int retval = dequeue_from_overflow_chain(u132, endp,
|
||||
int retval;
|
||||
|
||||
usb_hcd_unlink_urb_from_ep(u132_to_hcd(u132), urb);
|
||||
retval = dequeue_from_overflow_chain(u132, endp,
|
||||
urb);
|
||||
spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
|
||||
return retval;
|
||||
@ -2476,7 +2536,7 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
|
||||
}
|
||||
}
|
||||
|
||||
static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
||||
static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
struct u132 *u132 = hcd_to_u132(hcd);
|
||||
if (u132->going > 2) {
|
||||
@ -2491,11 +2551,11 @@ static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
||||
if (usb_pipein(urb->pipe)) {
|
||||
u8 endp_number = udev->endp_number_in[usb_endp];
|
||||
struct u132_endp *endp = u132->endp[endp_number - 1];
|
||||
return u132_endp_urb_dequeue(u132, endp, urb);
|
||||
return u132_endp_urb_dequeue(u132, endp, urb, status);
|
||||
} else {
|
||||
u8 endp_number = udev->endp_number_out[usb_endp];
|
||||
struct u132_endp *endp = u132->endp[endp_number - 1];
|
||||
return u132_endp_urb_dequeue(u132, endp, urb);
|
||||
return u132_endp_urb_dequeue(u132, endp, urb, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2805,7 +2865,7 @@ static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
return -ESHUTDOWN;
|
||||
} else {
|
||||
int retval = 0;
|
||||
down(&u132->sw_lock);
|
||||
mutex_lock(&u132->sw_lock);
|
||||
switch (typeReq) {
|
||||
case ClearHubFeature:
|
||||
switch (wValue) {
|
||||
@ -2868,7 +2928,7 @@ static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
stall:retval = -EPIPE;
|
||||
break;
|
||||
}
|
||||
up(&u132->sw_lock);
|
||||
mutex_unlock(&u132->sw_lock);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
@ -3004,7 +3064,7 @@ static int __devexit u132_remove(struct platform_device *pdev)
|
||||
dev_err(&u132->platform_dev->dev, "removing device u132"
|
||||
".%d\n", u132->sequence_num);
|
||||
msleep(100);
|
||||
down(&u132->sw_lock);
|
||||
mutex_lock(&u132->sw_lock);
|
||||
u132_monitor_cancel_work(u132);
|
||||
while (rings-- > 0) {
|
||||
struct u132_ring *ring = &u132->ring[rings];
|
||||
@ -3017,7 +3077,7 @@ static int __devexit u132_remove(struct platform_device *pdev)
|
||||
u132->going += 1;
|
||||
printk(KERN_INFO "removing device u132.%d\n",
|
||||
u132->sequence_num);
|
||||
up(&u132->sw_lock);
|
||||
mutex_unlock(&u132->sw_lock);
|
||||
usb_remove_hcd(hcd);
|
||||
u132_u132_put_kref(u132);
|
||||
return 0;
|
||||
@ -3037,7 +3097,7 @@ static void u132_initialise(struct u132 *u132, struct platform_device *pdev)
|
||||
u132->platform_dev = pdev;
|
||||
u132->power = 0;
|
||||
u132->reset = 0;
|
||||
init_MUTEX(&u132->sw_lock);
|
||||
mutex_init(&u132->sw_lock);
|
||||
init_MUTEX(&u132->scheduler_lock);
|
||||
while (rings-- > 0) {
|
||||
struct u132_ring *ring = &u132->ring[rings];
|
||||
@ -3047,7 +3107,7 @@ static void u132_initialise(struct u132 *u132, struct platform_device *pdev)
|
||||
ring->curr_endp = NULL;
|
||||
INIT_DELAYED_WORK(&ring->scheduler,
|
||||
u132_hcd_ring_work_scheduler);
|
||||
} down(&u132->sw_lock);
|
||||
} mutex_lock(&u132->sw_lock);
|
||||
INIT_DELAYED_WORK(&u132->monitor, u132_hcd_monitor_work);
|
||||
while (ports-- > 0) {
|
||||
struct u132_port *port = &u132->port[ports];
|
||||
@ -3077,7 +3137,7 @@ static void u132_initialise(struct u132 *u132, struct platform_device *pdev)
|
||||
while (endps-- > 0) {
|
||||
u132->endp[endps] = NULL;
|
||||
}
|
||||
up(&u132->sw_lock);
|
||||
mutex_unlock(&u132->sw_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -120,8 +120,8 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
|
||||
out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : ""));
|
||||
out += sprintf(out, " Actlen=%d", urbp->urb->actual_length);
|
||||
|
||||
if (urbp->urb->status != -EINPROGRESS)
|
||||
out += sprintf(out, " Status=%d", urbp->urb->status);
|
||||
if (urbp->urb->unlinked)
|
||||
out += sprintf(out, " Unlinked=%d", urbp->urb->unlinked);
|
||||
out += sprintf(out, "\n");
|
||||
|
||||
i = nactive = ninactive = 0;
|
||||
|
@ -146,7 +146,6 @@ struct uhci_qh {
|
||||
short phase; /* Between 0 and period-1 */
|
||||
short load; /* Periodic time requirement, in us */
|
||||
unsigned int iso_frame; /* Frame # for iso_packet_desc */
|
||||
int iso_status; /* Status for Isochronous URBs */
|
||||
|
||||
int state; /* QH_STATE_xxx; see above */
|
||||
int type; /* Queue type (control, bulk, etc) */
|
||||
@ -457,21 +456,6 @@ struct urb_priv {
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Locking in uhci.c
|
||||
*
|
||||
* Almost everything relating to the hardware schedule and processing
|
||||
* of URBs is protected by uhci->lock. urb->status is protected by
|
||||
* urb->lock; that's the one exception.
|
||||
*
|
||||
* To prevent deadlocks, never lock uhci->lock while holding urb->lock.
|
||||
* The safe order of locking is:
|
||||
*
|
||||
* #1 uhci->lock
|
||||
* #2 urb->lock
|
||||
*/
|
||||
|
||||
|
||||
/* Some special IDs */
|
||||
|
||||
#define PCI_VENDOR_ID_GENESYS 0x17a0
|
||||
|
@ -757,7 +757,6 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci,
|
||||
uhci_free_td(uhci, td);
|
||||
}
|
||||
|
||||
urbp->urb->hcpriv = NULL;
|
||||
kmem_cache_free(uhci_up_cachep, urbp);
|
||||
}
|
||||
|
||||
@ -1324,7 +1323,6 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
|
||||
if (list_empty(&qh->queue)) {
|
||||
qh->iso_packet_desc = &urb->iso_frame_desc[0];
|
||||
qh->iso_frame = urb->start_frame;
|
||||
qh->iso_status = 0;
|
||||
}
|
||||
|
||||
qh->skel = SKEL_ISO;
|
||||
@ -1361,22 +1359,18 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
||||
qh->iso_packet_desc->actual_length = actlength;
|
||||
qh->iso_packet_desc->status = status;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
if (status)
|
||||
urb->error_count++;
|
||||
qh->iso_status = status;
|
||||
}
|
||||
|
||||
uhci_remove_td_from_urbp(td);
|
||||
uhci_free_td(uhci, td);
|
||||
qh->iso_frame += qh->period;
|
||||
++qh->iso_packet_desc;
|
||||
}
|
||||
return qh->iso_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uhci_urb_enqueue(struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *hep,
|
||||
struct urb *urb, gfp_t mem_flags)
|
||||
{
|
||||
int ret;
|
||||
@ -1387,19 +1381,19 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
|
||||
|
||||
spin_lock_irqsave(&uhci->lock, flags);
|
||||
|
||||
ret = urb->status;
|
||||
if (ret != -EINPROGRESS) /* URB already unlinked! */
|
||||
goto done;
|
||||
ret = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (ret)
|
||||
goto done_not_linked;
|
||||
|
||||
ret = -ENOMEM;
|
||||
urbp = uhci_alloc_urb_priv(uhci, urb);
|
||||
if (!urbp)
|
||||
goto done;
|
||||
|
||||
if (hep->hcpriv)
|
||||
qh = (struct uhci_qh *) hep->hcpriv;
|
||||
if (urb->ep->hcpriv)
|
||||
qh = urb->ep->hcpriv;
|
||||
else {
|
||||
qh = uhci_alloc_qh(uhci, urb->dev, hep);
|
||||
qh = uhci_alloc_qh(uhci, urb->dev, urb->ep);
|
||||
if (!qh)
|
||||
goto err_no_qh;
|
||||
}
|
||||
@ -1440,27 +1434,29 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
|
||||
err_submit_failed:
|
||||
if (qh->state == QH_STATE_IDLE)
|
||||
uhci_make_qh_idle(uhci, qh); /* Reclaim unused QH */
|
||||
|
||||
err_no_qh:
|
||||
uhci_free_urb_priv(uhci, urbp);
|
||||
|
||||
done:
|
||||
if (ret)
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
done_not_linked:
|
||||
spin_unlock_irqrestore(&uhci->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
||||
static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
||||
unsigned long flags;
|
||||
struct urb_priv *urbp;
|
||||
struct uhci_qh *qh;
|
||||
int rc;
|
||||
|
||||
spin_lock_irqsave(&uhci->lock, flags);
|
||||
urbp = urb->hcpriv;
|
||||
if (!urbp) /* URB was never linked! */
|
||||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (rc)
|
||||
goto done;
|
||||
qh = urbp->qh;
|
||||
|
||||
qh = ((struct urb_priv *) urb->hcpriv)->qh;
|
||||
|
||||
/* Remove Isochronous TDs from the frame list ASAP */
|
||||
if (qh->type == USB_ENDPOINT_XFER_ISOC) {
|
||||
@ -1477,14 +1473,14 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&uhci->lock, flags);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish unlinking an URB and give it back
|
||||
*/
|
||||
static void uhci_giveback_urb(struct uhci_hcd *uhci, struct uhci_qh *qh,
|
||||
struct urb *urb)
|
||||
struct urb *urb, int status)
|
||||
__releases(uhci->lock)
|
||||
__acquires(uhci->lock)
|
||||
{
|
||||
@ -1497,13 +1493,6 @@ __acquires(uhci->lock)
|
||||
* unlinked first. Regardless, don't confuse people with a
|
||||
* negative length. */
|
||||
urb->actual_length = max(urb->actual_length, 0);
|
||||
|
||||
/* Report erroneous short transfers */
|
||||
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
|
||||
urb->actual_length <
|
||||
urb->transfer_buffer_length &&
|
||||
urb->status == 0))
|
||||
urb->status = -EREMOTEIO;
|
||||
}
|
||||
|
||||
/* When giving back the first URB in an Isochronous queue,
|
||||
@ -1516,7 +1505,6 @@ __acquires(uhci->lock)
|
||||
|
||||
qh->iso_packet_desc = &nurb->iso_frame_desc[0];
|
||||
qh->iso_frame = nurb->start_frame;
|
||||
qh->iso_status = 0;
|
||||
}
|
||||
|
||||
/* Take the URB off the QH's queue. If the queue is now empty,
|
||||
@ -1529,9 +1517,10 @@ __acquires(uhci->lock)
|
||||
}
|
||||
|
||||
uhci_free_urb_priv(uhci, urbp);
|
||||
usb_hcd_unlink_urb_from_ep(uhci_to_hcd(uhci), urb);
|
||||
|
||||
spin_unlock(&uhci->lock);
|
||||
usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb);
|
||||
usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb, status);
|
||||
spin_lock(&uhci->lock);
|
||||
|
||||
/* If the queue is now empty, we can unlink the QH and give up its
|
||||
@ -1567,24 +1556,17 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
if (status == -EINPROGRESS)
|
||||
break;
|
||||
|
||||
spin_lock(&urb->lock);
|
||||
if (urb->status == -EINPROGRESS) /* Not dequeued */
|
||||
urb->status = status;
|
||||
else
|
||||
status = ECONNRESET; /* Not -ECONNRESET */
|
||||
spin_unlock(&urb->lock);
|
||||
|
||||
/* Dequeued but completed URBs can't be given back unless
|
||||
* the QH is stopped or has finished unlinking. */
|
||||
if (status == ECONNRESET) {
|
||||
if (urb->unlinked) {
|
||||
if (QH_FINISHED_UNLINKING(qh))
|
||||
qh->is_stopped = 1;
|
||||
else if (!qh->is_stopped)
|
||||
return;
|
||||
}
|
||||
|
||||
uhci_giveback_urb(uhci, qh, urb);
|
||||
if (status < 0 && qh->type != USB_ENDPOINT_XFER_ISOC)
|
||||
uhci_giveback_urb(uhci, qh, urb, status);
|
||||
if (status < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1599,7 +1581,7 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||
restart:
|
||||
list_for_each_entry(urbp, &qh->queue, node) {
|
||||
urb = urbp->urb;
|
||||
if (urb->status != -EINPROGRESS) {
|
||||
if (urb->unlinked) {
|
||||
|
||||
/* Fix up the TD links and save the toggles for
|
||||
* non-Isochronous queues. For Isochronous queues,
|
||||
@ -1608,7 +1590,7 @@ restart:
|
||||
qh->is_stopped = 0;
|
||||
return;
|
||||
}
|
||||
uhci_giveback_urb(uhci, qh, urb);
|
||||
uhci_giveback_urb(uhci, qh, urb, 0);
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +188,8 @@ static void adu_interrupt_in_callback(struct urb *urb)
|
||||
spin_lock(&dev->buflock);
|
||||
|
||||
if (status != 0) {
|
||||
if ((status != -ENOENT) && (status != -ECONNRESET)) {
|
||||
if ((status != -ENOENT) && (status != -ECONNRESET) &&
|
||||
(status != -ESHUTDOWN)) {
|
||||
dbg(1," %s : nonzero status received: %d",
|
||||
__FUNCTION__, status);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ static int magic_charge(struct usb_device *udev)
|
||||
if (retval != 2) {
|
||||
dev_err(&udev->dev, "First magic command failed: %d.\n",
|
||||
retval);
|
||||
return retval;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dbg(&udev->dev, "Sending second magic command\n");
|
||||
@ -80,7 +80,7 @@ static int magic_charge(struct usb_device *udev)
|
||||
if (retval != 0) {
|
||||
dev_err(&udev->dev, "Second magic command failed: %d.\n",
|
||||
retval);
|
||||
return retval;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dbg(&udev->dev, "Calling set_configuration\n");
|
||||
@ -88,6 +88,8 @@ static int magic_charge(struct usb_device *udev)
|
||||
if (retval)
|
||||
dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval);
|
||||
|
||||
exit:
|
||||
kfree(dummy_buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -112,6 +114,7 @@ static int magic_dual_mode(struct usb_device *udev)
|
||||
if (retval)
|
||||
dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval);
|
||||
|
||||
kfree(dummy_buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -2777,12 +2777,14 @@ static int ftdi_elan_probe(struct usb_interface *interface,
|
||||
size_t buffer_size;
|
||||
int i;
|
||||
int retval = -ENOMEM;
|
||||
struct usb_ftdi *ftdi = kmalloc(sizeof(struct usb_ftdi), GFP_KERNEL);
|
||||
if (ftdi == NULL) {
|
||||
struct usb_ftdi *ftdi;
|
||||
|
||||
ftdi = kzalloc(sizeof(struct usb_ftdi), GFP_KERNEL);
|
||||
if (!ftdi) {
|
||||
printk(KERN_ERR "Out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(ftdi, 0x00, sizeof(struct usb_ftdi));
|
||||
|
||||
mutex_lock(&ftdi_module_lock);
|
||||
list_add_tail(&ftdi->ftdi_list, &ftdi_static_list);
|
||||
ftdi->sequence_num = ++ftdi_instances;
|
||||
|
@ -32,7 +32,7 @@
|
||||
* * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Thomas Winischhofer <thomas@winischhofer.net>
|
||||
* Author: Thomas Winischhofer <thomas@winischhofer.net>
|
||||
*
|
||||
*/
|
||||
|
||||
@ -962,12 +962,12 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
|
||||
packet.address = 0x00000194;
|
||||
packet.data = addr;
|
||||
ret = sisusb_send_bridge_packet(sisusb, 10,
|
||||
&packet, 0);
|
||||
&packet, 0);
|
||||
packet.header = 0x001f;
|
||||
packet.address = 0x00000190;
|
||||
packet.data = (length & ~3);
|
||||
ret |= sisusb_send_bridge_packet(sisusb, 10,
|
||||
&packet, 0);
|
||||
&packet, 0);
|
||||
if (sisusb->flagb0 != 0x16) {
|
||||
packet.header = 0x001f;
|
||||
packet.address = 0x00000180;
|
||||
@ -1003,23 +1003,17 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
|
||||
if (ret) {
|
||||
msgcount++;
|
||||
if (msgcount < 500)
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Wrote %zd of "
|
||||
"%d bytes, error %d\n",
|
||||
sisusb->minor, *bytes_written,
|
||||
length, ret);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Wrote %zd of %d bytes, error %d\n",
|
||||
*bytes_written, length, ret);
|
||||
else if (msgcount == 500)
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Too many errors"
|
||||
", logging stopped\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Too many errors, logging stopped\n");
|
||||
}
|
||||
addr += (*bytes_written);
|
||||
length -= (*bytes_written);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
@ -1261,51 +1255,10 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
|
||||
addr += 4;
|
||||
length -= 4;
|
||||
}
|
||||
#if 0 /* That does not work, as EP 2 is an OUT EP! */
|
||||
default:
|
||||
CLEARPACKET(&packet);
|
||||
packet.header = 0x001f;
|
||||
packet.address = 0x000001a0;
|
||||
packet.data = 0x00000006;
|
||||
ret |= sisusb_send_bridge_packet(sisusb, 10,
|
||||
&packet, 0);
|
||||
packet.header = 0x001f;
|
||||
packet.address = 0x000001b0;
|
||||
packet.data = (length & ~3) | 0x40000000;
|
||||
ret |= sisusb_send_bridge_packet(sisusb, 10,
|
||||
&packet, 0);
|
||||
packet.header = 0x001f;
|
||||
packet.address = 0x000001b4;
|
||||
packet.data = addr;
|
||||
ret |= sisusb_send_bridge_packet(sisusb, 10,
|
||||
&packet, 0);
|
||||
packet.header = 0x001f;
|
||||
packet.address = 0x000001a4;
|
||||
packet.data = 0x00000001;
|
||||
ret |= sisusb_send_bridge_packet(sisusb, 10,
|
||||
&packet, 0);
|
||||
if (userbuffer) {
|
||||
ret |= sisusb_recv_bulk_msg(sisusb,
|
||||
SISUSB_EP_GFX_BULK_IN,
|
||||
(length & ~3),
|
||||
NULL, userbuffer,
|
||||
bytes_read, 0);
|
||||
if (!ret) userbuffer += (*bytes_read);
|
||||
} else {
|
||||
ret |= sisusb_recv_bulk_msg(sisusb,
|
||||
SISUSB_EP_GFX_BULK_IN,
|
||||
(length & ~3),
|
||||
kernbuffer, NULL,
|
||||
bytes_read, 0);
|
||||
if (!ret) kernbuffer += (*bytes_read);
|
||||
}
|
||||
addr += (*bytes_read);
|
||||
length -= (*bytes_read);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1401,22 +1354,6 @@ sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data)
|
||||
return(sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
int
|
||||
sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data)
|
||||
{
|
||||
return(sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, adr, data));
|
||||
}
|
||||
|
||||
int
|
||||
sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data)
|
||||
{
|
||||
return(sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM, adr, data));
|
||||
}
|
||||
|
||||
#endif /* 0 */
|
||||
|
||||
int
|
||||
sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
|
||||
u32 dest, int length, size_t *bytes_written)
|
||||
@ -1446,10 +1383,10 @@ sisusb_testreadwrite(struct sisusb_usb_data *sisusb)
|
||||
sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7, &dummy);
|
||||
|
||||
for(i = 1; i <= 7; i++) {
|
||||
printk(KERN_DEBUG "sisusb: rwtest %d bytes\n", i);
|
||||
dev_dbg(&sisusb->sisusb_dev->dev, "sisusb: rwtest %d bytes\n", i);
|
||||
sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i, &dummy);
|
||||
for(j = 0; j < i; j++) {
|
||||
printk(KERN_DEBUG "sisusb: rwtest read[%d] = %x\n", j, destbuffer[j]);
|
||||
dev_dbg(&sisusb->sisusb_dev->dev, "rwtest read[%d] = %x\n", j, destbuffer[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1533,9 +1470,9 @@ sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length)
|
||||
#define SETIREGAND(r,i,a) sisusb_setidxregand(sisusb, r, i, a)
|
||||
#define SETIREGANDOR(r,i,a,o) sisusb_setidxregandor(sisusb, r, i, a, o)
|
||||
#define READL(a,d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
|
||||
#define WRITEL(a,d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
|
||||
#define WRITEL(a,d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
|
||||
#define READB(a,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
|
||||
#define WRITEB(a,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
|
||||
#define WRITEB(a,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
|
||||
|
||||
static int
|
||||
sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype)
|
||||
@ -2008,7 +1945,7 @@ sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines)
|
||||
SETIREG(SISSR, 0x26, 0x00);
|
||||
}
|
||||
|
||||
SETIREG(SISCR, 0x34, 0x44); /* we just set std mode #44 */
|
||||
SETIREG(SISCR, 0x34, 0x44); /* we just set std mode #44 */
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -2168,17 +2105,12 @@ sisusb_init_gfxcore(struct sisusb_usb_data *sisusb)
|
||||
if (ramtype <= 1) {
|
||||
ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab);
|
||||
if (iret) {
|
||||
printk(KERN_ERR "sisusbvga[%d]: RAM size "
|
||||
"detection failed, "
|
||||
"assuming 8MB video RAM\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev,"RAM size detection failed, assuming 8MB video RAM\n");
|
||||
ret |= SETIREG(SISSR,0x14,0x31);
|
||||
/* TODO */
|
||||
}
|
||||
} else {
|
||||
printk(KERN_ERR "sisusbvga[%d]: DDR RAM device found, "
|
||||
"assuming 8MB video RAM\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "DDR RAM device found, assuming 8MB video RAM\n");
|
||||
ret |= SETIREG(SISSR,0x14,0x31);
|
||||
/* *** TODO *** */
|
||||
}
|
||||
@ -2249,8 +2181,7 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
|
||||
break;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "sisusbvga[%d]: %dMB %s %s, bus width %d\n",
|
||||
sisusb->minor, (sisusb->vramsize >> 20), ramtypetext1,
|
||||
dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %s, bus width %d\n", (sisusb->vramsize >> 20), ramtypetext1,
|
||||
ramtypetext2[ramtype], bw);
|
||||
}
|
||||
|
||||
@ -2509,11 +2440,8 @@ sisusb_open(struct inode *inode, struct file *file)
|
||||
struct usb_interface *interface;
|
||||
int subminor = iminor(inode);
|
||||
|
||||
if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
|
||||
printk(KERN_ERR "sisusb[%d]: Failed to find interface\n",
|
||||
subminor);
|
||||
if (!(interface = usb_find_interface(&sisusb_driver, subminor)))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(sisusb = usb_get_intfdata(interface)))
|
||||
return -ENODEV;
|
||||
@ -2534,18 +2462,12 @@ sisusb_open(struct inode *inode, struct file *file)
|
||||
if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
|
||||
if (sisusb_init_gfxdevice(sisusb, 0)) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to initialize "
|
||||
"device\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to initialize device\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Device not attached to "
|
||||
"USB 2.0 hub\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Device not attached to USB 2.0 hub\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
@ -2586,7 +2508,6 @@ static int
|
||||
sisusb_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct sisusb_usb_data *sisusb;
|
||||
int myminor;
|
||||
|
||||
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
|
||||
return -ENODEV;
|
||||
@ -2599,8 +2520,6 @@ sisusb_release(struct inode *inode, struct file *file)
|
||||
sisusb_kill_all_busy(sisusb);
|
||||
}
|
||||
|
||||
myminor = sisusb->minor;
|
||||
|
||||
sisusb->isopen = 0;
|
||||
file->private_data = NULL;
|
||||
|
||||
@ -2942,7 +2861,7 @@ static int
|
||||
sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
|
||||
unsigned long arg)
|
||||
{
|
||||
int retval, port, length;
|
||||
int retval, port, length;
|
||||
u32 address;
|
||||
|
||||
/* All our commands require the device
|
||||
@ -3065,12 +2984,12 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
|
||||
|
||||
static int
|
||||
sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
unsigned long arg)
|
||||
{
|
||||
struct sisusb_usb_data *sisusb;
|
||||
struct sisusb_info x;
|
||||
struct sisusb_command y;
|
||||
int retval = 0;
|
||||
int retval = 0;
|
||||
u32 __user *argp = (u32 __user *)arg;
|
||||
|
||||
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
|
||||
@ -3095,7 +3014,7 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
||||
|
||||
case SISUSB_GET_CONFIG:
|
||||
|
||||
x.sisusb_id = SISUSB_ID;
|
||||
x.sisusb_id = SISUSB_ID;
|
||||
x.sisusb_version = SISUSB_VERSION;
|
||||
x.sisusb_revision = SISUSB_REVISION;
|
||||
x.sisusb_patchlevel = SISUSB_PATCHLEVEL;
|
||||
@ -3164,7 +3083,7 @@ static const struct file_operations usb_sisusb_fops = {
|
||||
.release = sisusb_release,
|
||||
.read = sisusb_read,
|
||||
.write = sisusb_write,
|
||||
.llseek = sisusb_lseek,
|
||||
.llseek = sisusb_lseek,
|
||||
#ifdef SISUSB_NEW_CONFIG_COMPAT
|
||||
.compat_ioctl = sisusb_compat_ioctl,
|
||||
#endif
|
||||
@ -3183,17 +3102,13 @@ static int sisusb_probe(struct usb_interface *intf,
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct sisusb_usb_data *sisusb;
|
||||
int retval = 0, i;
|
||||
const char *memfail =
|
||||
KERN_ERR
|
||||
"sisusbvga[%d]: Failed to allocate memory for %s buffer\n";
|
||||
|
||||
printk(KERN_INFO "sisusb: USB2VGA dongle found at address %d\n",
|
||||
dev_info(&dev->dev, "USB2VGA dongle found at address %d\n",
|
||||
dev->devnum);
|
||||
|
||||
/* Allocate memory for our private */
|
||||
if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) {
|
||||
printk(KERN_ERR
|
||||
"sisusb: Failed to allocate memory for private data\n");
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
kref_init(&sisusb->kref);
|
||||
@ -3202,8 +3117,7 @@ static int sisusb_probe(struct usb_interface *intf,
|
||||
|
||||
/* Register device */
|
||||
if ((retval = usb_register_dev(intf, &usb_sisusb_class))) {
|
||||
printk(KERN_ERR
|
||||
"sisusb: Failed to get a minor for device %d\n",
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to get a minor for device %d\n",
|
||||
dev->devnum);
|
||||
retval = -ENODEV;
|
||||
goto error_1;
|
||||
@ -3221,7 +3135,7 @@ static int sisusb_probe(struct usb_interface *intf,
|
||||
sisusb->ibufsize = SISUSB_IBUF_SIZE;
|
||||
if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE,
|
||||
GFP_KERNEL, &sisusb->transfer_dma_in))) {
|
||||
printk(memfail, "input", sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for input buffer");
|
||||
retval = -ENOMEM;
|
||||
goto error_2;
|
||||
}
|
||||
@ -3233,7 +3147,7 @@ static int sisusb_probe(struct usb_interface *intf,
|
||||
GFP_KERNEL,
|
||||
&sisusb->transfer_dma_out[i]))) {
|
||||
if (i == 0) {
|
||||
printk(memfail, "output", sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for output buffer\n");
|
||||
retval = -ENOMEM;
|
||||
goto error_3;
|
||||
}
|
||||
@ -3245,9 +3159,7 @@ static int sisusb_probe(struct usb_interface *intf,
|
||||
|
||||
/* Allocate URBs */
|
||||
if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) {
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to allocate URBs\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
|
||||
retval = -ENOMEM;
|
||||
goto error_3;
|
||||
}
|
||||
@ -3255,9 +3167,7 @@ static int sisusb_probe(struct usb_interface *intf,
|
||||
|
||||
for (i = 0; i < sisusb->numobufs; i++) {
|
||||
if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) {
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to allocate URBs\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
|
||||
retval = -ENOMEM;
|
||||
goto error_4;
|
||||
}
|
||||
@ -3266,15 +3176,12 @@ static int sisusb_probe(struct usb_interface *intf,
|
||||
sisusb->urbstatus[i] = 0;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "sisusbvga[%d]: Allocated %d output buffers\n",
|
||||
sisusb->minor, sisusb->numobufs);
|
||||
dev_info(&sisusb->sisusb_dev->dev, "Allocated %d output buffers\n", sisusb->numobufs);
|
||||
|
||||
#ifdef INCL_SISUSB_CON
|
||||
/* Allocate our SiS_Pr */
|
||||
if (!(sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL))) {
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to allocate SiS_Pr\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate SiS_Pr\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -3296,10 +3203,7 @@ static int sisusb_probe(struct usb_interface *intf,
|
||||
ret |= register_ioctl32_conversion(SISUSB_GET_CONFIG, NULL);
|
||||
ret |= register_ioctl32_conversion(SISUSB_COMMAND, NULL);
|
||||
if (ret)
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Error registering ioctl32 "
|
||||
"translations\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Error registering ioctl32 translations\n");
|
||||
else
|
||||
sisusb->ioctl32registered = 1;
|
||||
}
|
||||
@ -3315,23 +3219,17 @@ static int sisusb_probe(struct usb_interface *intf,
|
||||
initscreen = 0;
|
||||
#endif
|
||||
if (sisusb_init_gfxdevice(sisusb, initscreen))
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to early "
|
||||
"initialize device\n",
|
||||
sisusb->minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to early initialize device\n");
|
||||
|
||||
} else
|
||||
printk(KERN_INFO
|
||||
"sisusbvga[%d]: Not attached to USB 2.0 hub, "
|
||||
"deferring init\n",
|
||||
sisusb->minor);
|
||||
dev_info(&sisusb->sisusb_dev->dev, "Not attached to USB 2.0 hub, deferring init\n");
|
||||
|
||||
sisusb->ready = 1;
|
||||
|
||||
#ifdef SISUSBENDIANTEST
|
||||
printk(KERN_DEBUG "sisusb: *** RWTEST ***\n");
|
||||
dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST ***\n");
|
||||
sisusb_testreadwrite(sisusb);
|
||||
printk(KERN_DEBUG "sisusb: *** RWTEST END ***\n");
|
||||
dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST END ***\n");
|
||||
#endif
|
||||
|
||||
#ifdef INCL_SISUSB_CON
|
||||
@ -3354,7 +3252,6 @@ error_1:
|
||||
static void sisusb_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct sisusb_usb_data *sisusb;
|
||||
int minor;
|
||||
|
||||
/* This should *not* happen */
|
||||
if (!(sisusb = usb_get_intfdata(intf)))
|
||||
@ -3364,8 +3261,6 @@ static void sisusb_disconnect(struct usb_interface *intf)
|
||||
sisusb_console_exit(sisusb);
|
||||
#endif
|
||||
|
||||
minor = sisusb->minor;
|
||||
|
||||
usb_deregister_dev(intf, &usb_sisusb_class);
|
||||
|
||||
mutex_lock(&sisusb->lock);
|
||||
@ -3384,10 +3279,7 @@ static void sisusb_disconnect(struct usb_interface *intf)
|
||||
ret |= unregister_ioctl32_conversion(SISUSB_GET_CONFIG);
|
||||
ret |= unregister_ioctl32_conversion(SISUSB_COMMAND);
|
||||
if (ret) {
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Error unregistering "
|
||||
"ioctl32 translations\n",
|
||||
minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Error unregistering ioctl32 translations\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -3400,7 +3292,7 @@ static void sisusb_disconnect(struct usb_interface *intf)
|
||||
/* decrement our usage count */
|
||||
kref_put(&sisusb->kref, sisusb_delete);
|
||||
|
||||
printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor);
|
||||
dev_info(&sisusb->sisusb_dev->dev, "Disconnected\n");
|
||||
}
|
||||
|
||||
static struct usb_device_id sisusb_table [] = {
|
||||
@ -3424,22 +3316,12 @@ static struct usb_driver sisusb_driver = {
|
||||
|
||||
static int __init usb_sisusb_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
#ifdef INCL_SISUSB_CON
|
||||
sisusb_init_concode();
|
||||
#endif
|
||||
|
||||
if (!(retval = usb_register(&sisusb_driver))) {
|
||||
|
||||
printk(KERN_INFO "sisusb: Driver version %d.%d.%d\n",
|
||||
SISUSB_VERSION, SISUSB_REVISION, SISUSB_PATCHLEVEL);
|
||||
printk(KERN_INFO
|
||||
"sisusb: Copyright (C) 2005 Thomas Winischhofer\n");
|
||||
|
||||
}
|
||||
|
||||
return retval;
|
||||
return usb_register(&sisusb_driver);
|
||||
}
|
||||
|
||||
static void __exit usb_sisusb_exit(void)
|
||||
|
@ -8,29 +8,29 @@
|
||||
*
|
||||
* Otherwise, the following license terms apply:
|
||||
*
|
||||
* * Redistribution and use in source and binary forms, with or without
|
||||
* * modification, are permitted provided that the following conditions
|
||||
* * are met:
|
||||
* * 1) Redistributions of source code must retain the above copyright
|
||||
* * notice, this list of conditions and the following disclaimer.
|
||||
* * 2) Redistributions in binary form must reproduce the above copyright
|
||||
* * notice, this list of conditions and the following disclaimer in the
|
||||
* * documentation and/or other materials provided with the distribution.
|
||||
* * 3) The name of the author may not be used to endorse or promote products
|
||||
* * derived from this software without specific prior written permission.
|
||||
* *
|
||||
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
|
||||
* * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1) Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2) Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3) The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* Author: Thomas Winischhofer <thomas@winischhofer.net>
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Thomas Winischhofer <thomas@winischhofer.net>
|
||||
*
|
||||
*/
|
||||
|
||||
@ -44,16 +44,14 @@
|
||||
#include <linux/mutex.h>
|
||||
|
||||
/* For older kernels, support for text consoles is by default
|
||||
* off. To ensable text console support, change the following:
|
||||
* off. To enable text console support, change the following:
|
||||
*/
|
||||
#if 0
|
||||
#define CONFIG_USB_SISUSBVGA_CON
|
||||
#endif
|
||||
/* #define CONFIG_USB_SISUSBVGA_CON */
|
||||
|
||||
/* Version Information */
|
||||
|
||||
#define SISUSB_VERSION 0
|
||||
#define SISUSB_REVISION 0
|
||||
#define SISUSB_REVISION 0
|
||||
#define SISUSB_PATCHLEVEL 8
|
||||
|
||||
/* Include console and mode switching code? */
|
||||
@ -74,7 +72,7 @@
|
||||
#define SISUSB_IBUF_SIZE 0x01000
|
||||
#define SISUSB_OBUF_SIZE 0x10000 /* fixed */
|
||||
|
||||
#define NUMOBUFS 8 /* max number of output buffers/output URBs */
|
||||
#define NUMOBUFS 8 /* max number of output buffers/output URBs */
|
||||
|
||||
/* About endianness:
|
||||
*
|
||||
@ -93,7 +91,7 @@
|
||||
*/
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
#define SISUSB_CORRECT_ENDIANNESS_PACKET(p) \
|
||||
#define SISUSB_CORRECT_ENDIANNESS_PACKET(p) \
|
||||
do { \
|
||||
p->header = cpu_to_le16(p->header); \
|
||||
p->address = cpu_to_le32(p->address); \
|
||||
@ -105,7 +103,7 @@
|
||||
|
||||
struct sisusb_usb_data;
|
||||
|
||||
struct sisusb_urb_context { /* urb->context for outbound bulk URBs */
|
||||
struct sisusb_urb_context { /* urb->context for outbound bulk URBs */
|
||||
struct sisusb_usb_data *sisusb;
|
||||
int urbindex;
|
||||
int *actual_length;
|
||||
@ -116,16 +114,16 @@ struct sisusb_usb_data {
|
||||
struct usb_interface *interface;
|
||||
struct kref kref;
|
||||
wait_queue_head_t wait_q; /* for syncind and timeouts */
|
||||
struct mutex lock; /* general race avoidance */
|
||||
unsigned int ifnum; /* interface number of the USB device */
|
||||
int minor; /* minor (for logging clarity) */
|
||||
int isopen; /* !=0 if open */
|
||||
int present; /* !=0 if device is present on the bus */
|
||||
int ready; /* !=0 if device is ready for userland */
|
||||
struct mutex lock; /* general race avoidance */
|
||||
unsigned int ifnum; /* interface number of the USB device */
|
||||
int minor; /* minor (for logging clarity) */
|
||||
int isopen; /* !=0 if open */
|
||||
int present; /* !=0 if device is present on the bus */
|
||||
int ready; /* !=0 if device is ready for userland */
|
||||
#ifdef SISUSB_OLD_CONFIG_COMPAT
|
||||
int ioctl32registered;
|
||||
#endif
|
||||
int numobufs; /* number of obufs = number of out urbs */
|
||||
int numobufs; /* number of obufs = number of out urbs */
|
||||
char *obuf[NUMOBUFS], *ibuf; /* transfer buffers */
|
||||
int obufsize, ibufsize;
|
||||
dma_addr_t transfer_dma_out[NUMOBUFS];
|
||||
@ -136,13 +134,13 @@ struct sisusb_usb_data {
|
||||
unsigned char completein;
|
||||
struct sisusb_urb_context urbout_context[NUMOBUFS];
|
||||
unsigned long flagb0;
|
||||
unsigned long vrambase; /* framebuffer base */
|
||||
unsigned int vramsize; /* framebuffer size (bytes) */
|
||||
unsigned long vrambase; /* framebuffer base */
|
||||
unsigned int vramsize; /* framebuffer size (bytes) */
|
||||
unsigned long mmiobase;
|
||||
unsigned int mmiosize;
|
||||
unsigned long ioportbase;
|
||||
unsigned char devinit; /* device initialized? */
|
||||
unsigned char gfxinit; /* graphics core initialized? */
|
||||
unsigned char devinit; /* device initialized? */
|
||||
unsigned char gfxinit; /* graphics core initialized? */
|
||||
unsigned short chipid, chipvendor;
|
||||
unsigned short chiprevision;
|
||||
#ifdef INCL_SISUSB_CON
|
||||
@ -152,7 +150,7 @@ struct sisusb_usb_data {
|
||||
int haveconsole, con_first, con_last;
|
||||
int havethisconsole[MAX_NR_CONSOLES];
|
||||
int textmodedestroyed;
|
||||
unsigned int sisusb_num_columns; /* real number, not vt's idea */
|
||||
unsigned int sisusb_num_columns; /* real number, not vt's idea */
|
||||
int cur_start_addr, con_rolled_over;
|
||||
int sisusb_cursor_loc, bad_cursor_pos;
|
||||
int sisusb_cursor_size_from;
|
||||
@ -197,7 +195,7 @@ struct sisusb_packet {
|
||||
unsigned short header;
|
||||
u32 address;
|
||||
u32 data;
|
||||
} __attribute__((__packed__));
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#define CLEARPACKET(packet) memset(packet, 0, 10)
|
||||
|
||||
@ -265,36 +263,36 @@ struct sisusb_packet {
|
||||
|
||||
/* Structure argument for SISUSB_GET_INFO ioctl */
|
||||
struct sisusb_info {
|
||||
__u32 sisusb_id; /* for identifying sisusb */
|
||||
#define SISUSB_ID 0x53495355 /* Identify myself with 'SISU' */
|
||||
__u8 sisusb_version;
|
||||
__u8 sisusb_revision;
|
||||
__u8 sisusb_patchlevel;
|
||||
__u8 sisusb_gfxinit; /* graphics core initialized? */
|
||||
__u32 sisusb_id; /* for identifying sisusb */
|
||||
#define SISUSB_ID 0x53495355 /* Identify myself with 'SISU' */
|
||||
__u8 sisusb_version;
|
||||
__u8 sisusb_revision;
|
||||
__u8 sisusb_patchlevel;
|
||||
__u8 sisusb_gfxinit; /* graphics core initialized? */
|
||||
|
||||
__u32 sisusb_vrambase;
|
||||
__u32 sisusb_mmiobase;
|
||||
__u32 sisusb_iobase;
|
||||
__u32 sisusb_pcibase;
|
||||
__u32 sisusb_vrambase;
|
||||
__u32 sisusb_mmiobase;
|
||||
__u32 sisusb_iobase;
|
||||
__u32 sisusb_pcibase;
|
||||
|
||||
__u32 sisusb_vramsize; /* framebuffer size in bytes */
|
||||
__u32 sisusb_vramsize; /* framebuffer size in bytes */
|
||||
|
||||
__u32 sisusb_minor;
|
||||
__u32 sisusb_minor;
|
||||
|
||||
__u32 sisusb_fbdevactive; /* != 0 if framebuffer device active */
|
||||
__u32 sisusb_fbdevactive; /* != 0 if framebuffer device active */
|
||||
|
||||
__u32 sisusb_conactive; /* != 0 if console driver active */
|
||||
__u32 sisusb_conactive; /* != 0 if console driver active */
|
||||
|
||||
__u8 sisusb_reserved[28]; /* for future use */
|
||||
__u8 sisusb_reserved[28]; /* for future use */
|
||||
};
|
||||
|
||||
struct sisusb_command {
|
||||
__u8 operation; /* see below */
|
||||
__u8 data0; /* operation dependent */
|
||||
__u8 data1; /* operation dependent */
|
||||
__u8 data2; /* operation dependent */
|
||||
__u32 data3; /* operation dependent */
|
||||
__u32 data4; /* for future use */
|
||||
__u8 operation; /* see below */
|
||||
__u8 data0; /* operation dependent */
|
||||
__u8 data1; /* operation dependent */
|
||||
__u8 data2; /* operation dependent */
|
||||
__u32 data3; /* operation dependent */
|
||||
__u32 data4; /* for future use */
|
||||
};
|
||||
|
||||
#define SUCMD_GET 0x01 /* for all: data0 = index, data3 = port */
|
||||
@ -306,7 +304,7 @@ struct sisusb_command {
|
||||
|
||||
#define SUCMD_CLRSCR 0x07 /* data0:1:2 = length, data3 = address */
|
||||
|
||||
#define SUCMD_HANDLETEXTMODE 0x08 /* Reset/destroy text mode */
|
||||
#define SUCMD_HANDLETEXTMODE 0x08 /* Reset/destroy text mode */
|
||||
|
||||
#define SUCMD_SETMODE 0x09 /* Set a display mode (data3 = SiS mode) */
|
||||
#define SUCMD_SETVESAMODE 0x0a /* Set a display mode (data3 = VESA mode) */
|
||||
@ -315,6 +313,4 @@ struct sisusb_command {
|
||||
#define SISUSB_GET_CONFIG_SIZE _IOR(0xF3,0x3E,__u32)
|
||||
#define SISUSB_GET_CONFIG _IOR(0xF3,0x3F,struct sisusb_info)
|
||||
|
||||
|
||||
#endif /* SISUSB_H */
|
||||
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/string.h>
|
||||
@ -373,14 +374,6 @@ sisusbcon_putc(struct vc_data *c, int ch, int y, int x)
|
||||
return;
|
||||
|
||||
/* sisusb->lock is down */
|
||||
|
||||
/* Don't need to put the character into buffer ourselves,
|
||||
* because the vt does this BEFORE calling us.
|
||||
*/
|
||||
#if 0
|
||||
sisusbcon_writew(ch, SISUSB_VADDR(x, y));
|
||||
#endif
|
||||
|
||||
if (sisusb_is_inactive(c, sisusb)) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
return;
|
||||
@ -490,10 +483,6 @@ sisusbcon_bmove(struct vc_data *c, int sy, int sx,
|
||||
struct sisusb_usb_data *sisusb;
|
||||
ssize_t written;
|
||||
int cols, length;
|
||||
#if 0
|
||||
u16 *src, *dest;
|
||||
int i;
|
||||
#endif
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
@ -505,41 +494,6 @@ sisusbcon_bmove(struct vc_data *c, int sy, int sx,
|
||||
|
||||
cols = sisusb->sisusb_num_columns;
|
||||
|
||||
/* Don't need to move data outselves, because
|
||||
* vt does this BEFORE calling us.
|
||||
* This is only used by vt's insert/deletechar.
|
||||
*/
|
||||
#if 0
|
||||
if (sx == 0 && dx == 0 && width >= c->vc_cols && width <= cols) {
|
||||
|
||||
sisusbcon_memmovew(SISUSB_VADDR(0, dy), SISUSB_VADDR(0, sy),
|
||||
height * width * 2);
|
||||
|
||||
} else if (dy < sy || (dy == sy && dx < sx)) {
|
||||
|
||||
src = SISUSB_VADDR(sx, sy);
|
||||
dest = SISUSB_VADDR(dx, dy);
|
||||
|
||||
for (i = height; i > 0; i--) {
|
||||
sisusbcon_memmovew(dest, src, width * 2);
|
||||
src += cols;
|
||||
dest += cols;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
src = SISUSB_VADDR(sx, sy + height - 1);
|
||||
dest = SISUSB_VADDR(dx, dy + height - 1);
|
||||
|
||||
for (i = height; i > 0; i--) {
|
||||
sisusbcon_memmovew(dest, src, width * 2);
|
||||
src -= cols;
|
||||
dest -= cols;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sisusb_is_inactive(c, sisusb)) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
return;
|
||||
@ -584,7 +538,7 @@ sisusbcon_switch(struct vc_data *c)
|
||||
*/
|
||||
if (c->vc_origin == (unsigned long)c->vc_screenbuf) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
printk(KERN_DEBUG "sisusb: ASSERT ORIGIN != SCREENBUF!\n");
|
||||
dev_dbg(&sisusb->sisusb_dev->dev, "ASSERT ORIGIN != SCREENBUF!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1475,7 +1429,7 @@ static const struct consw sisusb_dummy_con = {
|
||||
int
|
||||
sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
|
||||
{
|
||||
int i, ret, minor = sisusb->minor;
|
||||
int i, ret;
|
||||
|
||||
mutex_lock(&sisusb->lock);
|
||||
|
||||
@ -1508,9 +1462,7 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
|
||||
/* Set up text mode (and upload default font) */
|
||||
if (sisusb_reset_text_mode(sisusb, 1)) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to set up text mode\n",
|
||||
minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to set up text mode\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1531,9 +1483,7 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
|
||||
/* Allocate screen buffer */
|
||||
if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to allocate screen buffer\n",
|
||||
minor);
|
||||
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate screen buffer\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
* * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Thomas Winischhofer <thomas@winischhofer.net>
|
||||
* Author: Thomas Winischhofer <thomas@winischhofer.net>
|
||||
*
|
||||
*/
|
||||
|
||||
@ -55,131 +55,39 @@
|
||||
/* POINTER INITIALIZATION */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiSUSB_InitPtr(struct SiS_Private *SiS_Pr)
|
||||
static void SiSUSB_InitPtr(struct SiS_Private *SiS_Pr)
|
||||
{
|
||||
SiS_Pr->SiS_ModeResInfo = SiSUSB_ModeResInfo;
|
||||
SiS_Pr->SiS_StandTable = SiSUSB_StandTable;
|
||||
SiS_Pr->SiS_ModeResInfo = SiSUSB_ModeResInfo;
|
||||
SiS_Pr->SiS_StandTable = SiSUSB_StandTable;
|
||||
|
||||
SiS_Pr->SiS_SModeIDTable = SiSUSB_SModeIDTable;
|
||||
SiS_Pr->SiS_EModeIDTable = SiSUSB_EModeIDTable;
|
||||
SiS_Pr->SiS_RefIndex = SiSUSB_RefIndex;
|
||||
SiS_Pr->SiS_CRT1Table = SiSUSB_CRT1Table;
|
||||
SiS_Pr->SiS_SModeIDTable = SiSUSB_SModeIDTable;
|
||||
SiS_Pr->SiS_EModeIDTable = SiSUSB_EModeIDTable;
|
||||
SiS_Pr->SiS_RefIndex = SiSUSB_RefIndex;
|
||||
SiS_Pr->SiS_CRT1Table = SiSUSB_CRT1Table;
|
||||
|
||||
SiS_Pr->SiS_VCLKData = SiSUSB_VCLKData;
|
||||
SiS_Pr->SiS_VCLKData = SiSUSB_VCLKData;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* HELPER: Get ModeID */
|
||||
/*********************************************/
|
||||
|
||||
#if 0
|
||||
unsigned short
|
||||
SiSUSB_GetModeID(int HDisplay, int VDisplay, int Depth)
|
||||
{
|
||||
unsigned short ModeIndex = 0;
|
||||
|
||||
switch (HDisplay)
|
||||
{
|
||||
case 320:
|
||||
if (VDisplay == 200)
|
||||
ModeIndex = ModeIndex_320x200[Depth];
|
||||
else if (VDisplay == 240)
|
||||
ModeIndex = ModeIndex_320x240[Depth];
|
||||
break;
|
||||
case 400:
|
||||
if (VDisplay == 300)
|
||||
ModeIndex = ModeIndex_400x300[Depth];
|
||||
break;
|
||||
case 512:
|
||||
if (VDisplay == 384)
|
||||
ModeIndex = ModeIndex_512x384[Depth];
|
||||
break;
|
||||
case 640:
|
||||
if (VDisplay == 480)
|
||||
ModeIndex = ModeIndex_640x480[Depth];
|
||||
else if (VDisplay == 400)
|
||||
ModeIndex = ModeIndex_640x400[Depth];
|
||||
break;
|
||||
case 720:
|
||||
if (VDisplay == 480)
|
||||
ModeIndex = ModeIndex_720x480[Depth];
|
||||
else if (VDisplay == 576)
|
||||
ModeIndex = ModeIndex_720x576[Depth];
|
||||
break;
|
||||
case 768:
|
||||
if (VDisplay == 576)
|
||||
ModeIndex = ModeIndex_768x576[Depth];
|
||||
break;
|
||||
case 800:
|
||||
if (VDisplay == 600)
|
||||
ModeIndex = ModeIndex_800x600[Depth];
|
||||
else if (VDisplay == 480)
|
||||
ModeIndex = ModeIndex_800x480[Depth];
|
||||
break;
|
||||
case 848:
|
||||
if (VDisplay == 480)
|
||||
ModeIndex = ModeIndex_848x480[Depth];
|
||||
break;
|
||||
case 856:
|
||||
if (VDisplay == 480)
|
||||
ModeIndex = ModeIndex_856x480[Depth];
|
||||
break;
|
||||
case 960:
|
||||
if (VDisplay == 540)
|
||||
ModeIndex = ModeIndex_960x540[Depth];
|
||||
else if (VDisplay == 600)
|
||||
ModeIndex = ModeIndex_960x600[Depth];
|
||||
break;
|
||||
case 1024:
|
||||
if (VDisplay == 576)
|
||||
ModeIndex = ModeIndex_1024x576[Depth];
|
||||
else if (VDisplay == 768)
|
||||
ModeIndex = ModeIndex_1024x768[Depth];
|
||||
break;
|
||||
case 1152:
|
||||
if (VDisplay == 864)
|
||||
ModeIndex = ModeIndex_1152x864[Depth];
|
||||
break;
|
||||
case 1280:
|
||||
switch (VDisplay) {
|
||||
case 720:
|
||||
ModeIndex = ModeIndex_1280x720[Depth];
|
||||
break;
|
||||
case 768:
|
||||
ModeIndex = ModeIndex_1280x768[Depth];
|
||||
break;
|
||||
case 1024:
|
||||
ModeIndex = ModeIndex_1280x1024[Depth];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ModeIndex;
|
||||
}
|
||||
#endif /* 0 */
|
||||
|
||||
/*********************************************/
|
||||
/* HELPER: SetReg, GetReg */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiS_SetReg(struct SiS_Private *SiS_Pr, unsigned long port,
|
||||
unsigned short index, unsigned short data)
|
||||
unsigned short index, unsigned short data)
|
||||
{
|
||||
sisusb_setidxreg(SiS_Pr->sisusb, port, index, data);
|
||||
}
|
||||
|
||||
static void
|
||||
SiS_SetRegByte(struct SiS_Private *SiS_Pr, unsigned long port,
|
||||
unsigned short data)
|
||||
unsigned short data)
|
||||
{
|
||||
sisusb_setreg(SiS_Pr->sisusb, port, data);
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
SiS_GetReg(struct SiS_Private *SiS_Pr, unsigned long port,
|
||||
unsigned short index)
|
||||
SiS_GetReg(struct SiS_Private *SiS_Pr, unsigned long port, unsigned short index)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
@ -200,22 +108,22 @@ SiS_GetRegByte(struct SiS_Private *SiS_Pr, unsigned long port)
|
||||
|
||||
static void
|
||||
SiS_SetRegANDOR(struct SiS_Private *SiS_Pr, unsigned long port,
|
||||
unsigned short index, unsigned short DataAND,
|
||||
unsigned short DataOR)
|
||||
unsigned short index, unsigned short DataAND,
|
||||
unsigned short DataOR)
|
||||
{
|
||||
sisusb_setidxregandor(SiS_Pr->sisusb, port, index, DataAND, DataOR);
|
||||
}
|
||||
|
||||
static void
|
||||
SiS_SetRegAND(struct SiS_Private *SiS_Pr, unsigned long port,
|
||||
unsigned short index, unsigned short DataAND)
|
||||
unsigned short index, unsigned short DataAND)
|
||||
{
|
||||
sisusb_setidxregand(SiS_Pr->sisusb, port, index, DataAND);
|
||||
}
|
||||
|
||||
static void
|
||||
SiS_SetRegOR(struct SiS_Private *SiS_Pr,unsigned long port,
|
||||
unsigned short index, unsigned short DataOR)
|
||||
SiS_SetRegOR(struct SiS_Private *SiS_Pr, unsigned long port,
|
||||
unsigned short index, unsigned short DataOR)
|
||||
{
|
||||
sisusb_setidxregor(SiS_Pr->sisusb, port, index, DataOR);
|
||||
}
|
||||
@ -224,8 +132,7 @@ SiS_SetRegOR(struct SiS_Private *SiS_Pr,unsigned long port,
|
||||
/* HELPER: DisplayOn, DisplayOff */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiS_DisplayOn(struct SiS_Private *SiS_Pr)
|
||||
static void SiS_DisplayOn(struct SiS_Private *SiS_Pr)
|
||||
{
|
||||
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, 0xDF);
|
||||
}
|
||||
@ -234,8 +141,7 @@ SiS_DisplayOn(struct SiS_Private *SiS_Pr)
|
||||
/* HELPER: Init Port Addresses */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr)
|
||||
static void SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr)
|
||||
{
|
||||
SiS_Pr->SiS_P3c4 = BaseAddr + 0x14;
|
||||
SiS_Pr->SiS_P3d4 = BaseAddr + 0x24;
|
||||
@ -258,8 +164,7 @@ SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr)
|
||||
/* HELPER: GetSysFlags */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiS_GetSysFlags(struct SiS_Private *SiS_Pr)
|
||||
static void SiS_GetSysFlags(struct SiS_Private *SiS_Pr)
|
||||
{
|
||||
SiS_Pr->SiS_MyCR63 = 0x63;
|
||||
}
|
||||
@ -268,8 +173,7 @@ SiS_GetSysFlags(struct SiS_Private *SiS_Pr)
|
||||
/* HELPER: Init PCI & Engines */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiSInitPCIetc(struct SiS_Private *SiS_Pr)
|
||||
static void SiSInitPCIetc(struct SiS_Private *SiS_Pr)
|
||||
{
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x20, 0xa1);
|
||||
/* - Enable 2D (0x40)
|
||||
@ -285,8 +189,7 @@ SiSInitPCIetc(struct SiS_Private *SiS_Pr)
|
||||
/* HELPER: SET SEGMENT REGISTERS */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiS_SetSegRegLower(struct SiS_Private *SiS_Pr, unsigned short value)
|
||||
static void SiS_SetSegRegLower(struct SiS_Private *SiS_Pr, unsigned short value)
|
||||
{
|
||||
unsigned short temp;
|
||||
|
||||
@ -299,8 +202,7 @@ SiS_SetSegRegLower(struct SiS_Private *SiS_Pr, unsigned short value)
|
||||
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd, temp);
|
||||
}
|
||||
|
||||
static void
|
||||
SiS_SetSegRegUpper(struct SiS_Private *SiS_Pr, unsigned short value)
|
||||
static void SiS_SetSegRegUpper(struct SiS_Private *SiS_Pr, unsigned short value)
|
||||
{
|
||||
unsigned short temp;
|
||||
|
||||
@ -313,15 +215,13 @@ SiS_SetSegRegUpper(struct SiS_Private *SiS_Pr, unsigned short value)
|
||||
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd, temp);
|
||||
}
|
||||
|
||||
static void
|
||||
SiS_SetSegmentReg(struct SiS_Private *SiS_Pr, unsigned short value)
|
||||
static void SiS_SetSegmentReg(struct SiS_Private *SiS_Pr, unsigned short value)
|
||||
{
|
||||
SiS_SetSegRegLower(SiS_Pr, value);
|
||||
SiS_SetSegRegUpper(SiS_Pr, value);
|
||||
}
|
||||
|
||||
static void
|
||||
SiS_ResetSegmentReg(struct SiS_Private *SiS_Pr)
|
||||
static void SiS_ResetSegmentReg(struct SiS_Private *SiS_Pr)
|
||||
{
|
||||
SiS_SetSegmentReg(SiS_Pr, 0);
|
||||
}
|
||||
@ -337,14 +237,12 @@ SiS_SetSegmentRegOver(struct SiS_Private *SiS_Pr, unsigned short value)
|
||||
SiS_SetSegmentReg(SiS_Pr, value);
|
||||
}
|
||||
|
||||
static void
|
||||
SiS_ResetSegmentRegOver(struct SiS_Private *SiS_Pr)
|
||||
static void SiS_ResetSegmentRegOver(struct SiS_Private *SiS_Pr)
|
||||
{
|
||||
SiS_SetSegmentRegOver(SiS_Pr, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
SiS_ResetSegmentRegisters(struct SiS_Private *SiS_Pr)
|
||||
static void SiS_ResetSegmentRegisters(struct SiS_Private *SiS_Pr)
|
||||
{
|
||||
SiS_ResetSegmentReg(SiS_Pr);
|
||||
SiS_ResetSegmentRegOver(SiS_Pr);
|
||||
@ -356,7 +254,7 @@ SiS_ResetSegmentRegisters(struct SiS_Private *SiS_Pr)
|
||||
|
||||
static int
|
||||
SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo,
|
||||
unsigned short *ModeIdIndex)
|
||||
unsigned short *ModeIdIndex)
|
||||
{
|
||||
if ((*ModeNo) <= 0x13) {
|
||||
|
||||
@ -367,12 +265,14 @@ SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo,
|
||||
|
||||
} else {
|
||||
|
||||
for(*ModeIdIndex = 0; ;(*ModeIdIndex)++) {
|
||||
for (*ModeIdIndex = 0;; (*ModeIdIndex)++) {
|
||||
|
||||
if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID == (*ModeNo))
|
||||
if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID ==
|
||||
(*ModeNo))
|
||||
break;
|
||||
|
||||
if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID == 0xFF)
|
||||
if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID ==
|
||||
0xFF)
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -385,8 +285,7 @@ SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo,
|
||||
/* HELPER: ENABLE CRT1 */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiS_HandleCRT1(struct SiS_Private *SiS_Pr)
|
||||
static void SiS_HandleCRT1(struct SiS_Private *SiS_Pr)
|
||||
{
|
||||
/* Enable CRT1 gating */
|
||||
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, SiS_Pr->SiS_MyCR63, 0xbf);
|
||||
@ -398,9 +297,9 @@ SiS_HandleCRT1(struct SiS_Private *SiS_Pr)
|
||||
|
||||
static unsigned short
|
||||
SiS_GetColorDepth(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short ModeIdIndex)
|
||||
unsigned short ModeIdIndex)
|
||||
{
|
||||
static const unsigned short ColorDepth[6] = { 1, 2, 4, 4, 6, 8};
|
||||
static const unsigned short ColorDepth[6] = { 1, 2, 4, 4, 6, 8 };
|
||||
unsigned short modeflag;
|
||||
short index;
|
||||
|
||||
@ -411,7 +310,8 @@ SiS_GetColorDepth(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
}
|
||||
|
||||
index = (modeflag & ModeTypeMask) - ModeEGA;
|
||||
if (index < 0) index = 0;
|
||||
if (index < 0)
|
||||
index = 0;
|
||||
return ColorDepth[index];
|
||||
}
|
||||
|
||||
@ -421,7 +321,7 @@ SiS_GetColorDepth(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
|
||||
static unsigned short
|
||||
SiS_GetOffset(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short ModeIdIndex, unsigned short rrti)
|
||||
unsigned short ModeIdIndex, unsigned short rrti)
|
||||
{
|
||||
unsigned short xres, temp, colordepth, infoflag;
|
||||
|
||||
@ -458,8 +358,8 @@ SiS_SetSeqRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
|
||||
SRdata = SiS_Pr->SiS_StandTable[StandTableIndex].SR[0] | 0x20;
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, SRdata);
|
||||
|
||||
for(i = 2; i <= 4; i++) {
|
||||
SRdata = SiS_Pr->SiS_StandTable[StandTableIndex].SR[i-1];
|
||||
for (i = 2; i <= 4; i++) {
|
||||
SRdata = SiS_Pr->SiS_StandTable[StandTableIndex].SR[i - 1];
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, i, SRdata);
|
||||
}
|
||||
}
|
||||
@ -488,7 +388,7 @@ SiS_SetCRTCRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
|
||||
|
||||
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, 0x11, 0x7f);
|
||||
|
||||
for(i = 0; i <= 0x18; i++) {
|
||||
for (i = 0; i <= 0x18; i++) {
|
||||
CRTCdata = SiS_Pr->SiS_StandTable[StandTableIndex].CRTC[i];
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, i, CRTCdata);
|
||||
}
|
||||
@ -504,7 +404,7 @@ SiS_SetATTRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
|
||||
unsigned char ARdata;
|
||||
unsigned short i;
|
||||
|
||||
for(i = 0; i <= 0x13; i++) {
|
||||
for (i = 0; i <= 0x13; i++) {
|
||||
ARdata = SiS_Pr->SiS_StandTable[StandTableIndex].ATTR[i];
|
||||
SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da);
|
||||
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, i);
|
||||
@ -529,7 +429,7 @@ SiS_SetGRCRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
|
||||
unsigned char GRdata;
|
||||
unsigned short i;
|
||||
|
||||
for(i = 0; i <= 0x08; i++) {
|
||||
for (i = 0; i <= 0x08; i++) {
|
||||
GRdata = SiS_Pr->SiS_StandTable[StandTableIndex].GRC[i];
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3ce, i, GRdata);
|
||||
}
|
||||
@ -544,12 +444,11 @@ SiS_SetGRCRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
|
||||
/* CLEAR EXTENDED REGISTERS */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiS_ClearExt1Regs(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
|
||||
static void SiS_ClearExt1Regs(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0x0A; i <= 0x0E; i++) {
|
||||
for (i = 0x0A; i <= 0x0E; i++) {
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, i, 0x00);
|
||||
}
|
||||
|
||||
@ -562,15 +461,16 @@ SiS_ClearExt1Regs(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
|
||||
|
||||
static unsigned short
|
||||
SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short ModeIdIndex)
|
||||
unsigned short ModeIdIndex)
|
||||
{
|
||||
unsigned short rrti, i, index, temp;
|
||||
|
||||
if (ModeNo <= 0x13)
|
||||
return 0xFFFF;
|
||||
|
||||
index = SiS_GetReg(SiS_Pr,SiS_Pr->SiS_P3d4, 0x33) & 0x0F;
|
||||
if (index > 0) index--;
|
||||
index = SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x33) & 0x0F;
|
||||
if (index > 0)
|
||||
index--;
|
||||
|
||||
rrti = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].REFindex;
|
||||
ModeNo = SiS_Pr->SiS_RefIndex[rrti].ModeID;
|
||||
@ -580,13 +480,14 @@ SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
if (SiS_Pr->SiS_RefIndex[rrti + i].ModeID != ModeNo)
|
||||
break;
|
||||
|
||||
temp = SiS_Pr->SiS_RefIndex[rrti + i].Ext_InfoFlag & ModeTypeMask;
|
||||
temp =
|
||||
SiS_Pr->SiS_RefIndex[rrti + i].Ext_InfoFlag & ModeTypeMask;
|
||||
if (temp < SiS_Pr->SiS_ModeType)
|
||||
break;
|
||||
|
||||
i++;
|
||||
index--;
|
||||
} while(index != 0xFFFF);
|
||||
} while (index != 0xFFFF);
|
||||
|
||||
i--;
|
||||
|
||||
@ -597,8 +498,7 @@ SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
/* SYNC */
|
||||
/*********************************************/
|
||||
|
||||
static void
|
||||
SiS_SetCRT1Sync(struct SiS_Private *SiS_Pr, unsigned short rrti)
|
||||
static void SiS_SetCRT1Sync(struct SiS_Private *SiS_Pr, unsigned short rrti)
|
||||
{
|
||||
unsigned short sync = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag >> 8;
|
||||
sync &= 0xC0;
|
||||
@ -612,39 +512,40 @@ SiS_SetCRT1Sync(struct SiS_Private *SiS_Pr, unsigned short rrti)
|
||||
|
||||
static void
|
||||
SiS_SetCRT1CRTC(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short ModeIdIndex, unsigned short rrti)
|
||||
unsigned short ModeIdIndex, unsigned short rrti)
|
||||
{
|
||||
unsigned char index;
|
||||
unsigned char index;
|
||||
unsigned short temp, i, j, modeflag;
|
||||
|
||||
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4,0x11,0x7f);
|
||||
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, 0x11, 0x7f);
|
||||
|
||||
modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
|
||||
|
||||
index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRT1CRTC;
|
||||
|
||||
for(i = 0,j = 0; i <= 7; i++, j++) {
|
||||
for (i = 0, j = 0; i <= 7; i++, j++) {
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j,
|
||||
SiS_Pr->SiS_CRT1Table[index].CR[i]);
|
||||
SiS_Pr->SiS_CRT1Table[index].CR[i]);
|
||||
}
|
||||
for(j = 0x10; i <= 10; i++, j++) {
|
||||
for (j = 0x10; i <= 10; i++, j++) {
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j,
|
||||
SiS_Pr->SiS_CRT1Table[index].CR[i]);
|
||||
SiS_Pr->SiS_CRT1Table[index].CR[i]);
|
||||
}
|
||||
for(j = 0x15; i <= 12; i++, j++) {
|
||||
for (j = 0x15; i <= 12; i++, j++) {
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j,
|
||||
SiS_Pr->SiS_CRT1Table[index].CR[i]);
|
||||
SiS_Pr->SiS_CRT1Table[index].CR[i]);
|
||||
}
|
||||
for(j = 0x0A; i <= 15; i++, j++) {
|
||||
for (j = 0x0A; i <= 15; i++, j++) {
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, j,
|
||||
SiS_Pr->SiS_CRT1Table[index].CR[i]);
|
||||
SiS_Pr->SiS_CRT1Table[index].CR[i]);
|
||||
}
|
||||
|
||||
temp = SiS_Pr->SiS_CRT1Table[index].CR[16] & 0xE0;
|
||||
SiS_SetReg(SiS_Pr,SiS_Pr->SiS_P3c4, 0x0E, temp);
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0E, temp);
|
||||
|
||||
temp = ((SiS_Pr->SiS_CRT1Table[index].CR[16]) & 0x01) << 5;
|
||||
if (modeflag & DoubleScanMode) temp |= 0x80;
|
||||
if (modeflag & DoubleScanMode)
|
||||
temp |= 0x80;
|
||||
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3d4, 0x09, 0x5F, temp);
|
||||
|
||||
if (SiS_Pr->SiS_ModeType > ModeVGA)
|
||||
@ -659,10 +560,10 @@ SiS_SetCRT1CRTC(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
|
||||
static void
|
||||
SiS_SetCRT1Offset(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short ModeIdIndex, unsigned short rrti)
|
||||
unsigned short ModeIdIndex, unsigned short rrti)
|
||||
{
|
||||
unsigned short du = SiS_GetOffset(SiS_Pr, ModeNo, ModeIdIndex, rrti);
|
||||
unsigned short infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag;
|
||||
unsigned short infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag;
|
||||
unsigned short temp;
|
||||
|
||||
temp = (du >> 8) & 0x0f;
|
||||
@ -670,11 +571,13 @@ SiS_SetCRT1Offset(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x13, (du & 0xFF));
|
||||
|
||||
if (infoflag & InterlaceMode) du >>= 1;
|
||||
if (infoflag & InterlaceMode)
|
||||
du >>= 1;
|
||||
|
||||
du <<= 5;
|
||||
temp = (du >> 8) & 0xff;
|
||||
if (du & 0xff) temp++;
|
||||
if (du & 0xff)
|
||||
temp++;
|
||||
temp++;
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x10, temp);
|
||||
}
|
||||
@ -685,17 +588,17 @@ SiS_SetCRT1Offset(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
|
||||
static void
|
||||
SiS_SetCRT1VCLK(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short rrti)
|
||||
unsigned short rrti)
|
||||
{
|
||||
unsigned short index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRTVCLK;
|
||||
unsigned short clka = SiS_Pr->SiS_VCLKData[index].SR2B;
|
||||
unsigned short clkb = SiS_Pr->SiS_VCLKData[index].SR2C;
|
||||
|
||||
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4,0x31,0xCF);
|
||||
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x31, 0xCF);
|
||||
|
||||
SiS_SetReg(SiS_Pr,SiS_Pr->SiS_P3c4,0x2B,clka);
|
||||
SiS_SetReg(SiS_Pr,SiS_Pr->SiS_P3c4,0x2C,clkb);
|
||||
SiS_SetReg(SiS_Pr,SiS_Pr->SiS_P3c4,0x2D,0x01);
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x2B, clka);
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x2C, clkb);
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x2D, 0x01);
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
@ -704,7 +607,7 @@ SiS_SetCRT1VCLK(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
|
||||
static void
|
||||
SiS_SetCRT1FIFO_310(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short mi)
|
||||
unsigned short mi)
|
||||
{
|
||||
unsigned short modeflag = SiS_Pr->SiS_EModeIDTable[mi].Ext_ModeFlag;
|
||||
|
||||
@ -729,7 +632,7 @@ SiS_SetCRT1FIFO_310(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
|
||||
static void
|
||||
SiS_SetVCLKState(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short rrti)
|
||||
unsigned short rrti)
|
||||
{
|
||||
unsigned short data = 0, VCLK = 0, index = 0;
|
||||
|
||||
@ -738,7 +641,8 @@ SiS_SetVCLKState(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
VCLK = SiS_Pr->SiS_VCLKData[index].CLOCK;
|
||||
}
|
||||
|
||||
if (VCLK >= 166) data |= 0x0c;
|
||||
if (VCLK >= 166)
|
||||
data |= 0x0c;
|
||||
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x32, 0xf3, data);
|
||||
|
||||
if (VCLK >= 166)
|
||||
@ -758,7 +662,7 @@ SiS_SetVCLKState(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
|
||||
static void
|
||||
SiS_SetCRT1ModeRegs(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short ModeIdIndex, unsigned short rrti)
|
||||
unsigned short ModeIdIndex, unsigned short rrti)
|
||||
{
|
||||
unsigned short data, infoflag = 0, modeflag;
|
||||
|
||||
@ -778,17 +682,22 @@ SiS_SetCRT1ModeRegs(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
data |= 0x02;
|
||||
data |= ((SiS_Pr->SiS_ModeType - ModeVGA) << 2);
|
||||
}
|
||||
if (infoflag & InterlaceMode) data |= 0x20;
|
||||
if (infoflag & InterlaceMode)
|
||||
data |= 0x20;
|
||||
}
|
||||
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x06, 0xC0, data);
|
||||
|
||||
data = 0;
|
||||
if (infoflag & InterlaceMode) {
|
||||
/* data = (Hsync / 8) - ((Htotal / 8) / 2) + 3 */
|
||||
unsigned short hrs = (SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x04) |
|
||||
((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0xc0) << 2)) - 3;
|
||||
unsigned short hto = (SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x00) |
|
||||
((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0x03) << 8)) + 5;
|
||||
unsigned short hrs =
|
||||
(SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x04) |
|
||||
((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0xc0) << 2))
|
||||
- 3;
|
||||
unsigned short hto =
|
||||
(SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x00) |
|
||||
((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0x03) << 8))
|
||||
+ 5;
|
||||
data = hrs - (hto >> 1) + 3;
|
||||
}
|
||||
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x19, (data & 0xFF));
|
||||
@ -829,20 +738,26 @@ SiS_SetCRT1ModeRegs(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
|
||||
static void
|
||||
SiS_WriteDAC(struct SiS_Private *SiS_Pr, unsigned long DACData,
|
||||
unsigned short shiftflag, unsigned short dl, unsigned short ah,
|
||||
unsigned short al, unsigned short dh)
|
||||
unsigned short shiftflag, unsigned short dl, unsigned short ah,
|
||||
unsigned short al, unsigned short dh)
|
||||
{
|
||||
unsigned short d1, d2, d3;
|
||||
|
||||
switch (dl) {
|
||||
case 0:
|
||||
d1 = dh; d2 = ah; d3 = al;
|
||||
break;
|
||||
case 1:
|
||||
d1 = ah; d2 = al; d3 = dh;
|
||||
break;
|
||||
default:
|
||||
d1 = al; d2 = dh; d3 = ah;
|
||||
case 0:
|
||||
d1 = dh;
|
||||
d2 = ah;
|
||||
d3 = al;
|
||||
break;
|
||||
case 1:
|
||||
d1 = ah;
|
||||
d2 = al;
|
||||
d3 = dh;
|
||||
break;
|
||||
default:
|
||||
d1 = al;
|
||||
d2 = dh;
|
||||
d3 = ah;
|
||||
}
|
||||
SiS_SetRegByte(SiS_Pr, DACData, (d1 << shiftflag));
|
||||
SiS_SetRegByte(SiS_Pr, DACData, (d2 << shiftflag));
|
||||
@ -850,7 +765,8 @@ SiS_WriteDAC(struct SiS_Private *SiS_Pr, unsigned long DACData,
|
||||
}
|
||||
|
||||
static void
|
||||
SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short mi)
|
||||
SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short mi)
|
||||
{
|
||||
unsigned short data, data2, time, i, j, k, m, n, o;
|
||||
unsigned short si, di, bx, sf;
|
||||
@ -884,41 +800,45 @@ SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short mi
|
||||
|
||||
SiS_SetRegByte(SiS_Pr, DACAddr, 0x00);
|
||||
|
||||
for(i = 0; i < j; i++) {
|
||||
for (i = 0; i < j; i++) {
|
||||
data = table[i];
|
||||
for(k = 0; k < 3; k++) {
|
||||
for (k = 0; k < 3; k++) {
|
||||
data2 = 0;
|
||||
if (data & 0x01) data2 += 0x2A;
|
||||
if (data & 0x02) data2 += 0x15;
|
||||
if (data & 0x01)
|
||||
data2 += 0x2A;
|
||||
if (data & 0x02)
|
||||
data2 += 0x15;
|
||||
SiS_SetRegByte(SiS_Pr, DACData, (data2 << sf));
|
||||
data >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (time == 256) {
|
||||
for(i = 16; i < 32; i++) {
|
||||
for (i = 16; i < 32; i++) {
|
||||
data = table[i] << sf;
|
||||
for(k = 0; k < 3; k++)
|
||||
for (k = 0; k < 3; k++)
|
||||
SiS_SetRegByte(SiS_Pr, DACData, data);
|
||||
}
|
||||
si = 32;
|
||||
for(m = 0; m < 9; m++) {
|
||||
for (m = 0; m < 9; m++) {
|
||||
di = si;
|
||||
bx = si + 4;
|
||||
for(n = 0; n < 3; n++) {
|
||||
for(o = 0; o < 5; o++) {
|
||||
for (n = 0; n < 3; n++) {
|
||||
for (o = 0; o < 5; o++) {
|
||||
SiS_WriteDAC(SiS_Pr, DACData, sf, n,
|
||||
table[di], table[bx], table[si]);
|
||||
table[di], table[bx],
|
||||
table[si]);
|
||||
si++;
|
||||
}
|
||||
si -= 2;
|
||||
for(o = 0; o < 3; o++) {
|
||||
for (o = 0; o < 3; o++) {
|
||||
SiS_WriteDAC(SiS_Pr, DACData, sf, n,
|
||||
table[di], table[si], table[bx]);
|
||||
table[di], table[si],
|
||||
table[bx]);
|
||||
si--;
|
||||
}
|
||||
}
|
||||
si += 5;
|
||||
si += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -929,7 +849,7 @@ SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short mi
|
||||
|
||||
static void
|
||||
SiS_SetCRT1Group(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
unsigned short ModeIdIndex)
|
||||
unsigned short ModeIdIndex)
|
||||
{
|
||||
unsigned short StandTableIndex, rrti;
|
||||
|
||||
@ -970,11 +890,10 @@ SiS_SetCRT1Group(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
|
||||
/* SiSSetMode() */
|
||||
/*********************************************/
|
||||
|
||||
int
|
||||
SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
|
||||
int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
|
||||
{
|
||||
unsigned short ModeIdIndex;
|
||||
unsigned long BaseAddr = SiS_Pr->IOAddress;
|
||||
unsigned long BaseAddr = SiS_Pr->IOAddress;
|
||||
|
||||
SiSUSB_InitPtr(SiS_Pr);
|
||||
SiSUSBRegInit(SiS_Pr, BaseAddr);
|
||||
@ -990,7 +909,7 @@ SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
|
||||
ModeNo &= 0x7f;
|
||||
|
||||
SiS_Pr->SiS_ModeType =
|
||||
SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag & ModeTypeMask;
|
||||
SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag & ModeTypeMask;
|
||||
|
||||
SiS_Pr->SiS_SetFlag = LowModeTests;
|
||||
|
||||
@ -1008,8 +927,7 @@ SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo)
|
||||
int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo)
|
||||
{
|
||||
unsigned short ModeNo = 0;
|
||||
int i;
|
||||
@ -1041,7 +959,3 @@ SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo)
|
||||
}
|
||||
|
||||
#endif /* INCL_SISUSB_CON */
|
||||
|
||||
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,7 @@
|
||||
* * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Thomas Winischhofer <thomas@winischhofer.net>
|
||||
* Author: Thomas Winischhofer <thomas@winischhofer.net>
|
||||
*
|
||||
*/
|
||||
|
||||
@ -52,85 +52,78 @@
|
||||
#define _SISUSB_STRUCT_H_
|
||||
|
||||
struct SiS_St {
|
||||
unsigned char St_ModeID;
|
||||
unsigned short St_ModeFlag;
|
||||
unsigned char St_StTableIndex;
|
||||
unsigned char St_CRT2CRTC;
|
||||
unsigned char St_ResInfo;
|
||||
unsigned char VB_StTVFlickerIndex;
|
||||
unsigned char VB_StTVEdgeIndex;
|
||||
unsigned char VB_StTVYFilterIndex;
|
||||
unsigned char St_PDC;
|
||||
unsigned char St_ModeID;
|
||||
unsigned short St_ModeFlag;
|
||||
unsigned char St_StTableIndex;
|
||||
unsigned char St_CRT2CRTC;
|
||||
unsigned char St_ResInfo;
|
||||
unsigned char VB_StTVFlickerIndex;
|
||||
unsigned char VB_StTVEdgeIndex;
|
||||
unsigned char VB_StTVYFilterIndex;
|
||||
unsigned char St_PDC;
|
||||
};
|
||||
|
||||
struct SiS_StandTable
|
||||
{
|
||||
unsigned char CRT_COLS;
|
||||
unsigned char ROWS;
|
||||
unsigned char CHAR_HEIGHT;
|
||||
unsigned short CRT_LEN;
|
||||
unsigned char SR[4];
|
||||
unsigned char MISC;
|
||||
unsigned char CRTC[0x19];
|
||||
unsigned char ATTR[0x14];
|
||||
unsigned char GRC[9];
|
||||
struct SiS_StandTable {
|
||||
unsigned char CRT_COLS;
|
||||
unsigned char ROWS;
|
||||
unsigned char CHAR_HEIGHT;
|
||||
unsigned short CRT_LEN;
|
||||
unsigned char SR[4];
|
||||
unsigned char MISC;
|
||||
unsigned char CRTC[0x19];
|
||||
unsigned char ATTR[0x14];
|
||||
unsigned char GRC[9];
|
||||
};
|
||||
|
||||
struct SiS_StResInfo_S {
|
||||
unsigned short HTotal;
|
||||
unsigned short VTotal;
|
||||
unsigned short HTotal;
|
||||
unsigned short VTotal;
|
||||
};
|
||||
|
||||
struct SiS_Ext
|
||||
{
|
||||
unsigned char Ext_ModeID;
|
||||
unsigned short Ext_ModeFlag;
|
||||
unsigned short Ext_VESAID;
|
||||
unsigned char Ext_RESINFO;
|
||||
unsigned char VB_ExtTVFlickerIndex;
|
||||
unsigned char VB_ExtTVEdgeIndex;
|
||||
unsigned char VB_ExtTVYFilterIndex;
|
||||
unsigned char VB_ExtTVYFilterIndexROM661;
|
||||
unsigned char REFindex;
|
||||
char ROMMODEIDX661;
|
||||
struct SiS_Ext {
|
||||
unsigned char Ext_ModeID;
|
||||
unsigned short Ext_ModeFlag;
|
||||
unsigned short Ext_VESAID;
|
||||
unsigned char Ext_RESINFO;
|
||||
unsigned char VB_ExtTVFlickerIndex;
|
||||
unsigned char VB_ExtTVEdgeIndex;
|
||||
unsigned char VB_ExtTVYFilterIndex;
|
||||
unsigned char VB_ExtTVYFilterIndexROM661;
|
||||
unsigned char REFindex;
|
||||
char ROMMODEIDX661;
|
||||
};
|
||||
|
||||
struct SiS_Ext2
|
||||
{
|
||||
unsigned short Ext_InfoFlag;
|
||||
unsigned char Ext_CRT1CRTC;
|
||||
unsigned char Ext_CRTVCLK;
|
||||
unsigned char Ext_CRT2CRTC;
|
||||
unsigned char Ext_CRT2CRTC_NS;
|
||||
unsigned char ModeID;
|
||||
unsigned short XRes;
|
||||
unsigned short YRes;
|
||||
unsigned char Ext_PDC;
|
||||
unsigned char Ext_FakeCRT2CRTC;
|
||||
unsigned char Ext_FakeCRT2Clk;
|
||||
struct SiS_Ext2 {
|
||||
unsigned short Ext_InfoFlag;
|
||||
unsigned char Ext_CRT1CRTC;
|
||||
unsigned char Ext_CRTVCLK;
|
||||
unsigned char Ext_CRT2CRTC;
|
||||
unsigned char Ext_CRT2CRTC_NS;
|
||||
unsigned char ModeID;
|
||||
unsigned short XRes;
|
||||
unsigned short YRes;
|
||||
unsigned char Ext_PDC;
|
||||
unsigned char Ext_FakeCRT2CRTC;
|
||||
unsigned char Ext_FakeCRT2Clk;
|
||||
};
|
||||
|
||||
struct SiS_CRT1Table
|
||||
{
|
||||
unsigned char CR[17];
|
||||
struct SiS_CRT1Table {
|
||||
unsigned char CR[17];
|
||||
};
|
||||
|
||||
struct SiS_VCLKData
|
||||
{
|
||||
unsigned char SR2B,SR2C;
|
||||
unsigned short CLOCK;
|
||||
struct SiS_VCLKData {
|
||||
unsigned char SR2B, SR2C;
|
||||
unsigned short CLOCK;
|
||||
};
|
||||
|
||||
struct SiS_ModeResInfo
|
||||
{
|
||||
unsigned short HTotal;
|
||||
unsigned short VTotal;
|
||||
unsigned char XChar;
|
||||
unsigned char YChar;
|
||||
struct SiS_ModeResInfo {
|
||||
unsigned short HTotal;
|
||||
unsigned short VTotal;
|
||||
unsigned char XChar;
|
||||
unsigned char YChar;
|
||||
};
|
||||
|
||||
struct SiS_Private
|
||||
{
|
||||
struct SiS_Private {
|
||||
void *sisusb;
|
||||
|
||||
unsigned long IOAddress;
|
||||
@ -151,19 +144,18 @@ struct SiS_Private
|
||||
unsigned long SiS_P3da;
|
||||
unsigned long SiS_Part1Port;
|
||||
|
||||
unsigned char SiS_MyCR63;
|
||||
unsigned short SiS_CRT1Mode;
|
||||
unsigned short SiS_ModeType;
|
||||
unsigned short SiS_SetFlag;
|
||||
unsigned char SiS_MyCR63;
|
||||
unsigned short SiS_CRT1Mode;
|
||||
unsigned short SiS_ModeType;
|
||||
unsigned short SiS_SetFlag;
|
||||
|
||||
const struct SiS_StandTable *SiS_StandTable;
|
||||
const struct SiS_St *SiS_SModeIDTable;
|
||||
const struct SiS_Ext *SiS_EModeIDTable;
|
||||
const struct SiS_Ext2 *SiS_RefIndex;
|
||||
const struct SiS_CRT1Table *SiS_CRT1Table;
|
||||
const struct SiS_VCLKData *SiS_VCLKData;
|
||||
const struct SiS_ModeResInfo *SiS_ModeResInfo;
|
||||
const struct SiS_StandTable *SiS_StandTable;
|
||||
const struct SiS_St *SiS_SModeIDTable;
|
||||
const struct SiS_Ext *SiS_EModeIDTable;
|
||||
const struct SiS_Ext2 *SiS_RefIndex;
|
||||
const struct SiS_CRT1Table *SiS_CRT1Table;
|
||||
const struct SiS_VCLKData *SiS_VCLKData;
|
||||
const struct SiS_ModeResInfo *SiS_ModeResInfo;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -172,6 +172,10 @@ static inline struct mon_bin_hdr *MON_OFF2HDR(const struct mon_reader_bin *rp,
|
||||
|
||||
#define MON_RING_EMPTY(rp) ((rp)->b_cnt == 0)
|
||||
|
||||
static unsigned char xfer_to_pipe[4] = {
|
||||
PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
|
||||
};
|
||||
|
||||
static struct class *mon_bin_class;
|
||||
static dev_t mon_bin_dev0;
|
||||
static struct cdev mon_bin_cdev;
|
||||
@ -354,13 +358,9 @@ static inline char mon_bin_get_setup(unsigned char *setupb,
|
||||
const struct urb *urb, char ev_type)
|
||||
{
|
||||
|
||||
if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
|
||||
if (!usb_endpoint_xfer_control(&urb->ep->desc) || ev_type != 'S')
|
||||
return '-';
|
||||
|
||||
if (urb->dev->bus->uses_dma &&
|
||||
(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
|
||||
return mon_dmapeek(setupb, urb->setup_dma, SETUP_LEN);
|
||||
}
|
||||
if (urb->setup_packet == NULL)
|
||||
return 'Z';
|
||||
|
||||
@ -386,13 +386,15 @@ static char mon_bin_get_data(const struct mon_reader_bin *rp,
|
||||
}
|
||||
|
||||
static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
||||
char ev_type)
|
||||
char ev_type, int status)
|
||||
{
|
||||
const struct usb_endpoint_descriptor *epd = &urb->ep->desc;
|
||||
unsigned long flags;
|
||||
struct timeval ts;
|
||||
unsigned int urb_length;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
unsigned char dir;
|
||||
struct mon_bin_hdr *ep;
|
||||
char data_tag = 0;
|
||||
|
||||
@ -410,16 +412,19 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
||||
if (length >= rp->b_size/5)
|
||||
length = rp->b_size/5;
|
||||
|
||||
if (usb_pipein(urb->pipe)) {
|
||||
if (usb_urb_dir_in(urb)) {
|
||||
if (ev_type == 'S') {
|
||||
length = 0;
|
||||
data_tag = '<';
|
||||
}
|
||||
/* Cannot rely on endpoint number in case of control ep.0 */
|
||||
dir = USB_DIR_IN;
|
||||
} else {
|
||||
if (ev_type == 'C') {
|
||||
length = 0;
|
||||
data_tag = '>';
|
||||
}
|
||||
dir = 0;
|
||||
}
|
||||
|
||||
if (rp->mmap_active)
|
||||
@ -440,15 +445,14 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
||||
*/
|
||||
memset(ep, 0, PKT_SIZE);
|
||||
ep->type = ev_type;
|
||||
ep->xfer_type = usb_pipetype(urb->pipe);
|
||||
/* We use the fact that usb_pipein() returns 0x80 */
|
||||
ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe);
|
||||
ep->devnum = usb_pipedevice(urb->pipe);
|
||||
ep->xfer_type = xfer_to_pipe[usb_endpoint_type(epd)];
|
||||
ep->epnum = dir | usb_endpoint_num(epd);
|
||||
ep->devnum = urb->dev->devnum;
|
||||
ep->busnum = urb->dev->bus->busnum;
|
||||
ep->id = (unsigned long) urb;
|
||||
ep->ts_sec = ts.tv_sec;
|
||||
ep->ts_usec = ts.tv_usec;
|
||||
ep->status = urb->status;
|
||||
ep->status = status;
|
||||
ep->len_urb = urb_length;
|
||||
ep->len_cap = length;
|
||||
|
||||
@ -471,13 +475,13 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
||||
static void mon_bin_submit(void *data, struct urb *urb)
|
||||
{
|
||||
struct mon_reader_bin *rp = data;
|
||||
mon_bin_event(rp, urb, 'S');
|
||||
mon_bin_event(rp, urb, 'S', -EINPROGRESS);
|
||||
}
|
||||
|
||||
static void mon_bin_complete(void *data, struct urb *urb)
|
||||
static void mon_bin_complete(void *data, struct urb *urb, int status)
|
||||
{
|
||||
struct mon_reader_bin *rp = data;
|
||||
mon_bin_event(rp, urb, 'C');
|
||||
mon_bin_event(rp, urb, 'C', status);
|
||||
}
|
||||
|
||||
static void mon_bin_error(void *data, struct urb *urb, int error)
|
||||
@ -500,10 +504,10 @@ static void mon_bin_error(void *data, struct urb *urb, int error)
|
||||
|
||||
memset(ep, 0, PKT_SIZE);
|
||||
ep->type = 'E';
|
||||
ep->xfer_type = usb_pipetype(urb->pipe);
|
||||
/* We use the fact that usb_pipein() returns 0x80 */
|
||||
ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe);
|
||||
ep->devnum = usb_pipedevice(urb->pipe);
|
||||
ep->xfer_type = xfer_to_pipe[usb_endpoint_type(&urb->ep->desc)];
|
||||
ep->epnum = usb_urb_dir_in(urb) ? USB_DIR_IN : 0;
|
||||
ep->epnum |= usb_endpoint_num(&urb->ep->desc);
|
||||
ep->devnum = urb->dev->devnum;
|
||||
ep->busnum = urb->dev->bus->busnum;
|
||||
ep->id = (unsigned long) urb;
|
||||
ep->status = error;
|
||||
|
@ -129,7 +129,8 @@ static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error)
|
||||
|
||||
/*
|
||||
*/
|
||||
static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb)
|
||||
static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb,
|
||||
int status)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *pos;
|
||||
@ -139,28 +140,18 @@ static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb)
|
||||
mbus->cnt_events++;
|
||||
list_for_each (pos, &mbus->r_list) {
|
||||
r = list_entry(pos, struct mon_reader, r_link);
|
||||
r->rnf_complete(r->r_data, urb);
|
||||
r->rnf_complete(r->r_data, urb, status);
|
||||
}
|
||||
spin_unlock_irqrestore(&mbus->lock, flags);
|
||||
}
|
||||
|
||||
static void mon_complete(struct usb_bus *ubus, struct urb *urb)
|
||||
static void mon_complete(struct usb_bus *ubus, struct urb *urb, int status)
|
||||
{
|
||||
struct mon_bus *mbus;
|
||||
|
||||
mbus = ubus->mon_bus;
|
||||
if (mbus == NULL) {
|
||||
/*
|
||||
* This should not happen.
|
||||
* At this point we do not even know the bus number...
|
||||
*/
|
||||
printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n",
|
||||
urb->pipe);
|
||||
return;
|
||||
}
|
||||
|
||||
mon_bus_complete(mbus, urb);
|
||||
mon_bus_complete(&mon_bus0, urb);
|
||||
if ((mbus = ubus->mon_bus) != NULL)
|
||||
mon_bus_complete(mbus, urb, status);
|
||||
mon_bus_complete(&mon_bus0, urb, status);
|
||||
}
|
||||
|
||||
/* int (*unlink_urb) (struct urb *urb, int status); */
|
||||
@ -170,7 +161,7 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb)
|
||||
*/
|
||||
static void mon_stop(struct mon_bus *mbus)
|
||||
{
|
||||
struct usb_bus *ubus = mbus->u_bus;
|
||||
struct usb_bus *ubus;
|
||||
struct list_head *p;
|
||||
|
||||
if (mbus == &mon_bus0) {
|
||||
|
@ -50,10 +50,13 @@ struct mon_iso_desc {
|
||||
struct mon_event_text {
|
||||
struct list_head e_link;
|
||||
int type; /* submit, complete, etc. */
|
||||
unsigned int pipe; /* Pipe */
|
||||
unsigned long id; /* From pointer, most of the time */
|
||||
unsigned int tstamp;
|
||||
int busnum;
|
||||
char devnum;
|
||||
char epnum;
|
||||
char is_in;
|
||||
char xfertype;
|
||||
int length; /* Depends on type: xfer length or act length */
|
||||
int status;
|
||||
int interval;
|
||||
@ -121,13 +124,9 @@ static inline char mon_text_get_setup(struct mon_event_text *ep,
|
||||
struct urb *urb, char ev_type, struct mon_bus *mbus)
|
||||
{
|
||||
|
||||
if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
|
||||
if (ep->xfertype != USB_ENDPOINT_XFER_CONTROL || ev_type != 'S')
|
||||
return '-';
|
||||
|
||||
if (urb->dev->bus->uses_dma &&
|
||||
(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
|
||||
return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX);
|
||||
}
|
||||
if (urb->setup_packet == NULL)
|
||||
return 'Z'; /* '0' would be not as pretty. */
|
||||
|
||||
@ -138,14 +137,12 @@ static inline char mon_text_get_setup(struct mon_event_text *ep,
|
||||
static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
|
||||
int len, char ev_type, struct mon_bus *mbus)
|
||||
{
|
||||
int pipe = urb->pipe;
|
||||
|
||||
if (len <= 0)
|
||||
return 'L';
|
||||
if (len >= DATA_MAX)
|
||||
len = DATA_MAX;
|
||||
|
||||
if (usb_pipein(pipe)) {
|
||||
if (ep->is_in) {
|
||||
if (ev_type != 'C')
|
||||
return '<';
|
||||
} else {
|
||||
@ -186,7 +183,7 @@ static inline unsigned int mon_get_timestamp(void)
|
||||
}
|
||||
|
||||
static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
|
||||
char ev_type)
|
||||
char ev_type, int status)
|
||||
{
|
||||
struct mon_event_text *ep;
|
||||
unsigned int stamp;
|
||||
@ -203,24 +200,28 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
|
||||
}
|
||||
|
||||
ep->type = ev_type;
|
||||
ep->pipe = urb->pipe;
|
||||
ep->id = (unsigned long) urb;
|
||||
ep->busnum = urb->dev->bus->busnum;
|
||||
ep->devnum = urb->dev->devnum;
|
||||
ep->epnum = usb_endpoint_num(&urb->ep->desc);
|
||||
ep->xfertype = usb_endpoint_type(&urb->ep->desc);
|
||||
ep->is_in = usb_urb_dir_in(urb);
|
||||
ep->tstamp = stamp;
|
||||
ep->length = (ev_type == 'S') ?
|
||||
urb->transfer_buffer_length : urb->actual_length;
|
||||
/* Collecting status makes debugging sense for submits, too */
|
||||
ep->status = urb->status;
|
||||
ep->status = status;
|
||||
|
||||
if (usb_pipeint(urb->pipe)) {
|
||||
if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
|
||||
ep->interval = urb->interval;
|
||||
} else if (usb_pipeisoc(urb->pipe)) {
|
||||
} else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
|
||||
ep->interval = urb->interval;
|
||||
ep->start_frame = urb->start_frame;
|
||||
ep->error_count = urb->error_count;
|
||||
}
|
||||
ep->numdesc = urb->number_of_packets;
|
||||
if (usb_pipeisoc(urb->pipe) && urb->number_of_packets > 0) {
|
||||
if (ep->xfertype == USB_ENDPOINT_XFER_ISOC &&
|
||||
urb->number_of_packets > 0) {
|
||||
if ((ndesc = urb->number_of_packets) > ISODESC_MAX)
|
||||
ndesc = ISODESC_MAX;
|
||||
fp = urb->iso_frame_desc;
|
||||
@ -247,13 +248,13 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
|
||||
static void mon_text_submit(void *data, struct urb *urb)
|
||||
{
|
||||
struct mon_reader_text *rp = data;
|
||||
mon_text_event(rp, urb, 'S');
|
||||
mon_text_event(rp, urb, 'S', -EINPROGRESS);
|
||||
}
|
||||
|
||||
static void mon_text_complete(void *data, struct urb *urb)
|
||||
static void mon_text_complete(void *data, struct urb *urb, int status)
|
||||
{
|
||||
struct mon_reader_text *rp = data;
|
||||
mon_text_event(rp, urb, 'C');
|
||||
mon_text_event(rp, urb, 'C', status);
|
||||
}
|
||||
|
||||
static void mon_text_error(void *data, struct urb *urb, int error)
|
||||
@ -268,9 +269,12 @@ static void mon_text_error(void *data, struct urb *urb, int error)
|
||||
}
|
||||
|
||||
ep->type = 'E';
|
||||
ep->pipe = urb->pipe;
|
||||
ep->id = (unsigned long) urb;
|
||||
ep->busnum = 0;
|
||||
ep->devnum = urb->dev->devnum;
|
||||
ep->epnum = usb_endpoint_num(&urb->ep->desc);
|
||||
ep->xfertype = usb_endpoint_type(&urb->ep->desc);
|
||||
ep->is_in = usb_urb_dir_in(urb);
|
||||
ep->tstamp = 0;
|
||||
ep->length = 0;
|
||||
ep->status = error;
|
||||
@ -413,10 +417,10 @@ static ssize_t mon_text_read_u(struct file *file, char __user *buf,
|
||||
mon_text_read_head_u(rp, &ptr, ep);
|
||||
if (ep->type == 'E') {
|
||||
mon_text_read_statset(rp, &ptr, ep);
|
||||
} else if (usb_pipeisoc(ep->pipe)) {
|
||||
} else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
|
||||
mon_text_read_isostat(rp, &ptr, ep);
|
||||
mon_text_read_isodesc(rp, &ptr, ep);
|
||||
} else if (usb_pipeint(ep->pipe)) {
|
||||
} else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
|
||||
mon_text_read_intstat(rp, &ptr, ep);
|
||||
} else {
|
||||
mon_text_read_statset(rp, &ptr, ep);
|
||||
@ -468,18 +472,17 @@ static void mon_text_read_head_t(struct mon_reader_text *rp,
|
||||
{
|
||||
char udir, utype;
|
||||
|
||||
udir = usb_pipein(ep->pipe) ? 'i' : 'o';
|
||||
switch (usb_pipetype(ep->pipe)) {
|
||||
case PIPE_ISOCHRONOUS: utype = 'Z'; break;
|
||||
case PIPE_INTERRUPT: utype = 'I'; break;
|
||||
case PIPE_CONTROL: utype = 'C'; break;
|
||||
udir = (ep->is_in ? 'i' : 'o');
|
||||
switch (ep->xfertype) {
|
||||
case USB_ENDPOINT_XFER_ISOC: utype = 'Z'; break;
|
||||
case USB_ENDPOINT_XFER_INT: utype = 'I'; break;
|
||||
case USB_ENDPOINT_XFER_CONTROL: utype = 'C'; break;
|
||||
default: /* PIPE_BULK */ utype = 'B';
|
||||
}
|
||||
p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
|
||||
"%lx %u %c %c%c:%03u:%02u",
|
||||
ep->id, ep->tstamp, ep->type,
|
||||
utype, udir,
|
||||
usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
|
||||
utype, udir, ep->devnum, ep->epnum);
|
||||
}
|
||||
|
||||
static void mon_text_read_head_u(struct mon_reader_text *rp,
|
||||
@ -487,18 +490,17 @@ static void mon_text_read_head_u(struct mon_reader_text *rp,
|
||||
{
|
||||
char udir, utype;
|
||||
|
||||
udir = usb_pipein(ep->pipe) ? 'i' : 'o';
|
||||
switch (usb_pipetype(ep->pipe)) {
|
||||
case PIPE_ISOCHRONOUS: utype = 'Z'; break;
|
||||
case PIPE_INTERRUPT: utype = 'I'; break;
|
||||
case PIPE_CONTROL: utype = 'C'; break;
|
||||
udir = (ep->is_in ? 'i' : 'o');
|
||||
switch (ep->xfertype) {
|
||||
case USB_ENDPOINT_XFER_ISOC: utype = 'Z'; break;
|
||||
case USB_ENDPOINT_XFER_INT: utype = 'I'; break;
|
||||
case USB_ENDPOINT_XFER_CONTROL: utype = 'C'; break;
|
||||
default: /* PIPE_BULK */ utype = 'B';
|
||||
}
|
||||
p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
|
||||
"%lx %u %c %c%c:%d:%03u:%u",
|
||||
ep->id, ep->tstamp, ep->type,
|
||||
utype, udir,
|
||||
ep->busnum, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
|
||||
utype, udir, ep->busnum, ep->devnum, ep->epnum);
|
||||
}
|
||||
|
||||
static void mon_text_read_statset(struct mon_reader_text *rp,
|
||||
|
@ -46,7 +46,7 @@ struct mon_reader {
|
||||
|
||||
void (*rnf_submit)(void *data, struct urb *urb);
|
||||
void (*rnf_error)(void *data, struct urb *urb, int error);
|
||||
void (*rnf_complete)(void *data, struct urb *urb);
|
||||
void (*rnf_complete)(void *data, struct urb *urb, int status);
|
||||
};
|
||||
|
||||
void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
|
||||
|
@ -92,6 +92,16 @@ config USB_SERIAL_BELKIN
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called belkin_sa.
|
||||
|
||||
config USB_SERIAL_CH341
|
||||
tristate "USB Winchiphead CH341 Single Port Serial Driver"
|
||||
depends on USB_SERIAL
|
||||
help
|
||||
Say Y here if you want to use a Winchiphead CH341 single port
|
||||
USB to serial adapter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ch341.
|
||||
|
||||
config USB_SERIAL_WHITEHEAT
|
||||
tristate "USB ConnectTech WhiteHEAT Serial Driver"
|
||||
depends on USB_SERIAL
|
||||
|
@ -15,6 +15,7 @@ obj-$(CONFIG_USB_SERIAL_AIRCABLE) += aircable.o
|
||||
obj-$(CONFIG_USB_SERIAL_AIRPRIME) += airprime.o
|
||||
obj-$(CONFIG_USB_SERIAL_ARK3116) += ark3116.o
|
||||
obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o
|
||||
obj-$(CONFIG_USB_SERIAL_CH341) += ch341.o
|
||||
obj-$(CONFIG_USB_SERIAL_CP2101) += cp2101.o
|
||||
obj-$(CONFIG_USB_SERIAL_CYBERJACK) += cyberjack.o
|
||||
obj-$(CONFIG_USB_SERIAL_CYPRESS_M8) += cypress_m8.o
|
||||
|
@ -172,11 +172,6 @@ static void ark3116_set_termios(struct usb_serial_port *port,
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
if (!port->tty || !port->tty->termios) {
|
||||
dbg("%s - no tty structures", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (!priv->termios_initialized) {
|
||||
*(port->tty->termios) = tty_std_termios;
|
||||
|
@ -36,6 +36,16 @@ static int usb_serial_device_match (struct device *dev, struct device_driver *dr
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t show_port_number(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_serial_port *port = to_usb_serial_port(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", port->number - port->serial->minor);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(port_number, S_IRUGO, show_port_number, NULL);
|
||||
|
||||
static int usb_serial_device_probe (struct device *dev)
|
||||
{
|
||||
struct usb_serial_driver *driver;
|
||||
@ -62,6 +72,10 @@ static int usb_serial_device_probe (struct device *dev)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = device_create_file(dev, &dev_attr_port_number);
|
||||
if (retval)
|
||||
goto exit;
|
||||
|
||||
minor = port->number;
|
||||
tty_register_device (usb_serial_tty_driver, minor, dev);
|
||||
dev_info(&port->serial->dev->dev,
|
||||
@ -84,6 +98,8 @@ static int usb_serial_device_remove (struct device *dev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
device_remove_file(&port->dev, &dev_attr_port_number);
|
||||
|
||||
driver = port->serial->type;
|
||||
if (driver->port_remove) {
|
||||
if (!try_module_get(driver->driver.owner)) {
|
||||
|
354
drivers/usb/serial/ch341.c
Normal file
354
drivers/usb/serial/ch341.c
Normal file
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk>
|
||||
*
|
||||
* ch341.c implements a serial port driver for the Winchiphead CH341.
|
||||
*
|
||||
* The CH341 device can be used to implement an RS232 asynchronous
|
||||
* serial port, an IEEE-1284 parallel printer port or a memory-like
|
||||
* interface. In all cases the CH341 supports an I2C interface as well.
|
||||
* This driver only supports the asynchronous serial interface.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/serial.h>
|
||||
#include <linux/serial.h>
|
||||
|
||||
#define DEFAULT_BAUD_RATE 2400
|
||||
#define DEFAULT_TIMEOUT 1000
|
||||
|
||||
static int debug;
|
||||
|
||||
static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x4348, 0x5523) },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
struct ch341_private {
|
||||
unsigned baud_rate;
|
||||
u8 dtr;
|
||||
u8 rts;
|
||||
};
|
||||
|
||||
static int ch341_control_out(struct usb_device *dev, u8 request,
|
||||
u16 value, u16 index)
|
||||
{
|
||||
int r;
|
||||
dbg("ch341_control_out(%02x,%02x,%04x,%04x)", USB_DIR_OUT|0x40,
|
||||
(int)request, (int)value, (int)index);
|
||||
|
||||
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
|
||||
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
|
||||
value, index, NULL, 0, DEFAULT_TIMEOUT);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ch341_control_in(struct usb_device *dev,
|
||||
u8 request, u16 value, u16 index,
|
||||
char *buf, unsigned bufsize)
|
||||
{
|
||||
int r;
|
||||
dbg("ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)", USB_DIR_IN|0x40,
|
||||
(int)request, (int)value, (int)index, buf, (int)bufsize);
|
||||
|
||||
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
|
||||
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
|
||||
value, index, buf, bufsize, DEFAULT_TIMEOUT);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ch341_set_baudrate(struct usb_device *dev,
|
||||
struct ch341_private *priv)
|
||||
{
|
||||
short a, b;
|
||||
int r;
|
||||
|
||||
dbg("ch341_set_baudrate(%d)", priv->baud_rate);
|
||||
switch (priv->baud_rate) {
|
||||
case 2400:
|
||||
a = 0xd901;
|
||||
b = 0x0038;
|
||||
break;
|
||||
case 4800:
|
||||
a = 0x6402;
|
||||
b = 0x001f;
|
||||
break;
|
||||
case 9600:
|
||||
a = 0xb202;
|
||||
b = 0x0013;
|
||||
break;
|
||||
case 19200:
|
||||
a = 0xd902;
|
||||
b = 0x000d;
|
||||
break;
|
||||
case 38400:
|
||||
a = 0x6403;
|
||||
b = 0x000a;
|
||||
break;
|
||||
case 115200:
|
||||
a = 0xcc03;
|
||||
b = 0x0008;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = ch341_control_out(dev, 0x9a, 0x1312, a);
|
||||
if (!r)
|
||||
r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ch341_set_handshake(struct usb_device *dev,
|
||||
struct ch341_private *priv)
|
||||
{
|
||||
dbg("ch341_set_handshake(%d,%d)", priv->dtr, priv->rts);
|
||||
return ch341_control_out(dev, 0xa4,
|
||||
~((priv->dtr?1<<5:0)|(priv->rts?1<<6:0)), 0);
|
||||
}
|
||||
|
||||
static int ch341_get_status(struct usb_device *dev)
|
||||
{
|
||||
char *buffer;
|
||||
int r;
|
||||
const unsigned size = 8;
|
||||
|
||||
dbg("ch341_get_status()");
|
||||
|
||||
buffer = kmalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
|
||||
if ( r < 0)
|
||||
goto out;
|
||||
|
||||
/* Not having the datasheet for the CH341, we ignore the bytes returned
|
||||
* from the device. Return error if the device did not respond in time.
|
||||
*/
|
||||
r = 0;
|
||||
|
||||
out: kfree(buffer);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
|
||||
{
|
||||
char *buffer;
|
||||
int r;
|
||||
const unsigned size = 8;
|
||||
|
||||
dbg("ch341_configure()");
|
||||
|
||||
buffer = kmalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
/* expect two bytes 0x27 0x00 */
|
||||
r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = ch341_control_out(dev, 0xa1, 0, 0);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = ch341_set_baudrate(dev, priv);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
/* expect two bytes 0x56 0x00 */
|
||||
r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
/* expect 0xff 0xee */
|
||||
r = ch341_get_status(dev);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = ch341_set_baudrate(dev, priv);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = ch341_set_handshake(dev, priv);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
/* expect 0x9f 0xee */
|
||||
r = ch341_get_status(dev);
|
||||
|
||||
out: kfree(buffer);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* allocate private data */
|
||||
static int ch341_attach(struct usb_serial *serial)
|
||||
{
|
||||
struct ch341_private *priv;
|
||||
int r;
|
||||
|
||||
dbg("ch341_attach()");
|
||||
|
||||
/* private data */
|
||||
priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->baud_rate = DEFAULT_BAUD_RATE;
|
||||
priv->dtr = 1;
|
||||
priv->rts = 1;
|
||||
|
||||
r = ch341_configure(serial->dev, priv);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
usb_set_serial_port_data(serial->port[0], priv);
|
||||
return 0;
|
||||
|
||||
error: kfree(priv);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* open this device, set default parameters */
|
||||
static int ch341_open(struct usb_serial_port *port, struct file *filp)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]);
|
||||
int r;
|
||||
|
||||
dbg("ch341_open()");
|
||||
|
||||
priv->baud_rate = DEFAULT_BAUD_RATE;
|
||||
priv->dtr = 1;
|
||||
priv->rts = 1;
|
||||
|
||||
r = ch341_configure(serial->dev, priv);
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
r = ch341_set_handshake(serial->dev, priv);
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
r = ch341_set_baudrate(serial->dev, priv);
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
r = usb_serial_generic_open(port, filp);
|
||||
|
||||
out: return r;
|
||||
}
|
||||
|
||||
/* Old_termios contains the original termios settings and
|
||||
* tty->termios contains the new setting to be used.
|
||||
*/
|
||||
static void ch341_set_termios(struct usb_serial_port *port,
|
||||
struct ktermios *old_termios)
|
||||
{
|
||||
struct ch341_private *priv = usb_get_serial_port_data(port);
|
||||
struct tty_struct *tty = port->tty;
|
||||
unsigned baud_rate;
|
||||
|
||||
dbg("ch341_set_termios()");
|
||||
|
||||
if (!tty || !tty->termios)
|
||||
return;
|
||||
|
||||
baud_rate = tty_get_baud_rate(tty);
|
||||
|
||||
switch (baud_rate) {
|
||||
case 2400:
|
||||
case 4800:
|
||||
case 9600:
|
||||
case 19200:
|
||||
case 38400:
|
||||
case 115200:
|
||||
priv->baud_rate = baud_rate;
|
||||
break;
|
||||
default:
|
||||
dbg("Rate %d not supported, using %d",
|
||||
baud_rate, DEFAULT_BAUD_RATE);
|
||||
priv->baud_rate = DEFAULT_BAUD_RATE;
|
||||
}
|
||||
|
||||
ch341_set_baudrate(port->serial->dev, priv);
|
||||
|
||||
/* Unimplemented:
|
||||
* (cflag & CSIZE) : data bits [5, 8]
|
||||
* (cflag & PARENB) : parity {NONE, EVEN, ODD}
|
||||
* (cflag & CSTOPB) : stop bits [1, 2]
|
||||
*/
|
||||
}
|
||||
|
||||
static struct usb_driver ch341_driver = {
|
||||
.name = "ch341",
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = id_table,
|
||||
.no_dynamic_id = 1,
|
||||
};
|
||||
|
||||
static struct usb_serial_driver ch341_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ch341-uart",
|
||||
},
|
||||
.id_table = id_table,
|
||||
.usb_driver = &ch341_driver,
|
||||
.num_interrupt_in = NUM_DONT_CARE,
|
||||
.num_bulk_in = 1,
|
||||
.num_bulk_out = 1,
|
||||
.num_ports = 1,
|
||||
.open = ch341_open,
|
||||
.set_termios = ch341_set_termios,
|
||||
.attach = ch341_attach,
|
||||
};
|
||||
|
||||
static int __init ch341_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = usb_serial_register(&ch341_device);
|
||||
if (retval)
|
||||
return retval;
|
||||
retval = usb_register(&ch341_driver);
|
||||
if (retval)
|
||||
usb_serial_deregister(&ch341_device);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit ch341_exit(void)
|
||||
{
|
||||
usb_deregister(&ch341_driver);
|
||||
usb_serial_deregister(&ch341_device);
|
||||
}
|
||||
|
||||
module_init(ch341_init);
|
||||
module_exit(ch341_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug enabled or not");
|
||||
|
||||
/* EOF ch341.c */
|
@ -53,6 +53,7 @@ static void cp2101_shutdown(struct usb_serial*);
|
||||
static int debug;
|
||||
|
||||
static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
|
||||
{ USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
|
||||
{ USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
|
||||
{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
|
||||
@ -521,7 +522,7 @@ static void cp2101_set_termios (struct usb_serial_port *port,
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
if ((!port->tty) || (!port->tty->termios)) {
|
||||
if (!port->tty || !port->tty->termios) {
|
||||
dbg("%s - no tty structures", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
@ -1169,7 +1169,9 @@ static void remove_sysfs_attrs(struct usb_serial_port *port)
|
||||
/* XXX see create_sysfs_attrs */
|
||||
if (priv->chip_type != SIO) {
|
||||
device_remove_file(&port->dev, &dev_attr_event_char);
|
||||
if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) {
|
||||
if (priv->chip_type == FT232BM ||
|
||||
priv->chip_type == FT2232C ||
|
||||
priv->chip_type == FT232RL) {
|
||||
device_remove_file(&port->dev, &dev_attr_latency_timer);
|
||||
}
|
||||
}
|
||||
@ -2102,6 +2104,7 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
|
||||
case FT8U232AM:
|
||||
case FT232BM:
|
||||
case FT2232C:
|
||||
case FT232RL:
|
||||
/* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
|
||||
format as the data returned from the in point */
|
||||
if ((ret = usb_control_msg(port->serial->dev,
|
||||
|
@ -24,26 +24,6 @@ static struct usb_device_id id_table [] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
static int funsoft_ioctl(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct ktermios t;
|
||||
|
||||
dbg("%s - port %d, cmd 0x%04x", __FUNCTION__, port->number, cmd);
|
||||
|
||||
if (cmd == TCSETSF) {
|
||||
if (user_termios_to_kernel_termios(&t, (struct termios __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
dbg("%s - iflag:%x oflag:%x cflag:%x lflag:%x", __FUNCTION__,
|
||||
t.c_iflag, t.c_oflag, t.c_cflag, t.c_lflag);
|
||||
|
||||
if (!(t.c_lflag & ICANON))
|
||||
return -EINVAL;
|
||||
}
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static struct usb_driver funsoft_driver = {
|
||||
.name = "funsoft",
|
||||
.probe = usb_serial_probe,
|
||||
@ -63,7 +43,6 @@ static struct usb_serial_driver funsoft_device = {
|
||||
.num_bulk_in = NUM_DONT_CARE,
|
||||
.num_bulk_out = NUM_DONT_CARE,
|
||||
.num_ports = 1,
|
||||
.ioctl = funsoft_ioctl,
|
||||
};
|
||||
|
||||
static int __init funsoft_init(void)
|
||||
|
@ -256,6 +256,7 @@ static struct usb_device_id ipaq_id_table [] = {
|
||||
{ USB_DEVICE(0x04DD, 0x9121) }, /* SHARP WS004SH USB Modem */
|
||||
{ USB_DEVICE(0x04DD, 0x9123) }, /* SHARP WS007SH USB Modem */
|
||||
{ USB_DEVICE(0x04DD, 0x9151) }, /* SHARP S01SH USB Modem */
|
||||
{ USB_DEVICE(0x04DD, 0x91AC) }, /* SHARP WS011SH USB Modem */
|
||||
{ USB_DEVICE(0x04E8, 0x5F00) }, /* Samsung NEXiO USB Sync */
|
||||
{ USB_DEVICE(0x04E8, 0x5F01) }, /* Samsung NEXiO USB Sync */
|
||||
{ USB_DEVICE(0x04E8, 0x5F02) }, /* Samsung NEXiO USB Sync */
|
||||
@ -646,11 +647,13 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp)
|
||||
kfree(port->bulk_out_buffer);
|
||||
port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
|
||||
if (port->bulk_in_buffer == NULL) {
|
||||
port->bulk_out_buffer = NULL; /* prevent double free */
|
||||
goto enomem;
|
||||
}
|
||||
port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
|
||||
if (port->bulk_out_buffer == NULL) {
|
||||
kfree(port->bulk_in_buffer);
|
||||
port->bulk_in_buffer = NULL;
|
||||
goto enomem;
|
||||
}
|
||||
port->read_urb->transfer_buffer = port->bulk_in_buffer;
|
||||
|
@ -728,24 +728,32 @@ static void klsi_105_set_termios (struct usb_serial_port *port,
|
||||
#endif
|
||||
}
|
||||
|
||||
switch(cflag & CBAUD) {
|
||||
case B0: /* handled below */
|
||||
switch(tty_get_baud_rate(port->tty)) {
|
||||
case 0: /* handled below */
|
||||
break;
|
||||
case B1200: priv->cfg.baudrate = kl5kusb105a_sio_b1200;
|
||||
case 1200:
|
||||
priv->cfg.baudrate = kl5kusb105a_sio_b1200;
|
||||
break;
|
||||
case B2400: priv->cfg.baudrate = kl5kusb105a_sio_b2400;
|
||||
case 2400:
|
||||
priv->cfg.baudrate = kl5kusb105a_sio_b2400;
|
||||
break;
|
||||
case B4800: priv->cfg.baudrate = kl5kusb105a_sio_b4800;
|
||||
case 4800:
|
||||
priv->cfg.baudrate = kl5kusb105a_sio_b4800;
|
||||
break;
|
||||
case B9600: priv->cfg.baudrate = kl5kusb105a_sio_b9600;
|
||||
case 9600:
|
||||
priv->cfg.baudrate = kl5kusb105a_sio_b9600;
|
||||
break;
|
||||
case B19200: priv->cfg.baudrate = kl5kusb105a_sio_b19200;
|
||||
case 19200:
|
||||
priv->cfg.baudrate = kl5kusb105a_sio_b19200;
|
||||
break;
|
||||
case B38400: priv->cfg.baudrate = kl5kusb105a_sio_b38400;
|
||||
case 38400:
|
||||
priv->cfg.baudrate = kl5kusb105a_sio_b38400;
|
||||
break;
|
||||
case B57600: priv->cfg.baudrate = kl5kusb105a_sio_b57600;
|
||||
case 57600:
|
||||
priv->cfg.baudrate = kl5kusb105a_sio_b57600;
|
||||
break;
|
||||
case B115200: priv->cfg.baudrate = kl5kusb105a_sio_b115200;
|
||||
case 115200:
|
||||
priv->cfg.baudrate = kl5kusb105a_sio_b115200;
|
||||
break;
|
||||
default:
|
||||
err("KLSI USB->Serial converter:"
|
||||
|
@ -82,6 +82,7 @@ static int kobil_tiocmset(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int set, unsigned int clear);
|
||||
static void kobil_read_int_callback( struct urb *urb );
|
||||
static void kobil_write_callback( struct urb *purb );
|
||||
static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old);
|
||||
|
||||
|
||||
static struct usb_device_id id_table [] = {
|
||||
@ -119,6 +120,7 @@ static struct usb_serial_driver kobil_device = {
|
||||
.attach = kobil_startup,
|
||||
.shutdown = kobil_shutdown,
|
||||
.ioctl = kobil_ioctl,
|
||||
.set_termios = kobil_set_termios,
|
||||
.tiocmget = kobil_tiocmget,
|
||||
.tiocmset = kobil_tiocmset,
|
||||
.open = kobil_open,
|
||||
@ -137,7 +139,6 @@ struct kobil_private {
|
||||
int cur_pos; // index of the next char to send in buf
|
||||
__u16 device_type;
|
||||
int line_state;
|
||||
struct ktermios internal_termios;
|
||||
};
|
||||
|
||||
|
||||
@ -216,7 +217,7 @@ static void kobil_shutdown (struct usb_serial *serial)
|
||||
|
||||
static int kobil_open (struct usb_serial_port *port, struct file *filp)
|
||||
{
|
||||
int i, result = 0;
|
||||
int result = 0;
|
||||
struct kobil_private *priv;
|
||||
unsigned char *transfer_buffer;
|
||||
int transfer_buffer_length = 8;
|
||||
@ -242,16 +243,6 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp)
|
||||
port->tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF;
|
||||
port->tty->termios->c_oflag &= ~ONLCR; // do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D)
|
||||
|
||||
// set up internal termios structure
|
||||
priv->internal_termios.c_iflag = port->tty->termios->c_iflag;
|
||||
priv->internal_termios.c_oflag = port->tty->termios->c_oflag;
|
||||
priv->internal_termios.c_cflag = port->tty->termios->c_cflag;
|
||||
priv->internal_termios.c_lflag = port->tty->termios->c_lflag;
|
||||
|
||||
for (i=0; i<NCCS; i++) {
|
||||
priv->internal_termios.c_cc[i] = port->tty->termios->c_cc[i];
|
||||
}
|
||||
|
||||
// allocate memory for transfer buffer
|
||||
transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
|
||||
if (! transfer_buffer) {
|
||||
@ -607,102 +598,79 @@ static int kobil_tiocmset(struct usb_serial_port *port, struct file *file,
|
||||
return (result < 0) ? result : 0;
|
||||
}
|
||||
|
||||
|
||||
static int kobil_ioctl(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old)
|
||||
{
|
||||
struct kobil_private * priv;
|
||||
int result;
|
||||
unsigned short urb_val = 0;
|
||||
unsigned char *transfer_buffer;
|
||||
int transfer_buffer_length = 8;
|
||||
char *settings;
|
||||
void __user *user_arg = (void __user *)arg;
|
||||
int c_cflag = port->tty->termios->c_cflag;
|
||||
speed_t speed;
|
||||
void * settings;
|
||||
|
||||
priv = usb_get_serial_port_data(port);
|
||||
if ((priv->device_type == KOBIL_USBTWIN_PRODUCT_ID) || (priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)) {
|
||||
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)
|
||||
// This device doesn't support ioctl calls
|
||||
return 0;
|
||||
}
|
||||
return;
|
||||
|
||||
switch (cmd) {
|
||||
case TCGETS: // 0x5401
|
||||
if (!access_ok(VERIFY_WRITE, user_arg, sizeof(struct ktermios))) {
|
||||
dbg("%s - port %d Error in access_ok", __FUNCTION__, port->number);
|
||||
return -EFAULT;
|
||||
}
|
||||
if (kernel_termios_to_user_termios((struct ktermios __user *)arg,
|
||||
&priv->internal_termios))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case TCSETS: // 0x5402
|
||||
if (!(port->tty->termios)) {
|
||||
dbg("%s - port %d Error: port->tty->termios is NULL", __FUNCTION__, port->number);
|
||||
return -ENOTTY;
|
||||
}
|
||||
if (!access_ok(VERIFY_READ, user_arg, sizeof(struct ktermios))) {
|
||||
dbg("%s - port %d Error in access_ok", __FUNCTION__, port->number);
|
||||
return -EFAULT;
|
||||
}
|
||||
if (user_termios_to_kernel_termios(&priv->internal_termios,
|
||||
(struct ktermios __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
settings = kzalloc(50, GFP_KERNEL);
|
||||
if (! settings) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
switch (priv->internal_termios.c_cflag & CBAUD) {
|
||||
case B1200:
|
||||
switch (speed = tty_get_baud_rate(port->tty)) {
|
||||
case 1200:
|
||||
urb_val = SUSBCR_SBR_1200;
|
||||
strcat(settings, "1200 ");
|
||||
break;
|
||||
case B9600:
|
||||
case 9600:
|
||||
default:
|
||||
urb_val = SUSBCR_SBR_9600;
|
||||
strcat(settings, "9600 ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits : SUSBCR_SPASB_1StopBit;
|
||||
|
||||
urb_val |= (priv->internal_termios.c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits : SUSBCR_SPASB_1StopBit;
|
||||
strcat(settings, (priv->internal_termios.c_cflag & CSTOPB) ? "2 StopBits " : "1 StopBit ");
|
||||
settings = kzalloc(50, GFP_KERNEL);
|
||||
if (! settings)
|
||||
return;
|
||||
|
||||
if (priv->internal_termios.c_cflag & PARENB) {
|
||||
if (priv->internal_termios.c_cflag & PARODD) {
|
||||
urb_val |= SUSBCR_SPASB_OddParity;
|
||||
strcat(settings, "Odd Parity");
|
||||
} else {
|
||||
urb_val |= SUSBCR_SPASB_EvenParity;
|
||||
strcat(settings, "Even Parity");
|
||||
}
|
||||
sprintf(settings, "%d ", speed);
|
||||
|
||||
if (c_cflag & PARENB) {
|
||||
if (c_cflag & PARODD) {
|
||||
urb_val |= SUSBCR_SPASB_OddParity;
|
||||
strcat(settings, "Odd Parity");
|
||||
} else {
|
||||
urb_val |= SUSBCR_SPASB_NoParity;
|
||||
strcat(settings, "No Parity");
|
||||
urb_val |= SUSBCR_SPASB_EvenParity;
|
||||
strcat(settings, "Even Parity");
|
||||
}
|
||||
dbg("%s - port %d setting port to: %s", __FUNCTION__, port->number, settings );
|
||||
} else {
|
||||
urb_val |= SUSBCR_SPASB_NoParity;
|
||||
strcat(settings, "No Parity");
|
||||
}
|
||||
|
||||
result = usb_control_msg( port->serial->dev,
|
||||
usb_rcvctrlpipe(port->serial->dev, 0 ),
|
||||
SUSBCRequest_SetBaudRateParityAndStopBits,
|
||||
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
|
||||
urb_val,
|
||||
0,
|
||||
settings,
|
||||
0,
|
||||
KOBIL_TIMEOUT
|
||||
);
|
||||
result = usb_control_msg( port->serial->dev,
|
||||
usb_rcvctrlpipe(port->serial->dev, 0 ),
|
||||
SUSBCRequest_SetBaudRateParityAndStopBits,
|
||||
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
|
||||
urb_val,
|
||||
0,
|
||||
settings,
|
||||
0,
|
||||
KOBIL_TIMEOUT
|
||||
);
|
||||
kfree(settings);
|
||||
}
|
||||
|
||||
dbg("%s - port %d Send set_baudrate URB returns: %i", __FUNCTION__, port->number, result);
|
||||
kfree(settings);
|
||||
static int kobil_ioctl(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct kobil_private * priv = usb_get_serial_port_data(port);
|
||||
unsigned char *transfer_buffer;
|
||||
int transfer_buffer_length = 8;
|
||||
int result;
|
||||
|
||||
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)
|
||||
// This device doesn't support ioctl calls
|
||||
return 0;
|
||||
|
||||
switch (cmd) {
|
||||
case TCFLSH: // 0x540B
|
||||
transfer_buffer = kmalloc(transfer_buffer_length, GFP_KERNEL);
|
||||
if (! transfer_buffer) {
|
||||
if (! transfer_buffer)
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
result = usb_control_msg( port->serial->dev,
|
||||
usb_rcvctrlpipe(port->serial->dev, 0 ),
|
||||
@ -716,15 +684,13 @@ static int kobil_ioctl(struct usb_serial_port *port, struct file *file,
|
||||
);
|
||||
|
||||
dbg("%s - port %d Send reset_all_queues (FLUSH) URB returns: %i", __FUNCTION__, port->number, result);
|
||||
|
||||
kfree(transfer_buffer);
|
||||
return ((result < 0) ? -EFAULT : 0);
|
||||
|
||||
return (result < 0) ? -EFAULT : 0;
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
|
||||
static int __init kobil_init (void)
|
||||
{
|
||||
int retval;
|
||||
|
@ -206,20 +206,20 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value
|
||||
}
|
||||
} else {
|
||||
switch (value) {
|
||||
case 300: break;
|
||||
case 600: break;
|
||||
case 1200: break;
|
||||
case 2400: break;
|
||||
case 4800: break;
|
||||
case 9600: break;
|
||||
case 19200: break;
|
||||
case 38400: break;
|
||||
case 57600: break;
|
||||
case 115200: break;
|
||||
default:
|
||||
err("MCT USB-RS232: unsupported baudrate request 0x%x,"
|
||||
" using default of B9600", value);
|
||||
value = 9600;
|
||||
case 300: break;
|
||||
case 600: break;
|
||||
case 1200: break;
|
||||
case 2400: break;
|
||||
case 4800: break;
|
||||
case 9600: break;
|
||||
case 19200: break;
|
||||
case 38400: break;
|
||||
case 57600: break;
|
||||
case 115200: break;
|
||||
default:
|
||||
err("MCT USB-RS232: unsupported baudrate request 0x%x,"
|
||||
" using default of B9600", value);
|
||||
value = 9600;
|
||||
}
|
||||
return 115200/value;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user