Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
Linus Torvalds [Fri, 12 Oct 2007 22:49:10 +0000 (15:49 -0700)]
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (142 commits)
  USB: fix race in autosuspend reschedule
  atmel_usba_udc: Keep track of the device status
  USB: Nikon D40X unusual_devs entry
  USB: serial core should respect driver requirements
  USB: documentation for USB power management
  USB: skip autosuspended devices during system resume
  USB: mutual exclusion for EHCI init and port resets
  USB: allow usbstorage to have LUNS greater than 2Tb
  USB: Adding support for SHARP WS011SH to ipaq.c
  USB: add atmel_usba_udc driver
  USB: ohci SSB bus glue
  USB: ehci build fixes on au1xxx, ppc-soc
  USB: add runtime frame_no quirk for big-endian OHCI
  USB: funsoft: Fix termios
  USB: visor: termios bits
  USB: unusual_devs entry for Nikon DSC D2Xs
  USB: re-remove <linux/usb_sl811.h>
  USB: move <linux/usb_gadget.h> to <linux/usb/gadget.h>
  USB: Export URB statistics for powertop
  USB: serial gadget: Disable endpoints on unload
  ...

117 files changed:
Documentation/usb/authorization.txt [new file with mode: 0644]
Documentation/usb/power-management.txt [new file with mode: 0644]
Documentation/usb/usb-serial.txt
Documentation/usb/usbmon.txt
MAINTAINERS
arch/blackfin/mach-bf537/boards/generic_board.c
arch/blackfin/mach-bf537/boards/pnav10.c
arch/blackfin/mach-bf537/boards/stamp.c
drivers/usb/Makefile
drivers/usb/atm/cxacru.c
drivers/usb/atm/speedtch.c
drivers/usb/atm/ueagle-atm.c
drivers/usb/class/usblp.c
drivers/usb/core/config.c
drivers/usb/core/devio.c
drivers/usb/core/driver.c
drivers/usb/core/endpoint.c
drivers/usb/core/generic.c
drivers/usb/core/hcd.c
drivers/usb/core/hcd.h
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/quirks.c
drivers/usb/core/sysfs.c
drivers/usb/core/urb.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/amd5536udc.c
drivers/usb/gadget/at91_udc.c
drivers/usb/gadget/atmel_usba_udc.c [new file with mode: 0644]
drivers/usb/gadget/atmel_usba_udc.h [new file with mode: 0644]
drivers/usb/gadget/config.c
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/epautoconf.c
drivers/usb/gadget/ether.c
drivers/usb/gadget/file_storage.c
drivers/usb/gadget/fsl_usb2_udc.c
drivers/usb/gadget/gmidi.c
drivers/usb/gadget/goku_udc.c
drivers/usb/gadget/inode.c
drivers/usb/gadget/lh7a40x_udc.h
drivers/usb/gadget/m66592-udc.c
drivers/usb/gadget/net2280.c
drivers/usb/gadget/omap_udc.c
drivers/usb/gadget/pxa2xx_udc.c
drivers/usb/gadget/s3c2410_udc.c
drivers/usb/gadget/serial.c
drivers/usb/gadget/usbstring.c
drivers/usb/gadget/zero.c
drivers/usb/host/Kconfig
drivers/usb/host/ehci-au1xxx.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-ppc-soc.c
drivers/usb/host/ehci-ps3.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/isp116x-hcd.c
drivers/usb/host/ohci-dbg.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-mem.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci-ppc-of.c
drivers/usb/host/ohci-ppc-soc.c
drivers/usb/host/ohci-q.c
drivers/usb/host/ohci-ssb.c [new file with mode: 0644]
drivers/usb/host/ohci.h
drivers/usb/host/r8a66597-hcd.c
drivers/usb/host/sl811-hcd.c
drivers/usb/host/u132-hcd.c
drivers/usb/host/uhci-debug.c
drivers/usb/host/uhci-hcd.h
drivers/usb/host/uhci-q.c
drivers/usb/misc/adutux.c
drivers/usb/misc/berry_charge.c
drivers/usb/misc/ftdi-elan.c
drivers/usb/misc/sisusbvga/sisusb.c
drivers/usb/misc/sisusbvga/sisusb.h
drivers/usb/misc/sisusbvga/sisusb_con.c
drivers/usb/misc/sisusbvga/sisusb_init.c
drivers/usb/misc/sisusbvga/sisusb_init.h
drivers/usb/misc/sisusbvga/sisusb_struct.h
drivers/usb/mon/mon_bin.c
drivers/usb/mon/mon_main.c
drivers/usb/mon/mon_text.c
drivers/usb/mon/usb_mon.h
drivers/usb/serial/Kconfig
drivers/usb/serial/Makefile
drivers/usb/serial/ark3116.c
drivers/usb/serial/bus.c
drivers/usb/serial/ch341.c [new file with mode: 0644]
drivers/usb/serial/cp2101.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/funsoft.c
drivers/usb/serial/ipaq.c
drivers/usb/serial/kl5kusb105.c
drivers/usb/serial/kobil_sct.c
drivers/usb/serial/mct_u232.c
drivers/usb/serial/oti6858.c
drivers/usb/serial/pl2303.c
drivers/usb/serial/pl2303.h
drivers/usb/serial/safe_serial.c
drivers/usb/serial/usb-serial.c
drivers/usb/serial/visor.c
drivers/usb/storage/initializers.c
drivers/usb/storage/initializers.h
drivers/usb/storage/shuttle_usbat.c
drivers/usb/storage/unusual_devs.h
drivers/usb/storage/usb.c
drivers/usb/usb-skeleton.c
include/linux/usb.h
include/linux/usb/gadget.h [moved from include/linux/usb_gadget.h with 92% similarity]
include/linux/usb/quirks.h
include/linux/usb/serial.h
include/linux/usb_sl811.h [deleted file]

