mirror of
https://github.com/joel16/android_kernel_sony_msm8994.git
synced 2024-11-28 22:51:06 +00:00
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: (149 commits) USB: ohci-pnx4008: Remove unnecessary cast of return value of kzalloc USB: additions to the quirk list usb-storage: implement autosuspend USB: cdc-acm: add new device id to option driver USB: goku_udc trivial cleanups USB: usb gadget stack can now -DDEBUG with Kconfig usb gadget stack: remove usb_ep_*_buffer(), part 2 usb gadget stack: remove usb_ep_*_buffer(), part 1 USB: pxa2xx_udc -- cleanups, mostly removing dma hooks USB: pxa2xx_udc: use generic gpio layer USB: quirk for samsung printer USB: usb/dma doc updates USB: drivers/usb/storage/unusual_devs.h whitespace cleanup USB: remove Makefile reference to obsolete OHCI_AT91 USB: io_*: remove bogus termios no change checks USB: mos7720: remove bogus no termios change check USB: visor and whiteheat: remove bogus termios change checks USB: pl2303: remove bogus checks and fix speed support to use tty_get_baud_rate() USB: mos7840.c: turn this into a serial driver USB: make the usb_device numa_node get assigned from controller ...
This commit is contained in:
commit
9374430a52
@ -39,3 +39,16 @@ Description:
|
||||
If you want to suspend a device immediately but leave it
|
||||
free to wake up in response to I/O requests, you should
|
||||
write "0" to power/autosuspend.
|
||||
|
||||
What: /sys/bus/usb/devices/.../power/persist
|
||||
Date: May 2007
|
||||
KernelVersion: 2.6.23
|
||||
Contact: Alan Stern <stern@rowland.harvard.edu>
|
||||
Description:
|
||||
If CONFIG_USB_PERSIST is set, then each USB device directory
|
||||
will contain a file named power/persist. The file holds a
|
||||
boolean value (0 or 1) indicating whether or not the
|
||||
"USB-Persist" facility is enabled for the device. Since the
|
||||
facility is inherently dangerous, it is disabled by default
|
||||
for all devices except hubs. For more information, see
|
||||
Documentation/usb/persist.txt.
|
||||
|
@ -393,6 +393,9 @@ safest thing is to unmount all filesystems on removable media (such USB,
|
||||
Firewire, CompactFlash, MMC, external SATA, or even IDE hotplug bays)
|
||||
before suspending; then remount them after resuming.
|
||||
|
||||
There is a work-around for this problem. For more information, see
|
||||
Documentation/usb/persist.txt.
|
||||
|
||||
Q: I upgraded the kernel from 2.6.15 to 2.6.16. Both kernels were
|
||||
compiled with the similar configuration files. Anyway I found that
|
||||
suspend to disk (and resume) is much slower on 2.6.16 compared to
|
||||
|
@ -32,12 +32,15 @@ ELIMINATING COPIES
|
||||
It's good to avoid making CPUs copy data needlessly. The costs can add up,
|
||||
and effects like cache-trashing can impose subtle penalties.
|
||||
|
||||
- When you're allocating a buffer for DMA purposes anyway, use the buffer
|
||||
primitives. Think of them as kmalloc and kfree that give you the right
|
||||
kind of addresses to store in urb->transfer_buffer and urb->transfer_dma,
|
||||
while guaranteeing that no hidden copies through DMA "bounce" buffers will
|
||||
slow things down. You'd also set URB_NO_TRANSFER_DMA_MAP in
|
||||
urb->transfer_flags:
|
||||
- If you're doing lots of small data transfers from the same buffer all
|
||||
the time, that can really burn up resources on systems which use an
|
||||
IOMMU to manage the DMA mappings. It can cost MUCH more to set up and
|
||||
tear down the IOMMU mappings with each request than perform the I/O!
|
||||
|
||||
For those specific cases, USB has primitives to allocate less expensive
|
||||
memory. They work like kmalloc and kfree versions that give you the right
|
||||
kind of addresses to store in urb->transfer_buffer and urb->transfer_dma.
|
||||
You'd also set URB_NO_TRANSFER_DMA_MAP in urb->transfer_flags:
|
||||
|
||||
void *usb_buffer_alloc (struct usb_device *dev, size_t size,
|
||||
int mem_flags, dma_addr_t *dma);
|
||||
@ -45,6 +48,10 @@ and effects like cache-trashing can impose subtle penalties.
|
||||
void usb_buffer_free (struct usb_device *dev, size_t size,
|
||||
void *addr, dma_addr_t dma);
|
||||
|
||||
Most drivers should *NOT* be using these primitives; they don't need
|
||||
to use this type of memory ("dma-coherent"), and memory returned from
|
||||
kmalloc() will work just fine.
|
||||
|
||||
For control transfers you can use the buffer primitives or not for each
|
||||
of the transfer buffer and setup buffer independently. Set the flag bits
|
||||
URB_NO_TRANSFER_DMA_MAP and URB_NO_SETUP_DMA_MAP to indicate which
|
||||
@ -54,29 +61,39 @@ and effects like cache-trashing can impose subtle penalties.
|
||||
The memory buffer returned is "dma-coherent"; sometimes you might need to
|
||||
force a consistent memory access ordering by using memory barriers. It's
|
||||
not using a streaming DMA mapping, so it's good for small transfers on
|
||||
systems where the I/O would otherwise tie up an IOMMU mapping. (See
|
||||
systems where the I/O would otherwise thrash an IOMMU mapping. (See
|
||||
Documentation/DMA-mapping.txt for definitions of "coherent" and "streaming"
|
||||
DMA mappings.)
|
||||
|
||||
Asking for 1/Nth of a page (as well as asking for N pages) is reasonably
|
||||
space-efficient.
|
||||
|
||||
On most systems the memory returned will be uncached, because the
|
||||
semantics of dma-coherent memory require either bypassing CPU caches
|
||||
or using cache hardware with bus-snooping support. While x86 hardware
|
||||
has such bus-snooping, many other systems use software to flush cache
|
||||
lines to prevent DMA conflicts.
|
||||
|
||||
- Devices on some EHCI controllers could handle DMA to/from high memory.
|
||||
Driver probe() routines can notice this using a generic DMA call, then
|
||||
tell higher level code (network, scsi, etc) about it like this:
|
||||
|
||||
if (dma_supported (&intf->dev, 0xffffffffffffffffULL))
|
||||
net->features |= NETIF_F_HIGHDMA;
|
||||
Unfortunately, the current Linux DMA infrastructure doesn't have a sane
|
||||
way to expose these capabilities ... and in any case, HIGHMEM is mostly a
|
||||
design wart specific to x86_32. So your best bet is to ensure you never
|
||||
pass a highmem buffer into a USB driver. That's easy; it's the default
|
||||
behavior. Just don't override it; e.g. with NETIF_F_HIGHDMA.
|
||||
|
||||
That can eliminate dma bounce buffering of requests that originate (or
|
||||
terminate) in high memory, in cases where the buffers aren't allocated
|
||||
with usb_buffer_alloc() but instead are dma-mapped.
|
||||
This may force your callers to do some bounce buffering, copying from
|
||||
high memory to "normal" DMA memory. If you can come up with a good way
|
||||
to fix this issue (for x86_32 machines with over 1 GByte of memory),
|
||||
feel free to submit patches.
|
||||
|
||||
|
||||
WORKING WITH EXISTING BUFFERS
|
||||
|
||||
Existing buffers aren't usable for DMA without first being mapped into the
|
||||
DMA address space of the device.
|
||||
DMA address space of the device. However, most buffers passed to your
|
||||
driver can safely be used with such DMA mapping. (See the first section
|
||||
of DMA-mapping.txt, titled "What memory is DMA-able?")
|
||||
|
||||
- When you're using scatterlists, you can map everything at once. On some
|
||||
systems, this kicks in an IOMMU and turns the scatterlists into single
|
||||
@ -114,3 +131,8 @@ DMA address space of the device.
|
||||
The calls manage urb->transfer_dma for you, and set URB_NO_TRANSFER_DMA_MAP
|
||||
so that usbcore won't map or unmap the buffer. The same goes for
|
||||
urb->setup_dma and URB_NO_SETUP_DMA_MAP for control requests.
|
||||
|
||||
Note that several of those interfaces are currently commented out, since
|
||||
they don't have current users. See the source code. Other than the dmasync
|
||||
calls (where the underlying DMA primitives have changed), most of them can
|
||||
easily be commented back in if you want to use them.
|
||||
|
156
Documentation/usb/persist.txt
Normal file
156
Documentation/usb/persist.txt
Normal file
@ -0,0 +1,156 @@
|
||||
USB device persistence during system suspend
|
||||
|
||||
Alan Stern <stern@rowland.harvard.edu>
|
||||
|
||||
September 2, 2006 (Updated May 29, 2007)
|
||||
|
||||
|
||||
What is the problem?
|
||||
|
||||
According to the USB specification, when a USB bus is suspended the
|
||||
bus must continue to supply suspend current (around 1-5 mA). This
|
||||
is so that devices can maintain their internal state and hubs can
|
||||
detect connect-change events (devices being plugged in or unplugged).
|
||||
The technical term is "power session".
|
||||
|
||||
If a USB device's power session is interrupted then the system is
|
||||
required to behave as though the device has been unplugged. It's a
|
||||
conservative approach; in the absence of suspend current the computer
|
||||
has no way to know what has actually happened. Perhaps the same
|
||||
device is still attached or perhaps it was removed and a different
|
||||
device plugged into the port. The system must assume the worst.
|
||||
|
||||
By default, Linux behaves according to the spec. If a USB host
|
||||
controller loses power during a system suspend, then when the system
|
||||
wakes up all the devices attached to that controller are treated as
|
||||
though they had disconnected. This is always safe and it is the
|
||||
"officially correct" thing to do.
|
||||
|
||||
For many sorts of devices this behavior doesn't matter in the least.
|
||||
If the kernel wants to believe that your USB keyboard was unplugged
|
||||
while the system was asleep and a new keyboard was plugged in when the
|
||||
system woke up, who cares? It'll still work the same when you type on
|
||||
it.
|
||||
|
||||
Unfortunately problems _can_ arise, particularly with mass-storage
|
||||
devices. The effect is exactly the same as if the device really had
|
||||
been unplugged while the system was suspended. If you had a mounted
|
||||
filesystem on the device, you're out of luck -- everything in that
|
||||
filesystem is now inaccessible. This is especially annoying if your
|
||||
root filesystem was located on the device, since your system will
|
||||
instantly crash.
|
||||
|
||||
Loss of power isn't the only mechanism to worry about. Anything that
|
||||
interrupts a power session will have the same effect. For example,
|
||||
even though suspend current may have been maintained while the system
|
||||
was asleep, on many systems during the initial stages of wakeup the
|
||||
firmware (i.e., the BIOS) resets the motherboard's USB host
|
||||
controllers. Result: all the power sessions are destroyed and again
|
||||
it's as though you had unplugged all the USB devices. Yes, it's
|
||||
entirely the BIOS's fault, but that doesn't do _you_ any good unless
|
||||
you can convince the BIOS supplier to fix the problem (lots of luck!).
|
||||
|
||||
On many systems the USB host controllers will get reset after a
|
||||
suspend-to-RAM. On almost all systems, no suspend current is
|
||||
available during hibernation (also known as swsusp or suspend-to-disk).
|
||||
You can check the kernel log after resuming to see if either of these
|
||||
has happened; look for lines saying "root hub lost power or was reset".
|
||||
|
||||
In practice, people are forced to unmount any filesystems on a USB
|
||||
device before suspending. If the root filesystem is on a USB device,
|
||||
the system can't be suspended at all. (All right, it _can_ be
|
||||
suspended -- but it will crash as soon as it wakes up, which isn't
|
||||
much better.)
|
||||
|
||||
|
||||
What is the solution?
|
||||
|
||||
Setting CONFIG_USB_PERSIST will cause the kernel to work around these
|
||||
issues. It enables a mode in which the core USB device data
|
||||
structures are allowed to persist across a power-session disruption.
|
||||
It works like this. If the kernel sees that a USB host controller is
|
||||
not in the expected state during resume (i.e., if the controller was
|
||||
reset or otherwise had lost power) then it applies a persistence check
|
||||
to each of the USB devices below that controller for which the
|
||||
"persist" attribute is set. It doesn't try to resume the device; that
|
||||
can't work once the power session is gone. Instead it issues a USB
|
||||
port reset and then re-enumerates the device. (This is exactly the
|
||||
same thing that happens whenever a USB device is reset.) If the
|
||||
re-enumeration shows that the device now attached to that port has the
|
||||
same descriptors as before, including the Vendor and Product IDs, then
|
||||
the kernel continues to use the same device structure. In effect, the
|
||||
kernel treats the device as though it had merely been reset instead of
|
||||
unplugged.
|
||||
|
||||
If no device is now attached to the port, or if the descriptors are
|
||||
different from what the kernel remembers, then the treatment is what
|
||||
you would expect. The kernel destroys the old device structure and
|
||||
behaves as though the old device had been unplugged and a new device
|
||||
plugged in, just as it would without the CONFIG_USB_PERSIST option.
|
||||
|
||||
The end result is that the USB device remains available and usable.
|
||||
Filesystem mounts and memory mappings are unaffected, and the world is
|
||||
now a good and happy place.
|
||||
|
||||
Note that even when CONFIG_USB_PERSIST is set, the "persist" feature
|
||||
will be applied only to those devices for which it is enabled. You
|
||||
can enable the feature by doing (as root):
|
||||
|
||||
echo 1 >/sys/bus/usb/devices/.../power/persist
|
||||
|
||||
where the "..." should be filled in the with the device's ID. Disable
|
||||
the feature by writing 0 instead of 1. For hubs the feature is
|
||||
automatically and permanently enabled, so you only have to worry about
|
||||
setting it for devices where it really matters.
|
||||
|
||||
|
||||
Is this the best solution?
|
||||
|
||||
Perhaps not. Arguably, keeping track of mounted filesystems and
|
||||
memory mappings across device disconnects should be handled by a
|
||||
centralized Logical Volume Manager. Such a solution would allow you
|
||||
to plug in a USB flash device, create a persistent volume associated
|
||||
with it, unplug the flash device, plug it back in later, and still
|
||||
have the same persistent volume associated with the device. As such
|
||||
it would be more far-reaching than CONFIG_USB_PERSIST.
|
||||
|
||||
On the other hand, writing a persistent volume manager would be a big
|
||||
job and using it would require significant input from the user. This
|
||||
solution is much quicker and easier -- and it exists now, a giant
|
||||
point in its favor!
|
||||
|
||||
Furthermore, the USB_PERSIST option applies to _all_ USB devices, not
|
||||
just mass-storage devices. It might turn out to be equally useful for
|
||||
other device types, such as network interfaces.
|
||||
|
||||
|
||||
WARNING: Using CONFIG_USB_PERSIST can be dangerous!!
|
||||
|
||||
When recovering an interrupted power session the kernel does its best
|
||||
to make sure the USB device hasn't been changed; that is, the same
|
||||
device is still plugged into the port as before. But the checks
|
||||
aren't guaranteed to be 100% accurate.
|
||||
|
||||
If you replace one USB device with another of the same type (same
|
||||
manufacturer, same IDs, and so on) there's an excellent chance the
|
||||
kernel won't detect the change. Serial numbers and other strings are
|
||||
not compared. In many cases it wouldn't help if they were, because
|
||||
manufacturers frequently omit serial numbers entirely in their
|
||||
devices.
|
||||
|
||||
Furthermore it's quite possible to leave a USB device exactly the same
|
||||
while changing its media. If you replace the flash memory card in a
|
||||
USB card reader while the system is asleep, the kernel will have no
|
||||
way to know you did it. The kernel will assume that nothing has
|
||||
happened and will continue to use the partition tables, inodes, and
|
||||
memory mappings for the old card.
|
||||
|
||||
If the kernel gets fooled in this way, it's almost certain to cause
|
||||
data corruption and to crash your system. You'll have no one to blame
|
||||
but yourself.
|
||||
|
||||
YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK!
|
||||
|
||||
That having been said, most of the time there shouldn't be any trouble
|
||||
at all. The "persist" feature can be extremely useful. Make the most
|
||||
of it.
|
@ -3724,12 +3724,12 @@ L: netdev@vger.kernel.org
|
||||
W: http://pegasus2.sourceforge.net/
|
||||
S: Maintained
|
||||
|
||||
USB PRINTER DRIVER
|
||||
P: Vojtech Pavlik
|
||||
M: vojtech@suse.cz
|
||||
USB PRINTER DRIVER (usblp)
|
||||
P: Pete Zaitcev
|
||||
M: zaitcev@redhat.com
|
||||
L: linux-usb-users@lists.sourceforge.net
|
||||
L: linux-usb-devel@lists.sourceforge.net
|
||||
S: Maintained
|
||||
S: Supported
|
||||
|
||||
USB RTL8150 DRIVER
|
||||
P: Petko Manolov
|
||||
|
@ -1547,10 +1547,8 @@ static void ub_reset_enter(struct ub_dev *sc, int try)
|
||||
#endif
|
||||
|
||||
#if 0 /* We let them stop themselves. */
|
||||
struct list_head *p;
|
||||
struct ub_lun *lun;
|
||||
list_for_each(p, &sc->luns) {
|
||||
lun = list_entry(p, struct ub_lun, link);
|
||||
list_for_each_entry(lun, &sc->luns, link) {
|
||||
blk_stop_queue(lun->disk->queue);
|
||||
}
|
||||
#endif
|
||||
@ -1562,7 +1560,6 @@ static void ub_reset_task(struct work_struct *work)
|
||||
{
|
||||
struct ub_dev *sc = container_of(work, struct ub_dev, reset_work);
|
||||
unsigned long flags;
|
||||
struct list_head *p;
|
||||
struct ub_lun *lun;
|
||||
int lkr, rc;
|
||||
|
||||
@ -1608,8 +1605,7 @@ static void ub_reset_task(struct work_struct *work)
|
||||
spin_lock_irqsave(sc->lock, flags);
|
||||
sc->reset = 0;
|
||||
tasklet_schedule(&sc->tasklet);
|
||||
list_for_each(p, &sc->luns) {
|
||||
lun = list_entry(p, struct ub_lun, link);
|
||||
list_for_each_entry(lun, &sc->luns, link) {
|
||||
blk_start_queue(lun->disk->queue);
|
||||
}
|
||||
wake_up(&sc->reset_wait);
|
||||
@ -2348,7 +2344,6 @@ err_alloc:
|
||||
static void ub_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct ub_dev *sc = usb_get_intfdata(intf);
|
||||
struct list_head *p;
|
||||
struct ub_lun *lun;
|
||||
unsigned long flags;
|
||||
|
||||
@ -2403,8 +2398,7 @@ static void ub_disconnect(struct usb_interface *intf)
|
||||
/*
|
||||
* Unregister the upper layer.
|
||||
*/
|
||||
list_for_each (p, &sc->luns) {
|
||||
lun = list_entry(p, struct ub_lun, link);
|
||||
list_for_each_entry(lun, &sc->luns, link) {
|
||||
del_gendisk(lun->disk);
|
||||
/*
|
||||
* I wish I could do:
|
||||
|
@ -1009,20 +1009,22 @@ static int hid_resume(struct usb_interface *intf)
|
||||
}
|
||||
|
||||
/* Treat USB reset pretty much the same as suspend/resume */
|
||||
static void hid_pre_reset(struct usb_interface *intf)
|
||||
static int hid_pre_reset(struct usb_interface *intf)
|
||||
{
|
||||
/* FIXME: What if the interface is already suspended? */
|
||||
hid_suspend(intf, PMSG_ON);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hid_post_reset(struct usb_interface *intf)
|
||||
/* Same routine used for post_reset and reset_resume */
|
||||
static int hid_post_reset(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev (intf);
|
||||
|
||||
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
|
||||
/* FIXME: Any more reinitialization needed? */
|
||||
|
||||
hid_resume(intf);
|
||||
return hid_resume(intf);
|
||||
}
|
||||
|
||||
static struct usb_device_id hid_usb_ids [] = {
|
||||
@ -1039,6 +1041,7 @@ static struct usb_driver hid_driver = {
|
||||
.disconnect = hid_disconnect,
|
||||
.suspend = hid_suspend,
|
||||
.resume = hid_resume,
|
||||
.reset_resume = hid_post_reset,
|
||||
.pre_reset = hid_pre_reset,
|
||||
.post_reset = hid_post_reset,
|
||||
.id_table = hid_usb_ids,
|
||||
|
@ -2,9 +2,12 @@
|
||||
# USB device configuration
|
||||
#
|
||||
|
||||
menu "USB support"
|
||||
menuconfig USB_SUPPORT
|
||||
bool "USB support"
|
||||
depends on HAS_IOMEM
|
||||
|
||||
if USB_SUPPORT
|
||||
|
||||
# Host-side USB depends on having a host controller
|
||||
# NOTE: dummy_hcd is always an option, but it's ignored here ...
|
||||
# NOTE: SL-811 option should be board-specific ...
|
||||
@ -12,6 +15,7 @@ config USB_ARCH_HAS_HCD
|
||||
boolean
|
||||
default y if USB_ARCH_HAS_OHCI
|
||||
default y if USB_ARCH_HAS_EHCI
|
||||
default y if PCMCIA # sl811_cs
|
||||
default y if ARM # SL-811
|
||||
default PCI
|
||||
|
||||
@ -130,5 +134,4 @@ source "drivers/usb/atm/Kconfig"
|
||||
|
||||
source "drivers/usb/gadget/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
endif # USB_SUPPORT
|
||||
|
@ -15,7 +15,7 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/
|
||||
obj-$(CONFIG_USB_UHCI_HCD) += host/
|
||||
obj-$(CONFIG_USB_SL811_HCD) += host/
|
||||
obj-$(CONFIG_USB_U132_HCD) += host/
|
||||
obj-$(CONFIG_USB_OHCI_AT91) += host/
|
||||
obj-$(CONFIG_USB_R8A66597_HCD) += host/
|
||||
|
||||
obj-$(CONFIG_USB_ACM) += class/
|
||||
obj-$(CONFIG_USB_PRINTER) += class/
|
||||
|
@ -171,7 +171,7 @@ struct cxacru_data {
|
||||
struct delayed_work poll_work;
|
||||
u32 card_info[CXINF_MAX];
|
||||
struct mutex poll_state_serialize;
|
||||
int poll_state;
|
||||
enum cxacru_poll_state poll_state;
|
||||
|
||||
/* contol handles */
|
||||
struct mutex cm_serialize;
|
||||
@ -226,58 +226,48 @@ static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf)
|
||||
|
||||
static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf)
|
||||
{
|
||||
if (unlikely(value < 0)) {
|
||||
return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
|
||||
value / 100, -value % 100);
|
||||
} else {
|
||||
return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
|
||||
value / 100, value % 100);
|
||||
}
|
||||
return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
|
||||
value / 100, abs(value) % 100);
|
||||
}
|
||||
|
||||
static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf)
|
||||
{
|
||||
switch (value) {
|
||||
case 0: return snprintf(buf, PAGE_SIZE, "no\n");
|
||||
case 1: return snprintf(buf, PAGE_SIZE, "yes\n");
|
||||
default: return 0;
|
||||
}
|
||||
static char *str[] = { "no", "yes" };
|
||||
if (unlikely(value >= ARRAY_SIZE(str)))
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", value);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
|
||||
}
|
||||
|
||||
static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf)
|
||||
{
|
||||
switch (value) {
|
||||
case 1: return snprintf(buf, PAGE_SIZE, "not connected\n");
|
||||
case 2: return snprintf(buf, PAGE_SIZE, "connected\n");
|
||||
case 3: return snprintf(buf, PAGE_SIZE, "lost\n");
|
||||
default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
|
||||
}
|
||||
static char *str[] = { NULL, "not connected", "connected", "lost" };
|
||||
if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL))
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", value);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
|
||||
}
|
||||
|
||||
static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf)
|
||||
{
|
||||
switch (value) {
|
||||
case 0: return snprintf(buf, PAGE_SIZE, "down\n");
|
||||
case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n");
|
||||
case 2: return snprintf(buf, PAGE_SIZE, "training\n");
|
||||
case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n");
|
||||
case 4: return snprintf(buf, PAGE_SIZE, "exchange\n");
|
||||
case 5: return snprintf(buf, PAGE_SIZE, "up\n");
|
||||
case 6: return snprintf(buf, PAGE_SIZE, "waiting\n");
|
||||
case 7: return snprintf(buf, PAGE_SIZE, "initialising\n");
|
||||
default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
|
||||
}
|
||||
static char *str[] = { "down", "attempting to activate",
|
||||
"training", "channel analysis", "exchange", "up",
|
||||
"waiting", "initialising"
|
||||
};
|
||||
if (unlikely(value >= ARRAY_SIZE(str)))
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", value);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
|
||||
}
|
||||
|
||||
static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
|
||||
{
|
||||
switch (value) {
|
||||
case 0: return 0;
|
||||
case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n");
|
||||
case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n");
|
||||
case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n");
|
||||
default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
|
||||
}
|
||||
static char *str[] = {
|
||||
NULL,
|
||||
"ANSI T1.413",
|
||||
"ITU-T G.992.1 (G.DMT)",
|
||||
"ITU-T G.992.2 (G.LITE)"
|
||||
};
|
||||
if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL))
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", value);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -308,11 +298,10 @@ static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
|
||||
struct cxacru_data *instance = usbatm_instance->driver_data;
|
||||
u32 value = instance->card_info[CXINF_LINE_STARTABLE];
|
||||
|
||||
switch (value) {
|
||||
case 0: return snprintf(buf, PAGE_SIZE, "running\n");
|
||||
case 1: return snprintf(buf, PAGE_SIZE, "stopped\n");
|
||||
default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
|
||||
}
|
||||
static char *str[] = { "running", "stopped" };
|
||||
if (unlikely(value >= ARRAY_SIZE(str)))
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", value);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
|
||||
}
|
||||
|
||||
static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
|
||||
|
@ -1157,6 +1157,9 @@ static struct usb_device_id acm_ids[] = {
|
||||
{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
|
||||
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
|
||||
},
|
||||
{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
|
||||
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
|
||||
},
|
||||
{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
|
||||
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* usblp.c Version 0.13
|
||||
* usblp.c
|
||||
*
|
||||
* Copyright (c) 1999 Michael Gee <michael@linuxspecific.com>
|
||||
* Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
|
||||
@ -61,11 +61,11 @@
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v0.13"
|
||||
#define DRIVER_AUTHOR "Michael Gee, Pavel Machek, Vojtech Pavlik, Randy Dunlap, Pete Zaitcev, David Paschal"
|
||||
#define DRIVER_DESC "USB Printer Device Class driver"
|
||||
|
||||
#define USBLP_BUF_SIZE 8192
|
||||
#define USBLP_BUF_SIZE_IN 1024
|
||||
#define USBLP_DEVICE_ID_SIZE 1024
|
||||
|
||||
/* ioctls: */
|
||||
@ -127,14 +127,22 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H
|
||||
*/
|
||||
#define STATUS_BUF_SIZE 8
|
||||
|
||||
/*
|
||||
* Locks down the locking order:
|
||||
* ->wmut locks wstatus.
|
||||
* ->mut locks the whole usblp, except [rw]complete, and thus, by indirection,
|
||||
* [rw]status. We only touch status when we know the side idle.
|
||||
* ->lock locks what interrupt accesses.
|
||||
*/
|
||||
struct usblp {
|
||||
struct usb_device *dev; /* USB device */
|
||||
struct mutex mut; /* locks this struct, especially "dev" */
|
||||
char *writebuf; /* write transfer_buffer */
|
||||
struct mutex wmut;
|
||||
struct mutex mut;
|
||||
spinlock_t lock; /* locks rcomplete, wcomplete */
|
||||
char *readbuf; /* read transfer_buffer */
|
||||
char *statusbuf; /* status transfer_buffer */
|
||||
struct urb *readurb, *writeurb; /* The urbs */
|
||||
wait_queue_head_t wait; /* Zzzzz ... */
|
||||
struct usb_anchor urbs;
|
||||
wait_queue_head_t rwait, wwait;
|
||||
int readcount; /* Counter for reads */
|
||||
int ifnum; /* Interface number */
|
||||
struct usb_interface *intf; /* The interface */
|
||||
@ -147,8 +155,9 @@ struct usblp {
|
||||
} protocol[USBLP_MAX_PROTOCOLS];
|
||||
int current_protocol;
|
||||
int minor; /* minor number of device */
|
||||
int wcomplete; /* writing is completed */
|
||||
int rcomplete; /* reading is completed */
|
||||
int wcomplete, rcomplete;
|
||||
int wstatus; /* bytes written or error */
|
||||
int rstatus; /* bytes ready or error */
|
||||
unsigned int quirks; /* quirks flags */
|
||||
unsigned char used; /* True if open */
|
||||
unsigned char present; /* True if not disconnected */
|
||||
@ -166,9 +175,6 @@ static void usblp_dump(struct usblp *usblp) {
|
||||
dbg("dev=0x%p", usblp->dev);
|
||||
dbg("present=%d", usblp->present);
|
||||
dbg("readbuf=0x%p", usblp->readbuf);
|
||||
dbg("writebuf=0x%p", usblp->writebuf);
|
||||
dbg("readurb=0x%p", usblp->readurb);
|
||||
dbg("writeurb=0x%p", usblp->writeurb);
|
||||
dbg("readcount=%d", usblp->readcount);
|
||||
dbg("ifnum=%d", usblp->ifnum);
|
||||
for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) {
|
||||
@ -178,8 +184,8 @@ static void usblp_dump(struct usblp *usblp) {
|
||||
}
|
||||
dbg("current_protocol=%d", usblp->current_protocol);
|
||||
dbg("minor=%d", usblp->minor);
|
||||
dbg("wcomplete=%d", usblp->wcomplete);
|
||||
dbg("rcomplete=%d", usblp->rcomplete);
|
||||
dbg("wstatus=%d", usblp->wstatus);
|
||||
dbg("rstatus=%d", usblp->rstatus);
|
||||
dbg("quirks=%d", usblp->quirks);
|
||||
dbg("used=%d", usblp->used);
|
||||
dbg("bidir=%d", usblp->bidir);
|
||||
@ -222,6 +228,11 @@ static const struct quirk_printer_struct quirk_printers[] = {
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static int usblp_wwait(struct usblp *usblp, int nonblock);
|
||||
static int usblp_wtest(struct usblp *usblp, int nonblock);
|
||||
static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock);
|
||||
static int usblp_rtest(struct usblp *usblp, int nonblock);
|
||||
static int usblp_submit_read(struct usblp *usblp);
|
||||
static int usblp_select_alts(struct usblp *usblp);
|
||||
static int usblp_set_protocol(struct usblp *usblp, int protocol);
|
||||
static int usblp_cache_device_id_string(struct usblp *usblp);
|
||||
@ -279,33 +290,47 @@ static void usblp_bulk_read(struct urb *urb)
|
||||
{
|
||||
struct usblp *usblp = urb->context;
|
||||
|
||||
if (unlikely(!usblp || !usblp->dev || !usblp->used))
|
||||
return;
|
||||
|
||||
if (unlikely(!usblp->present))
|
||||
goto unplug;
|
||||
if (unlikely(urb->status))
|
||||
warn("usblp%d: nonzero read/write bulk status received: %d",
|
||||
usblp->minor, urb->status);
|
||||
if (usblp->present && usblp->used) {
|
||||
if (urb->status)
|
||||
printk(KERN_WARNING "usblp%d: "
|
||||
"nonzero read bulk status received: %d\n",
|
||||
usblp->minor, urb->status);
|
||||
}
|
||||
spin_lock(&usblp->lock);
|
||||
if (urb->status < 0)
|
||||
usblp->rstatus = urb->status;
|
||||
else
|
||||
usblp->rstatus = urb->actual_length;
|
||||
usblp->rcomplete = 1;
|
||||
unplug:
|
||||
wake_up_interruptible(&usblp->wait);
|
||||
wake_up(&usblp->rwait);
|
||||
spin_unlock(&usblp->lock);
|
||||
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
|
||||
static void usblp_bulk_write(struct urb *urb)
|
||||
{
|
||||
struct usblp *usblp = urb->context;
|
||||
|
||||
if (unlikely(!usblp || !usblp->dev || !usblp->used))
|
||||
return;
|
||||
if (unlikely(!usblp->present))
|
||||
goto unplug;
|
||||
if (unlikely(urb->status))
|
||||
warn("usblp%d: nonzero read/write bulk status received: %d",
|
||||
usblp->minor, urb->status);
|
||||
if (usblp->present && usblp->used) {
|
||||
if (urb->status)
|
||||
printk(KERN_WARNING "usblp%d: "
|
||||
"nonzero write bulk status received: %d\n",
|
||||
usblp->minor, urb->status);
|
||||
}
|
||||
spin_lock(&usblp->lock);
|
||||
if (urb->status < 0)
|
||||
usblp->wstatus = urb->status;
|
||||
else
|
||||
usblp->wstatus = urb->actual_length;
|
||||
usblp->wcomplete = 1;
|
||||
unplug:
|
||||
wake_up_interruptible(&usblp->wait);
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -322,7 +347,8 @@ static int usblp_check_status(struct usblp *usblp, int err)
|
||||
error = usblp_read_status (usblp, usblp->statusbuf);
|
||||
if (error < 0) {
|
||||
if (printk_ratelimit())
|
||||
err("usblp%d: error %d reading printer status",
|
||||
printk(KERN_ERR
|
||||
"usblp%d: error %d reading printer status\n",
|
||||
usblp->minor, error);
|
||||
return 0;
|
||||
}
|
||||
@ -336,8 +362,10 @@ static int usblp_check_status(struct usblp *usblp, int err)
|
||||
if (~status & LP_PSELECD)
|
||||
newerr = 2;
|
||||
|
||||
if (newerr != err)
|
||||
info("usblp%d: %s", usblp->minor, usblp_messages[newerr]);
|
||||
if (newerr != err) {
|
||||
printk(KERN_INFO "usblp%d: %s\n",
|
||||
usblp->minor, usblp_messages[newerr]);
|
||||
}
|
||||
|
||||
return newerr;
|
||||
}
|
||||
@ -345,12 +373,9 @@ static int usblp_check_status(struct usblp *usblp, int err)
|
||||
static int handle_bidir (struct usblp *usblp)
|
||||
{
|
||||
if (usblp->bidir && usblp->used && !usblp->sleeping) {
|
||||
usblp->readcount = 0;
|
||||
usblp->readurb->dev = usblp->dev;
|
||||
if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0)
|
||||
if (usblp_submit_read(usblp) < 0)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -403,11 +428,9 @@ static int usblp_open(struct inode *inode, struct file *file)
|
||||
usblp->used = 1;
|
||||
file->private_data = usblp;
|
||||
|
||||
usblp->writeurb->transfer_buffer_length = 0;
|
||||
usblp->wcomplete = 1; /* we begin writeable */
|
||||
usblp->wstatus = 0;
|
||||
usblp->rcomplete = 0;
|
||||
usblp->writeurb->status = 0;
|
||||
usblp->readurb->status = 0;
|
||||
|
||||
if (handle_bidir(usblp) < 0) {
|
||||
usblp->used = 0;
|
||||
@ -421,20 +444,17 @@ out:
|
||||
|
||||
static void usblp_cleanup (struct usblp *usblp)
|
||||
{
|
||||
info("usblp%d: removed", usblp->minor);
|
||||
printk(KERN_INFO "usblp%d: removed\n", usblp->minor);
|
||||
|
||||
kfree(usblp->readbuf);
|
||||
kfree (usblp->device_id_string);
|
||||
kfree (usblp->statusbuf);
|
||||
usb_free_urb(usblp->writeurb);
|
||||
usb_free_urb(usblp->readurb);
|
||||
kfree (usblp);
|
||||
}
|
||||
|
||||
static void usblp_unlink_urbs(struct usblp *usblp)
|
||||
{
|
||||
usb_kill_urb(usblp->writeurb);
|
||||
if (usblp->bidir)
|
||||
usb_kill_urb(usblp->readurb);
|
||||
usb_kill_anchored_urbs(&usblp->urbs);
|
||||
}
|
||||
|
||||
static int usblp_release(struct inode *inode, struct file *file)
|
||||
@ -455,10 +475,18 @@ static int usblp_release(struct inode *inode, struct file *file)
|
||||
/* No kernel lock - fine */
|
||||
static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
struct usblp *usblp = file->private_data;
|
||||
poll_wait(file, &usblp->wait, wait);
|
||||
return ((!usblp->bidir || !usblp->rcomplete) ? 0 : POLLIN | POLLRDNORM)
|
||||
/* Should we check file->f_mode & FMODE_WRITE before poll_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);
|
||||
spin_unlock_irqrestore(&usblp->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
@ -632,10 +660,11 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
switch (cmd) {
|
||||
|
||||
case LPGETSTATUS:
|
||||
if (usblp_read_status(usblp, usblp->statusbuf)) {
|
||||
if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
|
||||
if (printk_ratelimit())
|
||||
err("usblp%d: failed reading printer status",
|
||||
usblp->minor);
|
||||
printk(KERN_ERR "usblp%d:"
|
||||
"failed reading printer status (%d)\n",
|
||||
usblp->minor, retval);
|
||||
retval = -EIO;
|
||||
goto done;
|
||||
}
|
||||
@ -656,161 +685,137 @@ done:
|
||||
static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct usblp *usblp = file->private_data;
|
||||
int timeout, intr, rv, err = 0, transfer_length = 0;
|
||||
size_t writecount = 0;
|
||||
char *writebuf;
|
||||
struct urb *writeurb;
|
||||
int rv;
|
||||
int transfer_length;
|
||||
ssize_t writecount = 0;
|
||||
|
||||
if (mutex_lock_interruptible(&usblp->wmut)) {
|
||||
rv = -EINTR;
|
||||
goto raise_biglock;
|
||||
}
|
||||
if ((rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK))) < 0)
|
||||
goto raise_wait;
|
||||
|
||||
while (writecount < count) {
|
||||
if (!usblp->wcomplete) {
|
||||
barrier();
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
writecount += transfer_length;
|
||||
return writecount ? writecount : -EAGAIN;
|
||||
}
|
||||
|
||||
timeout = USBLP_WRITE_TIMEOUT;
|
||||
|
||||
rv = wait_event_interruptible_timeout(usblp->wait, usblp->wcomplete || !usblp->present , timeout);
|
||||
if (rv < 0)
|
||||
return writecount ? writecount : -EINTR;
|
||||
}
|
||||
intr = mutex_lock_interruptible (&usblp->mut);
|
||||
if (intr)
|
||||
return writecount ? writecount : -EINTR;
|
||||
if (!usblp->present) {
|
||||
mutex_unlock (&usblp->mut);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (usblp->sleeping) {
|
||||
mutex_unlock (&usblp->mut);
|
||||
return writecount ? writecount : -ENODEV;
|
||||
}
|
||||
|
||||
if (usblp->writeurb->status != 0) {
|
||||
if (usblp->quirks & USBLP_QUIRK_BIDIR) {
|
||||
if (!usblp->wcomplete)
|
||||
err("usblp%d: error %d writing to printer",
|
||||
usblp->minor, usblp->writeurb->status);
|
||||
err = usblp->writeurb->status;
|
||||
} else
|
||||
err = usblp_check_status(usblp, err);
|
||||
mutex_unlock (&usblp->mut);
|
||||
|
||||
/* if the fault was due to disconnect, let khubd's
|
||||
* call to usblp_disconnect() grab usblp->mut ...
|
||||
*/
|
||||
schedule ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We must increment writecount here, and not at the
|
||||
* end of the loop. Otherwise, the final loop iteration may
|
||||
* be skipped, leading to incomplete printer output.
|
||||
/*
|
||||
* Step 1: Submit next block.
|
||||
*/
|
||||
writecount += transfer_length;
|
||||
if (writecount == count) {
|
||||
mutex_unlock(&usblp->mut);
|
||||
break;
|
||||
}
|
||||
|
||||
transfer_length=(count - writecount);
|
||||
if (transfer_length > USBLP_BUF_SIZE)
|
||||
if ((transfer_length = count - writecount) > USBLP_BUF_SIZE)
|
||||
transfer_length = USBLP_BUF_SIZE;
|
||||
|
||||
usblp->writeurb->transfer_buffer_length = transfer_length;
|
||||
rv = -ENOMEM;
|
||||
if ((writebuf = kmalloc(USBLP_BUF_SIZE, GFP_KERNEL)) == NULL)
|
||||
goto raise_buf;
|
||||
if ((writeurb = usb_alloc_urb(0, GFP_KERNEL)) == 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(usblp->writeurb->transfer_buffer,
|
||||
if (copy_from_user(writebuf,
|
||||
buffer + writecount, transfer_length)) {
|
||||
mutex_unlock(&usblp->mut);
|
||||
return writecount ? writecount : -EFAULT;
|
||||
rv = -EFAULT;
|
||||
goto raise_badaddr;
|
||||
}
|
||||
|
||||
usblp->writeurb->dev = usblp->dev;
|
||||
spin_lock_irq(&usblp->lock);
|
||||
usblp->wcomplete = 0;
|
||||
err = usb_submit_urb(usblp->writeurb, GFP_KERNEL);
|
||||
if (err) {
|
||||
spin_unlock_irq(&usblp->lock);
|
||||
if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) {
|
||||
usblp->wstatus = 0;
|
||||
spin_lock_irq(&usblp->lock);
|
||||
usblp->wcomplete = 1;
|
||||
if (err != -ENOMEM)
|
||||
count = -EIO;
|
||||
else
|
||||
count = writecount ? writecount : -ENOMEM;
|
||||
mutex_unlock (&usblp->mut);
|
||||
break;
|
||||
wake_up(&usblp->wwait);
|
||||
spin_unlock_irq(&usblp->lock);
|
||||
if (rv != -ENOMEM)
|
||||
rv = -EIO;
|
||||
goto raise_submit;
|
||||
}
|
||||
mutex_unlock (&usblp->mut);
|
||||
|
||||
/*
|
||||
* Step 2: Wait for transfer to end, collect results.
|
||||
*/
|
||||
rv = usblp_wwait(usblp, !!(file->f_flags&O_NONBLOCK));
|
||||
if (rv < 0) {
|
||||
/*
|
||||
* If interrupted, we simply leave the URB to dangle,
|
||||
* so the ->release will call usb_kill_urb().
|
||||
*/
|
||||
goto collect_error;
|
||||
}
|
||||
|
||||
if (usblp->wstatus < 0) {
|
||||
usblp_check_status(usblp, 0);
|
||||
rv = -EIO;
|
||||
goto collect_error;
|
||||
}
|
||||
/*
|
||||
* This is critical: it must be our URB, not other writer's.
|
||||
* The wmut exists mainly to cover us here.
|
||||
*/
|
||||
writecount += usblp->wstatus;
|
||||
}
|
||||
|
||||
return count;
|
||||
mutex_unlock(&usblp->wmut);
|
||||
return writecount;
|
||||
|
||||
raise_submit:
|
||||
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);
|
||||
raise_biglock:
|
||||
return writecount ? writecount : rv;
|
||||
}
|
||||
|
||||
static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
||||
/*
|
||||
* Notice that we fail to restart in a few cases: on EFAULT, on restart
|
||||
* error, etc. This is the historical behaviour. In all such cases we return
|
||||
* EIO, and applications loop in order to get the new read going.
|
||||
*/
|
||||
static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, loff_t *ppos)
|
||||
{
|
||||
struct usblp *usblp = file->private_data;
|
||||
int rv, intr;
|
||||
ssize_t count;
|
||||
ssize_t avail;
|
||||
int rv;
|
||||
|
||||
if (!usblp->bidir)
|
||||
return -EINVAL;
|
||||
|
||||
intr = mutex_lock_interruptible (&usblp->mut);
|
||||
if (intr)
|
||||
return -EINTR;
|
||||
if (!usblp->present) {
|
||||
count = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
rv = usblp_rwait_and_lock(usblp, !!(file->f_flags & O_NONBLOCK));
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
if (!usblp->rcomplete) {
|
||||
barrier();
|
||||
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
count = -EAGAIN;
|
||||
goto done;
|
||||
}
|
||||
mutex_unlock(&usblp->mut);
|
||||
rv = wait_event_interruptible(usblp->wait, usblp->rcomplete || !usblp->present);
|
||||
mutex_lock(&usblp->mut);
|
||||
if (rv < 0) {
|
||||
count = -EINTR;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (!usblp->present) {
|
||||
count = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (usblp->sleeping) {
|
||||
count = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (usblp->readurb->status) {
|
||||
err("usblp%d: error %d reading from printer",
|
||||
usblp->minor, usblp->readurb->status);
|
||||
usblp->readurb->dev = usblp->dev;
|
||||
usblp->readcount = 0;
|
||||
usblp->rcomplete = 0;
|
||||
if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0)
|
||||
dbg("error submitting urb");
|
||||
if ((avail = usblp->rstatus) < 0) {
|
||||
printk(KERN_ERR "usblp%d: error %d reading from printer\n",
|
||||
usblp->minor, (int)avail);
|
||||
usblp_submit_read(usblp);
|
||||
count = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
count = count < usblp->readurb->actual_length - usblp->readcount ?
|
||||
count : usblp->readurb->actual_length - usblp->readcount;
|
||||
|
||||
if (copy_to_user(buffer, usblp->readurb->transfer_buffer + usblp->readcount, count)) {
|
||||
count = len < avail - usblp->readcount ? len : avail - usblp->readcount;
|
||||
if (count != 0 &&
|
||||
copy_to_user(buffer, usblp->readbuf + usblp->readcount, count)) {
|
||||
count = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((usblp->readcount += count) == usblp->readurb->actual_length) {
|
||||
usblp->readcount = 0;
|
||||
usblp->readurb->dev = usblp->dev;
|
||||
usblp->rcomplete = 0;
|
||||
if (usb_submit_urb(usblp->readurb, GFP_KERNEL)) {
|
||||
count = -EIO;
|
||||
if ((usblp->readcount += count) == avail) {
|
||||
if (usblp_submit_read(usblp) < 0) {
|
||||
/* We don't want to leak USB return codes into errno. */
|
||||
if (count == 0)
|
||||
count = -EIO;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
@ -820,6 +825,165 @@ done:
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the write path to come idle.
|
||||
* This is called under the ->wmut, so the idle path stays idle.
|
||||
*
|
||||
* Our write path has a peculiar property: it does not buffer like a tty,
|
||||
* but waits for the write to succeed. This allows our ->release to bug out
|
||||
* without waiting for writes to drain. But it obviously does not work
|
||||
* 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;
|
||||
|
||||
add_wait_queue(&usblp->wwait, &waita);
|
||||
for (;;) {
|
||||
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;
|
||||
}
|
||||
mutex_unlock(&usblp->mut);
|
||||
if (rc == 0)
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&usblp->wwait, &waita);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int usblp_wtest(struct usblp *usblp, int nonblock)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!usblp->present)
|
||||
return -ENODEV;
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
spin_lock_irqsave(&usblp->lock, flags);
|
||||
if (usblp->wcomplete) {
|
||||
spin_unlock_irqrestore(&usblp->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&usblp->lock, flags);
|
||||
if (usblp->sleeping)
|
||||
return -ENODEV;
|
||||
if (nonblock)
|
||||
return -EAGAIN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for read bytes to become available. This probably should have been
|
||||
* called usblp_r_lock_and_wait(), because we lock first. But it's a traditional
|
||||
* name for functions which lock and return.
|
||||
*
|
||||
* We do not use wait_event_interruptible because it makes locking iffy.
|
||||
*/
|
||||
static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock)
|
||||
{
|
||||
DECLARE_WAITQUEUE(waita, current);
|
||||
int rc;
|
||||
|
||||
add_wait_queue(&usblp->rwait, &waita);
|
||||
for (;;) {
|
||||
if (mutex_lock_interruptible(&usblp->mut)) {
|
||||
rc = -EINTR;
|
||||
break;
|
||||
}
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if ((rc = usblp_rtest(usblp, nonblock)) < 0) {
|
||||
mutex_unlock(&usblp->mut);
|
||||
break;
|
||||
}
|
||||
if (rc == 0) /* Keep it locked */
|
||||
break;
|
||||
mutex_unlock(&usblp->mut);
|
||||
schedule();
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&usblp->rwait, &waita);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int usblp_rtest(struct usblp *usblp, int nonblock)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!usblp->present)
|
||||
return -ENODEV;
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
spin_lock_irqsave(&usblp->lock, flags);
|
||||
if (usblp->rcomplete) {
|
||||
spin_unlock_irqrestore(&usblp->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&usblp->lock, flags);
|
||||
if (usblp->sleeping)
|
||||
return -ENODEV;
|
||||
if (nonblock)
|
||||
return -EAGAIN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Please check ->bidir and other such things outside for now.
|
||||
*/
|
||||
static int usblp_submit_read(struct usblp *usblp)
|
||||
{
|
||||
struct urb *urb;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
rc = -ENOMEM;
|
||||
if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL)
|
||||
goto raise_urb;
|
||||
|
||||
usb_fill_bulk_urb(urb, usblp->dev,
|
||||
usb_rcvbulkpipe(usblp->dev,
|
||||
usblp->protocol[usblp->current_protocol].epread->bEndpointAddress),
|
||||
usblp->readbuf, USBLP_BUF_SIZE_IN,
|
||||
usblp_bulk_read, usblp);
|
||||
usb_anchor_urb(urb, &usblp->urbs);
|
||||
|
||||
spin_lock_irqsave(&usblp->lock, flags);
|
||||
usblp->readcount = 0; /* XXX Why here? */
|
||||
usblp->rcomplete = 0;
|
||||
spin_unlock_irqrestore(&usblp->lock, flags);
|
||||
if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
|
||||
dbg("error submitting urb (%d)", rc);
|
||||
spin_lock_irqsave(&usblp->lock, flags);
|
||||
usblp->rstatus = rc;
|
||||
usblp->rcomplete = 1;
|
||||
spin_unlock_irqrestore(&usblp->lock, flags);
|
||||
goto raise_submit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
raise_submit:
|
||||
usb_unanchor_urb(urb);
|
||||
usb_free_urb(urb);
|
||||
raise_urb:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks for printers that have quirks, such as requiring unidirectional
|
||||
* communication but reporting bidirectional; currently some HP printers
|
||||
@ -891,55 +1055,41 @@ static int usblp_probe(struct usb_interface *intf,
|
||||
/* Malloc and start initializing usblp structure so we can use it
|
||||
* directly. */
|
||||
if (!(usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL))) {
|
||||
err("out of memory for usblp");
|
||||
retval = -ENOMEM;
|
||||
goto abort;
|
||||
}
|
||||
usblp->dev = dev;
|
||||
mutex_init(&usblp->wmut);
|
||||
mutex_init (&usblp->mut);
|
||||
init_waitqueue_head(&usblp->wait);
|
||||
spin_lock_init(&usblp->lock);
|
||||
init_waitqueue_head(&usblp->rwait);
|
||||
init_waitqueue_head(&usblp->wwait);
|
||||
init_usb_anchor(&usblp->urbs);
|
||||
usblp->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
|
||||
usblp->intf = intf;
|
||||
|
||||
usblp->writeurb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!usblp->writeurb) {
|
||||
err("out of memory");
|
||||
goto abort;
|
||||
}
|
||||
usblp->readurb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!usblp->readurb) {
|
||||
err("out of memory");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Malloc device ID string buffer to the largest expected length,
|
||||
* since we can re-query it on an ioctl and a dynamic string
|
||||
* could change in length. */
|
||||
if (!(usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL))) {
|
||||
err("out of memory for device_id_string");
|
||||
retval = -ENOMEM;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
usblp->writebuf = usblp->readbuf = NULL;
|
||||
usblp->writeurb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
|
||||
usblp->readurb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
|
||||
/* Malloc write & read buffers. We somewhat wastefully
|
||||
/*
|
||||
* Allocate read buffer. We somewhat wastefully
|
||||
* malloc both regardless of bidirectionality, because the
|
||||
* alternate setting can be changed later via an ioctl. */
|
||||
if (!(usblp->writebuf = usb_buffer_alloc(dev, USBLP_BUF_SIZE,
|
||||
GFP_KERNEL, &usblp->writeurb->transfer_dma))) {
|
||||
err("out of memory for write buf");
|
||||
goto abort;
|
||||
}
|
||||
if (!(usblp->readbuf = usb_buffer_alloc(dev, USBLP_BUF_SIZE,
|
||||
GFP_KERNEL, &usblp->readurb->transfer_dma))) {
|
||||
err("out of memory for read buf");
|
||||
* alternate setting can be changed later via an ioctl.
|
||||
*/
|
||||
if (!(usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL))) {
|
||||
retval = -ENOMEM;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Allocate buffer for printer status */
|
||||
usblp->statusbuf = kmalloc(STATUS_BUF_SIZE, GFP_KERNEL);
|
||||
if (!usblp->statusbuf) {
|
||||
err("out of memory for statusbuf");
|
||||
retval = -ENOMEM;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
@ -954,12 +1104,15 @@ static int usblp_probe(struct usb_interface *intf,
|
||||
dbg("incompatible printer-class device 0x%4.4X/0x%4.4X",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
retval = -ENODEV;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Setup the selected alternate setting and endpoints. */
|
||||
if (usblp_set_protocol(usblp, protocol) < 0)
|
||||
if (usblp_set_protocol(usblp, protocol) < 0) {
|
||||
retval = -ENODEV; /* ->probe isn't ->ioctl */
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Retrieve and store the device ID string. */
|
||||
usblp_cache_device_id_string(usblp);
|
||||
@ -977,12 +1130,14 @@ static int usblp_probe(struct usb_interface *intf,
|
||||
|
||||
retval = usb_register_dev(intf, &usblp_class);
|
||||
if (retval) {
|
||||
err("Not able to get a minor for this device.");
|
||||
printk(KERN_ERR "usblp: Not able to get a minor"
|
||||
" (base %u, slice default): %d\n",
|
||||
USBLP_MINOR_BASE, retval);
|
||||
goto abort_intfdata;
|
||||
}
|
||||
usblp->minor = intf->minor;
|
||||
info("usblp%d: USB %sdirectional printer dev %d "
|
||||
"if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X",
|
||||
printk(KERN_INFO "usblp%d: USB %sdirectional printer dev %d "
|
||||
"if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X\n",
|
||||
usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum,
|
||||
usblp->ifnum,
|
||||
usblp->protocol[usblp->current_protocol].alt_setting,
|
||||
@ -997,19 +1152,12 @@ abort_intfdata:
|
||||
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
|
||||
abort:
|
||||
if (usblp) {
|
||||
if (usblp->writebuf)
|
||||
usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
|
||||
usblp->writebuf, usblp->writeurb->transfer_dma);
|
||||
if (usblp->readbuf)
|
||||
usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
|
||||
usblp->readbuf, usblp->readurb->transfer_dma);
|
||||
kfree(usblp->readbuf);
|
||||
kfree(usblp->statusbuf);
|
||||
kfree(usblp->device_id_string);
|
||||
usb_free_urb(usblp->writeurb);
|
||||
usb_free_urb(usblp->readurb);
|
||||
kfree(usblp);
|
||||
}
|
||||
return -EIO;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1078,8 +1226,9 @@ static int usblp_select_alts(struct usblp *usblp)
|
||||
if (ifd->desc.bInterfaceProtocol == 1) {
|
||||
epread = NULL;
|
||||
} else if (usblp->quirks & USBLP_QUIRK_BIDIR) {
|
||||
info("Disabling reads from problem bidirectional "
|
||||
"printer on usblp%d", usblp->minor);
|
||||
printk(KERN_INFO "usblp%d: Disabling reads from "
|
||||
"problematic bidirectional printer\n",
|
||||
usblp->minor);
|
||||
epread = NULL;
|
||||
}
|
||||
|
||||
@ -1119,25 +1268,12 @@ static int usblp_set_protocol(struct usblp *usblp, int protocol)
|
||||
return -EINVAL;
|
||||
r = usb_set_interface(usblp->dev, usblp->ifnum, alts);
|
||||
if (r < 0) {
|
||||
err("can't set desired altsetting %d on interface %d",
|
||||
printk(KERN_ERR "usblp: can't set desired altsetting %d on interface %d\n",
|
||||
alts, usblp->ifnum);
|
||||
return r;
|
||||
}
|
||||
|
||||
usb_fill_bulk_urb(usblp->writeurb, usblp->dev,
|
||||
usb_sndbulkpipe(usblp->dev,
|
||||
usblp->protocol[protocol].epwrite->bEndpointAddress),
|
||||
usblp->writebuf, 0,
|
||||
usblp_bulk_write, usblp);
|
||||
|
||||
usblp->bidir = (usblp->protocol[protocol].epread != NULL);
|
||||
if (usblp->bidir)
|
||||
usb_fill_bulk_urb(usblp->readurb, usblp->dev,
|
||||
usb_rcvbulkpipe(usblp->dev,
|
||||
usblp->protocol[protocol].epread->bEndpointAddress),
|
||||
usblp->readbuf, USBLP_BUF_SIZE,
|
||||
usblp_bulk_read, usblp);
|
||||
|
||||
usblp->current_protocol = protocol;
|
||||
dbg("usblp%d set protocol %d", usblp->minor, protocol);
|
||||
return 0;
|
||||
@ -1190,13 +1326,11 @@ static void usblp_disconnect(struct usb_interface *intf)
|
||||
mutex_lock (&usblp_mutex);
|
||||
mutex_lock (&usblp->mut);
|
||||
usblp->present = 0;
|
||||
wake_up(&usblp->wwait);
|
||||
wake_up(&usblp->rwait);
|
||||
usb_set_intfdata (intf, NULL);
|
||||
|
||||
usblp_unlink_urbs(usblp);
|
||||
usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
|
||||
usblp->writebuf, usblp->writeurb->transfer_dma);
|
||||
usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
|
||||
usblp->readbuf, usblp->readurb->transfer_dma);
|
||||
mutex_unlock (&usblp->mut);
|
||||
|
||||
if (!usblp->used)
|
||||
@ -1211,6 +1345,11 @@ static int usblp_suspend (struct usb_interface *intf, pm_message_t message)
|
||||
/* we take no more IO */
|
||||
usblp->sleeping = 1;
|
||||
usblp_unlink_urbs(usblp);
|
||||
#if 0 /* XXX Do we want this? What if someone is reading, should we fail? */
|
||||
/* not strictly necessary, but just in case */
|
||||
wake_up(&usblp->wwait);
|
||||
wake_up(&usblp->rwait);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1251,12 +1390,7 @@ static struct usb_driver usblp_driver = {
|
||||
|
||||
static int __init usblp_init(void)
|
||||
{
|
||||
int retval;
|
||||
retval = usb_register(&usblp_driver);
|
||||
if (!retval)
|
||||
info(DRIVER_VERSION ": " DRIVER_DESC);
|
||||
|
||||
return retval;
|
||||
return usb_register(&usblp_driver);
|
||||
}
|
||||
|
||||
static void __exit usblp_exit(void)
|
||||
|
@ -86,6 +86,31 @@ config USB_SUSPEND
|
||||
|
||||
If you are unsure about this, say N here.
|
||||
|
||||
config USB_PERSIST
|
||||
bool "USB device persistence during system suspend (DANGEROUS)"
|
||||
depends on USB && PM && EXPERIMENTAL
|
||||
default n
|
||||
help
|
||||
|
||||
If you say Y here and enable the "power/persist" attribute
|
||||
for a USB device, the device's data structures will remain
|
||||
persistent across system suspend, even if the USB bus loses
|
||||
power. (This includes hibernation, also known as swsusp or
|
||||
suspend-to-disk.) The devices will reappear as if by magic
|
||||
when the system wakes up, with no need to unmount USB
|
||||
filesystems, rmmod host-controller drivers, or do anything
|
||||
else.
|
||||
|
||||
WARNING: This option can be dangerous!
|
||||
|
||||
If a USB device is replaced by another of the same type while
|
||||
the system is asleep, there's a good chance the kernel won't
|
||||
detect the change. Likewise if the media in a USB storage
|
||||
device is replaced. When this happens it's almost certain to
|
||||
cause data corruption and maybe even crash your system.
|
||||
|
||||
If you are unsure, say N here.
|
||||
|
||||
config USB_OTG
|
||||
bool
|
||||
depends on USB && EXPERIMENTAL
|
||||
|
@ -85,15 +85,21 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
||||
memcpy(&endpoint->desc, d, n);
|
||||
INIT_LIST_HEAD(&endpoint->urb_list);
|
||||
|
||||
/* If the bInterval value is outside the legal range,
|
||||
* set it to a default value: 32 ms */
|
||||
/* Fix up bInterval values outside the legal range. Use 32 ms if no
|
||||
* proper value can be guessed. */
|
||||
i = 0; /* i = min, j = max, n = default */
|
||||
j = 255;
|
||||
if (usb_endpoint_xfer_int(d)) {
|
||||
i = 1;
|
||||
switch (to_usb_device(ddev)->speed) {
|
||||
case USB_SPEED_HIGH:
|
||||
n = 9; /* 32 ms = 2^(9-1) uframes */
|
||||
/* Many device manufacturers are using full-speed
|
||||
* bInterval values in high-speed interrupt endpoint
|
||||
* descriptors. Try to fix those and fall back to a
|
||||
* 32 ms default value otherwise. */
|
||||
n = fls(d->bInterval*8);
|
||||
if (n == 0)
|
||||
n = 9; /* 32 ms = 2^(9-1) uframes */
|
||||
j = 16;
|
||||
break;
|
||||
default: /* USB_SPEED_FULL or _LOW */
|
||||
@ -124,6 +130,21 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
||||
endpoint->desc.bInterval = n;
|
||||
}
|
||||
|
||||
/* Some buggy low-speed devices have Bulk endpoints, which is
|
||||
* explicitly forbidden by the USB spec. In an attempt to make
|
||||
* them usable, we will try treating them as Interrupt endpoints.
|
||||
*/
|
||||
if (to_usb_device(ddev)->speed == USB_SPEED_LOW &&
|
||||
usb_endpoint_xfer_bulk(d)) {
|
||||
dev_warn(ddev, "config %d interface %d altsetting %d "
|
||||
"endpoint 0x%X is Bulk; changing to Interrupt\n",
|
||||
cfgno, inum, asnum, d->bEndpointAddress);
|
||||
endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT;
|
||||
endpoint->desc.bInterval = 1;
|
||||
if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8)
|
||||
endpoint->desc.wMaxPacketSize = cpu_to_le16(8);
|
||||
}
|
||||
|
||||
/* Skip over any Class Specific or Vendor Specific descriptors;
|
||||
* find the next endpoint or interface descriptor */
|
||||
endpoint->extra = buffer;
|
||||
@ -274,6 +295,7 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
|
||||
struct usb_descriptor_header *header;
|
||||
int len, retval;
|
||||
u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
|
||||
unsigned iad_num = 0;
|
||||
|
||||
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
|
||||
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
|
||||
@ -351,6 +373,20 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
|
||||
++n;
|
||||
}
|
||||
|
||||
} else if (header->bDescriptorType ==
|
||||
USB_DT_INTERFACE_ASSOCIATION) {
|
||||
if (iad_num == USB_MAXIADS) {
|
||||
dev_warn(ddev, "found more Interface "
|
||||
"Association Descriptors "
|
||||
"than allocated for in "
|
||||
"configuration %d\n", cfgno);
|
||||
} else {
|
||||
config->intf_assoc[iad_num] =
|
||||
(struct usb_interface_assoc_descriptor
|
||||
*)header;
|
||||
iad_num++;
|
||||
}
|
||||
|
||||
} else if (header->bDescriptorType == USB_DT_DEVICE ||
|
||||
header->bDescriptorType == USB_DT_CONFIG)
|
||||
dev_warn(ddev, "config %d contains an unexpected "
|
||||
|
@ -102,6 +102,10 @@ static const char *format_config =
|
||||
/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
|
||||
"C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
|
||||
|
||||
static const char *format_iad =
|
||||
/* A: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */
|
||||
"A: FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n";
|
||||
|
||||
static const char *format_iface =
|
||||
/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
|
||||
"I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
|
||||
@ -146,6 +150,7 @@ static const struct class_info clas_info[] =
|
||||
{USB_CLASS_STILL_IMAGE, "still"},
|
||||
{USB_CLASS_CSCID, "scard"},
|
||||
{USB_CLASS_CONTENT_SEC, "c-sec"},
|
||||
{USB_CLASS_VIDEO, "video"},
|
||||
{-1, "unk."} /* leave as last */
|
||||
};
|
||||
|
||||
@ -286,6 +291,21 @@ static char *usb_dump_interface(
|
||||
return start;
|
||||
}
|
||||
|
||||
static char *usb_dump_iad_descriptor(char *start, char *end,
|
||||
const struct usb_interface_assoc_descriptor *iad)
|
||||
{
|
||||
if (start > end)
|
||||
return start;
|
||||
start += sprintf(start, format_iad,
|
||||
iad->bFirstInterface,
|
||||
iad->bInterfaceCount,
|
||||
iad->bFunctionClass,
|
||||
class_decode(iad->bFunctionClass),
|
||||
iad->bFunctionSubClass,
|
||||
iad->bFunctionProtocol);
|
||||
return start;
|
||||
}
|
||||
|
||||
/* TBD:
|
||||
* 0. TBDs
|
||||
* 1. marking active interface altsettings (code lists all, but should mark
|
||||
@ -322,6 +342,12 @@ static char *usb_dump_config (
|
||||
if (!config) /* getting these some in 2.3.7; none in 2.3.6 */
|
||||
return start + sprintf(start, "(null Cfg. desc.)\n");
|
||||
start = usb_dump_config_descriptor(start, end, &config->desc, active);
|
||||
for (i = 0; i < USB_MAXIADS; i++) {
|
||||
if (config->intf_assoc[i] == NULL)
|
||||
break;
|
||||
start = usb_dump_iad_descriptor(start, end,
|
||||
config->intf_assoc[i]);
|
||||
}
|
||||
for (i = 0; i < config->desc.bNumInterfaces; i++) {
|
||||
intfc = config->intf_cache[i];
|
||||
interface = config->interface[i];
|
||||
|
@ -24,10 +24,19 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "hcd.h"
|
||||
#include "usb.h"
|
||||
|
||||
#define VERBOSE_DEBUG 0
|
||||
|
||||
#if VERBOSE_DEBUG
|
||||
#define dev_vdbg dev_dbg
|
||||
#else
|
||||
#define dev_vdbg(dev, fmt, args...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HOTPLUG
|
||||
|
||||
/*
|
||||
@ -802,18 +811,17 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
|
||||
udev->state == USB_STATE_SUSPENDED)
|
||||
goto done;
|
||||
|
||||
/* For devices that don't have a driver, we do a standard suspend. */
|
||||
if (udev->dev.driver == NULL) {
|
||||
/* For devices that don't have a driver, we do a generic suspend. */
|
||||
if (udev->dev.driver)
|
||||
udriver = to_usb_device_driver(udev->dev.driver);
|
||||
else {
|
||||
udev->do_remote_wakeup = 0;
|
||||
status = usb_port_suspend(udev);
|
||||
goto done;
|
||||
udriver = &usb_generic_driver;
|
||||
}
|
||||
|
||||
udriver = to_usb_device_driver(udev->dev.driver);
|
||||
status = udriver->suspend(udev, msg);
|
||||
|
||||
done:
|
||||
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
done:
|
||||
dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
if (status == 0)
|
||||
udev->dev.power.power_state.event = msg.event;
|
||||
return status;
|
||||
@ -825,8 +833,9 @@ static int usb_resume_device(struct usb_device *udev)
|
||||
struct usb_device_driver *udriver;
|
||||
int status = 0;
|
||||
|
||||
if (udev->state == USB_STATE_NOTATTACHED ||
|
||||
udev->state != USB_STATE_SUSPENDED)
|
||||
if (udev->state == USB_STATE_NOTATTACHED)
|
||||
goto done;
|
||||
if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume)
|
||||
goto done;
|
||||
|
||||
/* Can't resume it if it doesn't have a driver. */
|
||||
@ -835,11 +844,14 @@ static int usb_resume_device(struct usb_device *udev)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (udev->quirks & USB_QUIRK_RESET_RESUME)
|
||||
udev->reset_resume = 1;
|
||||
|
||||
udriver = to_usb_device_driver(udev->dev.driver);
|
||||
status = udriver->resume(udev);
|
||||
|
||||
done:
|
||||
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
done:
|
||||
dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
if (status == 0) {
|
||||
udev->autoresume_disabled = 0;
|
||||
udev->dev.power.power_state.event = PM_EVENT_ON;
|
||||
@ -877,15 +889,13 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
|
||||
mark_quiesced(intf);
|
||||
}
|
||||
|
||||
done:
|
||||
// dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
if (status == 0)
|
||||
intf->dev.power.power_state.event = msg.event;
|
||||
done:
|
||||
dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Caller has locked intf's usb_device's pm_mutex */
|
||||
static int usb_resume_interface(struct usb_interface *intf)
|
||||
static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
|
||||
{
|
||||
struct usb_driver *driver;
|
||||
int status = 0;
|
||||
@ -905,23 +915,37 @@ static int usb_resume_interface(struct usb_interface *intf)
|
||||
}
|
||||
driver = to_usb_driver(intf->dev.driver);
|
||||
|
||||
if (driver->resume) {
|
||||
status = driver->resume(intf);
|
||||
if (status)
|
||||
dev_err(&intf->dev, "%s error %d\n",
|
||||
"resume", status);
|
||||
else
|
||||
mark_active(intf);
|
||||
if (reset_resume) {
|
||||
if (driver->reset_resume) {
|
||||
status = driver->reset_resume(intf);
|
||||
if (status)
|
||||
dev_err(&intf->dev, "%s error %d\n",
|
||||
"reset_resume", status);
|
||||
} else {
|
||||
// status = -EOPNOTSUPP;
|
||||
dev_warn(&intf->dev, "no %s for driver %s?\n",
|
||||
"reset_resume", driver->name);
|
||||
}
|
||||
} else {
|
||||
dev_warn(&intf->dev, "no resume for driver %s?\n",
|
||||
driver->name);
|
||||
mark_active(intf);
|
||||
if (driver->resume) {
|
||||
status = driver->resume(intf);
|
||||
if (status)
|
||||
dev_err(&intf->dev, "%s error %d\n",
|
||||
"resume", status);
|
||||
} else {
|
||||
// status = -EOPNOTSUPP;
|
||||
dev_warn(&intf->dev, "no %s for driver %s?\n",
|
||||
"resume", driver->name);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
// dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
if (status == 0)
|
||||
intf->dev.power.power_state.event = PM_EVENT_ON;
|
||||
mark_active(intf);
|
||||
|
||||
/* FIXME: Unbind the driver and reprobe if the resume failed
|
||||
* (not possible if auto_pm is set) */
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -958,6 +982,18 @@ static int autosuspend_check(struct usb_device *udev)
|
||||
"for autosuspend\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Don't allow autosuspend if the device will need
|
||||
* a reset-resume and any of its interface drivers
|
||||
* doesn't include support.
|
||||
*/
|
||||
if (udev->quirks & USB_QUIRK_RESET_RESUME) {
|
||||
struct usb_driver *driver;
|
||||
|
||||
driver = to_usb_driver(intf->dev.driver);
|
||||
if (!driver->reset_resume)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -974,7 +1010,7 @@ static int autosuspend_check(struct usb_device *udev)
|
||||
* or for the past.
|
||||
*/
|
||||
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
|
||||
suspend_time - jiffies);
|
||||
round_jiffies_relative(suspend_time - jiffies));
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
@ -1054,14 +1090,21 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (status == 0)
|
||||
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;
|
||||
status = usb_suspend_device(udev, msg);
|
||||
}
|
||||
|
||||
/* If the suspend failed, resume interfaces that did get suspended */
|
||||
if (status != 0) {
|
||||
while (--i >= 0) {
|
||||
intf = udev->actconfig->interface[i];
|
||||
usb_resume_interface(intf);
|
||||
usb_resume_interface(intf, 0);
|
||||
}
|
||||
|
||||
/* Try another autosuspend when the interfaces aren't busy */
|
||||
@ -1076,7 +1119,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
||||
}
|
||||
|
||||
done:
|
||||
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1131,7 +1174,8 @@ static int usb_resume_both(struct usb_device *udev)
|
||||
status = usb_autoresume_device(parent);
|
||||
if (status == 0) {
|
||||
status = usb_resume_device(udev);
|
||||
if (status) {
|
||||
if (status || udev->state ==
|
||||
USB_STATE_NOTATTACHED) {
|
||||
usb_autosuspend_device(parent);
|
||||
|
||||
/* It's possible usb_resume_device()
|
||||
@ -1152,28 +1196,25 @@ static int usb_resume_both(struct usb_device *udev)
|
||||
/* We can't progagate beyond the USB subsystem,
|
||||
* so if a root hub's controller is suspended
|
||||
* then we're stuck. */
|
||||
if (udev->dev.parent->power.power_state.event !=
|
||||
PM_EVENT_ON)
|
||||
status = -EHOSTUNREACH;
|
||||
else
|
||||
status = usb_resume_device(udev);
|
||||
status = usb_resume_device(udev);
|
||||
}
|
||||
} else {
|
||||
|
||||
/* Needed only for setting udev->dev.power.power_state.event
|
||||
* and for possible debugging message. */
|
||||
/* Needed for setting udev->dev.power.power_state.event,
|
||||
* for possible debugging message, and for reset_resume. */
|
||||
status = usb_resume_device(udev);
|
||||
}
|
||||
|
||||
if (status == 0 && udev->actconfig) {
|
||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
||||
intf = udev->actconfig->interface[i];
|
||||
usb_resume_interface(intf);
|
||||
usb_resume_interface(intf, udev->reset_resume);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||
udev->reset_resume = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1240,8 +1281,8 @@ void usb_autosuspend_device(struct usb_device *udev)
|
||||
int status;
|
||||
|
||||
status = usb_autopm_do_device(udev, -1);
|
||||
// dev_dbg(&udev->dev, "%s: cnt %d\n",
|
||||
// __FUNCTION__, udev->pm_usage_cnt);
|
||||
dev_vdbg(&udev->dev, "%s: cnt %d\n",
|
||||
__FUNCTION__, udev->pm_usage_cnt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1260,8 +1301,8 @@ void usb_autosuspend_device(struct usb_device *udev)
|
||||
void usb_try_autosuspend_device(struct usb_device *udev)
|
||||
{
|
||||
usb_autopm_do_device(udev, 0);
|
||||
// dev_dbg(&udev->dev, "%s: cnt %d\n",
|
||||
// __FUNCTION__, udev->pm_usage_cnt);
|
||||
dev_vdbg(&udev->dev, "%s: cnt %d\n",
|
||||
__FUNCTION__, udev->pm_usage_cnt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1288,8 +1329,8 @@ int usb_autoresume_device(struct usb_device *udev)
|
||||
int status;
|
||||
|
||||
status = usb_autopm_do_device(udev, 1);
|
||||
// dev_dbg(&udev->dev, "%s: status %d cnt %d\n",
|
||||
// __FUNCTION__, status, udev->pm_usage_cnt);
|
||||
dev_vdbg(&udev->dev, "%s: status %d cnt %d\n",
|
||||
__FUNCTION__, status, udev->pm_usage_cnt);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1361,8 +1402,8 @@ void usb_autopm_put_interface(struct usb_interface *intf)
|
||||
int status;
|
||||
|
||||
status = usb_autopm_do_interface(intf, -1);
|
||||
// dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
|
||||
// __FUNCTION__, status, intf->pm_usage_cnt);
|
||||
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
|
||||
__FUNCTION__, status, intf->pm_usage_cnt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
|
||||
|
||||
@ -1405,8 +1446,8 @@ int usb_autopm_get_interface(struct usb_interface *intf)
|
||||
int status;
|
||||
|
||||
status = usb_autopm_do_interface(intf, 1);
|
||||
// dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
|
||||
// __FUNCTION__, status, intf->pm_usage_cnt);
|
||||
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
|
||||
__FUNCTION__, status, intf->pm_usage_cnt);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
|
||||
@ -1427,8 +1468,8 @@ int usb_autopm_set_interface(struct usb_interface *intf)
|
||||
int status;
|
||||
|
||||
status = usb_autopm_do_interface(intf, 0);
|
||||
// dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
|
||||
// __FUNCTION__, status, intf->pm_usage_cnt);
|
||||
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
|
||||
__FUNCTION__, status, intf->pm_usage_cnt);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
|
||||
@ -1508,8 +1549,15 @@ static int usb_resume(struct device *dev)
|
||||
if (!is_usb_device(dev)) /* Ignore PM for interfaces */
|
||||
return 0;
|
||||
udev = to_usb_device(dev);
|
||||
if (udev->autoresume_disabled)
|
||||
return -EPERM;
|
||||
|
||||
/* 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->reset_resume && udev->do_remote_wakeup))
|
||||
return -EPERM;
|
||||
}
|
||||
return usb_external_resume_device(udev);
|
||||
}
|
||||
|
||||
|
@ -16,15 +16,15 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
#define MAX_USB_MINORS 256
|
||||
static const struct file_operations *usb_minors[MAX_USB_MINORS];
|
||||
static DEFINE_SPINLOCK(minor_lock);
|
||||
static DECLARE_RWSEM(minor_rwsem);
|
||||
|
||||
static int usb_open(struct inode * inode, struct file * file)
|
||||
{
|
||||
@ -33,14 +33,11 @@ static int usb_open(struct inode * inode, struct file * file)
|
||||
int err = -ENODEV;
|
||||
const struct file_operations *old_fops, *new_fops = NULL;
|
||||
|
||||
spin_lock (&minor_lock);
|
||||
down_read(&minor_rwsem);
|
||||
c = usb_minors[minor];
|
||||
|
||||
if (!c || !(new_fops = fops_get(c))) {
|
||||
spin_unlock(&minor_lock);
|
||||
return err;
|
||||
}
|
||||
spin_unlock(&minor_lock);
|
||||
if (!c || !(new_fops = fops_get(c)))
|
||||
goto done;
|
||||
|
||||
old_fops = file->f_op;
|
||||
file->f_op = new_fops;
|
||||
@ -52,6 +49,8 @@ static int usb_open(struct inode * inode, struct file * file)
|
||||
file->f_op = fops_get(old_fops);
|
||||
}
|
||||
fops_put(old_fops);
|
||||
done:
|
||||
up_read(&minor_rwsem);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -166,7 +165,7 @@ int usb_register_dev(struct usb_interface *intf,
|
||||
if (class_driver->fops == NULL)
|
||||
goto exit;
|
||||
|
||||
spin_lock (&minor_lock);
|
||||
down_write(&minor_rwsem);
|
||||
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
|
||||
if (usb_minors[minor])
|
||||
continue;
|
||||
@ -176,7 +175,7 @@ int usb_register_dev(struct usb_interface *intf,
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
spin_unlock (&minor_lock);
|
||||
up_write(&minor_rwsem);
|
||||
|
||||
if (retval)
|
||||
goto exit;
|
||||
@ -197,9 +196,9 @@ int usb_register_dev(struct usb_interface *intf,
|
||||
intf->usb_dev = device_create(usb_class->class, &intf->dev,
|
||||
MKDEV(USB_MAJOR, minor), "%s", temp);
|
||||
if (IS_ERR(intf->usb_dev)) {
|
||||
spin_lock (&minor_lock);
|
||||
down_write(&minor_rwsem);
|
||||
usb_minors[intf->minor] = NULL;
|
||||
spin_unlock (&minor_lock);
|
||||
up_write(&minor_rwsem);
|
||||
retval = PTR_ERR(intf->usb_dev);
|
||||
}
|
||||
exit:
|
||||
@ -236,9 +235,9 @@ void usb_deregister_dev(struct usb_interface *intf,
|
||||
|
||||
dbg ("removing %d minor", intf->minor);
|
||||
|
||||
spin_lock (&minor_lock);
|
||||
down_write(&minor_rwsem);
|
||||
usb_minors[intf->minor] = NULL;
|
||||
spin_unlock (&minor_lock);
|
||||
up_write(&minor_rwsem);
|
||||
|
||||
snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
|
||||
device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
|
||||
@ -247,5 +246,3 @@ void usb_deregister_dev(struct usb_interface *intf,
|
||||
destroy_usb_class();
|
||||
}
|
||||
EXPORT_SYMBOL(usb_deregister_dev);
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
static inline const char *plural(int n)
|
||||
{
|
||||
@ -193,16 +194,34 @@ static void generic_disconnect(struct usb_device *udev)
|
||||
|
||||
static int generic_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
/* USB devices enter SUSPEND state through their hubs, but can be
|
||||
* marked for FREEZE as soon as their children are already idled.
|
||||
* But those semantics are useless, so we equate the two (sigh).
|
||||
int rc;
|
||||
|
||||
/* Normal USB devices suspend through their upstream port.
|
||||
* Root hubs don't have upstream ports to suspend,
|
||||
* so we have to shut down their downstream HC-to-USB
|
||||
* interfaces manually by doing a bus (or "global") suspend.
|
||||
*/
|
||||
return usb_port_suspend(udev);
|
||||
if (!udev->parent)
|
||||
rc = hcd_bus_suspend(udev);
|
||||
else
|
||||
rc = usb_port_suspend(udev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int generic_resume(struct usb_device *udev)
|
||||
{
|
||||
return usb_port_resume(udev);
|
||||
int rc;
|
||||
|
||||
/* Normal USB devices resume/reset through their upstream port.
|
||||
* Root hubs don't have upstream ports to resume or reset,
|
||||
* so we have to start up their downstream HC-to-USB
|
||||
* interfaces manually by doing a bus (or "global") resume.
|
||||
*/
|
||||
if (!udev->parent)
|
||||
rc = hcd_bus_resume(udev);
|
||||
else
|
||||
rc = usb_port_resume(udev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
@ -207,7 +207,8 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
|
||||
* We must ignore the FREEZE vs SUSPEND distinction here, because
|
||||
* otherwise the swsusp will save (and restore) garbage state.
|
||||
*/
|
||||
if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON)
|
||||
if (!(hcd->state == HC_STATE_SUSPENDED ||
|
||||
hcd->state == HC_STATE_HALT))
|
||||
return -EBUSY;
|
||||
|
||||
if (hcd->driver->suspend) {
|
||||
|
@ -582,10 +582,12 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
|
||||
}
|
||||
|
||||
/* The USB 2.0 spec says 256 ms. This is close enough and won't
|
||||
* exceed that limit if HZ is 100. */
|
||||
* exceed that limit if HZ is 100. The math is more clunky than
|
||||
* maybe expected, this is to make sure that all timers for USB devices
|
||||
* fire at the same time to give the CPU a break inbetween */
|
||||
if (hcd->uses_new_polling ? hcd->poll_rh :
|
||||
(length == 0 && hcd->status_urb != NULL))
|
||||
mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250));
|
||||
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
|
||||
|
||||
@ -614,8 +616,8 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
|
||||
urb->hcpriv = hcd; /* indicate it's queued */
|
||||
|
||||
if (!hcd->uses_new_polling)
|
||||
mod_timer (&hcd->rh_timer, jiffies +
|
||||
msecs_to_jiffies(250));
|
||||
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)
|
||||
@ -901,17 +903,32 @@ EXPORT_SYMBOL (usb_calc_bus_time);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void urb_unlink (struct urb *urb)
|
||||
static void urb_unlink(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
unsigned long flags;
|
||||
int at_root_hub = (urb->dev == hcd->self.root_hub);
|
||||
|
||||
/* clear all state linking urb to this dev (and hcd) */
|
||||
|
||||
spin_lock_irqsave (&hcd_data_lock, flags);
|
||||
list_del_init (&urb->urb_list);
|
||||
spin_unlock_irqrestore (&hcd_data_lock, flags);
|
||||
}
|
||||
|
||||
if (hcd->self.uses_dma && !at_root_hub) {
|
||||
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
|
||||
@ -948,19 +965,9 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
|
||||
else switch (hcd->state) {
|
||||
case HC_STATE_RUNNING:
|
||||
case HC_STATE_RESUMING:
|
||||
doit:
|
||||
list_add_tail (&urb->urb_list, &ep->urb_list);
|
||||
status = 0;
|
||||
break;
|
||||
case HC_STATE_SUSPENDED:
|
||||
/* HC upstream links (register access, wakeup signaling) can work
|
||||
* even when the downstream links (and DMA etc) are quiesced; let
|
||||
* usbcore talk to the root hub.
|
||||
*/
|
||||
if (hcd->self.controller->power.power_state.event == PM_EVENT_ON
|
||||
&& urb->dev->parent == NULL)
|
||||
goto doit;
|
||||
/* FALL THROUGH */
|
||||
default:
|
||||
status = -ESHUTDOWN;
|
||||
break;
|
||||
@ -1014,7 +1021,7 @@ doit:
|
||||
status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);
|
||||
done:
|
||||
if (unlikely (status)) {
|
||||
urb_unlink (urb);
|
||||
urb_unlink(hcd, urb);
|
||||
atomic_dec (&urb->use_count);
|
||||
if (urb->reject)
|
||||
wake_up (&usb_kill_urb_queue);
|
||||
@ -1255,42 +1262,59 @@ rescan:
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
int hcd_bus_suspend (struct usb_bus *bus)
|
||||
int hcd_bus_suspend(struct usb_device *rhdev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int status;
|
||||
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
|
||||
int status;
|
||||
int old_state = hcd->state;
|
||||
|
||||
hcd = container_of (bus, struct usb_hcd, self);
|
||||
if (!hcd->driver->bus_suspend)
|
||||
return -ENOENT;
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
status = hcd->driver->bus_suspend (hcd);
|
||||
if (status == 0)
|
||||
dev_dbg(&rhdev->dev, "bus %s%s\n",
|
||||
rhdev->auto_pm ? "auto-" : "", "suspend");
|
||||
if (!hcd->driver->bus_suspend) {
|
||||
status = -ENOENT;
|
||||
} else {
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
status = hcd->driver->bus_suspend(hcd);
|
||||
}
|
||||
if (status == 0) {
|
||||
usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
else
|
||||
dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
|
||||
} else {
|
||||
hcd->state = old_state;
|
||||
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
|
||||
"suspend", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int hcd_bus_resume (struct usb_bus *bus)
|
||||
int hcd_bus_resume(struct usb_device *rhdev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int status;
|
||||
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
|
||||
int status;
|
||||
int old_state = hcd->state;
|
||||
|
||||
hcd = container_of (bus, struct usb_hcd, self);
|
||||
dev_dbg(&rhdev->dev, "usb %s%s\n",
|
||||
rhdev->auto_pm ? "auto-" : "", "resume");
|
||||
if (!hcd->driver->bus_resume)
|
||||
return -ENOENT;
|
||||
if (hcd->state == HC_STATE_RUNNING)
|
||||
return 0;
|
||||
|
||||
hcd->state = HC_STATE_RESUMING;
|
||||
status = hcd->driver->bus_resume (hcd);
|
||||
if (status == 0)
|
||||
status = hcd->driver->bus_resume(hcd);
|
||||
if (status == 0) {
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
usb_set_device_state(rhdev, rhdev->actconfig
|
||||
? USB_STATE_CONFIGURED
|
||||
: USB_STATE_ADDRESS);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
else {
|
||||
dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
|
||||
} else {
|
||||
hcd->state = old_state;
|
||||
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
|
||||
"resume", status);
|
||||
usb_hc_died(hcd);
|
||||
if (status != -ESHUTDOWN)
|
||||
usb_hc_died(hcd);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@ -1384,30 +1408,10 @@ EXPORT_SYMBOL (usb_bus_start_enum);
|
||||
*/
|
||||
void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
int at_root_hub;
|
||||
|
||||
at_root_hub = (urb->dev == hcd->self.root_hub);
|
||||
urb_unlink (urb);
|
||||
|
||||
/* lower level hcd code should use *_dma exclusively if the
|
||||
* host controller does DMA */
|
||||
if (hcd->self.uses_dma && !at_root_hub) {
|
||||
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);
|
||||
}
|
||||
|
||||
urb_unlink(hcd, urb);
|
||||
usbmon_urb_complete (&hcd->self, urb);
|
||||
usb_unanchor_urb(urb);
|
||||
|
||||
/* pass ownership to the completion handler */
|
||||
urb->complete (urb);
|
||||
atomic_dec (&urb->use_count);
|
||||
|
@ -364,23 +364,13 @@ extern int usb_find_interface_driver (struct usb_device *dev,
|
||||
#ifdef CONFIG_PM
|
||||
extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
|
||||
extern void usb_root_hub_lost_power (struct usb_device *rhdev);
|
||||
extern int hcd_bus_suspend (struct usb_bus *bus);
|
||||
extern int hcd_bus_resume (struct usb_bus *bus);
|
||||
extern int hcd_bus_suspend(struct usb_device *rhdev);
|
||||
extern int hcd_bus_resume(struct usb_device *rhdev);
|
||||
#else
|
||||
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int hcd_bus_suspend(struct usb_bus *bus)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hcd_bus_resume (struct usb_bus *bus)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*
|
||||
|
@ -31,9 +31,16 @@
|
||||
#include "hcd.h"
|
||||
#include "hub.h"
|
||||
|
||||
#ifdef CONFIG_USB_PERSIST
|
||||
#define USB_PERSIST 1
|
||||
#else
|
||||
#define USB_PERSIST 0
|
||||
#endif
|
||||
|
||||
struct usb_hub {
|
||||
struct device *intfdev; /* the "interface" device */
|
||||
struct usb_device *hdev;
|
||||
struct kref kref;
|
||||
struct urb *urb; /* for interrupt polling pipe */
|
||||
|
||||
/* buffer for urb ... with extra space in case of babble */
|
||||
@ -66,6 +73,7 @@ struct usb_hub {
|
||||
unsigned limited_power:1;
|
||||
unsigned quiescing:1;
|
||||
unsigned activating:1;
|
||||
unsigned disconnected:1;
|
||||
|
||||
unsigned has_indicators:1;
|
||||
u8 indicator[USB_MAXCHILDREN];
|
||||
@ -321,7 +329,7 @@ static void kick_khubd(struct usb_hub *hub)
|
||||
to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
|
||||
|
||||
spin_lock_irqsave(&hub_event_lock, flags);
|
||||
if (list_empty(&hub->event_list)) {
|
||||
if (!hub->disconnected & list_empty(&hub->event_list)) {
|
||||
list_add_tail(&hub->event_list, &hub_event_list);
|
||||
wake_up(&khubd_wait);
|
||||
}
|
||||
@ -330,6 +338,7 @@ static void kick_khubd(struct usb_hub *hub)
|
||||
|
||||
void usb_kick_khubd(struct usb_device *hdev)
|
||||
{
|
||||
/* FIXME: What if hdev isn't bound to the hub driver? */
|
||||
kick_khubd(hdev_to_hub(hdev));
|
||||
}
|
||||
|
||||
@ -400,9 +409,10 @@ static void hub_tt_kevent (struct work_struct *work)
|
||||
struct usb_hub *hub =
|
||||
container_of(work, struct usb_hub, tt.kevent);
|
||||
unsigned long flags;
|
||||
int limit = 100;
|
||||
|
||||
spin_lock_irqsave (&hub->tt.lock, flags);
|
||||
while (!list_empty (&hub->tt.clear_list)) {
|
||||
while (--limit && !list_empty (&hub->tt.clear_list)) {
|
||||
struct list_head *temp;
|
||||
struct usb_tt_clear *clear;
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
@ -550,48 +560,68 @@ static int hub_hub_status(struct usb_hub *hub,
|
||||
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
|
||||
{
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (hdev->children[port1-1] && set_state) {
|
||||
if (hdev->children[port1-1] && set_state)
|
||||
usb_set_device_state(hdev->children[port1-1],
|
||||
USB_STATE_NOTATTACHED);
|
||||
}
|
||||
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
|
||||
if (!hub->error)
|
||||
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
|
||||
if (ret)
|
||||
dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
|
||||
port1, ret);
|
||||
|
||||
port1, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable a port and mark a logical connnect-change event, so that some
|
||||
* time later khubd will disconnect() any existing usb_device on the port
|
||||
* and will re-enumerate if there actually is a device attached.
|
||||
*/
|
||||
static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
|
||||
{
|
||||
dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);
|
||||
hub_port_disable(hub, port1, 1);
|
||||
|
||||
/* FIXME let caller ask to power down the port:
|
||||
* - some devices won't enumerate without a VBUS power cycle
|
||||
* - SRP saves power that way
|
||||
* - ... new call, TBD ...
|
||||
* That's easy if this hub can switch power per-port, and
|
||||
* khubd reactivates the port later (timer, SRP, etc).
|
||||
* Powerdown must be optional, because of reset/DFU.
|
||||
*/
|
||||
|
||||
set_bit(port1, hub->change_bits);
|
||||
kick_khubd(hub);
|
||||
}
|
||||
|
||||
/* caller has locked the hub device */
|
||||
static void hub_pre_reset(struct usb_interface *intf)
|
||||
static int hub_pre_reset(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_hub *hub = usb_get_intfdata(intf);
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
int port1;
|
||||
int i;
|
||||
|
||||
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
||||
if (hdev->children[port1 - 1]) {
|
||||
usb_disconnect(&hdev->children[port1 - 1]);
|
||||
if (hub->error == 0)
|
||||
hub_port_disable(hub, port1, 0);
|
||||
}
|
||||
/* Disconnect all the children */
|
||||
for (i = 0; i < hdev->maxchild; ++i) {
|
||||
if (hdev->children[i])
|
||||
usb_disconnect(&hdev->children[i]);
|
||||
}
|
||||
hub_quiesce(hub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* caller has locked the hub device */
|
||||
static void hub_post_reset(struct usb_interface *intf)
|
||||
static int hub_post_reset(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_hub *hub = usb_get_intfdata(intf);
|
||||
|
||||
hub_activate(hub);
|
||||
hub_power_on(hub);
|
||||
hub_activate(hub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hub_configure(struct usb_hub *hub,
|
||||
struct usb_endpoint_descriptor *endpoint)
|
||||
{
|
||||
@ -845,43 +875,42 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hub_release(struct kref *kref)
|
||||
{
|
||||
struct usb_hub *hub = container_of(kref, struct usb_hub, kref);
|
||||
|
||||
usb_put_intf(to_usb_interface(hub->intfdev));
|
||||
kfree(hub);
|
||||
}
|
||||
|
||||
static unsigned highspeed_hubs;
|
||||
|
||||
static void hub_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||
struct usb_device *hdev;
|
||||
|
||||
/* Take the hub off the event list and don't let it be added again */
|
||||
spin_lock_irq(&hub_event_lock);
|
||||
list_del_init(&hub->event_list);
|
||||
hub->disconnected = 1;
|
||||
spin_unlock_irq(&hub_event_lock);
|
||||
|
||||
/* Disconnect all children and quiesce the hub */
|
||||
hub->error = 0;
|
||||
hub_pre_reset(intf);
|
||||
|
||||
usb_set_intfdata (intf, NULL);
|
||||
hdev = hub->hdev;
|
||||
|
||||
if (hdev->speed == USB_SPEED_HIGH)
|
||||
if (hub->hdev->speed == USB_SPEED_HIGH)
|
||||
highspeed_hubs--;
|
||||
|
||||
usb_free_urb(hub->urb);
|
||||
hub->urb = NULL;
|
||||
|
||||
spin_lock_irq(&hub_event_lock);
|
||||
list_del_init(&hub->event_list);
|
||||
spin_unlock_irq(&hub_event_lock);
|
||||
|
||||
kfree(hub->descriptor);
|
||||
hub->descriptor = NULL;
|
||||
|
||||
kfree(hub->status);
|
||||
hub->status = NULL;
|
||||
usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer,
|
||||
hub->buffer_dma);
|
||||
|
||||
if (hub->buffer) {
|
||||
usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer,
|
||||
hub->buffer_dma);
|
||||
hub->buffer = NULL;
|
||||
}
|
||||
|
||||
kfree(hub);
|
||||
kref_put(&hub->kref, hub_release);
|
||||
}
|
||||
|
||||
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
@ -929,10 +958,12 @@ descriptor_error:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kref_init(&hub->kref);
|
||||
INIT_LIST_HEAD(&hub->event_list);
|
||||
hub->intfdev = &intf->dev;
|
||||
hub->hdev = hdev;
|
||||
INIT_DELAYED_WORK(&hub->leds, led_work);
|
||||
usb_get_intf(intf);
|
||||
|
||||
usb_set_intfdata (intf, hub);
|
||||
intf->needs_remote_wakeup = 1;
|
||||
@ -982,49 +1013,6 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
|
||||
}
|
||||
|
||||
|
||||
/* grab device/port lock, returning index of that port (zero based).
|
||||
* protects the upstream link used by this device from concurrent
|
||||
* tree operations like suspend, resume, reset, and disconnect, which
|
||||
* apply to everything downstream of a given port.
|
||||
*/
|
||||
static int locktree(struct usb_device *udev)
|
||||
{
|
||||
int t;
|
||||
struct usb_device *hdev;
|
||||
|
||||
if (!udev)
|
||||
return -ENODEV;
|
||||
|
||||
/* root hub is always the first lock in the series */
|
||||
hdev = udev->parent;
|
||||
if (!hdev) {
|
||||
usb_lock_device(udev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* on the path from root to us, lock everything from
|
||||
* top down, dropping parent locks when not needed
|
||||
*/
|
||||
t = locktree(hdev);
|
||||
if (t < 0)
|
||||
return t;
|
||||
|
||||
/* everything is fail-fast once disconnect
|
||||
* processing starts
|
||||
*/
|
||||
if (udev->state == USB_STATE_NOTATTACHED) {
|
||||
usb_unlock_device(hdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* when everyone grabs locks top->bottom,
|
||||
* non-overlapping work may be concurrent
|
||||
*/
|
||||
usb_lock_device(udev);
|
||||
usb_unlock_device(hdev);
|
||||
return udev->portnum;
|
||||
}
|
||||
|
||||
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
|
||||
{
|
||||
int i;
|
||||
@ -1089,41 +1077,6 @@ void usb_set_device_state(struct usb_device *udev,
|
||||
spin_unlock_irqrestore(&device_state_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/**
|
||||
* usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
|
||||
* @rhdev: struct usb_device for the root hub
|
||||
*
|
||||
* The USB host controller driver calls this function when its root hub
|
||||
* is resumed and Vbus power has been interrupted or the controller
|
||||
* has been reset. The routine marks all the children of the root hub
|
||||
* as NOTATTACHED and marks logical connect-change events on their ports.
|
||||
*/
|
||||
void usb_root_hub_lost_power(struct usb_device *rhdev)
|
||||
{
|
||||
struct usb_hub *hub;
|
||||
int port1;
|
||||
unsigned long flags;
|
||||
|
||||
dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
|
||||
|
||||
spin_lock_irqsave(&device_state_lock, flags);
|
||||
hub = hdev_to_hub(rhdev);
|
||||
for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
|
||||
if (rhdev->children[port1 - 1]) {
|
||||
recursively_mark_NOTATTACHED(
|
||||
rhdev->children[port1 - 1]);
|
||||
set_bit(port1, hub->change_bits);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&device_state_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static void choose_address(struct usb_device *udev)
|
||||
{
|
||||
int devnum;
|
||||
@ -1264,7 +1217,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
|
||||
|
||||
#ifdef CONFIG_USB_OTG
|
||||
#include "otg_whitelist.h"
|
||||
static int __usb_port_suspend(struct usb_device *, int port1);
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -1370,11 +1322,11 @@ int usb_new_device(struct usb_device *udev)
|
||||
* (Includes HNP test device.)
|
||||
*/
|
||||
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
|
||||
err = __usb_port_suspend(udev, udev->bus->otg_port);
|
||||
err = usb_port_suspend(udev);
|
||||
if (err < 0)
|
||||
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
|
||||
}
|
||||
err = -ENODEV;
|
||||
err = -ENOTSUPP;
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
@ -1471,9 +1423,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
||||
if (!(portstatus & USB_PORT_STAT_CONNECTION))
|
||||
return -ENOTCONN;
|
||||
|
||||
/* bomb out completely if something weird happened */
|
||||
/* bomb out completely if the connection bounced */
|
||||
if ((portchange & USB_PORT_STAT_C_CONNECTION))
|
||||
return -EINVAL;
|
||||
return -ENOTCONN;
|
||||
|
||||
/* if we`ve finished resetting, then break out of the loop */
|
||||
if (!(portstatus & USB_PORT_STAT_RESET) &&
|
||||
@ -1552,34 +1504,24 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable a port and mark a logical connnect-change event, so that some
|
||||
* time later khubd will disconnect() any existing usb_device on the port
|
||||
* and will re-enumerate if there actually is a device attached.
|
||||
*/
|
||||
static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
|
||||
{
|
||||
dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);
|
||||
hub_port_disable(hub, port1, 1);
|
||||
|
||||
/* FIXME let caller ask to power down the port:
|
||||
* - some devices won't enumerate without a VBUS power cycle
|
||||
* - SRP saves power that way
|
||||
* - ... new call, TBD ...
|
||||
* That's easy if this hub can switch power per-port, and
|
||||
* khubd reactivates the port later (timer, SRP, etc).
|
||||
* Powerdown must be optional, because of reset/DFU.
|
||||
*/
|
||||
|
||||
set_bit(port1, hub->change_bits);
|
||||
kick_khubd(hub);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
/*
|
||||
* usb_port_suspend - suspend a usb device's upstream port
|
||||
* @udev: device that's no longer in active use, not a root hub
|
||||
* Context: must be able to sleep; device not locked; pm locks held
|
||||
*
|
||||
* Suspends a USB device that isn't in active use, conserving power.
|
||||
* Devices may wake out of a suspend, if anything important happens,
|
||||
* using the remote wakeup mechanism. They may also be taken out of
|
||||
* suspend by the host, using usb_port_resume(). It's also routine
|
||||
* to disconnect devices while they are suspended.
|
||||
*
|
||||
* This only affects the USB hardware for a device; its interfaces
|
||||
* (and, for hubs, child devices) must already have been suspended.
|
||||
*
|
||||
* Selective port suspend reduces power; most suspended devices draw
|
||||
* less than 500 uA. It's also used in OTG, along with remote wakeup.
|
||||
* All devices below the suspended port are also suspended.
|
||||
@ -1588,11 +1530,35 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
|
||||
* also support "remote wakeup", where the device can activate the USB
|
||||
* tree above them to deliver data, such as a keypress or packet. In
|
||||
* some cases, this wakes the USB host.
|
||||
*
|
||||
* Suspending OTG devices may trigger HNP, if that's been enabled
|
||||
* between a pair of dual-role devices. That will change roles, such
|
||||
* as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
|
||||
*
|
||||
* Devices on USB hub ports have only one "suspend" state, corresponding
|
||||
* to ACPI D2, "may cause the device to lose some context".
|
||||
* State transitions include:
|
||||
*
|
||||
* - suspend, resume ... when the VBUS power link stays live
|
||||
* - suspend, disconnect ... VBUS lost
|
||||
*
|
||||
* Once VBUS drop breaks the circuit, the port it's using has to go through
|
||||
* normal re-enumeration procedures, starting with enabling VBUS power.
|
||||
* Other than re-initializing the hub (plug/unplug, except for root hubs),
|
||||
* Linux (2.6) currently has NO mechanisms to initiate that: no khubd
|
||||
* timer, no SRP, no requests through sysfs.
|
||||
*
|
||||
* If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
|
||||
* the root hub for their bus goes into global suspend ... so we don't
|
||||
* (falsely) update the device power state to say it suspended.
|
||||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
static int hub_port_suspend(struct usb_hub *hub, int port1,
|
||||
struct usb_device *udev)
|
||||
int usb_port_suspend(struct usb_device *udev)
|
||||
{
|
||||
int status;
|
||||
struct usb_hub *hub = hdev_to_hub(udev->parent);
|
||||
int port1 = udev->portnum;
|
||||
int status;
|
||||
|
||||
// dev_dbg(hub->intfdev, "suspend port %d\n", port1);
|
||||
|
||||
@ -1609,17 +1575,15 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
|
||||
NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (status)
|
||||
dev_dbg(&udev->dev,
|
||||
"won't remote wakeup, status %d\n",
|
||||
status);
|
||||
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
|
||||
status);
|
||||
}
|
||||
|
||||
/* see 7.1.7.6 */
|
||||
status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
|
||||
if (status) {
|
||||
dev_dbg(hub->intfdev,
|
||||
"can't suspend port %d, status %d\n",
|
||||
port1, status);
|
||||
dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
|
||||
port1, status);
|
||||
/* paranoia: "should not happen" */
|
||||
(void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
|
||||
@ -1636,86 +1600,25 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Devices on USB hub ports have only one "suspend" state, corresponding
|
||||
* to ACPI D2, "may cause the device to lose some context".
|
||||
* State transitions include:
|
||||
*
|
||||
* - suspend, resume ... when the VBUS power link stays live
|
||||
* - suspend, disconnect ... VBUS lost
|
||||
*
|
||||
* Once VBUS drop breaks the circuit, the port it's using has to go through
|
||||
* normal re-enumeration procedures, starting with enabling VBUS power.
|
||||
* Other than re-initializing the hub (plug/unplug, except for root hubs),
|
||||
* Linux (2.6) currently has NO mechanisms to initiate that: no khubd
|
||||
* timer, no SRP, no requests through sysfs.
|
||||
*
|
||||
* If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
|
||||
* the root hub for their bus goes into global suspend ... so we don't
|
||||
* (falsely) update the device power state to say it suspended.
|
||||
*/
|
||||
static int __usb_port_suspend (struct usb_device *udev, int port1)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
/* caller owns the udev device lock */
|
||||
if (port1 < 0)
|
||||
return port1;
|
||||
|
||||
/* we change the device's upstream USB link,
|
||||
* but root hubs have no upstream USB link.
|
||||
*/
|
||||
if (udev->parent)
|
||||
status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
|
||||
udev);
|
||||
else {
|
||||
dev_dbg(&udev->dev, "usb %ssuspend\n",
|
||||
udev->auto_pm ? "auto-" : "");
|
||||
usb_set_device_state(udev, USB_STATE_SUSPENDED);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* usb_port_suspend - suspend a usb device's upstream port
|
||||
* @udev: device that's no longer in active use
|
||||
* Context: must be able to sleep; device not locked; pm locks held
|
||||
*
|
||||
* Suspends a USB device that isn't in active use, conserving power.
|
||||
* Devices may wake out of a suspend, if anything important happens,
|
||||
* using the remote wakeup mechanism. They may also be taken out of
|
||||
* suspend by the host, using usb_port_resume(). It's also routine
|
||||
* to disconnect devices while they are suspended.
|
||||
*
|
||||
* This only affects the USB hardware for a device; its interfaces
|
||||
* (and, for hubs, child devices) must already have been suspended.
|
||||
*
|
||||
* Suspending OTG devices may trigger HNP, if that's been enabled
|
||||
* between a pair of dual-role devices. That will change roles, such
|
||||
* as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
|
||||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
int usb_port_suspend(struct usb_device *udev)
|
||||
{
|
||||
return __usb_port_suspend(udev, udev->portnum);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the USB "suspend" state is in use (rather than "global suspend"),
|
||||
* many devices will be individually taken out of suspend state using
|
||||
* special" resume" signaling. These routines kick in shortly after
|
||||
* special "resume" signaling. This routine kicks in shortly after
|
||||
* hardware resume signaling is finished, either because of selective
|
||||
* resume (by host) or remote wakeup (by device) ... now see what changed
|
||||
* in the tree that's rooted at this device.
|
||||
*
|
||||
* If @udev->reset_resume is set then the device is reset before the
|
||||
* status check is done.
|
||||
*/
|
||||
static int finish_port_resume(struct usb_device *udev)
|
||||
{
|
||||
int status;
|
||||
int status = 0;
|
||||
u16 devstatus;
|
||||
|
||||
/* caller owns the udev device lock */
|
||||
dev_dbg(&udev->dev, "finish resume\n");
|
||||
dev_dbg(&udev->dev, "finish %sresume\n",
|
||||
udev->reset_resume ? "reset-" : "");
|
||||
|
||||
/* usb ch9 identifies four variants of SUSPENDED, based on what
|
||||
* state the device resumes to. Linux currently won't see the
|
||||
@ -1726,22 +1629,30 @@ static int finish_port_resume(struct usb_device *udev)
|
||||
? USB_STATE_CONFIGURED
|
||||
: USB_STATE_ADDRESS);
|
||||
|
||||
/* 10.5.4.5 says not to reset a suspended port if the attached
|
||||
* device is enabled for remote wakeup. Hence the reset
|
||||
* operation is carried out here, after the port has been
|
||||
* resumed.
|
||||
*/
|
||||
if (udev->reset_resume)
|
||||
status = usb_reset_device(udev);
|
||||
|
||||
/* 10.5.4.5 says be sure devices in the tree are still there.
|
||||
* For now let's assume the device didn't go crazy on resume,
|
||||
* and device drivers will know about any resume quirks.
|
||||
*/
|
||||
status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
|
||||
if (status >= 0)
|
||||
status = (status == 2 ? 0 : -ENODEV);
|
||||
if (status == 0) {
|
||||
status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
|
||||
if (status >= 0)
|
||||
status = (status == 2 ? 0 : -ENODEV);
|
||||
}
|
||||
|
||||
if (status)
|
||||
dev_dbg(&udev->dev,
|
||||
"gone after usb resume? status %d\n",
|
||||
status);
|
||||
else if (udev->actconfig) {
|
||||
if (status) {
|
||||
dev_dbg(&udev->dev, "gone after usb resume? status %d\n",
|
||||
status);
|
||||
} else if (udev->actconfig) {
|
||||
le16_to_cpus(&devstatus);
|
||||
if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
|
||||
&& udev->parent) {
|
||||
if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
|
||||
status = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
@ -1754,19 +1665,52 @@ static int finish_port_resume(struct usb_device *udev)
|
||||
"wakeup, status %d\n", status);
|
||||
}
|
||||
status = 0;
|
||||
|
||||
} else if (udev->devnum <= 0) {
|
||||
dev_dbg(&udev->dev, "bogus resume!\n");
|
||||
status = -EINVAL;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
||||
/*
|
||||
* usb_port_resume - re-activate a suspended usb device's upstream port
|
||||
* @udev: device to re-activate, not a root hub
|
||||
* Context: must be able to sleep; device not locked; pm locks held
|
||||
*
|
||||
* This will re-activate the suspended device, increasing power usage
|
||||
* while letting drivers communicate again with its endpoints.
|
||||
* USB resume explicitly guarantees that the power session between
|
||||
* the host and the device is the same as it was when the device
|
||||
* suspended.
|
||||
*
|
||||
* If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this
|
||||
* routine won't check that the port is still enabled. Furthermore,
|
||||
* if @udev->reset_resume is set then finish_port_resume() above will
|
||||
* reset @udev. The end result is that a broken power session can be
|
||||
* recovered and @udev will appear to persist across a loss of VBUS power.
|
||||
*
|
||||
* For example, if a host controller doesn't maintain VBUS suspend current
|
||||
* during a system sleep or is reset when the system wakes up, all the USB
|
||||
* power sessions below it will be broken. This is especially troublesome
|
||||
* for mass-storage devices containing mounted filesystems, since the
|
||||
* device will appear to have disconnected and all the memory mappings
|
||||
* to it will be lost. Using the USB_PERSIST facility, the device can be
|
||||
* made to appear as if it had not disconnected.
|
||||
*
|
||||
* This facility is inherently dangerous. Although usb_reset_device()
|
||||
* makes every effort to insure that the same device is present after the
|
||||
* reset as before, it cannot provide a 100% guarantee. Furthermore it's
|
||||
* quite possible for a device to remain unaltered but its media to be
|
||||
* changed. If the user replaces a flash memory card while the system is
|
||||
* asleep, he will have only himself to blame when the filesystem on the
|
||||
* new card is corrupted and the system crashes.
|
||||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
int usb_port_resume(struct usb_device *udev)
|
||||
{
|
||||
int status;
|
||||
u16 portchange, portstatus;
|
||||
struct usb_hub *hub = hdev_to_hub(udev->parent);
|
||||
int port1 = udev->portnum;
|
||||
int status;
|
||||
u16 portchange, portstatus;
|
||||
unsigned mask_flags, want_flags;
|
||||
|
||||
/* Skip the initial Clear-Suspend step for a remote wakeup */
|
||||
status = hub_port_status(hub, port1, &portstatus, &portchange);
|
||||
@ -1781,30 +1725,31 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
||||
status = clear_port_feature(hub->hdev,
|
||||
port1, USB_PORT_FEAT_SUSPEND);
|
||||
if (status) {
|
||||
dev_dbg(hub->intfdev,
|
||||
"can't resume port %d, status %d\n",
|
||||
port1, status);
|
||||
dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",
|
||||
port1, status);
|
||||
} else {
|
||||
/* drive resume for at least 20 msec */
|
||||
if (udev)
|
||||
dev_dbg(&udev->dev, "usb %sresume\n",
|
||||
udev->auto_pm ? "auto-" : "");
|
||||
dev_dbg(&udev->dev, "usb %sresume\n",
|
||||
udev->auto_pm ? "auto-" : "");
|
||||
msleep(25);
|
||||
|
||||
#define LIVE_FLAGS ( USB_PORT_STAT_POWER \
|
||||
| USB_PORT_STAT_ENABLE \
|
||||
| USB_PORT_STAT_CONNECTION)
|
||||
|
||||
/* Virtual root hubs can trigger on GET_PORT_STATUS to
|
||||
* stop resume signaling. Then finish the resume
|
||||
* sequence.
|
||||
*/
|
||||
status = hub_port_status(hub, port1, &portstatus, &portchange);
|
||||
SuspendCleared:
|
||||
if (status < 0
|
||||
|| (portstatus & LIVE_FLAGS) != LIVE_FLAGS
|
||||
|| (portstatus & USB_PORT_STAT_SUSPEND) != 0
|
||||
) {
|
||||
|
||||
SuspendCleared:
|
||||
if (USB_PERSIST && udev->reset_resume)
|
||||
want_flags = USB_PORT_STAT_POWER
|
||||
| USB_PORT_STAT_CONNECTION;
|
||||
else
|
||||
want_flags = USB_PORT_STAT_POWER
|
||||
| USB_PORT_STAT_CONNECTION
|
||||
| USB_PORT_STAT_ENABLE;
|
||||
mask_flags = want_flags | USB_PORT_STAT_SUSPEND;
|
||||
|
||||
if (status < 0 || (portstatus & mask_flags) != want_flags) {
|
||||
dev_dbg(hub->intfdev,
|
||||
"port %d status %04x.%04x after resume, %d\n",
|
||||
port1, portchange, portstatus, status);
|
||||
@ -1816,51 +1761,19 @@ SuspendCleared:
|
||||
USB_PORT_FEAT_C_SUSPEND);
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
if (udev)
|
||||
status = finish_port_resume(udev);
|
||||
}
|
||||
}
|
||||
if (status < 0)
|
||||
hub_port_logical_disconnect(hub, port1);
|
||||
|
||||
clear_bit(port1, hub->busy_bits);
|
||||
if (!hub->hdev->parent && !hub->busy_bits[0])
|
||||
usb_enable_root_hub_irq(hub->hdev->bus);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* usb_port_resume - re-activate a suspended usb device's upstream port
|
||||
* @udev: device to re-activate
|
||||
* Context: must be able to sleep; device not locked; pm locks held
|
||||
*
|
||||
* This will re-activate the suspended device, increasing power usage
|
||||
* while letting drivers communicate again with its endpoints.
|
||||
* USB resume explicitly guarantees that the power session between
|
||||
* the host and the device is the same as it was when the device
|
||||
* suspended.
|
||||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
int usb_port_resume(struct usb_device *udev)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* we change the device's upstream USB link,
|
||||
* but root hubs have no upstream USB link.
|
||||
*/
|
||||
if (udev->parent) {
|
||||
// NOTE this fails if parent is also suspended...
|
||||
status = hub_port_resume(hdev_to_hub(udev->parent),
|
||||
udev->portnum, udev);
|
||||
} else {
|
||||
dev_dbg(&udev->dev, "usb %sresume\n",
|
||||
udev->auto_pm ? "auto-" : "");
|
||||
if (status == 0)
|
||||
status = finish_port_resume(udev);
|
||||
}
|
||||
if (status < 0)
|
||||
if (status < 0) {
|
||||
dev_dbg(&udev->dev, "can't resume, status %d\n", status);
|
||||
hub_port_logical_disconnect(hub, port1);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1887,21 +1800,16 @@ int usb_port_suspend(struct usb_device *udev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
finish_port_resume(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_port_resume(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
int status = 0;
|
||||
|
||||
/* However we may need to do a reset-resume */
|
||||
if (udev->reset_resume) {
|
||||
dev_dbg(&udev->dev, "reset-resume\n");
|
||||
status = usb_reset_device(udev);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline int remote_wakeup(struct usb_device *udev)
|
||||
@ -1916,7 +1824,6 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
unsigned port1;
|
||||
int status = 0;
|
||||
|
||||
/* fail if children aren't already suspended */
|
||||
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||
@ -1942,49 +1849,75 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||
|
||||
/* stop khubd and related activity */
|
||||
hub_quiesce(hub);
|
||||
|
||||
/* "global suspend" of the downstream HC-to-USB interface */
|
||||
if (!hdev->parent) {
|
||||
status = hcd_bus_suspend(hdev->bus);
|
||||
if (status != 0) {
|
||||
dev_dbg(&hdev->dev, "'global' suspend %d\n", status);
|
||||
hub_activate(hub);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hub_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
int status;
|
||||
|
||||
dev_dbg(&intf->dev, "%s\n", __FUNCTION__);
|
||||
|
||||
/* "global resume" of the downstream HC-to-USB interface */
|
||||
if (!hdev->parent) {
|
||||
struct usb_bus *bus = hdev->bus;
|
||||
if (bus) {
|
||||
status = hcd_bus_resume (bus);
|
||||
if (status) {
|
||||
dev_dbg(&intf->dev, "'global' resume %d\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
} else
|
||||
return -EOPNOTSUPP;
|
||||
if (status == 0) {
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
/* tell khubd to look for changes on this hub */
|
||||
hub_activate(hub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hub_reset_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_hub *hub = usb_get_intfdata(intf);
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
int port1;
|
||||
|
||||
hub_power_on(hub);
|
||||
|
||||
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
||||
struct usb_device *child = hdev->children[port1-1];
|
||||
|
||||
if (child) {
|
||||
|
||||
/* For "USB_PERSIST"-enabled children we must
|
||||
* mark the child device for reset-resume and
|
||||
* turn off the connect-change status to prevent
|
||||
* khubd from disconnecting it later.
|
||||
*/
|
||||
if (USB_PERSIST && child->persist_enabled) {
|
||||
child->reset_resume = 1;
|
||||
clear_port_feature(hdev, port1,
|
||||
USB_PORT_FEAT_C_CONNECTION);
|
||||
|
||||
/* Otherwise we must disconnect the child,
|
||||
* but as we may not lock the child device here
|
||||
* we have to do a "logical" disconnect.
|
||||
*/
|
||||
} else {
|
||||
hub_port_logical_disconnect(hub, port1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hub_activate(hub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
|
||||
* @rhdev: struct usb_device for the root hub
|
||||
*
|
||||
* The USB host controller driver calls this function when its root hub
|
||||
* is resumed and Vbus power has been interrupted or the controller
|
||||
* has been reset. The routine marks @rhdev as having lost power. When
|
||||
* the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST
|
||||
* is enabled then it will carry out power-session recovery, otherwise
|
||||
* it will disconnect all the child devices.
|
||||
*/
|
||||
void usb_root_hub_lost_power(struct usb_device *rhdev)
|
||||
{
|
||||
dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
|
||||
rhdev->reset_resume = 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
|
||||
|
||||
#else /* CONFIG_PM */
|
||||
|
||||
static inline int remote_wakeup(struct usb_device *udev)
|
||||
@ -1992,8 +1925,9 @@ static inline int remote_wakeup(struct usb_device *udev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define hub_suspend NULL
|
||||
#define hub_resume NULL
|
||||
#define hub_suspend NULL
|
||||
#define hub_resume NULL
|
||||
#define hub_reset_resume NULL
|
||||
#endif
|
||||
|
||||
|
||||
@ -2456,19 +2390,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
/* If something is connected, but the port is suspended, wake it up. */
|
||||
if (portstatus & USB_PORT_STAT_SUSPEND) {
|
||||
status = hub_port_resume(hub, port1, NULL);
|
||||
if (status < 0) {
|
||||
dev_dbg(hub_dev,
|
||||
"can't clear suspend on port %d; %d\n",
|
||||
port1, status);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < SET_CONFIG_TRIES; i++) {
|
||||
struct usb_device *udev;
|
||||
|
||||
@ -2579,7 +2500,7 @@ loop:
|
||||
ep0_reinit(udev);
|
||||
release_address(udev);
|
||||
usb_put_dev(udev);
|
||||
if (status == -ENOTCONN)
|
||||
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2620,10 +2541,12 @@ static void hub_events(void)
|
||||
list_del_init(tmp);
|
||||
|
||||
hub = list_entry(tmp, struct usb_hub, event_list);
|
||||
hdev = hub->hdev;
|
||||
intf = to_usb_interface(hub->intfdev);
|
||||
hub_dev = &intf->dev;
|
||||
kref_get(&hub->kref);
|
||||
spin_unlock_irq(&hub_event_lock);
|
||||
|
||||
hdev = hub->hdev;
|
||||
hub_dev = hub->intfdev;
|
||||
intf = to_usb_interface(hub_dev);
|
||||
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
|
||||
hdev->state, hub->descriptor
|
||||
? hub->descriptor->bNbrPorts
|
||||
@ -2632,16 +2555,10 @@ static void hub_events(void)
|
||||
(u16) hub->change_bits[0],
|
||||
(u16) hub->event_bits[0]);
|
||||
|
||||
usb_get_intf(intf);
|
||||
spin_unlock_irq(&hub_event_lock);
|
||||
|
||||
/* Lock the device, then check to see if we were
|
||||
* disconnected while waiting for the lock to succeed. */
|
||||
if (locktree(hdev) < 0) {
|
||||
usb_put_intf(intf);
|
||||
continue;
|
||||
}
|
||||
if (hub != usb_get_intfdata(intf))
|
||||
usb_lock_device(hdev);
|
||||
if (unlikely(hub->disconnected))
|
||||
goto loop;
|
||||
|
||||
/* If the hub has died, clean up after it */
|
||||
@ -2804,7 +2721,7 @@ loop_autopm:
|
||||
usb_autopm_enable(intf);
|
||||
loop:
|
||||
usb_unlock_device(hdev);
|
||||
usb_put_intf(intf);
|
||||
kref_put(&hub->kref, hub_release);
|
||||
|
||||
} /* end while (1) */
|
||||
}
|
||||
@ -2839,6 +2756,7 @@ static struct usb_driver hub_driver = {
|
||||
.disconnect = hub_disconnect,
|
||||
.suspend = hub_suspend,
|
||||
.resume = hub_resume,
|
||||
.reset_resume = hub_reset_resume,
|
||||
.pre_reset = hub_pre_reset,
|
||||
.post_reset = hub_post_reset,
|
||||
.ioctl = hub_ioctl,
|
||||
@ -2941,6 +2859,11 @@ static int config_descriptors_changed(struct usb_device *udev)
|
||||
* this from a driver probe() routine after downloading new firmware.
|
||||
* For calls that might not occur during probe(), drivers should lock
|
||||
* the device using usb_lock_device_for_reset().
|
||||
*
|
||||
* Locking exception: This routine may also be called from within an
|
||||
* autoresume handler. Such usage won't conflict with other tasks
|
||||
* holding the device lock because these tasks should always call
|
||||
* usb_autopm_resume_device(), thereby preventing any unwanted autoresume.
|
||||
*/
|
||||
int usb_reset_device(struct usb_device *udev)
|
||||
{
|
||||
@ -2971,7 +2894,7 @@ int usb_reset_device(struct usb_device *udev)
|
||||
* Other endpoints will be handled by re-enumeration. */
|
||||
ep0_reinit(udev);
|
||||
ret = hub_port_init(parent_hub, udev, port1, i);
|
||||
if (ret >= 0)
|
||||
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
|
||||
break;
|
||||
}
|
||||
clear_bit(port1, parent_hub->busy_bits);
|
||||
@ -3087,6 +3010,7 @@ int usb_reset_composite_device(struct usb_device *udev,
|
||||
drv = to_usb_driver(cintf->dev.driver);
|
||||
if (drv->pre_reset)
|
||||
(drv->pre_reset)(cintf);
|
||||
/* FIXME: Unbind if pre_reset returns an error or isn't defined */
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3105,6 +3029,7 @@ int usb_reset_composite_device(struct usb_device *udev,
|
||||
drv = to_usb_driver(cintf->dev.driver);
|
||||
if (drv->post_reset)
|
||||
(drv->post_reset)(cintf);
|
||||
/* FIXME: Unbind if post_reset returns an error or isn't defined */
|
||||
}
|
||||
if (cintf != iface)
|
||||
up(&cintf->dev.sem);
|
||||
|
@ -404,8 +404,6 @@ int usb_sg_init (
|
||||
|
||||
io->urbs [i]->complete = sg_complete;
|
||||
io->urbs [i]->context = io;
|
||||
io->urbs [i]->status = -EINPROGRESS;
|
||||
io->urbs [i]->actual_length = 0;
|
||||
|
||||
/*
|
||||
* Some systems need to revert to PIO when DMA is temporarily
|
||||
@ -499,7 +497,8 @@ void usb_sg_wait (struct usb_sg_request *io)
|
||||
|
||||
/* queue the urbs. */
|
||||
spin_lock_irq (&io->lock);
|
||||
for (i = 0; i < entries && !io->status; i++) {
|
||||
i = 0;
|
||||
while (i < entries && !io->status) {
|
||||
int retval;
|
||||
|
||||
io->urbs [i]->dev = io->dev;
|
||||
@ -516,7 +515,6 @@ void usb_sg_wait (struct usb_sg_request *io)
|
||||
case -ENOMEM:
|
||||
io->urbs[i]->dev = NULL;
|
||||
retval = 0;
|
||||
i--;
|
||||
yield ();
|
||||
break;
|
||||
|
||||
@ -527,6 +525,7 @@ void usb_sg_wait (struct usb_sg_request *io)
|
||||
* URBs are queued at once; N milliseconds?
|
||||
*/
|
||||
case 0:
|
||||
++i;
|
||||
cpu_relax ();
|
||||
break;
|
||||
|
||||
@ -1385,6 +1384,36 @@ struct device_type usb_if_device_type = {
|
||||
.uevent = usb_if_uevent,
|
||||
};
|
||||
|
||||
static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
|
||||
struct usb_host_config *config,
|
||||
u8 inum)
|
||||
{
|
||||
struct usb_interface_assoc_descriptor *retval = NULL;
|
||||
struct usb_interface_assoc_descriptor *intf_assoc;
|
||||
int first_intf;
|
||||
int last_intf;
|
||||
int i;
|
||||
|
||||
for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) {
|
||||
intf_assoc = config->intf_assoc[i];
|
||||
if (intf_assoc->bInterfaceCount == 0)
|
||||
continue;
|
||||
|
||||
first_intf = intf_assoc->bFirstInterface;
|
||||
last_intf = first_intf + (intf_assoc->bInterfaceCount - 1);
|
||||
if (inum >= first_intf && inum <= last_intf) {
|
||||
if (!retval)
|
||||
retval = intf_assoc;
|
||||
else
|
||||
dev_err(&dev->dev, "Interface #%d referenced"
|
||||
" by multiple IADs\n", inum);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* usb_set_configuration - Makes a particular device setting be current
|
||||
* @dev: the device whose configuration is being updated
|
||||
@ -1531,6 +1560,7 @@ free_interfaces:
|
||||
intfc = cp->intf_cache[i];
|
||||
intf->altsetting = intfc->altsetting;
|
||||
intf->num_altsetting = intfc->num_altsetting;
|
||||
intf->intf_assoc = find_iad(dev, cp, i);
|
||||
kref_get(&intfc->ref);
|
||||
|
||||
alt = usb_altnum_to_altsetting(intf, 0);
|
||||
|
@ -30,10 +30,28 @@
|
||||
static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* HP 5300/5370C scanner */
|
||||
{ USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 },
|
||||
/* Benq S2W 3300U */
|
||||
{ USB_DEVICE(0x04a5, 0x20b0), .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 1670 */
|
||||
{ USB_DEVICE(0x04b8, 0x011f), .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 },
|
||||
/* 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 },
|
||||
|
||||
/* 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 },
|
||||
|
||||
{ } /* terminating entry must be last */
|
||||
};
|
||||
|
@ -169,6 +169,73 @@ show_quirks(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
}
|
||||
static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
|
||||
|
||||
|
||||
#if defined(CONFIG_USB_PERSIST) || defined(CONFIG_USB_SUSPEND)
|
||||
static const char power_group[] = "power";
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PERSIST
|
||||
|
||||
static ssize_t
|
||||
show_persist(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", udev->persist_enabled);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
set_persist(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int value;
|
||||
|
||||
/* Hubs are always enabled for USB_PERSIST */
|
||||
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
|
||||
return -EPERM;
|
||||
|
||||
if (sscanf(buf, "%d", &value) != 1)
|
||||
return -EINVAL;
|
||||
usb_pm_lock(udev);
|
||||
udev->persist_enabled = !!value;
|
||||
usb_pm_unlock(udev);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(persist, S_IRUGO | S_IWUSR, show_persist, set_persist);
|
||||
|
||||
static int add_persist_attributes(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (is_usb_device(dev)) {
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
/* Hubs are automatically enabled for USB_PERSIST */
|
||||
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
|
||||
udev->persist_enabled = 1;
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_persist.attr,
|
||||
power_group);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void remove_persist_attributes(struct device *dev)
|
||||
{
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_persist.attr,
|
||||
power_group);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define add_persist_attributes(dev) 0
|
||||
#define remove_persist_attributes(dev) do {} while (0)
|
||||
|
||||
#endif /* CONFIG_USB_PERSIST */
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
static ssize_t
|
||||
@ -276,8 +343,6 @@ set_level(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
|
||||
|
||||
static char power_group[] = "power";
|
||||
|
||||
static int add_power_attributes(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
@ -311,6 +376,7 @@ static void remove_power_attributes(struct device *dev)
|
||||
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
|
||||
|
||||
/* Descriptor fields */
|
||||
#define usb_descriptor_attr_le16(field, format_string) \
|
||||
static ssize_t \
|
||||
@ -384,6 +450,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = add_persist_attributes(dev);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
retval = add_power_attributes(dev);
|
||||
if (retval)
|
||||
goto error;
|
||||
@ -421,9 +491,29 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
|
||||
device_remove_file(dev, &dev_attr_product);
|
||||
device_remove_file(dev, &dev_attr_serial);
|
||||
remove_power_attributes(dev);
|
||||
remove_persist_attributes(dev);
|
||||
sysfs_remove_group(&dev->kobj, &dev_attr_grp);
|
||||
}
|
||||
|
||||
/* Interface Accociation Descriptor fields */
|
||||
#define usb_intf_assoc_attr(field, format_string) \
|
||||
static ssize_t \
|
||||
show_iad_##field (struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct usb_interface *intf = to_usb_interface (dev); \
|
||||
\
|
||||
return sprintf (buf, format_string, \
|
||||
intf->intf_assoc->field); \
|
||||
} \
|
||||
static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL);
|
||||
|
||||
usb_intf_assoc_attr (bFirstInterface, "%02x\n")
|
||||
usb_intf_assoc_attr (bInterfaceCount, "%02d\n")
|
||||
usb_intf_assoc_attr (bFunctionClass, "%02x\n")
|
||||
usb_intf_assoc_attr (bFunctionSubClass, "%02x\n")
|
||||
usb_intf_assoc_attr (bFunctionProtocol, "%02x\n")
|
||||
|
||||
/* Interface fields */
|
||||
#define usb_intf_attr(field, format_string) \
|
||||
static ssize_t \
|
||||
@ -487,6 +577,18 @@ static ssize_t show_modalias(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
|
||||
|
||||
static struct attribute *intf_assoc_attrs[] = {
|
||||
&dev_attr_iad_bFirstInterface.attr,
|
||||
&dev_attr_iad_bInterfaceCount.attr,
|
||||
&dev_attr_iad_bFunctionClass.attr,
|
||||
&dev_attr_iad_bFunctionSubClass.attr,
|
||||
&dev_attr_iad_bFunctionProtocol.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group intf_assoc_attr_grp = {
|
||||
.attrs = intf_assoc_attrs,
|
||||
};
|
||||
|
||||
static struct attribute *intf_attrs[] = {
|
||||
&dev_attr_bInterfaceNumber.attr,
|
||||
&dev_attr_bAlternateSetting.attr,
|
||||
@ -538,6 +640,8 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf)
|
||||
alt->string = usb_cache_string(udev, alt->desc.iInterface);
|
||||
if (alt->string)
|
||||
retval = device_create_file(dev, &dev_attr_interface);
|
||||
if (intf->intf_assoc)
|
||||
retval = sysfs_create_group(&dev->kobj, &intf_assoc_attr_grp);
|
||||
usb_create_intf_ep_files(intf, udev);
|
||||
return 0;
|
||||
}
|
||||
@ -549,4 +653,5 @@ void usb_remove_sysfs_intf_files(struct usb_interface *intf)
|
||||
usb_remove_intf_ep_files(intf);
|
||||
device_remove_file(dev, &dev_attr_interface);
|
||||
sysfs_remove_group(&dev->kobj, &intf_attr_grp);
|
||||
sysfs_remove_group(&intf->dev.kobj, &intf_assoc_attr_grp);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
#include "hcd.h"
|
||||
|
||||
#define to_urb(d) container_of(d, struct urb, kref)
|
||||
@ -11,6 +12,10 @@
|
||||
static void urb_destroy(struct kref *kref)
|
||||
{
|
||||
struct urb *urb = to_urb(kref);
|
||||
|
||||
if (urb->transfer_flags & URB_FREE_BUFFER)
|
||||
kfree(urb->transfer_buffer);
|
||||
|
||||
kfree(urb);
|
||||
}
|
||||
|
||||
@ -34,6 +39,7 @@ void usb_init_urb(struct urb *urb)
|
||||
memset(urb, 0, sizeof(*urb));
|
||||
kref_init(&urb->kref);
|
||||
spin_lock_init(&urb->lock);
|
||||
INIT_LIST_HEAD(&urb->anchor_list);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,8 +106,60 @@ struct urb * usb_get_urb(struct urb *urb)
|
||||
kref_get(&urb->kref);
|
||||
return urb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* usb_anchor_urb - anchors an URB while it is processed
|
||||
* @urb: pointer to the urb to anchor
|
||||
* @anchor: pointer to the anchor
|
||||
*
|
||||
* This can be called to have access to URBs which are to be executed
|
||||
* without bothering to track them
|
||||
*/
|
||||
void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
usb_get_urb(urb);
|
||||
list_add_tail(&urb->anchor_list, &anchor->urb_list);
|
||||
urb->anchor = anchor;
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_anchor_urb);
|
||||
|
||||
/**
|
||||
* usb_unanchor_urb - unanchors an URB
|
||||
* @urb: pointer to the urb to anchor
|
||||
*
|
||||
* Call this to stop the system keeping track of this URB
|
||||
*/
|
||||
void usb_unanchor_urb(struct urb *urb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct usb_anchor *anchor;
|
||||
|
||||
if (!urb)
|
||||
return;
|
||||
|
||||
anchor = urb->anchor;
|
||||
if (!anchor)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
if (unlikely(anchor != urb->anchor)) {
|
||||
/* we've lost the race to another thread */
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
return;
|
||||
}
|
||||
urb->anchor = NULL;
|
||||
list_del(&urb->anchor_list);
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
usb_put_urb(urb);
|
||||
if (list_empty(&anchor->urb_list))
|
||||
wake_up(&anchor->wait);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unanchor_urb);
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@ -478,6 +536,48 @@ void usb_kill_urb(struct urb *urb)
|
||||
spin_unlock_irq(&urb->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_kill_anchored_urbs - cancel transfer requests en masse
|
||||
* @anchor: anchor the requests are bound to
|
||||
*
|
||||
* this allows all outstanding URBs to be killed starting
|
||||
* from the back of the queue
|
||||
*/
|
||||
void usb_kill_anchored_urbs(struct usb_anchor *anchor)
|
||||
{
|
||||
struct urb *victim;
|
||||
|
||||
spin_lock_irq(&anchor->lock);
|
||||
while (!list_empty(&anchor->urb_list)) {
|
||||
victim = list_entry(anchor->urb_list.prev, struct urb, anchor_list);
|
||||
/* we must make sure the URB isn't freed before we kill it*/
|
||||
usb_get_urb(victim);
|
||||
spin_unlock_irq(&anchor->lock);
|
||||
/* this will unanchor the URB */
|
||||
usb_kill_urb(victim);
|
||||
usb_put_urb(victim);
|
||||
spin_lock_irq(&anchor->lock);
|
||||
}
|
||||
spin_unlock_irq(&anchor->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs);
|
||||
|
||||
/**
|
||||
* usb_wait_anchor_empty_timeout - wait for an anchor to be unused
|
||||
* @anchor: the anchor you want to become unused
|
||||
* @timeout: how long you are willing to wait in milliseconds
|
||||
*
|
||||
* Call this is you want to be sure all an anchor's
|
||||
* URBs have finished
|
||||
*/
|
||||
int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
|
||||
unsigned int timeout)
|
||||
{
|
||||
return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
|
||||
msecs_to_jiffies(timeout));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
|
||||
|
||||
EXPORT_SYMBOL(usb_init_urb);
|
||||
EXPORT_SYMBOL(usb_alloc_urb);
|
||||
EXPORT_SYMBOL(usb_free_urb);
|
||||
@ -485,4 +585,3 @@ EXPORT_SYMBOL(usb_get_urb);
|
||||
EXPORT_SYMBOL(usb_submit_urb);
|
||||
EXPORT_SYMBOL(usb_unlink_urb);
|
||||
EXPORT_SYMBOL(usb_kill_urb);
|
||||
|
||||
|
@ -253,6 +253,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
|
||||
dev->dev.bus = &usb_bus_type;
|
||||
dev->dev.type = &usb_device_type;
|
||||
dev->dev.dma_mask = bus->controller->dma_mask;
|
||||
set_dev_node(&dev->dev, dev_to_node(bus->controller));
|
||||
dev->state = USB_STATE_ATTACHED;
|
||||
|
||||
INIT_LIST_HEAD(&dev->ep0.urb_list);
|
||||
@ -578,11 +579,12 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size,
|
||||
* address (through the pointer provided).
|
||||
*
|
||||
* These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags
|
||||
* to avoid behaviors like using "DMA bounce buffers", or tying down I/O
|
||||
* mapping hardware for long idle periods. The implementation varies between
|
||||
* to avoid behaviors like using "DMA bounce buffers", or thrashing IOMMU
|
||||
* hardware during URB completion/resubmit. The implementation varies between
|
||||
* platforms, depending on details of how DMA will work to this device.
|
||||
* Using these buffers also helps prevent cacheline sharing problems on
|
||||
* architectures where CPU caches are not DMA-coherent.
|
||||
* Using these buffers also eliminates cacheline sharing problems on
|
||||
* architectures where CPU caches are not DMA-coherent. On systems without
|
||||
* bus-snooping caches, these buffers are uncached.
|
||||
*
|
||||
* When the buffer is no longer used, free it with usb_buffer_free().
|
||||
*/
|
||||
@ -607,7 +609,7 @@ void *usb_buffer_alloc(
|
||||
*
|
||||
* This reclaims an I/O buffer, letting it be reused. The memory must have
|
||||
* been allocated using usb_buffer_alloc(), and the parameters must match
|
||||
* those provided in that allocation request.
|
||||
* those provided in that allocation request.
|
||||
*/
|
||||
void usb_buffer_free(
|
||||
struct usb_device *dev,
|
||||
|
@ -52,8 +52,16 @@ static inline void usb_pm_unlock(struct usb_device *udev)
|
||||
|
||||
#else
|
||||
|
||||
#define usb_port_suspend(dev) 0
|
||||
#define usb_port_resume(dev) 0
|
||||
static inline int usb_port_suspend(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int usb_port_resume(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void usb_pm_lock(struct usb_device *udev) {}
|
||||
static inline void usb_pm_unlock(struct usb_device *udev) {}
|
||||
|
||||
@ -100,11 +108,13 @@ static inline int is_usb_device_driver(struct device_driver *drv)
|
||||
static inline void mark_active(struct usb_interface *f)
|
||||
{
|
||||
f->is_active = 1;
|
||||
f->dev.power.power_state.event = PM_EVENT_ON;
|
||||
}
|
||||
|
||||
static inline void mark_quiesced(struct usb_interface *f)
|
||||
{
|
||||
f->is_active = 0;
|
||||
f->dev.power.power_state.event = PM_EVENT_SUSPEND;
|
||||
}
|
||||
|
||||
static inline int is_active(const struct usb_interface *f)
|
||||
|
@ -42,6 +42,20 @@ config USB_GADGET
|
||||
For more information, see <http://www.linux-usb.org/gadget> and
|
||||
the kernel DocBook documentation for this API.
|
||||
|
||||
config USB_GADGET_DEBUG
|
||||
boolean "Debugging messages"
|
||||
depends on USB_GADGET && DEBUG_KERNEL && EXPERIMENTAL
|
||||
help
|
||||
Many controller and gadget drivers will print some debugging
|
||||
messages if you use this option to ask for those messages.
|
||||
|
||||
Avoid enabling these messages, even if you're actively
|
||||
debugging such a driver. Many drivers will emit so many
|
||||
messages that the driver timings are affected, which will
|
||||
either create new failure modes or remove the one you're
|
||||
trying to track down. Never enable these messages for a
|
||||
production build.
|
||||
|
||||
config USB_GADGET_DEBUG_FILES
|
||||
boolean "Debugging information files"
|
||||
depends on USB_GADGET && PROC_FS
|
||||
@ -208,6 +222,27 @@ config USB_OTG
|
||||
|
||||
Select this only if your OMAP board has a Mini-AB connector.
|
||||
|
||||
config USB_GADGET_S3C2410
|
||||
boolean "S3C2410 USB Device Controller"
|
||||
depends on ARCH_S3C2410
|
||||
help
|
||||
Samsung's S3C2410 is an ARM-4 processor with an integrated
|
||||
full speed USB 1.1 device controller. It has 4 configurable
|
||||
endpoints, as well as endpoint zero (for control transfers).
|
||||
|
||||
This driver has been tested on the S3C2410, S3C2412, and
|
||||
S3C2440 processors.
|
||||
|
||||
config USB_S3C2410
|
||||
tristate
|
||||
depends on USB_GADGET_S3C2410
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_S3C2410_DEBUG
|
||||
boolean "S3C2410 udc debug messages"
|
||||
depends on USB_GADGET_S3C2410
|
||||
|
||||
config USB_GADGET_AT91
|
||||
boolean "AT91 USB Device Port"
|
||||
depends on ARCH_AT91 && !ARCH_AT91SAM9RL
|
||||
@ -226,6 +261,24 @@ config USB_AT91
|
||||
depends on USB_GADGET_AT91
|
||||
default USB_GADGET
|
||||
|
||||
config USB_GADGET_M66592
|
||||
boolean "M66592 driver"
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
M66592 is a USB 2.0 peripheral controller.
|
||||
|
||||
It has seven configurable endpoints, and endpoint zero.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "m66592_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_M66592
|
||||
tristate
|
||||
depends on USB_GADGET_M66592
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_DUMMY_HCD
|
||||
boolean "Dummy HCD (DEVELOPMENT)"
|
||||
depends on (USB=y || (USB=m && USB_GADGET=m)) && EXPERIMENTAL
|
||||
|
@ -1,14 +1,20 @@
|
||||
#
|
||||
# USB peripheral controller drivers
|
||||
#
|
||||
ifeq ($(CONFIG_USB_GADGET_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
|
||||
obj-$(CONFIG_USB_NET2280) += net2280.o
|
||||
obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o
|
||||
obj-$(CONFIG_USB_GOKU) += goku_udc.o
|
||||
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_FSL_USB2) += fsl_usb2_udc.o
|
||||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
|
||||
#
|
||||
# USB gadget drivers
|
||||
|
@ -601,25 +601,6 @@ static void at91_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
static void *at91_ep_alloc_buffer(
|
||||
struct usb_ep *_ep,
|
||||
unsigned bytes,
|
||||
dma_addr_t *dma,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
*dma = ~0;
|
||||
return kmalloc(bytes, gfp_flags);
|
||||
}
|
||||
|
||||
static void at91_ep_free_buffer(
|
||||
struct usb_ep *ep,
|
||||
void *buf,
|
||||
dma_addr_t dma,
|
||||
unsigned bytes)
|
||||
{
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static int at91_ep_queue(struct usb_ep *_ep,
|
||||
struct usb_request *_req, gfp_t gfp_flags)
|
||||
{
|
||||
@ -788,8 +769,6 @@ static const struct usb_ep_ops at91_ep_ops = {
|
||||
.disable = at91_ep_disable,
|
||||
.alloc_request = at91_ep_alloc_request,
|
||||
.free_request = at91_ep_free_request,
|
||||
.alloc_buffer = at91_ep_alloc_buffer,
|
||||
.free_buffer = at91_ep_free_buffer,
|
||||
.queue = at91_ep_queue,
|
||||
.dequeue = at91_ep_dequeue,
|
||||
.set_halt = at91_ep_set_halt,
|
||||
|
@ -497,38 +497,6 @@ dummy_free_request (struct usb_ep *_ep, struct usb_request *_req)
|
||||
kfree (req);
|
||||
}
|
||||
|
||||
static void *
|
||||
dummy_alloc_buffer (
|
||||
struct usb_ep *_ep,
|
||||
unsigned bytes,
|
||||
dma_addr_t *dma,
|
||||
gfp_t mem_flags
|
||||
) {
|
||||
char *retval;
|
||||
struct dummy_ep *ep;
|
||||
struct dummy *dum;
|
||||
|
||||
ep = usb_ep_to_dummy_ep (_ep);
|
||||
dum = ep_to_dummy (ep);
|
||||
|
||||
if (!dum->driver)
|
||||
return NULL;
|
||||
retval = kmalloc (bytes, mem_flags);
|
||||
*dma = (dma_addr_t) retval;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
dummy_free_buffer (
|
||||
struct usb_ep *_ep,
|
||||
void *buf,
|
||||
dma_addr_t dma,
|
||||
unsigned bytes
|
||||
) {
|
||||
if (bytes)
|
||||
kfree (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
fifo_complete (struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
@ -659,10 +627,6 @@ static const struct usb_ep_ops dummy_ep_ops = {
|
||||
.alloc_request = dummy_alloc_request,
|
||||
.free_request = dummy_free_request,
|
||||
|
||||
.alloc_buffer = dummy_alloc_buffer,
|
||||
.free_buffer = dummy_free_buffer,
|
||||
/* map, unmap, ... eventually hook the "generic" dma calls */
|
||||
|
||||
.queue = dummy_queue,
|
||||
.dequeue = dummy_dequeue,
|
||||
|
||||
@ -1784,8 +1748,7 @@ static int dummy_bus_resume (struct usb_hcd *hcd)
|
||||
|
||||
spin_lock_irq (&dum->lock);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
dev_warn (&hcd->self.root_hub->dev, "HC isn't running!\n");
|
||||
rc = -ENODEV;
|
||||
rc = -ESHUTDOWN;
|
||||
} else {
|
||||
dum->rh_state = DUMMY_RH_RUNNING;
|
||||
set_link_state (dum);
|
||||
|
@ -277,7 +277,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
|
||||
#define DEV_CONFIG_CDC
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_HUSB2DEV
|
||||
#ifdef CONFIG_USB_GADGET_ATMEL_USBA
|
||||
#define DEV_CONFIG_CDC
|
||||
#endif
|
||||
|
||||
@ -292,7 +292,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
|
||||
#define DEV_CONFIG_SUBSET
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_SH
|
||||
#ifdef CONFIG_USB_GADGET_SUPERH
|
||||
#define DEV_CONFIG_SUBSET
|
||||
#endif
|
||||
|
||||
@ -301,6 +301,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
|
||||
#define DEV_CONFIG_SUBSET
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_M66592
|
||||
#define DEV_CONFIG_CDC
|
||||
#endif
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -3733,19 +3733,12 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
|
||||
}
|
||||
|
||||
/* Free the data buffers */
|
||||
for (i = 0; i < NUM_BUFFERS; ++i) {
|
||||
struct fsg_buffhd *bh = &fsg->buffhds[i];
|
||||
|
||||
if (bh->buf)
|
||||
usb_ep_free_buffer(fsg->bulk_in, bh->buf, bh->dma,
|
||||
mod_data.buflen);
|
||||
}
|
||||
for (i = 0; i < NUM_BUFFERS; ++i)
|
||||
kfree(fsg->buffhds[i].buf);
|
||||
|
||||
/* Free the request and buffer for endpoint 0 */
|
||||
if (req) {
|
||||
if (req->buf)
|
||||
usb_ep_free_buffer(fsg->ep0, req->buf,
|
||||
req->dma, EP0_BUFSIZE);
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(fsg->ep0, req);
|
||||
}
|
||||
|
||||
@ -3963,8 +3956,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
#endif
|
||||
|
||||
if (gadget->is_otg) {
|
||||
otg_desc.bmAttributes |= USB_OTG_HNP,
|
||||
config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
otg_desc.bmAttributes |= USB_OTG_HNP;
|
||||
}
|
||||
|
||||
rc = -ENOMEM;
|
||||
@ -3973,8 +3965,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL);
|
||||
if (!req)
|
||||
goto out;
|
||||
req->buf = usb_ep_alloc_buffer(fsg->ep0, EP0_BUFSIZE,
|
||||
&req->dma, GFP_KERNEL);
|
||||
req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL);
|
||||
if (!req->buf)
|
||||
goto out;
|
||||
req->complete = ep0_complete;
|
||||
@ -3986,8 +3977,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
/* Allocate for the bulk-in endpoint. We assume that
|
||||
* the buffer will also work with the bulk-out (and
|
||||
* interrupt-in) endpoint. */
|
||||
bh->buf = usb_ep_alloc_buffer(fsg->bulk_in, mod_data.buflen,
|
||||
&bh->dma, GFP_KERNEL);
|
||||
bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL);
|
||||
if (!bh->buf)
|
||||
goto out;
|
||||
bh->next = bh + 1;
|
||||
|
@ -228,7 +228,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
|
||||
|
||||
/* Config PHY interface */
|
||||
portctrl = fsl_readl(&dr_regs->portsc1);
|
||||
portctrl &= ~(PORTSCX_PHY_TYPE_SEL & PORTSCX_PORT_WIDTH);
|
||||
portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
|
||||
switch (udc->phy_mode) {
|
||||
case FSL_USB2_PHY_ULPI:
|
||||
portctrl |= PORTSCX_PTS_ULPI;
|
||||
@ -601,39 +601,6 @@ static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
* Allocate an I/O buffer
|
||||
*---------------------------------------------------------------------*/
|
||||
static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
|
||||
dma_addr_t *dma, gfp_t gfp_flags)
|
||||
{
|
||||
struct fsl_ep *ep;
|
||||
|
||||
if (!_ep)
|
||||
return NULL;
|
||||
|
||||
ep = container_of(_ep, struct fsl_ep, ep);
|
||||
|
||||
return dma_alloc_coherent(ep->udc->gadget.dev.parent,
|
||||
bytes, dma, gfp_flags);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
* frees an i/o buffer
|
||||
*---------------------------------------------------------------------*/
|
||||
static void fsl_free_buffer(struct usb_ep *_ep, void *buf,
|
||||
dma_addr_t dma, unsigned bytes)
|
||||
{
|
||||
struct fsl_ep *ep;
|
||||
|
||||
if (!_ep)
|
||||
return;
|
||||
|
||||
ep = container_of(_ep, struct fsl_ep, ep);
|
||||
|
||||
dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
|
||||
{
|
||||
@ -1047,9 +1014,6 @@ static struct usb_ep_ops fsl_ep_ops = {
|
||||
.alloc_request = fsl_alloc_request,
|
||||
.free_request = fsl_free_request,
|
||||
|
||||
.alloc_buffer = fsl_alloc_buffer,
|
||||
.free_buffer = fsl_free_buffer,
|
||||
|
||||
.queue = fsl_ep_queue,
|
||||
.dequeue = fsl_ep_dequeue,
|
||||
|
||||
@ -2189,27 +2153,19 @@ static void fsl_udc_release(struct device *dev)
|
||||
* init resource for globle controller
|
||||
* Return the udc handle on success or NULL on failure
|
||||
------------------------------------------------------------------*/
|
||||
static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev)
|
||||
static int __init struct_udc_setup(struct fsl_udc *udc,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_udc *udc;
|
||||
struct fsl_usb2_platform_data *pdata;
|
||||
size_t size;
|
||||
|
||||
udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
|
||||
if (udc == NULL) {
|
||||
ERR("malloc udc failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
udc->phy_mode = pdata->phy_mode;
|
||||
/* max_ep_nr is bidirectional ep number, max_ep doubles the number */
|
||||
udc->max_ep = pdata->max_ep_nr * 2;
|
||||
|
||||
udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL);
|
||||
if (!udc->eps) {
|
||||
ERR("malloc fsl_ep failed\n");
|
||||
goto cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* initialized QHs, take care of alignment */
|
||||
@ -2225,7 +2181,7 @@ static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev)
|
||||
if (!udc->ep_qh) {
|
||||
ERR("malloc QHs for udc failed\n");
|
||||
kfree(udc->eps);
|
||||
goto cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
udc->ep_qh_size = size;
|
||||
@ -2244,11 +2200,7 @@ static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev)
|
||||
udc->remote_wakeup = 0; /* default to 0 on reset */
|
||||
spin_lock_init(&udc->lock);
|
||||
|
||||
return udc;
|
||||
|
||||
cleanup:
|
||||
kfree(udc);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------
|
||||
@ -2287,35 +2239,37 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
|
||||
}
|
||||
|
||||
/* Driver probe function
|
||||
* all intialize operations implemented here except enabling usb_intr reg
|
||||
* all intialization operations implemented here except enabling usb_intr reg
|
||||
* board setup should have been done in the platform code
|
||||
*/
|
||||
static int __init fsl_udc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
int ret = -ENODEV;
|
||||
unsigned int i;
|
||||
u32 dccparams;
|
||||
|
||||
if (strcmp(pdev->name, driver_name)) {
|
||||
VDBG("Wrong device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* board setup should have been done in the platform code */
|
||||
|
||||
/* Initialize the udc structure including QH member and other member */
|
||||
udc_controller = struct_udc_setup(pdev);
|
||||
if (!udc_controller) {
|
||||
VDBG("udc_controller is NULL \n");
|
||||
udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
|
||||
if (udc_controller == NULL) {
|
||||
ERR("malloc udc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
if (!res) {
|
||||
kfree(udc_controller);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (!request_mem_region(res->start, res->end - res->start + 1,
|
||||
driver_name)) {
|
||||
ERR("request mem region for %s failed \n", pdev->name);
|
||||
kfree(udc_controller);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
@ -2328,13 +2282,24 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
||||
usb_sys_regs = (struct usb_sys_interface *)
|
||||
((u32)dr_regs + USB_DR_SYS_OFFSET);
|
||||
|
||||
/* Read Device Controller Capability Parameters register */
|
||||
dccparams = fsl_readl(&dr_regs->dccparams);
|
||||
if (!(dccparams & DCCPARAMS_DC)) {
|
||||
ERR("This SOC doesn't support device role\n");
|
||||
ret = -ENODEV;
|
||||
goto err2;
|
||||
}
|
||||
/* Get max device endpoints */
|
||||
/* DEN is bidirectional ep number, max_ep doubles the number */
|
||||
udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
|
||||
|
||||
udc_controller->irq = platform_get_irq(pdev, 0);
|
||||
if (!udc_controller->irq) {
|
||||
ret = -ENODEV;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ,
|
||||
ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
|
||||
driver_name, udc_controller);
|
||||
if (ret != 0) {
|
||||
ERR("cannot request irq %d err %d \n",
|
||||
@ -2342,6 +2307,13 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
||||
goto err2;
|
||||
}
|
||||
|
||||
/* Initialize the udc structure including QH member and other member */
|
||||
if (struct_udc_setup(udc_controller, pdev)) {
|
||||
ERR("Can't initialize udc data structure\n");
|
||||
ret = -ENOMEM;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
/* initialize usb hw reg except for regs for EP,
|
||||
* leave usbintr reg untouched */
|
||||
dr_controller_setup(udc_controller);
|
||||
@ -2403,6 +2375,7 @@ err2:
|
||||
iounmap(dr_regs);
|
||||
err1:
|
||||
release_mem_region(res->start, res->end - res->start + 1);
|
||||
kfree(udc_controller);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -101,6 +101,10 @@ struct usb_sys_interface {
|
||||
#define WAIT_FOR_OUT_STATUS 3
|
||||
#define DATA_STATE_RECV 4
|
||||
|
||||
/* Device Controller Capability Parameter register */
|
||||
#define DCCPARAMS_DC 0x00000080
|
||||
#define DCCPARAMS_DEN_MASK 0x0000001f
|
||||
|
||||
/* Frame Index Register Bit Masks */
|
||||
#define USB_FRINDEX_MASKS 0x3fff
|
||||
/* USB CMD Register Bit Masks */
|
||||
|
@ -8,6 +8,8 @@
|
||||
* (And avoiding all runtime comparisons in typical one-choice configs!)
|
||||
*
|
||||
* NOTE: some of these controller drivers may not be available yet.
|
||||
* Some are available on 2.4 kernels; several are available, but not
|
||||
* yet pushed in the 2.6 mainline tree.
|
||||
*/
|
||||
#ifdef CONFIG_USB_GADGET_NET2280
|
||||
#define gadget_is_net2280(g) !strcmp("net2280", (g)->name)
|
||||
@ -33,12 +35,14 @@
|
||||
#define gadget_is_goku(g) 0
|
||||
#endif
|
||||
|
||||
/* SH3 UDC -- not yet ported 2.4 --> 2.6 */
|
||||
#ifdef CONFIG_USB_GADGET_SUPERH
|
||||
#define gadget_is_sh(g) !strcmp("sh_udc", (g)->name)
|
||||
#else
|
||||
#define gadget_is_sh(g) 0
|
||||
#endif
|
||||
|
||||
/* not yet stable on 2.6 (would help "original Zaurus") */
|
||||
#ifdef CONFIG_USB_GADGET_SA1100
|
||||
#define gadget_is_sa1100(g) !strcmp("sa1100_udc", (g)->name)
|
||||
#else
|
||||
@ -51,6 +55,7 @@
|
||||
#define gadget_is_lh7a40x(g) 0
|
||||
#endif
|
||||
|
||||
/* handhelds.org tree (?) */
|
||||
#ifdef CONFIG_USB_GADGET_MQ11XX
|
||||
#define gadget_is_mq11xx(g) !strcmp("mq11xx_udc", (g)->name)
|
||||
#else
|
||||
@ -63,22 +68,24 @@
|
||||
#define gadget_is_omap(g) 0
|
||||
#endif
|
||||
|
||||
/* not yet ported 2.4 --> 2.6 */
|
||||
#ifdef CONFIG_USB_GADGET_N9604
|
||||
#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name)
|
||||
#else
|
||||
#define gadget_is_n9604(g) 0
|
||||
#endif
|
||||
|
||||
/* various unstable versions available */
|
||||
#ifdef CONFIG_USB_GADGET_PXA27X
|
||||
#define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name)
|
||||
#else
|
||||
#define gadget_is_pxa27x(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_HUSB2DEV
|
||||
#define gadget_is_husb2dev(g) !strcmp("husb2_udc", (g)->name)
|
||||
#ifdef CONFIG_USB_GADGET_ATMEL_USBA
|
||||
#define gadget_is_atmel_usba(g) !strcmp("atmel_usba_udc", (g)->name)
|
||||
#else
|
||||
#define gadget_is_husb2dev(g) 0
|
||||
#define gadget_is_atmel_usba(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_S3C2410
|
||||
@ -93,6 +100,7 @@
|
||||
#define gadget_is_at91(g) 0
|
||||
#endif
|
||||
|
||||
/* status unclear */
|
||||
#ifdef CONFIG_USB_GADGET_IMX
|
||||
#define gadget_is_imx(g) !strcmp("imx_udc", (g)->name)
|
||||
#else
|
||||
@ -106,6 +114,7 @@
|
||||
#endif
|
||||
|
||||
/* Mentor high speed function controller */
|
||||
/* from Montavista kernel (?) */
|
||||
#ifdef CONFIG_USB_GADGET_MUSBHSFC
|
||||
#define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name)
|
||||
#else
|
||||
@ -119,12 +128,20 @@
|
||||
#define gadget_is_musbhdrc(g) 0
|
||||
#endif
|
||||
|
||||
/* from Montavista kernel (?) */
|
||||
#ifdef CONFIG_USB_GADGET_MPC8272
|
||||
#define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name)
|
||||
#else
|
||||
#define gadget_is_mpc8272(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_M66592
|
||||
#define gadget_is_m66592(g) !strcmp("m66592_udc", (g)->name)
|
||||
#else
|
||||
#define gadget_is_m66592(g) 0
|
||||
#endif
|
||||
|
||||
|
||||
// CONFIG_USB_GADGET_SX2
|
||||
// CONFIG_USB_GADGET_AU1X00
|
||||
// ...
|
||||
@ -181,9 +198,11 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
||||
return 0x16;
|
||||
else if (gadget_is_mpc8272(gadget))
|
||||
return 0x17;
|
||||
else if (gadget_is_husb2dev(gadget))
|
||||
else if (gadget_is_atmel_usba(gadget))
|
||||
return 0x18;
|
||||
else if (gadget_is_fsl_usb2(gadget))
|
||||
return 0x19;
|
||||
else if (gadget_is_m66592(gadget))
|
||||
return 0x20;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
@ -1248,17 +1248,11 @@ autoconf_fail:
|
||||
tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev);
|
||||
|
||||
/* preallocate control response and buffer */
|
||||
dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
|
||||
dev->req = alloc_ep_req(gadget->ep0, USB_BUFSIZ);
|
||||
if (!dev->req) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
dev->req->buf = usb_ep_alloc_buffer(gadget->ep0, USB_BUFSIZ,
|
||||
&dev->req->dma, GFP_KERNEL);
|
||||
if (!dev->req->buf) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dev->req->complete = gmidi_setup_complete;
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
* - DMA works with ep1 (OUT transfers) and ep2 (IN transfers).
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
// #define VERBOSE /* extra debug messages (success too) */
|
||||
// #define USB_TRACE /* packet-level success messages */
|
||||
|
||||
@ -296,51 +295,6 @@ goku_free_request(struct usb_ep *_ep, struct usb_request *_req)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* allocating buffers this way eliminates dma mapping overhead, which
|
||||
* on some platforms will mean eliminating a per-io buffer copy. with
|
||||
* some kinds of system caches, further tweaks may still be needed.
|
||||
*/
|
||||
static void *
|
||||
goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
|
||||
dma_addr_t *dma, gfp_t gfp_flags)
|
||||
{
|
||||
void *retval;
|
||||
struct goku_ep *ep;
|
||||
|
||||
ep = container_of(_ep, struct goku_ep, ep);
|
||||
if (!_ep)
|
||||
return NULL;
|
||||
*dma = DMA_ADDR_INVALID;
|
||||
|
||||
if (ep->dma) {
|
||||
/* the main problem with this call is that it wastes memory
|
||||
* on typical 1/N page allocations: it allocates 1-N pages.
|
||||
*/
|
||||
#warning Using dma_alloc_coherent even with buffers smaller than a page.
|
||||
retval = dma_alloc_coherent(&ep->dev->pdev->dev,
|
||||
bytes, dma, gfp_flags);
|
||||
} else
|
||||
retval = kmalloc(bytes, gfp_flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes)
|
||||
{
|
||||
/* free memory into the right allocator */
|
||||
if (dma != DMA_ADDR_INVALID) {
|
||||
struct goku_ep *ep;
|
||||
|
||||
ep = container_of(_ep, struct goku_ep, ep);
|
||||
if (!_ep)
|
||||
return;
|
||||
dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma);
|
||||
} else
|
||||
kfree (buf);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void
|
||||
done(struct goku_ep *ep, struct goku_request *req, int status)
|
||||
{
|
||||
@ -485,7 +439,7 @@ top:
|
||||
/* use ep1/ep2 double-buffering for OUT */
|
||||
if (!(size & PACKET_ACTIVE))
|
||||
size = readl(®s->EPxSizeLB[ep->num]);
|
||||
if (!(size & PACKET_ACTIVE)) // "can't happen"
|
||||
if (!(size & PACKET_ACTIVE)) /* "can't happen" */
|
||||
break;
|
||||
size &= DATASIZE; /* EPxSizeH == 0 */
|
||||
|
||||
@ -1026,9 +980,6 @@ static struct usb_ep_ops goku_ep_ops = {
|
||||
.alloc_request = goku_alloc_request,
|
||||
.free_request = goku_free_request,
|
||||
|
||||
.alloc_buffer = goku_alloc_buffer,
|
||||
.free_buffer = goku_free_buffer,
|
||||
|
||||
.queue = goku_queue,
|
||||
.dequeue = goku_dequeue,
|
||||
|
||||
@ -1140,17 +1091,17 @@ udc_proc_read(char *buffer, char **start, off_t off, int count,
|
||||
is_usb_connected
|
||||
? ((tmp & PW_PULLUP) ? "full speed" : "powered")
|
||||
: "disconnected",
|
||||
({char *tmp;
|
||||
({char *state;
|
||||
switch(dev->ep0state){
|
||||
case EP0_DISCONNECT: tmp = "ep0_disconnect"; break;
|
||||
case EP0_IDLE: tmp = "ep0_idle"; break;
|
||||
case EP0_IN: tmp = "ep0_in"; break;
|
||||
case EP0_OUT: tmp = "ep0_out"; break;
|
||||
case EP0_STATUS: tmp = "ep0_status"; break;
|
||||
case EP0_STALL: tmp = "ep0_stall"; break;
|
||||
case EP0_SUSPEND: tmp = "ep0_suspend"; break;
|
||||
default: tmp = "ep0_?"; break;
|
||||
} tmp; })
|
||||
case EP0_DISCONNECT: state = "ep0_disconnect"; break;
|
||||
case EP0_IDLE: state = "ep0_idle"; break;
|
||||
case EP0_IN: state = "ep0_in"; break;
|
||||
case EP0_OUT: state = "ep0_out"; break;
|
||||
case EP0_STATUS: state = "ep0_status"; break;
|
||||
case EP0_STALL: state = "ep0_stall"; break;
|
||||
case EP0_SUSPEND: state = "ep0_suspend"; break;
|
||||
default: state = "ep0_?"; break;
|
||||
} state; })
|
||||
);
|
||||
size -= t;
|
||||
next += t;
|
||||
@ -1195,7 +1146,6 @@ udc_proc_read(char *buffer, char **start, off_t off, int count,
|
||||
for (i = 0; i < 4; i++) {
|
||||
struct goku_ep *ep = &dev->ep [i];
|
||||
struct goku_request *req;
|
||||
int t;
|
||||
|
||||
if (i && !ep->desc)
|
||||
continue;
|
||||
@ -1283,7 +1233,7 @@ done:
|
||||
static void udc_reinit (struct goku_udc *dev)
|
||||
{
|
||||
static char *names [] = { "ep0", "ep1-bulk", "ep2-bulk", "ep3-bulk" };
|
||||
|
||||
|
||||
unsigned i;
|
||||
|
||||
INIT_LIST_HEAD (&dev->gadget.ep_list);
|
||||
@ -1896,9 +1846,9 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
|
||||
/* done */
|
||||
the_controller = dev;
|
||||
device_register(&dev->gadget.dev);
|
||||
|
||||
return 0;
|
||||
retval = device_register(&dev->gadget.dev);
|
||||
if (retval == 0)
|
||||
return 0;
|
||||
|
||||
done:
|
||||
if (dev)
|
||||
@ -1910,8 +1860,8 @@ done:
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct pci_device_id pci_ids [] = { {
|
||||
.class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
|
||||
.class_mask = ~0,
|
||||
.class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
|
||||
.class_mask = ~0,
|
||||
.vendor = 0x102f, /* Toshiba */
|
||||
.device = 0x0107, /* this UDC */
|
||||
.subvendor = PCI_ANY_ID,
|
||||
|
@ -41,8 +41,10 @@ struct goku_udc_regs {
|
||||
#define INT_SYSERROR 0x40000
|
||||
#define INT_PWRDETECT 0x80000
|
||||
|
||||
#define INT_DEVWIDE (INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND)
|
||||
#define INT_EP0 (INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK)
|
||||
#define INT_DEVWIDE \
|
||||
(INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND)
|
||||
#define INT_EP0 \
|
||||
(INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK)
|
||||
|
||||
u32 dma_master;
|
||||
#define MST_EOPB_DIS 0x0800
|
||||
@ -231,7 +233,7 @@ struct goku_request {
|
||||
enum ep0state {
|
||||
EP0_DISCONNECT, /* no host */
|
||||
EP0_IDLE, /* between STATUS ack and SETUP report */
|
||||
EP0_IN, EP0_OUT, /* data stage */
|
||||
EP0_IN, EP0_OUT, /* data stage */
|
||||
EP0_STATUS, /* status stage */
|
||||
EP0_STALL, /* data or status stages */
|
||||
EP0_SUSPEND, /* usb suspend */
|
||||
@ -242,7 +244,7 @@ struct goku_udc {
|
||||
struct usb_gadget gadget;
|
||||
spinlock_t lock;
|
||||
struct goku_ep ep[4];
|
||||
struct usb_gadget_driver *driver;
|
||||
struct usb_gadget_driver *driver;
|
||||
|
||||
enum ep0state ep0state;
|
||||
unsigned got_irq:1,
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <linux/usb_gadgetfs.h>
|
||||
#include <linux/usb/gadgetfs.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
|
||||
|
||||
@ -923,7 +923,7 @@ static void clean_req (struct usb_ep *ep, struct usb_request *req)
|
||||
struct dev_data *dev = ep->driver_data;
|
||||
|
||||
if (req->buf != dev->rbuf) {
|
||||
usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
|
||||
kfree(req->buf);
|
||||
req->buf = dev->rbuf;
|
||||
req->dma = DMA_ADDR_INVALID;
|
||||
}
|
||||
@ -963,7 +963,7 @@ static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
|
||||
return -EBUSY;
|
||||
}
|
||||
if (len > sizeof (dev->rbuf))
|
||||
req->buf = usb_ep_alloc_buffer (ep, len, &req->dma, GFP_ATOMIC);
|
||||
req->buf = kmalloc(len, GFP_ATOMIC);
|
||||
if (req->buf == 0) {
|
||||
req->buf = dev->rbuf;
|
||||
return -ENOMEM;
|
||||
@ -1505,7 +1505,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
}
|
||||
break;
|
||||
|
||||
#ifndef CONFIG_USB_GADGETFS_PXA2XX
|
||||
#ifndef CONFIG_USB_GADGET_PXA2XX
|
||||
/* PXA automagically handles this request too */
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
if (ctrl->bRequestType != 0x80)
|
||||
|
@ -75,10 +75,6 @@ static int lh7a40x_ep_enable(struct usb_ep *ep,
|
||||
static int lh7a40x_ep_disable(struct usb_ep *ep);
|
||||
static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, gfp_t);
|
||||
static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *);
|
||||
static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *,
|
||||
gfp_t);
|
||||
static void lh7a40x_free_buffer(struct usb_ep *ep, void *, dma_addr_t,
|
||||
unsigned);
|
||||
static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, gfp_t);
|
||||
static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *);
|
||||
static int lh7a40x_set_halt(struct usb_ep *ep, int);
|
||||
@ -104,9 +100,6 @@ static struct usb_ep_ops lh7a40x_ep_ops = {
|
||||
.alloc_request = lh7a40x_alloc_request,
|
||||
.free_request = lh7a40x_free_request,
|
||||
|
||||
.alloc_buffer = lh7a40x_alloc_buffer,
|
||||
.free_buffer = lh7a40x_free_buffer,
|
||||
|
||||
.queue = lh7a40x_queue,
|
||||
.dequeue = lh7a40x_dequeue,
|
||||
|
||||
@ -1134,26 +1127,6 @@ static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *_req)
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned bytes,
|
||||
dma_addr_t * dma, gfp_t gfp_flags)
|
||||
{
|
||||
char *retval;
|
||||
|
||||
DEBUG("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags);
|
||||
|
||||
retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM));
|
||||
if (retval)
|
||||
*dma = virt_to_bus(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void lh7a40x_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma,
|
||||
unsigned bytes)
|
||||
{
|
||||
DEBUG("%s, %p\n", __FUNCTION__, ep);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
/** Queue one request
|
||||
* Kickstart transfer if needed
|
||||
* NOTE: Sets INDEX register
|
||||
|
1653
drivers/usb/gadget/m66592-udc.c
Normal file
1653
drivers/usb/gadget/m66592-udc.c
Normal file
File diff suppressed because it is too large
Load Diff
577
drivers/usb/gadget/m66592-udc.h
Normal file
577
drivers/usb/gadget/m66592-udc.h
Normal file
@ -0,0 +1,577 @@
|
||||
/*
|
||||
* M66592 UDC (USB gadget)
|
||||
*
|
||||
* Copyright (C) 2006-2007 Renesas Solutions Corp.
|
||||
*
|
||||
* Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __M66592_UDC_H__
|
||||
#define __M66592_UDC_H__
|
||||
|
||||
#define M66592_SYSCFG 0x00
|
||||
#define M66592_XTAL 0xC000 /* b15-14: Crystal selection */
|
||||
#define M66592_XTAL48 0x8000 /* 48MHz */
|
||||
#define M66592_XTAL24 0x4000 /* 24MHz */
|
||||
#define M66592_XTAL12 0x0000 /* 12MHz */
|
||||
#define M66592_XCKE 0x2000 /* b13: External clock enable */
|
||||
#define M66592_RCKE 0x1000 /* b12: Register clock enable */
|
||||
#define M66592_PLLC 0x0800 /* b11: PLL control */
|
||||
#define M66592_SCKE 0x0400 /* b10: USB clock enable */
|
||||
#define M66592_ATCKM 0x0100 /* b8: Automatic supply functional enable */
|
||||
#define M66592_HSE 0x0080 /* b7: Hi-speed enable */
|
||||
#define M66592_DCFM 0x0040 /* b6: Controller function select */
|
||||
#define M66592_DMRPD 0x0020 /* b5: D- pull down control */
|
||||
#define M66592_DPRPU 0x0010 /* b4: D+ pull up control */
|
||||
#define M66592_FSRPC 0x0004 /* b2: Full-speed receiver enable */
|
||||
#define M66592_PCUT 0x0002 /* b1: Low power sleep enable */
|
||||
#define M66592_USBE 0x0001 /* b0: USB module operation enable */
|
||||
|
||||
#define M66592_SYSSTS 0x02
|
||||
#define M66592_LNST 0x0003 /* b1-0: D+, D- line status */
|
||||
#define M66592_SE1 0x0003 /* SE1 */
|
||||
#define M66592_KSTS 0x0002 /* K State */
|
||||
#define M66592_JSTS 0x0001 /* J State */
|
||||
#define M66592_SE0 0x0000 /* SE0 */
|
||||
|
||||
#define M66592_DVSTCTR 0x04
|
||||
#define M66592_WKUP 0x0100 /* b8: Remote wakeup */
|
||||
#define M66592_RWUPE 0x0080 /* b7: Remote wakeup sense */
|
||||
#define M66592_USBRST 0x0040 /* b6: USB reset enable */
|
||||
#define M66592_RESUME 0x0020 /* b5: Resume enable */
|
||||
#define M66592_UACT 0x0010 /* b4: USB bus enable */
|
||||
#define M66592_RHST 0x0003 /* b1-0: Reset handshake status */
|
||||
#define M66592_HSMODE 0x0003 /* Hi-Speed mode */
|
||||
#define M66592_FSMODE 0x0002 /* Full-Speed mode */
|
||||
#define M66592_HSPROC 0x0001 /* HS handshake is processing */
|
||||
|
||||
#define M66592_TESTMODE 0x06
|
||||
#define M66592_UTST 0x000F /* b4-0: Test select */
|
||||
#define M66592_H_TST_PACKET 0x000C /* HOST TEST Packet */
|
||||
#define M66592_H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */
|
||||
#define M66592_H_TST_K 0x000A /* HOST TEST K */
|
||||
#define M66592_H_TST_J 0x0009 /* HOST TEST J */
|
||||
#define M66592_H_TST_NORMAL 0x0000 /* HOST Normal Mode */
|
||||
#define M66592_P_TST_PACKET 0x0004 /* PERI TEST Packet */
|
||||
#define M66592_P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */
|
||||
#define M66592_P_TST_K 0x0002 /* PERI TEST K */
|
||||
#define M66592_P_TST_J 0x0001 /* PERI TEST J */
|
||||
#define M66592_P_TST_NORMAL 0x0000 /* PERI Normal Mode */
|
||||
|
||||
#define M66592_PINCFG 0x0A
|
||||
#define M66592_LDRV 0x8000 /* b15: Drive Current Adjust */
|
||||
#define M66592_BIGEND 0x0100 /* b8: Big endian mode */
|
||||
|
||||
#define M66592_DMA0CFG 0x0C
|
||||
#define M66592_DMA1CFG 0x0E
|
||||
#define M66592_DREQA 0x4000 /* b14: Dreq active select */
|
||||
#define M66592_BURST 0x2000 /* b13: Burst mode */
|
||||
#define M66592_DACKA 0x0400 /* b10: Dack active select */
|
||||
#define M66592_DFORM 0x0380 /* b9-7: DMA mode select */
|
||||
#define M66592_CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */
|
||||
#define M66592_CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */
|
||||
#define M66592_CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */
|
||||
#define M66592_SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */
|
||||
#define M66592_SPLIT_DACK_DSTB 0x0300 /* DACK + DSTB0 mode (SPLIT bus) */
|
||||
#define M66592_DENDA 0x0040 /* b6: Dend active select */
|
||||
#define M66592_PKTM 0x0020 /* b5: Packet mode */
|
||||
#define M66592_DENDE 0x0010 /* b4: Dend enable */
|
||||
#define M66592_OBUS 0x0004 /* b2: OUTbus mode */
|
||||
|
||||
#define M66592_CFIFO 0x10
|
||||
#define M66592_D0FIFO 0x14
|
||||
#define M66592_D1FIFO 0x18
|
||||
|
||||
#define M66592_CFIFOSEL 0x1E
|
||||
#define M66592_D0FIFOSEL 0x24
|
||||
#define M66592_D1FIFOSEL 0x2A
|
||||
#define M66592_RCNT 0x8000 /* b15: Read count mode */
|
||||
#define M66592_REW 0x4000 /* b14: Buffer rewind */
|
||||
#define M66592_DCLRM 0x2000 /* b13: DMA buffer clear mode */
|
||||
#define M66592_DREQE 0x1000 /* b12: DREQ output enable */
|
||||
#define M66592_MBW 0x0400 /* b10: Maximum bit width for FIFO access */
|
||||
#define M66592_MBW_8 0x0000 /* 8bit */
|
||||
#define M66592_MBW_16 0x0400 /* 16bit */
|
||||
#define M66592_TRENB 0x0200 /* b9: Transaction counter enable */
|
||||
#define M66592_TRCLR 0x0100 /* b8: Transaction counter clear */
|
||||
#define M66592_DEZPM 0x0080 /* b7: Zero-length packet additional mode */
|
||||
#define M66592_ISEL 0x0020 /* b5: DCP FIFO port direction select */
|
||||
#define M66592_CURPIPE 0x0007 /* b2-0: PIPE select */
|
||||
|
||||
#define M66592_CFIFOCTR 0x20
|
||||
#define M66592_D0FIFOCTR 0x26
|
||||
#define M66592_D1FIFOCTR 0x2c
|
||||
#define M66592_BVAL 0x8000 /* b15: Buffer valid flag */
|
||||
#define M66592_BCLR 0x4000 /* b14: Buffer clear */
|
||||
#define M66592_FRDY 0x2000 /* b13: FIFO ready */
|
||||
#define M66592_DTLN 0x0FFF /* b11-0: FIFO received data length */
|
||||
|
||||
#define M66592_CFIFOSIE 0x22
|
||||
#define M66592_TGL 0x8000 /* b15: Buffer toggle */
|
||||
#define M66592_SCLR 0x4000 /* b14: Buffer clear */
|
||||
#define M66592_SBUSY 0x2000 /* b13: SIE_FIFO busy */
|
||||
|
||||
#define M66592_D0FIFOTRN 0x28
|
||||
#define M66592_D1FIFOTRN 0x2E
|
||||
#define M66592_TRNCNT 0xFFFF /* b15-0: Transaction counter */
|
||||
|
||||
#define M66592_INTENB0 0x30
|
||||
#define M66592_VBSE 0x8000 /* b15: VBUS interrupt */
|
||||
#define M66592_RSME 0x4000 /* b14: Resume interrupt */
|
||||
#define M66592_SOFE 0x2000 /* b13: Frame update interrupt */
|
||||
#define M66592_DVSE 0x1000 /* b12: Device state transition interrupt */
|
||||
#define M66592_CTRE 0x0800 /* b11: Control transfer stage transition interrupt */
|
||||
#define M66592_BEMPE 0x0400 /* b10: Buffer empty interrupt */
|
||||
#define M66592_NRDYE 0x0200 /* b9: Buffer not ready interrupt */
|
||||
#define M66592_BRDYE 0x0100 /* b8: Buffer ready interrupt */
|
||||
#define M66592_URST 0x0080 /* b7: USB reset detected interrupt */
|
||||
#define M66592_SADR 0x0040 /* b6: Set address executed interrupt */
|
||||
#define M66592_SCFG 0x0020 /* b5: Set configuration executed interrupt */
|
||||
#define M66592_SUSP 0x0010 /* b4: Suspend detected interrupt */
|
||||
#define M66592_WDST 0x0008 /* b3: Control write data stage completed interrupt */
|
||||
#define M66592_RDST 0x0004 /* b2: Control read data stage completed interrupt */
|
||||
#define M66592_CMPL 0x0002 /* b1: Control transfer complete interrupt */
|
||||
#define M66592_SERR 0x0001 /* b0: Sequence error interrupt */
|
||||
|
||||
#define M66592_INTENB1 0x32
|
||||
#define M66592_BCHGE 0x4000 /* b14: USB us chenge interrupt */
|
||||
#define M66592_DTCHE 0x1000 /* b12: Detach sense interrupt */
|
||||
#define M66592_SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */
|
||||
#define M66592_SACKE 0x0010 /* b4: SETUP ACK interrupt */
|
||||
#define M66592_BRDYM 0x0004 /* b2: BRDY clear timing */
|
||||
#define M66592_INTL 0x0002 /* b1: Interrupt sense select */
|
||||
#define M66592_PCSE 0x0001 /* b0: PCUT enable by CS assert */
|
||||
|
||||
#define M66592_BRDYENB 0x36
|
||||
#define M66592_BRDYSTS 0x46
|
||||
#define M66592_BRDY7 0x0080 /* b7: PIPE7 */
|
||||
#define M66592_BRDY6 0x0040 /* b6: PIPE6 */
|
||||
#define M66592_BRDY5 0x0020 /* b5: PIPE5 */
|
||||
#define M66592_BRDY4 0x0010 /* b4: PIPE4 */
|
||||
#define M66592_BRDY3 0x0008 /* b3: PIPE3 */
|
||||
#define M66592_BRDY2 0x0004 /* b2: PIPE2 */
|
||||
#define M66592_BRDY1 0x0002 /* b1: PIPE1 */
|
||||
#define M66592_BRDY0 0x0001 /* b1: PIPE0 */
|
||||
|
||||
#define M66592_NRDYENB 0x38
|
||||
#define M66592_NRDYSTS 0x48
|
||||
#define M66592_NRDY7 0x0080 /* b7: PIPE7 */
|
||||
#define M66592_NRDY6 0x0040 /* b6: PIPE6 */
|
||||
#define M66592_NRDY5 0x0020 /* b5: PIPE5 */
|
||||
#define M66592_NRDY4 0x0010 /* b4: PIPE4 */
|
||||
#define M66592_NRDY3 0x0008 /* b3: PIPE3 */
|
||||
#define M66592_NRDY2 0x0004 /* b2: PIPE2 */
|
||||
#define M66592_NRDY1 0x0002 /* b1: PIPE1 */
|
||||
#define M66592_NRDY0 0x0001 /* b1: PIPE0 */
|
||||
|
||||
#define M66592_BEMPENB 0x3A
|
||||
#define M66592_BEMPSTS 0x4A
|
||||
#define M66592_BEMP7 0x0080 /* b7: PIPE7 */
|
||||
#define M66592_BEMP6 0x0040 /* b6: PIPE6 */
|
||||
#define M66592_BEMP5 0x0020 /* b5: PIPE5 */
|
||||
#define M66592_BEMP4 0x0010 /* b4: PIPE4 */
|
||||
#define M66592_BEMP3 0x0008 /* b3: PIPE3 */
|
||||
#define M66592_BEMP2 0x0004 /* b2: PIPE2 */
|
||||
#define M66592_BEMP1 0x0002 /* b1: PIPE1 */
|
||||
#define M66592_BEMP0 0x0001 /* b0: PIPE0 */
|
||||
|
||||
#define M66592_SOFCFG 0x3C
|
||||
#define M66592_SOFM 0x000C /* b3-2: SOF palse mode */
|
||||
#define M66592_SOF_125US 0x0008 /* SOF OUT 125us uFrame Signal */
|
||||
#define M66592_SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */
|
||||
#define M66592_SOF_DISABLE 0x0000 /* SOF OUT Disable */
|
||||
|
||||
#define M66592_INTSTS0 0x40
|
||||
#define M66592_VBINT 0x8000 /* b15: VBUS interrupt */
|
||||
#define M66592_RESM 0x4000 /* b14: Resume interrupt */
|
||||
#define M66592_SOFR 0x2000 /* b13: SOF frame update interrupt */
|
||||
#define M66592_DVST 0x1000 /* b12: Device state transition interrupt */
|
||||
#define M66592_CTRT 0x0800 /* b11: Control transfer stage transition interrupt */
|
||||
#define M66592_BEMP 0x0400 /* b10: Buffer empty interrupt */
|
||||
#define M66592_NRDY 0x0200 /* b9: Buffer not ready interrupt */
|
||||
#define M66592_BRDY 0x0100 /* b8: Buffer ready interrupt */
|
||||
#define M66592_VBSTS 0x0080 /* b7: VBUS input port */
|
||||
#define M66592_DVSQ 0x0070 /* b6-4: Device state */
|
||||
#define M66592_DS_SPD_CNFG 0x0070 /* Suspend Configured */
|
||||
#define M66592_DS_SPD_ADDR 0x0060 /* Suspend Address */
|
||||
#define M66592_DS_SPD_DFLT 0x0050 /* Suspend Default */
|
||||
#define M66592_DS_SPD_POWR 0x0040 /* Suspend Powered */
|
||||
#define M66592_DS_SUSP 0x0040 /* Suspend */
|
||||
#define M66592_DS_CNFG 0x0030 /* Configured */
|
||||
#define M66592_DS_ADDS 0x0020 /* Address */
|
||||
#define M66592_DS_DFLT 0x0010 /* Default */
|
||||
#define M66592_DS_POWR 0x0000 /* Powered */
|
||||
#define M66592_DVSQS 0x0030 /* b5-4: Device state */
|
||||
#define M66592_VALID 0x0008 /* b3: Setup packet detected flag */
|
||||
#define M66592_CTSQ 0x0007 /* b2-0: Control transfer stage */
|
||||
#define M66592_CS_SQER 0x0006 /* Sequence error */
|
||||
#define M66592_CS_WRND 0x0005 /* Control write nodata status stage */
|
||||
#define M66592_CS_WRSS 0x0004 /* Control write status stage */
|
||||
#define M66592_CS_WRDS 0x0003 /* Control write data stage */
|
||||
#define M66592_CS_RDSS 0x0002 /* Control read status stage */
|
||||
#define M66592_CS_RDDS 0x0001 /* Control read data stage */
|
||||
#define M66592_CS_IDST 0x0000 /* Idle or setup stage */
|
||||
|
||||
#define M66592_INTSTS1 0x42
|
||||
#define M66592_BCHG 0x4000 /* b14: USB bus chenge interrupt */
|
||||
#define M66592_DTCH 0x1000 /* b12: Detach sense interrupt */
|
||||
#define M66592_SIGN 0x0020 /* b5: SETUP IGNORE interrupt */
|
||||
#define M66592_SACK 0x0010 /* b4: SETUP ACK interrupt */
|
||||
|
||||
#define M66592_FRMNUM 0x4C
|
||||
#define M66592_OVRN 0x8000 /* b15: Overrun error */
|
||||
#define M66592_CRCE 0x4000 /* b14: Received data error */
|
||||
#define M66592_SOFRM 0x0800 /* b11: SOF output mode */
|
||||
#define M66592_FRNM 0x07FF /* b10-0: Frame number */
|
||||
|
||||
#define M66592_UFRMNUM 0x4E
|
||||
#define M66592_UFRNM 0x0007 /* b2-0: Micro frame number */
|
||||
|
||||
#define M66592_RECOVER 0x50
|
||||
#define M66592_STSRECOV 0x0700 /* Status recovery */
|
||||
#define M66592_STSR_HI 0x0400 /* FULL(0) or HI(1) Speed */
|
||||
#define M66592_STSR_DEFAULT 0x0100 /* Default state */
|
||||
#define M66592_STSR_ADDRESS 0x0200 /* Address state */
|
||||
#define M66592_STSR_CONFIG 0x0300 /* Configured state */
|
||||
#define M66592_USBADDR 0x007F /* b6-0: USB address */
|
||||
|
||||
#define M66592_USBREQ 0x54
|
||||
#define M66592_bRequest 0xFF00 /* b15-8: bRequest */
|
||||
#define M66592_GET_STATUS 0x0000
|
||||
#define M66592_CLEAR_FEATURE 0x0100
|
||||
#define M66592_ReqRESERVED 0x0200
|
||||
#define M66592_SET_FEATURE 0x0300
|
||||
#define M66592_ReqRESERVED1 0x0400
|
||||
#define M66592_SET_ADDRESS 0x0500
|
||||
#define M66592_GET_DESCRIPTOR 0x0600
|
||||
#define M66592_SET_DESCRIPTOR 0x0700
|
||||
#define M66592_GET_CONFIGURATION 0x0800
|
||||
#define M66592_SET_CONFIGURATION 0x0900
|
||||
#define M66592_GET_INTERFACE 0x0A00
|
||||
#define M66592_SET_INTERFACE 0x0B00
|
||||
#define M66592_SYNCH_FRAME 0x0C00
|
||||
#define M66592_bmRequestType 0x00FF /* b7-0: bmRequestType */
|
||||
#define M66592_bmRequestTypeDir 0x0080 /* b7 : Data transfer direction */
|
||||
#define M66592_HOST_TO_DEVICE 0x0000
|
||||
#define M66592_DEVICE_TO_HOST 0x0080
|
||||
#define M66592_bmRequestTypeType 0x0060 /* b6-5: Type */
|
||||
#define M66592_STANDARD 0x0000
|
||||
#define M66592_CLASS 0x0020
|
||||
#define M66592_VENDOR 0x0040
|
||||
#define M66592_bmRequestTypeRecip 0x001F /* b4-0: Recipient */
|
||||
#define M66592_DEVICE 0x0000
|
||||
#define M66592_INTERFACE 0x0001
|
||||
#define M66592_ENDPOINT 0x0002
|
||||
|
||||
#define M66592_USBVAL 0x56
|
||||
#define M66592_wValue 0xFFFF /* b15-0: wValue */
|
||||
/* Standard Feature Selector */
|
||||
#define M66592_ENDPOINT_HALT 0x0000
|
||||
#define M66592_DEVICE_REMOTE_WAKEUP 0x0001
|
||||
#define M66592_TEST_MODE 0x0002
|
||||
/* Descriptor Types */
|
||||
#define M66592_DT_TYPE 0xFF00
|
||||
#define M66592_GET_DT_TYPE(v) (((v) & DT_TYPE) >> 8)
|
||||
#define M66592_DT_DEVICE 0x01
|
||||
#define M66592_DT_CONFIGURATION 0x02
|
||||
#define M66592_DT_STRING 0x03
|
||||
#define M66592_DT_INTERFACE 0x04
|
||||
#define M66592_DT_ENDPOINT 0x05
|
||||
#define M66592_DT_DEVICE_QUALIFIER 0x06
|
||||
#define M66592_DT_OTHER_SPEED_CONFIGURATION 0x07
|
||||
#define M66592_DT_INTERFACE_POWER 0x08
|
||||
#define M66592_DT_INDEX 0x00FF
|
||||
#define M66592_CONF_NUM 0x00FF
|
||||
#define M66592_ALT_SET 0x00FF
|
||||
|
||||
#define M66592_USBINDEX 0x58
|
||||
#define M66592_wIndex 0xFFFF /* b15-0: wIndex */
|
||||
#define M66592_TEST_SELECT 0xFF00 /* b15-b8: Test Mode Selectors */
|
||||
#define M66592_TEST_J 0x0100 /* Test_J */
|
||||
#define M66592_TEST_K 0x0200 /* Test_K */
|
||||
#define M66592_TEST_SE0_NAK 0x0300 /* Test_SE0_NAK */
|
||||
#define M66592_TEST_PACKET 0x0400 /* Test_Packet */
|
||||
#define M66592_TEST_FORCE_ENABLE 0x0500 /* Test_Force_Enable */
|
||||
#define M66592_TEST_STSelectors 0x0600 /* Standard test selectors */
|
||||
#define M66592_TEST_Reserved 0x4000 /* Reserved */
|
||||
#define M66592_TEST_VSTModes 0xC000 /* Vendor-specific test modes */
|
||||
#define M66592_EP_DIR 0x0080 /* b7: Endpoint Direction */
|
||||
#define M66592_EP_DIR_IN 0x0080
|
||||
#define M66592_EP_DIR_OUT 0x0000
|
||||
|
||||
#define M66592_USBLENG 0x5A
|
||||
#define M66592_wLength 0xFFFF /* b15-0: wLength */
|
||||
|
||||
#define M66592_DCPCFG 0x5C
|
||||
#define M66592_CNTMD 0x0100 /* b8: Continuous transfer mode select */
|
||||
#define M66592_DIR 0x0010 /* b4: Control transfer DIR select */
|
||||
|
||||
#define M66592_DCPMAXP 0x5E
|
||||
#define M66592_DEVSEL 0xC000 /* b15-14: Device address select */
|
||||
#define M66592_DEVICE_0 0x0000 /* Device address 0 */
|
||||
#define M66592_DEVICE_1 0x4000 /* Device address 1 */
|
||||
#define M66592_DEVICE_2 0x8000 /* Device address 2 */
|
||||
#define M66592_DEVICE_3 0xC000 /* Device address 3 */
|
||||
#define M66592_MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */
|
||||
|
||||
#define M66592_DCPCTR 0x60
|
||||
#define M66592_BSTS 0x8000 /* b15: Buffer status */
|
||||
#define M66592_SUREQ 0x4000 /* b14: Send USB request */
|
||||
#define M66592_SQCLR 0x0100 /* b8: Sequence toggle bit clear */
|
||||
#define M66592_SQSET 0x0080 /* b7: Sequence toggle bit set */
|
||||
#define M66592_SQMON 0x0040 /* b6: Sequence toggle bit monitor */
|
||||
#define M66592_CCPL 0x0004 /* b2: Enable control transfer complete */
|
||||
#define M66592_PID 0x0003 /* b1-0: Response PID */
|
||||
#define M66592_PID_STALL 0x0002 /* STALL */
|
||||
#define M66592_PID_BUF 0x0001 /* BUF */
|
||||
#define M66592_PID_NAK 0x0000 /* NAK */
|
||||
|
||||
#define M66592_PIPESEL 0x64
|
||||
#define M66592_PIPENM 0x0007 /* b2-0: Pipe select */
|
||||
#define M66592_PIPE0 0x0000 /* PIPE 0 */
|
||||
#define M66592_PIPE1 0x0001 /* PIPE 1 */
|
||||
#define M66592_PIPE2 0x0002 /* PIPE 2 */
|
||||
#define M66592_PIPE3 0x0003 /* PIPE 3 */
|
||||
#define M66592_PIPE4 0x0004 /* PIPE 4 */
|
||||
#define M66592_PIPE5 0x0005 /* PIPE 5 */
|
||||
#define M66592_PIPE6 0x0006 /* PIPE 6 */
|
||||
#define M66592_PIPE7 0x0007 /* PIPE 7 */
|
||||
|
||||
#define M66592_PIPECFG 0x66
|
||||
#define M66592_TYP 0xC000 /* b15-14: Transfer type */
|
||||
#define M66592_ISO 0xC000 /* Isochronous */
|
||||
#define M66592_INT 0x8000 /* Interrupt */
|
||||
#define M66592_BULK 0x4000 /* Bulk */
|
||||
#define M66592_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */
|
||||
#define M66592_DBLB 0x0200 /* b9: Double buffer mode select */
|
||||
#define M66592_CNTMD 0x0100 /* b8: Continuous transfer mode select */
|
||||
#define M66592_SHTNAK 0x0080 /* b7: Transfer end NAK */
|
||||
#define M66592_DIR 0x0010 /* b4: Transfer direction select */
|
||||
#define M66592_DIR_H_OUT 0x0010 /* HOST OUT */
|
||||
#define M66592_DIR_P_IN 0x0010 /* PERI IN */
|
||||
#define M66592_DIR_H_IN 0x0000 /* HOST IN */
|
||||
#define M66592_DIR_P_OUT 0x0000 /* PERI OUT */
|
||||
#define M66592_EPNUM 0x000F /* b3-0: Eendpoint number select */
|
||||
#define M66592_EP1 0x0001
|
||||
#define M66592_EP2 0x0002
|
||||
#define M66592_EP3 0x0003
|
||||
#define M66592_EP4 0x0004
|
||||
#define M66592_EP5 0x0005
|
||||
#define M66592_EP6 0x0006
|
||||
#define M66592_EP7 0x0007
|
||||
#define M66592_EP8 0x0008
|
||||
#define M66592_EP9 0x0009
|
||||
#define M66592_EP10 0x000A
|
||||
#define M66592_EP11 0x000B
|
||||
#define M66592_EP12 0x000C
|
||||
#define M66592_EP13 0x000D
|
||||
#define M66592_EP14 0x000E
|
||||
#define M66592_EP15 0x000F
|
||||
|
||||
#define M66592_PIPEBUF 0x68
|
||||
#define M66592_BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */
|
||||
#define M66592_BUF_SIZE(x) ((((x) / 64) - 1) << 10)
|
||||
#define M66592_BUFNMB 0x00FF /* b7-0: Pipe buffer number */
|
||||
|
||||
#define M66592_PIPEMAXP 0x6A
|
||||
#define M66592_MXPS 0x07FF /* b10-0: Maxpacket size */
|
||||
|
||||
#define M66592_PIPEPERI 0x6C
|
||||
#define M66592_IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */
|
||||
#define M66592_IITV 0x0007 /* b2-0: Isochronous interval */
|
||||
|
||||
#define M66592_PIPE1CTR 0x70
|
||||
#define M66592_PIPE2CTR 0x72
|
||||
#define M66592_PIPE3CTR 0x74
|
||||
#define M66592_PIPE4CTR 0x76
|
||||
#define M66592_PIPE5CTR 0x78
|
||||
#define M66592_PIPE6CTR 0x7A
|
||||
#define M66592_PIPE7CTR 0x7C
|
||||
#define M66592_BSTS 0x8000 /* b15: Buffer status */
|
||||
#define M66592_INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */
|
||||
#define M66592_ACLRM 0x0200 /* b9: Out buffer auto clear mode */
|
||||
#define M66592_SQCLR 0x0100 /* b8: Sequence toggle bit clear */
|
||||
#define M66592_SQSET 0x0080 /* b7: Sequence toggle bit set */
|
||||
#define M66592_SQMON 0x0040 /* b6: Sequence toggle bit monitor */
|
||||
#define M66592_PID 0x0003 /* b1-0: Response PID */
|
||||
|
||||
#define M66592_INVALID_REG 0x7E
|
||||
|
||||
|
||||
#define __iomem
|
||||
|
||||
#define get_pipectr_addr(pipenum) (M66592_PIPE1CTR + (pipenum - 1) * 2)
|
||||
|
||||
#define M66592_MAX_SAMPLING 10
|
||||
|
||||
#define M66592_MAX_NUM_PIPE 8
|
||||
#define M66592_MAX_NUM_BULK 3
|
||||
#define M66592_MAX_NUM_ISOC 2
|
||||
#define M66592_MAX_NUM_INT 2
|
||||
|
||||
#define M66592_BASE_PIPENUM_BULK 3
|
||||
#define M66592_BASE_PIPENUM_ISOC 1
|
||||
#define M66592_BASE_PIPENUM_INT 6
|
||||
|
||||
#define M66592_BASE_BUFNUM 6
|
||||
#define M66592_MAX_BUFNUM 0x4F
|
||||
|
||||
struct m66592_pipe_info {
|
||||
u16 pipe;
|
||||
u16 epnum;
|
||||
u16 maxpacket;
|
||||
u16 type;
|
||||
u16 interval;
|
||||
u16 dir_in;
|
||||
};
|
||||
|
||||
struct m66592_request {
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
};
|
||||
|
||||
struct m66592_ep {
|
||||
struct usb_ep ep;
|
||||
struct m66592 *m66592;
|
||||
|
||||
struct list_head queue;
|
||||
unsigned busy:1;
|
||||
unsigned internal_ccpl:1; /* use only control */
|
||||
|
||||
/* this member can able to after m66592_enable */
|
||||
unsigned use_dma:1;
|
||||
u16 pipenum;
|
||||
u16 type;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
/* register address */
|
||||
unsigned long fifoaddr;
|
||||
unsigned long fifosel;
|
||||
unsigned long fifoctr;
|
||||
unsigned long fifotrn;
|
||||
unsigned long pipectr;
|
||||
};
|
||||
|
||||
struct m66592 {
|
||||
spinlock_t lock;
|
||||
void __iomem *reg;
|
||||
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
|
||||
struct m66592_ep ep[M66592_MAX_NUM_PIPE];
|
||||
struct m66592_ep *pipenum2ep[M66592_MAX_NUM_PIPE];
|
||||
struct m66592_ep *epaddr2ep[16];
|
||||
|
||||
struct usb_request *ep0_req; /* for internal request */
|
||||
u16 *ep0_buf; /* for internal request */
|
||||
|
||||
struct timer_list timer;
|
||||
|
||||
u16 old_vbus;
|
||||
int scount;
|
||||
|
||||
int old_dvsq;
|
||||
|
||||
/* pipe config */
|
||||
int bulk;
|
||||
int interrupt;
|
||||
int isochronous;
|
||||
int num_dma;
|
||||
int bi_bufnum; /* bulk and isochronous's bufnum */
|
||||
};
|
||||
|
||||
#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget)
|
||||
#define m66592_to_gadget(m66592) (&m66592->gadget)
|
||||
|
||||
#define is_bulk_pipe(pipenum) \
|
||||
((pipenum >= M66592_BASE_PIPENUM_BULK) && \
|
||||
(pipenum < (M66592_BASE_PIPENUM_BULK + M66592_MAX_NUM_BULK)))
|
||||
#define is_interrupt_pipe(pipenum) \
|
||||
((pipenum >= M66592_BASE_PIPENUM_INT) && \
|
||||
(pipenum < (M66592_BASE_PIPENUM_INT + M66592_MAX_NUM_INT)))
|
||||
#define is_isoc_pipe(pipenum) \
|
||||
((pipenum >= M66592_BASE_PIPENUM_ISOC) && \
|
||||
(pipenum < (M66592_BASE_PIPENUM_ISOC + M66592_MAX_NUM_ISOC)))
|
||||
|
||||
#define enable_irq_ready(m66592, pipenum) \
|
||||
enable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
|
||||
#define disable_irq_ready(m66592, pipenum) \
|
||||
disable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
|
||||
#define enable_irq_empty(m66592, pipenum) \
|
||||
enable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
|
||||
#define disable_irq_empty(m66592, pipenum) \
|
||||
disable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
|
||||
#define enable_irq_nrdy(m66592, pipenum) \
|
||||
enable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
|
||||
#define disable_irq_nrdy(m66592, pipenum) \
|
||||
disable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static inline u16 m66592_read(struct m66592 *m66592, unsigned long offset)
|
||||
{
|
||||
return inw((unsigned long)m66592->reg + offset);
|
||||
}
|
||||
|
||||
static inline void m66592_read_fifo(struct m66592 *m66592,
|
||||
unsigned long offset,
|
||||
void *buf, unsigned long len)
|
||||
{
|
||||
unsigned long fifoaddr = (unsigned long)m66592->reg + offset;
|
||||
|
||||
len = (len + 1) / 2;
|
||||
insw(fifoaddr, buf, len);
|
||||
}
|
||||
|
||||
static inline void m66592_write(struct m66592 *m66592, u16 val,
|
||||
unsigned long offset)
|
||||
{
|
||||
outw(val, (unsigned long)m66592->reg + offset);
|
||||
}
|
||||
|
||||
static inline void m66592_write_fifo(struct m66592 *m66592,
|
||||
unsigned long offset,
|
||||
void *buf, unsigned long len)
|
||||
{
|
||||
unsigned long fifoaddr = (unsigned long)m66592->reg + offset;
|
||||
unsigned long odd = len & 0x0001;
|
||||
|
||||
len = len / 2;
|
||||
outsw(fifoaddr, buf, len);
|
||||
if (odd) {
|
||||
unsigned char *p = buf + len*2;
|
||||
outb(*p, fifoaddr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat,
|
||||
unsigned long offset)
|
||||
{
|
||||
u16 tmp;
|
||||
tmp = m66592_read(m66592, offset);
|
||||
tmp = tmp & (~pat);
|
||||
tmp = tmp | val;
|
||||
m66592_write(m66592, tmp, offset);
|
||||
}
|
||||
|
||||
#define m66592_bclr(m66592, val, offset) \
|
||||
m66592_mdfy(m66592, 0, val, offset)
|
||||
#define m66592_bset(m66592, val, offset) \
|
||||
m66592_mdfy(m66592, val, 0, offset)
|
||||
|
||||
#endif /* ifndef __M66592_UDC_H__ */
|
||||
|
||||
|
@ -450,100 +450,6 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* dma-coherent memory allocation (for dma-capable endpoints)
|
||||
*
|
||||
* NOTE: the dma_*_coherent() API calls suck. Most implementations are
|
||||
* (a) page-oriented, so small buffers lose big; and (b) asymmetric with
|
||||
* respect to calls with irqs disabled: alloc is safe, free is not.
|
||||
* We currently work around (b), but not (a).
|
||||
*/
|
||||
|
||||
static void *
|
||||
net2280_alloc_buffer (
|
||||
struct usb_ep *_ep,
|
||||
unsigned bytes,
|
||||
dma_addr_t *dma,
|
||||
gfp_t gfp_flags
|
||||
)
|
||||
{
|
||||
void *retval;
|
||||
struct net2280_ep *ep;
|
||||
|
||||
ep = container_of (_ep, struct net2280_ep, ep);
|
||||
if (!_ep)
|
||||
return NULL;
|
||||
*dma = DMA_ADDR_INVALID;
|
||||
|
||||
if (ep->dma)
|
||||
retval = dma_alloc_coherent(&ep->dev->pdev->dev,
|
||||
bytes, dma, gfp_flags);
|
||||
else
|
||||
retval = kmalloc(bytes, gfp_flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(buflock);
|
||||
static LIST_HEAD(buffers);
|
||||
|
||||
struct free_record {
|
||||
struct list_head list;
|
||||
struct device *dev;
|
||||
unsigned bytes;
|
||||
dma_addr_t dma;
|
||||
};
|
||||
|
||||
static void do_free(unsigned long ignored)
|
||||
{
|
||||
spin_lock_irq(&buflock);
|
||||
while (!list_empty(&buffers)) {
|
||||
struct free_record *buf;
|
||||
|
||||
buf = list_entry(buffers.next, struct free_record, list);
|
||||
list_del(&buf->list);
|
||||
spin_unlock_irq(&buflock);
|
||||
|
||||
dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);
|
||||
|
||||
spin_lock_irq(&buflock);
|
||||
}
|
||||
spin_unlock_irq(&buflock);
|
||||
}
|
||||
|
||||
static DECLARE_TASKLET(deferred_free, do_free, 0);
|
||||
|
||||
static void
|
||||
net2280_free_buffer (
|
||||
struct usb_ep *_ep,
|
||||
void *address,
|
||||
dma_addr_t dma,
|
||||
unsigned bytes
|
||||
) {
|
||||
/* free memory into the right allocator */
|
||||
if (dma != DMA_ADDR_INVALID) {
|
||||
struct net2280_ep *ep;
|
||||
struct free_record *buf = address;
|
||||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep)
|
||||
return;
|
||||
|
||||
ep = container_of (_ep, struct net2280_ep, ep);
|
||||
buf->dev = &ep->dev->pdev->dev;
|
||||
buf->bytes = bytes;
|
||||
buf->dma = dma;
|
||||
|
||||
spin_lock_irqsave(&buflock, flags);
|
||||
list_add_tail(&buf->list, &buffers);
|
||||
tasklet_schedule(&deferred_free);
|
||||
spin_unlock_irqrestore(&buflock, flags);
|
||||
} else
|
||||
kfree (address);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* load a packet into the fifo we use for usb IN transfers.
|
||||
* works for all endpoints.
|
||||
*
|
||||
@ -1392,9 +1298,6 @@ static const struct usb_ep_ops net2280_ep_ops = {
|
||||
.alloc_request = net2280_alloc_request,
|
||||
.free_request = net2280_free_request,
|
||||
|
||||
.alloc_buffer = net2280_alloc_buffer,
|
||||
.free_buffer = net2280_free_buffer,
|
||||
|
||||
.queue = net2280_queue,
|
||||
.dequeue = net2280_dequeue,
|
||||
|
||||
|
@ -296,111 +296,6 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* dma-coherent memory allocation (for dma-capable endpoints)
|
||||
*
|
||||
* NOTE: the dma_*_coherent() API calls suck. Most implementations are
|
||||
* (a) page-oriented, so small buffers lose big; and (b) asymmetric with
|
||||
* respect to calls with irqs disabled: alloc is safe, free is not.
|
||||
* We currently work around (b), but not (a).
|
||||
*/
|
||||
|
||||
static void *
|
||||
omap_alloc_buffer(
|
||||
struct usb_ep *_ep,
|
||||
unsigned bytes,
|
||||
dma_addr_t *dma,
|
||||
gfp_t gfp_flags
|
||||
)
|
||||
{
|
||||
void *retval;
|
||||
struct omap_ep *ep;
|
||||
|
||||
if (!_ep)
|
||||
return NULL;
|
||||
|
||||
ep = container_of(_ep, struct omap_ep, ep);
|
||||
if (use_dma && ep->has_dma) {
|
||||
static int warned;
|
||||
if (!warned && bytes < PAGE_SIZE) {
|
||||
dev_warn(ep->udc->gadget.dev.parent,
|
||||
"using dma_alloc_coherent for "
|
||||
"small allocations wastes memory\n");
|
||||
warned++;
|
||||
}
|
||||
return dma_alloc_coherent(ep->udc->gadget.dev.parent,
|
||||
bytes, dma, gfp_flags);
|
||||
}
|
||||
|
||||
retval = kmalloc(bytes, gfp_flags);
|
||||
if (retval)
|
||||
*dma = virt_to_phys(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(buflock);
|
||||
static LIST_HEAD(buffers);
|
||||
|
||||
struct free_record {
|
||||
struct list_head list;
|
||||
struct device *dev;
|
||||
unsigned bytes;
|
||||
dma_addr_t dma;
|
||||
};
|
||||
|
||||
static void do_free(unsigned long ignored)
|
||||
{
|
||||
spin_lock_irq(&buflock);
|
||||
while (!list_empty(&buffers)) {
|
||||
struct free_record *buf;
|
||||
|
||||
buf = list_entry(buffers.next, struct free_record, list);
|
||||
list_del(&buf->list);
|
||||
spin_unlock_irq(&buflock);
|
||||
|
||||
dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);
|
||||
|
||||
spin_lock_irq(&buflock);
|
||||
}
|
||||
spin_unlock_irq(&buflock);
|
||||
}
|
||||
|
||||
static DECLARE_TASKLET(deferred_free, do_free, 0);
|
||||
|
||||
static void omap_free_buffer(
|
||||
struct usb_ep *_ep,
|
||||
void *buf,
|
||||
dma_addr_t dma,
|
||||
unsigned bytes
|
||||
)
|
||||
{
|
||||
if (!_ep) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* free memory into the right allocator */
|
||||
if (dma != DMA_ADDR_INVALID) {
|
||||
struct omap_ep *ep;
|
||||
struct free_record *rec = buf;
|
||||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct omap_ep, ep);
|
||||
|
||||
rec->dev = ep->udc->gadget.dev.parent;
|
||||
rec->bytes = bytes;
|
||||
rec->dma = dma;
|
||||
|
||||
spin_lock_irqsave(&buflock, flags);
|
||||
list_add_tail(&rec->list, &buffers);
|
||||
tasklet_schedule(&deferred_free);
|
||||
spin_unlock_irqrestore(&buflock, flags);
|
||||
} else
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void
|
||||
done(struct omap_ep *ep, struct omap_req *req, int status)
|
||||
{
|
||||
@ -1271,9 +1166,6 @@ static struct usb_ep_ops omap_ep_ops = {
|
||||
.alloc_request = omap_alloc_request,
|
||||
.free_request = omap_free_request,
|
||||
|
||||
.alloc_buffer = omap_alloc_buffer,
|
||||
.free_buffer = omap_free_buffer,
|
||||
|
||||
.queue = omap_ep_queue,
|
||||
.dequeue = omap_ep_dequeue,
|
||||
|
||||
|
@ -24,9 +24,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
// #define VERBOSE DBG_VERBOSE
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
@ -46,19 +46,17 @@
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/hardware.h>
|
||||
#ifdef CONFIG_ARCH_PXA
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#endif
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb_gadget.h>
|
||||
|
||||
#include <asm/arch/udc.h>
|
||||
#include <asm/mach/udc_pxa2xx.h>
|
||||
|
||||
|
||||
/*
|
||||
@ -76,9 +74,17 @@
|
||||
* it constrains the sorts of USB configuration change events that work.
|
||||
* The errata for these chips are misleading; some "fixed" bugs from
|
||||
* pxa250 a0/a1 b0/b1/b2 sure act like they're still there.
|
||||
*
|
||||
* Note that the UDC hardware supports DMA (except on IXP) but that's
|
||||
* not used here. IN-DMA (to host) is simple enough, when the data is
|
||||
* suitably aligned (16 bytes) ... the network stack doesn't do that,
|
||||
* other software can. OUT-DMA is buggy in most chip versions, as well
|
||||
* as poorly designed (data toggle not automatic). So this driver won't
|
||||
* bother using DMA. (Mostly-working IN-DMA support was available in
|
||||
* kernels before 2.6.23, but was never enabled or well tested.)
|
||||
*/
|
||||
|
||||
#define DRIVER_VERSION "4-May-2005"
|
||||
#define DRIVER_VERSION "30-June-2007"
|
||||
#define DRIVER_DESC "PXA 25x USB Device Controller driver"
|
||||
|
||||
|
||||
@ -87,12 +93,9 @@ static const char driver_name [] = "pxa2xx_udc";
|
||||
static const char ep0name [] = "ep0";
|
||||
|
||||
|
||||
// #define USE_DMA
|
||||
// #define USE_OUT_DMA
|
||||
// #define DISABLE_TEST_MODE
|
||||
|
||||
#ifdef CONFIG_ARCH_IXP4XX
|
||||
#undef USE_DMA
|
||||
|
||||
/* cpu-specific register addresses are compiled in to this code */
|
||||
#ifdef CONFIG_ARCH_PXA
|
||||
@ -104,25 +107,6 @@ static const char ep0name [] = "ep0";
|
||||
#include "pxa2xx_udc.h"
|
||||
|
||||
|
||||
#ifdef USE_DMA
|
||||
static int use_dma = 1;
|
||||
module_param(use_dma, bool, 0);
|
||||
MODULE_PARM_DESC (use_dma, "true to use dma");
|
||||
|
||||
static void dma_nodesc_handler (int dmach, void *_ep);
|
||||
static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req);
|
||||
|
||||
#ifdef USE_OUT_DMA
|
||||
#define DMASTR " (dma support)"
|
||||
#else
|
||||
#define DMASTR " (dma in)"
|
||||
#endif
|
||||
|
||||
#else /* !USE_DMA */
|
||||
#define DMASTR " (pio only)"
|
||||
#undef USE_OUT_DMA
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PXA2XX_SMALL
|
||||
#define SIZE_STR " (small)"
|
||||
#else
|
||||
@ -155,7 +139,7 @@ static int is_vbus_present(void)
|
||||
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
|
||||
|
||||
if (mach->gpio_vbus)
|
||||
return udc_gpio_get(mach->gpio_vbus);
|
||||
return gpio_get_value(mach->gpio_vbus);
|
||||
if (mach->udc_is_connected)
|
||||
return mach->udc_is_connected();
|
||||
return 1;
|
||||
@ -167,7 +151,7 @@ static void pullup_off(void)
|
||||
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
|
||||
|
||||
if (mach->gpio_pullup)
|
||||
udc_gpio_set(mach->gpio_pullup, 0);
|
||||
gpio_set_value(mach->gpio_pullup, 0);
|
||||
else if (mach->udc_command)
|
||||
mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
|
||||
}
|
||||
@ -177,7 +161,7 @@ static void pullup_on(void)
|
||||
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
|
||||
|
||||
if (mach->gpio_pullup)
|
||||
udc_gpio_set(mach->gpio_pullup, 1);
|
||||
gpio_set_value(mach->gpio_pullup, 1);
|
||||
else if (mach->udc_command)
|
||||
mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
|
||||
}
|
||||
@ -281,9 +265,8 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep,
|
||||
}
|
||||
|
||||
ep->desc = desc;
|
||||
ep->dma = -1;
|
||||
ep->stopped = 0;
|
||||
ep->pio_irqs = ep->dma_irqs = 0;
|
||||
ep->pio_irqs = 0;
|
||||
ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize);
|
||||
|
||||
/* flush fifo (mostly for OUT buffers) */
|
||||
@ -291,30 +274,6 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep,
|
||||
|
||||
/* ... reset halt state too, if we could ... */
|
||||
|
||||
#ifdef USE_DMA
|
||||
/* for (some) bulk and ISO endpoints, try to get a DMA channel and
|
||||
* bind it to the endpoint. otherwise use PIO.
|
||||
*/
|
||||
switch (ep->bmAttributes) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if (le16_to_cpu(desc->wMaxPacketSize) % 32)
|
||||
break;
|
||||
// fall through
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (!use_dma || !ep->reg_drcmr)
|
||||
break;
|
||||
ep->dma = pxa_request_dma ((char *)_ep->name,
|
||||
(le16_to_cpu (desc->wMaxPacketSize) > 64)
|
||||
? DMA_PRIO_MEDIUM /* some iso */
|
||||
: DMA_PRIO_LOW,
|
||||
dma_nodesc_handler, ep);
|
||||
if (ep->dma >= 0) {
|
||||
*ep->reg_drcmr = DRCMR_MAPVLD | ep->dma;
|
||||
DMSG("%s using dma%d\n", _ep->name, ep->dma);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
|
||||
return 0;
|
||||
}
|
||||
@ -334,14 +293,6 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep)
|
||||
|
||||
nuke (ep, -ESHUTDOWN);
|
||||
|
||||
#ifdef USE_DMA
|
||||
if (ep->dma >= 0) {
|
||||
*ep->reg_drcmr = 0;
|
||||
pxa_free_dma (ep->dma);
|
||||
ep->dma = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* flush fifo (mostly for IN buffers) */
|
||||
pxa2xx_ep_fifo_flush (_ep);
|
||||
|
||||
@ -390,35 +341,6 @@ pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req)
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
|
||||
/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's
|
||||
* no device-affinity and the heap works perfectly well for i/o buffers.
|
||||
* It wastes much less memory than dma_alloc_coherent() would, and even
|
||||
* prevents cacheline (32 bytes wide) sharing problems.
|
||||
*/
|
||||
static void *
|
||||
pxa2xx_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
|
||||
dma_addr_t *dma, gfp_t gfp_flags)
|
||||
{
|
||||
char *retval;
|
||||
|
||||
retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM));
|
||||
if (retval)
|
||||
#ifdef USE_DMA
|
||||
*dma = virt_to_bus (retval);
|
||||
#else
|
||||
*dma = (dma_addr_t)~0;
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
pxa2xx_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma,
|
||||
unsigned bytes)
|
||||
{
|
||||
kfree (buf);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
@ -518,18 +440,8 @@ write_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req)
|
||||
/* requests complete when all IN data is in the FIFO */
|
||||
if (is_last) {
|
||||
done (ep, req, 0);
|
||||
if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) {
|
||||
if (list_empty(&ep->queue))
|
||||
pio_irq_disable (ep->bEndpointAddress);
|
||||
#ifdef USE_DMA
|
||||
/* unaligned data and zlps couldn't use dma */
|
||||
if (unlikely(!list_empty(&ep->queue))) {
|
||||
req = list_entry(ep->queue.next,
|
||||
struct pxa2xx_request, queue);
|
||||
kick_dma(ep,req);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -728,182 +640,6 @@ read_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef USE_DMA
|
||||
|
||||
#define MAX_IN_DMA ((DCMD_LENGTH + 1) - BULK_FIFO_SIZE)
|
||||
|
||||
static void
|
||||
start_dma_nodesc(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int is_in)
|
||||
{
|
||||
u32 dcmd = req->req.length;
|
||||
u32 buf = req->req.dma;
|
||||
u32 fifo = io_v2p ((u32)ep->reg_uddr);
|
||||
|
||||
/* caller guarantees there's a packet or more remaining
|
||||
* - IN may end with a short packet (TSP set separately),
|
||||
* - OUT is always full length
|
||||
*/
|
||||
buf += req->req.actual;
|
||||
dcmd -= req->req.actual;
|
||||
ep->dma_fixup = 0;
|
||||
|
||||
/* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */
|
||||
DCSR(ep->dma) = DCSR_NODESC;
|
||||
if (is_in) {
|
||||
DSADR(ep->dma) = buf;
|
||||
DTADR(ep->dma) = fifo;
|
||||
if (dcmd > MAX_IN_DMA)
|
||||
dcmd = MAX_IN_DMA;
|
||||
else
|
||||
ep->dma_fixup = (dcmd % ep->ep.maxpacket) != 0;
|
||||
dcmd |= DCMD_BURST32 | DCMD_WIDTH1
|
||||
| DCMD_FLOWTRG | DCMD_INCSRCADDR;
|
||||
} else {
|
||||
#ifdef USE_OUT_DMA
|
||||
DSADR(ep->dma) = fifo;
|
||||
DTADR(ep->dma) = buf;
|
||||
if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC)
|
||||
dcmd = ep->ep.maxpacket;
|
||||
dcmd |= DCMD_BURST32 | DCMD_WIDTH1
|
||||
| DCMD_FLOWSRC | DCMD_INCTRGADDR;
|
||||
#endif
|
||||
}
|
||||
DCMD(ep->dma) = dcmd;
|
||||
DCSR(ep->dma) = DCSR_RUN | DCSR_NODESC
|
||||
| (unlikely(is_in)
|
||||
? DCSR_STOPIRQEN /* use dma_nodesc_handler() */
|
||||
: 0); /* use handle_ep() */
|
||||
}
|
||||
|
||||
static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req)
|
||||
{
|
||||
int is_in = ep->bEndpointAddress & USB_DIR_IN;
|
||||
|
||||
if (is_in) {
|
||||
/* unaligned tx buffers and zlps only work with PIO */
|
||||
if ((req->req.dma & 0x0f) != 0
|
||||
|| unlikely((req->req.length - req->req.actual)
|
||||
== 0)) {
|
||||
pio_irq_enable(ep->bEndpointAddress);
|
||||
if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0)
|
||||
(void) write_fifo(ep, req);
|
||||
} else {
|
||||
start_dma_nodesc(ep, req, USB_DIR_IN);
|
||||
}
|
||||
} else {
|
||||
if ((req->req.length - req->req.actual) < ep->ep.maxpacket) {
|
||||
DMSG("%s short dma read...\n", ep->ep.name);
|
||||
/* we're always set up for pio out */
|
||||
read_fifo (ep, req);
|
||||
} else {
|
||||
*ep->reg_udccs = UDCCS_BO_DME
|
||||
| (*ep->reg_udccs & UDCCS_BO_FST);
|
||||
start_dma_nodesc(ep, req, USB_DIR_OUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cancel_dma(struct pxa2xx_ep *ep)
|
||||
{
|
||||
struct pxa2xx_request *req;
|
||||
u32 tmp;
|
||||
|
||||
if (DCSR(ep->dma) == 0 || list_empty(&ep->queue))
|
||||
return;
|
||||
|
||||
DCSR(ep->dma) = 0;
|
||||
while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0)
|
||||
cpu_relax();
|
||||
|
||||
req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
|
||||
tmp = DCMD(ep->dma) & DCMD_LENGTH;
|
||||
req->req.actual = req->req.length - (tmp & DCMD_LENGTH);
|
||||
|
||||
/* the last tx packet may be incomplete, so flush the fifo.
|
||||
* FIXME correct req.actual if we can
|
||||
*/
|
||||
if (ep->bEndpointAddress & USB_DIR_IN)
|
||||
*ep->reg_udccs = UDCCS_BI_FTF;
|
||||
}
|
||||
|
||||
/* dma channel stopped ... normal tx end (IN), or on error (IN/OUT) */
|
||||
static void dma_nodesc_handler(int dmach, void *_ep)
|
||||
{
|
||||
struct pxa2xx_ep *ep = _ep;
|
||||
struct pxa2xx_request *req;
|
||||
u32 tmp, completed;
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
|
||||
|
||||
ep->dma_irqs++;
|
||||
ep->dev->stats.irqs++;
|
||||
HEX_DISPLAY(ep->dev->stats.irqs);
|
||||
|
||||
/* ack/clear */
|
||||
tmp = DCSR(ep->dma);
|
||||
DCSR(ep->dma) = tmp;
|
||||
if ((tmp & DCSR_STOPSTATE) == 0
|
||||
|| (DDADR(ep->dma) & DDADR_STOP) != 0) {
|
||||
DBG(DBG_VERBOSE, "%s, dcsr %08x ddadr %08x\n",
|
||||
ep->ep.name, DCSR(ep->dma), DDADR(ep->dma));
|
||||
goto done;
|
||||
}
|
||||
DCSR(ep->dma) = 0; /* clear DCSR_STOPSTATE */
|
||||
|
||||
/* update transfer status */
|
||||
completed = tmp & DCSR_BUSERR;
|
||||
if (ep->bEndpointAddress & USB_DIR_IN)
|
||||
tmp = DSADR(ep->dma);
|
||||
else
|
||||
tmp = DTADR(ep->dma);
|
||||
req->req.actual = tmp - req->req.dma;
|
||||
|
||||
/* FIXME seems we sometimes see partial transfers... */
|
||||
|
||||
if (unlikely(completed != 0))
|
||||
req->req.status = -EIO;
|
||||
else if (req->req.actual) {
|
||||
/* these registers have zeroes in low bits; they miscount
|
||||
* some (end-of-transfer) short packets: tx 14 as tx 12
|
||||
*/
|
||||
if (ep->dma_fixup)
|
||||
req->req.actual = min(req->req.actual + 3,
|
||||
req->req.length);
|
||||
|
||||
tmp = (req->req.length - req->req.actual);
|
||||
completed = (tmp == 0);
|
||||
if (completed && (ep->bEndpointAddress & USB_DIR_IN)) {
|
||||
|
||||
/* maybe validate final short packet ... */
|
||||
if ((req->req.actual % ep->ep.maxpacket) != 0)
|
||||
*ep->reg_udccs = UDCCS_BI_TSP/*|UDCCS_BI_TPC*/;
|
||||
|
||||
/* ... or zlp, using pio fallback */
|
||||
else if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK
|
||||
&& req->req.zero) {
|
||||
DMSG("%s zlp terminate ...\n", ep->ep.name);
|
||||
completed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(completed)) {
|
||||
done(ep, req, 0);
|
||||
|
||||
/* maybe re-activate after completion */
|
||||
if (ep->stopped || list_empty(&ep->queue))
|
||||
goto done;
|
||||
req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
|
||||
}
|
||||
kick_dma(ep, req);
|
||||
done:
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int
|
||||
@ -942,19 +678,8 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
(ep->desc->wMaxPacketSize)))
|
||||
return -EMSGSIZE;
|
||||
|
||||
#ifdef USE_DMA
|
||||
// FIXME caller may already have done the dma mapping
|
||||
if (ep->dma >= 0) {
|
||||
_req->dma = dma_map_single(dev->dev,
|
||||
_req->buf, _req->length,
|
||||
((ep->bEndpointAddress & USB_DIR_IN) != 0)
|
||||
? DMA_TO_DEVICE
|
||||
: DMA_FROM_DEVICE);
|
||||
}
|
||||
#endif
|
||||
|
||||
DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
|
||||
_ep->name, _req, _req->length, _req->buf);
|
||||
_ep->name, _req, _req->length, _req->buf);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
@ -1002,11 +727,6 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
local_irq_restore (flags);
|
||||
return -EL2HLT;
|
||||
}
|
||||
#ifdef USE_DMA
|
||||
/* either start dma or prime pio pump */
|
||||
} else if (ep->dma >= 0) {
|
||||
kick_dma(ep, req);
|
||||
#endif
|
||||
/* can the FIFO can satisfy the request immediately? */
|
||||
} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
|
||||
if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0
|
||||
@ -1017,7 +737,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
req = NULL;
|
||||
}
|
||||
|
||||
if (likely (req && ep->desc) && ep->dma < 0)
|
||||
if (likely (req && ep->desc))
|
||||
pio_irq_enable(ep->bEndpointAddress);
|
||||
}
|
||||
|
||||
@ -1038,10 +758,6 @@ static void nuke(struct pxa2xx_ep *ep, int status)
|
||||
struct pxa2xx_request *req;
|
||||
|
||||
/* called with irqs blocked */
|
||||
#ifdef USE_DMA
|
||||
if (ep->dma >= 0 && !ep->stopped)
|
||||
cancel_dma(ep);
|
||||
#endif
|
||||
while (!list_empty(&ep->queue)) {
|
||||
req = list_entry(ep->queue.next,
|
||||
struct pxa2xx_request,
|
||||
@ -1076,19 +792,7 @@ static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef USE_DMA
|
||||
if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) {
|
||||
cancel_dma(ep);
|
||||
done(ep, req, -ECONNRESET);
|
||||
/* restart i/o */
|
||||
if (!list_empty(&ep->queue)) {
|
||||
req = list_entry(ep->queue.next,
|
||||
struct pxa2xx_request, queue);
|
||||
kick_dma(ep, req);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
done(ep, req, -ECONNRESET);
|
||||
done(ep, req, -ECONNRESET);
|
||||
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
@ -1203,9 +907,6 @@ static struct usb_ep_ops pxa2xx_ep_ops = {
|
||||
.alloc_request = pxa2xx_ep_alloc_request,
|
||||
.free_request = pxa2xx_ep_free_request,
|
||||
|
||||
.alloc_buffer = pxa2xx_ep_alloc_buffer,
|
||||
.free_buffer = pxa2xx_ep_free_buffer,
|
||||
|
||||
.queue = pxa2xx_ep_queue,
|
||||
.dequeue = pxa2xx_ep_dequeue,
|
||||
|
||||
@ -1325,7 +1026,7 @@ udc_proc_read(char *page, char **start, off_t off, int count,
|
||||
/* basic device status */
|
||||
t = scnprintf(next, size, DRIVER_DESC "\n"
|
||||
"%s version: %s\nGadget driver: %s\nHost %s\n\n",
|
||||
driver_name, DRIVER_VERSION SIZE_STR DMASTR,
|
||||
driver_name, DRIVER_VERSION SIZE_STR "(pio)",
|
||||
dev->driver ? dev->driver->driver.name : "(none)",
|
||||
is_vbus_present() ? "full speed" : "disconnected");
|
||||
size -= t;
|
||||
@ -1390,7 +1091,6 @@ udc_proc_read(char *page, char **start, off_t off, int count,
|
||||
for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
|
||||
struct pxa2xx_ep *ep = &dev->ep [i];
|
||||
struct pxa2xx_request *req;
|
||||
int t;
|
||||
|
||||
if (i != 0) {
|
||||
const struct usb_endpoint_descriptor *d;
|
||||
@ -1400,10 +1100,9 @@ udc_proc_read(char *page, char **start, off_t off, int count,
|
||||
continue;
|
||||
tmp = *dev->ep [i].reg_udccs;
|
||||
t = scnprintf(next, size,
|
||||
"%s max %d %s udccs %02x irqs %lu/%lu\n",
|
||||
"%s max %d %s udccs %02x irqs %lu\n",
|
||||
ep->ep.name, le16_to_cpu (d->wMaxPacketSize),
|
||||
(ep->dma >= 0) ? "dma" : "pio", tmp,
|
||||
ep->pio_irqs, ep->dma_irqs);
|
||||
"pio", tmp, ep->pio_irqs);
|
||||
/* TODO translate all five groups of udccs bits! */
|
||||
|
||||
} else /* ep0 should only have one transfer queued */
|
||||
@ -1423,19 +1122,7 @@ udc_proc_read(char *page, char **start, off_t off, int count,
|
||||
continue;
|
||||
}
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
#ifdef USE_DMA
|
||||
if (ep->dma >= 0 && req->queue.prev == &ep->queue)
|
||||
t = scnprintf(next, size,
|
||||
"\treq %p len %d/%d "
|
||||
"buf %p (dma%d dcmd %08x)\n",
|
||||
&req->req, req->req.actual,
|
||||
req->req.length, req->req.buf,
|
||||
ep->dma, DCMD(ep->dma)
|
||||
// low 13 bits == bytes-to-go
|
||||
);
|
||||
else
|
||||
#endif
|
||||
t = scnprintf(next, size,
|
||||
t = scnprintf(next, size,
|
||||
"\treq %p len %d/%d buf %p\n",
|
||||
&req->req, req->req.actual,
|
||||
req->req.length, req->req.buf);
|
||||
@ -1488,7 +1175,6 @@ static void udc_disable(struct pxa2xx_udc *dev)
|
||||
|
||||
ep0_idle (dev);
|
||||
dev->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
LED_CONNECTED_OFF;
|
||||
}
|
||||
|
||||
|
||||
@ -1514,7 +1200,7 @@ static void udc_reinit(struct pxa2xx_udc *dev)
|
||||
ep->desc = NULL;
|
||||
ep->stopped = 0;
|
||||
INIT_LIST_HEAD (&ep->queue);
|
||||
ep->pio_irqs = ep->dma_irqs = 0;
|
||||
ep->pio_irqs = 0;
|
||||
}
|
||||
|
||||
/* the rest was statically initialized, and is read-only */
|
||||
@ -1666,7 +1352,6 @@ stop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver)
|
||||
del_timer_sync(&dev->timer);
|
||||
|
||||
/* report disconnect; the driver is already quiesced */
|
||||
LED_CONNECTED_OFF;
|
||||
if (driver)
|
||||
driver->disconnect(&dev->gadget);
|
||||
|
||||
@ -1715,16 +1400,13 @@ lubbock_vbus_irq(int irq, void *_dev)
|
||||
int vbus;
|
||||
|
||||
dev->stats.irqs++;
|
||||
HEX_DISPLAY(dev->stats.irqs);
|
||||
switch (irq) {
|
||||
case LUBBOCK_USB_IRQ:
|
||||
LED_CONNECTED_ON;
|
||||
vbus = 1;
|
||||
disable_irq(LUBBOCK_USB_IRQ);
|
||||
enable_irq(LUBBOCK_USB_DISC_IRQ);
|
||||
break;
|
||||
case LUBBOCK_USB_DISC_IRQ:
|
||||
LED_CONNECTED_OFF;
|
||||
vbus = 0;
|
||||
disable_irq(LUBBOCK_USB_DISC_IRQ);
|
||||
enable_irq(LUBBOCK_USB_IRQ);
|
||||
@ -1742,7 +1424,7 @@ lubbock_vbus_irq(int irq, void *_dev)
|
||||
static irqreturn_t udc_vbus_irq(int irq, void *_dev)
|
||||
{
|
||||
struct pxa2xx_udc *dev = _dev;
|
||||
int vbus = udc_gpio_get(dev->mach->gpio_vbus);
|
||||
int vbus = gpio_get_value(dev->mach->gpio_vbus);
|
||||
|
||||
pxa2xx_udc_vbus_session(&dev->gadget, vbus);
|
||||
return IRQ_HANDLED;
|
||||
@ -2040,18 +1722,6 @@ static void handle_ep(struct pxa2xx_ep *ep)
|
||||
|
||||
/* fifos can hold packets, ready for reading... */
|
||||
if (likely(req)) {
|
||||
#ifdef USE_OUT_DMA
|
||||
// TODO didn't yet debug out-dma. this approach assumes
|
||||
// the worst about short packets and RPC; it might be better.
|
||||
|
||||
if (likely(ep->dma >= 0)) {
|
||||
if (!(udccs & UDCCS_BO_RSP)) {
|
||||
*ep->reg_udccs = UDCCS_BO_RPC;
|
||||
ep->dma_irqs++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
completed = read_fifo(ep, req);
|
||||
} else
|
||||
pio_irq_disable (ep->bEndpointAddress);
|
||||
@ -2074,7 +1744,6 @@ pxa2xx_udc_irq(int irq, void *_dev)
|
||||
int handled;
|
||||
|
||||
dev->stats.irqs++;
|
||||
HEX_DISPLAY(dev->stats.irqs);
|
||||
do {
|
||||
u32 udccr = UDCCR;
|
||||
|
||||
@ -2125,7 +1794,6 @@ pxa2xx_udc_irq(int irq, void *_dev)
|
||||
} else {
|
||||
DBG(DBG_VERBOSE, "USB reset end\n");
|
||||
dev->gadget.speed = USB_SPEED_FULL;
|
||||
LED_CONNECTED_ON;
|
||||
memset(&dev->stats, 0, sizeof dev->stats);
|
||||
/* driver and endpoints are still reset */
|
||||
}
|
||||
@ -2217,7 +1885,6 @@ static struct pxa2xx_udc memory = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.reg_udccs = &UDCCS1,
|
||||
.reg_uddr = &UDDR1,
|
||||
drcmr (25)
|
||||
},
|
||||
.ep[2] = {
|
||||
.ep = {
|
||||
@ -2232,7 +1899,6 @@ static struct pxa2xx_udc memory = {
|
||||
.reg_udccs = &UDCCS2,
|
||||
.reg_ubcr = &UBCR2,
|
||||
.reg_uddr = &UDDR2,
|
||||
drcmr (26)
|
||||
},
|
||||
#ifndef CONFIG_USB_PXA2XX_SMALL
|
||||
.ep[3] = {
|
||||
@ -2247,7 +1913,6 @@ static struct pxa2xx_udc memory = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.reg_udccs = &UDCCS3,
|
||||
.reg_uddr = &UDDR3,
|
||||
drcmr (27)
|
||||
},
|
||||
.ep[4] = {
|
||||
.ep = {
|
||||
@ -2262,7 +1927,6 @@ static struct pxa2xx_udc memory = {
|
||||
.reg_udccs = &UDCCS4,
|
||||
.reg_ubcr = &UBCR4,
|
||||
.reg_uddr = &UDDR4,
|
||||
drcmr (28)
|
||||
},
|
||||
.ep[5] = {
|
||||
.ep = {
|
||||
@ -2291,7 +1955,6 @@ static struct pxa2xx_udc memory = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.reg_udccs = &UDCCS6,
|
||||
.reg_uddr = &UDDR6,
|
||||
drcmr (30)
|
||||
},
|
||||
.ep[7] = {
|
||||
.ep = {
|
||||
@ -2306,7 +1969,6 @@ static struct pxa2xx_udc memory = {
|
||||
.reg_udccs = &UDCCS7,
|
||||
.reg_ubcr = &UBCR7,
|
||||
.reg_uddr = &UDDR7,
|
||||
drcmr (31)
|
||||
},
|
||||
.ep[8] = {
|
||||
.ep = {
|
||||
@ -2320,7 +1982,6 @@ static struct pxa2xx_udc memory = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.reg_udccs = &UDCCS8,
|
||||
.reg_uddr = &UDDR8,
|
||||
drcmr (32)
|
||||
},
|
||||
.ep[9] = {
|
||||
.ep = {
|
||||
@ -2335,7 +1996,6 @@ static struct pxa2xx_udc memory = {
|
||||
.reg_udccs = &UDCCS9,
|
||||
.reg_ubcr = &UBCR9,
|
||||
.reg_uddr = &UDDR9,
|
||||
drcmr (33)
|
||||
},
|
||||
.ep[10] = {
|
||||
.ep = {
|
||||
@ -2364,7 +2024,6 @@ static struct pxa2xx_udc memory = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.reg_udccs = &UDCCS11,
|
||||
.reg_uddr = &UDDR11,
|
||||
drcmr (35)
|
||||
},
|
||||
.ep[12] = {
|
||||
.ep = {
|
||||
@ -2379,7 +2038,6 @@ static struct pxa2xx_udc memory = {
|
||||
.reg_udccs = &UDCCS12,
|
||||
.reg_ubcr = &UBCR12,
|
||||
.reg_uddr = &UDDR12,
|
||||
drcmr (36)
|
||||
},
|
||||
.ep[13] = {
|
||||
.ep = {
|
||||
@ -2393,7 +2051,6 @@ static struct pxa2xx_udc memory = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.reg_udccs = &UDCCS13,
|
||||
.reg_uddr = &UDDR13,
|
||||
drcmr (37)
|
||||
},
|
||||
.ep[14] = {
|
||||
.ep = {
|
||||
@ -2408,7 +2065,6 @@ static struct pxa2xx_udc memory = {
|
||||
.reg_udccs = &UDCCS14,
|
||||
.reg_ubcr = &UBCR14,
|
||||
.reg_uddr = &UDDR14,
|
||||
drcmr (38)
|
||||
},
|
||||
.ep[15] = {
|
||||
.ep = {
|
||||
@ -2466,7 +2122,7 @@ static struct pxa2xx_udc memory = {
|
||||
static int __init pxa2xx_udc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pxa2xx_udc *dev = &memory;
|
||||
int retval, out_dma = 1, vbus_irq, irq;
|
||||
int retval, vbus_irq, irq;
|
||||
u32 chiprev;
|
||||
|
||||
/* insist on Intel/ARM/XScale */
|
||||
@ -2489,7 +2145,7 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
|
||||
case PXA250_B2: case PXA210_B2:
|
||||
case PXA250_B1: case PXA210_B1:
|
||||
case PXA250_B0: case PXA210_B0:
|
||||
out_dma = 0;
|
||||
/* OUT-DMA is broken ... */
|
||||
/* fall through */
|
||||
case PXA250_C0: case PXA210_C0:
|
||||
break;
|
||||
@ -2498,11 +2154,9 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
|
||||
case IXP425_B0:
|
||||
case IXP465_AD:
|
||||
dev->has_cfr = 1;
|
||||
out_dma = 0;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
out_dma = 0;
|
||||
printk(KERN_ERR "%s: unrecognized processor: %08x\n",
|
||||
driver_name, chiprev);
|
||||
/* iop3xx, ixp4xx, ... */
|
||||
@ -2513,36 +2167,41 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return -ENODEV;
|
||||
|
||||
pr_debug("%s: IRQ %d%s%s%s\n", driver_name, irq,
|
||||
pr_debug("%s: IRQ %d%s%s\n", driver_name, irq,
|
||||
dev->has_cfr ? "" : " (!cfr)",
|
||||
out_dma ? "" : " (broken dma-out)",
|
||||
SIZE_STR DMASTR
|
||||
SIZE_STR "(pio)"
|
||||
);
|
||||
|
||||
#ifdef USE_DMA
|
||||
#ifndef USE_OUT_DMA
|
||||
out_dma = 0;
|
||||
#endif
|
||||
/* pxa 250 erratum 130 prevents using OUT dma (fixed C0) */
|
||||
if (!out_dma) {
|
||||
DMSG("disabled OUT dma\n");
|
||||
dev->ep[ 2].reg_drcmr = dev->ep[ 4].reg_drcmr = 0;
|
||||
dev->ep[ 7].reg_drcmr = dev->ep[ 9].reg_drcmr = 0;
|
||||
dev->ep[12].reg_drcmr = dev->ep[14].reg_drcmr = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* other non-static parts of init */
|
||||
dev->dev = &pdev->dev;
|
||||
dev->mach = pdev->dev.platform_data;
|
||||
|
||||
if (dev->mach->gpio_vbus) {
|
||||
udc_gpio_init_vbus(dev->mach->gpio_vbus);
|
||||
vbus_irq = udc_gpio_to_irq(dev->mach->gpio_vbus);
|
||||
if ((retval = gpio_request(dev->mach->gpio_vbus,
|
||||
"pxa2xx_udc GPIO VBUS"))) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"can't get vbus gpio %d, err: %d\n",
|
||||
dev->mach->gpio_vbus, retval);
|
||||
return -EBUSY;
|
||||
}
|
||||
gpio_direction_input(dev->mach->gpio_vbus);
|
||||
vbus_irq = gpio_to_irq(dev->mach->gpio_vbus);
|
||||
set_irq_type(vbus_irq, IRQT_BOTHEDGE);
|
||||
} else
|
||||
vbus_irq = 0;
|
||||
if (dev->mach->gpio_pullup)
|
||||
udc_gpio_init_pullup(dev->mach->gpio_pullup);
|
||||
|
||||
if (dev->mach->gpio_pullup) {
|
||||
if ((retval = gpio_request(dev->mach->gpio_pullup,
|
||||
"pca2xx_udc GPIO PULLUP"))) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"can't get pullup gpio %d, err: %d\n",
|
||||
dev->mach->gpio_pullup, retval);
|
||||
if (dev->mach->gpio_vbus)
|
||||
gpio_free(dev->mach->gpio_vbus);
|
||||
return -EBUSY;
|
||||
}
|
||||
gpio_direction_output(dev->mach->gpio_pullup, 0);
|
||||
}
|
||||
|
||||
init_timer(&dev->timer);
|
||||
dev->timer.function = udc_watchdog;
|
||||
@ -2566,6 +2225,10 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
|
||||
if (retval != 0) {
|
||||
printk(KERN_ERR "%s: can't get irq %d, err %d\n",
|
||||
driver_name, irq, retval);
|
||||
if (dev->mach->gpio_pullup)
|
||||
gpio_free(dev->mach->gpio_pullup);
|
||||
if (dev->mach->gpio_vbus)
|
||||
gpio_free(dev->mach->gpio_vbus);
|
||||
return -EBUSY;
|
||||
}
|
||||
dev->got_irq = 1;
|
||||
@ -2581,6 +2244,10 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
|
||||
driver_name, LUBBOCK_USB_DISC_IRQ, retval);
|
||||
lubbock_fail0:
|
||||
free_irq(irq, dev);
|
||||
if (dev->mach->gpio_pullup)
|
||||
gpio_free(dev->mach->gpio_pullup);
|
||||
if (dev->mach->gpio_vbus)
|
||||
gpio_free(dev->mach->gpio_vbus);
|
||||
return -EBUSY;
|
||||
}
|
||||
retval = request_irq(LUBBOCK_USB_IRQ,
|
||||
@ -2593,11 +2260,6 @@ lubbock_fail0:
|
||||
free_irq(LUBBOCK_USB_DISC_IRQ, dev);
|
||||
goto lubbock_fail0;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
/* with U-Boot (but not BLOB), hex is off by default */
|
||||
HEX_DISPLAY(dev->stats.irqs);
|
||||
LUB_DISC_BLNK_LED &= 0xff;
|
||||
#endif
|
||||
} else
|
||||
#endif
|
||||
if (vbus_irq) {
|
||||
@ -2608,6 +2270,10 @@ lubbock_fail0:
|
||||
printk(KERN_ERR "%s: can't get irq %i, err %d\n",
|
||||
driver_name, vbus_irq, retval);
|
||||
free_irq(irq, dev);
|
||||
if (dev->mach->gpio_pullup)
|
||||
gpio_free(dev->mach->gpio_pullup);
|
||||
if (dev->mach->gpio_vbus)
|
||||
gpio_free(dev->mach->gpio_vbus);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
@ -2641,8 +2307,13 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
|
||||
free_irq(LUBBOCK_USB_IRQ, dev);
|
||||
}
|
||||
#endif
|
||||
if (dev->mach->gpio_vbus)
|
||||
free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
|
||||
if (dev->mach->gpio_vbus) {
|
||||
free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev);
|
||||
gpio_free(dev->mach->gpio_vbus);
|
||||
}
|
||||
if (dev->mach->gpio_pullup)
|
||||
gpio_free(dev->mach->gpio_pullup);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
the_controller = NULL;
|
||||
return 0;
|
||||
|
@ -54,8 +54,6 @@ struct pxa2xx_ep {
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct list_head queue;
|
||||
unsigned long pio_irqs;
|
||||
unsigned long dma_irqs;
|
||||
short dma;
|
||||
|
||||
unsigned short fifo_size;
|
||||
u8 bEndpointAddress;
|
||||
@ -63,7 +61,7 @@ struct pxa2xx_ep {
|
||||
|
||||
unsigned stopped : 1;
|
||||
unsigned dma_fixup : 1;
|
||||
|
||||
|
||||
/* UDCCS = UDC Control/Status for this EP
|
||||
* UBCR = UDC Byte Count Remaining (contents of OUT fifo)
|
||||
* UDDR = UDC Endpoint Data Register (the fifo)
|
||||
@ -72,12 +70,6 @@ struct pxa2xx_ep {
|
||||
volatile u32 *reg_udccs;
|
||||
volatile u32 *reg_ubcr;
|
||||
volatile u32 *reg_uddr;
|
||||
#ifdef USE_DMA
|
||||
volatile u32 *reg_drcmr;
|
||||
#define drcmr(n) .reg_drcmr = & DRCMR ## n ,
|
||||
#else
|
||||
#define drcmr(n)
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pxa2xx_request {
|
||||
@ -85,7 +77,7 @@ struct pxa2xx_request {
|
||||
struct list_head queue;
|
||||
};
|
||||
|
||||
enum ep0_state {
|
||||
enum ep0_state {
|
||||
EP0_IDLE,
|
||||
EP0_IN_DATA_PHASE,
|
||||
EP0_OUT_DATA_PHASE,
|
||||
@ -108,7 +100,6 @@ struct udc_stats {
|
||||
|
||||
#ifdef CONFIG_USB_PXA2XX_SMALL
|
||||
/* when memory's tight, SMALL config saves code+data. */
|
||||
#undef USE_DMA
|
||||
#define PXA_UDC_NUM_ENDPOINTS 3
|
||||
#endif
|
||||
|
||||
@ -144,37 +135,8 @@ struct pxa2xx_udc {
|
||||
#ifdef CONFIG_ARCH_LUBBOCK
|
||||
#include <asm/arch/lubbock.h>
|
||||
/* lubbock can also report usb connect/disconnect irqs */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define HEX_DISPLAY(n) if (machine_is_lubbock()) { LUB_HEXLED = (n); }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* LEDs are only for debug */
|
||||
#ifndef HEX_DISPLAY
|
||||
#define HEX_DISPLAY(n) do {} while(0)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <asm/leds.h>
|
||||
|
||||
#define LED_CONNECTED_ON leds_event(led_green_on)
|
||||
#define LED_CONNECTED_OFF do { \
|
||||
leds_event(led_green_off); \
|
||||
HEX_DISPLAY(0); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#ifndef LED_CONNECTED_ON
|
||||
#define LED_CONNECTED_ON do {} while(0)
|
||||
#define LED_CONNECTED_OFF do {} while(0)
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct pxa2xx_udc *the_controller;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -204,7 +166,7 @@ static const char *state_name[] = {
|
||||
# define UDC_DEBUG DBG_NORMAL
|
||||
#endif
|
||||
|
||||
static void __attribute__ ((__unused__))
|
||||
static void __maybe_unused
|
||||
dump_udccr(const char *label)
|
||||
{
|
||||
u32 udccr = UDCCR;
|
||||
@ -220,7 +182,7 @@ dump_udccr(const char *label)
|
||||
(udccr & UDCCR_UDE) ? " ude" : "");
|
||||
}
|
||||
|
||||
static void __attribute__ ((__unused__))
|
||||
static void __maybe_unused
|
||||
dump_udccs0(const char *label)
|
||||
{
|
||||
u32 udccs0 = UDCCS0;
|
||||
@ -237,7 +199,7 @@ dump_udccs0(const char *label)
|
||||
(udccs0 & UDCCS0_OPR) ? " opr" : "");
|
||||
}
|
||||
|
||||
static void __attribute__ ((__unused__))
|
||||
static void __maybe_unused
|
||||
dump_state(struct pxa2xx_udc *dev)
|
||||
{
|
||||
u32 tmp;
|
||||
|
@ -53,7 +53,7 @@
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#define DEBUG(str,args...) do { \
|
||||
#define DBG(str,args...) do { \
|
||||
if (rndis_debug) \
|
||||
printk(KERN_DEBUG str , ## args ); \
|
||||
} while (0)
|
||||
@ -65,7 +65,7 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging");
|
||||
#else
|
||||
|
||||
#define rndis_debug 0
|
||||
#define DEBUG(str,args...) do{}while(0)
|
||||
#define DBG(str,args...) do{}while(0)
|
||||
#endif
|
||||
|
||||
#define RNDIS_MAX_CONFIGS 1
|
||||
@ -183,9 +183,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
if (!resp) return -ENOMEM;
|
||||
|
||||
if (buf_len && rndis_debug > 1) {
|
||||
DEBUG("query OID %08x value, len %d:\n", OID, buf_len);
|
||||
DBG("query OID %08x value, len %d:\n", OID, buf_len);
|
||||
for (i = 0; i < buf_len; i += 16) {
|
||||
DEBUG ("%03d: %08x %08x %08x %08x\n", i,
|
||||
DBG("%03d: %08x %08x %08x %08x\n", i,
|
||||
le32_to_cpu(get_unaligned((__le32 *)
|
||||
&buf[i])),
|
||||
le32_to_cpu(get_unaligned((__le32 *)
|
||||
@ -207,7 +207,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_SUPPORTED_LIST:
|
||||
DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__);
|
||||
length = sizeof (oid_supported_list);
|
||||
count = length / sizeof (u32);
|
||||
for (i = 0; i < count; i++)
|
||||
@ -217,7 +217,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_HARDWARE_STATUS:
|
||||
DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__);
|
||||
/* Bogus question!
|
||||
* Hardware must be ready to receive high level protocols.
|
||||
* BTW:
|
||||
@ -230,14 +230,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_MEDIA_SUPPORTED:
|
||||
DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__);
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_MEDIA_IN_USE:
|
||||
DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__);
|
||||
/* one medium, one transport... (maybe you do it better) */
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
|
||||
retval = 0;
|
||||
@ -245,7 +245,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_MAXIMUM_FRAME_SIZE:
|
||||
DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].dev) {
|
||||
*outbuf = cpu_to_le32 (
|
||||
rndis_per_dev_params [configNr].dev->mtu);
|
||||
@ -256,7 +256,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
/* mandatory */
|
||||
case OID_GEN_LINK_SPEED:
|
||||
if (rndis_debug > 1)
|
||||
DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].media_state
|
||||
== NDIS_MEDIA_STATE_DISCONNECTED)
|
||||
*outbuf = __constant_cpu_to_le32 (0);
|
||||
@ -268,7 +268,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_TRANSMIT_BLOCK_SIZE:
|
||||
DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].dev) {
|
||||
*outbuf = cpu_to_le32 (
|
||||
rndis_per_dev_params [configNr].dev->mtu);
|
||||
@ -278,7 +278,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_RECEIVE_BLOCK_SIZE:
|
||||
DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].dev) {
|
||||
*outbuf = cpu_to_le32 (
|
||||
rndis_per_dev_params [configNr].dev->mtu);
|
||||
@ -288,7 +288,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_VENDOR_ID:
|
||||
DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__);
|
||||
*outbuf = cpu_to_le32 (
|
||||
rndis_per_dev_params [configNr].vendorID);
|
||||
retval = 0;
|
||||
@ -296,7 +296,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_VENDOR_DESCRIPTION:
|
||||
DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__);
|
||||
length = strlen (rndis_per_dev_params [configNr].vendorDescr);
|
||||
memcpy (outbuf,
|
||||
rndis_per_dev_params [configNr].vendorDescr, length);
|
||||
@ -304,7 +304,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_VENDOR_DRIVER_VERSION:
|
||||
DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__);
|
||||
/* Created as LE */
|
||||
*outbuf = rndis_driver_version;
|
||||
retval = 0;
|
||||
@ -312,14 +312,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_CURRENT_PACKET_FILTER:
|
||||
DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
|
||||
*outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_MAXIMUM_TOTAL_SIZE:
|
||||
DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__);
|
||||
*outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
|
||||
retval = 0;
|
||||
break;
|
||||
@ -327,14 +327,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
/* mandatory */
|
||||
case OID_GEN_MEDIA_CONNECT_STATUS:
|
||||
if (rndis_debug > 1)
|
||||
DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__);
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.media_state);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
case OID_GEN_PHYSICAL_MEDIUM:
|
||||
DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__);
|
||||
*outbuf = __constant_cpu_to_le32 (0);
|
||||
retval = 0;
|
||||
break;
|
||||
@ -344,7 +344,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
* versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
|
||||
*/
|
||||
case OID_GEN_MAC_OPTIONS: /* from WinME */
|
||||
DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__);
|
||||
*outbuf = __constant_cpu_to_le32(
|
||||
NDIS_MAC_OPTION_RECEIVE_SERIALIZED
|
||||
| NDIS_MAC_OPTION_FULL_DUPLEX);
|
||||
@ -356,7 +356,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
/* mandatory */
|
||||
case OID_GEN_XMIT_OK:
|
||||
if (rndis_debug > 1)
|
||||
DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (
|
||||
rndis_per_dev_params [configNr].stats->tx_packets -
|
||||
@ -369,7 +369,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
/* mandatory */
|
||||
case OID_GEN_RCV_OK:
|
||||
if (rndis_debug > 1)
|
||||
DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (
|
||||
rndis_per_dev_params [configNr].stats->rx_packets -
|
||||
@ -382,7 +382,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
/* mandatory */
|
||||
case OID_GEN_XMIT_ERROR:
|
||||
if (rndis_debug > 1)
|
||||
DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->tx_errors);
|
||||
@ -393,7 +393,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
/* mandatory */
|
||||
case OID_GEN_RCV_ERROR:
|
||||
if (rndis_debug > 1)
|
||||
DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->rx_errors);
|
||||
@ -403,7 +403,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_GEN_RCV_NO_BUFFER:
|
||||
DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->rx_dropped);
|
||||
@ -413,7 +413,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
#ifdef RNDIS_OPTIONAL_STATS
|
||||
case OID_GEN_DIRECTED_BYTES_XMIT:
|
||||
DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__);
|
||||
/*
|
||||
* Aunt Tilly's size of shoes
|
||||
* minus antarctica count of penguins
|
||||
@ -433,7 +433,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_DIRECTED_FRAMES_XMIT:
|
||||
DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__);
|
||||
/* dito */
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (
|
||||
@ -449,7 +449,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_MULTICAST_BYTES_XMIT:
|
||||
DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->multicast*1234);
|
||||
@ -458,7 +458,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_MULTICAST_FRAMES_XMIT:
|
||||
DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->multicast);
|
||||
@ -467,7 +467,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_BROADCAST_BYTES_XMIT:
|
||||
DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->tx_packets/42*255);
|
||||
@ -476,7 +476,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_BROADCAST_FRAMES_XMIT:
|
||||
DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->tx_packets/42);
|
||||
@ -485,19 +485,19 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_DIRECTED_BYTES_RCV:
|
||||
DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__);
|
||||
*outbuf = __constant_cpu_to_le32 (0);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
case OID_GEN_DIRECTED_FRAMES_RCV:
|
||||
DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__);
|
||||
*outbuf = __constant_cpu_to_le32 (0);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
case OID_GEN_MULTICAST_BYTES_RCV:
|
||||
DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->multicast * 1111);
|
||||
@ -506,7 +506,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_MULTICAST_FRAMES_RCV:
|
||||
DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->multicast);
|
||||
@ -515,7 +515,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_BROADCAST_BYTES_RCV:
|
||||
DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->rx_packets/42*255);
|
||||
@ -524,7 +524,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_BROADCAST_FRAMES_RCV:
|
||||
DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->rx_packets/42);
|
||||
@ -533,7 +533,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_RCV_CRC_ERROR:
|
||||
DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->rx_crc_errors);
|
||||
@ -542,7 +542,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
break;
|
||||
|
||||
case OID_GEN_TRANSMIT_QUEUE_LENGTH:
|
||||
DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__);
|
||||
DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__);
|
||||
*outbuf = __constant_cpu_to_le32 (0);
|
||||
retval = 0;
|
||||
break;
|
||||
@ -552,7 +552,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_802_3_PERMANENT_ADDRESS:
|
||||
DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].dev) {
|
||||
length = ETH_ALEN;
|
||||
memcpy (outbuf,
|
||||
@ -564,7 +564,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_802_3_CURRENT_ADDRESS:
|
||||
DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].dev) {
|
||||
length = ETH_ALEN;
|
||||
memcpy (outbuf,
|
||||
@ -576,7 +576,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_802_3_MULTICAST_LIST:
|
||||
DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
|
||||
/* Multicast base address only */
|
||||
*outbuf = __constant_cpu_to_le32 (0xE0000000);
|
||||
retval = 0;
|
||||
@ -584,21 +584,21 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_802_3_MAXIMUM_LIST_SIZE:
|
||||
DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__);
|
||||
/* Multicast base address only */
|
||||
*outbuf = __constant_cpu_to_le32 (1);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
case OID_802_3_MAC_OPTIONS:
|
||||
DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__);
|
||||
break;
|
||||
|
||||
/* ieee802.3 statistics OIDs (table 4-4) */
|
||||
|
||||
/* mandatory */
|
||||
case OID_802_3_RCV_ERROR_ALIGNMENT:
|
||||
DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__);
|
||||
if (rndis_per_dev_params [configNr].stats) {
|
||||
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
|
||||
.stats->rx_frame_errors);
|
||||
@ -608,51 +608,51 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
|
||||
/* mandatory */
|
||||
case OID_802_3_XMIT_ONE_COLLISION:
|
||||
DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__);
|
||||
*outbuf = __constant_cpu_to_le32 (0);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
/* mandatory */
|
||||
case OID_802_3_XMIT_MORE_COLLISIONS:
|
||||
DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__);
|
||||
*outbuf = __constant_cpu_to_le32 (0);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
#ifdef RNDIS_OPTIONAL_STATS
|
||||
case OID_802_3_XMIT_DEFERRED:
|
||||
DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__);
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
case OID_802_3_XMIT_MAX_COLLISIONS:
|
||||
DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__);
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
case OID_802_3_RCV_OVERRUN:
|
||||
DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__);
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
case OID_802_3_XMIT_UNDERRUN:
|
||||
DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__);
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
case OID_802_3_XMIT_HEARTBEAT_FAILURE:
|
||||
DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__);
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
case OID_802_3_XMIT_TIMES_CRS_LOST:
|
||||
DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__);
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
case OID_802_3_XMIT_LATE_COLLISIONS:
|
||||
DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__);
|
||||
/* TODO */
|
||||
break;
|
||||
#endif /* RNDIS_OPTIONAL_STATS */
|
||||
@ -660,7 +660,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
#ifdef RNDIS_PM
|
||||
/* power management OIDs (table 4-5) */
|
||||
case OID_PNP_CAPABILITIES:
|
||||
DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__);
|
||||
DBG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__);
|
||||
|
||||
/* for now, no wakeup capabilities */
|
||||
length = sizeof (struct NDIS_PNP_CAPABILITIES);
|
||||
@ -668,7 +668,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
|
||||
retval = 0;
|
||||
break;
|
||||
case OID_PNP_QUERY_POWER:
|
||||
DEBUG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__,
|
||||
DBG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__,
|
||||
le32_to_cpu(get_unaligned((__le32 *)buf)) - 1);
|
||||
/* only suspend is a real power state, and
|
||||
* it can't be entered by OID_PNP_SET_POWER...
|
||||
@ -705,9 +705,9 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
|
||||
return -ENOMEM;
|
||||
|
||||
if (buf_len && rndis_debug > 1) {
|
||||
DEBUG("set OID %08x value, len %d:\n", OID, buf_len);
|
||||
DBG("set OID %08x value, len %d:\n", OID, buf_len);
|
||||
for (i = 0; i < buf_len; i += 16) {
|
||||
DEBUG ("%03d: %08x %08x %08x %08x\n", i,
|
||||
DBG("%03d: %08x %08x %08x %08x\n", i,
|
||||
le32_to_cpu(get_unaligned((__le32 *)
|
||||
&buf[i])),
|
||||
le32_to_cpu(get_unaligned((__le32 *)
|
||||
@ -731,7 +731,7 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
|
||||
*/
|
||||
*params->filter = (u16) le32_to_cpu(get_unaligned(
|
||||
(__le32 *)buf));
|
||||
DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
|
||||
DBG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
|
||||
__FUNCTION__, *params->filter);
|
||||
|
||||
/* this call has a significant side effect: it's
|
||||
@ -756,7 +756,7 @@ update_linkstate:
|
||||
|
||||
case OID_802_3_MULTICAST_LIST:
|
||||
/* I think we can ignore this */
|
||||
DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
|
||||
DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
|
||||
retval = 0;
|
||||
break;
|
||||
#if 0
|
||||
@ -764,7 +764,7 @@ update_linkstate:
|
||||
{
|
||||
struct rndis_config_parameter *param;
|
||||
param = (struct rndis_config_parameter *) buf;
|
||||
DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
|
||||
DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
|
||||
__FUNCTION__,
|
||||
min(cpu_to_le32(param->ParameterNameLength),80),
|
||||
buf + param->ParameterNameOffset);
|
||||
@ -781,7 +781,7 @@ update_linkstate:
|
||||
* FIXME ... then things go batty; Windows wedges itself.
|
||||
*/
|
||||
i = le32_to_cpu(get_unaligned((__le32 *)buf));
|
||||
DEBUG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1);
|
||||
DBG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1);
|
||||
switch (i) {
|
||||
case NdisDeviceStateD0:
|
||||
*params->filter = params->saved_filter;
|
||||
@ -858,7 +858,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
|
||||
rndis_query_cmplt_type *resp;
|
||||
rndis_resp_t *r;
|
||||
|
||||
// DEBUG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID));
|
||||
// DBG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID));
|
||||
if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
|
||||
|
||||
/*
|
||||
@ -911,15 +911,15 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
|
||||
BufOffset = le32_to_cpu (buf->InformationBufferOffset);
|
||||
|
||||
#ifdef VERBOSE
|
||||
DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength);
|
||||
DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset);
|
||||
DEBUG("%s: InfoBuffer: ", __FUNCTION__);
|
||||
DBG("%s: Length: %d\n", __FUNCTION__, BufLength);
|
||||
DBG("%s: Offset: %d\n", __FUNCTION__, BufOffset);
|
||||
DBG("%s: InfoBuffer: ", __FUNCTION__);
|
||||
|
||||
for (i = 0; i < BufLength; i++) {
|
||||
DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
|
||||
DBG("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
|
||||
}
|
||||
|
||||
DEBUG ("\n");
|
||||
DBG("\n");
|
||||
#endif
|
||||
|
||||
resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT);
|
||||
@ -1082,14 +1082,14 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
|
||||
/* For USB: responses may take up to 10 seconds */
|
||||
switch (MsgType) {
|
||||
case REMOTE_NDIS_INITIALIZE_MSG:
|
||||
DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
|
||||
DBG("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
|
||||
__FUNCTION__ );
|
||||
params->state = RNDIS_INITIALIZED;
|
||||
return rndis_init_response (configNr,
|
||||
(rndis_init_msg_type *) buf);
|
||||
|
||||
case REMOTE_NDIS_HALT_MSG:
|
||||
DEBUG("%s: REMOTE_NDIS_HALT_MSG\n",
|
||||
DBG("%s: REMOTE_NDIS_HALT_MSG\n",
|
||||
__FUNCTION__ );
|
||||
params->state = RNDIS_UNINITIALIZED;
|
||||
if (params->dev) {
|
||||
@ -1107,7 +1107,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
|
||||
(rndis_set_msg_type *) buf);
|
||||
|
||||
case REMOTE_NDIS_RESET_MSG:
|
||||
DEBUG("%s: REMOTE_NDIS_RESET_MSG\n",
|
||||
DBG("%s: REMOTE_NDIS_RESET_MSG\n",
|
||||
__FUNCTION__ );
|
||||
return rndis_reset_response (configNr,
|
||||
(rndis_reset_msg_type *) buf);
|
||||
@ -1115,7 +1115,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
|
||||
case REMOTE_NDIS_KEEPALIVE_MSG:
|
||||
/* For USB: host does this every 5 seconds */
|
||||
if (rndis_debug > 1)
|
||||
DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
|
||||
DBG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
|
||||
__FUNCTION__ );
|
||||
return rndis_keepalive_response (configNr,
|
||||
(rndis_keepalive_msg_type *)
|
||||
@ -1132,7 +1132,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < MsgLength; i += 16) {
|
||||
DEBUG ("%03d: "
|
||||
DBG("%03d: "
|
||||
" %02x %02x %02x %02x"
|
||||
" %02x %02x %02x %02x"
|
||||
" %02x %02x %02x %02x"
|
||||
@ -1163,18 +1163,18 @@ int rndis_register (int (* rndis_control_ack) (struct net_device *))
|
||||
if (!rndis_per_dev_params [i].used) {
|
||||
rndis_per_dev_params [i].used = 1;
|
||||
rndis_per_dev_params [i].ack = rndis_control_ack;
|
||||
DEBUG("%s: configNr = %d\n", __FUNCTION__, i);
|
||||
DBG("%s: configNr = %d\n", __FUNCTION__, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
DEBUG("failed\n");
|
||||
DBG("failed\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void rndis_deregister (int configNr)
|
||||
{
|
||||
DEBUG("%s: \n", __FUNCTION__ );
|
||||
DBG("%s: \n", __FUNCTION__ );
|
||||
|
||||
if (configNr >= RNDIS_MAX_CONFIGS) return;
|
||||
rndis_per_dev_params [configNr].used = 0;
|
||||
@ -1186,7 +1186,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev,
|
||||
struct net_device_stats *stats,
|
||||
u16 *cdc_filter)
|
||||
{
|
||||
DEBUG("%s:\n", __FUNCTION__ );
|
||||
DBG("%s:\n", __FUNCTION__ );
|
||||
if (!dev || !stats) return -1;
|
||||
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
|
||||
|
||||
@ -1199,7 +1199,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev,
|
||||
|
||||
int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
|
||||
{
|
||||
DEBUG("%s:\n", __FUNCTION__ );
|
||||
DBG("%s:\n", __FUNCTION__ );
|
||||
if (!vendorDescr) return -1;
|
||||
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
|
||||
|
||||
@ -1211,7 +1211,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
|
||||
|
||||
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
|
||||
{
|
||||
DEBUG("%s: %u %u\n", __FUNCTION__, medium, speed);
|
||||
DBG("%s: %u %u\n", __FUNCTION__, medium, speed);
|
||||
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
|
||||
|
||||
rndis_per_dev_params [configNr].medium = medium;
|
||||
@ -1390,7 +1390,7 @@ static int rndis_proc_write (struct file *file, const char __user *buffer,
|
||||
break;
|
||||
default:
|
||||
if (fl_speed) p->speed = speed;
|
||||
else DEBUG ("%c is not valid\n", c);
|
||||
else DBG("%c is not valid\n", c);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1419,12 +1419,12 @@ int __devinit rndis_init (void)
|
||||
if (!(rndis_connect_state [i]
|
||||
= create_proc_entry (name, 0660, NULL)))
|
||||
{
|
||||
DEBUG ("%s :remove entries", __FUNCTION__);
|
||||
DBG("%s :remove entries", __FUNCTION__);
|
||||
while (i) {
|
||||
sprintf (name, NAME_TEMPLATE, --i);
|
||||
remove_proc_entry (name, NULL);
|
||||
}
|
||||
DEBUG ("\n");
|
||||
DBG("\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
2045
drivers/usb/gadget/s3c2410_udc.c
Normal file
2045
drivers/usb/gadget/s3c2410_udc.c
Normal file
File diff suppressed because it is too large
Load Diff
110
drivers/usb/gadget/s3c2410_udc.h
Normal file
110
drivers/usb/gadget/s3c2410_udc.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* linux/drivers/usb/gadget/s3c2410_udc.h
|
||||
* Samsung on-chip full speed USB device controllers
|
||||
*
|
||||
* Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard
|
||||
* Additional cleanups by Ben Dooks <ben-linux@fluff.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _S3C2410_UDC_H
|
||||
#define _S3C2410_UDC_H
|
||||
|
||||
struct s3c2410_ep {
|
||||
struct list_head queue;
|
||||
unsigned long last_io; /* jiffies timestamp */
|
||||
struct usb_gadget *gadget;
|
||||
struct s3c2410_udc *dev;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct usb_ep ep;
|
||||
u8 num;
|
||||
|
||||
unsigned short fifo_size;
|
||||
u8 bEndpointAddress;
|
||||
u8 bmAttributes;
|
||||
|
||||
unsigned halted : 1;
|
||||
unsigned already_seen : 1;
|
||||
unsigned setup_stage : 1;
|
||||
};
|
||||
|
||||
|
||||
/* Warning : ep0 has a fifo of 16 bytes */
|
||||
/* Don't try to set 32 or 64 */
|
||||
/* also testusb 14 fails wit 16 but is */
|
||||
/* fine with 8 */
|
||||
#define EP0_FIFO_SIZE 8
|
||||
#define EP_FIFO_SIZE 64
|
||||
#define DEFAULT_POWER_STATE 0x00
|
||||
|
||||
#define S3C2440_EP_FIFO_SIZE 128
|
||||
|
||||
static const char ep0name [] = "ep0";
|
||||
|
||||
static const char *const ep_name[] = {
|
||||
ep0name, /* everyone has ep0 */
|
||||
/* s3c2410 four bidirectional bulk endpoints */
|
||||
"ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
|
||||
};
|
||||
|
||||
#define S3C2410_ENDPOINTS ARRAY_SIZE(ep_name)
|
||||
|
||||
struct s3c2410_request {
|
||||
struct list_head queue; /* ep's requests */
|
||||
struct usb_request req;
|
||||
};
|
||||
|
||||
enum ep0_state {
|
||||
EP0_IDLE,
|
||||
EP0_IN_DATA_PHASE,
|
||||
EP0_OUT_DATA_PHASE,
|
||||
EP0_END_XFER,
|
||||
EP0_STALL,
|
||||
};
|
||||
|
||||
static const char *ep0states[]= {
|
||||
"EP0_IDLE",
|
||||
"EP0_IN_DATA_PHASE",
|
||||
"EP0_OUT_DATA_PHASE",
|
||||
"EP0_END_XFER",
|
||||
"EP0_STALL",
|
||||
};
|
||||
|
||||
struct s3c2410_udc {
|
||||
spinlock_t lock;
|
||||
|
||||
struct s3c2410_ep ep[S3C2410_ENDPOINTS];
|
||||
int address;
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct s3c2410_request fifo_req;
|
||||
u8 fifo_buf[EP_FIFO_SIZE];
|
||||
u16 devstatus;
|
||||
|
||||
u32 port_status;
|
||||
int ep0state;
|
||||
|
||||
unsigned got_irq : 1;
|
||||
|
||||
unsigned req_std : 1;
|
||||
unsigned req_config : 1;
|
||||
unsigned req_pending : 1;
|
||||
u8 vbus;
|
||||
struct dentry *regs_info;
|
||||
};
|
||||
|
||||
#endif
|
@ -2215,7 +2215,7 @@ static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags)
|
||||
*
|
||||
* Free the buffer and all associated memory.
|
||||
*/
|
||||
void gs_buf_free(struct gs_buf *gb)
|
||||
static void gs_buf_free(struct gs_buf *gb)
|
||||
{
|
||||
if (gb) {
|
||||
kfree(gb->buf_buf);
|
||||
@ -2228,7 +2228,7 @@ void gs_buf_free(struct gs_buf *gb)
|
||||
*
|
||||
* Clear out all data in the circular buffer.
|
||||
*/
|
||||
void gs_buf_clear(struct gs_buf *gb)
|
||||
static void gs_buf_clear(struct gs_buf *gb)
|
||||
{
|
||||
if (gb != NULL)
|
||||
gb->buf_get = gb->buf_put;
|
||||
@ -2241,7 +2241,7 @@ void gs_buf_clear(struct gs_buf *gb)
|
||||
* Return the number of bytes of data available in the circular
|
||||
* buffer.
|
||||
*/
|
||||
unsigned int gs_buf_data_avail(struct gs_buf *gb)
|
||||
static unsigned int gs_buf_data_avail(struct gs_buf *gb)
|
||||
{
|
||||
if (gb != NULL)
|
||||
return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
|
||||
@ -2255,7 +2255,7 @@ unsigned int gs_buf_data_avail(struct gs_buf *gb)
|
||||
* Return the number of bytes of space available in the circular
|
||||
* buffer.
|
||||
*/
|
||||
unsigned int gs_buf_space_avail(struct gs_buf *gb)
|
||||
static unsigned int gs_buf_space_avail(struct gs_buf *gb)
|
||||
{
|
||||
if (gb != NULL)
|
||||
return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
|
||||
@ -2271,7 +2271,8 @@ unsigned int gs_buf_space_avail(struct gs_buf *gb)
|
||||
*
|
||||
* Return the number of bytes copied.
|
||||
*/
|
||||
unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count)
|
||||
static unsigned int
|
||||
gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
@ -2309,7 +2310,8 @@ unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count)
|
||||
*
|
||||
* Return the number of bytes copied.
|
||||
*/
|
||||
unsigned int gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count)
|
||||
static unsigned int
|
||||
gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
|
@ -481,8 +481,7 @@ alloc_ep_req (struct usb_ep *ep, unsigned length)
|
||||
req = usb_ep_alloc_request (ep, GFP_ATOMIC);
|
||||
if (req) {
|
||||
req->length = length;
|
||||
req->buf = usb_ep_alloc_buffer (ep, length,
|
||||
&req->dma, GFP_ATOMIC);
|
||||
req->buf = kmalloc(length, GFP_ATOMIC);
|
||||
if (!req->buf) {
|
||||
usb_ep_free_request (ep, req);
|
||||
req = NULL;
|
||||
@ -493,8 +492,7 @@ alloc_ep_req (struct usb_ep *ep, unsigned length)
|
||||
|
||||
static void free_ep_req (struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
if (req->buf)
|
||||
usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request (ep, req);
|
||||
}
|
||||
|
||||
@ -1199,8 +1197,7 @@ autoconf_fail:
|
||||
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
|
||||
if (!dev->req)
|
||||
goto enomem;
|
||||
dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
|
||||
&dev->req->dma, GFP_KERNEL);
|
||||
dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
|
||||
if (!dev->req->buf)
|
||||
goto enomem;
|
||||
|
||||
|
@ -69,8 +69,20 @@ config USB_EHCI_TT_NEWSCHED
|
||||
|
||||
config USB_EHCI_BIG_ENDIAN_MMIO
|
||||
bool
|
||||
depends on USB_EHCI_HCD
|
||||
default n
|
||||
depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX)
|
||||
default y
|
||||
|
||||
config USB_EHCI_BIG_ENDIAN_DESC
|
||||
bool
|
||||
depends on USB_EHCI_HCD && 440EPX
|
||||
default y
|
||||
|
||||
config USB_EHCI_FSL
|
||||
bool
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
default y if MPC834x || PPC_MPC831x
|
||||
---help---
|
||||
Variation of ARC USB block used in some Freescale chips.
|
||||
|
||||
config USB_ISP116X_HCD
|
||||
tristate "ISP116X HCD support"
|
||||
@ -224,3 +236,15 @@ config USB_SL811_CS
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called "sl811_cs".
|
||||
|
||||
config USB_R8A66597_HCD
|
||||
tristate "R8A66597 HCD suppoort"
|
||||
depends on USB
|
||||
help
|
||||
The R8A66597 is a USB 2.0 host and peripheral controller.
|
||||
|
||||
Enable this option if your board has this chip, and you want
|
||||
to use it as a host controller. If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called r8a66597-hcd.
|
||||
|
||||
|
@ -15,3 +15,5 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
|
||||
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
|
||||
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
|
||||
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
|
||||
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
|
||||
|
||||
|
@ -52,7 +52,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
|
||||
HCS_INDICATOR (params) ? " ind" : "",
|
||||
HCS_N_CC (params),
|
||||
HCS_N_PCC (params),
|
||||
HCS_PORTROUTED (params) ? "" : " ordered",
|
||||
HCS_PORTROUTED (params) ? "" : " ordered",
|
||||
HCS_PPC (params) ? "" : " !ppc",
|
||||
HCS_N_PORTS (params)
|
||||
);
|
||||
@ -91,20 +91,20 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
|
||||
|
||||
if (HCC_ISOC_CACHE (params)) {
|
||||
ehci_dbg (ehci,
|
||||
"%s hcc_params %04x caching frame %s%s%s\n",
|
||||
label, params,
|
||||
HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
|
||||
HCC_CANPARK (params) ? " park" : "",
|
||||
HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
|
||||
"%s hcc_params %04x caching frame %s%s%s\n",
|
||||
label, params,
|
||||
HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
|
||||
HCC_CANPARK(params) ? " park" : "",
|
||||
HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
|
||||
} else {
|
||||
ehci_dbg (ehci,
|
||||
"%s hcc_params %04x thresh %d uframes %s%s%s\n",
|
||||
label,
|
||||
params,
|
||||
HCC_ISOC_THRES (params),
|
||||
HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
|
||||
HCC_CANPARK (params) ? " park" : "",
|
||||
HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
|
||||
"%s hcc_params %04x thresh %d uframes %s%s%s\n",
|
||||
label,
|
||||
params,
|
||||
HCC_ISOC_THRES(params),
|
||||
HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
|
||||
HCC_CANPARK(params) ? " park" : "",
|
||||
HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -115,23 +115,23 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
static void __attribute__((__unused__))
|
||||
static void __maybe_unused
|
||||
dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
||||
{
|
||||
ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
|
||||
le32_to_cpup (&qtd->hw_next),
|
||||
le32_to_cpup (&qtd->hw_alt_next),
|
||||
le32_to_cpup (&qtd->hw_token),
|
||||
le32_to_cpup (&qtd->hw_buf [0]));
|
||||
ehci_dbg(ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
|
||||
hc32_to_cpup(ehci, &qtd->hw_next),
|
||||
hc32_to_cpup(ehci, &qtd->hw_alt_next),
|
||||
hc32_to_cpup(ehci, &qtd->hw_token),
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf [0]));
|
||||
if (qtd->hw_buf [1])
|
||||
ehci_dbg (ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
|
||||
le32_to_cpup (&qtd->hw_buf [1]),
|
||||
le32_to_cpup (&qtd->hw_buf [2]),
|
||||
le32_to_cpup (&qtd->hw_buf [3]),
|
||||
le32_to_cpup (&qtd->hw_buf [4]));
|
||||
ehci_dbg(ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[1]),
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[2]),
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[3]),
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[4]));
|
||||
}
|
||||
|
||||
static void __attribute__((__unused__))
|
||||
static void __maybe_unused
|
||||
dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{
|
||||
ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
|
||||
@ -140,51 +140,53 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
|
||||
}
|
||||
|
||||
static void __attribute__((__unused__))
|
||||
static void __maybe_unused
|
||||
dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
|
||||
{
|
||||
ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
|
||||
label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
|
||||
label, itd->frame, itd, hc32_to_cpu(ehci, itd->hw_next),
|
||||
itd->urb);
|
||||
ehci_dbg (ehci,
|
||||
" trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
|
||||
le32_to_cpu(itd->hw_transaction[0]),
|
||||
le32_to_cpu(itd->hw_transaction[1]),
|
||||
le32_to_cpu(itd->hw_transaction[2]),
|
||||
le32_to_cpu(itd->hw_transaction[3]),
|
||||
le32_to_cpu(itd->hw_transaction[4]),
|
||||
le32_to_cpu(itd->hw_transaction[5]),
|
||||
le32_to_cpu(itd->hw_transaction[6]),
|
||||
le32_to_cpu(itd->hw_transaction[7]));
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[0]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[1]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[2]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[3]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[4]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[5]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[6]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[7]));
|
||||
ehci_dbg (ehci,
|
||||
" buf: %08x %08x %08x %08x %08x %08x %08x\n",
|
||||
le32_to_cpu(itd->hw_bufp[0]),
|
||||
le32_to_cpu(itd->hw_bufp[1]),
|
||||
le32_to_cpu(itd->hw_bufp[2]),
|
||||
le32_to_cpu(itd->hw_bufp[3]),
|
||||
le32_to_cpu(itd->hw_bufp[4]),
|
||||
le32_to_cpu(itd->hw_bufp[5]),
|
||||
le32_to_cpu(itd->hw_bufp[6]));
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[0]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[1]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[2]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[3]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[4]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[5]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[6]));
|
||||
ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n",
|
||||
itd->index[0], itd->index[1], itd->index[2],
|
||||
itd->index[3], itd->index[4], itd->index[5],
|
||||
itd->index[6], itd->index[7]);
|
||||
}
|
||||
|
||||
static void __attribute__((__unused__))
|
||||
static void __maybe_unused
|
||||
dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
|
||||
{
|
||||
ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
|
||||
label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
|
||||
label, sitd->frame, sitd, hc32_to_cpu(ehci, sitd->hw_next),
|
||||
sitd->urb);
|
||||
ehci_dbg (ehci,
|
||||
" addr %08x sched %04x result %08x buf %08x %08x\n",
|
||||
le32_to_cpu(sitd->hw_fullspeed_ep),
|
||||
le32_to_cpu(sitd->hw_uframe),
|
||||
le32_to_cpu(sitd->hw_results),
|
||||
le32_to_cpu(sitd->hw_buf [0]),
|
||||
le32_to_cpu(sitd->hw_buf [1]));
|
||||
hc32_to_cpu(ehci, sitd->hw_fullspeed_ep),
|
||||
hc32_to_cpu(ehci, sitd->hw_uframe),
|
||||
hc32_to_cpu(ehci, sitd->hw_results),
|
||||
hc32_to_cpu(ehci, sitd->hw_buf[0]),
|
||||
hc32_to_cpu(ehci, sitd->hw_buf[1]));
|
||||
}
|
||||
|
||||
static int __attribute__((__unused__))
|
||||
static int __maybe_unused
|
||||
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
@ -203,7 +205,7 @@ dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
||||
);
|
||||
}
|
||||
|
||||
static int __attribute__((__unused__))
|
||||
static int __maybe_unused
|
||||
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
@ -267,28 +269,27 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
(status & PORT_PEC) ? " PEC" : "",
|
||||
(status & PORT_PE) ? " PE" : "",
|
||||
(status & PORT_CSC) ? " CSC" : "",
|
||||
(status & PORT_CONNECT) ? " CONNECT" : ""
|
||||
);
|
||||
(status & PORT_CONNECT) ? " CONNECT" : "");
|
||||
}
|
||||
|
||||
#else
|
||||
static inline void __attribute__((__unused__))
|
||||
static inline void __maybe_unused
|
||||
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{}
|
||||
|
||||
static inline int __attribute__((__unused__))
|
||||
static inline int __maybe_unused
|
||||
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
||||
{ return 0; }
|
||||
|
||||
static inline int __attribute__((__unused__))
|
||||
static inline int __maybe_unused
|
||||
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
|
||||
{ return 0; }
|
||||
|
||||
static inline int __attribute__((__unused__))
|
||||
static inline int __maybe_unused
|
||||
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
|
||||
{ return 0; }
|
||||
|
||||
static inline int __attribute__((__unused__))
|
||||
static inline int __maybe_unused
|
||||
dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
{ return 0; }
|
||||
|
||||
@ -332,9 +333,10 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { }
|
||||
default: tmp = '?'; break; \
|
||||
}; tmp; })
|
||||
|
||||
static inline char token_mark (__le32 token)
|
||||
static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
|
||||
{
|
||||
__u32 v = le32_to_cpu (token);
|
||||
__u32 v = hc32_to_cpu(ehci, token);
|
||||
|
||||
if (v & QTD_STS_ACTIVE)
|
||||
return '*';
|
||||
if (v & QTD_STS_HALT)
|
||||
@ -360,46 +362,48 @@ static void qh_lines (
|
||||
unsigned size = *sizep;
|
||||
char *next = *nextp;
|
||||
char mark;
|
||||
u32 list_end = EHCI_LIST_END(ehci);
|
||||
|
||||
if (qh->hw_qtd_next == EHCI_LIST_END) /* NEC does this */
|
||||
if (qh->hw_qtd_next == list_end) /* NEC does this */
|
||||
mark = '@';
|
||||
else
|
||||
mark = token_mark (qh->hw_token);
|
||||
mark = token_mark(ehci, qh->hw_token);
|
||||
if (mark == '/') { /* qh_alt_next controls qh advance? */
|
||||
if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
|
||||
if ((qh->hw_alt_next & QTD_MASK(ehci))
|
||||
== ehci->async->hw_alt_next)
|
||||
mark = '#'; /* blocked */
|
||||
else if (qh->hw_alt_next == EHCI_LIST_END)
|
||||
else if (qh->hw_alt_next == list_end)
|
||||
mark = '.'; /* use hw_qtd_next */
|
||||
/* else alt_next points to some other qtd */
|
||||
}
|
||||
scratch = le32_to_cpup (&qh->hw_info1);
|
||||
hw_curr = (mark == '*') ? le32_to_cpup (&qh->hw_current) : 0;
|
||||
scratch = hc32_to_cpup(ehci, &qh->hw_info1);
|
||||
hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &qh->hw_current) : 0;
|
||||
temp = scnprintf (next, size,
|
||||
"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
|
||||
qh, scratch & 0x007f,
|
||||
speed_char (scratch),
|
||||
(scratch >> 8) & 0x000f,
|
||||
scratch, le32_to_cpup (&qh->hw_info2),
|
||||
le32_to_cpup (&qh->hw_token), mark,
|
||||
(__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token)
|
||||
scratch, hc32_to_cpup(ehci, &qh->hw_info2),
|
||||
hc32_to_cpup(ehci, &qh->hw_token), mark,
|
||||
(cpu_to_hc32(ehci, QTD_TOGGLE) & qh->hw_token)
|
||||
? "data1" : "data0",
|
||||
(le32_to_cpup (&qh->hw_alt_next) >> 1) & 0x0f);
|
||||
(hc32_to_cpup(ehci, &qh->hw_alt_next) >> 1) & 0x0f);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
/* hc may be modifying the list as we read it ... */
|
||||
list_for_each (entry, &qh->qtd_list) {
|
||||
td = list_entry (entry, struct ehci_qtd, qtd_list);
|
||||
scratch = le32_to_cpup (&td->hw_token);
|
||||
scratch = hc32_to_cpup(ehci, &td->hw_token);
|
||||
mark = ' ';
|
||||
if (hw_curr == td->qtd_dma)
|
||||
mark = '*';
|
||||
else if (qh->hw_qtd_next == cpu_to_le32(td->qtd_dma))
|
||||
else if (qh->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
|
||||
mark = '+';
|
||||
else if (QTD_LENGTH (scratch)) {
|
||||
if (td->hw_alt_next == ehci->async->hw_alt_next)
|
||||
mark = '#';
|
||||
else if (td->hw_alt_next != EHCI_LIST_END)
|
||||
else if (td->hw_alt_next != list_end)
|
||||
mark = '/';
|
||||
}
|
||||
temp = snprintf (next, size,
|
||||
@ -490,7 +494,7 @@ show_periodic (struct class_device *class_dev, char *buf)
|
||||
unsigned temp, size, seen_count;
|
||||
char *next;
|
||||
unsigned i;
|
||||
__le32 tag;
|
||||
__hc32 tag;
|
||||
|
||||
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
|
||||
return 0;
|
||||
@ -514,18 +518,19 @@ show_periodic (struct class_device *class_dev, char *buf)
|
||||
p = ehci->pshadow [i];
|
||||
if (likely (!p.ptr))
|
||||
continue;
|
||||
tag = Q_NEXT_TYPE (ehci->periodic [i]);
|
||||
tag = Q_NEXT_TYPE(ehci, ehci->periodic [i]);
|
||||
|
||||
temp = scnprintf (next, size, "%4d: ", i);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
do {
|
||||
switch (tag) {
|
||||
switch (hc32_to_cpu(ehci, tag)) {
|
||||
case Q_TYPE_QH:
|
||||
temp = scnprintf (next, size, " qh%d-%04x/%p",
|
||||
p.qh->period,
|
||||
le32_to_cpup (&p.qh->hw_info2)
|
||||
hc32_to_cpup(ehci,
|
||||
&p.qh->hw_info2)
|
||||
/* uframe masks */
|
||||
& (QH_CMASK | QH_SMASK),
|
||||
p.qh);
|
||||
@ -543,7 +548,7 @@ show_periodic (struct class_device *class_dev, char *buf)
|
||||
}
|
||||
/* show more info the first time around */
|
||||
if (temp == seen_count && p.ptr) {
|
||||
u32 scratch = le32_to_cpup (
|
||||
u32 scratch = hc32_to_cpup(ehci,
|
||||
&p.qh->hw_info1);
|
||||
struct ehci_qtd *qtd;
|
||||
char *type = "";
|
||||
@ -554,7 +559,8 @@ show_periodic (struct class_device *class_dev, char *buf)
|
||||
&p.qh->qtd_list,
|
||||
qtd_list) {
|
||||
temp++;
|
||||
switch (0x03 & (le32_to_cpu (
|
||||
switch (0x03 & (hc32_to_cpu(
|
||||
ehci,
|
||||
qtd->hw_token) >> 8)) {
|
||||
case 0: type = "out"; continue;
|
||||
case 1: type = "in"; continue;
|
||||
@ -576,7 +582,7 @@ show_periodic (struct class_device *class_dev, char *buf)
|
||||
} else
|
||||
temp = 0;
|
||||
if (p.qh) {
|
||||
tag = Q_NEXT_TYPE (p.qh->hw_next);
|
||||
tag = Q_NEXT_TYPE(ehci, p.qh->hw_next);
|
||||
p = p.qh->qh_next;
|
||||
}
|
||||
break;
|
||||
@ -584,23 +590,23 @@ show_periodic (struct class_device *class_dev, char *buf)
|
||||
temp = scnprintf (next, size,
|
||||
" fstn-%8x/%p", p.fstn->hw_prev,
|
||||
p.fstn);
|
||||
tag = Q_NEXT_TYPE (p.fstn->hw_next);
|
||||
tag = Q_NEXT_TYPE(ehci, p.fstn->hw_next);
|
||||
p = p.fstn->fstn_next;
|
||||
break;
|
||||
case Q_TYPE_ITD:
|
||||
temp = scnprintf (next, size,
|
||||
" itd/%p", p.itd);
|
||||
tag = Q_NEXT_TYPE (p.itd->hw_next);
|
||||
tag = Q_NEXT_TYPE(ehci, p.itd->hw_next);
|
||||
p = p.itd->itd_next;
|
||||
break;
|
||||
case Q_TYPE_SITD:
|
||||
temp = scnprintf (next, size,
|
||||
" sitd%d-%04x/%p",
|
||||
p.sitd->stream->interval,
|
||||
le32_to_cpup (&p.sitd->hw_uframe)
|
||||
hc32_to_cpup(ehci, &p.sitd->hw_uframe)
|
||||
& 0x0000ffff,
|
||||
p.sitd);
|
||||
tag = Q_NEXT_TYPE (p.sitd->hw_next);
|
||||
tag = Q_NEXT_TYPE(ehci, p.sitd->hw_next);
|
||||
p = p.sitd->sitd_next;
|
||||
break;
|
||||
}
|
||||
@ -673,7 +679,8 @@ show_registers (struct class_device *class_dev, char *buf)
|
||||
unsigned count = 256/4;
|
||||
|
||||
pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
|
||||
offset = HCC_EXT_CAPS (ehci_readl(ehci, &ehci->caps->hcc_params));
|
||||
offset = HCC_EXT_CAPS(ehci_readl(ehci,
|
||||
&ehci->caps->hcc_params));
|
||||
while (offset && count--) {
|
||||
pci_read_config_dword (pdev, offset, &cap);
|
||||
switch (cap & 0xff) {
|
||||
@ -740,14 +747,16 @@ show_registers (struct class_device *class_dev, char *buf)
|
||||
|
||||
for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) {
|
||||
temp = dbg_port_buf (scratch, sizeof scratch, label, i,
|
||||
ehci_readl(ehci, &ehci->regs->port_status [i - 1]));
|
||||
ehci_readl(ehci,
|
||||
&ehci->regs->port_status[i - 1]));
|
||||
temp = scnprintf (next, size, fmt, temp, scratch);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
if (i == HCS_DEBUG_PORT(ehci->hcs_params) && ehci->debug) {
|
||||
temp = scnprintf (next, size,
|
||||
" debug control %08x\n",
|
||||
ehci_readl(ehci, &ehci->debug->control));
|
||||
ehci_readl(ehci,
|
||||
&ehci->debug->control));
|
||||
size -= temp;
|
||||
next += temp;
|
||||
}
|
||||
|
@ -67,7 +67,8 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
|
||||
* in host mode.
|
||||
*/
|
||||
if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
|
||||
(pdata->operating_mode == FSL_USB2_MPH_HOST))) {
|
||||
(pdata->operating_mode == FSL_USB2_MPH_HOST) ||
|
||||
(pdata->operating_mode == FSL_USB2_DR_OTG))) {
|
||||
dev_err(&pdev->dev,
|
||||
"Non Host Mode configured for %s. Wrong driver linked.\n",
|
||||
pdev->dev.bus_id);
|
||||
@ -185,12 +186,14 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct fsl_usb2_platform_data *pdata;
|
||||
void __iomem *non_ehci = hcd->regs;
|
||||
u32 temp;
|
||||
|
||||
pdata =
|
||||
(struct fsl_usb2_platform_data *)hcd->self.controller->
|
||||
platform_data;
|
||||
/* Enable PHY interface in the control reg. */
|
||||
out_be32(non_ehci + FSL_SOC_USB_CTRL, 0x00000004);
|
||||
temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
|
||||
out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
|
||||
out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
|
||||
|
||||
#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
|
||||
@ -206,7 +209,8 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
|
||||
out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB);
|
||||
#endif
|
||||
|
||||
if (pdata->operating_mode == FSL_USB2_DR_HOST)
|
||||
if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
|
||||
(pdata->operating_mode == FSL_USB2_DR_OTG))
|
||||
mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
|
||||
|
||||
if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
|
||||
|
@ -41,10 +41,6 @@
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/unaligned.h>
|
||||
#ifdef CONFIG_PPC_PS3
|
||||
#include <asm/firmware.h>
|
||||
#endif
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -201,9 +197,15 @@ static void tdi_reset (struct ehci_hcd *ehci)
|
||||
u32 __iomem *reg_ptr;
|
||||
u32 tmp;
|
||||
|
||||
reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + 0x68);
|
||||
reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE);
|
||||
tmp = ehci_readl(ehci, reg_ptr);
|
||||
tmp |= 0x3;
|
||||
tmp |= USBMODE_CM_HC;
|
||||
/* The default byte access to MMR space is LE after
|
||||
* controller reset. Set the required endian mode
|
||||
* for transfer buffers to match the host microprocessor
|
||||
*/
|
||||
if (ehci_big_endian_mmio(ehci))
|
||||
tmp |= USBMODE_BE;
|
||||
ehci_writel(ehci, tmp, reg_ptr);
|
||||
}
|
||||
|
||||
@ -273,6 +275,58 @@ static void ehci_work(struct ehci_hcd *ehci);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
|
||||
#include <linux/cpufreq.h>
|
||||
|
||||
static void ehci_cpufreq_pause (struct ehci_hcd *ehci)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
if (!ehci->cpufreq_changing++)
|
||||
qh_inactivate_split_intr_qhs(ehci);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
}
|
||||
|
||||
static void ehci_cpufreq_unpause (struct ehci_hcd *ehci)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
if (!--ehci->cpufreq_changing)
|
||||
qh_reactivate_split_intr_qhs(ehci);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* ehci_cpufreq_notifier is needed to avoid MMF errors that occur when
|
||||
* EHCI controllers that don't cache many uframes get delayed trying to
|
||||
* read main memory during CPU frequency transitions. This can cause
|
||||
* split interrupt transactions to not be completed in the required uframe.
|
||||
* This has been observed on the Broadcom/ServerWorks HT1000 controller.
|
||||
*/
|
||||
static int ehci_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *data)
|
||||
{
|
||||
struct ehci_hcd *ehci = container_of(nb, struct ehci_hcd,
|
||||
cpufreq_transition);
|
||||
|
||||
switch (val) {
|
||||
case CPUFREQ_PRECHANGE:
|
||||
ehci_cpufreq_pause(ehci);
|
||||
break;
|
||||
case CPUFREQ_POSTCHANGE:
|
||||
ehci_cpufreq_unpause(ehci);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void ehci_watchdog (unsigned long param)
|
||||
{
|
||||
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
|
||||
@ -347,6 +401,8 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
|
||||
is_on ? SetPortFeature : ClearPortFeature,
|
||||
USB_PORT_FEAT_POWER,
|
||||
port--, NULL, 0);
|
||||
/* Flush those writes */
|
||||
ehci_readl(ehci, &ehci->regs->command);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
@ -404,6 +460,10 @@ static void ehci_stop (struct usb_hcd *hcd)
|
||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
cpufreq_unregister_notifier(&ehci->cpufreq_transition,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
#endif
|
||||
/* let companion controllers work when we aren't */
|
||||
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
|
||||
|
||||
@ -470,12 +530,12 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||
* from automatically advancing to the next td after short reads.
|
||||
*/
|
||||
ehci->async->qh_next.qh = NULL;
|
||||
ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma);
|
||||
ehci->async->hw_info1 = cpu_to_le32(QH_HEAD);
|
||||
ehci->async->hw_token = cpu_to_le32(QTD_STS_HALT);
|
||||
ehci->async->hw_qtd_next = EHCI_LIST_END;
|
||||
ehci->async->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
|
||||
ehci->async->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
|
||||
ehci->async->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
|
||||
ehci->async->hw_qtd_next = EHCI_LIST_END(ehci);
|
||||
ehci->async->qh_state = QH_STATE_LINKED;
|
||||
ehci->async->hw_alt_next = QTD_NEXT(ehci->async->dummy->qtd_dma);
|
||||
ehci->async->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
|
||||
|
||||
/* clear interrupt enables, set irq latency */
|
||||
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
|
||||
@ -509,6 +569,17 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||
}
|
||||
ehci->command = temp;
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
INIT_LIST_HEAD(&ehci->split_intr_qhs);
|
||||
/*
|
||||
* If the EHCI controller caches enough uframes, this probably
|
||||
* isn't needed unless there are so many low/full speed devices
|
||||
* that the controller's can't cache it all.
|
||||
*/
|
||||
ehci->cpufreq_transition.notifier_call = ehci_cpufreq_notifier;
|
||||
cpufreq_register_notifier(&ehci->cpufreq_transition,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -925,7 +996,7 @@ MODULE_LICENSE ("GPL");
|
||||
#define PCI_DRIVER ehci_pci_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MPC834x
|
||||
#ifdef CONFIG_USB_EHCI_FSL
|
||||
#include "ehci-fsl.c"
|
||||
#define PLATFORM_DRIVER ehci_fsl_driver
|
||||
#endif
|
||||
@ -937,7 +1008,12 @@ MODULE_LICENSE ("GPL");
|
||||
|
||||
#ifdef CONFIG_PPC_PS3
|
||||
#include "ehci-ps3.c"
|
||||
#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_sb_driver
|
||||
#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_440EPX
|
||||
#include "ehci-ppc-soc.c"
|
||||
#define PLATFORM_DRIVER ehci_ppc_soc_driver
|
||||
#endif
|
||||
|
||||
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
|
||||
@ -971,18 +1047,15 @@ static int __init ehci_hcd_init(void)
|
||||
#endif
|
||||
|
||||
#ifdef PS3_SYSTEM_BUS_DRIVER
|
||||
if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
|
||||
retval = ps3_system_bus_driver_register(
|
||||
&PS3_SYSTEM_BUS_DRIVER);
|
||||
if (retval < 0) {
|
||||
retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
|
||||
if (retval < 0) {
|
||||
#ifdef PLATFORM_DRIVER
|
||||
platform_driver_unregister(&PLATFORM_DRIVER);
|
||||
platform_driver_unregister(&PLATFORM_DRIVER);
|
||||
#endif
|
||||
#ifdef PCI_DRIVER
|
||||
pci_unregister_driver(&PCI_DRIVER);
|
||||
pci_unregister_driver(&PCI_DRIVER);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -999,8 +1072,7 @@ static void __exit ehci_hcd_cleanup(void)
|
||||
pci_unregister_driver(&PCI_DRIVER);
|
||||
#endif
|
||||
#ifdef PS3_SYSTEM_BUS_DRIVER
|
||||
if (firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
|
||||
ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
|
||||
#endif
|
||||
}
|
||||
module_exit(ehci_hcd_cleanup);
|
||||
|
@ -28,6 +28,89 @@
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_USB_PERSIST
|
||||
|
||||
static int ehci_hub_control(
|
||||
struct usb_hcd *hcd,
|
||||
u16 typeReq,
|
||||
u16 wValue,
|
||||
u16 wIndex,
|
||||
char *buf,
|
||||
u16 wLength
|
||||
);
|
||||
|
||||
/* After a power loss, ports that were owned by the companion must be
|
||||
* reset so that the companion can still own them.
|
||||
*/
|
||||
static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
||||
{
|
||||
u32 __iomem *reg;
|
||||
u32 status;
|
||||
int port;
|
||||
__le32 buf;
|
||||
struct usb_hcd *hcd = ehci_to_hcd(ehci);
|
||||
|
||||
if (!ehci->owned_ports)
|
||||
return;
|
||||
|
||||
/* Give the connections some time to appear */
|
||||
msleep(20);
|
||||
|
||||
port = HCS_N_PORTS(ehci->hcs_params);
|
||||
while (port--) {
|
||||
if (test_bit(port, &ehci->owned_ports)) {
|
||||
reg = &ehci->regs->port_status[port];
|
||||
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
|
||||
|
||||
/* Port already owned by companion? */
|
||||
if (status & PORT_OWNER)
|
||||
clear_bit(port, &ehci->owned_ports);
|
||||
else if (test_bit(port, &ehci->companion_ports))
|
||||
ehci_writel(ehci, status & ~PORT_PE, reg);
|
||||
else
|
||||
ehci_hub_control(hcd, SetPortFeature,
|
||||
USB_PORT_FEAT_RESET, port + 1,
|
||||
NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ehci->owned_ports)
|
||||
return;
|
||||
msleep(90); /* Wait for resets to complete */
|
||||
|
||||
port = HCS_N_PORTS(ehci->hcs_params);
|
||||
while (port--) {
|
||||
if (test_bit(port, &ehci->owned_ports)) {
|
||||
ehci_hub_control(hcd, GetPortStatus,
|
||||
0, port + 1,
|
||||
(char *) &buf, sizeof(buf));
|
||||
|
||||
/* The companion should now own the port,
|
||||
* but if something went wrong the port must not
|
||||
* remain enabled.
|
||||
*/
|
||||
reg = &ehci->regs->port_status[port];
|
||||
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
|
||||
if (status & PORT_OWNER)
|
||||
ehci_writel(ehci, status | PORT_CSC, reg);
|
||||
else {
|
||||
ehci_dbg(ehci, "failed handover port %d: %x\n",
|
||||
port + 1, status);
|
||||
ehci_writel(ehci, status & ~PORT_PE, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ehci->owned_ports = 0;
|
||||
}
|
||||
|
||||
#else /* CONFIG_USB_PERSIST */
|
||||
|
||||
static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
||||
{ }
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||
@ -60,14 +143,16 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||
* then manually resume them in the bus_resume() routine.
|
||||
*/
|
||||
ehci->bus_suspended = 0;
|
||||
ehci->owned_ports = 0;
|
||||
while (port--) {
|
||||
u32 __iomem *reg = &ehci->regs->port_status [port];
|
||||
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
|
||||
u32 t2 = t1;
|
||||
|
||||
/* keep track of which ports we suspend */
|
||||
if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) &&
|
||||
!(t1 & PORT_SUSPEND)) {
|
||||
if (t1 & PORT_OWNER)
|
||||
set_bit(port, &ehci->owned_ports);
|
||||
else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
|
||||
t2 |= PORT_SUSPEND;
|
||||
set_bit(port, &ehci->bus_suspended);
|
||||
}
|
||||
@ -108,11 +193,16 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
u32 temp;
|
||||
u32 power_okay;
|
||||
int i;
|
||||
|
||||
if (time_before (jiffies, ehci->next_statechange))
|
||||
msleep(5);
|
||||
spin_lock_irq (&ehci->lock);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
/* Ideally and we've got a real resume here, and no port's power
|
||||
* was lost. (For PCI, that means Vaux was maintained.) But we
|
||||
@ -120,8 +210,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||
* the last user of the controller, not reset/pm hardware keeping
|
||||
* state we gave to it.
|
||||
*/
|
||||
temp = ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||
ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss");
|
||||
power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||
ehci_dbg(ehci, "resume root hub%s\n",
|
||||
power_okay ? "" : " after power loss");
|
||||
|
||||
/* at least some APM implementations will try to deliver
|
||||
* IRQs right away, so delay them until we're ready.
|
||||
@ -184,6 +275,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
|
||||
|
||||
spin_unlock_irq (&ehci->lock);
|
||||
|
||||
if (!power_okay)
|
||||
ehci_handover_companion_ports(ehci);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -448,7 +542,8 @@ static int ehci_hub_control (
|
||||
) {
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
int ports = HCS_N_PORTS (ehci->hcs_params);
|
||||
u32 __iomem *status_reg = &ehci->regs->port_status[wIndex - 1];
|
||||
u32 __iomem *status_reg = &ehci->regs->port_status[
|
||||
(wIndex & 0xff) - 1];
|
||||
u32 temp, status;
|
||||
unsigned long flags;
|
||||
int retval = 0;
|
||||
@ -556,9 +651,24 @@ static int ehci_hub_control (
|
||||
status |= 1 << USB_PORT_FEAT_C_CONNECTION;
|
||||
if (temp & PORT_PEC)
|
||||
status |= 1 << USB_PORT_FEAT_C_ENABLE;
|
||||
if ((temp & PORT_OCC) && !ignore_oc)
|
||||
|
||||
if ((temp & PORT_OCC) && !ignore_oc){
|
||||
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
|
||||
|
||||
/*
|
||||
* Hubs should disable port power on over-current.
|
||||
* However, not all EHCI implementations do this
|
||||
* automatically, even if they _do_ support per-port
|
||||
* power switching; they're allowed to just limit the
|
||||
* current. khubd will turn the power back on.
|
||||
*/
|
||||
if (HCS_PPC (ehci->hcs_params)){
|
||||
ehci_writel(ehci,
|
||||
temp & ~(PORT_RWC_BITS | PORT_POWER),
|
||||
status_reg);
|
||||
}
|
||||
}
|
||||
|
||||
/* whoever resumes must GetPortStatus to complete it!! */
|
||||
if (temp & PORT_RESUME) {
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
* need to use dma_pool or dma_alloc_coherent
|
||||
* - driver buffers, read/written by HC ... single shot DMA mapped
|
||||
*
|
||||
* There's also PCI "register" data, which is memory mapped.
|
||||
* There's also "register" data (e.g. PCI or SOC), which is memory mapped.
|
||||
* No memory seen by this driver is pageable.
|
||||
*/
|
||||
|
||||
@ -35,13 +35,14 @@
|
||||
|
||||
/* Allocate the key transfer structures from the previously allocated pool */
|
||||
|
||||
static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
|
||||
static inline void ehci_qtd_init(struct ehci_hcd *ehci, struct ehci_qtd *qtd,
|
||||
dma_addr_t dma)
|
||||
{
|
||||
memset (qtd, 0, sizeof *qtd);
|
||||
qtd->qtd_dma = dma;
|
||||
qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
|
||||
qtd->hw_next = EHCI_LIST_END;
|
||||
qtd->hw_alt_next = EHCI_LIST_END;
|
||||
qtd->hw_next = EHCI_LIST_END(ehci);
|
||||
qtd->hw_alt_next = EHCI_LIST_END(ehci);
|
||||
INIT_LIST_HEAD (&qtd->qtd_list);
|
||||
}
|
||||
|
||||
@ -52,7 +53,7 @@ static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, gfp_t flags)
|
||||
|
||||
qtd = dma_pool_alloc (ehci->qtd_pool, flags, &dma);
|
||||
if (qtd != NULL) {
|
||||
ehci_qtd_init (qtd, dma);
|
||||
ehci_qtd_init(ehci, qtd, dma);
|
||||
}
|
||||
return qtd;
|
||||
}
|
||||
@ -63,9 +64,8 @@ static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
||||
}
|
||||
|
||||
|
||||
static void qh_destroy (struct kref *kref)
|
||||
static void qh_destroy(struct ehci_qh *qh)
|
||||
{
|
||||
struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref);
|
||||
struct ehci_hcd *ehci = qh->ehci;
|
||||
|
||||
/* clean qtds first, and know this is not linked */
|
||||
@ -89,11 +89,14 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
|
||||
return qh;
|
||||
|
||||
memset (qh, 0, sizeof *qh);
|
||||
kref_init(&qh->kref);
|
||||
qh->refcount = 1;
|
||||
qh->ehci = ehci;
|
||||
qh->qh_dma = dma;
|
||||
// INIT_LIST_HEAD (&qh->qh_list);
|
||||
INIT_LIST_HEAD (&qh->qtd_list);
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
INIT_LIST_HEAD (&qh->split_intr_qhs);
|
||||
#endif
|
||||
|
||||
/* dummy td enables safe urb queuing */
|
||||
qh->dummy = ehci_qtd_alloc (ehci, flags);
|
||||
@ -108,13 +111,15 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
|
||||
/* to share a qh (cpu threads, or hc) */
|
||||
static inline struct ehci_qh *qh_get (struct ehci_qh *qh)
|
||||
{
|
||||
kref_get(&qh->kref);
|
||||
WARN_ON(!qh->refcount);
|
||||
qh->refcount++;
|
||||
return qh;
|
||||
}
|
||||
|
||||
static inline void qh_put (struct ehci_qh *qh)
|
||||
{
|
||||
kref_put(&qh->kref, qh_destroy);
|
||||
if (!--qh->refcount)
|
||||
qh_destroy(qh);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -217,7 +222,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
|
||||
goto fail;
|
||||
}
|
||||
for (i = 0; i < ehci->periodic_size; i++)
|
||||
ehci->periodic [i] = EHCI_LIST_END;
|
||||
ehci->periodic [i] = EHCI_LIST_END(ehci);
|
||||
|
||||
/* software shadow of hardware table */
|
||||
ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags);
|
||||
|
@ -312,13 +312,14 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
|
||||
ehci_work(ehci);
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
|
||||
/* here we "know" root ports should always stay powered */
|
||||
ehci_port_power(ehci, 1);
|
||||
|
||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
||||
|
||||
/* here we "know" root ports should always stay powered */
|
||||
ehci_port_power(ehci, 1);
|
||||
ehci_handover_companion_ports(ehci);
|
||||
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
return 0;
|
||||
}
|
||||
|
182
drivers/usb/host/ehci-ppc-soc.c
Normal file
182
drivers/usb/host/ehci-ppc-soc.c
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* EHCI HCD (Host Controller Driver) for USB.
|
||||
*
|
||||
* (C) Copyright 2006-2007 Stefan Roese <sr@denx.de>, DENX Software Engineering
|
||||
*
|
||||
* Bus Glue for PPC On-Chip EHCI driver
|
||||
* Tested on AMCC 440EPx
|
||||
*
|
||||
* Based on "ehci-au12xx.c" by David Brownell <dbrownell@users.sourceforge.net>
|
||||
*
|
||||
* This file is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
extern int usb_disabled(void);
|
||||
|
||||
/**
|
||||
* usb_ehci_ppc_soc_probe - initialize PPC-SoC-based HCDs
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Allocates basic resources for this USB host controller, and
|
||||
* then invokes the start() method for the HCD associated with it
|
||||
* through the hotplug entry's driver_data.
|
||||
*
|
||||
*/
|
||||
int usb_ehci_ppc_soc_probe(const struct hc_driver *driver,
|
||||
struct usb_hcd **hcd_out,
|
||||
struct platform_device *dev)
|
||||
{
|
||||
int retval;
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
|
||||
if (dev->resource[1].flags != IORESOURCE_IRQ) {
|
||||
pr_debug("resource[1] is not IORESOURCE_IRQ");
|
||||
retval = -ENOMEM;
|
||||
}
|
||||
hcd = usb_create_hcd(driver, &dev->dev, "PPC-SOC EHCI");
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
hcd->rsrc_start = dev->resource[0].start;
|
||||
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
|
||||
pr_debug("request_mem_region failed");
|
||||
retval = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
pr_debug("ioremap failed");
|
||||
retval = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->big_endian_mmio = 1;
|
||||
ehci->big_endian_desc = 1;
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
#if defined(CONFIG_440EPX)
|
||||
/*
|
||||
* 440EPx Errata USBH_3
|
||||
* Fix: Enable Break Memory Transfer (BMT) in INSNREG3
|
||||
*/
|
||||
out_be32((void *)((ulong)(&ehci->regs->command) + 0x8c), (1 << 0));
|
||||
ehci_dbg(ehci, "Break Memory Transfer (BMT) has beed enabled!\n");
|
||||
#endif
|
||||
|
||||
retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);
|
||||
if (retval == 0)
|
||||
return retval;
|
||||
|
||||
iounmap(hcd->regs);
|
||||
err2:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
err1:
|
||||
usb_put_hcd(hcd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* may be called without controller electrically present */
|
||||
/* may be called with controller, bus, and devices active */
|
||||
|
||||
/**
|
||||
* usb_ehci_hcd_ppc_soc_remove - shutdown processing for PPC-SoC-based HCDs
|
||||
* @dev: USB Host Controller being removed
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Reverses the effect of usb_ehci_hcd_ppc_soc_probe(), first invoking
|
||||
* the HCD's stop() method. It is always called from a thread
|
||||
* context, normally "rmmod", "apmd", or something similar.
|
||||
*
|
||||
*/
|
||||
void usb_ehci_ppc_soc_remove(struct usb_hcd *hcd, struct platform_device *dev)
|
||||
{
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_ppc_soc_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "PPC-SOC EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ehci_init,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
#ifdef CONFIG_PM
|
||||
.hub_suspend = ehci_hub_suspend,
|
||||
.hub_resume = ehci_hub_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = NULL;
|
||||
int ret;
|
||||
|
||||
pr_debug("In ehci_hcd_ppc_soc_drv_probe\n");
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
ret = usb_ehci_ppc_soc_probe(&ehci_ppc_soc_hc_driver, &hcd, pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ehci_hcd_ppc_soc_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
usb_ehci_ppc_soc_remove(hcd, pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("ppc-soc-ehci");
|
||||
static struct platform_driver ehci_ppc_soc_driver = {
|
||||
.probe = ehci_hcd_ppc_soc_drv_probe,
|
||||
.remove = ehci_hcd_ppc_soc_drv_remove,
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "ppc-soc-ehci",
|
||||
.bus = &platform_bus_type
|
||||
}
|
||||
};
|
@ -18,6 +18,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/ps3.h>
|
||||
|
||||
static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
|
||||
@ -73,7 +74,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
|
||||
static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
int result;
|
||||
struct usb_hcd *hcd;
|
||||
@ -85,13 +86,30 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
|
||||
goto fail_start;
|
||||
}
|
||||
|
||||
result = ps3_open_hv_device(dev);
|
||||
|
||||
if (result) {
|
||||
dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_open;
|
||||
}
|
||||
|
||||
result = ps3_dma_region_create(dev->d_region);
|
||||
|
||||
if (result) {
|
||||
dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
|
||||
"(%d)\n", __func__, __LINE__, result);
|
||||
BUG_ON("check region type");
|
||||
goto fail_dma_region;
|
||||
}
|
||||
|
||||
result = ps3_mmio_region_create(dev->m_region);
|
||||
|
||||
if (result) {
|
||||
dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
|
||||
__func__, __LINE__);
|
||||
result = -EPERM;
|
||||
goto fail_mmio;
|
||||
goto fail_mmio_region;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
|
||||
@ -120,6 +138,11 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
|
||||
|
||||
hcd->rsrc_start = dev->m_region->lpar_addr;
|
||||
hcd->rsrc_len = dev->m_region->len;
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
|
||||
dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
|
||||
__func__, __LINE__);
|
||||
|
||||
hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
|
||||
|
||||
if (!hcd->regs) {
|
||||
@ -153,34 +176,73 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
|
||||
fail_add_hcd:
|
||||
iounmap(hcd->regs);
|
||||
fail_ioremap:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
ps3_io_irq_destroy(virq);
|
||||
fail_irq:
|
||||
ps3_free_mmio_region(dev->m_region);
|
||||
fail_mmio:
|
||||
fail_mmio_region:
|
||||
ps3_dma_region_free(dev->d_region);
|
||||
fail_dma_region:
|
||||
ps3_close_hv_device(dev);
|
||||
fail_open:
|
||||
fail_start:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_ehci_sb_remove(struct ps3_system_bus_device *dev)
|
||||
static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
unsigned int tmp;
|
||||
struct usb_hcd *hcd =
|
||||
(struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
BUG_ON(!hcd);
|
||||
|
||||
dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
|
||||
dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
|
||||
|
||||
tmp = hcd->irq;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
ps3_system_bus_set_driver_data(dev, NULL);
|
||||
|
||||
BUG_ON(!hcd->regs);
|
||||
iounmap(hcd->regs);
|
||||
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
ps3_io_irq_destroy(tmp);
|
||||
ps3_free_mmio_region(dev->m_region);
|
||||
|
||||
ps3_dma_region_free(dev->d_region);
|
||||
ps3_close_hv_device(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("ps3-ehci");
|
||||
static int ps3_ehci_driver_register(struct ps3_system_bus_driver *drv)
|
||||
{
|
||||
return firmware_has_feature(FW_FEATURE_PS3_LV1)
|
||||
? ps3_system_bus_driver_register(drv)
|
||||
: 0;
|
||||
}
|
||||
|
||||
static struct ps3_system_bus_driver ps3_ehci_sb_driver = {
|
||||
static void ps3_ehci_driver_unregister(struct ps3_system_bus_driver *drv)
|
||||
{
|
||||
if (firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
ps3_system_bus_driver_unregister(drv);
|
||||
}
|
||||
|
||||
MODULE_ALIAS(PS3_MODULE_ALIAS_EHCI);
|
||||
|
||||
static struct ps3_system_bus_driver ps3_ehci_driver = {
|
||||
.core.name = "ps3-ehci-driver",
|
||||
.core.owner = THIS_MODULE,
|
||||
.match_id = PS3_MATCH_ID_EHCI,
|
||||
.core = {
|
||||
.name = "ps3-ehci-driver",
|
||||
},
|
||||
.probe = ps3_ehci_sb_probe,
|
||||
.remove = ps3_ehci_sb_remove,
|
||||
.probe = ps3_ehci_probe,
|
||||
.remove = ps3_ehci_remove,
|
||||
.shutdown = ps3_ehci_remove,
|
||||
};
|
||||
|
@ -43,15 +43,15 @@
|
||||
/* fill a qtd, returning how much of the buffer we were able to queue up */
|
||||
|
||||
static int
|
||||
qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
|
||||
int token, int maxpacket)
|
||||
qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf,
|
||||
size_t len, int token, int maxpacket)
|
||||
{
|
||||
int i, count;
|
||||
u64 addr = buf;
|
||||
|
||||
/* one buffer entry per 4K ... first might be short or unaligned */
|
||||
qtd->hw_buf [0] = cpu_to_le32 ((u32)addr);
|
||||
qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32));
|
||||
qtd->hw_buf[0] = cpu_to_hc32(ehci, (u32)addr);
|
||||
qtd->hw_buf_hi[0] = cpu_to_hc32(ehci, (u32)(addr >> 32));
|
||||
count = 0x1000 - (buf & 0x0fff); /* rest of that page */
|
||||
if (likely (len < count)) /* ... iff needed */
|
||||
count = len;
|
||||
@ -62,8 +62,9 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
|
||||
/* per-qtd limit: from 16K to 20K (best alignment) */
|
||||
for (i = 1; count < len && i < 5; i++) {
|
||||
addr = buf;
|
||||
qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);
|
||||
qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));
|
||||
qtd->hw_buf[i] = cpu_to_hc32(ehci, (u32)addr);
|
||||
qtd->hw_buf_hi[i] = cpu_to_hc32(ehci,
|
||||
(u32)(addr >> 32));
|
||||
buf += 0x1000;
|
||||
if ((count + 0x1000) < len)
|
||||
count += 0x1000;
|
||||
@ -75,7 +76,7 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
|
||||
if (count != len)
|
||||
count -= (count % maxpacket);
|
||||
}
|
||||
qtd->hw_token = cpu_to_le32 ((count << 16) | token);
|
||||
qtd->hw_token = cpu_to_hc32(ehci, (count << 16) | token);
|
||||
qtd->length = count;
|
||||
|
||||
return count;
|
||||
@ -89,28 +90,28 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
|
||||
/* writes to an active overlay are unsafe */
|
||||
BUG_ON(qh->qh_state != QH_STATE_IDLE);
|
||||
|
||||
qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
|
||||
qh->hw_alt_next = EHCI_LIST_END;
|
||||
qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
|
||||
qh->hw_alt_next = EHCI_LIST_END(ehci);
|
||||
|
||||
/* Except for control endpoints, we make hardware maintain data
|
||||
* toggle (like OHCI) ... here (re)initialize the toggle in the QH,
|
||||
* and set the pseudo-toggle in udev. Only usb_clear_halt() will
|
||||
* ever clear it.
|
||||
*/
|
||||
if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) {
|
||||
if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
|
||||
unsigned is_out, epnum;
|
||||
|
||||
is_out = !(qtd->hw_token & cpu_to_le32(1 << 8));
|
||||
epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f;
|
||||
is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
|
||||
epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
|
||||
if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
|
||||
qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
|
||||
qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
|
||||
usb_settoggle (qh->dev, epnum, is_out, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
|
||||
wmb ();
|
||||
qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
|
||||
qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
|
||||
}
|
||||
|
||||
/* if it weren't for a common silicon quirk (writing the dummy into the qh
|
||||
@ -128,7 +129,7 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
qtd = list_entry (qh->qtd_list.next,
|
||||
struct ehci_qtd, qtd_list);
|
||||
/* first qtd may already be partially processed */
|
||||
if (cpu_to_le32 (qtd->qtd_dma) == qh->hw_current)
|
||||
if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw_current)
|
||||
qtd = NULL;
|
||||
}
|
||||
|
||||
@ -222,7 +223,7 @@ __acquires(ehci->lock)
|
||||
struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
|
||||
|
||||
/* S-mask in a QH means it's an interrupt urb */
|
||||
if ((qh->hw_info2 & __constant_cpu_to_le32 (QH_SMASK)) != 0) {
|
||||
if ((qh->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) {
|
||||
|
||||
/* ... update hc-wide periodic stats (for usbfs) */
|
||||
ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
|
||||
@ -277,7 +278,6 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||
* Chases up to qh->hw_current. Returns number of completions called,
|
||||
* indicating how much "real" work we did.
|
||||
*/
|
||||
#define HALT_BIT __constant_cpu_to_le32(QTD_STS_HALT)
|
||||
static unsigned
|
||||
qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{
|
||||
@ -287,6 +287,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
unsigned count = 0;
|
||||
int do_status = 0;
|
||||
u8 state;
|
||||
u32 halt = HALT_BIT(ehci);
|
||||
|
||||
if (unlikely (list_empty (&qh->qtd_list)))
|
||||
return count;
|
||||
@ -311,6 +312,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
struct urb *urb;
|
||||
u32 token = 0;
|
||||
|
||||
/* ignore QHs that are currently inactive */
|
||||
if (qh->hw_info1 & __constant_cpu_to_le32(QH_INACTIVATE))
|
||||
break;
|
||||
|
||||
qtd = list_entry (entry, struct ehci_qtd, qtd_list);
|
||||
urb = qtd->urb;
|
||||
|
||||
@ -330,7 +335,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
|
||||
/* hardware copies qtd out of qh overlay */
|
||||
rmb ();
|
||||
token = le32_to_cpu (qtd->hw_token);
|
||||
token = hc32_to_cpu(ehci, qtd->hw_token);
|
||||
|
||||
/* always clean up qtds the hc de-activated */
|
||||
if ((token & QTD_STS_ACTIVE) == 0) {
|
||||
@ -342,7 +347,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
* that silicon quirk can kick in with this dummy too.
|
||||
*/
|
||||
} else if (IS_SHORT_READ (token)
|
||||
&& !(qtd->hw_alt_next & EHCI_LIST_END)) {
|
||||
&& !(qtd->hw_alt_next
|
||||
& EHCI_LIST_END(ehci))) {
|
||||
stopped = 1;
|
||||
goto halt;
|
||||
}
|
||||
@ -374,17 +380,17 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
|
||||
/* token in overlay may be most current */
|
||||
if (state == QH_STATE_IDLE
|
||||
&& cpu_to_le32 (qtd->qtd_dma)
|
||||
&& cpu_to_hc32(ehci, qtd->qtd_dma)
|
||||
== qh->hw_current)
|
||||
token = le32_to_cpu (qh->hw_token);
|
||||
token = hc32_to_cpu(ehci, qh->hw_token);
|
||||
|
||||
/* force halt for unlinked or blocked qh, so we'll
|
||||
* patch the qh later and so that completions can't
|
||||
* activate it while we "know" it's stopped.
|
||||
*/
|
||||
if ((HALT_BIT & qh->hw_token) == 0) {
|
||||
if ((halt & qh->hw_token) == 0) {
|
||||
halt:
|
||||
qh->hw_token |= HALT_BIT;
|
||||
qh->hw_token |= halt;
|
||||
wmb ();
|
||||
}
|
||||
}
|
||||
@ -419,7 +425,7 @@ halt:
|
||||
* it after fault cleanup, or recovering from silicon wrongly
|
||||
* overlaying the dummy qtd (which reduces DMA chatter).
|
||||
*/
|
||||
if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) {
|
||||
if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) {
|
||||
switch (state) {
|
||||
case QH_STATE_IDLE:
|
||||
qh_refresh(ehci, qh);
|
||||
@ -428,7 +434,7 @@ halt:
|
||||
/* should be rare for periodic transfers,
|
||||
* except maybe high bandwidth ...
|
||||
*/
|
||||
if ((__constant_cpu_to_le32 (QH_SMASK)
|
||||
if ((cpu_to_hc32(ehci, QH_SMASK)
|
||||
& qh->hw_info2) != 0) {
|
||||
intr_deschedule (ehci, qh);
|
||||
(void) qh_schedule (ehci, qh);
|
||||
@ -502,8 +508,9 @@ qh_urb_transaction (
|
||||
is_input = usb_pipein (urb->pipe);
|
||||
if (usb_pipecontrol (urb->pipe)) {
|
||||
/* SETUP pid */
|
||||
qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest),
|
||||
token | (2 /* "setup" */ << 8), 8);
|
||||
qtd_fill(ehci, qtd, urb->setup_dma,
|
||||
sizeof (struct usb_ctrlrequest),
|
||||
token | (2 /* "setup" */ << 8), 8);
|
||||
|
||||
/* ... and always at least one more pid */
|
||||
token ^= QTD_TOGGLE;
|
||||
@ -512,7 +519,7 @@ qh_urb_transaction (
|
||||
if (unlikely (!qtd))
|
||||
goto cleanup;
|
||||
qtd->urb = urb;
|
||||
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
|
||||
qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
|
||||
list_add_tail (&qtd->qtd_list, head);
|
||||
|
||||
/* for zero length DATA stages, STATUS is always IN */
|
||||
@ -539,7 +546,7 @@ qh_urb_transaction (
|
||||
for (;;) {
|
||||
int this_qtd_len;
|
||||
|
||||
this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket);
|
||||
this_qtd_len = qtd_fill(ehci, qtd, buf, len, token, maxpacket);
|
||||
len -= this_qtd_len;
|
||||
buf += this_qtd_len;
|
||||
if (is_input)
|
||||
@ -557,7 +564,7 @@ qh_urb_transaction (
|
||||
if (unlikely (!qtd))
|
||||
goto cleanup;
|
||||
qtd->urb = urb;
|
||||
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
|
||||
qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
|
||||
list_add_tail (&qtd->qtd_list, head);
|
||||
}
|
||||
|
||||
@ -566,7 +573,7 @@ qh_urb_transaction (
|
||||
*/
|
||||
if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
|
||||
|| usb_pipecontrol (urb->pipe)))
|
||||
qtd->hw_alt_next = EHCI_LIST_END;
|
||||
qtd->hw_alt_next = EHCI_LIST_END(ehci);
|
||||
|
||||
/*
|
||||
* control requests may need a terminating data "status" ack;
|
||||
@ -590,17 +597,17 @@ qh_urb_transaction (
|
||||
if (unlikely (!qtd))
|
||||
goto cleanup;
|
||||
qtd->urb = urb;
|
||||
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
|
||||
qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
|
||||
list_add_tail (&qtd->qtd_list, head);
|
||||
|
||||
/* never any data in such packets */
|
||||
qtd_fill (qtd, 0, 0, token, 0);
|
||||
qtd_fill(ehci, qtd, 0, 0, token, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* by default, enable interrupt on urb completion */
|
||||
if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
|
||||
qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
|
||||
qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC);
|
||||
return head;
|
||||
|
||||
cleanup:
|
||||
@ -769,8 +776,8 @@ done:
|
||||
|
||||
/* init as live, toggle clear, advance to dummy */
|
||||
qh->qh_state = QH_STATE_IDLE;
|
||||
qh->hw_info1 = cpu_to_le32 (info1);
|
||||
qh->hw_info2 = cpu_to_le32 (info2);
|
||||
qh->hw_info1 = cpu_to_hc32(ehci, info1);
|
||||
qh->hw_info2 = cpu_to_hc32(ehci, info2);
|
||||
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
|
||||
qh_refresh (ehci, qh);
|
||||
return qh;
|
||||
@ -782,7 +789,7 @@ done:
|
||||
|
||||
static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{
|
||||
__le32 dma = QH_NEXT (qh->qh_dma);
|
||||
__hc32 dma = QH_NEXT(ehci, qh->qh_dma);
|
||||
struct ehci_qh *head;
|
||||
|
||||
/* (re)start the async schedule? */
|
||||
@ -820,8 +827,6 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define QH_ADDR_MASK __constant_cpu_to_le32(0x7f)
|
||||
|
||||
/*
|
||||
* For control/bulk/interrupt, return QH with these TDs appended.
|
||||
* Allocates and initializes the QH if necessary.
|
||||
@ -837,6 +842,7 @@ static struct ehci_qh *qh_append_tds (
|
||||
)
|
||||
{
|
||||
struct ehci_qh *qh = NULL;
|
||||
u32 qh_addr_mask = cpu_to_hc32(ehci, 0x7f);
|
||||
|
||||
qh = (struct ehci_qh *) *ptr;
|
||||
if (unlikely (qh == NULL)) {
|
||||
@ -858,7 +864,7 @@ static struct ehci_qh *qh_append_tds (
|
||||
|
||||
/* usb_reset_device() briefly reverts to address 0 */
|
||||
if (usb_pipedevice (urb->pipe) == 0)
|
||||
qh->hw_info1 &= ~QH_ADDR_MASK;
|
||||
qh->hw_info1 &= ~qh_addr_mask;
|
||||
}
|
||||
|
||||
/* just one way to queue requests: swap with the dummy qtd.
|
||||
@ -867,7 +873,7 @@ static struct ehci_qh *qh_append_tds (
|
||||
if (likely (qtd != NULL)) {
|
||||
struct ehci_qtd *dummy;
|
||||
dma_addr_t dma;
|
||||
__le32 token;
|
||||
__hc32 token;
|
||||
|
||||
/* to avoid racing the HC, use the dummy td instead of
|
||||
* the first td of our list (becomes new dummy). both
|
||||
@ -875,7 +881,7 @@ static struct ehci_qh *qh_append_tds (
|
||||
* HC is allowed to fetch the old dummy (4.10.2).
|
||||
*/
|
||||
token = qtd->hw_token;
|
||||
qtd->hw_token = HALT_BIT;
|
||||
qtd->hw_token = HALT_BIT(ehci);
|
||||
wmb ();
|
||||
dummy = qh->dummy;
|
||||
|
||||
@ -887,14 +893,14 @@ static struct ehci_qh *qh_append_tds (
|
||||
list_add (&dummy->qtd_list, qtd_list);
|
||||
__list_splice (qtd_list, qh->qtd_list.prev);
|
||||
|
||||
ehci_qtd_init (qtd, qtd->qtd_dma);
|
||||
ehci_qtd_init(ehci, qtd, qtd->qtd_dma);
|
||||
qh->dummy = qtd;
|
||||
|
||||
/* hc must see the new dummy at list end */
|
||||
dma = qtd->qtd_dma;
|
||||
qtd = list_entry (qh->qtd_list.prev,
|
||||
struct ehci_qtd, qtd_list);
|
||||
qtd->hw_next = QTD_NEXT (dma);
|
||||
qtd->hw_next = QTD_NEXT(ehci, dma);
|
||||
|
||||
/* let the hc process these next qtds */
|
||||
wmb ();
|
||||
@ -970,7 +976,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
|
||||
|
||||
timer_action_done (ehci, TIMER_IAA_WATCHDOG);
|
||||
|
||||
// qh->hw_next = cpu_to_le32 (qh->qh_dma);
|
||||
// qh->hw_next = cpu_to_hc32(qh->qh_dma);
|
||||
qh->qh_state = QH_STATE_IDLE;
|
||||
qh->qh_next.qh = NULL;
|
||||
qh_put (qh); // refcount from reclaim
|
||||
|
@ -44,9 +44,10 @@ static int ehci_get_frame (struct usb_hcd *hcd);
|
||||
* @tag: hardware tag for type of this record
|
||||
*/
|
||||
static union ehci_shadow *
|
||||
periodic_next_shadow (union ehci_shadow *periodic, __le32 tag)
|
||||
periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic,
|
||||
__hc32 tag)
|
||||
{
|
||||
switch (tag) {
|
||||
switch (hc32_to_cpu(ehci, tag)) {
|
||||
case Q_TYPE_QH:
|
||||
return &periodic->qh->qh_next;
|
||||
case Q_TYPE_FSTN:
|
||||
@ -62,13 +63,14 @@ periodic_next_shadow (union ehci_shadow *periodic, __le32 tag)
|
||||
/* caller must hold ehci->lock */
|
||||
static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
|
||||
{
|
||||
union ehci_shadow *prev_p = &ehci->pshadow [frame];
|
||||
__le32 *hw_p = &ehci->periodic [frame];
|
||||
union ehci_shadow *prev_p = &ehci->pshadow[frame];
|
||||
__hc32 *hw_p = &ehci->periodic[frame];
|
||||
union ehci_shadow here = *prev_p;
|
||||
|
||||
/* find predecessor of "ptr"; hw and shadow lists are in sync */
|
||||
while (here.ptr && here.ptr != ptr) {
|
||||
prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p));
|
||||
prev_p = periodic_next_shadow(ehci, prev_p,
|
||||
Q_NEXT_TYPE(ehci, *hw_p));
|
||||
hw_p = here.hw_next;
|
||||
here = *prev_p;
|
||||
}
|
||||
@ -79,7 +81,8 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
|
||||
/* update shadow and hardware lists ... the old "next" pointers
|
||||
* from ptr may still be in use, the caller updates them.
|
||||
*/
|
||||
*prev_p = *periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p));
|
||||
*prev_p = *periodic_next_shadow(ehci, &here,
|
||||
Q_NEXT_TYPE(ehci, *hw_p));
|
||||
*hw_p = *here.hw_next;
|
||||
}
|
||||
|
||||
@ -87,18 +90,19 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
|
||||
static unsigned short
|
||||
periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
|
||||
{
|
||||
__le32 *hw_p = &ehci->periodic [frame];
|
||||
__hc32 *hw_p = &ehci->periodic [frame];
|
||||
union ehci_shadow *q = &ehci->pshadow [frame];
|
||||
unsigned usecs = 0;
|
||||
|
||||
while (q->ptr) {
|
||||
switch (Q_NEXT_TYPE (*hw_p)) {
|
||||
switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
|
||||
case Q_TYPE_QH:
|
||||
/* is it in the S-mask? */
|
||||
if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
|
||||
if (q->qh->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
|
||||
usecs += q->qh->usecs;
|
||||
/* ... or C-mask? */
|
||||
if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
|
||||
if (q->qh->hw_info2 & cpu_to_hc32(ehci,
|
||||
1 << (8 + uframe)))
|
||||
usecs += q->qh->c_usecs;
|
||||
hw_p = &q->qh->hw_next;
|
||||
q = &q->qh->qh_next;
|
||||
@ -108,7 +112,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
|
||||
/* for "save place" FSTNs, count the relevant INTR
|
||||
* bandwidth from the previous frame
|
||||
*/
|
||||
if (q->fstn->hw_prev != EHCI_LIST_END) {
|
||||
if (q->fstn->hw_prev != EHCI_LIST_END(ehci)) {
|
||||
ehci_dbg (ehci, "ignoring FSTN cost ...\n");
|
||||
}
|
||||
hw_p = &q->fstn->hw_next;
|
||||
@ -121,9 +125,10 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
|
||||
break;
|
||||
case Q_TYPE_SITD:
|
||||
/* is it in the S-mask? (count SPLIT, DATA) */
|
||||
if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
|
||||
if (q->sitd->hw_uframe & cpu_to_hc32(ehci,
|
||||
1 << uframe)) {
|
||||
if (q->sitd->hw_fullspeed_ep &
|
||||
__constant_cpu_to_le32 (1<<31))
|
||||
cpu_to_hc32(ehci, 1<<31))
|
||||
usecs += q->sitd->stream->usecs;
|
||||
else /* worst case for OUT start-split */
|
||||
usecs += HS_USECS_ISO (188);
|
||||
@ -131,7 +136,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
|
||||
|
||||
/* ... C-mask? (count CSPLIT, DATA) */
|
||||
if (q->sitd->hw_uframe &
|
||||
cpu_to_le32 (1 << (8 + uframe))) {
|
||||
cpu_to_hc32(ehci, 1 << (8 + uframe))) {
|
||||
/* worst case for IN complete-split */
|
||||
usecs += q->sitd->stream->c_usecs;
|
||||
}
|
||||
@ -173,9 +178,9 @@ static int same_tt (struct usb_device *dev1, struct usb_device *dev2)
|
||||
* will cause a transfer in "B-frame" uframe 0. "B-frames" lag
|
||||
* "H-frames" by 1 uframe. See the EHCI spec sec 4.5 and figure 4.7.
|
||||
*/
|
||||
static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __le32 mask)
|
||||
static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask)
|
||||
{
|
||||
unsigned char smask = QH_SMASK & le32_to_cpu(mask);
|
||||
unsigned char smask = QH_SMASK & hc32_to_cpu(ehci, mask);
|
||||
if (!smask) {
|
||||
ehci_err(ehci, "invalid empty smask!\n");
|
||||
/* uframe 7 can't have bw so this will indicate failure */
|
||||
@ -217,14 +222,14 @@ periodic_tt_usecs (
|
||||
unsigned short tt_usecs[8]
|
||||
)
|
||||
{
|
||||
__le32 *hw_p = &ehci->periodic [frame];
|
||||
__hc32 *hw_p = &ehci->periodic [frame];
|
||||
union ehci_shadow *q = &ehci->pshadow [frame];
|
||||
unsigned char uf;
|
||||
|
||||
memset(tt_usecs, 0, 16);
|
||||
|
||||
while (q->ptr) {
|
||||
switch (Q_NEXT_TYPE(*hw_p)) {
|
||||
switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
|
||||
case Q_TYPE_ITD:
|
||||
hw_p = &q->itd->hw_next;
|
||||
q = &q->itd->itd_next;
|
||||
@ -247,8 +252,8 @@ periodic_tt_usecs (
|
||||
continue;
|
||||
// case Q_TYPE_FSTN:
|
||||
default:
|
||||
ehci_dbg(ehci,
|
||||
"ignoring periodic frame %d FSTN\n", frame);
|
||||
ehci_dbg(ehci, "ignoring periodic frame %d FSTN\n",
|
||||
frame);
|
||||
hw_p = &q->fstn->hw_next;
|
||||
q = &q->fstn->fstn_next;
|
||||
}
|
||||
@ -368,41 +373,42 @@ static int tt_no_collision (
|
||||
*/
|
||||
for (; frame < ehci->periodic_size; frame += period) {
|
||||
union ehci_shadow here;
|
||||
__le32 type;
|
||||
__hc32 type;
|
||||
|
||||
here = ehci->pshadow [frame];
|
||||
type = Q_NEXT_TYPE (ehci->periodic [frame]);
|
||||
type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]);
|
||||
while (here.ptr) {
|
||||
switch (type) {
|
||||
switch (hc32_to_cpu(ehci, type)) {
|
||||
case Q_TYPE_ITD:
|
||||
type = Q_NEXT_TYPE (here.itd->hw_next);
|
||||
type = Q_NEXT_TYPE(ehci, here.itd->hw_next);
|
||||
here = here.itd->itd_next;
|
||||
continue;
|
||||
case Q_TYPE_QH:
|
||||
if (same_tt (dev, here.qh->dev)) {
|
||||
u32 mask;
|
||||
|
||||
mask = le32_to_cpu (here.qh->hw_info2);
|
||||
mask = hc32_to_cpu(ehci,
|
||||
here.qh->hw_info2);
|
||||
/* "knows" no gap is needed */
|
||||
mask |= mask >> 8;
|
||||
if (mask & uf_mask)
|
||||
break;
|
||||
}
|
||||
type = Q_NEXT_TYPE (here.qh->hw_next);
|
||||
type = Q_NEXT_TYPE(ehci, here.qh->hw_next);
|
||||
here = here.qh->qh_next;
|
||||
continue;
|
||||
case Q_TYPE_SITD:
|
||||
if (same_tt (dev, here.sitd->urb->dev)) {
|
||||
u16 mask;
|
||||
|
||||
mask = le32_to_cpu (here.sitd
|
||||
mask = hc32_to_cpu(ehci, here.sitd
|
||||
->hw_uframe);
|
||||
/* FIXME assumes no gap for IN! */
|
||||
mask |= mask >> 8;
|
||||
if (mask & uf_mask)
|
||||
break;
|
||||
}
|
||||
type = Q_NEXT_TYPE (here.sitd->hw_next);
|
||||
type = Q_NEXT_TYPE(ehci, here.sitd->hw_next);
|
||||
here = here.sitd->sitd_next;
|
||||
continue;
|
||||
// case Q_TYPE_FSTN:
|
||||
@ -473,6 +479,109 @@ static int disable_periodic (struct ehci_hcd *ehci)
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
|
||||
static int safe_to_modify_i (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{
|
||||
int now; /* current (frame * 8) + uframe */
|
||||
int prev_start, next_start; /* uframes from/to split start */
|
||||
int start_uframe = ffs(le32_to_cpup (&qh->hw_info2) & QH_SMASK);
|
||||
int end_uframe = fls((le32_to_cpup (&qh->hw_info2) & QH_CMASK) >> 8);
|
||||
int split_duration = end_uframe - start_uframe;
|
||||
|
||||
now = readl(&ehci->regs->frame_index) % (ehci->periodic_size << 3);
|
||||
|
||||
next_start = ((1024 << 3) + (qh->start << 3) + start_uframe - now)
|
||||
% (qh->period << 3);
|
||||
prev_start = (qh->period << 3) - next_start;
|
||||
|
||||
/*
|
||||
* Make sure there will be at least one uframe when qh is safe.
|
||||
*/
|
||||
if ((qh->period << 3) <= (ehci->i_thresh + 2 + split_duration))
|
||||
/* never safe */
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Wait 1 uframe after transaction should have started, to make
|
||||
* sure controller has time to write back overlay, so we can
|
||||
* check QTD_STS_STS to see if transaction is in progress.
|
||||
*/
|
||||
if ((next_start > ehci->i_thresh) && (prev_start > 1))
|
||||
/* safe to set "i" bit if split isn't in progress */
|
||||
return (qh->hw_token & STATUS_BIT(ehci)) ? 0 : 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set inactivate bit for all the split interrupt QHs. */
|
||||
static void qh_inactivate_split_intr_qhs (struct ehci_hcd *ehci)
|
||||
{
|
||||
struct ehci_qh *qh;
|
||||
int not_done, safe;
|
||||
u32 inactivate = INACTIVATE_BIT(ehci);
|
||||
u32 active = ACTIVE_BIT(ehci);
|
||||
|
||||
do {
|
||||
not_done = 0;
|
||||
list_for_each_entry(qh, &ehci->split_intr_qhs,
|
||||
split_intr_qhs) {
|
||||
if (qh->hw_info1 & inactivate)
|
||||
/* already off */
|
||||
continue;
|
||||
/*
|
||||
* To avoid setting "I" after the start split happens,
|
||||
* don't set it if the QH might be cached in the
|
||||
* controller. Some HCs (Broadcom/ServerWorks HT1000)
|
||||
* will stop in the middle of a split transaction when
|
||||
* the "I" bit is set.
|
||||
*/
|
||||
safe = safe_to_modify_i(ehci, qh);
|
||||
if (safe == 0) {
|
||||
not_done = 1;
|
||||
} else if (safe > 0) {
|
||||
qh->was_active = qh->hw_token & active;
|
||||
qh->hw_info1 |= inactivate;
|
||||
}
|
||||
}
|
||||
} while (not_done);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void qh_reactivate_split_intr_qhs (struct ehci_hcd *ehci)
|
||||
{
|
||||
struct ehci_qh *qh;
|
||||
u32 token;
|
||||
int not_done, safe;
|
||||
u32 inactivate = INACTIVATE_BIT(ehci);
|
||||
u32 active = ACTIVE_BIT(ehci);
|
||||
u32 halt = HALT_BIT(ehci);
|
||||
|
||||
do {
|
||||
not_done = 0;
|
||||
list_for_each_entry(qh, &ehci->split_intr_qhs, split_intr_qhs) {
|
||||
if (!(qh->hw_info1 & inactivate)) /* already on */
|
||||
continue;
|
||||
/*
|
||||
* Don't reactivate if cached, or controller might
|
||||
* overwrite overlay after we modify it!
|
||||
*/
|
||||
safe = safe_to_modify_i(ehci, qh);
|
||||
if (safe == 0) {
|
||||
not_done = 1;
|
||||
} else if (safe > 0) {
|
||||
/* See EHCI 1.0 section 4.15.2.4. */
|
||||
token = qh->hw_token;
|
||||
qh->hw_token = (token | halt) & ~active;
|
||||
wmb();
|
||||
qh->hw_info1 &= ~inactivate;
|
||||
wmb();
|
||||
qh->hw_token = (token & ~halt) | qh->was_active;
|
||||
}
|
||||
}
|
||||
} while (not_done);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* periodic schedule slots have iso tds (normal or split) first, then a
|
||||
* sparse tree for active interrupt transfers.
|
||||
@ -487,25 +596,36 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
|
||||
dev_dbg (&qh->dev->dev,
|
||||
"link qh%d-%04x/%p start %d [%d/%d us]\n",
|
||||
period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
|
||||
period, hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
|
||||
qh, qh->start, qh->usecs, qh->c_usecs);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
/*
|
||||
* If low/full speed interrupt QHs are inactive (because of
|
||||
* cpufreq changing processor speeds), start QH with I flag set--
|
||||
* it will automatically be cleared when cpufreq is done.
|
||||
*/
|
||||
if (ehci->cpufreq_changing)
|
||||
if (!(qh->hw_info1 & (cpu_to_le32(1 << 13))))
|
||||
qh->hw_info1 |= INACTIVATE_BIT(ehci);
|
||||
#endif
|
||||
|
||||
/* high bandwidth, or otherwise every microframe */
|
||||
if (period == 0)
|
||||
period = 1;
|
||||
|
||||
for (i = qh->start; i < ehci->periodic_size; i += period) {
|
||||
union ehci_shadow *prev = &ehci->pshadow [i];
|
||||
__le32 *hw_p = &ehci->periodic [i];
|
||||
union ehci_shadow *prev = &ehci->pshadow[i];
|
||||
__hc32 *hw_p = &ehci->periodic[i];
|
||||
union ehci_shadow here = *prev;
|
||||
__le32 type = 0;
|
||||
__hc32 type = 0;
|
||||
|
||||
/* skip the iso nodes at list head */
|
||||
while (here.ptr) {
|
||||
type = Q_NEXT_TYPE (*hw_p);
|
||||
if (type == Q_TYPE_QH)
|
||||
type = Q_NEXT_TYPE(ehci, *hw_p);
|
||||
if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
|
||||
break;
|
||||
prev = periodic_next_shadow (prev, type);
|
||||
prev = periodic_next_shadow(ehci, prev, type);
|
||||
hw_p = &here.qh->hw_next;
|
||||
here = *prev;
|
||||
}
|
||||
@ -527,7 +647,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
qh->hw_next = *hw_p;
|
||||
wmb ();
|
||||
prev->qh = qh;
|
||||
*hw_p = QH_NEXT (qh->qh_dma);
|
||||
*hw_p = QH_NEXT (ehci, qh->qh_dma);
|
||||
}
|
||||
}
|
||||
qh->qh_state = QH_STATE_LINKED;
|
||||
@ -538,6 +658,12 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
? ((qh->usecs + qh->c_usecs) / qh->period)
|
||||
: (qh->usecs * 8);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
/* add qh to list of low/full speed interrupt QHs, if applicable */
|
||||
if (!(qh->hw_info1 & (cpu_to_le32(1 << 13)))) {
|
||||
list_add(&qh->split_intr_qhs, &ehci->split_intr_qhs);
|
||||
}
|
||||
#endif
|
||||
/* maybe enable periodic schedule processing */
|
||||
if (!ehci->periodic_sched++)
|
||||
return enable_periodic (ehci);
|
||||
@ -555,7 +681,14 @@ static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
// and this qh is active in the current uframe
|
||||
// (and overlay token SplitXstate is false?)
|
||||
// THEN
|
||||
// qh->hw_info1 |= __constant_cpu_to_le32 (1 << 7 /* "ignore" */);
|
||||
// qh->hw_info1 |= __constant_cpu_to_hc32(1 << 7 /* "ignore" */);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
/* remove qh from list of low/full speed interrupt QHs */
|
||||
if (!(qh->hw_info1 & (cpu_to_le32(1 << 13)))) {
|
||||
list_del_init(&qh->split_intr_qhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* high bandwidth, or otherwise part of every microframe */
|
||||
if ((period = qh->period) == 0)
|
||||
@ -572,7 +705,7 @@ static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
dev_dbg (&qh->dev->dev,
|
||||
"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
|
||||
qh->period,
|
||||
le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
|
||||
hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
|
||||
qh, qh->start, qh->usecs, qh->c_usecs);
|
||||
|
||||
/* qh->qh_next still "live" to HC */
|
||||
@ -598,7 +731,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
* active high speed queues may need bigger delays...
|
||||
*/
|
||||
if (list_empty (&qh->qtd_list)
|
||||
|| (__constant_cpu_to_le32 (QH_CMASK)
|
||||
|| (cpu_to_hc32(ehci, QH_CMASK)
|
||||
& qh->hw_info2) != 0)
|
||||
wait = 2;
|
||||
else
|
||||
@ -606,7 +739,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
|
||||
udelay (wait);
|
||||
qh->qh_state = QH_STATE_IDLE;
|
||||
qh->hw_next = EHCI_LIST_END;
|
||||
qh->hw_next = EHCI_LIST_END(ehci);
|
||||
wmb ();
|
||||
}
|
||||
|
||||
@ -663,7 +796,7 @@ static int check_intr_schedule (
|
||||
unsigned frame,
|
||||
unsigned uframe,
|
||||
const struct ehci_qh *qh,
|
||||
__le32 *c_maskp
|
||||
__hc32 *c_maskp
|
||||
)
|
||||
{
|
||||
int retval = -ENOSPC;
|
||||
@ -695,7 +828,7 @@ static int check_intr_schedule (
|
||||
|
||||
retval = 0;
|
||||
|
||||
*c_maskp = cpu_to_le32 (mask << 8);
|
||||
*c_maskp = cpu_to_hc32(ehci, mask << 8);
|
||||
}
|
||||
#else
|
||||
/* Make sure this tt's buffer is also available for CSPLITs.
|
||||
@ -706,7 +839,7 @@ static int check_intr_schedule (
|
||||
* one smart pass...
|
||||
*/
|
||||
mask = 0x03 << (uframe + qh->gap_uf);
|
||||
*c_maskp = cpu_to_le32 (mask << 8);
|
||||
*c_maskp = cpu_to_hc32(ehci, mask << 8);
|
||||
|
||||
mask |= 1 << uframe;
|
||||
if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) {
|
||||
@ -726,20 +859,20 @@ done:
|
||||
/* "first fit" scheduling policy used the first time through,
|
||||
* or when the previous schedule slot can't be re-used.
|
||||
*/
|
||||
static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{
|
||||
int status;
|
||||
unsigned uframe;
|
||||
__le32 c_mask;
|
||||
__hc32 c_mask;
|
||||
unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
|
||||
|
||||
qh_refresh(ehci, qh);
|
||||
qh->hw_next = EHCI_LIST_END;
|
||||
qh->hw_next = EHCI_LIST_END(ehci);
|
||||
frame = qh->start;
|
||||
|
||||
/* reuse the previous schedule slots, if we can */
|
||||
if (frame < qh->period) {
|
||||
uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK);
|
||||
uframe = ffs(hc32_to_cpup(ehci, &qh->hw_info2) & QH_SMASK);
|
||||
status = check_intr_schedule (ehci, frame, --uframe,
|
||||
qh, &c_mask);
|
||||
} else {
|
||||
@ -775,10 +908,10 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
qh->start = frame;
|
||||
|
||||
/* reset S-frame and (maybe) C-frame masks */
|
||||
qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK));
|
||||
qh->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
|
||||
qh->hw_info2 |= qh->period
|
||||
? cpu_to_le32 (1 << uframe)
|
||||
: __constant_cpu_to_le32 (QH_SMASK);
|
||||
? cpu_to_hc32(ehci, 1 << uframe)
|
||||
: cpu_to_hc32(ehci, QH_SMASK);
|
||||
qh->hw_info2 |= c_mask;
|
||||
} else
|
||||
ehci_dbg (ehci, "reused qh %p schedule\n", qh);
|
||||
@ -808,7 +941,7 @@ static int intr_submit (
|
||||
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;
|
||||
goto done;
|
||||
}
|
||||
@ -898,9 +1031,9 @@ iso_stream_init (
|
||||
buf1 |= maxp;
|
||||
maxp *= multi;
|
||||
|
||||
stream->buf0 = cpu_to_le32 ((epnum << 8) | dev->devnum);
|
||||
stream->buf1 = cpu_to_le32 (buf1);
|
||||
stream->buf2 = cpu_to_le32 (multi);
|
||||
stream->buf0 = cpu_to_hc32(ehci, (epnum << 8) | dev->devnum);
|
||||
stream->buf1 = cpu_to_hc32(ehci, buf1);
|
||||
stream->buf2 = cpu_to_hc32(ehci, multi);
|
||||
|
||||
/* usbfs wants to report the average usecs per frame tied up
|
||||
* when transfers on this endpoint are scheduled ...
|
||||
@ -943,7 +1076,7 @@ iso_stream_init (
|
||||
bandwidth /= 1 << (interval + 2);
|
||||
|
||||
/* stream->splits gets created from raw_mask later */
|
||||
stream->address = cpu_to_le32 (addr);
|
||||
stream->address = cpu_to_hc32(ehci, addr);
|
||||
}
|
||||
stream->bandwidth = bandwidth;
|
||||
|
||||
@ -1077,7 +1210,8 @@ iso_sched_alloc (unsigned packets, gfp_t mem_flags)
|
||||
}
|
||||
|
||||
static inline void
|
||||
itd_sched_init (
|
||||
itd_sched_init(
|
||||
struct ehci_hcd *ehci,
|
||||
struct ehci_iso_sched *iso_sched,
|
||||
struct ehci_iso_stream *stream,
|
||||
struct urb *urb
|
||||
@ -1107,7 +1241,7 @@ itd_sched_init (
|
||||
&& !(urb->transfer_flags & URB_NO_INTERRUPT))
|
||||
trans |= EHCI_ITD_IOC;
|
||||
trans |= length << 16;
|
||||
uframe->transaction = cpu_to_le32 (trans);
|
||||
uframe->transaction = cpu_to_hc32(ehci, trans);
|
||||
|
||||
/* might need to cross a buffer page within a uframe */
|
||||
uframe->bufp = (buf & ~(u64)0x0fff);
|
||||
@ -1149,7 +1283,7 @@ itd_urb_transaction (
|
||||
if (unlikely (sched == NULL))
|
||||
return -ENOMEM;
|
||||
|
||||
itd_sched_init (sched, stream, urb);
|
||||
itd_sched_init(ehci, sched, stream, urb);
|
||||
|
||||
if (urb->interval < 8)
|
||||
num_itds = 1 + (sched->span + 7) / 8;
|
||||
@ -1167,7 +1301,7 @@ itd_urb_transaction (
|
||||
/* prefer previously-allocated itds */
|
||||
if (likely (!list_empty(&stream->free_list))) {
|
||||
itd = list_entry (stream->free_list.prev,
|
||||
struct ehci_itd, itd_list);
|
||||
struct ehci_itd, itd_list);
|
||||
list_del (&itd->itd_list);
|
||||
itd_dma = itd->itd_dma;
|
||||
} else
|
||||
@ -1294,7 +1428,7 @@ sitd_slot_ok (
|
||||
uframe += period_uframes;
|
||||
} while (uframe < mod);
|
||||
|
||||
stream->splits = cpu_to_le32(stream->raw_mask << (uframe & 7));
|
||||
stream->splits = cpu_to_hc32(ehci, stream->raw_mask << (uframe & 7));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1415,12 +1549,13 @@ ready:
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static inline void
|
||||
itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd)
|
||||
itd_init(struct ehci_hcd *ehci, struct ehci_iso_stream *stream,
|
||||
struct ehci_itd *itd)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* it's been recently zeroed */
|
||||
itd->hw_next = EHCI_LIST_END;
|
||||
itd->hw_next = EHCI_LIST_END(ehci);
|
||||
itd->hw_bufp [0] = stream->buf0;
|
||||
itd->hw_bufp [1] = stream->buf1;
|
||||
itd->hw_bufp [2] = stream->buf2;
|
||||
@ -1432,7 +1567,8 @@ itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd)
|
||||
}
|
||||
|
||||
static inline void
|
||||
itd_patch (
|
||||
itd_patch(
|
||||
struct ehci_hcd *ehci,
|
||||
struct ehci_itd *itd,
|
||||
struct ehci_iso_sched *iso_sched,
|
||||
unsigned index,
|
||||
@ -1447,17 +1583,18 @@ itd_patch (
|
||||
uframe &= 0x07;
|
||||
itd->index [uframe] = index;
|
||||
|
||||
itd->hw_transaction [uframe] = uf->transaction;
|
||||
itd->hw_transaction [uframe] |= cpu_to_le32 (pg << 12);
|
||||
itd->hw_bufp [pg] |= cpu_to_le32 (uf->bufp & ~(u32)0);
|
||||
itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32));
|
||||
itd->hw_transaction[uframe] = uf->transaction;
|
||||
itd->hw_transaction[uframe] |= cpu_to_hc32(ehci, pg << 12);
|
||||
itd->hw_bufp[pg] |= cpu_to_hc32(ehci, uf->bufp & ~(u32)0);
|
||||
itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(uf->bufp >> 32));
|
||||
|
||||
/* iso_frame_desc[].offset must be strictly increasing */
|
||||
if (unlikely (uf->cross)) {
|
||||
u64 bufp = uf->bufp + 4096;
|
||||
|
||||
itd->pg = ++pg;
|
||||
itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0);
|
||||
itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(bufp >> 32));
|
||||
itd->hw_bufp[pg] |= cpu_to_hc32(ehci, bufp & ~(u32)0);
|
||||
itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(bufp >> 32));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1470,7 +1607,7 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
|
||||
ehci->pshadow [frame].itd = itd;
|
||||
itd->frame = frame;
|
||||
wmb ();
|
||||
ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
|
||||
ehci->periodic[frame] = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
|
||||
}
|
||||
|
||||
/* fit urb's itds into the selected schedule slot; activate as needed */
|
||||
@ -1515,14 +1652,14 @@ itd_link_urb (
|
||||
list_move_tail (&itd->itd_list, &stream->td_list);
|
||||
itd->stream = iso_stream_get (stream);
|
||||
itd->urb = usb_get_urb (urb);
|
||||
itd_init (stream, itd);
|
||||
itd_init (ehci, stream, itd);
|
||||
}
|
||||
|
||||
uframe = next_uframe & 0x07;
|
||||
frame = next_uframe >> 3;
|
||||
|
||||
itd->usecs [uframe] = stream->usecs;
|
||||
itd_patch (itd, iso_sched, packet, uframe);
|
||||
itd_patch(ehci, itd, iso_sched, packet, uframe);
|
||||
|
||||
next_uframe += stream->interval;
|
||||
stream->depth += stream->interval;
|
||||
@ -1570,7 +1707,7 @@ itd_complete (
|
||||
urb_index = itd->index[uframe];
|
||||
desc = &urb->iso_frame_desc [urb_index];
|
||||
|
||||
t = le32_to_cpup (&itd->hw_transaction [uframe]);
|
||||
t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]);
|
||||
itd->hw_transaction [uframe] = 0;
|
||||
stream->depth -= stream->interval;
|
||||
|
||||
@ -1700,7 +1837,8 @@ done:
|
||||
*/
|
||||
|
||||
static inline void
|
||||
sitd_sched_init (
|
||||
sitd_sched_init(
|
||||
struct ehci_hcd *ehci,
|
||||
struct ehci_iso_sched *iso_sched,
|
||||
struct ehci_iso_stream *stream,
|
||||
struct urb *urb
|
||||
@ -1729,7 +1867,7 @@ sitd_sched_init (
|
||||
&& !(urb->transfer_flags & URB_NO_INTERRUPT))
|
||||
trans |= SITD_IOC;
|
||||
trans |= length << 16;
|
||||
packet->transaction = cpu_to_le32 (trans);
|
||||
packet->transaction = cpu_to_hc32(ehci, trans);
|
||||
|
||||
/* might need to cross a buffer page within a td */
|
||||
packet->bufp = buf;
|
||||
@ -1765,7 +1903,7 @@ sitd_urb_transaction (
|
||||
if (iso_sched == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
sitd_sched_init (iso_sched, stream, urb);
|
||||
sitd_sched_init(ehci, iso_sched, stream, urb);
|
||||
|
||||
/* allocate/init sITDs */
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
@ -1817,7 +1955,8 @@ sitd_urb_transaction (
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static inline void
|
||||
sitd_patch (
|
||||
sitd_patch(
|
||||
struct ehci_hcd *ehci,
|
||||
struct ehci_iso_stream *stream,
|
||||
struct ehci_sitd *sitd,
|
||||
struct ehci_iso_sched *iso_sched,
|
||||
@ -1827,20 +1966,20 @@ sitd_patch (
|
||||
struct ehci_iso_packet *uf = &iso_sched->packet [index];
|
||||
u64 bufp = uf->bufp;
|
||||
|
||||
sitd->hw_next = EHCI_LIST_END;
|
||||
sitd->hw_next = EHCI_LIST_END(ehci);
|
||||
sitd->hw_fullspeed_ep = stream->address;
|
||||
sitd->hw_uframe = stream->splits;
|
||||
sitd->hw_results = uf->transaction;
|
||||
sitd->hw_backpointer = EHCI_LIST_END;
|
||||
sitd->hw_backpointer = EHCI_LIST_END(ehci);
|
||||
|
||||
bufp = uf->bufp;
|
||||
sitd->hw_buf [0] = cpu_to_le32 (bufp);
|
||||
sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32);
|
||||
sitd->hw_buf[0] = cpu_to_hc32(ehci, bufp);
|
||||
sitd->hw_buf_hi[0] = cpu_to_hc32(ehci, bufp >> 32);
|
||||
|
||||
sitd->hw_buf [1] = cpu_to_le32 (uf->buf1);
|
||||
sitd->hw_buf[1] = cpu_to_hc32(ehci, uf->buf1);
|
||||
if (uf->cross)
|
||||
bufp += 4096;
|
||||
sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32);
|
||||
sitd->hw_buf_hi[1] = cpu_to_hc32(ehci, bufp >> 32);
|
||||
sitd->index = index;
|
||||
}
|
||||
|
||||
@ -1853,7 +1992,7 @@ sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
|
||||
ehci->pshadow [frame].sitd = sitd;
|
||||
sitd->frame = frame;
|
||||
wmb ();
|
||||
ehci->periodic [frame] = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD;
|
||||
ehci->periodic[frame] = cpu_to_hc32(ehci, sitd->sitd_dma | Q_TYPE_SITD);
|
||||
}
|
||||
|
||||
/* fit urb's sitds into the selected schedule slot; activate as needed */
|
||||
@ -1881,7 +2020,7 @@ sitd_link_urb (
|
||||
urb->dev->devpath, stream->bEndpointAddress & 0x0f,
|
||||
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
|
||||
(next_uframe >> 3) % ehci->periodic_size,
|
||||
stream->interval, le32_to_cpu (stream->splits));
|
||||
stream->interval, hc32_to_cpu(ehci, stream->splits));
|
||||
stream->start = jiffies;
|
||||
}
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
|
||||
@ -1902,7 +2041,7 @@ sitd_link_urb (
|
||||
sitd->stream = iso_stream_get (stream);
|
||||
sitd->urb = usb_get_urb (urb);
|
||||
|
||||
sitd_patch (stream, sitd, sched, packet);
|
||||
sitd_patch(ehci, stream, sitd, sched, packet);
|
||||
sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
|
||||
sitd);
|
||||
|
||||
@ -1940,7 +2079,7 @@ sitd_complete (
|
||||
|
||||
urb_index = sitd->index;
|
||||
desc = &urb->iso_frame_desc [urb_index];
|
||||
t = le32_to_cpup (&sitd->hw_results);
|
||||
t = hc32_to_cpup(ehci, &sitd->hw_results);
|
||||
|
||||
/* report transfer status */
|
||||
if (t & SITD_ERRS) {
|
||||
@ -2095,7 +2234,7 @@ scan_periodic (struct ehci_hcd *ehci)
|
||||
|
||||
for (;;) {
|
||||
union ehci_shadow q, *q_p;
|
||||
__le32 type, *hw_p;
|
||||
__hc32 type, *hw_p;
|
||||
unsigned uframes;
|
||||
|
||||
/* don't scan past the live uframe */
|
||||
@ -2113,7 +2252,7 @@ restart:
|
||||
q_p = &ehci->pshadow [frame];
|
||||
hw_p = &ehci->periodic [frame];
|
||||
q.ptr = q_p->ptr;
|
||||
type = Q_NEXT_TYPE (*hw_p);
|
||||
type = Q_NEXT_TYPE(ehci, *hw_p);
|
||||
modified = 0;
|
||||
|
||||
while (q.ptr != NULL) {
|
||||
@ -2122,11 +2261,11 @@ restart:
|
||||
int live;
|
||||
|
||||
live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
|
||||
switch (type) {
|
||||
switch (hc32_to_cpu(ehci, type)) {
|
||||
case Q_TYPE_QH:
|
||||
/* handle any completions */
|
||||
temp.qh = qh_get (q.qh);
|
||||
type = Q_NEXT_TYPE (q.qh->hw_next);
|
||||
type = Q_NEXT_TYPE(ehci, q.qh->hw_next);
|
||||
q = q.qh->qh_next;
|
||||
modified = qh_completions (ehci, temp.qh);
|
||||
if (unlikely (list_empty (&temp.qh->qtd_list)))
|
||||
@ -2137,10 +2276,10 @@ restart:
|
||||
/* for "save place" FSTNs, look at QH entries
|
||||
* in the previous frame for completions.
|
||||
*/
|
||||
if (q.fstn->hw_prev != EHCI_LIST_END) {
|
||||
if (q.fstn->hw_prev != EHCI_LIST_END(ehci)) {
|
||||
dbg ("ignoring completions from FSTNs");
|
||||
}
|
||||
type = Q_NEXT_TYPE (q.fstn->hw_next);
|
||||
type = Q_NEXT_TYPE(ehci, q.fstn->hw_next);
|
||||
q = q.fstn->fstn_next;
|
||||
break;
|
||||
case Q_TYPE_ITD:
|
||||
@ -2148,11 +2287,12 @@ restart:
|
||||
rmb ();
|
||||
for (uf = live ? uframes : 8; uf < 8; uf++) {
|
||||
if (0 == (q.itd->hw_transaction [uf]
|
||||
& ITD_ACTIVE))
|
||||
& ITD_ACTIVE(ehci)))
|
||||
continue;
|
||||
q_p = &q.itd->itd_next;
|
||||
hw_p = &q.itd->hw_next;
|
||||
type = Q_NEXT_TYPE (q.itd->hw_next);
|
||||
type = Q_NEXT_TYPE(ehci,
|
||||
q.itd->hw_next);
|
||||
q = *q_p;
|
||||
break;
|
||||
}
|
||||
@ -2164,23 +2304,24 @@ restart:
|
||||
*/
|
||||
*q_p = q.itd->itd_next;
|
||||
*hw_p = q.itd->hw_next;
|
||||
type = Q_NEXT_TYPE (q.itd->hw_next);
|
||||
type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
|
||||
wmb();
|
||||
modified = itd_complete (ehci, q.itd);
|
||||
q = *q_p;
|
||||
break;
|
||||
case Q_TYPE_SITD:
|
||||
if ((q.sitd->hw_results & SITD_ACTIVE)
|
||||
if ((q.sitd->hw_results & SITD_ACTIVE(ehci))
|
||||
&& live) {
|
||||
q_p = &q.sitd->sitd_next;
|
||||
hw_p = &q.sitd->hw_next;
|
||||
type = Q_NEXT_TYPE (q.sitd->hw_next);
|
||||
type = Q_NEXT_TYPE(ehci,
|
||||
q.sitd->hw_next);
|
||||
q = *q_p;
|
||||
break;
|
||||
}
|
||||
*q_p = q.sitd->sitd_next;
|
||||
*hw_p = q.sitd->hw_next;
|
||||
type = Q_NEXT_TYPE (q.sitd->hw_next);
|
||||
type = Q_NEXT_TYPE(ehci, q.sitd->hw_next);
|
||||
wmb();
|
||||
modified = sitd_complete (ehci, q.sitd);
|
||||
q = *q_p;
|
||||
|
@ -21,6 +21,22 @@
|
||||
|
||||
/* definitions used for the EHCI driver */
|
||||
|
||||
/*
|
||||
* __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
|
||||
* __leXX (normally) or __beXX (given EHCI_BIG_ENDIAN_DESC), depending on
|
||||
* the host controller implementation.
|
||||
*
|
||||
* To facilitate the strongest possible byte-order checking from "sparse"
|
||||
* and so on, we use __leXX unless that's not practical.
|
||||
*/
|
||||
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
|
||||
typedef __u32 __bitwise __hc32;
|
||||
typedef __u16 __bitwise __hc16;
|
||||
#else
|
||||
#define __hc32 __le32
|
||||
#define __hc16 __le16
|
||||
#endif
|
||||
|
||||
/* statistics can be kept for for tuning/monitoring */
|
||||
struct ehci_stats {
|
||||
/* irq usage */
|
||||
@ -55,6 +71,12 @@ struct ehci_hcd { /* one per controller */
|
||||
__u32 hcs_params; /* cached register copy */
|
||||
spinlock_t lock;
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct notifier_block cpufreq_transition;
|
||||
int cpufreq_changing;
|
||||
struct list_head split_intr_qhs;
|
||||
#endif
|
||||
|
||||
/* async schedule support */
|
||||
struct ehci_qh *async;
|
||||
struct ehci_qh *reclaim;
|
||||
@ -64,7 +86,7 @@ struct ehci_hcd { /* one per controller */
|
||||
/* periodic schedule support */
|
||||
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
|
||||
unsigned periodic_size;
|
||||
__le32 *periodic; /* hw periodic table */
|
||||
__hc32 *periodic; /* hw periodic table */
|
||||
dma_addr_t periodic_dma;
|
||||
unsigned i_thresh; /* uframes HC might cache */
|
||||
|
||||
@ -74,11 +96,14 @@ struct ehci_hcd { /* one per controller */
|
||||
|
||||
/* per root hub port */
|
||||
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
|
||||
|
||||
/* bit vectors (one bit per port) */
|
||||
unsigned long bus_suspended; /* which ports were
|
||||
already suspended at the start of a bus suspend */
|
||||
unsigned long companion_ports; /* which ports are
|
||||
dedicated to the companion controller */
|
||||
unsigned long owned_ports; /* which ports are
|
||||
owned by the companion during a bus suspend */
|
||||
|
||||
/* per-HC memory pools (could be per-bus, but ...) */
|
||||
struct dma_pool *qh_pool; /* qh per active urb */
|
||||
@ -97,6 +122,7 @@ struct ehci_hcd { /* one per controller */
|
||||
unsigned no_selective_suspend:1;
|
||||
unsigned has_fsl_port_bug:1; /* FreeScale */
|
||||
unsigned big_endian_mmio:1;
|
||||
unsigned big_endian_desc:1;
|
||||
|
||||
u8 sbrn; /* packed release number */
|
||||
|
||||
@ -276,6 +302,12 @@ struct ehci_regs {
|
||||
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define USBMODE 0x68 /* USB Device mode */
|
||||
#define USBMODE_SDIS (1<<3) /* Stream disable */
|
||||
#define USBMODE_BE (1<<2) /* BE/LE endianness select */
|
||||
#define USBMODE_CM_HC (3<<0) /* host controller mode */
|
||||
#define USBMODE_CM_IDLE (0<<0) /* idle state */
|
||||
|
||||
/* Appendix C, Debug port ... intended for use with special "debug devices"
|
||||
* that can help if there's no serial console. (nonstandard enumeration.)
|
||||
*/
|
||||
@ -303,7 +335,7 @@ struct ehci_dbg_port {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
|
||||
#define QTD_NEXT(ehci, dma) cpu_to_hc32(ehci, (u32)dma)
|
||||
|
||||
/*
|
||||
* EHCI Specification 0.95 Section 3.5
|
||||
@ -315,9 +347,9 @@ struct ehci_dbg_port {
|
||||
*/
|
||||
struct ehci_qtd {
|
||||
/* first part defined by EHCI spec */
|
||||
__le32 hw_next; /* see EHCI 3.5.1 */
|
||||
__le32 hw_alt_next; /* see EHCI 3.5.2 */
|
||||
__le32 hw_token; /* see EHCI 3.5.3 */
|
||||
__hc32 hw_next; /* see EHCI 3.5.1 */
|
||||
__hc32 hw_alt_next; /* see EHCI 3.5.2 */
|
||||
__hc32 hw_token; /* see EHCI 3.5.3 */
|
||||
#define QTD_TOGGLE (1 << 31) /* data toggle */
|
||||
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
|
||||
#define QTD_IOC (1 << 15) /* interrupt on complete */
|
||||
@ -331,8 +363,13 @@ struct ehci_qtd {
|
||||
#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
|
||||
#define QTD_STS_STS (1 << 1) /* split transaction state */
|
||||
#define QTD_STS_PING (1 << 0) /* issue PING? */
|
||||
__le32 hw_buf [5]; /* see EHCI 3.5.4 */
|
||||
__le32 hw_buf_hi [5]; /* Appendix B */
|
||||
|
||||
#define ACTIVE_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_ACTIVE)
|
||||
#define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT)
|
||||
#define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS)
|
||||
|
||||
__hc32 hw_buf [5]; /* see EHCI 3.5.4 */
|
||||
__hc32 hw_buf_hi [5]; /* Appendix B */
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t qtd_dma; /* qtd address */
|
||||
@ -342,26 +379,33 @@ struct ehci_qtd {
|
||||
} __attribute__ ((aligned (32)));
|
||||
|
||||
/* mask NakCnt+T in qh->hw_alt_next */
|
||||
#define QTD_MASK __constant_cpu_to_le32 (~0x1f)
|
||||
#define QTD_MASK(ehci) cpu_to_hc32 (ehci, ~0x1f)
|
||||
|
||||
#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* type tag from {qh,itd,sitd,fstn}->hw_next */
|
||||
#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
|
||||
#define Q_NEXT_TYPE(ehci,dma) ((dma) & cpu_to_hc32(ehci, 3 << 1))
|
||||
|
||||
/*
|
||||
* Now the following defines are not converted using the
|
||||
* __constant_cpu_to_le32() macro anymore, since we have to support
|
||||
* "dynamic" switching between be and le support, so that the driver
|
||||
* can be used on one system with SoC EHCI controller using big-endian
|
||||
* descriptors as well as a normal little-endian PCI EHCI controller.
|
||||
*/
|
||||
/* values for that type tag */
|
||||
#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1)
|
||||
#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1)
|
||||
#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1)
|
||||
#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1)
|
||||
#define Q_TYPE_ITD (0 << 1)
|
||||
#define Q_TYPE_QH (1 << 1)
|
||||
#define Q_TYPE_SITD (2 << 1)
|
||||
#define Q_TYPE_FSTN (3 << 1)
|
||||
|
||||
/* next async queue entry, or pointer to interrupt/periodic QH */
|
||||
#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
|
||||
#define QH_NEXT(ehci,dma) (cpu_to_hc32(ehci, (((u32)dma)&~0x01f)|Q_TYPE_QH))
|
||||
|
||||
/* for periodic/async schedules and qtd lists, mark end of list */
|
||||
#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */
|
||||
#define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */
|
||||
|
||||
/*
|
||||
* Entries in periodic shadow table are pointers to one of four kinds
|
||||
@ -376,7 +420,7 @@ union ehci_shadow {
|
||||
struct ehci_itd *itd; /* Q_TYPE_ITD */
|
||||
struct ehci_sitd *sitd; /* Q_TYPE_SITD */
|
||||
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
|
||||
__le32 *hw_next; /* (all types) */
|
||||
__hc32 *hw_next; /* (all types) */
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
@ -392,23 +436,27 @@ union ehci_shadow {
|
||||
|
||||
struct ehci_qh {
|
||||
/* first part defined by EHCI spec */
|
||||
__le32 hw_next; /* see EHCI 3.6.1 */
|
||||
__le32 hw_info1; /* see EHCI 3.6.2 */
|
||||
__hc32 hw_next; /* see EHCI 3.6.1 */
|
||||
__hc32 hw_info1; /* see EHCI 3.6.2 */
|
||||
#define QH_HEAD 0x00008000
|
||||
__le32 hw_info2; /* see EHCI 3.6.2 */
|
||||
#define QH_INACTIVATE 0x00000080
|
||||
|
||||
#define INACTIVATE_BIT(ehci) cpu_to_hc32(ehci, QH_INACTIVATE)
|
||||
|
||||
__hc32 hw_info2; /* see EHCI 3.6.2 */
|
||||
#define QH_SMASK 0x000000ff
|
||||
#define QH_CMASK 0x0000ff00
|
||||
#define QH_HUBADDR 0x007f0000
|
||||
#define QH_HUBPORT 0x3f800000
|
||||
#define QH_MULT 0xc0000000
|
||||
__le32 hw_current; /* qtd list - see EHCI 3.6.4 */
|
||||
__hc32 hw_current; /* qtd list - see EHCI 3.6.4 */
|
||||
|
||||
/* qtd overlay (hardware parts of a struct ehci_qtd) */
|
||||
__le32 hw_qtd_next;
|
||||
__le32 hw_alt_next;
|
||||
__le32 hw_token;
|
||||
__le32 hw_buf [5];
|
||||
__le32 hw_buf_hi [5];
|
||||
__hc32 hw_qtd_next;
|
||||
__hc32 hw_alt_next;
|
||||
__hc32 hw_token;
|
||||
__hc32 hw_buf [5];
|
||||
__hc32 hw_buf_hi [5];
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t qh_dma; /* address of qh */
|
||||
@ -418,7 +466,14 @@ struct ehci_qh {
|
||||
struct ehci_qh *reclaim; /* next to reclaim */
|
||||
|
||||
struct ehci_hcd *ehci;
|
||||
struct kref kref;
|
||||
|
||||
/*
|
||||
* Do NOT use atomic operations for QH refcounting. On some CPUs
|
||||
* (PPC7448 for example), atomic operations cannot be performed on
|
||||
* memory that is cache-inhibited (i.e. being used for DMA).
|
||||
* Spinlocks are used to protect all QH fields.
|
||||
*/
|
||||
u32 refcount;
|
||||
unsigned stamp;
|
||||
|
||||
u8 qh_state;
|
||||
@ -437,6 +492,10 @@ struct ehci_qh {
|
||||
unsigned short start; /* where polling starts */
|
||||
#define NO_FRAME ((unsigned short)~0) /* pick new start */
|
||||
struct usb_device *dev; /* access to TT */
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct list_head split_intr_qhs; /* list of split qhs */
|
||||
__le32 was_active; /* active bit before "i" set */
|
||||
#endif
|
||||
} __attribute__ ((aligned (32)));
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -445,7 +504,7 @@ struct ehci_qh {
|
||||
struct ehci_iso_packet {
|
||||
/* These will be copied to iTD when scheduling */
|
||||
u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
|
||||
__le32 transaction; /* itd->hw_transaction[i] |= */
|
||||
__hc32 transaction; /* itd->hw_transaction[i] |= */
|
||||
u8 cross; /* buf crosses pages */
|
||||
/* for full speed OUT splits */
|
||||
u32 buf1;
|
||||
@ -467,8 +526,8 @@ struct ehci_iso_sched {
|
||||
*/
|
||||
struct ehci_iso_stream {
|
||||
/* first two fields match QH, but info1 == 0 */
|
||||
__le32 hw_next;
|
||||
__le32 hw_info1;
|
||||
__hc32 hw_next;
|
||||
__hc32 hw_info1;
|
||||
|
||||
u32 refcount;
|
||||
u8 bEndpointAddress;
|
||||
@ -483,7 +542,7 @@ struct ehci_iso_stream {
|
||||
unsigned long start; /* jiffies */
|
||||
unsigned long rescheduled;
|
||||
int next_uframe;
|
||||
__le32 splits;
|
||||
__hc32 splits;
|
||||
|
||||
/* the rest is derived from the endpoint descriptor,
|
||||
* trusting urb->interval == f(epdesc->bInterval) and
|
||||
@ -497,12 +556,12 @@ struct ehci_iso_stream {
|
||||
unsigned bandwidth;
|
||||
|
||||
/* This is used to initialize iTD's hw_bufp fields */
|
||||
__le32 buf0;
|
||||
__le32 buf1;
|
||||
__le32 buf2;
|
||||
__hc32 buf0;
|
||||
__hc32 buf1;
|
||||
__hc32 buf2;
|
||||
|
||||
/* this is used to initialize sITD's tt info */
|
||||
__le32 address;
|
||||
__hc32 address;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -515,8 +574,8 @@ struct ehci_iso_stream {
|
||||
*/
|
||||
struct ehci_itd {
|
||||
/* first part defined by EHCI spec */
|
||||
__le32 hw_next; /* see EHCI 3.3.1 */
|
||||
__le32 hw_transaction [8]; /* see EHCI 3.3.2 */
|
||||
__hc32 hw_next; /* see EHCI 3.3.1 */
|
||||
__hc32 hw_transaction [8]; /* see EHCI 3.3.2 */
|
||||
#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
|
||||
#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
|
||||
#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
|
||||
@ -524,10 +583,10 @@ struct ehci_itd {
|
||||
#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
|
||||
#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
|
||||
|
||||
#define ITD_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
|
||||
#define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE)
|
||||
|
||||
__le32 hw_bufp [7]; /* see EHCI 3.3.3 */
|
||||
__le32 hw_bufp_hi [7]; /* Appendix B */
|
||||
__hc32 hw_bufp [7]; /* see EHCI 3.3.3 */
|
||||
__hc32 hw_bufp_hi [7]; /* Appendix B */
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t itd_dma; /* for this itd */
|
||||
@ -554,11 +613,11 @@ struct ehci_itd {
|
||||
*/
|
||||
struct ehci_sitd {
|
||||
/* first part defined by EHCI spec */
|
||||
__le32 hw_next;
|
||||
__hc32 hw_next;
|
||||
/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
|
||||
__le32 hw_fullspeed_ep; /* EHCI table 3-9 */
|
||||
__le32 hw_uframe; /* EHCI table 3-10 */
|
||||
__le32 hw_results; /* EHCI table 3-11 */
|
||||
__hc32 hw_fullspeed_ep; /* EHCI table 3-9 */
|
||||
__hc32 hw_uframe; /* EHCI table 3-10 */
|
||||
__hc32 hw_results; /* EHCI table 3-11 */
|
||||
#define SITD_IOC (1 << 31) /* interrupt on completion */
|
||||
#define SITD_PAGE (1 << 30) /* buffer 0/1 */
|
||||
#define SITD_LENGTH(x) (0x3ff & ((x)>>16))
|
||||
@ -570,11 +629,11 @@ struct ehci_sitd {
|
||||
#define SITD_STS_MMF (1 << 2) /* incomplete split transaction */
|
||||
#define SITD_STS_STS (1 << 1) /* split transaction state */
|
||||
|
||||
#define SITD_ACTIVE __constant_cpu_to_le32(SITD_STS_ACTIVE)
|
||||
#define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, SITD_STS_ACTIVE)
|
||||
|
||||
__le32 hw_buf [2]; /* EHCI table 3-12 */
|
||||
__le32 hw_backpointer; /* EHCI table 3-13 */
|
||||
__le32 hw_buf_hi [2]; /* Appendix B */
|
||||
__hc32 hw_buf [2]; /* EHCI table 3-12 */
|
||||
__hc32 hw_backpointer; /* EHCI table 3-13 */
|
||||
__hc32 hw_buf_hi [2]; /* Appendix B */
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t sitd_dma;
|
||||
@ -599,8 +658,8 @@ struct ehci_sitd {
|
||||
* it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
|
||||
*/
|
||||
struct ehci_fstn {
|
||||
__le32 hw_next; /* any periodic q entry */
|
||||
__le32 hw_prev; /* qh or EHCI_LIST_END */
|
||||
__hc32 hw_next; /* any periodic q entry */
|
||||
__hc32 hw_prev; /* qh or EHCI_LIST_END */
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t fstn_dma;
|
||||
@ -672,8 +731,21 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
|
||||
#define ehci_big_endian_mmio(e) 0
|
||||
#endif
|
||||
|
||||
static inline unsigned int ehci_readl (const struct ehci_hcd *ehci,
|
||||
__u32 __iomem * regs)
|
||||
/*
|
||||
* Big-endian read/write functions are arch-specific.
|
||||
* Other arches can be added if/when they're needed.
|
||||
*
|
||||
* REVISIT: arch/powerpc now has readl/writel_be, so the
|
||||
* definition below can die once the 4xx support is
|
||||
* finally ported over.
|
||||
*/
|
||||
#if defined(CONFIG_PPC)
|
||||
#define readl_be(addr) in_be32((__force unsigned *)addr)
|
||||
#define writel_be(val, addr) out_be32((__force unsigned *)addr, val)
|
||||
#endif
|
||||
|
||||
static inline unsigned int ehci_readl(const struct ehci_hcd *ehci,
|
||||
__u32 __iomem * regs)
|
||||
{
|
||||
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
|
||||
return ehci_big_endian_mmio(ehci) ?
|
||||
@ -684,8 +756,8 @@ static inline unsigned int ehci_readl (const struct ehci_hcd *ehci,
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void ehci_writel (const struct ehci_hcd *ehci,
|
||||
const unsigned int val, __u32 __iomem *regs)
|
||||
static inline void ehci_writel(const struct ehci_hcd *ehci,
|
||||
const unsigned int val, __u32 __iomem *regs)
|
||||
{
|
||||
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
|
||||
ehci_big_endian_mmio(ehci) ?
|
||||
@ -698,6 +770,62 @@ static inline void ehci_writel (const struct ehci_hcd *ehci,
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* The AMCC 440EPx not only implements its EHCI registers in big-endian
|
||||
* format, but also its DMA data structures (descriptors).
|
||||
*
|
||||
* EHCI controllers accessed through PCI work normally (little-endian
|
||||
* everywhere), so we won't bother supporting a BE-only mode for now.
|
||||
*/
|
||||
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
|
||||
#define ehci_big_endian_desc(e) ((e)->big_endian_desc)
|
||||
|
||||
/* cpu to ehci */
|
||||
static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
|
||||
{
|
||||
return ehci_big_endian_desc(ehci)
|
||||
? (__force __hc32)cpu_to_be32(x)
|
||||
: (__force __hc32)cpu_to_le32(x);
|
||||
}
|
||||
|
||||
/* ehci to cpu */
|
||||
static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
|
||||
{
|
||||
return ehci_big_endian_desc(ehci)
|
||||
? be32_to_cpu((__force __be32)x)
|
||||
: le32_to_cpu((__force __le32)x);
|
||||
}
|
||||
|
||||
static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
|
||||
{
|
||||
return ehci_big_endian_desc(ehci)
|
||||
? be32_to_cpup((__force __be32 *)x)
|
||||
: le32_to_cpup((__force __le32 *)x);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* cpu to ehci */
|
||||
static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
|
||||
{
|
||||
return cpu_to_le32(x);
|
||||
}
|
||||
|
||||
/* ehci to cpu */
|
||||
static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
|
||||
{
|
||||
return le32_to_cpu(x);
|
||||
}
|
||||
|
||||
static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
|
||||
{
|
||||
return le32_to_cpup(x);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef DEBUG
|
||||
#define STUB_DEBUG_FILES
|
||||
#endif /* DEBUG */
|
||||
|
@ -23,7 +23,7 @@
|
||||
/* debug| print the main components of an URB
|
||||
* small: 0) header + data packets 1) just header
|
||||
*/
|
||||
static void __attribute__((unused))
|
||||
static void __maybe_unused
|
||||
urb_print (struct urb * urb, char * str, int small)
|
||||
{
|
||||
unsigned int pipe= urb->pipe;
|
||||
@ -338,7 +338,7 @@ static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label,
|
||||
}
|
||||
|
||||
/* caller MUST own hcd spinlock if verbose is set! */
|
||||
static void __attribute__((unused))
|
||||
static void __maybe_unused
|
||||
ohci_dump_ed (const struct ohci_hcd *ohci, const char *label,
|
||||
const struct ed *ed, int verbose)
|
||||
{
|
||||
|
@ -35,15 +35,13 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/byteorder.h>
|
||||
#ifdef CONFIG_PPC_PS3
|
||||
#include <asm/firmware.h>
|
||||
#endif
|
||||
|
||||
#include "../core/hcd.h"
|
||||
|
||||
@ -82,6 +80,8 @@ static const char hcd_name [] = "ohci_hcd";
|
||||
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"
|
||||
@ -510,15 +510,7 @@ static int ohci_run (struct ohci_hcd *ohci)
|
||||
// flush the writes
|
||||
(void) ohci_readl (ohci, &ohci->regs->control);
|
||||
msleep(temp);
|
||||
temp = roothub_a (ohci);
|
||||
if (!(temp & RH_A_NPS)) {
|
||||
/* power down each port */
|
||||
for (temp = 0; temp < ohci->num_ports; temp++)
|
||||
ohci_writel (ohci, RH_PS_LSDA,
|
||||
&ohci->regs->roothub.portstatus [temp]);
|
||||
}
|
||||
// flush those writes
|
||||
(void) ohci_readl (ohci, &ohci->regs->control);
|
||||
|
||||
memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
|
||||
|
||||
/* 2msec timelimit here means no irqs/preempt */
|
||||
@ -659,9 +651,20 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
||||
}
|
||||
|
||||
if (ints & OHCI_INTR_UE) {
|
||||
disable (ohci);
|
||||
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
|
||||
// e.g. due to PCI Master/Target Abort
|
||||
if (ohci->flags & OHCI_QUIRK_NEC) {
|
||||
/* Workaround for a silicon bug in some NEC chips used
|
||||
* in Apple's PowerBooks. Adapted from Darwin code.
|
||||
*/
|
||||
ohci_err (ohci, "OHCI Unrecoverable Error, scheduling NEC chip restart\n");
|
||||
|
||||
ohci_writel (ohci, OHCI_INTR_UE, ®s->intrdisable);
|
||||
|
||||
schedule_work (&ohci->nec_work);
|
||||
} else {
|
||||
disable (ohci);
|
||||
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
|
||||
}
|
||||
|
||||
ohci_dump (ohci, 1);
|
||||
ohci_usb_reset (ohci);
|
||||
@ -763,23 +766,16 @@ static void ohci_stop (struct usb_hcd *hcd)
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* must not be called from interrupt context */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ohci_restart (struct ohci_hcd *ohci)
|
||||
{
|
||||
int temp;
|
||||
int i;
|
||||
struct urb_priv *priv;
|
||||
|
||||
/* mark any devices gone, so they do nothing till khubd disconnects.
|
||||
* recycle any "live" eds/tds (and urbs) right away.
|
||||
* later, khubd disconnect processing will recycle the other state,
|
||||
* (either as disconnect/reconnect, or maybe someday as a reset).
|
||||
*/
|
||||
spin_lock_irq(&ohci->lock);
|
||||
disable (ohci);
|
||||
usb_root_hub_lost_power(ohci_to_hcd(ohci)->self.root_hub);
|
||||
|
||||
/* Recycle any "live" eds/tds (and urbs). */
|
||||
if (!list_empty (&ohci->pending))
|
||||
ohci_dbg(ohci, "abort schedule...\n");
|
||||
list_for_each_entry (priv, &ohci->pending, pending) {
|
||||
@ -826,20 +822,31 @@ static int ohci_restart (struct ohci_hcd *ohci)
|
||||
if ((temp = ohci_run (ohci)) < 0) {
|
||||
ohci_err (ohci, "can't restart, %d\n", temp);
|
||||
return temp;
|
||||
} else {
|
||||
/* here we "know" root ports should always stay powered,
|
||||
* and that if we try to turn them back on the root hub
|
||||
* will respond to CSC processing.
|
||||
*/
|
||||
i = ohci->num_ports;
|
||||
while (i--)
|
||||
ohci_writel (ohci, RH_PS_PSS,
|
||||
&ohci->regs->roothub.portstatus [i]);
|
||||
ohci_dbg (ohci, "restart complete\n");
|
||||
}
|
||||
ohci_dbg(ohci, "restart complete\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -917,7 +924,7 @@ MODULE_LICENSE ("GPL");
|
||||
|
||||
#ifdef CONFIG_PPC_PS3
|
||||
#include "ohci-ps3.c"
|
||||
#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_sb_driver
|
||||
#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
|
||||
#endif
|
||||
|
||||
#if !defined(PCI_DRIVER) && \
|
||||
@ -940,12 +947,9 @@ static int __init ohci_hcd_mod_init(void)
|
||||
sizeof (struct ed), sizeof (struct td));
|
||||
|
||||
#ifdef PS3_SYSTEM_BUS_DRIVER
|
||||
if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
|
||||
retval = ps3_system_bus_driver_register(
|
||||
&PS3_SYSTEM_BUS_DRIVER);
|
||||
if (retval < 0)
|
||||
goto error_ps3;
|
||||
}
|
||||
retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
|
||||
if (retval < 0)
|
||||
goto error_ps3;
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_DRIVER
|
||||
@ -991,8 +995,7 @@ static int __init ohci_hcd_mod_init(void)
|
||||
error_platform:
|
||||
#endif
|
||||
#ifdef PS3_SYSTEM_BUS_DRIVER
|
||||
if (firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
|
||||
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
|
||||
error_ps3:
|
||||
#endif
|
||||
return retval;
|
||||
@ -1014,8 +1017,7 @@ static void __exit ohci_hcd_mod_exit(void)
|
||||
platform_driver_unregister(&PLATFORM_DRIVER);
|
||||
#endif
|
||||
#ifdef PS3_SYSTEM_BUS_DRIVER
|
||||
if (firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
|
||||
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
|
||||
#endif
|
||||
}
|
||||
module_exit(ohci_hcd_mod_exit);
|
||||
|
@ -55,8 +55,6 @@ static void dl_done_list (struct ohci_hcd *);
|
||||
static void finish_unlinks (struct ohci_hcd *, u16);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ohci_restart(struct ohci_hcd *ohci);
|
||||
|
||||
static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
|
||||
__releases(ohci->lock)
|
||||
__acquires(ohci->lock)
|
||||
@ -191,6 +189,9 @@ __acquires(ohci->lock)
|
||||
spin_unlock_irq (&ohci->lock);
|
||||
(void) ohci_init (ohci);
|
||||
status = ohci_restart (ohci);
|
||||
|
||||
usb_root_hub_lost_power(hcd->self.root_hub);
|
||||
|
||||
spin_lock_irq (&ohci->lock);
|
||||
}
|
||||
return status;
|
||||
|
@ -28,6 +28,7 @@ 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);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -111,6 +111,18 @@ static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check for NEC chip and apply quirk for allegedly lost interrupts.
|
||||
*/
|
||||
static int ohci_quirk_nec(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
|
||||
ohci->flags |= OHCI_QUIRK_NEC;
|
||||
ohci_dbg (ohci, "enabled NEC chipset lost interrupt quirk\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* List of quirks for OHCI */
|
||||
static const struct pci_device_id ohci_pci_quirks[] = {
|
||||
{
|
||||
@ -133,6 +145,10 @@ static const struct pci_device_id ohci_pci_quirks[] = {
|
||||
PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6),
|
||||
.driver_data = (unsigned long)ohci_quirk_toshiba_scc,
|
||||
},
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB),
|
||||
.driver_data = (unsigned long)ohci_quirk_nec,
|
||||
},
|
||||
{
|
||||
/* Toshiba portege 4000 */
|
||||
.vendor = PCI_VENDOR_ID_AL,
|
||||
@ -202,6 +218,42 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_PERSIST) && (defined(CONFIG_USB_EHCI_HCD) || \
|
||||
defined(CONFIG_USB_EHCI_HCD_MODULE))
|
||||
|
||||
/* Following a power loss, we must prepare to regain control of the ports
|
||||
* we used to own. This means turning on the port power before ehci-hcd
|
||||
* tries to switch ownership.
|
||||
*
|
||||
* This isn't a 100% perfect solution. On most systems the OHCI controllers
|
||||
* lie at lower PCI addresses than the EHCI controller, so they will be
|
||||
* discovered (and hence resumed) first. But there is no guarantee things
|
||||
* will always work this way. If the EHCI controller is resumed first and
|
||||
* the OHCI ports are unpowered, then the handover will fail.
|
||||
*/
|
||||
static void prepare_for_handover(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
int port;
|
||||
|
||||
/* Here we "know" root ports should always stay powered */
|
||||
ohci_dbg(ohci, "powerup ports\n");
|
||||
for (port = 0; port < ohci->num_ports; port++)
|
||||
ohci_writel(ohci, RH_PS_PPS,
|
||||
&ohci->regs->roothub.portstatus[port]);
|
||||
|
||||
/* Flush those writes */
|
||||
ohci_readl(ohci, &ohci->regs->control);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void prepare_for_handover(struct usb_hcd *hcd)
|
||||
{ }
|
||||
|
||||
#endif /* CONFIG_USB_PERSIST etc. */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
@ -241,7 +293,10 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
static int ohci_pci_resume (struct usb_hcd *hcd)
|
||||
{
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
|
||||
/* FIXME: we should try to detect loss of VBUS power here */
|
||||
prepare_for_handover(hcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind)
|
||||
{
|
||||
struct i2c_client *c;
|
||||
|
||||
c = (struct i2c_client *)kzalloc(sizeof(*c), GFP_KERNEL);
|
||||
c = kzalloc(sizeof(*c), GFP_KERNEL);
|
||||
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
@ -18,6 +18,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/ps3.h>
|
||||
|
||||
static int ps3_ohci_hc_reset(struct usb_hcd *hcd)
|
||||
@ -75,7 +76,7 @@ static const struct hc_driver ps3_ohci_hc_driver = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
|
||||
static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
int result;
|
||||
struct usb_hcd *hcd;
|
||||
@ -87,13 +88,31 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
|
||||
goto fail_start;
|
||||
}
|
||||
|
||||
result = ps3_open_hv_device(dev);
|
||||
|
||||
if (result) {
|
||||
dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
result = -EPERM;
|
||||
goto fail_open;
|
||||
}
|
||||
|
||||
result = ps3_dma_region_create(dev->d_region);
|
||||
|
||||
if (result) {
|
||||
dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
|
||||
"(%d)\n", __func__, __LINE__, result);
|
||||
BUG_ON("check region type");
|
||||
goto fail_dma_region;
|
||||
}
|
||||
|
||||
result = ps3_mmio_region_create(dev->m_region);
|
||||
|
||||
if (result) {
|
||||
dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
|
||||
__func__, __LINE__);
|
||||
result = -EPERM;
|
||||
goto fail_mmio;
|
||||
goto fail_mmio_region;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
|
||||
@ -122,6 +141,11 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
|
||||
|
||||
hcd->rsrc_start = dev->m_region->lpar_addr;
|
||||
hcd->rsrc_len = dev->m_region->len;
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
|
||||
dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
|
||||
__func__, __LINE__);
|
||||
|
||||
hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
|
||||
|
||||
if (!hcd->regs) {
|
||||
@ -155,34 +179,73 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
|
||||
fail_add_hcd:
|
||||
iounmap(hcd->regs);
|
||||
fail_ioremap:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
ps3_io_irq_destroy(virq);
|
||||
fail_irq:
|
||||
ps3_free_mmio_region(dev->m_region);
|
||||
fail_mmio:
|
||||
fail_mmio_region:
|
||||
ps3_dma_region_free(dev->d_region);
|
||||
fail_dma_region:
|
||||
ps3_close_hv_device(dev);
|
||||
fail_open:
|
||||
fail_start:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_ohci_sb_remove (struct ps3_system_bus_device *dev)
|
||||
static int ps3_ohci_remove (struct ps3_system_bus_device *dev)
|
||||
{
|
||||
unsigned int tmp;
|
||||
struct usb_hcd *hcd =
|
||||
(struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
BUG_ON(!hcd);
|
||||
|
||||
dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
|
||||
dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
|
||||
|
||||
tmp = hcd->irq;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
ps3_system_bus_set_driver_data(dev, NULL);
|
||||
|
||||
BUG_ON(!hcd->regs);
|
||||
iounmap(hcd->regs);
|
||||
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
ps3_io_irq_destroy(tmp);
|
||||
ps3_free_mmio_region(dev->m_region);
|
||||
|
||||
ps3_dma_region_free(dev->d_region);
|
||||
ps3_close_hv_device(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("ps3-ohci");
|
||||
static int ps3_ohci_driver_register(struct ps3_system_bus_driver *drv)
|
||||
{
|
||||
return firmware_has_feature(FW_FEATURE_PS3_LV1)
|
||||
? ps3_system_bus_driver_register(drv)
|
||||
: 0;
|
||||
}
|
||||
|
||||
static struct ps3_system_bus_driver ps3_ohci_sb_driver = {
|
||||
static void ps3_ohci_driver_unregister(struct ps3_system_bus_driver *drv)
|
||||
{
|
||||
if (firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
ps3_system_bus_driver_unregister(drv);
|
||||
}
|
||||
|
||||
MODULE_ALIAS(PS3_MODULE_ALIAS_OHCI);
|
||||
|
||||
static struct ps3_system_bus_driver ps3_ohci_driver = {
|
||||
.core.name = "ps3-ohci-driver",
|
||||
.core.owner = THIS_MODULE,
|
||||
.match_id = PS3_MATCH_ID_OHCI,
|
||||
.core = {
|
||||
.name = "ps3-ohci-driver",
|
||||
},
|
||||
.probe = ps3_ohci_sb_probe,
|
||||
.remove = ps3_ohci_sb_remove,
|
||||
.probe = ps3_ohci_probe,
|
||||
.remove = ps3_ohci_remove,
|
||||
.shutdown = ps3_ohci_remove,
|
||||
};
|
||||
|
@ -397,8 +397,10 @@ struct ohci_hcd {
|
||||
#define OHCI_QUIRK_BE_DESC 0x08 /* BE descriptors */
|
||||
#define OHCI_QUIRK_BE_MMIO 0x10 /* BE registers */
|
||||
#define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/
|
||||
#define OHCI_QUIRK_NEC 0x40 /* lost interrupts */
|
||||
// there are also chip quirks/bugs in init logic
|
||||
|
||||
struct work_struct nec_work; /* Worker for NEC quirk */
|
||||
};
|
||||
|
||||
/* convert between an hcd pointer and the corresponding ohci_hcd */
|
||||
|
2244
drivers/usb/host/r8a66597-hcd.c
Normal file
2244
drivers/usb/host/r8a66597-hcd.c
Normal file
File diff suppressed because it is too large
Load Diff
634
drivers/usb/host/r8a66597.h
Normal file
634
drivers/usb/host/r8a66597.h
Normal file
@ -0,0 +1,634 @@
|
||||
/*
|
||||
* R8A66597 HCD (Host Controller Driver)
|
||||
*
|
||||
* Copyright (C) 2006-2007 Renesas Solutions Corp.
|
||||
* Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
|
||||
* Portions Copyright (C) 2004-2005 David Brownell
|
||||
* Portions Copyright (C) 1999 Roman Weissgaerber
|
||||
*
|
||||
* Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __R8A66597_H__
|
||||
#define __R8A66597_H__
|
||||
|
||||
#define SYSCFG0 0x00
|
||||
#define SYSCFG1 0x02
|
||||
#define SYSSTS0 0x04
|
||||
#define SYSSTS1 0x06
|
||||
#define DVSTCTR0 0x08
|
||||
#define DVSTCTR1 0x0A
|
||||
#define TESTMODE 0x0C
|
||||
#define PINCFG 0x0E
|
||||
#define DMA0CFG 0x10
|
||||
#define DMA1CFG 0x12
|
||||
#define CFIFO 0x14
|
||||
#define D0FIFO 0x18
|
||||
#define D1FIFO 0x1C
|
||||
#define CFIFOSEL 0x20
|
||||
#define CFIFOCTR 0x22
|
||||
#define CFIFOSIE 0x24
|
||||
#define D0FIFOSEL 0x28
|
||||
#define D0FIFOCTR 0x2A
|
||||
#define D1FIFOSEL 0x2C
|
||||
#define D1FIFOCTR 0x2E
|
||||
#define INTENB0 0x30
|
||||
#define INTENB1 0x32
|
||||
#define INTENB2 0x34
|
||||
#define BRDYENB 0x36
|
||||
#define NRDYENB 0x38
|
||||
#define BEMPENB 0x3A
|
||||
#define SOFCFG 0x3C
|
||||
#define INTSTS0 0x40
|
||||
#define INTSTS1 0x42
|
||||
#define INTSTS2 0x44
|
||||
#define BRDYSTS 0x46
|
||||
#define NRDYSTS 0x48
|
||||
#define BEMPSTS 0x4A
|
||||
#define FRMNUM 0x4C
|
||||
#define UFRMNUM 0x4E
|
||||
#define USBADDR 0x50
|
||||
#define USBREQ 0x54
|
||||
#define USBVAL 0x56
|
||||
#define USBINDX 0x58
|
||||
#define USBLENG 0x5A
|
||||
#define DCPCFG 0x5C
|
||||
#define DCPMAXP 0x5E
|
||||
#define DCPCTR 0x60
|
||||
#define PIPESEL 0x64
|
||||
#define PIPECFG 0x68
|
||||
#define PIPEBUF 0x6A
|
||||
#define PIPEMAXP 0x6C
|
||||
#define PIPEPERI 0x6E
|
||||
#define PIPE1CTR 0x70
|
||||
#define PIPE2CTR 0x72
|
||||
#define PIPE3CTR 0x74
|
||||
#define PIPE4CTR 0x76
|
||||
#define PIPE5CTR 0x78
|
||||
#define PIPE6CTR 0x7A
|
||||
#define PIPE7CTR 0x7C
|
||||
#define PIPE8CTR 0x7E
|
||||
#define PIPE9CTR 0x80
|
||||
#define PIPE1TRE 0x90
|
||||
#define PIPE1TRN 0x92
|
||||
#define PIPE2TRE 0x94
|
||||
#define PIPE2TRN 0x96
|
||||
#define PIPE3TRE 0x98
|
||||
#define PIPE3TRN 0x9A
|
||||
#define PIPE4TRE 0x9C
|
||||
#define PIPE4TRN 0x9E
|
||||
#define PIPE5TRE 0xA0
|
||||
#define PIPE5TRN 0xA2
|
||||
#define DEVADD0 0xD0
|
||||
#define DEVADD1 0xD2
|
||||
#define DEVADD2 0xD4
|
||||
#define DEVADD3 0xD6
|
||||
#define DEVADD4 0xD8
|
||||
#define DEVADD5 0xDA
|
||||
#define DEVADD6 0xDC
|
||||
#define DEVADD7 0xDE
|
||||
#define DEVADD8 0xE0
|
||||
#define DEVADD9 0xE2
|
||||
#define DEVADDA 0xE4
|
||||
|
||||
/* System Configuration Control Register */
|
||||
#define XTAL 0xC000 /* b15-14: Crystal selection */
|
||||
#define XTAL48 0x8000 /* 48MHz */
|
||||
#define XTAL24 0x4000 /* 24MHz */
|
||||
#define XTAL12 0x0000 /* 12MHz */
|
||||
#define XCKE 0x2000 /* b13: External clock enable */
|
||||
#define PLLC 0x0800 /* b11: PLL control */
|
||||
#define SCKE 0x0400 /* b10: USB clock enable */
|
||||
#define PCSDIS 0x0200 /* b9: not CS wakeup */
|
||||
#define LPSME 0x0100 /* b8: Low power sleep mode */
|
||||
#define HSE 0x0080 /* b7: Hi-speed enable */
|
||||
#define DCFM 0x0040 /* b6: Controller function select */
|
||||
#define DRPD 0x0020 /* b5: D+/- pull down control */
|
||||
#define DPRPU 0x0010 /* b4: D+ pull up control */
|
||||
#define USBE 0x0001 /* b0: USB module operation enable */
|
||||
|
||||
/* System Configuration Status Register */
|
||||
#define OVCBIT 0x8000 /* b15-14: Over-current bit */
|
||||
#define OVCMON 0xC000 /* b15-14: Over-current monitor */
|
||||
#define SOFEA 0x0020 /* b5: SOF monitor */
|
||||
#define IDMON 0x0004 /* b3: ID-pin monitor */
|
||||
#define LNST 0x0003 /* b1-0: D+, D- line status */
|
||||
#define SE1 0x0003 /* SE1 */
|
||||
#define FS_KSTS 0x0002 /* Full-Speed K State */
|
||||
#define FS_JSTS 0x0001 /* Full-Speed J State */
|
||||
#define LS_JSTS 0x0002 /* Low-Speed J State */
|
||||
#define LS_KSTS 0x0001 /* Low-Speed K State */
|
||||
#define SE0 0x0000 /* SE0 */
|
||||
|
||||
/* Device State Control Register */
|
||||
#define EXTLP0 0x0400 /* b10: External port */
|
||||
#define VBOUT 0x0200 /* b9: VBUS output */
|
||||
#define WKUP 0x0100 /* b8: Remote wakeup */
|
||||
#define RWUPE 0x0080 /* b7: Remote wakeup sense */
|
||||
#define USBRST 0x0040 /* b6: USB reset enable */
|
||||
#define RESUME 0x0020 /* b5: Resume enable */
|
||||
#define UACT 0x0010 /* b4: USB bus enable */
|
||||
#define RHST 0x0007 /* b1-0: Reset handshake status */
|
||||
#define HSPROC 0x0004 /* HS handshake is processing */
|
||||
#define HSMODE 0x0003 /* Hi-Speed mode */
|
||||
#define FSMODE 0x0002 /* Full-Speed mode */
|
||||
#define LSMODE 0x0001 /* Low-Speed mode */
|
||||
#define UNDECID 0x0000 /* Undecided */
|
||||
|
||||
/* Test Mode Register */
|
||||
#define UTST 0x000F /* b3-0: Test select */
|
||||
#define H_TST_PACKET 0x000C /* HOST TEST Packet */
|
||||
#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */
|
||||
#define H_TST_K 0x000A /* HOST TEST K */
|
||||
#define H_TST_J 0x0009 /* HOST TEST J */
|
||||
#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */
|
||||
#define P_TST_PACKET 0x0004 /* PERI TEST Packet */
|
||||
#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */
|
||||
#define P_TST_K 0x0002 /* PERI TEST K */
|
||||
#define P_TST_J 0x0001 /* PERI TEST J */
|
||||
#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */
|
||||
|
||||
/* Data Pin Configuration Register */
|
||||
#define LDRV 0x8000 /* b15: Drive Current Adjust */
|
||||
#define VIF1 0x0000 /* VIF = 1.8V */
|
||||
#define VIF3 0x8000 /* VIF = 3.3V */
|
||||
#define INTA 0x0001 /* b1: USB INT-pin active */
|
||||
|
||||
/* DMAx Pin Configuration Register */
|
||||
#define DREQA 0x4000 /* b14: Dreq active select */
|
||||
#define BURST 0x2000 /* b13: Burst mode */
|
||||
#define DACKA 0x0400 /* b10: Dack active select */
|
||||
#define DFORM 0x0380 /* b9-7: DMA mode select */
|
||||
#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */
|
||||
#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */
|
||||
#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */
|
||||
#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */
|
||||
#define DENDA 0x0040 /* b6: Dend active select */
|
||||
#define PKTM 0x0020 /* b5: Packet mode */
|
||||
#define DENDE 0x0010 /* b4: Dend enable */
|
||||
#define OBUS 0x0004 /* b2: OUTbus mode */
|
||||
|
||||
/* CFIFO/DxFIFO Port Select Register */
|
||||
#define RCNT 0x8000 /* b15: Read count mode */
|
||||
#define REW 0x4000 /* b14: Buffer rewind */
|
||||
#define DCLRM 0x2000 /* b13: DMA buffer clear mode */
|
||||
#define DREQE 0x1000 /* b12: DREQ output enable */
|
||||
#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */
|
||||
#define MBW_8 0x0000 /* 8bit */
|
||||
#define MBW_16 0x0400 /* 16bit */
|
||||
#define BIGEND 0x0100 /* b8: Big endian mode */
|
||||
#define BYTE_LITTLE 0x0000 /* little dendian */
|
||||
#define BYTE_BIG 0x0100 /* big endifan */
|
||||
#define ISEL 0x0020 /* b5: DCP FIFO port direction select */
|
||||
#define CURPIPE 0x000F /* b2-0: PIPE select */
|
||||
|
||||
/* CFIFO/DxFIFO Port Control Register */
|
||||
#define BVAL 0x8000 /* b15: Buffer valid flag */
|
||||
#define BCLR 0x4000 /* b14: Buffer clear */
|
||||
#define FRDY 0x2000 /* b13: FIFO ready */
|
||||
#define DTLN 0x0FFF /* b11-0: FIFO received data length */
|
||||
|
||||
/* Interrupt Enable Register 0 */
|
||||
#define VBSE 0x8000 /* b15: VBUS interrupt */
|
||||
#define RSME 0x4000 /* b14: Resume interrupt */
|
||||
#define SOFE 0x2000 /* b13: Frame update interrupt */
|
||||
#define DVSE 0x1000 /* b12: Device state transition interrupt */
|
||||
#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */
|
||||
#define BEMPE 0x0400 /* b10: Buffer empty interrupt */
|
||||
#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */
|
||||
#define BRDYE 0x0100 /* b8: Buffer ready interrupt */
|
||||
|
||||
/* Interrupt Enable Register 1 */
|
||||
#define OVRCRE 0x8000 /* b15: Over-current interrupt */
|
||||
#define BCHGE 0x4000 /* b14: USB us chenge interrupt */
|
||||
#define DTCHE 0x1000 /* b12: Detach sense interrupt */
|
||||
#define ATTCHE 0x0800 /* b11: Attach sense interrupt */
|
||||
#define EOFERRE 0x0040 /* b6: EOF error interrupt */
|
||||
#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */
|
||||
#define SACKE 0x0010 /* b4: SETUP ACK interrupt */
|
||||
|
||||
/* BRDY Interrupt Enable/Status Register */
|
||||
#define BRDY9 0x0200 /* b9: PIPE9 */
|
||||
#define BRDY8 0x0100 /* b8: PIPE8 */
|
||||
#define BRDY7 0x0080 /* b7: PIPE7 */
|
||||
#define BRDY6 0x0040 /* b6: PIPE6 */
|
||||
#define BRDY5 0x0020 /* b5: PIPE5 */
|
||||
#define BRDY4 0x0010 /* b4: PIPE4 */
|
||||
#define BRDY3 0x0008 /* b3: PIPE3 */
|
||||
#define BRDY2 0x0004 /* b2: PIPE2 */
|
||||
#define BRDY1 0x0002 /* b1: PIPE1 */
|
||||
#define BRDY0 0x0001 /* b1: PIPE0 */
|
||||
|
||||
/* NRDY Interrupt Enable/Status Register */
|
||||
#define NRDY9 0x0200 /* b9: PIPE9 */
|
||||
#define NRDY8 0x0100 /* b8: PIPE8 */
|
||||
#define NRDY7 0x0080 /* b7: PIPE7 */
|
||||
#define NRDY6 0x0040 /* b6: PIPE6 */
|
||||
#define NRDY5 0x0020 /* b5: PIPE5 */
|
||||
#define NRDY4 0x0010 /* b4: PIPE4 */
|
||||
#define NRDY3 0x0008 /* b3: PIPE3 */
|
||||
#define NRDY2 0x0004 /* b2: PIPE2 */
|
||||
#define NRDY1 0x0002 /* b1: PIPE1 */
|
||||
#define NRDY0 0x0001 /* b1: PIPE0 */
|
||||
|
||||
/* BEMP Interrupt Enable/Status Register */
|
||||
#define BEMP9 0x0200 /* b9: PIPE9 */
|
||||
#define BEMP8 0x0100 /* b8: PIPE8 */
|
||||
#define BEMP7 0x0080 /* b7: PIPE7 */
|
||||
#define BEMP6 0x0040 /* b6: PIPE6 */
|
||||
#define BEMP5 0x0020 /* b5: PIPE5 */
|
||||
#define BEMP4 0x0010 /* b4: PIPE4 */
|
||||
#define BEMP3 0x0008 /* b3: PIPE3 */
|
||||
#define BEMP2 0x0004 /* b2: PIPE2 */
|
||||
#define BEMP1 0x0002 /* b1: PIPE1 */
|
||||
#define BEMP0 0x0001 /* b0: PIPE0 */
|
||||
|
||||
/* SOF Pin Configuration Register */
|
||||
#define TRNENSEL 0x0100 /* b8: Select transaction enable period */
|
||||
#define BRDYM 0x0040 /* b6: BRDY clear timing */
|
||||
#define INTL 0x0020 /* b5: Interrupt sense select */
|
||||
#define EDGESTS 0x0010 /* b4: */
|
||||
#define SOFMODE 0x000C /* b3-2: SOF pin select */
|
||||
#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */
|
||||
#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */
|
||||
#define SOF_DISABLE 0x0000 /* SOF OUT Disable */
|
||||
|
||||
/* Interrupt Status Register 0 */
|
||||
#define VBINT 0x8000 /* b15: VBUS interrupt */
|
||||
#define RESM 0x4000 /* b14: Resume interrupt */
|
||||
#define SOFR 0x2000 /* b13: SOF frame update interrupt */
|
||||
#define DVST 0x1000 /* b12: Device state transition interrupt */
|
||||
#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */
|
||||
#define BEMP 0x0400 /* b10: Buffer empty interrupt */
|
||||
#define NRDY 0x0200 /* b9: Buffer not ready interrupt */
|
||||
#define BRDY 0x0100 /* b8: Buffer ready interrupt */
|
||||
#define VBSTS 0x0080 /* b7: VBUS input port */
|
||||
#define DVSQ 0x0070 /* b6-4: Device state */
|
||||
#define DS_SPD_CNFG 0x0070 /* Suspend Configured */
|
||||
#define DS_SPD_ADDR 0x0060 /* Suspend Address */
|
||||
#define DS_SPD_DFLT 0x0050 /* Suspend Default */
|
||||
#define DS_SPD_POWR 0x0040 /* Suspend Powered */
|
||||
#define DS_SUSP 0x0040 /* Suspend */
|
||||
#define DS_CNFG 0x0030 /* Configured */
|
||||
#define DS_ADDS 0x0020 /* Address */
|
||||
#define DS_DFLT 0x0010 /* Default */
|
||||
#define DS_POWR 0x0000 /* Powered */
|
||||
#define DVSQS 0x0030 /* b5-4: Device state */
|
||||
#define VALID 0x0008 /* b3: Setup packet detected flag */
|
||||
#define CTSQ 0x0007 /* b2-0: Control transfer stage */
|
||||
#define CS_SQER 0x0006 /* Sequence error */
|
||||
#define CS_WRND 0x0005 /* Control write nodata status stage */
|
||||
#define CS_WRSS 0x0004 /* Control write status stage */
|
||||
#define CS_WRDS 0x0003 /* Control write data stage */
|
||||
#define CS_RDSS 0x0002 /* Control read status stage */
|
||||
#define CS_RDDS 0x0001 /* Control read data stage */
|
||||
#define CS_IDST 0x0000 /* Idle or setup stage */
|
||||
|
||||
/* Interrupt Status Register 1 */
|
||||
#define OVRCR 0x8000 /* b15: Over-current interrupt */
|
||||
#define BCHG 0x4000 /* b14: USB bus chenge interrupt */
|
||||
#define DTCH 0x1000 /* b12: Detach sense interrupt */
|
||||
#define ATTCH 0x0800 /* b11: Attach sense interrupt */
|
||||
#define EOFERR 0x0040 /* b6: EOF-error interrupt */
|
||||
#define SIGN 0x0020 /* b5: Setup ignore interrupt */
|
||||
#define SACK 0x0010 /* b4: Setup acknowledge interrupt */
|
||||
|
||||
/* Frame Number Register */
|
||||
#define OVRN 0x8000 /* b15: Overrun error */
|
||||
#define CRCE 0x4000 /* b14: Received data error */
|
||||
#define FRNM 0x07FF /* b10-0: Frame number */
|
||||
|
||||
/* Micro Frame Number Register */
|
||||
#define UFRNM 0x0007 /* b2-0: Micro frame number */
|
||||
|
||||
/* USB Address / Low Power Status Recovery Register */
|
||||
//#define USBADDR 0x007F /* b6-0: USB address */
|
||||
|
||||
/* Default Control Pipe Maxpacket Size Register */
|
||||
/* Pipe Maxpacket Size Register */
|
||||
#define DEVSEL 0xF000 /* b15-14: Device address select */
|
||||
#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */
|
||||
|
||||
/* Default Control Pipe Control Register */
|
||||
#define BSTS 0x8000 /* b15: Buffer status */
|
||||
#define SUREQ 0x4000 /* b14: Send USB request */
|
||||
#define CSCLR 0x2000 /* b13: complete-split status clear */
|
||||
#define CSSTS 0x1000 /* b12: complete-split status */
|
||||
#define SUREQCLR 0x0800 /* b11: stop setup request */
|
||||
#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
|
||||
#define SQSET 0x0080 /* b7: Sequence toggle bit set */
|
||||
#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
|
||||
#define PBUSY 0x0020 /* b5: pipe busy */
|
||||
#define PINGE 0x0010 /* b4: ping enable */
|
||||
#define CCPL 0x0004 /* b2: Enable control transfer complete */
|
||||
#define PID 0x0003 /* b1-0: Response PID */
|
||||
#define PID_STALL11 0x0003 /* STALL */
|
||||
#define PID_STALL 0x0002 /* STALL */
|
||||
#define PID_BUF 0x0001 /* BUF */
|
||||
#define PID_NAK 0x0000 /* NAK */
|
||||
|
||||
/* Pipe Window Select Register */
|
||||
#define PIPENM 0x0007 /* b2-0: Pipe select */
|
||||
|
||||
/* Pipe Configuration Register */
|
||||
#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */
|
||||
#define R8A66597_ISO 0xC000 /* Isochronous */
|
||||
#define R8A66597_INT 0x8000 /* Interrupt */
|
||||
#define R8A66597_BULK 0x4000 /* Bulk */
|
||||
#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */
|
||||
#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */
|
||||
#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */
|
||||
#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */
|
||||
#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */
|
||||
#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */
|
||||
|
||||
/* Pipe Buffer Configuration Register */
|
||||
#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */
|
||||
#define BUFNMB 0x007F /* b6-0: Pipe buffer number */
|
||||
#define PIPE0BUF 256
|
||||
#define PIPExBUF 64
|
||||
|
||||
/* Pipe Maxpacket Size Register */
|
||||
#define MXPS 0x07FF /* b10-0: Maxpacket size */
|
||||
|
||||
/* Pipe Cycle Configuration Register */
|
||||
#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */
|
||||
#define IITV 0x0007 /* b2-0: Isochronous interval */
|
||||
|
||||
/* Pipex Control Register */
|
||||
#define BSTS 0x8000 /* b15: Buffer status */
|
||||
#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */
|
||||
#define CSCLR 0x2000 /* b13: complete-split status clear */
|
||||
#define CSSTS 0x1000 /* b12: complete-split status */
|
||||
#define ATREPM 0x0400 /* b10: Auto repeat mode */
|
||||
#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */
|
||||
#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
|
||||
#define SQSET 0x0080 /* b7: Sequence toggle bit set */
|
||||
#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
|
||||
#define PBUSY 0x0020 /* b5: pipe busy */
|
||||
#define PID 0x0003 /* b1-0: Response PID */
|
||||
|
||||
/* PIPExTRE */
|
||||
#define TRENB 0x0200 /* b9: Transaction counter enable */
|
||||
#define TRCLR 0x0100 /* b8: Transaction counter clear */
|
||||
|
||||
/* PIPExTRN */
|
||||
#define TRNCNT 0xFFFF /* b15-0: Transaction counter */
|
||||
|
||||
/* DEVADDx */
|
||||
#define UPPHUB 0x7800
|
||||
#define HUBPORT 0x0700
|
||||
#define USBSPD 0x00C0
|
||||
#define RTPORT 0x0001
|
||||
|
||||
#define R8A66597_MAX_NUM_PIPE 10
|
||||
#define R8A66597_BUF_BSIZE 8
|
||||
#define R8A66597_MAX_DEVICE 10
|
||||
#define R8A66597_MAX_ROOT_HUB 2
|
||||
#define R8A66597_MAX_SAMPLING 10
|
||||
#define R8A66597_MAX_DMA_CHANNEL 2
|
||||
#define R8A66597_PIPE_NO_DMA R8A66597_MAX_DMA_CHANNEL
|
||||
#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5))
|
||||
#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9))
|
||||
#define make_devsel(addr) (addr << 12)
|
||||
|
||||
struct r8a66597_pipe_info {
|
||||
u16 pipenum;
|
||||
u16 address; /* R8A66597 HCD usb addres */
|
||||
u16 epnum;
|
||||
u16 maxpacket;
|
||||
u16 type;
|
||||
u16 bufnum;
|
||||
u16 buf_bsize;
|
||||
u16 interval;
|
||||
u16 dir_in;
|
||||
};
|
||||
|
||||
struct r8a66597_pipe {
|
||||
struct r8a66597_pipe_info info;
|
||||
|
||||
unsigned long fifoaddr;
|
||||
unsigned long fifosel;
|
||||
unsigned long fifoctr;
|
||||
unsigned long pipectr;
|
||||
unsigned long pipetre;
|
||||
unsigned long pipetrn;
|
||||
};
|
||||
|
||||
struct r8a66597_td {
|
||||
struct r8a66597_pipe *pipe;
|
||||
struct urb *urb;
|
||||
struct list_head queue;
|
||||
|
||||
u16 type;
|
||||
u16 pipenum;
|
||||
int iso_cnt;
|
||||
|
||||
u16 address; /* R8A66597's USB address */
|
||||
u16 maxpacket;
|
||||
|
||||
unsigned zero_packet:1;
|
||||
unsigned short_packet:1;
|
||||
unsigned set_address:1;
|
||||
};
|
||||
|
||||
struct r8a66597_device {
|
||||
u16 address; /* R8A66597's USB address */
|
||||
u16 hub_port;
|
||||
u16 root_port;
|
||||
|
||||
unsigned short ep_in_toggle;
|
||||
unsigned short ep_out_toggle;
|
||||
unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
|
||||
unsigned char dma_map;
|
||||
|
||||
enum usb_device_state state;
|
||||
|
||||
struct usb_device *udev;
|
||||
int usb_address;
|
||||
struct list_head device_list;
|
||||
};
|
||||
|
||||
struct r8a66597_root_hub {
|
||||
u32 port;
|
||||
u16 old_syssts;
|
||||
int scount;
|
||||
|
||||
struct r8a66597_device *dev;
|
||||
};
|
||||
|
||||
struct r8a66597 {
|
||||
spinlock_t lock;
|
||||
unsigned long reg;
|
||||
|
||||
struct r8a66597_device device0;
|
||||
struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB];
|
||||
struct list_head pipe_queue[R8A66597_MAX_NUM_PIPE];
|
||||
|
||||
struct timer_list rh_timer;
|
||||
struct timer_list td_timer[R8A66597_MAX_NUM_PIPE];
|
||||
|
||||
unsigned short address_map;
|
||||
unsigned short timeout_map;
|
||||
unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
|
||||
unsigned char dma_map;
|
||||
|
||||
struct list_head child_device;
|
||||
unsigned long child_connect_map[4];
|
||||
};
|
||||
|
||||
static inline struct r8a66597 *hcd_to_r8a66597(struct usb_hcd *hcd)
|
||||
{
|
||||
return (struct r8a66597 *)(hcd->hcd_priv);
|
||||
}
|
||||
|
||||
static inline struct usb_hcd *r8a66597_to_hcd(struct r8a66597 *r8a66597)
|
||||
{
|
||||
return container_of((void *)r8a66597, struct usb_hcd, hcd_priv);
|
||||
}
|
||||
|
||||
static inline struct r8a66597_td *r8a66597_get_td(struct r8a66597 *r8a66597,
|
||||
u16 pipenum)
|
||||
{
|
||||
if (unlikely(list_empty(&r8a66597->pipe_queue[pipenum])))
|
||||
return NULL;
|
||||
|
||||
return list_entry(r8a66597->pipe_queue[pipenum].next,
|
||||
struct r8a66597_td, queue);
|
||||
}
|
||||
|
||||
static inline struct urb *r8a66597_get_urb(struct r8a66597 *r8a66597,
|
||||
u16 pipenum)
|
||||
{
|
||||
struct r8a66597_td *td;
|
||||
|
||||
td = r8a66597_get_td(r8a66597, pipenum);
|
||||
return (td ? td->urb : NULL);
|
||||
}
|
||||
|
||||
static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
|
||||
{
|
||||
return inw(r8a66597->reg + offset);
|
||||
}
|
||||
|
||||
static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
|
||||
unsigned long offset, u16 *buf,
|
||||
int len)
|
||||
{
|
||||
len = (len + 1) / 2;
|
||||
insw(r8a66597->reg + offset, buf, len);
|
||||
}
|
||||
|
||||
static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
|
||||
unsigned long offset)
|
||||
{
|
||||
outw(val, r8a66597->reg + offset);
|
||||
}
|
||||
|
||||
static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
|
||||
unsigned long offset, u16 *buf,
|
||||
int len)
|
||||
{
|
||||
unsigned long fifoaddr = r8a66597->reg + offset;
|
||||
int odd = len & 0x0001;
|
||||
|
||||
len = len / 2;
|
||||
outsw(fifoaddr, buf, len);
|
||||
if (unlikely(odd)) {
|
||||
buf = &buf[len];
|
||||
outb((unsigned char)*buf, fifoaddr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
|
||||
u16 val, u16 pat, unsigned long offset)
|
||||
{
|
||||
u16 tmp;
|
||||
tmp = r8a66597_read(r8a66597, offset);
|
||||
tmp = tmp & (~pat);
|
||||
tmp = tmp | val;
|
||||
r8a66597_write(r8a66597, tmp, offset);
|
||||
}
|
||||
|
||||
#define r8a66597_bclr(r8a66597, val, offset) \
|
||||
r8a66597_mdfy(r8a66597, 0, val, offset)
|
||||
#define r8a66597_bset(r8a66597, val, offset) \
|
||||
r8a66597_mdfy(r8a66597, val, 0, offset)
|
||||
|
||||
static inline unsigned long get_syscfg_reg(int port)
|
||||
{
|
||||
return port == 0 ? SYSCFG0 : SYSCFG1;
|
||||
}
|
||||
|
||||
static inline unsigned long get_syssts_reg(int port)
|
||||
{
|
||||
return port == 0 ? SYSSTS0 : SYSSTS1;
|
||||
}
|
||||
|
||||
static inline unsigned long get_dvstctr_reg(int port)
|
||||
{
|
||||
return port == 0 ? DVSTCTR0 : DVSTCTR1;
|
||||
}
|
||||
|
||||
static inline unsigned long get_intenb_reg(int port)
|
||||
{
|
||||
return port == 0 ? INTENB1 : INTENB2;
|
||||
}
|
||||
|
||||
static inline unsigned long get_intsts_reg(int port)
|
||||
{
|
||||
return port == 0 ? INTSTS1 : INTSTS2;
|
||||
}
|
||||
|
||||
static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port)
|
||||
{
|
||||
unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
||||
|
||||
return r8a66597_read(r8a66597, dvstctr_reg) & RHST;
|
||||
}
|
||||
|
||||
static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
|
||||
int power)
|
||||
{
|
||||
unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
||||
|
||||
if (power)
|
||||
r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
|
||||
else
|
||||
r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
|
||||
}
|
||||
|
||||
#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2)
|
||||
#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4)
|
||||
#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4)
|
||||
#define get_devadd_addr(address) (DEVADD0 + address * 2)
|
||||
|
||||
#define enable_irq_ready(r8a66597, pipenum) \
|
||||
enable_pipe_irq(r8a66597, pipenum, BRDYENB)
|
||||
#define disable_irq_ready(r8a66597, pipenum) \
|
||||
disable_pipe_irq(r8a66597, pipenum, BRDYENB)
|
||||
#define enable_irq_empty(r8a66597, pipenum) \
|
||||
enable_pipe_irq(r8a66597, pipenum, BEMPENB)
|
||||
#define disable_irq_empty(r8a66597, pipenum) \
|
||||
disable_pipe_irq(r8a66597, pipenum, BEMPENB)
|
||||
#define enable_irq_nrdy(r8a66597, pipenum) \
|
||||
enable_pipe_irq(r8a66597, pipenum, NRDYENB)
|
||||
#define disable_irq_nrdy(r8a66597, pipenum) \
|
||||
disable_pipe_irq(r8a66597, pipenum, NRDYENB)
|
||||
|
||||
#endif /* __R8A66597_H__ */
|
||||
|
@ -730,10 +730,9 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
|
||||
int rc = 0;
|
||||
|
||||
spin_lock_irq(&uhci->lock);
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
|
||||
dev_warn(&hcd->self.root_hub->dev, "HC isn't running!\n");
|
||||
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
|
||||
rc = -ESHUTDOWN;
|
||||
} else if (!uhci->dead)
|
||||
else if (!uhci->dead)
|
||||
wakeup_rh(uhci);
|
||||
spin_unlock_irq(&uhci->lock);
|
||||
return rc;
|
||||
|
@ -108,8 +108,6 @@ struct adu_device {
|
||||
struct urb* interrupt_out_urb;
|
||||
};
|
||||
|
||||
/* prevent races between open() and disconnect */
|
||||
static DEFINE_MUTEX(disconnect_mutex);
|
||||
static struct usb_driver adu_driver;
|
||||
|
||||
static void adu_debug_data(int level, const char *function, int size,
|
||||
@ -256,8 +254,6 @@ static int adu_open(struct inode *inode, struct file *file)
|
||||
|
||||
subminor = iminor(inode);
|
||||
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
interface = usb_find_interface(&adu_driver, subminor);
|
||||
if (!interface) {
|
||||
err("%s - error, can't find device for minor %d",
|
||||
@ -306,7 +302,6 @@ static int adu_open(struct inode *inode, struct file *file)
|
||||
up(&dev->sem);
|
||||
|
||||
exit_no_device:
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval);
|
||||
|
||||
return retval;
|
||||
@ -318,12 +313,6 @@ static int adu_release_internal(struct adu_device *dev)
|
||||
|
||||
dbg(2," %s : enter", __FUNCTION__);
|
||||
|
||||
if (dev->udev == NULL) {
|
||||
/* the device was unplugged before the file was released */
|
||||
adu_delete(dev);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* decrement our usage count for the device */
|
||||
--dev->open_count;
|
||||
dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
|
||||
@ -332,7 +321,6 @@ static int adu_release_internal(struct adu_device *dev)
|
||||
dev->open_count = 0;
|
||||
}
|
||||
|
||||
exit:
|
||||
dbg(2," %s : leave", __FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
@ -367,8 +355,15 @@ static int adu_release(struct inode *inode, struct file *file)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* do the work */
|
||||
retval = adu_release_internal(dev);
|
||||
if (dev->udev == NULL) {
|
||||
/* the device was unplugged before the file was released */
|
||||
up(&dev->sem);
|
||||
adu_delete(dev);
|
||||
dev = NULL;
|
||||
} else {
|
||||
/* do the work */
|
||||
retval = adu_release_internal(dev);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (dev)
|
||||
@ -831,19 +826,17 @@ static void adu_disconnect(struct usb_interface *interface)
|
||||
|
||||
dbg(2," %s : enter", __FUNCTION__);
|
||||
|
||||
mutex_lock(&disconnect_mutex); /* not interruptible */
|
||||
|
||||
dev = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
||||
down(&dev->sem); /* not interruptible */
|
||||
|
||||
minor = dev->minor;
|
||||
|
||||
/* give back our minor */
|
||||
usb_deregister_dev(interface, &adu_class);
|
||||
dev->minor = 0;
|
||||
|
||||
down(&dev->sem); /* not interruptible */
|
||||
|
||||
/* if the device is not opened, then we clean up right now */
|
||||
dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
|
||||
if (!dev->open_count) {
|
||||
@ -854,8 +847,6 @@ static void adu_disconnect(struct usb_interface *interface)
|
||||
up(&dev->sem);
|
||||
}
|
||||
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
|
||||
dev_info(&interface->dev, "ADU device adutux%d now disconnected",
|
||||
(minor - ADU_MINOR_BASE));
|
||||
|
||||
|
@ -2034,12 +2034,12 @@ static void auerswald_disconnect (struct usb_interface *intf)
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
down (&cp->mutex);
|
||||
info ("device /dev/%s now disconnecting", cp->name);
|
||||
|
||||
/* give back our USB minor number */
|
||||
usb_deregister_dev(intf, &auerswald_class);
|
||||
|
||||
down (&cp->mutex);
|
||||
info ("device /dev/%s now disconnecting", cp->name);
|
||||
|
||||
/* Stop the interrupt endpoint */
|
||||
auerswald_int_release (cp);
|
||||
|
||||
|
@ -26,8 +26,11 @@
|
||||
|
||||
#define RIM_VENDOR 0x0fca
|
||||
#define BLACKBERRY 0x0001
|
||||
#define BLACKBERRY_PEARL_DUAL 0x0004
|
||||
#define BLACKBERRY_PEARL 0x0006
|
||||
|
||||
static int debug;
|
||||
static int pearl_dual_mode = 1;
|
||||
|
||||
#ifdef dbg
|
||||
#undef dbg
|
||||
@ -38,6 +41,8 @@ static int debug;
|
||||
|
||||
static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(RIM_VENDOR, BLACKBERRY) },
|
||||
{ USB_DEVICE(RIM_VENDOR, BLACKBERRY_PEARL) },
|
||||
{ USB_DEVICE(RIM_VENDOR, BLACKBERRY_PEARL_DUAL) },
|
||||
{ }, /* Terminating entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
@ -86,6 +91,30 @@ static int magic_charge(struct usb_device *udev)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int magic_dual_mode(struct usb_device *udev)
|
||||
{
|
||||
char *dummy_buffer = kzalloc(2, GFP_KERNEL);
|
||||
int retval;
|
||||
|
||||
if (!dummy_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
/* send magic command so that the Blackberry Pearl device exposes
|
||||
* two interfaces: both the USB mass-storage one and one which can
|
||||
* be used for database access. */
|
||||
dbg(&udev->dev, "Sending magic pearl command\n");
|
||||
retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
|
||||
0xa9, 0xc0, 1, 1, dummy_buffer, 2, 100);
|
||||
dbg(&udev->dev, "Magic pearl command returned %d\n", retval);
|
||||
|
||||
dbg(&udev->dev, "Calling set_configuration\n");
|
||||
retval = usb_driver_set_configuration(udev, 1);
|
||||
if (retval)
|
||||
dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int berry_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
@ -105,6 +134,10 @@ static int berry_probe(struct usb_interface *intf,
|
||||
/* turn the power on */
|
||||
magic_charge(udev);
|
||||
|
||||
if ((le16_to_cpu(udev->descriptor.idProduct) == BLACKBERRY_PEARL) &&
|
||||
(pearl_dual_mode))
|
||||
magic_dual_mode(udev);
|
||||
|
||||
/* we don't really want to bind to the device, userspace programs can
|
||||
* handle the syncing just fine, so get outta here. */
|
||||
return -ENODEV;
|
||||
@ -138,3 +171,5 @@ MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug enabled or not");
|
||||
module_param(pearl_dual_mode, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(pearl_dual_mode, "Change Blackberry Pearl to run in dual mode");
|
||||
|
@ -119,9 +119,6 @@ static struct usb_driver idmouse_driver = {
|
||||
.id_table = idmouse_table,
|
||||
};
|
||||
|
||||
/* prevent races between open() and disconnect() */
|
||||
static DEFINE_MUTEX(disconnect_mutex);
|
||||
|
||||
static int idmouse_create_image(struct usb_idmouse *dev)
|
||||
{
|
||||
int bytes_read;
|
||||
@ -211,21 +208,15 @@ static int idmouse_open(struct inode *inode, struct file *file)
|
||||
struct usb_interface *interface;
|
||||
int result;
|
||||
|
||||
/* prevent disconnects */
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
/* get the interface from minor number and driver information */
|
||||
interface = usb_find_interface (&idmouse_driver, iminor (inode));
|
||||
if (!interface) {
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
if (!interface)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* get the device information block from the interface */
|
||||
dev = usb_get_intfdata(interface);
|
||||
if (!dev) {
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* lock this device */
|
||||
down(&dev->sem);
|
||||
@ -255,9 +246,6 @@ error:
|
||||
|
||||
/* unlock this device */
|
||||
up(&dev->sem);
|
||||
|
||||
/* unlock the disconnect semaphore */
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -265,15 +253,10 @@ static int idmouse_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct usb_idmouse *dev;
|
||||
|
||||
/* prevent a race condition with open() */
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
dev = file->private_data;
|
||||
|
||||
if (dev == NULL) {
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
if (dev == NULL)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* lock our device */
|
||||
down(&dev->sem);
|
||||
@ -281,7 +264,6 @@ static int idmouse_release(struct inode *inode, struct file *file)
|
||||
/* are we really open? */
|
||||
if (dev->open <= 0) {
|
||||
up(&dev->sem);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -291,12 +273,9 @@ static int idmouse_release(struct inode *inode, struct file *file)
|
||||
/* the device was unplugged before the file was released */
|
||||
up(&dev->sem);
|
||||
idmouse_delete(dev);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return 0;
|
||||
} else {
|
||||
up(&dev->sem);
|
||||
}
|
||||
|
||||
up(&dev->sem);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -391,30 +370,27 @@ static void idmouse_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct usb_idmouse *dev;
|
||||
|
||||
/* prevent races with open() */
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
/* get device structure */
|
||||
dev = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
||||
/* lock it */
|
||||
down(&dev->sem);
|
||||
|
||||
/* give back our minor */
|
||||
usb_deregister_dev(interface, &idmouse_class);
|
||||
|
||||
/* lock it */
|
||||
down(&dev->sem);
|
||||
|
||||
/* prevent device read, write and ioctl */
|
||||
dev->present = 0;
|
||||
|
||||
/* unlock */
|
||||
up(&dev->sem);
|
||||
|
||||
/* if the device is opened, idmouse_release will clean this up */
|
||||
if (!dev->open)
|
||||
if (!dev->open) {
|
||||
up(&dev->sem);
|
||||
idmouse_delete(dev);
|
||||
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
} else {
|
||||
/* unlock */
|
||||
up(&dev->sem);
|
||||
}
|
||||
|
||||
info("%s disconnected", DRIVER_DESC);
|
||||
}
|
||||
|
@ -100,8 +100,6 @@ struct iowarrior {
|
||||
/*--------------*/
|
||||
/* globals */
|
||||
/*--------------*/
|
||||
/* prevent races between open() and disconnect() */
|
||||
static DECLARE_MUTEX(disconnect_sem);
|
||||
|
||||
/*
|
||||
* USB spec identifies 5 second timeouts.
|
||||
@ -600,22 +598,18 @@ static int iowarrior_open(struct inode *inode, struct file *file)
|
||||
|
||||
subminor = iminor(inode);
|
||||
|
||||
/* prevent disconnects */
|
||||
down(&disconnect_sem);
|
||||
|
||||
interface = usb_find_interface(&iowarrior_driver, subminor);
|
||||
if (!interface) {
|
||||
err("%s - error, can't find device for minor %d", __FUNCTION__,
|
||||
subminor);
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev = usb_get_intfdata(interface);
|
||||
if (!dev) {
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&dev->mutex);
|
||||
|
||||
/* Only one process can open each device, no sharing. */
|
||||
if (dev->opened) {
|
||||
@ -636,7 +630,7 @@ static int iowarrior_open(struct inode *inode, struct file *file)
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
up(&disconnect_sem);
|
||||
mutex_unlock(&dev->mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -868,19 +862,16 @@ static void iowarrior_disconnect(struct usb_interface *interface)
|
||||
struct iowarrior *dev;
|
||||
int minor;
|
||||
|
||||
/* prevent races with open() */
|
||||
down(&disconnect_sem);
|
||||
|
||||
dev = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
||||
mutex_lock(&dev->mutex);
|
||||
|
||||
minor = dev->minor;
|
||||
|
||||
/* give back our minor */
|
||||
usb_deregister_dev(interface, &iowarrior_class);
|
||||
|
||||
mutex_lock(&dev->mutex);
|
||||
|
||||
/* prevent device read, write and ioctl */
|
||||
dev->present = 0;
|
||||
|
||||
@ -898,7 +889,6 @@ static void iowarrior_disconnect(struct usb_interface *interface)
|
||||
/* no process is using the device, cleanup now */
|
||||
iowarrior_delete(dev);
|
||||
}
|
||||
up(&disconnect_sem);
|
||||
|
||||
dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n",
|
||||
minor - IOWARRIOR_MINOR_BASE);
|
||||
|
@ -176,9 +176,6 @@ struct ld_usb {
|
||||
int interrupt_out_busy;
|
||||
};
|
||||
|
||||
/* prevent races between open() and disconnect() */
|
||||
static DEFINE_MUTEX(disconnect_mutex);
|
||||
|
||||
static struct usb_driver ld_usb_driver;
|
||||
|
||||
/**
|
||||
@ -298,35 +295,28 @@ static int ld_usb_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ld_usb *dev;
|
||||
int subminor;
|
||||
int retval = 0;
|
||||
int retval;
|
||||
struct usb_interface *interface;
|
||||
|
||||
nonseekable_open(inode, file);
|
||||
subminor = iminor(inode);
|
||||
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
interface = usb_find_interface(&ld_usb_driver, subminor);
|
||||
|
||||
if (!interface) {
|
||||
err("%s - error, can't find device for minor %d\n",
|
||||
__FUNCTION__, subminor);
|
||||
retval = -ENODEV;
|
||||
goto unlock_disconnect_exit;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev = usb_get_intfdata(interface);
|
||||
|
||||
if (!dev) {
|
||||
retval = -ENODEV;
|
||||
goto unlock_disconnect_exit;
|
||||
}
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
/* lock this device */
|
||||
if (down_interruptible(&dev->sem)) {
|
||||
retval = -ERESTARTSYS;
|
||||
goto unlock_disconnect_exit;
|
||||
}
|
||||
if (down_interruptible(&dev->sem))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* allow opening only once */
|
||||
if (dev->open_count) {
|
||||
@ -366,9 +356,6 @@ static int ld_usb_open(struct inode *inode, struct file *file)
|
||||
unlock_exit:
|
||||
up(&dev->sem);
|
||||
|
||||
unlock_disconnect_exit:
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -766,18 +753,16 @@ static void ld_usb_disconnect(struct usb_interface *intf)
|
||||
struct ld_usb *dev;
|
||||
int minor;
|
||||
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
dev = usb_get_intfdata(intf);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
down(&dev->sem);
|
||||
|
||||
minor = intf->minor;
|
||||
|
||||
/* give back our minor */
|
||||
usb_deregister_dev(intf, &ld_usb_class);
|
||||
|
||||
down(&dev->sem);
|
||||
|
||||
/* if the device is not opened, then we clean up right now */
|
||||
if (!dev->open_count) {
|
||||
up(&dev->sem);
|
||||
@ -787,8 +772,6 @@ static void ld_usb_disconnect(struct usb_interface *intf)
|
||||
up(&dev->sem);
|
||||
}
|
||||
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
|
||||
dev_info(&intf->dev, "LD USB Device #%d now disconnected\n",
|
||||
(minor - USB_LD_MINOR_BASE));
|
||||
}
|
||||
|
@ -254,9 +254,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_devic
|
||||
static void tower_disconnect (struct usb_interface *interface);
|
||||
|
||||
|
||||
/* prevent races between open() and disconnect */
|
||||
static DEFINE_MUTEX (disconnect_mutex);
|
||||
|
||||
/* file operations needed when we register this driver */
|
||||
static const struct file_operations tower_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
@ -344,28 +341,26 @@ static int tower_open (struct inode *inode, struct file *file)
|
||||
nonseekable_open(inode, file);
|
||||
subminor = iminor(inode);
|
||||
|
||||
mutex_lock (&disconnect_mutex);
|
||||
|
||||
interface = usb_find_interface (&tower_driver, subminor);
|
||||
|
||||
if (!interface) {
|
||||
err ("%s - error, can't find device for minor %d",
|
||||
__FUNCTION__, subminor);
|
||||
retval = -ENODEV;
|
||||
goto unlock_disconnect_exit;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dev = usb_get_intfdata(interface);
|
||||
|
||||
if (!dev) {
|
||||
retval = -ENODEV;
|
||||
goto unlock_disconnect_exit;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* lock this device */
|
||||
if (down_interruptible (&dev->sem)) {
|
||||
retval = -ERESTARTSYS;
|
||||
goto unlock_disconnect_exit;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* allow opening only once */
|
||||
@ -421,9 +416,7 @@ static int tower_open (struct inode *inode, struct file *file)
|
||||
unlock_exit:
|
||||
up (&dev->sem);
|
||||
|
||||
unlock_disconnect_exit:
|
||||
mutex_unlock (&disconnect_mutex);
|
||||
|
||||
exit:
|
||||
dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval);
|
||||
|
||||
return retval;
|
||||
@ -993,19 +986,16 @@ static void tower_disconnect (struct usb_interface *interface)
|
||||
|
||||
dbg(2, "%s: enter", __FUNCTION__);
|
||||
|
||||
mutex_lock (&disconnect_mutex);
|
||||
|
||||
dev = usb_get_intfdata (interface);
|
||||
usb_set_intfdata (interface, NULL);
|
||||
|
||||
|
||||
down (&dev->sem);
|
||||
|
||||
minor = dev->minor;
|
||||
|
||||
/* give back our minor */
|
||||
usb_deregister_dev (interface, &tower_class);
|
||||
|
||||
down (&dev->sem);
|
||||
|
||||
/* if the device is not opened, then we clean up right now */
|
||||
if (!dev->open_count) {
|
||||
up (&dev->sem);
|
||||
@ -1015,8 +1005,6 @@ static void tower_disconnect (struct usb_interface *interface)
|
||||
up (&dev->sem);
|
||||
}
|
||||
|
||||
mutex_unlock (&disconnect_mutex);
|
||||
|
||||
info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE));
|
||||
|
||||
dbg(2, "%s: leave", __FUNCTION__);
|
||||
|
@ -72,8 +72,6 @@ MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES
|
||||
|
||||
static struct usb_driver sisusb_driver;
|
||||
|
||||
DEFINE_MUTEX(disconnect_mutex);
|
||||
|
||||
static void
|
||||
sisusb_free_buffers(struct sisusb_usb_data *sisusb)
|
||||
{
|
||||
@ -2511,31 +2509,24 @@ sisusb_open(struct inode *inode, struct file *file)
|
||||
struct usb_interface *interface;
|
||||
int subminor = iminor(inode);
|
||||
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
|
||||
printk(KERN_ERR "sisusb[%d]: Failed to find interface\n",
|
||||
subminor);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(sisusb = usb_get_intfdata(interface))) {
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
if (!(sisusb = usb_get_intfdata(interface)))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&sisusb->lock);
|
||||
|
||||
if (!sisusb->present || !sisusb->ready) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (sisusb->isopen) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
@ -2543,7 +2534,6 @@ 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);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to initialize "
|
||||
"device\n",
|
||||
@ -2552,7 +2542,6 @@ sisusb_open(struct inode *inode, struct file *file)
|
||||
}
|
||||
} else {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Device not attached to "
|
||||
"USB 2.0 hub\n",
|
||||
@ -2570,8 +2559,6 @@ sisusb_open(struct inode *inode, struct file *file)
|
||||
|
||||
mutex_unlock(&sisusb->lock);
|
||||
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2601,12 +2588,8 @@ sisusb_release(struct inode *inode, struct file *file)
|
||||
struct sisusb_usb_data *sisusb;
|
||||
int myminor;
|
||||
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) {
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&sisusb->lock);
|
||||
|
||||
@ -2626,8 +2609,6 @@ sisusb_release(struct inode *inode, struct file *file)
|
||||
/* decrement the usage count on our device */
|
||||
kref_put(&sisusb->kref, sisusb_delete);
|
||||
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3383,12 +3364,9 @@ static void sisusb_disconnect(struct usb_interface *intf)
|
||||
sisusb_console_exit(sisusb);
|
||||
#endif
|
||||
|
||||
/* The above code doesn't need the disconnect
|
||||
* semaphore to be down; its meaning is to
|
||||
* protect all other routines from the disconnect
|
||||
* case, not the other way round.
|
||||
*/
|
||||
mutex_lock(&disconnect_mutex);
|
||||
minor = sisusb->minor;
|
||||
|
||||
usb_deregister_dev(intf, &usb_sisusb_class);
|
||||
|
||||
mutex_lock(&sisusb->lock);
|
||||
|
||||
@ -3396,12 +3374,8 @@ static void sisusb_disconnect(struct usb_interface *intf)
|
||||
if (!sisusb_wait_all_out_complete(sisusb))
|
||||
sisusb_kill_all_busy(sisusb);
|
||||
|
||||
minor = sisusb->minor;
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
usb_deregister_dev(intf, &usb_sisusb_class);
|
||||
|
||||
#ifdef SISUSB_OLD_CONFIG_COMPAT
|
||||
if (sisusb->ioctl32registered) {
|
||||
int ret;
|
||||
@ -3426,8 +3400,6 @@ static void sisusb_disconnect(struct usb_interface *intf)
|
||||
/* decrement our usage count */
|
||||
kref_put(&sisusb->kref, sisusb_delete);
|
||||
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
|
||||
printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor);
|
||||
}
|
||||
|
||||
|
@ -214,18 +214,13 @@ sisusbcon_init(struct vc_data *c, int init)
|
||||
* are set up/restored.
|
||||
*/
|
||||
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&sisusb->lock);
|
||||
|
||||
if (!sisusb_sisusb_valid(sisusb)) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -264,8 +259,6 @@ sisusbcon_init(struct vc_data *c, int init)
|
||||
|
||||
mutex_unlock(&sisusb->lock);
|
||||
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
|
||||
if (init) {
|
||||
c->vc_cols = cols;
|
||||
c->vc_rows = rows;
|
||||
@ -284,12 +277,8 @@ sisusbcon_deinit(struct vc_data *c)
|
||||
* and others, ie not under our control.
|
||||
*/
|
||||
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&sisusb->lock);
|
||||
|
||||
@ -314,8 +303,6 @@ sisusbcon_deinit(struct vc_data *c)
|
||||
|
||||
/* decrement the usage count on our sisusb */
|
||||
kref_put(&sisusb->kref, sisusb_delete);
|
||||
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
}
|
||||
|
||||
/* interface routine */
|
||||
@ -1490,14 +1477,11 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
|
||||
{
|
||||
int i, ret, minor = sisusb->minor;
|
||||
|
||||
mutex_lock(&disconnect_mutex);
|
||||
|
||||
mutex_lock(&sisusb->lock);
|
||||
|
||||
/* Erm.. that should not happen */
|
||||
if (sisusb->haveconsole || !sisusb->SiS_Pr) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1508,14 +1492,12 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
|
||||
first > MAX_NR_CONSOLES ||
|
||||
last > MAX_NR_CONSOLES) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If gfxcore not initialized or no consoles given, quit graciously */
|
||||
if (!sisusb->gfxinit || first < 1 || last < 1) {
|
||||
mutex_unlock(&sisusb->lock);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1526,7 +1508,6 @@ 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);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to set up text mode\n",
|
||||
minor);
|
||||
@ -1550,7 +1531,6 @@ 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);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
printk(KERN_ERR
|
||||
"sisusbvga[%d]: Failed to allocate screen buffer\n",
|
||||
minor);
|
||||
@ -1558,7 +1538,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
|
||||
}
|
||||
|
||||
mutex_unlock(&sisusb->lock);
|
||||
mutex_unlock(&disconnect_mutex);
|
||||
|
||||
/* Now grab the desired console(s) */
|
||||
ret = take_over_console(&sisusb_con, first - 1, last - 1, 0);
|
||||
|
@ -808,8 +808,6 @@ static const struct SiS_VCLKData SiSUSB_VCLKData[] =
|
||||
{ 0x2b,0xc2, 35} /* 0x71 768@576@60 */
|
||||
};
|
||||
|
||||
extern struct mutex disconnect_mutex;
|
||||
|
||||
int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
|
||||
int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
|
||||
|
||||
|
@ -45,13 +45,13 @@ struct usb_lcd {
|
||||
struct kref kref;
|
||||
struct semaphore limit_sem; /* to stop writes at full throttle from
|
||||
* using up all RAM */
|
||||
struct usb_anchor submitted; /* URBs to wait for before suspend */
|
||||
};
|
||||
#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
|
||||
|
||||
#define USB_LCD_CONCURRENT_WRITES 5
|
||||
|
||||
static struct usb_driver lcd_driver;
|
||||
static DEFINE_MUTEX(usb_lcd_open_mutex);
|
||||
|
||||
|
||||
static void lcd_delete(struct kref *kref)
|
||||
@ -68,35 +68,35 @@ static int lcd_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct usb_lcd *dev;
|
||||
struct usb_interface *interface;
|
||||
int subminor;
|
||||
int retval = 0;
|
||||
int subminor, r;
|
||||
|
||||
subminor = iminor(inode);
|
||||
|
||||
mutex_lock(&usb_lcd_open_mutex);
|
||||
interface = usb_find_interface(&lcd_driver, subminor);
|
||||
if (!interface) {
|
||||
err ("USBLCD: %s - error, can't find device for minor %d",
|
||||
__FUNCTION__, subminor);
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev = usb_get_intfdata(interface);
|
||||
if (!dev) {
|
||||
retval = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
/* increment our usage count for the device */
|
||||
kref_get(&dev->kref);
|
||||
|
||||
/* grab a power reference */
|
||||
r = usb_autopm_get_interface(interface);
|
||||
if (r < 0) {
|
||||
kref_put(&dev->kref, lcd_delete);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* save our object in the file's private structure */
|
||||
file->private_data = dev;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&usb_lcd_open_mutex);
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd_release(struct inode *inode, struct file *file)
|
||||
@ -108,6 +108,7 @@ static int lcd_release(struct inode *inode, struct file *file)
|
||||
return -ENODEV;
|
||||
|
||||
/* decrement the count on our device */
|
||||
usb_autopm_put_interface(dev->interface);
|
||||
kref_put(&dev->kref, lcd_delete);
|
||||
return 0;
|
||||
}
|
||||
@ -233,12 +234,14 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz
|
||||
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
|
||||
buf, count, lcd_write_bulk_callback, dev);
|
||||
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
usb_anchor_urb(urb, &dev->submitted);
|
||||
|
||||
/* send the data out the bulk port */
|
||||
retval = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (retval) {
|
||||
err("USBLCD: %s - failed submitting write urb, error %d", __FUNCTION__, retval);
|
||||
goto error;
|
||||
goto error_unanchor;
|
||||
}
|
||||
|
||||
/* release our reference to this urb, the USB core will eventually free it entirely */
|
||||
@ -246,7 +249,8 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz
|
||||
|
||||
exit:
|
||||
return count;
|
||||
|
||||
error_unanchor:
|
||||
usb_unanchor_urb(urb);
|
||||
error:
|
||||
usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
|
||||
usb_free_urb(urb);
|
||||
@ -291,6 +295,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
|
||||
}
|
||||
kref_init(&dev->kref);
|
||||
sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);
|
||||
init_usb_anchor(&dev->submitted);
|
||||
|
||||
dev->udev = usb_get_dev(interface_to_usbdev(interface));
|
||||
dev->interface = interface;
|
||||
@ -358,22 +363,41 @@ error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void lcd_draw_down(struct usb_lcd *dev)
|
||||
{
|
||||
int time;
|
||||
|
||||
time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
|
||||
if (!time)
|
||||
usb_kill_anchored_urbs(&dev->submitted);
|
||||
}
|
||||
|
||||
static int lcd_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct usb_lcd *dev = usb_get_intfdata(intf);
|
||||
|
||||
if (!dev)
|
||||
return 0;
|
||||
lcd_draw_down(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd_resume (struct usb_interface *intf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lcd_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct usb_lcd *dev;
|
||||
int minor = interface->minor;
|
||||
|
||||
/* prevent skel_open() from racing skel_disconnect() */
|
||||
mutex_lock(&usb_lcd_open_mutex);
|
||||
|
||||
dev = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
||||
/* give back our minor */
|
||||
usb_deregister_dev(interface, &lcd_class);
|
||||
|
||||
mutex_unlock(&usb_lcd_open_mutex);
|
||||
|
||||
/* decrement our usage count */
|
||||
kref_put(&dev->kref, lcd_delete);
|
||||
|
||||
@ -384,7 +408,10 @@ static struct usb_driver lcd_driver = {
|
||||
.name = "usblcd",
|
||||
.probe = lcd_probe,
|
||||
.disconnect = lcd_disconnect,
|
||||
.suspend = lcd_suspend,
|
||||
.resume = lcd_resume,
|
||||
.id_table = id_table,
|
||||
.supports_autosuspend = 1,
|
||||
};
|
||||
|
||||
static int __init usb_lcd_init(void)
|
||||
|
@ -4,7 +4,7 @@
|
||||
* This is a binary format reader.
|
||||
*
|
||||
* Copyright (C) 2006 Paolo Abeni (paolo.abeni@email.it)
|
||||
* Copyright (C) 2006 Pete Zaitcev (zaitcev@redhat.com)
|
||||
* Copyright (C) 2006,2007 Pete Zaitcev (zaitcev@redhat.com)
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
@ -172,6 +172,7 @@ static inline struct mon_bin_hdr *MON_OFF2HDR(const struct mon_reader_bin *rp,
|
||||
|
||||
#define MON_RING_EMPTY(rp) ((rp)->b_cnt == 0)
|
||||
|
||||
static struct class *mon_bin_class;
|
||||
static dev_t mon_bin_dev0;
|
||||
static struct cdev mon_bin_cdev;
|
||||
|
||||
@ -1144,10 +1145,38 @@ static void mon_free_buff(struct mon_pgmap *map, int npages)
|
||||
free_page((unsigned long) map[n].ptr);
|
||||
}
|
||||
|
||||
int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus)
|
||||
{
|
||||
struct device *dev;
|
||||
unsigned minor = ubus? ubus->busnum: 0;
|
||||
|
||||
if (minor >= MON_BIN_MAX_MINOR)
|
||||
return 0;
|
||||
|
||||
dev = device_create(mon_bin_class, ubus? ubus->controller: NULL,
|
||||
MKDEV(MAJOR(mon_bin_dev0), minor), "usbmon%d", minor);
|
||||
if (IS_ERR(dev))
|
||||
return 0;
|
||||
|
||||
mbus->classdev = dev;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void mon_bin_del(struct mon_bus *mbus)
|
||||
{
|
||||
device_destroy(mon_bin_class, mbus->classdev->devt);
|
||||
}
|
||||
|
||||
int __init mon_bin_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
mon_bin_class = class_create(THIS_MODULE, "usbmon");
|
||||
if (IS_ERR(mon_bin_class)) {
|
||||
rc = PTR_ERR(mon_bin_class);
|
||||
goto err_class;
|
||||
}
|
||||
|
||||
rc = alloc_chrdev_region(&mon_bin_dev0, 0, MON_BIN_MAX_MINOR, "usbmon");
|
||||
if (rc < 0)
|
||||
goto err_dev;
|
||||
@ -1164,6 +1193,8 @@ int __init mon_bin_init(void)
|
||||
err_add:
|
||||
unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
|
||||
err_dev:
|
||||
class_destroy(mon_bin_class);
|
||||
err_class:
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1171,4 +1202,5 @@ void mon_bin_exit(void)
|
||||
{
|
||||
cdev_del(&mon_bin_cdev);
|
||||
unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
|
||||
class_destroy(mon_bin_class);
|
||||
}
|
||||
|
@ -220,6 +220,8 @@ static void mon_bus_remove(struct usb_bus *ubus)
|
||||
list_del(&mbus->bus_link);
|
||||
if (mbus->text_inited)
|
||||
mon_text_del(mbus);
|
||||
if (mbus->bin_inited)
|
||||
mon_bin_del(mbus);
|
||||
|
||||
mon_dissolve(mbus, ubus);
|
||||
kref_put(&mbus->ref, mon_bus_drop);
|
||||
@ -301,8 +303,8 @@ static void mon_bus_init(struct usb_bus *ubus)
|
||||
mbus->u_bus = ubus;
|
||||
ubus->mon_bus = mbus;
|
||||
|
||||
mbus->text_inited = mon_text_add(mbus, ubus->busnum);
|
||||
// mon_bin_add(...)
|
||||
mbus->text_inited = mon_text_add(mbus, ubus);
|
||||
mbus->bin_inited = mon_bin_add(mbus, ubus);
|
||||
|
||||
mutex_lock(&mon_lock);
|
||||
list_add_tail(&mbus->bus_link, &mon_buses);
|
||||
@ -321,8 +323,8 @@ static void mon_bus0_init(void)
|
||||
spin_lock_init(&mbus->lock);
|
||||
INIT_LIST_HEAD(&mbus->r_list);
|
||||
|
||||
mbus->text_inited = mon_text_add(mbus, 0);
|
||||
// mbus->bin_inited = mon_bin_add(mbus, 0);
|
||||
mbus->text_inited = mon_text_add(mbus, NULL);
|
||||
mbus->bin_inited = mon_bin_add(mbus, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -403,6 +405,8 @@ static void __exit mon_exit(void)
|
||||
|
||||
if (mbus->text_inited)
|
||||
mon_text_del(mbus);
|
||||
if (mbus->bin_inited)
|
||||
mon_bin_del(mbus);
|
||||
|
||||
/*
|
||||
* This never happens, because the open/close paths in
|
||||
@ -423,6 +427,8 @@ static void __exit mon_exit(void)
|
||||
mbus = &mon_bus0;
|
||||
if (mbus->text_inited)
|
||||
mon_text_del(mbus);
|
||||
if (mbus->bin_inited)
|
||||
mon_bin_del(mbus);
|
||||
|
||||
mutex_unlock(&mon_lock);
|
||||
|
||||
|
@ -655,20 +655,24 @@ static const struct file_operations mon_fops_text_u = {
|
||||
.release = mon_text_release,
|
||||
};
|
||||
|
||||
int mon_text_add(struct mon_bus *mbus, int busnum)
|
||||
int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
|
||||
{
|
||||
struct dentry *d;
|
||||
enum { NAMESZ = 10 };
|
||||
char name[NAMESZ];
|
||||
int busnum = ubus? ubus->busnum: 0;
|
||||
int rc;
|
||||
|
||||
rc = snprintf(name, NAMESZ, "%dt", busnum);
|
||||
if (rc <= 0 || rc >= NAMESZ)
|
||||
goto err_print_t;
|
||||
d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t);
|
||||
if (d == NULL)
|
||||
goto err_create_t;
|
||||
mbus->dent_t = d;
|
||||
if (ubus != NULL) {
|
||||
rc = snprintf(name, NAMESZ, "%dt", busnum);
|
||||
if (rc <= 0 || rc >= NAMESZ)
|
||||
goto err_print_t;
|
||||
d = debugfs_create_file(name, 0600, mon_dir, mbus,
|
||||
&mon_fops_text_t);
|
||||
if (d == NULL)
|
||||
goto err_create_t;
|
||||
mbus->dent_t = d;
|
||||
}
|
||||
|
||||
rc = snprintf(name, NAMESZ, "%du", busnum);
|
||||
if (rc <= 0 || rc >= NAMESZ)
|
||||
@ -694,8 +698,10 @@ err_print_s:
|
||||
mbus->dent_u = NULL;
|
||||
err_create_u:
|
||||
err_print_u:
|
||||
debugfs_remove(mbus->dent_t);
|
||||
mbus->dent_t = NULL;
|
||||
if (ubus != NULL) {
|
||||
debugfs_remove(mbus->dent_t);
|
||||
mbus->dent_t = NULL;
|
||||
}
|
||||
err_create_t:
|
||||
err_print_t:
|
||||
return 0;
|
||||
@ -704,7 +710,8 @@ err_print_t:
|
||||
void mon_text_del(struct mon_bus *mbus)
|
||||
{
|
||||
debugfs_remove(mbus->dent_u);
|
||||
debugfs_remove(mbus->dent_t);
|
||||
if (mbus->dent_t != NULL)
|
||||
debugfs_remove(mbus->dent_t);
|
||||
debugfs_remove(mbus->dent_s);
|
||||
}
|
||||
|
||||
|
@ -20,9 +20,11 @@ struct mon_bus {
|
||||
struct usb_bus *u_bus;
|
||||
|
||||
int text_inited;
|
||||
int bin_inited;
|
||||
struct dentry *dent_s; /* Debugging file */
|
||||
struct dentry *dent_t; /* Text interface file */
|
||||
struct dentry *dent_u; /* Second text interface file */
|
||||
struct device *classdev; /* Device in usbmon class */
|
||||
|
||||
/* Ref */
|
||||
int nreaders; /* Under mon_lock AND mbus->lock */
|
||||
@ -52,9 +54,10 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
|
||||
|
||||
struct mon_bus *mon_bus_lookup(unsigned int num);
|
||||
|
||||
int /*bool*/ mon_text_add(struct mon_bus *mbus, int busnum);
|
||||
int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus);
|
||||
void mon_text_del(struct mon_bus *mbus);
|
||||
// void mon_bin_add(struct mon_bus *);
|
||||
int /*bool*/ mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus);
|
||||
void mon_bin_del(struct mon_bus *mbus);
|
||||
|
||||
int __init mon_text_init(void);
|
||||
void mon_text_exit(void);
|
||||
|
@ -464,6 +464,16 @@ config USB_SERIAL_PL2303
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pl2303.
|
||||
|
||||
config USB_SERIAL_OTI6858
|
||||
tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge Controller (EXPERIMENTAL)"
|
||||
depends on USB_SERIAL
|
||||
help
|
||||
Say Y here if you want to use the OTi-6858 single port USB to serial
|
||||
converter device.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called oti6858.
|
||||
|
||||
config USB_SERIAL_HP4X
|
||||
tristate "USB HP4x Calculators support"
|
||||
depends on USB_SERIAL
|
||||
|
@ -40,6 +40,7 @@ obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o
|
||||
obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o
|
||||
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
|
||||
obj-$(CONFIG_USB_SERIAL_OPTION) += option.o
|
||||
obj-$(CONFIG_USB_SERIAL_OTI6858) += oti6858.o
|
||||
obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o
|
||||
obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o
|
||||
obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o
|
||||
|
@ -411,12 +411,13 @@ static int aircable_write(struct usb_serial_port *port,
|
||||
static void aircable_write_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
int status = urb->status;
|
||||
int result;
|
||||
|
||||
dbg("%s - urb->status: %d", __FUNCTION__ , urb->status);
|
||||
dbg("%s - urb status: %d", __FUNCTION__ , status);
|
||||
|
||||
/* This has been taken from cypress_m8.c cypress_write_int_callback */
|
||||
switch (urb->status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
@ -425,14 +426,14 @@ static void aircable_write_bulk_callback(struct urb *urb)
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
__FUNCTION__, status);
|
||||
port->write_urb_busy = 0;
|
||||
return;
|
||||
default:
|
||||
/* error in the urb, so we have to resubmit it */
|
||||
dbg("%s - Overflow in write", __FUNCTION__);
|
||||
dbg("%s - nonzero write bulk status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
__FUNCTION__, status);
|
||||
port->write_urb->transfer_buffer_length = 1;
|
||||
port->write_urb->dev = port->serial->dev;
|
||||
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||||
@ -457,16 +458,17 @@ static void aircable_read_bulk_callback(struct urb *urb)
|
||||
unsigned long no_packages, remaining, package_length, i;
|
||||
int result, shift = 0;
|
||||
unsigned char *temp;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
if (urb->status) {
|
||||
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
|
||||
if (status) {
|
||||
dbg("%s - urb status = %d", __FUNCTION__, status);
|
||||
if (!port->open_count) {
|
||||
dbg("%s - port is closed, exiting.", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
if (urb->status == -EPROTO) {
|
||||
if (status == -EPROTO) {
|
||||
dbg("%s - caught -EPROTO, resubmitting the urb",
|
||||
__FUNCTION__);
|
||||
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
|
||||
|
@ -82,12 +82,13 @@ static void airprime_read_bulk_callback(struct urb *urb)
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
struct tty_struct *tty;
|
||||
int result;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
if (urb->status) {
|
||||
if (status) {
|
||||
dbg("%s - nonzero read bulk status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
__FUNCTION__, status);
|
||||
return;
|
||||
}
|
||||
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
|
||||
@ -109,6 +110,7 @@ static void airprime_write_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
struct airprime_private *priv = usb_get_serial_port_data(port);
|
||||
int status = urb->status;
|
||||
unsigned long flags;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
@ -116,9 +118,9 @@ static void airprime_write_bulk_callback(struct urb *urb)
|
||||
/* free up the transfer buffer, as usb_free_urb() does not do this */
|
||||
kfree (urb->transfer_buffer);
|
||||
|
||||
if (urb->status)
|
||||
if (status)
|
||||
dbg("%s - nonzero write bulk status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
__FUNCTION__, status);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
--priv->outstanding_urbs;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
@ -172,7 +172,7 @@ static void ark3116_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;
|
||||
}
|
||||
@ -188,16 +188,6 @@ static void ark3116_set_termios(struct usb_serial_port *port,
|
||||
|
||||
cflag = port->tty->termios->c_cflag;
|
||||
|
||||
/* check that they really want us to change something: */
|
||||
if (old_termios) {
|
||||
if ((cflag == old_termios->c_cflag) &&
|
||||
(RELEVANT_IFLAG(port->tty->termios->c_iflag) ==
|
||||
RELEVANT_IFLAG(old_termios->c_iflag))) {
|
||||
dbg("%s - nothing to change...", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
buf = kmalloc(1, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
dbg("error kmalloc");
|
||||
@ -220,7 +210,7 @@ static void ark3116_set_termios(struct usb_serial_port *port,
|
||||
dbg("setting CS7");
|
||||
break;
|
||||
default:
|
||||
err("CSIZE was set but not CS5-CS8, using CS8!");
|
||||
dbg("CSIZE was set but not CS5-CS8, using CS8!");
|
||||
/* fall through */
|
||||
case CS8:
|
||||
config |= 0x03;
|
||||
@ -251,38 +241,33 @@ static void ark3116_set_termios(struct usb_serial_port *port,
|
||||
}
|
||||
|
||||
/* set baudrate */
|
||||
baud = 0;
|
||||
switch (cflag & CBAUD) {
|
||||
case B0:
|
||||
err("can't set 0 baud, using 9600 instead");
|
||||
break;
|
||||
case B75: baud = 75; break;
|
||||
case B150: baud = 150; break;
|
||||
case B300: baud = 300; break;
|
||||
case B600: baud = 600; break;
|
||||
case B1200: baud = 1200; break;
|
||||
case B1800: baud = 1800; break;
|
||||
case B2400: baud = 2400; break;
|
||||
case B4800: baud = 4800; break;
|
||||
case B9600: baud = 9600; break;
|
||||
case B19200: baud = 19200; break;
|
||||
case B38400: baud = 38400; break;
|
||||
case B57600: baud = 57600; break;
|
||||
case B115200: baud = 115200; break;
|
||||
case B230400: baud = 230400; break;
|
||||
case B460800: baud = 460800; break;
|
||||
default:
|
||||
dbg("does not support the baudrate requested (fix it)");
|
||||
break;
|
||||
}
|
||||
baud = tty_get_baud_rate(port->tty);
|
||||
|
||||
/* set 9600 as default (if given baudrate is invalid for example) */
|
||||
if (baud == 0)
|
||||
baud = 9600;
|
||||
switch (baud) {
|
||||
case 75:
|
||||
case 150:
|
||||
case 300:
|
||||
case 600:
|
||||
case 1200:
|
||||
case 1800:
|
||||
case 2400:
|
||||
case 4800:
|
||||
case 9600:
|
||||
case 19200:
|
||||
case 38400:
|
||||
case 57600:
|
||||
case 115200:
|
||||
case 230400:
|
||||
case 460800:
|
||||
break;
|
||||
/* set 9600 as default (if given baudrate is invalid for example) */
|
||||
default:
|
||||
baud = 9600;
|
||||
}
|
||||
|
||||
/*
|
||||
* found by try'n'error, be careful, maybe there are other options
|
||||
* for multiplicator etc!
|
||||
* for multiplicator etc! (3.5 for example)
|
||||
*/
|
||||
if (baud == 460800)
|
||||
/* strange, for 460800 the formula is wrong
|
||||
|
@ -255,9 +255,10 @@ static void belkin_sa_read_int_callback (struct urb *urb)
|
||||
struct belkin_sa_private *priv;
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
int retval;
|
||||
int status = urb->status;
|
||||
unsigned long flags;
|
||||
|
||||
switch (urb->status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
@ -265,10 +266,12 @@ static void belkin_sa_read_int_callback (struct urb *urb)
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__FUNCTION__, status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@ -346,6 +349,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios
|
||||
unsigned long flags;
|
||||
unsigned long control_state;
|
||||
int bad_flow_control;
|
||||
speed_t baud;
|
||||
|
||||
if ((!port->tty) || (!port->tty->termios)) {
|
||||
dbg ("%s - no tty or termios structure", __FUNCTION__);
|
||||
@ -361,16 +365,8 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios
|
||||
bad_flow_control = priv->bad_flow_control;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
/* check that they really want us to change something */
|
||||
if (old_termios) {
|
||||
if ((cflag == old_termios->c_cflag) &&
|
||||
(RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
|
||||
dbg("%s - nothing to change...", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
old_iflag = old_termios->c_iflag;
|
||||
old_cflag = old_termios->c_cflag;
|
||||
}
|
||||
old_iflag = old_termios->c_iflag;
|
||||
old_cflag = old_termios->c_cflag;
|
||||
|
||||
/* Set the baud rate */
|
||||
if( (cflag&CBAUD) != (old_cflag&CBAUD) ) {
|
||||
@ -384,38 +380,30 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios
|
||||
if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 1) < 0)
|
||||
err("Set RTS error");
|
||||
}
|
||||
}
|
||||
|
||||
switch(cflag & CBAUD) {
|
||||
case B0: /* handled below */ break;
|
||||
case B300: urb_value = BELKIN_SA_BAUD(300); break;
|
||||
case B600: urb_value = BELKIN_SA_BAUD(600); break;
|
||||
case B1200: urb_value = BELKIN_SA_BAUD(1200); break;
|
||||
case B2400: urb_value = BELKIN_SA_BAUD(2400); break;
|
||||
case B4800: urb_value = BELKIN_SA_BAUD(4800); break;
|
||||
case B9600: urb_value = BELKIN_SA_BAUD(9600); break;
|
||||
case B19200: urb_value = BELKIN_SA_BAUD(19200); break;
|
||||
case B38400: urb_value = BELKIN_SA_BAUD(38400); break;
|
||||
case B57600: urb_value = BELKIN_SA_BAUD(57600); break;
|
||||
case B115200: urb_value = BELKIN_SA_BAUD(115200); break;
|
||||
case B230400: urb_value = BELKIN_SA_BAUD(230400); break;
|
||||
default: err("BELKIN USB Serial Adapter: unsupported baudrate request, using default of 9600");
|
||||
urb_value = BELKIN_SA_BAUD(9600); break;
|
||||
}
|
||||
if ((cflag & CBAUD) != B0 ) {
|
||||
if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0)
|
||||
err("Set baudrate error");
|
||||
} else {
|
||||
/* Disable flow control */
|
||||
if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, BELKIN_SA_FLOW_NONE) < 0)
|
||||
err("Disable flowcontrol error");
|
||||
baud = tty_get_baud_rate(port->tty);
|
||||
urb_value = BELKIN_SA_BAUD(baud);
|
||||
/* Clip to maximum speed */
|
||||
if (urb_value == 0)
|
||||
urb_value = 1;
|
||||
/* Turn it back into a resulting real baud rate */
|
||||
baud = BELKIN_SA_BAUD(urb_value);
|
||||
/* FIXME: Once the tty updates are done then push this back to the tty */
|
||||
|
||||
/* Drop RTS and DTR */
|
||||
control_state &= ~(TIOCM_DTR | TIOCM_RTS);
|
||||
if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0)
|
||||
err("DTR LOW error");
|
||||
if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0)
|
||||
err("RTS LOW error");
|
||||
}
|
||||
if ((cflag & CBAUD) != B0 ) {
|
||||
if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0)
|
||||
err("Set baudrate error");
|
||||
} else {
|
||||
/* Disable flow control */
|
||||
if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, BELKIN_SA_FLOW_NONE) < 0)
|
||||
err("Disable flowcontrol error");
|
||||
/* Drop RTS and DTR */
|
||||
control_state &= ~(TIOCM_DTR | TIOCM_RTS);
|
||||
if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0)
|
||||
err("DTR LOW error");
|
||||
if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0)
|
||||
err("RTS LOW error");
|
||||
}
|
||||
|
||||
/* set the parity */
|
||||
@ -435,7 +423,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios
|
||||
case CS6: urb_value = BELKIN_SA_DATA_BITS(6); break;
|
||||
case CS7: urb_value = BELKIN_SA_DATA_BITS(7); break;
|
||||
case CS8: urb_value = BELKIN_SA_DATA_BITS(8); break;
|
||||
default: err("CSIZE was not CS5-CS8, using default of 8");
|
||||
default: dbg("CSIZE was not CS5-CS8, using default of 8");
|
||||
urb_value = BELKIN_SA_DATA_BITS(8);
|
||||
break;
|
||||
}
|
||||
|
@ -305,12 +305,13 @@ static void cyberjack_read_int_callback( struct urb *urb )
|
||||
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
|
||||
struct cyberjack_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
int status = urb->status;
|
||||
int result;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
/* the urb might have been killed. */
|
||||
if (urb->status)
|
||||
if (status)
|
||||
return;
|
||||
|
||||
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
|
||||
@ -365,12 +366,14 @@ static void cyberjack_read_bulk_callback (struct urb *urb)
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
short todo;
|
||||
int result;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
|
||||
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
|
||||
if (urb->status) {
|
||||
dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
|
||||
if (status) {
|
||||
dbg("%s - nonzero read bulk status received: %d",
|
||||
__FUNCTION__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -411,12 +414,14 @@ static void cyberjack_write_bulk_callback (struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
|
||||
struct cyberjack_private *priv = usb_get_serial_port_data(port);
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
port->write_urb_busy = 0;
|
||||
if (urb->status) {
|
||||
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
|
||||
if (status) {
|
||||
dbg("%s - nonzero write bulk status received: %d",
|
||||
__FUNCTION__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1275,10 +1275,11 @@ static void cypress_read_int_callback(struct urb *urb)
|
||||
int bytes = 0;
|
||||
int result;
|
||||
int i = 0;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
switch (urb->status) {
|
||||
switch (status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
@ -1292,7 +1293,7 @@ static void cypress_read_int_callback(struct urb *urb)
|
||||
default:
|
||||
/* something ugly is going on... */
|
||||
dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n",
|
||||
__FUNCTION__,urb->status);
|
||||
__FUNCTION__, status);
|
||||
cypress_set_dead(port);
|
||||
return;
|
||||
}
|
||||
@ -1419,10 +1420,11 @@ static void cypress_write_int_callback(struct urb *urb)
|
||||
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
|
||||
struct cypress_private *priv = usb_get_serial_port_data(port);
|
||||
int result;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
switch (urb->status) {
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
@ -1430,7 +1432,8 @@ static void cypress_write_int_callback(struct urb *urb)
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, status);
|
||||
priv->write_urb_in_use = 0;
|
||||
return;
|
||||
case -EPIPE: /* no break needed; clear halt and resubmit */
|
||||
@ -1438,7 +1441,8 @@ static void cypress_write_int_callback(struct urb *urb)
|
||||
break;
|
||||
usb_clear_halt(port->serial->dev, 0x02);
|
||||
/* error in the urb, so we have to resubmit it */
|
||||
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
|
||||
dbg("%s - nonzero write bulk status received: %d",
|
||||
__FUNCTION__, status);
|
||||
port->interrupt_out_urb->transfer_buffer_length = 1;
|
||||
port->interrupt_out_urb->dev = port->serial->dev;
|
||||
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
|
||||
@ -1450,7 +1454,7 @@ static void cypress_write_int_callback(struct urb *urb)
|
||||
break;
|
||||
default:
|
||||
dev_err(&urb->dev->dev,"%s - unexpected nonzero write status received: %d\n",
|
||||
__FUNCTION__,urb->status);
|
||||
__FUNCTION__, status);
|
||||
cypress_set_dead(port);
|
||||
break;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user