/*****************************************************************************/

/*
 *	audio.c  --  USB Audio Class driver
 *
 *	Copyright (C) 1999, 2000, 2001
 *	    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
 * 2002-10-16:  Monty <monty@xiph.org>
 *              Expand device support from a maximum of 8/16bit,mono/stereo to 
 *              8/16/24/32bit,N channels.  Add AFMT_?24_?? and AFMT_?32_?? to OSS
 *              functionality. Tested and used in production with the emagic emi 2|6 
 *              on PPC and Intel. Also fixed a few logic 'crash and burn' corner 
 *              cases.
 * 2003-06-30:  Thomas Sailer
 *              Fix SETTRIGGER non OSS API conformity
 */

/*
 * 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:
 * - Pretty stable on UHCI-Acher/Fliegl/Sailer
 * - Does not work on OHCI due to lack of OHCI driver supporting URB's
 *
 * 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 fetched from the device.
 * Then the HEADER descriptor is fetched. 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/version.h>
#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/wrapper.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))

#define MAXCHANNELS       32
#define MAXWIDTH          4
#define MAXSAMPLEWIDTH    (MAXCHANNELS*MAXWIDTH)
#define TMPCOPYWIDTH      MAXSAMPLEWIDTH /* max (128,MAXSAMPLEWIDTH) */

/*
 * 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 int bufsize;
	unsigned int numfrag;
	unsigned int fragshift;
	unsigned int wrptr, rdptr;
	unsigned int total_bytes;
	int count;
	unsigned int error; /* over/underrun */
	wait_queue_head_t wait;
	/* redundant, but makes calculations easier */
	unsigned int fragsize;
	unsigned int dmasize;
	/* OSS stuff */
	unsigned int mapped:1;
	unsigned int ready:1;
	unsigned int enabled:1;
	unsigned int ossfragshift;
	int ossmaxfrags;
	unsigned int 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 iso_packet_descriptor isoframe[DESCFRAMES];
};

struct my_sync_urb {
	struct urb urb;
	struct iso_packet_descriptor isoframe[SYNCFRAMES];
};


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 */
};

/* in the event we don't have the extended soundcard.h, we still need
   to compile successfully.  Supply definitions */

#ifndef AFMT_S24_LE
#	define AFMT_S24_LE	        0x00000800	
#endif
#ifndef AFMT_S24_BE
#	define AFMT_S24_BE	        0x00001000	
#endif
#ifndef AFMT_U24_LE
#	define AFMT_U24_LE	        0x00002000	
#endif
#ifndef AFMT_U24_BE
#	define AFMT_U24_BE	        0x00004000	
#endif
#ifndef AFMT_S32_LE
#	define AFMT_S32_LE	        0x00008000	
#endif
#ifndef AFMT_S32_BE
#	define AFMT_S32_BE	        0x00010000	
#endif
#ifndef AFMT_U32_LE
#	define AFMT_U32_LE	        0x00020000	
#endif
#ifndef AFMT_U32_BE
#	define AFMT_U32_BE	        0x00040000	
#endif

/* private audio format extensions */
#define AFMT_STEREO         0x01000000
#define AFMT_CHMASK         0xff000000
#define AFMT_8MASK          (AFMT_U8 | AFMT_S8)
#define AFMT_16MASK         (AFMT_U16_LE | AFMT_S16_LE | AFMT_U16_BE | AFMT_S16_BE)
#define AFMT_24MASK         (AFMT_U24_LE | AFMT_S24_LE | AFMT_U24_BE | AFMT_S24_BE)
#define AFMT_32MASK         (AFMT_U32_LE | AFMT_S32_LE | AFMT_U32_BE | AFMT_S32_BE)

#define AFMT_SIGNMASK       (AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE |\
                                       AFMT_S24_LE | AFMT_S24_BE |\
                                       AFMT_S32_LE | AFMT_S32_BE)

/* a little odd, but the code counts on byte formats being identified as 'big endian' */
#define AFMT_ENDIANMASK     (AFMT_S8 | AFMT_U8 |\
			               AFMT_S16_BE | AFMT_U16_BE |\
                                       AFMT_S24_BE | AFMT_U24_BE |\
                                       AFMT_S32_BE | AFMT_U32_BE)

#define AFMT_ISSTEREO(x)    (((x) & 0xff000000) == AFMT_STEREO)
#define AFMT_CHANNELS(x)    (((unsigned)(x) >> 24) + 1)
#define AFMT_BYTES(x)       ( (((x)&AFMT_8MASK)!=0)+\
                              (((x)&AFMT_16MASK)!=0)*2+\
                              (((x)&AFMT_24MASK)!=0)*3+\
                              (((x)&AFMT_32MASK)!=0)*4 )