diff --git a/Documentation/usb/authorization.txt b/Documentation/usb/authorization.txt
new file mode 100644 (file)
index 0000000..2af4006
--- /dev/null
@@ -0,0 +1,92 @@
+
+Authorizing (or not) your USB devices to connect to the system
+
+(C) 2007 Inaky Perez-Gonzalez <inaky@linux.intel.com> Intel Corporation
+
+This feature allows you to control if a USB device can be used (or
+not) in a system. This feature will allow you to implement a lock-down
+of USB devices, fully controlled by user space.
+
+As of now, when a USB device is connected it is configured and
+it's interfaces inmediately made available to the users. With this
+modification, only if root authorizes the device to be configured will
+then it be possible to use it.
+
+Usage:
+
+Authorize a device to connect:
+
+$ echo 1 > /sys/usb/devices/DEVICE/authorized
+
+Deauthorize a device:
+
+$ echo 0 > /sys/usb/devices/DEVICE/authorized
+
+Set new devices connected to hostX to be deauthorized by default (ie:
+lock down):
+
+$ echo 0 > /sys/bus/devices/usbX/authorized_default
+
+Remove the lock down:
+
+$ echo 1 > /sys/bus/devices/usbX/authorized_default
+
+By default, Wired USB devices are authorized by default to
+connect. Wireless USB hosts deauthorize by default all new connected
+devices (this is so because we need to do an authentication phase
+before authorizing).
+
+
+Example system lockdown (lame)
+-----------------------
+
+Imagine you want to implement a lockdown so only devices of type XYZ
+can be connected (for example, it is a kiosk machine with a visible
+USB port):
+
+boot up
+rc.local ->
+
+ for host in /sys/bus/devices/usb*
+ do
+    echo 0 > $host/authorized_default
+ done
+
+Hookup an script to udev, for new USB devices
+
+ if device_is_my_type $DEV
+ then
+   echo 1 > $device_path/authorized
+ done
+
+
+Now, device_is_my_type() is where the juice for a lockdown is. Just
+checking if the class, type and protocol match something is the worse
+security verification you can make (or the best, for someone willing
+to break it). If you need something secure, use crypto and Certificate
+Authentication or stuff like that. Something simple for an storage key
+could be:
+
+function device_is_my_type()
+{
+   echo 1 > authorized         # temporarily authorize it
+                                # FIXME: make sure none can mount it
+   mount DEVICENODE /mntpoint
+   sum=$(md5sum /mntpoint/.signature)
+   if [ $sum = $(cat /etc/lockdown/keysum) ]
+   then
+        echo "We are good, connected"
+        umount /mntpoint
+        # Other stuff so others can use it
+   else
+        echo 0 > authorized
+   fi
+}
+
+
+Of course, this is lame, you'd want to do a real certificate
+verification stuff with PKI, so you don't depend on a shared secret,
+etc, but you get the idea. Anybody with access to a device gadget kit
+can fake descriptors and device info. Don't trust that. You are
+welcome.
+
diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt
new file mode 100644 (file)
index 0000000..97842de
--- /dev/null
@@ -0,0 +1,517 @@
+                       Power Management for USB
+
+                Alan Stern <stern@rowland.harvard.edu>
+
+                           October 5, 2007
+
+
+
+       What is Power Management?
+       -------------------------
+
+Power Management (PM) is the practice of saving energy by suspending
+parts of a computer system when they aren't being used.  While a
+component is "suspended" it is in a nonfunctional low-power state; it
+might even be turned off completely.  A suspended component can be
+"resumed" (returned to a functional full-power state) when the kernel
+needs to use it.  (There also are forms of PM in which components are
+placed in a less functional but still usable state instead of being
+suspended; an example would be reducing the CPU's clock rate.  This
+document will not discuss those other forms.)
+
+When the parts being suspended include the CPU and most of the rest of
+the system, we speak of it as a "system suspend".  When a particular
+device is turned off while the system as a whole remains running, we
+call it a "dynamic suspend" (also known as a "runtime suspend" or
+"selective suspend").  This document concentrates mostly on how
+dynamic PM is implemented in the USB subsystem, although system PM is
+covered to some extent (see Documentation/power/*.txt for more
+information about system PM).
+
+Note: Dynamic PM support for USB is present only if the kernel was
+built with CONFIG_USB_SUSPEND enabled.  System PM support is present
+only if the kernel was built with CONFIG_SUSPEND or CONFIG_HIBERNATION
+enabled.
+
+
+       What is Remote Wakeup?
+       ----------------------
+
+When a device has been suspended, it generally doesn't resume until
+the computer tells it to.  Likewise, if the entire computer has been
+suspended, it generally doesn't resume until the user tells it to, say
+by pressing a power button or opening the cover.
+
+However some devices have the capability of resuming by themselves, or
+asking the kernel to resume them, or even telling the entire computer
+to resume.  This capability goes by several names such as "Wake On
+LAN"; we will refer to it generically as "remote wakeup".  When a
+device is enabled for remote wakeup and it is suspended, it may resume
+itself (or send a request to be resumed) in response to some external
+event.  Examples include a suspended keyboard resuming when a key is
+pressed, or a suspended USB hub resuming when a device is plugged in.
+
+
+       When is a USB device idle?
+       --------------------------
+
+A device is idle whenever the kernel thinks it's not busy doing
+anything important and thus is a candidate for being suspended.  The
+exact definition depends on the device's driver; drivers are allowed
+to declare that a device isn't idle even when there's no actual
+communication taking place.  (For example, a hub isn't considered idle
+unless all the devices plugged into that hub are already suspended.)
+In addition, a device isn't considered idle so long as a program keeps
+its usbfs file open, whether or not any I/O is going on.
+
+If a USB device has no driver, its usbfs file isn't open, and it isn't
+being accessed through sysfs, then it definitely is idle.
+
+
+       Forms of dynamic PM
+       -------------------
+
+Dynamic suspends can occur in two ways: manual and automatic.
+"Manual" means that the user has told the kernel to suspend a device,
+whereas "automatic" means that the kernel has decided all by itself to
+suspend a device.  Automatic suspend is called "autosuspend" for
+short.  In general, a device won't be autosuspended unless it has been
+idle for some minimum period of time, the so-called idle-delay time.
+
+Of course, nothing the kernel does on its own initiative should
+prevent the computer or its devices from working properly.  If a
+device has been autosuspended and a program tries to use it, the
+kernel will automatically resume the device (autoresume).  For the
+same reason, an autosuspended device will usually have remote wakeup
+enabled, if the device supports remote wakeup.
+
+It is worth mentioning that many USB drivers don't support
+autosuspend.  In fact, at the time of this writing (Linux 2.6.23) the
+only drivers which do support it are the hub driver, kaweth, asix,
+usblp, usblcd, and usb-skeleton (which doesn't count).  If a
+non-supporting driver is bound to a device, the device won't be
+autosuspended.  In effect, the kernel pretends the device is never
+idle.
+
+We can categorize power management events in two broad classes:
+external and internal.  External events are those triggered by some
+agent outside the USB stack: system suspend/resume (triggered by
+userspace), manual dynamic suspend/resume (also triggered by
+userspace), and remote wakeup (triggered by the device).  Internal
+events are those triggered within the USB stack: autosuspend and
+autoresume.
+
+
+       The user interface for dynamic PM
+       ---------------------------------
+
+The user interface for controlling dynamic PM is located in the power/
+subdirectory of each USB device's sysfs directory, that is, in
+/sys/bus/usb/devices/.../power/ where "..." is the device's ID.  The
+relevant attribute files are: wakeup, level, and autosuspend.
+
+       power/wakeup
+
+               This file is empty if the device does not support
+               remote wakeup.  Otherwise the file contains either the
+               word "enabled" or the word "disabled", and you can
+               write those words to the file.  The setting determines
+               whether or not remote wakeup will be enabled when the
+               device is next suspended.  (If the setting is changed
+               while the device is suspended, the change won't take
+               effect until the following suspend.)
+
+       power/level
+
+               This file contains one of three words: "on", "auto",
+               or "suspend".  You can write those words to the file
+               to change the device's setting.
+
+               "on" means that the device should be resumed and
+               autosuspend is not allowed.  (Of course, system
+               suspends are still allowed.)
+
+               "auto" is the normal state in which the kernel is
+               allowed to autosuspend and autoresume the device.
+
+               "suspend" means that the device should remain
+               suspended, and autoresume is not allowed.  (But remote
+               wakeup may still be allowed, since it is controlled
+               separately by the power/wakeup attribute.)
+
+       power/autosuspend
+
+               This file contains an integer value, which is the
+               number of seconds the device should remain idle before
+               the kernel will autosuspend it (the idle-delay time).
+               The default is 2.  0 means to autosuspend as soon as
+               the device becomes idle, and -1 means never to
+               autosuspend.  You can write a number to the file to
+               change the autosuspend idle-delay time.
+
+Writing "-1" to power/autosuspend and writing "on" to power/level do
+essentially the same thing -- they both prevent the device from being
+autosuspended.  Yes, this is a redundancy in the API.
+
+(In 2.6.21 writing "0" to power/autosuspend would prevent the device
+from being autosuspended; the behavior was changed in 2.6.22.  The
+power/autosuspend attribute did not exist prior to 2.6.21, and the
+power/level attribute did not exist prior to 2.6.22.)
+
+
+       Changing the default idle-delay time
+       ------------------------------------
+
+The default autosuspend idle-delay time is controlled by a module
+parameter in usbcore.  You can specify the value when usbcore is
+loaded.  For example, to set it to 5 seconds instead of 2 you would
+do:
+
+       modprobe usbcore autosuspend=5
+
+Equivalently, you could add to /etc/modprobe.conf a line saying:
+
+       options usbcore autosuspend=5
+
+Some distributions load the usbcore module very early during the boot
+process, by means of a program or script running from an initramfs
+image.  To alter the parameter value you would have to rebuild that
+image.
+
+If usbcore is compiled into the kernel rather than built as a loadable
+module, you can add
+
+       usbcore.autosuspend=5
+
+to the kernel's boot command line.
+
+Finally, the parameter value can be changed while the system is
+running.  If you do:
+
+       echo 5 >/sys/module/usbcore/parameters/autosuspend
+
+then each new USB device will have its autosuspend idle-delay
+initialized to 5.  (The idle-delay values for already existing devices
+will not be affected.)
+
+Setting the initial default idle-delay to -1 will prevent any
+autosuspend of any USB device.  This is a simple alternative to
+disabling CONFIG_USB_SUSPEND and rebuilding the kernel, and it has the
+added benefit of allowing you to enable autosuspend for selected
+devices.
+
+
+       Warnings
+       --------
+
+The USB specification states that all USB devices must support power
+management.  Nevertheless, the sad fact is that many devices do not
+support it very well.  You can suspend them all right, but when you
+try to resume them they disconnect themselves from the USB bus or
+they stop working entirely.  This seems to be especially prevalent
+among printers and scanners, but plenty of other types of device have
+the same deficiency.
+
+For this reason, by default the kernel disables autosuspend (the
+power/level attribute is initialized to "on") for all devices other
+than hubs.  Hubs, at least, appear to be reasonably well-behaved in
+this regard.
+
+(In 2.6.21 and 2.6.22 this wasn't the case.  Autosuspend was enabled
+by default for almost all USB devices.  A number of people experienced
+problems as a result.)
+
+This means that non-hub devices won't be autosuspended unless the user
+or a program explicitly enables it.  As of this writing there aren't
+any widespread programs which will do this; we hope that in the near
+future device managers such as HAL will take on this added
+responsibility.  In the meantime you can always carry out the
+necessary operations by hand or add them to a udev script.  You can
+also change the idle-delay time; 2 seconds is not the best choice for
+every device.
+
+Sometimes it turns out that even when a device does work okay with
+autosuspend there are still problems.  For example, there are
+experimental patches adding autosuspend support to the usbhid driver,
+which manages keyboards and mice, among other things.  Tests with a
+number of keyboards showed that typing on a suspended keyboard, while
+causing the keyboard to do a remote wakeup all right, would
+nonetheless frequently result in lost keystrokes.  Tests with mice
+showed that some of them would issue a remote-wakeup request in
+response to button presses but not to motion, and some in response to
+neither.
+
+The kernel will not prevent you from enabling autosuspend on devices
+that can't handle it.  It is even possible in theory to damage a
+device by suspending it at the wrong time -- for example, suspending a
+USB hard disk might cause it to spin down without parking the heads.
+(Highly unlikely, but possible.)  Take care.
+
+
+       The driver interface for Power Management
+       -----------------------------------------
+
+The requirements for a USB driver to support external power management
+are pretty modest; the driver need only define
+
+       .suspend
+       .resume
+       .reset_resume
+
+methods in its usb_driver structure, and the reset_resume method is
+optional.  The methods' jobs are quite simple:
+
+       The suspend method is called to warn the driver that the
+       device is going to be suspended.  If the driver returns a
+       negative error code, the suspend will be aborted.  Normally
+       the driver will return 0, in which case it must cancel all
+       outstanding URBs (usb_kill_urb()) and not submit any more.
+
+       The resume method is called to tell the driver that the
+       device has been resumed and the driver can return to normal
+       operation.  URBs may once more be submitted.
+
+       The reset_resume method is called to tell the driver that
+       the device has been resumed and it also has been reset.
+       The driver should redo any necessary device initialization,
+       since the device has probably lost most or all of its state
+       (although the interfaces will be in the same altsettings as
+       before the suspend).
+
+The reset_resume method is used by the USB Persist facility (see
+Documentation/usb/persist.txt) and it can also be used under certain
+circumstances when CONFIG_USB_PERSIST is not enabled.  Currently, if a
+device is reset during a resume and the driver does not have a
+reset_resume method, the driver won't receive any notification about
+the resume.  Later kernels will call the driver's disconnect method;
+2.6.23 doesn't do this.
+
+USB drivers are bound to interfaces, so their suspend and resume
+methods get called when the interfaces are suspended or resumed.  In
+principle one might want to suspend some interfaces on a device (i.e.,
+force the drivers for those interface to stop all activity) without
+suspending the other interfaces.  The USB core doesn't allow this; all
+interfaces are suspended when the device itself is suspended and all
+interfaces are resumed when the device is resumed.  It isn't possible
+to suspend or resume some but not all of a device's interfaces.  The
+closest you can come is to unbind the interfaces' drivers.
+
+
+       The driver interface for autosuspend and autoresume
+       ---------------------------------------------------
+
+To support autosuspend and autoresume, a driver should implement all
+three of the methods listed above.  In addition, a driver indicates
+that it supports autosuspend by setting the .supports_autosuspend flag
+in its usb_driver structure.  It is then responsible for informing the
+USB core whenever one of its interfaces becomes busy or idle.  The
+driver does so by calling these three functions:
+
+       int  usb_autopm_get_interface(struct usb_interface *intf);
+       void usb_autopm_put_interface(struct usb_interface *intf);
+       int  usb_autopm_set_interface(struct usb_interface *intf);
+
+The functions work by maintaining a counter in the usb_interface
+structure.  When intf->pm_usage_count is > 0 then the interface is
+deemed to be busy, and the kernel will not autosuspend the interface's
+device.  When intf->pm_usage_count is <= 0 then the interface is
+considered to be idle, and the kernel may autosuspend the device.
+
+(There is a similar pm_usage_count field in struct usb_device,
+associated with the device itself rather than any of its interfaces.
+This field is used only by the USB core.)
+
+The driver owns intf->pm_usage_count; it can modify the value however
+and whenever it likes.  A nice aspect of the usb_autopm_* routines is
+that the changes they make are protected by the usb_device structure's
+PM mutex (udev->pm_mutex); however drivers may change pm_usage_count
+without holding the mutex.
+
+       usb_autopm_get_interface() increments pm_usage_count and
+       attempts an autoresume if the new value is > 0 and the
+       device is suspended.
+
+       usb_autopm_put_interface() decrements pm_usage_count and
+       attempts an autosuspend if the new value is <= 0 and the
+       device isn't suspended.
+
+       usb_autopm_set_interface() leaves pm_usage_count alone.
+       It attempts an autoresume if the value is > 0 and the device
+       is suspended, and it attempts an autosuspend if the value is
+       <= 0 and the device isn't suspended.
+
+There also are a couple of utility routines drivers can use:
+
+       usb_autopm_enable() sets pm_usage_cnt to 1 and then calls
+       usb_autopm_set_interface(), which will attempt an autoresume.
+
+       usb_autopm_disable() sets pm_usage_cnt to 0 and then calls
+       usb_autopm_set_interface(), which will attempt an autosuspend.
+
+The conventional usage pattern is that a driver calls
+usb_autopm_get_interface() in its open routine and
+usb_autopm_put_interface() in its close or release routine.  But
+other patterns are possible.
+
+The autosuspend attempts mentioned above will often fail for one
+reason or another.  For example, the power/level attribute might be
+set to "on", or another interface in the same device might not be
+idle.  This is perfectly normal.  If the reason for failure was that
+the device hasn't been idle for long enough, a delayed workqueue
+routine is automatically set up to carry out the operation when the
+autosuspend idle-delay has expired.
+
+Autoresume attempts also can fail.  This will happen if power/level is
+set to "suspend" or if the device doesn't manage to resume properly.
+Unlike autosuspend, there's no delay for an autoresume.
+
+
+       Other parts of the driver interface
+       -----------------------------------
+
+Sometimes a driver needs to make sure that remote wakeup is enabled
+during autosuspend.  For example, there's not much point
+autosuspending a keyboard if the user can't cause the keyboard to do a
+remote wakeup by typing on it.  If the driver sets
+intf->needs_remote_wakeup to 1, the kernel won't autosuspend the
+device if remote wakeup isn't available or has been disabled through
+the power/wakeup attribute.  (If the device is already autosuspended,
+though, setting this flag won't cause the kernel to autoresume it.
+Normally a driver would set this flag in its probe method, at which
+time the device is guaranteed not to be autosuspended.)
+
+The usb_autopm_* routines have to run in a sleepable process context;
+they must not be called from an interrupt handler or while holding a
+spinlock.  In fact, the entire autosuspend mechanism is not well geared
+toward interrupt-driven operation.  However there is one thing a
+driver can do in an interrupt handler:
+
+       usb_mark_last_busy(struct usb_device *udev);
+
+This sets udev->last_busy to the current time.  udev->last_busy is the
+field used for idle-delay calculations; updating it will cause any
+pending autosuspend to be moved back.  The usb_autopm_* routines will
+also set the last_busy field to the current time.
+
+Calling urb_mark_last_busy() from within an URB completion handler is
+subject to races: The kernel may have just finished deciding the
+device has been idle for long enough but not yet gotten around to
+calling the driver's suspend method.  The driver would have to be
+responsible for synchronizing its suspend method with its URB
+completion handler and causing the autosuspend to fail with -EBUSY if
+an URB had completed too recently.
+
+External suspend calls should never be allowed to fail in this way,
+only autosuspend calls.  The driver can tell them apart by checking
+udev->auto_pm; this flag will be set to 1 for internal PM events
+(autosuspend or autoresume) and 0 for external PM events.
+
+Many of the ingredients in the autosuspend framework are oriented
+towards interfaces: The usb_interface structure contains the
+pm_usage_cnt field, and the usb_autopm_* routines take an interface
+pointer as their argument.  But somewhat confusingly, a few of the
+pieces (usb_mark_last_busy() and udev->auto_pm) use the usb_device
+structure instead.  Drivers need to keep this straight; they can call
+interface_to_usbdev() to find the device structure for a given
+interface.
+
+
+       Locking requirements
+       --------------------
+
+All three suspend/resume methods are always called while holding the
+usb_device's PM mutex.  For external events -- but not necessarily for
+autosuspend or autoresume -- the device semaphore (udev->dev.sem) will
+also be held.  This implies that external suspend/resume events are
+mutually exclusive with calls to probe, disconnect, pre_reset, and
+post_reset; the USB core guarantees that this is true of internal
+suspend/resume events as well.
+
+If a driver wants to block all suspend/resume calls during some
+critical section, it can simply acquire udev->pm_mutex.
+Alternatively, if the critical section might call some of the
+usb_autopm_* routines, the driver can avoid deadlock by doing:
+
+       down(&udev->dev.sem);
+       rc = usb_autopm_get_interface(intf);
+
+and at the end of the critical section:
+
+       if (!rc)
+               usb_autopm_put_interface(intf);
+       up(&udev->dev.sem);
+
+Holding the device semaphore will block all external PM calls, and the
+usb_autopm_get_interface() will prevent any internal PM calls, even if
+it fails.  (Exercise: Why?)
+
+The rules for locking order are:
+
+       Never acquire any device semaphore while holding any PM mutex.
+
+       Never acquire udev->pm_mutex while holding the PM mutex for
+       a device that isn't a descendant of udev.
+
+In other words, PM mutexes should only be acquired going up the device
+tree, and they should be acquired only after locking all the device
+semaphores you need to hold.  These rules don't matter to drivers very
+much; they usually affect just the USB core.
+
+Still, drivers do need to be careful.  For example, many drivers use a
+private mutex to synchronize their normal I/O activities with their
+disconnect method.  Now if the driver supports autosuspend then it
+must call usb_autopm_put_interface() from somewhere -- maybe from its
+close method.  It should make the call while holding the private mutex,
+since a driver shouldn't call any of the usb_autopm_* functions for an
+interface from which it has been unbound.
+
+But the usb_autpm_* routines always acquire the device's PM mutex, and
+consequently the locking order has to be: private mutex first, PM
+mutex second.  Since the suspend method is always called with the PM
+mutex held, it mustn't try to acquire the private mutex.  It has to
+synchronize with the driver's I/O activities in some other way.
+
+
+       Interaction between dynamic PM and system PM
+       --------------------------------------------
+
+Dynamic power management and system power management can interact in
+a couple of ways.
+
+Firstly, a device may already be manually suspended or autosuspended
+when a system suspend occurs.  Since system suspends are supposed to
+be as transparent as possible, the device should remain suspended
+following the system resume.  The 2.6.23 kernel obeys this principle
+for manually suspended devices but not for autosuspended devices; they
+do get resumed when the system wakes up.  (Presumably they will be
+autosuspended again after their idle-delay time expires.)  In later
+kernels this behavior will be fixed.
+
+(There is an exception.  If a device would undergo a reset-resume
+instead of a normal resume, and the device is enabled for remote
+wakeup, then the reset-resume takes place even if the device was
+already suspended when the system suspend began.  The justification is
+that a reset-resume is a kind of remote-wakeup event.  Or to put it
+another way, a device which needs a reset won't be able to generate
+normal remote-wakeup signals, so it ought to be resumed immediately.)
+
+Secondly, a dynamic power-management event may occur as a system
+suspend is underway.  The window for this is short, since system
+suspends don't take long (a few seconds usually), but it can happen.
+For example, a suspended device may send a remote-wakeup signal while
+the system is suspending.  The remote wakeup may succeed, which would
+cause the system suspend to abort.  If the remote wakeup doesn't
+succeed, it may still remain active and thus cause the system to
+resume as soon as the system suspend is complete.  Or the remote
+wakeup may fail and get lost.  Which outcome occurs depends on timing
+and on the hardware and firmware design.
+
+More interestingly, a device might undergo a manual resume or
+autoresume during system suspend.  With current kernels this shouldn't
+happen, because manual resumes must be initiated by userspace and
+autoresumes happen in response to I/O requests, but all user processes
+and I/O should be quiescent during a system suspend -- thanks to the
+freezer.  However there are plans to do away with the freezer, which
+would mean these things would become possible.  If and when this comes
+about, the USB core will carefully arrange matters so that either type
+of resume will block until the entire system has resumed.
index 5b635ae..4e0b62b 100644 (file)
@@ -428,6 +428,17 @@ Options supported:
   See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
   information on this driver.
 
+Winchiphead CH341 Driver
+
+  This driver is for the Winchiphead CH341 USB-RS232 Converter. This chip
+  also implements an IEEE 1284 parallel port, I2C and SPI, but that is not
+  supported by the driver. The protocol was analyzed from the behaviour
+  of the Windows driver, no datasheet is available at present.
+  The manufacturer's website: http://www.winchiphead.com/.
+  For any questions or problems with this driver, please contact
+  frank@kingswood-consulting.co.uk.
+
+
 Generic Serial driver
 
   If your device is not one of the above listed devices, compatible with
index 53ae866..2917ce4 100644 (file)
@@ -34,9 +34,12 @@ if usbmon is built into the kernel.
 Verify that bus sockets are present.
 
 # ls /sys/kernel/debug/usbmon
-1s  1t  1u  2s  2t  2u  3s  3t  3u  4s  4t  4u
+0s  0t  0u  1s  1t  1u  2s  2t  2u  3s  3t  3u  4s  4t  4u
 #
 
+Now you can choose to either use the sockets numbered '0' (to capture packets on
+all buses), and skip to step #3, or find the bus used by your device with step #2.
+
 2. Find which bus connects to the desired device
 
 Run "cat /proc/bus/usb/devices", and find the T-line which corresponds to
@@ -56,6 +59,10 @@ Bus=03 means it's bus 3.
 
 # cat /sys/kernel/debug/usbmon/3u > /tmp/1.mon.out
 
+to listen on a single bus, otherwise, to listen on all buses, type:
+
+# cat /sys/kernel/debug/usbmon/0u > /tmp/1.mon.out
+
 This process will be reading until killed. Naturally, the output can be
 redirected to a desirable location. This is preferred, because it is going
 to be quite long.
index c4eca56..60e649f 100644 (file)
@@ -677,6 +677,13 @@ P: Haavard Skinnemoen
 M:     hskinnemoen@atmel.com
 S:     Supported
 
+ATMEL USBA UDC DRIVER
+P:     Haavard Skinnemoen
+M:     hskinnemoen@atmel.com
+L:     kernel@avr32linux.org
+W:     http://avr32linux.org/twiki/bin/view/Main/AtmelUsbDeviceDriver
+S:     Supported
+
 ATMEL WIRELESS DRIVER
 P:     Simon Kelley
 M:     simon@thekelleys.org.uk
index 5e9d09e..6668c8e 100644 (file)
@@ -40,7 +40,7 @@
 #include <linux/pata_platform.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
-#include <linux/usb_sl811.h>
+#include <linux/usb/sl811.h>
 #include <asm/dma.h>
 #include <asm/bfin5xx_spi.h>
 #include <asm/reboot.h>
index 20507e9..f83a254 100644 (file)
@@ -40,7 +40,7 @@
 #include <linux/irq.h>
 #include <asm/dma.h>
 #include <asm/bfin5xx_spi.h>
-#include <linux/usb_sl811.h>
+#include <linux/usb/sl811.h>
 
 #include <linux/spi/ad7877.h>
 
index 47d7d4a..f42ba3a 100644 (file)
@@ -40,7 +40,7 @@
 #include <linux/pata_platform.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
-#include <linux/usb_sl811.h>
+#include <linux/usb/sl811.h>
 #include <asm/dma.h>
 #include <asm/bfin5xx_spi.h>
 #include <asm/reboot.h>
index ac49b15..516a640 100644 (file)
@@ -28,27 +28,7 @@ obj-$(CONFIG_USB_MICROTEK)   += image/
 
 obj-$(CONFIG_USB_SERIAL)       += serial/
 
-obj-$(CONFIG_USB_ADUTUX)       += misc/
-obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
-obj-$(CONFIG_USB_AUERSWALD)    += misc/
-obj-$(CONFIG_USB_BERRY_CHARGE) += misc/
-obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/
-obj-$(CONFIG_USB_CYTHERM)      += misc/
-obj-$(CONFIG_USB_EMI26)                += misc/
-obj-$(CONFIG_USB_EMI62)                += misc/
-obj-$(CONFIG_USB_FTDI_ELAN)    += misc/
-obj-$(CONFIG_USB_IDMOUSE)      += misc/
-obj-$(CONFIG_USB_LCD)          += misc/
-obj-$(CONFIG_USB_LD)           += misc/
-obj-$(CONFIG_USB_LED)          += misc/
-obj-$(CONFIG_USB_LEGOTOWER)    += misc/
-obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
-obj-$(CONFIG_USB_RIO500)       += misc/
-obj-$(CONFIG_USB_SISUSBVGA)    += misc/
-obj-$(CONFIG_USB_TEST)         += misc/
-obj-$(CONFIG_USB_TRANCEVIBRATOR)+= misc/
-obj-$(CONFIG_USB_USS720)       += misc/
-obj-$(CONFIG_USB_IOWARRIOR)    += misc/
+obj-$(CONFIG_USB)              += misc/
 
 obj-$(CONFIG_USB_ATM)          += atm/
 obj-$(CONFIG_USB_SPEEDTOUCH)   += atm/
index a73e714..a51eeed 100644 (file)
@@ -482,7 +482,9 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
        int rbuflen = ((rsize - 1) / stride + 1) * CMD_PACKET_SIZE;
 
        if (wbuflen > PAGE_SIZE || rbuflen > PAGE_SIZE) {
-               dbg("too big transfer requested");
+               if (printk_ratelimit())
+                       usb_err(instance->usbatm, "requested transfer size too large (%d, %d)\n",
+                               wbuflen, rbuflen);
                ret = -ENOMEM;
                goto fail;
        }
@@ -493,8 +495,9 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
        init_completion(&instance->rcv_done);
        ret = usb_submit_urb(instance->rcv_urb, GFP_KERNEL);
        if (ret < 0) {
-               dbg("submitting read urb for cm %#x failed", cm);
-               ret = ret;
+               if (printk_ratelimit())
+                       usb_err(instance->usbatm, "submit of read urb for cm %#x failed (%d)\n",
+                               cm, ret);
                goto fail;
        }
 
@@ -510,27 +513,29 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
        init_completion(&instance->snd_done);
        ret = usb_submit_urb(instance->snd_urb, GFP_KERNEL);
        if (ret < 0) {
-               dbg("submitting write urb for cm %#x failed", cm);
-               ret = ret;
+               if (printk_ratelimit())
+                       usb_err(instance->usbatm, "submit of write urb for cm %#x failed (%d)\n",
+                               cm, ret);
                goto fail;
        }
 
        ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL);
        if (ret < 0) {
-               dbg("sending cm %#x failed", cm);
-               ret = ret;
+               if (printk_ratelimit())
+                       usb_err(instance->usbatm, "send of cm %#x failed (%d)\n", cm, ret);
                goto fail;
        }
 
        ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen);
        if (ret < 0) {
-               dbg("receiving cm %#x failed", cm);
-               ret = ret;
+               if (printk_ratelimit())
+                       usb_err(instance->usbatm, "receive of cm %#x failed (%d)\n", cm, ret);
                goto fail;
        }
        if (actlen % CMD_PACKET_SIZE || !actlen) {
-               dbg("response is not a positive multiple of %d: %#x",
-                               CMD_PACKET_SIZE, actlen);
+               if (printk_ratelimit())
+                       usb_err(instance->usbatm, "invalid response length to cm %#x: %d\n",
+                               cm, actlen);
                ret = -EIO;
                goto fail;
        }
@@ -538,12 +543,16 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
        /* check the return status and copy the data to the output buffer, if needed */
        for (offb = offd = 0; offd < rsize && offb < actlen; offb += CMD_PACKET_SIZE) {
                if (rbuf[offb] != cm) {
-                       dbg("wrong cm %#x in response", rbuf[offb]);
+                       if (printk_ratelimit())
+                               usb_err(instance->usbatm, "wrong cm %#x in response to cm %#x\n",
+                                       rbuf[offb], cm);
                        ret = -EIO;
                        goto fail;
                }
                if (rbuf[offb + 1] != CM_STATUS_SUCCESS) {
-                       dbg("response failed: %#x", rbuf[offb + 1]);
+                       if (printk_ratelimit())
+                               usb_err(instance->usbatm, "response to cm %#x failed: %#x\n",
+                                       cm, rbuf[offb + 1]);
                        ret = -EIO;
                        goto fail;
                }
@@ -582,14 +591,18 @@ static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_requ
        for (offb = 0; offb < len; ) {
                int l = le32_to_cpu(buf[offb++]);
                if (l > stride || l > (len - offb) / 2) {
-                       dbg("wrong data length %#x in response", l);
+                       if (printk_ratelimit())
+                               usb_err(instance->usbatm, "invalid data length from cm %#x: %d\n",
+                                       cm, l);
                        ret = -EIO;
                        goto cleanup;
                }
                while (l--) {
                        offd = le32_to_cpu(buf[offb++]);
                        if (offd >= size) {
-                               dbg("wrong index %#x in response", offd);
+                               if (printk_ratelimit())
+                                       usb_err(instance->usbatm, "wrong index #%x in response to cm #%x\n",
+                                               offd, cm);
                                ret = -EIO;
                                goto cleanup;
                        }
index eb0615a..8b132c4 100644 (file)
@@ -251,7 +251,6 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
 {
        unsigned char *buffer;
        struct usbatm_data *usbatm = instance->usbatm;
-       struct usb_interface *intf;
        struct usb_device *usb_dev = usbatm->usb_dev;
        int actual_length;
        int ret = 0;
@@ -265,7 +264,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
                goto out;
        }
 
-       if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
+       if (!usb_ifnum_to_if(usb_dev, 2)) {
                ret = -ENODEV;
                usb_dbg(usbatm, "%s: interface not found!\n", __func__);
                goto out_free;
index 29807d0..389c5b1 100644 (file)
@@ -2,7 +2,8 @@
  * Copyright (c) 2003, 2004
  *     Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
  *
- * Copyright (c) 2005 Matthieu Castet <castet.matthieu@free.fr>
+ * Copyright (c) 2005-2007 Matthieu Castet <castet.matthieu@free.fr>
+ * Copyright (c) 2005-2007 Stanislaw Gruszka <stf_xl@wp.pl>
  *
  * This software is available to you under a choice of one of two
  * licenses. You may choose to be licensed under the terms of the GNU
 #define uea_info(usb_dev, format,args...) \
        dev_info(&(usb_dev)->dev ,"[ueagle-atm] " format, ##args)
 
-struct uea_cmvs {
+struct intr_pkt;
+
+/* cmv's from firmware */
+struct uea_cmvs_v1 {
        u32 address;
        u16 offset;
        u32 data;
 } __attribute__ ((packed));
 
+struct uea_cmvs_v2 {
+       u32 group;
+       u32 address;
+       u32 offset;
+       u32 data;
+} __attribute__ ((packed));
+
+/* information about currently processed cmv */
+struct cmv_dsc_e1 {
+       u8 function;
+       u16 idx;
+       u32 address;
+       u16 offset;
+};
+
+struct cmv_dsc_e4 {
+       u16 function;
+       u16 offset;
+       u16 address;
+       u16 group;
+};
+
+union cmv_dsc {
+       struct cmv_dsc_e1 e1;
+       struct cmv_dsc_e4 e4;
+};
+
 struct uea_softc {
        struct usb_device *usb_dev;
        struct usbatm_data *usbatm;
 
        int modem_index;
        unsigned int driver_info;
+       int annex;
+#define ANNEXA 0
+#define ANNEXB 1
 
        int booting;
        int reset;
@@ -127,20 +161,23 @@ struct uea_softc {
 
        struct task_struct *kthread;
        u32 data;
-       wait_queue_head_t cmv_ack_wait;
+       u32 data1;
+
        int cmv_ack;
+       union cmv_dsc cmv_dsc;
 
        struct work_struct task;
+       struct workqueue_struct *work_q;
        u16 pageno;
        u16 ovl;
 
        const struct firmware *dsp_firm;
        struct urb *urb_int;
 
-       u8 cmv_function;
-       u16 cmv_idx;
-       u32 cmv_address;
-       u16 cmv_offset;
+       void (*dispatch_cmv) (struct uea_softc *, struct intr_pkt *);
+       void (*schedule_load_page) (struct uea_softc *, struct intr_pkt *);
+       int (*stat) (struct uea_softc *);
+       int (*send_cmvs) (struct uea_softc *);
 
        /* keep in sync with eaglectl */
        struct uea_stats {
@@ -174,10 +211,34 @@ struct uea_softc {
 #define ELSA_PID_PSTFIRM       0x3350
 #define ELSA_PID_PREFIRM       0x3351
 
+#define ELSA_PID_A_PREFIRM     0x3352
+#define ELSA_PID_A_PSTFIRM     0x3353
+#define ELSA_PID_B_PREFIRM     0x3362
+#define ELSA_PID_B_PSTFIRM     0x3363
+
 /*
- * Sagem USB IDs
+ * Devolo IDs : pots if (pid & 0x10)
  */
-#define EAGLE_VID              0x1110
+#define DEVOLO_VID                     0x1039
+#define DEVOLO_EAGLE_I_A_PID_PSTFIRM   0x2110
+#define DEVOLO_EAGLE_I_A_PID_PREFIRM   0x2111
+
+#define DEVOLO_EAGLE_I_B_PID_PSTFIRM   0x2100
+#define DEVOLO_EAGLE_I_B_PID_PREFIRM   0x2101
+
+#define DEVOLO_EAGLE_II_A_PID_PSTFIRM  0x2130
+#define DEVOLO_EAGLE_II_A_PID_PREFIRM  0x2131
+
+#define DEVOLO_EAGLE_II_B_PID_PSTFIRM  0x2120
+#define DEVOLO_EAGLE_II_B_PID_PREFIRM  0x2121
+
+/*
+ * Reference design USB IDs
+ */
+#define ANALOG_VID             0x1110
+#define ADI930_PID_PREFIRM     0x9001
+#define ADI930_PID_PSTFIRM     0x9000
+
 #define EAGLE_I_PID_PREFIRM    0x9010  /* Eagle I */
 #define EAGLE_I_PID_PSTFIRM    0x900F  /* Eagle I */
 
@@ -187,12 +248,12 @@ struct uea_softc {
 #define EAGLE_II_PID_PREFIRM   0x9022  /* Eagle II */
 #define EAGLE_II_PID_PSTFIRM   0x9021  /* Eagle II */
 
-/*
- *  Eagle III Pid
- */
 #define EAGLE_III_PID_PREFIRM  0x9032  /* Eagle III */
 #define EAGLE_III_PID_PSTFIRM  0x9031  /* Eagle III */
 
+#define EAGLE_IV_PID_PREFIRM   0x9042  /* Eagle IV */
+#define EAGLE_IV_PID_PSTFIRM   0x9041  /* Eagle IV */
+
 /*
  * USR USB IDs
  */
@@ -208,11 +269,15 @@ struct uea_softc {
 
 #define PREFIRM 0
 #define PSTFIRM (1<<7)
+#define AUTO_ANNEX_A (1<<8)
+#define AUTO_ANNEX_B (1<<9)
+
 enum {
        ADI930 = 0,
        EAGLE_I,
        EAGLE_II,
-       EAGLE_III
+       EAGLE_III,
+       EAGLE_IV
 };
 
 /* macros for both struct usb_device_id and struct uea_softc */
@@ -221,15 +286,18 @@ enum {
 #define UEA_CHIP_VERSION(x) \
        ((x)->driver_info & 0xf)
 
-#define IS_ISDN(usb_dev) \
-       (le16_to_cpu((usb_dev)->descriptor.bcdDevice) & 0x80)
+#define IS_ISDN(x) \
+       ((x)->annex & ANNEXB)
 
 #define INS_TO_USBDEV(ins) ins->usb_dev
 
 #define GET_STATUS(data) \
        ((data >> 8) & 0xf)
+
 #define IS_OPERATIONAL(sc) \
-       (GET_STATUS(sc->stats.phy.state) == 2)
+       ((UEA_CHIP_VERSION(sc) != EAGLE_IV) ? \
+       (GET_STATUS(sc->stats.phy.state) == 2) : \
+       (sc->stats.phy.state == 7))
 
 /*
  * Set of macros to handle unaligned data in the firmware blob.
@@ -259,7 +327,8 @@ enum {
 #define UEA_INTR_PIPE          0x04
 #define UEA_ISO_DATA_PIPE      0x08
 
-#define UEA_SET_BLOCK          0x0001
+#define UEA_E1_SET_BLOCK       0x0001
+#define UEA_E4_SET_BLOCK       0x002c
 #define UEA_SET_MODE           0x0003
 #define UEA_SET_2183_DATA      0x0004
 #define UEA_SET_TIMEOUT                0x0011
@@ -275,71 +344,179 @@ enum {
 #define UEA_MPTX_MAILBOX       (0x3fd6 | 0x4000)
 #define UEA_MPRX_MAILBOX       (0x3fdf | 0x4000)
 
-/* structure describing a block within a DSP page */
-struct block_info {
+/* block information in eagle4 dsp firmware  */
+struct block_index {
+       __le32 PageOffset;
+       __le32 NotLastBlock;
+       __le32 dummy;
+       __le32 PageSize;
+       __le32 PageAddress;
+       __le16 dummy1;
+       __le16 PageNumber;
+} __attribute__ ((packed));
+
+#define E4_IS_BOOT_PAGE(PageSize) ((le32_to_cpu(PageSize)) & 0x80000000)
+#define E4_PAGE_BYTES(PageSize) ((le32_to_cpu(PageSize) & 0x7fffffff) * 4)
+
+#define E4_L1_STRING_HEADER 0x10
+#define E4_MAX_PAGE_NUMBER 0x58
+#define E4_NO_SWAPPAGE_HEADERS 0x31
+
+/* l1_code is eagle4 dsp firmware format */
+struct l1_code {
+       u8 string_header[E4_L1_STRING_HEADER];
+       u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER];
+       struct block_index page_header[E4_NO_SWAPPAGE_HEADERS];
+       u8 code [0];
+} __attribute__ ((packed));
+
+/* structures describing a block within a DSP page */
+struct block_info_e1 {
        __le16 wHdr;
-#define UEA_BIHDR 0xabcd
        __le16 wAddress;
        __le16 wSize;
        __le16 wOvlOffset;
        __le16 wOvl;            /* overlay */
        __le16 wLast;
 } __attribute__ ((packed));
-#define BLOCK_INFO_SIZE 12
+#define E1_BLOCK_INFO_SIZE 12
+
+struct block_info_e4 {
+       __be16 wHdr;
+       __u8 bBootPage;
+       __u8 bPageNumber;
+       __be32 dwSize;
+       __be32 dwAddress;
+       __be16 wReserved;
+} __attribute__ ((packed));
+#define E4_BLOCK_INFO_SIZE 14
 
-/* structure representing a CMV (Configuration and Management Variable) */
-struct cmv {
-       __le16 wPreamble;
-#define PREAMBLE 0x535c
-       __u8 bDirection;
-#define MODEMTOHOST 0x01
-#define HOSTTOMODEM 0x10
-       __u8 bFunction;
-#define FUNCTION_TYPE(f)    ((f) >> 4)
-#define MEMACCESS      0x1
-#define ADSLDIRECTIVE  0x7
+#define UEA_BIHDR 0xabcd
+#define UEA_RESERVED 0xffff
+
+/* constants describing cmv type */
+#define E1_PREAMBLE 0x535c
+#define E1_MODEMTOHOST 0x01
+#define E1_HOSTTOMODEM 0x10
+
+#define E1_MEMACCESS 0x1
+#define E1_ADSLDIRECTIVE 0x7
+#define E1_FUNCTION_TYPE(f) ((f) >> 4)
+#define E1_FUNCTION_SUBTYPE(f) ((f) & 0x0f)
+
+#define E4_MEMACCESS 0
+#define E4_ADSLDIRECTIVE 0xf
+#define E4_FUNCTION_TYPE(f) ((f) >> 8)
+#define E4_FUNCTION_SIZE(f) ((f) & 0x0f)
+#define E4_FUNCTION_SUBTYPE(f) (((f) >> 4) & 0x0f)
 
-#define FUNCTION_SUBTYPE(f) ((f) & 0x0f)
 /* for MEMACCESS */
-#define REQUESTREAD    0x0
-#define REQUESTWRITE   0x1
-#define REPLYREAD      0x2
-#define REPLYWRITE     0x3
+#define E1_REQUESTREAD 0x0
+#define E1_REQUESTWRITE        0x1
+#define E1_REPLYREAD   0x2
+#define E1_REPLYWRITE  0x3
+
+#define E4_REQUESTREAD 0x0
+#define E4_REQUESTWRITE        0x4
+#define E4_REPLYREAD   (E4_REQUESTREAD | 1)
+#define E4_REPLYWRITE  (E4_REQUESTWRITE | 1)
+
 /* for ADSLDIRECTIVE */
-#define KERNELREADY    0x0
-#define MODEMREADY     0x1
+#define E1_KERNELREADY 0x0
+#define E1_MODEMREADY  0x1
 
-#define MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
-       __le16 wIndex;
-       __le32 dwSymbolicAddress;
-#define MAKESA(a, b, c, d)                                             \
+#define E4_KERNELREADY 0x0
+#define E4_MODEMREADY  0x1
+
+#define E1_MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
+#define E4_MAKEFUNCTION(t, st, s) (((t) & 0xf) << 8 | ((st) & 0xf) << 4 | ((s) & 0xf))
+
+#define E1_MAKESA(a, b, c, d)                                          \
        (((c) & 0xff) << 24 |                                           \
         ((d) & 0xff) << 16 |                                           \
         ((a) & 0xff) << 8  |                                           \
         ((b) & 0xff))
-#define GETSA1(a) ((a >> 8) & 0xff)
-#define GETSA2(a) (a & 0xff)
-#define GETSA3(a) ((a >> 24) & 0xff)
-#define GETSA4(a) ((a >> 16) & 0xff)
-
-#define SA_CNTL MAKESA('C', 'N', 'T', 'L')
-#define SA_DIAG MAKESA('D', 'I', 'A', 'G')
-#define SA_INFO MAKESA('I', 'N', 'F', 'O')
-#define SA_OPTN MAKESA('O', 'P', 'T', 'N')
-#define SA_RATE MAKESA('R', 'A', 'T', 'E')
-#define SA_STAT MAKESA('S', 'T', 'A', 'T')
+
+#define E1_GETSA1(a) ((a >> 8) & 0xff)
+#define E1_GETSA2(a) (a & 0xff)
+#define E1_GETSA3(a) ((a >> 24) & 0xff)
+#define E1_GETSA4(a) ((a >> 16) & 0xff)
+
+#define E1_SA_CNTL E1_MAKESA('C', 'N', 'T', 'L')
+#define E1_SA_DIAG E1_MAKESA('D', 'I', 'A', 'G')
+#define E1_SA_INFO E1_MAKESA('I', 'N', 'F', 'O')
+#define E1_SA_OPTN E1_MAKESA('O', 'P', 'T', 'N')
+#define E1_SA_RATE E1_MAKESA('R', 'A', 'T', 'E')
+#define E1_SA_STAT E1_MAKESA('S', 'T', 'A', 'T')
+
+#define E4_SA_CNTL 1
+#define E4_SA_STAT 2
+#define E4_SA_INFO 3
+#define E4_SA_TEST 4
+#define E4_SA_OPTN 5
+#define E4_SA_RATE 6
+#define E4_SA_DIAG 7
+#define E4_SA_CNFG 8
+
+/* structures representing a CMV (Configuration and Management Variable) */
+struct cmv_e1 {
+       __le16 wPreamble;
+       __u8 bDirection;
+       __u8 bFunction;
+       __le16 wIndex;
+       __le32 dwSymbolicAddress;
        __le16 wOffsetAddress;
        __le32 dwData;
 } __attribute__ ((packed));
-#define CMV_SIZE 16
 
-/* structure representing swap information */
-struct swap_info {
+struct cmv_e4 {
+       __be16 wGroup;
+       __be16 wFunction;
+       __be16 wOffset;
+       __be16 wAddress;
+       __be32 dwData [6];
+} __attribute__ ((packed));
+
+/* structures representing swap information */
+struct swap_info_e1 {
        __u8 bSwapPageNo;
        __u8 bOvl;              /* overlay */
 } __attribute__ ((packed));
 
-/* structure representing interrupt data */
+struct swap_info_e4 {
+       __u8 bSwapPageNo;
+} __attribute__ ((packed));
+
+/* structures representing interrupt data */
+#define e1_bSwapPageNo u.e1.s1.swapinfo.bSwapPageNo
+#define e1_bOvl                u.e1.s1.swapinfo.bOvl
+#define e4_bSwapPageNo  u.e4.s1.swapinfo.bSwapPageNo
+
+#define INT_LOADSWAPPAGE 0x0001
+#define INT_INCOMINGCMV  0x0002
+
+union intr_data_e1 {
+       struct {
+               struct swap_info_e1 swapinfo;
+               __le16 wDataSize;
+       } __attribute__ ((packed)) s1;
+       struct {
+               struct cmv_e1 cmv;
+               __le16 wDataSize;
+       } __attribute__ ((packed)) s2;
+} __attribute__ ((packed));
+
+union intr_data_e4 {
+       struct {
+               struct swap_info_e4 swapinfo;
+               __le16 wDataSize;
+       } __attribute__ ((packed)) s1;
+       struct {
+               struct cmv_e4 cmv;
+               __le16 wDataSize;
+       } __attribute__ ((packed)) s2;
+} __attribute__ ((packed));
+
 struct intr_pkt {
        __u8 bType;
        __u8 bNotification;
@@ -347,43 +524,48 @@ struct intr_pkt {
        __le16 wIndex;
        __le16 wLength;
        __le16 wInterrupt;
-#define INT_LOADSWAPPAGE 0x0001
-#define INT_INCOMINGCMV  0x0002
        union {
-               struct {
-                       struct swap_info swapinfo;
-                       __le16 wDataSize;
-               } __attribute__ ((packed)) s1;
-
-               struct {
-                       struct cmv cmv;
-                       __le16 wDataSize;
-               } __attribute__ ((packed)) s2;
-       } __attribute__ ((packed)) u;
-#define bSwapPageNo    u.s1.swapinfo.bSwapPageNo
-#define bOvl           u.s1.swapinfo.bOvl
+               union intr_data_e1 e1;
+               union intr_data_e4 e4;
+       } u;
 } __attribute__ ((packed));
-#define INTR_PKT_SIZE 28
+
+#define E1_INTR_PKT_SIZE 28
+#define E4_INTR_PKT_SIZE 64
 
 static struct usb_driver uea_driver;
 static DEFINE_MUTEX(uea_mutex);
-static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"};
+static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III", "Eagle IV"};
 
 static int modem_index;
 static unsigned int debug;
-static int use_iso[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = 1};
+static unsigned int altsetting[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = FASTEST_ISO_INTF};
 static int sync_wait[NB_MODEM];
 static char *cmv_file[NB_MODEM];
+static int annex[NB_MODEM];
 
 module_param(debug, uint, 0644);
 MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)");
-module_param_array(use_iso, bool, NULL, 0644);
-MODULE_PARM_DESC(use_iso, "use isochronous usb pipe for incoming traffic");
+module_param_array(altsetting, uint, NULL, 0644);
+MODULE_PARM_DESC(altsetting, "alternate setting for incoming traffic: 0=bulk, "
+                            "1=isoc slowest, ... , 8=isoc fastest (default)");
 module_param_array(sync_wait, bool, NULL, 0644);
 MODULE_PARM_DESC(sync_wait, "wait the synchronisation before starting ATM");
 module_param_array(cmv_file, charp, NULL, 0644);
 MODULE_PARM_DESC(cmv_file,
                "file name with configuration and management variables");
+module_param_array(annex, uint, NULL, 0644);
+MODULE_PARM_DESC(annex,
+                 "manually set annex a/b (0=auto, 1=annex a, 2=annex b)");
+
+#define uea_wait(sc, cond, timeo) \
+({ \
+       int _r = wait_event_interruptible_timeout(sc->sync_q, \
+                       (cond) || kthread_should_stop(), timeo); \
+       if (kthread_should_stop()) \
+               _r = -ENODEV; \
+       _r; \
+})
 
 #define UPDATE_ATM_STAT(type, val) \
        do { \
@@ -519,6 +701,9 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
        case EAGLE_III:
                fw_name = FW_DIR "eagleIII.fw";
                break;
+       case EAGLE_IV:
+               fw_name = FW_DIR "eagleIV.fw";
+               break;
        }
 
        ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, usb, uea_upload_pre_firmware);
@@ -537,7 +722,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
 /*
  * Make sure that the DSP code provided is safe to use.
  */
-static int check_dsp(u8 *dsp, unsigned int len)
+static int check_dsp_e1(u8 *dsp, unsigned int len)
 {
        u8 pagecount, blockcount;
        u16 blocksize;
@@ -588,6 +773,51 @@ static int check_dsp(u8 *dsp, unsigned int len)
        return 0;
 }
 
+static int check_dsp_e4(u8 *dsp, int len)
+{
+       int i;
+       struct l1_code *p = (struct l1_code *) dsp;
+       unsigned int sum = p->code - dsp;
+
+       if (len < sum)
+               return 1;
+
+       if (strcmp("STRATIPHY ANEXA", p->string_header) != 0 &&
+           strcmp("STRATIPHY ANEXB", p->string_header) != 0)
+               return 1;
+
+       for (i = 0; i < E4_MAX_PAGE_NUMBER; i++) {
+               struct block_index *blockidx;
+               u8 blockno = p->page_number_to_block_index[i];
+               if (blockno >= E4_NO_SWAPPAGE_HEADERS)
+                       continue;
+
+               do {
+                       u64 l;
+
+                       if (blockno >= E4_NO_SWAPPAGE_HEADERS)
+                               return 1;
+
+                       blockidx = &p->page_header[blockno++];
+                       if ((u8 *)(blockidx + 1) - dsp  >= len)
+                               return 1;
+
+                       if (le16_to_cpu(blockidx->PageNumber) != i)
+                               return 1;
+
+                       l = E4_PAGE_BYTES(blockidx->PageSize);
+                       sum += l;
+                       l += le32_to_cpu(blockidx->PageOffset);
+                       if (l > len)
+                               return 1;
+
+               /* zero is zero regardless endianes */
+               } while (blockidx->NotLastBlock);
+       }
+
+       return (sum == len) ? 0 : 1;
+}
+
 /*
  * send data to the idma pipe
  * */
@@ -624,13 +854,18 @@ static int request_dsp(struct uea_softc *sc)
        int ret;
        char *dsp_name;
 
-       if (UEA_CHIP_VERSION(sc) == ADI930) {
-               if (IS_ISDN(sc->usb_dev))
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+               if (IS_ISDN(sc))
+                       dsp_name = FW_DIR "DSP4i.bin";
+               else
+                       dsp_name = FW_DIR "DSP4p.bin";
+       } else if (UEA_CHIP_VERSION(sc) == ADI930) {
+               if (IS_ISDN(sc))
                        dsp_name = FW_DIR "DSP9i.bin";
                else
                        dsp_name = FW_DIR "DSP9p.bin";
        } else {
-               if (IS_ISDN(sc->usb_dev))
+               if (IS_ISDN(sc))
                        dsp_name = FW_DIR "DSPei.bin";
                else
                        dsp_name = FW_DIR "DSPep.bin";
@@ -640,11 +875,16 @@ static int request_dsp(struct uea_softc *sc)
        if (ret < 0) {
                uea_err(INS_TO_USBDEV(sc),
                       "requesting firmware %s failed with error %d\n",
-                      dsp_name, ret);
+                       dsp_name, ret);
                return ret;
        }
 
-       if (check_dsp(sc->dsp_firm->data, sc->dsp_firm->size)) {
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
+               ret = check_dsp_e4(sc->dsp_firm->data, sc->dsp_firm->size);
+       else
+               ret = check_dsp_e1(sc->dsp_firm->data, sc->dsp_firm->size);
+
+       if (ret) {
                uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
                       dsp_name);
                release_firmware(sc->dsp_firm);
@@ -658,12 +898,12 @@ static int request_dsp(struct uea_softc *sc)
 /*
  * The uea_load_page() function must be called within a process context
  */
-static void uea_load_page(struct work_struct *work)
+static void uea_load_page_e1(struct work_struct *work)
 {
        struct uea_softc *sc = container_of(work, struct uea_softc, task);
        u16 pageno = sc->pageno;
        u16 ovl = sc->ovl;
-       struct block_info bi;
+       struct block_info_e1 bi;
 
        u8 *p;
        u8 pagecount, blockcount;
@@ -716,7 +956,7 @@ static void uea_load_page(struct work_struct *work)
                bi.wLast = cpu_to_le16((i == blockcount - 1) ? 1 : 0);
 
                /* send block info through the IDMA pipe */
-               if (uea_idma_write(sc, &bi, BLOCK_INFO_SIZE))
+               if (uea_idma_write(sc, &bi, E1_BLOCK_INFO_SIZE))
                        goto bad2;
 
                /* send block data through the IDMA pipe */
@@ -735,17 +975,114 @@ bad1:
        uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n", pageno);
 }
 
+static void __uea_load_page_e4(struct uea_softc *sc, u8 pageno, int boot)
+{
+       struct block_info_e4 bi;
+       struct block_index *blockidx;
+       struct l1_code *p = (struct l1_code *) sc->dsp_firm->data;
+       u8 blockno = p->page_number_to_block_index[pageno];
+
+       bi.wHdr = cpu_to_be16(UEA_BIHDR);
+       bi.bBootPage = boot;
+       bi.bPageNumber = pageno;
+       bi.wReserved = cpu_to_be16(UEA_RESERVED);
+
+       do {
+               u8 *blockoffset;
+               unsigned int blocksize;
+
+               blockidx = &p->page_header[blockno];
+               blocksize = E4_PAGE_BYTES(blockidx->PageSize);
+               blockoffset = sc->dsp_firm->data + le32_to_cpu(blockidx->PageOffset);
+
+               bi.dwSize = cpu_to_be32(blocksize);
+               bi.dwAddress = swab32(blockidx->PageAddress);
+
+               uea_dbg(INS_TO_USBDEV(sc),
+                      "sending block %u for DSP page %u size %u adress %x\n",
+                      blockno, pageno, blocksize, le32_to_cpu(blockidx->PageAddress));
+
+               /* send block info through the IDMA pipe */
+               if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE))
+                       goto bad;
+
+               /* send block data through the IDMA pipe */
+               if (uea_idma_write(sc, blockoffset, blocksize))
+                       goto bad;
+
+               blockno++;
+       } while (blockidx->NotLastBlock);
+
+       return;
+
+bad:
+       uea_err(INS_TO_USBDEV(sc), "sending DSP block %u failed\n", blockno);
+       return;
+}
+
+static void uea_load_page_e4(struct work_struct *work)
+{
+       struct uea_softc *sc = container_of(work, struct uea_softc, task);
+       u8 pageno = sc->pageno;
+       int i;
+       struct block_info_e4 bi;
+       struct l1_code *p;
+
+       uea_dbg(INS_TO_USBDEV(sc), "sending DSP page %u\n", pageno);
+
+       /* reload firmware when reboot start and it's loaded already */
+       if (pageno == 0 && sc->dsp_firm) {
+               release_firmware(sc->dsp_firm);
+               sc->dsp_firm = NULL;
+       }
+
+       if (sc->dsp_firm == NULL && request_dsp(sc) < 0)
+               return;
+
+       p = (struct l1_code *) sc->dsp_firm->data;
+       if (pageno >= p->page_header[0].PageNumber) {
+               uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n", pageno);
+               return;
+       }
+
+       if (pageno != 0) {
+               __uea_load_page_e4(sc, pageno, 0);
+               return;
+       }
+
+       uea_dbg(INS_TO_USBDEV(sc),
+              "sending Main DSP page %u\n", p->page_header[0].PageNumber);
+
+       for (i = 0; i < le16_to_cpu(p->page_header[0].PageNumber); i++) {
+               if (E4_IS_BOOT_PAGE(p->page_header[i].PageSize))
+                       __uea_load_page_e4(sc, i, 1);
+       }
+
+       uea_dbg(INS_TO_USBDEV(sc),"sending start bi\n");
+
+       bi.wHdr = cpu_to_be16(UEA_BIHDR);
+       bi.bBootPage = 0;
+       bi.bPageNumber = 0xff;
+       bi.wReserved = cpu_to_be16(UEA_RESERVED);
+       bi.dwSize = cpu_to_be32(E4_PAGE_BYTES(p->page_header[0].PageSize));
+       bi.dwAddress = swab32(p->page_header[0].PageAddress);
+
+       /* send block info through the IDMA pipe */
+       if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE))
+               uea_err(INS_TO_USBDEV(sc), "sending DSP start bi failed\n");
+}
+
 static inline void wake_up_cmv_ack(struct uea_softc *sc)
 {
        BUG_ON(sc->cmv_ack);
        sc->cmv_ack = 1;
-       wake_up(&sc->cmv_ack_wait);
+       wake_up(&sc->sync_q);
 }
 
 static inline int wait_cmv_ack(struct uea_softc *sc)
 {
-       int ret = wait_event_interruptible_timeout(sc->cmv_ack_wait,
-                                                  sc->cmv_ack, ACK_TIMEOUT);
+       int ret = uea_wait(sc, sc->cmv_ack , ACK_TIMEOUT);
+
        sc->cmv_ack = 0;
 
        uea_dbg(INS_TO_USBDEV(sc), "wait_event_timeout : %d ms\n",
@@ -792,33 +1129,68 @@ static int uea_request(struct uea_softc *sc,
        return 0;
 }
 
-static int uea_cmv(struct uea_softc *sc,
+static int uea_cmv_e1(struct uea_softc *sc,
                u8 function, u32 address, u16 offset, u32 data)
 {
-       struct cmv cmv;
+       struct cmv_e1 cmv;
        int ret;
 
        uea_enters(INS_TO_USBDEV(sc));
        uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, "
                        "offset : 0x%04x, data : 0x%08x\n",
-                       FUNCTION_TYPE(function), FUNCTION_SUBTYPE(function),
-                       GETSA1(address), GETSA2(address), GETSA3(address),
-                       GETSA4(address), offset, data);
+                       E1_FUNCTION_TYPE(function), E1_FUNCTION_SUBTYPE(function),
+                       E1_GETSA1(address), E1_GETSA2(address), E1_GETSA3(address),
+                       E1_GETSA4(address), offset, data);
+
        /* we send a request, but we expect a reply */
-       sc->cmv_function = function | 0x2;
-       sc->cmv_idx++;
-       sc->cmv_address = address;
-       sc->cmv_offset = offset;
+       sc->cmv_dsc.e1.function = function | 0x2;
+       sc->cmv_dsc.e1.idx++;
+       sc->cmv_dsc.e1.address = address;
+       sc->cmv_dsc.e1.offset = offset;
 
-       cmv.wPreamble = cpu_to_le16(PREAMBLE);
-       cmv.bDirection = HOSTTOMODEM;
+       cmv.wPreamble = cpu_to_le16(E1_PREAMBLE);
+       cmv.bDirection = E1_HOSTTOMODEM;
        cmv.bFunction = function;
-       cmv.wIndex = cpu_to_le16(sc->cmv_idx);
+       cmv.wIndex = cpu_to_le16(sc->cmv_dsc.e1.idx);
        put_unaligned(cpu_to_le32(address), &cmv.dwSymbolicAddress);
        cmv.wOffsetAddress = cpu_to_le16(offset);
        put_unaligned(cpu_to_le32(data >> 16 | data << 16), &cmv.dwData);
 
-       ret = uea_request(sc, UEA_SET_BLOCK, UEA_MPTX_START, CMV_SIZE, &cmv);
+       ret = uea_request(sc, UEA_E1_SET_BLOCK, UEA_MPTX_START, sizeof(cmv), &cmv);
+       if (ret < 0)
+               return ret;
+       ret = wait_cmv_ack(sc);
+       uea_leaves(INS_TO_USBDEV(sc));
+       return ret;
+}
+
+static int uea_cmv_e4(struct uea_softc *sc,
+               u16 function, u16 group, u16 address, u16 offset, u32 data)
+{
+       struct cmv_e4 cmv;
+       int ret;
+
+       uea_enters(INS_TO_USBDEV(sc));
+       memset(&cmv, 0, sizeof(cmv));
+
+       uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, "
+                "Address : 0x%04x, offset : 0x%04x, data : 0x%08x\n",
+                E4_FUNCTION_TYPE(function), E4_FUNCTION_SUBTYPE(function),
+                group, address, offset, data);
+
+       /* we send a request, but we expect a reply */
+       sc->cmv_dsc.e4.function = function | (0x1 << 4);
+       sc->cmv_dsc.e4.offset = offset;
+       sc->cmv_dsc.e4.address = address;
+       sc->cmv_dsc.e4.group = group;
+
+       cmv.wFunction = cpu_to_be16(function);
+       cmv.wGroup = cpu_to_be16(group);
+       cmv.wAddress = cpu_to_be16(address);
+       cmv.wOffset = cpu_to_be16(offset);
+       cmv.dwData[0] = cpu_to_be32(data);
+
+       ret = uea_request(sc, UEA_E4_SET_BLOCK, UEA_MPTX_START, sizeof(cmv), &cmv);
        if (ret < 0)
                return ret;
        ret = wait_cmv_ack(sc);
@@ -826,10 +1198,10 @@ static int uea_cmv(struct uea_softc *sc,
        return ret;
 }
 
-static inline int uea_read_cmv(struct uea_softc *sc,
+static inline int uea_read_cmv_e1(struct uea_softc *sc,
                u32 address, u16 offset, u32 *data)
 {
-       int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTREAD),
+       int ret = uea_cmv_e1(sc, E1_MAKEFUNCTION(E1_MEMACCESS, E1_REQUESTREAD),
                          address, offset, 0);
        if (ret < 0)
                uea_err(INS_TO_USBDEV(sc),
@@ -840,10 +1212,27 @@ static inline int uea_read_cmv(struct uea_softc *sc,
        return ret;
 }
 
-static inline int uea_write_cmv(struct uea_softc *sc,
+static inline int uea_read_cmv_e4(struct uea_softc *sc,
+               u8 size, u16 group, u16 address, u16 offset, u32 *data)
+{
+       int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, E4_REQUESTREAD, size),
+                         group, address, offset, 0);
+       if (ret < 0)
+               uea_err(INS_TO_USBDEV(sc),
+                       "reading cmv failed with error %d\n", ret);
+       else {
+               *data = sc->data;
+               /* size is in 16-bit word quantities */
+               if (size > 2)
+                       *(data + 1) = sc->data1;
+       }
+       return ret;
+}
+
+static inline int uea_write_cmv_e1(struct uea_softc *sc,
                u32 address, u16 offset, u32 data)
 {
-       int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTWRITE),
+       int ret = uea_cmv_e1(sc, E1_MAKEFUNCTION(E1_MEMACCESS, E1_REQUESTWRITE),
                          address, offset, data);
        if (ret < 0)
                uea_err(INS_TO_USBDEV(sc),
@@ -852,12 +1241,48 @@ static inline int uea_write_cmv(struct uea_softc *sc,
        return ret;
 }
 
+static inline int uea_write_cmv_e4(struct uea_softc *sc,
+               u8 size, u16 group, u16 address, u16 offset, u32 data)
+{
+       int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, E4_REQUESTWRITE, size),
+                         group, address, offset, data);
+       if (ret < 0)
+               uea_err(INS_TO_USBDEV(sc),
+                       "writing cmv failed with error %d\n", ret);
+
+       return ret;
+}
+
+static void uea_set_bulk_timeout(struct uea_softc *sc, u32 dsrate)
+{
+       int ret;
+       u16 timeout;
+
+       /* in bulk mode the modem have problem with high rate
+        * changing internal timing could improve things, but the
+        * value is misterious.
+        * ADI930 don't support it (-EPIPE error).
+        */
+
+       if (UEA_CHIP_VERSION(sc) == ADI930 ||
+           altsetting[sc->modem_index] > 0 ||
+           sc->stats.phy.dsrate == dsrate)
+               return;
+
+       /* Original timming (1Mbit/s) from ADI (used in windows driver) */
+       timeout = (dsrate <= 1024*1024) ? 0 : 1;
+       ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL);
+       uea_info(INS_TO_USBDEV(sc), "setting new timeout %d%s\n",
+                timeout,  ret < 0 ? " failed" : "");
+
+}
+
 /*
  * Monitor the modem and update the stat
  * return 0 if everything is ok
  * return < 0 if an error occurs (-EAGAIN reboot needed)
  */
-static int uea_stat(struct uea_softc *sc)
+static int uea_stat_e1(struct uea_softc *sc)
 {
        u32 data;
        int ret;
@@ -865,7 +1290,7 @@ static int uea_stat(struct uea_softc *sc)
        uea_enters(INS_TO_USBDEV(sc));
        data = sc->stats.phy.state;
 
-       ret = uea_read_cmv(sc, SA_STAT, 0, &sc->stats.phy.state);
+       ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state);
        if (ret < 0)
                return ret;
 
@@ -885,7 +1310,7 @@ static int uea_stat(struct uea_softc *sc)
 
        case 3:         /* fail ... */
                uea_info(INS_TO_USBDEV(sc), "modem synchronization failed"
-                               " (may be try other cmv/dsp)\n");
+                                       " (may be try other cmv/dsp)\n");
                return -EAGAIN;
 
        case 4 ... 6:   /* test state */
@@ -923,7 +1348,7 @@ static int uea_stat(struct uea_softc *sc)
        /* wake up processes waiting for synchronization */
        wake_up(&sc->sync_q);
 
-       ret = uea_read_cmv(sc, SA_DIAG, 2, &sc->stats.phy.flags);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 2, &sc->stats.phy.flags);
        if (ret < 0)
                return ret;
        sc->stats.phy.mflags |= sc->stats.phy.flags;
@@ -937,105 +1362,223 @@ static int uea_stat(struct uea_softc *sc)
                return 0;
        }
 
-       ret = uea_read_cmv(sc, SA_RATE, 0, &data);
+       ret = uea_read_cmv_e1(sc, E1_SA_RATE, 0, &data);
        if (ret < 0)
                return ret;
 
-       /* in bulk mode the modem have problem with high rate
-        * changing internal timing could improve things, but the
-        * value is misterious.
-        * ADI930 don't support it (-EPIPE error).
-        */
-       if (UEA_CHIP_VERSION(sc) != ADI930
-                   && !use_iso[sc->modem_index]
-                   && sc->stats.phy.dsrate != (data >> 16) * 32) {
-               /* Original timming from ADI(used in windows driver)
-                * 0x20ffff>>16 * 32 = 32 * 32 = 1Mbits
-                */
-               u16 timeout = (data <= 0x20ffff) ? 0 : 1;
-               ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL);
-               uea_info(INS_TO_USBDEV(sc),
-                               "setting new timeout %d%s\n", timeout,
-                               ret < 0?" failed":"");
-       }
+       uea_set_bulk_timeout(sc, (data >> 16) * 32);
        sc->stats.phy.dsrate = (data >> 16) * 32;
        sc->stats.phy.usrate = (data & 0xffff) * 32;
        UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424);
 
-       ret = uea_read_cmv(sc, SA_DIAG, 23, &data);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 23, &data);
        if (ret < 0)
                return ret;
        sc->stats.phy.dsattenuation = (data & 0xff) / 2;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 47, &data);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 47, &data);
        if (ret < 0)
                return ret;
        sc->stats.phy.usattenuation = (data & 0xff) / 2;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 25, &sc->stats.phy.dsmargin);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 25, &sc->stats.phy.dsmargin);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 49, &sc->stats.phy.usmargin);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 49, &sc->stats.phy.usmargin);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 51, &sc->stats.phy.rxflow);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 51, &sc->stats.phy.rxflow);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 52, &sc->stats.phy.txflow);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 52, &sc->stats.phy.txflow);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 54, &sc->stats.phy.dsunc);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 54, &sc->stats.phy.dsunc);
        if (ret < 0)
                return ret;
 
        /* only for atu-c */
