aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/class
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/class
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/usb/class')
-rw-r--r--drivers/usb/class/Kconfig86
-rw-r--r--drivers/usb/class/Makefile10
-rw-r--r--drivers/usb/class/audio.c3882
-rw-r--r--drivers/usb/class/audio.h110
-rw-r--r--drivers/usb/class/bluetty.c1279
-rw-r--r--drivers/usb/class/cdc-acm.c942
-rw-r--r--drivers/usb/class/cdc-acm.h82
-rw-r--r--drivers/usb/class/usb-midi.c2154
-rw-r--r--drivers/usb/class/usb-midi.h164
-rw-r--r--drivers/usb/class/usblp.c1214
10 files changed, 9923 insertions, 0 deletions
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
new file mode 100644
index 00000000000..0561d0234f2
--- /dev/null
+++ b/drivers/usb/class/Kconfig
@@ -0,0 +1,86 @@
+#
+# USB Class driver configuration
+#
+comment "USB Device Class drivers"
+ depends on USB
+
+config USB_AUDIO
+ tristate "USB Audio support"
+ depends on USB && SOUND
+ 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.
+
+comment "USB Bluetooth TTY can only be used with disabled Bluetooth subsystem"
+ depends on USB && BT
+
+config USB_BLUETOOTH_TTY
+ tristate "USB Bluetooth TTY support"
+ depends on USB && BT=n
+ ---help---
+ This driver implements a nonstandard tty interface to a Bluetooth
+ device that can be used only by specialized Bluetooth HCI software.
+
+ Say Y here if you want to use OpenBT Bluetooth stack (available
+ at <http://developer.axis.com/software>), or other TTY based
+ Bluetooth stacks, and want to connect a USB Bluetooth device
+ to your computer's USB port.
+
+ Do *not* enable this driver if you want to use generic Linux
+ Bluetooth support.
+
+ If in doubt, say N here.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bluetty.
+
+config USB_MIDI
+ tristate "USB MIDI support"
+ depends on USB && SOUND
+ ---help---
+ Say Y here if you want to connect a USB MIDI device to your
+ computer's USB port. 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
+ ---help---
+ This driver supports USB modems and ISDN adapters which support the
+ Communication Device Class Abstract Control Model interface.
+ Please read <file:Documentation/usb/acm.txt> for details.
+
+ If your modem only reports "Cls=ff(vend.)" in the descriptors in
+ /proc/bus/usb/devices, then your modem will not work with this
+ driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cdc-acm.
+
+config USB_PRINTER
+ tristate "USB Printer support"
+ depends on USB
+ help
+ Say Y here if you want to connect a USB printer to your computer's
+ USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usblp.
+
diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile
new file mode 100644
index 00000000000..971e5497a3f
--- /dev/null
+++ b/drivers/usb/class/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for USB Class drivers
+# (one step up from the misc category)
+#
+
+obj-$(CONFIG_USB_ACM) += cdc-acm.o
+obj-$(CONFIG_USB_AUDIO) += audio.o
+obj-$(CONFIG_USB_BLUETOOTH_TTY) += bluetty.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
new file mode 100644
index 00000000000..f432b7d5b23
--- /dev/null
+++ b/drivers/usb/class/audio.c
@@ -0,0 +1,3882 @@
+/*****************************************************************************/
+
+/*
+ * 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)) {
+ set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ schedule_timeout(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);
+ if (u->durb[0].urb->transfer_buffer)
+ kfree(u->durb[0].urb->transfer_buffer);
+ if (u->durb[1].urb->transfer_buffer)
+ kfree(u->durb[1].urb->transfer_buffer);
+ if (u->surb[0].urb->transfer_buffer)
+ kfree(u->surb[0].urb->transfer_buffer);
+ if (u->surb[1].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->d