#define AFMT_SAMPLEBYTES(x) (AFMT_BYTES(x)*AFMT_CHANNELS(x))
#define AFMT_SIGN(x)        ((x)&AFMT_SIGNMASK)
#define AFMT_ENDIAN(x)      ((x)&AFMT_ENDIANMASK)


/* --------------------------------------------------------------------- */

/* prevent picking up a bogus abs macro */
#undef my_abs
static inline int my_abs(int x)
{
        if (x < 0)
		return -x;
	return 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;
		mem_map_unreserve(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_SAMPLEBYTES(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_free_page(GFP_KERNEL);
			if (!p)
				return -ENOMEM;
			db->sgbuf[nr] = p;
			mem_map_reserve(virt_to_page(p));
		}
		memset(db->sgbuf[nr], AFMT_SIGN(db->format) ? 0 : 0x80, PAGE_SIZE);
		if ((nr << PAGE_SHIFT) >= db->dmasize)
			break;
	}
	db->bufsize = nr << PAGE_SHIFT;
	db->enabled = 1;
	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 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++) {
		if (remap_page_range(start, virt_to_phys(db->sgbuf[nr]), 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;
	const char *buffer = _buffer;

	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;
	char *buffer = _buffer;

	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 *_buffer, unsigned int size)
{
	unsigned int pgrem, rem;
	const char *buffer = _buffer;

	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 *_buffer, unsigned int size)
{
	unsigned int pgrem, rem;
	char *buffer = _buffer;

	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_unlink_urb(&u->durb[0].urb);
			if (i & FLG_URB1RUNNING)
				usb_unlink_urb(&u->durb[1].urb);
			if (i & FLG_SYNC0RUNNING)
				usb_unlink_urb(&u->surb[0].urb);
			if (i & FLG_SYNC1RUNNING)
				usb_unlink_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 inline int iconvert(unsigned char **xx,int jump)
{
  int value=0;
  unsigned char *x=*xx;
			
  /* conversion fall-through cascade compiles to a jump table */
  switch(jump){
  case 0:
    /* 32 bit BE */
    value  = x[3];
  case 1:
    /* 24 bit BE */
    value |= x[2] << 8;
  case 2:
    /* 16 bit BE */
    value |= x[1] << 16;
  case 3:
    /* 8 bit */
    value |= x[0] << 24;
    x+=(4-jump);
    break;

  case 4:
    /* 32 bit LE */
    value  = *x++;
  case 5:
    /* 24 bit LE */
    value |= *x++ << 8;
  case 6:
    /* 16 bit LE */
    value |= *x++ << 16;
    value |= *x++ << 24;
    break;
  }
  *xx=x;
  return(value);
}

static inline void oconvert(unsigned char **yy,int jump,int value)
{
  unsigned char *y=*yy;

  /* conversion fall-through cascade compiles to a jump table */
  switch(jump){
  case 0:
    /* 32 bit BE */
    y[3] = value;
  case 1:
    /* 24 bit BE */
    y[2] = value >> 8;
  case 2:
    /* 16 bit BE */
    y[1] = value >> 16;
  case 3:
    /* 8 bit */
    y[0] = value >> 24;
    y+=(4-jump);
    break;

  case 4:
    /* 32 bit LE */
    *y++ = value;
  case 5:
    /* 24 bit LE */
    *y++ = value >> 8;
  case 6:
    /* 16 bit LE */
    *y++ = value >> 16;
    *y++ = value >> 24;
    break;
  }
  *yy=y;
}

/* capable of any-to-any conversion */
static void conversion(const void *ibuf, unsigned int ifmt, 
		       void *obuf, unsigned int ofmt, unsigned int scnt)
{

  /* some conversion is indeed needed */
  unsigned int i,j;
  unsigned char *x=(unsigned char *)ibuf;
  unsigned char *y=(unsigned char *)obuf;

  int ichannels = AFMT_CHANNELS(ifmt);
  int ochannels = AFMT_CHANNELS(ofmt);
  int ibytes    = AFMT_BYTES(ifmt);
  int obytes    = AFMT_BYTES(ofmt);
  int iendian   = AFMT_ENDIAN(ifmt);
  int oendian   = AFMT_ENDIAN(ofmt);
  int isign     = AFMT_SIGN(ifmt)?0:0x80000000UL;
  int osign     = AFMT_SIGN(ofmt)?0:0x80000000UL;
  int sign      = (isign==osign?0:0x80000000UL);
  
  /* build the byte/endian jump table offsets */
  int ijump = (iendian ? 4-ibytes : 8-ibytes);
  int ojump = (oendian ? 4-obytes : 8-obytes);
  
  if(ichannels == 2 && ochannels == 1){
    /* Stereo -> mono is a special case loop; we downmix */
    for(i=0;i<scnt;i++){
      int valueL = iconvert(&x,ijump) ^ isign; /* side effect; increments x */
      int valueR = iconvert(&x,ijump) ^ isign; /* side effect; increments x */
      int value  = (valueL>>1) + (valueR>>1);
      oconvert(&y,ojump,value^osign);  /* side effect; increments y */
		}
    return;
		}
  if(ichannels == 1 && ochannels == 2){
    /* mono->stereo is a special case loop; we replicate */
    for(i=0;i<scnt;i++){
      int value = iconvert(&x,ijump) ^ sign; /* side effect; increments x */
      oconvert(&y,ojump,value);  /* side effect; increments y */
      oconvert(&y,ojump,value);  /* side effect; increments y */
	}
    return;
		}
  if(ichannels<ochannels){
    /* zero out extra output channels */
    for(i=0;i<scnt;i++){
      for(j=0;j<ichannels;j++){
	int value = iconvert(&x,ijump) ^ sign; /* side effect; increments x */
	oconvert(&y,ojump,value);  /* side effect; increments y */
	
	}
      for(;j<ochannels;j++){
	oconvert(&y,ojump,osign);  /* side effect; increments y */
	}
		}
    return;
		}
  if(ichannels>=ochannels){
    /* discard extra input channels */
    int xincrement=ibytes*(ichannels-ochannels);
    for(i=0;i<scnt;i++){
      for(j=0;j<ichannels;j++){
	int value = iconvert(&x,ijump) ^ sign; /* side effect; increments x */
	oconvert(&y,ojump,value);  /* side effect; increments y */

		}
      x+=xincrement;
		}
    return;
	}
}

static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples)
{
        unsigned int scnt;
	unsigned int ufmtb = AFMT_SAMPLEBYTES(u->format);
	unsigned int dfmtb = AFMT_SAMPLEBYTES(u->dma.format);
        unsigned char tmp[TMPCOPYWIDTH];
	unsigned int maxs  = sizeof(tmp)/dfmtb;

	while (samples > 0) {
		scnt = samples;
		if (scnt > maxs)
			scnt = maxs;

	        conversion(buffer, u->format, tmp, u->dma.format, scnt);
                dmabuf_copyin(&u->dma, tmp, scnt * dfmtb);
                buffer += scnt * ufmtb;
		samples -= scnt;
	}
}		

static int usbin_prepare_desc(struct usbin *u, struct urb *urb)
{
	unsigned int i, maxsize, offs;

	maxsize = ((u->freqmax + 0x3fff) * AFMT_SAMPLEBYTES(u->format)) >> 14;
	//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;
	}
	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, ufmtb, dfmtb, err = 0, cnt, scnt, dmafree;
	unsigned char *cp;

	ufmtb = AFMT_SAMPLEBYTES(u->format);
	dfmtb = AFMT_SAMPLEBYTES(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 / ufmtb;
		if (!scnt)
			continue;
		cnt = scnt * dfmtb;
		if (!u->dma.mapped) {
			dmafree = u->dma.dmasize - u->dma.count;
			if (cnt > dmafree) {
				scnt = dmafree / dfmtb;
				cnt = scnt * dfmtb;
				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 usb_audiodev *as = (struct usb_audiodev *)urb->context;
	struct usbin *u = &as->usbin;
	unsigned long flags;
	unsigned int mask;
	int suret = USB_ST_NOERROR;

#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)) == USB_ST_NOERROR) {
		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;
	}
	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 usb_audiodev *as = (struct usb_audiodev *)urb->context;
	struct usbin *u = &as->usbin;
	unsigned long flags;
	unsigned int mask;
	int suret = USB_ST_NOERROR;

#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)) == USB_ST_NOERROR) {
		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) * AFMT_SAMPLEBYTES(u->format)) >> 14;
		bufsz = DESCFRAMES * maxsze;
		if (u->durb[0].urb.transfer_buffer)
			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;
		if (u->durb[1].urb.transfer_buffer)
			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) {
			if (u->surb[0].urb.transfer_buffer)
				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;
			if (u->surb[1].urb.transfer_buffer)
				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 = USB_ISO_ASAP;
		urb->number_of_packets = DESCFRAMES;
		urb->context = as;
		urb->complete = usbin_completed;
		if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb))
			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 = USB_ISO_ASAP;
		urb->number_of_packets = DESCFRAMES;
		urb->context = as;
		urb->complete = usbin_completed;
		if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb))
			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 = USB_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))
				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 = USB_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))
				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)) {
		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_unlink_urb(&u->durb[0].urb);
			if (i & FLG_URB1RUNNING)
				usb_unlink_urb(&u->durb[1].urb);
			if (i & FLG_SYNC0RUNNING)
				usb_unlink_urb(&u->surb[0].urb);
			if (i & FLG_SYNC1RUNNING)
				usb_unlink_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 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)
{
        unsigned char tmp[TMPCOPYWIDTH];
        unsigned int scnt;
	unsigned int ufmtb = AFMT_SAMPLEBYTES(u->format);
	unsigned int dfmtb = AFMT_SAMPLEBYTES(u->dma.format);
	unsigned int maxs  = sizeof(tmp)/dfmtb;
	
	while (samples > 0) {
		scnt = samples;
		if (scnt > maxs)
			scnt = maxs;

		dmabuf_copyout(&u->dma, tmp, scnt * dfmtb);
		conversion(tmp, u->dma.format, buffer, u->format, scnt);
                buffer += scnt * ufmtb;
		samples -= scnt;
	}
}		