-       ret = uea_read_cmv(sc, SA_DIAG, 58, &sc->stats.phy.usunc);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 58, &sc->stats.phy.usunc);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 53, &sc->stats.phy.dscorr);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 53, &sc->stats.phy.dscorr);
        if (ret < 0)
                return ret;
 
        /* only for atu-c */
-       ret = uea_read_cmv(sc, SA_DIAG, 57, &sc->stats.phy.uscorr);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 57, &sc->stats.phy.uscorr);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_INFO, 8, &sc->stats.phy.vidco);
+       ret = uea_read_cmv_e1(sc, E1_SA_INFO, 8, &sc->stats.phy.vidco);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_INFO, 13, &sc->stats.phy.vidcpe);
+       ret = uea_read_cmv_e1(sc, E1_SA_INFO, 13, &sc->stats.phy.vidcpe);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-static int request_cmvs(struct uea_softc *sc,
-                struct uea_cmvs **cmvs, const struct firmware **fw)
+static int uea_stat_e4(struct uea_softc *sc)
 {
-       int ret, size;
-       u8 *data;
+       u32 data;
+       u32 tmp_arr[2];
+       int ret;
+
+       uea_enters(INS_TO_USBDEV(sc));
+       data = sc->stats.phy.state;
+
+       /* XXX only need to be done before operationnal... */
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_STAT, 0, 0, &sc->stats.phy.state);
+       if (ret < 0)
+               return ret;
+
+       switch (sc->stats.phy.state) {
+               case 0x0:       /* not yet synchronized */
+               case 0x1:
+               case 0x3:
+               case 0x4:
+                       uea_dbg(INS_TO_USBDEV(sc), "modem not yet synchronized\n");
+                       return 0;
+               case 0x5:       /* initialization */
+               case 0x6:
+               case 0x9:
+               case 0xa:
+                       uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n");
+                       return 0;
+               case 0x2:       /* fail ... */
+                       uea_info(INS_TO_USBDEV(sc), "modem synchronization failed"
+                                       " (may be try other cmv/dsp)\n");
+                       return -EAGAIN;
+               case 0x7:       /* operational */
+                       break;
+               default:
+                       uea_warn(INS_TO_USBDEV(sc), "unknown state: %x\n", sc->stats.phy.state);
+                       return 0;
+       }
+
+       if (data != 7) {
+               uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_OFF, 0, NULL);
+               uea_info(INS_TO_USBDEV(sc), "modem operational\n");
+
+               /* release the dsp firmware as it is not needed until
+                * the next failure
+                */
+               if (sc->dsp_firm) {
+                       release_firmware(sc->dsp_firm);
+                       sc->dsp_firm = NULL;
+               }
+       }
+
+       /* always update it as atm layer could not be init when we switch to
+        * operational state
+        */
+       UPDATE_ATM_STAT(signal, ATM_PHY_SIG_FOUND);
+
+       /* wake up processes waiting for synchronization */
+       wake_up(&sc->sync_q);
+
+       /* TODO improve this state machine :
+        * we need some CMV info : what they do and their unit
+        * we should find the equivalent of eagle3- CMV
+        */
+       /* check flags */
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_DIAG, 0, 0, &sc->stats.phy.flags);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.mflags |= sc->stats.phy.flags;
+
+       /* in case of a flags ( for example delineation LOSS (& 0x10)),
+        * we check the status again in order to detect the failure earlier
+        */
+       if (sc->stats.phy.flags) {
+               uea_dbg(INS_TO_USBDEV(sc), "Stat flag = 0x%x\n",
+                      sc->stats.phy.flags);
+               if (sc->stats.phy.flags & 1) //delineation LOSS
+                       return -EAGAIN;
+               if (sc->stats.phy.flags & 0x4000) //Reset Flag
+                       return -EAGAIN;
+               return 0;
+       }
+
+       /* rate data may be in upper or lower half of 64 bit word, strange */
+       ret = uea_read_cmv_e4(sc, 4, E4_SA_RATE, 0, 0, tmp_arr);
+       if (ret < 0)
+               return ret;
+       data = (tmp_arr[0]) ? tmp_arr[0] : tmp_arr[1];
+       sc->stats.phy.usrate = data / 1000;
+
+       ret = uea_read_cmv_e4(sc, 4, E4_SA_RATE, 1, 0, tmp_arr);
+       if (ret < 0)
+               return ret;
+       data = (tmp_arr[0]) ? tmp_arr[0] : tmp_arr[1];
+       uea_set_bulk_timeout(sc, data / 1000);
+       sc->stats.phy.dsrate = data / 1000;
+       UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424);
+
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 68, 1, &data);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.dsattenuation = data / 10;
+
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 69, 1, &data);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.usattenuation = data / 10;
+
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 68, 3, &data);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.dsmargin = data / 2;
+
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 69, 3, &data);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.usmargin = data / 10;
+
+       return 0;
+}
+
+static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver)
+{
+       char file_arr[] = "CMVxy.bin";
        char *file;
-       char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */
 
+       /* set proper name corresponding modem version and line type */
        if (cmv_file[sc->modem_index] == NULL) {
                if (UEA_CHIP_VERSION(sc) == ADI930)
-                       file = (IS_ISDN(sc->usb_dev)) ? "CMV9i.bin" : "CMV9p.bin";
+                       file_arr[3] = '9';
+               else if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
+                       file_arr[3] = '4';
                else
-                       file = (IS_ISDN(sc->usb_dev)) ? "CMVei.bin" : "CMVep.bin";
+                       file_arr[3] = 'e';
+
+               file_arr[4] = IS_ISDN(sc) ? 'i' : 'p';
+               file = file_arr;
        } else
                file = cmv_file[sc->modem_index];
 
        strcpy(cmv_name, FW_DIR);
-       strlcat(cmv_name, file, sizeof(cmv_name));
+       strlcat(cmv_name, file, FIRMWARE_NAME_MAX);
+       if (ver == 2)
+               strlcat(cmv_name, ".v2", FIRMWARE_NAME_MAX);
+}
+
+static int request_cmvs_old(struct uea_softc *sc,
+                void **cmvs, const struct firmware **fw)
+{
+       int ret, size;
+       u8 *data;
+       char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */
 
+       cmvs_file_name(sc, cmv_name, 1);
        ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev);
        if (ret < 0) {
                uea_err(INS_TO_USBDEV(sc),
@@ -1045,16 +1588,197 @@ static int request_cmvs(struct uea_softc *sc,
        }
 
        data = (u8 *) (*fw)->data;
-       size = *data * sizeof(struct uea_cmvs) + 1;
-       if (size != (*fw)->size) {
-               uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
-                      cmv_name);
-               release_firmware(*fw);
-               return -EILSEQ;
+       size = (*fw)->size;
+       if (size < 1)
+               goto err_fw_corrupted;
+
+       if (size != *data * sizeof(struct uea_cmvs_v1) + 1)
+               goto err_fw_corrupted;
+
+       *cmvs = (void *)(data + 1);
+       return *data;
+
+err_fw_corrupted:
+       uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", cmv_name);
+       release_firmware(*fw);
+       return -EILSEQ;
+}
+
+static int request_cmvs(struct uea_softc *sc,
+                void **cmvs, const struct firmware **fw, int *ver)
+{
+       int ret, size;
+       u32 crc;
+       u8 *data;
+       char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */
+
+       cmvs_file_name(sc, cmv_name, 2);
+       ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev);
+       if (ret < 0) {
+               /* if caller can handle old version, try to provide it */
+               if (*ver == 1) {
+                       uea_warn(INS_TO_USBDEV(sc), "requesting firmware %s failed, "
+                               "try to get older cmvs\n", cmv_name);
+                       return request_cmvs_old(sc, cmvs, fw);
+               }
+               uea_err(INS_TO_USBDEV(sc),
+                      "requesting firmware %s failed with error %d\n",
+                      cmv_name, ret);
+               return ret;
+       }
+
+       size = (*fw)->size;
+       data = (u8 *) (*fw)->data;
+       if (size < 4 || strncmp(data, "cmv2", 4) != 0) {
+               if (*ver == 1) {
+                       uea_warn(INS_TO_USBDEV(sc), "firmware %s is corrupted, "
+                               "try to get older cmvs\n", cmv_name);
+                       release_firmware(*fw);
+                       return request_cmvs_old(sc, cmvs, fw);
+               }
+               goto err_fw_corrupted;
        }
 
-       *cmvs = (struct uea_cmvs *)(data + 1);
+       *ver = 2;
+
+       data += 4;
+       size -= 4;
+       if (size < 5)
+               goto err_fw_corrupted;
+
+       crc = FW_GET_LONG(data);
+       data += 4;
+       size -= 4;
+       if (crc32_be(0, data, size) != crc)
+               goto err_fw_corrupted;
+
+       if (size != *data * sizeof(struct uea_cmvs_v2) + 1)
+               goto err_fw_corrupted;
+
+       *cmvs = (void *) (data + 1);
        return *data;
+
+err_fw_corrupted:
+       uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", cmv_name);
+       release_firmware(*fw);
+       return -EILSEQ;
+}
+
+static int uea_send_cmvs_e1(struct uea_softc *sc)
+{
+       int i, ret, len;
+       void *cmvs_ptr;
+       const struct firmware *cmvs_fw;
+       int ver = 1; // we can handle v1 cmv firmware version;
+
+       /* Enter in R-IDLE (cmv) until instructed otherwise */
+       ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Dump firmware version */
+       ret = uea_read_cmv_e1(sc, E1_SA_INFO, 10, &sc->stats.phy.firmid);
+       if (ret < 0)
+               return ret;
+       uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
+                       sc->stats.phy.firmid);
+
+       /* get options */
+       ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
+       if (ret < 0)
+               return ret;
+
+       /* send options */
+       if (ver == 1) {
+               struct uea_cmvs_v1 *cmvs_v1 = cmvs_ptr;
+
+               uea_warn(INS_TO_USBDEV(sc), "use deprecated cmvs version, "
+                       "please update your firmware\n");
+
+               for (i = 0; i < len; i++) {
+                       ret = uea_write_cmv_e1(sc, FW_GET_LONG(&cmvs_v1[i].address),
+                                               FW_GET_WORD(&cmvs_v1[i].offset),
+                                               FW_GET_LONG(&cmvs_v1[i].data));
+                       if (ret < 0)
+                               goto out;
+               }
+       } else if (ver == 2) {
+               struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr;
+
+               for (i = 0; i < len; i++) {
+                       ret = uea_write_cmv_e1(sc, FW_GET_LONG(&cmvs_v2[i].address),
+                                               (u16) FW_GET_LONG(&cmvs_v2[i].offset),
+                                               FW_GET_LONG(&cmvs_v2[i].data));
+                       if (ret < 0)
+                               goto out;
+               }
+       } else {
+               /* This realy should not happen */
+               uea_err(INS_TO_USBDEV(sc), "bad cmvs version %d\n", ver);
+               goto out;
+       }
+
+       /* Enter in R-ACT-REQ */
+       ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 2);
+       uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
+       uea_info(INS_TO_USBDEV(sc), "modem started, waiting synchronization...\n");
+out:
+       release_firmware(cmvs_fw);
+       return ret;
+}
+
+static int uea_send_cmvs_e4(struct uea_softc *sc)
+{
+       int i, ret, len;
+       void *cmvs_ptr;
+       const struct firmware *cmvs_fw;
+       int ver = 2; // we can only handle v2 cmv firmware version;
+
+       /* Enter in R-IDLE (cmv) until instructed otherwise */
+       ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Dump firmware version */
+       /* XXX don't read the 3th byte as it is always 6 */
+       ret = uea_read_cmv_e4(sc, 2, E4_SA_INFO, 55, 0, &sc->stats.phy.firmid);
+       if (ret < 0)
+               return ret;
+       uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
+                       sc->stats.phy.firmid);
+
+
+       /* get options */
+       ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
+       if (ret < 0)
+               return ret;
+
+       /* send options */
+       if (ver == 2) {
+               struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr;
+
+               for (i = 0; i < len; i++) {
+                       ret = uea_write_cmv_e4(sc, 1,
+                                               FW_GET_LONG(&cmvs_v2[i].group),
+                                               FW_GET_LONG(&cmvs_v2[i].address),
+                                               FW_GET_LONG(&cmvs_v2[i].offset),
+                                               FW_GET_LONG(&cmvs_v2[i].data));
+                       if (ret < 0)
+                               goto out;
+               }
+       } else {
+               /* This realy should not happen */
+               uea_err(INS_TO_USBDEV(sc), "bad cmvs version %d\n", ver);
+               goto out;
+       }
+
+       /* Enter in R-ACT-REQ */
+       ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 2);
+       uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
+       uea_info(INS_TO_USBDEV(sc), "modem started, waiting synchronization...\n");
+out:
+       release_firmware(cmvs_fw);
+       return ret;
 }
 
 /* Start boot post firmware modem:
@@ -1066,9 +1790,7 @@ static int request_cmvs(struct uea_softc *sc,
 static int uea_start_reset(struct uea_softc *sc)
 {
        u16 zero = 0;   /* ;-) */
-       int i, len, ret;
-       struct uea_cmvs *cmvs;
-       const struct firmware *cmvs_fw;
+       int ret;
 
        uea_enters(INS_TO_USBDEV(sc));
        uea_info(INS_TO_USBDEV(sc), "(re)booting started\n");
@@ -1093,25 +1815,36 @@ static int uea_start_reset(struct uea_softc *sc)
        uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL);
 
        /* original driver use 200ms, but windows driver use 100ms */
-       msleep(100);
+       ret = uea_wait(sc, 0, msecs_to_jiffies(100));
+       if (ret < 0)
+               return ret;
 
        /* leave reset mode */
        uea_request(sc, UEA_SET_MODE, UEA_END_RESET, 0, NULL);
 
-       /* clear tx and rx mailboxes */
-       uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero);
-       uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero);
-       uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero);
+       if (UEA_CHIP_VERSION(sc) != EAGLE_IV) {
+               /* clear tx and rx mailboxes */
+               uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero);
+               uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero);
+               uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero);
+       }
+
+       ret = uea_wait(sc, 0, msecs_to_jiffies(1000));
+       if (ret < 0)
+               return ret;
+
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
+               sc->cmv_dsc.e4.function = E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1);
+       else
+               sc->cmv_dsc.e1.function = E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY);
 
-       msleep(1000);
-       sc->cmv_function = MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY);
        /* demask interrupt */
        sc->booting = 0;
 
        /* start loading DSP */
        sc->pageno = 0;
        sc->ovl = 0;
-       schedule_work(&sc->task);
+       queue_work(sc->work_q, &sc->task);
 
        /* wait for modem ready CMV */
        ret = wait_cmv_ack(sc);
@@ -1120,38 +1853,10 @@ static int uea_start_reset(struct uea_softc *sc)
 
        uea_vdbg(INS_TO_USBDEV(sc), "Ready CMV received\n");
 
-       /* Enter in R-IDLE (cmv) until instructed otherwise */
-       ret = uea_write_cmv(sc, SA_CNTL, 0, 1);
-       if (ret < 0)
-               return ret;
-
-       /* Dump firmware version */
-       ret = uea_read_cmv(sc, SA_INFO, 10, &sc->stats.phy.firmid);
+       ret = sc->send_cmvs(sc);
        if (ret < 0)
                return ret;
-       uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
-                       sc->stats.phy.firmid);
 
-       /* get options */
-       ret = len = request_cmvs(sc, &cmvs, &cmvs_fw);
-       if (ret < 0)
-               return ret;
-
-       /* send options */
-       for (i = 0; i < len; i++) {
-               ret = uea_write_cmv(sc, FW_GET_LONG(&cmvs[i].address),
-                                       FW_GET_WORD(&cmvs[i].offset),
-                                       FW_GET_LONG(&cmvs[i].data));
-               if (ret < 0)
-                       goto out;
-       }
-       /* Enter in R-ACT-REQ */
-       ret = uea_write_cmv(sc, SA_CNTL, 0, 2);
-       uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
-       uea_info(INS_TO_USBDEV(sc), "Modem started, "
-               "waiting synchronization\n");
-out:
-       release_firmware(cmvs_fw);
        sc->reset = 0;
        uea_leaves(INS_TO_USBDEV(sc));
        return ret;
@@ -1174,12 +1879,10 @@ static int uea_kthread(void *data)
                if (ret < 0 || sc->reset)
                        ret = uea_start_reset(sc);
                if (!ret)
-                       ret = uea_stat(sc);
+                       ret = sc->stat(sc);
                if (ret != -EAGAIN)
