aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2006-03-23 23:44:19 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2006-03-23 23:44:19 -0500
commit1ebbe2b20091d306453a5cf480a87e6cd28ae76f (patch)
treef5cd7a0fa69b8b1938cb5a0faed2e7b0628072a5 /drivers/usb
parentac58c9059da8886b5e8cde012a80266b18ca146e (diff)
parent674a396c6d2ba0341ebdd7c1c9950f32f018e2dd (diff)
Merge branch 'linus'
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Kconfig9
-rw-r--r--drivers/usb/Makefile4
-rw-r--r--drivers/usb/class/Kconfig47
-rw-r--r--drivers/usb/class/Makefile2
-rw-r--r--drivers/usb/class/audio.c3869
-rw-r--r--drivers/usb/class/audio.h110
-rw-r--r--drivers/usb/class/cdc-acm.c23
-rw-r--r--drivers/usb/class/usb-midi.c2153
-rw-r--r--drivers/usb/class/usb-midi.h164
-rw-r--r--drivers/usb/class/usblp.c15
-rw-r--r--drivers/usb/core/devices.c7
-rw-r--r--drivers/usb/core/devio.c24
-rw-r--r--drivers/usb/core/driver.c6
-rw-r--r--drivers/usb/core/hcd-pci.c11
-rw-r--r--drivers/usb/core/hcd.c153
-rw-r--r--drivers/usb/core/hcd.h4
-rw-r--r--drivers/usb/core/hub.c45
-rw-r--r--drivers/usb/core/message.c17
-rw-r--r--drivers/usb/core/notify.c15
-rw-r--r--drivers/usb/core/usb.c5
-rw-r--r--drivers/usb/gadget/Kconfig17
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/at91_udc.c1773
-rw-r--r--drivers/usb/gadget/at91_udc.h181
-rw-r--r--drivers/usb/gadget/dummy_hcd.c3
-rw-r--r--drivers/usb/gadget/ether.c53
-rw-r--r--drivers/usb/gadget/file_storage.c4
-rw-r--r--drivers/usb/gadget/gadget_chips.h30
-rw-r--r--drivers/usb/gadget/goku_udc.c3
-rw-r--r--drivers/usb/gadget/inode.c6
-rw-r--r--drivers/usb/gadget/lh7a40x_udc.c3
-rw-r--r--drivers/usb/gadget/net2280.c3
-rw-r--r--drivers/usb/gadget/omap_udc.c6
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.c3
-rw-r--r--drivers/usb/gadget/serial.c9
-rw-r--r--drivers/usb/gadget/zero.c15
-rw-r--r--drivers/usb/host/Kconfig2
-rw-r--r--drivers/usb/host/ehci-au1xxx.c297
-rw-r--r--drivers/usb/host/ehci-fsl.c366
-rw-r--r--drivers/usb/host/ehci-fsl.h37
-rw-r--r--drivers/usb/host/ehci-hcd.c13
-rw-r--r--drivers/usb/host/ehci-hub.c4
-rw-r--r--drivers/usb/host/ehci-mem.c11
-rw-r--r--drivers/usb/host/ehci-pci.c25
-rw-r--r--drivers/usb/host/ehci-q.c17
-rw-r--r--drivers/usb/host/ehci-sched.c20
-rw-r--r--drivers/usb/host/ehci.h18
-rw-r--r--drivers/usb/host/hc_crisv10.c12
-rw-r--r--drivers/usb/host/isp116x-hcd.c5
-rw-r--r--drivers/usb/host/ohci-at91.c306
-rw-r--r--drivers/usb/host/ohci-au1xxx.c102
-rw-r--r--drivers/usb/host/ohci-hcd.c54
-rw-r--r--drivers/usb/host/ohci-hub.c12
-rw-r--r--drivers/usb/host/ohci-omap.c9
-rw-r--r--drivers/usb/host/ohci-pci.c15
-rw-r--r--drivers/usb/host/sl811-hcd.c3
-rw-r--r--drivers/usb/host/uhci-debug.c356
-rw-r--r--drivers/usb/host/uhci-hcd.c127
-rw-r--r--drivers/usb/host/uhci-hcd.h188
-rw-r--r--drivers/usb/host/uhci-hub.c21
-rw-r--r--drivers/usb/host/uhci-q.c1294
-rw-r--r--drivers/usb/image/mdc800.c67
-rw-r--r--drivers/usb/input/ati_remote.c2
-rw-r--r--drivers/usb/input/hid-core.c175
-rw-r--r--drivers/usb/input/hid-lgff.c6
-rw-r--r--drivers/usb/input/hid-tmff.c3
-rw-r--r--drivers/usb/input/hid.h10
-rw-r--r--drivers/usb/input/hiddev.c6
-rw-r--r--drivers/usb/media/Kconfig15
-rw-r--r--drivers/usb/media/Makefile7
-rw-r--r--drivers/usb/media/dabusb.c36
-rw-r--r--drivers/usb/media/dabusb.h2
-rw-r--r--drivers/usb/media/et61x251.h28
-rw-r--r--drivers/usb/media/et61x251_core.c321
-rw-r--r--drivers/usb/media/et61x251_sensor.h5
-rw-r--r--drivers/usb/media/et61x251_tas5130d1b.c10
-rw-r--r--drivers/usb/media/ov511.c97
-rw-r--r--drivers/usb/media/ov511.h11
-rw-r--r--drivers/usb/media/pwc/pwc-ctrl.c1
-rw-r--r--drivers/usb/media/pwc/pwc-if.c9
-rw-r--r--drivers/usb/media/se401.c16
-rw-r--r--drivers/usb/media/se401.h3
-rw-r--r--drivers/usb/media/sn9c102.h28
-rw-r--r--drivers/usb/media/sn9c102_core.c326
-rw-r--r--drivers/usb/media/sn9c102_ov7630.c33
-rw-r--r--drivers/usb/media/sn9c102_pas202bca.c238
-rw-r--r--drivers/usb/media/sn9c102_pas202bcb.c2
-rw-r--r--drivers/usb/media/sn9c102_sensor.h15
-rw-r--r--drivers/usb/media/sn9c102_tas5110c1b.c14
-rw-r--r--drivers/usb/media/sn9c102_tas5130d1b.c12
-rw-r--r--drivers/usb/media/stv680.c20
-rw-r--r--drivers/usb/media/stv680.h2
-rw-r--r--drivers/usb/media/usbvideo.c31
-rw-r--r--drivers/usb/media/usbvideo.h5
-rw-r--r--drivers/usb/media/vicam.c22
-rw-r--r--drivers/usb/media/w9968cf.c88
-rw-r--r--drivers/usb/media/w9968cf.h14
-rw-r--r--drivers/usb/media/zc0301.h192
-rw-r--r--drivers/usb/media/zc0301_core.c2055
-rw-r--r--drivers/usb/media/zc0301_pas202bcb.c361
-rw-r--r--drivers/usb/media/zc0301_sensor.h103
-rw-r--r--drivers/usb/misc/auerswald.c6
-rw-r--r--drivers/usb/misc/cytherm.c3
-rw-r--r--drivers/usb/misc/idmouse.c28
-rw-r--r--drivers/usb/misc/ldusb.c14
-rw-r--r--drivers/usb/misc/legousbtower.c11
-rw-r--r--drivers/usb/misc/phidgetkit.c9
-rw-r--r--drivers/usb/misc/phidgetservo.c3
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c5
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.h8
-rw-r--r--drivers/usb/misc/usblcd.c3
-rw-r--r--drivers/usb/misc/usbled.c3
-rw-r--r--drivers/usb/misc/usbtest.c9
-rw-r--r--drivers/usb/mon/mon_main.c22
-rw-r--r--drivers/usb/mon/mon_text.c24
-rw-r--r--drivers/usb/mon/usb_mon.h2
-rw-r--r--drivers/usb/net/pegasus.c1
-rw-r--r--drivers/usb/net/pegasus.h26
-rw-r--r--drivers/usb/net/rtl8150.c4
-rw-r--r--drivers/usb/net/zd1201.c9
-rw-r--r--drivers/usb/serial/Kconfig7
-rw-r--r--drivers/usb/serial/Makefile1
-rw-r--r--drivers/usb/serial/cp2101.c7
-rw-r--r--drivers/usb/serial/cypress_m8.c73
-rw-r--r--drivers/usb/serial/cypress_m8.h5
-rw-r--r--drivers/usb/serial/ftdi_sio.c4
-rw-r--r--drivers/usb/serial/ftdi_sio.h7
-rw-r--r--drivers/usb/serial/garmin_gps.c3
-rw-r--r--drivers/usb/serial/io_edgeport.c3
-rw-r--r--drivers/usb/serial/io_ti.c6
-rw-r--r--drivers/usb/serial/ir-usb.c3
-rw-r--r--drivers/usb/serial/keyspan.c6
-rw-r--r--drivers/usb/serial/kobil_sct.c16
-rw-r--r--drivers/usb/serial/mct_u232.c3
-rw-r--r--drivers/usb/serial/navman.c157
-rw-r--r--drivers/usb/serial/omninet.c10
-rw-r--r--drivers/usb/serial/option.c3
-rw-r--r--drivers/usb/serial/pl2303.c8
-rw-r--r--drivers/usb/serial/pl2303.h4
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c3
-rw-r--r--drivers/usb/serial/usb-serial.c6
-rw-r--r--drivers/usb/serial/visor.c3
-rw-r--r--drivers/usb/storage/datafab.c3
-rw-r--r--drivers/usb/storage/isd200.c10
-rw-r--r--drivers/usb/storage/jumpshot.c3
-rw-r--r--drivers/usb/storage/scsiglue.c9
-rw-r--r--drivers/usb/storage/sddr55.c3
-rw-r--r--drivers/usb/storage/shuttle_usbat.c3
-rw-r--r--drivers/usb/storage/unusual_devs.h32
-rw-r--r--drivers/usb/storage/usb.c25
-rw-r--r--drivers/usb/storage/usb.h5
151 files changed, 8590 insertions, 8529 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 85dacc92545..b1222cd4aa4 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -10,6 +10,7 @@ menu "USB support"
config USB_ARCH_HAS_HCD
boolean
default y if USB_ARCH_HAS_OHCI
+ default y if USB_ARCH_HAS_EHCI
default y if ARM # SL-811
default PCI
@@ -22,6 +23,7 @@ config USB_ARCH_HAS_OHCI
default y if ARCH_LH7A404
default y if ARCH_S3C2410
default y if PXA27x
+ default y if ARCH_AT91RM9200
# PPC:
default y if STB03xxx
default y if PPC_MPC52xx
@@ -30,6 +32,13 @@ config USB_ARCH_HAS_OHCI
# more:
default PCI
+# some non-PCI hcds implement EHCI
+config USB_ARCH_HAS_EHCI
+ boolean
+ default y if PPC_83xx
+ default y if SOC_AU1200
+ default PCI
+
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
config USB
tristate "Support for Host-side USB"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 36e476dd912..bb36a1c1dbb 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -15,10 +15,9 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
obj-$(CONFIG_ETRAX_USB_HOST) += host/
+obj-$(CONFIG_USB_OHCI_AT91) += host/
obj-$(CONFIG_USB_ACM) += class/
-obj-$(CONFIG_USB_AUDIO) += class/
-obj-$(CONFIG_USB_MIDI) += class/
obj-$(CONFIG_USB_PRINTER) += class/
obj-$(CONFIG_USB_STORAGE) += storage/
@@ -48,6 +47,7 @@ obj-$(CONFIG_USB_SN9C102) += media/
obj-$(CONFIG_USB_STV680) += media/
obj-$(CONFIG_USB_VICAM) += media/
obj-$(CONFIG_USB_W9968CF) += media/
+obj-$(CONFIG_USB_ZC0301) += media/
obj-$(CONFIG_USB_CATC) += net/
obj-$(CONFIG_USB_KAWETH) += net/
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
index ef105a92a7b..3a9102d2591 100644
--- a/drivers/usb/class/Kconfig
+++ b/drivers/usb/class/Kconfig
@@ -4,53 +4,6 @@
comment "USB Device Class drivers"
depends on USB
-config OBSOLETE_OSS_USB_DRIVER
- bool "Obsolete OSS USB drivers"
- depends on USB && SOUND
- help
- This option enables support for the obsolete USB Audio and Midi
- drivers that are scheduled for removal in the near future since
- there are ALSA drivers for the same hardware.
-
- Please contact Adrian Bunk <bunk@stusta.de> if you had to
- say Y here because of missing support in the ALSA drivers.
-
- If unsure, say N.
-
-config USB_AUDIO
- tristate "USB Audio support"
- depends on USB && SOUND && OBSOLETE_OSS_USB_DRIVER
- help
- Say Y here if you want to connect USB audio equipment such as
- speakers to your computer's USB port. You only need this if you use
- the OSS sound driver; ALSA has its own option for usb audio support.
-
- To compile this driver as a module, choose M here: the
- module will be called audio.
-
-config USB_MIDI
- tristate "USB MIDI support"
- depends on USB && SOUND && OBSOLETE_OSS_USB_DRIVER
- ---help---
- Say Y here if you want to connect a USB MIDI device to your
- computer's USB port. You only need this if you use the OSS
- sound system; USB MIDI devices are supported by ALSA's USB
- audio driver. This driver is for devices that comply with
- 'Universal Serial Bus Device Class Definition for MIDI Device'.
-
- The following devices are known to work:
- * Steinberg USB2MIDI
- * Roland MPU64
- * Roland PC-300
- * Roland SC8850
- * Roland UM-1
- * Roland UM-2
- * Roland UA-100
- * Yamaha MU1000
-
- To compile this driver as a module, choose M here: the
- module will be called usb-midi.
-
config USB_ACM
tristate "USB Modem (CDC ACM) support"
depends on USB
diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile
index 22947124775..cc391e6c2af 100644
--- a/drivers/usb/class/Makefile
+++ b/drivers/usb/class/Makefile
@@ -4,6 +4,4 @@
#
obj-$(CONFIG_USB_ACM) += cdc-acm.o
-obj-$(CONFIG_USB_AUDIO) += audio.o
-obj-$(CONFIG_USB_MIDI) += usb-midi.o
obj-$(CONFIG_USB_PRINTER) += usblp.o
diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c
deleted file mode 100644
index 3ad9ee8b84a..00000000000
--- a/drivers/usb/class/audio.c
+++ /dev/null
@@ -1,3869 +0,0 @@
-/*****************************************************************************/
-
-/*
- * audio.c -- USB Audio Class driver
- *
- * Copyright (C) 1999, 2000, 2001, 2003, 2004
- * Alan Cox (alan@lxorguk.ukuu.org.uk)
- * Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Debugging:
- * Use the 'lsusb' utility to dump the descriptors.
- *
- * 1999-09-07: Alan Cox
- * Parsing Audio descriptor patch
- * 1999-09-08: Thomas Sailer
- * Added OSS compatible data io functions; both parts of the
- * driver remain to be glued together
- * 1999-09-10: Thomas Sailer
- * Beautified the driver. Added sample format conversions.
- * Still not properly glued with the parsing code.
- * The parsing code seems to have its problems btw,
- * Since it parses all available configs but doesn't
- * store which iface/altsetting belongs to which config.
- * 1999-09-20: Thomas Sailer
- * Threw out Alan's parsing code and implemented my own one.
- * You cannot reasonnably linearly parse audio descriptors,
- * especially the AudioClass descriptors have to be considered
- * pointer lists. Mixer parsing untested, due to lack of device.
- * First stab at synch pipe implementation, the Dallas USB DAC
- * wants to use an Asynch out pipe. usb_audio_state now basically
- * only contains lists of mixer and wave devices. We can therefore
- * now have multiple mixer/wave devices per USB device.
- * 1999-10-28: Thomas Sailer
- * Converted to URB API. Fixed a taskstate/wakeup semantics mistake
- * that made the driver consume all available CPU cycles.
- * Now runs stable on UHCI-Acher/Fliegl/Sailer.
- * 1999-10-31: Thomas Sailer
- * Audio can now be unloaded if it is not in use by any mixer
- * or dsp client (formerly you had to disconnect the audio devices
- * from the USB port)
- * Finally, about three months after ordering, my "Maxxtro SPK222"
- * speakers arrived, isn't disdata a great mail order company 8-)
- * Parse class specific endpoint descriptor of the audiostreaming
- * interfaces and take the endpoint attributes from there.
- * Unbelievably, the Philips USB DAC has a sampling rate range
- * of over a decade, yet does not support the sampling rate control!
- * No wonder it sounds so bad, has very audible sampling rate
- * conversion distortion. Don't try to listen to it using
- * decent headphones!
- * "Let's make things better" -> but please Philips start with your
- * own stuff!!!!
- * 1999-11-02: Thomas Sailer
- * It takes the Philips boxes several seconds to acquire synchronisation
- * that means they won't play short sounds. Should probably maintain
- * the ISO datastream even if there's nothing to play.
- * Fix counting the total_bytes counter, RealPlayer G2 depends on it.
- * 1999-12-20: Thomas Sailer
- * Fix bad bug in conversion to per interface probing.
- * disconnect was called multiple times for the audio device,
- * leading to a premature freeing of the audio structures
- * 2000-05-13: Thomas Sailer
- * I don't remember who changed the find_format routine,
- * but the change was completely broken for the Dallas
- * chip. Anyway taking sampling rate into account in find_format
- * is bad and should not be done unless there are devices with
- * completely broken audio descriptors. Unless someone shows
- * me such a descriptor, I will not allow find_format to
- * take the sampling rate into account.
- * Also, the former find_format made:
- * - mpg123 play mono instead of stereo
- * - sox completely fail for wav's with sample rates < 44.1kHz
- * for the Dallas chip.
- * Also fix a rather long standing problem with applications that
- * use "small" writes producing no sound at all.
- * 2000-05-15: Thomas Sailer
- * My fears came true, the Philips camera indeed has pretty stupid
- * audio descriptors.
- * 2000-05-17: Thomas Sailer
- * Nemsoft spotted my stupid last minute change, thanks
- * 2000-05-19: Thomas Sailer
- * Fixed FEATURE_UNIT thinkos found thanks to the KC Technology
- * Xtend device. Basically the driver treated FEATURE_UNIT's sourced
- * by mono terminals as stereo.
- * 2000-05-20: Thomas Sailer
- * SELECTOR support (and thus selecting record channels from the mixer).
- * Somewhat peculiar due to OSS interface limitations. Only works
- * for channels where a "slider" is already in front of it (i.e.
- * a MIXER unit or a FEATURE unit with volume capability).
- * 2000-11-26: Thomas Sailer
- * Workaround for Dallas DS4201. The DS4201 uses PCM8 as format tag for
- * its 8 bit modes, but expects signed data (and should therefore have used PCM).
- * 2001-03-10: Thomas Sailer
- * provide abs function, prevent picking up a bogus kernel macro
- * for abs. Bug report by Andrew Morton <andrewm@uow.edu.au>
- * 2001-06-16: Bryce Nesbitt <bryce@obviously.com>
- * Fix SNDCTL_DSP_STEREO API violation
- * 2003-04-08: Oliver Neukum (oliver@neukum.name):
- * Setting a configuration is done by usbcore and must not be overridden
- * 2004-02-27: Workaround for broken synch descriptors
- * 2004-03-07: Alan Stern <stern@rowland.harvard.edu>
- * Add usb_ifnum_to_if() and usb_altnum_to_altsetting() support.
- * Use the in-memory descriptors instead of reading them from the device.
- *
- */
-
-/*
- * Strategy:
- *
- * Alan Cox and Thomas Sailer are starting to dig at opposite ends and
- * are hoping to meet in the middle, just like tunnel diggers :)
- * Alan tackles the descriptor parsing, Thomas the actual data IO and the
- * OSS compatible interface.
- *
- * Data IO implementation issues
- *
- * A mmap'able ring buffer per direction is implemented, because
- * almost every OSS app expects it. It is however impractical to
- * transmit/receive USB data directly into and out of the ring buffer,
- * due to alignment and synchronisation issues. Instead, the ring buffer
- * feeds a constant time delay line that handles the USB issues.
- *
- * Now we first try to find an alternate setting that exactly matches
- * the sample format requested by the user. If we find one, we do not
- * need to perform any sample rate conversions. If there is no matching
- * altsetting, we choose the closest one and perform sample format
- * conversions. We never do sample rate conversion; these are too
- * expensive to be performed in the kernel.
- *
- * Current status: no known HCD-specific issues.
- *
- * Generally: Due to the brokenness of the Audio Class spec
- * it seems generally impossible to write a generic Audio Class driver,
- * so a reasonable driver should implement the features that are actually
- * used.
- *
- * Parsing implementation issues
- *
- * One cannot reasonably parse the AudioClass descriptors linearly.
- * Therefore the current implementation features routines to look
- * for a specific descriptor in the descriptor list.
- *
- * How does the parsing work? First, all interfaces are searched
- * for an AudioControl class interface. If found, the config descriptor
- * that belongs to the current configuration is searched and
- * the HEADER descriptor is found. It contains a list of
- * all AudioStreaming and MIDIStreaming devices. This list is then walked,
- * and all AudioStreaming interfaces are classified into input and output
- * interfaces (according to the endpoint0 direction in altsetting1) (MIDIStreaming
- * is currently not supported). The input & output list is then used
- * to group inputs and outputs together and issued pairwise to the
- * AudioStreaming class parser. Finally, all OUTPUT_TERMINAL descriptors
- * are walked and issued to the mixer construction routine.
- *
- * The AudioStreaming parser simply enumerates all altsettings belonging
- * to the specified interface. It looks for AS_GENERAL and FORMAT_TYPE
- * class specific descriptors to extract the sample format/sample rate
- * data. Only sample format types PCM and PCM8 are supported right now, and
- * only FORMAT_TYPE_I is handled. The isochronous data endpoint needs to
- * be the first endpoint of the interface, and the optional synchronisation
- * isochronous endpoint the second one.
- *
- * Mixer construction works as follows: The various TERMINAL and UNIT
- * descriptors span a tree from the root (OUTPUT_TERMINAL) through the
- * intermediate nodes (UNITs) to the leaves (INPUT_TERMINAL). We walk
- * that tree in a depth first manner. FEATURE_UNITs may contribute volume,
- * bass and treble sliders to the mixer, MIXER_UNITs volume sliders.
- * The terminal type encoded in the INPUT_TERMINALs feeds a heuristic
- * to determine "meaningful" OSS slider numbers, however we will see
- * how well this works in practice. Other features are not used at the
- * moment, they seem less often used. Also, it seems difficult at least
- * to construct recording source switches from SELECTOR_UNITs, but
- * since there are not many USB ADC's available, we leave that for later.
- */
-
-/*****************************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
-#include <linux/module.h>
-#include <linux/sound.h>
-#include <linux/soundcard.h>
-#include <linux/list.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/poll.h>
-#include <linux/bitops.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <linux/usb.h>
-
-#include "audio.h"
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.0.0"
-#define DRIVER_AUTHOR "Alan Cox <alan@lxorguk.ukuu.org.uk>, Thomas Sailer (sailer@ife.ee.ethz.ch)"
-#define DRIVER_DESC "USB Audio Class driver"
-
-#define AUDIO_DEBUG 1
-
-#define SND_DEV_DSP16 5
-
-#define dprintk(x)
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Linked list of all audio devices...
- */
-static struct list_head audiodevs = LIST_HEAD_INIT(audiodevs);
-static DECLARE_MUTEX(open_sem);
-
-/*
- * wait queue for processes wanting to open an USB audio device
- */
-static DECLARE_WAIT_QUEUE_HEAD(open_wait);
-
-
-#define MAXFORMATS MAX_ALT
-#define DMABUFSHIFT 17 /* 128k worth of DMA buffer */
-#define NRSGBUF (1U<<(DMABUFSHIFT-PAGE_SHIFT))
-
-/*
- * This influences:
- * - Latency
- * - Interrupt rate
- * - Synchronisation behaviour
- * Don't touch this if you don't understand all of the above.
- */
-#define DESCFRAMES 5
-#define SYNCFRAMES DESCFRAMES
-
-#define MIXFLG_STEREOIN 1
-#define MIXFLG_STEREOOUT 2
-
-struct mixerchannel {
- __u16 value;
- __u16 osschannel; /* number of the OSS channel */
- __s16 minval, maxval;
- __u16 slctunitid;
- __u8 unitid;
- __u8 selector;
- __u8 chnum;
- __u8 flags;
-};
-
-struct audioformat {
- unsigned int format;
- unsigned int sratelo;
- unsigned int sratehi;
- unsigned char altsetting;
- unsigned char attributes;
-};
-
-struct dmabuf {
- /* buffer data format */
- unsigned int format;
- unsigned int srate;
- /* physical buffer */
- unsigned char *sgbuf[NRSGBUF];
- unsigned bufsize;
- unsigned numfrag;
- unsigned fragshift;
- unsigned wrptr, rdptr;
- unsigned total_bytes;
- int count;
- unsigned error; /* over/underrun */
- wait_queue_head_t wait;
- /* redundant, but makes calculations easier */
- unsigned fragsize;
- unsigned dmasize;
- /* OSS stuff */
- unsigned mapped:1;
- unsigned ready:1;
- unsigned ossfragshift;
- int ossmaxfrags;
- unsigned subdivision;
-};
-
-struct usb_audio_state;
-
-#define FLG_URB0RUNNING 1
-#define FLG_URB1RUNNING 2
-#define FLG_SYNC0RUNNING 4
-#define FLG_SYNC1RUNNING 8
-#define FLG_RUNNING 16
-#define FLG_CONNECTED 32
-
-struct my_data_urb {
- struct urb *urb;
-};
-
-struct my_sync_urb {
- struct urb *urb;
-};
-
-
-struct usb_audiodev {
- struct list_head list;
- struct usb_audio_state *state;
-
- /* soundcore stuff */
- int dev_audio;
-
- /* wave stuff */
- mode_t open_mode;
- spinlock_t lock; /* DMA buffer access spinlock */
-
- struct usbin {
- int interface; /* Interface number, -1 means not used */
- unsigned int format; /* USB data format */
- unsigned int datapipe; /* the data input pipe */
- unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but adaptive IN mode */
- unsigned int syncinterval; /* P for adaptive IN mode, 0 otherwise */
- unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */
- unsigned int freqmax; /* maximum sampling rate, used for buffer management */
- unsigned int phase; /* phase accumulator */
- unsigned int flags; /* see FLG_ defines */
-
- struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */
- struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */
-
- struct dmabuf dma;
- } usbin;
-
- struct usbout {
- int interface; /* Interface number, -1 means not used */
- unsigned int format; /* USB data format */
- unsigned int datapipe; /* the data input pipe */
- unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but asynchronous OUT mode */
- unsigned int syncinterval; /* P for asynchronous OUT mode, 0 otherwise */
- unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */
- unsigned int freqm; /* momentary sampling rate in USB format, i.e. fs/1000 in Q10.14 */
- unsigned int freqmax; /* maximum sampling rate, used for buffer management */
- unsigned int phase; /* phase accumulator */
- unsigned int flags; /* see FLG_ defines */
-
- struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */
- struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */
-
- struct dmabuf dma;
- } usbout;
-
-
- unsigned int numfmtin, numfmtout;
- struct audioformat fmtin[MAXFORMATS];
- struct audioformat fmtout[MAXFORMATS];
-};
-
-struct usb_mixerdev {
- struct list_head list;
- struct usb_audio_state *state;
-
- /* soundcore stuff */
- int dev_mixer;
-
- unsigned char iface; /* interface number of the AudioControl interface */
-
- /* USB format descriptions */
- unsigned int numch, modcnt;
-
- /* mixch is last and gets allocated dynamically */
- struct mixerchannel ch[0];
-};
-
-struct usb_audio_state {
- struct list_head audiodev;
-
- /* USB device */
- struct usb_device *usbdev;
-
- struct list_head audiolist;
- struct list_head mixerlist;
-
- unsigned count; /* usage counter; NOTE: the usb stack is also considered a user */
-};
-
-/* private audio format extensions */
-#define AFMT_STEREO 0x80000000
-#define AFMT_ISSTEREO(x) ((x) & AFMT_STEREO)
-#define AFMT_IS16BIT(x) ((x) & (AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE))
-#define AFMT_ISUNSIGNED(x) ((x) & (AFMT_U8|AFMT_U16_LE|AFMT_U16_BE))
-#define AFMT_BYTESSHIFT(x) ((AFMT_ISSTEREO(x) ? 1 : 0) + (AFMT_IS16BIT(x) ? 1 : 0))
-#define AFMT_BYTES(x) (1<<AFMT_BYTESSHFIT(x))
-
-/* --------------------------------------------------------------------- */
-
-static inline unsigned ld2(unsigned int x)
-{
- unsigned r = 0;
-
- if (x >= 0x10000) {
- x >>= 16;
- r += 16;
- }
- if (x >= 0x100) {
- x >>= 8;
- r += 8;
- }
- if (x >= 0x10) {
- x >>= 4;
- r += 4;
- }
- if (x >= 4) {
- x >>= 2;
- r += 2;
- }
- if (x >= 2)
- r++;
- return r;
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * OSS compatible ring buffer management. The ring buffer may be mmap'ed into
- * an application address space.
- *
- * I first used the rvmalloc stuff copied from bttv. Alan Cox did not like it, so
- * we now use an array of pointers to a single page each. This saves us the
- * kernel page table manipulations, but we have to do a page table alike mechanism
- * (though only one indirection) in software.
- */
-
-static void dmabuf_release(struct dmabuf *db)
-{
- unsigned int nr;
- void *p;
-
- for(nr = 0; nr < NRSGBUF; nr++) {
- if (!(p = db->sgbuf[nr]))
- continue;
- ClearPageReserved(virt_to_page(p));
- free_page((unsigned long)p);
- db->sgbuf[nr] = NULL;
- }
- db->mapped = db->ready = 0;
-}
-
-static int dmabuf_init(struct dmabuf *db)
-{
- unsigned int nr, bytepersec, bufs;
- void *p;
-
- /* initialize some fields */
- db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0;
- /* calculate required buffer size */
- bytepersec = db->srate << AFMT_BYTESSHIFT(db->format);
- bufs = 1U << DMABUFSHIFT;
- if (db->ossfragshift) {
- if ((1000 << db->ossfragshift) < bytepersec)
- db->fragshift = ld2(bytepersec/1000);
- else
- db->fragshift = db->ossfragshift;
- } else {
- db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
- if (db->fragshift < 3)
- db->fragshift = 3;
- }
- db->numfrag = bufs >> db->fragshift;
- while (db->numfrag < 4 && db->fragshift > 3) {
- db->fragshift--;
- db->numfrag = bufs >> db->fragshift;
- }
- db->fragsize = 1 << db->fragshift;
- if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
- db->numfrag = db->ossmaxfrags;
- db->dmasize = db->numfrag << db->fragshift;
- for(nr = 0; nr < NRSGBUF; nr++) {
- if (!db->sgbuf[nr]) {
- p = (void *)get_zeroed_page(GFP_KERNEL);
- if (!p)
- return -ENOMEM;
- db->sgbuf[nr] = p;
- SetPageReserved(virt_to_page(p));
- }
- memset(db->sgbuf[nr], AFMT_ISUNSIGNED(db->format) ? 0x80 : 0, PAGE_SIZE);
- if ((nr << PAGE_SHIFT) >= db->dmasize)
- break;
- }
- db->bufsize = nr << PAGE_SHIFT;
- db->ready = 1;
- dprintk((KERN_DEBUG "usbaudio: dmabuf_init bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d "
- "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x srate %d\n",
- bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize,
- db->numfrag, db->dmasize, db->bufsize, db->format, db->srate));
- return 0;
-}
-
-static int dmabuf_mmap(struct vm_area_struct *vma, struct dmabuf *db, unsigned long start, unsigned long size, pgprot_t prot)
-{
- unsigned int nr;
-
- if (!db->ready || db->mapped || (start | size) & (PAGE_SIZE-1) || size > db->bufsize)
- return -EINVAL;
- size >>= PAGE_SHIFT;
- for(nr = 0; nr < size; nr++)
- if (!db->sgbuf[nr])
- return -EINVAL;
- db->mapped = 1;
- for(nr = 0; nr < size; nr++) {
- unsigned long pfn;
-
- pfn = virt_to_phys(db->sgbuf[nr]) >> PAGE_SHIFT;
- if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, prot))
- return -EAGAIN;
- start += PAGE_SIZE;
- }
- return 0;
-}
-
-static void dmabuf_copyin(struct dmabuf *db, const void *buffer, unsigned int size)
-{
- unsigned int pgrem, rem;
-
- db->total_bytes += size;
- for (;;) {
- if (size <= 0)
- return;
- pgrem = ((~db->wrptr) & (PAGE_SIZE-1)) + 1;
- if (pgrem > size)
- pgrem = size;
- rem = db->dmasize - db->wrptr;
- if (pgrem > rem)
- pgrem = rem;
- memcpy((db->sgbuf[db->wrptr >> PAGE_SHIFT]) + (db->wrptr & (PAGE_SIZE-1)), buffer, pgrem);
- size -= pgrem;
- buffer += pgrem;
- db->wrptr += pgrem;
- if (db->wrptr >= db->dmasize)
- db->wrptr = 0;
- }
-}
-
-static void dmabuf_copyout(struct dmabuf *db, void *buffer, unsigned int size)
-{
- unsigned int pgrem, rem;
-
- db->total_bytes += size;
- for (;;) {
- if (size <= 0)
- return;
- pgrem = ((~db->rdptr) & (PAGE_SIZE-1)) + 1;
- if (pgrem > size)
- pgrem = size;
- rem = db->dmasize - db->rdptr;
- if (pgrem > rem)
- pgrem = rem;
- memcpy(buffer, (db->sgbuf[db->rdptr >> PAGE_SHIFT]) + (db->rdptr & (PAGE_SIZE-1)), pgrem);
- size -= pgrem;
- buffer += pgrem;
- db->rdptr += pgrem;
- if (db->rdptr >= db->dmasize)
- db->rdptr = 0;
- }
-}
-
-static int dmabuf_copyin_user(struct dmabuf *db, unsigned int ptr, const void __user *buffer, unsigned int size)
-{
- unsigned int pgrem, rem;
-
- if (!db->ready || db->mapped)
- return -EINVAL;
- for (;;) {
- if (size <= 0)
- return 0;
- pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1;
- if (pgrem > size)
- pgrem = size;
- rem = db->dmasize - ptr;
- if (pgrem > rem)
- pgrem = rem;
- if (copy_from_user((db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), buffer, pgrem))
- return -EFAULT;
- size -= pgrem;
- buffer += pgrem;
- ptr += pgrem;
- if (ptr >= db->dmasize)
- ptr = 0;
- }
-}
-
-static int dmabuf_copyout_user(struct dmabuf *db, unsigned int ptr, void __user *buffer, unsigned int size)
-{
- unsigned int pgrem, rem;
-
- if (!db->ready || db->mapped)
- return -EINVAL;
- for (;;) {
- if (size <= 0)
- return 0;
- pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1;
- if (pgrem > size)
- pgrem = size;
- rem = db->dmasize - ptr;
- if (pgrem > rem)
- pgrem = rem;
- if (copy_to_user(buffer, (db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), pgrem))
- return -EFAULT;
- size -= pgrem;
- buffer += pgrem;
- ptr += pgrem;
- if (ptr >= db->dmasize)
- ptr = 0;
- }
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * USB I/O code. We do sample format conversion if necessary
- */
-
-static void usbin_stop(struct usb_audiodev *as)
-{
- struct usbin *u = &as->usbin;
- unsigned long flags;
- unsigned int i, notkilled = 1;
-
- spin_lock_irqsave(&as->lock, flags);
- u->flags &= ~FLG_RUNNING;
- i = u->flags;
- spin_unlock_irqrestore(&as->lock, flags);
- while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) {
- if (notkilled)
- schedule_timeout_interruptible(1);
- else
- schedule_timeout_uninterruptible(1);
- spin_lock_irqsave(&as->lock, flags);
- i = u->flags;
- spin_unlock_irqrestore(&as->lock, flags);
- if (notkilled && signal_pending(current)) {
- if (i & FLG_URB0RUNNING)
- usb_kill_urb(u->durb[0].urb);
- if (i & FLG_URB1RUNNING)
- usb_kill_urb(u->durb[1].urb);
- if (i & FLG_SYNC0RUNNING)
- usb_kill_urb(u->surb[0].urb);
- if (i & FLG_SYNC1RUNNING)
- usb_kill_urb(u->surb[1].urb);
- notkilled = 0;
- }
- }
- set_current_state(TASK_RUNNING);
- kfree(u->durb[0].urb->transfer_buffer);
- kfree(u->durb[1].urb->transfer_buffer);
- kfree(u->surb[0].urb->transfer_buffer);
- kfree(u->surb[1].urb->transfer_buffer);
- u->durb[0].urb->transfer_buffer = u->durb[1].urb->transfer_buffer =
- u->surb[0].urb->transfer_buffer = u->surb[1].urb->transfer_buffer = NULL;
-}
-
-static inline void usbin_release(struct usb_audiodev *as)
-{
- usbin_stop(as);
-}
-
-static void usbin_disc(struct usb_audiodev *as)
-{
- struct usbin *u = &as->usbin;
-
- unsigned long flags;
-
- spin_lock_irqsave(&as->lock, flags);
- u->flags &= ~(FLG_RUNNING | FLG_CONNECTED);
- spin_unlock_irqrestore(&as->lock, flags);
- usbin_stop(as);
-}
-
-static void conversion(const void *ibuf, unsigned int ifmt, void *obuf, unsigned int ofmt, void *tmp, unsigned int scnt)
-{
- unsigned int cnt, i;
- __s16 *sp, *sp2, s;
- unsigned char *bp;
-
- cnt = scnt;
- if (AFMT_ISSTEREO(ifmt))
- cnt <<= 1;
- sp = ((__s16 *)tmp) + cnt;
- switch (ifmt & ~AFMT_STEREO) {
- case AFMT_U8:
- for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) {
- bp--;
- sp--;
- *sp = (*bp ^ 0x80) << 8;
- }
- break;
-
- case AFMT_S8:
- for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) {
- bp--;
- sp--;
- *sp = *bp << 8;
- }
- break;
-
- case AFMT_U16_LE:
- for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000;
- }
- break;
-
- case AFMT_U16_BE:
- for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000;
- }
- break;
-
- case AFMT_S16_LE:
- for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = bp[0] | (bp[1] << 8);
- }
- break;
-
- case AFMT_S16_BE:
- for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = bp[1] | (bp[0] << 8);
- }
- break;
- }
- if (!AFMT_ISSTEREO(ifmt) && AFMT_ISSTEREO(ofmt)) {
- /* expand from mono to stereo */
- for (sp = ((__s16 *)tmp)+scnt, sp2 = ((__s16 *)tmp)+2*scnt, i = 0; i < scnt; i++) {
- sp--;
- sp2 -= 2;
- sp2[0] = sp2[1] = sp[0];
- }
- }
- if (AFMT_ISSTEREO(ifmt) && !AFMT_ISSTEREO(ofmt)) {
- /* contract from stereo to mono */
- for (sp = sp2 = ((__s16 *)tmp), i = 0; i < scnt; i++, sp++, sp2 += 2)
- sp[0] = (sp2[0] + sp2[1]) >> 1;
- }
- cnt = scnt;
- if (AFMT_ISSTEREO(ofmt))
- cnt <<= 1;
- sp = ((__s16 *)tmp);
- bp = ((unsigned char *)obuf);
- switch (ofmt & ~AFMT_STEREO) {
- case AFMT_U8:
- for (i = 0; i < cnt; i++, sp++, bp++)
- *bp = (*sp >> 8) ^ 0x80;
- break;
-
- case AFMT_S8:
- for (i = 0; i < cnt; i++, sp++, bp++)
- *bp = *sp >> 8;
- break;
-
- case AFMT_U16_LE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[0] = s;
- bp[1] = (s >> 8) ^ 0x80;
- }
- break;
-
- case AFMT_U16_BE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[1] = s;
- bp[0] = (s >> 8) ^ 0x80;
- }
- break;
-
- case AFMT_S16_LE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[0] = s;
- bp[1] = s >> 8;
- }
- break;
-
- case AFMT_S16_BE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[1] = s;
- bp[0] = s >> 8;
- }
- break;
- }
-
-}
-
-static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples)
-{
- union {
- __s16 s[64];
- unsigned char b[0];
- } tmp;
- unsigned int scnt, maxs, ufmtsh, dfmtsh;
-
- ufmtsh = AFMT_BYTESSHIFT(u->format);
- dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
- maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64;
- while (samples > 0) {
- scnt = samples;
- if (scnt > maxs)
- scnt = maxs;
- conversion(buffer, u->format, tmp.b, u->dma.format, tmp.b, scnt);
- dmabuf_copyin(&u->dma, tmp.b, scnt << dfmtsh);
- buffer += scnt << ufmtsh;
- samples -= scnt;
- }
-}
-
-static int usbin_prepare_desc(struct usbin *u, struct urb *urb)
-{
- unsigned int i, maxsize, offs;
-
- maxsize = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
- //printk(KERN_DEBUG "usbin_prepare_desc: maxsize %d freq 0x%x format 0x%x\n", maxsize, u->freqn, u->format);
- for (i = offs = 0; i < DESCFRAMES; i++, offs += maxsize) {
- urb->iso_frame_desc[i].length = maxsize;
- urb->iso_frame_desc[i].offset = offs;
- }
- urb->interval = 1;
- return 0;
-}
-
-/*
- * return value: 0 if descriptor should be restarted, -1 otherwise
- * convert sample format on the fly if necessary
- */
-static int usbin_retire_desc(struct usbin *u, struct urb *urb)
-{
- unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree;
- unsigned char *cp;
-
- ufmtsh = AFMT_BYTESSHIFT(u->format);
- dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
- for (i = 0; i < DESCFRAMES; i++) {
- cp = ((unsigned char *)urb->transfer_buffer) + urb->iso_frame_desc[i].offset;
- if (urb->iso_frame_desc[i].status) {
- dprintk((KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
- continue;
- }
- scnt = urb->iso_frame_desc[i].actual_length >> ufmtsh;
- if (!scnt)
- continue;
- cnt = scnt << dfmtsh;
- if (!u->dma.mapped) {
- dmafree = u->dma.dmasize - u->dma.count;
- if (cnt > dmafree) {
- scnt = dmafree >> dfmtsh;
- cnt = scnt << dfmtsh;
- err++;
- }
- }
- u->dma.count += cnt;
- if (u->format == u->dma.format) {
- /* we do not need format conversion */
- dprintk((KERN_DEBUG "usbaudio: no sample format conversion\n"));
- dmabuf_copyin(&u->dma, cp, cnt);
- } else {
- /* we need sampling format conversion */
- dprintk((KERN_DEBUG "usbaudio: sample format conversion %x != %x\n", u->format, u->dma.format));
- usbin_convert(u, cp, scnt);
- }
- }
- if (err)
- u->dma.error++;
- if (u->dma.count >= (signed)u->dma.fragsize)
- wake_up(&u->dma.wait);
- return err ? -1 : 0;
-}
-
-static void usbin_completed(struct urb *urb, struct pt_regs *regs)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
- struct usbin *u = &as->usbin;
- unsigned long flags;
- unsigned int mask;
- int suret = 0;
-
-#if 0
- printk(KERN_DEBUG "usbin_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);
-#endif
- if (urb == u->durb[0].urb)
- mask = FLG_URB0RUNNING;
- else if (urb == u->durb[1].urb)
- mask = FLG_URB1RUNNING;
- else {
- mask = 0;
- printk(KERN_ERR "usbin_completed: panic: unknown URB\n");
- }
- urb->dev = as->state->usbdev;
- spin_lock_irqsave(&as->lock, flags);
- if (!usbin_retire_desc(u, urb) &&
- u->flags & FLG_RUNNING &&
- !usbin_prepare_desc(u, urb) &&
- (suret = usb_submit_urb(urb, GFP_ATOMIC)) == 0) {
- u->flags |= mask;
- } else {
- u->flags &= ~(mask | FLG_RUNNING);
- wake_up(&u->dma.wait);
- printk(KERN_DEBUG "usbin_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret);
- }
- spin_unlock_irqrestore(&as->lock, flags);
-}
-
-/*
- * we output sync data
- */
-static int usbin_sync_prepare_desc(struct usbin *u, struct urb *urb)
-{
- unsigned char *cp = urb->transfer_buffer;
- unsigned int i, offs;
-
- for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3, cp += 3) {
- urb->iso_frame_desc[i].length = 3;
- urb->iso_frame_desc[i].offset = offs;
- cp[0] = u->freqn;
- cp[1] = u->freqn >> 8;
- cp[2] = u->freqn >> 16;
- }
- urb->interval = 1;
- return 0;
-}
-
-/*
- * return value: 0 if descriptor should be restarted, -1 otherwise
- */
-static int usbin_sync_retire_desc(struct usbin *u, struct urb *urb)
-{
- unsigned int i;
-
- for (i = 0; i < SYNCFRAMES; i++)
- if (urb->iso_frame_desc[0].status)
- dprintk((KERN_DEBUG "usbin_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
- return 0;
-}
-
-static void usbin_sync_completed(struct urb *urb, struct pt_regs *regs)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
- struct usbin *u = &as->usbin;
- unsigned long flags;
- unsigned int mask;
- int suret = 0;
-
-#if 0
- printk(KERN_DEBUG "usbin_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);
-#endif
- if (urb == u->surb[0].urb)
- mask = FLG_SYNC0RUNNING;
- else if (urb == u->surb[1].urb)
- mask = FLG_SYNC1RUNNING;
- else {
- mask = 0;
- printk(KERN_ERR "usbin_sync_completed: panic: unknown URB\n");
- }
- urb->dev = as->state->usbdev;
- spin_lock_irqsave(&as->lock, flags);
- if (!usbin_sync_retire_desc(u, urb) &&
- u->flags & FLG_RUNNING &&
- !usbin_sync_prepare_desc(u, urb) &&
- (suret = usb_submit_urb(urb, GFP_ATOMIC)) == 0) {
- u->flags |= mask;
- } else {
- u->flags &= ~(mask | FLG_RUNNING);
- wake_up(&u->dma.wait);
- dprintk((KERN_DEBUG "usbin_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret));
- }
- spin_unlock_irqrestore(&as->lock, flags);
-}
-
-static int usbin_start(struct usb_audiodev *as)
-{
- struct usb_device *dev = as->state->usbdev;
- struct usbin *u = &as->usbin;
- struct urb *urb;
- unsigned long flags;
- unsigned int maxsze, bufsz;
-
-#if 0
- printk(KERN_DEBUG "usbin_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n",
- dev->devnum, u->format, u->dma.format, u->dma.srate);
-#endif
- /* allocate USB storage if not already done */
- spin_lock_irqsave(&as->lock, flags);
- if (!(u->flags & FLG_CONNECTED)) {
- spin_unlock_irqrestore(&as->lock, flags);
- return -EIO;
- }
- if (!(u->flags & FLG_RUNNING)) {
- spin_unlock_irqrestore(&as->lock, flags);
- u->freqn = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */
- u->freqmax = u->freqn + (u->freqn >> 2);
- u->phase = 0;
- maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
- bufsz = DESCFRAMES * maxsze;
- kfree(u->durb[0].urb->transfer_buffer);
- u->durb[0].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
- u->durb[0].urb->transfer_buffer_length = bufsz;
- kfree(u->durb[1].urb->transfer_buffer);
- u->durb[1].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
- u->durb[1].urb->transfer_buffer_length = bufsz;
- if (u->syncpipe) {
- kfree(u->surb[0].urb->transfer_buffer);
- u->surb[0].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
- u->surb[0].urb->transfer_buffer_length = 3*SYNCFRAMES;
- kfree(u->surb[1].urb->transfer_buffer);
- u->surb[1].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
- u->surb[1].urb->transfer_buffer_length = 3*SYNCFRAMES;
- }
- if (!u->durb[0].urb->transfer_buffer || !u->durb[1].urb->transfer_buffer ||
- (u->syncpipe && (!u->surb[0].urb->transfer_buffer || !u->surb[1].urb->transfer_buffer))) {
- printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum);
- return 0;
- }
- spin_lock_irqsave(&as->lock, flags);
- }
- if (u->dma.count >= u->dma.dmasize && !u->dma.mapped) {
- spin_unlock_irqrestore(&as->lock, flags);
- return 0;
- }
- u->flags |= FLG_RUNNING;
- if (!(u->flags & FLG_URB0RUNNING)) {
- urb = u->durb[0].urb;
- urb->dev = dev;
- urb->pipe = u->datapipe;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->number_of_packets = DESCFRAMES;
- urb->context = as;
- urb->complete = usbin_completed;
- if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL))
- u->flags |= FLG_URB0RUNNING;
- else
- u->flags &= ~FLG_RUNNING;
- }
- if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) {
- urb = u->durb[1].urb;
- urb->dev = dev;
- urb->pipe = u->datapipe;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->number_of_packets = DESCFRAMES;
- urb->context = as;
- urb->complete = usbin_completed;
- if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL))
- u->flags |= FLG_URB1RUNNING;
- else
- u->flags &= ~FLG_RUNNING;
- }
- if (u->syncpipe) {
- if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) {
- urb = u->surb[0].urb;
- urb->dev = dev;
- urb->pipe = u->syncpipe;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->number_of_packets = SYNCFRAMES;
- urb->context = as;
- urb->complete = usbin_sync_completed;
- /* stride: u->syncinterval */
- if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL))
- u->flags |= FLG_SYNC0RUNNING;
- else
- u->flags &= ~FLG_RUNNING;
- }
- if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) {
- urb = u->surb[1].urb;
- urb->dev = dev;
- urb->pipe = u->syncpipe;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->number_of_packets = SYNCFRAMES;
- urb->context = as;
- urb->complete = usbin_sync_completed;
- /* stride: u->syncinterval */
- if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL))
- u->flags |= FLG_SYNC1RUNNING;
- else
- u->flags &= ~FLG_RUNNING;
- }
- }
- spin_unlock_irqrestore(&as->lock, flags);
- return 0;
-}
-
-static void usbout_stop(struct usb_audiodev *as)
-{
- struct usbout *u = &as->usbout;
- unsigned long flags;
- unsigned int i, notkilled = 1;
-
- spin_lock_irqsave(&as->lock, flags);
- u->flags &= ~FLG_RUNNING;
- i = u->flags;
- spin_unlock_irqrestore(&as->lock, flags);
- while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) {
- if (notkilled)
- schedule_timeout_interruptible(1);
- else
- schedule_timeout_uninterruptible(1);
- spin_lock_irqsave(&as->lock, flags);
- i = u->flags;
- spin_unlock_irqrestore(&as->lock, flags);
- if (notkilled && signal_pending(current)) {
- if (i & FLG_URB0RUNNING)
- usb_kill_urb(u->durb[0].urb);
- if (i & FLG_URB1RUNNING)
- usb_kill_urb(u->durb[1].urb);
- if (i & FLG_SYNC0RUNNING)
- usb_kill_urb(u->surb[0].urb);
- if (i & FLG_SYNC1RUNNING)
- usb_kill_urb(u->surb[1].urb);
- notkilled = 0;
- }
- }
- set_current_state(TASK_RUNNING);
- kfree(u->durb[0].urb->transfer_buffer);
- kfree(u->durb[1].urb->transfer_buffer);
- kfree(u->surb[0].urb->transfer_buffer);
- kfree(u->surb[1].urb->transfer_buffer);
- u->durb[0].urb->transfer_buffer = u->durb[1].urb->transfer_buffer =
- u->surb[0].urb->transfer_buffer = u->surb[1].urb->transfer_buffer = NULL;
-}
-
-static inline void usbout_release(struct usb_audiodev *as)
-{
- usbout_stop(as);
-}
-
-static void usbout_disc(struct usb_audiodev *as)
-{
- struct usbout *u = &as->usbout;
- unsigned long flags;
-
- spin_lock_irqsave(&as->lock, flags);
- u->flags &= ~(FLG_RUNNING | FLG_CONNECTED);
- spin_unlock_irqrestore(&as->lock, flags);
- usbout_stop(as);
-}
-
-static void usbout_convert(struct usbout *u, unsigned char *buffer, unsigned int samples)
-{
- union {
- __s16 s[64];
- unsigned char b[0];
- } tmp;
- unsigned int scnt, maxs, ufmtsh, dfmtsh;
-
- ufmtsh = AFMT_BYTESSHIFT(u->format);
- dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
- maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64;
- while (samples > 0) {
- scnt = samples;
- if (scnt > maxs)
- scnt = maxs;
- dmabuf_copyout(&u->dma, tmp.b, scnt << dfmtsh);
- conversion(tmp.b, u->dma.format, buffer, u->format, tmp.b, scnt);
- buffer += scnt << ufmtsh;
- samples -= scnt;
- }
-}
-
-static int usbout_prepare_desc(struct usbout *u, struct urb *urb)
-{
- unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, offs;
- unsigned char *cp = urb->transfer_buffer;
-
- ufmtsh = AFMT_BYTESSHIFT(u->format);
- dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
- for (i = offs = 0; i < DESCFRAMES; i++) {
- urb->iso_frame_desc[i].offset = offs;
- u->phase = (u->phase & 0x3fff) + u->freqm;
- scnt = u->phase >> 14;
- if (!scnt) {
- urb->iso_frame_desc[i].length = 0;
- continue;
- }
- cnt = scnt << dfmtsh;
- if (!u->dma.mapped) {
- if (cnt > u->dma.count) {
- scnt = u->dma.count >> dfmtsh;
- cnt = scnt << dfmtsh;
- err++;
- }
- u->dma.count -= cnt;
- } else
- u->dma.count += cnt;
- if (u->format == u->dma.format) {
- /* we do not need format conversion */
- dmabuf_copyout(&u->dma, cp, cnt);
- } else {
- /* we need sampling format conversion */
- usbout_convert(u, cp, scnt);
- }
- cnt = scnt << ufmtsh;
- urb->iso_frame_desc[i].length = cnt;
- offs += cnt;
- cp += cnt;
- }
- urb->interval = 1;
- if (err)
- u->dma.error++;
- if (u->dma.mapped) {
- if (u->dma.count >= (signed)u->dma.fragsize)
- wake_up(&u->dma.wait);
- } else {
- if ((signed)u->dma.dmasize >= u->dma.count + (signed)u->dma.fragsize)
- wake_up(&u->dma.wait);
- }
- return err ? -1 : 0;
-}
-
-/*
- * return value: 0 if descriptor should be restarted, -1 otherwise
- */
-static int usbout_retire_desc(struct usbout *u, struct urb *urb)
-{
- unsigned int i;
-
- for (i = 0; i < DESCFRAMES; i++) {
- if (urb->iso_frame_desc[i].status) {
- dprintk((KERN_DEBUG "usbout_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
- continue;
- }
- }
- return 0;
-}
-
-static void usbout_completed(struct urb *urb, struct pt_regs *regs)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
- struct usbout *u = &as->usbout;
- unsigned long flags;
- unsigned int mask;
- int suret = 0;
-
-#if 0
- printk(KERN_DEBUG "usbout_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);
-#endif
- if (urb == u->durb[0].urb)
- mask = FLG_URB0RUNNING;
- else if (urb == u->durb[1].urb)
- mask = FLG_URB1RUNNING;
- else {
- mask = 0;
- printk(KERN_ERR "usbout_completed: panic: unknown URB\n");
- }
- urb->dev = as->state->usbdev;
- spin_lock_irqsave(&as->lock, flags);
- if (!usbout_retire_desc(u, urb) &&
- u->flags & FLG_RUNNING &&
- !usbout_prepare_desc(u, urb) &&
- (suret = usb_submit_urb(urb, GFP_ATOMIC)) == 0) {
- u->flags |= mask;
- } else {
- u->flags &= ~(mask | FLG_RUNNING);
- wake_up(&u->dma.wait);
- dprintk((KERN_DEBUG "usbout_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret));
- }
- spin_unlock_irqrestore(&as->lock, flags);
-}
-
-static int usbout_sync_prepare_desc(struct usbout *u, struct urb *urb)
-{
- unsigned int i, offs;
-
- for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3) {
- urb->iso_frame_desc[i].length = 3;
- urb->iso_frame_desc[i].offset = offs;
- }
- urb->interval = 1;
- return 0;
-}
-
-/*
- * return value: 0 if descriptor should be restarted, -1 otherwise
- */
-static int usbout_sync_retire_desc(struct usbout *u, struct urb *urb)
-{
- unsigned char *cp = urb->transfer_buffer;
- unsigned int f, i;
-
- for (i = 0; i < SYNCFRAMES; i++, cp += 3) {
- if (urb->iso_frame_desc[i].status) {
- dprintk((KERN_DEBUG "usbout_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
- continue;
- }
- if (urb->iso_frame_desc[i].actual_length < 3) {
- dprintk((KERN_DEBUG "usbout_sync_retire_desc: frame %u length %d\n", i, urb->iso_frame_desc[i].actual_length));
- continue;
- }
- f = cp[0] | (cp[1] << 8) | (cp[2] << 16);
- if (abs(f - u->freqn) > (u->freqn >> 3) || f > u->freqmax) {
- printk(KERN_WARNING "usbout_sync_retire_desc: requested frequency %u (nominal %u) out of range!\n", f, u->freqn);
- continue;
- }
- u->freqm = f;
- }
- return 0;
-}
-
-static void usbout_sync_completed(struct urb *urb, struct pt_regs *regs)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
- struct usbout *u = &as->usbout;
- unsigned long flags;
- unsigned int mask;
- int suret = 0;
-
-#if 0
- printk(KERN_DEBUG "usbout_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);
-#endif
- if (urb == u->surb[0].urb)
- mask = FLG_SYNC0RUNNING;
- else if (urb == u->surb[1].urb)
- mask = FLG_SYNC1RUNNING;
- else {
- mask = 0;
- printk(KERN_ERR "usbout_sync_completed: panic: unknown URB\n");
- }
- urb->dev = as->state->usbdev;
- spin_lock_irqsave(&as->lock, flags);
- if (!usbout_sync_retire_desc(u, urb) &&
- u->flags & FLG_RUNNING &&
- !usbout_sync_prepare_desc(u, urb) &&
- (suret = usb_submit_urb(urb, GFP_ATOMIC)) == 0) {
- u->flags |= mask;
- } else {
- u->flags &= ~(mask | FLG_RUNNING);
- wake_up(&u->dma.wait);
- dprintk((KERN_DEBUG "usbout_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret));
- }
- spin_unlock_irqrestore(&as->lock, flags);
-}
-
-static int usbout_start(struct usb_audiodev *as)
-{
- struct usb_device *dev = as->state->usbdev;
- struct usbout *u = &as->usbout;
- struct urb *urb;
- unsigned long flags;
- unsigned int maxsze, bufsz;
-
-#if 0
- printk(KERN_DEBUG "usbout_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n",
- dev->devnum, u->format, u->dma.format, u->dma.srate);
-#endif
- /* allocate USB storage if not already done */
- spin_lock_irqsave(&as->lock, flags);
- if (!(u->flags & FLG_CONNECTED)) {
- spin_unlock_irqrestore(&as->lock, flags);
- return -EIO;
- }
- if (!(u->flags & FLG_RUNNING)) {
- spin_unlock_irqrestore(&as->lock, flags);
- u->freqn = u->freqm = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */
- u->freqmax = u->freqn + (u->freqn >> 2);
- u->phase = 0;
- maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
- bufsz = DESCFRAMES * maxsze;
- kfree(u->durb[0].urb->transfer_buffer);
- u->durb[0].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
- u->durb[0].urb->transfer_buffer_length = bufsz;
- kfree(u->durb[1].urb->transfer_buffer);
- u->durb[1].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
- u->durb[1].urb->transfer_buffer_length = bufsz;
- if (u->syncpipe) {
- kfree(u->surb[0].urb->transfer_buffer);
- u->surb[0].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
- u->surb[0].urb->transfer_buffer_length = 3*SYNCFRAMES;
- kfree(u->surb[1].urb->transfer_buffer);
- u->surb[1].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
- u->surb[1].urb->transfer_buffer_length = 3*SYNCFRAMES;
- }
- if (!u->durb[0].urb->transfer_buffer || !u->durb[1].urb->transfer_buffer ||
- (u->syncpipe && (!u->surb[0].urb->transfer_buffer || !u->surb[1].urb->transfer_buffer))) {
- printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum);
- return 0;
- }
- spin_lock_irqsave(&as->lock, flags);
- }
- if (u->dma.count <= 0 && !u->dma.mapped) {
- spin_unlock_irqrestore(&as->lock, flags);
- return 0;
- }
- u->flags |= FLG_RUNNING;
- if (!(u->flags & FLG_URB0RUNNING)) {
- urb = u->durb[0].urb;
- urb->dev = dev;
- urb->pipe = u->datapipe;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->number_of_packets = DESCFRAMES;
- urb->context = as;
- urb->complete = usbout_completed;
- if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_ATOMIC))
- u->flags |= FLG_URB0RUNNING;
- else
- u->flags &= ~FLG_RUNNING;
- }
- if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) {
- urb = u->durb[1].urb;
- urb->dev = dev;
- urb->pipe = u->datapipe;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->number_of_packets = DESCFRAMES;
- urb->context = as;
- urb->complete = usbout_completed;
- if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_ATOMIC))
- u->flags |= FLG_URB1RUNNING;
- else
- u->flags &= ~FLG_RUNNING;
- }
- if (u->syncpipe) {
- if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) {
- urb = u->surb[0].urb;
- urb->dev = dev;
- urb->pipe = u->syncpipe;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->number_of_packets = SYNCFRAMES;
- urb->context = as;
- urb->complete = usbout_sync_completed;
- /* stride: u->syncinterval */
- if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_ATOMIC))
- u->flags |= FLG_SYNC0RUNNING;
- else
- u->flags &= ~FLG_RUNNING;
- }
- if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) {
- urb = u->surb[1].urb;
- urb->dev = dev;
- urb->pipe = u->syncpipe;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->number_of_packets = SYNCFRAMES;
- urb->context = as;
- urb->complete = usbout_sync_completed;
- /* stride: u->syncinterval */
- if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_ATOMIC))
- u->flags |= FLG_SYNC1RUNNING;
- else
- u->flags &= ~FLG_RUNNING;
- }
- }
- spin_unlock_irqrestore(&as->lock, flags);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static unsigned int format_goodness(struct audioformat *afp, unsigned int fmt, unsigned int srate)
-{
- unsigned int g = 0;
-
- if (srate < afp->sratelo)
- g += afp->sratelo - srate;
- if (srate > afp->sratehi)
- g += srate - afp->sratehi;
- if (AFMT_ISSTEREO(afp->format) && !AFMT_ISSTEREO(fmt))
- g += 0x100000;
- if (!AFMT_ISSTEREO(afp->format) && AFMT_ISSTEREO(fmt))
- g += 0x400000;
- if (AFMT_IS16BIT(afp->format) && !AFMT_IS16BIT(fmt))
- g += 0x100000;
- if (!AFMT_IS16BIT(afp->format) && AFMT_IS16BIT(fmt))
- g += 0x400000;
- return g;
-}
-
-static int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int srate)
-{
- unsigned int i, g, gb = ~0;
- int j = -1; /* default to failure */
-
- /* find "best" format (according to format_goodness) */
- for (i = 0; i < nr; i++) {
- g = format_goodness(&afp[i], fmt, srate);
- if (g >= gb)
- continue;
- j = i;
- gb = g;
- }
- return j;
-}
-
-static int set_format_in(struct usb_audiodev *as)
-{
- struct usb_device *dev = as->state->usbdev;
- struct usb_host_interface *alts;
- struct usb_interface *iface;
- struct usbin *u = &as->usbin;
- struct dmabuf *d = &u->dma;
- struct audioformat *fmt;
- unsigned int ep;
- unsigned char data[3];
- int fmtnr, ret;
-
- iface = usb_ifnum_to_if(dev, u->interface);
- if (!iface)
- return 0;
-
- fmtnr = find_format(as->fmtin, as->numfmtin, d->format, d->srate);
- if (fmtnr < 0) {
- printk(KERN_ERR "usbaudio: set_format_in(): failed to find desired format/speed combination.\n");
- return -1;
- }
-
- fmt = as->fmtin + fmtnr;
- alts = usb_altnum_to_altsetting(iface, fmt->altsetting);
- u->format = fmt->format;
- u->datapipe = usb_rcvisocpipe(dev, alts->endpoint[0].desc.bEndpointAddress & 0xf);
- u->syncpipe = u->syncinterval = 0;
- if ((alts->endpoint[0].desc.bmAttributes & 0x0c) == 0x08) {
- if (alts->desc.bNumEndpoints < 2 ||
- alts->endpoint[1].desc.bmAttributes != 0x01 ||
- alts->endpoint[1].desc.bSynchAddress != 0 ||
- alts->endpoint[1].desc.bEndpointAddress != (alts->endpoint[0].desc.bSynchAddress & 0x7f)) {
- printk(KERN_WARNING "usbaudio: device %d interface %d altsetting %d claims adaptive in "
- "but has invalid synch pipe; treating as asynchronous in\n",
- dev->devnum, u->interface, fmt->altsetting);
- } else {
- u->syncpipe = usb_sndisocpipe(dev, alts->endpoint[1].desc.bEndpointAddress & 0xf);
- u->syncinterval = alts->endpoint[1].desc.bRefresh;
- }
- }
- if (d->srate < fmt->sratelo)
- d->srate = fmt->sratelo;
- if (d->srate > fmt->sratehi)
- d->srate = fmt->sratehi;
- dprintk((KERN_DEBUG "usbaudio: set_format_in: usb_set_interface %u %u\n",
- u->interface, fmt->altsetting));
- if (usb_set_interface(dev, alts->desc.bInterfaceNumber, fmt->altsetting) < 0) {
- printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n",
- dev->devnum, u->interface, fmt->altsetting);
- return -1;
- }
- if (fmt->sratelo == fmt->sratehi)
- return 0;
- ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN);
- /* if endpoint has pitch control, enable it */
- if (fmt->attributes & 0x02) {
- data[0] = 1;
- if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
- PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) {
- printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n",
- ret, dev->devnum, u->interface, ep, d->srate);
- return -1;
- }
- }
- /* if endpoint has sampling rate control, set it */
- if (fmt->attributes & 0x01) {
- data[0] = d->srate;
- data[1] = d->srate >> 8;
- data[2] = d->srate >> 16;
- if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
- SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) {
- printk(KERN_ERR "usbaudio: failure (error %d) to set input sampling frequency device %d interface %u endpoint 0x%x to %u\n",
- ret, dev->devnum, u->interface, ep, d->srate);
- return -1;
- }
- if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
- SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) {
- printk(KERN_ERR "usbaudio: failure (error %d) to get input sampling frequency device %d interface %u endpoint 0x%x\n",
- ret, dev->devnum, u->interface, ep);
- return -1;
- }
- dprintk((KERN_DEBUG "usbaudio: set_format_in: device %d interface %d altsetting %d srate req: %u real %u\n",
- dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16)));
- d->srate = data[0] | (data[1] << 8) | (data[2] << 16);
- }
- dprintk((KERN_DEBUG "usbaudio: set_format_in: USB format 0x%x, DMA format 0x%x srate %u\n", u->format, d->format, d->srate));
- return 0;
-}
-
-static int set_format_out(struct usb_audiodev *as)
-{
- struct usb_device *dev = as->state->usbdev;
- struct usb_host_interface *alts;
- struct usb_interface *iface;
- struct usbout *u = &as->usbout;
- struct dmabuf *d = &u->dma;
- struct audioformat *fmt;
- unsigned int ep;
- unsigned char data[3];
- int fmtnr, ret;
-
- iface = usb_ifnum_to_if(dev, u->interface);
- if (!iface)
- return 0;
-
- fmtnr = find_format(as->fmtout, as->numfmtout, d->format, d->srate);
- if (fmtnr < 0) {
- printk(KERN_ERR "usbaudio: set_format_out(): failed to find desired format/speed combination.\n");
- return -1;
- }
-
- fmt = as->fmtout + fmtnr;
- u->format = fmt->format;
- alts = usb_altnum_to_altsetting(iface, fmt->altsetting);
- u->datapipe = usb_sndisocpipe(dev, alts->endpoint[0].desc.bEndpointAddress & 0xf);
- u->syncpipe = u->syncinterval = 0;
- if ((alts->endpoint[0].desc.bmAttributes & 0x0c) == 0x04) {
-#if 0
- printk(KERN_DEBUG "bNumEndpoints 0x%02x endpoint[1].bmAttributes 0x%02x\n"
- KERN_DEBUG "endpoint[1].bSynchAddress 0x%02x endpoint[1].bEndpointAddress 0x%02x\n"
- KERN_DEBUG "endpoint[0].bSynchAddress 0x%02x\n", alts->bNumEndpoints,
- alts->endpoint[1].bmAttributes, alts->endpoint[1].bSynchAddress,
- alts->endpoint[1].bEndpointAddress, alts->endpoint[0].bSynchAddress);
-#endif
- if (alts->desc.bNumEndpoints < 2 ||
- alts->endpoint[1].desc.bmAttributes != 0x01 ||
- alts->endpoint[1].desc.bSynchAddress != 0 ||
- alts->endpoint[1].desc.bEndpointAddress != (alts->endpoint[0].desc.bSynchAddress | 0x80)) {
- printk(KERN_WARNING "usbaudio: device %d interface %d altsetting %d claims asynch out "
- "but has invalid synch pipe; treating as adaptive out\n",
- dev->devnum, u->interface, fmt->altsetting);
- } else {
- u->syncpipe = usb_rcvisocpipe(dev, alts->endpoint[1].desc.bEndpointAddress & 0xf);
- u->syncinterval = alts->endpoint[1].desc.bRefresh;
- }
- }
- if (d->srate < fmt->sratelo)
- d->srate = fmt->sratelo;
- if (d->srate > fmt->sratehi)
- d->srate = fmt->sratehi;
- dprintk((KERN_DEBUG "usbaudio: set_format_out: usb_set_interface %u %u\n",
- u->interface, fmt->altsetting));
- if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) {
- printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n",
- dev->devnum, u->interface, fmt->altsetting);
- return -1;
- }
- if (fmt->sratelo == fmt->sratehi)
- return 0;
- ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN);
- /* if endpoint has pitch control, enable it */
- if (fmt->attributes & 0x02) {
- data[0] = 1;
- if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
- PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) {
- printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n",
- ret, dev->devnum, u->interface, ep, d->srate);
- return -1;
- }
- }
- /* if endpoint has sampling rate control, set it */
- if (fmt->attributes & 0x01) {
- data[0] = d->srate;
- data[1] = d->srate >> 8;
- data[2] = d->srate >> 16;
- if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
- SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) {
- printk(KERN_ERR "usbaudio: failure (error %d) to set output sampling frequency device %d interface %u endpoint 0x%x to %u\n",
- ret, dev->devnum, u->interface, ep, d->srate);
- return -1;
- }
- if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
- SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) {
- printk(KERN_ERR "usbaudio: failure (error %d) to get output sampling frequency device %d interface %u endpoint 0x%x\n",
- ret, dev->devnum, u->interface, ep);
- return -1;
- }
- dprintk((KERN_DEBUG "usbaudio: set_format_out: device %d interface %d altsetting %d srate req: %u real %u\n",
- dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16)));
- d->srate = data[0] | (data[1] << 8) | (data[2] << 16);
- }
- dprintk((KERN_DEBUG "usbaudio: set_format_out: USB format 0x%x, DMA format 0x%x srate %u\n", u->format, d->format, d->srate));
- return 0;
-}
-
-static int set_format(struct usb_audiodev *s, unsigned int fmode, unsigned int fmt, unsigned int srate)
-{
- int ret1 = 0, ret2 = 0;
-
- if (!(fmode & (FMODE_READ|FMODE_WRITE)))
- return -EINVAL;
- if (fmode & FMODE_READ) {
- usbin_stop(s);
- s->usbin.dma.ready = 0;
- if (fmt == AFMT_QUERY)
- fmt = s->usbin.dma.format;
- else
- s->usbin.dma.format = fmt;
- if (!srate)
- srate = s->usbin.dma.srate;
- else
- s->usbin.dma.srate = srate;
- }
- if (fmode & FMODE_WRITE) {
- usbout_stop(s);
- s->usbout.dma.ready = 0;
- if (fmt == AFMT_QUERY)
- fmt = s->usbout.dma.format;
- else
- s->usbout.dma.format = fmt;
- if (!srate)
- srate = s->usbout.dma.srate;
- else
- s->usbout.dma.srate = srate;
- }
- if (fmode & FMODE_READ)
- ret1 = set_format_in(s);
- if (fmode & FMODE_WRITE)
- ret2 = set_format_out(s);
- return ret1 ? ret1 : ret2;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wrmixer(struct usb_mixerdev *ms, unsigned mixch, unsigned value)
-{
- struct usb_device *dev = ms->state->usbdev;
- unsigned char data[2];
- struct mixerchannel *ch;
- int v1, v2, v3;
-
- if (mixch >= ms->numch)
- return -1;
- ch = &ms->ch[mixch];
- v3 = ch->maxval - ch->minval;
- v1 = value & 0xff;
- v2 = (value >> 8) & 0xff;
- if (v1 > 100)
- v1 = 100;
- if (v2 > 100)
- v2 = 100;
- if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
- v2 = v1;
- ch->value = v1 | (v2 << 8);
- v1 = (v1 * v3) / 100 + ch->minval;
- v2 = (v2 * v3) / 100 + ch->minval;
- switch (ch->selector) {
- case 0: /* mixer unit request */
- data[0] = v1;
- data[1] = v1 >> 8;
- if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- (ch->chnum << 8) | 1, ms->iface | (ch->unitid << 8), data, 2, 1000) < 0)
- goto err;
- if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
- return 0;
- data[0] = v2;
- data[1] = v2 >> 8;
- if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)),
- ms->iface | (ch->unitid << 8), data, 2, 1000) < 0)
- goto err;
- return 0;
-
- /* various feature unit controls */
- case VOLUME_CONTROL:
- data[0] = v1;
- data[1] = v1 >> 8;
- if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 2, 1000) < 0)
- goto err;
- if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
- return 0;
- data[0] = v2;
- data[1] = v2 >> 8;
- if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 2, 1000) < 0)
- goto err;
- return 0;
-
- case BASS_CONTROL:
- case MID_CONTROL:
- case TREBLE_CONTROL:
- data[0] = v1 >> 8;
- if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 1, 1000) < 0)
- goto err;
- if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
- return 0;
- data[0] = v2 >> 8;
- if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 1, 1000) < 0)
- goto err;
- return 0;
-
- default:
- return -1;
- }
- return 0;
-
- err:
- printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n",
- dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector);
- return -1;
-}
-
-static int get_rec_src(struct usb_mixerdev *ms)
-{
- struct usb_device *dev = ms->state->usbdev;
- unsigned int mask = 0, retmask = 0;
- unsigned int i, j;
- unsigned char buf;
- int err = 0;
-
- for (i = 0; i < ms->numch; i++) {
- if (!ms->ch[i].slctunitid || (mask & (1 << i)))
- continue;
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, 1000) < 0) {
- err = -EIO;
- printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n",
- dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff);
- continue;
- }
- for (j = i; j < ms->numch; j++) {
- if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff)
- continue;
- mask |= 1 << j;
- if (buf == (ms->ch[j].slctunitid >> 8))
- retmask |= 1 << ms->ch[j].osschannel;
- }
- }
- if (err)
- return -EIO;
- return retmask;
-}
-
-static int set_rec_src(struct usb_mixerdev *ms, int srcmask)
-{
- struct usb_device *dev = ms->state->usbdev;
- unsigned int mask = 0, smask, bmask;
- unsigned int i, j;
- unsigned char buf;
- int err = 0;
-
- for (i = 0; i < ms->numch; i++) {
- if (!ms->ch[i].slctunitid || (mask & (1 << i)))
- continue;
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, 1000) < 0) {
- err = -EIO;
- printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n",
- dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff);
- continue;
- }
- /* first generate smask */
- smask = bmask = 0;
- for (j = i; j < ms->numch; j++) {
- if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff)
- continue;
- smask |= 1 << ms->ch[j].osschannel;
- if (buf == (ms->ch[j].slctunitid >> 8))
- bmask |= 1 << ms->ch[j].osschannel;
- mask |= 1 << j;
- }
- /* check for multiple set sources */
- j = hweight32(srcmask & smask);
- if (j == 0)
- continue;
- if (j > 1)
- srcmask &= ~bmask;
- for (j = i; j < ms->numch; j++) {
- if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff)
- continue;
- if (!(srcmask & (1 << ms->ch[j].osschannel)))
- continue;
- buf = ms->ch[j].slctunitid >> 8;
- if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- 0, ms->iface | (ms->ch[j].slctunitid << 8), &buf, 1, 1000) < 0) {
- err = -EIO;
- printk(KERN_ERR "usbaudio: selector write request device %u if %u unit %u failed\n",
- dev->devnum, ms->iface, ms->ch[j].slctunitid & 0xff);
- continue;
- }
- }
- }
- return err ? -EIO : 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * should be called with open_sem hold, so that no new processes
- * look at the audio device to be destroyed
- */
-
-static void release(struct usb_audio_state *s)
-{
- struct usb_audiodev *as;
- struct usb_mixerdev *ms;
-
- s->count--;
- if (s->count) {
- up(&open_sem);
- return;
- }
- up(&open_sem);
- wake_up(&open_wait);
- while (!list_empty(&s->audiolist)) {
- as = list_entry(s->audiolist.next, struct usb_audiodev, list);
- list_del(&as->list);
- usbin_release(as);
- usbout_release(as);
- dmabuf_release(&as->usbin.dma);
- dmabuf_release(&as->usbout.dma);
- usb_free_urb(as->usbin.durb[0].urb);
- usb_free_urb(as->usbin.durb[1].urb);
- usb_free_urb(as->usbin.surb[0].urb);
- usb_free_urb(as->usbin.surb[1].urb);
- usb_free_urb(as->usbout.durb[0].urb);
- usb_free_urb(as->usbout.durb[1].urb);
- usb_free_urb(as->usbout.surb[0].urb);
- usb_free_urb(as->usbout.surb[1].urb);
- kfree(as);
- }
- while (!list_empty(&s->mixerlist)) {
- ms = list_entry(s->mixerlist.next, struct usb_mixerdev, list);
- list_del(&ms->list);
- kfree(ms);
- }
- kfree(s);
-}
-
-static inline int prog_dmabuf_in(struct usb_audiodev *as)
-{
- usbin_stop(as);
- return dmabuf_init(&as->usbin.dma);
-}
-
-static inline int prog_dmabuf_out(struct usb_audiodev *as)
-{
- usbout_stop(as);
- return dmabuf_init(&as->usbout.dma);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int usb_audio_open_mixdev(struct inode *inode, struct file *file)
-{
- unsigned int minor = iminor(inode);
- struct usb_mixerdev *ms;
- struct usb_audio_state *s;
-
- down(&open_sem);
- list_for_each_entry(s, &audiodevs, audiodev) {
- list_for_each_entry(ms, &s->mixerlist, list) {
- if (ms->dev_mixer == minor)
- goto mixer_found;
- }
- }
- up(&open_sem);
- return -ENODEV;
-
- mixer_found:
- if (!s->usbdev) {
- up(&open_sem);
- return -EIO;
- }
- file->private_data = ms;
- s->count++;
-
- up(&open_sem);
- return nonseekable_open(inode, file);
-}
-
-static int usb_audio_release_mixdev(struct inode *inode, struct file *file)
-{
- struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data;
- struct usb_audio_state *s;
-
- lock_kernel();
- s = ms->state;
- down(&open_sem);
- release(s);
- unlock_kernel();
- return 0;
-}
-
-static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data;
- int i, j, val;
- int __user *user_arg = (int __user *)arg;
-
- if (!ms->state->usbdev)
- return -ENODEV;
-
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
-
- memset(&info, 0, sizeof(info));
- strncpy(info.id, "USB_AUDIO", sizeof(info.id));
- strncpy(info.name, "USB Audio Class Driver", sizeof(info.name));
- info.modify_counter = ms->modcnt;
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
-
- memset(&info, 0, sizeof(info));
- strncpy(info.id, "USB_AUDIO", sizeof(info.id));
- strncpy(info.name, "USB Audio Class Driver", sizeof(info.name));
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == OSS_GETVERSION)
- return put_user(SOUND_VERSION, user_arg);
- if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
- if (_IOC_DIR(cmd) == _IOC_READ) {
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
- val = get_rec_src(ms);
- if (val < 0)
- return val;
- return put_user(val, user_arg);
-
- case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
- for (val = i = 0; i < ms->numch; i++)
- val |= 1 << ms->ch[i].osschannel;
- return put_user(val, user_arg);
-
- case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
- for (val = i = 0; i < ms->numch; i++)
- if (ms->ch[i].slctunitid)
- val |= 1 << ms->ch[i].osschannel;
- return put_user(val, user_arg);
-
- case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
- for (val = i = 0; i < ms->numch; i++)
- if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))
- val |= 1 << ms->ch[i].osschannel;
- return put_user(val, user_arg);
-
- case SOUND_MIXER_CAPS:
- return put_user(SOUND_CAP_EXCL_INPUT, user_arg);
-
- default:
- i = _IOC_NR(cmd);
- if (i >= SOUND_MIXER_NRDEVICES)
- return -EINVAL;
- for (j = 0; j < ms->numch; j++) {
- if (ms->ch[j].osschannel == i) {
- return put_user(ms->ch[j].value, user_arg);
- }
- }
- return -EINVAL;
- }
- }
- if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE))
- return -EINVAL;
- ms->modcnt++;
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
- if (get_user(val, user_arg))
- return -EFAULT;
- return set_rec_src(ms, val);
-
- default:
- i = _IOC_NR(cmd);
- if (i >= SOUND_MIXER_NRDEVICES)
- return -EINVAL;
- for (j = 0; j < ms->numch && ms->ch[j].osschannel != i; j++);
- if (j >= ms->numch)
- return -EINVAL;
- if (get_user(val, user_arg))
- return -EFAULT;
- if (wrmixer(ms, j, val))
- return -EIO;
- return put_user(ms->ch[j].value, user_arg);
- }
-}
-
-static /*const*/ struct file_operations usb_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .ioctl = usb_audio_ioctl_mixdev,
- .open = usb_audio_open_mixdev,
- .release = usb_audio_release_mixdev,
-};
-
-/* --------------------------------------------------------------------- */
-
-static int drain_out(struct usb_audiodev *as, int nonblock)
-{
- DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
- int count, tmo;
-
- if (as->usbout.dma.mapped || !as->usbout.dma.ready)
- return 0;
- usbout_start(as);
- add_wait_queue(&as->usbout.dma.wait, &wait);
- for (;;) {
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_lock_irqsave(&as->lock, flags);
- count = as->usbout.dma.count;
- spin_unlock_irqrestore(&as->lock, flags);
- if (count <= 0)
- break;
- if (signal_pending(current))
- break;
- if (nonblock) {
- remove_wait_queue(&as->usbout.dma.wait, &wait);
- set_current_state(TASK_RUNNING);
- return -EBUSY;
- }
- tmo = 3 * HZ * count / as->usbout.dma.srate;
- tmo >>= AFMT_BYTESSHIFT(as->usbout.dma.format);
- if (!schedule_timeout(tmo + 1)) {
- printk(KERN_DEBUG "usbaudio: dma timed out??\n");
- break;
- }
- }
- remove_wait_queue(&as->usbout.dma.wait, &wait);
- set_current_state(TASK_RUNNING);
- if (signal_pending(current))
- return -ERESTARTSYS;
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static ssize_t usb_audio_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret = 0;
- unsigned long flags;
- unsigned int ptr;
- int cnt, err;
-
- if (as->usbin.dma.mapped)
- return -ENXIO;
- if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as)))
- return ret;
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- add_wait_queue(&as->usbin.dma.wait, &wait);
- while (count > 0) {
- spin_lock_irqsave(&as->lock, flags);
- ptr = as->usbin.dma.rdptr;
- cnt = as->usbin.dma.count;
- /* set task state early to avoid wakeup races */
- if (cnt <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&as->lock, flags);
- if (cnt > count)
- cnt = count;
- if (cnt <= 0) {
- if (usbin_start(as)) {
- if (!ret)
- ret = -ENODEV;
- break;
- }
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- break;
- }
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- break;
- }
- continue;
- }
- if ((err = dmabuf_copyout_user(&as->usbin.dma, ptr, buffer, cnt))) {
- if (!ret)
- ret = err;
- break;
- }
- ptr += cnt;
- if (ptr >= as->usbin.dma.dmasize)
- ptr -= as->usbin.dma.dmasize;
- spin_lock_irqsave(&as->lock, flags);
- as->usbin.dma.rdptr = ptr;
- as->usbin.dma.count -= cnt;
- spin_unlock_irqrestore(&as->lock, flags);
- count -= cnt;
- buffer += cnt;
- ret += cnt;
- }
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&as->usbin.dma.wait, &wait);
- return ret;
-}
-
-static ssize_t usb_audio_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret = 0;
- unsigned long flags;
- unsigned int ptr;
- unsigned int start_thr;
- int cnt, err;
-
- if (as->usbout.dma.mapped)
- return -ENXIO;
- if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as)))
- return ret;
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
- start_thr = (as->usbout.dma.srate << AFMT_BYTESSHIFT(as->usbout.dma.format)) / (1000 / (3 * DESCFRAMES));
- add_wait_queue(&as->usbout.dma.wait, &wait);
- while (count > 0) {
-#if 0
- printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%lx\n",
- count, as->usbout.dma.count, as->usbout.dma.rdptr, as->usbout.dma.wrptr, as->usbout.dma.dmasize, as->usbout.dma.fragsize,
- as->usbout.flags, current->state);
-#endif
- spin_lock_irqsave(&as->lock, flags);
- if (as->usbout.dma.count < 0) {
- as->usbout.dma.count = 0;
- as->usbout.dma.rdptr = as->usbout.dma.wrptr;
- }
- ptr = as->usbout.dma.wrptr;
- cnt = as->usbout.dma.dmasize - as->usbout.dma.count;
- /* set task state early to avoid wakeup races */
- if (cnt <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&as->lock, flags);
- if (cnt > count)
- cnt = count;
- if (cnt <= 0) {
- if (usbout_start(as)) {
- if (!ret)
- ret = -ENODEV;
- break;
- }
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- break;
- }
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- break;
- }
- continue;
- }
- if ((err = dmabuf_copyin_user(&as->usbout.dma, ptr, buffer, cnt))) {
- if (!ret)
- ret = err;
- break;
- }
- ptr += cnt;
- if (ptr >= as->usbout.dma.dmasize)
- ptr -= as->usbout.dma.dmasize;
- spin_lock_irqsave(&as->lock, flags);
- as->usbout.dma.wrptr = ptr;
- as->usbout.dma.count += cnt;
- spin_unlock_irqrestore(&as->lock, flags);
- count -= cnt;
- buffer += cnt;
- ret += cnt;
- if (as->usbout.dma.count >= start_thr && usbout_start(as)) {
- if (!ret)
- ret = -ENODEV;
- break;
- }
- }
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&as->usbout.dma.wait, &wait);
- return ret;
-}
-
-/* Called without the kernel lock - fine */
-static unsigned int usb_audio_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
- unsigned long flags;
- unsigned int mask = 0;
-
- if (file->f_mode & FMODE_WRITE) {
- if (!as->usbout.dma.ready)
- prog_dmabuf_out(as);
- poll_wait(file, &as->usbout.dma.wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
- if (!as->usbin.dma.ready)
- prog_dmabuf_in(as);
- poll_wait(file, &as->usbin.dma.wait, wait);
- }
- spin_lock_irqsave(&as->lock, flags);
- if (file->f_mode & FMODE_READ) {
- if (as->usbin.dma.count >= (signed)as->usbin.dma.fragsize)
- mask |= POLLIN | POLLRDNORM;
- }
- if (file->f_mode & FMODE_WRITE) {
- if (as->usbout.dma.mapped) {
- if (as->usbout.dma.count >= (signed)as->usbout.dma.fragsize)
- mask |= POLLOUT | POLLWRNORM;
- } else {
- if ((signed)as->usbout.dma.dmasize >= as->usbout.dma.count + (signed)as->usbout.dma.fragsize)
- mask |= POLLOUT | POLLWRNORM;
- }
- }
- spin_unlock_irqrestore(&as->lock, flags);
- return mask;
-}
-
-static int usb_audio_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
- struct dmabuf *db;
- int ret = -EINVAL;
-
- lock_kernel();
- if (vma->vm_flags & VM_WRITE) {
- if ((ret = prog_dmabuf_out(as)) != 0)
- goto out;
- db = &as->usbout.dma;
- } else if (vma->vm_flags & VM_READ) {
- if ((ret = prog_dmabuf_in(as)) != 0)
- goto out;
- db = &as->usbin.dma;
- } else
- goto out;
-
- ret = -EINVAL;
- if (vma->vm_pgoff != 0)
- goto out;
-
- ret = dmabuf_mmap(vma, db, vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot);
-out:
- unlock_kernel();
- return ret;
-}
-
-static int usb_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
- struct usb_audio_state *s = as->state;
- int __user *user_arg = (int __user *)arg;
- unsigned long flags;
- audio_buf_info abinfo;
- count_info cinfo;
- int val = 0;
- int val2, mapped, ret;
-
- if (!s->usbdev)
- return -EIO;
- mapped = ((file->f_mode & FMODE_WRITE) && as->usbout.dma.mapped) ||
- ((file->f_mode & FMODE_READ) && as->usbin.dma.mapped);
-#if 0
- if (arg)
- get_user(val, (int *)arg);
- printk(KERN_DEBUG "usbaudio: usb_audio_ioctl cmd=%x arg=%lx *arg=%d\n", cmd, arg, val)
-#endif
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, user_arg);
-
- case SNDCTL_DSP_SYNC:
- if (file->f_mode & FMODE_WRITE)
- return drain_out(as, 0/*file->f_flags & O_NONBLOCK*/);
- return 0;
-
- case SNDCTL_DSP_SETDUPLEX:
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER |
- DSP_CAP_MMAP | DSP_CAP_BATCH, user_arg);
-
- case SNDCTL_DSP_RESET:
- if (file->f_mode & FMODE_WRITE) {
- usbout_stop(as);
- as->usbout.dma.rdptr = as->usbout.dma.wrptr = as->usbout.dma.count = as->usbout.dma.total_bytes = 0;
- }
- if (file->f_mode & FMODE_READ) {
- usbin_stop(as);
- as->usbin.dma.rdptr = as->usbin.dma.wrptr = as->usbin.dma.count = as->usbin.dma.total_bytes = 0;
- }
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, user_arg))
- return -EFAULT;
- if (val >= 0) {
- if (val < 4000)
- val = 4000;
- if (val > 100000)
- val = 100000;
- if (set_format(as, file->f_mode, AFMT_QUERY, val))
- return -EIO;
- }
- return put_user((file->f_mode & FMODE_READ) ?
- as->usbin.dma.srate : as->usbout.dma.srate,
- user_arg);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, user_arg))
- return -EFAULT;
- val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
- if (val)
- val2 |= AFMT_STEREO;
- else
- val2 &= ~AFMT_STEREO;
- if (set_format(as, file->f_mode, val2, 0))
- return -EIO;
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, user_arg))
- return -EFAULT;
- if (val != 0) {
- val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
- if (val == 1)
- val2 &= ~AFMT_STEREO;
- else
- val2 |= AFMT_STEREO;
- if (set_format(as, file->f_mode, val2, 0))
- return -EIO;
- }
- val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
- return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, user_arg);
-
- case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE |
- AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE, user_arg);
-
- case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
- if (get_user(val, user_arg))
- return -EFAULT;
- if (val != AFMT_QUERY) {
- if (hweight32(val) != 1)
- return -EINVAL;
- if (!(val & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE |
- AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE)))
- return -EINVAL;
- val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
- val |= val2 & AFMT_STEREO;
- if (set_format(as, file->f_mode, val, 0))
- return -EIO;
- }
- val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
- return put_user(val2 & ~AFMT_STEREO, user_arg);
-
- case SNDCTL_DSP_POST:
- return 0;
-
- case SNDCTL_DSP_GETTRIGGER:
- val = 0;
- if (file->f_mode & FMODE_READ && as->usbin.flags & FLG_RUNNING)
- val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && as->usbout.flags & FLG_RUNNING)
- val |= PCM_ENABLE_OUTPUT;
- return put_user(val, user_arg);
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, user_arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as)))
- return ret;
- if (usbin_start(as))
- return -ENODEV;
- } else
- usbin_stop(as);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as)))
- return ret;
- if (usbout_start(as))
- return -ENODEV;
- } else
- usbout_stop(as);
- }
- return 0;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- if (!(as->usbout.flags & FLG_RUNNING) && (val = prog_dmabuf_out(as)) != 0)
- return val;
- spin_lock_irqsave(&as->lock, flags);
- abinfo.fragsize = as->usbout.dma.fragsize;
- abinfo.bytes = as->usbout.dma.dmasize - as->usbout.dma.count;
- abinfo.fragstotal = as->usbout.dma.numfrag;
- abinfo.fragments = abinfo.bytes >> as->usbout.dma.fragshift;
- spin_unlock_irqrestore(&as->lock, flags);
- return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- if (!(as->usbin.flags & FLG_RUNNING) && (val = prog_dmabuf_in(as)) != 0)
- return val;
- spin_lock_irqsave(&as->lock, flags);
- abinfo.fragsize = as->usbin.dma.fragsize;
- abinfo.bytes = as->usbin.dma.count;
- abinfo.fragstotal = as->usbin.dma.numfrag;
- abinfo.fragments = abinfo.bytes >> as->usbin.dma.fragshift;
- spin_unlock_irqrestore(&as->lock, flags);
- return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_NONBLOCK:
- file->f_flags |= O_NONBLOCK;
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- spin_lock_irqsave(&as->lock, flags);
- val = as->usbout.dma.count;
- spin_unlock_irqrestore(&as->lock, flags);
- return put_user(val, user_arg);
-
- case SNDCTL_DSP_GETIPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&as->lock, flags);
- cinfo.bytes = as->usbin.dma.total_bytes;
- cinfo.blocks = as->usbin.dma.count >> as->usbin.dma.fragshift;
- cinfo.ptr = as->usbin.dma.wrptr;
- if (as->usbin.dma.mapped)
- as->usbin.dma.count &= as->usbin.dma.fragsize-1;
- spin_unlock_irqrestore(&as->lock, flags);
- if (copy_to_user((void __user *)arg, &cinfo, sizeof(cinfo)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETOPTR:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- spin_lock_irqsave(&as->lock, flags);
- cinfo.bytes = as->usbout.dma.total_bytes;
- cinfo.blocks = as->usbout.dma.count >> as->usbout.dma.fragshift;
- cinfo.ptr = as->usbout.dma.rdptr;
- if (as->usbout.dma.mapped)
- as->usbout.dma.count &= as->usbout.dma.fragsize-1;
- spin_unlock_irqrestore(&as->lock, flags);
- if (copy_to_user((void __user *)arg, &cinfo, sizeof(cinfo)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETBLKSIZE:
- if (file->f_mode & FMODE_WRITE) {
- if ((val = prog_dmabuf_out(as)))
- return val;
- return put_user(as->usbout.dma.fragsize, user_arg);
- }
- if ((val = prog_dmabuf_in(as)))
- return val;
- return put_user(as->usbin.dma.fragsize, user_arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, user_arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- as->usbin.dma.ossfragshift = val & 0xffff;
- as->usbin.dma.ossmaxfrags = (val >> 16) & 0xffff;
- if (as->usbin.dma.ossfragshift < 4)
- as->usbin.dma.ossfragshift = 4;
- if (as->usbin.dma.ossfragshift > 15)
- as->usbin.dma.ossfragshift = 15;
- if (as->usbin.dma.ossmaxfrags < 4)
- as->usbin.dma.ossmaxfrags = 4;
- }
- if (file->f_mode & FMODE_WRITE) {
- as->usbout.dma.ossfragshift = val & 0xffff;
- as->usbout.dma.ossmaxfrags = (val >> 16) & 0xffff;
- if (as->usbout.dma.ossfragshift < 4)
- as->usbout.dma.ossfragshift = 4;
- if (as->usbout.dma.ossfragshift > 15)
- as->usbout.dma.ossfragshift = 15;
- if (as->usbout.dma.ossmaxfrags < 4)
- as->usbout.dma.ossmaxfrags = 4;
- }
- return 0;
-
- case SNDCTL_DSP_SUBDIVIDE:
- if ((file->f_mode & FMODE_READ && as->usbin.dma.subdivision) ||
- (file->f_mode & FMODE_WRITE && as->usbout.dma.subdivision))
- return -EINVAL;
- if (get_user(val, user_arg))
- return -EFAULT;
- if (val != 1 && val != 2 && val != 4)
- return -EINVAL;
- if (file->f_mode & FMODE_READ)
- as->usbin.dma.subdivision = val;
- if (file->f_mode & FMODE_WRITE)
- as->usbout.dma.subdivision = val;
- return 0;
-
- case SOUND_PCM_READ_RATE:
- return put_user((file->f_mode & FMODE_READ) ?
- as->usbin.dma.srate : as->usbout.dma.srate,
- user_arg);
-
- case SOUND_PCM_READ_CHANNELS:
- val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
- return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, user_arg);
-
- case SOUND_PCM_READ_BITS:
- val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
- return put_user(AFMT_IS16BIT(val2) ? 16 : 8, user_arg);
-
- case SOUND_PCM_WRITE_FILTER:
- case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_READ_FILTER:
- return -EINVAL;
- }
- dprintk((KERN_DEBUG "usbaudio: usb_audio_ioctl - no command found\n"));
- return -ENOIOCTLCMD;
-}
-
-static int usb_audio_open(struct inode *inode, struct file *file)
-{
- unsigned int minor = iminor(inode);
- DECLARE_WAITQUEUE(wait, current);
- struct usb_audiodev *as;
- struct usb_audio_state *s;
-
- for (;;) {
- down(&open_sem);
- list_for_each_entry(s, &audiodevs, audiodev) {
- list_for_each_entry(as, &s->audiolist, list) {
- if (!((as->dev_audio ^ minor) & ~0xf))
- goto device_found;
- }
- }
- up(&open_sem);
- return -ENODEV;
-
- device_found:
- if (!s->usbdev) {
- up(&open_sem);
- return -EIO;
- }
- /* wait for device to become free */
- if (!(as->open_mode & file->f_mode))
- break;
- if (file->f_flags & O_NONBLOCK) {
- up(&open_sem);
- return -EBUSY;
- }
- __set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&open_wait, &wait);
- up(&open_sem);
- schedule();
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&open_wait, &wait);
- if (signal_pending(current))
- return -ERESTARTSYS;
- }
- if (file->f_mode & FMODE_READ)
- as->usbin.dma.ossfragshift = as->usbin.dma.ossmaxfrags = as->usbin.dma.subdivision = 0;
- if (file->f_mode & FMODE_WRITE)
- as->usbout.dma.ossfragshift = as->usbout.dma.ossmaxfrags = as->usbout.dma.subdivision = 0;
- if (set_format(as, file->f_mode, ((minor & 0xf) == SND_DEV_DSP16) ? AFMT_S16_LE : AFMT_U8 /* AFMT_ULAW */, 8000)) {
- up(&open_sem);
- return -EIO;
- }
- file->private_data = as;
- as->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- s->count++;
- up(&open_sem);
- return nonseekable_open(inode, file);
-}
-
-static int usb_audio_release(struct inode *inode, struct file *file)
-{
- struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
- struct usb_audio_state *s;
- struct usb_device *dev;
-
- lock_kernel();
- s = as->state;
- dev = s->usbdev;
- if (file->f_mode & FMODE_WRITE)
- drain_out(as, file->f_flags & O_NONBLOCK);
- down(&open_sem);
- if (file->f_mode & FMODE_WRITE) {
- usbout_stop(as);
- if (dev && as->usbout.interface >= 0)
- usb_set_interface(dev, as->usbout.interface, 0);
- dmabuf_release(&as->usbout.dma);
- usbout_release(as);
- }
- if (file->f_mode & FMODE_READ) {
- usbin_stop(as);
- if (dev && as->usbin.interface >= 0)
- usb_set_interface(dev, as->usbin.interface, 0);
- dmabuf_release(&as->usbin.dma);
- usbin_release(as);
- }
- as->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
- release(s);
- wake_up(&open_wait);
- unlock_kernel();
- return 0;
-}
-
-static /*const*/ struct file_operations usb_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = usb_audio_read,
- .write = usb_audio_write,
- .poll = usb_audio_poll,
- .ioctl = usb_audio_ioctl,
- .mmap = usb_audio_mmap,
- .open = usb_audio_open,
- .release = usb_audio_release,
-};
-
-/* --------------------------------------------------------------------- */
-
-static int usb_audio_probe(struct usb_interface *iface,
- const struct usb_device_id *id);
-static void usb_audio_disconnect(struct usb_interface *iface);
-
-static struct usb_device_id usb_audio_ids [] = {
- { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
- .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = 1},
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, usb_audio_ids);
-
-static struct usb_driver usb_audio_driver = {
- .name = "audio",
- .probe = usb_audio_probe,
- .disconnect = usb_audio_disconnect,
- .id_table = usb_audio_ids,
-};
-
-static void *find_descriptor(void *descstart, unsigned int desclen, void *after,
- u8 dtype, int iface, int altsetting)
-{
- u8 *p, *end, *next;
- int ifc = -1, as = -1;
-
- p = descstart;
- end = p + desclen;
- for (; p < end;) {
- if (p[0] < 2)
- return NULL;
- next = p + p[0];
- if (next > end)
- return NULL;
- if (p[1] == USB_DT_INTERFACE) {
- /* minimum length of interface descriptor */
- if (p[0] < 9)
- return NULL;
- ifc = p[2];
- as = p[3];
- }
- if (p[1] == dtype && (!after || (void *)p > after) &&
- (iface == -1 || iface == ifc) && (altsetting == -1 || altsetting == as)) {
- return p;
- }
- p = next;
- }
- return NULL;
-}
-
-static void *find_csinterface_descriptor(void *descstart, unsigned int desclen, void *after, u8 dsubtype, int iface, int altsetting)
-{
- unsigned char *p;
-
- p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, altsetting);
- while (p) {
- if (p[0] >= 3 && p[2] == dsubtype)
- return p;
- p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, altsetting);
- }
- return NULL;
-}
-
-static void *find_audiocontrol_unit(void *descstart, unsigned int desclen, void *after, u8 unit, int iface)
-{
- unsigned char *p;
-
- p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, -1);
- while (p) {
- if (p[0] >= 4 && p[2] >= INPUT_TERMINAL && p[2] <= EXTENSION_UNIT && p[3] == unit)
- return p;
- p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, -1);
- }
- return NULL;
-}
-
-static void usb_audio_parsestreaming(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, int asifin, int asifout)
-{
- struct usb_device *dev = s->usbdev;
- struct usb_audiodev *as;
- struct usb_host_interface *alts;
- struct usb_interface *iface;
- struct audioformat *fp;
- unsigned char *fmt, *csep;
- unsigned int i, j, k, format, idx;
-
- if (!(as = kmalloc(sizeof(struct usb_audiodev), GFP_KERNEL)))
- return;
- memset(as, 0, sizeof(struct usb_audiodev));
- init_waitqueue_head(&as->usbin.dma.wait);
- init_waitqueue_head(&as->usbout.dma.wait);
- spin_lock_init(&as->lock);
- as->usbin.durb[0].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL);
- as->usbin.durb[1].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL);
- as->usbin.surb[0].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL);
- as->usbin.surb[1].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL);
- as->usbout.durb[0].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL);
- as->usbout.durb[1].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL);
- as->usbout.surb[0].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL);
- as->usbout.surb[1].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL);
- if ((!as->usbin.durb[0].urb) ||
- (!as->usbin.durb[1].urb) ||
- (!as->usbin.surb[0].urb) ||
- (!as->usbin.surb[1].urb) ||
- (!as->usbout.durb[0].urb) ||
- (!as->usbout.durb[1].urb) ||
- (!as->usbout.surb[0].urb) ||
- (!as->usbout.surb[1].urb)) {
- usb_free_urb(as->usbin.durb[0].urb);
- usb_free_urb(as->usbin.durb[1].urb);
- usb_free_urb(as->usbin.surb[0].urb);
- usb_free_urb(as->usbin.surb[1].urb);
- usb_free_urb(as->usbout.durb[0].urb);
- usb_free_urb(as->usbout.durb[1].urb);
- usb_free_urb(as->usbout.surb[0].urb);
- usb_free_urb(as->usbout.surb[1].urb);
- kfree(as);
- return;
- }
- as->state = s;
- as->usbin.interface = asifin;
- as->usbout.interface = asifout;
- /* search for input formats */
- if (asifin >= 0) {
- as->usbin.flags = FLG_CONNECTED;
- iface = usb_ifnum_to_if(dev, asifin);
- for (idx = 0; idx < iface->num_altsetting; idx++) {
- alts = &iface->altsetting[idx];
- i = alts->desc.bAlternateSetting;
- if (alts->desc.bInterfaceClass != USB_CLASS_AUDIO || alts->desc.bInterfaceSubClass != 2)
- continue;
- if (alts->desc.bNumEndpoints < 1) {
- if (i != 0) { /* altsetting 0 has no endpoints (Section B.3.4.1) */
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n",
- dev->devnum, asifin, i);
- }
- continue;
- }
- if ((alts->endpoint[0].desc.bmAttributes & 0x03) != 0x01 ||
- !(alts->endpoint[0].desc.bEndpointAddress & 0x80)) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous in\n",
- dev->devnum, asifin, i);
- continue;
- }
- fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifin, i);
- if (!fmt) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n",
- dev->devnum, asifin, i);
- continue;
- }
- if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n",
- dev->devnum, asifin, i);
- continue;
- }
- format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8);
- fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifin, i);
- if (!fmt) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n",
- dev->devnum, asifin, i);
- continue;
- }
- if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n",
- dev->devnum, asifin, i);
- continue;
- }
- if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n",
- dev->devnum, asifin, i, fmt[4], fmt[5]);
- continue;
- }
- csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifin, i);
- if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n",
- dev->devnum, asifin, i);
- continue;
- }
- if (as->numfmtin >= MAXFORMATS)
- continue;
- fp = &as->fmtin[as->numfmtin++];
- if (fmt[5] == 2)
- format &= (AFMT_U16_LE | AFMT_S16_LE);
- else
- format &= (AFMT_U8 | AFMT_S8);
- if (fmt[4] == 2)
- format |= AFMT_STEREO;
- fp->format = format;
- fp->altsetting = i;
- fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16);
- printk(KERN_INFO "usbaudio: valid input sample rate %u\n", fp->sratelo);
- for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) {
- k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16);
- printk(KERN_INFO "usbaudio: valid input sample rate %u\n", k);
- if (k > fp->sratehi)
- fp->sratehi = k;
- if (k < fp->sratelo)
- fp->sratelo = k;
- }
- fp->attributes = csep[3];
- printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n",
- dev->devnum, asifin, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes);
- }
- }
- /* search for output formats */
- if (asifout >= 0) {
- as->usbout.flags = FLG_CONNECTED;
- iface = usb_ifnum_to_if(dev, asifout);
- for (idx = 0; idx < iface->num_altsetting; idx++) {
- alts = &iface->altsetting[idx];
- i = alts->desc.bAlternateSetting;
- if (alts->desc.bInterfaceClass != USB_CLASS_AUDIO || alts->desc.bInterfaceSubClass != 2)
- continue;
- if (alts->desc.bNumEndpoints < 1) {
- /* altsetting 0 should never have iso EPs */
- if (i != 0)
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n",
- dev->devnum, asifout, i);
- continue;
- }
- if ((alts->endpoint[0].desc.bmAttributes & 0x03) != 0x01 ||
- (alts->endpoint[0].desc.bEndpointAddress & 0x80)) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous out\n",
- dev->devnum, asifout, i);
- continue;
- }
- /* See USB audio formats manual, section 2 */
- fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifout, i);
- if (!fmt) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n",
- dev->devnum, asifout, i);
- continue;
- }
- if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n",
- dev->devnum, asifout, i);
- continue;
- }
- format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8);
- /* Dallas DS4201 workaround */
- if (le16_to_cpu(dev->descriptor.idVendor) == 0x04fa &&
- le16_to_cpu(dev->descriptor.idProduct) == 0x4201)
- format = (AFMT_S16_LE | AFMT_S8);
- fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifout, i);
- if (!fmt) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n",
- dev->devnum, asifout, i);
- continue;
- }
- if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n",
- dev->devnum, asifout, i);
- continue;
- }
- if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n",
- dev->devnum, asifout, i, fmt[4], fmt[5]);
- continue;
- }
- csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifout, i);
- if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
- printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n",
- dev->devnum, asifout, i);
- continue;
- }
- if (as->numfmtout >= MAXFORMATS)
- continue;
- fp = &as->fmtout[as->numfmtout++];
- if (fmt[5] == 2)
- format &= (AFMT_U16_LE | AFMT_S16_LE);
- else
- format &= (AFMT_U8 | AFMT_S8);
- if (fmt[4] == 2)
- format |= AFMT_STEREO;
- fp->format = format;
- fp->altsetting = i;
- fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16);
- printk(KERN_INFO "usbaudio: valid output sample rate %u\n", fp->sratelo);
- for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) {
- k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16);
- printk(KERN_INFO "usbaudio: valid output sample rate %u\n", k);
- if (k > fp->sratehi)
- fp->sratehi = k;
- if (k < fp->sratelo)
- fp->sratelo = k;
- }
- fp->attributes = csep[3];
- printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n",
- dev->devnum, asifout, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes);
- }
- }
- if (as->numfmtin == 0 && as->numfmtout == 0) {
- usb_free_urb(as->usbin.durb[0].urb);
- usb_free_urb(as->usbin.durb[1].urb);
- usb_free_urb(as->usbin.surb[0].urb);
- usb_free_urb(as->usbin.surb[1].urb);
- usb_free_urb(as->usbout.durb[0].urb);
- usb_free_urb(as->usbout.durb[1].urb);
- usb_free_urb(as->usbout.surb[0].urb);
- usb_free_urb(as->usbout.surb[1].urb);
- kfree(as);
- return;
- }
- if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1)) < 0) {
- printk(KERN_ERR "usbaudio: cannot register dsp\n");
- usb_free_urb(as->usbin.durb[0].urb);
- usb_free_urb(as->usbin.durb[1].urb);
- usb_free_urb(as->usbin.surb[0].urb);
- usb_free_urb(as->usbin.surb[1].urb);
- usb_free_urb(as->usbout.durb[0].urb);
- usb_free_urb(as->usbout.durb[1].urb);
- usb_free_urb(as->usbout.surb[0].urb);
- usb_free_urb(as->usbout.surb[1].urb);
- kfree(as);
- return;
- }
- printk(KERN_INFO "usbaudio: registered dsp 14,%d\n", as->dev_audio);
- /* everything successful */
- list_add_tail(&as->list, &s->audiolist);
-}
-
-struct consmixstate {
- struct usb_audio_state *s;
- unsigned char *buffer;
- unsigned int buflen;
- unsigned int ctrlif;
- struct mixerchannel mixch[SOUND_MIXER_NRDEVICES];
- unsigned int nrmixch;
- unsigned int mixchmask;
- unsigned long unitbitmap[32/sizeof(unsigned long)];
- /* return values */
- unsigned int nrchannels;
- unsigned int termtype;
- unsigned int chconfig;
-};
-
-static struct mixerchannel *getmixchannel(struct consmixstate *state, unsigned int nr)
-{
- struct mixerchannel *c;
-
- if (nr >= SOUND_MIXER_NRDEVICES) {
- printk(KERN_ERR "usbaudio: invalid OSS mixer channel %u\n", nr);
- return NULL;
- }
- if (!(state->mixchmask & (1 << nr))) {
- printk(KERN_WARNING "usbaudio: OSS mixer channel %u already in use\n", nr);
- return NULL;
- }
- c = &state->mixch[state->nrmixch++];
- c->osschannel = nr;
- state->mixchmask &= ~(1 << nr);
- return c;
-}
-
-static unsigned int getvolchannel(struct consmixstate *state)
-{
- unsigned int u;
-
- if ((state->termtype & 0xff00) == 0x0000 && (state->mixchmask & SOUND_MASK_VOLUME))
- return SOUND_MIXER_VOLUME;
- if ((state->termtype & 0xff00) == 0x0100) {
- if (state->mixchmask & SOUND_MASK_PCM)
- return SOUND_MIXER_PCM;
- if (state->mixchmask & SOUND_MASK_ALTPCM)
- return SOUND_MIXER_ALTPCM;
- }
- if ((state->termtype & 0xff00) == 0x0200 && (state->mixchmask & SOUND_MASK_MIC))
- return SOUND_MIXER_MIC;
- if ((state->termtype & 0xff00) == 0x0300 && (state->mixchmask & SOUND_MASK_SPEAKER))
- return SOUND_MIXER_SPEAKER;
- if ((state->termtype & 0xff00) == 0x0500) {
- if (state->mixchmask & SOUND_MASK_PHONEIN)
- return SOUND_MIXER_PHONEIN;
- if (state->mixchmask & SOUND_MASK_PHONEOUT)
- return SOUND_MIXER_PHONEOUT;
- }
- if (state->termtype >= 0x710 && state->termtype <= 0x711 && (state->mixchmask & SOUND_MASK_RADIO))
- return SOUND_MIXER_RADIO;
- if (state->termtype >= 0x709 && state->termtype <= 0x70f && (state->mixchmask & SOUND_MASK_VIDEO))
- return SOUND_MIXER_VIDEO;
- u = ffs(state->mixchmask & (SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | SOUND_MASK_LINE3 |
- SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | SOUND_MASK_DIGITAL3));
- return u-1;
-}
-
-static void prepmixch(struct consmixstate *state)
-{
- struct usb_device *dev = state->s->usbdev;
- struct mixerchannel *ch;
- unsigned char *buf;
- __s16 v1;
- unsigned int v2, v3;
-
- if (!state->nrmixch || state->nrmixch > SOUND_MIXER_NRDEVICES)
- return;
- buf = kmalloc(sizeof(*buf) * 2, GFP_KERNEL);
- if (!buf) {
- printk(KERN_ERR "prepmixch: out of memory\n") ;
- return;
- }
-
- ch = &state->mixch[state->nrmixch-1];
- switch (ch->selector) {
- case 0: /* mixer unit request */
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0)
- goto err;
- ch->minval = buf[0] | (buf[1] << 8);
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0)
- goto err;
- ch->maxval = buf[0] | (buf[1] << 8);
- v2 = ch->maxval - ch->minval;
- if (!v2)
- v2 = 1;
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0)
- goto err;
- v1 = buf[0] | (buf[1] << 8);
- v3 = v1 - ch->minval;
- v3 = 100 * v3 / v2;
- if (v3 > 100)
- v3 = 100;
- ch->value = v3;
- if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) {
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)),
- state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0)
- goto err;
- v1 = buf[0] | (buf[1] << 8);
- v3 = v1 - ch->minval;
- v3 = 100 * v3 / v2;
- if (v3 > 100)
- v3 = 100;
- }
- ch->value |= v3 << 8;
- break;
-
- /* various feature unit controls */
- case VOLUME_CONTROL:
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0)
- goto err;
- ch->minval = buf[0] | (buf[1] << 8);
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0)
- goto err;
- ch->maxval = buf[0] | (buf[1] << 8);
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0)
- goto err;
- v1 = buf[0] | (buf[1] << 8);
- v2 = ch->maxval - ch->minval;
- v3 = v1 - ch->minval;
- if (!v2)
- v2 = 1;
- v3 = 100 * v3 / v2;
- if (v3 > 100)
- v3 = 100;
- ch->value = v3;
- if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) {
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0)
- goto err;
- v1 = buf[0] | (buf[1] << 8);
- v3 = v1 - ch->minval;
- v3 = 100 * v3 / v2;
- if (v3 > 100)
- v3 = 100;
- }
- ch->value |= v3 << 8;
- break;
-
- case BASS_CONTROL:
- case MID_CONTROL:
- case TREBLE_CONTROL:
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, 1000) < 0)
- goto err;
- ch->minval = buf[0] << 8;
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, 1000) < 0)
- goto err;
- ch->maxval = buf[0] << 8;
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, 1000) < 0)
- goto err;
- v1 = buf[0] << 8;
- v2 = ch->maxval - ch->minval;
- v3 = v1 - ch->minval;
- if (!v2)
- v2 = 1;
- v3 = 100 * v3 / v2;
- if (v3 > 100)
- v3 = 100;
- ch->value = v3;
- if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) {
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 1, 1000) < 0)
- goto err;
- v1 = buf[0] << 8;
- v3 = v1 - ch->minval;
- v3 = 100 * v3 / v2;
- if (v3 > 100)
- v3 = 100;
- }
- ch->value |= v3 << 8;
- break;
-
- default:
- goto err;
- }
-
- freebuf:
- kfree(buf);
- return;
- err:
- printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n",
- dev->devnum, state->ctrlif, ch->unitid, ch->chnum, ch->selector);
- if (state->nrmixch)
- state->nrmixch--;
- goto freebuf;
-}
-
-
-static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid);
-
-static inline int checkmixbmap(unsigned char *bmap, unsigned char flg, unsigned int inidx, unsigned int numoch)
-{
- unsigned int idx;
-
- idx = inidx*numoch;
- if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7))))
- return 0;
- if (!(flg & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
- return 1;
- idx = (inidx+!!(flg & MIXFLG_STEREOIN))*numoch+!!(flg & MIXFLG_STEREOOUT);
- if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7))))
- return 0;
- return 1;
-}
-
-static void usb_audio_mixerunit(struct consmixstate *state, unsigned char *mixer)
-{
- unsigned int nroutch = mixer[5+mixer[4]];
- unsigned int chidx[SOUND_MIXER_NRDEVICES+1];
- unsigned int termt[SOUND_MIXER_NRDEVICES];
- unsigned char flg = (nroutch >= 2) ? MIXFLG_STEREOOUT : 0;
- unsigned char *bmap = &mixer[9+mixer[4]];
- unsigned int bmapsize;
- struct mixerchannel *ch;
- unsigned int i;
-
- if (!mixer[4]) {
- printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor\n", mixer[3]);
- return;
- }
- if (mixer[4] > SOUND_MIXER_NRDEVICES) {
- printk(KERN_ERR "usbaudio: mixer unit %u: too many input pins\n", mixer[3]);
- return;
- }
- chidx[0] = 0;
- for (i = 0; i < mixer[4]; i++) {
- usb_audio_recurseunit(state, mixer[5+i]);
- chidx[i+1] = chidx[i] + state->nrchannels;
- termt[i] = state->termtype;
- }
- state->termtype = 0;
- state->chconfig = mixer[6+mixer[4]] | (mixer[7+mixer[4]] << 8);
- bmapsize = (nroutch * chidx[mixer[4]] + 7) >> 3;
- bmap += bmapsize - 1;
- if (mixer[0] < 10+mixer[4]+bmapsize) {
- printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor (bitmap too small)\n", mixer[3]);
- return;
- }
- for (i = 0; i < mixer[4]; i++) {
- state->termtype = termt[i];
- if (chidx[i+1]-chidx[i] >= 2) {
- flg |= MIXFLG_STEREOIN;
- if (checkmixbmap(bmap, flg, chidx[i], nroutch)) {
- ch = getmixchannel(state, getvolchannel(state));
- if (ch) {
- ch->unitid = mixer[3];
- ch->selector = 0;
- ch->chnum = chidx[i]+1;
- ch->flags = flg;
- prepmixch(state);
- }
- continue;
- }
- }
- flg &= ~MIXFLG_STEREOIN;
- if (checkmixbmap(bmap, flg, chidx[i], nroutch)) {
- ch = getmixchannel(state, getvolchannel(state));
- if (ch) {
- ch->unitid = mixer[3];
- ch->selector = 0;
- ch->chnum = chidx[i]+1;
- ch->flags = flg;
- prepmixch(state);
- }
- }
- }
- state->termtype = 0;
-}
-
-static struct mixerchannel *slctsrc_findunit(struct consmixstate *state, __u8 unitid)
-{
- unsigned int i;
-
- for (i = 0; i < state->nrmixch; i++)
- if (state->mixch[i].unitid == unitid)
- return &state->mixch[i];
- return NULL;
-}
-
-static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector)
-{
- unsigned int chnum, i, mixch;
- struct mixerchannel *mch;
-
- if (!selector[4]) {
- printk(KERN_ERR "usbaudio: unit %u invalid SELECTOR_UNIT descriptor\n", selector[3]);
- return;
- }
- mixch = state->nrmixch;
- usb_audio_recurseunit(state, selector[5]);
- if (state->nrmixch != mixch) {
- mch = &state->mixch[state->nrmixch-1];
- mch->slctunitid = selector[3] | (1 << 8);
- } else if ((mch = slctsrc_findunit(state, selector[5]))) {
- mch->slctunitid = selector[3] | (1 << 8);
- } else {
- printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel 1\n", selector[3]);
- }
- chnum = state->nrchannels;
- for (i = 1; i < selector[4]; i++) {
- mixch = state->nrmixch;
- usb_audio_recurseunit(state, selector[5+i]);
- if (chnum != state->nrchannels) {
- printk(KERN_ERR "usbaudio: selector unit %u: input pins with varying channel numbers\n", selector[3]);
- state->termtype = 0;
- state->chconfig = 0;
- state->nrchannels = 0;
- return;
- }
- if (state->nrmixch != mixch) {
- mch = &state->mixch[state->nrmixch-1];
- mch->slctunitid = selector[3] | ((i + 1) << 8);
- } else if ((mch = slctsrc_findunit(state, selector[5+i]))) {
- mch->slctunitid = selector[3] | ((i + 1) << 8);
- } else {
- printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel %u\n", selector[3], i+1);
- }
- }
- state->termtype = 0;
- state->chconfig = 0;
-}
-
-/* in the future we might try to handle 3D etc. effect units */
-
-static void usb_audio_processingunit(struct consmixstate *state, unsigned char *proc)
-{
- unsigned int i;
-
- for (i = 0; i < proc[6]; i++)
- usb_audio_recurseunit(state, proc[7+i]);
- state->nrchannels = proc[7+proc[6]];
- state->termtype = 0;
- state->chconfig = proc[8+proc[6]] | (proc[9+proc[6]] << 8);
-}
-
-
-/* See Audio Class Spec, section 4.3.2.5 */
-static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr)
-{
- struct mixerchannel *ch;
- unsigned short chftr, mchftr;
-#if 0
- struct usb_device *dev = state->s->usbdev;
- unsigned char data[1];
-#endif
- unsigned char nr_logical_channels, i;
-
- usb_audio_recurseunit(state, ftr[4]);
-
- if (ftr[5] == 0 ) {
- printk(KERN_ERR "usbaudio: wrong controls size in feature unit %u\n",ftr[3]);
- return;
- }
-
- if (state->nrchannels == 0) {
- printk(KERN_ERR "usbaudio: feature unit %u source has no channels\n", ftr[3]);
- return;
- }
- if (state->nrchannels > 2)
- printk(KERN_WARNING "usbaudio: feature unit %u: OSS mixer interface does not support more than 2 channels\n", ftr[3]);
-
- nr_logical_channels=(ftr[0]-7)/ftr[5]-1;
-
- if (nr_logical_channels != state->nrchannels) {
- printk(KERN_WARNING "usbaudio: warning: found %d of %d logical channels.\n", state->nrchannels,nr_logical_channels);
-
- if (state->nrchannels == 1 && nr_logical_channels==0) {
- printk(KERN_INFO "usbaudio: assuming the channel found is the master channel (got a Philips camera?). Should be fine.\n");
- } else if (state->nrchannels == 1 && nr_logical_channels==2) {
- printk(KERN_INFO "usbaudio: assuming that a stereo channel connected directly to a mixer is missing in search (got Labtec headset?). Should be fine.\n");
- state->nrchannels=nr_logical_channels;
- } else {
- printk(KERN_WARNING "usbaudio: no idea what's going on..., contact linux-usb-devel@lists.sourceforge.net\n");
- }
- }
-
- /* There is always a master channel */
- mchftr = ftr[6];
- /* Binary AND over logical channels if they exist */
- if (nr_logical_channels) {
- chftr = ftr[6+ftr[5]];
- for (i = 2; i <= nr_logical_channels; i++)
- chftr &= ftr[6+i*ftr[5]];
- } else {
- chftr = 0;
- }
-
- /* volume control */
- if (chftr & 2) {
- ch = getmixchannel(state, getvolchannel(state));
- if (ch) {
- ch->unitid = ftr[3];
- ch->selector = VOLUME_CONTROL;
- ch->chnum = 1;
- ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0;
- prepmixch(state);
- }
- } else if (mchftr & 2) {
- ch = getmixchannel(state, getvolchannel(state));
- if (ch) {
- ch->unitid = ftr[3];
- ch->selector = VOLUME_CONTROL;
- ch->chnum = 0;
- ch->flags = 0;
- prepmixch(state);
- }
- }
- /* bass control */
- if (chftr & 4) {
- ch = getmixchannel(state, SOUND_MIXER_BASS);
- if (ch) {
- ch->unitid = ftr[3];
- ch->selector = BASS_CONTROL;
- ch->chnum = 1;
- ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0;
- prepmixch(state);
- }
- } else if (mchftr & 4) {
- ch = getmixchannel(state, SOUND_MIXER_BASS);
- if (ch) {
- ch->unitid = ftr[3];
- ch->selector = BASS_CONTROL;
- ch->chnum = 0;
- ch->flags = 0;
- prepmixch(state);
- }
- }
- /* treble control */
- if (chftr & 16) {
- ch = getmixchannel(state, SOUND_MIXER_TREBLE);
- if (ch) {
- ch->unitid = ftr[3];
- ch->selector = TREBLE_CONTROL;
- ch->chnum = 1;
- ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0;
- prepmixch(state);
- }
- } else if (mchftr & 16) {
- ch = getmixchannel(state, SOUND_MIXER_TREBLE);
- if (ch) {
- ch->unitid = ftr[3];
- ch->selector = TREBLE_CONTROL;
- ch->chnum = 0;
- ch->flags = 0;
- prepmixch(state);
- }
- }
-#if 0
- /* if there are mute controls, unmute them */
- /* does not seem to be necessary, and the Dallas chip does not seem to support the "all" channel (255) */
- if ((chftr & 1) || (mchftr & 1)) {
- printk(KERN_DEBUG "usbaudio: unmuting feature unit %u interface %u\n", ftr[3], state->ctrlif);
- data[0] = 0;
- if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- (MUTE_CONTROL << 8) | 0xff, state->ctrlif | (ftr[3] << 8), data, 1, 1000) < 0)
- printk(KERN_WARNING "usbaudio: failure to unmute feature unit %u interface %u\n", ftr[3], state->ctrlif);
- }
-#endif
-}
-
-static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid)
-{
- unsigned char *p1;
- unsigned int i, j;
-
- if (test_and_set_bit(unitid, state->unitbitmap)) {
- printk(KERN_INFO "usbaudio: mixer path revisits unit %d\n", unitid);
- return;
- }
- p1 = find_audiocontrol_unit(state->buffer, state->buflen, NULL, unitid, state->ctrlif);
- if (!p1) {
- printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid);
- return;
- }
- state->nrchannels = 0;
- state->termtype = 0;
- state->chconfig = 0;
- switch (p1[2]) {
- case INPUT_TERMINAL:
- if (p1[0] < 12) {
- printk(KERN_ERR "usbaudio: unit %u: invalid INPUT_TERMINAL descriptor\n", unitid);
- return;
- }
- state->nrchannels = p1[7];
- state->termtype = p1[4] | (p1[5] << 8);
- state->chconfig = p1[8] | (p1[9] << 8);
- return;
-
- case MIXER_UNIT:
- if (p1[0] < 10 || p1[0] < 10+p1[4]) {
- printk(KERN_ERR "usbaudio: unit %u: invalid MIXER_UNIT descriptor\n", unitid);
- return;
- }
- usb_audio_mixerunit(state, p1);
- return;
-
- case SELECTOR_UNIT:
- if (p1[0] < 6 || p1[0] < 6+p1[4]) {
- printk(KERN_ERR "usbaudio: unit %u: invalid SELECTOR_UNIT descriptor\n", unitid);
- return;
- }
- usb_audio_selectorunit(state, p1);
- return;
-
- case FEATURE_UNIT: /* See USB Audio Class Spec 4.3.2.5 */
- if (p1[0] < 7 || p1[0] < 7+p1[5]) {
- printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid);
- return;
- }
- usb_audio_featureunit(state, p1);
- return;
-
- case PROCESSING_UNIT:
- if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) {
- printk(KERN_ERR "usbaudio: unit %u: invalid PROCESSING_UNIT descriptor\n", unitid);
- return;
- }
- usb_audio_processingunit(state, p1);
- return;
-
- case EXTENSION_UNIT:
- if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) {
- printk(KERN_ERR "usbaudio: unit %u: invalid EXTENSION_UNIT descriptor\n", unitid);
- return;
- }
- for (j = i = 0; i < p1[6]; i++) {
- usb_audio_recurseunit(state, p1[7+i]);
- if (!i)
- j = state->termtype;
- else if (j != state->termtype)
- j = 0;
- }
- state->nrchannels = p1[7+p1[6]];
- state->chconfig = p1[8+p1[6]] | (p1[9+p1[6]] << 8);
- state->termtype = j;
- return;
-
- default:
- printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
- return;
- }
-}
-
-static void usb_audio_constructmixer(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif, unsigned char *oterm)
-{
- struct usb_mixerdev *ms;
- struct consmixstate state;
-
- memset(&state, 0, sizeof(state));
- state.s = s;
- state.nrmixch = 0;
- state.mixchmask = ~0;
- state.buffer = buffer;
- state.buflen = buflen;
- state.ctrlif = ctrlif;
- set_bit(oterm[3], state.unitbitmap); /* mark terminal ID as visited */
- printk(KERN_DEBUG "usbaudio: constructing mixer for Terminal %u type 0x%04x\n",
- oterm[3], oterm[4] | (oterm[5] << 8));
- usb_audio_recurseunit(&state, oterm[7]);
- if (!state.nrmixch) {
- printk(KERN_INFO "usbaudio: no mixer controls found for Terminal %u\n", oterm[3]);
- return;
- }
- if (!(ms = kmalloc(sizeof(struct usb_mixerdev)+state.nrmixch*sizeof(struct mixerchannel), GFP_KERNEL)))
- return;
- memset(ms, 0, sizeof(struct usb_mixerdev));
- memcpy(&ms->ch, &state.mixch, state.nrmixch*sizeof(struct mixerchannel));
- ms->state = s;
- ms->iface = ctrlif;
- ms->numch = state.nrmixch;
- if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1)) < 0) {
- printk(KERN_ERR "usbaudio: cannot register mixer\n");
- kfree(ms);
- return;
- }
- printk(KERN_INFO "usbaudio: registered mixer 14,%d\n", ms->dev_mixer);
- list_add_tail(&ms->list, &s->mixerlist);
-}
-
-/* arbitrary limit, we won't check more interfaces than this */
-#define USB_MAXINTERFACES 32
-
-static struct usb_audio_state *usb_audio_parsecontrol(struct usb_device *dev, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif)
-{
- struct usb_audio_state *s;
- struct usb_interface *iface;
- struct usb_host_interface *alt;
- unsigned char ifin[USB_MAXINTERFACES], ifout[USB_MAXINTERFACES];
- unsigned char *p1;
- unsigned int i, j, k, numifin = 0, numifout = 0;
-
- if (!(s = kmalloc(sizeof(struct usb_audio_state), GFP_KERNEL)))
- return NULL;
- memset(s, 0, sizeof(struct usb_audio_state));
- INIT_LIST_HEAD(&s->audiolist);
- INIT_LIST_HEAD(&s->mixerlist);
- s->usbdev = dev;
- s->count = 1;
-
- /* find audiocontrol interface */
- if (!(p1 = find_csinterface_descriptor(buffer, buflen, NULL, HEADER, ctrlif, -1))) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u no HEADER found\n",
- dev->devnum, ctrlif);
- goto ret;
- }
- if (p1[0] < 8 + p1[7]) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u HEADER error\n",
- dev->devnum, ctrlif);
- goto ret;
- }
- if (!p1[7])
- printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has no AudioStreaming and MidiStreaming interfaces\n",
- dev->devnum, ctrlif);
- for (i = 0; i < p1[7]; i++) {
- j = p1[8+i];
- iface = usb_ifnum_to_if(dev, j);
- if (!iface) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u does not exist\n",
- dev->devnum, ctrlif, j);
- continue;
- }
- if (iface->num_altsetting == 1) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has only 1 altsetting.\n", dev->devnum, ctrlif);
- continue;
- }
- alt = usb_altnum_to_altsetting(iface, 0);
- if (!alt) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no altsetting 0\n",
- dev->devnum, ctrlif, j);
- continue;
- }
- if (alt->desc.bInterfaceClass != USB_CLASS_AUDIO) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u is not an AudioClass interface\n",
- dev->devnum, ctrlif, j);
- continue;
- }
- if (alt->desc.bInterfaceSubClass == 3) {
- printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u interface %u MIDIStreaming not supported\n",
- dev->devnum, ctrlif, j);
- continue;
- }
- if (alt->desc.bInterfaceSubClass != 2) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u invalid AudioClass subtype\n",
- dev->devnum, ctrlif, j);
- continue;
- }
- if (alt->desc.bNumEndpoints > 0) {
- /* Check all endpoints; should they all have a bandwidth of 0 ? */
- for (k = 0; k < alt->desc.bNumEndpoints; k++) {
- if (le16_to_cpu(alt->endpoint[k].desc.wMaxPacketSize) > 0) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u endpoint %d does not have 0 bandwidth at alt[0]\n", dev->devnum, ctrlif, k);
- break;
- }
- }
- if (k < alt->desc.bNumEndpoints)
- continue;
- }
-
- alt = usb_altnum_to_altsetting(iface, 1);
- if (!alt) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no altsetting 1\n",
- dev->devnum, ctrlif, j);
- continue;
- }
- if (alt->desc.bNumEndpoints < 1) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no endpoint\n",
- dev->devnum, ctrlif, j);
- continue;
- }
- /* note: this requires the data endpoint to be ep0 and the optional sync
- ep to be ep1, which seems to be the case */
- if (alt->endpoint[0].desc.bEndpointAddress & USB_DIR_IN) {
- if (numifin < USB_MAXINTERFACES) {
- ifin[numifin++] = j;
- usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
- }
- } else {
- if (numifout < USB_MAXINTERFACES) {
- ifout[numifout++] = j;
- usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
- }
- }
- }
- printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has %u input and %u output AudioStreaming interfaces\n",
- dev->devnum, ctrlif, numifin, numifout);
- for (i = 0; i < numifin && i < numifout; i++)
- usb_audio_parsestreaming(s, buffer, buflen, ifin[i], ifout[i]);
- for (j = i; j < numifin; j++)
- usb_audio_parsestreaming(s, buffer, buflen, ifin[i], -1);
- for (j = i; j < numifout; j++)
- usb_audio_parsestreaming(s, buffer, buflen, -1, ifout[i]);
- /* now walk through all OUTPUT_TERMINAL descriptors to search for mixers */
- p1 = find_csinterface_descriptor(buffer, buflen, NULL, OUTPUT_TERMINAL, ctrlif, -1);
- while (p1) {
- if (p1[0] >= 9)
- usb_audio_constructmixer(s, buffer, buflen, ctrlif, p1);
- p1 = find_csinterface_descriptor(buffer, buflen, p1, OUTPUT_TERMINAL, ctrlif, -1);
- }
-
-ret:
- if (list_empty(&s->audiolist) && list_empty(&s->mixerlist)) {
- kfree(s);
- return NULL;
- }
- /* everything successful */
- down(&open_sem);
- list_add_tail(&s->audiodev, &audiodevs);
- up(&open_sem);
- printk(KERN_DEBUG "usb_audio_parsecontrol: usb_audio_state at %p\n", s);
- return s;
-}
-
-/* we only care for the currently active configuration */
-
-static int usb_audio_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev (intf);
- struct usb_audio_state *s;
- unsigned char *buffer;
- unsigned int buflen;
-
-#if 0
- printk(KERN_DEBUG "usbaudio: Probing if %i: IC %x, ISC %x\n", ifnum,
- config->interface[ifnum].altsetting[0].desc.bInterfaceClass,
- config->interface[ifnum].altsetting[0].desc.bInterfaceSubClass);
-#endif
-
- /*
- * audiocontrol interface found
- * find which configuration number is active
- */
- buffer = dev->rawdescriptors[dev->actconfig - dev->config];
- buflen = le16_to_cpu(dev->actconfig->desc.wTotalLength);
- s = usb_audio_parsecontrol(dev, buffer, buflen, intf->altsetting->desc.bInterfaceNumber);
- if (s) {
- usb_set_intfdata (intf, s);
- return 0;
- }
- return -ENODEV;
-}
-
-
-/* a revoke facility would make things simpler */
-
-static void usb_audio_disconnect(struct usb_interface *intf)
-{
- struct usb_audio_state *s = usb_get_intfdata (intf);
- struct usb_audiodev *as;
- struct usb_mixerdev *ms;
-
- if (!s)
- return;
-
- /* we get called with -1 for every audiostreaming interface registered */
- if (s == (struct usb_audio_state *)-1) {
- dprintk((KERN_DEBUG "usbaudio: note, usb_audio_disconnect called with -1\n"));
- return;
- }
- if (!s->usbdev) {
- dprintk((KERN_DEBUG "usbaudio: error, usb_audio_disconnect already called for %p!\n", s));
- return;
- }
- down(&open_sem);
- list_del_init(&s->audiodev);
- s->usbdev = NULL;
- usb_set_intfdata (intf, NULL);
-
- /* deregister all audio and mixer devices, so no new processes can open this device */
- list_for_each_entry(as, &s->audiolist, list) {
- usbin_disc(as);
- usbout_disc(as);
- wake_up(&as->usbin.dma.wait);
- wake_up(&as->usbout.dma.wait);
- if (as->dev_audio >= 0) {
- unregister_sound_dsp(as->dev_audio);
- printk(KERN_INFO "usbaudio: unregister dsp 14,%d\n", as->dev_audio);
- }
- as->dev_audio = -1;
- }
- list_for_each_entry(ms, &s->mixerlist, list) {
- if (ms->dev_mixer >= 0) {
- unregister_sound_mixer(ms->dev_mixer);
- printk(KERN_INFO "usbaudio: unregister mixer 14,%d\n", ms->dev_mixer);
- }
- ms->dev_mixer = -1;
- }
- release(s);
- wake_up(&open_wait);
-}
-
-static int __init usb_audio_init(void)
-{
- int result = usb_register(&usb_audio_driver);
- if (result == 0)
- info(DRIVER_VERSION ":" DRIVER_DESC);
- return result;
-}
-
-
-static void __exit usb_audio_cleanup(void)
-{
- usb_deregister(&usb_audio_driver);
-}
-
-module_init(usb_audio_init);
-module_exit(usb_audio_cleanup);
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/usb/class/audio.h b/drivers/usb/class/audio.h
deleted file mode 100644
index 45916eb1210..00000000000
--- a/drivers/usb/class/audio.h
+++ /dev/null
@@ -1,110 +0,0 @@
-#define CS_AUDIO_UNDEFINED 0x20
-#define CS_AUDIO_DEVICE 0x21
-#define CS_AUDIO_CONFIGURATION 0x22
-#define CS_AUDIO_STRING 0x23
-#define CS_AUDIO_INTERFACE 0x24
-#define CS_AUDIO_ENDPOINT 0x25
-
-#define HEADER 0x01
-#define INPUT_TERMINAL 0x02
-#define OUTPUT_TERMINAL 0x03
-#define MIXER_UNIT 0x04
-#define SELECTOR_UNIT 0x05
-#define FEATURE_UNIT 0x06
-#define PROCESSING_UNIT 0x07
-#define EXTENSION_UNIT 0x08
-
-#define AS_GENERAL 0x01
-#define FORMAT_TYPE 0x02
-#define FORMAT_SPECIFIC 0x03
-
-#define EP_GENERAL 0x01
-
-#define MAX_CHAN 9
-#define MAX_FREQ 16
-#define MAX_IFACE 8
-#define MAX_FORMAT 8
-#define MAX_ALT 32 /* Sorry, we need quite a few for the Philips webcams */
-
-struct usb_audio_terminal
-{
- u8 flags;
- u8 assoc;
- u16 type; /* Mic etc */
- u8 channels;
- u8 source;
- u16 chancfg;
-};
-
-struct usb_audio_format
-{
- u8 type;
- u8 channels;
- u8 num_freq;
- u8 sfz;
- u8 bits;
- u16 freq[MAX_FREQ];
-};
-
-struct usb_audio_interface
-{
- u8 terminal;
- u8 delay;
- u16 num_formats;
- u16 format_type;
- u8 flags;
- u8 idleconf; /* Idle config */
-#define AU_IFACE_FOUND 1
- struct usb_audio_format format[MAX_FORMAT];
-};
-
-struct usb_audio_device
-{
- struct list_head list;
- u8 mixer;
- u8 selector;
- void *irq_handle;
- u8 num_channels;
- u8 num_dsp_iface;
- u8 channel_map[MAX_CHAN];
- struct usb_audio_terminal terminal[MAX_CHAN];
- struct usb_audio_interface interface[MAX_IFACE][MAX_ALT];
-};
-
-
-
-/* Audio Class specific Request Codes */
-
-#define SET_CUR 0x01
-#define GET_CUR 0x81
-#define SET_MIN 0x02
-#define GET_MIN 0x82
-#define SET_MAX 0x03
-#define GET_MAX 0x83
-#define SET_RES 0x04
-#define GET_RES 0x84
-#define SET_MEM 0x05
-#define GET_MEM 0x85
-#define GET_STAT 0xff
-
-/* Terminal Control Selectors */
-
-#define COPY_PROTECT_CONTROL 0x01
-
-/* Feature Unit Control Selectors */
-
-#define MUTE_CONTROL 0x01
-#define VOLUME_CONTROL 0x02
-#define BASS_CONTROL 0x03
-#define MID_CONTROL 0x04
-#define TREBLE_CONTROL 0x05
-#define GRAPHIC_EQUALIZER_CONTROL 0x06
-#define AUTOMATIC_GAIN_CONTROL 0x07
-#define DELAY_CONTROL 0x08
-#define BASS_BOOST_CONTROL 0x09
-#define LOUDNESS_CONTROL 0x0a
-
-/* Endpoint Control Selectors */
-
-#define SAMPLING_FREQ_CONTROL 0x01
-#define PITCH_CONTROL 0x02
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 97bdeb1c218..6dd339f4c0f 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -60,6 +60,7 @@
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/usb_cdc.h>
@@ -80,7 +81,7 @@ static struct usb_driver acm_driver;
static struct tty_driver *acm_tty_driver;
static struct acm *acm_table[ACM_TTY_MINORS];
-static DECLARE_MUTEX(open_sem);
+static DEFINE_MUTEX(open_mutex);
#define ACM_READY(acm) (acm && acm->dev && acm->used)
@@ -431,8 +432,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
int rv = -EINVAL;
int i;
dbg("Entering acm_tty_open.\n");
-
- down(&open_sem);
+
+ mutex_lock(&open_mutex);
acm = acm_table[tty->index];
if (!acm || !acm->dev)
@@ -474,14 +475,14 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
done:
err_out:
- up(&open_sem);
+ mutex_unlock(&open_mutex);
return rv;
full_bailout:
usb_kill_urb(acm->ctrlurb);
bail_out:
acm->used--;
- up(&open_sem);
+ mutex_unlock(&open_mutex);
return -EIO;
}
@@ -507,7 +508,7 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
if (!acm || !acm->used)
return;
- down(&open_sem);
+ mutex_lock(&open_mutex);
if (!--acm->used) {
if (acm->dev) {
acm_set_control(acm, acm->ctrlout = 0);
@@ -518,7 +519,7 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
} else
acm_tty_unregister(acm);
}
- up(&open_sem);
+ mutex_unlock(&open_mutex);
}
static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
@@ -1013,9 +1014,9 @@ static void acm_disconnect(struct usb_interface *intf)
return;
}
- down(&open_sem);
+ mutex_lock(&open_mutex);
if (!usb_get_intfdata(intf)) {
- up(&open_sem);
+ mutex_unlock(&open_mutex);
return;
}
acm->dev = NULL;
@@ -1045,11 +1046,11 @@ static void acm_disconnect(struct usb_interface *intf)
if (!acm->used) {
acm_tty_unregister(acm);
- up(&open_sem);
+ mutex_unlock(&open_mutex);
return;
}
- up(&open_sem);
+ mutex_unlock(&open_mutex);
if (acm->tty)
tty_hangup(acm->tty);
diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c
deleted file mode 100644
index f13f004d311..00000000000
--- a/drivers/usb/class/usb-midi.c
+++ /dev/null
@@ -1,2153 +0,0 @@
-/*
- usb-midi.c -- USB-MIDI driver
-
- Copyright (C) 2001
- NAGANO Daisuke <breeze.nagano@nifty.ne.jp>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- This driver is based on:
- - 'Universal Serial Bus Device Class Definition for MIDI Device'
- - linux/drivers/sound/es1371.c, linux/drivers/usb/audio.c
- - alsa/lowlevel/pci/cs64xx.c
- - umidi.c for NetBSD
- */
-
-/* ------------------------------------------------------------------------- */
-
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/poll.h>
-#include <linux/sound.h>
-#include <linux/init.h>
-#include <asm/semaphore.h>
-
-#include "usb-midi.h"
-
-/* ------------------------------------------------------------------------- */
-
-/* More verbose on syslog */
-#undef MIDI_DEBUG
-
-#define MIDI_IN_BUFSIZ 1024
-
-#define HAVE_SUPPORT_USB_MIDI_CLASS
-
-#undef HAVE_SUPPORT_ALSA
-
-/* ------------------------------------------------------------------------- */
-
-static int singlebyte = 0;
-module_param(singlebyte, int, 0);
-MODULE_PARM_DESC(singlebyte,"Enable sending MIDI messages with single message packet");
-
-static int maxdevices = 4;
-module_param(maxdevices, int, 0);
-MODULE_PARM_DESC(maxdevices,"Max number of allocatable MIDI device");
-
-static int uvendor = -1;
-module_param(uvendor, int, 0);
-MODULE_PARM_DESC(uvendor, "The USB Vendor ID of a semi-compliant interface");
-
-static int uproduct = -1;
-module_param(uproduct, int, 0);
-MODULE_PARM_DESC(uproduct, "The USB Product ID of a semi-compliant interface");
-
-static int uinterface = -1;
-module_param(uinterface, int, 0);
-MODULE_PARM_DESC(uinterface, "The Interface number of a semi-compliant interface");
-
-static int ualt = -1;
-module_param(ualt, int, 0);
-MODULE_PARM_DESC(ualt, "The optional alternative setting of a semi-compliant interface");
-
-static int umin = -1;
-module_param(umin, int, 0);
-MODULE_PARM_DESC(umin, "The input endpoint of a semi-compliant interface");
-
-static int umout = -1;
-module_param(umout, int, 0);
-MODULE_PARM_DESC(umout, "The output endpoint of a semi-compliant interface");
-
-static int ucable = -1;
-module_param(ucable, int, 0);
-MODULE_PARM_DESC(ucable, "The cable number used for a semi-compliant interface");
-
-/** Note -- the usb_string() returns only Latin-1 characters.
- * (unicode chars <= 255). To support Japanese, a unicode16LE-to-EUC or
- * unicode16LE-to-JIS routine is needed to wrap around usb_get_string().
- **/
-static unsigned short ulangid = 0x0409; /** 0x0411 for Japanese **/
-module_param(ulangid, ushort, 0);
-MODULE_PARM_DESC(ulangid, "The optional preferred USB Language ID for all devices");
-
-MODULE_AUTHOR("NAGANO Daisuke <breeze.nagano@nifty.ne.jp>");
-MODULE_DESCRIPTION("USB-MIDI driver");
-MODULE_LICENSE("GPL");
-
-/* ------------------------------------------------------------------------- */
-
-/** MIDIStreaming Class-Specific Interface Descriptor Subtypes **/
-
-#define MS_DESCRIPTOR_UNDEFINED 0
-#define MS_HEADER 1
-#define MIDI_IN_JACK 2
-#define MIDI_OUT_JACK 3
-/* Spec reads: ELEMENT */
-#define ELEMENT_DESCRIPTOR 4
-
-#define MS_HEADER_LENGTH 7
-
-/** MIDIStreaming Class-Specific Endpoint Descriptor Subtypes **/
-
-#define DESCRIPTOR_UNDEFINED 0
-/* Spec reads: MS_GENERAL */
-#define MS_GENERAL_ENDPOINT 1
-
-/** MIDIStreaming MIDI IN and OUT Jack Types **/
-
-#define JACK_TYPE_UNDEFINED 0
-/* Spec reads: EMBEDDED */
-#define EMBEDDED_JACK 1
-/* Spec reads: EXTERNAL */
-#define EXTERNAL_JACK 2
-
-
-/* structure summary
-
- usb_midi_state usb_device
- | |
- *| *| per ep
- in_ep out_ep
- | |
- *| *| per cable
- min mout
- | | (cable to device pairing magic)
- | |
- usb_midi_dev dev_id (major,minor) == file->private_data
-
-*/
-
-/* usb_midi_state: corresponds to a USB-MIDI module */
-struct usb_midi_state {
- struct list_head mididev;
-
- struct usb_device *usbdev;
-
- struct list_head midiDevList;
- struct list_head inEndpointList;
- struct list_head outEndpointList;
-
- spinlock_t lock;
-
- unsigned int count; /* usage counter */
-};
-
-/* midi_out_endpoint: corresponds to an output endpoint */
-struct midi_out_endpoint {
- struct list_head list;
-
- struct usb_device *usbdev;
- int endpoint;
- spinlock_t lock;
- wait_queue_head_t wait;
-
- unsigned char *buf;
- int bufWrPtr;
- int bufSize;
-
- struct urb *urb;
-};
-
-/* midi_in_endpoint: corresponds to an input endpoint */
-struct midi_in_endpoint {
- struct list_head list;
-
- struct usb_device *usbdev;
- int endpoint;
- spinlock_t lock;
- wait_queue_head_t wait;
-
- struct usb_mididev *cables[16]; // cables open for read
- int readers; // number of cables open for read
-
- struct urb *urb;
- unsigned char *recvBuf;
- int recvBufSize;
- int urbSubmitted; //FIXME: == readers > 0
-};
-
-/* usb_mididev: corresponds to a logical device */
-struct usb_mididev {
- struct list_head list;
-
- struct usb_midi_state *midi;
- int dev_midi;
- mode_t open_mode;
-
- struct {
- struct midi_in_endpoint *ep;
- int cableId;
-
-// as we are pushing data from usb_bulk_read to usb_midi_read,
-// we need a larger, cyclic buffer here.
- unsigned char buf[MIDI_IN_BUFSIZ];
- int bufRdPtr;
- int bufWrPtr;
- int bufRemains;
- } min;
-
- struct {
- struct midi_out_endpoint *ep;
- int cableId;
-
- unsigned char buf[3];
- int bufPtr;
- int bufRemains;
-
- int isInExclusive;
- unsigned char lastEvent;
- } mout;
-
- int singlebyte;
-};
-
-/** Map the high nybble of MIDI voice messages to number of Message bytes.
- * High nyble ranges from 0x8 to 0xe
- */
-
-static int remains_80e0[] = {
- 3, /** 0x8X Note Off **/
- 3, /** 0x9X Note On **/
- 3, /** 0xAX Poly-key pressure **/
- 3, /** 0xBX Control Change **/
- 2, /** 0xCX Program Change **/
- 2, /** 0xDX Channel pressure **/
- 3 /** 0xEX PitchBend Change **/
-};
-
-/** Map the messages to a number of Message bytes.
- *
- **/
-static int remains_f0f6[] = {
- 0, /** 0xF0 **/
- 2, /** 0XF1 **/
- 3, /** 0XF2 **/
- 2, /** 0XF3 **/
- 2, /** 0XF4 (Undefined by MIDI Spec, and subject to change) **/
- 2, /** 0XF5 (Undefined by MIDI Spec, and subject to change) **/
- 1 /** 0XF6 **/
-};
-
-/** Map the messages to a CIN (Code Index Number).
- *
- **/
-static int cin_f0ff[] = {
- 4, /** 0xF0 System Exclusive Message Start (special cases may be 6 or 7) */
- 2, /** 0xF1 **/
- 3, /** 0xF2 **/
- 2, /** 0xF3 **/
- 2, /** 0xF4 **/
- 2, /** 0xF5 **/
- 5, /** 0xF6 **/
- 5, /** 0xF7 End of System Exclusive Message (May be 6 or 7) **/
- 5, /** 0xF8 **/
- 5, /** 0xF9 **/
- 5, /** 0xFA **/
- 5, /** 0xFB **/
- 5, /** 0xFC **/
- 5, /** 0xFD **/
- 5, /** 0xFE **/
- 5 /** 0xFF **/
-};
-
-/** Map MIDIStreaming Event packet Code Index Number (low nybble of byte 0)
- * to the number of bytes of valid MIDI data.
- *
- * CIN of 0 and 1 are NOT USED in MIDIStreaming 1.0.
- *
- **/
-static int cin_to_len[] = {
- 0, 0, 2, 3,
- 3, 1, 2, 3,
- 3, 3, 3, 3,
- 2, 2, 3, 1
-};
-
-
-/* ------------------------------------------------------------------------- */
-
-static struct list_head mididevs = LIST_HEAD_INIT(mididevs);
-
-static DECLARE_MUTEX(open_sem);
-static DECLARE_WAIT_QUEUE_HEAD(open_wait);
-
-
-/* ------------------------------------------------------------------------- */
-
-static void usb_write_callback(struct urb *urb, struct pt_regs *regs)
-{
- struct midi_out_endpoint *ep = (struct midi_out_endpoint *)urb->context;
-
- if ( waitqueue_active( &ep->wait ) )
- wake_up_interruptible( &ep->wait );
-}
-
-
-static int usb_write( struct midi_out_endpoint *ep, unsigned char *buf, int len )
-{
- struct usb_device *d;
- int pipe;
- int ret = 0;
- int status;
- int maxretry = 50;
-
- DECLARE_WAITQUEUE(wait,current);
- init_waitqueue_head(&ep->wait);
-
- d = ep->usbdev;
- pipe = usb_sndbulkpipe(d, ep->endpoint);
- usb_fill_bulk_urb( ep->urb, d, pipe, (unsigned char*)buf, len,
- usb_write_callback, ep );
-
- status = usb_submit_urb(ep->urb, GFP_KERNEL);
-
- if (status) {
- printk(KERN_ERR "usbmidi: Cannot submit urb (%d)\n",status);
- ret = -EIO;
- goto error;
- }
-
- add_wait_queue( &ep->wait, &wait );
- set_current_state( TASK_INTERRUPTIBLE );
-
- while( ep->urb->status == -EINPROGRESS ) {
- if ( maxretry-- < 0 ) {
- printk(KERN_ERR "usbmidi: usb_bulk_msg timed out\n");
- ret = -ETIME;
- break;
- }
- interruptible_sleep_on_timeout( &ep->wait, 10 );
- }
- set_current_state( TASK_RUNNING );
- remove_wait_queue( &ep->wait, &wait );
-
-error:
- return ret;
-}
-
-
-/** Copy data from URB to In endpoint buf.
- * Discard if CIN == 0 or CIN = 1.
- *
- *
- **/
-
-static void usb_bulk_read(struct urb *urb, struct pt_regs *regs)
-{
- struct midi_in_endpoint *ep = (struct midi_in_endpoint *)(urb->context);
- unsigned char *data = urb->transfer_buffer;
- int i, j, wake;
-
- if ( !ep->urbSubmitted ) {
- return;
- }
-
- if ( (urb->status == 0) && (urb->actual_length > 0) ) {
- wake = 0;
- spin_lock( &ep->lock );
-
- for(j = 0; j < urb->actual_length; j += 4) {
- int cin = (data[j]>>0)&0xf;
- int cab = (data[j]>>4)&0xf;
- struct usb_mididev *cable = ep->cables[cab];
- if ( cable ) {
- int len = cin_to_len[cin]; /** length of MIDI data **/
- for (i = 0; i < len; i++) {
- cable->min.buf[cable->min.bufWrPtr] = data[1+i+j];
- cable->min.bufWrPtr = (cable->min.bufWrPtr+1)%MIDI_IN_BUFSIZ;
- if (cable->min.bufRemains < MIDI_IN_BUFSIZ)
- cable->min.bufRemains += 1;
- else /** need to drop data **/
- cable->min.bufRdPtr += (cable->min.bufRdPtr+1)%MIDI_IN_BUFSIZ;
- wake = 1;
- }
- }
- }
-
- spin_unlock ( &ep->lock );
- if ( wake ) {
- wake_up( &ep->wait );
- }
- }
-
- /* urb->dev must be reinitialized on 2.4.x kernels */
- urb->dev = ep->usbdev;
-
- urb->actual_length = 0;
- usb_submit_urb(urb, GFP_ATOMIC);
-}
-
-
-
-/* ------------------------------------------------------------------------- */
-
-/* This routine must be called with spin_lock */
-
-/** Wrapper around usb_write().
- * This routine must be called with spin_lock held on ep.
- * Called by midiWrite(), putOneMidiEvent(), and usb_midi_write();
- **/
-static int flush_midi_buffer( struct midi_out_endpoint *ep )
-{
- int ret=0;
-
- if ( ep->bufWrPtr > 0 ) {
- ret = usb_write( ep, ep->buf, ep->bufWrPtr );
- ep->bufWrPtr = 0;
- }
-
- return ret;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-
-/** Given a MIDI Event, determine size of data to be attached to
- * USB-MIDI packet.
- * Returns 1, 2 or 3.
- * Called by midiWrite();
- * Uses remains_80e0 and remains_f0f6;
- **/
-static int get_remains(int event)
-{
- int ret;
-
- if ( event < 0x80 ) {
- ret = 1;
- } else if ( event < 0xf0 ) {
- ret = remains_80e0[((event-0x80)>>4)&0x0f];
- } else if ( event < 0xf7 ) {
- ret = remains_f0f6[event-0xf0];
- } else {
- ret = 1;
- }
-
- return ret;
-}
-
-/** Given the output MIDI data in the output buffer, computes a reasonable
- * CIN.
- * Called by putOneMidiEvent().
- **/
-static int get_CIN( struct usb_mididev *m )
-{
- int cin;
-
- if ( m->mout.buf[0] == 0xf7 ) {
- cin = 5;
- }
- else if ( m->mout.buf[1] == 0xf7 ) {
- cin = 6;
- }
- else if ( m->mout.buf[2] == 0xf7 ) {
- cin = 7;
- }
- else {
- if ( m->mout.isInExclusive == 1 ) {
- cin = 4;
- } else if ( m->mout.buf[0] < 0x80 ) {
- /** One byte that we know nothing about. **/
- cin = 0xF;
- } else if ( m->mout.buf[0] < 0xf0 ) {
- /** MIDI Voice messages 0x8X to 0xEX map to cin 0x8 to 0xE. **/
- cin = (m->mout.buf[0]>>4)&0x0f;
- }
- else {
- /** Special lookup table exists for real-time events. **/
- cin = cin_f0ff[m->mout.buf[0]-0xf0];
- }
- }
-
- return cin;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-
-
-/** Move data to USB endpoint buffer.
- *
- **/
-static int put_one_midi_event(struct usb_mididev *m)
-{
- int cin;
- unsigned long flags;
- struct midi_out_endpoint *ep = m->mout.ep;
- int ret=0;
-
- cin = get_CIN( m );
- if ( cin > 0x0f || cin < 0 ) {
- return -EINVAL;
- }
-
- spin_lock_irqsave( &ep->lock, flags );
- ep->buf[ep->bufWrPtr++] = (m->mout.cableId<<4) | cin;
- ep->buf[ep->bufWrPtr++] = m->mout.buf[0];
- ep->buf[ep->bufWrPtr++] = m->mout.buf[1];
- ep->buf[ep->bufWrPtr++] = m->mout.buf[2];
- if ( ep->bufWrPtr >= ep->bufSize ) {
- ret = flush_midi_buffer( ep );
- }
- spin_unlock_irqrestore( &ep->lock, flags);
-
- m->mout.buf[0] = m->mout.buf[1] = m->mout.buf[2] = 0;
- m->mout.bufPtr = 0;
-
- return ret;
-}
-
-/** Write the MIDI message v on the midi device.
- * Called by usb_midi_write();
- * Responsible for packaging a MIDI data stream into USB-MIDI packets.
- **/
-
-static int midi_write( struct usb_mididev *m, int v )
-{
- unsigned long flags;
- struct midi_out_endpoint *ep = m->mout.ep;
- int ret=0;
- unsigned char c = (unsigned char)v;
- unsigned char sysrt_buf[4];
-
- if ( m->singlebyte != 0 ) {
- /** Simple code to handle the single-byte USB-MIDI protocol. */
- spin_lock_irqsave( &ep->lock, flags );
- if ( ep->bufWrPtr+4 > ep->bufSize ) {
- ret = flush_midi_buffer( ep );
- if ( !ret ) {
- spin_unlock_irqrestore( &ep->lock, flags );
- return ret;
- }
- }
- ep->buf[ep->bufWrPtr++] = (m->mout.cableId<<4) | 0x0f; /* single byte */
- ep->buf[ep->bufWrPtr++] = c;
- ep->buf[ep->bufWrPtr++] = 0;
- ep->buf[ep->bufWrPtr++] = 0;
- if ( ep->bufWrPtr >= ep->bufSize ) {
- ret = flush_midi_buffer( ep );
- }
- spin_unlock_irqrestore( &ep->lock, flags );
-
- return ret;
- }
- /** Normal USB-MIDI protocol begins here. */
-
- if ( c > 0xf7 ) { /* system: Realtime messages */
- /** Realtime messages are written IMMEDIATELY. */
- sysrt_buf[0] = (m->mout.cableId<<4) | 0x0f;
- sysrt_buf[1] = c;
- sysrt_buf[2] = 0;
- sysrt_buf[3] = 0;
- spin_lock_irqsave( &ep->lock, flags );
- ret = usb_write( ep, sysrt_buf, 4 );
- spin_unlock_irqrestore( &ep->lock, flags );
- /* m->mout.lastEvent = 0; */
-
- return ret;
- }
-
- if ( c >= 0x80 ) {
- if ( c < 0xf0 ) {
- m->mout.lastEvent = c;
- m->mout.isInExclusive = 0;
- m->mout.bufRemains = get_remains(c);
- } else if ( c == 0xf0 ) {
- /* m->mout.lastEvent = 0; */
- m->mout.isInExclusive = 1;
- m->mout.bufRemains = get_remains(c);
- } else if ( c == 0xf7 && m->mout.isInExclusive == 1 ) {
- /* m->mout.lastEvent = 0; */
- m->mout.isInExclusive = 0;
- m->mout.bufRemains = 1;
- } else if ( c > 0xf0 ) {
- /* m->mout.lastEvent = 0; */
- m->mout.isInExclusive = 0;
- m->mout.bufRemains = get_remains(c);
- }
-
- } else if ( m->mout.bufRemains == 0 && m->mout.isInExclusive == 0 ) {
- if ( m->mout.lastEvent == 0 ) {
- return 0; /* discard, waiting for the first event */
- }
- /** track status **/
- m->mout.buf[0] = m->mout.lastEvent;
- m->mout.bufPtr = 1;
- m->mout.bufRemains = get_remains(m->mout.lastEvent)-1;
- }
-
- m->mout.buf[m->mout.bufPtr++] = c;
- m->mout.bufRemains--;
- if ( m->mout.bufRemains == 0 || m->mout.bufPtr >= 3) {
- ret = put_one_midi_event(m);
- }
-
- return ret;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-/** Basic operation on /dev/midiXX as registered through struct file_operations.
- *
- * Basic contract: Used to change the current read/write position in a file.
- * On success, the non-negative position is reported.
- * On failure, the negative of an error code is reported.
- *
- * Because a MIDIStream is not a file, all seek operations are doomed to fail.
- *
- **/
-static loff_t usb_midi_llseek(struct file *file, loff_t offset, int origin)
-{
- /** Tell user you cannot seek on a PIPE-like device. **/
- return -ESPIPE;
-}
-
-
-/** Basic operation on /dev/midiXX as registered through struct file_operations.
- *
- * Basic contract: Block until count bytes have been read or an error occurs.
- *
- **/
-
-static ssize_t usb_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
-{
- struct usb_mididev *m = (struct usb_mididev *)file->private_data;
- struct midi_in_endpoint *ep = m->min.ep;
- ssize_t ret;
- DECLARE_WAITQUEUE(wait, current);
-
- if ( !access_ok(VERIFY_READ, buffer, count) ) {
- return -EFAULT;
- }
- if ( count == 0 ) {
- return 0;
- }
-
- add_wait_queue( &ep->wait, &wait );
- ret = 0;
- while( count > 0 ) {
- int cnt;
- int d = (int)count;
-
- cnt = m->min.bufRemains;
- if ( cnt > d ) {
- cnt = d;
- }
-
- if ( cnt <= 0 ) {
- if ( file->f_flags & O_NONBLOCK ) {
- if (!ret)
- ret = -EAGAIN;
- break;
- }
- __set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- if (signal_pending(current)) {
- if(!ret)
- ret=-ERESTARTSYS;
- break;
- }
- continue;
- }
-
- {
- int i;
- unsigned long flags; /* used to synchronize access to the endpoint */
- spin_lock_irqsave( &ep->lock, flags );
- for (i = 0; i < cnt; i++) {
- if ( copy_to_user( buffer+i, m->min.buf+m->min.bufRdPtr, 1 ) ) {
- if ( !ret )
- ret = -EFAULT;
- break;
- }
- m->min.bufRdPtr = (m->min.bufRdPtr+1)%MIDI_IN_BUFSIZ;
- m->min.bufRemains -= 1;
- }
- spin_unlock_irqrestore( &ep->lock, flags );
- }
-
- count-=cnt;
- buffer+=cnt;
- ret+=cnt;
-
- break;
- }
-
- remove_wait_queue( &ep->wait, &wait );
- set_current_state(TASK_RUNNING);
-
- return ret;
-}
-
-
-/** Basic operation on /dev/midiXX as registered through struct file_operations.
- *
- * Basic Contract: Take MIDI data byte-by-byte and pass it to
- * writeMidi() which packages MIDI data into USB-MIDI stream.
- * Then flushMidiData() is called to ensure all bytes have been written
- * in a timely fashion.
- *
- **/
-
-static ssize_t usb_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
-{
- struct usb_mididev *m = (struct usb_mididev *)file->private_data;
- ssize_t ret;
- unsigned long int flags;
-
- if ( !access_ok(VERIFY_READ, buffer, count) ) {
- return -EFAULT;
- }
- if ( count == 0 ) {
- return 0;
- }
-
- ret = 0;
- while( count > 0 ) {
- unsigned char c;
-
- if (copy_from_user((unsigned char *)&c, buffer, 1)) {
- if ( ret == 0 )
- ret = -EFAULT;
- break;
- }
- if( midi_write(m, (int)c) ) {
- if ( ret == 0 )
- ret = -EFAULT;
- break;
- }
- count--;
- buffer++;
- ret++;
- }
-
- spin_lock_irqsave( &m->mout.ep->lock, flags );
- if ( flush_midi_buffer(m->mout.ep) < 0 ) {
- ret = -EFAULT;
- }
- spin_unlock_irqrestore( &m->mout.ep->lock, flags );
-
- return ret;
-}
-
-/** Basic operation on /dev/midiXX as registered through struct file_operations.
- *
- * Basic contract: Wait (spin) until ready to read or write on the file.
- *
- **/
-static unsigned int usb_midi_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct usb_mididev *m = (struct usb_mididev *)file->private_data;
- struct midi_in_endpoint *iep = m->min.ep;
- struct midi_out_endpoint *oep = m->mout.ep;
- unsigned long flags;
- unsigned int mask = 0;
-
- if ( file->f_mode & FMODE_READ ) {
- poll_wait( file, &iep->wait, wait );
- spin_lock_irqsave( &iep->lock, flags );
- if ( m->min.bufRemains > 0 )
- mask |= POLLIN | POLLRDNORM;
- spin_unlock_irqrestore( &iep->lock, flags );
- }
-
- if ( file->f_mode & FMODE_WRITE ) {
- poll_wait( file, &oep->wait, wait );
- spin_lock_irqsave( &oep->lock, flags );
- if ( oep->bufWrPtr < oep->bufSize )
- mask |= POLLOUT | POLLWRNORM;
- spin_unlock_irqrestore( &oep->lock, flags );
- }
-
- return mask;
-}
-
-
-/** Basic operation on /dev/midiXX as registered through struct file_operations.
- *
- * Basic contract: This is always the first operation performed on the
- * device node. If no method is defined, the open succeeds without any
- * notification given to the module.
- *
- **/
-
-static int usb_midi_open(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- DECLARE_WAITQUEUE(wait, current);
- struct usb_midi_state *s;
- struct usb_mididev *m;
- unsigned long flags;
- int succeed = 0;
-
-#if 0
- printk(KERN_INFO "usb-midi: Open minor= %d.\n", minor);
-#endif
-
- for(;;) {
- down(&open_sem);
- list_for_each_entry(s, &mididevs, mididev) {
- list_for_each_entry(m, &s->midiDevList, list) {
- if ( !((m->dev_midi ^ minor) & ~0xf) )
- goto device_found;
- }
- }
- up(&open_sem);
- return -ENODEV;
-
- device_found:
- if ( !s->usbdev ) {
- up(&open_sem);
- return -EIO;
- }
- if ( !(m->open_mode & file->f_mode) ) {
- break;
- }
- if ( file->f_flags & O_NONBLOCK ) {
- up(&open_sem);
- return -EBUSY;
- }
- __set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue( &open_wait, &wait );
- up(&open_sem);
- schedule();
- remove_wait_queue( &open_wait, &wait );
- if ( signal_pending(current) ) {
- return -ERESTARTSYS;
- }
- }
-
- file->private_data = m;
- spin_lock_irqsave( &s->lock, flags );
-
- if ( !(m->open_mode & (FMODE_READ | FMODE_WRITE)) ) {
- //FIXME: intented semantics unclear here
- m->min.bufRdPtr = 0;
- m->min.bufWrPtr = 0;
- m->min.bufRemains = 0;
- spin_lock_init(&m->min.ep->lock);
-
- m->mout.bufPtr = 0;
- m->mout.bufRemains = 0;
- m->mout.isInExclusive = 0;
- m->mout.lastEvent = 0;
- spin_lock_init(&m->mout.ep->lock);
- }
-
- if ( (file->f_mode & FMODE_READ) && m->min.ep != NULL ) {
- unsigned long int flagsep;
- spin_lock_irqsave( &m->min.ep->lock, flagsep );
- m->min.ep->cables[m->min.cableId] = m;
- m->min.ep->readers += 1;
- m->min.bufRdPtr = 0;
- m->min.bufWrPtr = 0;
- m->min.bufRemains = 0;
- spin_unlock_irqrestore( &m->min.ep->lock, flagsep );
-
- if ( !(m->min.ep->urbSubmitted)) {
-
- /* urb->dev must be reinitialized on 2.4.x kernels */
- m->min.ep->urb->dev = m->min.ep->usbdev;
-
- if ( usb_submit_urb(m->min.ep->urb, GFP_ATOMIC) ) {
- printk(KERN_ERR "usbmidi: Cannot submit urb for MIDI-IN\n");
- }
- m->min.ep->urbSubmitted = 1;
- }
- m->open_mode |= FMODE_READ;
- succeed = 1;
- }
-
- if ( (file->f_mode & FMODE_WRITE) && m->mout.ep != NULL ) {
- m->mout.bufPtr = 0;
- m->mout.bufRemains = 0;
- m->mout.isInExclusive = 0;
- m->mout.lastEvent = 0;
- m->open_mode |= FMODE_WRITE;
- succeed = 1;
- }
-
- spin_unlock_irqrestore( &s->lock, flags );
-
- s->count++;
- up(&open_sem);
-
- /** Changed to prevent extra increments to USE_COUNT. **/
- if (!succeed) {
- return -EBUSY;
- }
-
-#if 0
- printk(KERN_INFO "usb-midi: Open Succeeded. minor= %d.\n", minor);
-#endif
-
- return nonseekable_open(inode, file); /** Success. **/
-}
-
-
-/** Basic operation on /dev/midiXX as registered through struct file_operations.
- *
- * Basic contract: Close an opened file and deallocate anything we allocated.
- * Like open(), this can be missing. If open set file->private_data,
- * release() must clear it.
- *
- **/
-
-static int usb_midi_release(struct inode *inode, struct file *file)
-{
- struct usb_mididev *m = (struct usb_mididev *)file->private_data;
- struct usb_midi_state *s = (struct usb_midi_state *)m->midi;
-
-#if 0
- printk(KERN_INFO "usb-midi: Close.\n");
-#endif
-
- down(&open_sem);
-
- if ( m->open_mode & FMODE_WRITE ) {
- m->open_mode &= ~FMODE_WRITE;
- usb_kill_urb( m->mout.ep->urb );
- }
-
- if ( m->open_mode & FMODE_READ ) {
- unsigned long int flagsep;
- spin_lock_irqsave( &m->min.ep->lock, flagsep );
- m->min.ep->cables[m->min.cableId] = NULL; // discard cable
- m->min.ep->readers -= 1;
- m->open_mode &= ~FMODE_READ;
- if ( m->min.ep->readers == 0 &&
- m->min.ep->urbSubmitted ) {
- m->min.ep->urbSubmitted = 0;
- usb_kill_urb(m->min.ep->urb);
- }
- spin_unlock_irqrestore( &m->min.ep->lock, flagsep );
- }
-
- s->count--;
-
- up(&open_sem);
- wake_up(&open_wait);
-
- file->private_data = NULL;
- return 0;
-}
-
-static struct file_operations usb_midi_fops = {
- .owner = THIS_MODULE,
- .llseek = usb_midi_llseek,
- .read = usb_midi_read,
- .write = usb_midi_write,
- .poll = usb_midi_poll,
- .open = usb_midi_open,
- .release = usb_midi_release,
-};
-
-/* ------------------------------------------------------------------------- */
-
-/** Returns filled midi_in_endpoint structure or null on failure.
- *
- * Parameters:
- * d - a usb_device
- * endPoint - An usb endpoint in the range 0 to 15.
- * Called by allocUsbMidiDev();
- *
- **/
-
-static struct midi_in_endpoint *alloc_midi_in_endpoint( struct usb_device *d, int endPoint )
-{
- struct midi_in_endpoint *ep;
- int bufSize;
- int pipe;
-
- endPoint &= 0x0f; /* Silently force endPoint to lie in range 0 to 15. */
-
- pipe = usb_rcvbulkpipe( d, endPoint );
- bufSize = usb_maxpacket( d, pipe, 0 );
- /* usb_pipein() = ! usb_pipeout() = true for an in Endpoint */
-
- ep = (struct midi_in_endpoint *)kmalloc(sizeof(struct midi_in_endpoint), GFP_KERNEL);
- if ( !ep ) {
- printk(KERN_ERR "usbmidi: no memory for midi in-endpoint\n");
- return NULL;
- }
- memset( ep, 0, sizeof(struct midi_in_endpoint) );
-// this sets cables[] and readers to 0, too.
-// for (i=0; i<16; i++) ep->cables[i] = 0; // discard cable
-// ep->readers = 0;
-
- ep->endpoint = endPoint;
-
- ep->recvBuf = (unsigned char *)kmalloc(sizeof(unsigned char)*(bufSize), GFP_KERNEL);
- if ( !ep->recvBuf ) {
- printk(KERN_ERR "usbmidi: no memory for midi in-endpoint buffer\n");
- kfree(ep);
- return NULL;
- }
-
- ep->urb = usb_alloc_urb(0, GFP_KERNEL); /* no ISO */
- if ( !ep->urb ) {
- printk(KERN_ERR "usbmidi: no memory for midi in-endpoint urb\n");
- kfree(ep->recvBuf);
- kfree(ep);
- return NULL;
- }
- usb_fill_bulk_urb( ep->urb, d,
- usb_rcvbulkpipe(d, endPoint),
- (unsigned char *)ep->recvBuf, bufSize,
- usb_bulk_read, ep );
-
- /* ep->bufRdPtr = 0; */
- /* ep->bufWrPtr = 0; */
- /* ep->bufRemains = 0; */
- /* ep->urbSubmitted = 0; */
- ep->recvBufSize = bufSize;
-
- init_waitqueue_head(&ep->wait);
-
- return ep;
-}
-
-static int remove_midi_in_endpoint( struct midi_in_endpoint *min )
-{
- usb_kill_urb( min->urb );
- usb_free_urb( min->urb );
- kfree( min->recvBuf );
- kfree( min );
-
- return 0;
-}
-
-/** Returns filled midi_out_endpoint structure or null on failure.
- *
- * Parameters:
- * d - a usb_device
- * endPoint - An usb endpoint in the range 0 to 15.
- * Called by allocUsbMidiDev();
- *
- **/
-static struct midi_out_endpoint *alloc_midi_out_endpoint( struct usb_device *d, int endPoint )
-{
- struct midi_out_endpoint *ep = NULL;
- int pipe;
- int bufSize;
-
- endPoint &= 0x0f;
- pipe = usb_sndbulkpipe( d, endPoint );
- bufSize = usb_maxpacket( d, pipe, 1 );
-
- ep = (struct midi_out_endpoint *)kmalloc(sizeof(struct midi_out_endpoint), GFP_KERNEL);
- if ( !ep ) {
- printk(KERN_ERR "usbmidi: no memory for midi out-endpoint\n");
- return NULL;
- }
- memset( ep, 0, sizeof(struct midi_out_endpoint) );
-
- ep->endpoint = endPoint;
- ep->buf = (unsigned char *)kmalloc(sizeof(unsigned char)*bufSize, GFP_KERNEL);
- if ( !ep->buf ) {
- printk(KERN_ERR "usbmidi: no memory for midi out-endpoint buffer\n");
- kfree(ep);
- return NULL;
- }
-
- ep->urb = usb_alloc_urb(0, GFP_KERNEL); /* no ISO */
- if ( !ep->urb ) {
- printk(KERN_ERR "usbmidi: no memory for midi out-endpoint urb\n");
- kfree(ep->buf);
- kfree(ep);
- return NULL;
- }
-
- ep->bufSize = bufSize;
- /* ep->bufWrPtr = 0; */
-
- init_waitqueue_head(&ep->wait);
-
- return ep;
-}
-
-
-static int remove_midi_out_endpoint( struct midi_out_endpoint *mout )
-{
- usb_kill_urb( mout->urb );
- usb_free_urb( mout->urb );
- kfree( mout->buf );
- kfree( mout );
-
- return 0;
-}
-
-
-/** Returns a filled usb_mididev structure, registered as a Linux MIDI device.
- *
- * Returns null if memory is not available or the device cannot be registered.
- * Called by allocUsbMidiDev();
- *
- **/
-static struct usb_mididev *allocMidiDev(
- struct usb_midi_state *s,
- struct midi_in_endpoint *min,
- struct midi_out_endpoint *mout,
- int inCableId,
- int outCableId )
-{
- struct usb_mididev *m;
-
- m = (struct usb_mididev *)kmalloc(sizeof(struct usb_mididev), GFP_KERNEL);
- if (!m) {
- printk(KERN_ERR "usbmidi: no memory for midi device\n");
- return NULL;
- }
-
- memset(m, 0, sizeof(struct usb_mididev));
-
- if ((m->dev_midi = register_sound_midi(&usb_midi_fops, -1)) < 0) {
- printk(KERN_ERR "usbmidi: cannot register midi device\n");
- kfree(m);
- return NULL;
- }
-
- m->midi = s;
- /* m->open_mode = 0; */
-
- if ( min ) {
- m->min.ep = min;
- m->min.ep->usbdev = s->usbdev;
- m->min.cableId = inCableId;
- }
- /* m->min.bufPtr = 0; */
- /* m->min.bufRemains = 0; */
-
- if ( mout ) {
- m->mout.ep = mout;
- m->mout.ep->usbdev = s->usbdev;
- m->mout.cableId = outCableId;
- }
- /* m->mout.bufPtr = 0; */
- /* m->mout.bufRemains = 0; */
- /* m->mout.isInExclusive = 0; */
- /* m->mout.lastEvent = 0; */
-
- m->singlebyte = singlebyte;
-
- return m;
-}
-
-
-static void release_midi_device( struct usb_midi_state *s )
-{
- struct usb_mididev *m;
- struct midi_in_endpoint *min;
- struct midi_out_endpoint *mout;
-
- if ( s->count > 0 ) {
- up(&open_sem);
- return;
- }
- up( &open_sem );
- wake_up( &open_wait );
-
- while(!list_empty(&s->inEndpointList)) {
- min = list_entry(s->inEndpointList.next, struct midi_in_endpoint, list);
- list_del(&min->list);
- remove_midi_in_endpoint(min);
- }
-
- while(!list_empty(&s->outEndpointList)) {
- mout = list_entry(s->outEndpointList.next, struct midi_out_endpoint, list);
- list_del(&mout->list);
- remove_midi_out_endpoint(mout);
- }
-
- while(!list_empty(&s->midiDevList)) {
- m = list_entry(s->midiDevList.next, struct usb_mididev, list);
- list_del(&m->list);
- kfree(m);
- }
-
- kfree(s);
-
- return;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-/** Utility routine to find a descriptor in a dump of many descriptors.
- * Returns start of descriptor or NULL if not found.
- * descStart pointer to list of interfaces.
- * descLength length (in bytes) of dump
- * after (ignored if NULL) this routine returns only descriptors after "after"
- * dtype (mandatory) The descriptor type.
- * iface (ignored if -1) returns descriptor at/following given interface
- * altSetting (ignored if -1) returns descriptor at/following given altSetting
- *
- *
- * Called by parseDescriptor(), find_csinterface_descriptor();
- *
- */
-static void *find_descriptor( void *descStart, unsigned int descLength, void *after, unsigned char dtype, int iface, int altSetting )
-{
- unsigned char *p, *end, *next;
- int interfaceNumber = -1, altSet = -1;
-
- p = descStart;
- end = p + descLength;
- for( ; p < end; ) {
- if ( p[0] < 2 )
- return NULL;
- next = p + p[0];
- if ( next > end )
- return NULL;
- if ( p[1] == USB_DT_INTERFACE ) {
- if ( p[0] < USB_DT_INTERFACE_SIZE )
- return NULL;
- interfaceNumber = p[2];
- altSet = p[3];
- }
- if ( p[1] == dtype &&
- ( !after || ( p > (unsigned char *)after) ) &&
- ( ( iface == -1) || (iface == interfaceNumber) ) &&
- ( (altSetting == -1) || (altSetting == altSet) )) {
- return p;
- }
- p = next;
- }
- return NULL;
-}
-
-/** Utility to find a class-specific interface descriptor.
- * dsubtype is a descriptor subtype
- * Called by parseDescriptor();
- **/
-static void *find_csinterface_descriptor(void *descStart, unsigned int descLength, void *after, u8 dsubtype, int iface, int altSetting)
-{
- unsigned char *p;
-
- p = find_descriptor( descStart, descLength, after, USB_DT_CS_INTERFACE, iface, altSetting );
- while ( p ) {
- if ( p[0] >= 3 && p[2] == dsubtype )
- return p;
- p = find_descriptor( descStart, descLength, p, USB_DT_CS_INTERFACE,
- iface, altSetting );
- }
- return NULL;
-}
-
-
-/** The magic of making a new usb_midi_device from config happens here.
- *
- * The caller is responsible for free-ing this return value (if not NULL).
- *
- **/
-static struct usb_midi_device *parse_descriptor( struct usb_device *d, unsigned char *buffer, int bufSize, unsigned int ifnum , unsigned int altSetting, int quirks)
-{
- struct usb_midi_device *u;
- unsigned char *p1;
- unsigned char *p2;
- unsigned char *next;
- int iep, oep;
- int length;
- unsigned long longBits;
- int pins, nbytes, offset, shift, jack;
-#ifdef HAVE_JACK_STRINGS
- /** Jacks can have associated names. **/
- unsigned char jack2string[256];
-#endif
-
- u = NULL;
- /* find audiocontrol interface */
- p1 = find_csinterface_descriptor( buffer, bufSize, NULL,
- MS_HEADER, ifnum, altSetting);
-
- if ( !p1 ) {
- goto error_end;
- }
-
- if ( p1[0] < MS_HEADER_LENGTH ) {
- goto error_end;
- }
-
- /* Assume success. Since the device corresponds to USB-MIDI spec, we assume
- that the rest of the USB 2.0 spec is obeyed. */
-
- u = (struct usb_midi_device *)kmalloc( sizeof(struct usb_midi_device), GFP_KERNEL );
- if ( !u ) {
- return NULL;
- }
- u->deviceName = NULL;
- u->idVendor = le16_to_cpu(d->descriptor.idVendor);
- u->idProduct = le16_to_cpu(d->descriptor.idProduct);
- u->interface = ifnum;
- u->altSetting = altSetting;
- u->in[0].endpoint = -1;
- u->in[0].cableId = -1;
- u->out[0].endpoint = -1;
- u->out[0].cableId = -1;
-
-
- printk(KERN_INFO "usb-midi: Found MIDIStreaming device corresponding to Release %d.%02d of spec.\n",
- (p1[4] >> 4) * 10 + (p1[4] & 0x0f ),
- (p1[3] >> 4) * 10 + (p1[3] & 0x0f )
- );
-
- length = p1[5] | (p1[6] << 8);
-
-#ifdef HAVE_JACK_STRINGS
- memset(jack2string, 0, sizeof(unsigned char) * 256);
-#endif
-
- length -= p1[0];
- for (p2 = p1 + p1[0]; length > 0; p2 = next) {
- next = p2 + p2[0];
- length -= p2[0];
-
- if (p2[0] < 2 )
- break;
- if (p2[1] != USB_DT_CS_INTERFACE)
- break;
- if (p2[2] == MIDI_IN_JACK && p2[0] >= 6 ) {
- jack = p2[4];
-#ifdef HAVE_JACK_STRINGS
- jack2string[jack] = p2[5];
-#endif
- printk(KERN_INFO "usb-midi: Found IN Jack 0x%02x %s\n",
- jack, (p2[3] == EMBEDDED_JACK)?"EMBEDDED":"EXTERNAL" );
- } else if ( p2[2] == MIDI_OUT_JACK && p2[0] >= 6) {
- pins = p2[5];
- if ( p2[0] < (6 + 2 * pins) )
- continue;
- jack = p2[4];
-#ifdef HAVE_JACK_STRINGS
- jack2string[jack] = p2[5 + 2 * pins];
-#endif
- printk(KERN_INFO "usb-midi: Found OUT Jack 0x%02x %s, %d pins\n",
- jack, (p2[3] == EMBEDDED_JACK)?"EMBEDDED":"EXTERNAL", pins );
- } else if ( p2[2] == ELEMENT_DESCRIPTOR && p2[0] >= 10) {
- pins = p2[4];
- if ( p2[0] < (9 + 2 * pins ) )
- continue;
- nbytes = p2[8 + 2 * pins ];
- if ( p2[0] < (10 + 2 * pins + nbytes) )
- continue;
- longBits = 0L;
- for ( offset = 0, shift = 0; offset < nbytes && offset < 8; offset ++, shift += 8) {
- longBits |= ((long)(p2[9 + 2 * pins + offset])) << shift;
- }
- jack = p2[3];
-#ifdef HAVE_JACK_STRINGS
- jack2string[jack] = p2[9 + 2 * pins + nbytes];
-#endif
- printk(KERN_INFO "usb-midi: Found ELEMENT 0x%02x, %d/%d pins in/out, bits: 0x%016lx\n",
- jack, pins, (int)(p2[5 + 2 * pins]), (long)longBits );
- } else {
- }
- }
-
- iep=0;
- oep=0;
-
- if (quirks==0) {
- /* MIDISTREAM */
- p2 = NULL;
- for (p1 = find_descriptor(buffer, bufSize, NULL, USB_DT_ENDPOINT,
- ifnum, altSetting ); p1; p1 = next ) {
- next = find_descriptor(buffer, bufSize, p1, USB_DT_ENDPOINT,
- ifnum, altSetting );
- p2 = find_descriptor(buffer, bufSize, p1, USB_DT_CS_ENDPOINT,
- ifnum, altSetting );
-
- if ( p2 && next && ( p2 > next ) )
- p2 = NULL;
-
- if ( p1[0] < 9 || !p2 || p2[0] < 4 )
- continue;
-
- if ( (p1[2] & 0x80) == 0x80 ) {
- if ( iep < 15 ) {
- pins = p2[3]; /* not pins -- actually "cables" */
- if ( pins > 16 )
- pins = 16;
- u->in[iep].endpoint = p1[2];
- u->in[iep].cableId = ( 1 << pins ) - 1;
- if ( u->in[iep].cableId )
- iep ++;
- if ( iep < 15 ) {
- u->in[iep].endpoint = -1;
- u->in[iep].cableId = -1;
- }
- }
- } else {
- if ( oep < 15 ) {
- pins = p2[3]; /* not pins -- actually "cables" */
- if ( pins > 16 )
- pins = 16;
- u->out[oep].endpoint = p1[2];
- u->out[oep].cableId = ( 1 << pins ) - 1;
- if ( u->out[oep].cableId )
- oep ++;
- if ( oep < 15 ) {
- u->out[oep].endpoint = -1;
- u->out[oep].cableId = -1;
- }
- }
- }
-
- }
- } else if (quirks==1) {
- /* YAMAHA quirks */
- for (p1 = find_descriptor(buffer, bufSize, NULL, USB_DT_ENDPOINT,
- ifnum, altSetting ); p1; p1 = next ) {
- next = find_descriptor(buffer, bufSize, p1, USB_DT_ENDPOINT,
- ifnum, altSetting );
-
- if ( p1[0] < 7 )
- continue;
-
- if ( (p1[2] & 0x80) == 0x80 ) {
- if ( iep < 15 ) {
- pins = iep+1;
- if ( pins > 16 )
- pins = 16;
- u->in[iep].endpoint = p1[2];
- u->in[iep].cableId = ( 1 << pins ) - 1;
- if ( u->in[iep].cableId )
- iep ++;
- if ( iep < 15 ) {
- u->in[iep].endpoint = -1;
- u->in[iep].cableId = -1;
- }
- }
- } else {
- if ( oep < 15 ) {
- pins = oep+1;
- u->out[oep].endpoint = p1[2];
- u->out[oep].cableId = ( 1 << pins ) - 1;
- if ( u->out[oep].cableId )
- oep ++;
- if ( oep < 15 ) {
- u->out[oep].endpoint = -1;
- u->out[oep].cableId = -1;
- }
- }
- }
-
- }
- }
-
- if ( !iep && ! oep ) {
- goto error_end;
- }
-
- return u;
-
-error_end:
- kfree(u);
- return NULL;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/** Returns number between 0 and 16.
- *
- **/
-static int on_bits( unsigned short v )
-{
- int i;
- int ret=0;
-
- for ( i=0 ; i<16 ; i++ ) {
- if ( v & (1<<i) )
- ret++;
- }
-
- return ret;
-}
-
-
-/** USB-device will be interrogated for altSetting.
- *
- * Returns negative on error.
- * Called by allocUsbMidiDev();
- *
- **/
-
-static int get_alt_setting( struct usb_device *d, int ifnum )
-{
- int alts, alt=0;
- struct usb_interface *iface;
- struct usb_host_interface *interface;
- struct usb_endpoint_descriptor *ep;
- int epin, epout;
- int i;
-
- iface = usb_ifnum_to_if( d, ifnum );
- alts = iface->num_altsetting;
-
- for ( alt=0 ; alt<alts ; alt++ ) {
- interface = &iface->altsetting[alt];
- epin = -1;
- epout = -1;
-
- for ( i=0 ; i<interface->desc.bNumEndpoints ; i++ ) {
- ep = &interface->endpoint[i].desc;
- if ( (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ) {
- continue;
- }
- if ( (ep->bEndpointAddress & USB_DIR_IN) && epin < 0 ) {
- epin = i;
- } else if ( epout < 0 ) {
- epout = i;
- }
- if ( epin >= 0 && epout >= 0 ) {
- return interface->desc.bAlternateSetting;
- }
- }
- }
-
- return -ENODEV;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-
-/** Returns 0 if successful in allocating and registering internal structures.
- * Returns negative on failure.
- * Calls allocMidiDev which additionally registers /dev/midiXX devices.
- * Writes messages on success to indicate which /dev/midiXX is which physical
- * endpoint.
- *
- **/
-static int alloc_usb_midi_device( struct usb_device *d, struct usb_midi_state *s, struct usb_midi_device *u )
-{
- struct usb_mididev **mdevs=NULL;
- struct midi_in_endpoint *mins[15], *min;
- struct midi_out_endpoint *mouts[15], *mout;
- int inDevs=0, outDevs=0;
- int inEndpoints=0, outEndpoints=0;
- int inEndpoint, outEndpoint;
- int inCableId, outCableId;
- int i;
- int devices = 0;
- int alt = 0;
-
- /* Obtain altSetting or die.. */
- alt = u->altSetting;
- if ( alt < 0 ) {
- alt = get_alt_setting( d, u->interface );
- }
- if ( alt < 0 )
- return -ENXIO;
-
- /* Configure interface */
- if ( usb_set_interface( d, u->interface, alt ) < 0 ) {
- return -ENXIO;
- }
-
- for ( i = 0 ; i < 15 ; i++ ) {
- mins[i] = NULL;
- mouts[i] = NULL;
- }
-
- /* Begin Allocation */
- while( inEndpoints < 15
- && inDevs < maxdevices
- && u->in[inEndpoints].cableId >= 0 ) {
- inDevs += on_bits((unsigned short)u->in[inEndpoints].cableId);
- mins[inEndpoints] = alloc_midi_in_endpoint( d, u->in[inEndpoints].endpoint );
- if ( mins[inEndpoints] == NULL )
- goto error_end;
- inEndpoints++;
- }
-
- while( outEndpoints < 15
- && outDevs < maxdevices
- && u->out[outEndpoints].cableId >= 0 ) {
- outDevs += on_bits((unsigned short)u->out[outEndpoints].cableId);
- mouts[outEndpoints] = alloc_midi_out_endpoint( d, u->out[outEndpoints].endpoint );
- if ( mouts[outEndpoints] == NULL )
- goto error_end;
- outEndpoints++;
- }
-
- devices = inDevs > outDevs ? inDevs : outDevs;
- devices = maxdevices > devices ? devices : maxdevices;
-
- /* obtain space for device name (iProduct) if not known. */
- if ( ! u->deviceName ) {
- mdevs = (struct usb_mididev **)
- kmalloc(sizeof(struct usb_mididevs *)*devices
- + sizeof(char) * 256, GFP_KERNEL);
- } else {
- mdevs = (struct usb_mididev **)
- kmalloc(sizeof(struct usb_mididevs *)*devices, GFP_KERNEL);
- }
-
- if ( !mdevs ) {
- /* devices = 0; */
- /* mdevs = NULL; */
- goto error_end;
- }
- for ( i=0 ; i<devices ; i++ ) {
- mdevs[i] = NULL;
- }
-
- /* obtain device name (iProduct) if not known. */
- if ( ! u->deviceName ) {
- u->deviceName = (char *) (mdevs + devices);
- if ( ! d->have_langid && d->descriptor.iProduct) {
- alt = usb_get_string(d, 0, 0, u->deviceName, 250);
- if (alt < 0) {
- printk(KERN_INFO "error getting string descriptor 0 (error=%d)\n", alt);
- } else if (u->deviceName[0] < 4) {
- printk(KERN_INFO "string descriptor 0 too short (length = %d)\n", alt);
- } else {
- printk(KERN_INFO "string descriptor 0 found (length = %d)\n", alt);
- for(; alt >= 4; alt -= 2) {
- i = u->deviceName[alt-2] | (u->deviceName[alt-1]<< 8);
- printk(KERN_INFO "usb-midi: langid(%d) 0x%04x\n",
- (alt-4) >> 1, i);
- if ( ( ( i ^ ulangid ) & 0xff ) == 0 ) {
- d->have_langid = 1;
- d->string_langid = i;
- printk(KERN_INFO "usb-midi: langid(match) 0x%04x\n", i);
- if ( i == ulangid )
- break;
- }
- }
- }
- }
- u->deviceName[0] = (char) 0;
- if (d->descriptor.iProduct) {
- printk(KERN_INFO "usb-midi: fetchString(%d)\n", d->descriptor.iProduct);
- alt = usb_string(d, d->descriptor.iProduct, u->deviceName, 255);
- if( alt < 0 ) {
- u->deviceName[0] = (char) 0;
- }
- printk(KERN_INFO "usb-midi: fetchString = %d\n", alt);
- }
- /* Failsafe */
- if ( !u->deviceName[0] ) {
- if (le16_to_cpu(d->descriptor.idVendor) == USB_VENDOR_ID_ROLAND ) {
- strcpy(u->deviceName, "Unknown Roland");
- } else if (le16_to_cpu(d->descriptor.idVendor) == USB_VENDOR_ID_STEINBERG ) {
- strcpy(u->deviceName, "Unknown Steinberg");
- } else if (le16_to_cpu(d->descriptor.idVendor) == USB_VENDOR_ID_YAMAHA ) {
- strcpy(u->deviceName, "Unknown Yamaha");
- } else {
- strcpy(u->deviceName, "Unknown");
- }
- }
- }
-
- inEndpoint = 0; inCableId = -1;
- outEndpoint = 0; outCableId = -1;
-
- for ( i=0 ; i<devices ; i++ ) {
- for ( inCableId ++ ;
- inEndpoint <15
- && mins[inEndpoint]
- && !(u->in[inEndpoint].cableId & (1<<inCableId)) ;
- inCableId++ ) {
- if ( inCableId >= 16 ) {
- inEndpoint ++;
- inCableId = 0;
- }
- }
- min = mins[inEndpoint];
- for ( outCableId ++ ;
- outEndpoint <15
- && mouts[outEndpoint]
- && !(u->out[outEndpoint].cableId & (1<<outCableId)) ;
- outCableId++ ) {
- if ( outCableId >= 16 ) {
- outEndpoint ++;
- outCableId = 0;
- }
- }
- mout = mouts[outEndpoint];
-
- mdevs[i] = allocMidiDev( s, min, mout, inCableId, outCableId );
- if ( mdevs[i] == NULL )
- goto error_end;
-
- }
-
- /* Success! */
- for ( i=0 ; i<devices ; i++ ) {
- list_add_tail( &mdevs[i]->list, &s->midiDevList );
- }
- for ( i=0 ; i<inEndpoints ; i++ ) {
- list_add_tail( &mins[i]->list, &s->inEndpointList );
- }
- for ( i=0 ; i<outEndpoints ; i++ ) {
- list_add_tail( &mouts[i]->list, &s->outEndpointList );
- }
-
- printk(KERN_INFO "usbmidi: found [ %s ] (0x%04x:0x%04x), attached:\n", u->deviceName, u->idVendor, u->idProduct );
- for ( i=0 ; i<devices ; i++ ) {
- int dm = (mdevs[i]->dev_midi-2)>>4;
- if ( mdevs[i]->mout.ep != NULL && mdevs[i]->min.ep != NULL ) {
- printk(KERN_INFO "usbmidi: /dev/midi%02d: in (ep:%02x cid:%2d bufsiz:%2d) out (ep:%02x cid:%2d bufsiz:%2d)\n",
- dm,
- mdevs[i]->min.ep->endpoint|USB_DIR_IN, mdevs[i]->min.cableId, mdevs[i]->min.ep->recvBufSize,
- mdevs[i]->mout.ep->endpoint, mdevs[i]->mout.cableId, mdevs[i]->mout.ep->bufSize);
- } else if ( mdevs[i]->min.ep != NULL ) {
- printk(KERN_INFO "usbmidi: /dev/midi%02d: in (ep:%02x cid:%2d bufsiz:%02d)\n",
- dm,
- mdevs[i]->min.ep->endpoint|USB_DIR_IN, mdevs[i]->min.cableId, mdevs[i]->min.ep->recvBufSize);
- } else if ( mdevs[i]->mout.ep != NULL ) {
- printk(KERN_INFO "usbmidi: /dev/midi%02d: out (ep:%02x cid:%2d bufsiz:%02d)\n",
- dm,
- mdevs[i]->mout.ep->endpoint, mdevs[i]->mout.cableId, mdevs[i]->mout.ep->bufSize);
- }
- }
-
- kfree(mdevs);
- return 0;
-
- error_end:
- if ( mdevs != NULL ) {
- for ( i=0 ; i<devices ; i++ ) {
- if ( mdevs[i] != NULL ) {
- unregister_sound_midi( mdevs[i]->dev_midi );
- kfree(mdevs[i]);
- }
- }
- kfree(mdevs);
- }
-
- for ( i=0 ; i<15 ; i++ ) {
- if ( mins[i] != NULL ) {
- remove_midi_in_endpoint( mins[i] );
- }
- if ( mouts[i] != NULL ) {
- remove_midi_out_endpoint( mouts[i] );
- }
- }
-
- return -ENOMEM;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/** Attempt to scan YAMAHA's device descriptor and detect correct values of
- * them.
- * Return 0 on succes, negative on failure.
- * Called by usb_midi_probe();
- **/
-
-static int detect_yamaha_device( struct usb_device *d,
- struct usb_interface *iface, unsigned int ifnum,
- struct usb_midi_state *s)
-{
- struct usb_host_interface *interface;
- struct usb_midi_device *u;
- unsigned char *buffer;
- int bufSize;
- int i;
- int alts=-1;
- int ret;
-
- if (le16_to_cpu(d->descriptor.idVendor) != USB_VENDOR_ID_YAMAHA) {
- return -EINVAL;
- }
-
- for ( i=0 ; i < iface->num_altsetting; i++ ) {
- interface = iface->altsetting + i;
-
- if ( interface->desc.bInterfaceClass != 255 ||
- interface->desc.bInterfaceSubClass != 0 )
- continue;
- alts = interface->desc.bAlternateSetting;
- }
- if ( alts == -1 ) {
- return -EINVAL;
- }
-
- printk(KERN_INFO "usb-midi: Found YAMAHA USB-MIDI device on dev %04x:%04x, iface %d\n",
- le16_to_cpu(d->descriptor.idVendor),
- le16_to_cpu(d->descriptor.idProduct), ifnum);
-
- i = d->actconfig - d->config;
- buffer = d->rawdescriptors[i];
- bufSize = le16_to_cpu(d->actconfig->desc.wTotalLength);
-
- u = parse_descriptor( d, buffer, bufSize, ifnum, alts, 1);
- if ( u == NULL ) {
- return -EINVAL;
- }
-
- ret = alloc_usb_midi_device( d, s, u );
-
- kfree(u);
-
- return ret;
-}
-
-
-/** Scan table of known devices which are only partially compliant with
- * the MIDIStreaming specification.
- * Called by usb_midi_probe();
- *
- **/
-
-static int detect_vendor_specific_device( struct usb_device *d, unsigned int ifnum, struct usb_midi_state *s )
-{
- struct usb_midi_device *u;
- int i;
- int ret = -ENXIO;
-
- for ( i=0; i<VENDOR_SPECIFIC_USB_MIDI_DEVICES ; i++ ) {
- u=&(usb_midi_devices[i]);
-
- if ( le16_to_cpu(d->descriptor.idVendor) != u->idVendor ||
- le16_to_cpu(d->descriptor.idProduct) != u->idProduct ||
- ifnum != u->interface )
- continue;
-
- ret = alloc_usb_midi_device( d, s, u );
- break;
- }
-
- return ret;
-}
-
-
-/** Attempt to match any config of an interface to a MIDISTREAMING interface.
- * Returns 0 on success, negative on failure.
- * Called by usb_midi_probe();
- **/
-static int detect_midi_subclass(struct usb_device *d,
- struct usb_interface *iface, unsigned int ifnum,
- struct usb_midi_state *s)
-{
- struct usb_host_interface *interface;
- struct usb_midi_device *u;
- unsigned char *buffer;
- int bufSize;
- int i;
- int alts=-1;
- int ret;
-
- for ( i=0 ; i < iface->num_altsetting; i++ ) {
- interface = iface->altsetting + i;
-
- if ( interface->desc.bInterfaceClass != USB_CLASS_AUDIO ||
- interface->desc.bInterfaceSubClass != USB_SUBCLASS_MIDISTREAMING )
- continue;
- alts = interface->desc.bAlternateSetting;
- }
- if ( alts == -1 ) {
- return -EINVAL;
- }
-
- printk(KERN_INFO "usb-midi: Found MIDISTREAMING on dev %04x:%04x, iface %d\n",
- le16_to_cpu(d->descriptor.idVendor),
- le16_to_cpu(d->descriptor.idProduct), ifnum);
-
-
- /* From USB Spec v2.0, Section 9.5.
- If the class or vendor specific descriptors use the same format
- as standard descriptors (e.g., start with a length byte and
- followed by a type byte), they must be returned interleaved with
- standard descriptors in the configuration information returned by
- a GetDescriptor(Configuration) request. In this case, the class
- or vendor-specific descriptors must follow a related standard
- descriptor they modify or extend.
- */
-
- i = d->actconfig - d->config;
- buffer = d->rawdescriptors[i];
- bufSize = le16_to_cpu(d->actconfig->desc.wTotalLength);
-
- u = parse_descriptor( d, buffer, bufSize, ifnum, alts, 0);
- if ( u == NULL ) {
- return -EINVAL;
- }
-
- ret = alloc_usb_midi_device( d, s, u );
-
- kfree(u);
-
- return ret;
-}
-
-
-/** When user has requested a specific device, match it exactly.
- *
- * Uses uvendor, uproduct, uinterface, ualt, umin, umout and ucable.
- * Called by usb_midi_probe();
- *
- **/
-static int detect_by_hand(struct usb_device *d, unsigned int ifnum, struct usb_midi_state *s)
-{
- struct usb_midi_device u;
-
- if ( le16_to_cpu(d->descriptor.idVendor) != uvendor ||
- le16_to_cpu(d->descriptor.idProduct) != uproduct ||
- ifnum != uinterface ) {
- return -EINVAL;
- }
-
- if ( ualt < 0 )
- ualt = -1;
-
- if ( umin < 0 || umin > 15 )
- umin = 0x01 | USB_DIR_IN;
- if ( umout < 0 || umout > 15 )
- umout = 0x01;
- if ( ucable < 0 || ucable > 15 )
- ucable = 0;
-
- u.deviceName = NULL; /* A flag for alloc_usb_midi_device to get device
- name from device. */
- u.idVendor = uvendor;
- u.idProduct = uproduct;
- u.interface = uinterface;
- u.altSetting = ualt;
-
- u.in[0].endpoint = umin;
- u.in[0].cableId = (1<<ucable);
-
- u.out[0].endpoint = umout;
- u.out[0].cableId = (1<<ucable);
-
- return alloc_usb_midi_device( d, s, &u );
-}
-
-
-
-/* ------------------------------------------------------------------------- */
-
-static int usb_midi_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_midi_state *s;
- struct usb_device *dev = interface_to_usbdev(intf);
- int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
-
- s = (struct usb_midi_state *)kmalloc(sizeof(struct usb_midi_state), GFP_KERNEL);
- if ( !s )
- return -ENOMEM;
-
- memset( s, 0, sizeof(struct usb_midi_state) );
- INIT_LIST_HEAD(&s->midiDevList);
- INIT_LIST_HEAD(&s->inEndpointList);
- INIT_LIST_HEAD(&s->outEndpointList);
- s->usbdev = dev;
- s->count = 0;
- spin_lock_init(&s->lock);
-
- if (
- detect_by_hand( dev, ifnum, s ) &&
- detect_midi_subclass( dev, intf, ifnum, s ) &&
- detect_vendor_specific_device( dev, ifnum, s ) &&
- detect_yamaha_device( dev, intf, ifnum, s) ) {
- kfree(s);
- return -EIO;
- }
-
- down(&open_sem);
- list_add_tail(&s->mididev, &mididevs);
- up(&open_sem);
-
- usb_set_intfdata (intf, s);
- return 0;
-}
-
-
-static void usb_midi_disconnect(struct usb_interface *intf)
-{
- struct usb_midi_state *s = usb_get_intfdata (intf);
- struct usb_mididev *m;
-
- if ( !s )
- return;
-
- if ( s == (struct usb_midi_state *)-1 ) {
- return;
- }
- if ( !s->usbdev ) {
- return;
- }
- down(&open_sem);
- list_del(&s->mididev);
- INIT_LIST_HEAD(&s->mididev);
- s->usbdev = NULL;
- usb_set_intfdata (intf, NULL);
-
- list_for_each_entry(m, &s->midiDevList, list) {
- wake_up(&(m->min.ep->wait));
- wake_up(&(m->mout.ep->wait));
- if ( m->dev_midi >= 0 ) {
- unregister_sound_midi(m->dev_midi);
- }
- m->dev_midi = -1;
- }
- release_midi_device(s);
- wake_up(&open_wait);
-}
-
-/* we want to look at all devices by hand */
-static struct usb_device_id id_table[] = {
- {.driver_info = 42},
- {}
-};
-
-static struct usb_driver usb_midi_driver = {
- .name = "midi",
- .probe = usb_midi_probe,
- .disconnect = usb_midi_disconnect,
- .id_table = id_table,
-};
-
-/* ------------------------------------------------------------------------- */
-
-static int __init usb_midi_init(void)
-{
- return usb_register(&usb_midi_driver);
-}
-
-static void __exit usb_midi_exit(void)
-{
- usb_deregister(&usb_midi_driver);
-}
-
-module_init(usb_midi_init) ;
-module_exit(usb_midi_exit) ;
-
-#ifdef HAVE_ALSA_SUPPORT
-#define SNDRV_MAIN_OBJECT_FILE
-#include "../../include/driver.h"
-#include "../../include/control.h"
-#include "../../include/info.h"
-#include "../../include/cs46xx.h"
-
-/* ------------------------------------------------------------------------- */
-
-static int snd_usbmidi_input_close(snd_rawmidi_substream_t * substream)
-{
- return 0;
-}
-
-static int snd_usbmidi_input_open(snd_rawmidi_substream_t * substream )
-{
- return 0;
-}
-
-static void snd_usbmidi_input_trigger(snd_rawmidi_substream_t * substream, int up)
-{
- return 0;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-static int snd_usbmidi_output_close(snd_rawmidi_substream_t * substream)
-{
- return 0;
-}
-
-static int snd_usbmidi_output_open(snd_rawmidi_substream_t * substream)
-{
- return 0;
-}
-
-static void snd_usb_midi_output_trigger(snd_rawmidi_substream_t * substream,
- int up)
-{
- return 0;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static snd_rawmidi_ops_t snd_usbmidi_output =
-{
- .open = snd_usbmidi_output_open,
- .close = snd_usbmidi_output_close,
- .trigger = snd_usbmidi_output_trigger,
-};
-static snd_rawmidi_ops_t snd_usbmidi_input =
-{
- .open = snd_usbmidi_input_open,
- .close = snd_usbmidi_input_close,
- .trigger = snd_usbmidi_input_trigger,
-};
-
-int snd_usbmidi_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi)
-{
- snd_rawmidi_t *rmidi;
- int err;
-
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(chip->card, "USB-MIDI", device, 1, 1, &rmidi)) < 0)
- return err;
- strcpy(rmidi->name, "USB-MIDI");
-
- snd_rawmidi_set_ops( rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output );
- snd_rawmidi_set_ops( rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_usbmidi_input );
-
- rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
-
- rmidi->private_data = chip;
- chip->rmidi = rmidi;
- if (rrawmidi)
- *rrawmidi = NULL;
-
- return 0;
-}
-
-int snd_usbmidi_create( snd_card_t * card,
- struct pci_dev * pci,
- usbmidi_t ** rchip )
-{
- usbmidi_t *chip;
- int err, idx;
- snd_region_t *region;
- static snd_device_opt_t ops = {
- .dev_free = snd_usbmidi_dev_free,
- };
-
- *rchip = NULL;
- chip = snd_magic_kcalloc( usbmidi_t, 0, GFP_KERNEL );
- if ( chip == NULL )
- return -ENOMEM;
-}
-
-EXPORT_SYMBOL(snd_usbmidi_create);
-EXPORT_SYMBOL(snd_usbmidi_midi);
-#endif /* HAVE_ALSA_SUPPORT */
-
diff --git a/drivers/usb/class/usb-midi.h b/drivers/usb/class/usb-midi.h
deleted file mode 100644
index 358cdef8492..00000000000
--- a/drivers/usb/class/usb-midi.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- usb-midi.h -- USB-MIDI driver
-
- Copyright (C) 2001
- NAGANO Daisuke <breeze.nagano@nifty.ne.jp>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* ------------------------------------------------------------------------- */
-
-#ifndef _USB_MIDI_H_
-#define _USB_MIDI_H_
-
-#ifndef USB_SUBCLASS_MIDISTREAMING
-#define USB_SUBCLASS_MIDISTREAMING 3
-#endif
-
-/* ------------------------------------------------------------------------- */
-/* Roland MIDI Devices */
-
-#define USB_VENDOR_ID_ROLAND 0x0582
-#define USBMIDI_ROLAND_UA100G 0x0000
-#define USBMIDI_ROLAND_MPU64 0x0002
-#define USBMIDI_ROLAND_SC8850 0x0003
-#define USBMIDI_ROLAND_SC8820 0x0007
-#define USBMIDI_ROLAND_UM2 0x0005
-#define USBMIDI_ROLAND_UM1 0x0009
-#define USBMIDI_ROLAND_PC300 0x0008
-
-/* YAMAHA MIDI Devices */
-#define USB_VENDOR_ID_YAMAHA 0x0499
-#define USBMIDI_YAMAHA_MU1000 0x1001
-
-/* Steinberg MIDI Devices */
-#define USB_VENDOR_ID_STEINBERG 0x0763
-#define USBMIDI_STEINBERG_USB2MIDI 0x1001
-
-/* Mark of the Unicorn MIDI Devices */
-#define USB_VENDOR_ID_MOTU 0x07fd
-#define USBMIDI_MOTU_FASTLANE 0x0001
-
-/* ------------------------------------------------------------------------- */
-/* Supported devices */
-
-struct usb_midi_endpoint {
- int endpoint;
- int cableId; /* if bit-n == 1 then cableId-n is enabled (n: 0 - 15) */
-};
-
-struct usb_midi_device {
- char *deviceName;
-
- u16 idVendor;
- u16 idProduct;
- int interface;
- int altSetting; /* -1: auto detect */
-
- struct usb_midi_endpoint in[15];
- struct usb_midi_endpoint out[15];
-};
-
-static struct usb_midi_device usb_midi_devices[] = {
- { /* Roland UM-1 */
- "Roland UM-1",
- USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM1, 2, -1,
- { { 0x81, 1 }, {-1, -1} },
- { { 0x01, 1,}, {-1, -1} },
- },
-
- { /* Roland UM-2 */
- "Roland UM-2" ,
- USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM2, 2, -1,
- { { 0x81, 3 }, {-1, -1} },
- { { 0x01, 3,}, {-1, -1} },
- },
-
-/** Next entry courtesy research by Michael Minn <michael@michaelminn.com> **/
- { /* Roland UA-100 */
- "Roland UA-100",
- USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UA100G, 2, -1,
- { { 0x82, 7 }, {-1, -1} }, /** cables 0,1 and 2 for SYSEX **/
- { { 0x02, 7 }, {-1, -1} },
- },
-
-/** Next entry courtesy research by Michael Minn <michael@michaelminn.com> **/
- { /* Roland SC8850 */
- "Roland SC8850",
- USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8850, 2, -1,
- { { 0x81, 0x3f }, {-1, -1} },
- { { 0x01, 0x3f }, {-1, -1} },
- },
-
- { /* Roland SC8820 */
- "Roland SC8820",
- USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820, 2, -1,
- { { 0x81, 0x13 }, {-1, -1} },
- { { 0x01, 0x13 }, {-1, -1} },
- },
-
- { /* Roland SC8820 */
- "Roland SC8820",
- USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820, 2, -1,
- { { 0x81, 17 }, {-1, -1} },
- { { 0x01, 17 }, {-1, -1} },
- },
-
- { /* YAMAHA MU1000 */
- "YAMAHA MU1000",
- USB_VENDOR_ID_YAMAHA, USBMIDI_YAMAHA_MU1000, 0, -1,
- { { 0x81, 1 }, {-1, -1} },
- { { 0x01, 15 }, {-1, -1} },
- },
- { /* Roland PC-300 */
- "Roland PC-300",
- USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_PC300, 2, -1,
- { { 0x81, 1 }, {-1, -1} },
- { { 0x01, 1 }, {-1, -1} },
- },
- { /* MOTU Fastlane USB */
- "MOTU Fastlane USB",
- USB_VENDOR_ID_MOTU, USBMIDI_MOTU_FASTLANE, 1, 0,
- { { 0x82, 3 }, {-1, -1} },
- { { 0x02, 3 }, {-1, -1} },
- }
-};
-
-#define VENDOR_SPECIFIC_USB_MIDI_DEVICES (sizeof(usb_midi_devices)/sizeof(struct usb_midi_device))
-
-/* for Hot-Plugging */
-
-static struct usb_device_id usb_midi_ids [] = {
- { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
- .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING},
- { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM1 ) },
- { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM2 ) },
- { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UA100G ) },
- { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_PC300 ) },
- { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8850 ) },
- { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820 ) },
- { USB_DEVICE( USB_VENDOR_ID_YAMAHA, USBMIDI_YAMAHA_MU1000 ) },
- { USB_DEVICE( USB_VENDOR_ID_MOTU, USBMIDI_MOTU_FASTLANE ) },
-/* { USB_DEVICE( USB_VENDOR_ID_STEINBERG, USBMIDI_STEINBERG_USB2MIDI ) },*/
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, usb_midi_ids);
-
-/* ------------------------------------------------------------------------- */
-#endif /* _USB_MIDI_H_ */
-
-
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index d34848ac30b..48dee4b8d8e 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -55,6 +55,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/lp.h>
+#include <linux/mutex.h>
#undef DEBUG
#include <linux/usb.h>
@@ -223,7 +224,7 @@ static int usblp_cache_device_id_string(struct usblp *usblp);
/* forward reference to make our lives easier */
static struct usb_driver usblp_driver;
-static DECLARE_MUTEX(usblp_sem); /* locks the existence of usblp's */
+static DEFINE_MUTEX(usblp_mutex); /* locks the existence of usblp's */
/*
* Functions for usblp control messages.
@@ -351,7 +352,7 @@ static int usblp_open(struct inode *inode, struct file *file)
if (minor < 0)
return -ENODEV;
- down (&usblp_sem);
+ mutex_lock (&usblp_mutex);
retval = -ENODEV;
intf = usb_find_interface(&usblp_driver, minor);
@@ -399,7 +400,7 @@ static int usblp_open(struct inode *inode, struct file *file)
}
}
out:
- up (&usblp_sem);
+ mutex_unlock (&usblp_mutex);
return retval;
}
@@ -425,13 +426,13 @@ static int usblp_release(struct inode *inode, struct file *file)
{
struct usblp *usblp = file->private_data;
- down (&usblp_sem);
+ mutex_lock (&usblp_mutex);
usblp->used = 0;
if (usblp->present) {
usblp_unlink_urbs(usblp);
} else /* finish cleanup from disconnect */
usblp_cleanup (usblp);
- up (&usblp_sem);
+ mutex_unlock (&usblp_mutex);
return 0;
}
@@ -1152,7 +1153,7 @@ static void usblp_disconnect(struct usb_interface *intf)
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
- down (&usblp_sem);
+ mutex_lock (&usblp_mutex);
down (&usblp->sem);
usblp->present = 0;
usb_set_intfdata (intf, NULL);
@@ -1166,7 +1167,7 @@ static void usblp_disconnect(struct usb_interface *intf)
if (!usblp->used)
usblp_cleanup (usblp);
- up (&usblp_sem);
+ mutex_unlock (&usblp_mutex);
}
static struct usb_device_id usblp_ids [] = {
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 2684e15b813..c0f37343a27 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -57,6 +57,7 @@
#include <linux/usb.h>
#include <linux/smp_lock.h>
#include <linux/usbdevice_fs.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
#include "usb.h"
@@ -570,7 +571,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte
if (!access_ok(VERIFY_WRITE, buf, nbytes))
return -EFAULT;
- down (&usb_bus_list_lock);
+ mutex_lock(&usb_bus_list_lock);
/* print devices for all busses */
list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */
@@ -580,12 +581,12 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0);
usb_unlock_device(bus->root_hub);
if (ret < 0) {
- up(&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
return ret;
}
total_written += ret;
}
- up (&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
return total_written;
}
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 2b68998fe4b..545da37afca 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -134,26 +134,21 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
}
if (pos < sizeof(struct usb_device_descriptor)) {
- struct usb_device_descriptor *desc = kmalloc(sizeof(*desc), GFP_KERNEL);
- if (!desc) {
- ret = -ENOMEM;
- goto err;
- }
- memcpy(desc, &dev->descriptor, sizeof(dev->descriptor));
- le16_to_cpus(&desc->bcdUSB);
- le16_to_cpus(&desc->idVendor);
- le16_to_cpus(&desc->idProduct);
- le16_to_cpus(&desc->bcdDevice);
+ struct usb_device_descriptor temp_desc ; /* 18 bytes - fits on the stack */
+
+ memcpy(&temp_desc, &dev->descriptor, sizeof(dev->descriptor));
+ le16_to_cpus(&temp_desc.bcdUSB);
+ le16_to_cpus(&temp_desc.idVendor);
+ le16_to_cpus(&temp_desc.idProduct);
+ le16_to_cpus(&temp_desc.bcdDevice);
len = sizeof(struct usb_device_descriptor) - pos;
if (len > nbytes)
len = nbytes;
- if (copy_to_user(buf, ((char *)desc) + pos, len)) {
- kfree(desc);
+ if (copy_to_user(buf, ((char *)&temp_desc) + pos, len)) {
ret = -EFAULT;
goto err;
}
- kfree(desc);
*ppos += len;
buf += len;
@@ -498,7 +493,8 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
{
int ret = 0;
- if (ps->dev->state != USB_STATE_CONFIGURED)
+ if (ps->dev->state != USB_STATE_ADDRESS
+ && ps->dev->state != USB_STATE_CONFIGURED)
return -EHOSTUNREACH;
if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
return 0;
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index dce9d987f0f..c196f384530 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -378,7 +378,7 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface,
return NULL;
}
-EXPORT_SYMBOL(usb_match_id);
+EXPORT_SYMBOL_GPL_FUTURE(usb_match_id);
int usb_device_match(struct device *dev, struct device_driver *drv)
{
@@ -446,7 +446,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
return retval;
}
-EXPORT_SYMBOL(usb_register_driver);
+EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
/**
* usb_deregister - unregister a USB driver
@@ -469,4 +469,4 @@ void usb_deregister(struct usb_driver *driver)
usbfs_update_special();
}
-EXPORT_SYMBOL(usb_deregister);
+EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 29b5b2a6e18..e0afb5ad29e 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -264,14 +264,19 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
*/
retval = pci_set_power_state (dev, PCI_D3hot);
if (retval == 0) {
- dev_dbg (hcd->self.controller, "--> PCI D3\n");
+ int wake = device_can_wakeup(&hcd->self.root_hub->dev);
+
+ wake = wake && device_may_wakeup(hcd->self.controller);
+
+ dev_dbg (hcd->self.controller, "--> PCI D3%s\n",
+ wake ? "/wakeup" : "");
/* Ignore these return values. We rely on pci code to
* reject requests the hardware can't implement, rather
* than coding the same thing.
*/
- (void) pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup);
- (void) pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup);
+ (void) pci_enable_wake (dev, PCI_D3hot, wake);
+ (void) pci_enable_wake (dev, PCI_D3cold, wake);
} else {
dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n",
retval);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 0018bbc4de3..fbd938d4ea5 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -34,6 +34,7 @@
#include <asm/scatterlist.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
@@ -93,7 +94,7 @@ struct usb_busmap {
static struct usb_busmap busmap;
/* used when updating list of hcds */
-DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */
+DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */
EXPORT_SYMBOL_GPL (usb_bus_list_lock);
/* used for controlling access to virtual root hubs */
@@ -366,21 +367,39 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/* DEVICE REQUESTS */
+ /* The root hub's remote wakeup enable bit is implemented using
+ * driver model wakeup flags. If this system supports wakeup
+ * through USB, userspace may change the default "allow wakeup"
+ * policy through sysfs or these calls.
+ *
+ * Most root hubs support wakeup from downstream devices, for
+ * runtime power management (disabling USB clocks and reducing
+ * VBUS power usage). However, not all of them do so; silicon,
+ * board, and BIOS bugs here are not uncommon, so these can't
+ * be treated quite like external hubs.
+ *
+ * Likewise, not all root hubs will pass wakeup events upstream,
+ * to wake up the whole system. So don't assume root hub and
+ * controller capabilities are identical.
+ */
+
case DeviceRequest | USB_REQ_GET_STATUS:
- tbuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP)
+ tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
+ << USB_DEVICE_REMOTE_WAKEUP)
| (1 << USB_DEVICE_SELF_POWERED);
tbuf [1] = 0;
len = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (wValue == USB_DEVICE_REMOTE_WAKEUP)
- hcd->remote_wakeup = 0;
+ device_set_wakeup_enable(&hcd->self.root_hub->dev, 0);
else
goto error;
break;
case DeviceOutRequest | USB_REQ_SET_FEATURE:
- if (hcd->can_wakeup && wValue == USB_DEVICE_REMOTE_WAKEUP)
- hcd->remote_wakeup = 1;
+ if (device_can_wakeup(&hcd->self.root_hub->dev)
+ && wValue == USB_DEVICE_REMOTE_WAKEUP)
+ device_set_wakeup_enable(&hcd->self.root_hub->dev, 1);
else
goto error;
break;
@@ -409,7 +428,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
bufp = fs_rh_config_descriptor;
len = sizeof fs_rh_config_descriptor;
}
- if (hcd->can_wakeup)
+ if (device_can_wakeup(&hcd->self.root_hub->dev))
patch_wakeup = 1;
break;
case USB_DT_STRING << 8:
@@ -761,14 +780,14 @@ static int usb_register_bus(struct usb_bus *bus)
{
int busnum;
- down (&usb_bus_list_lock);
+ 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 {
printk (KERN_ERR "%s: too many buses\n", usbcore_name);
- up(&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
return -E2BIG;
}
@@ -776,7 +795,7 @@ static int usb_register_bus(struct usb_bus *bus)
bus->controller, "usb_host%d", busnum);
if (IS_ERR(bus->class_dev)) {
clear_bit(busnum, busmap.busmap);
- up(&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
return PTR_ERR(bus->class_dev);
}
@@ -784,7 +803,7 @@ static int usb_register_bus(struct usb_bus *bus)
/* Add it to the local list of buses */
list_add (&bus->bus_list, &usb_bus_list);
- up (&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
usb_notify_add_bus(bus);
@@ -809,9 +828,9 @@ static void usb_deregister_bus (struct usb_bus *bus)
* controller code, as well as having it call this when cleaning
* itself up
*/
- down (&usb_bus_list_lock);
+ mutex_lock(&usb_bus_list_lock);
list_del (&bus->bus_list);
- up (&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
usb_notify_remove_bus(bus);
@@ -822,18 +841,17 @@ static void usb_deregister_bus (struct usb_bus *bus)
/**
* register_root_hub - called by usb_add_hcd() to register a root hub
- * @usb_dev: the usb root hub device to be registered.
* @hcd: host controller for this root hub
*
* This function registers the root hub with the USB subsystem. It sets up
- * the device properly in the device tree and stores the root_hub pointer
- * in the bus structure, then calls usb_new_device() to register the usb
- * device. It also assigns the root hub's USB address (always 1).
+ * the device properly in the device tree and then calls usb_new_device()
+ * to register the usb device. It also assigns the root hub's USB address
+ * (always 1).
*/
-static int register_root_hub (struct usb_device *usb_dev,
- struct usb_hcd *hcd)
+static int register_root_hub(struct usb_hcd *hcd)
{
struct device *parent_dev = hcd->self.controller;
+ struct usb_device *usb_dev = hcd->self.root_hub;
const int devnum = 1;
int retval;
@@ -844,14 +862,12 @@ static int register_root_hub (struct usb_device *usb_dev,
set_bit (devnum, usb_dev->bus->devmap.devicemap);
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
- down (&usb_bus_list_lock);
- usb_dev->bus->root_hub = usb_dev;
+ mutex_lock(&usb_bus_list_lock);
usb_dev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
if (retval != sizeof usb_dev->descriptor) {
- usb_dev->bus->root_hub = NULL;
- up (&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
usb_dev->dev.bus_id, retval);
return (retval < 0) ? retval : -EMSGSIZE;
@@ -859,11 +875,10 @@ static int register_root_hub (struct usb_device *usb_dev,
retval = usb_new_device (usb_dev);
if (retval) {
- usb_dev->bus->root_hub = NULL;
dev_err (parent_dev, "can't register root hub for %s, %d\n",
usb_dev->dev.bus_id, retval);
}
- up (&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
if (retval == 0) {
spin_lock_irq (&hcd_root_hub_lock);
@@ -1090,7 +1105,6 @@ static void urb_unlink (struct urb *urb)
spin_lock_irqsave (&hcd_data_lock, flags);
list_del_init (&urb->urb_list);
spin_unlock_irqrestore (&hcd_data_lock, flags);
- usb_put_dev (urb->dev);
}
@@ -1130,7 +1144,6 @@ static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
case HC_STATE_RUNNING:
case HC_STATE_RESUMING:
doit:
- usb_get_dev (urb->dev);
list_add_tail (&urb->urb_list, &ep->urb_list);
status = 0;
break;
@@ -1771,12 +1784,10 @@ int usb_add_hcd(struct usb_hcd *hcd,
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- /* till now HC has been in an indeterminate state ... */
- if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
- dev_err(hcd->self.controller, "can't reset\n");
- return retval;
- }
-
+ /* HC is in reset state, but accessible. Now do the one-time init,
+ * bottom up so that hcds can customize the root hubs before khubd
+ * starts talking to them. (Note, bus id is assigned early too.)
+ */
if ((retval = hcd_buffer_create(hcd)) != 0) {
dev_dbg(hcd->self.controller, "pool alloc failed\n");
return retval;
@@ -1785,6 +1796,36 @@ int usb_add_hcd(struct usb_hcd *hcd,
if ((retval = usb_register_bus(&hcd->self)) < 0)
goto err_register_bus;
+ if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
+ dev_err(hcd->self.controller, "unable to allocate root hub\n");
+ retval = -ENOMEM;
+ goto err_allocate_root_hub;
+ }
+ rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
+ USB_SPEED_FULL;
+ hcd->self.root_hub = rhdev;
+
+ /* "reset" is misnamed; its role is now one-time init. the controller
+ * should already have been reset (and boot firmware kicked off etc).
+ */
+ if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
+ dev_err(hcd->self.controller, "can't setup\n");
+ goto err_hcd_driver_setup;
+ }
+
+ /* wakeup flag init is in transition; for now we can't rely on PCI to
+ * initialize these bits properly, so we let reset() override it.
+ * This init should _precede_ the reset() once PCI behaves.
+ */
+ device_init_wakeup(&rhdev->dev,
+ device_can_wakeup(hcd->self.controller));
+
+ /* NOTE: root hub and controller capabilities may not be the same */
+ if (device_can_wakeup(hcd->self.controller)
+ && device_can_wakeup(&hcd->self.root_hub->dev))
+ dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
+
+ /* enable irqs just before we start the controller */
if (hcd->driver->irq) {
char buf[8], *bufp = buf;
@@ -1816,56 +1857,32 @@ int usb_add_hcd(struct usb_hcd *hcd,
(unsigned long long)hcd->rsrc_start);
}
- /* Allocate the root hub before calling hcd->driver->start(),
- * but don't register it until afterward so that the hardware
- * is running.
- */
- if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
- dev_err(hcd->self.controller, "unable to allocate root hub\n");
- retval = -ENOMEM;
- goto err_allocate_root_hub;
- }
-
- /* Although in principle hcd->driver->start() might need to use rhdev,
- * none of the current drivers do.
- */
if ((retval = hcd->driver->start(hcd)) < 0) {
dev_err(hcd->self.controller, "startup error %d\n", retval);
goto err_hcd_driver_start;
}
- /* hcd->driver->start() reported can_wakeup, probably with
- * assistance from board's boot firmware.
- * NOTE: normal devices won't enable wakeup by default.
- */
- if (hcd->can_wakeup)
- dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
- hcd->remote_wakeup = hcd->can_wakeup;
-
- rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
- USB_SPEED_FULL;
+ /* starting here, usbcore will pay attention to this root hub */
rhdev->bus_mA = min(500u, hcd->power_budget);
- if ((retval = register_root_hub(rhdev, hcd)) != 0)
+ if ((retval = register_root_hub(hcd)) != 0)
goto err_register_root_hub;
if (hcd->uses_new_polling && hcd->poll_rh)
usb_hcd_poll_rh_status(hcd);
return retval;
- err_register_root_hub:
+err_register_root_hub:
hcd->driver->stop(hcd);
-
- err_hcd_driver_start:
- usb_put_dev(rhdev);
-
- err_allocate_root_hub:
+err_hcd_driver_start:
if (hcd->irq >= 0)
free_irq(irqnum, hcd);
-
- err_request_irq:
+err_request_irq:
+err_hcd_driver_setup:
+ hcd->self.root_hub = NULL;
+ usb_put_dev(rhdev);
+err_allocate_root_hub:
usb_deregister_bus(&hcd->self);
-
- err_register_bus:
+err_register_bus:
hcd_buffer_destroy(hcd);
return retval;
}
@@ -1891,9 +1908,9 @@ void usb_remove_hcd(struct usb_hcd *hcd)
hcd->rh_registered = 0;
spin_unlock_irq (&hcd_root_hub_lock);
- down(&usb_bus_list_lock);
+ mutex_lock(&usb_bus_list_lock);
usb_disconnect(&hcd->self.root_hub);
- up(&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
hcd->poll_rh = 0;
del_timer_sync(&hcd->rh_timer);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 591b5aad1a1..7022aafb2ae 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -78,8 +78,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
#define HCD_FLAG_HW_ACCESSIBLE 0x00000001
#define HCD_FLAG_SAW_IRQ 0x00000002
- unsigned can_wakeup:1; /* hw supports wakeup? */
- unsigned remote_wakeup:1;/* sw should use wakeup? */
unsigned rh_registered:1;/* is root hub registered? */
/* The next flag is a stopgap, to be removed when all the HCDs
@@ -364,7 +362,7 @@ extern void usb_set_device_state(struct usb_device *udev,
/* exported only within usbcore */
extern struct list_head usb_bus_list;
-extern struct semaphore usb_bus_list_lock;
+extern struct mutex usb_bus_list_lock;
extern wait_queue_head_t usb_kill_urb_queue;
extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 650d5ee5871..8e65f7a237e 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -22,6 +22,7 @@
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/kthread.h>
+#include <linux/mutex.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
@@ -1005,12 +1006,18 @@ void usb_set_device_state(struct usb_device *udev,
; /* do nothing */
else if (new_state != USB_STATE_NOTATTACHED) {
udev->state = new_state;
- if (new_state == USB_STATE_CONFIGURED)
- device_init_wakeup(&udev->dev,
- (udev->actconfig->desc.bmAttributes
- & USB_CONFIG_ATT_WAKEUP));
- else if (new_state != USB_STATE_SUSPENDED)
- device_init_wakeup(&udev->dev, 0);
+
+ /* root hub wakeup capabilities are managed out-of-band
+ * and may involve silicon errata ... ignore them here.
+ */
+ if (udev->parent) {
+ if (new_state == USB_STATE_CONFIGURED)
+ device_init_wakeup(&udev->dev,
+ (udev->actconfig->desc.bmAttributes
+ & USB_CONFIG_ATT_WAKEUP));
+ else if (new_state != USB_STATE_SUSPENDED)
+ device_init_wakeup(&udev->dev, 0);
+ }
} else
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
@@ -1172,8 +1179,11 @@ static int choose_configuration(struct usb_device *udev)
c = udev->config;
num_configs = udev->descriptor.bNumConfigurations;
for (i = 0; i < num_configs; (i++, c++)) {
- struct usb_interface_descriptor *desc =
- &c->intf_cache[0]->altsetting->desc;
+ struct usb_interface_descriptor *desc = NULL;
+
+ /* It's possible that a config has no interfaces! */
+ if (c->desc.bNumInterfaces > 0)
+ desc = &c->intf_cache[0]->altsetting->desc;
/*
* HP's USB bus-powered keyboard has only one configuration
@@ -1208,7 +1218,8 @@ static int choose_configuration(struct usb_device *udev)
/* If the first config's first interface is COMM/2/0xff
* (MSFT RNDIS), rule it out unless Linux has host-side
* RNDIS support. */
- if (i == 0 && desc->bInterfaceClass == USB_CLASS_COMM
+ if (i == 0 && desc
+ && desc->bInterfaceClass == USB_CLASS_COMM
&& desc->bInterfaceSubClass == 2
&& desc->bInterfaceProtocol == 0xff) {
#ifndef CONFIG_USB_NET_RNDIS
@@ -1224,8 +1235,8 @@ static int choose_configuration(struct usb_device *udev)
* than a vendor-specific driver. */
else if (udev->descriptor.bDeviceClass !=
USB_CLASS_VENDOR_SPEC &&
- desc->bInterfaceClass !=
- USB_CLASS_VENDOR_SPEC) {
+ (!desc || desc->bInterfaceClass !=
+ USB_CLASS_VENDOR_SPEC)) {
best = c;
break;
}
@@ -1876,18 +1887,18 @@ int usb_resume_device(struct usb_device *udev)
if (udev->state == USB_STATE_NOTATTACHED)
return -ENODEV;
-#ifdef CONFIG_USB_SUSPEND
/* selective resume of one downstream hub-to-device port */
if (udev->parent) {
+#ifdef CONFIG_USB_SUSPEND
if (udev->state == USB_STATE_SUSPENDED) {
// NOTE swsusp may bork us, device state being wrong...
// NOTE this fails if parent is also suspended...
status = hub_port_resume(hdev_to_hub(udev->parent),
udev->portnum, udev);
} else
+#endif
status = 0;
} else
-#endif
status = finish_device_resume(udev);
if (status < 0)
dev_dbg(&udev->dev, "can't resume, status %d\n",
@@ -2162,7 +2173,7 @@ static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int retry_counter)
{
- static DECLARE_MUTEX(usb_address0_sem);
+ static DEFINE_MUTEX(usb_address0_mutex);
struct usb_device *hdev = hub->hdev;
int i, j, retval;
@@ -2183,7 +2194,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
if (oldspeed == USB_SPEED_LOW)
delay = HUB_LONG_RESET_TIME;
- down(&usb_address0_sem);
+ mutex_lock(&usb_address0_mutex);
/* Reset the device; full speed may morph to high speed */
retval = hub_port_reset(hub, port1, udev, delay);
@@ -2381,7 +2392,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
fail:
if (retval)
hub_port_disable(hub, port1, 0);
- up(&usb_address0_sem);
+ mutex_unlock(&usb_address0_mutex);
return retval;
}
@@ -3017,7 +3028,7 @@ int usb_reset_device(struct usb_device *udev)
parent_hub = hdev_to_hub(parent_hdev);
/* If we're resetting an active hub, take some special actions */
- if (udev->actconfig &&
+ if (udev->actconfig && udev->actconfig->desc.bNumInterfaces > 0 &&
udev->actconfig->interface[0]->dev.driver ==
&hub_driver.driver &&
(hub = hdev_to_hub(udev)) != NULL) {
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 7135e542679..08fb20f06f3 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -631,8 +631,8 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char
* Returns the number of bytes received on success, or else the status code
* returned by the underlying usb_control_msg() call.
*/
-int usb_get_string(struct usb_device *dev, unsigned short langid,
- unsigned char index, void *buf, int size)
+static int usb_get_string(struct usb_device *dev, unsigned short langid,
+ unsigned char index, void *buf, int size)
{
int i;
int result;
@@ -1388,11 +1388,13 @@ free_interfaces:
if (dev->state != USB_STATE_ADDRESS)
usb_disable_device (dev, 1); // Skip ep0
- i = dev->bus_mA - cp->desc.bMaxPower * 2;
- if (i < 0)
- dev_warn(&dev->dev, "new config #%d exceeds power "
- "limit by %dmA\n",
- configuration, -i);
+ if (cp) {
+ i = dev->bus_mA - cp->desc.bMaxPower * 2;
+ if (i < 0)
+ dev_warn(&dev->dev, "new config #%d exceeds power "
+ "limit by %dmA\n",
+ configuration, -i);
+ }
if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
@@ -1488,7 +1490,6 @@ EXPORT_SYMBOL(usb_sg_wait);
// synchronous control message convenience routines
EXPORT_SYMBOL(usb_get_descriptor);
EXPORT_SYMBOL(usb_get_status);
-EXPORT_SYMBOL(usb_get_string);
EXPORT_SYMBOL(usb_string);
// synchronous calls that also maintain usbcore state
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index fbbebab52fb..4b55285de9a 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -13,16 +13,17 @@
#include <linux/kernel.h>
#include <linux/notifier.h>
#include <linux/usb.h>
+#include <linux/mutex.h>
#include "usb.h"
static struct notifier_block *usb_notifier_list;
-static DECLARE_MUTEX(usb_notifier_lock);
+static DEFINE_MUTEX(usb_notifier_lock);
static void usb_notifier_chain_register(struct notifier_block **list,
struct notifier_block *n)
{
- down(&usb_notifier_lock);
+ mutex_lock(&usb_notifier_lock);
while (*list) {
if (n->priority > (*list)->priority)
break;
@@ -30,13 +31,13 @@ static void usb_notifier_chain_register(struct notifier_block **list,
}
n->next = *list;
*list = n;
- up(&usb_notifier_lock);
+ mutex_unlock(&usb_notifier_lock);
}
static void usb_notifier_chain_unregister(struct notifier_block **nl,
struct notifier_block *n)
{
- down(&usb_notifier_lock);
+ mutex_lock(&usb_notifier_lock);
while ((*nl)!=NULL) {
if ((*nl)==n) {
*nl = n->next;
@@ -45,7 +46,7 @@ static void usb_notifier_chain_unregister(struct notifier_block **nl,
nl=&((*nl)->next);
}
exit:
- up(&usb_notifier_lock);
+ mutex_unlock(&usb_notifier_lock);
}
static int usb_notifier_call_chain(struct notifier_block **n,
@@ -54,7 +55,7 @@ static int usb_notifier_call_chain(struct notifier_block **n,
int ret=NOTIFY_DONE;
struct notifier_block *nb = *n;
- down(&usb_notifier_lock);
+ mutex_lock(&usb_notifier_lock);
while (nb) {
ret = nb->notifier_call(nb,val,v);
if (ret&NOTIFY_STOP_MASK) {
@@ -63,7 +64,7 @@ static int usb_notifier_call_chain(struct notifier_block **n,
nb = nb->next;
}
exit:
- up(&usb_notifier_lock);
+ mutex_unlock(&usb_notifier_lock);
return ret;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 13d1d367f7f..d7352aa73b5 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -33,6 +33,7 @@
#include <linux/errno.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
+#include <linux/mutex.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
@@ -639,7 +640,7 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
struct usb_bus *bus;
struct usb_device *dev = NULL;
- down(&usb_bus_list_lock);
+ mutex_lock(&usb_bus_list_lock);
for (buslist = usb_bus_list.next;
buslist != &usb_bus_list;
buslist = buslist->next) {
@@ -653,7 +654,7 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
goto exit;
}
exit:
- up(&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
return dev;
}
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index ff075a53c8d..d80f71877d6 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -187,6 +187,23 @@ config USB_OTG
Select this only if your OMAP board has a Mini-AB connector.
+config USB_GADGET_AT91
+ boolean "AT91 USB Device Port"
+ depends on ARCH_AT91RM9200
+ select USB_GADGET_SELECTED
+ help
+ Many Atmel AT91 processors (such as the AT91RM2000) have a
+ full speed USB Device Port with support for five configurable
+ endpoints (plus endpoint zero).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "at91_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_AT91
+ tristate
+ depends on USB_GADGET_AT91
+ default USB_GADGET
config USB_GADGET_DUMMY_HCD
boolean "Dummy HCD (DEVELOPMENT)"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index d5fd04d886e..5a28e61392e 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
+obj-$(CONFIG_USB_AT91) += at91_udc.o
#
# USB gadget drivers
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
new file mode 100644
index 00000000000..865858cfd1c
--- /dev/null
+++ b/drivers/usb/gadget/at91_udc.c
@@ -0,0 +1,1773 @@
+/*
+ * at91_udc -- driver for at91-series USB peripheral controller
+ *
+ * Copyright (C) 2004 by Thomas Rathbone
+ * Copyright (C) 2005 by HP Labs
+ * Copyright (C) 2005 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#undef DEBUG
+#undef VERBOSE
+#undef PACKET_TRACE
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/board.h>
+
+#include "at91_udc.h"
+
+
+/*
+ * This controller is simple and PIO-only. It's used in many AT91-series
+ * ARMv4T controllers, including the at91rm9200 (arm920T, with MMU),
+ * at91sam9261 (arm926ejs, with MMU), and several no-mmu versions.
+ *
+ * This driver expects the board has been wired with two GPIOs suppporting
+ * a VBUS sensing IRQ, and a D+ pullup. (They may be omitted, but the
+ * testing hasn't covered such cases.) The pullup is most important; it
+ * provides software control over whether the host enumerates the device.
+ * The VBUS sensing helps during enumeration, and allows both USB clocks
+ * (and the transceiver) to stay gated off until they're necessary, saving
+ * power. During USB suspend, the 48 MHz clock is gated off.
+ */
+
+#define DRIVER_VERSION "8 March 2005"
+
+static const char driver_name [] = "at91_udc";
+static const char ep0name[] = "ep0";
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Read from a UDP register.
+ */
+static inline unsigned long at91_udp_read(unsigned int reg)
+{
+ void __iomem *udp_base = (void __iomem *)AT91_VA_BASE_UDP;
+
+ return __raw_readl(udp_base + reg);
+}
+
+/*
+ * Write to a UDP register.
+ */
+static inline void at91_udp_write(unsigned int reg, unsigned long value)
+{
+ void __iomem *udp_base = (void __iomem *)AT91_VA_BASE_UDP;
+
+ __raw_writel(value, udp_base + reg);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char debug_filename[] = "driver/udc";
+
+#define FOURBITS "%s%s%s%s"
+#define EIGHTBITS FOURBITS FOURBITS
+
+static void proc_ep_show(struct seq_file *s, struct at91_ep *ep)
+{
+ static char *types[] = {
+ "control", "out-iso", "out-bulk", "out-int",
+ "BOGUS", "in-iso", "in-bulk", "in-int"};
+
+ u32 csr;
+ struct at91_request *req;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ csr = __raw_readl(ep->creg);
+
+ /* NOTE: not collecting per-endpoint irq statistics... */
+
+ seq_printf(s, "\n");
+ seq_printf(s, "%s, maxpacket %d %s%s %s%s\n",
+ ep->ep.name, ep->ep.maxpacket,
+ ep->is_in ? "in" : "out",
+ ep->is_iso ? " iso" : "",
+ ep->is_pingpong
+ ? (ep->fifo_bank ? "pong" : "ping")
+ : "",
+ ep->stopped ? " stopped" : "");
+ seq_printf(s, "csr %08x rxbytes=%d %s %s %s" EIGHTBITS "\n",
+ csr,
+ (csr & 0x07ff0000) >> 16,
+ (csr & (1 << 15)) ? "enabled" : "disabled",
+ (csr & (1 << 11)) ? "DATA1" : "DATA0",
+ types[(csr & 0x700) >> 8],
+
+ /* iff type is control then print current direction */
+ (!(csr & 0x700))
+ ? ((csr & (1 << 7)) ? " IN" : " OUT")
+ : "",
+ (csr & (1 << 6)) ? " rxdatabk1" : "",
+ (csr & (1 << 5)) ? " forcestall" : "",
+ (csr & (1 << 4)) ? " txpktrdy" : "",
+
+ (csr & (1 << 3)) ? " stallsent" : "",
+ (csr & (1 << 2)) ? " rxsetup" : "",
+ (csr & (1 << 1)) ? " rxdatabk0" : "",
+ (csr & (1 << 0)) ? " txcomp" : "");
+ if (list_empty (&ep->queue))
+ seq_printf(s, "\t(queue empty)\n");
+
+ else list_for_each_entry (req, &ep->queue, queue) {
+ unsigned length = req->req.actual;
+
+ seq_printf(s, "\treq %p len %d/%d buf %p\n",
+ &req->req, length,
+ req->req.length, req->req.buf);
+ }
+ local_irq_restore(flags);
+}
+
+static void proc_irq_show(struct seq_file *s, const char *label, u32 mask)
+{
+ int i;
+
+ seq_printf(s, "%s %04x:%s%s" FOURBITS, label, mask,
+ (mask & (1 << 13)) ? " wakeup" : "",
+ (mask & (1 << 12)) ? " endbusres" : "",
+
+ (mask & (1 << 11)) ? " sofint" : "",
+ (mask & (1 << 10)) ? " extrsm" : "",
+ (mask & (1 << 9)) ? " rxrsm" : "",
+ (mask & (1 << 8)) ? " rxsusp" : "");
+ for (i = 0; i < 8; i++) {
+ if (mask & (1 << i))
+ seq_printf(s, " ep%d", i);
+ }
+ seq_printf(s, "\n");
+}
+
+static int proc_udc_show(struct seq_file *s, void *unused)
+{
+ struct at91_udc *udc = s->private;
+ struct at91_ep *ep;
+ u32 tmp;
+
+ seq_printf(s, "%s: version %s\n", driver_name, DRIVER_VERSION);
+
+ seq_printf(s, "vbus %s, pullup %s, %s powered%s, gadget %s\n\n",
+ udc->vbus ? "present" : "off",
+ udc->enabled
+ ? (udc->vbus ? "active" : "enabled")
+ : "disabled",
+ udc->selfpowered ? "self" : "VBUS",
+ udc->suspended ? ", suspended" : "",
+ udc->driver ? udc->driver->driver.name : "(none)");
+
+ /* don't access registers when interface isn't clocked */
+ if (!udc->clocked) {
+ seq_printf(s, "(not clocked)\n");
+ return 0;
+ }
+
+ tmp = at91_udp_read(AT91_UDP_FRM_NUM);
+ seq_printf(s, "frame %05x:%s%s frame=%d\n", tmp,
+ (tmp & AT91_UDP_FRM_OK) ? " ok" : "",
+ (tmp & AT91_UDP_FRM_ERR) ? " err" : "",
+ (tmp & AT91_UDP_NUM));
+
+ tmp = at91_udp_read(AT91_UDP_GLB_STAT);
+ seq_printf(s, "glbstate %02x:%s" FOURBITS "\n", tmp,
+ (tmp & AT91_UDP_RMWUPE) ? " rmwupe" : "",
+ (tmp & AT91_UDP_RSMINPR) ? " rsminpr" : "",
+ (tmp & AT91_UDP_ESR) ? " esr" : "",
+ (tmp & AT91_UDP_CONFG) ? " confg" : "",
+ (tmp & AT91_UDP_FADDEN) ? " fadden" : "");
+
+ tmp = at91_udp_read(AT91_UDP_FADDR);
+ seq_printf(s, "faddr %03x:%s fadd=%d\n", tmp,
+ (tmp & AT91_UDP_FEN) ? " fen" : "",
+ (tmp & AT91_UDP_FADD));
+
+ proc_irq_show(s, "imr ", at91_udp_read(AT91_UDP_IMR));
+ proc_irq_show(s, "isr ", at91_udp_read(AT91_UDP_ISR));
+
+ if (udc->enabled && udc->vbus) {
+ proc_ep_show(s, &udc->ep[0]);
+ list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) {
+ if (ep->desc)
+ proc_ep_show(s, ep);
+ }
+ }
+ return 0;
+}
+
+static int proc_udc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, proc_udc_show, PDE(inode)->data);
+}
+
+static struct file_operations proc_ops = {
+ .open = proc_udc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void create_debug_file(struct at91_udc *udc)
+{
+ struct proc_dir_entry *pde;
+
+ pde = create_proc_entry (debug_filename, 0, NULL);
+ udc->pde = pde;
+ if (pde == NULL)
+ return;
+
+ pde->proc_fops = &proc_ops;
+ pde->data = udc;
+}
+
+static void remove_debug_file(struct at91_udc *udc)
+{
+ if (udc->pde)
+ remove_proc_entry(debug_filename, NULL);
+}
+
+#else
+
+static inline void create_debug_file(struct at91_udc *udc) {}
+static inline void remove_debug_file(struct at91_udc *udc) {}
+
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+static void done(struct at91_ep *ep, struct at91_request *req, int status)
+{
+ unsigned stopped = ep->stopped;
+
+ list_del_init(&req->queue);
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+ if (status && status != -ESHUTDOWN)
+ VDBG("%s done %p, status %d\n", ep->ep.name, req, status);
+
+ ep->stopped = 1;
+ req->req.complete(&ep->ep, &req->req);
+ ep->stopped = stopped;
+
+ /* ep0 is always ready; other endpoints need a non-empty queue */
+ if (list_empty(&ep->queue) && ep->int_mask != (1 << 0))
+ at91_udp_write(AT91_UDP_IDR, ep->int_mask);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* bits indicating OUT fifo has data ready */
+#define RX_DATA_READY (AT91_UDP_RX_DATA_BK0 | AT91_UDP_RX_DATA_BK1)
+
+/*
+ * Endpoint FIFO CSR bits have a mix of bits, making it unsafe to just write
+ * back most of the value you just read (because of side effects, including
+ * bits that may change after reading and before writing).
+ *
+ * Except when changing a specific bit, always write values which:
+ * - clear SET_FX bits (setting them could change something)
+ * - set CLR_FX bits (clearing them could change something)
+ *
+ * There are also state bits like FORCESTALL, EPEDS, DIR, and EPTYPE
+ * that shouldn't normally be changed.
+ */
+#define SET_FX (AT91_UDP_TXPKTRDY)
+#define CLR_FX (RX_DATA_READY | AT91_UDP_RXSETUP | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)
+
+/* pull OUT packet data from the endpoint's fifo */
+static int read_fifo (struct at91_ep *ep, struct at91_request *req)
+{
+ u32 __iomem *creg = ep->creg;
+ u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
+ u32 csr;
+ u8 *buf;
+ unsigned int count, bufferspace, is_done;
+
+ buf = req->req.buf + req->req.actual;
+ bufferspace = req->req.length - req->req.actual;
+
+ /*
+ * there might be nothing to read if ep_queue() calls us,
+ * or if we already emptied both pingpong buffers
+ */
+rescan:
+ csr = __raw_readl(creg);
+ if ((csr & RX_DATA_READY) == 0)
+ return 0;
+
+ count = (csr & AT91_UDP_RXBYTECNT) >> 16;
+ if (count > ep->ep.maxpacket)
+ count = ep->ep.maxpacket;
+ if (count > bufferspace) {
+ DBG("%s buffer overflow\n", ep->ep.name);
+ req->req.status = -EOVERFLOW;
+ count = bufferspace;
+ }
+ __raw_readsb(dreg, buf, count);
+
+ /* release and swap pingpong mem bank */
+ csr |= CLR_FX;
+ if (ep->is_pingpong) {
+ if (ep->fifo_bank == 0) {
+ csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);
+ ep->fifo_bank = 1;
+ } else {
+ csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK1);
+ ep->fifo_bank = 0;
+ }
+ } else
+ csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);
+ __raw_writel(csr, creg);
+
+ req->req.actual += count;
+ is_done = (count < ep->ep.maxpacket);
+ if (count == bufferspace)
+ is_done = 1;
+
+ PACKET("%s %p out/%d%s\n", ep->ep.name, &req->req, count,
+ is_done ? " (done)" : "");
+
+ /*
+ * avoid extra trips through IRQ logic for packets already in
+ * the fifo ... maybe preventing an extra (expensive) OUT-NAK
+ */
+ if (is_done)
+ done(ep, req, 0);
+ else if (ep->is_pingpong) {
+ bufferspace -= count;
+ buf += count;
+ goto rescan;
+ }
+
+ return is_done;
+}
+
+/* load fifo for an IN packet */
+static int write_fifo(struct at91_ep *ep, struct at91_request *req)
+{
+ u32 __iomem *creg = ep->creg;
+ u32 csr = __raw_readl(creg);
+ u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
+ unsigned total, count, is_last;
+
+ /*
+ * TODO: allow for writing two packets to the fifo ... that'll
+ * reduce the amount of IN-NAKing, but probably won't affect
+ * throughput much. (Unlike preventing OUT-NAKing!)
+ */
+
+ /*
+ * If ep_queue() calls us, the queue is empty and possibly in
+ * odd states like TXCOMP not yet cleared (we do it, saving at
+ * least one IRQ) or the fifo not yet being free. Those aren't
+ * issues normally (IRQ handler fast path).
+ */
+ if (unlikely(csr & (AT91_UDP_TXCOMP | AT91_UDP_TXPKTRDY))) {
+ if (csr & AT91_UDP_TXCOMP) {
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_TXCOMP);
+ __raw_writel(csr, creg);
+ csr = __raw_readl(creg);
+ }
+ if (csr & AT91_UDP_TXPKTRDY)
+ return 0;
+ }
+
+ total = req->req.length - req->req.actual;
+ if (ep->ep.maxpacket < total) {
+ count = ep->ep.maxpacket;
+ is_last = 0;
+ } else {
+ count = total;
+ is_last = (count < ep->ep.maxpacket) || !req->req.zero;
+ }
+
+ /*
+ * Write the packet, maybe it's a ZLP.
+ *
+ * NOTE: incrementing req->actual before we receive the ACK means
+ * gadget driver IN bytecounts can be wrong in fault cases. That's
+ * fixable with PIO drivers like this one (save "count" here, and
+ * do the increment later on TX irq), but not for most DMA hardware.
+ *
+ * So all gadget drivers must accept that potential error. Some
+ * hardware supports precise fifo status reporting, letting them
+ * recover when the actual bytecount matters (e.g. for USB Test
+ * and Measurement Class devices).
+ */
+ __raw_writesb(dreg, req->req.buf + req->req.actual, count);
+ csr &= ~SET_FX;
+ csr |= CLR_FX | AT91_UDP_TXPKTRDY;
+ __raw_writel(csr, creg);
+ req->req.actual += count;
+
+ PACKET("%s %p in/%d%s\n", ep->ep.name, &req->req, count,
+ is_last ? " (done)" : "");
+ if (is_last)
+ done(ep, req, 0);
+ return is_last;
+}
+
+static void nuke(struct at91_ep *ep, int status)
+{
+ struct at91_request *req;
+
+ // terminer chaque requete dans la queue
+ ep->stopped = 1;
+ if (list_empty(&ep->queue))
+ return;
+
+ VDBG("%s %s\n", __FUNCTION__, ep->ep.name);
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct at91_request, queue);
+ done(ep, req, status);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int at91_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+ struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
+ struct at91_udc *dev = ep->udc;
+ u16 maxpacket;
+ u32 tmp;
+ unsigned long flags;
+
+ if (!_ep || !ep
+ || !desc || ep->desc
+ || _ep->name == ep0name
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0
+ || maxpacket > ep->maxpacket) {
+ DBG("bad ep or descriptor\n");
+ return -EINVAL;
+ }
+
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ DBG("bogus device state\n");
+ return -ESHUTDOWN;
+ }
+
+ tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ switch (tmp) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ DBG("only one control endpoint\n");
+ return -EINVAL;
+ case USB_ENDPOINT_XFER_INT:
+ if (maxpacket > 64)
+ goto bogus_max;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ switch (maxpacket) {
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ goto ok;
+ }
+bogus_max:
+ DBG("bogus maxpacket %d\n", maxpacket);
+ return -EINVAL;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (!ep->is_pingpong) {
+ DBG("iso requires double buffering\n");
+ return -EINVAL;
+ }
+ break;
+ }
+
+ok:
+ local_irq_save(flags);
+
+ /* initialize endpoint to match this descriptor */
+ ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
+ ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
+ ep->stopped = 0;
+ if (ep->is_in)
+ tmp |= 0x04;
+ tmp <<= 8;
+ tmp |= AT91_UDP_EPEDS;
+ __raw_writel(tmp, ep->creg);
+
+ ep->desc = desc;
+ ep->ep.maxpacket = maxpacket;
+
+ /*
+ * reset/init endpoint fifo. NOTE: leaves fifo_bank alone,
+ * since endpoint resets don't reset hw pingpong state.
+ */
+ at91_udp_write(AT91_UDP_RST_EP, ep->int_mask);
+ at91_udp_write(AT91_UDP_RST_EP, 0);
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int at91_ep_disable (struct usb_ep * _ep)
+{
+ struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
+ unsigned long flags;
+
+ if (ep == &ep->udc->ep[0])
+ return -EINVAL;
+
+ local_irq_save(flags);
+
+ nuke(ep, -ESHUTDOWN);
+
+ /* restore the endpoint's pristine config */
+ ep->desc = NULL;
+ ep->ep.maxpacket = ep->maxpacket;
+
+ /* reset fifos and endpoint */
+ if (ep->udc->clocked) {
+ at91_udp_write(AT91_UDP_RST_EP, ep->int_mask);
+ at91_udp_write(AT91_UDP_RST_EP, 0);
+ __raw_writel(0, ep->creg);
+ }
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+/*
+ * this is a PIO-only driver, so there's nothing
+ * interesting for request or buffer allocation.
+ */
+
+static struct usb_request *at91_ep_alloc_request (struct usb_ep *_ep, unsigned int gfp_flags)
+{
+ struct at91_request *req;
+
+ req = kcalloc(1, sizeof (struct at91_request), SLAB_KERNEL);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+ return &req->req;
+}
+
+static void at91_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct at91_request *req;
+
+ req = container_of(_req, struct at91_request, req);
+ BUG_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+static void *at91_ep_alloc_buffer(
+ struct usb_ep *_ep,
+ unsigned bytes,
+ dma_addr_t *dma,
+ gfp_t gfp_flags)
+{
+ *dma = ~0;
+ return kmalloc(bytes, gfp_flags);
+}
+
+static void at91_ep_free_buffer(
+ struct usb_ep *ep,
+ void *buf,
+ dma_addr_t dma,
+ unsigned bytes)
+{
+ kfree(buf);
+}
+
+static int at91_ep_queue(struct usb_ep *_ep,
+ struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct at91_request *req;
+ struct at91_ep *ep;
+ struct at91_udc *dev;
+ int status;
+ unsigned long flags;
+
+ req = container_of(_req, struct at91_request, req);
+ ep = container_of(_ep, struct at91_ep, ep);
+
+ if (!_req || !_req->complete
+ || !_req->buf || !list_empty(&req->queue)) {
+ DBG("invalid request\n");
+ return -EINVAL;
+ }
+
+ if (!_ep || (!ep->desc && ep->ep.name != ep0name)) {
+ DBG("invalid ep\n");
+ return -EINVAL;
+ }
+
+ dev = ep->udc;
+
+ if (!dev || !dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ DBG("invalid device\n");
+ return -EINVAL;
+ }
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ local_irq_save(flags);
+
+ /* try to kickstart any empty and idle queue */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+ int is_ep0;
+
+ /*
+ * If this control request has a non-empty DATA stage, this
+ * will start that stage. It works just like a non-control
+ * request (until the status stage starts, maybe early).
+ *
+ * If the data stage is empty, then this starts a successful
+ * IN/STATUS stage. (Unsuccessful ones use set_halt.)
+ */
+ is_ep0 = (ep->ep.name == ep0name);
+ if (is_ep0) {
+ u32 tmp;
+
+ if (!dev->req_pending) {
+ status = -EINVAL;
+ goto done;
+ }
+
+ /*
+ * defer changing CONFG until after the gadget driver
+ * reconfigures the endpoints.
+ */
+ if (dev->wait_for_config_ack) {
+ tmp = at91_udp_read(AT91_UDP_GLB_STAT);
+ tmp ^= AT91_UDP_CONFG;
+ VDBG("toggle config\n");
+ at91_udp_write(AT91_UDP_GLB_STAT, tmp);
+ }
+ if (req->req.length == 0) {
+ep0_in_status:
+ PACKET("ep0 in/status\n");
+ status = 0;
+ tmp = __raw_readl(ep->creg);
+ tmp &= ~SET_FX;
+ tmp |= CLR_FX | AT91_UDP_TXPKTRDY;
+ __raw_writel(tmp, ep->creg);
+ dev->req_pending = 0;
+ goto done;
+ }
+ }
+
+ if (ep->is_in)
+ status = write_fifo(ep, req);
+ else {
+ status = read_fifo(ep, req);
+
+ /* IN/STATUS stage is otherwise triggered by irq */
+ if (status && is_ep0)
+ goto ep0_in_status;
+ }
+ } else
+ status = 0;
+
+ if (req && !status) {
+ list_add_tail (&req->queue, &ep->queue);
+ at91_udp_write(AT91_UDP_IER, ep->int_mask);
+ }
+done:
+ local_irq_restore(flags);
+ return (status < 0) ? status : 0;
+}
+
+static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct at91_ep *ep;
+ struct at91_request *req;
+
+ ep = container_of(_ep, struct at91_ep, ep);
+ if (!_ep || ep->ep.name == ep0name)
+ return -EINVAL;
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry (req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req)
+ return -EINVAL;
+
+ done(ep, req, -ECONNRESET);
+ return 0;
+}
+
+static int at91_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
+ u32 __iomem *creg;
+ u32 csr;
+ unsigned long flags;
+ int status = 0;
+
+ if (!_ep || ep->is_iso || !ep->udc->clocked)
+ return -EINVAL;
+
+ creg = ep->creg;
+ local_irq_save(flags);
+
+ csr = __raw_readl(creg);
+
+ /*
+ * fail with still-busy IN endpoints, ensuring correct sequencing
+ * of data tx then stall. note that the fifo rx bytecount isn't
+ * completely accurate as a tx bytecount.
+ */
+ if (ep->is_in && (!list_empty(&ep->queue) || (csr >> 16) != 0))
+ status = -EAGAIN;
+ else {
+ csr |= CLR_FX;
+ csr &= ~SET_FX;
+ if (value) {
+ csr |= AT91_UDP_FORCESTALL;
+ VDBG("halt %s\n", ep->ep.name);
+ } else {
+ at91_udp_write(AT91_UDP_RST_EP, ep->int_mask);
+ at91_udp_write(AT91_UDP_RST_EP, 0);
+ csr &= ~AT91_UDP_FORCESTALL;
+ }
+ __raw_writel(csr, creg);
+ }
+
+ local_irq_restore(flags);
+ return status;
+}
+
+static struct usb_ep_ops at91_ep_ops = {
+ .enable = at91_ep_enable,
+ .disable = at91_ep_disable,
+ .alloc_request = at91_ep_alloc_request,
+ .free_request = at91_ep_free_request,
+ .alloc_buffer = at91_ep_alloc_buffer,
+ .free_buffer = at91_ep_free_buffer,
+ .queue = at91_ep_queue,
+ .dequeue = at91_ep_dequeue,
+ .set_halt = at91_ep_set_halt,
+ // there's only imprecise fifo status reporting
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int at91_get_frame(struct usb_gadget *gadget)
+{
+ if (!to_udc(gadget)->clocked)
+ return -EINVAL;
+ return at91_udp_read(AT91_UDP_FRM_NUM) & AT91_UDP_NUM;
+}
+
+static int at91_wakeup(struct usb_gadget *gadget)
+{
+ struct at91_udc *udc = to_udc(gadget);
+ u32 glbstate;
+ int status = -EINVAL;
+ unsigned long flags;
+
+ DBG("%s\n", __FUNCTION__ );
+ local_irq_save(flags);
+
+ if (!udc->clocked || !udc->suspended)
+ goto done;
+
+ /* NOTE: some "early versions" handle ESR differently ... */
+
+ glbstate = at91_udp_read(AT91_UDP_GLB_STAT);
+ if (!(glbstate & AT91_UDP_ESR))
+ goto done;
+ glbstate |= AT91_UDP_ESR;
+ at91_udp_write(AT91_UDP_GLB_STAT, glbstate);
+
+done:
+ local_irq_restore(flags);
+ return status;
+}
+
+/* reinit == restore inital software state */
+static void udc_reinit(struct at91_udc *udc)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ struct at91_ep *ep = &udc->ep[i];
+
+ if (i != 0)
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ ep->desc = NULL;
+ ep->stopped = 0;
+ ep->fifo_bank = 0;
+ ep->ep.maxpacket = ep->maxpacket;
+ // initialiser une queue par endpoint
+ INIT_LIST_HEAD(&ep->queue);
+ }
+}
+
+static void stop_activity(struct at91_udc *udc)
+{
+ struct usb_gadget_driver *driver = udc->driver;
+ int i;
+
+ if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ struct at91_ep *ep = &udc->ep[i];
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+ if (driver)
+ driver->disconnect(&udc->gadget);
+
+ udc_reinit(udc);
+}
+
+static void clk_on(struct at91_udc *udc)
+{
+ if (udc->clocked)
+ return;
+ udc->clocked = 1;
+ clk_enable(udc->iclk);
+ clk_enable(udc->fclk);
+}
+
+static void clk_off(struct at91_udc *udc)
+{
+ if (!udc->clocked)
+ return;
+ udc->clocked = 0;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ clk_disable(udc->iclk);
+ clk_disable(udc->fclk);
+}
+
+/*
+ * activate/deactivate link with host; minimize power usage for
+ * inactive links by cutting clocks and transceiver power.
+ */
+static void pullup(struct at91_udc *udc, int is_on)
+{
+ if (!udc->enabled || !udc->vbus)
+ is_on = 0;
+ DBG("%sactive\n", is_on ? "" : "in");
+ if (is_on) {
+ clk_on(udc);
+ at91_udp_write(AT91_UDP_TXVC, 0);
+ at91_set_gpio_value(udc->board.pullup_pin, 1);
+ } else {
+ stop_activity(udc);
+ at91_udp_write(AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
+ at91_set_gpio_value(udc->board.pullup_pin, 0);
+ clk_off(udc);
+
+ // REVISIT: with transceiver disabled, will D- float
+ // so that a host would falsely detect a device?
+ }
+}
+
+/* vbus is here! turn everything on that's ready */
+static int at91_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct at91_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ // VDBG("vbus %s\n", is_active ? "on" : "off");
+ local_irq_save(flags);
+ udc->vbus = (is_active != 0);
+ pullup(udc, is_active);
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int at91_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct at91_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ udc->enabled = is_on = !!is_on;
+ pullup(udc, is_on);
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
+{
+ struct at91_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ udc->selfpowered = (is_on != 0);
+ local_irq_restore(flags);
+ return 0;
+}
+
+static const struct usb_gadget_ops at91_udc_ops = {
+ .get_frame = at91_get_frame,
+ .wakeup = at91_wakeup,
+ .set_selfpowered = at91_set_selfpowered,
+ .vbus_session = at91_vbus_session,
+ .pullup = at91_pullup,
+
+ /*
+ * VBUS-powered devices may also also want to support bigger
+ * power budgets after an appropriate SET_CONFIGURATION.
+ */
+ // .vbus_power = at91_vbus_power,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int handle_ep(struct at91_ep *ep)
+{
+ struct at91_request *req;
+ u32 __iomem *creg = ep->creg;
+ u32 csr = __raw_readl(creg);
+
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next,
+ struct at91_request, queue);
+ else
+ req = NULL;
+
+ if (ep->is_in) {
+ if (csr & (AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)) {
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP);
+ __raw_writel(csr, creg);
+ }
+ if (req)
+ return write_fifo(ep, req);
+
+ } else {
+ if (csr & AT91_UDP_STALLSENT) {
+ /* STALLSENT bit == ISOERR */
+ if (ep->is_iso && req)
+ req->req.status = -EILSEQ;
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_STALLSENT);
+ __raw_writel(csr, creg);
+ csr = __raw_readl(creg);
+ }
+ if (req && (csr & RX_DATA_READY))
+ return read_fifo(ep, req);
+ }
+ return 0;
+}
+
+union setup {
+ u8 raw[8];
+ struct usb_ctrlrequest r;
+};
+
+static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
+{
+ u32 __iomem *creg = ep->creg;
+ u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
+ unsigned rxcount, i = 0;
+ u32 tmp;
+ union setup pkt;
+ int status = 0;
+
+ /* read and ack SETUP; hard-fail for bogus packets */
+ rxcount = (csr & AT91_UDP_RXBYTECNT) >> 16;
+ if (likely(rxcount == 8)) {
+ while (rxcount--)
+ pkt.raw[i++] = __raw_readb(dreg);
+ if (pkt.r.bRequestType & USB_DIR_IN) {
+ csr |= AT91_UDP_DIR;
+ ep->is_in = 1;
+ } else {
+ csr &= ~AT91_UDP_DIR;
+ ep->is_in = 0;
+ }
+ } else {
+ // REVISIT this happens sometimes under load; why??
+ ERR("SETUP len %d, csr %08x\n", rxcount, csr);
+ status = -EINVAL;
+ }
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_RXSETUP);
+ __raw_writel(csr, creg);
+ udc->wait_for_addr_ack = 0;
+ udc->wait_for_config_ack = 0;
+ ep->stopped = 0;
+ if (unlikely(status != 0))
+ goto stall;
+
+#define w_index le16_to_cpu(pkt.r.wIndex)
+#define w_value le16_to_cpu(pkt.r.wValue)
+#define w_length le16_to_cpu(pkt.r.wLength)
+
+ VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",
+ pkt.r.bRequestType, pkt.r.bRequest,
+ w_value, w_index, w_length);
+
+ /*
+ * A few standard requests get handled here, ones that touch
+ * hardware ... notably for device and endpoint features.
+ */
+ udc->req_pending = 1;
+ csr = __raw_readl(creg);
+ csr |= CLR_FX;
+ csr &= ~SET_FX;
+ switch ((pkt.r.bRequestType << 8) | pkt.r.bRequest) {
+
+ case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_SET_ADDRESS:
+ __raw_writel(csr | AT91_UDP_TXPKTRDY, creg);
+ udc->addr = w_value;
+ udc->wait_for_addr_ack = 1;
+ udc->req_pending = 0;
+ /* FADDR is set later, when we ack host STATUS */
+ return;
+
+ case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_SET_CONFIGURATION:
+ tmp = at91_udp_read(AT91_UDP_GLB_STAT) & AT91_UDP_CONFG;
+ if (pkt.r.wValue)
+ udc->wait_for_config_ack = (tmp == 0);
+ else
+ udc->wait_for_config_ack = (tmp != 0);
+ if (udc->wait_for_config_ack)
+ VDBG("wait for config\n");
+ /* CONFG is toggled later, if gadget driver succeeds */
+ break;
+
+ /*
+ * Hosts may set or clear remote wakeup status, and
+ * devices may report they're VBUS powered.
+ */
+ case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_GET_STATUS:
+ tmp = (udc->selfpowered << USB_DEVICE_SELF_POWERED);
+ if (at91_udp_read(AT91_UDP_GLB_STAT) & AT91_UDP_ESR)
+ tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP);
+ PACKET("get device status\n");
+ __raw_writeb(tmp, dreg);
+ __raw_writeb(0, dreg);
+ goto write_in;
+ /* then STATUS starts later, automatically */
+ case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_SET_FEATURE:
+ if (w_value != USB_DEVICE_REMOTE_WAKEUP)
+ goto stall;
+ tmp = at91_udp_read(AT91_UDP_GLB_STAT);
+ tmp |= AT91_UDP_ESR;
+ at91_udp_write(AT91_UDP_GLB_STAT, tmp);
+ goto succeed;
+ case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+ | USB_REQ_CLEAR_FEATURE:
+ if (w_value != USB_DEVICE_REMOTE_WAKEUP)
+ goto stall;
+ tmp = at91_udp_read(AT91_UDP_GLB_STAT);
+ tmp &= ~AT91_UDP_ESR;
+ at91_udp_write(AT91_UDP_GLB_STAT, tmp);
+ goto succeed;
+
+ /*
+ * Interfaces have no feature settings; this is pretty useless.
+ * we won't even insist the interface exists...
+ */
+ case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)
+ | USB_REQ_GET_STATUS:
+ PACKET("get interface status\n");
+ __raw_writeb(0, dreg);
+ __raw_writeb(0, dreg);
+ goto write_in;
+ /* then STATUS starts later, automatically */
+ case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)
+ | USB_REQ_SET_FEATURE:
+ case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)
+ | USB_REQ_CLEAR_FEATURE:
+ goto stall;
+
+ /*
+ * Hosts may clear bulk/intr endpoint halt after the gadget
+ * driver sets it (not widely used); or set it (for testing)
+ */
+ case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)
+ | USB_REQ_GET_STATUS:
+ tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
+ ep = &udc->ep[tmp];
+ if (tmp > NUM_ENDPOINTS || (tmp && !ep->desc))
+ goto stall;
+
+ if (tmp) {
+ if ((w_index & USB_DIR_IN)) {
+ if (!ep->is_in)
+ goto stall;
+ } else if (ep->is_in)
+ goto stall;
+ }
+ PACKET("get %s status\n", ep->ep.name);
+ if (__raw_readl(ep->creg) & AT91_UDP_FORCESTALL)
+ tmp = (1 << USB_ENDPOINT_HALT);
+ else
+ tmp = 0;
+ __raw_writeb(tmp, dreg);
+ __raw_writeb(0, dreg);
+ goto write_in;
+ /* then STATUS starts later, automatically */
+ case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)
+ | USB_REQ_SET_FEATURE:
+ tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
+ ep = &udc->ep[tmp];
+ if (w_value != USB_ENDPOINT_HALT || tmp > NUM_ENDPOINTS)
+ goto stall;
+ if (!ep->desc || ep->is_iso)
+ goto stall;
+ if ((w_index & USB_DIR_IN)) {
+ if (!ep->is_in)
+ goto stall;
+ } else if (ep->is_in)
+ goto stall;
+
+ tmp = __raw_readl(ep->creg);
+ tmp &= ~SET_FX;
+ tmp |= CLR_FX | AT91_UDP_FORCESTALL;
+ __raw_writel(tmp, ep->creg);
+ goto succeed;
+ case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)
+ | USB_REQ_CLEAR_FEATURE:
+ tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
+ ep = &udc->ep[tmp];
+ if (w_value != USB_ENDPOINT_HALT || tmp > NUM_ENDPOINTS)
+ goto stall;
+ if (tmp == 0)
+ goto succeed;
+ if (!ep->desc || ep->is_iso)
+ goto stall;
+ if ((w_index & USB_DIR_IN)) {
+ if (!ep->is_in)
+ goto stall;
+ } else if (ep->is_in)
+ goto stall;
+
+ at91_udp_write(AT91_UDP_RST_EP, ep->int_mask);
+ at91_udp_write(AT91_UDP_RST_EP, 0);
+ tmp = __raw_readl(ep->creg);
+ tmp |= CLR_FX;
+ tmp &= ~(SET_FX | AT91_UDP_FORCESTALL);
+ __raw_writel(tmp, ep->creg);
+ if (!list_empty(&ep->queue))
+ handle_ep(ep);
+ goto succeed;
+ }
+
+#undef w_value
+#undef w_index
+#undef w_length
+
+ /* pass request up to the gadget driver */
+ status = udc->driver->setup(&udc->gadget, &pkt.r);
+ if (status < 0) {
+stall:
+ VDBG("req %02x.%02x protocol STALL; stat %d\n",
+ pkt.r.bRequestType, pkt.r.bRequest, status);
+ csr |= AT91_UDP_FORCESTALL;
+ __raw_writel(csr, creg);
+ udc->req_pending = 0;
+ }
+ return;
+
+succeed:
+ /* immediate successful (IN) STATUS after zero length DATA */
+ PACKET("ep0 in/status\n");
+write_in:
+ csr |= AT91_UDP_TXPKTRDY;
+ __raw_writel(csr, creg);
+ udc->req_pending = 0;
+ return;
+}
+
+static void handle_ep0(struct at91_udc *udc)
+{
+ struct at91_ep *ep0 = &udc->ep[0];
+ u32 __iomem *creg = ep0->creg;
+ u32 csr = __raw_readl(creg);
+ struct at91_request *req;
+
+ if (unlikely(csr & AT91_UDP_STALLSENT)) {
+ nuke(ep0, -EPROTO);
+ udc->req_pending = 0;
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_FORCESTALL);
+ __raw_writel(csr, creg);
+ VDBG("ep0 stalled\n");
+ csr = __raw_readl(creg);
+ }
+ if (csr & AT91_UDP_RXSETUP) {
+ nuke(ep0, 0);
+ udc->req_pending = 0;
+ handle_setup(udc, ep0, csr);
+ return;
+ }
+
+ if (list_empty(&ep0->queue))
+ req = NULL;
+ else
+ req = list_entry(ep0->queue.next, struct at91_request, queue);
+
+ /* host ACKed an IN packet that we sent */
+ if (csr & AT91_UDP_TXCOMP) {
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_TXCOMP);
+
+ /* write more IN DATA? */
+ if (req && ep0->is_in) {
+ if (handle_ep(ep0))
+ udc->req_pending = 0;
+
+ /*
+ * Ack after:
+ * - last IN DATA packet (including GET_STATUS)
+ * - IN/STATUS for OUT DATA
+ * - IN/STATUS for any zero-length DATA stage
+ * except for the IN DATA case, the host should send
+ * an OUT status later, which we'll ack.
+ */
+ } else {
+ udc->req_pending = 0;
+ __raw_writel(csr, creg);
+
+ /*
+ * SET_ADDRESS takes effect only after the STATUS
+ * (to the original address) gets acked.
+ */
+ if (udc->wait_for_addr_ack) {
+ u32 tmp;
+
+ at91_udp_write(AT91_UDP_FADDR, AT91_UDP_FEN | udc->addr);
+ tmp = at91_udp_read(AT91_UDP_GLB_STAT);
+ tmp &= ~AT91_UDP_FADDEN;
+ if (udc->addr)
+ tmp |= AT91_UDP_FADDEN;
+ at91_udp_write(AT91_UDP_GLB_STAT, tmp);
+
+ udc->wait_for_addr_ack = 0;
+ VDBG("address %d\n", udc->addr);
+ }
+ }
+ }
+
+ /* OUT packet arrived ... */
+ else if (csr & AT91_UDP_RX_DATA_BK0) {
+ csr |= CLR_FX;
+ csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);
+
+ /* OUT DATA stage */
+ if (!ep0->is_in) {
+ if (req) {
+ if (handle_ep(ep0)) {
+ /* send IN/STATUS */
+ PACKET("ep0 in/status\n");
+ csr = __raw_readl(creg);
+ csr &= ~SET_FX;
+ csr |= CLR_FX | AT91_UDP_TXPKTRDY;
+ __raw_writel(csr, creg);
+ udc->req_pending = 0;
+ }
+ } else if (udc->req_pending) {
+ /*
+ * AT91 hardware has a hard time with this
+ * "deferred response" mode for control-OUT
+ * transfers. (For control-IN it's fine.)
+ *
+ * The normal solution leaves OUT data in the
+ * fifo until the gadget driver is ready.
+ * We couldn't do that here without disabling
+ * the IRQ that tells about SETUP packets,
+ * e.g. when the host gets impatient...
+ *
+ * Working around it by copying into a buffer
+ * would almost be a non-deferred response,
+ * except that it wouldn't permit reliable
+ * stalling of the request. Instead, demand
+ * that gadget drivers not use this mode.
+ */
+ DBG("no control-OUT deferred responses!\n");
+ __raw_writel(csr | AT91_UDP_FORCESTALL, creg);
+ udc->req_pending = 0;
+ }
+
+ /* STATUS stage for control-IN; ack. */
+ } else {
+ PACKET("ep0 out/status ACK\n");
+ __raw_writel(csr, creg);
+
+ /* "early" status stage */
+ if (req)
+ done(ep0, req, 0);
+ }
+ }
+}
+
+static irqreturn_t at91_udc_irq (int irq, void *_udc, struct pt_regs *r)
+{
+ struct at91_udc *udc = _udc;
+ u32 rescans = 5;
+
+ while (rescans--) {
+ u32 status = at91_udp_read(AT91_UDP_ISR);
+
+ status &= at91_udp_read(AT91_UDP_IMR);
+ if (!status)
+ break;
+
+ /* USB reset irq: not maskable */
+ if (status & AT91_UDP_ENDBUSRES) {
+ at91_udp_write(AT91_UDP_IDR, ~MINIMUS_INTERRUPTUS);
+ at91_udp_write(AT91_UDP_IER, MINIMUS_INTERRUPTUS);
+ /* Atmel code clears this irq twice */
+ at91_udp_write(AT91_UDP_ICR, AT91_UDP_ENDBUSRES);
+ at91_udp_write(AT91_UDP_ICR, AT91_UDP_ENDBUSRES);
+ VDBG("end bus reset\n");
+ udc->addr = 0;
+ stop_activity(udc);
+
+ /* enable ep0 */
+ at91_udp_write(AT91_UDP_CSR(0), AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL);
+ udc->gadget.speed = USB_SPEED_FULL;
+ udc->suspended = 0;
+ at91_udp_write(AT91_UDP_IER, AT91_UDP_EP(0));
+
+ /*
+ * NOTE: this driver keeps clocks off unless the
+ * USB host is present. That saves power, and also
+ * eliminates IRQs (reset, resume, suspend) that can
+ * otherwise flood from the controller. If your
+ * board doesn't support VBUS detection, suspend and
+ * resume irq logic may need more attention...
+ */
+
+ /* host initiated suspend (3+ms bus idle) */
+ } else if (status & AT91_UDP_RXSUSP) {
+ at91_udp_write(AT91_UDP_IDR, AT91_UDP_RXSUSP);
+ at91_udp_write(AT91_UDP_IER, AT91_UDP_RXRSM);
+ at91_udp_write(AT91_UDP_ICR, AT91_UDP_RXSUSP);
+ // VDBG("bus suspend\n");
+ if (udc->suspended)
+ continue;
+ udc->suspended = 1;
+
+ /*
+ * NOTE: when suspending a VBUS-powered device, the
+ * gadget driver should switch into slow clock mode
+ * and then into standby to avoid drawing more than
+ * 500uA power (2500uA for some high-power configs).
+ */
+ if (udc->driver && udc->driver->suspend)
+ udc->driver->suspend(&udc->gadget);
+
+ /* host initiated resume */
+ } else if (status & AT91_UDP_RXRSM) {
+ at91_udp_write(AT91_UDP_IDR, AT91_UDP_RXRSM);
+ at91_udp_write(AT91_UDP_IER, AT91_UDP_RXSUSP);
+ at91_udp_write(AT91_UDP_ICR, AT91_UDP_RXRSM);
+ // VDBG("bus resume\n");
+ if (!udc->suspended)
+ continue;
+ udc->suspended = 0;
+
+ /*
+ * NOTE: for a VBUS-powered device, the gadget driver
+ * would normally want to switch out of slow clock
+ * mode into normal mode.
+ */
+ if (udc->driver && udc->driver->resume)
+ udc->driver->resume(&udc->gadget);
+
+ /* endpoint IRQs are cleared by handling them */
+ } else {
+ int i;
+ unsigned mask = 1;
+ struct at91_ep *ep = &udc->ep[1];
+
+ if (status & mask)
+ handle_ep0(udc);
+ for (i = 1; i < NUM_ENDPOINTS; i++) {
+ mask <<= 1;
+ if (status & mask)
+ handle_ep(ep);
+ ep++;
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct at91_udc controller = {
+ .gadget = {
+ .ops = &at91_udc_ops,
+ .ep0 = &controller.ep[0].ep,
+ .name = driver_name,
+ .dev = {
+ .bus_id = "gadget"
+ }
+ },
+ .ep[0] = {
+ .ep = {
+ .name = ep0name,
+ .ops = &at91_ep_ops,
+ },
+ .udc = &controller,
+ .maxpacket = 8,
+ .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(0)),
+ .int_mask = 1 << 0,
+ },
+ .ep[1] = {
+ .ep = {
+ .name = "ep1",
+ .ops = &at91_ep_ops,
+ },
+ .udc = &controller,
+ .is_pingpong = 1,
+ .maxpacket = 64,
+ .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(1)),
+ .int_mask = 1 << 1,
+ },
+ .ep[2] = {
+ .ep = {
+ .name = "ep2",
+ .ops = &at91_ep_ops,
+ },
+ .udc = &controller,
+ .is_pingpong = 1,
+ .maxpacket = 64,
+ .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(2)),
+ .int_mask = 1 << 2,
+ },
+ .ep[3] = {
+ .ep = {
+ /* could actually do bulk too */
+ .name = "ep3-int",
+ .ops = &at91_ep_ops,
+ },
+ .udc = &controller,
+ .maxpacket = 8,
+ .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(3)),
+ .int_mask = 1 << 3,
+ },
+ .ep[4] = {
+ .ep = {
+ .name = "ep4",
+ .ops = &at91_ep_ops,
+ },
+ .udc = &controller,
+ .is_pingpong = 1,
+ .maxpacket = 256,
+ .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(4)),
+ .int_mask = 1 << 4,
+ },
+ .ep[5] = {
+ .ep = {
+ .name = "ep5",
+ .ops = &at91_ep_ops,
+ },
+ .udc = &controller,
+ .is_pingpong = 1,
+ .maxpacket = 256,
+ .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(5)),
+ .int_mask = 1 << 5,
+ },
+ /* ep6 and ep7 are also reserved */
+};
+
+static irqreturn_t at91_vbus_irq(int irq, void *_udc, struct pt_regs *r)
+{
+ struct at91_udc *udc = _udc;
+ unsigned value;
+
+ /* vbus needs at least brief debouncing */
+ udelay(10);
+ value = at91_get_gpio_value(udc->board.vbus_pin);
+ if (value != udc->vbus)
+ at91_vbus_session(&udc->gadget, value);
+
+ return IRQ_HANDLED;
+}
+
+int usb_gadget_register_driver (struct usb_gadget_driver *driver)
+{
+ struct at91_udc *udc = &controller;
+ int retval;
+
+ if (!driver
+ || driver->speed != USB_SPEED_FULL
+ || !driver->bind
+ || !driver->unbind
+ || !driver->setup) {
+ DBG("bad parameter.\n");
+ return -EINVAL;
+ }
+
+ if (udc->driver) {
+ DBG("UDC already has a gadget driver\n");
+ return -EBUSY;
+ }
+
+ udc->driver = driver;
+ udc->gadget.dev.driver = &driver->driver;
+ udc->gadget.dev.driver_data = &driver->driver;
+ udc->enabled = 1;
+ udc->selfpowered = 1;
+
+ retval = driver->bind(&udc->gadget);
+ if (retval) {
+ DBG("driver->bind() returned %d\n", retval);
+ udc->driver = NULL;
+ return retval;
+ }
+
+ local_irq_disable();
+ pullup(udc, 1);
+ local_irq_enable();
+
+ DBG("bound to %s\n", driver->driver.name);
+ return 0;
+}
+EXPORT_SYMBOL (usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+{
+ struct at91_udc *udc = &controller;
+
+ if (!driver || driver != udc->driver)
+ return -EINVAL;
+
+ local_irq_disable();
+ udc->enabled = 0;
+ pullup(udc, 0);
+ local_irq_enable();
+
+ driver->unbind(&udc->gadget);
+ udc->driver = NULL;
+
+ DBG("unbound from %s\n", driver->driver.name);
+ return 0;
+}
+EXPORT_SYMBOL (usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------*/
+
+static void at91udc_shutdown(struct platform_device *dev)
+{
+ /* force disconnect on reboot */
+ pullup(platform_get_drvdata(dev), 0);
+}
+
+static int __devinit at91udc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct at91_udc *udc;
+ int retval;
+
+ if (!dev->platform_data) {
+ /* small (so we copy it) but critical! */
+ DBG("missing platform_data\n");
+ return -ENODEV;
+ }
+
+ if (!request_mem_region(AT91_BASE_UDP, SZ_16K, driver_name)) {
+ DBG("someone's using UDC memory\n");
+ return -EBUSY;
+ }
+
+ /* init software state */
+ udc = &controller;
+ udc->gadget.dev.parent = dev;
+ udc->board = *(struct at91_udc_data *) dev->platform_data;
+ udc->pdev = pdev;
+ udc_reinit(udc);
+ udc->enabled = 0;
+
+ /* get interface and function clocks */
+ udc->iclk = clk_get(dev, "udc_clk");
+ udc->fclk = clk_get(dev, "udpck");
+ if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk)) {
+ DBG("clocks missing\n");
+ return -ENODEV;
+ }
+
+ retval = device_register(&udc->gadget.dev);
+ if (retval < 0)
+ goto fail0;
+
+ /* disable everything until there's a gadget driver and vbus */
+ pullup(udc, 0);
+
+ /* request UDC and maybe VBUS irqs */
+ if (request_irq(AT91_ID_UDP, at91_udc_irq, SA_INTERRUPT, driver_name, udc)) {
+ DBG("request irq %d failed\n", AT91_ID_UDP);
+ retval = -EBUSY;
+ goto fail1;
+ }
+ if (udc->board.vbus_pin > 0) {
+ if (request_irq(udc->board.vbus_pin, at91_vbus_irq, SA_INTERRUPT, driver_name, udc)) {
+ DBG("request vbus irq %d failed\n", udc->board.vbus_pin);
+ free_irq(AT91_ID_UDP, udc);
+ retval = -EBUSY;
+ goto fail1;
+ }
+ } else {
+ DBG("no VBUS detection, assuming always-on\n");
+ udc->vbus = 1;
+ }
+ dev_set_drvdata(dev, udc);
+ create_debug_file(udc);
+
+ INFO("%s version %s\n", driver_name, DRIVER_VERSION);
+ return 0;
+
+fail1:
+ device_unregister(&udc->gadget.dev);
+fail0:
+ release_mem_region(AT91_VA_BASE_UDP, SZ_16K);
+ DBG("%s probe failed, %d\n", driver_name, retval);
+ return retval;
+}
+
+static int __devexit at91udc_remove(struct platform_device *dev)
+{
+ struct at91_udc *udc = platform_get_drvdata(dev);
+
+ DBG("remove\n");
+
+ pullup(udc, 0);
+
+ if (udc->driver != 0)
+ usb_gadget_unregister_driver(udc->driver);
+
+ remove_debug_file(udc);
+ if (udc->board.vbus_pin > 0)
+ free_irq(udc->board.vbus_pin, udc);
+ free_irq(AT91_ID_UDP, udc);
+ device_unregister(&udc->gadget.dev);
+ release_mem_region(AT91_BASE_UDP, SZ_16K);
+
+ clk_put(udc->iclk);
+ clk_put(udc->fclk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int at91udc_suspend(struct platform_device *dev, u32 state, u32 level)
+{
+ struct at91_udc *udc = platform_get_drvdata(dev);
+
+ /*
+ * The "safe" suspend transitions are opportunistic ... e.g. when
+ * the USB link is suspended (48MHz clock autogated off), or when
+ * it's disconnected (programmatically gated off, elsewhere).
+ * Then we can suspend, and the chip can enter slow clock mode.
+ *
+ * The problem case is some component (user mode?) suspending this
+ * device while it's active, with the 48 MHz clock in use. There
+ * are two basic approaches: (a) veto suspend levels involving slow
+ * clock mode, (b) disconnect, so 48 MHz will no longer be in use
+ * and we can enter slow clock mode. This uses (b) for now, since
+ * it's simplest until AT91 PM exists and supports the other option.
+ */
+ if (udc->vbus && !udc->suspended)
+ pullup(udc, 0);
+ return 0;
+}
+
+static int at91udc_resume(struct platform_device *dev, u32 level)
+{
+ struct at91_udc *udc = platform_get_drvdata(dev);
+
+ /* maybe reconnect to host; if so, clocks on */
+ pullup(udc, 1);
+ return 0;
+}
+#else
+#define at91udc_suspend NULL
+#define at91udc_resume NULL
+#endif
+
+static struct platform_driver at91_udc = {
+ .probe = at91udc_probe,
+ .remove = __devexit_p(at91udc_remove),
+ .shutdown = at91udc_shutdown,
+ .suspend = at91udc_suspend,
+ .resume = at91udc_resume,
+ .driver = {
+ .name = (char *) driver_name,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit udc_init_module(void)
+{
+ return platform_driver_register(&at91_udc);
+}
+module_init(udc_init_module);
+
+static void __devexit udc_exit_module(void)
+{
+ platform_driver_unregister(&at91_udc);
+}
+module_exit(udc_exit_module);
+
+MODULE_DESCRIPTION("AT91RM9200 udc driver");
+MODULE_AUTHOR("Thomas Rathbone, David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h
new file mode 100644
index 00000000000..5a4799cedd1
--- /dev/null
+++ b/drivers/usb/gadget/at91_udc.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2004 by Thomas Rathbone, HP Labs
+ * Copyright (C) 2005 by Ivan Kokshaysky
+ * Copyright (C) 2006 by SAN People
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AT91_UDC_H
+#define AT91_UDC_H
+
+/*
+ * USB Device Port (UDP) registers.
+ * Based on AT91RM9200 datasheet revision E.
+ */
+
+#define AT91_UDP_FRM_NUM 0x00 /* Frame Number Register */
+#define AT91_UDP_NUM (0x7ff << 0) /* Frame Number */
+#define AT91_UDP_FRM_ERR (1 << 16) /* Frame Error */
+#define AT91_UDP_FRM_OK (1 << 17) /* Frame OK */
+
+#define AT91_UDP_GLB_STAT 0x04 /* Global State Register */
+#define AT91_UDP_FADDEN (1 << 0) /* Function Address Enable */
+#define AT91_UDP_CONFG (1 << 1) /* Configured */
+#define AT91_UDP_ESR (1 << 2) /* Enable Send Resume */
+#define AT91_UDP_RSMINPR (1 << 3) /* Resume has been sent */
+#define AT91_UDP_RMWUPE (1 << 4) /* Remote Wake Up Enable */
+
+#define AT91_UDP_FADDR 0x08 /* Function Address Register */
+#define AT91_UDP_FADD (0x7f << 0) /* Function Address Value */
+#define AT91_UDP_FEN (1 << 8) /* Function Enable */
+
+#define AT91_UDP_IER 0x10 /* Interrupt Enable Register */
+#define AT91_UDP_IDR 0x14 /* Interrupt Disable Register */
+#define AT91_UDP_IMR 0x18 /* Interrupt Mask Register */
+
+#define AT91_UDP_ISR 0x1c /* Interrupt Status Register */
+#define AT91_UDP_EP(n) (1 << (n)) /* Endpoint Interrupt Status */
+#define AT91_UDP_RXSUSP (1 << 8) /* USB Suspend Interrupt Status */
+#define AT91_UDP_RXRSM (1 << 9) /* USB Resume Interrupt Status */
+#define AT91_UDP_EXTRSM (1 << 10) /* External Resume Interrupt Status */
+#define AT91_UDP_SOFINT (1 << 11) /* Start of Frame Interrupt Status */
+#define AT91_UDP_ENDBUSRES (1 << 12) /* End of Bus Reset Interrpt Status */
+#define AT91_UDP_WAKEUP (1 << 13) /* USB Wakeup Interrupt Status */
+
+#define AT91_UDP_ICR 0x20 /* Interrupt Clear Register */
+#define AT91_UDP_RST_EP 0x28 /* Reset Endpoint Register */
+
+#define AT91_UDP_CSR(n) (0x30+((n)*4)) /* Endpoint Control/Status Registers 0-7 */
+#define AT91_UDP_TXCOMP (1 << 0) /* Generates IN packet with data previously written in DPR */
+#define AT91_UDP_RX_DATA_BK0 (1 << 1) /* Receive Data Bank 0 */
+#define AT91_UDP_RXSETUP (1 << 2) /* Send STALL to the host */
+#define AT91_UDP_STALLSENT (1 << 3) /* Stall Sent / Isochronous error (Isochronous endpoints) */
+#define AT91_UDP_TXPKTRDY (1 << 4) /* Transmit Packet Ready */
+#define AT91_UDP_FORCESTALL (1 << 5) /* Force Stall */
+#define AT91_UDP_RX_DATA_BK1 (1 << 6) /* Receive Data Bank 1 */
+#define AT91_UDP_DIR (1 << 7) /* Transfer Direction */
+#define AT91_UDP_EPTYPE (7 << 8) /* Endpoint Type */
+#define AT91_UDP_EPTYPE_CTRL (0 << 8)
+#define AT91_UDP_EPTYPE_ISO_OUT (1 << 8)
+#define AT91_UDP_EPTYPE_BULK_OUT (2 << 8)
+#define AT91_UDP_EPTYPE_INT_OUT (3 << 8)
+#define AT91_UDP_EPTYPE_ISO_IN (5 << 8)
+#define AT91_UDP_EPTYPE_BULK_IN (6 << 8)
+#define AT91_UDP_EPTYPE_INT_IN (7 << 8)
+#define AT91_UDP_DTGLE (1 << 11) /* Data Toggle */
+#define AT91_UDP_EPEDS (1 << 15) /* Endpoint Enable/Disable */
+#define AT91_UDP_RXBYTECNT (0x7ff << 16) /* Number of bytes in FIFO */
+
+#define AT91_UDP_FDR(n) (0x50+((n)*4)) /* Endpoint FIFO Data Registers 0-7 */
+
+#define AT91_UDP_TXVC 0x74 /* Transceiver Control Register */
+#define AT91_UDP_TXVC_TXVDIS (1 << 8) /* Transceiver Disable */
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * controller driver data structures
+ */
+
+#define NUM_ENDPOINTS 6
+
+/*
+ * hardware won't disable bus reset, or resume while the controller
+ * is suspended ... watching suspend helps keep the logic symmetric.
+ */
+#define MINIMUS_INTERRUPTUS \
+ (AT91_UDP_ENDBUSRES | AT91_UDP_RXRSM | AT91_UDP_RXSUSP)
+
+struct at91_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+ struct at91_udc *udc;
+ void __iomem *creg;
+
+ unsigned maxpacket:16;
+ u8 int_mask;
+ unsigned is_pingpong:1;
+
+ unsigned stopped:1;
+ unsigned is_in:1;
+ unsigned is_iso:1;
+ unsigned fifo_bank:1;
+
+ const struct usb_endpoint_descriptor
+ *desc;
+};
+
+/*
+ * driver is non-SMP, and just blocks IRQs whenever it needs
+ * access protection for chip registers or driver state
+ */
+struct at91_udc {
+ struct usb_gadget gadget;
+ struct at91_ep ep[NUM_ENDPOINTS];
+ struct usb_gadget_driver *driver;
+ unsigned vbus:1;
+ unsigned enabled:1;
+ unsigned clocked:1;
+ unsigned suspended:1;
+ unsigned req_pending:1;
+ unsigned wait_for_addr_ack:1;
+ unsigned wait_for_config_ack:1;
+ unsigned selfpowered:1;
+ u8 addr;
+ struct at91_udc_data board;
+ struct clk *iclk, *fclk;
+ struct platform_device *pdev;
+ struct proc_dir_entry *pde;
+};
+
+static inline struct at91_udc *to_udc(struct usb_gadget *g)
+{
+ return container_of(g, struct at91_udc, gadget);
+}
+
+struct at91_request {
+ struct usb_request req;
+ struct list_head queue;
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(stuff...) printk(KERN_DEBUG "udc: " stuff)
+#else
+#define DBG(stuff...) do{}while(0)
+#endif
+
+#ifdef VERBOSE
+# define VDBG DBG
+#else
+# define VDBG(stuff...) do{}while(0)
+#endif
+
+#ifdef PACKET_TRACE
+# define PACKET VDBG
+#else
+# define PACKET(stuff...) do{}while(0)
+#endif
+
+#define ERR(stuff...) printk(KERN_ERR "udc: " stuff)
+#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
+
+#endif
+
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 9734cb76dd6..42ce41d71b7 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -478,10 +478,9 @@ dummy_alloc_request (struct usb_ep *_ep, gfp_t mem_flags)
return NULL;
ep = usb_ep_to_dummy_ep (_ep);
- req = kmalloc (sizeof *req, mem_flags);
+ req = kzalloc(sizeof(*req), mem_flags);
if (!req)
return NULL;
- memset (req, 0, sizeof *req);
INIT_LIST_HEAD (&req->queue);
return &req->req;
}
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index afc84cfb61f..c3d8e5c5bf2 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -182,33 +182,37 @@ struct eth_dev {
* parameters are in UTF-8 (superset of ASCII's 7 bit characters).
*/
-static ushort __initdata idVendor;
+static ushort idVendor;
module_param(idVendor, ushort, S_IRUGO);
MODULE_PARM_DESC(idVendor, "USB Vendor ID");
-static ushort __initdata idProduct;
+static ushort idProduct;
module_param(idProduct, ushort, S_IRUGO);
MODULE_PARM_DESC(idProduct, "USB Product ID");
-static ushort __initdata bcdDevice;
+static ushort bcdDevice;
module_param(bcdDevice, ushort, S_IRUGO);
MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
-static char *__initdata iManufacturer;
+static char *iManufacturer;
module_param(iManufacturer, charp, S_IRUGO);
MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
-static char *__initdata iProduct;
+static char *iProduct;
module_param(iProduct, charp, S_IRUGO);
MODULE_PARM_DESC(iProduct, "USB Product string");
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber");
+
/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
-static char *__initdata dev_addr;
+static char *dev_addr;
module_param(dev_addr, charp, S_IRUGO);
MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
/* this address is invisible to ifconfig */
-static char *__initdata host_addr;
+static char *host_addr;
module_param(host_addr, charp, S_IRUGO);
MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
@@ -253,6 +257,14 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
+#ifdef CONFIG_USB_GADGET_MUSBHSFC
+#define DEV_CONFIG_CDC
+#endif
+
+#ifdef CONFIG_USB_GADGET_MUSBHDRC
+#define DEV_CONFIG_CDC
+#endif
+
/* For CDC-incapable hardware, choose the simple cdc subset.
* Anything that talks bulk (without notable bugs) can do this.
@@ -395,6 +407,7 @@ static inline int BITRATE(struct usb_gadget *g)
#define STRING_CDC 7
#define STRING_SUBSET 8
#define STRING_RNDIS 9
+#define STRING_SERIALNUMBER 10
/* holds our biggest descriptor (or RNDIS response) */
#define USB_BUFSIZ 256
@@ -862,6 +875,7 @@ static inline void __init hs_subset_descriptors(void)
static char manufacturer [50];
static char product_desc [40] = DRIVER_DESC;
+static char serial_number [20];
#ifdef DEV_CONFIG_CDC
/* address that the host will use ... usually assigned at random */
@@ -872,6 +886,7 @@ static char ethaddr [2 * ETH_ALEN + 1];
static struct usb_string strings [] = {
{ STRING_MANUFACTURER, manufacturer, },
{ STRING_PRODUCT, product_desc, },
+ { STRING_SERIALNUMBER, serial_number, },
{ STRING_DATA, "Ethernet Data", },
#ifdef DEV_CONFIG_CDC
{ STRING_CDC, "CDC Ethernet", },
@@ -1549,7 +1564,8 @@ static int eth_change_mtu (struct net_device *net, int new_mtu)
{
struct eth_dev *dev = netdev_priv(net);
- // FIXME if rndis, don't change while link's live
+ if (dev->rndis)
+ return -EBUSY;
if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
return -ERANGE;
@@ -2116,7 +2132,7 @@ eth_req_free (struct usb_ep *ep, struct usb_request *req)
}
-static void
+static void __exit
eth_unbind (struct usb_gadget *gadget)
{
struct eth_dev *dev = get_gadget_data (gadget);
@@ -2153,7 +2169,7 @@ static u8 __init nibble (unsigned char c)
return 0;
}
-static void __init get_ether_addr (const char *str, u8 *dev_addr)
+static int __init get_ether_addr(const char *str, u8 *dev_addr)
{
if (str) {
unsigned i;
@@ -2168,9 +2184,10 @@ static void __init get_ether_addr (const char *str, u8 *dev_addr)
dev_addr [i] = num;
}
if (is_valid_ether_addr (dev_addr))
- return;
+ return 0;
}
random_ether_addr(dev_addr);
+ return 1;
}
static int __init
@@ -2268,6 +2285,10 @@ eth_bind (struct usb_gadget *gadget)
strlcpy (manufacturer, iManufacturer, sizeof manufacturer);
if (iProduct)
strlcpy (product_desc, iProduct, sizeof product_desc);
+ if (iSerialNumber) {
+ device_desc.iSerialNumber = STRING_SERIALNUMBER,
+ strlcpy(serial_number, iSerialNumber, sizeof serial_number);
+ }
/* all we really need is bulk IN/OUT */
usb_ep_autoconfig_reset (gadget);
@@ -2377,9 +2398,13 @@ autoconf_fail:
* The host side address is used with CDC and RNDIS, and commonly
* ends up in a persistent config database.
*/
- get_ether_addr(dev_addr, net->dev_addr);
+ if (get_ether_addr(dev_addr, net->dev_addr))
+ dev_warn(&gadget->dev,
+ "using random %s ethernet address\n", "self");
if (cdc || rndis) {
- get_ether_addr(host_addr, dev->host_mac);
+ if (get_ether_addr(host_addr, dev->host_mac))
+ dev_warn(&gadget->dev,
+ "using random %s ethernet address\n", "host");
#ifdef DEV_CONFIG_CDC
snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
dev->host_mac [0], dev->host_mac [1],
@@ -2523,7 +2548,7 @@ static struct usb_gadget_driver eth_driver = {
.function = (char *) driver_desc,
.bind = eth_bind,
- .unbind = eth_unbind,
+ .unbind = __exit_p(eth_unbind),
.setup = eth_setup,
.disconnect = eth_disconnect,
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index de59c58896d..cf3be299e35 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -3678,7 +3678,7 @@ static void lun_release(struct device *dev)
kref_put(&fsg->ref, fsg_release);
}
-static void fsg_unbind(struct usb_gadget *gadget)
+static void __exit fsg_unbind(struct usb_gadget *gadget)
{
struct fsg_dev *fsg = get_gadget_data(gadget);
int i;
@@ -4064,7 +4064,7 @@ static struct usb_gadget_driver fsg_driver = {
#endif
.function = (char *) longname,
.bind = fsg_bind,
- .unbind = fsg_unbind,
+ .unbind = __exit_p(fsg_unbind),
.disconnect = fsg_disconnect,
.setup = fsg_setup,
.suspend = fsg_suspend,
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 8cbae21d84b..c4081407171 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -3,9 +3,9 @@
* gadget drivers or other code that needs to deal with them, and which
* autoconfigures instead of using early binding to the hardware.
*
- * This could eventually work like the ARM mach_is_*() stuff, driven by
+ * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by
* some config file that gets updated as new hardware is supported.
- * (And avoiding the runtime comparisons in typical one-choice cases.)
+ * (And avoiding all runtime comparisons in typical one-choice configs!)
*
* NOTE: some of these controller drivers may not be available yet.
*/
@@ -93,6 +93,26 @@
#define gadget_is_imx(g) 0
#endif
+/* Mentor high speed function controller */
+#ifdef CONFIG_USB_GADGET_MUSBHSFC
+#define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name)
+#else
+#define gadget_is_musbhsfc(g) 0
+#endif
+
+/* Mentor high speed "dual role" controller, peripheral mode */
+#ifdef CONFIG_USB_GADGET_MUSBHDRC
+#define gadget_is_musbhdrc(g) !strcmp("musbhdrc_udc", (g)->name)
+#else
+#define gadget_is_musbhdrc(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_MPC8272
+#define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name)
+#else
+#define gadget_is_mpc8272(g) 0
+#endif
+
// CONFIG_USB_GADGET_SX2
// CONFIG_USB_GADGET_AU1X00
// ...
@@ -143,5 +163,11 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x13;
else if (gadget_is_imx(gadget))
return 0x14;
+ else if (gadget_is_musbhsfc(gadget))
+ return 0x15;
+ else if (gadget_is_musbhdrc(gadget))
+ return 0x16;
+ else if (gadget_is_mpc8272(gadget))
+ return 0x17;
return -ENOENT;
}
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index b0f3cd63e3b..66b81bbf6be 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -275,11 +275,10 @@ goku_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
if (!_ep)
return NULL;
- req = kmalloc(sizeof *req, gfp_flags);
+ req = kzalloc(sizeof *req, gfp_flags);
if (!req)
return NULL;
- memset(req, 0, sizeof *req);
req->req.dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD(&req->queue);
return &req->req;
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 0aab7d24c76..b44cfda76b6 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -170,10 +170,9 @@ static struct dev_data *dev_new (void)
{
struct dev_data *dev;
- dev = kmalloc (sizeof *dev, GFP_KERNEL);
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
- memset (dev, 0, sizeof *dev);
dev->state = STATE_DEV_DISABLED;
atomic_set (&dev->count, 1);
spin_lock_init (&dev->lock);
@@ -1592,10 +1591,9 @@ static int activate_ep_files (struct dev_data *dev)
gadget_for_each_ep (ep, dev->gadget) {
struct ep_data *data;
- data = kmalloc (sizeof *data, GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
goto enomem;
- memset (data, 0, sizeof data);
data->state = STATE_EP_DISABLED;
init_MUTEX (&data->lock);
init_waitqueue_head (&data->wait);
diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c
index 1a362c5e7f3..0d3424eda03 100644
--- a/drivers/usb/gadget/lh7a40x_udc.c
+++ b/drivers/usb/gadget/lh7a40x_udc.c
@@ -1114,11 +1114,10 @@ static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep,
DEBUG("%s, %p\n", __FUNCTION__, ep);
- req = kmalloc(sizeof *req, gfp_flags);
+ req = kzalloc(sizeof(*req), gfp_flags);
if (!req)
return 0;
- memset(req, 0, sizeof *req);
INIT_LIST_HEAD(&req->queue);
return &req->req;
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index 67b13ab2f3f..fb73dc10053 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -386,11 +386,10 @@ net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
return NULL;
ep = container_of (_ep, struct net2280_ep, ep);
- req = kmalloc (sizeof *req, gfp_flags);
+ req = kzalloc(sizeof(*req), gfp_flags);
if (!req)
return NULL;
- memset (req, 0, sizeof *req);
req->req.dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD (&req->queue);
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index a8972d7c97b..fbea5144890 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -273,9 +273,8 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
{
struct omap_req *req;
- req = kmalloc(sizeof *req, gfp_flags);
+ req = kzalloc(sizeof(*req), gfp_flags);
if (req) {
- memset (req, 0, sizeof *req);
req->req.dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD (&req->queue);
}
@@ -2586,11 +2585,10 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
/* UDC_PULLUP_EN gates the chip clock */
// OTG_SYSCON_1_REG |= DEV_IDLE_EN;
- udc = kmalloc (sizeof *udc, SLAB_KERNEL);
+ udc = kzalloc(sizeof(*udc), SLAB_KERNEL);
if (!udc)
return -ENOMEM;
- memset(udc, 0, sizeof *udc);
spin_lock_init (&udc->lock);
udc->gadget.ops = &omap_gadget_ops;
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index bb028c5b895..680f7fc5b17 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -335,11 +335,10 @@ pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
{
struct pxa2xx_request *req;
- req = kmalloc (sizeof *req, gfp_flags);
+ req = kzalloc(sizeof(*req), gfp_flags);
if (!req)
return NULL;
- memset (req, 0, sizeof *req);
INIT_LIST_HEAD (&req->queue);
return &req->req;
}
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index ba9acd53102..b992546c394 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -369,7 +369,7 @@ static struct usb_gadget_driver gs_gadget_driver = {
#endif /* CONFIG_USB_GADGET_DUALSPEED */
.function = GS_LONG_NAME,
.bind = gs_bind,
- .unbind = gs_unbind,
+ .unbind = __exit_p(gs_unbind),
.setup = gs_setup,
.disconnect = gs_disconnect,
.driver = {
@@ -1413,7 +1413,7 @@ requeue:
* Called on module load. Allocates and initializes the device
* structure and a control request.
*/
-static int gs_bind(struct usb_gadget *gadget)
+static int __init gs_bind(struct usb_gadget *gadget)
{
int ret;
struct usb_ep *ep;
@@ -1538,7 +1538,7 @@ autoconf_fail:
* Called on module unload. Frees the control request and device
* structure.
*/
-static void gs_unbind(struct usb_gadget *gadget)
+static void __exit gs_unbind(struct usb_gadget *gadget)
{
struct gs_dev *dev = get_gadget_data(gadget);
@@ -2178,10 +2178,9 @@ static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags)
return -EIO;
for (i=0; i<GS_NUM_PORTS; i++) {
- if ((port=(struct gs_port *)kmalloc(sizeof(struct gs_port), kmalloc_flags)) == NULL)
+ if ((port=kzalloc(sizeof(struct gs_port), kmalloc_flags)) == NULL)
return -ENOMEM;
- memset(port, 0, sizeof(struct gs_port));
port->port_dev = dev;
port->port_num = i;
port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE);
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index ae7a1c0f574..51424f66a76 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -1119,7 +1119,7 @@ zero_autoresume (unsigned long _dev)
/*-------------------------------------------------------------------------*/
-static void
+static void __exit
zero_unbind (struct usb_gadget *gadget)
{
struct zero_dev *dev = get_gadget_data (gadget);
@@ -1136,7 +1136,7 @@ zero_unbind (struct usb_gadget *gadget)
set_gadget_data (gadget, NULL);
}
-static int
+static int __init
zero_bind (struct usb_gadget *gadget)
{
struct zero_dev *dev;
@@ -1188,10 +1188,9 @@ autoconf_fail:
/* ok, we made sense of the hardware ... */
- dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+ dev = kzalloc(sizeof(*dev), SLAB_KERNEL);
if (!dev)
return -ENOMEM;
- memset (dev, 0, sizeof *dev);
spin_lock_init (&dev->lock);
dev->gadget = gadget;
set_gadget_data (gadget, dev);
@@ -1224,12 +1223,6 @@ autoconf_fail:
loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- if (gadget->is_otg) {
- otg_descriptor.bmAttributes |= USB_OTG_HNP,
- source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
- loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
- }
-
usb_gadget_set_selfpowered (gadget);
init_timer (&dev->resume);
@@ -1294,7 +1287,7 @@ static struct usb_gadget_driver zero_driver = {
#endif
.function = (char *) longname,
.bind = zero_bind,
- .unbind = zero_unbind,
+ .unbind = __exit_p(zero_unbind),
.setup = zero_setup,
.disconnect = zero_disconnect,
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index be3fd9bce57..e27b79a3c05 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -6,7 +6,7 @@ comment "USB Host Controller Drivers"
config USB_EHCI_HCD
tristate "EHCI HCD (USB 2.0) support"
- depends on USB && PCI
+ depends on USB && USB_ARCH_HAS_EHCI
---help---
The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
"high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
new file mode 100644
index 00000000000..63eadeec132
--- /dev/null
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -0,0 +1,297 @@
+/*
+ * EHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * Bus Glue for AMD Alchemy Au1xxx
+ *
+ * Based on "ohci-au1xxx.c" by Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Modified for AMD Alchemy Au1200 EHC
+ * by K.Boge <karsten.boge@amd.com>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach-au1x00/au1000.h>
+
+#ifndef CONFIG_SOC_AU1200
+#error "this Alchemy chip doesn't have EHCI"
+#else /* Au1200 */
+
+#define USB_HOST_CONFIG (USB_MSR_BASE + USB_MSR_MCFG)
+#define USB_MCFG_PFEN (1<<31)
+#define USB_MCFG_RDCOMB (1<<30)
+#define USB_MCFG_SSDEN (1<<23)
+#define USB_MCFG_PHYPLLEN (1<<19)
+#define USB_MCFG_EHCCLKEN (1<<17)
+#define USB_MCFG_UCAM (1<<7)
+#define USB_MCFG_EBMEN (1<<3)
+#define USB_MCFG_EMEMEN (1<<2)
+
+#define USBH_ENABLE_CE (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN)
+
+#ifdef CONFIG_DMA_COHERENT
+#define USBH_ENABLE_INIT (USBH_ENABLE_CE \
+ | USB_MCFG_PFEN | USB_MCFG_RDCOMB \
+ | USB_MCFG_SSDEN | USB_MCFG_UCAM \
+ | USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
+#else
+#define USBH_ENABLE_INIT (USBH_ENABLE_CE \
+ | USB_MCFG_PFEN | USB_MCFG_RDCOMB \
+ | USB_MCFG_SSDEN \
+ | USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
+#endif
+#define USBH_DISABLE (USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
+
+#endif /* Au1200 */
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void au1xxx_start_ehc(struct platform_device *dev)
+{
+ pr_debug(__FILE__ ": starting Au1xxx EHCI USB Controller\n");
+
+ /* write HW defaults again in case Yamon cleared them */
+ if (au_readl(USB_HOST_CONFIG) == 0) {
+ au_writel(0x00d02000, USB_HOST_CONFIG);
+ au_readl(USB_HOST_CONFIG);
+ udelay(1000);
+ }
+ /* enable host controller */
+ au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+ au_readl(USB_HOST_CONFIG);
+ udelay(1000);
+ au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG),
+ USB_HOST_CONFIG);
+ au_readl(USB_HOST_CONFIG);
+ udelay(1000);
+
+ pr_debug(__FILE__ ": Clock to USB host has been enabled\n");
+}
+
+static void au1xxx_stop_ehc(struct platform_device *dev)
+{
+ pr_debug(__FILE__ ": stopping Au1xxx EHCI USB Controller\n");
+
+ /* Disable mem */
+ au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+ udelay(1000);
+ /* Disable clock */
+ au_writel(~USB_MCFG_EHCCLKEN & au_readl(USB_HOST_CONFIG),
+ USB_HOST_CONFIG);
+ au_readl(USB_HOST_CONFIG);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_ehci_au1xxx_probe - initialize Au1xxx-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_ehci_au1xxx_probe(const struct hc_driver *driver,
+ struct usb_hcd **hcd_out, struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+
+#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
+
+ /* Au1200 AB USB does not support coherent memory */
+ if (!(read_c0_prid() & 0xff)) {
+ pr_info("%s: this is chip revision AB!\n", dev->dev.name);
+ pr_info("%s: update your board or re-configure the kernel\n",
+ dev->dev.name);
+ return -ENODEV;
+ }
+#endif
+
+ au1xxx_start_ehc(dev);
+
+ if (dev->resource[1].flags != IORESOURCE_IRQ) {
+ pr_debug("resource[1] is not IORESOURCE_IRQ");
+ retval = -ENOMEM;
+ }
+ hcd = usb_create_hcd(driver, &dev->dev, "Au1xxx");
+ if (!hcd)
+ return -ENOMEM;
+ hcd->rsrc_start = dev->resource[0].start;
+ hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_debug("request_mem_region failed");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+ /* ehci_hcd_init(hcd_to_ehci(hcd)); */
+
+ retval =
+ usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT | SA_SHIRQ);
+ if (retval == 0)
+ return retval;
+
+ au1xxx_stop_ehc(dev);
+ iounmap(hcd->regs);
+err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_ehci_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_ehci_hcd_au1xxx_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_ehci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev)
+{
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ au1xxx_stop_ehc(dev);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ehci_au1xxx_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Au1xxx EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_init,
+ .start = ehci_run,
+ .stop = ehci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+#ifdef CONFIG_PM
+ .hub_suspend = ehci_hub_suspend,
+ .hub_resume = ehci_hub_resume,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_hcd_au1xxx_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = NULL;
+ int ret;
+
+ pr_debug("In ehci_hcd_au1xxx_drv_probe\n");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_ehci_au1xxx_probe(&ehci_au1xxx_hc_driver, &hcd, pdev);
+ return ret;
+}
+
+static int ehci_hcd_au1xxx_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_ehci_au1xxx_remove(hcd, pdev);
+ return 0;
+}
+
+ /*TBD*/
+/*static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ return 0;
+}
+static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ return 0;
+}
+*/
+static struct device_driver ehci_hcd_au1xxx_driver = {
+ .name = "au1xxx-ehci",
+ .bus = &platform_bus_type,
+ .probe = ehci_hcd_au1xxx_drv_probe,
+ .remove = ehci_hcd_au1xxx_drv_remove,
+ /*.suspend = ehci_hcd_au1xxx_drv_suspend, */
+ /*.resume = ehci_hcd_au1xxx_drv_resume, */
+};
+
+static int __init ehci_hcd_au1xxx_init(void)
+{
+ pr_debug(DRIVER_INFO " (Au1xxx)\n");
+
+ return driver_register(&ehci_hcd_au1xxx_driver);
+}
+
+static void __exit ehci_hcd_au1xxx_cleanup(void)
+{
+ driver_unregister(&ehci_hcd_au1xxx_driver);
+}
+
+module_init(ehci_hcd_au1xxx_init);
+module_exit(ehci_hcd_au1xxx_cleanup);
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
new file mode 100644
index 00000000000..f985f121a24
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.c
@@ -0,0 +1,366 @@
+/*
+ * (C) Copyright David Brownell 2000-2002
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Ported to 834x by Randy Vinson <rvinson@mvista.com> using code provided
+ * by Hunter Wu.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+
+#include "ehci-fsl.h"
+
+/* FIXME: Power Managment is un-ported so temporarily disable it */
+#undef CONFIG_PM
+
+/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_fsl_probe - initialize FSL-based HCDs
+ * @drvier: Driver to be used for this HCD
+ * @pdev: USB Host Controller being probed
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller.
+ *
+ */
+int usb_hcd_fsl_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq;
+ int retval;
+ unsigned int temp;
+
+ pr_debug("initializing FSL-SOC USB Controller\n");
+
+ /* Need platform data for setup */
+ pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev,
+ "No platform data for %s.\n", pdev->dev.bus_id);
+ return -ENODEV;
+ }
+
+ /*
+ * This is a host mode driver, verify that we're supposed to be
+ * in host mode.
+ */
+ if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
+ (pdata->operating_mode == FSL_USB2_MPH_HOST))) {
+ dev_err(&pdev->dev,
+ "Non Host Mode configured for %s. Wrong driver linked.\n",
+ pdev->dev.bus_id);
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ pdev->dev.bus_id);
+ return -ENODEV;
+ }
+ irq = res->start;
+
+ hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no register addr. Check %s setup!\n",
+ pdev->dev.bus_id);
+ retval = -ENODEV;
+ goto err2;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto err2;
+ }
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+
+ if (hcd->regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto err3;
+ }
+
+ /* Enable USB controller */
+ temp = in_be32(hcd->regs + 0x500);
+ out_be32(hcd->regs + 0x500, temp | 0x4);
+
+ /* Set to Host mode */
+ temp = in_le32(hcd->regs + 0x1a8);
+ out_le32(hcd->regs + 0x1a8, temp | 0x3);
+
+ retval = usb_add_hcd(hcd, irq, SA_SHIRQ);
+ if (retval != 0)
+ goto err4;
+ return retval;
+
+ err4:
+ iounmap(hcd->regs);
+ err3:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err2:
+ usb_put_hcd(hcd);
+ err1:
+ dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval);
+ return retval;
+}
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_fsl_probe().
+ *
+ */
+void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev)
+{
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+static void mpc83xx_setup_phy(struct ehci_hcd *ehci,
+ enum fsl_usb2_phy_modes phy_mode,
+ unsigned int port_offset)
+{
+ u32 portsc = 0;
+ switch (phy_mode) {
+ case FSL_USB2_PHY_ULPI:
+ portsc |= PORT_PTS_ULPI;
+ break;
+ case FSL_USB2_PHY_SERIAL:
+ portsc |= PORT_PTS_SERIAL;
+ break;
+ case FSL_USB2_PHY_UTMI_WIDE:
+ portsc |= PORT_PTS_PTW;
+ /* fall through */
+ case FSL_USB2_PHY_UTMI:
+ portsc |= PORT_PTS_UTMI;
+ break;
+ case FSL_USB2_PHY_NONE:
+ break;
+ }
+ writel(portsc, &ehci->regs->port_status[port_offset]);
+}
+
+static void mpc83xx_usb_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct fsl_usb2_platform_data *pdata;
+ void __iomem *non_ehci = hcd->regs;
+
+ pdata =
+ (struct fsl_usb2_platform_data *)hcd->self.controller->
+ platform_data;
+ /* Enable PHY interface in the control reg. */
+ out_be32(non_ehci + FSL_SOC_USB_CTRL, 0x00000004);
+ out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
+
+ if (pdata->operating_mode == FSL_USB2_DR_HOST)
+ mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
+
+ if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
+ unsigned int chip, rev, svr;
+
+ svr = mfspr(SPRN_SVR);
+ chip = svr >> 16;
+ rev = (svr >> 4) & 0xf;
+
+ /* Deal with USB Erratum #14 on MPC834x Rev 1.0 & 1.1 chips */
+ if ((rev == 1) && (chip >= 0x8050) && (chip <= 0x8055))
+ ehci->has_fsl_port_bug = 1;
+
+ if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
+ mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
+ if (pdata->port_enables & FSL_USB2_PORT1_ENABLED)
+ mpc83xx_setup_phy(ehci, pdata->phy_mode, 1);
+ }
+
+ /* put controller in host mode. */
+ writel(0x00000003, non_ehci + FSL_SOC_USB_USBMODE);
+ out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
+ out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
+ out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
+}
+
+/* called after powerup, by probe or system-pm "wakeup" */
+static int ehci_fsl_reinit(struct ehci_hcd *ehci)
+{
+ mpc83xx_usb_setup(ehci_to_hcd(ehci));
+ ehci_port_power(ehci, 0);
+
+ return 0;
+}
+
+/* called during probe() after chip reset completes */
+static int ehci_fsl_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ /* EHCI registers start at offset 0x100 */
+ ehci->caps = hcd->regs + 0x100;
+ ehci->regs = hcd->regs + 0x100 +
+ HC_LENGTH(readl(&ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci->is_tdi_rh_tt = 1;
+
+ ehci->sbrn = 0x20;
+
+ ehci_reset(ehci);
+
+ retval = ehci_fsl_reinit(ehci);
+ return retval;
+}
+
+static const struct hc_driver ehci_fsl_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Freescale On-Chip EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_fsl_setup,
+ .start = ehci_run,
+#ifdef CONFIG_PM
+ .suspend = ehci_bus_suspend,
+ .resume = ehci_bus_resume,
+#endif
+ .stop = ehci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+};
+
+static int ehci_fsl_drv_probe(struct platform_device *pdev)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev);
+}
+
+static int ehci_fsl_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_hcd_fsl_remove(hcd, pdev);
+
+ return 0;
+}
+
+static struct platform_driver ehci_fsl_dr_driver = {
+ .probe = ehci_fsl_drv_probe,
+ .remove = ehci_fsl_drv_remove,
+ .driver = {
+ .name = "fsl-usb2-dr",
+ },
+};
+
+static struct platform_driver ehci_fsl_mph_driver = {
+ .probe = ehci_fsl_drv_probe,
+ .remove = ehci_fsl_drv_remove,
+ .driver = {
+ .name = "fsl-usb2-mph",
+ },
+};
+
+static int __init ehci_fsl_init(void)
+{
+ int retval;
+
+ pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
+ hcd_name,
+ sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
+ sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
+
+ retval = platform_driver_register(&ehci_fsl_dr_driver);
+ if (retval)
+ return retval;
+
+ return platform_driver_register(&ehci_fsl_mph_driver);
+}
+
+static void __exit ehci_fsl_cleanup(void)
+{
+ platform_driver_unregister(&ehci_fsl_mph_driver);
+ platform_driver_unregister(&ehci_fsl_dr_driver);
+}
+
+module_init(ehci_fsl_init);
+module_exit(ehci_fsl_cleanup);
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
new file mode 100644
index 00000000000..caac0d1967d
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2005 freescale semiconductor
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _EHCI_FSL_H
+#define _EHCI_FSL_H
+
+/* offsets for the non-ehci registers in the FSL SOC USB controller */
+#define FSL_SOC_USB_ULPIVP 0x170
+#define FSL_SOC_USB_PORTSC1 0x184
+#define PORT_PTS_MSK (3<<30)
+#define PORT_PTS_UTMI (0<<30)
+#define PORT_PTS_ULPI (2<<30)
+#define PORT_PTS_SERIAL (3<<30)
+#define PORT_PTS_PTW (1<<28)
+#define FSL_SOC_USB_PORTSC2 0x188
+#define FSL_SOC_USB_USBMODE 0x1a8
+#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
+#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
+#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
+#define FSL_SOC_USB_SICTRL 0x40c /* NOTE: big-endian */
+#define FSL_SOC_USB_PRICTRL 0x410 /* NOTE: big-endian */
+#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
+#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 9dd3d14c64f..79f2d8b9bfb 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -889,8 +889,19 @@ MODULE_LICENSE ("GPL");
#ifdef CONFIG_PCI
#include "ehci-pci.c"
+#define EHCI_BUS_GLUED
#endif
-#if !defined(CONFIG_PCI)
+#ifdef CONFIG_PPC_83xx
+#include "ehci-fsl.c"
+#define EHCI_BUS_GLUED
+#endif
+
+#ifdef CONFIG_SOC_AU1X00
+#include "ehci-au1xxx.c"
+#define EHCI_BUS_GLUED
+#endif
+
+#ifndef EHCI_BUS_GLUED
#error "missing bus glue for ehci-hcd"
#endif
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 69b0b9be7a6..d03e3cad5ca 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -359,6 +359,8 @@ static int ehci_hub_control (
case USB_PORT_FEAT_SUSPEND:
if (temp & PORT_RESET)
goto error;
+ if (ehci->no_selective_suspend)
+ break;
if (temp & PORT_SUSPEND) {
if ((temp & PORT_PE) == 0)
goto error;
@@ -514,6 +516,8 @@ static int ehci_hub_control (
temp &= ~PORT_RWC_BITS;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
+ if (ehci->no_selective_suspend)
+ break;
if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0)
goto error;
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 91c2ab43cbc..766061e0260 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -75,7 +75,6 @@ static void qh_destroy (struct kref *kref)
}
if (qh->dummy)
ehci_qtd_free (ehci, qh->dummy);
- usb_put_dev (qh->dev);
dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
}
@@ -221,13 +220,9 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
ehci->periodic [i] = EHCI_LIST_END;
/* software shadow of hardware table */
- ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags);
- if (ehci->pshadow == NULL) {
- goto fail;
- }
- memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
-
- return 0;
+ ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags);
+ if (ehci->pshadow != NULL)
+ return 0;
fail:
ehci_dbg (ehci, "couldn't init memory\n");
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 3a6687df559..1e03f1a5a5f 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -106,11 +106,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
}
break;
case PCI_VENDOR_ID_NVIDIA:
+ switch (pdev->device) {
/* NVidia reports that certain chips don't handle
* QH, ITD, or SITD addresses above 2GB. (But TD,
* data buffer, and periodic schedule are normal.)
*/
- switch (pdev->device) {
case 0x003c: /* MCP04 */
case 0x005b: /* CK804 */
case 0x00d8: /* CK8 */
@@ -120,6 +120,14 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
ehci_warn(ehci, "can't enable NVidia "
"workaround for >2GB RAM\n");
break;
+ /* Some NForce2 chips have problems with selective suspend;
+ * fixed in newer silicon.
+ */
+ case 0x0068:
+ pci_read_config_dword(pdev, PCI_REVISION_ID, &temp);
+ if ((temp & 0xff) < 0xa4)
+ ehci->no_selective_suspend = 1;
+ break;
}
break;
}
@@ -163,6 +171,21 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
device_init_wakeup(&pdev->dev, 1);
}
+#ifdef CONFIG_USB_SUSPEND
+ /* REVISIT: the controller works fine for wakeup iff the root hub
+ * itself is "globally" suspended, but usbcore currently doesn't
+ * understand such things.
+ *
+ * System suspend currently expects to be able to suspend the entire
+ * device tree, device-at-a-time. If we failed selective suspend
+ * reports, system suspend would fail; so the root hub code must claim
+ * success. That's lying to usbcore, and it matters for for runtime
+ * PM scenarios with selective suspend and remote wakeup...
+ */
+ if (ehci->no_selective_suspend && device_can_wakeup(&pdev->dev))
+ ehci_warn(ehci, "selective suspend/wakeup unavailable\n");
+#endif
+
retval = ehci_pci_reinit(ehci, pdev);
done:
return retval;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 9b13bf2fa98..e469221e7ec 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -702,7 +702,7 @@ qh_make (
}
/* support for tt scheduling, and access to toggles */
- qh->dev = usb_get_dev (urb->dev);
+ qh->dev = urb->dev;
/* using TT? */
switch (urb->dev->speed) {
@@ -721,7 +721,14 @@ qh_make (
info1 |= maxp << 16;
info2 |= (EHCI_TUNE_MULT_TT << 30);
- info2 |= urb->dev->ttport << 23;
+
+ /* Some Freescale processors have an erratum in which the
+ * port number in the queue head was 0..N-1 instead of 1..N.
+ */
+ if (ehci_has_fsl_portno_bug(ehci))
+ info2 |= (urb->dev->ttport-1) << 23;
+ else
+ info2 |= urb->dev->ttport << 23;
/* set the address of the TT; for TDI's integrated
* root hub tt, leave it zeroed.
@@ -1015,12 +1022,14 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* stop async schedule right now? */
if (unlikely (qh == ehci->async)) {
/* can't get here without STS_ASS set */
- if (ehci_to_hcd(ehci)->state != HC_STATE_HALT) {
+ if (ehci_to_hcd(ehci)->state != HC_STATE_HALT
+ && !ehci->reclaim) {
+ /* ... and CMD_IAAD clear */
writel (cmd & ~CMD_ASE, &ehci->regs->command);
wmb ();
// handshake later, if we need to
+ timer_action_done (ehci, TIMER_ASYNC_OFF);
}
- timer_action_done (ehci, TIMER_ASYNC_OFF);
return;
}
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index ebcca970067..5871944e614 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -707,6 +707,7 @@ iso_stream_init (
} else {
u32 addr;
int think_time;
+ int hs_transfers;
addr = dev->ttport << 24;
if (!ehci_is_TDI(ehci)
@@ -719,6 +720,7 @@ iso_stream_init (
think_time = dev->tt ? dev->tt->think_time : 0;
stream->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time (
dev->speed, is_input, 1, maxp));
+ hs_transfers = max (1u, (maxp + 187) / 188);
if (is_input) {
u32 tmp;
@@ -727,12 +729,11 @@ iso_stream_init (
stream->usecs = HS_USECS_ISO (1);
stream->raw_mask = 1;
- /* pessimistic c-mask */
- tmp = usb_calc_bus_time (USB_SPEED_FULL, 1, 0, maxp)
- / (125 * 1000);
- stream->raw_mask |= 3 << (tmp + 9);
+ /* c-mask as specified in USB 2.0 11.18.4 3.c */
+ tmp = (1 << (hs_transfers + 2)) - 1;
+ stream->raw_mask |= tmp << (8 + 2);
} else
- stream->raw_mask = smask_out [maxp / 188];
+ stream->raw_mask = smask_out [hs_transfers - 1];
bandwidth = stream->usecs + stream->c_usecs;
bandwidth /= 1 << (interval + 2);
@@ -863,9 +864,8 @@ iso_sched_alloc (unsigned packets, gfp_t mem_flags)
int size = sizeof *iso_sched;
size += packets * sizeof (struct ehci_iso_packet);
- iso_sched = kmalloc (size, mem_flags);
+ iso_sched = kzalloc(size, mem_flags);
if (likely (iso_sched != NULL)) {
- memset(iso_sched, 0, size);
INIT_LIST_HEAD (&iso_sched->td_list);
}
return iso_sched;
@@ -1398,7 +1398,7 @@ itd_complete (
*/
/* give urb back to the driver ... can be out-of-order */
- dev = usb_get_dev (urb->dev);
+ dev = urb->dev;
ehci_urb_done (ehci, urb, regs);
urb = NULL;
@@ -1417,7 +1417,6 @@ itd_complete (
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
}
iso_stream_put (ehci, stream);
- usb_put_dev (dev);
return 1;
}
@@ -1764,7 +1763,7 @@ sitd_complete (
*/
/* give urb back to the driver */
- dev = usb_get_dev (urb->dev);
+ dev = urb->dev;
ehci_urb_done (ehci, urb, regs);
urb = NULL;
@@ -1783,7 +1782,6 @@ sitd_complete (
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
}
iso_stream_put (ehci, stream);
- usb_put_dev (dev);
return 1;
}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 18e257c2bdb..679c1cdcc91 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -88,7 +88,12 @@ struct ehci_hcd { /* one per controller */
unsigned long next_statechange;
u32 command;
+ /* SILICON QUIRKS */
unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */
+ unsigned no_selective_suspend:1;
+ unsigned has_fsl_port_bug:1; /* FreeScale */
+
+ u8 sbrn; /* packed release number */
/* irq statistics */
#ifdef EHCI_STATS
@@ -97,7 +102,6 @@ struct ehci_hcd { /* one per controller */
#else
# define COUNT(x) do {} while (0)
#endif
- u8 sbrn; /* packed release number */
};
/* convert between an HCD pointer and the corresponding EHCI_HCD */
@@ -638,6 +642,18 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_PPC_83xx
+/* Some Freescale processors have an erratum in which the TT
+ * port number in the queue head was 0..N-1 instead of 1..N.
+ */
+#define ehci_has_fsl_portno_bug(e) ((e)->has_fsl_port_bug)
+#else
+#define ehci_has_fsl_portno_bug(e) (0)
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
index 641268d7e6f..2fe7fd19437 100644
--- a/drivers/usb/host/hc_crisv10.c
+++ b/drivers/usb/host/hc_crisv10.c
@@ -2137,10 +2137,9 @@ static int etrax_usb_submit_bulk_urb(struct urb *urb)
urb->status = -EINPROGRESS;
/* Setup the hcpriv data. */
- urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+ urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
assert(urb_priv != NULL);
/* This sets rx_offset to 0. */
- memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
urb_priv->urb_state = NOT_STARTED;
urb->hcpriv = urb_priv;
@@ -2475,10 +2474,9 @@ static int etrax_usb_submit_ctrl_urb(struct urb *urb)
urb->status = -EINPROGRESS;
/* Setup the hcpriv data. */
- urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+ urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
assert(urb_priv != NULL);
/* This sets rx_offset to 0. */
- memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
urb_priv->urb_state = NOT_STARTED;
urb->hcpriv = urb_priv;
@@ -2767,9 +2765,8 @@ static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid)
maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
interval = urb->interval;
- urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+ urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
assert(urb_priv != NULL);
- memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
urb->hcpriv = urb_priv;
first_ep = &TxIntrEPList[0];
@@ -2997,9 +2994,8 @@ static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid)
prev_sb_desc = next_sb_desc = temp_sb_desc = NULL;
- urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC);
+ urb_priv = kzalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC);
assert(urb_priv != NULL);
- memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
urb->hcpriv = urb_priv;
urb_priv->epid = epid;
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 972ce04889f..e99210b7909 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -724,7 +724,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
ep = hep->hcpriv;
else {
INIT_LIST_HEAD(&ep->schedule);
- ep->udev = usb_get_dev(udev);
+ ep->udev = udev;
ep->epnum = epnum;
ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
usb_settoggle(udev, epnum, is_out, 0);
@@ -891,7 +891,6 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd,
if (!list_empty(&hep->urb_list))
WARN("ep %p not empty?\n", ep);
- usb_put_dev(ep->udev);
kfree(ep);
hep->hcpriv = NULL;
}
@@ -1553,7 +1552,7 @@ static struct hc_driver isp116x_hc_driver = {
/*----------------------------------------------------------------*/
-static int __init_or_module isp116x_remove(struct platform_device *pdev)
+static int isp116x_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct isp116x *isp116x;
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
new file mode 100644
index 00000000000..980030d684d
--- /dev/null
+++ b/drivers/usb/host/ohci-at91.c
@@ -0,0 +1,306 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * Copyright (C) 2004 SAN People (Pty) Ltd.
+ * Copyright (C) 2005 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ * AT91RM9200 Bus Glue
+ *
+ * Based on fragments of 2.4 driver by Rick Bronson.
+ * Based on ohci-omap.c
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+#include <asm/arch/board.h>
+
+#ifndef CONFIG_ARCH_AT91RM9200
+#error "This file is AT91RM9200 bus glue. CONFIG_ARCH_AT91RM9200 must be defined."
+#endif
+
+/* interface and function clocks */
+static struct clk *iclk, *fclk;
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void at91_start_hc(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_regs __iomem *regs = hcd->regs;
+
+ dev_dbg(&pdev->dev, "starting AT91RM9200 OHCI USB Controller\n");
+
+ /*
+ * Start the USB clocks.
+ */
+ clk_enable(iclk);
+ clk_enable(fclk);
+
+ /*
+ * The USB host controller must remain in reset.
+ */
+ writel(0, &regs->control);
+}
+
+static void at91_stop_hc(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_regs __iomem *regs = hcd->regs;
+
+ dev_dbg(&pdev->dev, "stopping AT91RM9200 OHCI USB Controller\n");
+
+ /*
+ * Put the USB host controller into reset.
+ */
+ writel(0, &regs->control);
+
+ /*
+ * Stop the USB clocks.
+ */
+ clk_disable(fclk);
+ clk_disable(iclk);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_at91_probe - initialize AT91RM9200-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+int usb_hcd_at91_probe (const struct hc_driver *driver, struct platform_device *pdev)
+{
+ int retval;
+ struct usb_hcd *hcd = NULL;
+
+ if (pdev->num_resources != 2) {
+ pr_debug("hcd probe: invalid num_resources");
+ return -ENODEV;
+ }
+
+ if ((pdev->resource[0].flags != IORESOURCE_MEM) || (pdev->resource[1].flags != IORESOURCE_IRQ)) {
+ pr_debug("hcd probe: invalid resource type\n");
+ return -ENODEV;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, "at91rm9200");
+ if (!hcd)
+ return -ENOMEM;
+ hcd->rsrc_start = pdev->resource[0].start;
+ hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_debug("request_mem_region failed\n");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_debug("ioremap failed\n");
+ retval = -EIO;
+ goto err2;
+ }
+
+ iclk = clk_get(&pdev->dev, "ohci_clk");
+ fclk = clk_get(&pdev->dev, "uhpck");
+
+ at91_start_hc(pdev);
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ retval = usb_add_hcd(hcd, pdev->resource[1].start, SA_INTERRUPT);
+ if (retval == 0)
+ return retval;
+
+ /* Error handling */
+ at91_stop_hc(pdev);
+
+ clk_put(fclk);
+ clk_put(iclk);
+
+ iounmap(hcd->regs);
+
+ err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+ err1:
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_at91_remove - shutdown processing for AT91RM9200-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_at91_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+static int usb_hcd_at91_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+{
+ usb_remove_hcd(hcd);
+ at91_stop_hc(pdev);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+ clk_put(fclk);
+ clk_put(iclk);
+ fclk = iclk = NULL;
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_at91_start (struct usb_hcd *hcd)
+{
+// struct at91_ohci_data *board = hcd->self.controller->platform_data;
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run(ohci)) < 0) {
+ err("can't start %s", hcd->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+// hcd->self.root_hub->maxchild = board->ports;
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_at91_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "AT91RM9200 OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_at91_start,
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+
+#ifdef CONFIG_PM
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_at91_drv_probe(struct platform_device *dev)
+{
+ return usb_hcd_at91_probe(&ohci_at91_hc_driver, dev);
+}
+
+static int ohci_hcd_at91_drv_remove(struct platform_device *dev)
+{
+ return usb_hcd_at91_remove(platform_get_drvdata(dev), dev);
+}
+
+#ifdef CONFIG_PM
+static int ohci_hcd_at91_drv_suspend(struct platform_device *dev, u32 state, u32 level)
+{
+ printk("%s(%s:%d): not implemented yet\n",
+ __func__, __FILE__, __LINE__);
+
+ clk_disable(fclk);
+
+ return 0;
+}
+
+static int ohci_hcd_at91_drv_resume(struct platform_device *dev, u32 state)
+{
+ printk("%s(%s:%d): not implemented yet\n",
+ __func__, __FILE__, __LINE__);
+
+ clk_enable(fclk);
+
+ return 0;
+}
+#else
+#define ohci_hcd_at91_drv_suspend NULL
+#define ohci_hcd_at91_drv_resume NULL
+#endif
+
+static struct platform_driver ohci_hcd_at91_driver = {
+ .probe = ohci_hcd_at91_drv_probe,
+ .remove = ohci_hcd_at91_drv_remove,
+ .suspend = ohci_hcd_at91_drv_suspend,
+ .resume = ohci_hcd_at91_drv_resume,
+ .driver = {
+ .name = "at91rm9200-ohci",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ohci_hcd_at91_init (void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return platform_driver_register(&ohci_hcd_at91_driver);
+}
+
+static void __exit ohci_hcd_at91_cleanup (void)
+{
+ platform_driver_unregister(&ohci_hcd_at91_driver);
+}
+
+module_init (ohci_hcd_at91_init);
+module_exit (ohci_hcd_at91_cleanup);
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index db280ca7b7a..a1c8b3b2fcc 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -23,6 +23,8 @@
#include <asm/mach-au1x00/au1000.h>
+#ifndef CONFIG_SOC_AU1200
+
#define USBH_ENABLE_BE (1<<0)
#define USBH_ENABLE_C (1<<1)
#define USBH_ENABLE_E (1<<2)
@@ -37,21 +39,68 @@
#error not byte order defined
#endif
+#else /* Au1200 */
+
+#define USB_HOST_CONFIG (USB_MSR_BASE + USB_MSR_MCFG)
+#define USB_MCFG_PFEN (1<<31)
+#define USB_MCFG_RDCOMB (1<<30)
+#define USB_MCFG_SSDEN (1<<23)
+#define USB_MCFG_OHCCLKEN (1<<16)
+#define USB_MCFG_UCAM (1<<7)
+#define USB_MCFG_OBMEN (1<<1)
+#define USB_MCFG_OMEMEN (1<<0)
+
+#define USBH_ENABLE_CE USB_MCFG_OHCCLKEN
+#ifdef CONFIG_DMA_COHERENT
+#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \
+ | USB_MCFG_PFEN | USB_MCFG_RDCOMB \
+ | USB_MCFG_SSDEN | USB_MCFG_UCAM \
+ | USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
+#else
+#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \
+ | USB_MCFG_PFEN | USB_MCFG_RDCOMB \
+ | USB_MCFG_SSDEN \
+ | USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
+#endif
+#define USBH_DISABLE (USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
+
+#endif /* Au1200 */
+
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
-static void au1xxx_start_hc(struct platform_device *dev)
+static void au1xxx_start_ohc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": starting Au1xxx OHCI USB Controller\n");
/* enable host controller */
+
+#ifndef CONFIG_SOC_AU1200
+
au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
udelay(1000);
au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG);
udelay(1000);
+#else /* Au1200 */
+
+ /* write HW defaults again in case Yamon cleared them */
+ if (au_readl(USB_HOST_CONFIG) == 0) {
+ au_writel(0x00d02000, USB_HOST_CONFIG);
+ au_readl(USB_HOST_CONFIG);
+ udelay(1000);
+ }
+ au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+ au_readl(USB_HOST_CONFIG);
+ udelay(1000);
+ au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+ au_readl(USB_HOST_CONFIG);
+ udelay(1000);
+
+#endif /* Au1200 */
+
/* wait for reset complete (read register twice; see au1500 errata) */
while (au_readl(USB_HOST_CONFIG),
!(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
@@ -61,13 +110,25 @@ static void au1xxx_start_hc(struct platform_device *dev)
": Clock to USB host has been enabled \n");
}
-static void au1xxx_stop_hc(struct platform_device *dev)
+static void au1xxx_stop_ohc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": stopping Au1xxx OHCI USB Controller\n");
+#ifndef CONFIG_SOC_AU1200
+
/* Disable clock */
au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
+
+#else /* Au1200 */
+
+ /* Disable mem */
+ au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+ udelay(1000);
+ /* Disable clock */
+ au_writel(~USBH_ENABLE_CE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
+ au_readl(USB_HOST_CONFIG);
+#endif /* Au1200 */
}
@@ -78,7 +139,7 @@ static void au1xxx_stop_hc(struct platform_device *dev)
/**
- * usb_hcd_au1xxx_probe - initialize Au1xxx-based HCDs
+ * usb_ohci_au1xxx_probe - initialize Au1xxx-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
@@ -86,14 +147,25 @@ static void au1xxx_stop_hc(struct platform_device *dev)
* through the hotplug entry's driver_data.
*
*/
-int usb_hcd_au1xxx_probe (const struct hc_driver *driver,
+static int usb_ohci_au1xxx_probe(const struct hc_driver *driver,
struct platform_device *dev)
{
int retval;
struct usb_hcd *hcd;
- if(dev->resource[1].flags != IORESOURCE_IRQ) {
- pr_debug ("resource[1] is not IORESOURCE_IRQ");
+#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
+ /* Au1200 AB USB does not support coherent memory */
+ if (!(read_c0_prid() & 0xff)) {
+ pr_info("%s: this is chip revision AB !!\n",
+ dev->dev.name);
+ pr_info("%s: update your board or re-configure the kernel\n",
+ dev->dev.name);
+ return -ENODEV;
+ }
+#endif
+
+ if (dev->resource[1].flags != IORESOURCE_IRQ) {
+ pr_debug("resource[1] is not IORESOURCE_IRQ\n");
return -ENOMEM;
}
@@ -104,26 +176,26 @@ int usb_hcd_au1xxx_probe (const struct hc_driver *driver,
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- pr_debug("request_mem_region failed");
+ pr_debug("request_mem_region failed\n");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
- pr_debug("ioremap failed");
+ pr_debug("ioremap failed\n");
retval = -ENOMEM;
goto err2;
}
- au1xxx_start_hc(dev);
+ au1xxx_start_ohc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
+ retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT | SA_SHIRQ);
if (retval == 0)
return retval;
- au1xxx_stop_hc(dev);
+ au1xxx_stop_ohc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
@@ -146,10 +218,10 @@ int usb_hcd_au1xxx_probe (const struct hc_driver *driver,
* context, normally "rmmod", "apmd", or something similar.
*
*/
-void usb_hcd_au1xxx_remove (struct usb_hcd *hcd, struct platform_device *dev)
+static void usb_ohci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
- au1xxx_stop_hc(dev);
+ au1xxx_stop_ohc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
@@ -235,7 +307,7 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
if (usb_disabled())
return -ENODEV;
- ret = usb_hcd_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev);
+ ret = usb_ohci_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev);
return ret;
}
@@ -243,7 +315,7 @@ static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
- usb_hcd_au1xxx_remove(hcd, pdev);
+ usb_ohci_au1xxx_remove(hcd, pdev);
return 0;
}
/*TBD*/
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index a4b12404ae0..544f7589912 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -443,11 +443,16 @@ ohci_reboot (struct notifier_block *block, unsigned long code, void *null)
static int ohci_init (struct ohci_hcd *ohci)
{
int ret;
+ struct usb_hcd *hcd = ohci_to_hcd(ohci);
disable (ohci);
- ohci->regs = ohci_to_hcd(ohci)->regs;
+ ohci->regs = hcd->regs;
ohci->next_statechange = jiffies;
+ /* REVISIT this BIOS handshake is now moved into PCI "quirks", and
+ * was never needed for most non-PCI systems ... remove the code?
+ */
+
#ifndef IR_DISABLE
/* SMM owns the HC? not for long! */
if (!no_handshake && ohci_readl (ohci,
@@ -478,8 +483,10 @@ static int ohci_init (struct ohci_hcd *ohci)
/* Disable HC interrupts */
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
- // flush the writes
- (void) ohci_readl (ohci, &ohci->regs->control);
+
+ /* flush the writes, and save key bits like RWC */
+ if (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_RWC)
+ ohci->hc_control |= OHCI_CTRL_RWC;
/* Read the number of ports unless overridden */
if (ohci->num_ports == 0)
@@ -488,16 +495,19 @@ static int ohci_init (struct ohci_hcd *ohci)
if (ohci->hcca)
return 0;
- ohci->hcca = dma_alloc_coherent (ohci_to_hcd(ohci)->self.controller,
+ ohci->hcca = dma_alloc_coherent (hcd->self.controller,
sizeof *ohci->hcca, &ohci->hcca_dma, 0);
if (!ohci->hcca)
return -ENOMEM;
if ((ret = ohci_mem_init (ohci)) < 0)
- ohci_stop (ohci_to_hcd(ohci));
+ ohci_stop (hcd);
+ else {
+ register_reboot_notifier (&ohci->reboot_notifier);
+ create_debug_files (ohci);
+ }
return ret;
-
}
/*-------------------------------------------------------------------------*/
@@ -510,6 +520,7 @@ static int ohci_run (struct ohci_hcd *ohci)
{
u32 mask, temp;
int first = ohci->fminterval == 0;
+ struct usb_hcd *hcd = ohci_to_hcd(ohci);
disable (ohci);
@@ -525,18 +536,17 @@ static int ohci_run (struct ohci_hcd *ohci)
/* also: power/overcurrent flags in roothub.a */
}
- /* Reset USB nearly "by the book". RemoteWakeupConnected
- * saved if boot firmware (BIOS/SMM/...) told us it's connected
- * (for OHCI integrated on mainboard, it normally is)
+ /* Reset USB nearly "by the book". RemoteWakeupConnected was
+ * saved if boot firmware (BIOS/SMM/...) told us it's connected,
+ * or if bus glue did the same (e.g. for PCI add-in cards with
+ * PCI PM support).
*/
- ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
- ohci->hc_control);
-
- if (ohci->hc_control & OHCI_CTRL_RWC
- && !(ohci->flags & OHCI_QUIRK_AMD756))
- ohci_to_hcd(ohci)->can_wakeup = 1;
+ ohci_readl (ohci, &ohci->regs->control));
+ if ((ohci->hc_control & OHCI_CTRL_RWC) != 0
+ && !device_may_wakeup(hcd->self.controller))
+ device_init_wakeup(hcd->self.controller, 1);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_OPER:
@@ -632,7 +642,7 @@ retry:
ohci->hc_control &= OHCI_CTRL_RWC;
ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
- ohci_to_hcd(ohci)->state = HC_STATE_RUNNING;
+ hcd->state = HC_STATE_RUNNING;
/* wake on ConnectStatusChange, matching external hubs */
ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);
@@ -667,15 +677,10 @@ retry:
// POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((temp >> 23) & 0x1fe);
- ohci_to_hcd(ohci)->state = HC_STATE_RUNNING;
+ hcd->state = HC_STATE_RUNNING;
ohci_dump (ohci, 1);
- if (ohci_to_hcd(ohci)->self.root_hub == NULL) {
- register_reboot_notifier (&ohci->reboot_notifier);
- create_debug_files (ohci);
- }
-
return 0;
}
@@ -905,6 +910,10 @@ MODULE_LICENSE ("GPL");
#include "ohci-ppc-soc.c"
#endif
+#ifdef CONFIG_ARCH_AT91RM9200
+#include "ohci-at91.c"
+#endif
+
#if !(defined(CONFIG_PCI) \
|| defined(CONFIG_SA1111) \
|| defined(CONFIG_ARCH_S3C2410) \
@@ -913,6 +922,7 @@ MODULE_LICENSE ("GPL");
|| defined (CONFIG_PXA27x) \
|| defined (CONFIG_SOC_AU1X00) \
|| defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
+ || defined (CONFIG_ARCH_AT91RM9200) \
)
#error "missing bus glue for ohci-hcd"
#endif
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 4b2226d77b3..0bb972b5833 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -107,7 +107,7 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
&ohci->regs->intrstatus);
/* maybe resume can wake root hub */
- if (hcd->remote_wakeup)
+ if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev))
ohci->hc_control |= OHCI_CTRL_RWE;
else
ohci->hc_control &= ~OHCI_CTRL_RWE;
@@ -246,9 +246,9 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
(void) ohci_readl (ohci, &ohci->regs->control);
msleep (3);
- temp = OHCI_CONTROL_INIT | OHCI_USB_OPER;
- if (hcd->can_wakeup)
- temp |= OHCI_CTRL_RWC;
+ temp = ohci->hc_control;
+ temp &= OHCI_CTRL_RWC;
+ temp |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
ohci->hc_control = temp;
ohci_writel (ohci, temp, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
@@ -302,7 +302,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int i, changed = 0, length = 1;
- int can_suspend = hcd->can_wakeup;
+ int can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
@@ -354,7 +354,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
*/
if (!(status & RH_PS_CCS))
continue;
- if ((status & RH_PS_PSS) && hcd->remote_wakeup)
+ if ((status & RH_PS_PSS) && can_suspend)
continue;
can_suspend = 0;
}
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 3785b3f7df1..ca19abe01c5 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -286,7 +286,7 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
int usb_hcd_omap_probe (const struct hc_driver *driver,
struct platform_device *pdev)
{
- int retval;
+ int retval, irq;
struct usb_hcd *hcd = 0;
struct ohci_hcd *ohci;
@@ -329,7 +329,12 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
if (retval < 0)
goto err2;
- retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), SA_INTERRUPT);
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ retval = -ENXIO;
+ goto err2;
+ }
+ retval = usb_add_hcd(hcd, irq, SA_INTERRUPT);
if (retval == 0)
return retval;
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 1b09dde068e..1bfe96f4d04 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -35,7 +35,10 @@ ohci_pci_start (struct usb_hcd *hcd)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
- if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) {
+ /* REVISIT this whole block should move to reset(), which handles
+ * all the other one-time init.
+ */
+ if (hcd->self.controller) {
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
/* AMD 756, for most chips (early revs), corrupts register
@@ -45,7 +48,8 @@ ohci_pci_start (struct usb_hcd *hcd)
&& pdev->device == 0x740c) {
ohci->flags = OHCI_QUIRK_AMD756;
ohci_dbg (ohci, "AMD756 erratum 4 workaround\n");
- // also somewhat erratum 10 (suspend/resume issues)
+ /* also erratum 10 (suspend/resume issues) */
+ device_init_wakeup(&hcd->self.root_hub->dev, 0);
}
/* FIXME for some of the early AMD 760 southbridges, OHCI
@@ -88,6 +92,13 @@ ohci_pci_start (struct usb_hcd *hcd)
ohci_dbg (ohci,
"enabled Compaq ZFMicro chipset quirk\n");
}
+
+ /* RWC may not be set for add-in PCI cards, since boot
+ * firmware probably ignored them. This transfers PCI
+ * PM wakeup capabilities (once the PCI layer is fixed).
+ */
+ if (device_may_wakeup(&pdev->dev))
+ ohci->hc_control |= OHCI_CTRL_RWC;
}
/* NOTE: there may have already been a first reset, to
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 517360b77d8..a9234305275 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -853,7 +853,7 @@ static int sl811h_urb_enqueue(
} else {
INIT_LIST_HEAD(&ep->schedule);
- ep->udev = usb_get_dev(udev);
+ ep->udev = udev;
ep->epnum = epnum;
ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE;
@@ -1052,7 +1052,6 @@ sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
if (!list_empty(&hep->urb_list))
WARN("ep %p not empty?\n", ep);
- usb_put_dev(ep->udev);
kfree(ep);
hep->hcpriv = NULL;
}
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index 5832953086f..e1239319655 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -17,10 +17,13 @@
#include "uhci-hcd.h"
-static struct dentry *uhci_debugfs_root = NULL;
+#define uhci_debug_operations (* (struct file_operations *) NULL)
+static struct dentry *uhci_debugfs_root;
+
+#ifdef DEBUG
/* Handle REALLY large printks so we don't overflow buffers */
-static inline void lprintk(char *buf)
+static void lprintk(char *buf)
{
char *p;
@@ -90,13 +93,59 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
return out - buf;
}
-static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
+static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
{
char *out = buf;
- struct urb_priv *urbp;
- struct list_head *head, *tmp;
struct uhci_td *td;
- int i = 0, checked = 0, prevactive = 0;
+ int i, nactive, ninactive;
+
+ if (len < 200)
+ return 0;
+
+ out += sprintf(out, "urb_priv [%p] ", urbp);
+ out += sprintf(out, "urb [%p] ", urbp->urb);
+ out += sprintf(out, "qh [%p] ", urbp->qh);
+ out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
+ out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe),
+ (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
+
+ switch (usb_pipetype(urbp->urb->pipe)) {
+ case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO"); break;
+ case PIPE_INTERRUPT: out += sprintf(out, "INT"); break;
+ case PIPE_BULK: out += sprintf(out, "BLK"); break;
+ case PIPE_CONTROL: out += sprintf(out, "CTL"); break;
+ }
+
+ out += sprintf(out, "%s", (urbp->fsbr ? " FSBR" : ""));
+
+ if (urbp->urb->status != -EINPROGRESS)
+ out += sprintf(out, " Status=%d", urbp->urb->status);
+ out += sprintf(out, "\n");
+
+ i = nactive = ninactive = 0;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ if (++i <= 10 || debug > 2) {
+ out += sprintf(out, "%*s%d: ", space + 2, "", i);
+ out += uhci_show_td(td, out, len - (out - buf), 0);
+ } else {
+ if (td_status(td) & TD_CTRL_ACTIVE)
+ ++nactive;
+ else
+ ++ninactive;
+ }
+ }
+ if (nactive + ninactive > 0)
+ out += sprintf(out, "%*s[skipped %d inactive and %d active "
+ "TDs]\n",
+ space, "", ninactive, nactive);
+
+ return out - buf;
+}
+
+static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
+{
+ char *out = buf;
+ int i, nurbs;
__le32 element = qh_element(qh);
/* Try to make sure there's enough memory */
@@ -118,86 +167,40 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
out += sprintf(out, "%*s Element is NULL (bug?)\n", space, "");
- if (!qh->urbp) {
- out += sprintf(out, "%*s urbp == NULL\n", space, "");
- goto out;
- }
-
- urbp = qh->urbp;
-
- head = &urbp->td_list;
- tmp = head->next;
-
- td = list_entry(tmp, struct uhci_td, list);
-
- if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
- out += sprintf(out, "%*s Element != First TD\n", space, "");
-
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
- out += sprintf(out, "%*s%d: ", space + 2, "", i++);
- out += uhci_show_td(td, out, len - (out - buf), 0);
-
- if (i > 10 && !checked && prevactive && tmp != head &&
- debug <= 2) {
- struct list_head *ntmp = tmp;
- struct uhci_td *ntd = td;
- int active = 1, ni = i;
-
- checked = 1;
-
- while (ntmp != head && ntmp->next != head && active) {
- ntd = list_entry(ntmp, struct uhci_td, list);
-
- ntmp = ntmp->next;
-
- active = td_status(ntd) & TD_CTRL_ACTIVE;
-
- ni++;
- }
-
- if (active && ni > i) {
- out += sprintf(out, "%*s[skipped %d active TDs]\n", space, "", ni - i);
- tmp = ntmp;
- td = ntd;
- i = ni;
- }
+ if (list_empty(&qh->queue)) {
+ out += sprintf(out, "%*s queue is empty\n", space, "");
+ } else {
+ struct urb_priv *urbp = list_entry(qh->queue.next,
+ struct urb_priv, node);
+ struct uhci_td *td = list_entry(urbp->td_list.next,
+ struct uhci_td, list);
+
+ if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
+ out += sprintf(out, "%*s Element != First TD\n",
+ space, "");
+ i = nurbs = 0;
+ list_for_each_entry(urbp, &qh->queue, node) {
+ if (++i <= 10)
+ out += uhci_show_urbp(urbp, out,
+ len - (out - buf), space + 2);
+ else
+ ++nurbs;
}
-
- prevactive = td_status(td) & TD_CTRL_ACTIVE;
+ if (nurbs > 0)
+ out += sprintf(out, "%*s Skipped %d URBs\n",
+ space, "", nurbs);
}
- if (list_empty(&urbp->queue_list) || urbp->queued)
- goto out;
-
- out += sprintf(out, "%*sQueued QHs:\n", -space, "--");
-
- head = &urbp->queue_list;
- tmp = head->next;
-
- while (tmp != head) {
- struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
- queue_list);
- tmp = tmp->next;
-
- out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
+ if (qh->udev) {
+ out += sprintf(out, "%*s Dummy TD\n", space, "");
+ out += uhci_show_td(qh->dummy_td, out, len - (out - buf), 0);
}
-out:
return out - buf;
}
-#define show_frame_num() \
- if (!shown) { \
- shown = 1; \
- out += sprintf(out, "- Frame %d\n", i); \
- }
-
-#ifdef CONFIG_PROC_FS
static const char * const qh_names[] = {
+ "skel_unlink_qh", "skel_iso_qh",
"skel_int128_qh", "skel_int64_qh",
"skel_int32_qh", "skel_int16_qh",
"skel_int8_qh", "skel_int4_qh",
@@ -206,12 +209,6 @@ static const char * const qh_names[] = {
"skel_bulk_qh", "skel_term_qh"
};
-#define show_qh_name() \
- if (!shown) { \
- shown = 1; \
- out += sprintf(out, "- %s\n", qh_names[i]); \
- }
-
static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
{
char *out = buf;
@@ -321,139 +318,29 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
return out - buf;
}
-static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *buf, int len)
-{
- struct list_head *tmp;
- char *out = buf;
- int count = 0;
-
- if (len < 200)
- return 0;
-
- out += sprintf(out, "urb_priv [%p] ", urbp);
- out += sprintf(out, "urb [%p] ", urbp->urb);
- out += sprintf(out, "qh [%p] ", urbp->qh);
- out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
- out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
-
- switch (usb_pipetype(urbp->urb->pipe)) {
- case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO "); break;
- case PIPE_INTERRUPT: out += sprintf(out, "INT "); break;
- case PIPE_BULK: out += sprintf(out, "BLK "); break;
- case PIPE_CONTROL: out += sprintf(out, "CTL "); break;
- }
-
- out += sprintf(out, "%s", (urbp->fsbr ? "FSBR " : ""));
- out += sprintf(out, "%s", (urbp->fsbr_timeout ? "FSBR_TO " : ""));
-
- if (urbp->urb->status != -EINPROGRESS)
- out += sprintf(out, "Status=%d ", urbp->urb->status);
- //out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime);
-
- count = 0;
- list_for_each(tmp, &urbp->td_list)
- count++;
- out += sprintf(out, "TDs=%d ",count);
-
- if (urbp->queued)
- out += sprintf(out, "queued\n");
- else {
- count = 0;
- list_for_each(tmp, &urbp->queue_list)
- count++;
- out += sprintf(out, "queued URBs=%d\n", count);
- }
-
- return out - buf;
-}
-
-static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
-{
- char *out = buf;
- struct list_head *head, *tmp;
- int count;
-
- out += sprintf(out, "Main list URBs:");
- if (list_empty(&uhci->urb_list))
- out += sprintf(out, " Empty\n");
- else {
- out += sprintf(out, "\n");
- count = 0;
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
-
- out += sprintf(out, " %d: ", ++count);
- out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
- tmp = tmp->next;
- }
- }
-
- out += sprintf(out, "Remove list URBs:");
- if (list_empty(&uhci->urb_remove_list))
- out += sprintf(out, " Empty\n");
- else {
- out += sprintf(out, "\n");
- count = 0;
- head = &uhci->urb_remove_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
-
- out += sprintf(out, " %d: ", ++count);
- out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
- tmp = tmp->next;
- }
- }
-
- out += sprintf(out, "Complete list URBs:");
- if (list_empty(&uhci->complete_list))
- out += sprintf(out, " Empty\n");
- else {
- out += sprintf(out, "\n");
- count = 0;
- head = &uhci->complete_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
-
- out += sprintf(out, " %d: ", ++count);
- out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
- tmp = tmp->next;
- }
- }
-
- return out - buf;
-}
-
static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
{
- unsigned long flags;
char *out = buf;
int i, j;
struct uhci_qh *qh;
struct uhci_td *td;
struct list_head *tmp, *head;
- spin_lock_irqsave(&uhci->lock, flags);
-
out += uhci_show_root_hub_state(uhci, out, len - (out - buf));
out += sprintf(out, "HC status\n");
out += uhci_show_status(uhci, out, len - (out - buf));
+ if (debug <= 1)
+ return out - buf;
out += sprintf(out, "Frame List\n");
for (i = 0; i < UHCI_NUMFRAMES; ++i) {
- int shown = 0;
td = uhci->frame_cpu[i];
if (!td)
continue;
- if (td->dma_handle != (dma_addr_t)uhci->frame[i]) {
- show_frame_num();
+ out += sprintf(out, "- Frame %d\n", i); \
+ if (td->dma_handle != (dma_addr_t)uhci->frame[i])
out += sprintf(out, " frame list does not match td->dma_handle!\n");
- }
- show_frame_num();
head = &td->fl_list;
tmp = head;
@@ -467,14 +354,11 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
out += sprintf(out, "Skeleton QHs\n");
for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
- int shown = 0;
+ int cnt = 0;
qh = uhci->skelqh[i];
-
- if (debug > 1) {
- show_qh_name();
- out += uhci_show_qh(qh, out, len - (out - buf), 4);
- }
+ out += sprintf(out, "- %s\n", qh_names[i]); \
+ out += uhci_show_qh(qh, out, len - (out - buf), 4);
/* Last QH is the Terminating QH, it's different */
if (i == UHCI_NUM_SKELQH - 1) {
@@ -487,53 +371,37 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
continue;
}
- j = (i < 7) ? 7 : i+1; /* Next skeleton */
- if (list_empty(&qh->list)) {
- if (i < UHCI_NUM_SKELQH - 1) {
- if (qh->link !=
- (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) {
- show_qh_name();
- out += sprintf(out, " skeleton QH not linked to next skeleton QH!\n");
- }
- }
-
- continue;
- }
-
- show_qh_name();
-
- head = &qh->list;
+ j = (i < 9) ? 9 : i+1; /* Next skeleton */
+ head = &qh->node;
tmp = head->next;
while (tmp != head) {
- qh = list_entry(tmp, struct uhci_qh, list);
-
+ qh = list_entry(tmp, struct uhci_qh, node);
tmp = tmp->next;
-
- out += uhci_show_qh(qh, out, len - (out - buf), 4);
+ if (++cnt <= 10)
+ out += uhci_show_qh(qh, out,
+ len - (out - buf), 4);
}
+ if ((cnt -= 10) > 0)
+ out += sprintf(out, " Skipped %d QHs\n", cnt);
- if (i < UHCI_NUM_SKELQH - 1) {
+ if (i > 1 && i < UHCI_NUM_SKELQH - 1) {
if (qh->link !=
(cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH))
out += sprintf(out, " last QH not linked to next skeleton!\n");
}
}
- if (debug > 2)
- out += uhci_show_lists(uhci, out, len - (out - buf));
-
- spin_unlock_irqrestore(&uhci->lock, flags);
-
return out - buf;
}
+#ifdef CONFIG_DEBUG_FS
+
#define MAX_OUTPUT (64 * 1024)
struct uhci_debug {
int size;
char *data;
- struct uhci_hcd *uhci;
};
static int uhci_debug_open(struct inode *inode, struct file *file)
@@ -541,6 +409,7 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
struct uhci_hcd *uhci = inode->u.generic_ip;
struct uhci_debug *up;
int ret = -ENOMEM;
+ unsigned long flags;
lock_kernel();
up = kmalloc(sizeof(*up), GFP_KERNEL);
@@ -553,7 +422,11 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
goto out;
}
- up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
+ up->size = 0;
+ spin_lock_irqsave(&uhci->lock, flags);
+ if (uhci->is_initialized)
+ up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
+ spin_unlock_irqrestore(&uhci->lock, flags);
file->private_data = up;
@@ -604,15 +477,32 @@ static int uhci_debug_release(struct inode *inode, struct file *file)
return 0;
}
+#undef uhci_debug_operations
static struct file_operations uhci_debug_operations = {
+ .owner = THIS_MODULE,
.open = uhci_debug_open,
.llseek = uhci_debug_lseek,
.read = uhci_debug_read,
.release = uhci_debug_release,
};
-#else /* CONFIG_DEBUG_FS */
+#endif /* CONFIG_DEBUG_FS */
-#define uhci_debug_operations (* (struct file_operations *) NULL)
+#else /* DEBUG */
+
+static inline void lprintk(char *buf)
+{}
+
+static inline int uhci_show_qh(struct uhci_qh *qh, char *buf,
+ int len, int space)
+{
+ return 0;
+}
+
+static inline int uhci_sprint_schedule(struct uhci_hcd *uhci,
+ char *buf, int len)
+{
+ return 0;
+}
#endif
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index dfe121d3588..4edb8330c44 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -54,7 +54,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v2.3"
+#define DRIVER_VERSION "v3.0"
#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \
Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \
Alan Stern"
@@ -68,12 +68,16 @@ Alan Stern"
* debug = 3, show all TDs in URBs when dumping
*/
#ifdef DEBUG
+#define DEBUG_CONFIGURED 1
static int debug = 1;
-#else
-static int debug = 0;
-#endif
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level");
+
+#else
+#define DEBUG_CONFIGURED 0
+#define debug 0
+#endif
+
static char *errbuf;
#define ERRBUF_LEN (32 * 1024)
@@ -338,6 +342,12 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
dev_err(uhci_dev(uhci),
"host controller halted, "
"very bad!\n");
+ if (debug > 1 && errbuf) {
+ /* Print the schedule for debugging */
+ uhci_sprint_schedule(uhci,
+ errbuf, ERRBUF_LEN);
+ lprintk(errbuf);
+ }
hc_died(uhci);
/* Force a callback in case there are
@@ -376,6 +386,14 @@ static void release_uhci(struct uhci_hcd *uhci)
{
int i;
+ if (DEBUG_CONFIGURED) {
+ spin_lock_irq(&uhci->lock);
+ uhci->is_initialized = 0;
+ spin_unlock_irq(&uhci->lock);
+
+ debugfs_remove(uhci->dentry);
+ }
+
for (i = 0; i < UHCI_NUM_SKELQH; i++)
uhci_free_qh(uhci, uhci->skelqh[i]);
@@ -390,8 +408,6 @@ static void release_uhci(struct uhci_hcd *uhci)
dma_free_coherent(uhci_dev(uhci),
UHCI_NUMFRAMES * sizeof(*uhci->frame),
uhci->frame, uhci->frame_dma_handle);
-
- debugfs_remove(uhci->dentry);
}
static int uhci_reset(struct usb_hcd *hcd)
@@ -474,33 +490,29 @@ static int uhci_start(struct usb_hcd *hcd)
hcd->uses_new_polling = 1;
- dentry = debugfs_create_file(hcd->self.bus_name,
- S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci,
- &uhci_debug_operations);
- if (!dentry) {
- dev_err(uhci_dev(uhci),
- "couldn't create uhci debugfs entry\n");
- retval = -ENOMEM;
- goto err_create_debug_entry;
- }
- uhci->dentry = dentry;
-
uhci->fsbr = 0;
uhci->fsbrtimeout = 0;
spin_lock_init(&uhci->lock);
- INIT_LIST_HEAD(&uhci->qh_remove_list);
INIT_LIST_HEAD(&uhci->td_remove_list);
-
- INIT_LIST_HEAD(&uhci->urb_remove_list);
-
- INIT_LIST_HEAD(&uhci->urb_list);
-
- INIT_LIST_HEAD(&uhci->complete_list);
+ INIT_LIST_HEAD(&uhci->idle_qh_list);
init_waitqueue_head(&uhci->waitqh);
+ if (DEBUG_CONFIGURED) {
+ dentry = debugfs_create_file(hcd->self.bus_name,
+ S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
+ uhci, &uhci_debug_operations);
+ if (!dentry) {
+ dev_err(uhci_dev(uhci), "couldn't create uhci "
+ "debugfs entry\n");
+ retval = -ENOMEM;
+ goto err_create_debug_entry;
+ }
+ uhci->dentry = dentry;
+ }
+
uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
UHCI_NUMFRAMES * sizeof(*uhci->frame),
&uhci->frame_dma_handle, 0);
@@ -540,7 +552,7 @@ static int uhci_start(struct usb_hcd *hcd)
}
for (i = 0; i < UHCI_NUM_SKELQH; i++) {
- uhci->skelqh[i] = uhci_alloc_qh(uhci);
+ uhci->skelqh[i] = uhci_alloc_qh(uhci, NULL, NULL);
if (!uhci->skelqh[i]) {
dev_err(uhci_dev(uhci), "unable to allocate QH\n");
goto err_alloc_skelqh;
@@ -557,13 +569,17 @@ static int uhci_start(struct usb_hcd *hcd)
uhci->skel_int16_qh->link =
uhci->skel_int8_qh->link =
uhci->skel_int4_qh->link =
- uhci->skel_int2_qh->link =
- cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH;
- uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH;
-
- uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
- uhci->skel_fs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
- uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
+ uhci->skel_int2_qh->link = UHCI_PTR_QH |
+ cpu_to_le32(uhci->skel_int1_qh->dma_handle);
+
+ uhci->skel_int1_qh->link = UHCI_PTR_QH |
+ cpu_to_le32(uhci->skel_ls_control_qh->dma_handle);
+ uhci->skel_ls_control_qh->link = UHCI_PTR_QH |
+ cpu_to_le32(uhci->skel_fs_control_qh->dma_handle);
+ uhci->skel_fs_control_qh->link = UHCI_PTR_QH |
+ cpu_to_le32(uhci->skel_bulk_qh->dma_handle);
+ uhci->skel_bulk_qh->link = UHCI_PTR_QH |
+ cpu_to_le32(uhci->skel_term_qh->dma_handle);
/* This dummy TD is to work around a bug in Intel PIIX controllers */
uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
@@ -589,15 +605,15 @@ static int uhci_start(struct usb_hcd *hcd)
/*
* ffs (Find First bit Set) does exactly what we need:
- * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[6],
- * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[5], etc.
- * ffs > 6 => not on any high-period queue, so use
- * skel_int1_qh = skelqh[7].
+ * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8],
+ * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc.
+ * ffs >= 7 => not on any high-period queue, so use
+ * skel_int1_qh = skelqh[9].
* Add UHCI_NUMFRAMES to insure at least one bit is set.
*/
- irq = 6 - (int) __ffs(i + UHCI_NUMFRAMES);
- if (irq < 0)
- irq = 7;
+ irq = 8 - (int) __ffs(i + UHCI_NUMFRAMES);
+ if (irq <= 1)
+ irq = 9;
/* Only place we don't use the frame list routines */
uhci->frame[i] = UHCI_PTR_QH |
@@ -611,6 +627,7 @@ static int uhci_start(struct usb_hcd *hcd)
mb();
configure_hc(uhci);
+ uhci->is_initialized = 1;
start_rh(uhci);
return 0;
@@ -767,13 +784,30 @@ static int uhci_resume(struct usb_hcd *hcd)
}
#endif
-/* Wait until all the URBs for a particular device/endpoint are gone */
+/* Wait until a particular device/endpoint's QH is idle, and free it */
static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
- struct usb_host_endpoint *ep)
+ struct usb_host_endpoint *hep)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ struct uhci_qh *qh;
+
+ spin_lock_irq(&uhci->lock);
+ qh = (struct uhci_qh *) hep->hcpriv;
+ if (qh == NULL)
+ goto done;
+
+ while (qh->state != QH_STATE_IDLE) {
+ ++uhci->num_waiting;
+ spin_unlock_irq(&uhci->lock);
+ wait_event_interruptible(uhci->waitqh,
+ qh->state == QH_STATE_IDLE);
+ spin_lock_irq(&uhci->lock);
+ --uhci->num_waiting;
+ }
- wait_event_interruptible(uhci->waitqh, list_empty(&ep->urb_list));
+ uhci_free_qh(uhci, qh);
+done:
+ spin_unlock_irq(&uhci->lock);
}
static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
@@ -857,16 +891,15 @@ static int __init uhci_hcd_init(void)
if (usb_disabled())
return -ENODEV;
- if (debug) {
+ if (DEBUG_CONFIGURED) {
errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
if (!errbuf)
goto errbuf_failed;
+ uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
+ if (!uhci_debugfs_root)
+ goto debug_failed;
}
- uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
- if (!uhci_debugfs_root)
- goto debug_failed;
-
uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
sizeof(struct urb_priv), 0, 0, NULL, NULL);
if (!uhci_up_cachep)
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index 8b4b887a7d4..4a69c7eb09b 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -28,8 +28,9 @@
#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
#define USBSTS_RD 0x0004 /* Resume Detect */
-#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */
-#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */
+#define USBSTS_HSE 0x0008 /* Host System Error: PCI problems */
+#define USBSTS_HCPE 0x0010 /* Host Controller Process Error:
+ * the schedule is buggy */
#define USBSTS_HCH 0x0020 /* HC Halted */
/* Interrupt enable register */
@@ -47,7 +48,8 @@
/* USB port status and control registers */
#define USBPORTSC1 16
#define USBPORTSC2 18
-#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */
+#define USBPORTSC_CCS 0x0001 /* Current Connect Status
+ * ("device present") */
#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
#define USBPORTSC_PE 0x0004 /* Port Enable */
#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
@@ -71,15 +73,16 @@
#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
#define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
-#define UHCI_PTR_BITS cpu_to_le32(0x000F)
-#define UHCI_PTR_TERM cpu_to_le32(0x0001)
-#define UHCI_PTR_QH cpu_to_le32(0x0002)
-#define UHCI_PTR_DEPTH cpu_to_le32(0x0004)
-#define UHCI_PTR_BREADTH cpu_to_le32(0x0000)
+#define UHCI_PTR_BITS __constant_cpu_to_le32(0x000F)
+#define UHCI_PTR_TERM __constant_cpu_to_le32(0x0001)
+#define UHCI_PTR_QH __constant_cpu_to_le32(0x0002)
+#define UHCI_PTR_DEPTH __constant_cpu_to_le32(0x0004)
+#define UHCI_PTR_BREADTH __constant_cpu_to_le32(0x0000)
#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
-#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
+#define CAN_SCHEDULE_FRAMES 1000 /* how far in the future frames
+ * can be scheduled */
/*
@@ -87,38 +90,59 @@
*/
/*
- * One role of a QH is to hold a queue of TDs for some endpoint. Each QH is
- * used with one URB, and qh->element (updated by the HC) is either:
- * - the next unprocessed TD for the URB, or
- * - UHCI_PTR_TERM (when there's no more traffic for this endpoint), or
- * - the QH for the next URB queued to the same endpoint.
+ * One role of a QH is to hold a queue of TDs for some endpoint. One QH goes
+ * with each endpoint, and qh->element (updated by the HC) is either:
+ * - the next unprocessed TD in the endpoint's queue, or
+ * - UHCI_PTR_TERM (when there's no more traffic for this endpoint).
*
* The other role of a QH is to serve as a "skeleton" framelist entry, so we
* can easily splice a QH for some endpoint into the schedule at the right
* place. Then qh->element is UHCI_PTR_TERM.
*
- * In the frame list, qh->link maintains a list of QHs seen by the HC:
+ * In the schedule, qh->link maintains a list of QHs seen by the HC:
* skel1 --> ep1-qh --> ep2-qh --> ... --> skel2 --> ...
+ *
+ * qh->node is the software equivalent of qh->link. The differences
+ * are that the software list is doubly-linked and QHs in the UNLINKING
+ * state are on the software list but not the hardware schedule.
+ *
+ * For bookkeeping purposes we maintain QHs even for Isochronous endpoints,
+ * but they never get added to the hardware schedule.
*/
+#define QH_STATE_IDLE 1 /* QH is not being used */
+#define QH_STATE_UNLINKING 2 /* QH has been removed from the
+ * schedule but the hardware may
+ * still be using it */
+#define QH_STATE_ACTIVE 3 /* QH is on the schedule */
+
struct uhci_qh {
/* Hardware fields */
- __le32 link; /* Next queue */
- __le32 element; /* Queue element pointer */
+ __le32 link; /* Next QH in the schedule */
+ __le32 element; /* Queue element (TD) pointer */
/* Software fields */
dma_addr_t dma_handle;
- struct urb_priv *urbp;
+ struct list_head node; /* Node in the list of QHs */
+ struct usb_host_endpoint *hep; /* Endpoint information */
+ struct usb_device *udev;
+ struct list_head queue; /* Queue of urbps for this QH */
+ struct uhci_qh *skel; /* Skeleton for this QH */
+ struct uhci_td *dummy_td; /* Dummy TD to end the queue */
- struct list_head list;
- struct list_head remove_list;
+ unsigned int unlink_frame; /* When the QH was unlinked */
+ int state; /* QH_STATE_xxx; see above */
+
+ unsigned int initial_toggle:1; /* Endpoint's current toggle value */
+ unsigned int needs_fixup:1; /* Must fix the TD toggle values */
+ unsigned int is_stopped:1; /* Queue was stopped by an error */
} __attribute__((aligned(16)));
/*
* We need a special accessor for the element pointer because it is
* subject to asynchronous updates by the controller.
*/
-static __le32 inline qh_element(struct uhci_qh *qh) {
+static inline __le32 qh_element(struct uhci_qh *qh) {
__le32 element = qh->element;
barrier();
@@ -149,11 +173,13 @@ static __le32 inline qh_element(struct uhci_qh *qh) {
#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */
#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
- TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
+ TD_CTRL_BABBLE | TD_CTRL_CRCTIME | \
+ TD_CTRL_BITSTUFF)
#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT)
#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xF60000)
-#define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+#define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & \
+ TD_CTRL_ACTLEN_MASK) /* 1-based */
/*
* for TD <info>: (a.k.a. Token)
@@ -163,7 +189,7 @@ static __le32 inline qh_element(struct uhci_qh *qh) {
#define TD_TOKEN_TOGGLE_SHIFT 19
#define TD_TOKEN_TOGGLE (1 << 19)
#define TD_TOKEN_EXPLEN_SHIFT 21
-#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */
+#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n-1 */
#define TD_TOKEN_PID_MASK 0xFF
#define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << \
@@ -187,7 +213,7 @@ static __le32 inline qh_element(struct uhci_qh *qh) {
* sw space after the TD entry.
*
* td->link points to either another TD (not necessarily for the same urb or
- * even the same endpoint), or nothing (PTR_TERM), or a QH (for queued urbs).
+ * even the same endpoint), or nothing (PTR_TERM), or a QH.
*/
struct uhci_td {
/* Hardware fields */
@@ -210,7 +236,7 @@ struct uhci_td {
* We need a special accessor for the control/status word because it is
* subject to asynchronous updates by the controller.
*/
-static u32 inline td_status(struct uhci_td *td) {
+static inline u32 td_status(struct uhci_td *td) {
__le32 status = td->status;
barrier();
@@ -223,17 +249,14 @@ static u32 inline td_status(struct uhci_td *td) {
*/
/*
- * The UHCI driver places Interrupt, Control and Bulk into QHs both
- * to group together TDs for one transfer, and also to facilitate queuing
- * of URBs. To make it easy to insert entries into the schedule, we have
- * a skeleton of QHs for each predefined Interrupt latency, low-speed
- * control, full-speed control and terminating QH (see explanation for
- * the terminating QH below).
+ * The UHCI driver uses QHs with Interrupt, Control and Bulk URBs for
+ * automatic queuing. To make it easy to insert entries into the schedule,
+ * we have a skeleton of QHs for each predefined Interrupt latency,
+ * low-speed control, full-speed control, bulk, and terminating QH
+ * (see explanation for the terminating QH below).
*
* When we want to add a new QH, we add it to the end of the list for the
- * skeleton QH.
- *
- * For instance, the queue can look like this:
+ * skeleton QH. For instance, the schedule list can look like this:
*
* skel int128 QH
* dev 1 interrupt QH
@@ -256,26 +279,31 @@ static u32 inline td_status(struct uhci_td *td) {
* - To loop back to the full-speed control queue for full-speed bandwidth
* reclamation.
*
- * Isochronous transfers are stored before the start of the skeleton
- * schedule and don't use QHs. While the UHCI spec doesn't forbid the
- * use of QHs for Isochronous, it doesn't use them either. And the spec
- * says that queues never advance on an error completion status, which
- * makes them totally unsuitable for Isochronous transfers.
+ * There's a special skeleton QH for Isochronous QHs. It never appears
+ * on the schedule, and Isochronous TDs go on the schedule before the
+ * the skeleton QHs. The hardware accesses them directly rather than
+ * through their QH, which is used only for bookkeeping purposes.
+ * While the UHCI spec doesn't forbid the use of QHs for Isochronous,
+ * it doesn't use them either. And the spec says that queues never
+ * advance on an error completion status, which makes them totally
+ * unsuitable for Isochronous transfers.
*/
-#define UHCI_NUM_SKELQH 12
-#define skel_int128_qh skelqh[0]
-#define skel_int64_qh skelqh[1]
-#define skel_int32_qh skelqh[2]
-#define skel_int16_qh skelqh[3]
-#define skel_int8_qh skelqh[4]
-#define skel_int4_qh skelqh[5]
-#define skel_int2_qh skelqh[6]
-#define skel_int1_qh skelqh[7]
-#define skel_ls_control_qh skelqh[8]
-#define skel_fs_control_qh skelqh[9]
-#define skel_bulk_qh skelqh[10]
-#define skel_term_qh skelqh[11]
+#define UHCI_NUM_SKELQH 14
+#define skel_unlink_qh skelqh[0]
+#define skel_iso_qh skelqh[1]
+#define skel_int128_qh skelqh[2]
+#define skel_int64_qh skelqh[3]
+#define skel_int32_qh skelqh[4]
+#define skel_int16_qh skelqh[5]
+#define skel_int8_qh skelqh[6]
+#define skel_int4_qh skelqh[7]
+#define skel_int2_qh skelqh[8]
+#define skel_int1_qh skelqh[9]
+#define skel_ls_control_qh skelqh[10]
+#define skel_fs_control_qh skelqh[11]
+#define skel_bulk_qh skelqh[12]
+#define skel_term_qh skelqh[13]
/*
* Search tree for determining where <interval> fits in the skelqh[]
@@ -293,21 +321,21 @@ static inline int __interval_to_skel(int interval)
if (interval < 16) {
if (interval < 4) {
if (interval < 2)
- return 7; /* int1 for 0-1 ms */
- return 6; /* int2 for 2-3 ms */
+ return 9; /* int1 for 0-1 ms */
+ return 8; /* int2 for 2-3 ms */
}
if (interval < 8)
- return 5; /* int4 for 4-7 ms */
- return 4; /* int8 for 8-15 ms */
+ return 7; /* int4 for 4-7 ms */
+ return 6; /* int8 for 8-15 ms */
}
if (interval < 64) {
if (interval < 32)
- return 3; /* int16 for 16-31 ms */
- return 2; /* int32 for 32-63 ms */
+ return 5; /* int16 for 16-31 ms */
+ return 4; /* int32 for 32-63 ms */
}
if (interval < 128)
- return 1; /* int64 for 64-127 ms */
- return 0; /* int128 for 128-255 ms (Max.) */
+ return 3; /* int64 for 64-127 ms */
+ return 2; /* int128 for 128-255 ms (Max.) */
}
@@ -360,15 +388,16 @@ struct uhci_hcd {
struct uhci_td *term_td; /* Terminating TD, see UHCI bug */
struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QHs */
+ struct uhci_qh *next_qh; /* Next QH to scan */
spinlock_t lock;
- dma_addr_t frame_dma_handle; /* Hardware frame list */
+ dma_addr_t frame_dma_handle; /* Hardware frame list */
__le32 *frame;
- void **frame_cpu; /* CPU's frame list */
+ void **frame_cpu; /* CPU's frame list */
- int fsbr; /* Full-speed bandwidth reclamation */
- unsigned long fsbrtimeout; /* FSBR delay */
+ int fsbr; /* Full-speed bandwidth reclamation */
+ unsigned long fsbrtimeout; /* FSBR delay */
enum uhci_rh_state rh_state;
unsigned long auto_stop_time; /* When to AUTO_STOP */
@@ -382,6 +411,7 @@ struct uhci_hcd {
unsigned int hc_inaccessible:1; /* HC is suspended or dead */
unsigned int working_RD:1; /* Suspended root hub doesn't
need to be polled */
+ unsigned int is_initialized:1; /* Data structure is usable */
/* Support for port suspend/resume/reset */
unsigned long port_c_suspend; /* Bit-arrays of ports */
@@ -389,27 +419,16 @@ struct uhci_hcd {
unsigned long resuming_ports;
unsigned long ports_timeout; /* Time to stop signalling */
- /* Main list of URBs currently controlled by this HC */
- struct list_head urb_list;
-
- /* List of QHs that are done, but waiting to be unlinked (race) */
- struct list_head qh_remove_list;
- unsigned int qh_remove_age; /* Age in frames */
-
/* List of TDs that are done, but waiting to be freed (race) */
struct list_head td_remove_list;
unsigned int td_remove_age; /* Age in frames */
- /* List of asynchronously unlinked URBs */
- struct list_head urb_remove_list;
- unsigned int urb_remove_age; /* Age in frames */
-
- /* List of URBs awaiting completion callback */
- struct list_head complete_list;
+ struct list_head idle_qh_list; /* Where the idle QHs live */
int rh_numports; /* Number of root-hub ports */
wait_queue_head_t waitqh; /* endpoint_disable waiters */
+ int num_waiting; /* Number of waiters */
};
/* Convert between a usb_hcd pointer and the corresponding uhci_hcd */
@@ -429,7 +448,7 @@ static inline struct usb_hcd *uhci_to_hcd(struct uhci_hcd *uhci)
* Private per-URB data
*/
struct urb_priv {
- struct list_head urb_list;
+ struct list_head node; /* Node in the QH's urbp list */
struct urb *urb;
@@ -437,15 +456,8 @@ struct urb_priv {
struct list_head td_list;
unsigned fsbr : 1; /* URB turned on FSBR */
- unsigned fsbr_timeout : 1; /* URB timed out on FSBR */
- unsigned queued : 1; /* QH was queued (not linked in) */
- unsigned short_control_packet : 1; /* If we get a short packet during */
- /* a control transfer, retrigger */
- /* the status phase */
-
- unsigned long fsbrtime; /* In jiffies */
-
- struct list_head queue_list;
+ unsigned short_transfer : 1; /* URB got a short transfer, no
+ * need to rescan */
};
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index a71e48a6680..152971d1676 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -99,6 +99,21 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
}
}
+/* Wait for the UHCI controller in HP's iLO2 server management chip.
+ * It can take up to 250 us to finish a reset and set the CSC bit.
+ */
+static void wait_for_HP(unsigned long port_addr)
+{
+ int i;
+
+ for (i = 10; i < 250; i += 10) {
+ if (inw(port_addr) & USBPORTSC_CSC)
+ return;
+ udelay(10);
+ }
+ /* Log a warning? */
+}
+
static void uhci_check_ports(struct uhci_hcd *uhci)
{
unsigned int port;
@@ -113,6 +128,12 @@ static void uhci_check_ports(struct uhci_hcd *uhci)
CLR_RH_PORTSTAT(USBPORTSC_PR);
udelay(10);
+ /* HP's server management chip requires
+ * a longer delay. */
+ if (to_pci_dev(uhci_dev(uhci))->vendor ==
+ PCI_VENDOR_ID_HP)
+ wait_for_HP(port_addr);
+
/* If the port was enabled before, turning
* reset on caused a port enable change.
* Turning reset off causes a port connect
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 782398045f9..a06d84c19e1 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -13,13 +13,9 @@
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
+ * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu
*/
-static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
-static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb);
-static void uhci_remove_pending_urbps(struct uhci_hcd *uhci);
-static void uhci_free_pending_qhs(struct uhci_hcd *uhci);
static void uhci_free_pending_tds(struct uhci_hcd *uhci);
/*
@@ -30,7 +26,7 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci);
* games with the FSBR code to make sure we get the correct order in all
* the cases. I don't think it's worth the effort
*/
-static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
+static void uhci_set_next_interrupt(struct uhci_hcd *uhci)
{
if (uhci->is_stopped)
mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
@@ -42,12 +38,6 @@ static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
}
-static inline void uhci_moveto_complete(struct uhci_hcd *uhci,
- struct urb_priv *urbp)
-{
- list_move_tail(&urbp->urb_list, &uhci->complete_list);
-}
-
static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
{
dma_addr_t dma_handle;
@@ -58,10 +48,6 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
return NULL;
td->dma_handle = dma_handle;
-
- td->link = UHCI_PTR_TERM;
- td->buffer = 0;
-
td->frame = -1;
INIT_LIST_HEAD(&td->list);
@@ -71,6 +57,18 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
return td;
}
+static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+ if (!list_empty(&td->list))
+ dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
+ if (!list_empty(&td->remove_list))
+ dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td);
+ if (!list_empty(&td->fl_list))
+ dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+
+ dma_pool_free(uhci->td_pool, td, td->dma_handle);
+}
+
static inline void uhci_fill_td(struct uhci_td *td, u32 status,
u32 token, u32 buffer)
{
@@ -82,7 +80,8 @@ static inline void uhci_fill_td(struct uhci_td *td, u32 status,
/*
* We insert Isochronous URBs directly into the frame list at the beginning
*/
-static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
+static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci,
+ struct uhci_td *td, unsigned framenum)
{
framenum &= (UHCI_NUMFRAMES - 1);
@@ -108,7 +107,7 @@ static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td,
}
}
-static inline void uhci_remove_td_frame_list(struct uhci_hcd *uhci,
+static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
struct uhci_td *td)
{
/* If it's not inserted, don't remove it */
@@ -139,48 +138,21 @@ static inline void uhci_remove_td_frame_list(struct uhci_hcd *uhci,
td->frame = -1;
}
-static void unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb)
+/*
+ * Remove all the TDs for an Isochronous URB from the frame list
+ */
+static void uhci_unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
struct uhci_td *td;
list_for_each_entry(td, &urbp->td_list, list)
- uhci_remove_td_frame_list(uhci, td);
+ uhci_remove_td_from_frame_list(uhci, td);
wmb();
}
-/*
- * Inserts a td list into qh.
- */
-static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth)
-{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td;
- __le32 *plink;
-
- /* Ordering isn't important here yet since the QH hasn't been */
- /* inserted into the schedule yet */
- plink = &qh->element;
- list_for_each_entry(td, &urbp->td_list, list) {
- *plink = cpu_to_le32(td->dma_handle) | breadth;
- plink = &td->link;
- }
- *plink = UHCI_PTR_TERM;
-}
-
-static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
-{
- if (!list_empty(&td->list))
- dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
- if (!list_empty(&td->remove_list))
- dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td);
- if (!list_empty(&td->fl_list))
- dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
-
- dma_pool_free(uhci->td_pool, td, td->dma_handle);
-}
-
-static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci)
+static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
+ struct usb_device *udev, struct usb_host_endpoint *hep)
{
dma_addr_t dma_handle;
struct uhci_qh *qh;
@@ -194,256 +166,217 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci)
qh->element = UHCI_PTR_TERM;
qh->link = UHCI_PTR_TERM;
- qh->urbp = NULL;
-
- INIT_LIST_HEAD(&qh->list);
- INIT_LIST_HEAD(&qh->remove_list);
+ INIT_LIST_HEAD(&qh->queue);
+ INIT_LIST_HEAD(&qh->node);
+ if (udev) { /* Normal QH */
+ qh->dummy_td = uhci_alloc_td(uhci);
+ if (!qh->dummy_td) {
+ dma_pool_free(uhci->qh_pool, qh, dma_handle);
+ return NULL;
+ }
+ qh->state = QH_STATE_IDLE;
+ qh->hep = hep;
+ qh->udev = udev;
+ hep->hcpriv = qh;
+
+ } else { /* Skeleton QH */
+ qh->state = QH_STATE_ACTIVE;
+ qh->udev = NULL;
+ }
return qh;
}
static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
- if (!list_empty(&qh->list))
+ WARN_ON(qh->state != QH_STATE_IDLE && qh->udev);
+ if (!list_empty(&qh->queue))
dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
- if (!list_empty(&qh->remove_list))
- dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh);
+ list_del(&qh->node);
+ if (qh->udev) {
+ qh->hep->hcpriv = NULL;
+ uhci_free_td(uhci, qh->dummy_td);
+ }
dma_pool_free(uhci->qh_pool, qh, qh->dma_handle);
}
/*
- * Append this urb's qh after the last qh in skelqh->list
- *
- * Note that urb_priv.queue_list doesn't have a separate queue head;
- * it's a ring with every element "live".
+ * When the currently executing URB is dequeued, save its current toggle value
*/
-static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
+static void uhci_save_toggle(struct uhci_qh *qh, struct urb *urb)
{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct urb_priv *turbp;
- struct uhci_qh *lqh;
+ struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+ struct uhci_td *td;
- /* Grab the last QH */
- lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
+ /* If the QH element pointer is UHCI_PTR_TERM then then currently
+ * executing URB has already been unlinked, so this one isn't it. */
+ if (qh_element(qh) == UHCI_PTR_TERM ||
+ qh->queue.next != &urbp->node)
+ return;
+ qh->element = UHCI_PTR_TERM;
- /* Point to the next skelqh */
- urbp->qh->link = lqh->link;
- wmb(); /* Ordering is important */
+ /* Only bulk and interrupt pipes have to worry about toggles */
+ if (!(usb_pipetype(urb->pipe) == PIPE_BULK ||
+ usb_pipetype(urb->pipe) == PIPE_INTERRUPT))
+ return;
- /*
- * Patch QHs for previous endpoint's queued URBs? HC goes
- * here next, not to the next skelqh it now points to.
- *
- * lqh --> td ... --> qh ... --> td --> qh ... --> td
- * | | |
- * v v v
- * +<----------------+-----------------+
- * v
- * newqh --> td ... --> td
- * |
- * v
- * ...
- *
- * The HC could see (and use!) any of these as we write them.
- */
- lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
- if (lqh->urbp) {
- list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list)
- turbp->qh->link = lqh->link;
+ /* Find the first active TD; that's the device's toggle state */
+ list_for_each_entry(td, &urbp->td_list, list) {
+ if (td_status(td) & TD_CTRL_ACTIVE) {
+ qh->needs_fixup = 1;
+ qh->initial_toggle = uhci_toggle(td_token(td));
+ return;
+ }
}
- list_add_tail(&urbp->qh->list, &skelqh->list);
+ WARN_ON(1);
}
/*
- * Start removal of QH from schedule; it finishes next frame.
- * TDs should be unlinked before this is called.
+ * Fix up the data toggles for URBs in a queue, when one of them
+ * terminates early (short transfer, error, or dequeued).
*/
-static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first)
{
- struct uhci_qh *pqh;
- __le32 newlink;
-
- if (!qh)
- return;
-
- /*
- * Only go through the hoops if it's actually linked in
- */
- if (!list_empty(&qh->list)) {
-
- /* If our queue is nonempty, make the next URB the head */
- if (!list_empty(&qh->urbp->queue_list)) {
- struct urb_priv *nurbp;
-
- nurbp = list_entry(qh->urbp->queue_list.next,
- struct urb_priv, queue_list);
- nurbp->queued = 0;
- list_add(&nurbp->qh->list, &qh->list);
- newlink = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
- } else
- newlink = qh->link;
-
- /* Fix up the previous QH's queue to link to either
- * the new head of this queue or the start of the
- * next endpoint's queue. */
- pqh = list_entry(qh->list.prev, struct uhci_qh, list);
- pqh->link = newlink;
- if (pqh->urbp) {
- struct urb_priv *turbp;
-
- list_for_each_entry(turbp, &pqh->urbp->queue_list,
- queue_list)
- turbp->qh->link = newlink;
+ struct urb_priv *urbp = NULL;
+ struct uhci_td *td;
+ unsigned int toggle = qh->initial_toggle;
+ unsigned int pipe;
+
+ /* Fixups for a short transfer start with the second URB in the
+ * queue (the short URB is the first). */
+ if (skip_first)
+ urbp = list_entry(qh->queue.next, struct urb_priv, node);
+
+ /* When starting with the first URB, if the QH element pointer is
+ * still valid then we know the URB's toggles are okay. */
+ else if (qh_element(qh) != UHCI_PTR_TERM)
+ toggle = 2;
+
+ /* Fix up the toggle for the URBs in the queue. Normally this
+ * loop won't run more than once: When an error or short transfer
+ * occurs, the queue usually gets emptied. */
+ urbp = list_prepare_entry(urbp, &qh->queue, node);
+ list_for_each_entry_continue(urbp, &qh->queue, node) {
+
+ /* If the first TD has the right toggle value, we don't
+ * need to change any toggles in this URB */
+ td = list_entry(urbp->td_list.next, struct uhci_td, list);
+ if (toggle > 1 || uhci_toggle(td_token(td)) == toggle) {
+ td = list_entry(urbp->td_list.next, struct uhci_td,
+ list);
+ toggle = uhci_toggle(td_token(td)) ^ 1;
+
+ /* Otherwise all the toggles in the URB have to be switched */
+ } else {
+ list_for_each_entry(td, &urbp->td_list, list) {
+ td->token ^= __constant_cpu_to_le32(
+ TD_TOKEN_TOGGLE);
+ toggle ^= 1;
+ }
}
- wmb();
-
- /* Leave qh->link in case the HC is on the QH now, it will */
- /* continue the rest of the schedule */
- qh->element = UHCI_PTR_TERM;
-
- list_del_init(&qh->list);
- }
-
- list_del_init(&qh->urbp->queue_list);
- qh->urbp = NULL;
-
- uhci_get_current_frame_number(uhci);
- if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) {
- uhci_free_pending_qhs(uhci);
- uhci->qh_remove_age = uhci->frame_number;
}
- /* Check to see if the remove list is empty. Set the IOC bit */
- /* to force an interrupt so we can remove the QH */
- if (list_empty(&uhci->qh_remove_list))
- uhci_set_next_interrupt(uhci);
-
- list_add(&qh->remove_list, &uhci->qh_remove_list);
+ wmb();
+ pipe = list_entry(qh->queue.next, struct urb_priv, node)->urb->pipe;
+ usb_settoggle(qh->udev, usb_pipeendpoint(pipe),
+ usb_pipeout(pipe), toggle);
+ qh->needs_fixup = 0;
}
-static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
+/*
+ * Put a QH on the schedule in both hardware and software
+ */
+static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td;
-
- list_for_each_entry(td, &urbp->td_list, list) {
- if (toggle)
- td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
- else
- td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
-
- toggle ^= 1;
- }
-
- return toggle;
-}
+ struct uhci_qh *pqh;
-/* This function will append one URB's QH to another URB's QH. This is for */
-/* queuing interrupt, control or bulk transfers */
-static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
-{
- struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
- struct uhci_td *lltd;
+ WARN_ON(list_empty(&qh->queue));
- eurbp = eurb->hcpriv;
- urbp = urb->hcpriv;
+ /* Set the element pointer if it isn't set already.
+ * This isn't needed for Isochronous queues, but it doesn't hurt. */
+ if (qh_element(qh) == UHCI_PTR_TERM) {
+ struct urb_priv *urbp = list_entry(qh->queue.next,
+ struct urb_priv, node);
+ struct uhci_td *td = list_entry(urbp->td_list.next,
+ struct uhci_td, list);
- /* Find the first URB in the queue */
- furbp = eurbp;
- if (eurbp->queued) {
- list_for_each_entry(furbp, &eurbp->queue_list, queue_list)
- if (!furbp->queued)
- break;
+ qh->element = cpu_to_le32(td->dma_handle);
}
- lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
-
- lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
-
- /* Control transfers always start with toggle 0 */
- if (!usb_pipecontrol(urb->pipe))
- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe),
- uhci_fixup_toggle(urb,
- uhci_toggle(td_token(lltd)) ^ 1));
-
- /* All qhs in the queue need to link to the next queue */
- urbp->qh->link = eurbp->qh->link;
-
- wmb(); /* Make sure we flush everything */
-
- lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
-
- list_add_tail(&urbp->queue_list, &furbp->queue_list);
-
- urbp->queued = 1;
+ if (qh->state == QH_STATE_ACTIVE)
+ return;
+ qh->state = QH_STATE_ACTIVE;
+
+ /* Move the QH from its old list to the end of the appropriate
+ * skeleton's list */
+ if (qh == uhci->next_qh)
+ uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
+ node);
+ list_move_tail(&qh->node, &qh->skel->node);
+
+ /* Link it into the schedule */
+ pqh = list_entry(qh->node.prev, struct uhci_qh, node);
+ qh->link = pqh->link;
+ wmb();
+ pqh->link = UHCI_PTR_QH | cpu_to_le32(qh->dma_handle);
}
-static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
+/*
+ * Take a QH off the hardware schedule
+ */
+static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
- struct urb_priv *urbp, *nurbp, *purbp, *turbp;
- struct uhci_td *pltd;
- unsigned int toggle;
-
- urbp = urb->hcpriv;
+ struct uhci_qh *pqh;
- if (list_empty(&urbp->queue_list))
+ if (qh->state == QH_STATE_UNLINKING)
return;
+ WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev);
+ qh->state = QH_STATE_UNLINKING;
- nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
+ /* Unlink the QH from the schedule and record when we did it */
+ pqh = list_entry(qh->node.prev, struct uhci_qh, node);
+ pqh->link = qh->link;
+ mb();
- /*
- * Fix up the toggle for the following URBs in the queue.
- * Only needed for bulk and interrupt: control and isochronous
- * endpoints don't propagate toggles between messages.
- */
- if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) {
- if (!urbp->queued)
- /* We just set the toggle in uhci_unlink_generic */
- toggle = usb_gettoggle(urb->dev,
- usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe));
- else {
- /* If we're in the middle of the queue, grab the */
- /* toggle from the TD previous to us */
- purbp = list_entry(urbp->queue_list.prev,
- struct urb_priv, queue_list);
- pltd = list_entry(purbp->td_list.prev,
- struct uhci_td, list);
- toggle = uhci_toggle(td_token(pltd)) ^ 1;
- }
+ uhci_get_current_frame_number(uhci);
+ qh->unlink_frame = uhci->frame_number;
- list_for_each_entry(turbp, &urbp->queue_list, queue_list) {
- if (!turbp->queued)
- break;
- toggle = uhci_fixup_toggle(turbp->urb, toggle);
- }
+ /* Force an interrupt so we know when the QH is fully unlinked */
+ if (list_empty(&uhci->skel_unlink_qh->node))
+ uhci_set_next_interrupt(uhci);
- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe), toggle);
- }
+ /* Move the QH from its old list to the end of the unlinking list */
+ if (qh == uhci->next_qh)
+ uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
+ node);
+ list_move_tail(&qh->node, &uhci->skel_unlink_qh->node);
+}
- if (urbp->queued) {
- /* We're somewhere in the middle (or end). The case where
- * we're at the head is handled in uhci_remove_qh(). */
- purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
- queue_list);
+/*
+ * When we and the controller are through with a QH, it becomes IDLE.
+ * This happens when a QH has been off the schedule (on the unlinking
+ * list) for more than one frame, or when an error occurs while adding
+ * the first URB onto a new QH.
+ */
+static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ WARN_ON(qh->state == QH_STATE_ACTIVE);
- pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
- if (nurbp->queued)
- pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
- else
- /* The next URB happens to be the beginning, so */
- /* we're the last, end the chain */
- pltd->link = UHCI_PTR_TERM;
- }
+ if (qh == uhci->next_qh)
+ uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
+ node);
+ list_move(&qh->node, &uhci->idle_qh_list);
+ qh->state = QH_STATE_IDLE;
- /* urbp->queue_list is handled in uhci_remove_qh() */
+ /* If anyone is waiting for a QH to become idle, wake them up */
+ if (uhci->num_waiting)
+ wake_up_all(&uhci->waitqh);
}
-static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+static inline struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci,
+ struct urb *urb)
{
struct urb_priv *urbp;
@@ -453,16 +386,11 @@ static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *u
memset((void *)urbp, 0, sizeof(*urbp));
- urbp->fsbrtime = jiffies;
urbp->urb = urb;
+ urb->hcpriv = urbp;
+ INIT_LIST_HEAD(&urbp->node);
INIT_LIST_HEAD(&urbp->td_list);
- INIT_LIST_HEAD(&urbp->queue_list);
- INIT_LIST_HEAD(&urbp->urb_list);
-
- list_add_tail(&urbp->urb_list, &uhci->urb_list);
-
- urb->hcpriv = urbp;
return urbp;
}
@@ -482,18 +410,14 @@ static void uhci_remove_td_from_urb(struct uhci_td *td)
list_del_init(&td->list);
}
-static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+static void uhci_free_urb_priv(struct uhci_hcd *uhci,
+ struct urb_priv *urbp)
{
struct uhci_td *td, *tmp;
- struct urb_priv *urbp;
-
- urbp = (struct urb_priv *)urb->hcpriv;
- if (!urbp)
- return;
- if (!list_empty(&urbp->urb_list))
- dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list "
- "or uhci->remove_list!\n", urb);
+ if (!list_empty(&urbp->node))
+ dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n",
+ urbp->urb);
uhci_get_current_frame_number(uhci);
if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) {
@@ -502,7 +426,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
}
/* Check to see if the remove list is empty. Set the IOC bit */
- /* to force an interrupt so we can remove the TDs*/
+ /* to force an interrupt so we can remove the TDs. */
if (list_empty(&uhci->td_remove_list))
uhci_set_next_interrupt(uhci);
@@ -511,7 +435,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
list_add(&td->remove_list, &uhci->td_remove_list);
}
- urb->hcpriv = NULL;
+ urbp->urb->hcpriv = NULL;
kmem_cache_free(uhci_up_cachep, urbp);
}
@@ -570,34 +494,33 @@ static int uhci_map_status(int status, int dir_out)
/*
* Control transfers
*/
-static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci_td *td;
- struct uhci_qh *qh, *skelqh;
unsigned long destination, status;
- int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+ int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize);
int len = urb->transfer_buffer_length;
dma_addr_t data = urb->transfer_dma;
+ __le32 *plink;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
- /* 3 errors */
- status = TD_CTRL_ACTIVE | uhci_maxerr(3);
+ /* 3 errors, dummy TD remains inactive */
+ status = uhci_maxerr(3);
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
/*
* Build the TD for the control request setup packet
*/
- td = uhci_alloc_td(uhci);
- if (!td)
- return -ENOMEM;
-
+ td = qh->dummy_td;
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | uhci_explen(8),
- urb->setup_dma);
+ urb->setup_dma);
+ plink = &td->link;
+ status |= TD_CTRL_ACTIVE;
/*
* If direction is "send", change the packet ID from SETUP (0x2D)
@@ -615,21 +538,20 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
* Build the DATA TDs
*/
while (len > 0) {
- int pktsze = len;
-
- if (pktsze > maxsze)
- pktsze = maxsze;
+ int pktsze = min(len, maxsze);
td = uhci_alloc_td(uhci);
if (!td)
- return -ENOMEM;
+ goto nomem;
+ *plink = cpu_to_le32(td->dma_handle);
/* Alternate Data0/1 (start with Data1) */
destination ^= TD_TOKEN_TOGGLE;
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | uhci_explen(pktsze),
- data);
+ data);
+ plink = &td->link;
data += pktsze;
len -= pktsze;
@@ -640,7 +562,8 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
*/
td = uhci_alloc_td(uhci);
if (!td)
- return -ENOMEM;
+ goto nomem;
+ *plink = cpu_to_le32(td->dma_handle);
/*
* It's IN if the pipe is an output pipe or we're not expecting
@@ -658,16 +581,21 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status | TD_CTRL_IOC,
- destination | uhci_explen(0), 0);
-
- qh = uhci_alloc_qh(uhci);
- if (!qh)
- return -ENOMEM;
+ destination | uhci_explen(0), 0);
+ plink = &td->link;
- urbp->qh = qh;
- qh->urbp = urbp;
+ /*
+ * Build the new dummy TD and activate the old one
+ */
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ goto nomem;
+ *plink = cpu_to_le32(td->dma_handle);
- uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
+ uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
+ wmb();
+ qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE);
+ qh->dummy_td = td;
/* Low-speed transfers get a different queue, and won't hog the bus.
* Also, some devices enumerate better without FSBR; the easiest way
@@ -675,18 +603,17 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
* isn't in the CONFIGURED state. */
if (urb->dev->speed == USB_SPEED_LOW ||
urb->dev->state != USB_STATE_CONFIGURED)
- skelqh = uhci->skel_ls_control_qh;
+ qh->skel = uhci->skel_ls_control_qh;
else {
- skelqh = uhci->skel_fs_control_qh;
+ qh->skel = uhci->skel_fs_control_qh;
uhci_inc_fsbr(uhci, urb);
}
+ return 0;
- if (eurb)
- uhci_append_queued_urb(uhci, eurb, urb);
- else
- uhci_insert_qh(uhci, skelqh, urb);
-
- return -EINPROGRESS;
+nomem:
+ /* Remove the dummy TD from the td_list so it doesn't get freed */
+ uhci_remove_td_from_urb(qh->dummy_td);
+ return -ENOMEM;
}
/*
@@ -703,7 +630,7 @@ static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb)
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci_td *td;
- urbp->short_control_packet = 1;
+ urbp->short_transfer = 1;
td = list_entry(urbp->td_list.prev, struct uhci_td, list);
urbp->qh->element = cpu_to_le32(td->dma_handle);
@@ -720,16 +647,14 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
unsigned int status;
int ret = 0;
- if (list_empty(&urbp->td_list))
- return -EINVAL;
-
head = &urbp->td_list;
-
- if (urbp->short_control_packet) {
+ if (urbp->short_transfer) {
tmp = head->prev;
goto status_stage;
}
+ urb->actual_length = 0;
+
tmp = head->next;
td = list_entry(tmp, struct uhci_td, list);
@@ -742,8 +667,6 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
if (status)
goto td_error;
- urb->actual_length = 0;
-
/* The rest of the TDs (but the last) are data */
tmp = tmp->next;
while (tmp != head && tmp->next != head) {
@@ -770,10 +693,7 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
goto err;
}
- if (uhci_packetid(td_token(td)) == USB_PID_IN)
- return usb_control_retrigger_status(uhci, urb);
- else
- return 0;
+ return usb_control_retrigger_status(uhci, urb);
}
}
@@ -814,34 +734,40 @@ err:
if (errbuf) {
/* Print the chain for debugging purposes */
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
-
lprintk(errbuf);
}
}
+ /* Note that the queue has stopped */
+ urbp->qh->element = UHCI_PTR_TERM;
+ urbp->qh->is_stopped = 1;
return ret;
}
/*
* Common submit for bulk and interrupt
*/
-static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh)
+static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
{
struct uhci_td *td;
- struct uhci_qh *qh;
unsigned long destination, status;
- int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+ int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize);
int len = urb->transfer_buffer_length;
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
dma_addr_t data = urb->transfer_dma;
+ __le32 *plink;
+ unsigned int toggle;
if (len < 0)
return -EINVAL;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+ toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe));
- status = uhci_maxerr(3) | TD_CTRL_ACTIVE;
+ /* 3 errors, dummy TD remains inactive */
+ status = uhci_maxerr(3);
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
if (usb_pipein(urb->pipe))
@@ -850,30 +776,34 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
/*
* Build the DATA TDs
*/
+ plink = NULL;
+ td = qh->dummy_td;
do { /* Allow zero length packets */
int pktsze = maxsze;
- if (pktsze >= len) {
+ if (len <= pktsze) { /* The last packet */
pktsze = len;
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
status &= ~TD_CTRL_SPD;
}
- td = uhci_alloc_td(uhci);
- if (!td)
- return -ENOMEM;
-
+ if (plink) {
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ goto nomem;
+ *plink = cpu_to_le32(td->dma_handle);
+ }
uhci_add_td_to_urb(urb, td);
- uhci_fill_td(td, status, destination | uhci_explen(pktsze) |
- (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
- data);
+ uhci_fill_td(td, status,
+ destination | uhci_explen(pktsze) |
+ (toggle << TD_TOKEN_TOGGLE_SHIFT),
+ data);
+ plink = &td->link;
+ status |= TD_CTRL_ACTIVE;
data += pktsze;
len -= maxsze;
-
- usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe));
+ toggle ^= 1;
} while (len > 0);
/*
@@ -883,20 +813,22 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
* however, if transfer_length == 0, the zero packet was already
* prepared above.
*/
- if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) &&
- !len && urb->transfer_buffer_length) {
+ if ((urb->transfer_flags & URB_ZERO_PACKET) &&
+ usb_pipeout(urb->pipe) && len == 0 &&
+ urb->transfer_buffer_length > 0) {
td = uhci_alloc_td(uhci);
if (!td)
- return -ENOMEM;
+ goto nomem;
+ *plink = cpu_to_le32(td->dma_handle);
uhci_add_td_to_urb(urb, td);
- uhci_fill_td(td, status, destination | uhci_explen(0) |
- (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
- data);
+ uhci_fill_td(td, status,
+ destination | uhci_explen(0) |
+ (toggle << TD_TOKEN_TOGGLE_SHIFT),
+ data);
+ plink = &td->link;
- usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe));
+ toggle ^= 1;
}
/* Set the interrupt-on-completion flag on the last packet.
@@ -905,24 +837,29 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
* fast side but not enough to justify delaying an interrupt
* more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT
* flag setting. */
- td->status |= cpu_to_le32(TD_CTRL_IOC);
+ td->status |= __constant_cpu_to_le32(TD_CTRL_IOC);
- qh = uhci_alloc_qh(uhci);
- if (!qh)
- return -ENOMEM;
-
- urbp->qh = qh;
- qh->urbp = urbp;
+ /*
+ * Build the new dummy TD and activate the old one
+ */
+ td = uhci_alloc_td(uhci);
+ if (!td)
+ goto nomem;
+ *plink = cpu_to_le32(td->dma_handle);
- /* Always breadth first */
- uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
+ uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
+ wmb();
+ qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE);
+ qh->dummy_td = td;
- if (eurb)
- uhci_append_queued_urb(uhci, eurb, urb);
- else
- uhci_insert_qh(uhci, skelqh, urb);
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), toggle);
+ return 0;
- return -EINPROGRESS;
+nomem:
+ /* Remove the dummy TD from the td_list so it doesn't get freed */
+ uhci_remove_td_from_urb(qh->dummy_td);
+ return -ENOMEM;
}
/*
@@ -954,8 +891,27 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
if (urb->transfer_flags & URB_SHORT_NOT_OK) {
ret = -EREMOTEIO;
goto err;
- } else
- return 0;
+ }
+
+ /*
+ * This URB stopped short of its end. We have to
+ * fix up the toggles of the following URBs on the
+ * queue and restart the queue.
+ *
+ * Do this only the first time we encounter the
+ * short URB.
+ */
+ if (!urbp->short_transfer) {
+ urbp->short_transfer = 1;
+ urbp->qh->initial_toggle =
+ uhci_toggle(td_token(td)) ^ 1;
+ uhci_fixup_toggles(urbp->qh, 1);
+
+ td = list_entry(urbp->td_list.prev,
+ struct uhci_td, list);
+ urbp->qh->element = td->link;
+ }
+ break;
}
}
@@ -964,31 +920,30 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
td_error:
ret = uhci_map_status(status, uhci_packetout(td_token(td)));
-err:
- /*
- * Enable this chunk of code if you want to see some more debugging.
- * But be careful, it has the tendancy to starve out khubd and prevent
- * disconnects from happening successfully if you have a slow debug
- * log interface (like a serial console.
- */
-#if 0
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
/* Some debugging code */
dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
__FUNCTION__, status);
- if (errbuf) {
+ if (debug > 1 && errbuf) {
/* Print the chain for debugging purposes */
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
-
lprintk(errbuf);
}
}
-#endif
+err:
+
+ /* Note that the queue has stopped and save the next toggle value */
+ urbp->qh->element = UHCI_PTR_TERM;
+ urbp->qh->is_stopped = 1;
+ urbp->qh->needs_fixup = 1;
+ urbp->qh->initial_toggle = uhci_toggle(td_token(td)) ^
+ (ret == -EREMOTEIO);
return ret;
}
-static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
{
int ret;
@@ -996,95 +951,60 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struc
if (urb->dev->speed == USB_SPEED_LOW)
return -EINVAL;
- ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh);
- if (ret == -EINPROGRESS)
+ qh->skel = uhci->skel_bulk_qh;
+ ret = uhci_submit_common(uhci, urb, qh);
+ if (ret == 0)
uhci_inc_fsbr(uhci, urb);
-
return ret;
}
-static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
{
- /* USB 1.1 interrupt transfers only involve one packet per interval;
- * that's the uhci_submit_common() "breadth first" policy. Drivers
- * can submit urbs of any length, but longer ones might need many
- * intervals to complete.
+ /* USB 1.1 interrupt transfers only involve one packet per interval.
+ * Drivers can submit URBs of any length, but longer ones will need
+ * multiple intervals to complete.
*/
- return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]);
+ qh->skel = uhci->skelqh[__interval_to_skel(urb->interval)];
+ return uhci_submit_common(uhci, urb, qh);
}
/*
* Isochronous transfers
*/
-static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
-{
- struct urb *last_urb = NULL;
- struct urb_priv *up;
- int ret = 0;
-
- list_for_each_entry(up, &uhci->urb_list, urb_list) {
- struct urb *u = up->urb;
-
- /* look for pending URBs with identical pipe handle */
- if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
- (u->status == -EINPROGRESS) && (u != urb)) {
- if (!last_urb)
- *start = u->start_frame;
- last_urb = u;
- }
- }
-
- if (last_urb) {
- *end = (last_urb->start_frame + last_urb->number_of_packets *
- last_urb->interval) & (UHCI_NUMFRAMES-1);
- ret = 0;
- } else
- ret = -1; /* no previous urb found */
-
- return ret;
-}
-
-static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb)
+static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
+ struct uhci_qh *qh)
{
- int limits;
- unsigned int start = 0, end = 0;
+ struct uhci_td *td = NULL; /* Since urb->number_of_packets > 0 */
+ int i, frame;
+ unsigned long destination, status;
+ struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
if (urb->number_of_packets > 900) /* 900? Why? */
return -EFBIG;
- limits = isochronous_find_limits(uhci, urb, &start, &end);
+ status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+ /* Figure out the starting frame number */
if (urb->transfer_flags & URB_ISO_ASAP) {
- if (limits) {
+ if (list_empty(&qh->queue)) {
uhci_get_current_frame_number(uhci);
- urb->start_frame = (uhci->frame_number + 10)
- & (UHCI_NUMFRAMES - 1);
- } else
- urb->start_frame = end;
+ urb->start_frame = (uhci->frame_number + 10);
+
+ } else { /* Go right after the last one */
+ struct urb *last_urb;
+
+ last_urb = list_entry(qh->queue.prev,
+ struct urb_priv, node)->urb;
+ urb->start_frame = (last_urb->start_frame +
+ last_urb->number_of_packets *
+ last_urb->interval);
+ }
} else {
- urb->start_frame &= (UHCI_NUMFRAMES - 1);
/* FIXME: Sanity check */
}
-
- return 0;
-}
-
-/*
- * Isochronous transfers
- */
-static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
-{
- struct uhci_td *td;
- int i, ret, frame;
- int status, destination;
- struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
-
- status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
- destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
-
- ret = isochronous_find_start(uhci, urb);
- if (ret)
- return ret;
+ urb->start_frame &= (UHCI_NUMFRAMES - 1);
for (i = 0; i < urb->number_of_packets; i++) {
td = uhci_alloc_td(uhci);
@@ -1092,20 +1012,25 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
return -ENOMEM;
uhci_add_td_to_urb(urb, td);
- uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length),
- urb->transfer_dma + urb->iso_frame_desc[i].offset);
-
- if (i + 1 >= urb->number_of_packets)
- td->status |= cpu_to_le32(TD_CTRL_IOC);
+ uhci_fill_td(td, status, destination |
+ uhci_explen(urb->iso_frame_desc[i].length),
+ urb->transfer_dma +
+ urb->iso_frame_desc[i].offset);
}
+ /* Set the interrupt-on-completion flag on the last packet. */
+ td->status |= __constant_cpu_to_le32(TD_CTRL_IOC);
+
+ qh->skel = uhci->skel_iso_qh;
+
+ /* Add the TDs to the frame list */
frame = urb->start_frame;
list_for_each_entry(td, &urbp->td_list, list) {
- uhci_insert_td_frame_list(uhci, td, frame);
+ uhci_insert_td_in_frame_list(uhci, td, frame);
frame += urb->interval;
}
- return -EINPROGRESS;
+ return 0;
}
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
@@ -1139,80 +1064,67 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
i++;
}
- unlink_isochronous_tds(uhci, urb);
return ret;
}
-static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
-{
- struct urb_priv *up;
-
- /* We don't match Isoc transfers since they are special */
- if (usb_pipeisoc(urb->pipe))
- return NULL;
-
- list_for_each_entry(up, &uhci->urb_list, urb_list) {
- struct urb *u = up->urb;
-
- if (u->dev == urb->dev && u->status == -EINPROGRESS) {
- /* For control, ignore the direction */
- if (usb_pipecontrol(urb->pipe) &&
- (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))
- return u;
- else if (u->pipe == urb->pipe)
- return u;
- }
- }
-
- return NULL;
-}
-
static int uhci_urb_enqueue(struct usb_hcd *hcd,
- struct usb_host_endpoint *ep,
+ struct usb_host_endpoint *hep,
struct urb *urb, gfp_t mem_flags)
{
int ret;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long flags;
- struct urb *eurb;
+ struct urb_priv *urbp;
+ struct uhci_qh *qh;
int bustime;
spin_lock_irqsave(&uhci->lock, flags);
ret = urb->status;
if (ret != -EINPROGRESS) /* URB already unlinked! */
- goto out;
+ goto done;
- eurb = uhci_find_urb_ep(uhci, urb);
+ ret = -ENOMEM;
+ urbp = uhci_alloc_urb_priv(uhci, urb);
+ if (!urbp)
+ goto done;
- if (!uhci_alloc_urb_priv(uhci, urb)) {
- ret = -ENOMEM;
- goto out;
+ if (hep->hcpriv)
+ qh = (struct uhci_qh *) hep->hcpriv;
+ else {
+ qh = uhci_alloc_qh(uhci, urb->dev, hep);
+ if (!qh)
+ goto err_no_qh;
}
+ urbp->qh = qh;
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
- ret = uhci_submit_control(uhci, urb, eurb);
+ ret = uhci_submit_control(uhci, urb, qh);
+ break;
+ case PIPE_BULK:
+ ret = uhci_submit_bulk(uhci, urb, qh);
break;
case PIPE_INTERRUPT:
- if (!eurb) {
+ if (list_empty(&qh->queue)) {
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0)
ret = bustime;
else {
- ret = uhci_submit_interrupt(uhci, urb, eurb);
- if (ret == -EINPROGRESS)
+ ret = uhci_submit_interrupt(uhci, urb, qh);
+ if (ret == 0)
usb_claim_bandwidth(urb->dev, urb, bustime, 0);
}
} else { /* inherit from parent */
- urb->bandwidth = eurb->bandwidth;
- ret = uhci_submit_interrupt(uhci, urb, eurb);
+ struct urb_priv *eurbp;
+
+ eurbp = list_entry(qh->queue.prev, struct urb_priv,
+ node);
+ urb->bandwidth = eurbp->urb->bandwidth;
+ ret = uhci_submit_interrupt(uhci, urb, qh);
}
break;
- case PIPE_BULK:
- ret = uhci_submit_bulk(uhci, urb, eurb);
- break;
case PIPE_ISOCHRONOUS:
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0) {
@@ -1220,221 +1132,208 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
break;
}
- ret = uhci_submit_isochronous(uhci, urb);
- if (ret == -EINPROGRESS)
+ ret = uhci_submit_isochronous(uhci, urb, qh);
+ if (ret == 0)
usb_claim_bandwidth(urb->dev, urb, bustime, 1);
break;
}
+ if (ret != 0)
+ goto err_submit_failed;
+
+ /* Add this URB to the QH */
+ urbp->qh = qh;
+ list_add_tail(&urbp->node, &qh->queue);
- if (ret != -EINPROGRESS) {
- /* Submit failed, so delete it from the urb_list */
- struct urb_priv *urbp = urb->hcpriv;
+ /* If the new URB is the first and only one on this QH then either
+ * the QH is new and idle or else it's unlinked and waiting to
+ * become idle, so we can activate it right away. */
+ if (qh->queue.next == &urbp->node)
+ uhci_activate_qh(uhci, qh);
+ goto done;
- list_del_init(&urbp->urb_list);
- uhci_destroy_urb_priv(uhci, urb);
- } else
- ret = 0;
+err_submit_failed:
+ if (qh->state == QH_STATE_IDLE)
+ uhci_make_qh_idle(uhci, qh); /* Reclaim unused QH */
-out:
+err_no_qh:
+ uhci_free_urb_priv(uhci, urbp);
+
+done:
spin_unlock_irqrestore(&uhci->lock, flags);
return ret;
}
-/*
- * Return the result of a transfer
- */
-static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb)
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
{
- int ret = -EINPROGRESS;
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ unsigned long flags;
struct urb_priv *urbp;
- spin_lock(&urb->lock);
+ spin_lock_irqsave(&uhci->lock, flags);
+ urbp = urb->hcpriv;
+ if (!urbp) /* URB was never linked! */
+ goto done;
+
+ /* Remove Isochronous TDs from the frame list ASAP */
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+ uhci_unlink_isochronous_tds(uhci, urb);
+ uhci_unlink_qh(uhci, urbp->qh);
+
+done:
+ spin_unlock_irqrestore(&uhci->lock, flags);
+ return 0;
+}
- urbp = (struct urb_priv *)urb->hcpriv;
+/*
+ * Finish unlinking an URB and give it back
+ */
+static void uhci_giveback_urb(struct uhci_hcd *uhci, struct uhci_qh *qh,
+ struct urb *urb, struct pt_regs *regs)
+__releases(uhci->lock)
+__acquires(uhci->lock)
+{
+ struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
- if (urb->status != -EINPROGRESS) /* URB already dequeued */
- goto out;
+ /* Isochronous TDs get unlinked directly from the frame list */
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+ uhci_unlink_isochronous_tds(uhci, urb);
+
+ /* If the URB isn't first on its queue, adjust the link pointer
+ * of the last TD in the previous URB. */
+ else if (qh->queue.next != &urbp->node) {
+ struct urb_priv *purbp;
+ struct uhci_td *ptd, *ltd;
+
+ purbp = list_entry(urbp->node.prev, struct urb_priv, node);
+ ptd = list_entry(purbp->td_list.prev, struct uhci_td,
+ list);
+ ltd = list_entry(urbp->td_list.prev, struct uhci_td,
+ list);
+ ptd->link = ltd->link;
+ }
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_CONTROL:
- ret = uhci_result_control(uhci, urb);
- break;
- case PIPE_BULK:
- case PIPE_INTERRUPT:
- ret = uhci_result_common(uhci, urb);
- break;
- case PIPE_ISOCHRONOUS:
- ret = uhci_result_isochronous(uhci, urb);
- break;
+ /* Take the URB off the QH's queue. If the queue is now empty,
+ * this is a perfect time for a toggle fixup. */
+ list_del_init(&urbp->node);
+ if (list_empty(&qh->queue) && qh->needs_fixup) {
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), qh->initial_toggle);
+ qh->needs_fixup = 0;
}
- if (ret == -EINPROGRESS)
- goto out;
- urb->status = ret;
+ uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
+ uhci_free_urb_priv(uhci, urbp);
switch (usb_pipetype(urb->pipe)) {
- case PIPE_CONTROL:
- case PIPE_BULK:
case PIPE_ISOCHRONOUS:
/* Release bandwidth for Interrupt or Isoc. transfers */
if (urb->bandwidth)
usb_release_bandwidth(urb->dev, urb, 1);
- uhci_unlink_generic(uhci, urb);
break;
case PIPE_INTERRUPT:
/* Release bandwidth for Interrupt or Isoc. transfers */
/* Make sure we don't release if we have a queued URB */
- if (list_empty(&urbp->queue_list) && urb->bandwidth)
+ if (list_empty(&qh->queue) && urb->bandwidth)
usb_release_bandwidth(urb->dev, urb, 0);
else
/* bandwidth was passed on to queued URB, */
/* so don't let usb_unlink_urb() release it */
urb->bandwidth = 0;
- uhci_unlink_generic(uhci, urb);
break;
- default:
- dev_info(uhci_dev(uhci), "%s: unknown pipe type %d "
- "for urb %p\n",
- __FUNCTION__, usb_pipetype(urb->pipe), urb);
}
- /* Move it from uhci->urb_list to uhci->complete_list */
- uhci_moveto_complete(uhci, urbp);
-
-out:
- spin_unlock(&urb->lock);
-}
-
-static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
-{
- struct list_head *head;
- struct uhci_td *td;
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- int prevactive = 0;
-
- uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
+ spin_unlock(&uhci->lock);
+ usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb, regs);
+ spin_lock(&uhci->lock);
- /*
- * Now we need to find out what the last successful toggle was
- * so we can update the local data toggle for the next transfer
- *
- * There are 2 ways the last successful completed TD is found:
- *
- * 1) The TD is NOT active and the actual length < expected length
- * 2) The TD is NOT active and it's the last TD in the chain
- *
- * and a third way the first uncompleted TD is found:
- *
- * 3) The TD is active and the previous TD is NOT active
- *
- * Control and Isochronous ignore the toggle, so this is safe
- * for all types
- *
- * FIXME: The toggle fixups won't be 100% reliable until we
- * change over to using a single queue for each endpoint and
- * stop the queue before unlinking.
- */
- head = &urbp->td_list;
- list_for_each_entry(td, head, list) {
- unsigned int ctrlstat = td_status(td);
+ /* If the queue is now empty, we can unlink the QH and give up its
+ * reserved bandwidth. */
+ if (list_empty(&qh->queue)) {
+ uhci_unlink_qh(uhci, qh);
- if (!(ctrlstat & TD_CTRL_ACTIVE) &&
- (uhci_actual_length(ctrlstat) <
- uhci_expected_length(td_token(td)) ||
- td->list.next == head))
- usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
- uhci_packetout(td_token(td)),
- uhci_toggle(td_token(td)) ^ 1);
- else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive)
- usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
- uhci_packetout(td_token(td)),
- uhci_toggle(td_token(td)));
-
- prevactive = ctrlstat & TD_CTRL_ACTIVE;
+ /* Bandwidth stuff not yet implemented */
}
-
- uhci_delete_queued_urb(uhci, urb);
-
- /* The interrupt loop will reclaim the QHs */
- uhci_remove_qh(uhci, urbp->qh);
- urbp->qh = NULL;
}
-static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+/*
+ * Scan the URBs in a QH's queue
+ */
+#define QH_FINISHED_UNLINKING(qh) \
+ (qh->state == QH_STATE_UNLINKING && \
+ uhci->frame_number + uhci->is_stopped != qh->unlink_frame)
+
+static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh,
+ struct pt_regs *regs)
{
- struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- unsigned long flags;
struct urb_priv *urbp;
+ struct urb *urb;
+ int status;
- spin_lock_irqsave(&uhci->lock, flags);
- urbp = urb->hcpriv;
- if (!urbp) /* URB was never linked! */
- goto done;
- list_del_init(&urbp->urb_list);
-
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
- unlink_isochronous_tds(uhci, urb);
- uhci_unlink_generic(uhci, urb);
-
- uhci_get_current_frame_number(uhci);
- if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age) {
- uhci_remove_pending_urbps(uhci);
- uhci->urb_remove_age = uhci->frame_number;
- }
-
- /* If we're the first, set the next interrupt bit */
- if (list_empty(&uhci->urb_remove_list))
- uhci_set_next_interrupt(uhci);
- list_add_tail(&urbp->urb_list, &uhci->urb_remove_list);
-
-done:
- spin_unlock_irqrestore(&uhci->lock, flags);
- return 0;
-}
+ while (!list_empty(&qh->queue)) {
+ urbp = list_entry(qh->queue.next, struct urb_priv, node);
+ urb = urbp->urb;
-static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
-{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *head;
- struct uhci_td *td;
- int count = 0;
-
- uhci_dec_fsbr(uhci, urb);
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ status = uhci_result_control(uhci, urb);
+ break;
+ case PIPE_ISOCHRONOUS:
+ status = uhci_result_isochronous(uhci, urb);
+ break;
+ default: /* PIPE_BULK or PIPE_INTERRUPT */
+ status = uhci_result_common(uhci, urb);
+ break;
+ }
+ if (status == -EINPROGRESS)
+ break;
- urbp->fsbr_timeout = 1;
+ spin_lock(&urb->lock);
+ if (urb->status == -EINPROGRESS) /* Not dequeued */
+ urb->status = status;
+ else
+ status = -ECONNRESET;
+ spin_unlock(&urb->lock);
- /*
- * Ideally we would want to fix qh->element as well, but it's
- * read/write by the HC, so that can introduce a race. It's not
- * really worth the hassle
- */
+ /* Dequeued but completed URBs can't be given back unless
+ * the QH is stopped or has finished unlinking. */
+ if (status == -ECONNRESET &&
+ !(qh->is_stopped || QH_FINISHED_UNLINKING(qh)))
+ return;
- head = &urbp->td_list;
- list_for_each_entry(td, head, list) {
- /*
- * Make sure we don't do the last one (since it'll have the
- * TERM bit set) as well as we skip every so many TDs to
- * make sure it doesn't hog the bandwidth
- */
- if (td->list.next != head && (count % DEPTH_INTERVAL) ==
- (DEPTH_INTERVAL - 1))
- td->link |= UHCI_PTR_DEPTH;
-
- count++;
+ uhci_giveback_urb(uhci, qh, urb, regs);
+ if (qh->is_stopped)
+ break;
}
- return 0;
-}
-
-static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
-{
- struct uhci_qh *qh, *tmp;
-
- list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {
- list_del_init(&qh->remove_list);
+ /* If the QH is neither stopped nor finished unlinking (normal case),
+ * our work here is done. */
+ restart:
+ if (!(qh->is_stopped || QH_FINISHED_UNLINKING(qh)))
+ return;
- uhci_free_qh(uhci, qh);
+ /* Otherwise give back each of the dequeued URBs */
+ list_for_each_entry(urbp, &qh->queue, node) {
+ urb = urbp->urb;
+ if (urb->status != -EINPROGRESS) {
+ uhci_save_toggle(qh, urb);
+ uhci_giveback_urb(uhci, qh, urb, regs);
+ goto restart;
+ }
+ }
+ qh->is_stopped = 0;
+
+ /* There are no more dequeued URBs. If there are still URBs on the
+ * queue, the QH can now be re-activated. */
+ if (!list_empty(&qh->queue)) {
+ if (qh->needs_fixup)
+ uhci_fixup_toggles(qh, 0);
+ uhci_activate_qh(uhci, qh);
}
+
+ /* The queue is empty. The QH can become idle if it is fully
+ * unlinked. */
+ else if (QH_FINISHED_UNLINKING(qh))
+ uhci_make_qh_idle(uhci, qh);
}
static void uhci_free_pending_tds(struct uhci_hcd *uhci)
@@ -1448,43 +1347,13 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci)
}
}
-static void
-uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
-__releases(uhci->lock)
-__acquires(uhci->lock)
-{
- struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-
- uhci_destroy_urb_priv(uhci, urb);
-
- spin_unlock(&uhci->lock);
- usb_hcd_giveback_urb(hcd, urb, regs);
- spin_lock(&uhci->lock);
-}
-
-static void uhci_finish_completion(struct uhci_hcd *uhci, struct pt_regs *regs)
-{
- struct urb_priv *urbp, *tmp;
-
- list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {
- struct urb *urb = urbp->urb;
-
- list_del_init(&urbp->urb_list);
- uhci_finish_urb(uhci_to_hcd(uhci), urb, regs);
- }
-}
-
-static void uhci_remove_pending_urbps(struct uhci_hcd *uhci)
-{
-
- /* Splice the urb_remove_list onto the end of the complete_list */
- list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev);
-}
-
-/* Process events in the schedule, but only in one thread at a time */
+/*
+ * Process events in the schedule, but only in one thread at a time
+ */
static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
{
- struct urb_priv *urbp, *tmp;
+ int i;
+ struct uhci_qh *qh;
/* Don't allow re-entrant calls */
if (uhci->scan_in_progress) {
@@ -1498,60 +1367,39 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
uhci_clear_next_interrupt(uhci);
uhci_get_current_frame_number(uhci);
- if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age)
- uhci_free_pending_qhs(uhci);
if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age)
uhci_free_pending_tds(uhci);
- if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age)
- uhci_remove_pending_urbps(uhci);
-
- /* Walk the list of pending URBs to see which ones completed
- * (must be _safe because uhci_transfer_result() dequeues URBs) */
- list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) {
- struct urb *urb = urbp->urb;
- /* Checks the status and does all of the magic necessary */
- uhci_transfer_result(uhci, urb);
- }
- uhci_finish_completion(uhci, regs);
-
- /* If the controller is stopped, we can finish these off right now */
- if (uhci->is_stopped) {
- uhci_free_pending_qhs(uhci);
- uhci_free_pending_tds(uhci);
- uhci_remove_pending_urbps(uhci);
+ /* Go through all the QH queues and process the URBs in each one */
+ for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {
+ uhci->next_qh = list_entry(uhci->skelqh[i]->node.next,
+ struct uhci_qh, node);
+ while ((qh = uhci->next_qh) != uhci->skelqh[i]) {
+ uhci->next_qh = list_entry(qh->node.next,
+ struct uhci_qh, node);
+ uhci_scan_qh(uhci, qh, regs);
+ }
}
if (uhci->need_rescan)
goto rescan;
uhci->scan_in_progress = 0;
- if (list_empty(&uhci->urb_remove_list) &&
- list_empty(&uhci->td_remove_list) &&
- list_empty(&uhci->qh_remove_list))
+ /* If the controller is stopped, we can finish these off right now */
+ if (uhci->is_stopped)
+ uhci_free_pending_tds(uhci);
+
+ if (list_empty(&uhci->td_remove_list) &&
+ list_empty(&uhci->skel_unlink_qh->node))
uhci_clear_next_interrupt(uhci);
else
uhci_set_next_interrupt(uhci);
-
- /* Wake up anyone waiting for an URB to complete */
- wake_up_all(&uhci->waitqh);
}
static void check_fsbr(struct uhci_hcd *uhci)
{
- struct urb_priv *up;
-
- list_for_each_entry(up, &uhci->urb_list, urb_list) {
- struct urb *u = up->urb;
-
- spin_lock(&u->lock);
-
- /* Check if the FSBR timed out */
- if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
- uhci_fsbr_timeout(uhci, u);
-
- spin_unlock(&u->lock);
- }
+ /* For now, don't scan URBs for FSBR timeouts.
+ * Add it back in later... */
/* Really disable FSBR */
if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index 049871145d6..08daf400f98 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -96,6 +96,7 @@
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/wait.h>
+#include <linux/mutex.h>
#include <linux/usb.h>
#include <linux/fs.h>
@@ -169,7 +170,7 @@ struct mdc800_data
int out_count; // Bytes in the buffer
int open; // Camera device open ?
- struct semaphore io_lock; // IO -lock
+ struct mutex io_lock; // IO -lock
char in [8]; // Command Input Buffer
int in_count;
@@ -497,7 +498,7 @@ static int mdc800_usb_probe (struct usb_interface *intf,
info ("Found Mustek MDC800 on USB.");
- down (&mdc800->io_lock);
+ mutex_lock(&mdc800->io_lock);
retval = usb_register_dev(intf, &mdc800_class);
if (retval) {
@@ -542,7 +543,7 @@ static int mdc800_usb_probe (struct usb_interface *intf,
mdc800->state=READY;
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
usb_set_intfdata(intf, mdc800);
return 0;
@@ -620,7 +621,7 @@ static int mdc800_device_open (struct inode* inode, struct file *file)
int retval=0;
int errn=0;
- down (&mdc800->io_lock);
+ mutex_lock(&mdc800->io_lock);
if (mdc800->state == NOT_CONNECTED)
{
@@ -656,7 +657,7 @@ static int mdc800_device_open (struct inode* inode, struct file *file)
dbg ("Mustek MDC800 device opened.");
error_out:
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return errn;
}
@@ -669,7 +670,7 @@ static int mdc800_device_release (struct inode* inode, struct file *file)
int retval=0;
dbg ("Mustek MDC800 device closed.");
- down (&mdc800->io_lock);
+ mutex_lock(&mdc800->io_lock);
if (mdc800->open && (mdc800->state != NOT_CONNECTED))
{
usb_kill_urb(mdc800->irq_urb);
@@ -682,7 +683,7 @@ static int mdc800_device_release (struct inode* inode, struct file *file)
retval=-EIO;
}
- up(&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return retval;
}
@@ -695,21 +696,21 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
size_t left=len, sts=len; /* single transfer size */
char __user *ptr = buf;
- down (&mdc800->io_lock);
+ mutex_lock(&mdc800->io_lock);
if (mdc800->state == NOT_CONNECTED)
{
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EBUSY;
}
if (mdc800->state == WORKING)
{
warn ("Illegal State \"working\" reached during read ?!");
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EBUSY;
}
if (!mdc800->open)
{
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EBUSY;
}
@@ -717,7 +718,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
{
if (signal_pending (current))
{
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EINTR;
}
@@ -736,7 +737,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
if (usb_submit_urb (mdc800->download_urb, GFP_KERNEL))
{
err ("Can't submit download urb (status=%i)",mdc800->download_urb->status);
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return len-left;
}
wait_event_timeout(mdc800->download_wait, mdc800->downloaded,
@@ -745,14 +746,14 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
if (mdc800->download_urb->status != 0)
{
err ("request download-bytes fails (status=%i)",mdc800->download_urb->status);
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return len-left;
}
}
else
{
/* No more bytes -> that's an error*/
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EIO;
}
}
@@ -761,7 +762,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
/* Copy Bytes */
if (copy_to_user(ptr, &mdc800->out [mdc800->out_ptr],
sts)) {
- up(&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EFAULT;
}
ptr+=sts;
@@ -770,7 +771,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
}
}
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return len-left;
}
@@ -785,15 +786,15 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
{
size_t i=0;
- down (&mdc800->io_lock);
+ mutex_lock(&mdc800->io_lock);
if (mdc800->state != READY)
{
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EBUSY;
}
if (!mdc800->open )
{
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EBUSY;
}
@@ -802,13 +803,13 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
unsigned char c;
if (signal_pending (current))
{
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EINTR;
}
if(get_user(c, buf+i))
{
- up(&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EFAULT;
}
@@ -829,7 +830,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
}
else
{
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EIO;
}
@@ -841,7 +842,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
if (mdc800_usb_waitForIRQ (0,TO_GET_READY))
{
err ("Camera didn't get ready.\n");
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EIO;
}
@@ -853,7 +854,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
if (usb_submit_urb (mdc800->write_urb, GFP_KERNEL))
{
err ("submitting write urb fails (status=%i)", mdc800->write_urb->status);
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EIO;
}
wait_event_timeout(mdc800->write_wait, mdc800->written, TO_WRITE_GET_READY*HZ/1000);
@@ -861,7 +862,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
if (mdc800->state == WORKING)
{
usb_kill_urb(mdc800->write_urb);
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EIO;
}
@@ -873,7 +874,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
{
err ("call 0x07 before 0x05,0x3e");
mdc800->state=READY;
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EIO;
}
mdc800->pic_len=-1;
@@ -892,7 +893,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))
{
err ("requesting answer from irq fails");
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EIO;
}
@@ -920,7 +921,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))
{
err ("Command Timeout.");
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return -EIO;
}
}
@@ -930,7 +931,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
}
i++;
}
- up (&mdc800->io_lock);
+ mutex_unlock(&mdc800->io_lock);
return i;
}
@@ -978,15 +979,13 @@ static int __init usb_mdc800_init (void)
{
int retval = -ENODEV;
/* Allocate Memory */
- mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL);
+ mdc800=kzalloc (sizeof (struct mdc800_data), GFP_KERNEL);
if (!mdc800)
goto cleanup_on_fail;
- memset(mdc800, 0, sizeof(struct mdc800_data));
mdc800->dev = NULL;
- mdc800->open=0;
mdc800->state=NOT_CONNECTED;
- init_MUTEX (&mdc800->io_lock);
+ mutex_init (&mdc800->io_lock);
init_waitqueue_head (&mdc800->irq_wait);
init_waitqueue_head (&mdc800->write_wait);
diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c
index f7bdc506e61..99f986cb6e9 100644
--- a/drivers/usb/input/ati_remote.c
+++ b/drivers/usb/input/ati_remote.c
@@ -159,8 +159,6 @@ static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
*/
#define FILTER_TIME (HZ / 20)
-static DECLARE_MUTEX(disconnect_sem);
-
struct ati_remote {
struct input_dev *idev;
struct usb_device *udev;
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index 07a012f8877..58b59f6e988 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -66,9 +66,8 @@ static struct hid_report *hid_register_report(struct hid_device *device, unsigne
if (report_enum->report_id_hash[id])
return report_enum->report_id_hash[id];
- if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL)))
+ if (!(report = kzalloc(sizeof(struct hid_report), GFP_KERNEL)))
return NULL;
- memset(report, 0, sizeof(struct hid_report));
if (id != 0)
report_enum->numbered = 1;
@@ -97,12 +96,9 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
return NULL;
}
- if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ if (!(field = kzalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ values * sizeof(unsigned), GFP_KERNEL))) return NULL;
- memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
- + values * sizeof(unsigned));
-
field->index = report->maxfield++;
report->field[field->index] = field;
field->usage = (struct hid_usage *)(field + 1);
@@ -651,17 +647,14 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
hid_parser_reserved
};
- if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL)))
+ if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))
return NULL;
- memset(device, 0, sizeof(struct hid_device));
- if (!(device->collection = kmalloc(sizeof(struct hid_collection) *
+ if (!(device->collection = kzalloc(sizeof(struct hid_collection) *
HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {
kfree(device);
return NULL;
}
- memset(device->collection, 0, sizeof(struct hid_collection) *
- HID_DEFAULT_NUM_COLLECTIONS);
device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
for (i = 0; i < HID_REPORT_TYPES; i++)
@@ -675,13 +668,12 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
memcpy(device->rdesc, start, size);
device->rsize = size;
- if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
+ if (!(parser = kzalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
kfree(device->rdesc);
kfree(device->collection);
kfree(device);
return NULL;
}
- memset(parser, 0, sizeof(struct hid_parser));
parser->device = device;
end = start + size;
@@ -911,6 +903,99 @@ static int hid_input_report(int type, struct urb *urb, int interrupt, struct pt_
}
/*
+ * Input submission and I/O error handler.
+ */
+
+static void hid_io_error(struct hid_device *hid);
+
+/* Start up the input URB */
+static int hid_start_in(struct hid_device *hid)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&hid->inlock, flags);
+ if (hid->open > 0 && !test_bit(HID_SUSPENDED, &hid->iofl) &&
+ !test_and_set_bit(HID_IN_RUNNING, &hid->iofl)) {
+ rc = usb_submit_urb(hid->urbin, GFP_ATOMIC);
+ if (rc != 0)
+ clear_bit(HID_IN_RUNNING, &hid->iofl);
+ }
+ spin_unlock_irqrestore(&hid->inlock, flags);
+ return rc;
+}
+
+/* I/O retry timer routine */
+static void hid_retry_timeout(unsigned long _hid)
+{
+ struct hid_device *hid = (struct hid_device *) _hid;
+
+ dev_dbg(&hid->intf->dev, "retrying intr urb\n");
+ if (hid_start_in(hid))
+ hid_io_error(hid);
+}
+
+/* Workqueue routine to reset the device */
+static void hid_reset(void *_hid)
+{
+ struct hid_device *hid = (struct hid_device *) _hid;
+ int rc_lock, rc;
+
+ dev_dbg(&hid->intf->dev, "resetting device\n");
+ rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf);
+ if (rc_lock >= 0) {
+ rc = usb_reset_device(hid->dev);
+ if (rc_lock)
+ usb_unlock_device(hid->dev);
+ }
+ clear_bit(HID_RESET_PENDING, &hid->iofl);
+
+ if (rc == 0) {
+ hid->retry_delay = 0;
+ if (hid_start_in(hid))
+ hid_io_error(hid);
+ } else if (!(rc == -ENODEV || rc == -EHOSTUNREACH || rc == -EINTR))
+ err("can't reset device, %s-%s/input%d, status %d",
+ hid->dev->bus->bus_name,
+ hid->dev->devpath,
+ hid->ifnum, rc);
+}
+
+/* Main I/O error handler */
+static void hid_io_error(struct hid_device *hid)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hid->inlock, flags);
+
+ /* Stop when disconnected */
+ if (usb_get_intfdata(hid->intf) == NULL)
+ goto done;
+
+ /* When an error occurs, retry at increasing intervals */
+ if (hid->retry_delay == 0) {
+ hid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */
+ hid->stop_retry = jiffies + msecs_to_jiffies(1000);
+ } else if (hid->retry_delay < 100)
+ hid->retry_delay *= 2;
+
+ if (time_after(jiffies, hid->stop_retry)) {
+
+ /* Retries failed, so do a port reset */
+ if (!test_and_set_bit(HID_RESET_PENDING, &hid->iofl)) {
+ if (schedule_work(&hid->reset_work))
+ goto done;
+ clear_bit(HID_RESET_PENDING, &hid->iofl);
+ }
+ }
+
+ mod_timer(&hid->io_retry,
+ jiffies + msecs_to_jiffies(hid->retry_delay));
+done:
+ spin_unlock_irqrestore(&hid->inlock, flags);
+}
+
+/*
* Input interrupt completion handler.
*/
@@ -921,25 +1006,35 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs)
switch (urb->status) {
case 0: /* success */
+ hid->retry_delay = 0;
hid_input_report(HID_INPUT_REPORT, urb, 1, regs);
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
- case -EPERM:
case -ESHUTDOWN: /* unplug */
- case -EILSEQ: /* unplug timeout on uhci */
+ clear_bit(HID_IN_RUNNING, &hid->iofl);
return;
+ case -EILSEQ: /* protocol error or unplug */
+ case -EPROTO: /* protocol error or unplug */
case -ETIMEDOUT: /* NAK */
- break;
+ clear_bit(HID_IN_RUNNING, &hid->iofl);
+ hid_io_error(hid);
+ return;
default: /* error */
warn("input irq status %d received", urb->status);
}
status = usb_submit_urb(urb, SLAB_ATOMIC);
- if (status)
- err("can't resubmit intr, %s-%s/input%d, status %d",
- hid->dev->bus->bus_name, hid->dev->devpath,
- hid->ifnum, status);
+ if (status) {
+ clear_bit(HID_IN_RUNNING, &hid->iofl);
+ if (status != -EPERM) {
+ err("can't resubmit intr, %s-%s/input%d, status %d",
+ hid->dev->bus->bus_name,
+ hid->dev->devpath,
+ hid->ifnum, status);
+ hid_io_error(hid);
+ }
+ }
}
/*
@@ -1101,8 +1196,9 @@ static void hid_irq_out(struct urb *urb, struct pt_regs *regs)
case 0: /* success */
break;
case -ESHUTDOWN: /* unplug */
- case -EILSEQ: /* unplug timeout on uhci */
unplug = 1;
+ case -EILSEQ: /* protocol error or unplug */
+ case -EPROTO: /* protocol error or unplug */
case -ECONNRESET: /* unlink */
case -ENOENT:
break;
@@ -1149,8 +1245,9 @@ static void hid_ctrl(struct urb *urb, struct pt_regs *regs)
hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, 0, regs);
break;
case -ESHUTDOWN: /* unplug */
- case -EILSEQ: /* unplug timectrl on uhci */
unplug = 1;
+ case -EILSEQ: /* protocol error or unplug */
+ case -EPROTO: /* protocol error or unplug */
case -ECONNRESET: /* unlink */
case -ENOENT:
case -EPIPE: /* report not available */
@@ -1263,14 +1360,9 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
int hid_open(struct hid_device *hid)
{
- if (hid->open++)
- return 0;
-
- hid->urbin->dev = hid->dev;
-
- if (usb_submit_urb(hid->urbin, GFP_KERNEL))
- return -EIO;
-
+ ++hid->open;
+ if (hid_start_in(hid))
+ hid_io_error(hid);
return 0;
}
@@ -1460,6 +1552,9 @@ void hid_init_reports(struct hid_device *hid)
#define USB_VENDOR_ID_HP 0x03f0
#define USB_DEVICE_ID_HP_USBHUB_KB 0x020c
+#define USB_VENDOR_ID_CREATIVELABS 0x062a
+#define USB_DEVICE_ID_CREATIVELABS_SILVERCREST 0x0201
+
/*
* Alphabetically sorted blacklist by quirk type.
*/
@@ -1576,6 +1671,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_KEYBOARD, HID_QUIRK_NOGET},
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_USBHUB_KB, HID_QUIRK_NOGET},
+ { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVELABS_SILVERCREST, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_HP, USB_DEVICE_ID_HP_USBHUB_KB, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
@@ -1795,6 +1891,10 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
init_waitqueue_head(&hid->wait);
+ INIT_WORK(&hid->reset_work, hid_reset, hid);
+ setup_timer(&hid->io_retry, hid_retry_timeout, (unsigned long) hid);
+
+ spin_lock_init(&hid->inlock);
spin_lock_init(&hid->outlock);
spin_lock_init(&hid->ctrllock);
@@ -1863,11 +1963,16 @@ static void hid_disconnect(struct usb_interface *intf)
if (!hid)
return;
+ spin_lock_irq(&hid->inlock); /* Sync with error handler */
usb_set_intfdata(intf, NULL);
+ spin_unlock_irq(&hid->inlock);
usb_kill_urb(hid->urbin);
usb_kill_urb(hid->urbout);
usb_kill_urb(hid->urbctrl);
+ del_timer_sync(&hid->io_retry);
+ flush_scheduled_work();
+
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(hid);
if (hid->claimed & HID_CLAIMED_HIDDEV)
@@ -1942,6 +2047,10 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
{
struct hid_device *hid = usb_get_intfdata (intf);
+ spin_lock_irq(&hid->inlock); /* Sync with error handler */
+ set_bit(HID_SUSPENDED, &hid->iofl);
+ spin_unlock_irq(&hid->inlock);
+ del_timer(&hid->io_retry);
usb_kill_urb(hid->urbin);
dev_dbg(&intf->dev, "suspend\n");
return 0;
@@ -1952,10 +2061,8 @@ static int hid_resume(struct usb_interface *intf)
struct hid_device *hid = usb_get_intfdata (intf);
int status;
- if (hid->open)
- status = usb_submit_urb(hid->urbin, GFP_NOIO);
- else
- status = 0;
+ clear_bit(HID_SUSPENDED, &hid->iofl);
+ status = hid_start_in(hid);
dev_dbg(&intf->dev, "resume status %d\n", status);
return status;
}
diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c
index f82c9c9e5d5..f07d44357ff 100644
--- a/drivers/usb/input/hid-lgff.c
+++ b/drivers/usb/input/hid-lgff.c
@@ -154,10 +154,9 @@ int hid_lgff_init(struct hid_device* hid)
return -1;
}
- private = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
+ private = kzalloc(sizeof(struct lgff_device), GFP_KERNEL);
if (!private)
return -1;
- memset(private, 0, sizeof(struct lgff_device));
hid->ff_private = private;
/* Input init */
@@ -228,13 +227,12 @@ static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report)
}
*ret->field[0] = *report->field[0];
- ret->field[0]->value = kmalloc(sizeof(s32[8]), GFP_KERNEL);
+ ret->field[0]->value = kzalloc(sizeof(s32[8]), GFP_KERNEL);
if (!ret->field[0]->value) {
kfree(ret->field[0]);
kfree(ret);
return NULL;
}
- memset(ret->field[0]->value, 0, sizeof(s32[8]));
return ret;
}
diff --git a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c
index 023fd5ac31c..534425c69c0 100644
--- a/drivers/usb/input/hid-tmff.c
+++ b/drivers/usb/input/hid-tmff.c
@@ -113,11 +113,10 @@ int hid_tmff_init(struct hid_device *hid)
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct input_dev *input_dev = hidinput->input;
- private = kmalloc(sizeof(struct tmff_device), GFP_KERNEL);
+ private = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
if (!private)
return -ENOMEM;
- memset(private, 0, sizeof(struct tmff_device));
hid->ff_private = private;
/* Find the report to use */
diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h
index 8b0d4346ce9..4e1b784fe52 100644
--- a/drivers/usb/input/hid.h
+++ b/drivers/usb/input/hid.h
@@ -31,6 +31,8 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
/*
* USB HID (Human Interface Device) interface class code
@@ -370,6 +372,9 @@ struct hid_control_fifo {
#define HID_CTRL_RUNNING 1
#define HID_OUT_RUNNING 2
+#define HID_IN_RUNNING 3
+#define HID_RESET_PENDING 4
+#define HID_SUSPENDED 5
struct hid_input {
struct list_head list;
@@ -393,12 +398,17 @@ struct hid_device { /* device report descriptor */
int ifnum; /* USB interface number */
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
+ struct timer_list io_retry; /* Retry timer */
+ unsigned long stop_retry; /* Time to give up, in jiffies */
+ unsigned int retry_delay; /* Delay length in ms */
+ struct work_struct reset_work; /* Task context for resets */
unsigned int bufsize; /* URB buffer size */
struct urb *urbin; /* Input URB */
char *inbuf; /* Input buffer */
dma_addr_t inbuf_dma; /* Input buffer dma */
+ spinlock_t inlock; /* Input fifo spinlock */
struct urb *urbctrl; /* Control URB */
struct usb_ctrlrequest *cr; /* Control request struct */
diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c
index 925f5aba06f..6dd66669617 100644
--- a/drivers/usb/input/hiddev.c
+++ b/drivers/usb/input/hiddev.c
@@ -257,9 +257,8 @@ static int hiddev_open(struct inode * inode, struct file * file) {
if (i >= HIDDEV_MINORS || !hiddev_table[i])
return -ENODEV;
- if (!(list = kmalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
+ if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
return -ENOMEM;
- memset(list, 0, sizeof(struct hiddev_list));
list->hiddev = hiddev_table[i];
list->next = hiddev_table[i]->list;
@@ -754,9 +753,8 @@ int hiddev_connect(struct hid_device *hid)
if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
return -1;
- if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL)))
+ if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
return -1;
- memset(hiddev, 0, sizeof(struct hiddev));
retval = usb_register_dev(hid->intf, &hiddev_class);
if (retval) {
diff --git a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig
index 0d3d2cc5d7b..189d40f96be 100644
--- a/drivers/usb/media/Kconfig
+++ b/drivers/usb/media/Kconfig
@@ -191,6 +191,21 @@ config USB_W9968CF
To compile this driver as a module, choose M here: the
module will be called w9968cf.
+config USB_ZC0301
+ tristate "USB ZC0301 Image Processor and Control Chip support"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y here if you want support for cameras based on the ZC0301
+ Image Processor and Control Chip.
+
+ See <file:Documentation/usb/zc0301.txt> for more informations.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" to use this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zc0301.
+
config USB_PWC
tristate "USB Philips Cameras"
depends on USB && VIDEO_DEV
diff --git a/drivers/usb/media/Makefile b/drivers/usb/media/Makefile
index 3957aa1be0f..50e89a33b85 100644
--- a/drivers/usb/media/Makefile
+++ b/drivers/usb/media/Makefile
@@ -2,8 +2,12 @@
# Makefile for USB Media drivers
#
-sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bcb.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o
+sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \
+ sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bca.o \
+ sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \
+ sn9c102_tas5130d1b.o
et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o
+zc0301-objs := zc0301_core.o zc0301_pas202bcb.o
obj-$(CONFIG_USB_DABUSB) += dabusb.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
@@ -16,4 +20,5 @@ obj-$(CONFIG_USB_SN9C102) += sn9c102.o
obj-$(CONFIG_USB_STV680) += stv680.o
obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o
obj-$(CONFIG_USB_W9968CF) += w9968cf.o
+obj-$(CONFIG_USB_ZC0301) += zc0301.o
obj-$(CONFIG_USB_PWC) += pwc/
diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c
index 18d8eaf408d..1774ab7a40d 100644
--- a/drivers/usb/media/dabusb.c
+++ b/drivers/usb/media/dabusb.c
@@ -38,6 +38,7 @@
#include <linux/delay.h>
#include <linux/usb.h>
#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include "dabusb.h"
#include "dabfirmware.h"
@@ -217,12 +218,11 @@ static int dabusb_alloc_buffers (pdabusb_t s)
pipesize, packets, transfer_buffer_length);
while (buffers < (s->total_buffer_size << 10)) {
- b = (pbuff_t) kmalloc (sizeof (buff_t), GFP_KERNEL);
+ b = (pbuff_t) kzalloc (sizeof (buff_t), GFP_KERNEL);
if (!b) {
- err("kmalloc(sizeof(buff_t))==NULL");
+ err("kzalloc(sizeof(buff_t))==NULL");
goto err;
}
- memset (b, 0, sizeof (buff_t));
b->s = s;
b->purb = usb_alloc_urb(packets, GFP_KERNEL);
if (!b->purb) {
@@ -571,7 +571,7 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l
s->readptr = 0;
}
}
- err: //up(&s->mutex);
+ err: //mutex_unlock(&s->mutex);
return ret;
}
@@ -586,10 +586,10 @@ static int dabusb_open (struct inode *inode, struct file *file)
s = &dabusb[devnum - DABUSB_MINOR];
dbg("dabusb_open");
- down (&s->mutex);
+ mutex_lock(&s->mutex);
while (!s->usbdev || s->opened) {
- up (&s->mutex);
+ mutex_unlock(&s->mutex);
if (file->f_flags & O_NONBLOCK) {
return -EBUSY;
@@ -599,15 +599,15 @@ static int dabusb_open (struct inode *inode, struct file *file)
if (signal_pending (current)) {
return -EAGAIN;
}
- down (&s->mutex);
+ mutex_lock(&s->mutex);
}
if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
- up(&s->mutex);
+ mutex_unlock(&s->mutex);
err("set_interface failed");
return -EINVAL;
}
s->opened = 1;
- up (&s->mutex);
+ mutex_unlock(&s->mutex);
file->f_pos = 0;
file->private_data = s;
@@ -621,10 +621,10 @@ static int dabusb_release (struct inode *inode, struct file *file)
dbg("dabusb_release");
- down (&s->mutex);
+ mutex_lock(&s->mutex);
dabusb_stop (s);
dabusb_free_buffers (s);
- up (&s->mutex);
+ mutex_unlock(&s->mutex);
if (!s->remove_pending) {
if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0)
@@ -649,10 +649,10 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm
if (s->remove_pending)
return -EIO;
- down (&s->mutex);
+ mutex_lock(&s->mutex);
if (!s->usbdev) {
- up (&s->mutex);
+ mutex_unlock(&s->mutex);
return -EIO;
}
@@ -692,7 +692,7 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm
ret = -ENOIOCTLCMD;
break;
}
- up (&s->mutex);
+ mutex_unlock(&s->mutex);
return ret;
}
@@ -738,7 +738,7 @@ static int dabusb_probe (struct usb_interface *intf,
s = &dabusb[intf->minor];
- down (&s->mutex);
+ mutex_lock(&s->mutex);
s->remove_pending = 0;
s->usbdev = usbdev;
s->devnum = intf->minor;
@@ -761,7 +761,7 @@ static int dabusb_probe (struct usb_interface *intf,
}
dbg("bound to interface: %d", intf->altsetting->desc.bInterfaceNumber);
usb_set_intfdata (intf, s);
- up (&s->mutex);
+ mutex_unlock(&s->mutex);
retval = usb_register_dev(intf, &dabusb_class);
if (retval) {
@@ -772,7 +772,7 @@ static int dabusb_probe (struct usb_interface *intf,
return 0;
reject:
- up (&s->mutex);
+ mutex_unlock(&s->mutex);
s->usbdev = NULL;
return -ENODEV;
}
@@ -829,7 +829,7 @@ static int __init dabusb_init (void)
for (u = 0; u < NRDABUSB; u++) {
pdabusb_t s = &dabusb[u];
memset (s, 0, sizeof (dabusb_t));
- init_MUTEX (&s->mutex);
+ mutex_init (&s->mutex);
s->usbdev = NULL;
s->total_buffer_size = buffers;
init_waitqueue_head (&s->wait);
diff --git a/drivers/usb/media/dabusb.h b/drivers/usb/media/dabusb.h
index 10b666e43ab..96b03e4af8b 100644
--- a/drivers/usb/media/dabusb.h
+++ b/drivers/usb/media/dabusb.h
@@ -18,7 +18,7 @@ typedef enum { _stopped=0, _started } driver_state_t;
typedef struct
{
- struct semaphore mutex;
+ struct mutex mutex;
struct usb_device *usbdev;
wait_queue_head_t wait;
wait_queue_head_t remove_ok;
diff --git a/drivers/usb/media/et61x251.h b/drivers/usb/media/et61x251.h
index 652238f329f..eee8afc9be7 100644
--- a/drivers/usb/media/et61x251.h
+++ b/drivers/usb/media/et61x251.h
@@ -33,7 +33,9 @@
#include <linux/types.h>
#include <linux/param.h>
#include <linux/rwsem.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
#include "et61x251_sensor.h"
@@ -51,6 +53,7 @@
#define ET61X251_ALTERNATE_SETTING 13
#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS)
#define ET61X251_CTRL_TIMEOUT 100
+#define ET61X251_FRAME_TIMEOUT 2
/*****************************************************************************/
@@ -127,15 +130,16 @@ struct et61x251_sysfs_attr {
struct et61x251_module_param {
u8 force_munmap;
+ u16 frame_timeout;
};
-static DECLARE_MUTEX(et61x251_sysfs_lock);
+static DEFINE_MUTEX(et61x251_sysfs_lock);
static DECLARE_RWSEM(et61x251_disconnect);
struct et61x251_device {
struct video_device* v4ldev;
- struct et61x251_sensor* sensor;
+ struct et61x251_sensor sensor;
struct usb_device* usbdev;
struct urb* urb[ET61X251_URBS];
@@ -157,19 +161,28 @@ struct et61x251_device {
enum et61x251_dev_state state;
u8 users;
- struct semaphore dev_sem, fileop_sem;
+ struct mutex dev_mutex, fileop_mutex;
spinlock_t queue_lock;
wait_queue_head_t open, wait_frame, wait_stream;
};
/*****************************************************************************/
+struct et61x251_device*
+et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id)
+{
+ if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id))
+ return cam;
+
+ return NULL;
+}
+
+
void
et61x251_attach_sensor(struct et61x251_device* cam,
struct et61x251_sensor* sensor)
{
- cam->sensor = sensor;
- cam->sensor->usbdev = cam->usbdev;
+ memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor));
}
/*****************************************************************************/
@@ -212,7 +225,8 @@ do { \
#undef PDBG
#define PDBG(fmt, args...) \
-dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args)
+dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args)
#undef PDBGG
#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
diff --git a/drivers/usb/media/et61x251_core.c b/drivers/usb/media/et61x251_core.c
index 2c0171a5ad6..7cc01b828b3 100644
--- a/drivers/usb/media/et61x251_core.c
+++ b/drivers/usb/media/et61x251_core.c
@@ -25,11 +25,9 @@
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <linux/string.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>
-#include <linux/stddef.h>
#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
@@ -50,8 +48,8 @@
#define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia"
#define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define ET61X251_MODULE_LICENSE "GPL"
-#define ET61X251_MODULE_VERSION "1:1.01"
-#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1)
+#define ET61X251_MODULE_VERSION "1:1.02"
+#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2)
/*****************************************************************************/
@@ -90,6 +88,16 @@ MODULE_PARM_DESC(force_munmap,
"\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
"\n");
+static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] =
+ ET61X251_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ "\n<n[,...]> Timeout for a video frame in seconds."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "
+ __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"."
+ "\n");
+
#ifdef ET61X251_DEBUG
static unsigned short debug = ET61X251_DEBUG_LEVEL;
module_param(debug, ushort, 0644);
@@ -111,8 +119,8 @@ static u32
et61x251_request_buffers(struct et61x251_device* cam, u32 count,
enum et61x251_io_method io)
{
- struct v4l2_pix_format* p = &(cam->sensor->pix_format);
- struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
+ struct v4l2_pix_format* p = &(cam->sensor.pix_format);
+ struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
const size_t imagesize = cam->module_param.force_munmap ||
io == IO_READ ?
(p->width * p->height * p->priv) / 8 :
@@ -268,8 +276,8 @@ et61x251_i2c_try_read(struct et61x251_device* cam,
int err = 0, res;
data[0] = address;
- data[1] = cam->sensor->i2c_slave_id;
- data[2] = cam->sensor->rsta | 0x10;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x10;
data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02);
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT);
@@ -301,8 +309,8 @@ et61x251_i2c_try_write(struct et61x251_device* cam,
int err = 0, res;
data[0] = address;
- data[1] = cam->sensor->i2c_slave_id;
- data[2] = cam->sensor->rsta | 0x12;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x12;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
if (res < 0)
@@ -334,9 +342,6 @@ et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
u8* data = cam->control_buffer;
int err = 0, res;
- if (!cam->sensor)
- return -1;
-
data[0] = data2;
data[1] = data3;
data[2] = data4;
@@ -350,8 +355,8 @@ et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
err += res;
data[0] = address;
- data[1] = cam->sensor->i2c_slave_id;
- data[2] = cam->sensor->rsta | 0x02 | (n << 4);
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x02 | (n << 4);
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
if (res < 0)
@@ -364,11 +369,11 @@ et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
if (res < 0)
err += res;
- err += et61x251_i2c_wait(cam, cam->sensor);
+ err += et61x251_i2c_wait(cam, &cam->sensor);
if (err)
DBG(3, "I2C raw write failed for %s image sensor",
- cam->sensor->name);
+ cam->sensor.name);
PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, "
"data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X,"
@@ -382,19 +387,13 @@ et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
int et61x251_i2c_read(struct et61x251_device* cam, u8 address)
{
- if (!cam->sensor)
- return -1;
-
- return et61x251_i2c_try_read(cam, cam->sensor, address);
+ return et61x251_i2c_try_read(cam, &cam->sensor, address);
}
int et61x251_i2c_write(struct et61x251_device* cam, u8 address, u8 value)
{
- if (!cam->sensor)
- return -1;
-
- return et61x251_i2c_try_write(cam, cam->sensor, address, value);
+ return et61x251_i2c_try_write(cam, &cam->sensor, address, value);
}
/*****************************************************************************/
@@ -417,7 +416,7 @@ static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs)
if ((*f))
(*f)->state = F_QUEUED;
DBG(3, "Stream interrupted");
- wake_up_interruptible(&cam->wait_stream);
+ wake_up(&cam->wait_stream);
}
if (cam->state & DEV_DISCONNECTED)
@@ -435,9 +434,9 @@ static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs)
(*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t,
frame);
- imagesize = (cam->sensor->pix_format.width *
- cam->sensor->pix_format.height *
- cam->sensor->pix_format.priv) / 8;
+ imagesize = (cam->sensor.pix_format.width *
+ cam->sensor.pix_format.height *
+ cam->sensor.pix_format.priv) / 8;
for (i = 0; i < urb->number_of_packets; i++) {
unsigned int len, status;
@@ -476,7 +475,7 @@ start_of_frame:
if ((*f)->state == F_GRABBING) {
if (sof && (*f)->buf.bytesused) {
- if (cam->sensor->pix_format.pixelformat ==
+ if (cam->sensor.pix_format.pixelformat ==
V4L2_PIX_FMT_ET61X251)
goto end_of_frame;
else {
@@ -521,7 +520,7 @@ end_of_frame:
goto resubmit_urb;
if (sof &&
- cam->sensor->pix_format.pixelformat ==
+ cam->sensor.pix_format.pixelformat ==
V4L2_PIX_FMT_ET61X251)
goto start_of_frame;
}
@@ -650,21 +649,21 @@ static int et61x251_stop_transfer(struct et61x251_device* cam)
static int et61x251_stream_interrupt(struct et61x251_device* cam)
{
- int err = 0;
+ long timeout;
cam->stream = STREAM_INTERRUPT;
- err = wait_event_timeout(cam->wait_stream,
- (cam->stream == STREAM_OFF) ||
- (cam->state & DEV_DISCONNECTED),
- ET61X251_URB_TIMEOUT);
+ timeout = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ ET61X251_URB_TIMEOUT);
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
- else if (err) {
+ else if (cam->stream != STREAM_OFF) {
cam->state |= DEV_MISCONFIGURED;
DBG(1, "URB timeout reached. The camera is misconfigured. To "
"use it, close and open /dev/video%d again.",
cam->v4ldev->minor);
- return err;
+ return -EIO;
}
return 0;
@@ -709,18 +708,18 @@ static ssize_t et61x251_show_reg(struct class_device* cd, char* buf)
struct et61x251_device* cam;
ssize_t count;
- if (down_interruptible(&et61x251_sysfs_lock))
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENODEV;
}
count = sprintf(buf, "%u\n", cam->sysfs.reg);
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return count;
}
@@ -733,18 +732,18 @@ et61x251_store_reg(struct class_device* cd, const char* buf, size_t len)
u8 index;
ssize_t count;
- if (down_interruptible(&et61x251_sysfs_lock))
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENODEV;
}
index = et61x251_strtou8(buf, len, &count);
if (index > 0x8e || !count) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -EINVAL;
}
@@ -753,7 +752,7 @@ et61x251_store_reg(struct class_device* cd, const char* buf, size_t len)
DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg);
DBG(3, "Written bytes: %zd", count);
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return count;
}
@@ -765,17 +764,17 @@ static ssize_t et61x251_show_val(struct class_device* cd, char* buf)
ssize_t count;
int val;
- if (down_interruptible(&et61x251_sysfs_lock))
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENODEV;
}
if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -EIO;
}
@@ -783,7 +782,7 @@ static ssize_t et61x251_show_val(struct class_device* cd, char* buf)
DBG(3, "Read bytes: %zd", count);
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return count;
}
@@ -797,24 +796,24 @@ et61x251_store_val(struct class_device* cd, const char* buf, size_t len)
ssize_t count;
int err;
- if (down_interruptible(&et61x251_sysfs_lock))
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENODEV;
}
value = et61x251_strtou8(buf, len, &count);
if (!count) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -EINVAL;
}
err = et61x251_write_reg(cam, value, cam->sysfs.reg);
if (err) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -EIO;
}
@@ -822,7 +821,7 @@ et61x251_store_val(struct class_device* cd, const char* buf, size_t len)
cam->sysfs.reg, value);
DBG(3, "Written bytes: %zd", count);
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return count;
}
@@ -833,12 +832,12 @@ static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf)
struct et61x251_device* cam;
ssize_t count;
- if (down_interruptible(&et61x251_sysfs_lock))
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENODEV;
}
@@ -846,7 +845,7 @@ static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf)
DBG(3, "Read bytes: %zd", count);
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return count;
}
@@ -859,18 +858,18 @@ et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
u8 index;
ssize_t count;
- if (down_interruptible(&et61x251_sysfs_lock))
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENODEV;
}
index = et61x251_strtou8(buf, len, &count);
if (!count) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -EINVAL;
}
@@ -879,7 +878,7 @@ et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
DBG(3, "Written bytes: %zd", count);
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return count;
}
@@ -891,22 +890,22 @@ static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf)
ssize_t count;
int val;
- if (down_interruptible(&et61x251_sysfs_lock))
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENODEV;
}
- if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) {
- up(&et61x251_sysfs_lock);
+ if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) {
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENOSYS;
}
if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -EIO;
}
@@ -914,7 +913,7 @@ static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf)
DBG(3, "Read bytes: %zd", count);
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return count;
}
@@ -928,29 +927,29 @@ et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
ssize_t count;
int err;
- if (down_interruptible(&et61x251_sysfs_lock))
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENODEV;
}
- if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) {
- up(&et61x251_sysfs_lock);
+ if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) {
+ mutex_unlock(&et61x251_sysfs_lock);
return -ENOSYS;
}
value = et61x251_strtou8(buf, len, &count);
if (!count) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -EINVAL;
}
err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value);
if (err) {
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return -EIO;
}
@@ -958,7 +957,7 @@ et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
cam->sysfs.i2c_reg, value);
DBG(3, "Written bytes: %zd", count);
- up(&et61x251_sysfs_lock);
+ mutex_unlock(&et61x251_sysfs_lock);
return count;
}
@@ -980,7 +979,7 @@ static void et61x251_create_sysfs(struct et61x251_device* cam)
video_device_create_file(v4ldev, &class_device_attr_reg);
video_device_create_file(v4ldev, &class_device_attr_val);
- if (cam->sensor && cam->sensor->sysfs_ops) {
+ if (cam->sensor.sysfs_ops) {
video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
video_device_create_file(v4ldev, &class_device_attr_i2c_val);
}
@@ -1048,7 +1047,7 @@ static int et61x251_set_scale(struct et61x251_device* cam, u8 scale)
static int
et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect)
{
- struct et61x251_sensor* s = cam->sensor;
+ struct et61x251_sensor* s = &cam->sensor;
u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left +
s->active_pixel.left),
fmw_sy = (u16)(rect->top - s->cropcap.bounds.top +
@@ -1076,7 +1075,7 @@ et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect)
static int et61x251_init(struct et61x251_device* cam)
{
- struct et61x251_sensor* s = cam->sensor;
+ struct et61x251_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
struct v4l2_queryctrl *qctrl;
struct v4l2_rect* rect;
@@ -1143,7 +1142,7 @@ static int et61x251_init(struct et61x251_device* cam)
}
if (!(cam->state & DEV_INITIALIZED)) {
- init_MUTEX(&cam->fileop_sem);
+ mutex_init(&cam->fileop_mutex);
spin_lock_init(&cam->queue_lock);
init_waitqueue_head(&cam->wait_frame);
init_waitqueue_head(&cam->wait_stream);
@@ -1161,13 +1160,15 @@ static int et61x251_init(struct et61x251_device* cam)
static void et61x251_release_resources(struct et61x251_device* cam)
{
- down(&et61x251_sysfs_lock);
+ mutex_lock(&et61x251_sysfs_lock);
DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
- up(&et61x251_sysfs_lock);
+ usb_put_dev(cam->usbdev);
+
+ mutex_unlock(&et61x251_sysfs_lock);
kfree(cam->control_buffer);
}
@@ -1188,7 +1189,7 @@ static int et61x251_open(struct inode* inode, struct file* filp)
cam = video_get_drvdata(video_devdata(filp));
- if (down_interruptible(&cam->dev_sem)) {
+ if (mutex_lock_interruptible(&cam->dev_mutex)) {
up_read(&et61x251_disconnect);
return -ERESTARTSYS;
}
@@ -1200,7 +1201,7 @@ static int et61x251_open(struct inode* inode, struct file* filp)
err = -EWOULDBLOCK;
goto out;
}
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
err = wait_event_interruptible_exclusive(cam->open,
cam->state & DEV_DISCONNECTED
|| !cam->users);
@@ -1212,7 +1213,7 @@ static int et61x251_open(struct inode* inode, struct file* filp)
up_read(&et61x251_disconnect);
return -ENODEV;
}
- down(&cam->dev_sem);
+ mutex_lock(&cam->dev_mutex);
}
@@ -1240,7 +1241,7 @@ static int et61x251_open(struct inode* inode, struct file* filp)
DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
out:
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
up_read(&et61x251_disconnect);
return err;
}
@@ -1250,7 +1251,7 @@ static int et61x251_release(struct inode* inode, struct file* filp)
{
struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
- down(&cam->dev_sem); /* prevent disconnect() to be called */
+ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
et61x251_stop_transfer(cam);
@@ -1258,7 +1259,7 @@ static int et61x251_release(struct inode* inode, struct file* filp)
if (cam->state & DEV_DISCONNECTED) {
et61x251_release_resources(cam);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
kfree(cam);
return 0;
}
@@ -1268,7 +1269,7 @@ static int et61x251_release(struct inode* inode, struct file* filp)
DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
return 0;
}
@@ -1281,28 +1282,29 @@ et61x251_read(struct file* filp, char __user * buf,
struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
struct et61x251_frame_t* f, * i;
unsigned long lock_flags;
+ long timeout;
int err = 0;
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
if (cam->io == IO_MMAP) {
DBG(3, "Close and open the device again to choose the read "
"method");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EINVAL;
}
@@ -1310,7 +1312,7 @@ et61x251_read(struct file* filp, char __user * buf,
if (!et61x251_request_buffers(cam, cam->nreadbuffers,
IO_READ)) {
DBG(1, "read() failed, not enough memory");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENOMEM;
}
cam->io = IO_READ;
@@ -1324,30 +1326,32 @@ et61x251_read(struct file* filp, char __user * buf,
}
if (!count) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return 0;
}
if (list_empty(&cam->outqueue)) {
if (filp->f_flags & O_NONBLOCK) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EAGAIN;
}
- err = wait_event_interruptible
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED) );
- if (err) {
- up(&cam->fileop_sem);
- return err;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0) {
+ mutex_unlock(&cam->fileop_mutex);
+ return timeout;
}
if (cam->state & DEV_DISCONNECTED) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
- if (cam->state & DEV_MISCONFIGURED) {
- up(&cam->fileop_sem);
+ if (!timeout || (cam->state & DEV_MISCONFIGURED)) {
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
}
@@ -1375,7 +1379,7 @@ exit:
PDBGG("Frame #%lu, bytes read: %zu",
(unsigned long)f->buf.index, count);
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return err ? err : count;
}
@@ -1388,7 +1392,7 @@ static unsigned int et61x251_poll(struct file *filp, poll_table *wait)
unsigned long lock_flags;
unsigned int mask = 0;
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return POLLERR;
if (cam->state & DEV_DISCONNECTED) {
@@ -1426,12 +1430,12 @@ static unsigned int et61x251_poll(struct file *filp, poll_table *wait)
if (!list_empty(&cam->outqueue))
mask |= POLLIN | POLLRDNORM;
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return mask;
error:
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return POLLERR;
}
@@ -1465,25 +1469,25 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
void *pos;
u32 i;
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
size != PAGE_ALIGN(cam->frame[0].buf.length)) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EINVAL;
}
@@ -1492,7 +1496,7 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
break;
}
if (i == cam->nbuffers) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EINVAL;
}
@@ -1502,7 +1506,7 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
pos = cam->frame[i].bufmem;
while (size > 0) { /* size is page-aligned */
if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EAGAIN;
}
start += PAGE_SIZE;
@@ -1515,7 +1519,7 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
et61x251_vm_open(vma);
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return 0;
}
@@ -1557,6 +1561,7 @@ et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg)
memset(&i, 0, sizeof(i));
strcpy(i.name, "Camera");
+ i.type = V4L2_INPUT_TYPE_CAMERA;
if (copy_to_user(arg, &i, sizeof(i)))
return -EFAULT;
@@ -1566,7 +1571,19 @@ et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg)
static int
-et61x251_vidioc_gs_input(struct et61x251_device* cam, void __user * arg)
+et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg)
+{
+ int index = 0;
+
+ if (copy_to_user(arg, &index, sizeof(index)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg)
{
int index;
@@ -1583,7 +1600,7 @@ et61x251_vidioc_gs_input(struct et61x251_device* cam, void __user * arg)
static int
et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg)
{
- struct et61x251_sensor* s = cam->sensor;
+ struct et61x251_sensor* s = &cam->sensor;
struct v4l2_queryctrl qc;
u8 i;
@@ -1605,7 +1622,7 @@ et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg)
static int
et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg)
{
- struct et61x251_sensor* s = cam->sensor;
+ struct et61x251_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
int err = 0;
u8 i;
@@ -1637,7 +1654,7 @@ exit:
static int
et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg)
{
- struct et61x251_sensor* s = cam->sensor;
+ struct et61x251_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
u8 i;
int err = 0;
@@ -1650,6 +1667,8 @@ et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg)
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
if (ctrl.value < s->qctrl[i].minimum ||
ctrl.value > s->qctrl[i].maximum)
return -ERANGE;
@@ -1669,7 +1688,7 @@ et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg)
static int
et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg)
{
- struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
+ struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cc->pixelaspect.numerator = 1;
@@ -1685,7 +1704,7 @@ et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg)
static int
et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg)
{
- struct et61x251_sensor* s = cam->sensor;
+ struct et61x251_sensor* s = &cam->sensor;
struct v4l2_crop crop = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
};
@@ -1702,7 +1721,7 @@ et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg)
static int
et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
{
- struct et61x251_sensor* s = cam->sensor;
+ struct et61x251_sensor* s = &cam->sensor;
struct v4l2_crop crop;
struct v4l2_rect* rect;
struct v4l2_rect* bounds = &(s->cropcap.bounds);
@@ -1843,7 +1862,7 @@ static int
et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg)
{
struct v4l2_format format;
- struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
if (copy_from_user(&format, arg, sizeof(format)))
return -EFAULT;
@@ -1868,7 +1887,7 @@ static int
et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
void __user * arg)
{
- struct et61x251_sensor* s = cam->sensor;
+ struct et61x251_sensor* s = &cam->sensor;
struct v4l2_format format;
struct v4l2_pix_format* pix;
struct v4l2_pix_format* pfmt = &(s->pix_format);
@@ -2155,7 +2174,7 @@ et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp,
struct v4l2_buffer b;
struct et61x251_frame_t *f;
unsigned long lock_flags;
- int err = 0;
+ long timeout;
if (copy_from_user(&b, arg, sizeof(b)))
return -EFAULT;
@@ -2168,16 +2187,18 @@ et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp,
return -EINVAL;
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
- err = wait_event_interruptible
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED) );
- if (err)
- return err;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
- if (cam->state & DEV_MISCONFIGURED)
+ if (!timeout || (cam->state & DEV_MISCONFIGURED))
return -EIO;
}
@@ -2309,8 +2330,10 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp,
return et61x251_vidioc_enuminput(cam, arg);
case VIDIOC_G_INPUT:
+ return et61x251_vidioc_g_input(cam, arg);
+
case VIDIOC_S_INPUT:
- return et61x251_vidioc_gs_input(cam, arg);
+ return et61x251_vidioc_s_input(cam, arg);
case VIDIOC_QUERYCTRL:
return et61x251_vidioc_query_ctrl(cam, arg);
@@ -2393,19 +2416,19 @@ static int et61x251_ioctl(struct inode* inode, struct file* filp,
struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
int err = 0;
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
@@ -2413,7 +2436,7 @@ static int et61x251_ioctl(struct inode* inode, struct file* filp,
err = et61x251_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return err;
}
@@ -2459,7 +2482,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
goto fail;
}
- init_MUTEX(&cam->dev_sem);
+ mutex_init(&cam->dev_mutex);
DBG(2, "ET61X[12]51 PC Camera Controller detected "
"(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct);
@@ -2470,8 +2493,8 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
break;
}
- if (!err && cam->sensor)
- DBG(2, "%s image sensor detected", cam->sensor->name);
+ if (!err)
+ DBG(2, "%s image sensor detected", cam->sensor.name);
else {
DBG(1, "No supported image sensor detected");
err = -ENODEV;
@@ -2492,7 +2515,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
cam->v4ldev->release = video_device_release;
video_set_drvdata(cam->v4ldev, cam);
- down(&cam->dev_sem);
+ mutex_lock(&cam->dev_mutex);
err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
video_nr[dev_nr]);
@@ -2502,13 +2525,14 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
DBG(1, "Free /dev/videoX node not found");
video_nr[dev_nr] = -1;
dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
goto fail;
}
DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
@@ -2519,7 +2543,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
usb_set_intfdata(intf, cam);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
return 0;
@@ -2543,7 +2567,7 @@ static void et61x251_usb_disconnect(struct usb_interface* intf)
down_write(&et61x251_disconnect);
- down(&cam->dev_sem);
+ mutex_lock(&cam->dev_mutex);
DBG(2, "Disconnecting %s...", cam->v4ldev->name);
@@ -2557,13 +2581,14 @@ static void et61x251_usb_disconnect(struct usb_interface* intf)
et61x251_stop_transfer(cam);
cam->state |= DEV_DISCONNECTED;
wake_up_interruptible(&cam->wait_frame);
- wake_up_interruptible(&cam->wait_stream);
+ wake_up(&cam->wait_stream);
+ usb_get_dev(cam->usbdev);
} else {
cam->state |= DEV_DISCONNECTED;
et61x251_release_resources(cam);
}
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
if (!cam->users)
kfree(cam);
diff --git a/drivers/usb/media/et61x251_sensor.h b/drivers/usb/media/et61x251_sensor.h
index b9df91062fc..56841ae8a20 100644
--- a/drivers/usb/media/et61x251_sensor.h
+++ b/drivers/usb/media/et61x251_sensor.h
@@ -42,6 +42,9 @@ static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \
NULL, \
};
+extern struct et61x251_device*
+et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id);
+
extern void
et61x251_attach_sensor(struct et61x251_device* cam,
struct et61x251_sensor* sensor);
@@ -105,8 +108,6 @@ struct et61x251_sensor {
int (*set_pix_format)(struct et61x251_device* cam,
const struct v4l2_pix_format* pix);
- const struct usb_device* usbdev;
-
/* Private */
struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS];
struct v4l2_rect _rect;
diff --git a/drivers/usb/media/et61x251_tas5130d1b.c b/drivers/usb/media/et61x251_tas5130d1b.c
index 65f1ae9cf2b..3998d76a307 100644
--- a/drivers/usb/media/et61x251_tas5130d1b.c
+++ b/drivers/usb/media/et61x251_tas5130d1b.c
@@ -126,12 +126,16 @@ static struct et61x251_sensor tas5130d1b = {
int et61x251_probe_tas5130d1b(struct et61x251_device* cam)
{
- /* This sensor has no identifiers, so let's attach it anyway */
- et61x251_attach_sensor(cam, &tas5130d1b);
+ const struct usb_device_id tas5130d1b_id_table[] = {
+ { USB_DEVICE(0x102c, 0x6251), },
+ { }
+ };
/* Sensor detection is based on USB pid/vid */
- if (le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x6251)
+ if (!et61x251_match_id(cam, tas5130d1b_id_table))
return -ENODEV;
+ et61x251_attach_sensor(cam, &tas5130d1b);
+
return 0;
}
diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c
index 51e9cc06f7e..da44579d6f2 100644
--- a/drivers/usb/media/ov511.c
+++ b/drivers/usb/media/ov511.c
@@ -365,14 +365,14 @@ reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value)
PDEBUG(5, "0x%02X:0x%02X", reg, value);
- down(&ov->cbuf_lock);
+ mutex_lock(&ov->cbuf_lock);
ov->cbuf[0] = value;
rc = usb_control_msg(ov->dev,
usb_sndctrlpipe(ov->dev, 0),
(ov->bclass == BCL_OV518)?1:2 /* REG_IO */,
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, (__u16)reg, &ov->cbuf[0], 1, 1000);
- up(&ov->cbuf_lock);
+ mutex_unlock(&ov->cbuf_lock);
if (rc < 0)
err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc));
@@ -387,7 +387,7 @@ reg_r(struct usb_ov511 *ov, unsigned char reg)
{
int rc;
- down(&ov->cbuf_lock);
+ mutex_lock(&ov->cbuf_lock);
rc = usb_control_msg(ov->dev,
usb_rcvctrlpipe(ov->dev, 0),
(ov->bclass == BCL_OV518)?1:3 /* REG_IO */,
@@ -401,7 +401,7 @@ reg_r(struct usb_ov511 *ov, unsigned char reg)
PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]);
}
- up(&ov->cbuf_lock);
+ mutex_unlock(&ov->cbuf_lock);
return rc;
}
@@ -444,7 +444,7 @@ ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n)
PDEBUG(5, "0x%02X:%7d, n=%d", reg, val, n);
- down(&ov->cbuf_lock);
+ mutex_lock(&ov->cbuf_lock);
*((__le32 *)ov->cbuf) = __cpu_to_le32(val);
@@ -453,7 +453,7 @@ ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n)
1 /* REG_IO */,
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, (__u16)reg, ov->cbuf, n, 1000);
- up(&ov->cbuf_lock);
+ mutex_unlock(&ov->cbuf_lock);
if (rc < 0)
err("reg write multiple: error %d: %s", rc,
@@ -768,14 +768,14 @@ i2c_r(struct usb_ov511 *ov, unsigned char reg)
{
int rc;
- down(&ov->i2c_lock);
+ mutex_lock(&ov->i2c_lock);
if (ov->bclass == BCL_OV518)
rc = ov518_i2c_read_internal(ov, reg);
else
rc = ov511_i2c_read_internal(ov, reg);
- up(&ov->i2c_lock);
+ mutex_unlock(&ov->i2c_lock);
return rc;
}
@@ -785,14 +785,14 @@ i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value)
{
int rc;
- down(&ov->i2c_lock);
+ mutex_lock(&ov->i2c_lock);
if (ov->bclass == BCL_OV518)
rc = ov518_i2c_write_internal(ov, reg, value);
else
rc = ov511_i2c_write_internal(ov, reg, value);
- up(&ov->i2c_lock);
+ mutex_unlock(&ov->i2c_lock);
return rc;
}
@@ -842,9 +842,9 @@ i2c_w_mask(struct usb_ov511 *ov,
{
int rc;
- down(&ov->i2c_lock);
+ mutex_lock(&ov->i2c_lock);
rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask);
- up(&ov->i2c_lock);
+ mutex_unlock(&ov->i2c_lock);
return rc;
}
@@ -880,7 +880,7 @@ i2c_w_slave(struct usb_ov511 *ov,
{
int rc = 0;
- down(&ov->i2c_lock);
+ mutex_lock(&ov->i2c_lock);
/* Set new slave IDs */
rc = i2c_set_slave_internal(ov, slave);
@@ -894,7 +894,7 @@ out:
if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0)
err("Couldn't restore primary I2C slave");
- up(&ov->i2c_lock);
+ mutex_unlock(&ov->i2c_lock);
return rc;
}
@@ -906,7 +906,7 @@ i2c_r_slave(struct usb_ov511 *ov,
{
int rc;
- down(&ov->i2c_lock);
+ mutex_lock(&ov->i2c_lock);
/* Set new slave IDs */
rc = i2c_set_slave_internal(ov, slave);
@@ -923,7 +923,7 @@ out:
if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0)
err("Couldn't restore primary I2C slave");
- up(&ov->i2c_lock);
+ mutex_unlock(&ov->i2c_lock);
return rc;
}
@@ -933,7 +933,7 @@ ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid)
{
int rc;
- down(&ov->i2c_lock);
+ mutex_lock(&ov->i2c_lock);
rc = i2c_set_slave_internal(ov, sid);
if (rc < 0)
@@ -942,7 +942,7 @@ ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid)
// FIXME: Is this actually necessary?
rc = ov51x_reset(ov, OV511_RESET_NOREGS);
out:
- up(&ov->i2c_lock);
+ mutex_unlock(&ov->i2c_lock);
return rc;
}
@@ -3832,7 +3832,7 @@ ov51x_alloc(struct usb_ov511 *ov)
const int raw_bufsize = OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h);
PDEBUG(4, "entered");
- down(&ov->buf_lock);
+ mutex_lock(&ov->buf_lock);
if (ov->buf_state == BUF_ALLOCATED)
goto out;
@@ -3879,12 +3879,12 @@ ov51x_alloc(struct usb_ov511 *ov)
ov->buf_state = BUF_ALLOCATED;
out:
- up(&ov->buf_lock);
+ mutex_unlock(&ov->buf_lock);
PDEBUG(4, "leaving");
return 0;
error:
ov51x_do_dealloc(ov);
- up(&ov->buf_lock);
+ mutex_unlock(&ov->buf_lock);
PDEBUG(4, "errored");
return -ENOMEM;
}
@@ -3893,9 +3893,9 @@ static void
ov51x_dealloc(struct usb_ov511 *ov)
{
PDEBUG(4, "entered");
- down(&ov->buf_lock);
+ mutex_lock(&ov->buf_lock);
ov51x_do_dealloc(ov);
- up(&ov->buf_lock);
+ mutex_unlock(&ov->buf_lock);
PDEBUG(4, "leaving");
}
@@ -3914,7 +3914,7 @@ ov51x_v4l1_open(struct inode *inode, struct file *file)
PDEBUG(4, "opening");
- down(&ov->lock);
+ mutex_lock(&ov->lock);
err = -EBUSY;
if (ov->user)
@@ -3958,7 +3958,7 @@ ov51x_v4l1_open(struct inode *inode, struct file *file)
ov51x_led_control(ov, 1);
out:
- up(&ov->lock);
+ mutex_unlock(&ov->lock);
return err;
}
@@ -3970,7 +3970,7 @@ ov51x_v4l1_close(struct inode *inode, struct file *file)
PDEBUG(4, "ov511_close");
- down(&ov->lock);
+ mutex_lock(&ov->lock);
ov->user--;
ov51x_stop_isoc(ov);
@@ -3981,15 +3981,15 @@ ov51x_v4l1_close(struct inode *inode, struct file *file)
if (ov->dev)
ov51x_dealloc(ov);
- up(&ov->lock);
+ mutex_unlock(&ov->lock);
/* Device unplugged while open. Only a minimum of unregistration is done
* here; the disconnect callback already did the rest. */
if (!ov->dev) {
- down(&ov->cbuf_lock);
+ mutex_lock(&ov->cbuf_lock);
kfree(ov->cbuf);
ov->cbuf = NULL;
- up(&ov->cbuf_lock);
+ mutex_unlock(&ov->cbuf_lock);
ov51x_dealloc(ov);
kfree(ov);
@@ -4449,12 +4449,12 @@ ov51x_v4l1_ioctl(struct inode *inode, struct file *file,
struct usb_ov511 *ov = video_get_drvdata(vdev);
int rc;
- if (down_interruptible(&ov->lock))
+ if (mutex_lock_interruptible(&ov->lock))
return -EINTR;
rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal);
- up(&ov->lock);
+ mutex_unlock(&ov->lock);
return rc;
}
@@ -4468,7 +4468,7 @@ ov51x_v4l1_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos)
int i, rc = 0, frmx = -1;
struct ov511_frame *frame;
- if (down_interruptible(&ov->lock))
+ if (mutex_lock_interruptible(&ov->lock))
return -EINTR;
PDEBUG(4, "%ld bytes, noblock=%d", count, noblock);
@@ -4604,11 +4604,11 @@ restart:
PDEBUG(4, "read finished, returning %ld (sweet)", count);
- up(&ov->lock);
+ mutex_unlock(&ov->lock);
return count;
error:
- up(&ov->lock);
+ mutex_unlock(&ov->lock);
return rc;
}
@@ -4631,14 +4631,14 @@ ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma)
+ PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))))
return -EINVAL;
- if (down_interruptible(&ov->lock))
+ if (mutex_lock_interruptible(&ov->lock))
return -EINTR;
pos = (unsigned long)ov->fbuf;
while (size > 0) {
page = vmalloc_to_pfn((void *)pos);
if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
- up(&ov->lock);
+ mutex_unlock(&ov->lock);
return -EAGAIN;
}
start += PAGE_SIZE;
@@ -4649,7 +4649,7 @@ ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma)
size = 0;
}
- up(&ov->lock);
+ mutex_unlock(&ov->lock);
return 0;
}
@@ -5639,7 +5639,7 @@ static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
static ssize_t show_exposure(struct class_device *cd, char *buf)
{
struct usb_ov511 *ov = cd_to_ov(cd);
- unsigned char exp;
+ unsigned char exp = 0;
if (!ov->dev)
return -ENODEV;
@@ -5686,13 +5686,11 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (idesc->bInterfaceSubClass != 0x00)
return -ENODEV;
- if ((ov = kmalloc(sizeof(*ov), GFP_KERNEL)) == NULL) {
+ if ((ov = kzalloc(sizeof(*ov), GFP_KERNEL)) == NULL) {
err("couldn't kmalloc ov struct");
goto error_out;
}
- memset(ov, 0, sizeof(*ov));
-
ov->dev = dev;
ov->iface = idesc->bInterfaceNumber;
ov->led_policy = led;
@@ -5738,11 +5736,10 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id)
init_waitqueue_head(&ov->wq);
- init_MUTEX(&ov->lock); /* to 1 == available */
- init_MUTEX(&ov->buf_lock);
- init_MUTEX(&ov->param_lock);
- init_MUTEX(&ov->i2c_lock);
- init_MUTEX(&ov->cbuf_lock);
+ mutex_init(&ov->lock); /* to 1 == available */
+ mutex_init(&ov->buf_lock);
+ mutex_init(&ov->i2c_lock);
+ mutex_init(&ov->cbuf_lock);
ov->buf_state = BUF_NOT_ALLOCATED;
@@ -5833,10 +5830,10 @@ error:
}
if (ov->cbuf) {
- down(&ov->cbuf_lock);
+ mutex_lock(&ov->cbuf_lock);
kfree(ov->cbuf);
ov->cbuf = NULL;
- up(&ov->cbuf_lock);
+ mutex_unlock(&ov->cbuf_lock);
}
kfree(ov);
@@ -5881,10 +5878,10 @@ ov51x_disconnect(struct usb_interface *intf)
/* Free the memory */
if (ov && !ov->user) {
- down(&ov->cbuf_lock);
+ mutex_lock(&ov->cbuf_lock);
kfree(ov->cbuf);
ov->cbuf = NULL;
- up(&ov->cbuf_lock);
+ mutex_unlock(&ov->cbuf_lock);
ov51x_dealloc(ov);
kfree(ov);
diff --git a/drivers/usb/media/ov511.h b/drivers/usb/media/ov511.h
index 086509a137c..bce9b363388 100644
--- a/drivers/usb/media/ov511.h
+++ b/drivers/usb/media/ov511.h
@@ -5,6 +5,7 @@
#include <linux/videodev.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
+#include <linux/mutex.h>
#define OV511_DEBUG /* Turn on debug messages */
@@ -435,7 +436,7 @@ struct usb_ov511 {
int led_policy; /* LED: off|on|auto; OV511+ only */
- struct semaphore lock; /* Serializes user-accessible operations */
+ struct mutex lock; /* Serializes user-accessible operations */
int user; /* user count for exclusive use */
int streaming; /* Are we streaming Isochronous? */
@@ -473,11 +474,9 @@ struct usb_ov511 {
int packet_size; /* Frame size per isoc desc */
int packet_numbering; /* Is ISO frame numbering enabled? */
- struct semaphore param_lock; /* params lock for this camera */
-
/* Framebuffer/sbuf management */
int buf_state;
- struct semaphore buf_lock;
+ struct mutex buf_lock;
struct ov51x_decomp_ops *decomp_ops;
@@ -494,12 +493,12 @@ struct usb_ov511 {
int pal; /* Device is designed for PAL resolution */
/* I2C interface */
- struct semaphore i2c_lock; /* Protect I2C controller regs */
+ struct mutex i2c_lock; /* Protect I2C controller regs */
unsigned char primary_i2c_slave; /* I2C write id of sensor */
/* Control transaction stuff */
unsigned char *cbuf; /* Buffer for payload */
- struct semaphore cbuf_lock;
+ struct mutex cbuf_lock;
};
/* Used to represent a list of values and their respective symbolic names */
diff --git a/drivers/usb/media/pwc/pwc-ctrl.c b/drivers/usb/media/pwc/pwc-ctrl.c
index 3ebb6e9cdf9..0398b812e0c 100644
--- a/drivers/usb/media/pwc/pwc-ctrl.c
+++ b/drivers/usb/media/pwc/pwc-ctrl.c
@@ -41,7 +41,6 @@
#include <asm/uaccess.h>
#endif
#include <asm/errno.h>
-#include <linux/version.h>
#include "pwc.h"
#include "pwc-ioctl.h"
diff --git a/drivers/usb/media/pwc/pwc-if.c b/drivers/usb/media/pwc/pwc-if.c
index 4f9b0dc6fd7..90eb2604281 100644
--- a/drivers/usb/media/pwc/pwc-if.c
+++ b/drivers/usb/media/pwc/pwc-if.c
@@ -62,7 +62,6 @@
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
-#include <linux/version.h>
#include <asm/io.h>
#include "pwc.h"
@@ -827,13 +826,10 @@ static int pwc_isoc_init(struct pwc_device *pdev)
/* Get the current alternate interface, adjust packet size */
if (!udev->actconfig)
return -EFAULT;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)
- idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate];
-#else
+
intf = usb_ifnum_to_if(udev, 0);
if (intf)
idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
-#endif
if (!idesc)
return -EFAULT;
@@ -1871,12 +1867,11 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
Info("Warning: more than 1 configuration available.\n");
/* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
- pdev = kmalloc(sizeof(struct pwc_device), GFP_KERNEL);
+ pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
if (pdev == NULL) {
Err("Oops, could not allocate memory for pwc_device.\n");
return -ENOMEM;
}
- memset(pdev, 0, sizeof(struct pwc_device));
pdev->type = type_id;
pdev->vsize = default_size;
pdev->vframes = default_fps;
diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c
index 2ba562285fd..f03ea7f8959 100644
--- a/drivers/usb/media/se401.c
+++ b/drivers/usb/media/se401.c
@@ -1157,21 +1157,21 @@ static int se401_mmap(struct file *file, struct vm_area_struct *vma)
unsigned long size = vma->vm_end-vma->vm_start;
unsigned long page, pos;
- down(&se401->lock);
+ mutex_lock(&se401->lock);
if (se401->dev == NULL) {
- up(&se401->lock);
+ mutex_unlock(&se401->lock);
return -EIO;
}
if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
- up(&se401->lock);
+ mutex_unlock(&se401->lock);
return -EINVAL;
}
pos = (unsigned long)se401->fbuf;
while (size > 0) {
page = vmalloc_to_pfn((void *)pos);
if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
- up(&se401->lock);
+ mutex_unlock(&se401->lock);
return -EAGAIN;
}
start += PAGE_SIZE;
@@ -1181,7 +1181,7 @@ static int se401_mmap(struct file *file, struct vm_area_struct *vma)
else
size = 0;
}
- up(&se401->lock);
+ mutex_unlock(&se401->lock);
return 0;
}
@@ -1345,13 +1345,11 @@ static int se401_probe(struct usb_interface *intf,
/* We found one */
info("SE401 camera found: %s", camera_name);
- if ((se401 = kmalloc(sizeof(*se401), GFP_KERNEL)) == NULL) {
+ if ((se401 = kzalloc(sizeof(*se401), GFP_KERNEL)) == NULL) {
err("couldn't kmalloc se401 struct");
return -ENOMEM;
}
- memset(se401, 0, sizeof(*se401));
-
se401->dev = dev;
se401->iface = interface->bInterfaceNumber;
se401->camera_name = camera_name;
@@ -1366,7 +1364,7 @@ static int se401_probe(struct usb_interface *intf,
memcpy(&se401->vdev, &se401_template, sizeof(se401_template));
memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name));
init_waitqueue_head(&se401->wq);
- init_MUTEX(&se401->lock);
+ mutex_init(&se401->lock);
wmb();
if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
diff --git a/drivers/usb/media/se401.h b/drivers/usb/media/se401.h
index 2e5846f1eb2..e88a40d4c86 100644
--- a/drivers/usb/media/se401.h
+++ b/drivers/usb/media/se401.h
@@ -5,6 +5,7 @@
#include <asm/uaccess.h>
#include <linux/videodev.h>
#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#define se401_DEBUG /* Turn on debug messages */
@@ -189,7 +190,7 @@ struct usb_se401 {
int maxframesize;
int cframesize; /* current framesize */
- struct semaphore lock;
+ struct mutex lock;
int user; /* user count for exclusive use */
int removed; /* device disconnected */
diff --git a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h
index 17d60c1eea7..1d70a62b9f2 100644
--- a/drivers/usb/media/sn9c102.h
+++ b/drivers/usb/media/sn9c102.h
@@ -33,7 +33,9 @@
#include <linux/types.h>
#include <linux/param.h>
#include <linux/rwsem.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/stddef.h>
#include "sn9c102_sensor.h"
@@ -50,6 +52,7 @@
#define SN9C102_ALTERNATE_SETTING 8
#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS)
#define SN9C102_CTRL_TIMEOUT 300
+#define SN9C102_FRAME_TIMEOUT 2
/*****************************************************************************/
@@ -107,16 +110,17 @@ struct sn9c102_sysfs_attr {
struct sn9c102_module_param {
u8 force_munmap;
+ u16 frame_timeout;
};
-static DECLARE_MUTEX(sn9c102_sysfs_lock);
+static DEFINE_MUTEX(sn9c102_sysfs_lock);
static DECLARE_RWSEM(sn9c102_disconnect);
struct sn9c102_device {
struct video_device* v4ldev;
enum sn9c102_bridge bridge;
- struct sn9c102_sensor* sensor;
+ struct sn9c102_sensor sensor;
struct usb_device* usbdev;
struct urb* urb[SN9C102_URBS];
@@ -141,19 +145,28 @@ struct sn9c102_device {
enum sn9c102_dev_state state;
u8 users;
- struct semaphore dev_sem, fileop_sem;
+ struct mutex dev_mutex, fileop_mutex;
spinlock_t queue_lock;
wait_queue_head_t open, wait_frame, wait_stream;
};
/*****************************************************************************/
+struct sn9c102_device*
+sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id)
+{
+ if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id))
+ return cam;
+
+ return NULL;
+}
+
+
void
sn9c102_attach_sensor(struct sn9c102_device* cam,
struct sn9c102_sensor* sensor)
{
- cam->sensor = sensor;
- cam->sensor->usbdev = cam->usbdev;
+ memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor));
}
/*****************************************************************************/
@@ -196,7 +209,8 @@ do { \
#undef PDBG
#define PDBG(fmt, args...) \
-dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args)
+dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args)
#undef PDBGG
#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c
index c81397e4714..4c6cc639572 100644
--- a/drivers/usb/media/sn9c102_core.c
+++ b/drivers/usb/media/sn9c102_core.c
@@ -25,11 +25,9 @@
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <linux/string.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>
-#include <linux/stddef.h>
#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
@@ -49,8 +47,8 @@
#define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL"
-#define SN9C102_MODULE_VERSION "1:1.26"
-#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 26)
+#define SN9C102_MODULE_VERSION "1:1.27"
+#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 27)
/*****************************************************************************/
@@ -89,6 +87,15 @@ MODULE_PARM_DESC(force_munmap,
"\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
"\n");
+static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] =
+ SN9C102_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ "\n<n[,...]> Timeout for a video frame in seconds."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"."
+ "\n");
+
#ifdef SN9C102_DEBUG
static unsigned short debug = SN9C102_DEBUG_LEVEL;
module_param(debug, ushort, 0644);
@@ -128,8 +135,8 @@ static u32
sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
enum sn9c102_io_method io)
{
- struct v4l2_pix_format* p = &(cam->sensor->pix_format);
- struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
+ struct v4l2_pix_format* p = &(cam->sensor.pix_format);
+ struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
const size_t imagesize = cam->module_param.force_munmap ||
io == IO_READ ?
(p->width * p->height * p->priv) / 8 :
@@ -449,19 +456,13 @@ sn9c102_i2c_try_write(struct sn9c102_device* cam,
int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address)
{
- if (!cam->sensor)
- return -1;
-
- return sn9c102_i2c_try_read(cam, cam->sensor, address);
+ return sn9c102_i2c_try_read(cam, &cam->sensor, address);
}
int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
{
- if (!cam->sensor)
- return -1;
-
- return sn9c102_i2c_try_write(cam, cam->sensor, address, value);
+ return sn9c102_i2c_try_write(cam, &cam->sensor, address, value);
}
/*****************************************************************************/
@@ -505,7 +506,7 @@ sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
size_t eoflen = sizeof(sn9c102_eof_header_t), i;
unsigned j, n = sizeof(sn9c102_eof_header) / eoflen;
- if (cam->sensor->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+ if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
return NULL; /* EOF header does not exist in compressed data */
for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++)
@@ -535,7 +536,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
if ((*f))
(*f)->state = F_QUEUED;
DBG(3, "Stream interrupted");
- wake_up_interruptible(&cam->wait_stream);
+ wake_up(&cam->wait_stream);
}
if (cam->state & DEV_DISCONNECTED)
@@ -553,9 +554,9 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
(*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t,
frame);
- imagesize = (cam->sensor->pix_format.width *
- cam->sensor->pix_format.height *
- cam->sensor->pix_format.priv) / 8;
+ imagesize = (cam->sensor.pix_format.width *
+ cam->sensor.pix_format.height *
+ cam->sensor.pix_format.priv) / 8;
soflen = (cam->bridge) == BRIDGE_SN9C103 ?
sizeof(sn9c103_sof_header_t) :
@@ -579,7 +580,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
redo:
sof = sn9c102_find_sof_header(cam, pos, len);
- if (!sof) {
+ if (likely(!sof)) {
eof = sn9c102_find_eof_header(cam, pos, len);
if ((*f)->state == F_GRABBING) {
end_of_frame:
@@ -589,8 +590,9 @@ end_of_frame:
img = (eof > pos) ? eof - pos - 1 : 0;
if ((*f)->buf.bytesused+img > imagesize) {
- u32 b = (*f)->buf.bytesused + img -
- imagesize;
+ u32 b;
+ b = (*f)->buf.bytesused + img -
+ imagesize;
img = imagesize - (*f)->buf.bytesused;
DBG(3, "Expected EOF not found: "
"video frame cut");
@@ -608,9 +610,10 @@ end_of_frame:
(*f)->buf.bytesused += img;
if ((*f)->buf.bytesused == imagesize ||
- (cam->sensor->pix_format.pixelformat ==
+ (cam->sensor.pix_format.pixelformat ==
V4L2_PIX_FMT_SN9C10X && eof)) {
- u32 b = (*f)->buf.bytesused;
+ u32 b;
+ b = (*f)->buf.bytesused;
(*f)->state = F_DONE;
(*f)->buf.sequence= ++cam->frame_count;
spin_lock(&cam->queue_lock);
@@ -667,7 +670,7 @@ start_of_frame:
if (eof && eof < sof)
goto end_of_frame; /* (1) */
else {
- if (cam->sensor->pix_format.pixelformat ==
+ if (cam->sensor.pix_format.pixelformat ==
V4L2_PIX_FMT_SN9C10X) {
eof = sof - soflen;
goto end_of_frame;
@@ -808,20 +811,21 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam)
static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
{
- int err = 0;
+ long timeout;
cam->stream = STREAM_INTERRUPT;
- err = wait_event_timeout(cam->wait_stream,
- (cam->stream == STREAM_OFF) ||
- (cam->state & DEV_DISCONNECTED),
- SN9C102_URB_TIMEOUT);
+ timeout = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ SN9C102_URB_TIMEOUT);
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
- else if (err) {
+ else if (cam->stream != STREAM_OFF) {
cam->state |= DEV_MISCONFIGURED;
- DBG(1, "The camera is misconfigured. To use it, close and "
- "open /dev/video%d again.", cam->v4ldev->minor);
- return err;
+ DBG(1, "URB timeout reached. The camera is misconfigured. "
+ "To use it, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
}
return 0;
@@ -866,18 +870,18 @@ static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf)
struct sn9c102_device* cam;
ssize_t count;
- if (down_interruptible(&sn9c102_sysfs_lock))
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENODEV;
}
count = sprintf(buf, "%u\n", cam->sysfs.reg);
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return count;
}
@@ -890,18 +894,18 @@ sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len)
u8 index;
ssize_t count;
- if (down_interruptible(&sn9c102_sysfs_lock))
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENODEV;
}
index = sn9c102_strtou8(buf, len, &count);
if (index > 0x1f || !count) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -EINVAL;
}
@@ -910,7 +914,7 @@ sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len)
DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg);
DBG(3, "Written bytes: %zd", count);
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return count;
}
@@ -922,17 +926,17 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf)
ssize_t count;
int val;
- if (down_interruptible(&sn9c102_sysfs_lock))
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENODEV;
}
if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -EIO;
}
@@ -940,7 +944,7 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf)
DBG(3, "Read bytes: %zd", count);
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return count;
}
@@ -954,24 +958,24 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len)
ssize_t count;
int err;
- if (down_interruptible(&sn9c102_sysfs_lock))
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENODEV;
}
value = sn9c102_strtou8(buf, len, &count);
if (!count) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -EINVAL;
}
err = sn9c102_write_reg(cam, value, cam->sysfs.reg);
if (err) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -EIO;
}
@@ -979,7 +983,7 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len)
cam->sysfs.reg, value);
DBG(3, "Written bytes: %zd", count);
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return count;
}
@@ -990,12 +994,12 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf)
struct sn9c102_device* cam;
ssize_t count;
- if (down_interruptible(&sn9c102_sysfs_lock))
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENODEV;
}
@@ -1003,7 +1007,7 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf)
DBG(3, "Read bytes: %zd", count);
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return count;
}
@@ -1016,18 +1020,18 @@ sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
u8 index;
ssize_t count;
- if (down_interruptible(&sn9c102_sysfs_lock))
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENODEV;
}
index = sn9c102_strtou8(buf, len, &count);
if (!count) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -EINVAL;
}
@@ -1036,7 +1040,7 @@ sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
DBG(3, "Written bytes: %zd", count);
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return count;
}
@@ -1048,22 +1052,22 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
ssize_t count;
int val;
- if (down_interruptible(&sn9c102_sysfs_lock))
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENODEV;
}
- if (!(cam->sensor->sysfs_ops & SN9C102_I2C_READ)) {
- up(&sn9c102_sysfs_lock);
+ if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) {
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENOSYS;
}
if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -EIO;
}
@@ -1071,7 +1075,7 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
DBG(3, "Read bytes: %zd", count);
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return count;
}
@@ -1085,29 +1089,29 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
ssize_t count;
int err;
- if (down_interruptible(&sn9c102_sysfs_lock))
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENODEV;
}
- if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) {
- up(&sn9c102_sysfs_lock);
+ if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) {
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENOSYS;
}
value = sn9c102_strtou8(buf, len, &count);
if (!count) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -EINVAL;
}
err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value);
if (err) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -EIO;
}
@@ -1115,7 +1119,7 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
cam->sysfs.i2c_reg, value);
DBG(3, "Written bytes: %zd", count);
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return count;
}
@@ -1130,18 +1134,18 @@ sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
u8 value;
ssize_t count;
- if (down_interruptible(&sn9c102_sysfs_lock))
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
return -ERESTARTSYS;
cam = video_get_drvdata(to_video_device(cd));
if (!cam) {
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
return -ENODEV;
}
bridge = cam->bridge;
- up(&sn9c102_sysfs_lock);
+ mutex_unlock(&sn9c102_sysfs_lock);
value = sn9c102_strtou8(buf, len, &count);
if (!count)
@@ -1249,7 +1253,7 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam)
video_device_create_file(v4ldev, &class_device_attr_blue);
video_device_create_file(v4ldev, &class_device_attr_red);
}
- if (cam->sensor && cam->sensor->sysfs_ops) {
+ if (cam->sensor.sysfs_ops) {
video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
video_device_create_file(v4ldev, &class_device_attr_i2c_val);
}
@@ -1312,7 +1316,7 @@ static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
v_start = (u8)(rect->top - s->cropcap.bounds.top),
h_size = (u8)(rect->width / 16),
@@ -1335,7 +1339,7 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
static int sn9c102_init(struct sn9c102_device* cam)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
struct v4l2_queryctrl *qctrl;
struct v4l2_rect* rect;
@@ -1404,7 +1408,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
}
if (!(cam->state & DEV_INITIALIZED)) {
- init_MUTEX(&cam->fileop_sem);
+ mutex_init(&cam->fileop_mutex);
spin_lock_init(&cam->queue_lock);
init_waitqueue_head(&cam->wait_frame);
init_waitqueue_head(&cam->wait_stream);
@@ -1422,13 +1426,15 @@ static int sn9c102_init(struct sn9c102_device* cam)
static void sn9c102_release_resources(struct sn9c102_device* cam)
{
- down(&sn9c102_sysfs_lock);
+ mutex_lock(&sn9c102_sysfs_lock);
DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
- up(&sn9c102_sysfs_lock);
+ usb_put_dev(cam->usbdev);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
kfree(cam->control_buffer);
}
@@ -1449,7 +1455,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
cam = video_get_drvdata(video_devdata(filp));
- if (down_interruptible(&cam->dev_sem)) {
+ if (mutex_lock_interruptible(&cam->dev_mutex)) {
up_read(&sn9c102_disconnect);
return -ERESTARTSYS;
}
@@ -1461,7 +1467,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
err = -EWOULDBLOCK;
goto out;
}
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
err = wait_event_interruptible_exclusive(cam->open,
cam->state & DEV_DISCONNECTED
|| !cam->users);
@@ -1473,7 +1479,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
up_read(&sn9c102_disconnect);
return -ENODEV;
}
- down(&cam->dev_sem);
+ mutex_lock(&cam->dev_mutex);
}
@@ -1501,7 +1507,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
out:
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
up_read(&sn9c102_disconnect);
return err;
}
@@ -1511,7 +1517,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp)
{
struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
- down(&cam->dev_sem); /* prevent disconnect() to be called */
+ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
sn9c102_stop_transfer(cam);
@@ -1519,7 +1525,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp)
if (cam->state & DEV_DISCONNECTED) {
sn9c102_release_resources(cam);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
kfree(cam);
return 0;
}
@@ -1529,7 +1535,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp)
DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
return 0;
}
@@ -1541,35 +1547,36 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
struct sn9c102_frame_t* f, * i;
unsigned long lock_flags;
+ long timeout;
int err = 0;
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
if (cam->io == IO_MMAP) {
DBG(3, "Close and open the device again to choose "
"the read method");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EINVAL;
}
if (cam->io == IO_NONE) {
if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
DBG(1, "read() failed, not enough memory");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENOMEM;
}
cam->io = IO_READ;
@@ -1583,30 +1590,32 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
}
if (!count) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return 0;
}
if (list_empty(&cam->outqueue)) {
if (filp->f_flags & O_NONBLOCK) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EAGAIN;
}
- err = wait_event_interruptible
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED) );
- if (err) {
- up(&cam->fileop_sem);
- return err;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0) {
+ mutex_unlock(&cam->fileop_mutex);
+ return timeout;
}
if (cam->state & DEV_DISCONNECTED) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
- if (cam->state & DEV_MISCONFIGURED) {
- up(&cam->fileop_sem);
+ if (!timeout || (cam->state & DEV_MISCONFIGURED)) {
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
}
@@ -1634,7 +1643,7 @@ exit:
PDBGG("Frame #%lu, bytes read: %zu",
(unsigned long)f->buf.index, count);
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return count;
}
@@ -1647,7 +1656,7 @@ static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
unsigned long lock_flags;
unsigned int mask = 0;
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return POLLERR;
if (cam->state & DEV_DISCONNECTED) {
@@ -1685,12 +1694,12 @@ static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
if (!list_empty(&cam->outqueue))
mask |= POLLIN | POLLRDNORM;
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return mask;
error:
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return POLLERR;
}
@@ -1724,25 +1733,25 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
void *pos;
u32 i;
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
size != PAGE_ALIGN(cam->frame[0].buf.length)) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EINVAL;
}
@@ -1751,7 +1760,7 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
break;
}
if (i == cam->nbuffers) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EINVAL;
}
@@ -1761,7 +1770,7 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
pos = cam->frame[i].bufmem;
while (size > 0) { /* size is page-aligned */
if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EAGAIN;
}
start += PAGE_SIZE;
@@ -1774,7 +1783,7 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
sn9c102_vm_open(vma);
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return 0;
}
@@ -1816,6 +1825,7 @@ sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg)
memset(&i, 0, sizeof(i));
strcpy(i.name, "Camera");
+ i.type = V4L2_INPUT_TYPE_CAMERA;
if (copy_to_user(arg, &i, sizeof(i)))
return -EFAULT;
@@ -1825,7 +1835,19 @@ sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg)
static int
-sn9c102_vidioc_gs_input(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg)
+{
+ int index = 0;
+
+ if (copy_to_user(arg, &index, sizeof(index)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg)
{
int index;
@@ -1842,7 +1864,7 @@ sn9c102_vidioc_gs_input(struct sn9c102_device* cam, void __user * arg)
static int
sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_queryctrl qc;
u8 i;
@@ -1864,7 +1886,7 @@ sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
static int
sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
int err = 0;
u8 i;
@@ -1896,7 +1918,7 @@ exit:
static int
sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_control ctrl;
u8 i;
int err = 0;
@@ -1909,6 +1931,8 @@ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
if (ctrl.value < s->qctrl[i].minimum ||
ctrl.value > s->qctrl[i].maximum)
return -ERANGE;
@@ -1931,7 +1955,7 @@ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
static int
sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
{
- struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
+ struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cc->pixelaspect.numerator = 1;
@@ -1947,7 +1971,7 @@ sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
static int
sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_crop crop = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
};
@@ -1964,7 +1988,7 @@ sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
static int
sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_crop crop;
struct v4l2_rect* rect;
struct v4l2_rect* bounds = &(s->cropcap.bounds);
@@ -2105,7 +2129,7 @@ static int
sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
{
struct v4l2_format format;
- struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
if (copy_from_user(&format, arg, sizeof(format)))
return -EFAULT;
@@ -2130,7 +2154,7 @@ static int
sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
void __user * arg)
{
- struct sn9c102_sensor* s = cam->sensor;
+ struct sn9c102_sensor* s = &cam->sensor;
struct v4l2_format format;
struct v4l2_pix_format* pix;
struct v4l2_pix_format* pfmt = &(s->pix_format);
@@ -2417,7 +2441,7 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
struct v4l2_buffer b;
struct sn9c102_frame_t *f;
unsigned long lock_flags;
- int err = 0;
+ long timeout;
if (copy_from_user(&b, arg, sizeof(b)))
return -EFAULT;
@@ -2430,16 +2454,18 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
return -EINVAL;
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
- err = wait_event_interruptible
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED) );
- if (err)
- return err;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
if (cam->state & DEV_DISCONNECTED)
return -ENODEV;
- if (cam->state & DEV_MISCONFIGURED)
+ if (!timeout || (cam->state & DEV_MISCONFIGURED))
return -EIO;
}
@@ -2571,8 +2597,10 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
return sn9c102_vidioc_enuminput(cam, arg);
case VIDIOC_G_INPUT:
+ return sn9c102_vidioc_g_input(cam, arg);
+
case VIDIOC_S_INPUT:
- return sn9c102_vidioc_gs_input(cam, arg);
+ return sn9c102_vidioc_s_input(cam, arg);
case VIDIOC_QUERYCTRL:
return sn9c102_vidioc_query_ctrl(cam, arg);
@@ -2655,19 +2683,19 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp,
struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
int err = 0;
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->state & DEV_DISCONNECTED) {
DBG(1, "Device not present");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
if (cam->state & DEV_MISCONFIGURED) {
DBG(1, "The camera is misconfigured. Close and open it "
"again.");
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
@@ -2675,7 +2703,7 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp,
err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return err;
}
@@ -2722,7 +2750,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
goto fail;
}
- init_MUTEX(&cam->dev_sem);
+ mutex_init(&cam->dev_mutex);
r = sn9c102_read_reg(cam, 0x00);
if (r < 0 || r != 0x10) {
@@ -2752,10 +2780,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
break;
}
- if (!err && cam->sensor) {
- DBG(2, "%s image sensor detected", cam->sensor->name);
+ if (!err) {
+ DBG(2, "%s image sensor detected", cam->sensor.name);
DBG(3, "Support for %s maintained by %s",
- cam->sensor->name, cam->sensor->maintainer);
+ cam->sensor.name, cam->sensor.maintainer);
} else {
DBG(1, "No supported image sensor detected");
err = -ENODEV;
@@ -2776,7 +2804,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
cam->v4ldev->release = video_device_release;
video_set_drvdata(cam->v4ldev, cam);
- down(&cam->dev_sem);
+ mutex_lock(&cam->dev_mutex);
err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
video_nr[dev_nr]);
@@ -2786,13 +2814,14 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
DBG(1, "Free /dev/videoX node not found");
video_nr[dev_nr] = -1;
dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
goto fail;
}
DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
@@ -2803,7 +2832,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
usb_set_intfdata(intf, cam);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
return 0;
@@ -2827,7 +2856,7 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf)
down_write(&sn9c102_disconnect);
- down(&cam->dev_sem);
+ mutex_lock(&cam->dev_mutex);
DBG(2, "Disconnecting %s...", cam->v4ldev->name);
@@ -2841,13 +2870,14 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf)
sn9c102_stop_transfer(cam);
cam->state |= DEV_DISCONNECTED;
wake_up_interruptible(&cam->wait_frame);
- wake_up_interruptible(&cam->wait_stream);
+ wake_up(&cam->wait_stream);
+ usb_get_dev(cam->usbdev);
} else {
cam->state |= DEV_DISCONNECTED;
sn9c102_release_resources(cam);
}
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
if (!cam->users)
kfree(cam);
diff --git a/drivers/usb/media/sn9c102_ov7630.c b/drivers/usb/media/sn9c102_ov7630.c
index 4a36519b5af..42852b7cb04 100644
--- a/drivers/usb/media/sn9c102_ov7630.c
+++ b/drivers/usb/media/sn9c102_ov7630.c
@@ -34,8 +34,8 @@ static int ov7630_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x0f, 0x18);
err += sn9c102_write_reg(cam, 0x50, 0x19);
- err += sn9c102_i2c_write(cam, 0x12, 0x8d);
- err += sn9c102_i2c_write(cam, 0x11, 0x00);
+ err += sn9c102_i2c_write(cam, 0x12, 0x80);
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
err += sn9c102_i2c_write(cam, 0x15, 0x34);
err += sn9c102_i2c_write(cam, 0x16, 0x03);
err += sn9c102_i2c_write(cam, 0x17, 0x1c);
@@ -43,12 +43,14 @@ static int ov7630_init(struct sn9c102_device* cam)
err += sn9c102_i2c_write(cam, 0x19, 0x06);
err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
err += sn9c102_i2c_write(cam, 0x1b, 0x04);
- err += sn9c102_i2c_write(cam, 0x20, 0x44);
+ err += sn9c102_i2c_write(cam, 0x20, 0xf6);
err += sn9c102_i2c_write(cam, 0x23, 0xee);
err += sn9c102_i2c_write(cam, 0x26, 0xa0);
err += sn9c102_i2c_write(cam, 0x27, 0x9a);
- err += sn9c102_i2c_write(cam, 0x28, 0x20);
+ err += sn9c102_i2c_write(cam, 0x28, 0xa0);
err += sn9c102_i2c_write(cam, 0x29, 0x30);
+ err += sn9c102_i2c_write(cam, 0x2a, 0xa0);
+ err += sn9c102_i2c_write(cam, 0x2b, 0x1f);
err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
err += sn9c102_i2c_write(cam, 0x30, 0x24);
err += sn9c102_i2c_write(cam, 0x32, 0x86);
@@ -80,7 +82,7 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam,
err += sn9c102_i2c_write(cam, 0x02, ctrl->value);
break;
case V4L2_CID_BLUE_BALANCE:
- err += sn9c102_i2c_write(cam, 0x03, ctrl->value);
+ err += sn9c102_i2c_write(cam, 0x01, ctrl->value);
break;
case V4L2_CID_GAIN:
err += sn9c102_i2c_write(cam, 0x00, ctrl->value);
@@ -108,7 +110,7 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam,
err += sn9c102_i2c_write(cam, 0x0d, ctrl->value);
break;
case V4L2_CID_AUTO_WHITE_BALANCE:
- err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x09);
+ err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x78);
break;
case V4L2_CID_AUTOGAIN:
err += sn9c102_i2c_write(cam, 0x13, ctrl->value);
@@ -371,26 +373,29 @@ static struct sn9c102_sensor ov7630 = {
int sn9c102_probe_ov7630(struct sn9c102_device* cam)
{
+ const struct usb_device_id ov7630_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x602c), },
+ { USB_DEVICE(0x0c45, 0x602d), },
+ { USB_DEVICE(0x0c45, 0x608f), },
+ { USB_DEVICE(0x0c45, 0x60b0), },
+ { }
+ };
int err = 0;
- sn9c102_attach_sensor(cam, &ov7630);
-
- if (le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602c &&
- le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602d &&
- le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x608f &&
- le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x60b0)
+ if (!sn9c102_match_id(cam, ov7630_id_table))
return -ENODEV;
err += sn9c102_write_reg(cam, 0x01, 0x01);
err += sn9c102_write_reg(cam, 0x00, 0x01);
err += sn9c102_write_reg(cam, 0x28, 0x17);
-
if (err)
return -EIO;
- err += sn9c102_i2c_write(cam, 0x0b, 0);
+ err += sn9c102_i2c_try_write(cam, &ov7630, 0x0b, 0);
if (err)
return -ENODEV;
+ sn9c102_attach_sensor(cam, &ov7630);
+
return 0;
}
diff --git a/drivers/usb/media/sn9c102_pas202bca.c b/drivers/usb/media/sn9c102_pas202bca.c
new file mode 100644
index 00000000000..3453237055b
--- /dev/null
+++ b/drivers/usb/media/sn9c102_pas202bca.c
@@ -0,0 +1,238 @@
+/***************************************************************************
+ * Plug-in for PAS202BCA image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/delay.h>
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor pas202bca;
+
+
+static int pas202bca_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x00, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+ err += sn9c102_write_reg(cam, 0x30, 0x19);
+ err += sn9c102_write_reg(cam, 0x09, 0x18);
+
+ err += sn9c102_i2c_write(cam, 0x02, 0x14);
+ err += sn9c102_i2c_write(cam, 0x03, 0x40);
+ err += sn9c102_i2c_write(cam, 0x0d, 0x2c);
+ err += sn9c102_i2c_write(cam, 0x0e, 0x01);
+ err += sn9c102_i2c_write(cam, 0x0f, 0xa9);
+ err += sn9c102_i2c_write(cam, 0x10, 0x08);
+ err += sn9c102_i2c_write(cam, 0x13, 0x63);
+ err += sn9c102_i2c_write(cam, 0x15, 0x70);
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+
+ msleep(400);
+
+ return err;
+}
+
+
+static int pas202bca_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x24, 0x17);
+ else
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+
+ return err;
+}
+
+
+static int pas202bca_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6);
+ err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x07, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x08, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+
+ return err ? -EIO : 0;
+}
+
+
+static int pas202bca_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &pas202bca;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor pas202bca = {
+ .name = "PAS202BCA",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x40,
+ .init = &pas202bca_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x01e5,
+ .maximum = 0x3fff,
+ .step = 0x0001,
+ .default_value = 0x01e5,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x0c,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x05,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x04,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &pas202bca_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &pas202bca_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &pas202bca_set_pix_format
+};
+
+
+int sn9c102_probe_pas202bca(struct sn9c102_device* cam)
+{
+ const struct usb_device_id pas202bca_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x60af), },
+ { }
+ };
+ int err = 0;
+
+ if (!sn9c102_match_id(cam,pas202bca_id_table))
+ return -ENODEV;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x40, 0x01);
+ err += sn9c102_write_reg(cam, 0x28, 0x17);
+ if (err)
+ return -EIO;
+
+ if (sn9c102_i2c_try_write(cam, &pas202bca, 0x10, 0)) /* try to write */
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &pas202bca);
+
+ return 0;
+}
diff --git a/drivers/usb/media/sn9c102_pas202bcb.c b/drivers/usb/media/sn9c102_pas202bcb.c
index 5ca54c7daaf..d068616ab33 100644
--- a/drivers/usb/media/sn9c102_pas202bcb.c
+++ b/drivers/usb/media/sn9c102_pas202bcb.c
@@ -263,7 +263,7 @@ static struct sn9c102_sensor pas202bcb = {
int sn9c102_probe_pas202bcb(struct sn9c102_device* cam)
-{
+{
int r0 = 0, r1 = 0, err = 0;
unsigned int pid = 0;
diff --git a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h
index 7d953b24f2f..2afd9e9d09b 100644
--- a/drivers/usb/media/sn9c102_sensor.h
+++ b/drivers/usb/media/sn9c102_sensor.h
@@ -66,6 +66,7 @@ extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam);
extern int sn9c102_probe_mi0343(struct sn9c102_device* cam);
extern int sn9c102_probe_ov7630(struct sn9c102_device* cam);
extern int sn9c102_probe_pas106b(struct sn9c102_device* cam);
+extern int sn9c102_probe_pas202bca(struct sn9c102_device* cam);
extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam);
extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam);
extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
@@ -81,12 +82,17 @@ static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \
&sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ \
&sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ \
&sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ \
+ &sn9c102_probe_pas202bca, /* detection mostly based on USB pid/vid */ \
&sn9c102_probe_ov7630, /* detection mostly based on USB pid/vid */ \
&sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \
&sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \
NULL, \
};
+/* Device identification */
+extern struct sn9c102_device*
+sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id);
+
/* Attach a probed sensor to the camera. */
extern void
sn9c102_attach_sensor(struct sn9c102_device* cam,
@@ -108,6 +114,7 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
static const struct usb_device_id sn9c102_id_table[] = { \
{ USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \
{ USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6007), }, \
{ USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \
{ USB_DEVICE(0x0c45, 0x6024), }, \
@@ -126,7 +133,7 @@ static const struct usb_device_id sn9c102_id_table[] = { \
{ SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), }, \
{ SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), }, \
{ SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), }, \
- { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131x */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131/R */ \
{ SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */ \
{ SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */ \
{ SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), }, \
@@ -359,12 +366,6 @@ struct sn9c102_sensor {
error code without rolling back.
*/
- const struct usb_device* usbdev;
- /*
- Points to the usb_device struct after the sensor is attached.
- Do not touch unless you know what you are doing.
- */
-
/*
Do NOT write to the data below, it's READ ONLY. It is used by the
core module to store successfully updated values of the above
diff --git a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c
index 32ddf236caf..2e08c552f40 100644
--- a/drivers/usb/media/sn9c102_tas5110c1b.c
+++ b/drivers/usb/media/sn9c102_tas5110c1b.c
@@ -142,14 +142,18 @@ static struct sn9c102_sensor tas5110c1b = {
int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
{
- /* This sensor has no identifiers, so let's attach it anyway */
- sn9c102_attach_sensor(cam, &tas5110c1b);
+ const struct usb_device_id tas5110c1b_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6001), },
+ { USB_DEVICE(0x0c45, 0x6005), },
+ { USB_DEVICE(0x0c45, 0x60ab), },
+ { }
+ };
/* Sensor detection is based on USB pid/vid */
- if (le16_to_cpu(tas5110c1b.usbdev->descriptor.idProduct) != 0x6001 &&
- le16_to_cpu(tas5110c1b.usbdev->descriptor.idProduct) != 0x6005 &&
- le16_to_cpu(tas5110c1b.usbdev->descriptor.idProduct) != 0x60ab)
+ if (!sn9c102_match_id(cam, tas5110c1b_id_table))
return -ENODEV;
+ sn9c102_attach_sensor(cam, &tas5110c1b);
+
return 0;
}
diff --git a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c
index a0728f0ae00..c7b339740bb 100644
--- a/drivers/usb/media/sn9c102_tas5130d1b.c
+++ b/drivers/usb/media/sn9c102_tas5130d1b.c
@@ -153,13 +153,17 @@ static struct sn9c102_sensor tas5130d1b = {
int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
{
- /* This sensor has no identifiers, so let's attach it anyway */
- sn9c102_attach_sensor(cam, &tas5130d1b);
+ const struct usb_device_id tas5130d1b_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6025), },
+ { USB_DEVICE(0x0c45, 0x60aa), },
+ { }
+ };
/* Sensor detection is based on USB pid/vid */
- if (le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x6025 &&
- le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x60aa)
+ if (!sn9c102_match_id(cam, tas5130d1b_id_table))
return -ENODEV;
+ sn9c102_attach_sensor(cam, &tas5130d1b);
+
return 0;
}
diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c
index b497a6a0a20..9636da20748 100644
--- a/drivers/usb/media/stv680.c
+++ b/drivers/usb/media/stv680.c
@@ -67,6 +67,7 @@
#include <linux/errno.h>
#include <linux/videodev.h>
#include <linux/usb.h>
+#include <linux/mutex.h>
#include "stv680.h"
@@ -317,12 +318,11 @@ static int stv_init (struct usb_stv *stv680)
unsigned char *buffer;
unsigned long int bufsize;
- buffer = kmalloc (40, GFP_KERNEL);
+ buffer = kzalloc (40, GFP_KERNEL);
if (buffer == NULL) {
PDEBUG (0, "STV(e): Out of (small buf) memory");
return -1;
}
- memset (buffer, 0, 40);
udelay (100);
/* set config 1, interface 0, alternate 0 */
@@ -1258,22 +1258,22 @@ static int stv680_mmap (struct file *file, struct vm_area_struct *vma)
unsigned long size = vma->vm_end-vma->vm_start;
unsigned long page, pos;
- down (&stv680->lock);
+ mutex_lock(&stv680->lock);
if (stv680->udev == NULL) {
- up (&stv680->lock);
+ mutex_unlock(&stv680->lock);
return -EIO;
}
if (size > (((STV680_NUMFRAMES * stv680->maxframesize) + PAGE_SIZE - 1)
& ~(PAGE_SIZE - 1))) {
- up (&stv680->lock);
+ mutex_unlock(&stv680->lock);
return -EINVAL;
}
pos = (unsigned long) stv680->fbuf;
while (size > 0) {
page = vmalloc_to_pfn((void *)pos);
if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
- up (&stv680->lock);
+ mutex_unlock(&stv680->lock);
return -EAGAIN;
}
start += PAGE_SIZE;
@@ -1283,7 +1283,7 @@ static int stv680_mmap (struct file *file, struct vm_area_struct *vma)
else
size = 0;
}
- up (&stv680->lock);
+ mutex_unlock(&stv680->lock);
return 0;
}
@@ -1387,14 +1387,12 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id
goto error;
}
/* We found one */
- if ((stv680 = kmalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) {
+ if ((stv680 = kzalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) {
PDEBUG (0, "STV(e): couldn't kmalloc stv680 struct.");
retval = -ENOMEM;
goto error;
}
- memset (stv680, 0, sizeof (*stv680));
-
stv680->udev = dev;
stv680->camera_name = camera_name;
@@ -1409,7 +1407,7 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id
memcpy (stv680->vdev->name, stv680->camera_name, strlen (stv680->camera_name));
init_waitqueue_head (&stv680->wq);
- init_MUTEX (&stv680->lock);
+ mutex_init (&stv680->lock);
wmb ();
if (video_register_device (stv680->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
diff --git a/drivers/usb/media/stv680.h b/drivers/usb/media/stv680.h
index b0551cdb280..ea46e0001e6 100644
--- a/drivers/usb/media/stv680.h
+++ b/drivers/usb/media/stv680.h
@@ -118,7 +118,7 @@ struct usb_stv {
int origGain;
int origMode; /* original camera mode */
- struct semaphore lock; /* to lock the structure */
+ struct mutex lock; /* to lock the structure */
int user; /* user count for exclusive use */
int removed; /* device disconnected */
int streaming; /* Are we streaming video? */
diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c
index 63a72e550a1..0b51fae720a 100644
--- a/drivers/usb/media/usbvideo.c
+++ b/drivers/usb/media/usbvideo.c
@@ -690,14 +690,13 @@ int usbvideo_register(
}
base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo);
- cams = (struct usbvideo *) kmalloc(base_size, GFP_KERNEL);
+ cams = (struct usbvideo *) kzalloc(base_size, GFP_KERNEL);
if (cams == NULL) {
err("Failed to allocate %d. bytes for usbvideo struct", base_size);
return -ENOMEM;
}
dbg("%s: Allocated $%p (%d. bytes) for %d. cameras",
__FUNCTION__, cams, base_size, num_cams);
- memset(cams, 0, base_size);
/* Copy callbacks, apply defaults for those that are not set */
memmove(&cams->cb, cbTbl, sizeof(cams->cb));
@@ -715,7 +714,7 @@ int usbvideo_register(
cams->md_module = md;
if (cams->md_module == NULL)
warn("%s: module == NULL!", __FUNCTION__);
- init_MUTEX(&cams->lock); /* to 1 == available */
+ mutex_init(&cams->lock); /* to 1 == available */
for (i = 0; i < num_cams; i++) {
struct uvd *up = &cams->cam[i];
@@ -863,7 +862,7 @@ static void usbvideo_Disconnect(struct usb_interface *intf)
if (uvd->debug > 0)
info("%s(%p.)", __FUNCTION__, intf);
- down(&uvd->lock);
+ mutex_lock(&uvd->lock);
uvd->remove_pending = 1; /* Now all ISO data will be ignored */
/* At this time we ask to cancel outstanding URBs */
@@ -883,7 +882,7 @@ static void usbvideo_Disconnect(struct usb_interface *intf)
info("%s: In use, disconnect pending.", __FUNCTION__);
else
usbvideo_CameraRelease(uvd);
- up(&uvd->lock);
+ mutex_unlock(&uvd->lock);
info("USB camera disconnected.");
usbvideo_ClientDecModCount(uvd);
@@ -930,19 +929,19 @@ static int usbvideo_find_struct(struct usbvideo *cams)
err("No usbvideo handle?");
return -1;
}
- down(&cams->lock);
+ mutex_lock(&cams->lock);
for (u = 0; u < cams->num_cameras; u++) {
struct uvd *uvd = &cams->cam[u];
if (!uvd->uvd_used) /* This one is free */
{
uvd->uvd_used = 1; /* In use now */
- init_MUTEX(&uvd->lock); /* to 1 == available */
+ mutex_init(&uvd->lock); /* to 1 == available */
uvd->dev = NULL;
rv = u;
break;
}
}
- up(&cams->lock);
+ mutex_unlock(&cams->lock);
return rv;
}
@@ -984,7 +983,7 @@ struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams)
/* Not relying upon caller we increase module counter ourselves */
usbvideo_ClientIncModCount(uvd);
- down(&uvd->lock);
+ mutex_lock(&uvd->lock);
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
if (uvd->sbuf[i].urb == NULL) {
@@ -1007,7 +1006,7 @@ struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams)
* return control to the client's probe function right now.
*/
allocate_done:
- up (&uvd->lock);
+ mutex_unlock(&uvd->lock);
usbvideo_ClientDecModCount(uvd);
return uvd;
}
@@ -1121,7 +1120,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file)
info("%s($%p)", __FUNCTION__, dev);
usbvideo_ClientIncModCount(uvd);
- down(&uvd->lock);
+ mutex_lock(&uvd->lock);
if (uvd->user) {
err("%s: Someone tried to open an already opened device!", __FUNCTION__);
@@ -1202,7 +1201,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file)
}
}
}
- up(&uvd->lock);
+ mutex_unlock(&uvd->lock);
if (errCode != 0)
usbvideo_ClientDecModCount(uvd);
if (uvd->debug > 0)
@@ -1231,7 +1230,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file)
if (uvd->debug > 1)
info("%s($%p)", __FUNCTION__, dev);
- down(&uvd->lock);
+ mutex_lock(&uvd->lock);
GET_CALLBACK(uvd, stopDataPump)(uvd);
usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size);
uvd->fbuf = NULL;
@@ -1252,7 +1251,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file)
info("usbvideo_v4l_close: Final disconnect.");
usbvideo_CameraRelease(uvd);
}
- up(&uvd->lock);
+ mutex_unlock(&uvd->lock);
usbvideo_ClientDecModCount(uvd);
if (uvd->debug > 1)
@@ -1512,7 +1511,7 @@ static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf,
if (uvd->debug >= 1)
info("%s: %Zd. bytes, noblock=%d.", __FUNCTION__, count, noblock);
- down(&uvd->lock);
+ mutex_lock(&uvd->lock);
/* See if a frame is completed, then use it. */
for(i = 0; i < USBVIDEO_NUMFRAMES; i++) {
@@ -1644,7 +1643,7 @@ static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf,
}
}
read_done:
- up(&uvd->lock);
+ mutex_unlock(&uvd->lock);
return count;
}
diff --git a/drivers/usb/media/usbvideo.h b/drivers/usb/media/usbvideo.h
index 6c390a1f981..135433c2680 100644
--- a/drivers/usb/media/usbvideo.h
+++ b/drivers/usb/media/usbvideo.h
@@ -19,6 +19,7 @@
#include <linux/config.h>
#include <linux/videodev.h>
#include <linux/usb.h>
+#include <linux/mutex.h>
/* Most helpful debugging aid */
#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
@@ -213,7 +214,7 @@ struct uvd {
unsigned long flags; /* FLAGS_USBVIDEO_xxx */
unsigned long paletteBits; /* Which palettes we accept? */
unsigned short defaultPalette; /* What palette to use for read() */
- struct semaphore lock;
+ struct mutex lock;
int user; /* user count for exclusive use */
videosize_t videosize; /* Current setting */
@@ -272,7 +273,7 @@ struct usbvideo {
int num_cameras; /* As allocated */
struct usb_driver usbdrv; /* Interface to the USB stack */
char drvName[80]; /* Driver name */
- struct semaphore lock; /* Mutex protecting camera structures */
+ struct mutex lock; /* Mutex protecting camera structures */
struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */
struct video_device vdt; /* Video device template */
struct uvd *cam; /* Array of camera structures */
diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c
index 5df14407387..1d06e53ec7c 100644
--- a/drivers/usb/media/vicam.c
+++ b/drivers/usb/media/vicam.c
@@ -42,6 +42,7 @@
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
+#include <linux/mutex.h>
#include "usbvideo.h"
// #define VICAM_DEBUG
@@ -407,7 +408,7 @@ struct vicam_camera {
struct usb_device *udev; // usb device
/* guard against simultaneous accesses to the camera */
- struct semaphore cam_lock;
+ struct mutex cam_lock;
int is_initialized;
u8 open_count;
@@ -461,12 +462,12 @@ static int send_control_msg(struct vicam_camera *cam,
u16 size)
{
int status = -ENODEV;
- down(&cam->cam_lock);
+ mutex_lock(&cam->cam_lock);
if (cam->udev) {
status = __send_control_msg(cam, request, value,
index, cp, size);
}
- up(&cam->cam_lock);
+ mutex_unlock(&cam->cam_lock);
return status;
}
static int
@@ -763,6 +764,7 @@ vicam_open(struct inode *inode, struct file *file)
if (!cam) {
printk(KERN_ERR
"vicam video_device improperly initialized");
+ return -EINVAL;
}
/* the videodev_lock held above us protects us from
@@ -831,13 +833,13 @@ vicam_close(struct inode *inode, struct file *file)
rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
kfree(cam->cntrlbuf);
- down(&cam->cam_lock);
+ mutex_lock(&cam->cam_lock);
cam->open_count--;
open_count = cam->open_count;
udev = cam->udev;
- up(&cam->cam_lock);
+ mutex_unlock(&cam->cam_lock);
if (!open_count && !udev) {
kfree(cam);
@@ -960,7 +962,7 @@ read_frame(struct vicam_camera *cam, int framenum)
request[8] = 0;
// bytes 9-15 do not seem to affect exposure or image quality
- down(&cam->cam_lock);
+ mutex_lock(&cam->cam_lock);
if (!cam->udev) {
goto done;
@@ -985,7 +987,7 @@ read_frame(struct vicam_camera *cam, int framenum)
}
done:
- up(&cam->cam_lock);
+ mutex_unlock(&cam->cam_lock);
}
static ssize_t
@@ -1309,7 +1311,7 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id)
cam->shutter_speed = 15;
- init_MUTEX(&cam->cam_lock);
+ mutex_init(&cam->cam_lock);
memcpy(&cam->vdev, &vicam_template,
sizeof (vicam_template));
@@ -1351,7 +1353,7 @@ vicam_disconnect(struct usb_interface *intf)
/* stop the camera from being used */
- down(&cam->cam_lock);
+ mutex_lock(&cam->cam_lock);
/* mark the camera as gone */
@@ -1368,7 +1370,7 @@ vicam_disconnect(struct usb_interface *intf)
open_count = cam->open_count;
- up(&cam->cam_lock);
+ mutex_unlock(&cam->cam_lock);
if (!open_count) {
kfree(cam);
diff --git a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c
index 9937fc64c8b..b57dec3782e 100644
--- a/drivers/usb/media/w9968cf.c
+++ b/drivers/usb/media/w9968cf.c
@@ -47,6 +47,13 @@
#include "w9968cf.h"
#include "w9968cf_decoder.h"
+static struct w9968cf_vpp_t* w9968cf_vpp;
+static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait);
+
+static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */
+static DEFINE_MUTEX(w9968cf_devlist_mutex); /* semaphore for list traversal */
+
+static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */
/****************************************************************************
@@ -695,13 +702,12 @@ static int w9968cf_allocate_memory(struct w9968cf_device* cam)
/* Allocate memory for the isochronous transfer buffers */
for (i = 0; i < W9968CF_URBS; i++) {
if (!(cam->transfer_buffer[i] =
- kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) {
+ kzalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) {
DBG(1, "Couldn't allocate memory for the isochronous "
"transfer buffers (%u bytes)",
p_size * W9968CF_ISO_PACKETS)
return -ENOMEM;
}
- memset(cam->transfer_buffer[i], 0, W9968CF_ISO_PACKETS*p_size);
}
/* Allocate memory for the temporary frame buffer */
@@ -2419,7 +2425,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
enum w9968cf_model_id mod_id,
const unsigned short dev_nr)
{
- init_MUTEX(&cam->fileop_sem);
+ mutex_init(&cam->fileop_mutex);
init_waitqueue_head(&cam->open);
spin_lock_init(&cam->urb_lock);
spin_lock_init(&cam->flist_lock);
@@ -2647,7 +2653,7 @@ static void w9968cf_adjust_configuration(struct w9968cf_device* cam)
--------------------------------------------------------------------------*/
static void w9968cf_release_resources(struct w9968cf_device* cam)
{
- down(&w9968cf_devlist_sem);
+ mutex_lock(&w9968cf_devlist_mutex);
DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev->minor)
@@ -2658,7 +2664,7 @@ static void w9968cf_release_resources(struct w9968cf_device* cam)
kfree(cam->control_buffer);
kfree(cam->data_buffer);
- up(&w9968cf_devlist_sem);
+ mutex_unlock(&w9968cf_devlist_mutex);
}
@@ -2678,14 +2684,14 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
- down(&cam->dev_sem);
+ mutex_lock(&cam->dev_mutex);
if (cam->sensor == CC_UNKNOWN) {
DBG(2, "No supported image sensor has been detected by the "
"'ovcamchip' module for the %s (/dev/video%d). Make "
"sure it is loaded *before* (re)connecting the camera.",
symbolic(camlist, cam->id), cam->v4ldev->minor)
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
up_read(&w9968cf_disconnect);
return -ENODEV;
}
@@ -2694,11 +2700,11 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
DBG(2, "%s (/dev/video%d) has been already occupied by '%s'",
symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command)
if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
up_read(&w9968cf_disconnect);
return -EWOULDBLOCK;
}
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
err = wait_event_interruptible_exclusive(cam->open,
cam->disconnected ||
!cam->users);
@@ -2710,7 +2716,7 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
up_read(&w9968cf_disconnect);
return -ENODEV;
}
- down(&cam->dev_sem);
+ mutex_lock(&cam->dev_mutex);
}
DBG(5, "Opening '%s', /dev/video%d ...",
@@ -2739,7 +2745,7 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
DBG(5, "Video device is open")
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
up_read(&w9968cf_disconnect);
return 0;
@@ -2747,7 +2753,7 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
deallocate_memory:
w9968cf_deallocate_memory(cam);
DBG(2, "Failed to open the video device")
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
up_read(&w9968cf_disconnect);
return err;
}
@@ -2759,13 +2765,13 @@ static int w9968cf_release(struct inode* inode, struct file* filp)
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
- down(&cam->dev_sem); /* prevent disconnect() to be called */
+ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
w9968cf_stop_transfer(cam);
if (cam->disconnected) {
w9968cf_release_resources(cam);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
kfree(cam);
return 0;
}
@@ -2775,7 +2781,7 @@ static int w9968cf_release(struct inode* inode, struct file* filp)
wake_up_interruptible_nr(&cam->open, 1);
DBG(5, "Video device closed")
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
return 0;
}
@@ -2792,18 +2798,18 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
if (filp->f_flags & O_NONBLOCK)
return -EWOULDBLOCK;
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->disconnected) {
DBG(2, "Device not present")
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
if (cam->misconfigured) {
DBG(2, "The camera is misconfigured. Close and open it again.")
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
@@ -2818,11 +2824,11 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
cam->frame[1].status == F_READY ||
cam->disconnected);
if (err) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return err;
}
if (cam->disconnected) {
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
@@ -2836,7 +2842,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
if (copy_to_user(buf, fr->buffer, count)) {
fr->status = F_UNUSED;
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EFAULT;
}
*f_pos += count;
@@ -2845,7 +2851,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
DBG(5, "%zu bytes read", count)
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return count;
}
@@ -2899,24 +2905,24 @@ w9968cf_ioctl(struct inode* inode, struct file* filp,
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
- if (down_interruptible(&cam->fileop_sem))
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->disconnected) {
DBG(2, "Device not present")
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -ENODEV;
}
if (cam->misconfigured) {
DBG(2, "The camera is misconfigured. Close and open it again.")
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return -EIO;
}
err = w9968cf_v4l_ioctl(inode, filp, cmd, (void __user *)arg);
- up(&cam->fileop_sem);
+ mutex_unlock(&cam->fileop_mutex);
return err;
}
@@ -3499,14 +3505,12 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
return -ENODEV;
cam = (struct w9968cf_device*)
- kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
+ kzalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
if (!cam)
return -ENOMEM;
- memset(cam, 0, sizeof(*cam));
-
- init_MUTEX(&cam->dev_sem);
- down(&cam->dev_sem);
+ mutex_init(&cam->dev_mutex);
+ mutex_lock(&cam->dev_mutex);
cam->usbdev = udev;
/* NOTE: a local copy is used to avoid possible race conditions */
@@ -3518,10 +3522,10 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
simcams = W9968CF_SIMCAMS;
/* How many cameras are connected ? */
- down(&w9968cf_devlist_sem);
+ mutex_lock(&w9968cf_devlist_mutex);
list_for_each(ptr, &w9968cf_dev_list)
sc++;
- up(&w9968cf_devlist_sem);
+ mutex_unlock(&w9968cf_devlist_mutex);
if (sc >= simcams) {
DBG(2, "Device rejected: too many connected cameras "
@@ -3532,21 +3536,19 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
/* Allocate 2 bytes of memory for camera control USB transfers */
- if (!(cam->control_buffer = kmalloc(2, GFP_KERNEL))) {
+ if (!(cam->control_buffer = kzalloc(2, GFP_KERNEL))) {
DBG(1,"Couldn't allocate memory for camera control transfers")
err = -ENOMEM;
goto fail;
}
- memset(cam->control_buffer, 0, 2);
/* Allocate 8 bytes of memory for USB data transfers to the FSB */
- if (!(cam->data_buffer = kmalloc(8, GFP_KERNEL))) {
+ if (!(cam->data_buffer = kzalloc(8, GFP_KERNEL))) {
DBG(1, "Couldn't allocate memory for data "
"transfers to the FSB")
err = -ENOMEM;
goto fail;
}
- memset(cam->data_buffer, 0, 8);
/* Register the V4L device */
cam->v4ldev = video_device_alloc();
@@ -3583,9 +3585,9 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
/* Add a new entry into the list of V4L registered devices */
- down(&w9968cf_devlist_sem);
+ mutex_lock(&w9968cf_devlist_mutex);
list_add(&cam->v4llist, &w9968cf_dev_list);
- up(&w9968cf_devlist_sem);
+ mutex_unlock(&w9968cf_devlist_mutex);
dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
w9968cf_turn_on_led(cam);
@@ -3593,7 +3595,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
w9968cf_i2c_init(cam);
usb_set_intfdata(intf, cam);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
return 0;
fail: /* Free unused memory */
@@ -3601,7 +3603,7 @@ fail: /* Free unused memory */
kfree(cam->data_buffer);
if (cam->v4ldev)
video_device_release(cam->v4ldev);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
kfree(cam);
return err;
}
@@ -3616,7 +3618,7 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
if (cam) {
/* Prevent concurrent accesses to data */
- down(&cam->dev_sem);
+ mutex_lock(&cam->dev_mutex);
cam->disconnected = 1;
@@ -3635,7 +3637,7 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
} else
w9968cf_release_resources(cam);
- up(&cam->dev_sem);
+ mutex_unlock(&cam->dev_mutex);
if (!cam->users)
kfree(cam);
diff --git a/drivers/usb/media/w9968cf.h b/drivers/usb/media/w9968cf.h
index 47a6ff79417..a87be719a28 100644
--- a/drivers/usb/media/w9968cf.h
+++ b/drivers/usb/media/w9968cf.h
@@ -32,7 +32,7 @@
#include <linux/param.h>
#include <linux/types.h>
#include <linux/rwsem.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
#include <media/ovcamchip.h>
@@ -194,14 +194,6 @@ enum w9968cf_vpp_flag {
VPP_UYVY_TO_RGBX = 0x08,
};
-static struct w9968cf_vpp_t* w9968cf_vpp;
-static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait);
-
-static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */
-static DECLARE_MUTEX(w9968cf_devlist_sem); /* semaphore for list traversal */
-
-static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */
-
/* Main device driver structure */
struct w9968cf_device {
struct device dev; /* device structure */
@@ -277,8 +269,8 @@ struct w9968cf_device {
struct i2c_client* sensor_client;
/* Locks */
- struct semaphore dev_sem, /* for probe, disconnect,open and close */
- fileop_sem; /* for read and ioctl */
+ struct mutex dev_mutex, /* for probe, disconnect,open and close */
+ fileop_mutex; /* for read and ioctl */
spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */
flist_lock; /* for requested frame list accesses */
wait_queue_head_t open, wait_queue;
diff --git a/drivers/usb/media/zc0301.h b/drivers/usb/media/zc0301.h
new file mode 100644
index 00000000000..8e0655140e6
--- /dev/null
+++ b/drivers/usb/media/zc0301.h
@@ -0,0 +1,192 @@
+/***************************************************************************
+ * V4L2 driver for ZC0301 Image Processor and Control Chip *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _ZC0301_H_
+#define _ZC0301_H_
+
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+
+#include "zc0301_sensor.h"
+
+/*****************************************************************************/
+
+#define ZC0301_DEBUG
+#define ZC0301_DEBUG_LEVEL 2
+#define ZC0301_MAX_DEVICES 64
+#define ZC0301_FORCE_MUNMAP 0
+#define ZC0301_MAX_FRAMES 32
+#define ZC0301_COMPRESSION_QUALITY 0
+#define ZC0301_URBS 2
+#define ZC0301_ISO_PACKETS 7
+#define ZC0301_ALTERNATE_SETTING 7
+#define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS)
+#define ZC0301_CTRL_TIMEOUT 100
+#define ZC0301_FRAME_TIMEOUT 2
+
+/*****************************************************************************/
+
+ZC0301_ID_TABLE
+ZC0301_SENSOR_TABLE
+
+enum zc0301_frame_state {
+ F_UNUSED,
+ F_QUEUED,
+ F_GRABBING,
+ F_DONE,
+ F_ERROR,
+};
+
+struct zc0301_frame_t {
+ void* bufmem;
+ struct v4l2_buffer buf;
+ enum zc0301_frame_state state;
+ struct list_head frame;
+ unsigned long vma_use_count;
+};
+
+enum zc0301_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04,
+};
+
+enum zc0301_io_method {
+ IO_NONE,
+ IO_READ,
+ IO_MMAP,
+};
+
+enum zc0301_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON,
+};
+
+struct zc0301_module_param {
+ u8 force_munmap;
+ u16 frame_timeout;
+};
+
+static DECLARE_RWSEM(zc0301_disconnect);
+
+struct zc0301_device {
+ struct video_device* v4ldev;
+
+ struct zc0301_sensor sensor;
+
+ struct usb_device* usbdev;
+ struct urb* urb[ZC0301_URBS];
+ void* transfer_buffer[ZC0301_URBS];
+ u8* control_buffer;
+
+ struct zc0301_frame_t *frame_current, frame[ZC0301_MAX_FRAMES];
+ struct list_head inqueue, outqueue;
+ u32 frame_count, nbuffers, nreadbuffers;
+
+ enum zc0301_io_method io;
+ enum zc0301_stream_state stream;
+
+ struct v4l2_jpegcompression compression;
+
+ struct zc0301_module_param module_param;
+
+ enum zc0301_dev_state state;
+ u8 users;
+
+ struct mutex dev_mutex, fileop_mutex;
+ spinlock_t queue_lock;
+ wait_queue_head_t open, wait_frame, wait_stream;
+};
+
+/*****************************************************************************/
+
+struct zc0301_device*
+zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id)
+{
+ return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL;
+}
+
+void
+zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor)
+{
+ memcpy(&cam->sensor, sensor, sizeof(struct zc0301_sensor));
+}
+
+/*****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef ZC0301_DEBUG
+# define DBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1) \
+ dev_err(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) == 2) \
+ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) >= 3) \
+ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args); \
+ } \
+} while (0)
+# define KDBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1 || (level) == 2) \
+ pr_info("zc0301: " fmt "\n", ## args); \
+ else if ((level) == 3) \
+ pr_debug("zc0301: [%s:%d] " fmt "\n", __FUNCTION__, \
+ __LINE__ , ## args); \
+ } \
+} while (0)
+# define V4LDBG(level, name, cmd) \
+do { \
+ if (debug >= (level)) \
+ v4l_print_ioctl(name, cmd); \
+} while (0)
+#else
+# define DBG(level, fmt, args...) do {;} while(0)
+# define KDBG(level, fmt, args...) do {;} while(0)
+# define V4LDBG(level, name, cmd) do {;} while(0)
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...) \
+dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args)
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
+
+#endif /* _ZC0301_H_ */
diff --git a/drivers/usb/media/zc0301_core.c b/drivers/usb/media/zc0301_core.c
new file mode 100644
index 00000000000..4036c6268bf
--- /dev/null
+++ b/drivers/usb/media/zc0301_core.c
@@ -0,0 +1,2055 @@
+/***************************************************************************
+ * Video4Linux2 driver for ZC0301 Image Processor and Control Chip *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * Informations about the chip internals needed to enable the I2C protocol *
+ * have been taken from the documentation of the ZC030x Video4Linux1 *
+ * driver written by Andrew Birkett <andy@nobugs.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/page-flags.h>
+#include <linux/byteorder/generic.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "zc0301.h"
+
+/*****************************************************************************/
+
+#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301 " \
+ "Image Processor and Control Chip"
+#define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia"
+#define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
+#define ZC0301_MODULE_LICENSE "GPL"
+#define ZC0301_MODULE_VERSION "1:1.03"
+#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 3)
+
+/*****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, zc0301_id_table);
+
+MODULE_AUTHOR(ZC0301_MODULE_AUTHOR " " ZC0301_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(ZC0301_MODULE_NAME);
+MODULE_VERSION(ZC0301_MODULE_VERSION);
+MODULE_LICENSE(ZC0301_MODULE_LICENSE);
+
+static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1};
+module_param_array(video_nr, short, NULL, 0444);
+MODULE_PARM_DESC(video_nr,
+ "\n<-1|n[,...]> Specify V4L2 minor mode number."
+ "\n -1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify up to "
+ __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second registered camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+
+static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] =
+ ZC0301_FORCE_MUNMAP};
+module_param_array(force_munmap, bool, NULL, 0444);
+MODULE_PARM_DESC(force_munmap,
+ "\n<0|1[,...]> Force the application to unmap previously"
+ "\nmapped buffer memory before calling any VIDIOC_S_CROP or"
+ "\nVIDIOC_S_FMT ioctl's. Not all the applications support"
+ "\nthis feature. This parameter is specific for each"
+ "\ndetected camera."
+ "\n 0 = do not force memory unmapping"
+ "\n 1 = force memory unmapping (save memory)"
+ "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
+ "\n");
+
+static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] =
+ ZC0301_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ "\n<n[,...]> Timeout for a video frame in seconds."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"."
+ "\n");
+
+#ifdef ZC0301_DEBUG
+static unsigned short debug = ZC0301_DEBUG_LEVEL;
+module_param(debug, ushort, 0644);
+MODULE_PARM_DESC(debug,
+ "\n<n> Debugging information level, from 0 to 3:"
+ "\n0 = none (use carefully)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = more verbose messages"
+ "\nLevel 3 is useful for testing only, when only "
+ "one device is used."
+ "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"."
+ "\n");
+#endif
+
+/*****************************************************************************/
+
+static u32
+zc0301_request_buffers(struct zc0301_device* cam, u32 count,
+ enum zc0301_io_method io)
+{
+ struct v4l2_pix_format* p = &(cam->sensor.pix_format);
+ struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
+ const size_t imagesize = cam->module_param.force_munmap ||
+ io == IO_READ ?
+ (p->width * p->height * p->priv) / 8 :
+ (r->width * r->height * p->priv) / 8;
+ void* buff = NULL;
+ u32 i;
+
+ if (count > ZC0301_MAX_FRAMES)
+ count = ZC0301_MAX_FRAMES;
+
+ cam->nbuffers = count;
+ while (cam->nbuffers > 0) {
+ if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize))))
+ break;
+ cam->nbuffers--;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.index = i;
+ cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.length = imagesize;
+ cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buf.sequence = 0;
+ cam->frame[i].buf.field = V4L2_FIELD_NONE;
+ cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buf.flags = 0;
+ }
+
+ return cam->nbuffers;
+}
+
+
+static void zc0301_release_buffers(struct zc0301_device* cam)
+{
+ if (cam->nbuffers) {
+ vfree(cam->frame[0].bufmem);
+ cam->nbuffers = 0;
+ }
+ cam->frame_current = NULL;
+}
+
+
+static void zc0301_empty_framequeues(struct zc0301_device* cam)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&cam->inqueue);
+ INIT_LIST_HEAD(&cam->outqueue);
+
+ for (i = 0; i < ZC0301_MAX_FRAMES; i++) {
+ cam->frame[i].state = F_UNUSED;
+ cam->frame[i].buf.bytesused = 0;
+ }
+}
+
+
+static void zc0301_requeue_outqueue(struct zc0301_device* cam)
+{
+ struct zc0301_frame_t *i;
+
+ list_for_each_entry(i, &cam->outqueue, frame) {
+ i->state = F_QUEUED;
+ list_add(&i->frame, &cam->inqueue);
+ }
+
+ INIT_LIST_HEAD(&cam->outqueue);
+}
+
+
+static void zc0301_queue_unusedframes(struct zc0301_device* cam)
+{
+ unsigned long lock_flags;
+ u32 i;
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].state == F_UNUSED) {
+ cam->frame[i].state = F_QUEUED;
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[i].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ }
+}
+
+/*****************************************************************************/
+
+int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value)
+{
+ struct usb_device* udev = cam->usbdev;
+ int res;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40,
+ value, index, NULL, 0, ZC0301_CTRL_TIMEOUT);
+ if (res < 0) {
+ DBG(3, "Failed to write a register (index 0x%04X, "
+ "value 0x%02X, error %d)",index, value, res);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int zc0301_read_reg(struct zc0301_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0,
+ 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT);
+ if (res < 0)
+ DBG(3, "Failed to read a register (index 0x%04X, error %d)",
+ index, res);
+
+ PDBGG("Read: index 0x%04X, value: 0x%04X", index, (int)(*buff));
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+int zc0301_i2c_read(struct zc0301_device* cam, u16 address, u8 length)
+{
+ int err = 0, res, r0, r1;
+
+ err += zc0301_write_reg(cam, 0x0092, address);
+ err += zc0301_write_reg(cam, 0x0090, 0x02);
+
+ msleep(1);
+
+ res = zc0301_read_reg(cam, 0x0091);
+ if (res < 0)
+ err += res;
+ r0 = zc0301_read_reg(cam, 0x0095);
+ if (r0 < 0)
+ err += r0;
+ r1 = zc0301_read_reg(cam, 0x0096);
+ if (r1 < 0)
+ err += r1;
+
+ res = (length <= 1) ? r0 : r0 | (r1 << 8);
+
+ if (err)
+ DBG(3, "I2C read failed at address 0x%04X, value: 0x%04X",
+ address, res);
+
+
+ PDBGG("I2C read: address 0x%04X, value: 0x%04X", address, res);
+
+ return err ? -1 : res;
+}
+
+
+int zc0301_i2c_write(struct zc0301_device* cam, u16 address, u16 value)
+{
+ int err = 0, res;
+
+ err += zc0301_write_reg(cam, 0x0092, address);
+ err += zc0301_write_reg(cam, 0x0093, value & 0xff);
+ err += zc0301_write_reg(cam, 0x0094, value >> 8);
+ err += zc0301_write_reg(cam, 0x0090, 0x01);
+
+ msleep(1);
+
+ res = zc0301_read_reg(cam, 0x0091);
+ if (res < 0)
+ err += res;
+
+ if (err)
+ DBG(3, "I2C write failed at address 0x%04X, value: 0x%04X",
+ address, value);
+
+ PDBGG("I2C write: address 0x%04X, value: 0x%04X", address, value);
+
+ return err ? -1 : 0;
+}
+
+/*****************************************************************************/
+
+static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs)
+{
+ struct zc0301_device* cam = urb->context;
+ struct zc0301_frame_t** f;
+ size_t imagesize;
+ u8 i;
+ int err = 0;
+
+ if (urb->status == -ENOENT)
+ return;
+
+ f = &cam->frame_current;
+
+ if (cam->stream == STREAM_INTERRUPT) {
+ cam->stream = STREAM_OFF;
+ if ((*f))
+ (*f)->state = F_QUEUED;
+ DBG(3, "Stream interrupted");
+ wake_up(&cam->wait_stream);
+ }
+
+ if (cam->state & DEV_DISCONNECTED)
+ return;
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ wake_up_interruptible(&cam->wait_frame);
+ return;
+ }
+
+ if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
+ goto resubmit_urb;
+
+ if (!(*f))
+ (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t,
+ frame);
+
+ imagesize = (cam->sensor.pix_format.width *
+ cam->sensor.pix_format.height *
+ cam->sensor.pix_format.priv) / 8;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int len, status;
+ void *pos;
+ u16* soi;
+ u8 sof;
+
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status) {
+ DBG(3, "Error in isochronous frame");
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ sof = (*(soi = pos) == 0xd8ff);
+
+ PDBGG("Isochrnous frame: length %u, #%u i,", len, i);
+
+ if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR)
+start_of_frame:
+ if (sof) {
+ (*f)->state = F_GRABBING;
+ (*f)->buf.bytesused = 0;
+ do_gettimeofday(&(*f)->buf.timestamp);
+ DBG(3, "SOF detected: new video frame");
+ }
+
+ if ((*f)->state == F_GRABBING) {
+ if (sof && (*f)->buf.bytesused)
+ goto end_of_frame;
+
+ if ((*f)->buf.bytesused + len > imagesize) {
+ DBG(3, "Video frame size exceeded");
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, len);
+ (*f)->buf.bytesused += len;
+
+ if ((*f)->buf.bytesused == imagesize) {
+ u32 b;
+end_of_frame:
+ b = (*f)->buf.bytesused;
+ (*f)->state = F_DONE;
+ (*f)->buf.sequence= ++cam->frame_count;
+ spin_lock(&cam->queue_lock);
+ list_move_tail(&(*f)->frame, &cam->outqueue);
+ if (!list_empty(&cam->inqueue))
+ (*f) = list_entry(cam->inqueue.next,
+ struct zc0301_frame_t,
+ frame);
+ else
+ (*f) = NULL;
+ spin_unlock(&cam->queue_lock);
+ DBG(3, "Video frame captured: : %lu bytes",
+ (unsigned long)(b));
+
+ if (!(*f))
+ goto resubmit_urb;
+
+ if (sof)
+ goto start_of_frame;
+ }
+ }
+ }
+
+resubmit_urb:
+ urb->dev = cam->usbdev;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0 && err != -EPERM) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "usb_submit_urb() failed");
+ }
+
+ wake_up_interruptible(&cam->wait_frame);
+}
+
+
+static int zc0301_start_transfer(struct zc0301_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ const unsigned int wMaxPacketSize[] = {0, 128, 192, 256, 384,
+ 512, 768, 1023};
+ const unsigned int psz = wMaxPacketSize[ZC0301_ALTERNATE_SETTING];
+ s8 i, j;
+ int err = 0;
+
+ for (i = 0; i < ZC0301_URBS; i++) {
+ cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz,
+ GFP_KERNEL);
+ if (!cam->transfer_buffer[i]) {
+ err = -ENOMEM;
+ DBG(1, "Not enough memory");
+ goto free_buffers;
+ }
+ }
+
+ for (i = 0; i < ZC0301_URBS; i++) {
+ urb = usb_alloc_urb(ZC0301_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+ if (!urb) {
+ err = -ENOMEM;
+ DBG(1, "usb_alloc_urb() failed");
+ goto free_urbs;
+ }
+ urb->dev = udev;
+ urb->context = cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = ZC0301_ISO_PACKETS;
+ urb->complete = zc0301_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = psz * ZC0301_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < ZC0301_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = psz * j;
+ urb->iso_frame_desc[j].length = psz;
+ }
+ }
+
+ err = usb_set_interface(udev, 0, ZC0301_ALTERNATE_SETTING);
+ if (err) {
+ DBG(1, "usb_set_interface() failed");
+ goto free_urbs;
+ }
+
+ cam->frame_current = NULL;
+
+ for (i = 0; i < ZC0301_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ for (j = i-1; j >= 0; j--)
+ usb_kill_urb(cam->urb[j]);
+ DBG(1, "usb_submit_urb() failed, error %d", err);
+ goto free_urbs;
+ }
+ }
+
+ return 0;
+
+free_urbs:
+ for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++)
+ usb_free_urb(cam->urb[i]);
+
+free_buffers:
+ for (i = 0; (i < ZC0301_URBS) && cam->transfer_buffer[i]; i++)
+ kfree(cam->transfer_buffer[i]);
+
+ return err;
+}
+
+
+static int zc0301_stop_transfer(struct zc0301_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ s8 i;
+ int err = 0;
+
+ if (cam->state & DEV_DISCONNECTED)
+ return 0;
+
+ for (i = ZC0301_URBS-1; i >= 0; i--) {
+ usb_kill_urb(cam->urb[i]);
+ usb_free_urb(cam->urb[i]);
+ kfree(cam->transfer_buffer[i]);
+ }
+
+ err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ if (err)
+ DBG(3, "usb_set_interface() failed");
+
+ return err;
+}
+
+
+static int zc0301_stream_interrupt(struct zc0301_device* cam)
+{
+ long timeout;
+
+ cam->stream = STREAM_INTERRUPT;
+ timeout = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ ZC0301_URB_TIMEOUT);
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ else if (cam->stream != STREAM_OFF) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "URB timeout reached. The camera is misconfigured. To "
+ "use it, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+static int
+zc0301_set_compression(struct zc0301_device* cam,
+ struct v4l2_jpegcompression* compression)
+{
+ int r, err = 0;
+
+ if ((r = zc0301_read_reg(cam, 0x0008)) < 0)
+ err += r;
+ err += zc0301_write_reg(cam, 0x0008, r | 0x11 | compression->quality);
+
+ return err ? -EIO : 0;
+}
+
+
+static int zc0301_init(struct zc0301_device* cam)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ struct v4l2_queryctrl *qctrl;
+ struct v4l2_rect* rect;
+ u8 i = 0;
+ int err = 0;
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ init_waitqueue_head(&cam->open);
+ qctrl = s->qctrl;
+ rect = &(s->cropcap.defrect);
+ cam->compression.quality = ZC0301_COMPRESSION_QUALITY;
+ } else { /* use current values */
+ qctrl = s->_qctrl;
+ rect = &(s->_rect);
+ }
+
+ if (s->init) {
+ err = s->init(cam);
+ if (err) {
+ DBG(3, "Sensor initialization failed");
+ return err;
+ }
+ }
+
+ if ((err = zc0301_set_compression(cam, &cam->compression))) {
+ DBG(3, "set_compression() failed");
+ return err;
+ }
+
+ if (s->set_crop)
+ if ((err = s->set_crop(cam, rect))) {
+ DBG(3, "set_crop() failed");
+ return err;
+ }
+
+ if (s->set_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (s->qctrl[i].id != 0 &&
+ !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
+ ctrl.id = s->qctrl[i].id;
+ ctrl.value = qctrl[i].default_value;
+ err = s->set_ctrl(cam, &ctrl);
+ if (err) {
+ DBG(3, "Set %s control failed",
+ s->qctrl[i].name);
+ return err;
+ }
+ DBG(3, "Image sensor supports '%s' control",
+ s->qctrl[i].name);
+ }
+ }
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ mutex_init(&cam->fileop_mutex);
+ spin_lock_init(&cam->queue_lock);
+ init_waitqueue_head(&cam->wait_frame);
+ init_waitqueue_head(&cam->wait_stream);
+ cam->nreadbuffers = 2;
+ memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
+ memcpy(&(s->_rect), &(s->cropcap.defrect),
+ sizeof(struct v4l2_rect));
+ cam->state |= DEV_INITIALIZED;
+ }
+
+ DBG(2, "Initialization succeeded");
+ return 0;
+}
+
+
+static void zc0301_release_resources(struct zc0301_device* cam)
+{
+ DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
+ video_set_drvdata(cam->v4ldev, NULL);
+ video_unregister_device(cam->v4ldev);
+ kfree(cam->control_buffer);
+}
+
+/*****************************************************************************/
+
+static int zc0301_open(struct inode* inode, struct file* filp)
+{
+ struct zc0301_device* cam;
+ int err = 0;
+
+ /*
+ This is the only safe way to prevent race conditions with
+ disconnect
+ */
+ if (!down_read_trylock(&zc0301_disconnect))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(video_devdata(filp));
+
+ if (mutex_lock_interruptible(&cam->dev_mutex)) {
+ up_read(&zc0301_disconnect);
+ return -ERESTARTSYS;
+ }
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor);
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (filp->f_flags & O_NDELAY)) {
+ err = -EWOULDBLOCK;
+ goto out;
+ }
+ mutex_unlock(&cam->dev_mutex);
+ err = wait_event_interruptible_exclusive(cam->open,
+ cam->state & DEV_DISCONNECTED
+ || !cam->users);
+ if (err) {
+ up_read(&zc0301_disconnect);
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ up_read(&zc0301_disconnect);
+ return -ENODEV;
+ }
+ mutex_lock(&cam->dev_mutex);
+ }
+
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ err = zc0301_init(cam);
+ if (err) {
+ DBG(1, "Initialization failed again. "
+ "I will retry on next open().");
+ goto out;
+ }
+ cam->state &= ~DEV_MISCONFIGURED;
+ }
+
+ if ((err = zc0301_start_transfer(cam)))
+ goto out;
+
+ filp->private_data = cam;
+ cam->users++;
+ cam->io = IO_NONE;
+ cam->stream = STREAM_OFF;
+ cam->nbuffers = 0;
+ cam->frame_count = 0;
+ zc0301_empty_framequeues(cam);
+
+ DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
+
+out:
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&zc0301_disconnect);
+ return err;
+}
+
+
+static int zc0301_release(struct inode* inode, struct file* filp)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+
+ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
+
+ zc0301_stop_transfer(cam);
+
+ zc0301_release_buffers(cam);
+
+ if (cam->state & DEV_DISCONNECTED) {
+ zc0301_release_resources(cam);
+ usb_put_dev(cam->usbdev);
+ mutex_unlock(&cam->dev_mutex);
+ kfree(cam);
+ return 0;
+ }
+
+ cam->users--;
+ wake_up_interruptible_nr(&cam->open, 1);
+
+ DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ return 0;
+}
+
+
+static ssize_t
+zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_frame_t* f, * i;
+ unsigned long lock_flags;
+ long timeout;
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io == IO_MMAP) {
+ DBG(3, "Close and open the device again to choose the read "
+ "method");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) {
+ DBG(1, "read() failed, not enough memory");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENOMEM;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (list_empty(&cam->inqueue)) {
+ if (!list_empty(&cam->outqueue))
+ zc0301_empty_framequeues(cam);
+ zc0301_queue_unusedframes(cam);
+ }
+
+ if (!count) {
+ mutex_unlock(&cam->fileop_mutex);
+ return 0;
+ }
+
+ if (list_empty(&cam->outqueue)) {
+ if (filp->f_flags & O_NONBLOCK) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0) {
+ mutex_unlock(&cam->fileop_mutex);
+ return timeout;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+ if (!timeout || (cam->state & DEV_MISCONFIGURED)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+ }
+
+ f = list_entry(cam->outqueue.prev, struct zc0301_frame_t, frame);
+
+ if (count > f->buf.bytesused)
+ count = f->buf.bytesused;
+
+ if (copy_to_user(buf, f->bufmem, count)) {
+ err = -EFAULT;
+ goto exit;
+ }
+ *f_pos += count;
+
+exit:
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(i, &cam->outqueue, frame)
+ i->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ zc0301_queue_unusedframes(cam);
+
+ PDBGG("Frame #%lu, bytes read: %zu",
+ (unsigned long)f->buf.index, count);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err ? err : count;
+}
+
+
+static unsigned int zc0301_poll(struct file *filp, poll_table *wait)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_frame_t* f;
+ unsigned long lock_flags;
+ unsigned int mask = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return POLLERR;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ goto error;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ goto error;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) {
+ DBG(1, "poll() failed, not enough memory");
+ goto error;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (cam->io == IO_READ) {
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(f, &cam->outqueue, frame)
+ f->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ zc0301_queue_unusedframes(cam);
+ }
+
+ poll_wait(filp, &cam->wait_frame, wait);
+
+ if (!list_empty(&cam->outqueue))
+ mask |= POLLIN | POLLRDNORM;
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return mask;
+
+error:
+ mutex_unlock(&cam->fileop_mutex);
+ return POLLERR;
+}
+
+
+static void zc0301_vm_open(struct vm_area_struct* vma)
+{
+ struct zc0301_frame_t* f = vma->vm_private_data;
+ f->vma_use_count++;
+}
+
+
+static void zc0301_vm_close(struct vm_area_struct* vma)
+{
+ /* NOTE: buffers are not freed here */
+ struct zc0301_frame_t* f = vma->vm_private_data;
+ f->vma_use_count--;
+}
+
+
+static struct vm_operations_struct zc0301_vm_ops = {
+ .open = zc0301_vm_open,
+ .close = zc0301_vm_close,
+};
+
+
+static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start;
+ void *pos;
+ u32 i;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+ size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+ if (i == cam->nbuffers) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED;
+
+ pos = cam->frame[i].bufmem;
+ while (size > 0) { /* size is page-aligned */
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &zc0301_vm_ops;
+ vma->vm_private_data = &cam->frame[i];
+
+ zc0301_vm_open(vma);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+static int
+zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_capability cap = {
+ .driver = "zc0301",
+ .version = ZC0301_MODULE_VERSION_CODE,
+ .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING,
+ };
+
+ strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
+ if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
+ strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ sizeof(cap.bus_info));
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_input i;
+
+ if (copy_from_user(&i, arg, sizeof(i)))
+ return -EFAULT;
+
+ if (i.index)
+ return -EINVAL;
+
+ memset(&i, 0, sizeof(i));
+ strcpy(i.name, "Camera");
+ i.type = V4L2_INPUT_TYPE_CAMERA;
+
+ if (copy_to_user(arg, &i, sizeof(i)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_input(struct zc0301_device* cam, void __user * arg)
+{
+ int index = 0;
+
+ if (copy_to_user(arg, &index, sizeof(index)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_s_input(struct zc0301_device* cam, void __user * arg)
+{
+ int index;
+
+ if (copy_from_user(&index, arg, sizeof(index)))
+ return -EFAULT;
+
+ if (index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_queryctrl qc;
+ u8 i;
+
+ if (copy_from_user(&qc, arg, sizeof(qc)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (qc.id && qc.id == s->qctrl[i].id) {
+ memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+ if (copy_to_user(arg, &qc, sizeof(qc)))
+ return -EFAULT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+static int
+zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ int err = 0;
+ u8 i;
+
+ if (!s->get_ctrl && !s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ if (!s->get_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id == s->qctrl[i].id) {
+ ctrl.value = s->_qctrl[i].default_value;
+ goto exit;
+ }
+ return -EINVAL;
+ } else
+ err = s->get_ctrl(cam, &ctrl);
+
+exit:
+ if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+ return -EFAULT;
+
+ return err;
+}
+
+
+static int
+zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ u8 i;
+ int err = 0;
+
+ if (!s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (ctrl.value < s->qctrl[i].minimum ||
+ ctrl.value > s->qctrl[i].maximum)
+ return -ERANGE;
+ ctrl.value -= ctrl.value % s->qctrl[i].step;
+ break;
+ }
+
+ if ((err = s->set_ctrl(cam, &ctrl)))
+ return err;
+
+ s->_qctrl[i].default_value = ctrl.value;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
+
+ cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cc->pixelaspect.numerator = 1;
+ cc->pixelaspect.denominator = 1;
+
+ if (copy_to_user(arg, cc, sizeof(*cc)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_crop crop = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ };
+
+ memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+
+ if (copy_to_user(arg, &crop, sizeof(crop)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_crop crop;
+ struct v4l2_rect* rect;
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ const enum zc0301_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&crop, arg, sizeof(crop)))
+ return -EFAULT;
+
+ rect = &(crop.c);
+
+ if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_CROP failed. "
+ "Unmap the buffers first.");
+ return -EINVAL;
+ }
+
+ if (!s->set_crop) {
+ memcpy(rect, &(s->_rect), sizeof(*rect));
+ if (copy_to_user(arg, &crop, sizeof(crop)))
+ return -EFAULT;
+ return 0;
+ }
+
+ rect->left &= ~7L;
+ rect->top &= ~7L;
+ if (rect->width < 8)
+ rect->width = 8;
+ if (rect->height < 8)
+ rect->height = 8;
+ if (rect->width > bounds->width)
+ rect->width = bounds->width;
+ if (rect->height > bounds->height)
+ rect->height = bounds->height;
+ if (rect->left < bounds->left)
+ rect->left = bounds->left;
+ if (rect->top < bounds->top)
+ rect->top = bounds->top;
+ if (rect->left + rect->width > bounds->left + bounds->width)
+ rect->left = bounds->left+bounds->width - rect->width;
+ if (rect->top + rect->height > bounds->top + bounds->height)
+ rect->top = bounds->top+bounds->height - rect->height;
+ rect->width &= ~7L;
+ rect->height &= ~7L;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &crop, sizeof(crop))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ zc0301_release_buffers(cam);
+
+ if (s->set_crop)
+ err += s->set_crop(cam, rect);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ s->pix_format.width = rect->width;
+ s->pix_format.height = rect->height;
+ memcpy(&(s->_rect), rect, sizeof(*rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ zc0301_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ zc0301_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_fmtdesc fmtd;
+
+ if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+ return -EFAULT;
+
+ if (fmtd.index == 0) {
+ strcpy(fmtd.description, "JPEG");
+ fmtd.pixelformat = V4L2_PIX_FMT_JPEG;
+ fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+ } else
+ return -EINVAL;
+
+ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
+
+ if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_format format;
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pfmt->bytesperline = 0;
+ pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
+ pfmt->field = V4L2_FIELD_NONE;
+ memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
+
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd,
+ void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_format format;
+ struct v4l2_pix_format* pix;
+ struct v4l2_pix_format* pfmt = &(s->pix_format);
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_rect rect;
+ const enum zc0301_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ pix = &(format.fmt.pix);
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memcpy(&rect, &(s->_rect), sizeof(rect));
+
+ if (!s->set_crop) {
+ pix->width = rect.width;
+ pix->height = rect.height;
+ } else {
+ rect.width = pix->width;
+ rect.height = pix->height;
+ }
+
+ if (rect.width < 8)
+ rect.width = 8;
+ if (rect.height < 8)
+ rect.height = 8;
+ if (rect.width > bounds->left + bounds->width - rect.left)
+ rect.width = bounds->left + bounds->width - rect.left;
+ if (rect.height > bounds->top + bounds->height - rect.top)
+ rect.height = bounds->top + bounds->height - rect.top;
+ rect.width &= ~7L;
+ rect.height &= ~7L;
+
+ pix->width = rect.width;
+ pix->height = rect.height;
+ pix->pixelformat = pfmt->pixelformat;
+ pix->priv = pfmt->priv;
+ pix->colorspace = pfmt->colorspace;
+ pix->bytesperline = 0;
+ pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
+ pix->field = V4L2_FIELD_NONE;
+
+ if (cmd == VIDIOC_TRY_FMT) {
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_FMT failed. "
+ "Unmap the buffers first.");
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &format, sizeof(format))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ zc0301_release_buffers(cam);
+
+ if (s->set_crop)
+ err += s->set_crop(cam, &rect);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ memcpy(pfmt, pix, sizeof(*pix));
+ memcpy(&(s->_rect), &rect, sizeof(rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ zc0301_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ zc0301_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_jpegcomp(struct zc0301_device* cam, void __user * arg)
+{
+ if (copy_to_user(arg, &cam->compression, sizeof(cam->compression)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_jpegcompression jc;
+ const enum zc0301_stream_state stream = cam->stream;
+ int err = 0;
+
+ if (copy_from_user(&jc, arg, sizeof(jc)))
+ return -EFAULT;
+
+ if (jc.quality != 0)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ err += zc0301_set_compression(cam, &jc);
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
+ "problems. To use the camera, close and open "
+ "/dev/video%d again.", cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ cam->compression.quality = jc.quality;
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_reqbufs(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_requestbuffers rb;
+ u32 i;
+ int err;
+
+ if (copy_from_user(&rb, arg, sizeof(rb)))
+ return -EFAULT;
+
+ if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb.memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (cam->io == IO_READ) {
+ DBG(3, "Close and open the device again to choose the mmap "
+ "I/O method");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_REQBUFS failed. "
+ "Previous buffers are still mapped.");
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ zc0301_empty_framequeues(cam);
+
+ zc0301_release_buffers(cam);
+ if (rb.count)
+ rb.count = zc0301_request_buffers(cam, rb.count, IO_MMAP);
+
+ if (copy_to_user(arg, &rb, sizeof(rb))) {
+ zc0301_release_buffers(cam);
+ cam->io = IO_NONE;
+ return -EFAULT;
+ }
+
+ cam->io = rb.count ? IO_MMAP : IO_NONE;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_querybuf(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
+
+ if (cam->frame[b.index].vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (cam->frame[b.index].state == F_DONE)
+ b.flags |= V4L2_BUF_FLAG_DONE;
+ else if (cam->frame[b.index].state != F_UNUSED)
+ b.flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+ unsigned long lock_flags;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->frame[b.index].state != F_UNUSED)
+ return -EINVAL;
+
+ cam->frame[b.index].state = F_QUEUED;
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ PDBGG("Frame #%lu queued", (unsigned long)b.index);
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp,
+ void __user * arg)
+{
+ struct v4l2_buffer b;
+ struct zc0301_frame_t *f;
+ unsigned long lock_flags;
+ long timeout;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->outqueue)) {
+ if (cam->stream == STREAM_OFF)
+ return -EINVAL;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ if (!timeout || (cam->state & DEV_MISCONFIGURED))
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ f = list_entry(cam->outqueue.next, struct zc0301_frame_t, frame);
+ list_del(cam->outqueue.next);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ f->state = F_UNUSED;
+
+ memcpy(&b, &f->buf, sizeof(b));
+ if (f->vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_streamon(struct zc0301_device* cam, void __user * arg)
+{
+ int type;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->inqueue))
+ return -EINVAL;
+
+ cam->stream = STREAM_ON;
+
+ DBG(3, "Stream on");
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_streamoff(struct zc0301_device* cam, void __user * arg)
+{
+ int type, err;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ zc0301_empty_framequeues(cam);
+
+ DBG(3, "Stream off");
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_parm(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+
+ if (sp.parm.capture.readbuffers == 0)
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (sp.parm.capture.readbuffers > ZC0301_MAX_FRAMES)
+ sp.parm.capture.readbuffers = ZC0301_MAX_FRAMES;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ cam->nreadbuffers = sp.parm.capture.readbuffers;
+
+ return 0;
+}
+
+
+static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp,
+ unsigned int cmd, void __user * arg)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+
+ switch (cmd) {
+
+ case VIDIOC_QUERYCAP:
+ return zc0301_vidioc_querycap(cam, arg);
+
+ case VIDIOC_ENUMINPUT:
+ return zc0301_vidioc_enuminput(cam, arg);
+
+ case VIDIOC_G_INPUT:
+ return zc0301_vidioc_g_input(cam, arg);
+
+ case VIDIOC_S_INPUT:
+ return zc0301_vidioc_s_input(cam, arg);
+
+ case VIDIOC_QUERYCTRL:
+ return zc0301_vidioc_query_ctrl(cam, arg);
+
+ case VIDIOC_G_CTRL:
+ return zc0301_vidioc_g_ctrl(cam, arg);
+
+ case VIDIOC_S_CTRL_OLD:
+ case VIDIOC_S_CTRL:
+ return zc0301_vidioc_s_ctrl(cam, arg);
+
+ case VIDIOC_CROPCAP_OLD:
+ case VIDIOC_CROPCAP:
+ return zc0301_vidioc_cropcap(cam, arg);
+
+ case VIDIOC_G_CROP:
+ return zc0301_vidioc_g_crop(cam, arg);
+
+ case VIDIOC_S_CROP:
+ return zc0301_vidioc_s_crop(cam, arg);
+
+ case VIDIOC_ENUM_FMT:
+ return zc0301_vidioc_enum_fmt(cam, arg);
+
+ case VIDIOC_G_FMT:
+ return zc0301_vidioc_g_fmt(cam, arg);
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ return zc0301_vidioc_try_s_fmt(cam, cmd, arg);
+
+ case VIDIOC_G_JPEGCOMP:
+ return zc0301_vidioc_g_jpegcomp(cam, arg);
+
+ case VIDIOC_S_JPEGCOMP:
+ return zc0301_vidioc_s_jpegcomp(cam, arg);
+
+ case VIDIOC_REQBUFS:
+ return zc0301_vidioc_reqbufs(cam, arg);
+
+ case VIDIOC_QUERYBUF:
+ return zc0301_vidioc_querybuf(cam, arg);
+
+ case VIDIOC_QBUF:
+ return zc0301_vidioc_qbuf(cam, arg);
+
+ case VIDIOC_DQBUF:
+ return zc0301_vidioc_dqbuf(cam, filp, arg);
+
+ case VIDIOC_STREAMON:
+ return zc0301_vidioc_streamon(cam, arg);
+
+ case VIDIOC_STREAMOFF:
+ return zc0301_vidioc_streamoff(cam, arg);
+
+ case VIDIOC_G_PARM:
+ return zc0301_vidioc_g_parm(cam, arg);
+
+ case VIDIOC_S_PARM_OLD:
+ case VIDIOC_S_PARM:
+ return zc0301_vidioc_s_parm(cam, arg);
+
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_QUERYMENU:
+ return -EINVAL;
+
+ default:
+ return -EINVAL;
+
+ }
+}
+
+
+static int zc0301_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ V4LDBG(3, "zc0301", cmd);
+
+ err = zc0301_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err;
+}
+
+
+static struct file_operations zc0301_fops = {
+ .owner = THIS_MODULE,
+ .open = zc0301_open,
+ .release = zc0301_release,
+ .ioctl = zc0301_ioctl,
+ .read = zc0301_read,
+ .poll = zc0301_poll,
+ .mmap = zc0301_mmap,
+ .llseek = no_llseek,
+};
+
+/*****************************************************************************/
+
+static int
+zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct zc0301_device* cam;
+ static unsigned int dev_nr = 0;
+ unsigned int i;
+ int err = 0;
+
+ if (!(cam = kzalloc(sizeof(struct zc0301_device), GFP_KERNEL)))
+ return -ENOMEM;
+
+ cam->usbdev = udev;
+
+ if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) {
+ DBG(1, "kmalloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(cam->v4ldev = video_device_alloc())) {
+ DBG(1, "video_device_alloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ mutex_init(&cam->dev_mutex);
+
+ DBG(2, "ZC0301 Image Processor and Control Chip detected "
+ "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct);
+
+ for (i = 0; zc0301_sensor_table[i]; i++) {
+ err = zc0301_sensor_table[i](cam);
+ if (!err)
+ break;
+ }
+
+ if (!err)
+ DBG(2, "%s image sensor detected", cam->sensor.name);
+ else {
+ DBG(1, "No supported image sensor detected");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (zc0301_init(cam)) {
+ DBG(1, "Initialization failed. I will retry on open().");
+ cam->state |= DEV_MISCONFIGURED;
+ }
+
+ strcpy(cam->v4ldev->name, "ZC0301 PC Camera");
+ cam->v4ldev->owner = THIS_MODULE;
+ cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+ cam->v4ldev->hardware = 0;
+ cam->v4ldev->fops = &zc0301_fops;
+ cam->v4ldev->minor = video_nr[dev_nr];
+ cam->v4ldev->release = video_device_release;
+ video_set_drvdata(cam->v4ldev, cam);
+
+ mutex_lock(&cam->dev_mutex);
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L2 device registration failed");
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(1, "Free /dev/videoX node not found");
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ mutex_unlock(&cam->dev_mutex);
+ goto fail;
+ }
+
+ DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
+
+ cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
+
+ dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+ usb_set_intfdata(intf, cam);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ return 0;
+
+fail:
+ if (cam) {
+ kfree(cam->control_buffer);
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ kfree(cam);
+ }
+ return err;
+}
+
+
+static void zc0301_usb_disconnect(struct usb_interface* intf)
+{
+ struct zc0301_device* cam = usb_get_intfdata(intf);
+
+ if (!cam)
+ return;
+
+ down_write(&zc0301_disconnect);
+
+ mutex_lock(&cam->dev_mutex);
+
+ DBG(2, "Disconnecting %s...", cam->v4ldev->name);
+
+ wake_up_interruptible_all(&cam->open);
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is open! Deregistration and "
+ "memory deallocation are deferred on close.",
+ cam->v4ldev->minor);
+ cam->state |= DEV_MISCONFIGURED;
+ zc0301_stop_transfer(cam);
+ cam->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&cam->wait_frame);
+ wake_up(&cam->wait_stream);
+ usb_get_dev(cam->usbdev);
+ } else {
+ cam->state |= DEV_DISCONNECTED;
+ zc0301_release_resources(cam);
+ }
+
+ mutex_unlock(&cam->dev_mutex);
+
+ if (!cam->users)
+ kfree(cam);
+
+ up_write(&zc0301_disconnect);
+}
+
+
+static struct usb_driver zc0301_usb_driver = {
+ .name = "zc0301",
+ .id_table = zc0301_id_table,
+ .probe = zc0301_usb_probe,
+ .disconnect = zc0301_usb_disconnect,
+};
+
+/*****************************************************************************/
+
+static int __init zc0301_module_init(void)
+{
+ int err = 0;
+
+ KDBG(2, ZC0301_MODULE_NAME " v" ZC0301_MODULE_VERSION);
+ KDBG(3, ZC0301_MODULE_AUTHOR);
+
+ if ((err = usb_register(&zc0301_usb_driver)))
+ KDBG(1, "usb_register() failed");
+
+ return err;
+}
+
+
+static void __exit zc0301_module_exit(void)
+{
+ usb_deregister(&zc0301_usb_driver);
+}
+
+
+module_init(zc0301_module_init);
+module_exit(zc0301_module_exit);
diff --git a/drivers/usb/media/zc0301_pas202bcb.c b/drivers/usb/media/zc0301_pas202bcb.c
new file mode 100644
index 00000000000..9d282a22c15
--- /dev/null
+++ b/drivers/usb/media/zc0301_pas202bcb.c
@@ -0,0 +1,361 @@
+/***************************************************************************
+ * Plug-in for PAS202BCB image sensor connected to the ZC030! Image *
+ * Processor and Control Chip *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * Initialization values of the ZC0301 have been taken from the SPCA5XX *
+ * driver maintained by Michel Xhaard <mxhaard@magic.fr> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+/*
+ NOTE: Sensor controls are disabled for now, becouse changing them while
+ streaming sometimes results in out-of-sync video frames. We'll use
+ the default initialization, until we know how to stop and start video
+ in the chip. However, the image quality still looks good under various
+ light conditions.
+*/
+
+#include <linux/delay.h>
+#include "zc0301_sensor.h"
+
+
+static struct zc0301_sensor pas202bcb;
+
+
+static int pas202bcb_init(struct zc0301_device* cam)
+{
+ int err = 0;
+
+ err += zc0301_write_reg(cam, 0x0002, 0x00);
+ err += zc0301_write_reg(cam, 0x0003, 0x02);
+ err += zc0301_write_reg(cam, 0x0004, 0x80);
+ err += zc0301_write_reg(cam, 0x0005, 0x01);
+ err += zc0301_write_reg(cam, 0x0006, 0xE0);
+ err += zc0301_write_reg(cam, 0x0098, 0x00);
+ err += zc0301_write_reg(cam, 0x009A, 0x03);
+ err += zc0301_write_reg(cam, 0x011A, 0x00);
+ err += zc0301_write_reg(cam, 0x011C, 0x03);
+ err += zc0301_write_reg(cam, 0x009B, 0x01);
+ err += zc0301_write_reg(cam, 0x009C, 0xE6);
+ err += zc0301_write_reg(cam, 0x009D, 0x02);
+ err += zc0301_write_reg(cam, 0x009E, 0x86);
+
+ err += zc0301_i2c_write(cam, 0x02, 0x02);
+ err += zc0301_i2c_write(cam, 0x0A, 0x01);
+ err += zc0301_i2c_write(cam, 0x0B, 0x01);
+ err += zc0301_i2c_write(cam, 0x0D, 0x00);
+ err += zc0301_i2c_write(cam, 0x12, 0x05);
+ err += zc0301_i2c_write(cam, 0x13, 0x63);
+ err += zc0301_i2c_write(cam, 0x15, 0x70);
+
+ err += zc0301_write_reg(cam, 0x0101, 0xB7);
+ err += zc0301_write_reg(cam, 0x0100, 0x0D);
+ err += zc0301_write_reg(cam, 0x0189, 0x06);
+ err += zc0301_write_reg(cam, 0x01AD, 0x00);
+ err += zc0301_write_reg(cam, 0x01C5, 0x03);
+ err += zc0301_write_reg(cam, 0x01CB, 0x13);
+ err += zc0301_write_reg(cam, 0x0250, 0x08);
+ err += zc0301_write_reg(cam, 0x0301, 0x08);
+ err += zc0301_write_reg(cam, 0x018D, 0x70);
+ err += zc0301_write_reg(cam, 0x0008, 0x03);
+ err += zc0301_write_reg(cam, 0x01C6, 0x04);
+ err += zc0301_write_reg(cam, 0x01CB, 0x07);
+ err += zc0301_write_reg(cam, 0x0120, 0x11);
+ err += zc0301_write_reg(cam, 0x0121, 0x37);
+ err += zc0301_write_reg(cam, 0x0122, 0x58);
+ err += zc0301_write_reg(cam, 0x0123, 0x79);
+ err += zc0301_write_reg(cam, 0x0124, 0x91);
+ err += zc0301_write_reg(cam, 0x0125, 0xA6);
+ err += zc0301_write_reg(cam, 0x0126, 0xB8);
+ err += zc0301_write_reg(cam, 0x0127, 0xC7);
+ err += zc0301_write_reg(cam, 0x0128, 0xD3);
+ err += zc0301_write_reg(cam, 0x0129, 0xDE);
+ err += zc0301_write_reg(cam, 0x012A, 0xE6);
+ err += zc0301_write_reg(cam, 0x012B, 0xED);
+ err += zc0301_write_reg(cam, 0x012C, 0xF3);
+ err += zc0301_write_reg(cam, 0x012D, 0xF8);
+ err += zc0301_write_reg(cam, 0x012E, 0xFB);
+ err += zc0301_write_reg(cam, 0x012F, 0xFF);
+ err += zc0301_write_reg(cam, 0x0130, 0x26);
+ err += zc0301_write_reg(cam, 0x0131, 0x23);
+ err += zc0301_write_reg(cam, 0x0132, 0x20);
+ err += zc0301_write_reg(cam, 0x0133, 0x1C);
+ err += zc0301_write_reg(cam, 0x0134, 0x16);
+ err += zc0301_write_reg(cam, 0x0135, 0x13);
+ err += zc0301_write_reg(cam, 0x0136, 0x10);
+ err += zc0301_write_reg(cam, 0x0137, 0x0D);
+ err += zc0301_write_reg(cam, 0x0138, 0x0B);
+ err += zc0301_write_reg(cam, 0x0139, 0x09);
+ err += zc0301_write_reg(cam, 0x013A, 0x07);
+ err += zc0301_write_reg(cam, 0x013B, 0x06);
+ err += zc0301_write_reg(cam, 0x013C, 0x05);
+ err += zc0301_write_reg(cam, 0x013D, 0x04);
+ err += zc0301_write_reg(cam, 0x013E, 0x03);
+ err += zc0301_write_reg(cam, 0x013F, 0x02);
+ err += zc0301_write_reg(cam, 0x010A, 0x4C);
+ err += zc0301_write_reg(cam, 0x010B, 0xF5);
+ err += zc0301_write_reg(cam, 0x010C, 0xFF);
+ err += zc0301_write_reg(cam, 0x010D, 0xF9);
+ err += zc0301_write_reg(cam, 0x010E, 0x51);
+ err += zc0301_write_reg(cam, 0x010F, 0xF5);
+ err += zc0301_write_reg(cam, 0x0110, 0xFB);
+ err += zc0301_write_reg(cam, 0x0111, 0xED);
+ err += zc0301_write_reg(cam, 0x0112, 0x5F);
+ err += zc0301_write_reg(cam, 0x0180, 0x00);
+ err += zc0301_write_reg(cam, 0x0019, 0x00);
+ err += zc0301_write_reg(cam, 0x0087, 0x20);
+ err += zc0301_write_reg(cam, 0x0088, 0x21);
+
+ err += zc0301_i2c_write(cam, 0x20, 0x02);
+ err += zc0301_i2c_write(cam, 0x21, 0x1B);
+ err += zc0301_i2c_write(cam, 0x03, 0x44);
+ err += zc0301_i2c_write(cam, 0x0E, 0x01);
+ err += zc0301_i2c_write(cam, 0x0F, 0x00);
+
+ err += zc0301_write_reg(cam, 0x01A9, 0x14);
+ err += zc0301_write_reg(cam, 0x01AA, 0x24);
+ err += zc0301_write_reg(cam, 0x0190, 0x00);
+ err += zc0301_write_reg(cam, 0x0191, 0x02);
+ err += zc0301_write_reg(cam, 0x0192, 0x1B);
+ err += zc0301_write_reg(cam, 0x0195, 0x00);
+ err += zc0301_write_reg(cam, 0x0196, 0x00);
+ err += zc0301_write_reg(cam, 0x0197, 0x4D);
+ err += zc0301_write_reg(cam, 0x018C, 0x10);
+ err += zc0301_write_reg(cam, 0x018F, 0x20);
+ err += zc0301_write_reg(cam, 0x001D, 0x44);
+ err += zc0301_write_reg(cam, 0x001E, 0x6F);
+ err += zc0301_write_reg(cam, 0x001F, 0xAD);
+ err += zc0301_write_reg(cam, 0x0020, 0xEB);
+ err += zc0301_write_reg(cam, 0x0087, 0x0F);
+ err += zc0301_write_reg(cam, 0x0088, 0x0E);
+ err += zc0301_write_reg(cam, 0x0180, 0x40);
+ err += zc0301_write_reg(cam, 0x0192, 0x1B);
+ err += zc0301_write_reg(cam, 0x0191, 0x02);
+ err += zc0301_write_reg(cam, 0x0190, 0x00);
+ err += zc0301_write_reg(cam, 0x0116, 0x1D);
+ err += zc0301_write_reg(cam, 0x0117, 0x40);
+ err += zc0301_write_reg(cam, 0x0118, 0x99);
+ err += zc0301_write_reg(cam, 0x0180, 0x42);
+ err += zc0301_write_reg(cam, 0x0116, 0x1D);
+ err += zc0301_write_reg(cam, 0x0117, 0x40);
+ err += zc0301_write_reg(cam, 0x0118, 0x99);
+ err += zc0301_write_reg(cam, 0x0007, 0x00);
+
+ err += zc0301_i2c_write(cam, 0x11, 0x01);
+
+ msleep(100);
+
+ return err;
+}
+
+
+static int pas202bcb_get_ctrl(struct zc0301_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ {
+ int r1 = zc0301_i2c_read(cam, 0x04, 1),
+ r2 = zc0301_i2c_read(cam, 0x05, 1);
+ if (r1 < 0 || r2 < 0)
+ return -EIO;
+ ctrl->value = (r1 << 6) | (r2 & 0x3f);
+ }
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x09, 1)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x07, 1)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x10, 1)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case ZC0301_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x08, 1)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case ZC0301_V4L2_CID_DAC_MAGNITUDE:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x0c, 1)) < 0)
+ return -EIO;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int pas202bcb_set_ctrl(struct zc0301_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += zc0301_i2c_write(cam, 0x04, ctrl->value >> 6);
+ err += zc0301_i2c_write(cam, 0x05, ctrl->value & 0x3f);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += zc0301_i2c_write(cam, 0x09, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += zc0301_i2c_write(cam, 0x07, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += zc0301_i2c_write(cam, 0x10, ctrl->value);
+ break;
+ case ZC0301_V4L2_CID_GREEN_BALANCE:
+ err += zc0301_i2c_write(cam, 0x08, ctrl->value);
+ break;
+ case ZC0301_V4L2_CID_DAC_MAGNITUDE:
+ err += zc0301_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += zc0301_i2c_write(cam, 0x11, 0x01);
+
+ return err ? -EIO : 0;
+}
+
+
+static struct zc0301_sensor pas202bcb = {
+ .name = "PAS202BCB",
+ .init = &pas202bcb_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x01e5,
+ .maximum = 0x3fff,
+ .step = 0x0001,
+ .default_value = 0x01e5,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x0c,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = ZC0301_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x05,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = ZC0301_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ },
+ .get_ctrl = &pas202bcb_get_ctrl,
+ .set_ctrl = &pas202bcb_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_JPEG,
+ .priv = 8,
+ },
+};
+
+
+int zc0301_probe_pas202bcb(struct zc0301_device* cam)
+{
+ int r0 = 0, r1 = 0, err = 0;
+ unsigned int pid = 0;
+
+ err += zc0301_write_reg(cam, 0x0000, 0x01);
+ err += zc0301_write_reg(cam, 0x0010, 0x0e);
+ err += zc0301_write_reg(cam, 0x0001, 0x01);
+ err += zc0301_write_reg(cam, 0x0012, 0x03);
+ err += zc0301_write_reg(cam, 0x0012, 0x01);
+ err += zc0301_write_reg(cam, 0x008d, 0x08);
+
+ msleep(10);
+
+ r0 = zc0301_i2c_read(cam, 0x00, 1);
+ r1 = zc0301_i2c_read(cam, 0x01, 1);
+
+ if (r0 < 0 || r1 < 0 || err)
+ return -EIO;
+
+ pid = (r0 << 4) | ((r1 & 0xf0) >> 4);
+ if (pid != 0x017)
+ return -ENODEV;
+
+ zc0301_attach_sensor(cam, &pas202bcb);
+
+ return 0;
+}
diff --git a/drivers/usb/media/zc0301_sensor.h b/drivers/usb/media/zc0301_sensor.h
new file mode 100644
index 00000000000..cf0965a81d0
--- /dev/null
+++ b/drivers/usb/media/zc0301_sensor.h
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * API for image sensors connected to the ZC030! Image Processor and *
+ * Control Chip *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _ZC0301_SENSOR_H_
+#define _ZC0301_SENSOR_H_
+
+#include <linux/usb.h>
+#include <linux/videodev.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+
+struct zc0301_device;
+struct zc0301_sensor;
+
+/*****************************************************************************/
+
+extern int zc0301_probe_pas202bcb(struct zc0301_device* cam);
+
+#define ZC0301_SENSOR_TABLE \
+/* Weak detections must go at the end of the list */ \
+static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \
+ &zc0301_probe_pas202bcb, \
+ NULL, \
+};
+
+extern struct zc0301_device*
+zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id);
+
+extern void
+zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor);
+
+#define ZC0301_USB_DEVICE(vend, prod, intclass) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bInterfaceClass = (intclass)
+
+#define ZC0301_ID_TABLE \
+static const struct usb_device_id zc0301_id_table[] = { \
+ { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, \
+ { ZC0301_USB_DEVICE(0x041e, 0x401c, 0xff), }, /* PAS106 */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131B */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x4034, 0xff), }, /* PAS106 */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x4035, 0xff), }, /* PAS106 */ \
+ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \
+ { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \
+ { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \
+ { } \
+};
+
+/*****************************************************************************/
+
+extern int zc0301_write_reg(struct zc0301_device*, u16 index, u16 value);
+extern int zc0301_read_reg(struct zc0301_device*, u16 index);
+extern int zc0301_i2c_write(struct zc0301_device*, u16 address, u16 value);
+extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length);
+
+/*****************************************************************************/
+
+#define ZC0301_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10
+#define ZC0301_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE
+#define ZC0301_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1
+
+struct zc0301_sensor {
+ char name[32];
+
+ struct v4l2_queryctrl qctrl[ZC0301_MAX_CTRLS];
+ struct v4l2_cropcap cropcap;
+ struct v4l2_pix_format pix_format;
+
+ int (*init)(struct zc0301_device*);
+ int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl);
+ int (*set_ctrl)(struct zc0301_device*,
+ const struct v4l2_control* ctrl);
+ int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect);
+
+ /* Private */
+ struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS];
+ struct v4l2_rect _rect;
+};
+
+#endif /* _ZC0301_SENSOR_H_ */
diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
index ad2f4cccd38..1fef36e71c5 100644
--- a/drivers/usb/misc/auerswald.c
+++ b/drivers/usb/misc/auerswald.c
@@ -570,10 +570,9 @@ static int auerchain_setup (pauerchain_t acp, unsigned int numElements)
/* fill the list of free elements */
for (;numElements; numElements--) {
- acep = (pauerchainelement_t) kmalloc (sizeof (auerchainelement_t), GFP_KERNEL);
+ acep = kzalloc(sizeof(auerchainelement_t), GFP_KERNEL);
if (!acep)
goto ac_fail;
- memset (acep, 0, sizeof (auerchainelement_t));
INIT_LIST_HEAD (&acep->list);
list_add_tail (&acep->list, &acp->free_list);
}
@@ -761,10 +760,9 @@ static int auerbuf_setup (pauerbufctl_t bcp, unsigned int numElements, unsigned
/* fill the list of free elements */
for (;numElements; numElements--) {
- bep = (pauerbuf_t) kmalloc (sizeof (auerbuf_t), GFP_KERNEL);
+ bep = kzalloc(sizeof(auerbuf_t), GFP_KERNEL);
if (!bep)
goto bl_fail;
- memset (bep, 0, sizeof (auerbuf_t));
bep->list = bcp;
INIT_LIST_HEAD (&bep->buff_list);
bep->bufp = kmalloc (bufsize, GFP_KERNEL);
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c
index 6671317b495..a04204292aa 100644
--- a/drivers/usb/misc/cytherm.c
+++ b/drivers/usb/misc/cytherm.c
@@ -351,12 +351,11 @@ static int cytherm_probe(struct usb_interface *interface,
struct usb_cytherm *dev = NULL;
int retval = -ENOMEM;
- dev = kmalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
+ dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
if (dev == NULL) {
dev_err (&interface->dev, "Out of memory\n");
goto error;
}
- memset (dev, 0x00, sizeof (*dev));
dev->udev = usb_get_dev(udev);
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index d8cde101798..d0b16725669 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
@@ -121,7 +122,7 @@ static struct usb_driver idmouse_driver = {
};
/* prevent races between open() and disconnect() */
-static DECLARE_MUTEX(disconnect_sem);
+static DEFINE_MUTEX(disconnect_mutex);
static int idmouse_create_image(struct usb_idmouse *dev)
{
@@ -213,18 +214,18 @@ static int idmouse_open(struct inode *inode, struct file *file)
int result = 0;
/* prevent disconnects */
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
/* get the interface from minor number and driver information */
interface = usb_find_interface (&idmouse_driver, iminor (inode));
if (!interface) {
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return -ENODEV;
}
/* get the device information block from the interface */
dev = usb_get_intfdata(interface);
if (!dev) {
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return -ENODEV;
}
@@ -258,7 +259,7 @@ error:
up(&dev->sem);
/* unlock the disconnect semaphore */
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return result;
}
@@ -267,12 +268,12 @@ static int idmouse_release(struct inode *inode, struct file *file)
struct usb_idmouse *dev;
/* prevent a race condition with open() */
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
dev = (struct usb_idmouse *) file->private_data;
if (dev == NULL) {
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return -ENODEV;
}
@@ -282,7 +283,7 @@ static int idmouse_release(struct inode *inode, struct file *file)
/* are we really open? */
if (dev->open <= 0) {
up(&dev->sem);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return -ENODEV;
}
@@ -292,12 +293,12 @@ static int idmouse_release(struct inode *inode, struct file *file)
/* the device was unplugged before the file was released */
up(&dev->sem);
idmouse_delete(dev);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return 0;
}
up(&dev->sem);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return 0;
}
@@ -340,10 +341,9 @@ static int idmouse_probe(struct usb_interface *interface,
return -ENODEV;
/* allocate memory for our device state and initialize it */
- dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL)
return -ENOMEM;
- memset(dev, 0x00, sizeof(*dev));
init_MUTEX(&dev->sem);
dev->udev = udev;
@@ -400,7 +400,7 @@ static void idmouse_disconnect(struct usb_interface *interface)
struct usb_idmouse *dev;
/* prevent races with open() */
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
/* get device structure */
dev = usb_get_intfdata(interface);
@@ -422,7 +422,7 @@ static void idmouse_disconnect(struct usb_interface *interface)
if (!dev->open)
idmouse_delete(dev);
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
info("%s disconnected", DRIVER_DESC);
}
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index e2d1198623e..966acb474f6 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -33,6 +33,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/input.h>
@@ -172,7 +173,7 @@ struct ld_usb {
};
/* prevent races between open() and disconnect() */
-static DECLARE_MUTEX(disconnect_sem);
+static DEFINE_MUTEX(disconnect_mutex);
static struct usb_driver ld_usb_driver;
@@ -293,7 +294,7 @@ static int ld_usb_open(struct inode *inode, struct file *file)
nonseekable_open(inode, file);
subminor = iminor(inode);
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
interface = usb_find_interface(&ld_usb_driver, subminor);
@@ -355,7 +356,7 @@ unlock_exit:
up(&dev->sem);
unlock_disconnect_exit:
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
return retval;
}
@@ -626,12 +627,11 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
/* allocate memory for our device state and intialize it */
- dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&intf->dev, "Out of memory\n");
goto exit;
}
- memset(dev, 0x00, sizeof(*dev));
init_MUTEX(&dev->sem);
dev->intf = intf;
init_waitqueue_head(&dev->read_wait);
@@ -741,7 +741,7 @@ static void ld_usb_disconnect(struct usb_interface *intf)
struct ld_usb *dev;
int minor;
- down(&disconnect_sem);
+ mutex_lock(&disconnect_mutex);
dev = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
@@ -762,7 +762,7 @@ static void ld_usb_disconnect(struct usb_interface *intf)
up(&dev->sem);
}
- up(&disconnect_sem);
+ mutex_unlock(&disconnect_mutex);
dev_info(&intf->dev, "LD USB Device #%d now disconnected\n",
(minor - USB_LD_MINOR_BASE));
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 1336745b8f5..779bcf0373a 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -83,6 +83,7 @@
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/poll.h>
@@ -256,7 +257,7 @@ static void tower_disconnect (struct usb_interface *interface);
/* prevent races between open() and disconnect */
-static DECLARE_MUTEX (disconnect_sem);
+static DEFINE_MUTEX (disconnect_mutex);
/* file operations needed when we register this driver */
static struct file_operations tower_fops = {
@@ -349,7 +350,7 @@ static int tower_open (struct inode *inode, struct file *file)
nonseekable_open(inode, file);
subminor = iminor(inode);
- down (&disconnect_sem);
+ mutex_lock (&disconnect_mutex);
interface = usb_find_interface (&tower_driver, subminor);
@@ -427,7 +428,7 @@ unlock_exit:
up (&dev->sem);
unlock_disconnect_exit:
- up (&disconnect_sem);
+ mutex_unlock (&disconnect_mutex);
dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval);
@@ -1005,7 +1006,7 @@ static void tower_disconnect (struct usb_interface *interface)
dbg(2, "%s: enter", __FUNCTION__);
- down (&disconnect_sem);
+ mutex_lock (&disconnect_mutex);
dev = usb_get_intfdata (interface);
usb_set_intfdata (interface, NULL);
@@ -1027,7 +1028,7 @@ static void tower_disconnect (struct usb_interface *interface)
up (&dev->sem);
}
- up (&disconnect_sem);
+ mutex_unlock (&disconnect_mutex);
info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE));
diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c
index 605a3c87e05..997db5d8e35 100644
--- a/drivers/usb/misc/phidgetkit.c
+++ b/drivers/usb/misc/phidgetkit.c
@@ -88,7 +88,7 @@ static int change_outputs(struct phidget_interfacekit *kit, int output_num, int
int retval;
int n;
- buffer = kmalloc(4, GFP_KERNEL);
+ buffer = kzalloc(4, GFP_KERNEL);
if (!buffer) {
dev_err(&kit->udev->dev, "%s - out of memory\n",
__FUNCTION__);
@@ -96,7 +96,6 @@ static int change_outputs(struct phidget_interfacekit *kit, int output_num, int
}
kit->outputs[output_num] = enable;
- memset(buffer, 0, 4);
for (n=0; n<8; n++) {
if (kit->outputs[n]) {
buffer[0] |= 1 << n;
@@ -192,7 +191,7 @@ static ssize_t set_backlight(struct device *dev, struct device_attribute *attr,
unsigned char *buffer;
int retval = -ENOMEM;
- buffer = kmalloc(8, GFP_KERNEL);
+ buffer = kzalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&kit->udev->dev, "%s - out of memory\n", __FUNCTION__);
goto exit;
@@ -202,7 +201,6 @@ static ssize_t set_backlight(struct device *dev, struct device_attribute *attr,
retval = -EINVAL;
goto exit;
}
- memset(buffer, 0x00, 8);
if (enabled)
buffer[0] = 0x01;
buffer[7] = 0x11;
@@ -406,12 +404,11 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
- kit = kmalloc(sizeof(*kit), GFP_KERNEL);
+ kit = kzalloc(sizeof(*kit), GFP_KERNEL);
if (kit == NULL) {
dev_err(&intf->dev, "%s - out of memory\n", __FUNCTION__);
return -ENOMEM;
}
- memset(kit, 0, sizeof(*kit));
kit->ifkit = ifkit;
kit->data = usb_buffer_alloc(dev, 8, SLAB_ATOMIC, &kit->data_dma);
diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c
index b3418d2bcc6..5a040c205ee 100644
--- a/drivers/usb/misc/phidgetservo.c
+++ b/drivers/usb/misc/phidgetservo.c
@@ -252,12 +252,11 @@ servo_probe(struct usb_interface *interface, const struct usb_device_id *id)
struct usb_device *udev = interface_to_usbdev(interface);
struct phidget_servo *dev;
- dev = kmalloc(sizeof (struct phidget_servo), GFP_KERNEL);
+ dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
return -ENOMEM;
}
- memset(dev, 0x00, sizeof (*dev));
dev->udev = usb_get_dev(udev);
dev->type = id->driver_info;
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 3260d595441..196c8794a73 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -3188,7 +3188,7 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
break;
default:
- retval = -EINVAL;
+ retval = -ENOTTY;
break;
}
@@ -3251,12 +3251,11 @@ static int sisusb_probe(struct usb_interface *intf,
dev->devnum);
/* Allocate memory for our private */
- if (!(sisusb = kmalloc(sizeof(*sisusb), GFP_KERNEL))) {
+ if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) {
printk(KERN_ERR
"sisusb: Failed to allocate memory for private data\n");
return -ENOMEM;
}
- memset(sisusb, 0, sizeof(*sisusb));
kref_init(&sisusb->kref);
init_MUTEX(&(sisusb->lock));
diff --git a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h
index 1d7a77cc7c4..a716825d1f9 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.h
+++ b/drivers/usb/misc/sisusbvga/sisusb.h
@@ -37,24 +37,16 @@
#ifndef _SISUSB_H_
#define _SISUSB_H_
-#include <linux/version.h>
#ifdef CONFIG_COMPAT
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,10)
-#include <linux/ioctl32.h>
-#define SISUSB_OLD_CONFIG_COMPAT
-#else
#define SISUSB_NEW_CONFIG_COMPAT
#endif
-#endif
/* For older kernels, support for text consoles is by default
* off. To ensable text console support, change the following:
*/
#if 0
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13)
#define CONFIG_USB_SISUSBVGA_CON
#endif
-#endif
/* Version Information */
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index cc3dae3f34e..c82c402285a 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -270,12 +270,11 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
int retval = -ENOMEM;
/* allocate memory for our device state and initialize it */
- dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
err("Out of memory");
goto error;
}
- memset(dev, 0x00, sizeof(*dev));
kref_init(&dev->kref);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c
index 877b081a3a6..f441964132c 100644
--- a/drivers/usb/misc/usbled.c
+++ b/drivers/usb/misc/usbled.c
@@ -106,12 +106,11 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id
struct usb_led *dev = NULL;
int retval = -ENOMEM;
- dev = kmalloc(sizeof(struct usb_led), GFP_KERNEL);
+ dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
goto error;
}
- memset (dev, 0x00, sizeof (*dev));
dev->udev = usb_get_dev(udev);
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 84fa1728f05..9d59b901841 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -382,12 +382,11 @@ alloc_sglist (int nents, int max, int vary)
for (i = 0; i < nents; i++) {
char *buf;
- buf = kmalloc (size, SLAB_KERNEL);
+ buf = kzalloc (size, SLAB_KERNEL);
if (!buf) {
free_sglist (sg, i);
return NULL;
}
- memset (buf, 0, size);
/* kmalloc pages are always physically contiguous! */
sg_init_one(&sg[i], buf, size);
@@ -842,10 +841,9 @@ test_ctrl_queue (struct usbtest_dev *dev, struct usbtest_param *param)
* as with bulk/intr sglists, sglen is the queue depth; it also
* controls which subtests run (more tests than sglen) or rerun.
*/
- urb = kmalloc (param->sglen * sizeof (struct urb *), SLAB_KERNEL);
+ urb = kcalloc(param->sglen, sizeof(struct urb *), SLAB_KERNEL);
if (!urb)
return -ENOMEM;
- memset (urb, 0, param->sglen * sizeof (struct urb *));
for (i = 0; i < param->sglen; i++) {
int pipe = usb_rcvctrlpipe (udev, 0);
unsigned len;
@@ -1865,10 +1863,9 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id)
}
#endif
- dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+ dev = kzalloc(sizeof(*dev), SLAB_KERNEL);
if (!dev)
return -ENOMEM;
- memset (dev, 0, sizeof *dev);
info = (struct usbtest_info *) id->driver_info;
dev->info = info;
init_MUTEX (&dev->sem);
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index c34944c7504..6ecc2730221 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -12,6 +12,7 @@
#include <linux/debugfs.h>
#include <linux/smp_lock.h>
#include <linux/notifier.h>
+#include <linux/mutex.h>
#include "usb_mon.h"
#include "../core/hcd.h"
@@ -23,7 +24,7 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
static void mon_bus_drop(struct kref *r);
static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus);
-DECLARE_MUTEX(mon_lock);
+DEFINE_MUTEX(mon_lock);
static struct dentry *mon_dir; /* /dbg/usbmon */
static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */
@@ -196,14 +197,14 @@ static void mon_bus_remove(struct usb_bus *ubus)
{
struct mon_bus *mbus = ubus->mon_bus;
- down(&mon_lock);
+ mutex_lock(&mon_lock);
list_del(&mbus->bus_link);
debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
mon_dissolve(mbus, ubus);
kref_put(&mbus->ref, mon_bus_drop);
- up(&mon_lock);
+ mutex_unlock(&mon_lock);
}
static int mon_notify(struct notifier_block *self, unsigned long action,
@@ -276,9 +277,8 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
char name[NAMESZ];
int rc;
- if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
+ if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
goto err_alloc;
- memset(mbus, 0, sizeof(struct mon_bus));
kref_init(&mbus->ref);
spin_lock_init(&mbus->lock);
INIT_LIST_HEAD(&mbus->r_list);
@@ -307,9 +307,9 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
goto err_create_s;
mbus->dent_s = d;
- down(&mon_lock);
+ mutex_lock(&mon_lock);
list_add_tail(&mbus->bus_link, &mon_buses);
- up(&mon_lock);
+ mutex_unlock(&mon_lock);
return;
err_create_s:
@@ -347,11 +347,11 @@ static int __init mon_init(void)
usb_register_notify(&mon_nb);
- down(&usb_bus_list_lock);
+ mutex_lock(&usb_bus_list_lock);
list_for_each_entry (ubus, &usb_bus_list, bus_list) {
mon_bus_init(mondir, ubus);
}
- up(&usb_bus_list_lock);
+ mutex_unlock(&usb_bus_list_lock);
return 0;
}
@@ -363,7 +363,7 @@ static void __exit mon_exit(void)
usb_unregister_notify(&mon_nb);
usb_mon_deregister();
- down(&mon_lock);
+ mutex_lock(&mon_lock);
while (!list_empty(&mon_buses)) {
p = mon_buses.next;
mbus = list_entry(p, struct mon_bus, bus_link);
@@ -387,7 +387,7 @@ static void __exit mon_exit(void)
mon_dissolve(mbus, mbus->u_bus);
kref_put(&mbus->ref, mon_bus_drop);
}
- up(&mon_lock);
+ mutex_unlock(&mon_lock);
debugfs_remove(mon_dir);
}
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 611612146ae..ac043ec2b8d 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -8,6 +8,7 @@
#include <linux/list.h>
#include <linux/usb.h>
#include <linux/time.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
#include "usb_mon.h"
@@ -54,7 +55,7 @@ struct mon_reader_text {
wait_queue_head_t wait;
int printf_size;
char *printf_buf;
- struct semaphore printf_lock;
+ struct mutex printf_lock;
char slab_name[SLAB_NAME_SZ];
};
@@ -208,19 +209,18 @@ static int mon_text_open(struct inode *inode, struct file *file)
struct mon_reader_text *rp;
int rc;
- down(&mon_lock);
+ mutex_lock(&mon_lock);
mbus = inode->u.generic_ip;
ubus = mbus->u_bus;
- rp = kmalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
+ rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
if (rp == NULL) {
rc = -ENOMEM;
goto err_alloc;
}
- memset(rp, 0, sizeof(struct mon_reader_text));
INIT_LIST_HEAD(&rp->e_list);
init_waitqueue_head(&rp->wait);
- init_MUTEX(&rp->printf_lock);
+ mutex_init(&rp->printf_lock);
rp->printf_size = PRINTF_DFL;
rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL);
@@ -247,7 +247,7 @@ static int mon_text_open(struct inode *inode, struct file *file)
mon_reader_add(mbus, &rp->r);
file->private_data = rp;
- up(&mon_lock);
+ mutex_unlock(&mon_lock);
return 0;
// err_busy:
@@ -257,7 +257,7 @@ err_slab:
err_alloc_pr:
kfree(rp);
err_alloc:
- up(&mon_lock);
+ mutex_unlock(&mon_lock);
return rc;
}
@@ -301,7 +301,7 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
set_current_state(TASK_RUNNING);
remove_wait_queue(&rp->wait, &waita);
- down(&rp->printf_lock);
+ mutex_lock(&rp->printf_lock);
cnt = 0;
pbuf = rp->printf_buf;
limit = rp->printf_size;
@@ -358,7 +358,7 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
if (copy_to_user(buf, rp->printf_buf, cnt))
cnt = -EFAULT;
- up(&rp->printf_lock);
+ mutex_unlock(&rp->printf_lock);
kmem_cache_free(rp->e_slab, ep);
return cnt;
}
@@ -371,12 +371,12 @@ static int mon_text_release(struct inode *inode, struct file *file)
struct list_head *p;
struct mon_event_text *ep;
- down(&mon_lock);
+ mutex_lock(&mon_lock);
mbus = inode->u.generic_ip;
if (mbus->nreaders <= 0) {
printk(KERN_ERR TAG ": consistency error on close\n");
- up(&mon_lock);
+ mutex_unlock(&mon_lock);
return 0;
}
mon_reader_del(mbus, &rp->r);
@@ -402,7 +402,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
kfree(rp->printf_buf);
kfree(rp);
- up(&mon_lock);
+ mutex_unlock(&mon_lock);
return 0;
}
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h
index 4be0f934607..8e0613c350c 100644
--- a/drivers/usb/mon/usb_mon.h
+++ b/drivers/usb/mon/usb_mon.h
@@ -49,7 +49,7 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
*/
extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len);
-extern struct semaphore mon_lock;
+extern struct mutex mon_lock;
extern struct file_operations mon_fops_text;
extern struct file_operations mon_fops_stat;
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
index 156a2f1cb39..5b667568456 100644
--- a/drivers/usb/net/pegasus.c
+++ b/drivers/usb/net/pegasus.c
@@ -524,6 +524,7 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)
ret = set_registers(pegasus, EthCtrl0, 3, data);
if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
+ usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS2 ||
usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
u16 auxmode;
read_mii_word(pegasus, 0, 0x1b, &auxmode);
diff --git a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h
index 9fbd59b55cb..a54752ce149 100644
--- a/drivers/usb/net/pegasus.h
+++ b/drivers/usb/net/pegasus.h
@@ -25,7 +25,6 @@
#define PHY_READ 0x40
#define PHY_WRITE 0x20
#define DEFAULT_GPIO_RESET 0x24
-#define LINKSYS_GPIO_RESET 0x24
#define DEFAULT_GPIO_SET 0x26
#define PEGASUS_PRESENT 0x00000001
@@ -140,6 +139,7 @@ struct usb_eth_dev {
#define VENDOR_KINGSTON 0x0951
#define VENDOR_LANEED 0x056e
#define VENDOR_LINKSYS 0x066b
+#define VENDOR_LINKSYS2 0x077b
#define VENDOR_MELCO 0x0411
#define VENDOR_MICROSOFT 0x045e
#define VENDOR_MOBILITY 0x1342
@@ -218,15 +218,15 @@ PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004,
PEGASUS_DEV( "Corega FEter USB-TXS", VENDOR_COREGA, 0x000d,
DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4001,
- LINKSYS_GPIO_RESET )
+ DEFAULT_GPIO_RESET )
PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4002,
- LINKSYS_GPIO_RESET )
+ DEFAULT_GPIO_RESET )
PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4102,
- LINKSYS_GPIO_RESET | PEGASUS_II )
+ DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x400b,
- LINKSYS_GPIO_RESET | PEGASUS_II )
+ DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x200c,
- LINKSYS_GPIO_RESET | PEGASUS_II )
+ DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK, 0x4003,
DEFAULT_GPIO_RESET | HAS_HOME_PNA )
PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK, 0xabc1,
@@ -260,17 +260,19 @@ PEGASUS_DEV( "LANEED USB Ethernet LD-USB/T", VENDOR_LANEED, 0xabc1,
PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x200c,
DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x2202,
- LINKSYS_GPIO_RESET )
+ DEFAULT_GPIO_RESET )
PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2203,
- LINKSYS_GPIO_RESET )
+ DEFAULT_GPIO_RESET )
PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204,
- LINKSYS_GPIO_RESET | HAS_HOME_PNA )
+ DEFAULT_GPIO_RESET | HAS_HOME_PNA )
PEGASUS_DEV( "Linksys USB10T Ethernet Adapter", VENDOR_LINKSYS, 0x2206,
- LINKSYS_GPIO_RESET | PEGASUS_II)
+ DEFAULT_GPIO_RESET | PEGASUS_II)
+PEGASUS_DEV( "Linksys USBVPN1", VENDOR_LINKSYS2, 0x08b4,
+ DEFAULT_GPIO_RESET )
PEGASUS_DEV( "Linksys USB USB100TX", VENDOR_LINKSYS, 0x400b,
- LINKSYS_GPIO_RESET | PEGASUS_II )
+ DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x200c,
- LINKSYS_GPIO_RESET | PEGASUS_II )
+ DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0001,
DEFAULT_GPIO_RESET )
PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0005,
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index 8ca52be2397..1bbbae283c0 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -880,7 +880,6 @@ static int rtl8150_probe(struct usb_interface *intf,
}
fill_skb_pool(dev);
set_ethernet_addr(dev);
- info("%s: rtl8150 is detected", netdev->name);
usb_set_intfdata(intf, dev);
SET_NETDEV_DEV(netdev, &intf->dev);
@@ -888,6 +887,9 @@ static int rtl8150_probe(struct usb_interface *intf,
err("couldn't register the device");
goto out2;
}
+
+ info("%s: rtl8150 is detected", netdev->name);
+
return 0;
out2:
diff --git a/drivers/usb/net/zd1201.c b/drivers/usb/net/zd1201.c
index f3a8e2807c3..fe9b60cd8d9 100644
--- a/drivers/usb/net/zd1201.c
+++ b/drivers/usb/net/zd1201.c
@@ -621,10 +621,9 @@ static int zd1201_drvr_start(struct zd1201 *zd)
__le16 zdmax;
unsigned char *buffer;
- buffer = kmalloc(ZD1201_RXSIZE, GFP_KERNEL);
+ buffer = kzalloc(ZD1201_RXSIZE, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
- memset(buffer, 0, ZD1201_RXSIZE);
usb_fill_bulk_urb(zd->rx_urb, zd->usb,
usb_rcvbulkpipe(zd->usb, zd->endp_in), buffer, ZD1201_RXSIZE,
@@ -1750,11 +1749,9 @@ static int zd1201_probe(struct usb_interface *interface,
usb = interface_to_usbdev(interface);
- zd = kmalloc(sizeof(struct zd1201), GFP_KERNEL);
- if (!zd) {
+ zd = kzalloc(sizeof(struct zd1201), GFP_KERNEL);
+ if (!zd)
return -ENOMEM;
- }
- memset(zd, 0, sizeof(struct zd1201));
zd->ap = ap;
zd->usb = usb;
zd->removed = 0;
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index be5dc80836c..5a8a2c91c2b 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -403,6 +403,13 @@ config USB_SERIAL_MCT_U232
To compile this driver as a module, choose M here: the
module will be called mct_u232.
+config USB_SERIAL_NAVMAN
+ tristate "USB Navman GPS device"
+ depends on USB_SERIAL
+ help
+ To compile this driver as a module, choose M here: the
+ module will be called navman.
+
config USB_SERIAL_PL2303
tristate "USB Prolific 2303 Single Port Serial Driver"
depends on USB_SERIAL
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index f0b04420cea..f7fe4172efe 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o
obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o
obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o
+obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
obj-$(CONFIG_USB_SERIAL_OPTION) += option.o
obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o
diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c
index dc7a069503e..e0c2acdb3f0 100644
--- a/drivers/usb/serial/cp2101.c
+++ b/drivers/usb/serial/cp2101.c
@@ -32,7 +32,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.06"
+#define DRIVER_VERSION "v0.07"
#define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver"
/*
@@ -58,6 +58,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
+ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
{ USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
{ USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
@@ -169,9 +170,7 @@ static int cp2101_get_config(struct usb_serial_port* port, u8 request,
/* Number of integers required to contain the array */
length = (((size - 1) | 3) + 1)/4;
- buf = kmalloc (length * sizeof(u32), GFP_KERNEL);
- memset(buf, 0, length * sizeof(u32));
-
+ buf = kcalloc(length, sizeof(u32), GFP_KERNEL);
if (!buf) {
dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__);
return -ENOMEM;
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 68067fe117a..7212fbe3b6f 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -98,10 +98,16 @@ static struct usb_device_id id_table_cyphidcomrs232 [] = {
{ } /* Terminating entry */
};
+static struct usb_device_id id_table_nokiaca42v2 [] = {
+ { USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
+ { } /* Terminating entry */
+};
+
static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
+ { USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
{ } /* Terminating entry */
};
@@ -149,6 +155,7 @@ struct cypress_buf {
/* function prototypes for the Cypress USB to serial device */
static int cypress_earthmate_startup (struct usb_serial *serial);
static int cypress_hidcom_startup (struct usb_serial *serial);
+static int cypress_ca42v2_startup (struct usb_serial *serial);
static void cypress_shutdown (struct usb_serial *serial);
static int cypress_open (struct usb_serial_port *port, struct file *filp);
static void cypress_close (struct usb_serial_port *port, struct file *filp);
@@ -235,6 +242,34 @@ static struct usb_serial_driver cypress_hidcom_device = {
.write_int_callback = cypress_write_int_callback,
};
+static struct usb_serial_driver cypress_ca42v2_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "nokiaca42v2",
+ },
+ .description = "Nokia CA-42 V2 Adapter",
+ .id_table = id_table_nokiaca42v2,
+ .num_interrupt_in = 1,
+ .num_interrupt_out = 1,
+ .num_bulk_in = NUM_DONT_CARE,
+ .num_bulk_out = NUM_DONT_CARE,
+ .num_ports = 1,
+ .attach = cypress_ca42v2_startup,
+ .shutdown = cypress_shutdown,
+ .open = cypress_open,
+ .close = cypress_close,
+ .write = cypress_write,
+ .write_room = cypress_write_room,
+ .ioctl = cypress_ioctl,
+ .set_termios = cypress_set_termios,
+ .tiocmget = cypress_tiocmget,
+ .tiocmset = cypress_tiocmset,
+ .chars_in_buffer = cypress_chars_in_buffer,
+ .throttle = cypress_throttle,
+ .unthrottle = cypress_unthrottle,
+ .read_int_callback = cypress_read_int_callback,
+ .write_int_callback = cypress_write_int_callback,
+};
/*****************************************************************************
* Cypress serial helper functions
@@ -286,6 +321,12 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
__FUNCTION__);
new_baudrate = priv->baud_rate;
}
+ } else if (priv->chiptype == CT_CA42V2) {
+ if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
+ err("%s - failed setting baud rate, unsupported speed",
+ __FUNCTION__);
+ new_baudrate = priv->baud_rate;
+ }
} else if (priv->chiptype == CT_GENERIC) {
if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
err("%s - failed setting baud rate, unsupported speed",
@@ -435,11 +476,10 @@ static int generic_startup (struct usb_serial *serial)
dbg("%s - port %d", __FUNCTION__, serial->port[0]->number);
- priv = kmalloc(sizeof (struct cypress_private), GFP_KERNEL);
+ priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- memset(priv, 0x00, sizeof (struct cypress_private));
spin_lock_init(&priv->lock);
priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE);
if (priv->buf == NULL) {
@@ -500,6 +540,25 @@ static int cypress_hidcom_startup (struct usb_serial *serial)
} /* cypress_hidcom_startup */
+static int cypress_ca42v2_startup (struct usb_serial *serial)
+{
+ struct cypress_private *priv;
+
+ dbg("%s", __FUNCTION__);
+
+ if (generic_startup(serial)) {
+ dbg("%s - Failed setting up port %d", __FUNCTION__,
+ serial->port[0]->number);
+ return 1;
+ }
+
+ priv = usb_get_serial_port_data(serial->port[0]);
+ priv->chiptype = CT_CA42V2;
+
+ return 0;
+} /* cypress_ca42v2_startup */
+
+
static void cypress_shutdown (struct usb_serial *serial)
{
struct cypress_private *priv;
@@ -944,6 +1003,10 @@ static void cypress_set_termios (struct usb_serial_port *port,
*(tty->termios) = tty_std_termios;
tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL |
CLOCAL;
+ } else if (priv->chiptype == CT_CA42V2) {
+ *(tty->termios) = tty_std_termios;
+ tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL |
+ CLOCAL;
}
priv->termios_initialized = 1;
}
@@ -1542,6 +1605,9 @@ static int __init cypress_init(void)
retval = usb_serial_register(&cypress_hidcom_device);
if (retval)
goto failed_hidcom_register;
+ retval = usb_serial_register(&cypress_ca42v2_device);
+ if (retval)
+ goto failed_ca42v2_register;
retval = usb_register(&cypress_driver);
if (retval)
goto failed_usb_register;
@@ -1550,6 +1616,8 @@ static int __init cypress_init(void)
return 0;
failed_usb_register:
usb_deregister(&cypress_driver);
+failed_ca42v2_register:
+ usb_serial_deregister(&cypress_ca42v2_device);
failed_hidcom_register:
usb_serial_deregister(&cypress_hidcom_device);
failed_em_register:
@@ -1566,6 +1634,7 @@ static void __exit cypress_exit (void)
usb_deregister (&cypress_driver);
usb_serial_deregister (&cypress_earthmate_device);
usb_serial_deregister (&cypress_hidcom_device);
+ usb_serial_deregister (&cypress_ca42v2_device);
}
diff --git a/drivers/usb/serial/cypress_m8.h b/drivers/usb/serial/cypress_m8.h
index 1fa119efe41..e1c7c27e18b 100644
--- a/drivers/usb/serial/cypress_m8.h
+++ b/drivers/usb/serial/cypress_m8.h
@@ -18,6 +18,10 @@
/* Cypress HID->COM RS232 Adapter */
#define VENDOR_ID_CYPRESS 0x04b4
#define PRODUCT_ID_CYPHIDCOM 0x5500
+
+/* Nokia CA-42 USB to serial cable */
+#define VENDOR_ID_DAZZLE 0x07d0
+#define PRODUCT_ID_CA42 0x4101
/* End of device listing */
/* Used for setting / requesting serial line settings */
@@ -34,6 +38,7 @@
#define CT_EARTHMATE 0x01
#define CT_CYPHIDCOM 0x02
+#define CT_CA42V2 0x03
#define CT_GENERIC 0x0F
/* End of chiptype definitions */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index c145e1ed842..f3af81b4dd2 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -492,6 +492,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_777_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) },
+ { USB_DEVICE(ICOM_ID1_VID, ICOM_ID1_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
@@ -1141,12 +1142,11 @@ static int ftdi_sio_attach (struct usb_serial *serial)
dbg("%s",__FUNCTION__);
- priv = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL);
+ priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
if (!priv){
err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct ftdi_private));
return -ENOMEM;
}
- memset(priv, 0, sizeof(*priv));
spin_lock_init(&priv->rx_lock);
init_waitqueue_head(&priv->delta_msr_wait);
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index bdef3b8c731..8da773c2744 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -146,6 +146,13 @@
#define KOBIL_CONV_KAAN_PID 0x2021 /* KOBIL_Konverter for KAAN */
/*
+ * Icom ID-1 digital transceiver
+ */
+
+#define ICOM_ID1_VID 0x0C26
+#define ICOM_ID1_PID 0x0004
+
+/*
* DSS-20 Sync Station for Sony Ericsson P800
*/
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index d6f55e9dcca..5ec9bf5bac8 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -1422,12 +1422,11 @@ static int garmin_attach (struct usb_serial *serial)
dbg("%s", __FUNCTION__);
- garmin_data_p = kmalloc (sizeof(struct garmin_data), GFP_KERNEL);
+ garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL);
if (garmin_data_p == NULL) {
dev_err(&port->dev, "%s - Out of memory\n", __FUNCTION__);
return -ENOMEM;
}
- memset (garmin_data_p, 0, sizeof(struct garmin_data));
init_timer(&garmin_data_p->timer);
spin_lock_init(&garmin_data_p->lock);
INIT_LIST_HEAD(&garmin_data_p->pktlist);
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 3f29e6b0fd1..b606c596810 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2725,12 +2725,11 @@ static int edge_startup (struct usb_serial *serial)
dev = serial->dev;
/* create our private serial structure */
- edge_serial = kmalloc (sizeof(struct edgeport_serial), GFP_KERNEL);
+ edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
if (edge_serial == NULL) {
dev_err(&serial->dev->dev, "%s - Out of memory\n", __FUNCTION__);
return -ENOMEM;
}
- memset (edge_serial, 0, sizeof(struct edgeport_serial));
spin_lock_init(&edge_serial->es_lock);
edge_serial->serial = serial;
usb_set_serial_data(serial, edge_serial);
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index afc0f34b3a4..8e1e2253748 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -2727,12 +2727,11 @@ static int edge_startup (struct usb_serial *serial)
dev = serial->dev;
/* create our private serial structure */
- edge_serial = kmalloc (sizeof(struct edgeport_serial), GFP_KERNEL);
+ edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
if (edge_serial == NULL) {
dev_err(&serial->dev->dev, "%s - Out of memory\n", __FUNCTION__);
return -ENOMEM;
}
- memset (edge_serial, 0, sizeof(struct edgeport_serial));
sema_init(&edge_serial->es_sem, 1);
edge_serial->serial = serial;
usb_set_serial_data(serial, edge_serial);
@@ -2745,12 +2744,11 @@ static int edge_startup (struct usb_serial *serial)
/* set up our port private structures */
for (i = 0; i < serial->num_ports; ++i) {
- edge_port = kmalloc (sizeof(struct edgeport_port), GFP_KERNEL);
+ edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL);
if (edge_port == NULL) {
dev_err(&serial->dev->dev, "%s - Out of memory\n", __FUNCTION__);
goto cleanup;
}
- memset (edge_port, 0, sizeof(struct edgeport_port));
spin_lock_init(&edge_port->ep_lock);
edge_port->ep_out_buf = edge_buf_alloc(EDGE_OUT_BUF_SIZE);
if (edge_port->ep_out_buf == NULL) {
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index a5901042144..426182ddc42 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -184,10 +184,9 @@ static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev,
struct irda_class_desc *desc;
int ret;
- desc = kmalloc(sizeof (struct irda_class_desc), GFP_KERNEL);
+ desc = kzalloc(sizeof (struct irda_class_desc), GFP_KERNEL);
if (desc == NULL)
return NULL;
- memset(desc, 0, sizeof(struct irda_class_desc));
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0),
IU_REQ_GET_CLASS_DESC,
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 3b958e60f5e..052b735c4fb 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -2250,12 +2250,11 @@ static int keyspan_startup (struct usb_serial *serial)
}
/* Setup private data for serial driver */
- s_priv = kmalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL);
+ s_priv = kzalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL);
if (!s_priv) {
dbg("%s - kmalloc for keyspan_serial_private failed.", __FUNCTION__);
return -ENOMEM;
}
- memset(s_priv, 0, sizeof(struct keyspan_serial_private));
s_priv->device_details = d_details;
usb_set_serial_data(serial, s_priv);
@@ -2263,12 +2262,11 @@ static int keyspan_startup (struct usb_serial *serial)
/* Now setup per port private data */
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
- p_priv = kmalloc(sizeof(struct keyspan_port_private), GFP_KERNEL);
+ p_priv = kzalloc(sizeof(struct keyspan_port_private), GFP_KERNEL);
if (!p_priv) {
dbg("%s - kmalloc for keyspan_port_private (%d) failed!.", __FUNCTION__, i);
return (1);
}
- memset(p_priv, 0, sizeof(struct keyspan_port_private));
p_priv->device_details = d_details;
usb_set_serial_port_data(port, p_priv);
}
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index b8b213185d0..87dfcd89ffa 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -255,11 +255,9 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp)
}
// allocate memory for transfer buffer
- transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL);
+ transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
if (! transfer_buffer) {
return -ENOMEM;
- } else {
- memset(transfer_buffer, 0, transfer_buffer_length);
}
// allocate write_urb
@@ -383,11 +381,10 @@ static void kobil_read_int_callback( struct urb *purb, struct pt_regs *regs)
// BEGIN DEBUG
/*
- dbg_data = (unsigned char *) kmalloc((3 * purb->actual_length + 10) * sizeof(char), GFP_KERNEL);
+ dbg_data = kzalloc((3 * purb->actual_length + 10) * sizeof(char), GFP_KERNEL);
if (! dbg_data) {
return;
}
- memset(dbg_data, 0, (3 * purb->actual_length + 10));
for (i = 0; i < purb->actual_length; i++) {
sprintf(dbg_data +3*i, "%02X ", data[i]);
}
@@ -518,11 +515,10 @@ static int kobil_tiocmget(struct usb_serial_port *port, struct file *file)
}
// allocate memory for transfer buffer
- transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL);
+ transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
if (!transfer_buffer) {
return -ENOMEM;
}
- memset(transfer_buffer, 0, transfer_buffer_length);
result = usb_control_msg( port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0 ),
@@ -564,11 +560,10 @@ static int kobil_tiocmset(struct usb_serial_port *port, struct file *file,
}
// allocate memory for transfer buffer
- transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL);
+ transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
if (! transfer_buffer) {
return -ENOMEM;
}
- memset(transfer_buffer, 0, transfer_buffer_length);
if (set & TIOCM_RTS)
rts = 1;
@@ -655,11 +650,10 @@ static int kobil_ioctl(struct usb_serial_port *port, struct file *file,
(struct termios __user *)arg))
return -EFAULT;
- settings = (unsigned char *) kmalloc(50, GFP_KERNEL);
+ settings = kzalloc(50, GFP_KERNEL);
if (! settings) {
return -ENOBUFS;
}
- memset(settings, 0, 50);
switch (priv->internal_termios.c_cflag & CBAUD) {
case B1200:
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index b6d6cab9c85..35bd29b6c40 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -348,10 +348,9 @@ static int mct_u232_startup (struct usb_serial *serial)
struct mct_u232_private *priv;
struct usb_serial_port *port, *rport;
- priv = kmalloc(sizeof(struct mct_u232_private), GFP_KERNEL);
+ priv = kzalloc(sizeof(struct mct_u232_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- memset(priv, 0, sizeof(struct mct_u232_private));
spin_lock_init(&priv->lock);
usb_set_serial_port_data(serial->port[0], priv);
diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c
new file mode 100644
index 00000000000..7f544081032
--- /dev/null
+++ b/drivers/usb/serial/navman.c
@@ -0,0 +1,157 @@
+/*
+ * Navman Serial USB driver
+ *
+ * Copyright (C) 2006 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "usb-serial.h"
+
+static int debug;
+
+static struct usb_device_id id_table [] = {
+ { USB_DEVICE(0x0a99, 0x0001) }, /* Talon Technology device */
+ { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver navman_driver = {
+ .name = "navman",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = id_table,
+ .no_dynamic_id = 1,
+};
+
+static void navman_read_int_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ unsigned char *data = urb->transfer_buffer;
+ struct tty_struct *tty;
+ int result;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+ urb->actual_length, data);
+
+ tty = port->tty;
+ if (tty && urb->actual_length) {
+ tty_buffer_request_room(tty, urb->actual_length);
+ tty_insert_flip_string(tty, data, urb->actual_length);
+ tty_flip_buffer_push(tty);
+ }
+
+exit:
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&urb->dev->dev,
+ "%s - Error %d submitting interrupt urb\n",
+ __FUNCTION__, result);
+}
+
+static int navman_open(struct usb_serial_port *port, struct file *filp)
+{
+ int result = 0;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (port->interrupt_in_urb) {
+ dbg("%s - adding interrupt input for treo", __FUNCTION__);
+ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+ if (result)
+ dev_err(&port->dev,
+ "%s - failed submitting interrupt urb, error %d\n",
+ __FUNCTION__, result);
+ }
+ return result;
+}
+
+static void navman_close(struct usb_serial_port *port, struct file *filp)
+{
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (port->interrupt_in_urb)
+ usb_kill_urb(port->interrupt_in_urb);
+}
+
+static int navman_write(struct usb_serial_port *port,
+ const unsigned char *buf, int count)
+{
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /*
+ * This device can't write any data, only read from the device
+ * so we just silently eat all data sent to us and say it was
+ * successfully sent.
+ * Evil, I know, but do you have a better idea?
+ */
+
+ return count;
+}
+
+static struct usb_serial_driver navman_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "navman",
+ },
+ .id_table = id_table,
+ .num_interrupt_in = NUM_DONT_CARE,
+ .num_bulk_in = NUM_DONT_CARE,
+ .num_bulk_out = NUM_DONT_CARE,
+ .num_ports = 1,
+ .open = navman_open,
+ .close = navman_close,
+ .write = navman_write,
+ .read_int_callback = navman_read_int_callback,
+};
+
+static int __init navman_init(void)
+{
+ int retval;
+
+ retval = usb_serial_register(&navman_device);
+ if (retval)
+ return retval;
+ retval = usb_register(&navman_driver);
+ if (retval)
+ usb_serial_deregister(&navman_device);
+ return retval;
+}
+
+static void __exit navman_exit(void)
+{
+ usb_deregister(&navman_driver);
+ usb_serial_deregister(&navman_device);
+}
+
+module_init(navman_init);
+module_exit(navman_exit);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 762d8ff9a1e..4d40704dea2 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -204,7 +204,7 @@ static void omninet_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
int i;
int result;
-// dbg("omninet_read_bulk_callback");
+ dbg("%s - port %d", __FUNCTION__, port->number);
if (urb->status) {
dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
@@ -250,7 +250,7 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf
int result;
-// dbg("omninet_write port %d", port->number);
+ dbg("%s - port %d", __FUNCTION__, port->number);
if (count == 0) {
dbg("%s - write request of 0 bytes", __FUNCTION__);
@@ -302,7 +302,7 @@ static int omninet_write_room (struct usb_serial_port *port)
if (wport->write_urb_busy)
room = wport->bulk_out_size - OMNINET_HEADERLEN;
-// dbg("omninet_write_room returns %d", room);
+ dbg("%s - returns %d", __FUNCTION__, room);
return (room);
}
@@ -312,7 +312,7 @@ static void omninet_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
/* struct omninet_header *header = (struct omninet_header *) urb->transfer_buffer; */
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
-// dbg("omninet_write_bulk_callback, port %0x\n", port);
+ dbg("%s - port %0x\n", __FUNCTION__, port->number);
port->write_urb_busy = 0;
if (urb->status) {
@@ -321,8 +321,6 @@ static void omninet_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
}
schedule_work(&port->work);
-
-// dbg("omninet_write_bulk_callback, tty %0x\n", tty);
}
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 52bdf6fe46f..a8455c9e79d 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -631,13 +631,12 @@ static int option_startup(struct usb_serial *serial)
/* Now setup per port private data */
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
- portdata = kmalloc(sizeof(*portdata), GFP_KERNEL);
+ portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
if (!portdata) {
dbg("%s: kmalloc for option_port_private (%d) failed!.",
__FUNCTION__, i);
return (1);
}
- memset(portdata, 0, sizeof(struct option_port_private));
usb_set_serial_port_data(port, portdata);
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 37c81c08faa..b3014fda645 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -77,6 +77,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) },
{ USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) },
{ USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) },
+ { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) },
{ } /* Terminating entry */
};
@@ -218,10 +219,9 @@ static int pl2303_startup (struct usb_serial *serial)
dbg("device type: %d", type);
for (i = 0; i < serial->num_ports; ++i) {
- priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL);
+ priv = kzalloc(sizeof(struct pl2303_private), GFP_KERNEL);
if (!priv)
goto cleanup;
- memset (priv, 0x00, sizeof (struct pl2303_private));
spin_lock_init(&priv->lock);
priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE);
if (priv->buf == NULL) {
@@ -383,12 +383,11 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
}
}
- buf = kmalloc (7, GFP_KERNEL);
+ buf = kzalloc (7, GFP_KERNEL);
if (!buf) {
dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__);
return;
}
- memset (buf, 0x00, 0x07);
i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
@@ -828,6 +827,7 @@ static void pl2303_update_line_status(struct usb_serial_port *port,
spin_lock_irqsave(&priv->lock, flags);
priv->line_status = data[status_idx];
spin_unlock_irqrestore(&priv->lock, flags);
+ wake_up_interruptible (&priv->delta_msr_wait);
exit:
return;
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 9bc4755162a..77901d14397 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -75,3 +75,7 @@
/* Leadtek GPS 9531 (ID 0413:2101) */
#define LEADTEK_VENDOR_ID 0x0413
#define LEADTEK_9531_PRODUCT_ID 0x2101
+
+/* USB GSM cable from Speed Dragon Multimedia, Ltd */
+#define SPEEDDRAGON_VENDOR_ID 0x0e55
+#define SPEEDDRAGON_PRODUCT_ID 0x110b
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index c18db325707..c3a2071b802 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -416,12 +416,11 @@ static int ti_startup(struct usb_serial *serial)
dev->actconfig->desc.bConfigurationValue);
/* create device structure */
- tdev = kmalloc(sizeof(struct ti_device), GFP_KERNEL);
+ tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL);
if (tdev == NULL) {
dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
return -ENOMEM;
}
- memset(tdev, 0, sizeof(struct ti_device));
sema_init(&tdev->td_open_close_sem, 1);
tdev->td_serial = serial;
usb_set_serial_data(serial, tdev);
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index b5c96e74a90..097f4e8488f 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -564,12 +564,11 @@ static struct usb_serial * create_serial (struct usb_device *dev,
{
struct usb_serial *serial;
- serial = kmalloc (sizeof (*serial), GFP_KERNEL);
+ serial = kzalloc(sizeof(*serial), GFP_KERNEL);
if (!serial) {
dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
return NULL;
}
- memset (serial, 0, sizeof(*serial));
serial->dev = usb_get_dev(dev);
serial->type = driver;
serial->interface = interface;
@@ -778,10 +777,9 @@ int usb_serial_probe(struct usb_interface *interface,
serial->num_port_pointers = max_endpoints;
dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);
for (i = 0; i < max_endpoints; ++i) {
- port = kmalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
if (!port)
goto probe_error;
- memset(port, 0x00, sizeof(struct usb_serial_port));
port->number = i + serial->minor;
port->serial = serial;
spin_lock_init(&port->lock);
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 11a48d87475..f5c3841d484 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -763,10 +763,9 @@ static int generic_startup(struct usb_serial *serial)
int i;
for (i = 0; i < serial->num_ports; ++i) {
- priv = kmalloc (sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc (sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- memset (priv, 0x00, sizeof(*priv));
spin_lock_init(&priv->lock);
usb_set_serial_port_data(serial->port[i], priv);
}
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
index 54e3e6c7ecd..01d8971ad7d 100644
--- a/drivers/usb/storage/datafab.c
+++ b/drivers/usb/storage/datafab.c
@@ -512,13 +512,12 @@ int datafab_transport(struct scsi_cmnd * srb, struct us_data *us)
};
if (!us->extra) {
- us->extra = kmalloc(sizeof(struct datafab_info), GFP_NOIO);
+ us->extra = kzalloc(sizeof(struct datafab_info), GFP_NOIO);
if (!us->extra) {
US_DEBUGP("datafab_transport: Gah! "
"Can't allocate storage for Datafab info struct!\n");
return USB_STOR_TRANSPORT_ERROR;
}
- memset(us->extra, 0, sizeof(struct datafab_info));
us->extra_destructor = datafab_info_destructor;
((struct datafab_info *)us->extra)->lun = -1;
}
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index ecb328aa9ea..6831dca93c1 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1361,21 +1361,19 @@ static int isd200_init_info(struct us_data *us)
struct isd200_info *info;
info = (struct isd200_info *)
- kmalloc(sizeof(struct isd200_info), GFP_KERNEL);
+ kzalloc(sizeof(struct isd200_info), GFP_KERNEL);
if (!info)
retStatus = ISD200_ERROR;
else {
- memset(info, 0, sizeof(struct isd200_info));
info->id = (struct hd_driveid *)
- kmalloc(sizeof(struct hd_driveid), GFP_KERNEL);
+ kzalloc(sizeof(struct hd_driveid), GFP_KERNEL);
info->RegsBuf = (unsigned char *)
kmalloc(sizeof(info->ATARegs), GFP_KERNEL);
if (!info->id || !info->RegsBuf) {
isd200_free_info_ptrs(info);
kfree(info);
retStatus = ISD200_ERROR;
- } else
- memset(info->id, 0, sizeof(struct hd_driveid));
+ }
}
if (retStatus == ISD200_GOOD) {
@@ -1384,7 +1382,7 @@ static int isd200_init_info(struct us_data *us)
} else
US_DEBUGP("ERROR - kmalloc failure\n");
- return(retStatus);
+ return retStatus;
}
/**************************************************************************
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
index aff9d51c327..5031aa98f6a 100644
--- a/drivers/usb/storage/jumpshot.c
+++ b/drivers/usb/storage/jumpshot.c
@@ -441,12 +441,11 @@ int jumpshot_transport(struct scsi_cmnd * srb, struct us_data *us)
};
if (!us->extra) {
- us->extra = kmalloc(sizeof(struct jumpshot_info), GFP_NOIO);
+ us->extra = kzalloc(sizeof(struct jumpshot_info), GFP_NOIO);
if (!us->extra) {
US_DEBUGP("jumpshot_transport: Gah! Can't allocate storage for jumpshot info struct!\n");
return USB_STOR_TRANSPORT_ERROR;
}
- memset(us->extra, 0, sizeof(struct jumpshot_info));
us->extra_destructor = jumpshot_info_destructor;
}
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 4ef5527028c..5f11e19eaae 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -47,6 +47,7 @@
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -271,9 +272,9 @@ static int device_reset(struct scsi_cmnd *srb)
US_DEBUGP("%s called\n", __FUNCTION__);
/* lock the device pointers and do the reset */
- down(&(us->dev_semaphore));
+ mutex_lock(&(us->dev_mutex));
result = us->transport_reset(us);
- up(&(us->dev_semaphore));
+ mutex_unlock(&us->dev_mutex);
return result < 0 ? FAILED : SUCCESS;
}
@@ -286,9 +287,9 @@ static int bus_reset(struct scsi_cmnd *srb)
US_DEBUGP("%s called\n", __FUNCTION__);
- down(&(us->dev_semaphore));
+ mutex_lock(&(us->dev_mutex));
result = usb_stor_port_reset(us);
- up(&(us->dev_semaphore));
+ mutex_unlock(&us->dev_mutex);
return result < 0 ? FAILED : SUCCESS;
}
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
index 8451779f426..0b1b5b59ca7 100644
--- a/drivers/usb/storage/sddr55.c
+++ b/drivers/usb/storage/sddr55.c
@@ -751,11 +751,10 @@ int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us)
struct sddr55_card_info *info;
if (!us->extra) {
- us->extra = kmalloc(
+ us->extra = kzalloc(
sizeof(struct sddr55_card_info), GFP_NOIO);
if (!us->extra)
return USB_STOR_TRANSPORT_ERROR;
- memset(us->extra, 0, sizeof(struct sddr55_card_info));
us->extra_destructor = sddr55_card_info_destructor;
}
diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
index fea176d7e79..f2bc5c9e23d 100644
--- a/drivers/usb/storage/shuttle_usbat.c
+++ b/drivers/usb/storage/shuttle_usbat.c
@@ -1318,12 +1318,11 @@ int init_usbat(struct us_data *us)
unsigned char subcountL = USBAT_ATA_LBA_ME;
unsigned char *status = us->iobuf;
- us->extra = kmalloc(sizeof(struct usbat_info), GFP_NOIO);
+ us->extra = kzalloc(sizeof(struct usbat_info), GFP_NOIO);
if (!us->extra) {
US_DEBUGP("init_usbat: Gah! Can't allocate storage for usbat info struct!\n");
return 1;
}
- memset(us->extra, 0, sizeof(struct usbat_info));
info = (struct usbat_info *) (us->extra);
/* Enable peripheral control signals */
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 31ca92056c2..c4a9dcff5f2 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -62,6 +62,13 @@ UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_SINGLE_LUN ),
+/* Reported by Rodolfo Quesada <rquesada@roqz.net> */
+UNUSUAL_DEV( 0x03ee, 0x6906, 0x0003, 0x0003,
+ "VIA Technologies Inc.",
+ "Mitsumi multi cardreader",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
UNUSUAL_DEV( 0x03f0, 0x0107, 0x0200, 0x0200,
"HP",
"CD-Writer+",
@@ -120,6 +127,12 @@ UNUSUAL_DEV( 0x0419, 0xaaf6, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
+/* Reported by Pete Zaitcev <zaitcev@redhat.com>, bz#176584 */
+UNUSUAL_DEV( 0x0420, 0x0001, 0x0100, 0x0100,
+ "GENERIC", "MP3 PLAYER", /* MyMusix PD-205 on the outside. */
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */
UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210,
"SMSC",
@@ -760,12 +773,19 @@ UNUSUAL_DEV( 0x069b, 0x3004, 0x0001, 0x0001,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY ),
-UNUSUAL_DEV( 0x0781, 0x0001, 0x0200, 0x0200,
+/* Submitted by Roman Hodek <roman@hodek.net> */
+UNUSUAL_DEV( 0x0781, 0x0001, 0x0200, 0x0200,
"Sandisk",
"ImageMate SDDR-05a",
US_SC_SCSI, US_PR_CB, NULL,
US_FL_SINGLE_LUN ),
+UNUSUAL_DEV( 0x0781, 0x0002, 0x0009, 0x0009,
+ "SanDisk Corporation",
+ "ImageMate CompactFlash USB",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
#ifdef CONFIG_USB_STORAGE_USBAT
UNUSUAL_DEV( 0x0781, 0x0005, 0x0005, 0x0005,
"Sandisk",
@@ -1073,6 +1093,16 @@ UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff,
0),
#endif
+/*
+ * Pete Zaitcev <zaitcev@yahoo.com>, bz#164688.
+ * The device blatantly ignores LUN and returns 1 in GetMaxLUN.
+ */
+UNUSUAL_DEV( 0x0c45, 0x1060, 0x0100, 0x0100,
+ "Unknown",
+ "Unknown",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
/* Submitted by Joris Struyve <joris@struyve.be> */
UNUSUAL_DEV( 0x0d96, 0x410a, 0x0001, 0xffff,
"Medion",
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index dbcf23980ff..dd108634348 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -55,6 +55,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kthread.h>
+#include <linux/mutex.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -188,7 +189,7 @@ static int storage_suspend(struct usb_interface *iface, pm_message_t message)
struct us_data *us = usb_get_intfdata(iface);
/* Wait until no command is running */
- down(&us->dev_semaphore);
+ mutex_lock(&us->dev_mutex);
US_DEBUGP("%s\n", __FUNCTION__);
if (us->suspend_resume_hook)
@@ -198,7 +199,7 @@ static int storage_suspend(struct usb_interface *iface, pm_message_t message)
/* When runtime PM is working, we'll set a flag to indicate
* whether we should autoresume when a SCSI request arrives. */
- up(&us->dev_semaphore);
+ mutex_unlock(&us->dev_mutex);
return 0;
}
@@ -206,14 +207,14 @@ static int storage_resume(struct usb_interface *iface)
{
struct us_data *us = usb_get_intfdata(iface);
- down(&us->dev_semaphore);
+ mutex_lock(&us->dev_mutex);
US_DEBUGP("%s\n", __FUNCTION__);
if (us->suspend_resume_hook)
(us->suspend_resume_hook)(us, US_RESUME);
iface->dev.power.power_state.event = PM_EVENT_ON;
- up(&us->dev_semaphore);
+ mutex_unlock(&us->dev_mutex);
return 0;
}
@@ -276,12 +277,12 @@ static int usb_stor_control_thread(void * __us)
US_DEBUGP("*** thread awakened.\n");
/* lock the device pointers */
- down(&(us->dev_semaphore));
+ mutex_lock(&(us->dev_mutex));
/* if the device has disconnected, we are free to exit */
if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
US_DEBUGP("-- exiting\n");
- up(&(us->dev_semaphore));
+ mutex_unlock(&us->dev_mutex);
break;
}
@@ -370,7 +371,7 @@ SkipForAbort:
scsi_unlock(host);
/* unlock the device pointers */
- up(&(us->dev_semaphore));
+ mutex_unlock(&us->dev_mutex);
} /* for (;;) */
scsi_host_put(host);
@@ -815,8 +816,8 @@ static void quiesce_and_remove_host(struct us_data *us)
* The thread will exit when it sees the DISCONNECTING flag. */
/* Wait for the current command to finish, then remove the host */
- down(&us->dev_semaphore);
- up(&us->dev_semaphore);
+ mutex_lock(&us->dev_mutex);
+ mutex_unlock(&us->dev_mutex);
/* queuecommand won't accept any new commands and the control
* thread won't execute a previously-queued command. If there
@@ -870,9 +871,9 @@ retry:
/* For bulk-only devices, determine the max LUN value */
if (us->protocol == US_PR_BULK &&
!(us->flags & US_FL_SINGLE_LUN)) {
- down(&us->dev_semaphore);
+ mutex_lock(&us->dev_mutex);
us->max_lun = usb_stor_Bulk_max_lun(us);
- up(&us->dev_semaphore);
+ mutex_unlock(&us->dev_mutex);
}
scsi_scan_host(us_to_host(us));
printk(KERN_DEBUG "usb-storage: device scan complete\n");
@@ -912,7 +913,7 @@ static int storage_probe(struct usb_interface *intf,
us = host_to_us(host);
memset(us, 0, sizeof(struct us_data));
- init_MUTEX(&(us->dev_semaphore));
+ mutex_init(&(us->dev_mutex));
init_MUTEX_LOCKED(&(us->sema));
init_completion(&(us->notify));
init_waitqueue_head(&us->delay_wait);
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 7259fd1f6b0..009fb0953a5 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -49,6 +49,7 @@
#include <linux/blkdev.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
+#include <linux/mutex.h>
#include <scsi/scsi_host.h>
struct us_data;
@@ -103,9 +104,9 @@ typedef void (*pm_hook)(struct us_data *, int); /* power management hook */
struct us_data {
/* The device we're working with
* It's important to note:
- * (o) you must hold dev_semaphore to change pusb_dev
+ * (o) you must hold dev_mutex to change pusb_dev
*/
- struct semaphore dev_semaphore; /* protect pusb_dev */
+ struct mutex dev_mutex; /* protect pusb_dev */
struct usb_device *pusb_dev; /* this usb_device */
struct usb_interface *pusb_intf; /* this interface */
struct us_unusual_dev *unusual_dev; /* device-filter entry */