static int usbout_prepare_desc(struct usbout *u, struct urb *urb)
{
	unsigned int i, ufmtb, dfmtb, err = 0, cnt, scnt, offs;
	unsigned char *cp = urb->transfer_buffer;

	ufmtb = AFMT_SAMPLEBYTES(u->format);
	dfmtb = AFMT_SAMPLEBYTES(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 * dfmtb;
		if (!u->dma.mapped) {
			if (cnt > u->dma.count) {
				scnt = u->dma.count / dfmtb;
				cnt = scnt * dfmtb;
				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 * ufmtb;
		urb->iso_frame_desc[i].length = cnt;
		offs += cnt;
		cp += cnt;
	}
	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 usb_audiodev *as = (struct usb_audiodev *)urb->context;
	struct usbout *u = &as->usbout;
	unsigned long flags;
	unsigned int mask;
	int suret = USB_ST_NOERROR;

#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)) == USB_ST_NOERROR) {
		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;
	}
	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 (my_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 usb_audiodev *as = (struct usb_audiodev *)urb->context;
	struct usbout *u = &as->usbout;
	unsigned long flags;
	unsigned int mask;
	int suret = USB_ST_NOERROR;

#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)) == USB_ST_NOERROR) {
		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) * AFMT_SAMPLEBYTES(u->format)) >>14;

		bufsz = DESCFRAMES * maxsze;
		if (u->durb[0].urb.transfer_buffer)
			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;
		if (u->durb[1].urb.transfer_buffer)
			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) {
			if (u->surb[0].urb.transfer_buffer)
				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;
			if (u->surb[1].urb.transfer_buffer)
				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 = USB_ISO_ASAP;
		urb->number_of_packets = DESCFRAMES;
		urb->context = as;
		urb->complete = usbout_completed;
		if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb))
			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 = USB_ISO_ASAP;
		urb->number_of_packets = DESCFRAMES;
		urb->context = as;
		urb->complete = usbout_completed;
		if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb))
			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 = USB_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))
				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 = USB_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))
				u->flags |= FLG_SYNC1RUNNING;
			else
				u->flags &= ~FLG_RUNNING;
		}
	}
	spin_unlock_irqrestore(&as->lock, flags);
	return 0;
}