-                       msleep_interruptible(1000);
-               if (try_to_freeze())
-                       uea_err(INS_TO_USBDEV(sc), "suspend/resume not supported, "
-                               "please unplug/replug your modem\n");
+                       uea_wait(sc, 0, msecs_to_jiffies(1000));
+               try_to_freeze();
        }
        uea_leaves(INS_TO_USBDEV(sc));
        return ret;
@@ -1234,7 +1937,6 @@ static int load_XILINX_firmware(struct uea_softc *sc)
        if (ret < 0)
                uea_err(sc->usb_dev, "elsa de-assert failed with error %d\n", ret);
 
-
 err1:
        release_firmware(fw_entry);
 err0:
@@ -1243,40 +1945,41 @@ err0:
 }
 
 /* The modem send us an ack. First with check if it right */
-static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv)
+static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr)
 {
+       struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1;
+       struct cmv_e1 *cmv = &intr->u.e1.s2.cmv;
+
        uea_enters(INS_TO_USBDEV(sc));
-       if (le16_to_cpu(cmv->wPreamble) != PREAMBLE)
+       if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE)
                goto bad1;
 
-       if (cmv->bDirection != MODEMTOHOST)
+       if (cmv->bDirection != E1_MODEMTOHOST)
                goto bad1;
 
        /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to
         * the first MEMACESS cmv. Ignore it...
         */
-       if (cmv->bFunction != sc->cmv_function) {
+       if (cmv->bFunction != dsc->function) {
                if (UEA_CHIP_VERSION(sc) == ADI930
-                               && cmv->bFunction ==  MAKEFUNCTION(2, 2)) {
-                       cmv->wIndex = cpu_to_le16(sc->cmv_idx);
-                       put_unaligned(cpu_to_le32(sc->cmv_address), &cmv->dwSymbolicAddress);
-                       cmv->wOffsetAddress = cpu_to_le16(sc->cmv_offset);
-               }
-               else
+                               && cmv->bFunction ==  E1_MAKEFUNCTION(2, 2)) {
+                       cmv->wIndex = cpu_to_le16(dsc->idx);
+                       put_unaligned(cpu_to_le32(dsc->address), &cmv->dwSymbolicAddress);
+                       cmv->wOffsetAddress = cpu_to_le16(dsc->offset);
+               } else
                        goto bad2;
        }
 
-       if (cmv->bFunction == MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY)) {
+       if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY)) {
                wake_up_cmv_ack(sc);
                uea_leaves(INS_TO_USBDEV(sc));
                return;
        }
 
        /* in case of MEMACCESS */
-       if (le16_to_cpu(cmv->wIndex) != sc->cmv_idx ||
-           le32_to_cpu(get_unaligned(&cmv->dwSymbolicAddress)) !=
-           sc->cmv_address
-           || le16_to_cpu(cmv->wOffsetAddress) != sc->cmv_offset)
+       if (le16_to_cpu(cmv->wIndex) != dsc->idx ||
+           le32_to_cpu(get_unaligned(&cmv->dwSymbolicAddress)) != dsc->address ||
+           le16_to_cpu(cmv->wOffsetAddress) != dsc->offset)
                goto bad2;
 
        sc->data = le32_to_cpu(get_unaligned(&cmv->dwData));
@@ -1289,8 +1992,8 @@ static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv)
 bad2:
        uea_err(INS_TO_USBDEV(sc), "unexpected cmv received,"
                        "Function : %d, Subfunction : %d\n",
-                       FUNCTION_TYPE(cmv->bFunction),
-                       FUNCTION_SUBTYPE(cmv->bFunction));
+                       E1_FUNCTION_TYPE(cmv->bFunction),
+                       E1_FUNCTION_SUBTYPE(cmv->bFunction));
        uea_leaves(INS_TO_USBDEV(sc));
        return;
 
@@ -1301,6 +2004,61 @@ bad1:
        uea_leaves(INS_TO_USBDEV(sc));
 }
 
+/* The modem send us an ack. First with check if it right */
+static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr)
+{
+       struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4;
+       struct cmv_e4 *cmv = &intr->u.e4.s2.cmv;
+
+       uea_enters(INS_TO_USBDEV(sc));
+       uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n",
+               be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction),
+               be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress),
+               be32_to_cpu(cmv->dwData[0]), be32_to_cpu(cmv->dwData[1]));
+
+       if (be16_to_cpu(cmv->wFunction) != dsc->function)
+               goto bad2;
+
+       if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1)) {
+               wake_up_cmv_ack(sc);
+               uea_leaves(INS_TO_USBDEV(sc));
+               return;
+       }
+
+       /* in case of MEMACCESS */
+       if (be16_to_cpu(cmv->wOffset) != dsc->offset ||
+           be16_to_cpu(cmv->wGroup) != dsc->group ||
+           be16_to_cpu(cmv->wAddress) != dsc->address)
+               goto bad2;
+
+       sc->data = be32_to_cpu(cmv->dwData[0]);
+       sc->data1 = be32_to_cpu(cmv->dwData[1]);
+       wake_up_cmv_ack(sc);
+       uea_leaves(INS_TO_USBDEV(sc));
+       return;
+
+bad2:
+       uea_err(INS_TO_USBDEV(sc), "unexpected cmv received,"
+                       "Function : %d, Subfunction : %d\n",
+                       E4_FUNCTION_TYPE(cmv->wFunction),
+                       E4_FUNCTION_SUBTYPE(cmv->wFunction));
+       uea_leaves(INS_TO_USBDEV(sc));
+       return;
+}
+
+static void uea_schedule_load_page_e1(struct uea_softc *sc, struct intr_pkt *intr)
+{
+       sc->pageno = intr->e1_bSwapPageNo;
+       sc->ovl = intr->e1_bOvl >> 4 | intr->e1_bOvl << 4;
+       queue_work(sc->work_q, &sc->task);
+}
+
+static void uea_schedule_load_page_e4(struct uea_softc *sc, struct intr_pkt *intr)
+{
+       sc->pageno = intr->e4_bSwapPageNo;
+       queue_work(sc->work_q, &sc->task);
+}
+
 /*
  * interrupt handler
  */
@@ -1326,13 +2084,11 @@ static void uea_intr(struct urb *urb)
 
        switch (le16_to_cpu(intr->wInterrupt)) {
        case INT_LOADSWAPPAGE:
-               sc->pageno = intr->bSwapPageNo;
-               sc->ovl = intr->bOvl >> 4 | intr->bOvl << 4;
-               schedule_work(&sc->task);
+               sc->schedule_load_page(sc, intr);
                break;
 
        case INT_INCOMINGCMV:
-               uea_dispatch_cmv(sc, &intr->u.s2.cmv);
+               sc->dispatch_cmv(sc, intr);
                break;
 
        default:
@@ -1349,35 +2105,55 @@ resubmit:
  */
 static int uea_boot(struct uea_softc *sc)
 {
-       int ret;
+       int ret, size;
        struct intr_pkt *intr;
 
        uea_enters(INS_TO_USBDEV(sc));
 
-       INIT_WORK(&sc->task, uea_load_page);
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+               size = E4_INTR_PKT_SIZE;
+               sc->dispatch_cmv = uea_dispatch_cmv_e4;
+               sc->schedule_load_page = uea_schedule_load_page_e4;
+               sc->stat = uea_stat_e4;
+               sc->send_cmvs = uea_send_cmvs_e4;
+               INIT_WORK(&sc->task, uea_load_page_e4);
+       } else {
+               size = E1_INTR_PKT_SIZE;
+               sc->dispatch_cmv = uea_dispatch_cmv_e1;
+               sc->schedule_load_page = uea_schedule_load_page_e1;
+               sc->stat = uea_stat_e1;
+               sc->send_cmvs = uea_send_cmvs_e1;
+               INIT_WORK(&sc->task, uea_load_page_e1);
+       }
+
        init_waitqueue_head(&sc->sync_q);
-       init_waitqueue_head(&sc->cmv_ack_wait);
+
+       sc->work_q = create_workqueue("ueagle-dsp");
+       if (!sc->work_q) {
+               uea_err(INS_TO_USBDEV(sc), "cannot allocate workqueue\n");
+               uea_leaves(INS_TO_USBDEV(sc));
+               return -ENOMEM;
+       }
 
        if (UEA_CHIP_VERSION(sc) == ADI930)
                load_XILINX_firmware(sc);
 
-       intr = kmalloc(INTR_PKT_SIZE, GFP_KERNEL);
+       intr = kmalloc(size, GFP_KERNEL);
        if (!intr) {
                uea_err(INS_TO_USBDEV(sc),
                       "cannot allocate interrupt package\n");
-               uea_leaves(INS_TO_USBDEV(sc));
-               return -ENOMEM;
+               goto err0;
        }
 
        sc->urb_int = usb_alloc_urb(0, GFP_KERNEL);
        if (!sc->urb_int) {
                uea_err(INS_TO_USBDEV(sc), "cannot allocate interrupt URB\n");
-               goto err;
+               goto err1;
        }
 
        usb_fill_int_urb(sc->urb_int, sc->usb_dev,
                         usb_rcvintpipe(sc->usb_dev, UEA_INTR_PIPE),
-                        intr, INTR_PKT_SIZE, uea_intr, sc,
+                        intr, size, uea_intr, sc,
                         sc->usb_dev->actconfig->interface[0]->altsetting[0].
                         endpoint[0].desc.bInterval);
 
@@ -1385,7 +2161,7 @@ static int uea_boot(struct uea_softc *sc)
        if (ret < 0) {
                uea_err(INS_TO_USBDEV(sc),
                       "urb submition failed with error %d\n", ret);
-               goto err;
+               goto err1;
        }
 
        sc->kthread = kthread_run(uea_kthread, sc, "ueagle-atm");
@@ -1399,10 +2175,12 @@ static int uea_boot(struct uea_softc *sc)
 
 err2:
        usb_kill_urb(sc->urb_int);
-err:
+err1:
        usb_free_urb(sc->urb_int);
        sc->urb_int = NULL;
        kfree(intr);
+err0:
+       destroy_workqueue(sc->work_q);
        uea_leaves(INS_TO_USBDEV(sc));
        return -ENOMEM;
 }
@@ -1417,15 +2195,15 @@ static void uea_stop(struct uea_softc *sc)
        ret = kthread_stop(sc->kthread);
        uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
 
-       /* stop any pending boot process */
-       flush_scheduled_work();
-
        uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
 
        usb_kill_urb(sc->urb_int);
        kfree(sc->urb_int->transfer_buffer);
        usb_free_urb(sc->urb_int);
 
+       /* stop any pending boot process, when no one can schedule work */
+       destroy_workqueue(sc->work_q);
+
        if (sc->dsp_firm)
                release_firmware(sc->dsp_firm);
        uea_leaves(INS_TO_USBDEV(sc));
@@ -1487,6 +2265,7 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
                char *buf)
 {
        int ret = -ENODEV;
+       int modem_state;
        struct uea_softc *sc;
 
        mutex_lock(&uea_mutex);
@@ -1494,7 +2273,34 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
        if (!sc)
                goto out;
 
-       switch (GET_STATUS(sc->stats.phy.state)) {
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+               switch (sc->stats.phy.state) {
+               case 0x0:       /* not yet synchronized */
+               case 0x1:
+               case 0x3:
+               case 0x4:
+                       modem_state = 0;
+                       break;
+               case 0x5:       /* initialization */
+               case 0x6:
+               case 0x9:
+               case 0xa:
+                       modem_state = 1;
+                       break;
+               case 0x7:       /* operational */
+                       modem_state = 2;
+                       break;
+               case 0x2:       /* fail ... */
+                       modem_state = 3;
+                       break;
+               default:        /* unknown */
+                       modem_state = 4;
+                       break;
+               }
+       } else
+               modem_state = GET_STATUS(sc->stats.phy.state);
+
+       switch (modem_state) {
        case 0:
                ret = sprintf(buf, "Modem is booting\n");
                break;
@@ -1504,9 +2310,12 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
        case 2:
                ret = sprintf(buf, "Modem is operational\n");
                break;
-       default:
+       case 3:
                ret = sprintf(buf, "Modem synchronization failed\n");
                break;
+       default:
+               ret = sprintf(buf, "Modem state is unknown\n");
+               break;
        }
 out:
        mutex_unlock(&uea_mutex);
@@ -1520,18 +2329,26 @@ static ssize_t read_delin(struct device *dev, struct device_attribute *attr,
 {
        int ret = -ENODEV;
        struct uea_softc *sc;
+       char *delin = "GOOD";
 
        mutex_lock(&uea_mutex);
        sc = dev_to_uea(dev);
        if (!sc)
                goto out;
 
-       if (sc->stats.phy.flags & 0x0C00)
-               ret = sprintf(buf, "ERROR\n");
-       else if (sc->stats.phy.flags & 0x0030)
-               ret = sprintf(buf, "LOSS\n");
-       else
-               ret = sprintf(buf, "GOOD\n");
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+               if (sc->stats.phy.flags & 0x4000)
+                       delin = "RESET";
+               else if (sc->stats.phy.flags & 0x0001)
+                       delin = "LOSS";
+       } else {
+               if (sc->stats.phy.flags & 0x0C00)
+                       delin = "ERROR";
+               else if (sc->stats.phy.flags & 0x0030)
+                       delin = "LOSS";
+       }
+
+       ret = sprintf(buf, "%s\n", delin);
 out:
        mutex_unlock(&uea_mutex);
        return ret;
@@ -1662,6 +2479,7 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
        struct usb_device *usb = interface_to_usbdev(intf);
        struct uea_softc *sc;
        int ret, ifnum = intf->altsetting->desc.bInterfaceNumber;
+       unsigned int alt;
 
        uea_enters(usb);
 
@@ -1696,22 +2514,29 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
        sc->modem_index = (modem_index < NB_MODEM) ? modem_index++ : 0;
        sc->driver_info = id->driver_info;
 
-       /* ADI930 don't support iso */
-       if (UEA_CHIP_VERSION(id) != ADI930 && use_iso[sc->modem_index]) {
-               int i;
-
-               /* try set fastest alternate for inbound traffic interface */
-               for (i = FASTEST_ISO_INTF; i > 0; i--)
-                       if (usb_set_interface(usb, UEA_DS_IFACE_NO, i) == 0)
-                               break;
+       /* first try to use module parameter */
+       if (annex[sc->modem_index] == 1)
+               sc->annex = ANNEXA;
+       else if (annex[sc->modem_index] == 2)
+               sc->annex = ANNEXB;
+       /* try to autodetect annex */
+       else if (sc->driver_info & AUTO_ANNEX_A)
+               sc->annex = ANNEXA;
+       else if (sc->driver_info & AUTO_ANNEX_B)
+               sc->annex = ANNEXB;
+       else
+               sc->annex = (le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)?ANNEXB:ANNEXA;
 
-               if (i > 0) {
-                       uea_dbg(usb, "set alternate %d for 2 interface\n", i);
+       alt = altsetting[sc->modem_index];
+       /* ADI930 don't support iso */
+       if (UEA_CHIP_VERSION(id) != ADI930 && alt > 0) {
+               if (alt <= 8 && usb_set_interface(usb, UEA_DS_IFACE_NO, alt) == 0) {
+                       uea_dbg(usb, "set alternate %u for 2 interface\n", alt);
                        uea_info(usb, "using iso mode\n");
                        usbatm->flags |= UDSL_USE_ISOC | UDSL_IGNORE_EILSEQ;
                } else {
-                       uea_err(usb, "setting any alternate failed for "
-                                       "2 interface, using bulk mode\n");
+                       uea_err(usb, "setting alternate %u failed for "
+                                       "2 interface, using bulk mode\n", alt);
                }
        }
 
@@ -1757,10 +2582,11 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
        struct usb_device *usb = interface_to_usbdev(intf);
 
        uea_enters(usb);
-       uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) : %s %s\n",
-              le16_to_cpu(usb->descriptor.idVendor),
-              le16_to_cpu(usb->descriptor.idProduct),
-              chip_name[UEA_CHIP_VERSION(id)], IS_ISDN(usb)?"isdn":"pots");
+       uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n",
+               le16_to_cpu(usb->descriptor.idVendor),
+               le16_to_cpu(usb->descriptor.idProduct),
+               le16_to_cpu(usb->descriptor.bcdDevice),
+               chip_name[UEA_CHIP_VERSION(id)]);
 
        usb_reset_device(usb);
 
@@ -1793,24 +2619,40 @@ static void uea_disconnect(struct usb_interface *intf)
  * List of supported VID/PID
  */
 static const struct usb_device_id uea_ids[] = {
+       {USB_DEVICE(ANALOG_VID, ADI930_PID_PREFIRM),    .driver_info = ADI930 | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, ADI930_PID_PSTFIRM),    .driver_info = ADI930 | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PREFIRM),   .driver_info = EAGLE_I | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PSTFIRM),   .driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PREFIRM),  .driver_info = EAGLE_II | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PSTFIRM),  .driver_info = EAGLE_II | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PREFIRM), .driver_info = EAGLE_III | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PSTFIRM), .driver_info = EAGLE_III | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PREFIRM),  .driver_info = EAGLE_IV | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PSTFIRM),  .driver_info = EAGLE_IV | PSTFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PREFIRM),  .driver_info = EAGLE_I | PREFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PREFIRM),  .driver_info = EAGLE_I | PREFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_A},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_B},
        {USB_DEVICE(ELSA_VID,   ELSA_PID_PREFIRM),      .driver_info = ADI930 | PREFIRM},
        {USB_DEVICE(ELSA_VID,   ELSA_PID_PSTFIRM),      .driver_info = ADI930 | PSTFIRM},
-       {USB_DEVICE(EAGLE_VID,  EAGLE_I_PID_PREFIRM),   .driver_info = EAGLE_I | PREFIRM},
-       {USB_DEVICE(EAGLE_VID,  EAGLE_I_PID_PSTFIRM),   .driver_info = EAGLE_I | PSTFIRM},
-       {USB_DEVICE(EAGLE_VID,  EAGLE_II_PID_PREFIRM),  .driver_info = EAGLE_II | PREFIRM},
-       {USB_DEVICE(EAGLE_VID,  EAGLE_II_PID_PSTFIRM),  .driver_info = EAGLE_II | PSTFIRM},
-       {USB_DEVICE(EAGLE_VID,  EAGLE_IIC_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
-       {USB_DEVICE(EAGLE_VID,  EAGLE_IIC_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM},
-       {USB_DEVICE(EAGLE_VID,  EAGLE_III_PID_PREFIRM), .driver_info = EAGLE_III | PREFIRM},
-       {USB_DEVICE(EAGLE_VID,  EAGLE_III_PID_PSTFIRM), .driver_info = EAGLE_III | PSTFIRM},
+       {USB_DEVICE(ELSA_VID,   ELSA_PID_A_PREFIRM),    .driver_info = ADI930 | PREFIRM},
+       {USB_DEVICE(ELSA_VID,   ELSA_PID_A_PSTFIRM),    .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_A},
+       {USB_DEVICE(ELSA_VID,   ELSA_PID_B_PREFIRM),    .driver_info = ADI930 | PREFIRM},
+       {USB_DEVICE(ELSA_VID,   ELSA_PID_B_PSTFIRM),    .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_B},
        {USB_DEVICE(USR_VID,    MILLER_A_PID_PREFIRM),  .driver_info = EAGLE_I | PREFIRM},
-       {USB_DEVICE(USR_VID,    MILLER_A_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(USR_VID,    MILLER_A_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM  | AUTO_ANNEX_A},
        {USB_DEVICE(USR_VID,    MILLER_B_PID_PREFIRM),  .driver_info = EAGLE_I | PREFIRM},
-       {USB_DEVICE(USR_VID,    MILLER_B_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(USR_VID,    MILLER_B_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM  | AUTO_ANNEX_B},
        {USB_DEVICE(USR_VID,    HEINEKEN_A_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
-       {USB_DEVICE(USR_VID,    HEINEKEN_A_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(USR_VID,    HEINEKEN_A_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
        {USB_DEVICE(USR_VID,    HEINEKEN_B_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
-       {USB_DEVICE(USR_VID,    HEINEKEN_B_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(USR_VID,    HEINEKEN_B_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
        {}
 };
 
index 5192cd9..ad632f2 100644 (file)
@@ -28,6 +28,7 @@
  *     v0.12 - add hpoj.sourceforge.net ioctls (David Paschal)
  *     v0.13 - alloc space for statusbuf (<status> not on stack);
  *             use usb_buffer_alloc() for read buf & write buf;
+ *      none  - Maintained in Linux kernel after v0.13
  */
 
 /*
@@ -69,7 +70,6 @@
 #define USBLP_DEVICE_ID_SIZE   1024
 
 /* ioctls: */
-#define LPGETSTATUS            0x060b          /* same as in drivers/char/lp.c */
 #define IOCNR_GET_DEVICE_ID            1
 #define IOCNR_GET_PROTOCOLS            2
 #define IOCNR_SET_PROTOCOL             3
@@ -115,7 +115,7 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H
 #define USBLP_MINORS           16
 #define USBLP_MINOR_BASE       0
 
-#define USBLP_WRITE_TIMEOUT    (5000)                  /* 5 seconds */
+#define USBLP_CTL_TIMEOUT      5000                    /* 5 seconds */
 
 #define USBLP_FIRST_PROTOCOL   1
 #define USBLP_LAST_PROTOCOL    3
@@ -159,10 +159,12 @@ struct usblp {
        int                     wstatus;        /* bytes written or error */
        int                     rstatus;        /* bytes ready or error */
        unsigned int            quirks;                 /* quirks flags */
+       unsigned int            flags;                  /* mode flags */
        unsigned char           used;                   /* True if open */
        unsigned char           present;                /* True if not disconnected */
        unsigned char           bidir;                  /* interface is bidirectional */
        unsigned char           sleeping;               /* interface is suspended */
+       unsigned char           no_paper;               /* Paper Out happened */
        unsigned char           *device_id_string;      /* IEEE 1284 DEVICE ID string (ptr) */
                                                        /* first 2 bytes are (big-endian) length */
 };
@@ -259,7 +261,7 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i
 
        retval = usb_control_msg(usblp->dev,
                dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
-               request, type | dir | recip, value, index, buf, len, USBLP_WRITE_TIMEOUT);
+               request, type | dir | recip, value, index, buf, len, USBLP_CTL_TIMEOUT);
        dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d",
                request, !!dir, recip, value, index, len, retval);
        return retval < 0 ? retval : 0;
@@ -325,13 +327,11 @@ static void usblp_bulk_write(struct urb *urb)
                usblp->wstatus = status;
        else
                usblp->wstatus = urb->actual_length;
+       usblp->no_paper = 0;
        usblp->wcomplete = 1;
        wake_up(&usblp->wwait);
        spin_unlock(&usblp->lock);
 
-       /* XXX Use usb_setup_bulk_urb when available. Talk to Marcel. */
-       kfree(urb->transfer_buffer);
-       urb->transfer_buffer = NULL;    /* Not refcounted, so to be safe... */
        usb_free_urb(urb);
 }
 
@@ -346,16 +346,17 @@ static int usblp_check_status(struct usblp *usblp, int err)
        unsigned char status, newerr = 0;
        int error;
 
-       error = usblp_read_status (usblp, usblp->statusbuf);
-       if (error < 0) {
+       mutex_lock(&usblp->mut);
+       if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) {
+               mutex_unlock(&usblp->mut);
                if (printk_ratelimit())
                        printk(KERN_ERR
                                "usblp%d: error %d reading printer status\n",
                                usblp->minor, error);
                return 0;
        }
-
        status = *usblp->statusbuf;
+       mutex_unlock(&usblp->mut);
 
        if (~status & LP_PERRORP)
                newerr = 3;
@@ -411,18 +412,10 @@ static int usblp_open(struct inode *inode, struct file *file)
                goto out;
 
        /*
-        * TODO: need to implement LP_ABORTOPEN + O_NONBLOCK as in drivers/char/lp.c ???
-        * This is #if 0-ed because we *don't* want to fail an open
-        * just because the printer is off-line.
+        * We do not implement LP_ABORTOPEN/LPABORTOPEN for two reasons:
+        *  - We do not want persistent state which close(2) does not clear
+        *  - It is not used anyway, according to CUPS people
         */
-#if 0
-       if ((retval = usblp_check_status(usblp, 0))) {
-               retval = retval > 1 ? -EIO : -ENOSPC;
-               goto out;
-       }
-#else
-       retval = 0;
-#endif
 
        retval = usb_autopm_get_interface(intf);
        if (retval < 0)
@@ -463,6 +456,8 @@ static int usblp_release(struct inode *inode, struct file *file)
 {
        struct usblp *usblp = file->private_data;
 
+       usblp->flags &= ~LP_ABORT;
+
        mutex_lock (&usblp_mutex);
        usblp->used = 0;
        if (usblp->present) {
@@ -485,8 +480,8 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait
        poll_wait(file, &usblp->rwait, wait);
        poll_wait(file, &usblp->wwait, wait);
        spin_lock_irqsave(&usblp->lock, flags);
-       ret = ((!usblp->bidir || !usblp->rcomplete) ? 0 : POLLIN  | POLLRDNORM)
-                              | (!usblp->wcomplete ? 0 : POLLOUT | POLLWRNORM);
+       ret = ((usblp->bidir && usblp->rcomplete) ? POLLIN  | POLLRDNORM : 0) |
+          ((usblp->no_paper || usblp->wcomplete) ? POLLOUT | POLLWRNORM : 0);
        spin_unlock_irqrestore(&usblp->lock, flags);
        return ret;
 }
@@ -675,6 +670,13 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                        retval = -EFAULT;
                                break;
 
+                       case LPABORT:
+                               if (arg)
+                                       usblp->flags |= LP_ABORT;
+                               else
+                                       usblp->flags &= ~LP_ABORT;
+                               break;
+
                        default:
                                retval = -ENOTTY;
                }
@@ -684,10 +686,30 @@ done:
        return retval;
 }
 
+static struct urb *usblp_new_writeurb(struct usblp *usblp, int transfer_length)
+{
+       struct urb *urb;
+       char *writebuf;
+
+       if ((writebuf = kmalloc(transfer_length, GFP_KERNEL)) == NULL)
+               return NULL;
+       if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) {
+               kfree(writebuf);
+               return NULL;
+       }
+
+       usb_fill_bulk_urb(urb, usblp->dev,
+               usb_sndbulkpipe(usblp->dev,
+                usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress),
+               writebuf, transfer_length, usblp_bulk_write, usblp);
+       urb->transfer_flags |= URB_FREE_BUFFER;
+
+       return urb;
+}
+
 static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
 {
        struct usblp *usblp = file->private_data;
-       char *writebuf;
        struct urb *writeurb;
        int rv;
        int transfer_length;
@@ -708,17 +730,11 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
                        transfer_length = USBLP_BUF_SIZE;
 
                rv = -ENOMEM;
-               if ((writebuf = kmalloc(USBLP_BUF_SIZE, GFP_KERNEL)) == NULL)
-                       goto raise_buf;
-               if ((writeurb = usb_alloc_urb(0, GFP_KERNEL)) == NULL)
+               if ((writeurb = usblp_new_writeurb(usblp, transfer_length)) == NULL)
                        goto raise_urb;
-               usb_fill_bulk_urb(writeurb, usblp->dev,
-                       usb_sndbulkpipe(usblp->dev,
-                         usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress),
-                       writebuf, transfer_length, usblp_bulk_write, usblp);
                usb_anchor_urb(writeurb, &usblp->urbs);
 
-               if (copy_from_user(writebuf,
+               if (copy_from_user(writeurb->transfer_buffer,
                                   buffer + writecount, transfer_length)) {
                        rv = -EFAULT;
                        goto raise_badaddr;
@@ -730,6 +746,7 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
                if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) {
                        usblp->wstatus = 0;
                        spin_lock_irq(&usblp->lock);
+                       usblp->no_paper = 0;
                        usblp->wcomplete = 1;
                        wake_up(&usblp->wwait);
                        spin_unlock_irq(&usblp->lock);
@@ -747,12 +764,17 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
                                /* Presume that it's going to complete well. */
                                writecount += transfer_length;
                        }
+                       if (rv == -ENOSPC) {
+                               spin_lock_irq(&usblp->lock);
+                               usblp->no_paper = 1;    /* Mark for poll(2) */
+                               spin_unlock_irq(&usblp->lock);
+                               writecount += transfer_length;
+                       }
                        /* Leave URB dangling, to be cleaned on close. */
                        goto collect_error;
                }
 
                if (usblp->wstatus < 0) {
-                       usblp_check_status(usblp, 0);
                        rv = -EIO;
                        goto collect_error;
                }
@@ -771,8 +793,6 @@ raise_badaddr:
        usb_unanchor_urb(writeurb);
        usb_free_urb(writeurb);
 raise_urb:
-       kfree(writebuf);
-raise_buf:
 raise_wait:
 collect_error:         /* Out of raise sequence */
        mutex_unlock(&usblp->wmut);
@@ -838,32 +858,36 @@ done:
  * when O_NONBLOCK is set. So, applications setting O_NONBLOCK must use
  * select(2) or poll(2) to wait for the buffer to drain before closing.
  * Alternatively, set blocking mode with fcntl and issue a zero-size write.
- *
- * Old v0.13 code had a non-functional timeout for wait_event(). Someone forgot
- * to check the return code for timeout expiration, so it had no effect.
- * Apparently, it was intended to check for error conditons, such as out
- * of paper. It is going to return when we settle things with CUPS. XXX
  */
 static int usblp_wwait(struct usblp *usblp, int nonblock)
 {
        DECLARE_WAITQUEUE(waita, current);
        int rc;
+       int err = 0;
 
        add_wait_queue(&usblp->wwait, &waita);
        for (;;) {
+               set_current_state(TASK_INTERRUPTIBLE);
                if (mutex_lock_interruptible(&usblp->mut)) {
                        rc = -EINTR;
                        break;
                }
-               set_current_state(TASK_INTERRUPTIBLE);
-               if ((rc = usblp_wtest(usblp, nonblock)) < 0) {
-                       mutex_unlock(&usblp->mut);
-                       break;
-               }
+               rc = usblp_wtest(usblp, nonblock);
                mutex_unlock(&usblp->mut);
-               if (rc == 0)
+               if (rc <= 0)
                        break;
-               schedule();
+
+               if (usblp->flags & LP_ABORT) {
+                       if (schedule_timeout(msecs_to_jiffies(5000)) == 0) {
+                               err = usblp_check_status(usblp, err);
+                               if (err == 1) { /* Paper out */
+                                       rc = -ENOSPC;
+                                       break;
+                               }
+                       }
+               } else {
+                       schedule();
+               }
        }
        set_current_state(TASK_RUNNING);
        remove_wait_queue(&usblp->wwait, &waita);
index cb69aa1..1a8edce 100644 (file)
@@ -507,18 +507,30 @@ void usb_destroy_configuration(struct usb_device *dev)
 }
 
 
-// hub-only!! ... and only in reset path, or usb_new_device()
-// (used by real hubs and virtual root hubs)
+/*
+ * Get the USB config descriptors, cache and parse'em
+ *
+ * hub-only!! ... and only in reset path, or usb_new_device()
+ * (used by real hubs and virtual root hubs)
+ *
+ * NOTE: if this is a WUSB device and is not authorized, we skip the
+ *       whole thing. A non-authorized USB device has no
+ *       configurations.
+ */
 int usb_get_configuration(struct usb_device *dev)
 {
        struct device *ddev = &dev->dev;
        int ncfg = dev->descriptor.bNumConfigurations;
-       int result = -ENOMEM;
+       int result = 0;
        unsigned int cfgno, length;
        unsigned char *buffer;
        unsigned char *bigbuffer;
        struct usb_config_descriptor *desc;
 
+       cfgno = 0;
+       if (dev->authorized == 0)       /* Not really an error */
+               goto out_not_authorized;
+       result = -ENOMEM;
        if (ncfg > USB_MAXCONFIG) {
                dev_warn(ddev, "too many configurations: %d, "
                    "using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
@@ -545,14 +557,15 @@ int usb_get_configuration(struct usb_device *dev)
                goto err2;
        desc = (struct usb_config_descriptor *)buffer;
 
-       for (cfgno = 0; cfgno < ncfg; cfgno++) {
+       result = 0;
+       for (; cfgno < ncfg; cfgno++) {
                /* We grab just the first descriptor so we know how long
                 * the whole configuration is */
                result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
                    buffer, USB_DT_CONFIG_SIZE);
                if (result < 0) {
                        dev_err(ddev, "unable to read config index %d "
-                           "descriptor/%s\n", cfgno, "start");
+                           "descriptor/%s: %d\n", cfgno, "start", result);
                        dev_err(ddev, "chopping to %d config(s)\n", cfgno);
                        dev->descriptor.bNumConfigurations = cfgno;
                        break;
@@ -599,6 +612,7 @@ int usb_get_configuration(struct usb_device *dev)
 
 err:
        kfree(buffer);
+out_not_authorized:
        dev->descriptor.bNumConfigurations = cfgno;
 err2:
        if (result == -ENOMEM)
index 927a181..e5ad76b 100644 (file)
@@ -71,6 +71,7 @@ struct async {
        void __user *userbuffer;
        void __user *userurb;
        struct urb *urb;
+       int status;
        u32 secid;
 };
 
@@ -289,10 +290,8 @@ static void snoop_urb(struct urb *urb, void __user *userurb)
        if (!usbfs_snoop)
                return;
 
-       if (urb->pipe & USB_DIR_IN)
-               dev_info(&urb->dev->dev, "direction=IN\n");
-       else
-               dev_info(&urb->dev->dev, "direction=OUT\n");
+       dev_info(&urb->dev->dev, "direction=%s\n",
+                       usb_urb_dir_in(urb) ? "IN" : "OUT");
        dev_info(&urb->dev->dev, "userurb=%p\n", userurb);
        dev_info(&urb->dev->dev, "transfer_buffer_length=%d\n",
                 urb->transfer_buffer_length);
@@ -312,9 +311,10 @@ static void async_completed(struct urb *urb)
         spin_lock(&ps->lock);
         list_move_tail(&as->asynclist, &ps->async_completed);
         spin_unlock(&ps->lock);
+       as->status = urb->status;
        if (as->signr) {
                sinfo.si_signo = as->signr;
-               sinfo.si_errno = as->urb->status;
+               sinfo.si_errno = as->status;
                sinfo.si_code = SI_ASYNCIO;
                sinfo.si_addr = as->userurb;
                kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid,
@@ -910,6 +910,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        struct usb_ctrlrequest *dr = NULL;
        unsigned int u, totlen, isofrmlen;
        int ret, ifnum = -1;
+       int is_in;
 
        if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK|
                           URB_NO_FSBR|URB_ZERO_PACKET))
@@ -924,16 +925,18 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                if ((ret = checkintf(ps, ifnum)))
                        return ret;
        }
-       if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0)
-               ep = ps->dev->ep_in [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
-       else
-               ep = ps->dev->ep_out [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
+       if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) {
+               is_in = 1;
+               ep = ps->dev->ep_in[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
+       } else {
+               is_in = 0;
+               ep = ps->dev->ep_out[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
+       }
        if (!ep)
                return -ENOENT;
        switch(uurb->type) {
        case USBDEVFS_URB_TYPE_CONTROL:
-               if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                               != USB_ENDPOINT_XFER_CONTROL)
+               if (!usb_endpoint_xfer_control(&ep->desc))
                        return -EINVAL;
                /* min 8 byte setup packet, max 8 byte setup plus an arbitrary data stage */
                if (uurb->buffer_length < 8 || uurb->buffer_length > (8 + MAX_USBFS_BUFFER_SIZE))
@@ -952,23 +955,32 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        kfree(dr);
                        return ret;
                }
-               uurb->endpoint = (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK);
                uurb->number_of_packets = 0;
                uurb->buffer_length = le16_to_cpup(&dr->wLength);
                uurb->buffer += 8;
-               if (!access_ok((uurb->endpoint & USB_DIR_IN) ?  VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) {
+               if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
+                       is_in = 1;
+                       uurb->endpoint |= USB_DIR_IN;
+               } else {
+                       is_in = 0;
+                       uurb->endpoint &= ~USB_DIR_IN;
+               }
+               if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
+                               uurb->buffer, uurb->buffer_length)) {
                        kfree(dr);
                        return -EFAULT;
                }
                snoop(&ps->dev->dev, "control urb: bRequest=%02x "
                        "bRrequestType=%02x wValue=%04x "
                        "wIndex=%04x wLength=%04x\n",
-                       dr->bRequest, dr->bRequestType, dr->wValue,
-                       dr->wIndex, dr->wLength);
+                       dr->bRequest, dr->bRequestType,
+                       __le16_to_cpup(&dr->wValue),
+                       __le16_to_cpup(&dr->wIndex),
+                       __le16_to_cpup(&dr->wLength));
                break;
 
        case USBDEVFS_URB_TYPE_BULK:
-               switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+               switch (usb_endpoint_type(&ep->desc)) {
                case USB_ENDPOINT_XFER_CONTROL:
                case USB_ENDPOINT_XFER_ISOC:
                        return -EINVAL;
@@ -977,7 +989,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                uurb->number_of_packets = 0;
                if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
                        return -EINVAL;
-               if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
+               if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
+                               uurb->buffer, uurb->buffer_length))
                        return -EFAULT;
                snoop(&ps->dev->dev, "bulk urb\n");
                break;
@@ -986,8 +999,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                /* arbitrary limit */
                if (uurb->number_of_packets < 1 || uurb->number_of_packets > 128)
                        return -EINVAL;
-               if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                               != USB_ENDPOINT_XFER_ISOC)
+               if (!usb_endpoint_xfer_isoc(&ep->desc))
                        return -EINVAL;
                isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets;
                if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
@@ -1014,12 +1026,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
 
        case USBDEVFS_URB_TYPE_INTERRUPT:
                uurb->number_of_packets = 0;
-               if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                               != USB_ENDPOINT_XFER_INT)
+               if (!usb_endpoint_xfer_int(&ep->desc))
                        return -EINVAL;
                if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
                        return -EINVAL;
-               if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
+               if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
+                               uurb->buffer, uurb->buffer_length))
                        return -EFAULT;
                snoop(&ps->dev->dev, "interrupt urb\n");
                break;
@@ -1039,8 +1051,11 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                return -ENOMEM;
        }
         as->urb->dev = ps->dev;
-        as->urb->pipe = (uurb->type << 30) | __create_pipe(ps->dev, uurb->endpoint & 0xf) | (uurb->endpoint & USB_DIR_IN);
-        as->urb->transfer_flags = uurb->flags;
+        as->urb->pipe = (uurb->type << 30) |
+                       __create_pipe(ps->dev, uurb->endpoint & 0xf) |
+                       (uurb->endpoint & USB_DIR_IN);
+        as->urb->transfer_flags = uurb->flags |
+                       (is_in ? URB_DIR_IN : URB_DIR_OUT);
        as->urb->transfer_buffer_length = uurb->buffer_length;
        as->urb->setup_packet = (unsigned char*)dr;
        as->urb->start_frame = uurb->start_frame;
@@ -1070,13 +1085,13 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        as->uid = current->uid;
        as->euid = current->euid;
        security_task_getsecid(current, &as->secid);
-       if (!(uurb->endpoint & USB_DIR_IN)) {
-               if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) {
+       if (!is_in) {
+               if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
+                               as->urb->transfer_buffer_length)) {
                        free_async(as);
                        return -EFAULT;
                }
        }
-       snoop(&as->urb->dev->dev, "submit urb\n");
        snoop_urb(as->urb, as->userurb);
         async_newpending(as);
         if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) {
@@ -1119,14 +1134,14 @@ static int processcompl(struct async *as, void __user * __user *arg)
        if (as->userbuffer)
                if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length))
                        return -EFAULT;
-       if (put_user(urb->status, &userurb->status))
+       if (put_user(as->status, &userurb->status))
                return -EFAULT;
        if (put_user(urb->actual_length, &userurb->actual_length))
                return -EFAULT;
        if (put_user(urb->error_count, &userurb->error_count))
                return -EFAULT;
 
-       if (usb_pipeisoc(urb->pipe)) {
+       if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
                for (i = 0; i < urb->number_of_packets; i++) {
                        if (put_user(urb->iso_frame_desc[i].actual_length,
                                     &userurb->iso_frame_desc[i].actual_length))
@@ -1233,14 +1248,14 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
        if (as->userbuffer)
                if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length))
                        return -EFAULT;
-       if (put_user(urb->status, &userurb->status))
+       if (put_user(as->status, &userurb->status))
                return -EFAULT;
        if (put_user(urb->actual_length, &userurb->actual_length))
                return -EFAULT;
        if (put_user(urb->error_count, &userurb->error_count))
                return -EFAULT;
 
-       if (usb_pipeisoc(urb->pipe)) {
+       if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
                for (i = 0; i < urb->number_of_packets; i++) {
                        if (put_user(urb->iso_frame_desc[i].actual_length,
                                     &userurb->iso_frame_desc[i].actual_length))
index 63b1243..c27bc08 100644 (file)
@@ -202,6 +202,11 @@ static int usb_probe_interface(struct device *dev)
        intf = to_usb_interface(dev);
        udev = interface_to_usbdev(intf);
 
+       if (udev->authorized == 0) {
+               dev_err(&intf->dev, "Device is not authorized for usage\n");
+               return -ENODEV;
+       }
+
        id = usb_match_id(intf, driver->id_table);
        if (!id)
                id = usb_match_dynamic_id(intf, driver);
@@ -945,11 +950,11 @@ done:
 #ifdef CONFIG_USB_SUSPEND
 
 /* Internal routine to check whether we may autosuspend a device. */
-static int autosuspend_check(struct usb_device *udev)
+static int autosuspend_check(struct usb_device *udev, int reschedule)
 {
        int                     i;
        struct usb_interface    *intf;
-       unsigned long           suspend_time;
+       unsigned long           suspend_time, j;
 
        /* For autosuspend, fail fast if anything is in use or autosuspend
         * is disabled.  Also fail if any interfaces require remote wakeup
@@ -991,20 +996,20 @@ static int autosuspend_check(struct usb_device *udev)
        }
 
        /* If everything is okay but the device hasn't been idle for long
-        * enough, queue a delayed autosuspend request.
+        * enough, queue a delayed autosuspend request.  If the device
+        * _has_ been idle for long enough and the reschedule flag is set,
+        * likewise queue a delayed (1 second) autosuspend request.
         */
-       if (time_after(suspend_time, jiffies)) {
+       j = jiffies;
+       if (time_before(j, suspend_time))
+               reschedule = 1;
+       else
+               suspend_time = j + HZ;
+       if (reschedule) {
                if (!timer_pending(&udev->autosuspend.timer)) {
-
-                       /* The value of jiffies may change between the
-                        * time_after() comparison above and the subtraction
-                        * below.  That's okay; the system behaves sanely
-                        * when a timer is registered for the present moment
-                        * or for the past.
-                        */
                        queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
-                               round_jiffies_relative(suspend_time - jiffies));
-                       }
+                               round_jiffies_relative(suspend_time - j));
+               }
                return -EAGAIN;
        }
        return 0;
@@ -1012,7 +1017,7 @@ static int autosuspend_check(struct usb_device *udev)
 
 #else
 
-static inline int autosuspend_check(struct usb_device *udev)
+static inline int autosuspend_check(struct usb_device *udev, int reschedule)
 {
        return 0;
 }
@@ -1069,7 +1074,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
        udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
 
        if (udev->auto_pm) {
-               status = autosuspend_check(udev);
+               status = autosuspend_check(udev, 0);
                if (status < 0)
                        goto done;
        }
@@ -1083,15 +1088,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
                                break;
                }
        }
-       if (status == 0) {
-
-               /* Non-root devices don't need to do anything for FREEZE
-                * or PRETHAW. */
-               if (udev->parent && (msg.event == PM_EVENT_FREEZE ||
-                               msg.event == PM_EVENT_PRETHAW))
-                       goto done;
+       if (status == 0)
                status = usb_suspend_device(udev, msg);
-       }
 
        /* If the suspend failed, resume interfaces that did get suspended */
        if (status != 0) {
@@ -1102,12 +1100,24 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
 
                /* Try another autosuspend when the interfaces aren't busy */
                if (udev->auto_pm)
-                       autosuspend_check(udev);
+                       autosuspend_check(udev, status == -EBUSY);
 
-       /* If the suspend succeeded, propagate it up the tree */
+       /* If the suspend succeeded then prevent any more URB submissions,
+        * flush any outstanding URBs, and propagate the suspend up the tree.
+        */
        } else {
                cancel_delayed_work(&udev->autosuspend);
-               if (parent)
+               udev->can_submit = 0;
+               for (i = 0; i < 16; ++i) {
+                       usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
+                       usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
+               }
+
+               /* If this is just a FREEZE or a PRETHAW, udev might
+                * not really be suspended.  Only true suspends get
+                * propagated up the device tree.
+                */
+               if (parent && udev->state == USB_STATE_SUSPENDED)
                        usb_autosuspend_device(parent);
        }
 
@@ -1156,6 +1166,7 @@ static int usb_resume_both(struct usb_device *udev)
                status = -ENODEV;
                goto done;
        }
+       udev->can_submit = 1;
 
        /* Propagate the resume up the tree, if necessary */
        if (udev->state == USB_STATE_SUSPENDED) {
@@ -1529,9 +1540,21 @@ int usb_external_resume_device(struct usb_device *udev)
 
 static int usb_suspend(struct device *dev, pm_message_t message)
 {
+       struct usb_device       *udev;
+
        if (!is_usb_device(dev))        /* Ignore PM for interfaces */
                return 0;
-       return usb_external_suspend_device(to_usb_device(dev), message);
+       udev = to_usb_device(dev);
+
+       /* If udev is already suspended, we can skip this suspend and
+        * we should also skip the upcoming system resume. */
+       if (udev->state == USB_STATE_SUSPENDED) {
+               udev->skip_sys_resume = 1;
+               return 0;
+       }
+
+       udev->skip_sys_resume = 0;
+       return usb_external_suspend_device(udev, message);
 }
 
 static int usb_resume(struct device *dev)
@@ -1542,13 +1565,14 @@ static int usb_resume(struct device *dev)
                return 0;
        udev = to_usb_device(dev);
 
-       /* If autoresume is disabled then we also want to prevent resume
-        * during system wakeup.  However, a "persistent-device" reset-resume
-        * after power loss counts as a wakeup event.  So allow a
-        * reset-resume to occur if remote wakeup is enabled. */
-       if (udev->autoresume_disabled) {
+       /* If udev->skip_sys_resume is set then udev was already suspended
+        * when the system suspend started, so we don't want to resume
+        * udev during this system wakeup.  However a reset-resume counts
+        * as a wakeup event, so allow a reset-resume to occur if remote
+        * wakeup is enabled. */
+       if (udev->skip_sys_resume) {
                if (!(udev->reset_resume && udev->do_remote_wakeup))
-                       return -EPERM;
+                       return -EHOSTUNREACH;
        }
        return usb_external_resume_device(udev);
 }
index e0ec704..7dc123d 100644 (file)
@@ -267,7 +267,6 @@ static void ep_device_release(struct device *dev)
 {
        struct ep_device *ep_dev = to_ep_device(dev);
 
-       dev_dbg(dev, "%s called for %s\n", __FUNCTION__, dev->bus_id);
        endpoint_free_minor(ep_dev);
        kfree(ep_dev);
 }
index b2fc2b1..c1cb94e 100644 (file)
@@ -40,7 +40,7 @@ static int is_activesync(struct usb_interface_descriptor *desc)
                && desc->bInterfaceProtocol == 1;
 }
 
-static int choose_configuration(struct usb_device *udev)
+int usb_choose_configuration(struct usb_device *udev)
 {
        int i;
        int num_configs;
@@ -161,17 +161,20 @@ static int generic_probe(struct usb_device *udev)
        /* Choose and set the configuration.  This registers the interfaces
         * with the driver core and lets interface drivers bind to them.
         */
-       c = choose_configuration(udev);
-       if (c >= 0) {
-               err = usb_set_configuration(udev, c);
-               if (err) {
-                       dev_err(&udev->dev, "can't set config #%d, error %d\n",
+       if (udev->authorized == 0)
+               dev_err(&udev->dev, "Device is not authorized for usage\n");
+       else {
+               c = usb_choose_configuration(udev);
+               if (c >= 0) {
+                       err = usb_set_configuration(udev, c);
+                       if (err) {
+                               dev_err(&udev->dev, "can't set config #%d, error %d\n",
                                        c, err);
-                       /* This need not be fatal.  The user can try to
-                        * set other configurations. */
+                               /* This need not be fatal.  The user can try to
+                                * set other configurations. */
+                       }
                }
        }
-
        /* USB device state == configured ... usable */
        usb_notify_add_device(udev);
 
@@ -203,8 +206,13 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
         */
        if (!udev->parent)
                rc = hcd_bus_suspend(udev);
+
+       /* Non-root devices don't need to do anything for FREEZE or PRETHAW */
+       else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
+               rc = 0;
        else
                rc = usb_port_suspend(udev);
+
        return rc;
 }
 
index 42ef1d5..3dd997d 100644 (file)
@@ -356,10 +356,18 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
        const u8        *bufp = tbuf;
        int             len = 0;
        int             patch_wakeup = 0;
-       unsigned long   flags;
-       int             status = 0;
+       int             status;
        int             n;
 
+       might_sleep();
+
+       spin_lock_irq(&hcd_root_hub_lock);
+       status = usb_hcd_link_urb_to_ep(hcd, urb);
+       spin_unlock_irq(&hcd_root_hub_lock);
+       if (status)
+               return status;
+       urb->hcpriv = hcd;      /* Indicate it's queued */
+
        cmd = (struct usb_ctrlrequest *) urb->setup_packet;
        typeReq  = (cmd->bRequestType << 8) | cmd->bRequest;
        wValue   = le16_to_cpu (cmd->wValue);
@@ -523,13 +531,18 @@ error:
        }
 
        /* any errors get returned through the urb completion */
-       local_irq_save (flags);
-       spin_lock (&urb->lock);
-       if (urb->status == -EINPROGRESS)
-               urb->status = status;
-       spin_unlock (&urb->lock);
-       usb_hcd_giveback_urb (hcd, urb);
-       local_irq_restore (flags);
+       spin_lock_irq(&hcd_root_hub_lock);
+       usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+       /* This peculiar use of spinlocks echoes what real HC drivers do.
+        * Avoiding calls to local_irq_disable/enable makes the code
+        * RT-friendly.
+        */
+       spin_unlock(&hcd_root_hub_lock);
+       usb_hcd_giveback_urb(hcd, urb, status);
+       spin_lock(&hcd_root_hub_lock);
+
+       spin_unlock_irq(&hcd_root_hub_lock);
        return 0;
 }
 
@@ -559,31 +572,23 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
        if (length > 0) {
 
                /* try to complete the status urb */
-               local_irq_save (flags);
-               spin_lock(&hcd_root_hub_lock);
+               spin_lock_irqsave(&hcd_root_hub_lock, flags);
                urb = hcd->status_urb;
                if (urb) {
-                       spin_lock(&urb->lock);
-                       if (urb->status == -EINPROGRESS) {
-                               hcd->poll_pending = 0;
-                               hcd->status_urb = NULL;
-                               urb->status = 0;
-                               urb->hcpriv = NULL;
-                               urb->actual_length = length;
-                               memcpy(urb->transfer_buffer, buffer, length);
-                       } else          /* urb has been unlinked */
-                               length = 0;
-                       spin_unlock(&urb->lock);
-               } else
-                       length = 0;
-               spin_unlock(&hcd_root_hub_lock);
+                       hcd->poll_pending = 0;
+                       hcd->status_urb = NULL;
+                       urb->actual_length = length;
+                       memcpy(urb->transfer_buffer, buffer, length);
 
-               /* local irqs are always blocked in completions */
-               if (length > 0)
-                       usb_hcd_giveback_urb (hcd, urb);
-               else
+                       usb_hcd_unlink_urb_from_ep(hcd, urb);
+                       spin_unlock(&hcd_root_hub_lock);
+                       usb_hcd_giveback_urb(hcd, urb, 0);
+                       spin_lock(&hcd_root_hub_lock);
+               } else {
+                       length = 0;
                        hcd->poll_pending = 1;
-               local_irq_restore (flags);
+               }
+               spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
        }
 
        /* The USB 2.0 spec says 256 ms.  This is close enough and won't
@@ -611,33 +616,35 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
        int             len = 1 + (urb->dev->maxchild / 8);
 
        spin_lock_irqsave (&hcd_root_hub_lock, flags);
-       if (urb->status != -EINPROGRESS)        /* already unlinked */
-               retval = urb->status;
-       else if (hcd->status_urb || urb->transfer_buffer_length < len) {
+       if (hcd->status_urb || urb->transfer_buffer_length < len) {
                dev_dbg (hcd->self.controller, "not queuing rh status urb\n");
                retval = -EINVAL;
-       } else {
-               hcd->status_urb = urb;
-               urb->hcpriv = hcd;      /* indicate it's queued */
+               goto done;
+       }
 
-               if (!hcd->uses_new_polling)
-                       mod_timer (&hcd->rh_timer,
-                               (jiffies/(HZ/4) + 1) * (HZ/4));
+       retval = usb_hcd_link_urb_to_ep(hcd, urb);
+       if (retval)
+               goto done;
 
-               /* If a status change has already occurred, report it ASAP */
-               else if (hcd->poll_pending)
-                       mod_timer (&hcd->rh_timer, jiffies);
-               retval = 0;
-       }
+       hcd->status_urb = urb;
+       urb->hcpriv = hcd;      /* indicate it's queued */
+       if (!hcd->uses_new_polling)
+               mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
+
+       /* If a status change has already occurred, report it ASAP */
+       else if (hcd->poll_pending)
+               mod_timer(&hcd->rh_timer, jiffies);
+       retval = 0;
+ done:
        spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
        return retval;
 }
 
 static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
 {
-       if (usb_pipeint (urb->pipe))
+       if (usb_endpoint_xfer_int(&urb->ep->desc))
                return rh_queue_status (hcd, urb);
-       if (usb_pipecontrol (urb->pipe))
+       if (usb_endpoint_xfer_control(&urb->ep->desc))
                return rh_call_control (hcd, urb);
        return -EINVAL;
 }
@@ -647,32 +654,96 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
 /* Unlinks of root-hub control URBs are legal, but they don't do anything
  * since these URBs always execute synchronously.
  */
-static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 {
        unsigned long   flags;
+       int             rc;
+
+       spin_lock_irqsave(&hcd_root_hub_lock, flags);
+       rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+       if (rc)
+               goto done;
 
-       if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
+       if (usb_endpoint_num(&urb->ep->desc) == 0) {    /* Control URB */
                ;       /* Do nothing */
 
        } else {                                /* Status URB */
                if (!hcd->uses_new_polling)
                        del_timer (&hcd->rh_timer);
-               local_irq_save (flags);
-               spin_lock (&hcd_root_hub_lock);
                if (urb == hcd->status_urb) {
                        hcd->status_urb = NULL;
-                       urb->hcpriv = NULL;
-               } else
-                       urb = NULL;             /* wasn't fully queued */
-               spin_unlock (&hcd_root_hub_lock);
-               if (urb)
-                       usb_hcd_giveback_urb (hcd, urb);
-               local_irq_restore (flags);
+                       usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+                       spin_unlock(&hcd_root_hub_lock);
+                       usb_hcd_giveback_urb(hcd, urb, status);
+                       spin_lock(&hcd_root_hub_lock);
+               }
        }
+ done:
+       spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
+       return rc;
+}
 
-       return 0;
+
+
+/*
+ * Show & store the current value of authorized_default
+ */
+static ssize_t usb_host_authorized_default_show(struct device *dev,
+                                               struct device_attribute *attr,
+                                               char *buf)
+{
+       struct usb_device *rh_usb_dev = to_usb_device(dev);
+       struct usb_bus *usb_bus = rh_usb_dev->bus;
+       struct usb_hcd *usb_hcd;
+
+       if (usb_bus == NULL)    /* FIXME: not sure if this case is possible */
+               return -ENODEV;
+       usb_hcd = bus_to_hcd(usb_bus);
+       return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default);
+}
+
+static ssize_t usb_host_authorized_default_store(struct device *dev,
+                                                struct device_attribute *attr,
+                                                const char *buf, size_t size)
+{
+       ssize_t result;
+       unsigned val;
+       struct usb_device *rh_usb_dev = to_usb_device(dev);
+       struct usb_bus *usb_bus = rh_usb_dev->bus;
+       struct usb_hcd *usb_hcd;
+
+       if (usb_bus == NULL)    /* FIXME: not sure if this case is possible */
+               return -ENODEV;
+       usb_hcd = bus_to_hcd(usb_bus);
+       result = sscanf(buf, "%u\n", &val);
+       if (result == 1) {
+               usb_hcd->authorized_default = val? 1 : 0;
+               result = size;
+       }
+       else
+               result = -EINVAL;
+       return result;
 }
 
+static DEVICE_ATTR(authorized_default, 0644,
+           usb_host_authorized_default_show,
+           usb_host_authorized_default_store);
+
+
+/* Group all the USB bus attributes */
+static struct attribute *usb_bus_attrs[] = {
+               &dev_attr_authorized_default.attr,
+               NULL,
+};
+
+static struct attribute_group usb_bus_attr_group = {
+       .name = NULL,   /* we want them in the same directory */
+       .attrs = usb_bus_attrs,
+};
+
+
+
 /*-------------------------------------------------------------------------*/
 
 static struct class *usb_host_class;
@@ -726,27 +797,23 @@ static void usb_bus_init (struct usb_bus *bus)
  */
 static int usb_register_bus(struct usb_bus *bus)
 {
+       int result = -E2BIG;
        int busnum;
 
        mutex_lock(&usb_bus_list_lock);
        busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
-       if (busnum < USB_MAXBUS) {
-               set_bit (busnum, busmap.busmap);
-               bus->busnum = busnum;
-       } else {
+       if (busnum >= USB_MAXBUS) {
                printk (KERN_ERR "%s: too many buses\n", usbcore_name);
-               mutex_unlock(&usb_bus_list_lock);
-               return -E2BIG;
+               goto error_find_busnum;
        }
-
+       set_bit (busnum, busmap.busmap);
+       bus->busnum = busnum;
        bus->class_dev = class_device_create(usb_host_class, NULL, MKDEV(0,0),
-                                            bus->controller, "usb_host%d", busnum);
-       if (IS_ERR(bus->class_dev)) {
-               clear_bit(busnum, busmap.busmap);
-               mutex_unlock(&usb_bus_list_lock);
-               return PTR_ERR(bus->class_dev);
-       }
-
+                                            bus->controller, "usb_host%d",
+                                            busnum);
+       result = PTR_ERR(bus->class_dev);
+       if (IS_ERR(bus->class_dev))
+               goto error_create_class_dev;
        class_set_devdata(bus->class_dev, bus);
 
        /* Add it to the local list of buses */
@@ -755,8 +822,15 @@ static int usb_register_bus(struct usb_bus *bus)
 
        usb_notify_add_bus(bus);
 
-       dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);
+       dev_info (bus->controller, "new USB bus registered, assigned bus "
+                 "number %d\n", bus->busnum);
        return 0;
+
+error_create_class_dev:
+       clear_bit(busnum, busmap.busmap);
+error_find_busnum:
+       mutex_unlock(&usb_bus_list_lock);
+       return result;
 }
 
 /**
@@ -908,103 +982,145 @@ EXPORT_SYMBOL (usb_calc_bus_time);
 
 /*-------------------------------------------------------------------------*/
 