/* --------------------------------------------------------------------- */
/* allowed conversions (sign, endian, width, channels), and relative
   weighting penalties against fuzzy match selection.  For the
   purposes of not confusing users, 'lossy' format translation is
   disallowed, eg, don't allow a mono 8 bit device to successfully
   open as 5.1, 24 bit... Never allow a mode that tries to deliver greater
   than the hard capabilities of the device.

   device --=> app

   signed   => unsigned : 1
   unsigned => signed   : 1

   le       => be       : 1
   be       => le       : 1

   8        => 16       : not allowed
   8        => 24       : not allowed
   8        => 32       : not allowed
   16       => 24       : not allowed
   16       => 32       : not allowed
   24       => 32       : not allowed

   16       => 8        : 4
   24       => 16       : 4
   24       => 8        : 5
   32       => 24       : 4
   32       => 16       : 5
   32       => 8        : 5

   mono     => stereo   : not allowed
   stereo   => mono     : 32 (downmix to L+R/2)

   N        => >N       : not allowed
   N        => <N       : 32 */

static unsigned int format_goodness(struct audioformat *afp, unsigned int app,
				    unsigned int srate){
	unsigned int g = 0;
	unsigned int sratelo=afp->sratelo;
	unsigned int sratehi=afp->sratehi;
	unsigned int dev=afp->format;

	if(AFMT_SIGN(dev) && !AFMT_SIGN(app))     g += 1;
	if(!AFMT_SIGN(dev) && AFMT_SIGN(app))     g += 1;
	if(AFMT_ENDIAN(dev) && !AFMT_ENDIAN(app)) g += 1;
	if(!AFMT_ENDIAN(dev) && AFMT_ENDIAN(app)) g += 1;

	switch(AFMT_BYTES(app)+AFMT_BYTES(dev)*10){
	case 12: return ~0;
	case 13: return ~0;
	case 14: return ~0;
	case 21: g += 4; break;
	case 23: return ~0;
	case 24: return ~0;
	case 31: g += 5; break;
	case 32: g += 4; break;
	case 34: return ~0;
	case 41: g += 6; break;
	case 42: g += 5; break;
	case 43: g += 4; break;
	}

	if(AFMT_CHANNELS(dev) > AFMT_CHANNELS(app)){
	        g+=32;
	}else if(AFMT_CHANNELS(dev) < AFMT_CHANNELS(app)){
	        return ~0;
	}
	  
	g<<=20;

	if (srate < sratelo)
	        g += sratelo - srate;
        if (srate > sratehi)
	        g += srate - sratehi;

	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_config_descriptor *config = dev->actconfig;
	struct usb_interface_descriptor *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;

	if (u->interface < 0 || u->interface >= config->bNumInterfaces)
		return 0;
	iface = &config->interface[u->interface];

	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 = &iface->altsetting[fmt->altsetting];
	u->format = fmt->format;
	u->datapipe = usb_rcvisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf);
	u->syncpipe = u->syncinterval = 0;
	if ((alts->endpoint[0].bmAttributes & 0x0c) == 0x08) {
		if (alts->bNumEndpoints < 2 ||
		    alts->endpoint[1].bmAttributes != 0x01 ||
		    alts->endpoint[1].bSynchAddress != 0 ||
		    alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].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].bEndpointAddress & 0xf);
			u->syncinterval = alts->endpoint[1].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", alts->bInterfaceNumber, fmt->altsetting));
	if (usb_set_interface(dev, alts->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, HZ)) < 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, HZ)) < 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, HZ)) < 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_config_descriptor *config = dev->actconfig;
	struct usb_interface_descriptor *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;

	if (u->interface < 0 || u->interface >= config->bNumInterfaces)
		return 0;
	iface = &config->interface[u->interface];

	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 = &iface->altsetting[fmt->altsetting];
	u->datapipe = usb_sndisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf);
	u->syncpipe = u->syncinterval = 0;
	if ((alts->endpoint[0].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->bNumEndpoints < 2 ||
		    alts->endpoint[1].bmAttributes != 0x01 ||
		    alts->endpoint[1].bSynchAddress != 0 ||
		    alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].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].bEndpointAddress & 0xf);
			u->syncinterval = alts->endpoint[1].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", alts->bInterfaceNumber, 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, HZ)) < 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, HZ)) < 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, HZ)) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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);
		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)
{
	int minor = MINOR(inode->i_rdev);
	struct list_head *devs, *mdevs;
	struct usb_mixerdev *ms;
	struct usb_audio_state *s;

	down(&open_sem);
	for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) {
		s = list_entry(devs, struct usb_audio_state, audiodev);
		for (mdevs = s->mixerlist.next; mdevs != &s->mixerlist; mdevs = mdevs->next) {
			ms = list_entry(mdevs, struct usb_mixerdev, 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 0;
}

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;

	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 *)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 *)arg, &info, sizeof(info)))
			return -EFAULT;
		return 0;
	}
	if (cmd == OSS_GETVERSION)
		return put_user(SOUND_VERSION, (int *)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, (int *)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, (int *)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, (int *)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, (int *)arg);
			
		case SOUND_MIXER_CAPS:
			return put_user(SOUND_CAP_EXCL_INPUT, (int *)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, (int *)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, (int *)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, (int *)arg))
			return -EFAULT;
		if (wrmixer(ms, j, val))
			return -EIO;
		return put_user(ms->ch[j].value, (int *)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 * 
					AFMT_SAMPLEBYTES(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 *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 (ppos != &file->f_pos)
		return -ESPIPE;
	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 (as->usbin.dma.enabled && 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;
		if (as->usbin.dma.enabled && usbin_start(as)) {
			if (!ret)
				ret = -ENODEV;
			break;
		}
	}
	__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 *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 (ppos != &file->f_pos)
		return -ESPIPE;
	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_SAMPLEBYTES(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 (as->usbout.dma.enabled && 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.enabled && 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(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;
	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, (int *)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, (int *)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, (int *)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, (int *)arg);

	case SNDCTL_DSP_STEREO:
		if (get_user(val, (int *)arg))
			return -EFAULT;
		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
		val2 &= 0x00ffffff;
		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, (int *)arg))
			return -EFAULT;
		if (val != 0) {
			val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
			
			val2 &= 0x00ffffff;
			val2 |= (val-1)<<24;

			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_CHANNELS(val2), (int *)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 |
				AFMT_U24_LE | AFMT_U24_BE | AFMT_S24_LE | AFMT_S24_BE |
				AFMT_U32_LE | AFMT_U32_BE | AFMT_S32_LE | AFMT_S32_BE,
				(int *)arg);

	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
		if (get_user(val, (int *)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 |
				     AFMT_U24_LE | AFMT_U24_BE | AFMT_S24_LE | AFMT_S24_BE |
				     AFMT_U32_LE | AFMT_U32_BE | AFMT_S32_LE | AFMT_S32_BE)))
				return -EINVAL;
			val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
			val |= val2 & AFMT_CHMASK;
			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_CHMASK, (int *)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, (int *)arg);

	case SNDCTL_DSP_SETTRIGGER:
		if (get_user(val, (int *)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;
				as->usbin.dma.enabled = 1;
				if (usbin_start(as))
					return -ENODEV;
			} else {
				as->usbin.dma.enabled = 0;
				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;
				as->usbout.dma.enabled = 1;
				if (usbout_start(as))
					return -ENODEV;
			} else {
				as->usbout.dma.enabled = 0;
				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)

		The above is potentially disasterous; if the
		userspace app calls the GETOSPACE ioctl() before a
		data write on the device (as can happen in a
		sensible client that's tracking the write buffer
		low watermark), the kernel driver will never
		recover from momentary starvation (recall that
		FLG_RUNNING will be cleared by usbout_completed)
		because the ioctl will keep resetting the DMA
		buffer before each write, potentially never
		allowing us to fill the buffer back to the DMA
		restart threshhold.

		Can you tell this was actually biting me? :-) */

		if ((!as->usbout.dma.ready) && (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 *)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)*/
		if ((!as->usbin.dma.ready) && (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 *)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, (int *)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);
		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 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);
		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;

       case SNDCTL_DSP_GETBLKSIZE:
		if (file->f_mode & FMODE_WRITE) {

		  /* do not clobber devices that are already running! */
		  if ((!as->usbout.dma.ready) && (val = prog_dmabuf_out(as)) != 0)
				return val;
			return put_user(as->usbout.dma.fragsize, (int *)arg);
		}
		/* do not clobber devices that are already running! */
		if ((!as->usbin.dma.ready) && (val = prog_dmabuf_in(as)) != 0)
			return val;
		return put_user(as->usbin.dma.fragsize, (int *)arg);

	case SNDCTL_DSP_SETFRAGMENT:
		if (get_user(val, (int *)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, (int *)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, (int *)arg);

	case SOUND_PCM_READ_CHANNELS:
		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
		return put_user(AFMT_CHANNELS(val2), (int *)arg);

	case SOUND_PCM_READ_BITS:
		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
		return put_user(AFMT_BYTES(val2) * 8, (int *)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)
{
	int minor = MINOR(inode->i_rdev);
	DECLARE_WAITQUEUE(wait, current);
	struct list_head *devs, *adevs;
	struct usb_audiodev *as;
	struct usb_audio_state *s;

	for (;;) {
		down(&open_sem);
		for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) {
			s = list_entry(devs, struct usb_audio_state, audiodev);
			for (adevs = s->audiolist.next; adevs != &s->audiolist; adevs = adevs->next) {
				as = list_entry(adevs, struct usb_audiodev, 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;
		as->usbin.dma.enabled = 1;
	}
	if (file->f_mode & FMODE_WRITE) {
		as->usbout.dma.ossfragshift = as->usbout.dma.ossmaxfrags = as->usbout.dma.subdivision = 0;
		as->usbout.dma.enabled = 1;
	}
	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 0;
}

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;
	struct usb_interface *iface;

	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) {
			iface = &dev->actconfig->interface[as->usbout.interface];
			usb_set_interface(dev, iface->altsetting->bInterfaceNumber, 0);
		}
		dmabuf_release(&as->usbout.dma);
		usbout_release(as);
	}
	if (file->f_mode & FMODE_READ) {
		usbin_stop(as);
		if (dev && as->usbin.interface >= 0) {
			iface = &dev->actconfig->interface[as->usbin.interface];
			usb_set_interface(dev, iface->altsetting->bInterfaceNumber, 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 void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
			      const struct usb_device_id *id);
static void usb_audio_disconnect(struct usb_device *dev, void *ptr);

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,
	driver_list:	LIST_HEAD_INIT(usb_audio_driver.driver_list), 
	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_config_descriptor *config = dev->actconfig;
	struct usb_interface_descriptor *alts;
	struct usb_interface *iface;
	struct audioformat *fp;
	unsigned char *fmt, *csep;
	unsigned int i, j, k, format;

	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);
	spin_lock_init(&as->usbin.durb[0].urb.lock);
	spin_lock_init(&as->usbin.durb[1].urb.lock);
	spin_lock_init(&as->usbin.surb[0].urb.lock);
	spin_lock_init(&as->usbin.surb[1].urb.lock);
	spin_lock_init(&as->usbout.durb[0].urb.lock);
	spin_lock_init(&as->usbout.durb[1].urb.lock);
	spin_lock_init(&as->usbout.surb[0].urb.lock);
	spin_lock_init(&as->usbout.surb[1].urb.lock);
	as->state = s;
	as->usbin.interface = asifin;
	as->usbout.interface = asifout;
	/* search for input formats */
	if (asifin >= 0) {
		as->usbin.flags = FLG_CONNECTED;
		iface = &config->interface[asifin];
		for (i = 0; i < iface->num_altsetting; i++) {
			alts = &iface->altsetting[i];
			if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2)
				continue;
			if (alts->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].bmAttributes & 0x03) != 0x01 ||
			    !(alts->endpoint[0].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_U32_LE | AFMT_U24_LE | AFMT_U16_LE | AFMT_U8) : 
			  (AFMT_S32_LE | AFMT_S24_LE | 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] > MAXCHANNELS || fmt[5] < 1 || fmt[5] > 4) {
				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;
			printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u channels %u framesize %u configured\n", 
				       dev->devnum, asifin, i, fmt[4], fmt[5]);
			fp = &as->fmtin[as->numfmtin++];
			switch (fmt[5]) {
			case 1:
				format &= (AFMT_U8 | AFMT_S8);
				break;
			case 2:
				format &= (AFMT_U16_LE | AFMT_S16_LE);
				break;
			case 3:
				format &= (AFMT_U24_LE | AFMT_S24_LE);
				break;
			case 4:
				format &= (AFMT_U32_LE | AFMT_S32_LE);
				break;
			}
			
			format |= (fmt[4]-1) << 24;

			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 = &config->interface[asifout];
		for (i = 0; i < iface->num_altsetting; i++) {
			alts = &iface->altsetting[i];
			if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2)
				continue;
			if (alts->bNumEndpoints < 1) {
				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].bmAttributes & 0x03) != 0x01 ||
			    (alts->endpoint[0].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_U32_LE | AFMT_U24_LE | AFMT_U16_LE | AFMT_U8) : 
			  (AFMT_S32_LE | AFMT_S24_LE | AFMT_S16_LE | AFMT_S8);

			/* Dallas DS4201 workaround */
			if (dev->descriptor.idVendor == 0x04fa && 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] > MAXCHANNELS || fmt[5] < 1 || fmt[5] > 4) {
				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;
			printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u channels %u framesize %u configured\n", 
			       dev->devnum, asifout, i, fmt[4], fmt[5]);
			fp = &as->fmtout[as->numfmtout++];

			switch (fmt[5]) {
			case 1:
				format &= (AFMT_U8 | AFMT_S8);
				break;
			case 2:
				format &= (AFMT_U16_LE | AFMT_S16_LE);
				break;
			case 3:
				format &= (AFMT_U24_LE | AFMT_S24_LE);
				break;
			case 4:
				format &= (AFMT_U32_LE | AFMT_S32_LE);
				break;
			}

			format |= (fmt[4]-1) << 24;

			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) {
		kfree(as);
		return;
	}
	if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1)) < 0) {
		printk(KERN_ERR "usbaudio: cannot register dsp\n");
		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[2];
	__s16 v1;
	unsigned int v2, v3;

	if (!state->nrmixch || state->nrmixch > SOUND_MIXER_NRDEVICES)
		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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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, HZ) < 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;
	}
	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--;
}


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 with channels.\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, HZ) < 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]] || p1[0] < 13+p1[6]+p1[11+p1[6]]+p1[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);
}