-static void urb_unlink(struct usb_hcd *hcd, struct urb *urb)
+/**
+ * usb_hcd_link_urb_to_ep - add an URB to its endpoint queue
+ * @hcd: host controller to which @urb was submitted
+ * @urb: URB being submitted
+ *
+ * Host controller drivers should call this routine in their enqueue()
+ * method.  The HCD's private spinlock must be held and interrupts must
+ * be disabled.  The actions carried out here are required for URB
+ * submission, as well as for endpoint shutdown and for usb_kill_urb.
+ *
+ * Returns 0 for no error, otherwise a negative error code (in which case
+ * the enqueue() method must fail).  If no error occurs but enqueue() fails
+ * anyway, it must call usb_hcd_unlink_urb_from_ep() before releasing
+ * the private spinlock and returning.
+ */
+int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
 {
-       unsigned long           flags;
+       int             rc = 0;
 
-       /* clear all state linking urb to this dev (and hcd) */
-       spin_lock_irqsave(&hcd_urb_list_lock, flags);
-       list_del_init (&urb->urb_list);
-       spin_unlock_irqrestore(&hcd_urb_list_lock, flags);
+       spin_lock(&hcd_urb_list_lock);
 
-       if (hcd->self.uses_dma && !is_root_hub(urb->dev)) {
-               if (usb_pipecontrol (urb->pipe)
-                       && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
-                       dma_unmap_single (hcd->self.controller, urb->setup_dma,
-                                       sizeof (struct usb_ctrlrequest),
-                                       DMA_TO_DEVICE);
-               if (urb->transfer_buffer_length != 0
-                       && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
-                       dma_unmap_single (hcd->self.controller,
-                                       urb->transfer_dma,
-                                       urb->transfer_buffer_length,
-                                       usb_pipein (urb->pipe)
-                                           ? DMA_FROM_DEVICE
-                                           : DMA_TO_DEVICE);
+       /* Check that the URB isn't being killed */
+       if (unlikely(urb->reject)) {
+               rc = -EPERM;
+               goto done;
        }
-}
-
-/* may be called in any context with a valid urb->dev usecount
- * caller surrenders "ownership" of urb
- * expects usb_submit_urb() to have sanity checked and conditioned all
- * inputs in the urb
- */
-int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
-{
-       int                     status;
-       struct usb_hcd          *hcd = bus_to_hcd(urb->dev->bus);
-       struct usb_host_endpoint *ep;
-       unsigned long           flags;
 
-       if (!hcd)
-               return -ENODEV;
+       if (unlikely(!urb->ep->enabled)) {
+               rc = -ENOENT;
+               goto done;
+       }
 
-       usbmon_urb_submit(&hcd->self, urb);
+       if (unlikely(!urb->dev->can_submit)) {
+               rc = -EHOSTUNREACH;
+               goto done;
+       }
 
        /*
-        * Atomically queue the urb,  first to our records, then to the HCD.
-        * Access to urb->status is controlled by urb->lock ... changes on
-        * i/o completion (normal or fault) or unlinking.
+        * Check the host controller's state and add the URB to the
+        * endpoint's queue.
         */
-
-       // FIXME:  verify that quiescing hc works right (RH cleans up)
-
-       spin_lock_irqsave(&hcd_urb_list_lock, flags);
-       ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)
-                       [usb_pipeendpoint(urb->pipe)];
-       if (unlikely (!ep))
-               status = -ENOENT;
-       else if (unlikely (urb->reject))
-               status = -EPERM;
-       else switch (hcd->state) {
+       switch (hcd->state) {
        case HC_STATE_RUNNING:
        case HC_STATE_RESUMING:
-               list_add_tail (&urb->urb_list, &ep->urb_list);
-               status = 0;
+               urb->unlinked = 0;
+               list_add_tail(&urb->urb_list, &urb->ep->urb_list);
                break;
        default:
-               status = -ESHUTDOWN;
-               break;
+               rc = -ESHUTDOWN;
+               goto done;
        }
-       spin_unlock_irqrestore(&hcd_urb_list_lock, flags);
-       if (status) {
-               INIT_LIST_HEAD (&urb->urb_list);
-               usbmon_urb_submit_error(&hcd->self, urb, status);
-               return status;
+ done:
+       spin_unlock(&hcd_urb_list_lock);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(usb_hcd_link_urb_to_ep);
+
+/**
+ * usb_hcd_check_unlink_urb - check whether an URB may be unlinked
+ * @hcd: host controller to which @urb was submitted
+ * @urb: URB being checked for unlinkability
+ * @status: error code to store in @urb if the unlink succeeds
+ *
+ * Host controller drivers should call this routine in their dequeue()
+ * method.  The HCD's private spinlock must be held and interrupts must
+ * be disabled.  The actions carried out here are required for making
+ * sure than an unlink is valid.
+ *
+ * Returns 0 for no error, otherwise a negative error code (in which case
+ * the dequeue() method must fail).  The possible error codes are:
+ *
+ *     -EIDRM: @urb was not submitted or has already completed.
+ *             The completion function may not have been called yet.
+ *
+ *     -EBUSY: @urb has already been unlinked.
+ */
+int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
+               int status)
+{
+       struct list_head        *tmp;
+
+       /* insist the urb is still queued */
+       list_for_each(tmp, &urb->ep->urb_list) {
+               if (tmp == &urb->urb_list)
+                       break;
        }
+       if (tmp != &urb->urb_list)
+               return -EIDRM;
 
-       /* increment urb's reference count as part of giving it to the HCD
-        * (which now controls it).  HCD guarantees that it either returns
-        * an error or calls giveback(), but not both.
+       /* Any status except -EINPROGRESS means something already started to
+        * unlink this URB from the hardware.  So there's no more work to do.
         */
-       urb = usb_get_urb (urb);
-       atomic_inc (&urb->use_count);
-
-       if (is_root_hub(urb->dev)) {
-               /* NOTE:  requirement on hub callers (usbfs and the hub
-                * driver, for now) that URBs' urb->transfer_buffer be
-                * valid and usb_buffer_{sync,unmap}() not be needed, since
-                * they could clobber root hub response data.
-                */
-               status = rh_urb_enqueue (hcd, urb);
-               goto done;
+       if (urb->unlinked)
+               return -EBUSY;
+       urb->unlinked = status;
+
+       /* IRQ setup can easily be broken so that USB controllers
+        * never get completion IRQs ... maybe even the ones we need to
+        * finish unlinking the initial failed usb_set_address()
+        * or device descriptor fetch.
+        */
+       if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) &&
+                       !is_root_hub(urb->dev)) {
+               dev_warn(hcd->self.controller, "Unlink after no-IRQ?  "
+                       "Controller is probably using the wrong IRQ.\n");
+               set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
        }
 
-       /* lower level hcd code should use *_dma exclusively,
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_hcd_check_unlink_urb);
+
+/**
+ * usb_hcd_unlink_urb_from_ep - remove an URB from its endpoint queue
+ * @hcd: host controller to which @urb was submitted
+ * @urb: URB being unlinked
+ *
+ * Host controller drivers should call this routine before calling
+ * usb_hcd_giveback_urb().  The HCD's private spinlock must be held and
+ * interrupts must be disabled.  The actions carried out here are required
+ * for URB completion.
+ */
+void usb_hcd_unlink_urb_from_ep(struct usb_hcd *hcd, struct urb *urb)
+{
+       /* clear all state linking urb to this dev (and hcd) */
+       spin_lock(&hcd_urb_list_lock);
+       list_del_init(&urb->urb_list);
+       spin_unlock(&hcd_urb_list_lock);
+}
+EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep);
+
+static void map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+       /* Map the URB's buffers for DMA access.
+        * Lower level HCD code should use *_dma exclusively,
         * unless it uses pio or talks to another transport.
         */
-       if (hcd->self.uses_dma) {
-               if (usb_pipecontrol (urb->pipe)
+       if (hcd->self.uses_dma && !is_root_hub(urb->dev)) {
+               if (usb_endpoint_xfer_control(&urb->ep->desc)
                        && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
                        urb->setup_dma = dma_map_single (
                                        hcd->self.controller,
@@ -1017,20 +1133,75 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
                                        hcd->self.controller,
                                        urb->transfer_buffer,
                                        urb->transfer_buffer_length,
-                                       usb_pipein (urb->pipe)
+                                       usb_urb_dir_in(urb)
                                            ? DMA_FROM_DEVICE
                                            : DMA_TO_DEVICE);
        }
+}
 
-       status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);
-done:
-       if (unlikely (status)) {
-               urb_unlink(hcd, urb);
-               atomic_dec (&urb->use_count);
-               if (urb->reject)
-                       wake_up (&usb_kill_urb_queue);
+static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+       if (hcd->self.uses_dma && !is_root_hub(urb->dev)) {
+               if (usb_endpoint_xfer_control(&urb->ep->desc)
+                       && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
+                       dma_unmap_single(hcd->self.controller, urb->setup_dma,
+                                       sizeof(struct usb_ctrlrequest),
+                                       DMA_TO_DEVICE);
+               if (urb->transfer_buffer_length != 0
+                       && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+                       dma_unmap_single(hcd->self.controller,
+                                       urb->transfer_dma,
+                                       urb->transfer_buffer_length,
+                                       usb_urb_dir_in(urb)
+                                           ? DMA_FROM_DEVICE
+                                           : DMA_TO_DEVICE);
+       }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* may be called in any context with a valid urb->dev usecount
+ * caller surrenders "ownership" of urb
+ * expects usb_submit_urb() to have sanity checked and conditioned all
+ * inputs in the urb
+ */
+int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
+{
+       int                     status;
+       struct usb_hcd          *hcd = bus_to_hcd(urb->dev->bus);
+
+       /* increment urb's reference count as part of giving it to the HCD
+        * (which will control it).  HCD guarantees that it either returns
+        * an error or calls giveback(), but not both.
+        */
+       usb_get_urb(urb);
+       atomic_inc(&urb->use_count);
+       atomic_inc(&urb->dev->urbnum);
+       usbmon_urb_submit(&hcd->self, urb);
+
+       /* NOTE requirements on root-hub callers (usbfs and the hub
+        * driver, for now):  URBs' urb->transfer_buffer must be
+        * valid and usb_buffer_{sync,unmap}() not be needed, since
+        * they could clobber root hub response data.  Also, control
+        * URBs must be submitted in process context with interrupts
+        * enabled.
+        */
+       map_urb_for_dma(hcd, urb);
+       if (is_root_hub(urb->dev))
+               status = rh_urb_enqueue(hcd, urb);
+       else
+               status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
+
+       if (unlikely(status)) {
                usbmon_urb_submit_error(&hcd->self, urb, status);
-               usb_put_urb (urb);
+               unmap_urb_for_dma(hcd, urb);
+               urb->hcpriv = NULL;
+               INIT_LIST_HEAD(&urb->urb_list);
+               atomic_dec(&urb->use_count);
+               atomic_dec(&urb->dev->urbnum);
+               if (urb->reject)
+                       wake_up(&usb_kill_urb_queue);
+               usb_put_urb(urb);
        }
        return status;
 }
@@ -1042,24 +1213,19 @@ done:
  * soon as practical.  we've already set up the urb's return status,
  * but we can't know if the callback completed already.
  */
-static int
-unlink1 (struct usb_hcd *hcd, struct urb *urb)
+static int unlink1(struct usb_hcd *hcd, struct urb *urb, int status)
 {
        int             value;
 
        if (is_root_hub(urb->dev))
-               value = usb_rh_urb_dequeue (hcd, urb);
+               value = usb_rh_urb_dequeue(hcd, urb, status);
        else {
 
                /* The only reason an HCD might fail this call is if
                 * it has not yet fully queued the urb to begin with.
                 * Such failures should be harmless. */
-               value = hcd->driver->urb_dequeue (hcd, urb);
+               value = hcd->driver->urb_dequeue(hcd, urb, status);
        }
-
-       if (value != 0)
-               dev_dbg (hcd->self.controller, "dequeue %p --> %d\n",
-                               urb, value);
        return value;
 }
 
@@ -1071,88 +1237,17 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
  */
 int usb_hcd_unlink_urb (struct urb *urb, int status)
 {
-       struct usb_host_endpoint        *ep;
-       struct usb_hcd                  *hcd = NULL;
-       struct device                   *sys = NULL;
-       unsigned long                   flags;
-       struct list_head                *tmp;
-       int                             retval;
-
-       if (!urb)
-               return -EINVAL;
-       if (!urb->dev || !urb->dev->bus)
-               return -ENODEV;
-       ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)
-                       [usb_pipeendpoint(urb->pipe)];
-       if (!ep)
-               return -ENODEV;
-
-       /*
-        * we contend for urb->status with the hcd core,
-        * which changes it while returning the urb.
-        *
-        * Caller guaranteed that the urb pointer hasn't been freed, and
-        * that it was submitted.  But as a rule it can't know whether or
-        * not it's already been unlinked ... so we respect the reversed
-        * lock sequence needed for the usb_hcd_giveback_urb() code paths
-        * (urb lock, then hcd_urb_list_lock) in case some other CPU is now
-        * unlinking it.
-        */
-       spin_lock_irqsave (&urb->lock, flags);
-       spin_lock(&hcd_urb_list_lock);
+       struct usb_hcd          *hcd;
+       int                     retval;
 
-       sys = &urb->dev->dev;
        hcd = bus_to_hcd(urb->dev->bus);
-       if (hcd == NULL) {
-               retval = -ENODEV;
-               goto done;
-       }
+       retval = unlink1(hcd, urb, status);
 
-       /* insist the urb is still queued */
-       list_for_each(tmp, &ep->urb_list) {
-               if (tmp == &urb->urb_list)
-                       break;
-       }
-       if (tmp != &urb->urb_list) {
-               retval = -EIDRM;
-               goto done;
-       }
-
-       /* Any status except -EINPROGRESS means something already started to
-        * unlink this URB from the hardware.  So there's no more work to do.
-        */
-       if (urb->status != -EINPROGRESS) {
-               retval = -EBUSY;
-               goto done;
-       }
-
-       /* IRQ setup can easily be broken so that USB controllers
-        * never get completion IRQs ... maybe even the ones we need to
-        * finish unlinking the initial failed usb_set_address()
-        * or device descriptor fetch.
-        */
-       if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) &&
-                       !is_root_hub(urb->dev)) {
-               dev_warn (hcd->self.controller, "Unlink after no-IRQ?  "
-                       "Controller is probably using the wrong IRQ.\n");
-               set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
-       }
-
-       urb->status = status;
-
-       spin_unlock(&hcd_urb_list_lock);
-       spin_unlock_irqrestore (&urb->lock, flags);
-
-       retval = unlink1 (hcd, urb);
        if (retval == 0)
                retval = -EINPROGRESS;
-       return retval;
-
-done:
-       spin_unlock(&hcd_urb_list_lock);
-       spin_unlock_irqrestore (&urb->lock, flags);
-       if (retval != -EIDRM && sys && sys->driver)
-               dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);
+       else if (retval != -EIDRM && retval != -EBUSY)
+               dev_dbg(&urb->dev->dev, "hcd_unlink_urb %p fail %d\n",
+                               urb, retval);
        return retval;
 }
 
@@ -1162,6 +1257,7 @@ done:
  * usb_hcd_giveback_urb - return URB from HCD to device driver
  * @hcd: host controller returning the URB
  * @urb: urb being returned to the USB device driver.
+ * @status: completion status code for the URB.
  * Context: in_interrupt()
  *
  * This hands the URB from HCD to its USB device driver, using its
@@ -1169,14 +1265,27 @@ done:
  * (and is done using urb->hcpriv).  It also released all HCD locks;
  * the device driver won't cause problems if it frees, modifies,
  * or resubmits this URB.
+ *
+ * If @urb was unlinked, the value of @status will be overridden by
+ * @urb->unlinked.  Erroneous short transfers are detected in case
+ * the HCD hasn't checked for them.
  */
-void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
+void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
 {
-       urb_unlink(hcd, urb);
-       usbmon_urb_complete (&hcd->self, urb);
+       urb->hcpriv = NULL;
+       if (unlikely(urb->unlinked))
+               status = urb->unlinked;
+       else if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
+                       urb->actual_length < urb->transfer_buffer_length &&
+                       !status))
+               status = -EREMOTEIO;
+
+       unmap_urb_for_dma(hcd, urb);
+       usbmon_urb_complete(&hcd->self, urb, status);
        usb_unanchor_urb(urb);
 
        /* pass ownership to the completion handler */
+       urb->status = status;
        urb->complete (urb);
        atomic_dec (&urb->use_count);
        if (unlikely (urb->reject))
@@ -1187,78 +1296,61 @@ EXPORT_SYMBOL (usb_hcd_giveback_urb);
 
 /*-------------------------------------------------------------------------*/
 
-/* disables the endpoint: cancels any pending urbs, then synchronizes with
- * the hcd to make sure all endpoint state is gone from hardware, and then
- * waits until the endpoint's queue is completely drained. use for
- * set_configuration, set_interface, driver removal, physical disconnect.
- *
- * example:  a qh stored in ep->hcpriv, holding state related to endpoint
- * type, maxpacket size, toggle, halt status, and scheduling.
+/* Cancel all URBs pending on this endpoint and wait for the endpoint's
+ * queue to drain completely.  The caller must first insure that no more
+ * URBs can be submitted for this endpoint.
  */
-void usb_hcd_endpoint_disable (struct usb_device *udev,
+void usb_hcd_flush_endpoint(struct usb_device *udev,
                struct usb_host_endpoint *ep)
 {
        struct usb_hcd          *hcd;
        struct urb              *urb;
 
+       if (!ep)
+               return;
+       might_sleep();
        hcd = bus_to_hcd(udev->bus);
-       local_irq_disable ();
 
-       /* ep is already gone from udev->ep_{in,out}[]; no more submits */
+       /* No more submits can occur */
 rescan:
-       spin_lock(&hcd_urb_list_lock);
+       spin_lock_irq(&hcd_urb_list_lock);
        list_for_each_entry (urb, &ep->urb_list, urb_list) {
-               int     tmp;
+               int     is_in;
 
-               /* the urb may already have been unlinked */
-               if (urb->status != -EINPROGRESS)
+               if (urb->unlinked)
                        continue;
                usb_get_urb (urb);
+               is_in = usb_urb_dir_in(urb);
                spin_unlock(&hcd_urb_list_lock);
 
-               spin_lock (&urb->lock);
-               tmp = urb->status;
-               if (tmp == -EINPROGRESS)
-                       urb->status = -ESHUTDOWN;
-               spin_unlock (&urb->lock);
-
-               /* kick hcd unless it's already returning this */
-               if (tmp == -EINPROGRESS) {
-                       tmp = urb->pipe;
-                       unlink1 (hcd, urb);
-                       dev_dbg (hcd->self.controller,
-                               "shutdown urb %p pipe %08x ep%d%s%s\n",
-                               urb, tmp, usb_pipeendpoint (tmp),
-                               (tmp & USB_DIR_IN) ? "in" : "out",
-                               ({ char *s; \
-                                switch (usb_pipetype (tmp)) { \
-                                case PIPE_CONTROL:     s = ""; break; \
-                                case PIPE_BULK:        s = "-bulk"; break; \
-                                case PIPE_INTERRUPT:   s = "-intr"; break; \
-                                default:               s = "-iso"; break; \
-                               }; s;}));
-               }
+               /* kick hcd */
+               unlink1(hcd, urb, -ESHUTDOWN);
+               dev_dbg (hcd->self.controller,
+                       "shutdown urb %p ep%d%s%s\n",
+                       urb, usb_endpoint_num(&ep->desc),
+                       is_in ? "in" : "out",
+                       ({      char *s;
+
+                                switch (usb_endpoint_type(&ep->desc)) {
+                                case USB_ENDPOINT_XFER_CONTROL:
+                                       s = ""; break;
+                                case USB_ENDPOINT_XFER_BULK:
+                                       s = "-bulk"; break;
+                                case USB_ENDPOINT_XFER_INT:
+                                       s = "-intr"; break;
+                                default:
+                                       s = "-iso"; break;
+                               };
+                               s;
+                       }));
                usb_put_urb (urb);
 
                /* list contents may have changed */
                goto rescan;
        }
-       spin_unlock(&hcd_urb_list_lock);
-       local_irq_enable ();
-
-       /* synchronize with the hardware, so old configuration state
-        * clears out immediately (and will be freed).
-        */
-       might_sleep ();
-       if (hcd->driver->endpoint_disable)
-               hcd->driver->endpoint_disable (hcd, ep);
+       spin_unlock_irq(&hcd_urb_list_lock);
 
-       /* Wait until the endpoint queue is completely empty.  Most HCDs
-        * will have done this already in their endpoint_disable method,
-        * but some might not.  And there could be root-hub control URBs
-        * still pending since they aren't affected by the HCDs'
-        * endpoint_disable methods.
-        */
+       /* Wait until the endpoint queue is completely empty */
        while (!list_empty (&ep->urb_list)) {
                spin_lock_irq(&hcd_urb_list_lock);
 
@@ -1278,6 +1370,25 @@ rescan:
        }
 }
 
+/* Disables the endpoint: synchronizes with the hcd to make sure all
+ * endpoint state is gone from hardware.  usb_hcd_flush_endpoint() must
+ * have been called previously.  Use for set_configuration, set_interface,
+ * driver removal, physical disconnect.
+ *
+ * example:  a qh stored in ep->hcpriv, holding state related to endpoint
+ * type, maxpacket size, toggle, halt status, and scheduling.
+ */
+void usb_hcd_disable_endpoint(struct usb_device *udev,
+               struct usb_host_endpoint *ep)
+{
+       struct usb_hcd          *hcd;
+
+       might_sleep();
+       hcd = bus_to_hcd(udev->bus);
+       if (hcd->driver->endpoint_disable)
+               hcd->driver->endpoint_disable(hcd, ep);
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* called in any context */
@@ -1525,7 +1636,6 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
        hcd->driver = driver;
        hcd->product_desc = (driver->product_desc) ? driver->product_desc :
                        "USB Host Controller";
-
        return hcd;
 }
 EXPORT_SYMBOL (usb_create_hcd);
@@ -1570,6 +1680,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
 
        dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
 
+       hcd->authorized_default = hcd->wireless? 0 : 1;
        set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
 
        /* HC is in reset state, but accessible.  Now do the one-time init,
@@ -1646,10 +1757,20 @@ int usb_add_hcd(struct usb_hcd *hcd,
        if ((retval = register_root_hub(hcd)) != 0)
                goto err_register_root_hub;
 
+       retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);
+       if (retval < 0) {
+               printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n",
+                      retval);
+               goto error_create_attr_group;
+       }
        if (hcd->uses_new_polling && hcd->poll_rh)
                usb_hcd_poll_rh_status(hcd);
        return retval;
 
+error_create_attr_group:
+       mutex_lock(&usb_bus_list_lock);
+       usb_disconnect(&hcd->self.root_hub);
+       mutex_unlock(&usb_bus_list_lock);
 err_register_root_hub:
        hcd->driver->stop(hcd);
 err_hcd_driver_start:
@@ -1691,6 +1812,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
        cancel_work_sync(&hcd->wakeup_work);
 #endif
 
+       sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group);
        mutex_lock(&usb_bus_list_lock);
        usb_disconnect(&hcd->self.root_hub);
        mutex_unlock(&usb_bus_list_lock);
index b5ebb73..98e2419 100644 (file)
@@ -19,6 +19,8 @@
 
 #ifdef __KERNEL__
 
+#include <linux/rwsem.h>
+
 /* This file contains declarations of usbcore internals that are mostly
  * used or exposed by Host Controller Drivers.
  */
  *
  * Since "struct usb_bus" is so thin, you can't share much code in it.
  * This framework is a layer over that, and should be more sharable.
+ *
+ * @authorized_default: Specifies if new devices are authorized to
+ *                      connect by default or they require explicit
+ *                      user space authorization; this bit is settable
+ *                      through /sys/class/usb_host/X/authorized_default.
+ *                      For the rest is RO, so we don't lock to r/w it.
  */
 
 /*-------------------------------------------------------------------------*/
@@ -90,6 +98,7 @@ struct usb_hcd {
        unsigned                poll_rh:1;      /* poll for rh status? */
        unsigned                poll_pending:1; /* status has changed? */
        unsigned                wireless:1;     /* Wireless USB HCD */
+       unsigned                authorized_default:1;
 
        int                     irq;            /* irq allocated */
        void __iomem            *regs;          /* device memory/io */
@@ -182,11 +191,10 @@ struct hc_driver {
        int     (*get_frame_number) (struct usb_hcd *hcd);
 
        /* manage i/o requests, device state */
-       int     (*urb_enqueue) (struct usb_hcd *hcd,
-                                       struct usb_host_endpoint *ep,
-                                       struct urb *urb,
-                                       gfp_t mem_flags);
-       int     (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
+       int     (*urb_enqueue)(struct usb_hcd *hcd,
+                               struct urb *urb, gfp_t mem_flags);
+       int     (*urb_dequeue)(struct usb_hcd *hcd,
+                               struct urb *urb, int status);
 
        /* hw synch, freeing endpoint resources that urb_dequeue can't */
        void    (*endpoint_disable)(struct usb_hcd *hcd,
@@ -204,10 +212,18 @@ struct hc_driver {
                /* Needed only if port-change IRQs are level-triggered */
 };
 
+extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
+extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
+               int status);
+extern void usb_hcd_unlink_urb_from_ep(struct usb_hcd *hcd, struct urb *urb);
+
 extern int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags);
 extern int usb_hcd_unlink_urb (struct urb *urb, int status);
-extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb);
-extern void usb_hcd_endpoint_disable (struct usb_device *udev,
+extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb,
+               int status);
+extern void usb_hcd_flush_endpoint(struct usb_device *udev,
+               struct usb_host_endpoint *ep);
+extern void usb_hcd_disable_endpoint(struct usb_device *udev,
                struct usb_host_endpoint *ep);
 extern int usb_hcd_get_frame_number (struct usb_device *udev);
 
@@ -402,7 +418,7 @@ static inline void usbfs_cleanup(void) { }
 struct usb_mon_operations {
        void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
        void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err);
-       void (*urb_complete)(struct usb_bus *bus, struct urb *urb);
+       void (*urb_complete)(struct usb_bus *bus, struct urb *urb, int status);
        /* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
 };
 
@@ -421,10 +437,11 @@ static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
                (*mon_ops->urb_submit_error)(bus, urb, error);
 }
 
-static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb)
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
+               int status)
 {
        if (bus->monitored)
-               (*mon_ops->urb_complete)(bus, urb);
+               (*mon_ops->urb_complete)(bus, urb, status);
 }
 
 int usb_mon_register(struct usb_mon_operations *ops);
@@ -435,7 +452,8 @@ void usb_mon_deregister(void);
 static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {}
 static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
     int error) {}
-static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
+               int status) {}
 
 #endif /* CONFIG_USB_MON */
 
@@ -454,5 +472,9 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
                : (in_interrupt () ? "in_interrupt" : "can sleep"))
 
 
-#endif /* __KERNEL__ */
+/* This rwsem is for use only by the hub driver and ehci-hcd.
+ * Nobody else should touch it.
+ */
+extern struct rw_semaphore ehci_cf_port_reset_rwsem;
 
+#endif /* __KERNEL__ */
index f7b337f..d20cb54 100644 (file)
@@ -125,6 +125,12 @@ MODULE_PARM_DESC(use_both_schemes,
                "try the other device initialization scheme if the "
                "first one fails");
 
+/* Mutual exclusion for EHCI CF initialization.  This interferes with
+ * port reset on some companion controllers.
+ */
+DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
+EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
+
 
 static inline char *portspeed(int portstatus)
 {
@@ -347,11 +353,11 @@ void usb_kick_khubd(struct usb_device *hdev)
 static void hub_irq(struct urb *urb)
 {
        struct usb_hub *hub = urb->context;
-       int status;
+       int status = urb->status;
        int i;
        unsigned long bits;
 
-       switch (urb->status) {
+       switch (status) {
        case -ENOENT:           /* synchronous unlink */
        case -ECONNRESET:       /* async unlink */
        case -ESHUTDOWN:        /* hardware going away */
@@ -359,10 +365,10 @@ static void hub_irq(struct urb *urb)
 
        default:                /* presumably an error */
                /* Cause a hub reset after 10 consecutive errors */
-               dev_dbg (hub->intfdev, "transfer --> %d\n", urb->status);
+               dev_dbg (hub->intfdev, "transfer --> %d\n", status);
                if ((++hub->nerrors < 10) || hub->error)
                        goto resubmit;
-               hub->error = urb->status;
+               hub->error = status;
                /* FALL THROUGH */
 
        /* let khubd handle things */
@@ -1220,54 +1226,14 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
 #endif
 
 /**
- * usb_new_device - perform initial device setup (usbcore-internal)
+ * usb_configure_device_otg - FIXME (usbcore-internal)
  * @udev: newly addressed device (in ADDRESS state)
  *
- * This is called with devices which have been enumerated, but not yet
- * configured.  The device descriptor is available, but not descriptors
- * for any device configuration.  The caller must have locked either
- * the parent hub (if udev is a normal device) or else the
- * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to
- * udev has already been installed, but udev is not yet visible through
- * sysfs or other filesystem code.
- *
- * It will return if the device is configured properly or not.  Zero if
- * the interface was registered with the driver core; else a negative
- * errno value.
- *
- * This call is synchronous, and may not be used in an interrupt context.
- *
- * Only the hub driver or root-hub registrar should ever call this.
+ * Do configuration for On-The-Go devices
  */
-int usb_new_device(struct usb_device *udev)
+static int usb_configure_device_otg(struct usb_device *udev)
 {
-       int err;
-
-       /* Determine quirks */
-       usb_detect_quirks(udev);
-
-       err = usb_get_configuration(udev);
-       if (err < 0) {
-               dev_err(&udev->dev, "can't read configurations, error %d\n",
-                       err);
-               goto fail;
-       }
-
-       /* read the standard strings and cache them if present */
-       udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
-       udev->manufacturer = usb_cache_string(udev,
-                       udev->descriptor.iManufacturer);
-       udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
-
-       /* Tell the world! */
-       dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "
-                       "SerialNumber=%d\n",
-                       udev->descriptor.iManufacturer,
-                       udev->descriptor.iProduct,
-                       udev->descriptor.iSerialNumber);
-       show_string(udev, "Product", udev->product);
-       show_string(udev, "Manufacturer", udev->manufacturer);
-       show_string(udev, "SerialNumber", udev->serial);
+       int err = 0;
 
 #ifdef CONFIG_USB_OTG
        /*
@@ -1329,8 +1295,82 @@ int usb_new_device(struct usb_device *udev)
                err = -ENOTSUPP;
                goto fail;
        }
+fail:
 #endif
+       return err;
+}
+
+
+/**
+ * usb_configure_device - Detect and probe device intfs/otg (usbcore-internal)
+ * @udev: newly addressed device (in ADDRESS state)
+ *
+ * This is only called by usb_new_device() and usb_authorize_device()
+ * and FIXME -- all comments that apply to them apply here wrt to
+ * environment.
+ *
+ * If the device is WUSB and not authorized, we don't attempt to read
+ * the string descriptors, as they will be errored out by the device
+ * until it has been authorized.
+ */
+static int usb_configure_device(struct usb_device *udev)
+{
+       int err;
 
+       if (udev->config == NULL) {
+               err = usb_get_configuration(udev);
+               if (err < 0) {
+                       dev_err(&udev->dev, "can't read configurations, error %d\n",
+                               err);
+                       goto fail;
+               }
+       }
+       if (udev->wusb == 1 && udev->authorized == 0) {
+               udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+               udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+               udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+       }
+       else {
+               /* read the standard strings and cache them if present */
+               udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
+               udev->manufacturer = usb_cache_string(udev,
+                                                     udev->descriptor.iManufacturer);
+               udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
+       }
+       err = usb_configure_device_otg(udev);
+fail:
+       return err;
+}
+
+
+/**
+ * usb_new_device - perform initial device setup (usbcore-internal)
+ * @udev: newly addressed device (in ADDRESS state)
+ *
+ * This is called with devices which have been enumerated, but not yet
+ * configured.  The device descriptor is available, but not descriptors
+ * for any device configuration.  The caller must have locked either
+ * the parent hub (if udev is a normal device) or else the
+ * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to
+ * udev has already been installed, but udev is not yet visible through
+ * sysfs or other filesystem code.
+ *
+ * It will return if the device is configured properly or not.  Zero if
+ * the interface was registered with the driver core; else a negative
+ * errno value.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Only the hub driver or root-hub registrar should ever call this.
+ */
+int usb_new_device(struct usb_device *udev)
+{
+       int err;
+
+       usb_detect_quirks(udev);                /* Determine quirks */
+       err = usb_configure_device(udev);       /* detect & probe dev/intfs */
+       if (err < 0)
+               goto fail;
        /* export the usbdev device-node for libusb */
        udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
                        (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
@@ -1346,19 +1386,106 @@ int usb_new_device(struct usb_device *udev)
        err = device_add(&udev->dev);
        if (err) {
                dev_err(&udev->dev, "can't device_add, error %d\n", err);
-               if (udev->parent)
-                       usb_autosuspend_device(udev->parent);
                goto fail;
        }
 
-exit:
+       /* Tell the world! */
+       dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "
+               "SerialNumber=%d\n",
+               udev->descriptor.iManufacturer,
+               udev->descriptor.iProduct,
+               udev->descriptor.iSerialNumber);
+       show_string(udev, "Product", udev->product);
+       show_string(udev, "Manufacturer", udev->manufacturer);
+       show_string(udev, "SerialNumber", udev->serial);
        return err;
 
 fail:
        usb_set_device_state(udev, USB_STATE_NOTATTACHED);
-       goto exit;
+       return err;
 }
 
+
+/**
+ * Similar to usb_disconnect()
+ *
+ * We share a lock (that we have) with device_del(), so we need to
+ * defer its call.
+ */
+int usb_deauthorize_device(struct usb_device *usb_dev)
+{
+       unsigned cnt;
+       usb_lock_device(usb_dev);
+       if (usb_dev->authorized == 0)
+               goto out_unauthorized;
+       usb_dev->authorized = 0;
+       usb_set_configuration(usb_dev, -1);
+       usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+       usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+       usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
+       kfree(usb_dev->config);
+       usb_dev->config = NULL;
+       for (cnt = 0; cnt < usb_dev->descriptor.bNumConfigurations; cnt++)
+               kfree(usb_dev->rawdescriptors[cnt]);
+       usb_dev->descriptor.bNumConfigurations = 0;
+       kfree(usb_dev->rawdescriptors);
+out_unauthorized:
+       usb_unlock_device(usb_dev);
+       return 0;
+}
+
+
+int usb_authorize_device(struct usb_device *usb_dev)
+{
+       int result = 0, c;
+       usb_lock_device(usb_dev);
+       if (usb_dev->authorized == 1)
+               goto out_authorized;
+       kfree(usb_dev->product);
+       usb_dev->product = NULL;
+       kfree(usb_dev->manufacturer);
+       usb_dev->manufacturer = NULL;
+       kfree(usb_dev->serial);
+       usb_dev->serial = NULL;
+       result = usb_autoresume_device(usb_dev);
+       if (result < 0) {
+               dev_err(&usb_dev->dev,
+                       "can't autoresume for authorization: %d\n", result);
+               goto error_autoresume;
+       }
+       result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
+       if (result < 0) {
+               dev_err(&usb_dev->dev, "can't re-read device descriptor for "
+                       "authorization: %d\n", result);
+               goto error_device_descriptor;
+       }
+       usb_dev->authorized = 1;
+       result = usb_configure_device(usb_dev);
+       if (result < 0)
+               goto error_configure;
+       /* Choose and set the configuration.  This registers the interfaces
+        * with the driver core and lets interface drivers bind to them.
+        */
+       c = usb_choose_configuration(usb_dev);
+       if (c >= 0) {
+               result = usb_set_configuration(usb_dev, c);
+               if (result) {
+                       dev_err(&usb_dev->dev,
+                               "can't set config #%d, error %d\n", c, result);
+                       /* This need not be fatal.  The user can try to
+                        * set other configurations. */
+               }
+       }
+       dev_info(&usb_dev->dev, "authorized to connect\n");
+error_configure:
+error_device_descriptor:
+error_autoresume:
+out_authorized:
+       usb_unlock_device(usb_dev);     // complements locktree
+       return result;
+}
+
+
 static int hub_port_status(struct usb_hub *hub, int port1,
                               u16 *status, u16 *change)
 {
@@ -1460,6 +1587,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
 {
        int i, status;
 
+       /* Block EHCI CF initialization during the port reset.
+        * Some companion controllers don't like it when they mix.
+        */
+       down_read(&ehci_cf_port_reset_rwsem);
+
        /* Reset the port */
        for (i = 0; i < PORT_RESET_TRIES; i++) {
                status = set_port_feature(hub->hdev,
@@ -1481,6 +1613,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                case 0:
                        /* TRSTRCY = 10 ms; plus some extra */
                        msleep(10 + 40);
+                       udev->devnum = 0;       /* Device now at address 0 */
                        /* FALL THROUGH */
                case -ENOTCONN:
                case -ENODEV:
@@ -1490,7 +1623,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                        usb_set_device_state(udev, status
                                        ? USB_STATE_NOTATTACHED
                                        : USB_STATE_DEFAULT);
-                       return status;
+                       goto done;
                }
 
                dev_dbg (hub->intfdev,
@@ -1503,6 +1636,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                "Cannot enable port %i.  Maybe the USB cable is bad?\n",
                port1);
 
+ done:
+       up_read(&ehci_cf_port_reset_rwsem);
        return status;
 }
 
@@ -1833,14 +1968,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
                struct usb_device       *udev;
 
                udev = hdev->children [port1-1];
-               if (udev && msg.event == PM_EVENT_SUSPEND &&
-#ifdef CONFIG_USB_SUSPEND
-                               udev->state != USB_STATE_SUSPENDED
-#else
-                               udev->dev.power.power_state.event
-                                       == PM_EVENT_ON
-#endif
-                               ) {
+               if (udev && udev->can_submit) {
                        if (!hdev->auto_pm)
                                dev_dbg(&intf->dev, "port %d nyet suspended\n",
                                                port1);
@@ -1999,26 +2127,27 @@ static void ep0_reinit(struct usb_device *udev)
 {
        usb_disable_endpoint(udev, 0 + USB_DIR_IN);
        usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
-       udev->ep_in[0] = udev->ep_out[0] = &udev->ep0;
+       usb_enable_endpoint(udev, &udev->ep0);
 }
 
 #define usb_sndaddr0pipe()     (PIPE_CONTROL << 30)
 #define usb_rcvaddr0pipe()     ((PIPE_CONTROL << 30) | USB_DIR_IN)
 
-static int hub_set_address(struct usb_device *udev)
+static int hub_set_address(struct usb_device *udev, int devnum)
 {
        int retval;
 
-       if (udev->devnum == 0)
+       if (devnum <= 1)
                return -EINVAL;
        if (udev->state == USB_STATE_ADDRESS)
                return 0;
        if (udev->state != USB_STATE_DEFAULT)
                return -EINVAL;
        retval = usb_control_msg(udev, usb_sndaddr0pipe(),
-               USB_REQ_SET_ADDRESS, 0, udev->devnum, 0,
+               USB_REQ_SET_ADDRESS, 0, devnum, 0,
                NULL, 0, USB_CTRL_SET_TIMEOUT);
        if (retval == 0) {
+               udev->devnum = devnum;  /* Device now using proper address */
                usb_set_device_state(udev, USB_STATE_ADDRESS);
                ep0_reinit(udev);
        }
@@ -2045,6 +2174,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        unsigned                delay = HUB_SHORT_RESET_TIME;
        enum usb_device_speed   oldspeed = udev->speed;
        char                    *speed, *type;
+       int                     devnum = udev->devnum;
 
        /* root hub ports have a slightly longer reset period
         * (from USB 2.0 spec, section 7.1.7.5)
@@ -2074,7 +2204,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                goto fail;
        }
        oldspeed = udev->speed;
-  
+
        /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
         * it's fixed size except for full speed devices.
         * For Wireless USB devices, ep0 max packet is always 512 (tho
@@ -2115,7 +2245,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        dev_info (&udev->dev,
                  "%s %s speed %sUSB device using %s and address %d\n",
                  (udev->config) ? "reset" : "new", speed, type,
-                 udev->bus->controller->driver->name, udev->devnum);
+                 udev->bus->controller->driver->name, devnum);
 
        /* Set up TT records, if needed  */
        if (hdev->tt) {
@@ -2202,7 +2332,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                }
 
                for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
-                       retval = hub_set_address(udev);
+                       retval = hub_set_address(udev, devnum);
                        if (retval >= 0)
                                break;
                        msleep(200);
@@ -2210,7 +2340,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                if (retval < 0) {
                        dev_err(&udev->dev,
                                "device not accepting address %d, error %d\n",
-                               udev->devnum, retval);
+                               devnum, retval);
                        goto fail;
                }
  
@@ -2263,8 +2393,10 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        retval = 0;
 
 fail:
-       if (retval)
+       if (retval) {
                hub_port_disable(hub, port1, 0);
+               udev->devnum = devnum;  /* for disconnect processing */
+       }
        mutex_unlock(&usb_address0_mutex);
        return retval;
 }
@@ -2699,9 +2831,9 @@ static void hub_events(void)
                                clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
                                if (hubstatus & HUB_STATUS_LOCAL_POWER)
                                        /* FIXME: Is this always true? */
-                                       hub->limited_power = 0;
-                               else
                                        hub->limited_power = 1;
+                               else
+                                       hub->limited_power = 0;
                        }
                        if (hubchange & HUB_CHANGE_OVERCURRENT) {
                                dev_dbg (hub_dev, "overcurrent change\n");
index d8f7b08..98fcddb 100644 (file)
@@ -59,8 +59,8 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
                dev_dbg(&urb->dev->dev,
                        "%s timed out on ep%d%s len=%d/%d\n",
                        current->comm,
-                       usb_pipeendpoint(urb->pipe),
-                       usb_pipein(urb->pipe) ? "in" : "out",
+                       usb_endpoint_num(&urb->ep->desc),
+                       usb_urb_dir_in(urb) ? "in" : "out",
                        urb->actual_length,
                        urb->transfer_buffer_length);
        } else
@@ -250,7 +250,8 @@ static void sg_clean (struct usb_sg_request *io)
                io->urbs = NULL;
        }
        if (io->dev->dev.dma_mask != NULL)
-               usb_buffer_unmap_sg (io->dev, io->pipe, io->sg, io->nents);
+               usb_buffer_unmap_sg (io->dev, usb_pipein(io->pipe),
+                               io->sg, io->nents);
        io->dev = NULL;
 }
 
@@ -278,8 +279,8 @@ static void sg_complete (struct urb *urb)
                dev_err (io->dev->bus->controller,
                        "dev %s ep%d%s scatterlist error %d/%d\n",
                        io->dev->devpath,
-                       usb_pipeendpoint (urb->pipe),
-                       usb_pipein (urb->pipe) ? "in" : "out",
+                       usb_endpoint_num(&urb->ep->desc),
+                       usb_urb_dir_in(urb) ? "in" : "out",
                        status, io->status);
                // BUG ();
        }
@@ -379,7 +380,8 @@ int usb_sg_init (
         */
        dma = (dev->dev.dma_mask != NULL);
        if (dma)
-               io->entries = usb_buffer_map_sg (dev, pipe, sg, nents);
+               io->entries = usb_buffer_map_sg(dev, usb_pipein(pipe),
+                               sg, nents);
        else
                io->entries = nents;
 
@@ -1013,8 +1015,11 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
                ep = dev->ep_in[epnum];
                dev->ep_in[epnum] = NULL;
        }
-       if (ep && dev->bus)
-               usb_hcd_endpoint_disable(dev, ep);
+       if (ep) {
+               ep->enabled = 0;
+               usb_hcd_flush_endpoint(dev, ep);
+               usb_hcd_disable_endpoint(dev, ep);
+       }
 }
 
 /**
@@ -1096,23 +1101,21 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
  * Resets the endpoint toggle, and sets dev->ep_{in,out} pointers.
  * For control endpoints, both the input and output sides are handled.
  */
-static void
-usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
+void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
 {
-       unsigned int epaddr = ep->desc.bEndpointAddress;
-       unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
-       int is_control;
+       int epnum = usb_endpoint_num(&ep->desc);
+       int is_out = usb_endpoint_dir_out(&ep->desc);
+       int is_control = usb_endpoint_xfer_control(&ep->desc);
 
-       is_control = ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                       == USB_ENDPOINT_XFER_CONTROL);
-       if (usb_endpoint_out(epaddr) || is_control) {
+       if (is_out || is_control) {
                usb_settoggle(dev, epnum, 1, 0);
                dev->ep_out[epnum] = ep;
        }
-       if (!usb_endpoint_out(epaddr) || is_control) {
+       if (!is_out || is_control) {
                usb_settoggle(dev, epnum, 0, 0);
                dev->ep_in[epnum] = ep;
        }
+       ep->enabled = 1;
 }
 
 /*
@@ -1171,6 +1174,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
        struct usb_host_interface *alt;
        int ret;
        int manual = 0;
+       int changed;
 
        if (dev->state == USB_STATE_SUSPENDED)
                return -EHOSTUNREACH;
@@ -1210,7 +1214,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
         */
 
        /* prevent submissions using previous endpoint settings */
-       if (device_is_registered(&iface->dev))
+       changed = (iface->cur_altsetting != alt);
+       if (changed && device_is_registered(&iface->dev))
                usb_remove_sysfs_intf_files(iface);
        usb_disable_interface(dev, iface);
 
@@ -1247,7 +1252,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
         * (Likewise, EP0 never "halts" on well designed devices.)
         */
        usb_enable_interface(dev, iface);
-       if (device_is_registered(&iface->dev))
+       if (changed && device_is_registered(&iface->dev))
                usb_create_sysfs_intf_files(iface);
 
        return 0;
@@ -1328,7 +1333,7 @@ int usb_reset_configuration(struct usb_device *dev)
        return 0;
 }
 
-void usb_release_interface(struct device *dev)
+static void usb_release_interface(struct device *dev)
 {
        struct usb_interface *intf = to_usb_interface(dev);
        struct usb_interface_cache *intfc =
@@ -1481,6 +1486,9 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
  * channels are available independently; and choosing between open
  * standard device protocols (like CDC) or proprietary ones.
  *
+ * Note that a non-authorized device (dev->authorized == 0) will only
+ * be put in unconfigured mode.
+ *
  * Note that USB has an additional level of device configurability,
  * associated with interfaces.  That configurability is accessed using
  * usb_set_interface().
@@ -1502,7 +1510,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
        struct usb_interface **new_interfaces = NULL;
        int n, nintf;
 
-       if (configuration == -1)
+       if (dev->authorized == 0 || configuration == -1)
                configuration = 0;
        else {
                for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
index ebf3dc2..d42c561 100644 (file)
@@ -32,52 +32,6 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME },
        /* HP 5300/5370C scanner */
        { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 },
-       /* Hewlett-Packard PhotoSmart 720 / PhotoSmart 935 (storage) */
-       { USB_DEVICE(0x03f0, 0x4002), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-
-       /* SGS Thomson Microelectronics 4in1 card reader */
-       { USB_DEVICE(0x0483, 0x0321), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-
-       /* Acer Peripherals Inc. (now BenQ Corp.) Prisa 640BU */
-       { USB_DEVICE(0x04a5, 0x207e), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Benq S2W 3300U */
-       { USB_DEVICE(0x04a5, 0x20b0), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Canon, Inc. CanoScan N1240U/LiDE30 */
-       { USB_DEVICE(0x04a9, 0x220e), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Canon, Inc. CanoScan N650U/N656U */
-       { USB_DEVICE(0x04a9, 0x2206), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Canon, Inc. CanoScan 1220U */
-       { USB_DEVICE(0x04a9, 0x2207), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Canon, Inc. CanoScan N670U/N676U/LiDE 20 */
-       { USB_DEVICE(0x04a9, 0x220d), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* old Cannon scanner */
-       { USB_DEVICE(0x04a9, 0x2220), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Seiko Epson Corp. Perfection 1200 */
-       { USB_DEVICE(0x04b8, 0x0104), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Seiko Epson Corp. Perfection 660 */
-       { USB_DEVICE(0x04b8, 0x0114), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Epson Perfection 1260 Photo */
-       { USB_DEVICE(0x04b8, 0x011d), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Seiko Epson Corp - Perfection 1670 */
-       { USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* EPSON Perfection 2480 */
-       { USB_DEVICE(0x04b8, 0x0121), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Seiko Epson Corp.*/
-       { USB_DEVICE(0x04b8, 0x0122), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Samsung ML-2010 printer */
-       { USB_DEVICE(0x04e8, 0x326c), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Samsung ML-2510 Series printer */
-       { USB_DEVICE(0x04e8, 0x327e), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Elsa MicroLink 56k (V.250) */
-       { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Ultima Electronics Corp.*/
-       { USB_DEVICE(0x05d8, 0x4005), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-
-       /* Genesys USB-to-IDE */
-       { USB_DEVICE(0x0503, 0x0702), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-
-       /* USB Graphical LCD - EEH Datalink GmbH */
-       { USB_DEVICE(0x060c, 0x04eb), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
 
        /* INTEL VALUE SSD */
        { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -85,44 +39,15 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* M-Systems Flash Disk Pioneers */
        { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
 
-       /* Agfa Snapscan1212u */
-       { USB_DEVICE(0x06bd, 0x2061), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Seagate RSS LLC */
-       { USB_DEVICE(0x0bc2, 0x3000), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       /* Umax [hex] Astra 3400U */
-       { USB_DEVICE(0x1606, 0x0060), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-
        /* Philips PSC805 audio device */
        { USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME },
 
-       /* Alcor multi-card reader */
-       { USB_DEVICE(0x058f, 0x6366), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-
-       /* Canon EOS 5D in PC Connection mode */
-       { USB_DEVICE(0x04a9, 0x3101), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-
-       /* RIM Blackberry */
-       { USB_DEVICE(0x0fca, 0x0001), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       { USB_DEVICE(0x0fca, 0x0004), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-       { USB_DEVICE(0x0fca, 0x0006), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-
-       /* Apple iPhone */
-       { USB_DEVICE(0x05ac, 0x1290), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
-
        /* SKYMEDI USB_DRIVE */
        { USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME },
 
        { }  /* terminating entry must be last */
 };
 
-static void usb_autosuspend_quirk(struct usb_device *udev)
-{
-#ifdef CONFIG_USB_SUSPEND
-       /* disable autosuspend, but allow the user to re-enable it via sysfs */
-       udev->autosuspend_disabled = 1;
-#endif
-}
-
 static const struct usb_device_id *find_id(struct usb_device *udev)
 {
        const struct usb_device_id *id = usb_quirk_list;
@@ -149,13 +74,9 @@ void usb_detect_quirks(struct usb_device *udev)
                dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
                                udev->quirks);
 
-       /* do any special quirk handling here if needed */
-       if (udev->quirks & USB_QUIRK_NO_AUTOSUSPEND)
-               usb_autosuspend_quirk(udev);
-
        /* By default, disable autosuspend for all non-hubs */
 #ifdef CONFIG_USB_SUSPEND
        if (udev->descriptor.bDeviceClass != USB_CLASS_HUB)
-               udev->autosuspend_delay = -1;
+               udev->autosuspend_disabled = 1;
 #endif
 }
index 2ab222b..b04afd0 100644 (file)
@@ -169,6 +169,16 @@ show_quirks(struct device *dev, struct device_attribute *attr, char *buf)
 }
 static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
 
+static ssize_t
+show_urbnum(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct usb_device *udev;
+
+       udev = to_usb_device(dev);
+       return sprintf(buf, "%d\n", atomic_read(&udev->urbnum));
+}
+static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL);
+
 
 #if defined(CONFIG_USB_PERSIST) || defined(CONFIG_USB_SUSPEND)
 static const char power_group[] = "power";
@@ -413,6 +423,44 @@ usb_descriptor_attr(bDeviceProtocol, "%02x\n")
 usb_descriptor_attr(bNumConfigurations, "%d\n")
 usb_descriptor_attr(bMaxPacketSize0, "%d\n")
 
+
+
+/* show if the device is authorized (1) or not (0) */
+static ssize_t usb_dev_authorized_show(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct usb_device *usb_dev = to_usb_device(dev);
+       return snprintf(buf, PAGE_SIZE, "%u\n", usb_dev->authorized);
+}
+
+
+/*
+ * Authorize a device to be used in the system
+ *
+ * Writing a 0 deauthorizes the device, writing a 1 authorizes it.
+ */
+static ssize_t usb_dev_authorized_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t size)
+{
+       ssize_t result;
+       struct usb_device *usb_dev = to_usb_device(dev);
+       unsigned val;
+       result = sscanf(buf, "%u\n", &val);
+       if (result != 1)
+               result = -EINVAL;
+       else if (val == 0)
+               result = usb_deauthorize_device(usb_dev);
+       else
+               result = usb_authorize_device(usb_dev);
+       return result < 0? result : size;
+}
+
+static DEVICE_ATTR(authorized, 0644,
+           usb_dev_authorized_show, usb_dev_authorized_store);
+
+
 static struct attribute *dev_attrs[] = {
        /* current configuration's attributes */
        &dev_attr_configuration.attr,
@@ -420,6 +468,7 @@ static struct attribute *dev_attrs[] = {
        &dev_attr_bConfigurationValue.attr,
        &dev_attr_bmAttributes.attr,
        &dev_attr_bMaxPower.attr,
+       &dev_attr_urbnum.attr,
        /* device attributes */
        &dev_attr_idVendor.attr,
        &dev_attr_idProduct.attr,
@@ -435,6 +484,7 @@ static struct attribute *dev_attrs[] = {
        &dev_attr_version.attr,
        &dev_attr_maxchild.attr,
        &dev_attr_quirks.attr,
+       &dev_attr_authorized.attr,
        NULL,
 };
 static struct attribute_group dev_attr_grp = {
index be63022..c20c03a 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/bitops.h>
 #include <linux/slab.h>
 #include <linux/init.h>
+#include <linux/log2.h>
 #include <linux/usb.h>
 #include <linux/wait.h>
 #include "hcd.h"
@@ -38,7 +39,6 @@ void usb_init_urb(struct urb *urb)
        if (urb) {
                memset(urb, 0, sizeof(*urb));
                kref_init(&urb->kref);
-               spin_lock_init(&urb->lock);
                INIT_LIST_HEAD(&urb->anchor_list);
        }
 }
@@ -277,44 +277,58 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
  */
 int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 {
-       int                     pipe, temp, max;
-       struct usb_device       *dev;
-       int                     is_out;
+       int                             xfertype, max;
+       struct usb_device               *dev;
+       struct usb_host_endpoint        *ep;
+       int                             is_out;
 
        if (!urb || urb->hcpriv || !urb->complete)
                return -EINVAL;
-       if (!(dev = urb->dev) ||
-           (dev->state < USB_STATE_DEFAULT) ||
-           (!dev->bus) || (dev->devnum <= 0))
+       if (!(dev = urb->dev) || dev->state < USB_STATE_DEFAULT)
                return -ENODEV;
-       if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
-                       || dev->state == USB_STATE_SUSPENDED)
-               return -EHOSTUNREACH;
 
+       /* For now, get the endpoint from the pipe.  Eventually drivers
+        * will be required to set urb->ep directly and we will eliminate
+        * urb->pipe.
+        */
+       ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)
+                       [usb_pipeendpoint(urb->pipe)];
+       if (!ep)
+               return -ENOENT;
+
+       urb->ep = ep;
        urb->status = -EINPROGRESS;
        urb->actual_length = 0;
 
        /* Lots of sanity checks, so HCDs can rely on clean data
         * and don't need to duplicate tests
         */
-       pipe = urb->pipe;
-       temp = usb_pipetype(pipe);
-       is_out = usb_pipeout(pipe);
+       xfertype = usb_endpoint_type(&ep->desc);
+       if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
+               struct usb_ctrlrequest *setup =
+                               (struct usb_ctrlrequest *) urb->setup_packet;
+
+               if (!setup)
+                       return -ENOEXEC;
+               is_out = !(setup->bRequestType & USB_DIR_IN) ||
+                               !setup->wLength;
+       } else {
+               is_out = usb_endpoint_dir_out(&ep->desc);
+       }
 
-       if (!usb_pipecontrol(pipe) && dev->state < USB_STATE_CONFIGURED)
-               return -ENODEV;
+       /* Cache the direction for later use */
+       urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) |
+                       (is_out ? URB_DIR_OUT : URB_DIR_IN);
 
-       /* FIXME there should be a sharable lock protecting us against
-        * config/altsetting changes and disconnects, kicking in here.
-        * (here == before maxpacket, and eventually endpoint type,
-        * checks get made.)
-        */
+       if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
+                       dev->state < USB_STATE_CONFIGURED)
+               return -ENODEV;
 
-       max = usb_maxpacket(dev, pipe, is_out);
+       max = le16_to_cpu(ep->desc.wMaxPacketSize);
        if (max <= 0) {
                dev_dbg(&dev->dev,
                        "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
-                       usb_pipeendpoint(pipe), is_out ? "out" : "in",
+                       usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
                        __FUNCTION__, max);
                return -EMSGSIZE;
        }
@@ -323,7 +337,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
         * but drivers only control those sizes for ISO.
         * while we're checking, initialize return status.
         */
-       if (temp == PIPE_ISOCHRONOUS) {
+       if (xfertype == USB_ENDPOINT_XFER_ISOC) {
                int     n, len;
 
                /* "high bandwidth" mode, 1-3 packets/uframe? */
@@ -358,20 +372,20 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 
        /* enforce simple/standard policy */
        allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
-                       URB_NO_INTERRUPT);
-       switch (temp) {
-       case PIPE_BULK:
+                       URB_NO_INTERRUPT | URB_DIR_MASK);
+       switch (xfertype) {
+       case USB_ENDPOINT_XFER_BULK:
                if (is_out)
                        allowed |= URB_ZERO_PACKET;
                /* FALLTHROUGH */
-       case PIPE_CONTROL:
+       case USB_ENDPOINT_XFER_CONTROL:
                allowed |= URB_NO_FSBR; /* only affects UHCI */
                /* FALLTHROUGH */
        default:                        /* all non-iso endpoints */
                if (!is_out)
                        allowed |= URB_SHORT_NOT_OK;
                break;
-       case PIPE_ISOCHRONOUS:
+       case USB_ENDPOINT_XFER_ISOC:
                allowed |= URB_ISO_ASAP;
                break;
        }
@@ -393,9 +407,9 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
         * supports different values... this uses EHCI/UHCI defaults (and
         * EHCI can use smaller non-default values).
         */
-       switch (temp) {
-       case PIPE_ISOCHRONOUS:
-       case PIPE_INTERRUPT:
+       switch (xfertype) {
+       case USB_ENDPOINT_XFER_ISOC:
+       case USB_ENDPOINT_XFER_INT:
                /* too small? */
                if (urb->interval <= 0)
                        return -EINVAL;
@@ -405,29 +419,27 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                        // NOTE usb handles 2^15
                        if (urb->interval > (1024 * 8))
                                urb->interval = 1024 * 8;
-                       temp = 1024 * 8;
+                       max = 1024 * 8;
                        break;
                case USB_SPEED_FULL:    /* units are frames/msec */
                case USB_SPEED_LOW:
-                       if (temp == PIPE_INTERRUPT) {
+                       if (xfertype == USB_ENDPOINT_XFER_INT) {
                                if (urb->interval > 255)
                                        return -EINVAL;
                                // NOTE ohci only handles up to 32
-                               temp = 128;
+                               max = 128;
                        } else {
                                if (urb->interval > 1024)
                                        urb->interval = 1024;
                                // NOTE usb and ohci handle up to 2^15
-                               temp = 1024;
+                               max = 1024;
                        }
                        break;
                default:
                        return -EINVAL;
                }
-               /* power of two? */
-               while (temp > urb->interval)
-                       temp >>= 1;
-               urb->interval = temp;
+               /* Round down to a power of 2, no more than max */
+               urb->interval = min(max, 1 << ilog2(urb->interval));
        }
 
        return usb_hcd_submit_urb(urb, mem_flags);
@@ -496,8 +508,10 @@ int usb_unlink_urb(struct urb *urb)
 {
        if (!urb)
                return -EINVAL;
-       if (!(urb->dev && urb->dev->bus))
+       if (!urb->dev)
                return -ENODEV;
+       if (!urb->ep)
+               return -EIDRM;
        return usb_hcd_unlink_urb(urb, -ECONNRESET);
 }
 
@@ -523,19 +537,21 @@ int usb_unlink_urb(struct urb *urb)
  */
 void usb_kill_urb(struct urb *urb)
 {
+       static DEFINE_MUTEX(reject_mutex);
+
        might_sleep();
-       if (!(urb && urb->dev && urb->dev->bus))
+       if (!(urb && urb->dev && urb->ep))
                return;
-       spin_lock_irq(&urb->lock);
+       mutex_lock(&reject_mutex);
        ++urb->reject;
-       spin_unlock_irq(&urb->lock);
+       mutex_unlock(&reject_mutex);
 
        usb_hcd_unlink_urb(urb, -ENOENT);
        wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
 
-       spin_lock_irq(&urb->lock);
+       mutex_lock(&reject_mutex);
        --urb->reject;
-       spin_unlock_irq(&urb->lock);
+       mutex_unlock(&reject_mutex);
 }
 
 /**
index 0fee5c6..c99938d 100644 (file)
@@ -223,6 +223,15 @@ static void ksuspend_usb_cleanup(void)
 
 #endif /* CONFIG_PM */
 
+
+/* Returns 1 if @usb_bus is WUSB, 0 otherwise */
+static unsigned usb_bus_is_wusb(struct usb_bus *bus)
+{
+       struct usb_hcd *hcd = container_of(bus, struct usb_hcd, self);
+       return hcd->wireless;
+}
+
+
 /**
  * usb_alloc_dev - usb device constructor (usbcore-internal)
  * @parent: hub to which device is connected; null to allocate a root hub
@@ -239,6 +248,8 @@ struct usb_device *
 usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
 {
        struct usb_device *dev;
+       struct usb_hcd *usb_hcd = container_of(bus, struct usb_hcd, self);
+       unsigned root_hub = 0;
 
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev)
@@ -255,12 +266,14 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
        dev->dev.dma_mask = bus->controller->dma_mask;
        set_dev_node(&dev->dev, dev_to_node(bus->controller));
        dev->state = USB_STATE_ATTACHED;
+       atomic_set(&dev->urbnum, 0);
 
        INIT_LIST_HEAD(&dev->ep0.urb_list);
        dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
        dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
        /* ep0 maxpacket comes later, from device descriptor */
-       dev->ep_in[0] = dev->ep_out[0] = &dev->ep0;
+       usb_enable_endpoint(dev, &dev->ep0);
+       dev->can_submit = 1;
 
        /* Save readable and stable topology id, distinguishing devices
         * by location for diagnostics, tools, driver model, etc.  The
@@ -275,6 +288,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
 
                dev->dev.parent = bus->controller;
                sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum);
+               root_hub = 1;
        } else {
                /* match any labeling on the hubs; it's one-based */
                if (parent->devpath[0] == '0')
@@ -301,6 +315,12 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
        INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
        dev->autosuspend_delay = usb_autosuspend_delay * HZ;
 #endif
+       if (root_hub)   /* Root hub always ok [and always wired] */
+               dev->authorized = 1;
+       else {
+               dev->authorized = usb_hcd->authorized_default;
+               dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
+       }
        return dev;
 }
 
@@ -748,7 +768,7 @@ void usb_buffer_unmap(struct urb *urb)
 /**
  * usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
  * @dev: device to which the scatterlist will be mapped
- * @pipe: endpoint defining the mapping direction
+ * @is_in: mapping transfer direction
  * @sg: the scatterlist to map
  * @nents: the number of entries in the scatterlist
  *
@@ -771,14 +791,13 @@ void usb_buffer_unmap(struct urb *urb)
  *
  * Reverse the effect of this call with usb_buffer_unmap_sg().
  */
-int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
+int usb_buffer_map_sg(const struct usb_device *dev, int is_in,
                      struct scatterlist *sg, int nents)
 {
        struct usb_bus          *bus;
        struct device           *controller;
 
        if (!dev
-                       || usb_pipecontrol(pipe)
                        || !(bus = dev->bus)
                        || !(controller = bus->controller)
                        || !controller->dma_mask)
@@ -786,7 +805,7 @@ int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
 
        // FIXME generic api broken like pci, can't report errors
        return dma_map_sg(controller, sg, nents,
-                       usb_pipein(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+                       is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 
 /* XXX DISABLED, no users currently.  If you wish to re-enable this
@@ -799,14 +818,14 @@ int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
 /**
  * usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)
  * @dev: device to which the scatterlist will be mapped
- * @pipe: endpoint defining the mapping direction
+ * @is_in: mapping transfer direction
  * @sg: the scatterlist to synchronize
  * @n_hw_ents: the positive return value from usb_buffer_map_sg
  *
  * Use this when you are re-using a scatterlist's data buffers for
  * another USB request.
  */
-void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
+void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
                           struct scatterlist *sg, int n_hw_ents)
 {
        struct usb_bus          *bus;
@@ -819,20 +838,20 @@ void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
                return;
 
        dma_sync_sg(controller, sg, n_hw_ents,
-                       usb_pipein(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+                       is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 #endif
 
 /**
  * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
  * @dev: device to which the scatterlist will be mapped
- * @pipe: endpoint defining the mapping direction
+ * @is_in: mapping transfer direction
  * @sg: the scatterlist to unmap
  * @n_hw_ents: the positive return value from usb_buffer_map_sg
  *
  * Reverses the effect of usb_buffer_map_sg().
  */
-void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
+void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
                         struct scatterlist *sg, int n_hw_ents)
 {
        struct usb_bus          *bus;
@@ -845,7 +864,7 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
                return;
 
        dma_unmap_sg(controller, sg, n_hw_ents,
-                       usb_pipein(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+                       is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 
 /* format to disable USB on kernel command line is: nousb */
index ad5fa03..c52626c 100644 (file)
@@ -8,17 +8,22 @@ extern int usb_create_ep_files(struct device *parent, struct usb_host_endpoint *
                                struct usb_device *udev);
 extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
 
+extern void usb_enable_endpoint(struct usb_device *dev,
+               struct usb_host_endpoint *ep);
 extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr);
 extern void usb_disable_interface (struct usb_device *dev,
                struct usb_interface *intf);
 extern void usb_release_interface_cache(struct kref *ref);
 extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
+extern int usb_deauthorize_device (struct usb_device *);
+extern int usb_authorize_device (struct usb_device *);
 extern void usb_detect_quirks(struct usb_device *udev);
 
 extern int usb_get_device_descriptor(struct usb_device *dev,
                unsigned int size);
 extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
+extern int usb_choose_configuration(struct usb_device *udev);
 
 extern void usb_kick_khubd(struct usb_device *dev);
 extern int usb_match_device(struct usb_device *dev,
index 767aed5..f81d08d 100644 (file)
@@ -67,6 +67,17 @@ config USB_GADGET_DEBUG_FILES
           driver on a new board.   Enable these files by choosing "Y"
           here.  If in doubt, or to conserve kernel memory, say "N".
 
+config USB_GADGET_DEBUG_FS
+       boolean "Debugging information files in debugfs"
+       depends on USB_GADGET && DEBUG_FS
+       help
+          Some of the drivers in the "gadget" framework can expose
+          debugging information in files under /sys/kernel/debug/.
+          The information in these files may help when you're
+          troubleshooting or bringing up a driver on a new board.
+          Enable these files by choosing "Y" here.  If in doubt, or
+          to conserve kernel memory, say "N".
+
 config USB_GADGET_SELECTED
        boolean
 
@@ -103,6 +114,20 @@ config USB_AMD5536UDC
        default USB_GADGET
        select USB_GADGET_SELECTED
 
+config USB_GADGET_ATMEL_USBA
+       boolean "Atmel USBA"
+       select USB_GADGET_DUALSPEED
+       depends on AVR32
+       help
+         USBA is the integrated high-speed USB Device controller on
+         the AT32AP700x processors from Atmel.
+
+config USB_ATMEL_USBA
+       tristate
+       depends on USB_GADGET_ATMEL_USBA
+       default USB_GADGET
+       select USB_GADGET_SELECTED
+
 config USB_GADGET_FSL_USB2
        boolean "Freescale Highspeed USB DR Peripheral Controller"
        depends on MPC834x || PPC_MPC831x
@@ -228,7 +253,6 @@ config USB_LH7A40X
        default USB_GADGET
        select USB_GADGET_SELECTED
 
-
 config USB_GADGET_OMAP
        boolean "OMAP USB Device Controller"
        depends on ARCH_OMAP
index 1bc0f03..904e57b 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_OMAP)                += omap_udc.o
 obj-$(CONFIG_USB_LH7A40X)      += lh7a40x_udc.o
 obj-$(CONFIG_USB_S3C2410)      += s3c2410_udc.o
 obj-$(CONFIG_USB_AT91)         += at91_udc.o
+obj-$(CONFIG_USB_ATMEL_USBA)   += atmel_usba_udc.o
 obj-$(CONFIG_USB_FSL_USB2)     += fsl_usb2_udc.o
 obj-$(CONFIG_USB_M66592)       += m66592-udc.o
 
index 714156c..1c80406 100644 (file)
@@ -69,7 +69,7 @@
 
 /* gadget stack */
 #include <linux/usb/ch9.h>
-#include <linux/usb_gadget.h>
+#include <linux/usb/gadget.h>
 
 /* udc specific */
 #include "amd5536udc.h"
@@ -3244,7 +3244,6 @@ static int udc_pci_probe(
                retval = -ENOMEM;
                goto finished;
        }
-       memset(dev, 0, sizeof(struct udc));
 
        /* pci setup */
        if (pci_enable_device(pdev) < 0) {
@@ -3286,14 +3285,12 @@ static int udc_pci_probe(
 
        pci_set_drvdata(pdev, dev);
 
-       /* chip revision */
-       dev->chiprev = 0;
+       /* chip revision for Hs AMD5536 */
+       dev->chiprev = pdev->revision;
 
        pci_set_master(pdev);
        pci_set_mwi(pdev);
 
-       /* chip rev for Hs AMD5536 */
-       pci_read_config_byte(pdev, PCI_REVISION_ID, (u8 *) &dev->chiprev);
        /* init dma pools */
        if (use_dma) {
                retval = init_dma_pools(dev);
index 63d7d65..a6adf7e 100644 (file)
@@ -38,7 +38,7 @@
 #include <linux/proc_fs.h>
 #include <linux/clk.h>
 #include <linux/usb/ch9.h>
-#include <linux/usb_gadget.h>
+#include <linux/usb/gadget.h>
 
 #include <asm/byteorder.h>
 #include <asm/hardware.h>
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
new file mode 100644 (file)
index 0000000..4fb5ff4
--- /dev/null
@@ -0,0 +1,2077 @@
+/*
+ * Driver for the Atmel USBA high speed USB device controller
+ *
+ * Copyright (C) 2005-2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/delay.h>
+
+#include <asm/gpio.h>
+#include <asm/arch/board.h>
+
+#include "atmel_usba_udc.h"
+
+
+static struct usba_udc the_udc;
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+static int queue_dbg_open(struct inode *inode, struct file *file)
+{
+       struct usba_ep *ep = inode->i_private;
+       struct usba_request *req, *req_copy;
+       struct list_head *queue_data;
+
+       queue_data = kmalloc(sizeof(*queue_data), GFP_KERNEL);
+       if (!queue_data)
+               return -ENOMEM;
+       INIT_LIST_HEAD(queue_data);
+
+       spin_lock_irq(&ep->udc->lock);
+       list_for_each_entry(req, &ep->queue, queue) {
+               req_copy = kmalloc(sizeof(*req_copy), GFP_ATOMIC);
+               if (!req_copy)
+                       goto fail;
+               memcpy(req_copy, req, sizeof(*req_copy));
+               list_add_tail(&req_copy->queue, queue_data);
+       }
+       spin_unlock_irq(&ep->udc->lock);
+
+       file->private_data = queue_data;
+       return 0;
+
+fail:
+       spin_unlock_irq(&ep->udc->lock);
+       list_for_each_entry_safe(req, req_copy, queue_data, queue) {
+               list_del(&req->queue);
+               kfree(req);
+       }
+       kfree(queue_data);
+       return -ENOMEM;
+}
+
+/*
+ * bbbbbbbb llllllll IZS sssss nnnn FDL\n\0
+ *
+ * b: buffer address
+ * l: buffer length
+ * I/i: interrupt/no interrupt
+ * Z/z: zero/no zero
+ * S/s: short ok/short not ok
+ * s: status
+ * n: nr_packets
+ * F/f: submitted/not submitted to FIFO
+ * D/d: using/not using DMA
+ * L/l: last transaction/not last transaction
+ */
+static ssize_t queue_dbg_read(struct file *file, char __user *buf,
+               size_t nbytes, loff_t *ppos)
+{
+       struct list_head *queue = file->private_data;
+       struct usba_request *req, *tmp_req;
+       size_t len, remaining, actual = 0;
+       char tmpbuf[38];
+
+       if (!access_ok(VERIFY_WRITE, buf, nbytes))
+               return -EFAULT;
+
+       mutex_lock(&file->f_dentry->d_inode->i_mutex);
+       list_for_each_entry_safe(req, tmp_req, queue, queue) {
+               len = snprintf(tmpbuf, sizeof(tmpbuf),
+                               "%8p %08x %c%c%c %5d %c%c%c\n",
+                               req->req.buf, req->req.length,
+                               req->req.no_interrupt ? 'i' : 'I',
+                               req->req.zero ? 'Z' : 'z',
+                               req->req.short_not_ok ? 's' : 'S',
+                               req->req.status,
+                               req->submitted ? 'F' : 'f',
+                               req->using_dma ? 'D' : 'd',
+                               req->last_transaction ? 'L' : 'l');
+               len = min(len, sizeof(tmpbuf));
+               if (len > nbytes)
+                       break;
+
+               list_del(&req->queue);
+               kfree(req);
+
+               remaining = __copy_to_user(buf, tmpbuf, len);
+               actual += len - remaining;
+               if (remaining)
+                       break;
+
+               nbytes -= len;
+               buf += len;
+       }
+       mutex_unlock(&file->f_dentry->d_inode->i_mutex);
+
+       return actual;
+}
+
+static int queue_dbg_release(struct inode *inode, struct file *file)
+{
+       struct list_head *queue_data = file->private_data;
+       struct usba_request *req, *tmp_req;
+
+       list_for_each_entry_safe(req, tmp_req, queue_data, queue) {
+               list_del(&req->queue);
+               kfree(req);
+       }
+       kfree(queue_data);
+       return 0;
+}
+
+static int regs_dbg_open(struct inode *inode, struct file *file)
+{
+       struct usba_udc *udc;
+       unsigned int i;
+       u32 *data;
+       int ret = -ENOMEM;
+
+       mutex_lock(&inode->i_mutex);
+       udc = inode->i_private;
+       data = kmalloc(inode->i_size, GFP_KERNEL);
+       if (!data)
+               goto out;
+
+       spin_lock_irq(&udc->lock);
+       for (i = 0; i < inode->i_size / 4; i++)
+               data[i] = __raw_readl(udc->regs + i * 4);
+       spin_unlock_irq(&udc->lock);
+
+       file->private_data = data;
+       ret = 0;
+
+out:
+       mutex_unlock(&inode->i_mutex);
+
+       return ret;
+}
+
+static ssize_t regs_dbg_read(struct file *file, char __user *buf,
+               size_t nbytes, loff_t *ppos)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       int ret;
+
+       mutex_lock(&inode->i_mutex);
+       ret = simple_read_from_buffer(buf, nbytes, ppos,
+                       file->private_data,
+                       file->f_dentry->d_inode->i_size);
+       mutex_unlock(&inode->i_mutex);
+
+       return ret;
+}
+
+static int regs_dbg_release(struct inode *inode, struct file *file)
+{
+       kfree(file->private_data);
+       return 0;
+}
+
+const struct file_operations queue_dbg_fops = {
+       .owner          = THIS_MODULE,
+       .open           = queue_dbg_open,
+       .llseek         = no_llseek,
+       .read           = queue_dbg_read,
+       .release        = queue_dbg_release,
+};
+
+const struct file_operations regs_dbg_fops = {
+       .owner          = THIS_MODULE,
+       .open           = regs_dbg_open,
+       .llseek         = generic_file_llseek,
+       .read           = regs_dbg_read,
+       .release        = regs_dbg_release,
+};
+
+static void usba_ep_init_debugfs(struct usba_udc *udc,
+               struct usba_ep *ep)
+{
+       struct dentry *ep_root;
+
+       ep_root = debugfs_create_dir(ep->ep.name, udc->debugfs_root);
+       if (!ep_root)
+               goto err_root;
+       ep->debugfs_dir = ep_root;
+
+       ep->debugfs_queue = debugfs_create_file("queue", 0400, ep_root,
+                                               ep, &queue_dbg_fops);
+       if (!ep->debugfs_queue)
+               goto err_queue;
+
+       if (ep->can_dma) {
+               ep->debugfs_dma_status
+                       = debugfs_create_u32("dma_status", 0400, ep_root,
+                                       &ep->last_dma_status);
+               if (!ep->debugfs_dma_status)
+                       goto err_dma_status;
+       }
+       if (ep_is_control(ep)) {
+               ep->debugfs_state
+                       = debugfs_create_u32("state", 0400, ep_root,
+                                       &ep->state);
+               if (!ep->debugfs_state)
+                       goto err_state;
+       }
+
+       return;
+
+err_state:
+       if (ep->can_dma)
+               debugfs_remove(ep->debugfs_dma_status);
+err_dma_status:
+       debugfs_remove(ep->debugfs_queue);
+err_queue:
+       debugfs_remove(ep_root);
+err_root:
+       dev_err(&ep->udc->pdev->dev,
+               "failed to create debugfs directory for %s\n", ep->ep.name);
+}
+
+static void usba_ep_cleanup_debugfs(struct usba_ep *ep)
+{
+       debugfs_remove(ep->debugfs_queue);
+       debugfs_remove(ep->debugfs_dma_status);
+       debugfs_remove(ep->debugfs_state);
+       debugfs_remove(ep->debugfs_dir);
+       ep->debugfs_dma_status = NULL;
+       ep->debugfs_dir = NULL;
+}
+
+static void usba_init_debugfs(struct usba_udc *udc)
+{
+       struct dentry *root, *regs;
+       struct resource *regs_resource;
+
+       root = debugfs_create_dir(udc->gadget.name, NULL);
+       if (IS_ERR(root) || !root)
+               goto err_root;
+       udc->debugfs_root = root;
+
+       regs = debugfs_create_file("regs", 0400, root, udc, &regs_dbg_fops);
+       if (!regs)
+               goto err_regs;
+
+       regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM,
+                               CTRL_IOMEM_ID);
+       regs->d_inode->i_size = regs_resource->end - regs_resource->start + 1;
+       udc->debugfs_regs = regs;
+
+       usba_ep_init_debugfs(udc, to_usba_ep(udc->gadget.ep0));
+
+       return;
+
+err_regs:
+       debugfs_remove(root);
+err_root:
+       udc->debugfs_root = NULL;
+       dev_err(&udc->pdev->dev, "debugfs is not available\n");
+}
+
+static void usba_cleanup_debugfs(struct usba_udc *udc)
+{
+       usba_ep_cleanup_debugfs(to_usba_ep(udc->gadget.ep0));
+       debugfs_remove(udc->debugfs_regs);
+       debugfs_remove(udc->debugfs_root);
+       udc->debugfs_regs = NULL;
+       udc->debugfs_root = NULL;
+}
+#else
+static inline void usba_ep_init_debugfs(struct usba_udc *udc,
+                                        struct usba_ep *ep)
+{
+
+}
+
+static inline void usba_ep_cleanup_debugfs(struct usba_ep *ep)
+{
+
+}
+
+static inline void usba_init_debugfs(struct usba_udc *udc)
+{
+
+}
+
+static inline void usba_cleanup_debugfs(struct usba_udc *udc)
+{
+
+}
+#endif
+
+static int vbus_is_present(struct usba_udc *udc)
+{
+       if (udc->vbus_pin != -1)
+               return gpio_get_value(udc->vbus_pin);
+
+       /* No Vbus detection: Assume always present */
+       return 1;
+}
+
+static void copy_to_fifo(void __iomem *fifo, const void *buf, int len)
+{
+       unsigned long tmp;
+
+       DBG(DBG_FIFO, "copy to FIFO (len %d):\n", len);
+       for (; len > 0; len -= 4, buf += 4, fifo += 4) {
+               tmp = *(unsigned long *)buf;
+               if (len >= 4) {
+                       DBG(DBG_FIFO, "  -> %08lx\n", tmp);
+                       __raw_writel(tmp, fifo);
+               } else {
+                       do {
+                               DBG(DBG_FIFO, "  -> %02lx\n", tmp >> 24);
+                               __raw_writeb(tmp >> 24, fifo);
+                               fifo++;
+                               tmp <<= 8;
+                       } while (--len);
+                       break;
+               }
+       }
+}
+
+static void copy_from_fifo(void *buf, void __iomem *fifo, int len)
+{
+       union {
+               unsigned long *w;
+               unsigned char *b;
+       } p;
+       unsigned long tmp;
+
+       DBG(DBG_FIFO, "copy from FIFO (len %d):\n", len);
+       for (p.w = buf; len > 0; len -= 4, p.w++, fifo += 4) {
+               if (len >= 4) {
+                       tmp = __raw_readl(fifo);
+                       *p.w = tmp;
+                       DBG(DBG_FIFO, "  -> %08lx\n", tmp);
+               } else {
+                       do {
+                               tmp = __raw_readb(fifo);
+                               *p.b = tmp;
+                               DBG(DBG_FIFO, " -> %02lx\n", tmp);
+                               fifo++, p.b++;
+                       } while (--len);
+               }
+       }
+}
+
+static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
+{
+       unsigned int transaction_len;
+
+       transaction_len = req->req.length - req->req.actual;
+       req->last_transaction = 1;
+       if (transaction_len > ep->ep.maxpacket) {
+               transaction_len = ep->ep.maxpacket;
+               req->last_transaction = 0;
+       } else if (transaction_len == ep->ep.maxpacket && req->req.zero)
+               req->last_transaction = 0;
+
+       DBG(DBG_QUEUE, "%s: submit_transaction, req %p (length %d)%s\n",
+               ep->ep.name, req, transaction_len,
+               req->last_transaction ? ", done" : "");
+
+       copy_to_fifo(ep->fifo, req->req.buf + req->req.actual, transaction_len);
+       usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+       req->req.actual += transaction_len;
+}
+
+static void submit_request(struct usba_ep *ep, struct usba_request *req)
+{
+       DBG(DBG_QUEUE, "%s: submit_request: req %p (length %d)\n",
+               ep->ep.name, req, req->req.length);
+
+       req->req.actual = 0;
+       req->submitted = 1;
+
+       if (req->using_dma) {
+               if (req->req.length == 0) {
+                       usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+                       return;
+               }
+
+               if (req->req.zero)
+                       usba_ep_writel(ep, CTL_ENB, USBA_SHORT_PACKET);
+               else
+                       usba_ep_writel(ep, CTL_DIS, USBA_SHORT_PACKET);
+
+               usba_dma_writel(ep, ADDRESS, req->req.dma);
+               usba_dma_writel(ep, CONTROL, req->ctrl);
+       } else {
+               next_fifo_transaction(ep, req);
+               if (req->last_transaction) {
+                       usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+                       usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+               } else {
+                       usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+                       usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+               }
+       }
+}
+
+static void submit_next_request(struct usba_ep *ep)
+{
+       struct usba_request *req;
+
+       if (list_empty(&ep->queue)) {
+               usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY);
+               return;
+       }
+
+       req = list_entry(ep->queue.next, struct usba_request, queue);
+       if (!req->submitted)
+               submit_request(ep, req);
+}
+
+static void send_status(struct usba_udc *udc, struct usba_ep *ep)
+{
+       ep->state = STATUS_STAGE_IN;
+       usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+       usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+}
+
+static void receive_data(struct usba_ep *ep)
+{
+       struct usba_udc *udc = ep->udc;
+       struct usba_request *req;
+       unsigned long status;
+       unsigned int bytecount, nr_busy;
+       int is_complete = 0;
+
+       status = usba_ep_readl(ep, STA);
+       nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+
+       DBG(DBG_QUEUE, "receive data: nr_busy=%u\n", nr_busy);
+
+       while (nr_busy > 0) {
+               if (list_empty(&ep->queue)) {
+                       usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+                       break;
+               }
+               req = list_entry(ep->queue.next,
+                                struct usba_request, queue);
+
+               bytecount = USBA_BFEXT(BYTE_COUNT, status);
+
+               if (status & (1 << 31))
+                       is_complete = 1;
+               if (req->req.actual + bytecount >= req->req.length) {
+                       is_complete = 1;
+                       bytecount = req->req.length - req->req.actual;
+               }
+
+               copy_from_fifo(req->req.buf + req->req.actual,
+                               ep->fifo, bytecount);
+               req->req.actual += bytecount;
+
+               usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+
+               if (is_complete) {
+                       DBG(DBG_QUEUE, "%s: request done\n", ep->ep.name);
+                       req->req.status = 0;
+                       list_del_init(&req->queue);
+                       usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+                       spin_unlock(&udc->lock);
+                       req->req.complete(&ep->ep, &req->req);
+                       spin_lock(&udc->lock);
+               }
+
+               status = usba_ep_readl(ep, STA);
+               nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+
+               if (is_complete && ep_is_control(ep)) {
+                       send_status(udc, ep);
+                       break;
+               }
+       }
+}
+
+static void
+request_complete(struct usba_ep *ep, struct usba_request *req, int status)
+{
+       struct usba_udc *udc = ep->udc;
+
+       WARN_ON(!list_empty(&req->queue));
+
+       if (req->req.status == -EINPROGRESS)
+               req->req.status = status;
+
+       if (req->mapped) {
+               dma_unmap_single(
+                       &udc->pdev->dev, req->req.dma, req->req.length,
+                       ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+               req->req.dma = DMA_ADDR_INVALID;
+               req->mapped = 0;
+       }
+
+       DBG(DBG_GADGET | DBG_REQ,
+               "%s: req %p complete: status %d, actual %u\n",
+               ep->ep.name, req, req->req.status, req->req.actual);
+
+       spin_unlock(&udc->lock);
+       req->req.complete(&ep->ep, &req->req);
+       spin_lock(&udc->lock);
+}
+
+static void
+request_complete_list(struct usba_ep *ep, struct list_head *list, int status)
+{
+       struct usba_request *req, *tmp_req;
+
+       list_for_each_entry_safe(req, tmp_req, list, queue) {
+               list_del_init(&req->queue);
+               request_complete(ep, req, status);
+       }
+}
+
+static int
+usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+       struct usba_ep *ep = to_usba_ep(_ep);
+       struct usba_udc *udc = ep->udc;
+       unsigned long flags, ept_cfg, maxpacket;
+       unsigned int nr_trans;
+
+       DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc);
+
+       maxpacket = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff;
+
+       if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index)
+                       || ep->index == 0
+                       || desc->bDescriptorType != USB_DT_ENDPOINT
+                       || maxpacket == 0
+                       || maxpacket > ep->fifo_size) {
+               DBG(DBG_ERR, "ep_enable: Invalid argument");
+               return -EINVAL;
+       }
+
+       ep->is_isoc = 0;
+       ep->is_in = 0;
+
+       if (maxpacket <= 8)
+               ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+       else
+               /* LSB is bit 1, not 0 */
+               ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3);
+
+       DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n",
+                       ep->ep.name, ept_cfg, maxpacket);
+
+       if ((desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+               ep->is_in = 1;
+               ept_cfg |= USBA_EPT_DIR_IN;
+       }
+
+       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       case USB_ENDPOINT_XFER_CONTROL:
+               ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
+               ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+               break;
+       case USB_ENDPOINT_XFER_ISOC:
+               if (!ep->can_isoc) {
+                       DBG(DBG_ERR, "ep_enable: %s is not isoc capable\n",
+                                       ep->ep.name);
+                       return -EINVAL;
+               }
+
+               /*
+                * Bits 11:12 specify number of _additional_
+                * transactions per microframe.
+                */
+               nr_trans = ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 3) + 1;
+               if (nr_trans > 3)
+                       return -EINVAL;
+
+               ep->is_isoc = 1;
+               ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO);
+
+               /*
+                * Do triple-buffering on high-bandwidth iso endpoints.
+                */
+               if (nr_trans > 1 && ep->nr_banks == 3)
+                       ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE);
+               else
+                       ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+               ept_cfg |= USBA_BF(NB_TRANS, nr_trans);
+               break;
+       case USB_ENDPOINT_XFER_BULK:
+               ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK);
+               ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+               break;
+       case USB_ENDPOINT_XFER_INT:
+               ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT);
+               ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+               break;
+       }
+
+       spin_lock_irqsave(&ep->udc->lock, flags);
+
+       if (ep->desc) {
+               spin_unlock_irqrestore(&ep->udc->lock, flags);
+               DBG(DBG_ERR, "ep%d already enabled\n", ep->index);
+               return -EBUSY;
+       }
+
+       ep->desc = desc;
+       ep->ep.maxpacket = maxpacket;
+
+       usba_ep_writel(ep, CFG, ept_cfg);
+       usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
+
+       if (ep->can_dma) {
+               u32 ctrl;
+
+               usba_writel(udc, INT_ENB,
+                               (usba_readl(udc, INT_ENB)
+                                       | USBA_BF(EPT_INT, 1 << ep->index)
+                                       | USBA_BF(DMA_INT, 1 << ep->index)));
+               ctrl = USBA_AUTO_VALID | USBA_INTDIS_DMA;
+               usba_ep_writel(ep, CTL_ENB, ctrl);
+       } else {
+               usba_writel(udc, INT_ENB,
+                               (usba_readl(udc, INT_ENB)
+                                       | USBA_BF(EPT_INT, 1 << ep->index)));
+       }
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index,
+                       (unsigned long)usba_ep_readl(ep, CFG));
+       DBG(DBG_HW, "INT_ENB after init: %#08lx\n",
+                       (unsigned long)usba_readl(udc, INT_ENB));
+
+       return 0;
+}
+
+static int usba_ep_disable(struct usb_ep *_ep)
+{
+       struct usba_ep *ep = to_usba_ep(_ep);
+       struct usba_udc *udc = ep->udc;
+       LIST_HEAD(req_list);
+       unsigned long flags;
+
+       DBG(DBG_GADGET, "ep_disable: %s\n", ep->ep.name);
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       if (!ep->desc) {
+               spin_unlock_irqrestore(&udc->lock, flags);
+               DBG(DBG_ERR, "ep_disable: %s not enabled\n", ep->ep.name);
+               return -EINVAL;
+       }
+       ep->desc = NULL;
+
+       list_splice_init(&ep->queue, &req_list);
+       if (ep->can_dma) {
+               usba_dma_writel(ep, CONTROL, 0);
+               usba_dma_writel(ep, ADDRESS, 0);
+               usba_dma_readl(ep, STATUS);
+       }
+       usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
+       usba_writel(udc, INT_ENB,
+                       usba_readl(udc, INT_ENB)
+                       & ~USBA_BF(EPT_INT, 1 << ep->index));
+
+       request_complete_list(ep, &req_list, -ESHUTDOWN);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       return 0;
+}
+
+static struct usb_request *
+usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+       struct usba_request *req;
+
+       DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags);
+
+       req = kzalloc(sizeof(*req), gfp_flags);
+       if (!req)
+               return NULL;
+
+       INIT_LIST_HEAD(&req->queue);
+       req->req.dma = DMA_ADDR_INVALID;
+
+       return &req->req;
+}
+
+static void
+usba_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct usba_request *req = to_usba_req(_req);
+
+       DBG(DBG_GADGET, "ep_free_request: %p, %p\n", _ep, _req);
+
+       kfree(req);
+}
+
+static int queue_dma(struct usba_udc *udc, struct usba_ep *ep,
+               struct usba_request *req, gfp_t gfp_flags)
+{
+       unsigned long flags;
+       int ret;
+
+       DBG(DBG_DMA, "%s: req l/%u d/%08x %c%c%c\n",
+               ep->ep.name, req->req.length, req->req.dma,
+               req->req.zero ? 'Z' : 'z',
+               req->req.short_not_ok ? 'S' : 's',
+               req->req.no_interrupt ? 'I' : 'i');
+
+       if (req->req.length > 0x10000) {
+               /* Lengths from 0 to 65536 (inclusive) are supported */
+               DBG(DBG_ERR, "invalid request length %u\n", req->req.length);
+               return -EINVAL;
+       }
+
+       req->using_dma = 1;
+
+       if (req->req.dma == DMA_ADDR_INVALID) {
+               req->req.dma = dma_map_single(
+                       &udc->pdev->dev, req->req.buf, req->req.length,
+                       ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+               req->mapped = 1;
+       } else {
+               dma_sync_single_for_device(
+                       &udc->pdev->dev, req->req.dma, req->req.length,
+                       ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+               req->mapped = 0;
+       }
+
+       req->ctrl = USBA_BF(DMA_BUF_LEN, req->req.length)
+                       | USBA_DMA_CH_EN | USBA_DMA_END_BUF_IE
+                       | USBA_DMA_END_TR_EN | USBA_DMA_END_TR_IE;
+
+       if (ep->is_in)
+               req->ctrl |= USBA_DMA_END_BUF_EN;
+
+       /*
+        * Add this request to the queue and submit for DMA if
+        * possible. Check if we're still alive first -- we may have
+        * received a reset since last time we checked.
+        */
+       ret = -ESHUTDOWN;
+       spin_lock_irqsave(&udc->lock, flags);
+       if (ep->desc) {
+               if (list_empty(&ep->queue))
+                       submit_request(ep, req);
+
+               list_add_tail(&req->queue, &ep->queue);
+               ret = 0;
+       }
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       return ret;
+}
+
+static int
+usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+       struct usba_request *req = to_usba_req(_req);
+       struct usba_ep *ep = to_usba_ep(_ep);
+       struct usba_udc *udc = ep->udc;
+       unsigned long flags;
+       int ret;
+
+       DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, "%s: queue req %p, len %u\n",
+                       ep->ep.name, req, _req->length);
+
+       if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || !ep->desc)
+               return -ESHUTDOWN;
+
+       req->submitted = 0;
+       req->using_dma = 0;
+       req->last_transaction = 0;
+
+       _req->status = -EINPROGRESS;
+       _req->actual = 0;
+
+       if (ep->can_dma)
+               return queue_dma(udc, ep, req, gfp_flags);
+
+       /* May have received a reset since last time we checked */
+       ret = -ESHUTDOWN;
+       spin_lock_irqsave(&udc->lock, flags);
+       if (ep->desc) {
+               list_add_tail(&req->queue, &ep->queue);
+
+               if (ep->is_in || (ep_is_control(ep)
+                               && (ep->state == DATA_STAGE_IN
+                                       || ep->state == STATUS_STAGE_IN)))
+                       usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+               else
+                       usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);
+               ret = 0;
+       }
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       return ret;
+}
+
+static void
+usba_update_req(struct usba_ep *ep, struct usba_request *req, u32 status)
+{
+       req->req.actual = req->req.length - USBA_BFEXT(DMA_BUF_LEN, status);
+}
+
+static int stop_dma(struct usba_ep *ep, u32 *pstatus)
+{
+       unsigned int timeout;
+       u32 status;
+
+       /*
+        * Stop the DMA controller. When writing both CH_EN
+        * and LINK to 0, the other bits are not affected.
+        */
+       usba_dma_writel(ep, CONTROL, 0);
+
+       /* Wait for the FIFO to empty */
+       for (timeout = 40; timeout; --timeout) {
+               status = usba_dma_readl(ep, STATUS);
+               if (!(status & USBA_DMA_CH_EN))
+                       break;
+               udelay(1);
+       }
+
+       if (pstatus)
+               *pstatus = status;
+
+       if (timeout == 0) {
+               dev_err(&ep->udc->pdev->dev,
+                       "%s: timed out waiting for DMA FIFO to empty\n",
+                       ep->ep.name);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct usba_ep *ep = to