static void *usb_audio_parsecontrol(struct usb_device *dev, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif)
{
	struct usb_audio_state *s;
	struct usb_config_descriptor *config = dev->actconfig;
	struct usb_interface *iface;
	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];
		if (j >= config->bNumInterfaces) {
			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u does not exist\n",
			       dev->devnum, ctrlif, j);
			continue;
		}
		iface = &config->interface[j];
		if (iface->altsetting[0].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 (iface->altsetting[0].bInterfaceSubClass == 3) {
			printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u interface %u MIDIStreaming not supported\n",
			       dev->devnum, ctrlif, j);
			continue;
		}
		if (iface->altsetting[0].bInterfaceSubClass != 2) {
			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u invalid AudioClass subtype\n",
			       dev->devnum, ctrlif, j);
			continue;
		}
		if (iface->num_altsetting == 0) {
			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has no working interface.\n", dev->devnum, ctrlif);
			continue;
		}
		if (iface->num_altsetting == 1) {
			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has only 1 altsetting.\n", dev->devnum, ctrlif);
			continue;
		}
		if (iface->altsetting[0].bNumEndpoints > 0) {
			/* Check all endpoints; should they all have a bandwidth of 0 ? */
			for (k = 0; k < iface->altsetting[0].bNumEndpoints; k++) {
				if (iface->altsetting[0].endpoint[k].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 < iface->altsetting[0].bNumEndpoints)
				continue;
		}
		if (iface->altsetting[1].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 (iface->altsetting[1].endpoint[0].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 void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
			     const struct usb_device_id *id)
{
	struct usb_config_descriptor *config = dev->actconfig;	
	unsigned char *buffer;
	unsigned char buf[8];
	unsigned int i, buflen;
	int ret;

#if 0
	printk(KERN_DEBUG "usbaudio: Probing if %i: IC %x, ISC %x\n", ifnum,
	       config->interface[ifnum].altsetting[0].bInterfaceClass,
	       config->interface[ifnum].altsetting[0].bInterfaceSubClass);
#endif

	/*
	 * audiocontrol interface found
	 * find which configuration number is active
	 */
	i = dev->actconfig - config;

	if (usb_set_configuration(dev, config->bConfigurationValue) < 0) {
		printk(KERN_ERR "usbaudio: set_configuration failed (ConfigValue 0x%x)\n", config->bConfigurationValue);
		return NULL;
	}
	ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8);
	if (ret < 0) {
		printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d (error %d)\n", i, dev->devnum, ret);
		return NULL;
	}
	if (buf[1] != USB_DT_CONFIG || buf[0] < 9) {
		printk(KERN_ERR "usbaudio: invalid config descriptor %d of device %d\n", i, dev->devnum);
		return NULL;
	}
	buflen = buf[2] | (buf[3] << 8);
	if (!(buffer = kmalloc(buflen, GFP_KERNEL)))
		return NULL;
	ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buffer, buflen);
	if (ret < 0) {
		kfree(buffer);
		printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d (error %d)\n", i, dev->devnum, ret);
		return NULL;
	}
	return usb_audio_parsecontrol(dev, buffer, buflen, ifnum);
}


/* a revoke facility would make things simpler */

static void usb_audio_disconnect(struct usb_device *dev, void *ptr)
{
	struct usb_audio_state *s = (struct usb_audio_state *)ptr;
	struct list_head *list;
	struct usb_audiodev *as;
	struct usb_mixerdev *ms;

	/* 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(&s->audiodev);
	INIT_LIST_HEAD(&s->audiodev);
	s->usbdev = NULL;
	/* deregister all audio and mixer devices, so no new processes can open this device */
	for(list = s->audiolist.next; list != &s->audiolist; list = list->next) {
		as = list_entry(list, struct usb_audiodev, 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;
	}
	for(list = s->mixerlist.next; list != &s->mixerlist; list = list->next) {
		ms = list_entry(list, struct usb_mixerdev, 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)
{
	usb_register(&usb_audio_driver);
	info(DRIVER_VERSION ":" DRIVER_DESC);
	return 0;
}


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");

