]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge branch 'topic/misc' into topic/usb
authorTakashi Iwai <tiwai@suse.de>
Mon, 22 Mar 2010 16:05:48 +0000 (17:05 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 22 Mar 2010 16:05:48 +0000 (17:05 +0100)
34 files changed:
include/linux/usb/audio-v2.h [new file with mode: 0644]
include/linux/usb/audio.h
sound/usb/Makefile
sound/usb/card.c [new file with mode: 0644]
sound/usb/card.h [new file with mode: 0644]
sound/usb/debug.h [new file with mode: 0644]
sound/usb/endpoint.c [new file with mode: 0644]
sound/usb/endpoint.h [new file with mode: 0644]
sound/usb/format.c [new file with mode: 0644]
sound/usb/format.h [new file with mode: 0644]
sound/usb/helper.c [new file with mode: 0644]
sound/usb/helper.h [new file with mode: 0644]
sound/usb/midi.c [moved from sound/usb/usbmidi.c with 99% similarity]
sound/usb/midi.h [new file with mode: 0644]
sound/usb/misc/Makefile [new file with mode: 0644]
sound/usb/misc/ua101.c [moved from sound/usb/ua101.c with 99% similarity]
sound/usb/mixer.c [moved from sound/usb/usbmixer.c with 72% similarity]
sound/usb/mixer.h [new file with mode: 0644]
sound/usb/mixer_maps.c [moved from sound/usb/usbmixer_maps.c with 98% similarity]
sound/usb/mixer_quirks.c [new file with mode: 0644]
sound/usb/mixer_quirks.h [new file with mode: 0644]
sound/usb/pcm.c [new file with mode: 0644]
sound/usb/pcm.h [new file with mode: 0644]
sound/usb/proc.c [new file with mode: 0644]
sound/usb/proc.h [new file with mode: 0644]
sound/usb/quirks-table.h [moved from sound/usb/usbquirks.h with 99% similarity]
sound/usb/quirks.c [new file with mode: 0644]
sound/usb/quirks.h [new file with mode: 0644]
sound/usb/urb.c [new file with mode: 0644]
sound/usb/urb.h [new file with mode: 0644]
sound/usb/usbaudio.c [deleted file]
sound/usb/usbaudio.h
sound/usb/usx2y/us122l.c
sound/usb/usx2y/usbusx2y.h

diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
new file mode 100644 (file)
index 0000000..0952231
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2010 Daniel Mack <daniel@caiaq.de>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This file holds USB constants and structures defined
+ * by the USB Device Class Definition for Audio Devices in version 2.0.
+ * Comments below reference relevant sections of the documents contained
+ * in http://www.usb.org/developers/devclass_docs/Audio2.0_final.zip
+ */
+
+#ifndef __LINUX_USB_AUDIO_V2_H
+#define __LINUX_USB_AUDIO_V2_H
+
+#include <linux/types.h>
+
+/* v1.0 and v2.0 of this standard have many things in common. For the rest
+ * of the definitions, please refer to audio.h */
+
+/* 4.7.2.1 Clock Source Descriptor */
+
+struct uac_clock_source_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bClockID;
+       __u8 bmAttributes;
+       __u8 bmControls;
+       __u8 bAssocTerminal;
+       __u8 iClockSource;
+} __attribute__((packed));
+
+/* 4.7.2.2 Clock Source Descriptor */
+
+struct uac_clock_selector_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bClockID;
+       __u8 bNrInPins;
+       __u8 bmControls;
+       __u8 baCSourceID[];
+} __attribute__((packed));
+
+/* 4.7.2.4 Input terminal descriptor */
+
+struct uac2_input_terminal_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bTerminalID;
+       __u16 wTerminalType;
+       __u8 bAssocTerminal;
+       __u8 bCSourceID;
+       __u8 bNrChannels;
+       __u32 bmChannelConfig;
+       __u8 iChannelNames;
+       __u16 bmControls;
+       __u8 iTerminal;
+} __attribute__((packed));
+
+/* 4.7.2.5 Output terminal descriptor */
+
+struct uac2_output_terminal_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bTerminalID;
+       __u16 wTerminalType;
+       __u8 bAssocTerminal;
+       __u8 bSourceID;
+       __u8 bCSourceID;
+       __u16 bmControls;
+       __u8 iTerminal;
+} __attribute__((packed));
+
+
+
+/* 4.7.2.8 Feature Unit Descriptor */
+
+struct uac2_feature_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUnitID;
+       __u8 bSourceID;
+       /* bmaControls is actually u32,
+        * but u8 is needed for the hybrid parser */
+       __u8 bmaControls[0]; /* variable length */
+} __attribute__((packed));
+
+/* 4.9.2 Class-Specific AS Interface Descriptor */
+
+struct uac_as_header_descriptor_v2 {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bTerminalLink;
+       __u8 bmControls;
+       __u8 bFormatType;
+       __u32 bmFormats;
+       __u8 bNrChannels;
+       __u32 bmChannelConfig;
+       __u8 iChannelNames;
+} __attribute__((packed));
+
+
+/* A.7 Audio Function Category Codes */
+#define UAC2_FUNCTION_SUBCLASS_UNDEFINED       0x00
+#define UAC2_FUNCTION_DESKTOP_SPEAKER          0x01
+#define UAC2_FUNCTION_HOME_THEATER             0x02
+#define UAC2_FUNCTION_MICROPHONE               0x03
+#define UAC2_FUNCTION_HEADSET                  0x04
+#define UAC2_FUNCTION_TELEPHONE                        0x05
+#define UAC2_FUNCTION_CONVERTER                        0x06
+#define UAC2_FUNCTION_SOUND_RECORDER           0x07
+#define UAC2_FUNCTION_IO_BOX                   0x08
+#define UAC2_FUNCTION_MUSICAL_INSTRUMENT       0x09
+#define UAC2_FUNCTION_PRO_AUDIO                        0x0a
+#define UAC2_FUNCTION_AUDIO_VIDEO              0x0b
+#define UAC2_FUNCTION_CONTROL_PANEL            0x0c
+#define UAC2_FUNCTION_OTHER                    0xff
+
+/* A.9 Audio Class-Specific AC Interface Descriptor Subtypes */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC2_EFFECT_UNIT                       0x07
+#define UAC2_PROCESSING_UNIT_V2                0x08
+#define UAC2_EXTENSION_UNIT_V2         0x09
+#define UAC2_CLOCK_SOURCE              0x0a
+#define UAC2_CLOCK_SELECTOR            0x0b
+#define UAC2_CLOCK_MULTIPLIER          0x0c
+#define UAC2_SAMPLE_RATE_CONVERTER     0x0d
+
+/* A.10 Audio Class-Specific AS Interface Descriptor Subtypes */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC2_ENCODER                   0x03
+#define UAC2_DECODER                   0x04
+
+/* A.11 Effect Unit Effect Types */
+#define UAC2_EFFECT_UNDEFINED          0x00
+#define UAC2_EFFECT_PARAM_EQ           0x01
+#define UAC2_EFFECT_REVERB             0x02
+#define UAC2_EFFECT_MOD_DELAY          0x03
+#define UAC2_EFFECT_DYN_RANGE_COMP     0x04
+
+/* A.12 Processing Unit Process Types */
+#define UAC2_PROCESS_UNDEFINED         0x00
+#define UAC2_PROCESS_UP_DOWNMIX                0x01
+#define UAC2_PROCESS_DOLBY_PROLOCIC    0x02
+#define UAC2_PROCESS_STEREO_EXTENDER   0x03
+
+/* A.14 Audio Class-Specific Request Codes */
+#define UAC2_CS_CUR                    0x01
+#define UAC2_CS_RANGE                  0x02
+
+/* A.15 Encoder Type Codes */
+#define UAC2_ENCODER_UNDEFINED         0x00
+#define UAC2_ENCODER_OTHER             0x01
+#define UAC2_ENCODER_MPEG              0x02
+#define UAC2_ENCODER_AC3               0x03
+#define UAC2_ENCODER_WMA               0x04
+#define UAC2_ENCODER_DTS               0x05
+
+/* A.16 Decoder Type Codes */
+#define UAC2_DECODER_UNDEFINED         0x00
+#define UAC2_DECODER_OTHER             0x01
+#define UAC2_DECODER_MPEG              0x02
+#define UAC2_DECODER_AC3               0x03
+#define UAC2_DECODER_WMA               0x04
+#define UAC2_DECODER_DTS               0x05
+
+/* A.17.1 Clock Source Control Selectors */
+#define UAC2_CS_UNDEFINED              0x00
+#define UAC2_CS_CONTROL_SAM_FREQ       0x01
+#define UAC2_CS_CONTROL_CLOCK_VALID    0x02
+
+/* A.17.2 Clock Selector Control Selectors */
+#define UAC2_CX_UNDEFINED              0x00
+#define UAC2_CX_CLOCK_SELECTOR         0x01
+
+/* A.17.3 Clock Multiplier Control Selectors */
+#define UAC2_CM_UNDEFINED              0x00
+#define UAC2_CM_NUMERATOR              0x01
+#define UAC2_CM_DENOMINTATOR           0x02
+
+/* A.17.4 Terminal Control Selectors */
+#define UAC2_TE_UNDEFINED              0x00
+#define UAC2_TE_COPY_PROTECT           0x01
+#define UAC2_TE_CONNECTOR              0x02
+#define UAC2_TE_OVERLOAD               0x03
+#define UAC2_TE_CLUSTER                        0x04
+#define UAC2_TE_UNDERFLOW              0x05
+#define UAC2_TE_OVERFLOW               0x06
+#define UAC2_TE_LATENCY                        0x07
+
+/* A.17.5 Mixer Control Selectors */
+#define UAC2_MU_UNDEFINED              0x00
+#define UAC2_MU_MIXER                  0x01
+#define UAC2_MU_CLUSTER                        0x02
+#define UAC2_MU_UNDERFLOW              0x03
+#define UAC2_MU_OVERFLOW               0x04
+#define UAC2_MU_LATENCY                        0x05
+
+/* A.17.6 Selector Control Selectors */
+#define UAC2_SU_UNDEFINED              0x00
+#define UAC2_SU_SELECTOR               0x01
+#define UAC2_SU_LATENCY                        0x02
+
+/* A.17.7 Feature Unit Control Selectors */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC2_FU_INPUT_GAIN             0x0b
+#define UAC2_FU_INPUT_GAIN_PAD         0x0c
+#define UAC2_FU_PHASE_INVERTER         0x0d
+#define UAC2_FU_UNDERFLOW              0x0e
+#define UAC2_FU_OVERFLOW               0x0f
+#define UAC2_FU_LATENCY                        0x10
+
+/* A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors */
+#define UAC2_PE_UNDEFINED              0x00
+#define UAC2_PE_ENABLE                 0x01
+#define UAC2_PE_CENTERFREQ             0x02
+#define UAC2_PE_QFACTOR                        0x03
+#define UAC2_PE_GAIN                   0x04
+#define UAC2_PE_UNDERFLOW              0x05
+#define UAC2_PE_OVERFLOW               0x06
+#define UAC2_PE_LATENCY                        0x07
+
+/* A.17.8.2 Reverberation Effect Unit Control Selectors */
+#define UAC2_RV_UNDEFINED              0x00
+#define UAC2_RV_ENABLE                 0x01
+#define UAC2_RV_TYPE                   0x02
+#define UAC2_RV_LEVEL                  0x03
+#define UAC2_RV_TIME                   0x04
+#define UAC2_RV_FEEDBACK               0x05
+#define UAC2_RV_PREDELAY               0x06
+#define UAC2_RV_DENSITY                        0x07
+#define UAC2_RV_HIFREQ_ROLLOFF         0x08
+#define UAC2_RV_UNDERFLOW              0x09
+#define UAC2_RV_OVERFLOW               0x0a
+#define UAC2_RV_LATENCY                        0x0b
+
+/* A.17.8.3 Modulation Delay Effect Control Selectors */
+#define UAC2_MD_UNDEFINED              0x00
+#define UAC2_MD_ENABLE                 0x01
+#define UAC2_MD_BALANCE                        0x02
+#define UAC2_MD_RATE                   0x03
+#define UAC2_MD_DEPTH                  0x04
+#define UAC2_MD_TIME                   0x05
+#define UAC2_MD_FEEDBACK               0x06
+#define UAC2_MD_UNDERFLOW              0x07
+#define UAC2_MD_OVERFLOW               0x08
+#define UAC2_MD_LATENCY                        0x09
+
+/* A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors */
+#define UAC2_DR_UNDEFINED              0x00
+#define UAC2_DR_ENABLE                 0x01
+#define UAC2_DR_COMPRESSION_RATE       0x02
+#define UAC2_DR_MAXAMPL                        0x03
+#define UAC2_DR_THRESHOLD              0x04
+#define UAC2_DR_ATTACK_TIME            0x05
+#define UAC2_DR_RELEASE_TIME           0x06
+#define UAC2_DR_UNDEFLOW               0x07
+#define UAC2_DR_OVERFLOW               0x08
+#define UAC2_DR_LATENCY                        0x09
+
+/* A.17.9.1 Up/Down-mix Processing Unit Control Selectors */
+#define UAC2_UD_UNDEFINED              0x00
+#define UAC2_UD_ENABLE                 0x01
+#define UAC2_UD_MODE_SELECT            0x02
+#define UAC2_UD_CLUSTER                        0x03
+#define UAC2_UD_UNDERFLOW              0x04
+#define UAC2_UD_OVERFLOW               0x05
+#define UAC2_UD_LATENCY                        0x06
+
+/* A.17.9.2 Dolby Prologic[tm] Processing Unit Control Selectors */
+#define UAC2_DP_UNDEFINED              0x00
+#define UAC2_DP_ENABLE                 0x01
+#define UAC2_DP_MODE_SELECT            0x02
+#define UAC2_DP_CLUSTER                        0x03
+#define UAC2_DP_UNDERFFLOW             0x04
+#define UAC2_DP_OVERFLOW               0x05
+#define UAC2_DP_LATENCY                        0x06
+
+/* A.17.9.3 Stereo Expander Processing Unit Control Selectors */
+#define UAC2_ST_EXT_UNDEFINED          0x00
+#define UAC2_ST_EXT_ENABLE             0x01
+#define UAC2_ST_EXT_WIDTH              0x02
+#define UAC2_ST_EXT_UNDEFLOW           0x03
+#define UAC2_ST_EXT_OVERFLOW           0x04
+#define UAC2_ST_EXT_LATENCY            0x05
+
+/* A.17.10 Extension Unit Control Selectors */
+#define UAC2_XU_UNDEFINED              0x00
+#define UAC2_XU_ENABLE                 0x01
+#define UAC2_XU_CLUSTER                        0x02
+#define UAC2_XU_UNDERFLOW              0x03
+#define UAC2_XU_OVERFLOW               0x04
+#define UAC2_XU_LATENCY                        0x05
+
+/* A.17.11 AudioStreaming Interface Control Selectors */
+#define UAC2_AS_UNDEFINED              0x00
+#define UAC2_AS_ACT_ALT_SETTING                0x01
+#define UAC2_AS_VAL_ALT_SETTINGS       0x02
+#define UAC2_AS_AUDIO_DATA_FORMAT      0x03
+
+/* A.17.12 Encoder Control Selectors */
+#define UAC2_EN_UNDEFINED              0x00
+#define UAC2_EN_BIT_RATE               0x01
+#define UAC2_EN_QUALITY                        0x02
+#define UAC2_EN_VBR                    0x03
+#define UAC2_EN_TYPE                   0x04
+#define UAC2_EN_UNDERFLOW              0x05
+#define UAC2_EN_OVERFLOW               0x06
+#define UAC2_EN_ENCODER_ERROR          0x07
+#define UAC2_EN_PARAM1                 0x08
+#define UAC2_EN_PARAM2                 0x09
+#define UAC2_EN_PARAM3                 0x0a
+#define UAC2_EN_PARAM4                 0x0b
+#define UAC2_EN_PARAM5                 0x0c
+#define UAC2_EN_PARAM6                 0x0d
+#define UAC2_EN_PARAM7                 0x0e
+#define UAC2_EN_PARAM8                 0x0f
+
+/* A.17.13.1 MPEG Decoder Control Selectors */
+#define UAC2_MPEG_UNDEFINED            0x00
+#define UAC2_MPEG_DUAL_CHANNEL         0x01
+#define UAC2_MPEG_SECOND_STEREO                0x02
+#define UAC2_MPEG_MULTILINGUAL         0x03
+#define UAC2_MPEG_DYN_RANGE            0x04
+#define UAC2_MPEG_SCALING              0x05
+#define UAC2_MPEG_HILO_SCALING         0x06
+#define UAC2_MPEG_UNDERFLOW            0x07
+#define UAC2_MPEG_OVERFLOW             0x08
+#define UAC2_MPEG_DECODER_ERROR                0x09
+
+/* A17.13.2 AC3 Decoder Control Selectors */
+#define UAC2_AC3_UNDEFINED             0x00
+#define UAC2_AC3_MODE                  0x01
+#define UAC2_AC3_DYN_RANGE             0x02
+#define UAC2_AC3_SCALING               0x03
+#define UAC2_AC3_HILO_SCALING          0x04
+#define UAC2_AC3_UNDERFLOW             0x05
+#define UAC2_AC3_OVERFLOW              0x06
+#define UAC2_AC3_DECODER_ERROR         0x07
+
+/* A17.13.3 WMA Decoder Control Selectors */
+#define UAC2_WMA_UNDEFINED             0x00
+#define UAC2_WMA_UNDERFLOW             0x01
+#define UAC2_WMA_OVERFLOW              0x02
+#define UAC2_WMA_DECODER_ERROR         0x03
+
+/* A17.13.4 DTS Decoder Control Selectors */
+#define UAC2_DTS_UNDEFINED             0x00
+#define UAC2_DTS_UNDERFLOW             0x01
+#define UAC2_DTS_OVERFLOW              0x02
+#define UAC2_DTS_DECODER_ERROR         0x03
+
+/* A17.14 Endpoint Control Selectors */
+#define UAC2_EP_CS_UNDEFINED           0x00
+#define UAC2_EP_CS_PITCH               0x01
+#define UAC2_EP_CS_DATA_OVERRUN                0x02
+#define UAC2_EP_CS_DATA_UNDERRUN       0x03
+
+#endif /* __LINUX_USB_AUDIO_V2_H */
+
index 4d3e450e2b03066ea95e5309a211060f28ea69c1..905a87caf3fb248d12f7482e77cae21c54c4fdee 100644 (file)
@@ -13,6 +13,9 @@
  * Comments below reference relevant sections of that document:
  *
  * http://www.usb.org/developers/devclass_docs/audio10.pdf
+ *
+ * Types and defines in this file are either specific to version 1.0 of
+ * this standard or common for newer versions.
  */
 
 #ifndef __LINUX_USB_AUDIO_H
 
 #include <linux/types.h>
 
+/* bInterfaceProtocol values to denote the version of the standard used */
+#define UAC_VERSION_1                  0x00
+#define UAC_VERSION_2                  0x20
+
 /* A.2 Audio Interface Subclass Codes */
 #define USB_SUBCLASS_AUDIOCONTROL      0x01
 #define USB_SUBCLASS_AUDIOSTREAMING    0x02
 #define USB_SUBCLASS_MIDISTREAMING     0x03
 
-#define UAC_VERSION_1                  0x00
-#define UAC_VERSION_2                  0x20
-
 /* A.5 Audio Class-Specific AC Interface Descriptor Subtypes */
 #define UAC_HEADER                     0x01
 #define UAC_INPUT_TERMINAL             0x02
 #define UAC_PROCESSING_UNIT_V1         0x07
 #define UAC_EXTENSION_UNIT_V1          0x08
 
-/* UAC v2.0 types */
-#define UAC_EFFECT_UNIT                        0x07
-#define UAC_PROCESSING_UNIT_V2         0x08
-#define UAC_EXTENSION_UNIT_V2          0x09
-#define UAC_CLOCK_SOURCE               0x0a
-#define UAC_CLOCK_SELECTOR             0x0b
-#define UAC_CLOCK_MULTIPLIER           0x0c
-#define UAC_SAMPLE_RATE_CONVERTER      0x0d
-
 /* A.6 Audio Class-Specific AS Interface Descriptor Subtypes */
 #define UAC_AS_GENERAL                 0x01
 #define UAC_FORMAT_TYPE                        0x02
 
 #define UAC_GET_STAT                   0xff
 
-/* Audio class v2.0 handles all the parameter calls differently */
-#define UAC2_CS_CUR                    0x01
-#define UAC2_CS_RANGE                  0x02
-
 /* MIDI - A.1 MS Class-Specific Interface Descriptor Subtypes */
 #define UAC_MS_HEADER                  0x01
 #define UAC_MIDI_IN_JACK               0x02
@@ -190,6 +181,156 @@ struct uac_feature_unit_descriptor_##ch {                 \
        __u8  iFeature;                                         \
 } __attribute__ ((packed))
 
+/* 4.3.2.3 Mixer Unit Descriptor */
+struct uac_mixer_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUnitID;
+       __u8 bNrInPins;
+       __u8 baSourceID[];
+} __attribute__ ((packed));
+
+static inline __u8 uac_mixer_unit_bNrChannels(struct uac_mixer_unit_descriptor *desc)
+{
+       return desc->baSourceID[desc->bNrInPins];
+}
+
+static inline __u32 uac_mixer_unit_wChannelConfig(struct uac_mixer_unit_descriptor *desc,
+                                                 int protocol)
+{
+       if (protocol == UAC_VERSION_1)
+               return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
+                       desc->baSourceID[desc->bNrInPins + 1];
+       else
+               return  (desc->baSourceID[desc->bNrInPins + 4] << 24) |
+                       (desc->baSourceID[desc->bNrInPins + 3] << 16) |
+                       (desc->baSourceID[desc->bNrInPins + 2] << 8)  |
+                       (desc->baSourceID[desc->bNrInPins + 1]);
+}
+
+static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor *desc,
+                                               int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               desc->baSourceID[desc->bNrInPins + 3] :
+               desc->baSourceID[desc->bNrInPins + 5];
+}
+
+static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc,
+                                             int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               &desc->baSourceID[desc->bNrInPins + 4] :
+               &desc->baSourceID[desc->bNrInPins + 6];
+}
+
+static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc)
+{
+       __u8 *raw = (__u8 *) desc;
+       return raw[desc->bLength - 1];
+}
+
+/* 4.3.2.4 Selector Unit Descriptor */
+struct uac_selector_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUintID;
+       __u8 bNrInPins;
+       __u8 baSourceID[];
+} __attribute__ ((packed));
+
+static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc)
+{
+       __u8 *raw = (__u8 *) desc;
+       return raw[desc->bLength - 1];
+}
+
+/* 4.3.2.5 Feature Unit Descriptor */
+struct uac_feature_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUnitID;
+       __u8 bSourceID;
+       __u8 bControlSize;
+       __u8 bmaControls[0]; /* variable length */
+} __attribute__((packed));
+
+static inline __u8 uac_feature_unit_iFeature(struct uac_feature_unit_descriptor *desc)
+{
+       __u8 *raw = (__u8 *) desc;
+       return raw[desc->bLength - 1];
+}
+
+/* 4.3.2.6 Processing Unit Descriptors */
+struct uac_processing_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUnitID;
+       __u16 wProcessType;
+       __u8 bNrInPins;
+       __u8 baSourceID[];
+} __attribute__ ((packed));
+
+static inline __u8 uac_processing_unit_bNrChannels(struct uac_processing_unit_descriptor *desc)
+{
+       return desc->baSourceID[desc->bNrInPins];
+}
+
+static inline __u32 uac_processing_unit_wChannelConfig(struct uac_processing_unit_descriptor *desc,
+                                                      int protocol)
+{
+       if (protocol == UAC_VERSION_1)
+               return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
+                       desc->baSourceID[desc->bNrInPins + 1];
+       else
+               return  (desc->baSourceID[desc->bNrInPins + 4] << 24) |
+                       (desc->baSourceID[desc->bNrInPins + 3] << 16) |
+                       (desc->baSourceID[desc->bNrInPins + 2] << 8)  |
+                       (desc->baSourceID[desc->bNrInPins + 1]);
+}
+
+static inline __u8 uac_processing_unit_iChannelNames(struct uac_processing_unit_descriptor *desc,
+                                                    int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               desc->baSourceID[desc->bNrInPins + 3] :
+               desc->baSourceID[desc->bNrInPins + 5];
+}
+
+static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_descriptor *desc,
+                                                   int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               desc->baSourceID[desc->bNrInPins + 4] :
+               desc->baSourceID[desc->bNrInPins + 6];
+}
+
+static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc,
+                                                  int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               &desc->baSourceID[desc->bNrInPins + 5] :
+               &desc->baSourceID[desc->bNrInPins + 7];
+}
+
+static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc,
+                                                  int protocol)
+{
+       __u8 control_size = uac_processing_unit_bControlSize(desc, protocol);
+       return desc->baSourceID[desc->bNrInPins + control_size];
+}
+
+static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_descriptor *desc,
+                                                int protocol)
+{
+       __u8 control_size = uac_processing_unit_bControlSize(desc, protocol);
+       return &desc->baSourceID[desc->bNrInPins + control_size + 1];
+}
+
 /* 4.5.2 Class-Specific AS Interface Descriptor */
 struct uac_as_header_descriptor_v1 {
        __u8  bLength;                  /* in bytes: 7 */
@@ -200,19 +341,6 @@ struct uac_as_header_descriptor_v1 {
        __le16 wFormatTag;              /* The Audio Data Format */
 } __attribute__ ((packed));
 
-struct uac_as_header_descriptor_v2 {
-       __u8 bLength;
-       __u8 bDescriptorType;
-       __u8 bDescriptorSubtype;
-       __u8 bTerminalLink;
-       __u8 bmControls;
-       __u8 bFormatType;
-       __u32 bmFormats;
-       __u8 bNrChannels;
-       __u32 bmChannelConfig;
-       __u8 iChannelNames;
-} __attribute__((packed));
-
 #define UAC_DT_AS_HEADER_SIZE          7
 
 /* Formats - A.1.1 Audio Data Format Type I Codes */
@@ -277,7 +405,6 @@ struct uac_format_type_i_ext_descriptor {
        __u8 bSideBandProtocol;
 } __attribute__((packed));
 
-
 /* Formats - Audio Data Format Type I Codes */
 
 #define UAC_FORMAT_TYPE_II_MPEG        0x1001
@@ -336,31 +463,8 @@ struct uac_iso_endpoint_descriptor {
 #define UAC_EP_CS_ATTR_PITCH_CONTROL   0x02
 #define UAC_EP_CS_ATTR_FILL_MAX                0x80
 
-/* Audio class v2.0: CLOCK_SOURCE descriptor */
-
-struct uac_clock_source_descriptor {
-       __u8 bLength;
-       __u8 bDescriptorType;
-       __u8 bDescriptorSubtype;
-       __u8 bClockID;
-       __u8 bmAttributes;
-       __u8 bmControls;
-       __u8 bAssocTerminal;
-       __u8 iClockSource;
-} __attribute__((packed));
-
 /* A.10.2 Feature Unit Control Selectors */
 
-struct uac_feature_unit_descriptor {
-       __u8 bLength;
-       __u8 bDescriptorType;
-       __u8 bDescriptorSubtype;
-       __u8 bUnitID;
-       __u8 bSourceID;
-       __u8 bControlSize;
-       __u8 controls[0]; /* variable length */
-} __attribute__((packed));
-
 #define UAC_FU_CONTROL_UNDEFINED       0x00
 #define UAC_MUTE_CONTROL               0x01
 #define UAC_VOLUME_CONTROL             0x02
index 5bf64aef95581f813894dc5dc00b40c658288e17..e7ac7f493a8fa1d0874ca9992ee180679390ec69 100644 (file)
@@ -2,14 +2,24 @@
 # Makefile for ALSA
 #
 
-snd-usb-audio-objs := usbaudio.o usbmixer.o
-snd-usb-lib-objs := usbmidi.o
-snd-ua101-objs := ua101.o
+snd-usb-audio-objs :=  card.o \
+                       mixer.o \
+                       mixer_quirks.o \
+                       proc.o \
+                       quirks.o \
+                       format.o \
+                       endpoint.o \
+                       urb.o \
+                       pcm.o \
+                       helper.o
+
+snd-usbmidi-lib-objs := midi.o
 
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o
-obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o snd-usb-lib.o
-obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o
-obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o
+obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usbmidi-lib.o
+
+obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o
+obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
+obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
 
-obj-$(CONFIG_SND) += usx2y/ caiaq/
+obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/
diff --git a/sound/usb/card.c b/sound/usb/card.c
new file mode 100644 (file)
index 0000000..0bd62a1
--- /dev/null
@@ -0,0 +1,649 @@
+/*
+ *   (Tentative) USB Audio Driver for ALSA
+ *
+ *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   Many codes borrowed from audio.c by
+ *         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.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *
+ *  NOTES:
+ *
+ *   - async unlink should be used for avoiding the sleep inside lock.
+ *     2.4.22 usb-uhci seems buggy for async unlinking and results in
+ *     oops.  in such a cse, pass async_unlink=0 option.
+ *   - the linked URBs would be preferred but not used so far because of
+ *     the instability of unlinking.
+ *   - type II is not supported properly.  there is no device which supports
+ *     this type *correctly*.  SB extigy looks as if it supports, but it's
+ *     indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream).
+ */
+
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "midi.h"
+#include "mixer.h"
+#include "proc.h"
+#include "quirks.h"
+#include "endpoint.h"
+#include "helper.h"
+#include "debug.h"
+#include "pcm.h"
+#include "urb.h"
+#include "format.h"
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("USB Audio");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}");
+
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+/* Vendor/product IDs for this card */
+static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
+static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
+static int nrpacks = 8;                /* max. number of packets per urb */
+static int async_unlink = 1;
+static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
+static int ignore_ctl_error;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the USB audio adapter.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable USB audio adapter.");
+module_param_array(vid, int, NULL, 0444);
+MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");
+module_param_array(pid, int, NULL, 0444);
+MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");
+module_param(nrpacks, int, 0644);
+MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB.");
+module_param(async_unlink, bool, 0444);
+MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
+module_param_array(device_setup, int, NULL, 0444);
+MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
+module_param(ignore_ctl_error, bool, 0444);
+MODULE_PARM_DESC(ignore_ctl_error,
+                "Ignore errors from USB controller for mixer interfaces.");
+
+/*
+ * we keep the snd_usb_audio_t instances by ourselves for merging
+ * the all interfaces on the same card as one sound device.
+ */
+
+static DEFINE_MUTEX(register_mutex);
+static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
+static struct usb_driver usb_audio_driver;
+
+/*
+ * disconnect streams
+ * called from snd_usb_audio_disconnect()
+ */
+static void snd_usb_stream_disconnect(struct list_head *head)
+{
+       int idx;
+       struct snd_usb_stream *as;
+       struct snd_usb_substream *subs;
+
+       as = list_entry(head, struct snd_usb_stream, list);
+       for (idx = 0; idx < 2; idx++) {
+               subs = &as->substream[idx];
+               if (!subs->num_formats)
+                       return;
+               snd_usb_release_substream_urbs(subs, 1);
+               subs->interface = -1;
+       }
+}
+
+static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface)
+{
+       struct usb_device *dev = chip->dev;
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       struct usb_interface *iface = usb_ifnum_to_if(dev, interface);
+
+       if (!iface) {
+               snd_printk(KERN_ERR "%d:%u:%d : does not exist\n",
+                          dev->devnum, ctrlif, interface);
+               return -EINVAL;
+       }
+
+       if (usb_interface_claimed(iface)) {
+               snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n",
+                                               dev->devnum, ctrlif, interface);
+               return -EINVAL;
+       }
+
+       alts = &iface->altsetting[0];
+       altsd = get_iface_desc(alts);
+       if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
+            altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
+           altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
+               int err = snd_usbmidi_create(chip->card, iface,
+                                            &chip->midi_list, NULL);
+               if (err < 0) {
+                       snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n",
+                                               dev->devnum, ctrlif, interface);
+                       return -EINVAL;
+               }
+               usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
+
+               return 0;
+       }
+
+       if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
+            altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+           altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) {
+               snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n",
+                                       dev->devnum, ctrlif, interface, altsd->bInterfaceClass);
+               /* skip non-supported classes */
+               return -EINVAL;
+       }
+
+       if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
+               snd_printk(KERN_ERR "low speed audio streaming not supported\n");
+               return -EINVAL;
+       }
+
+       if (! snd_usb_parse_audio_endpoints(chip, interface)) {
+               usb_set_interface(dev, interface, 0); /* reset the current interface */
+               usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * parse audio control descriptor and create pcm/midi streams
+ */
+static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
+{
+       struct usb_device *dev = chip->dev;
+       struct usb_host_interface *host_iface;
+       struct usb_interface_descriptor *altsd;
+       void *control_header;
+       int i, protocol;
+
+       /* find audiocontrol interface */
+       host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
+       control_header = snd_usb_find_csint_desc(host_iface->extra,
+                                                host_iface->extralen,
+                                                NULL, UAC_HEADER);
+       altsd = get_iface_desc(host_iface);
+       protocol = altsd->bInterfaceProtocol;
+
+       if (!control_header) {
+               snd_printk(KERN_ERR "cannot find UAC_HEADER\n");
+               return -EINVAL;
+       }
+
+       switch (protocol) {
+       case UAC_VERSION_1: {
+               struct uac_ac_header_descriptor_v1 *h1 = control_header;
+
+               if (!h1->bInCollection) {
+                       snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
+                       return -EINVAL;
+               }
+
+               if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
+                       snd_printk(KERN_ERR "invalid UAC_HEADER (v1)\n");
+                       return -EINVAL;
+               }
+
+               for (i = 0; i < h1->bInCollection; i++)
+                       snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);
+
+               break;
+       }
+
+       case UAC_VERSION_2: {
+               struct uac_clock_source_descriptor *cs;
+               struct usb_interface_assoc_descriptor *assoc =
+                       usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
+
+               if (!assoc) {
+                       snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n");
+                       return -EINVAL;
+               }
+
+               /* FIXME: for now, we expect there is at least one clock source
+                * descriptor and we always take the first one.
+                * We should properly support devices with multiple clock sources,
+                * clock selectors and sample rate conversion units. */
+
+               cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
+                                               NULL, UAC2_CLOCK_SOURCE);
+
+               if (!cs) {
+                       snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
+                       return -EINVAL;
+               }
+
+               chip->clock_id = cs->bClockID;
+
+               for (i = 0; i < assoc->bInterfaceCount; i++) {
+                       int intf = assoc->bFirstInterface + i;
+
+                       if (intf != ctrlif)
+                               snd_usb_create_stream(chip, ctrlif, intf);
+               }
+
+               break;
+       }
+
+       default:
+               snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * free the chip instance
+ *
+ * here we have to do not much, since pcm and controls are already freed
+ *
+ */
+
+static int snd_usb_audio_free(struct snd_usb_audio *chip)
+{
+       kfree(chip);
+       return 0;
+}
+
+static int snd_usb_audio_dev_free(struct snd_device *device)
+{
+       struct snd_usb_audio *chip = device->device_data;
+       return snd_usb_audio_free(chip);
+}
+
+
+/*
+ * create a chip instance and set its names.
+ */
+static int snd_usb_audio_create(struct usb_device *dev, int idx,
+                               const struct snd_usb_audio_quirk *quirk,
+                               struct snd_usb_audio **rchip)
+{
+       struct snd_card *card;
+       struct snd_usb_audio *chip;
+       int err, len;
+       char component[14];
+       static struct snd_device_ops ops = {
+               .dev_free =     snd_usb_audio_dev_free,
+       };
+
+       *rchip = NULL;
+
+       if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
+           snd_usb_get_speed(dev) != USB_SPEED_FULL &&
+           snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
+               snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
+               return -ENXIO;
+       }
+
+       err = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card);
+       if (err < 0) {
+               snd_printk(KERN_ERR "cannot create card instance %d\n", idx);
+               return err;
+       }
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (! chip) {
+               snd_card_free(card);
+               return -ENOMEM;
+       }
+
+       chip->index = idx;
+       chip->dev = dev;
+       chip->card = card;
+       chip->setup = device_setup[idx];
+       chip->nrpacks = nrpacks;
+       chip->async_unlink = async_unlink;
+
+       chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+                             le16_to_cpu(dev->descriptor.idProduct));
+       INIT_LIST_HEAD(&chip->pcm_list);
+       INIT_LIST_HEAD(&chip->midi_list);
+       INIT_LIST_HEAD(&chip->mixer_list);
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+               snd_usb_audio_free(chip);
+               snd_card_free(card);
+               return err;
+       }
+
+       strcpy(card->driver, "USB-Audio");
+       sprintf(component, "USB%04x:%04x",
+               USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
+       snd_component_add(card, component);
+
+       /* retrieve the device string as shortname */
+       if (quirk && quirk->product_name) {
+               strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname));
+       } else {
+               if (!dev->descriptor.iProduct ||
+                   usb_string(dev, dev->descriptor.iProduct,
+                   card->shortname, sizeof(card->shortname)) <= 0) {
+                       /* no name available from anywhere, so use ID */
+                       sprintf(card->shortname, "USB Device %#04x:%#04x",
+                               USB_ID_VENDOR(chip->usb_id),
+                               USB_ID_PRODUCT(chip->usb_id));
+               }
+       }
+
+       /* retrieve the vendor and device strings as longname */
+       if (quirk && quirk->vendor_name) {
+               len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname));
+       } else {
+               if (dev->descriptor.iManufacturer)
+                       len = usb_string(dev, dev->descriptor.iManufacturer,
+                                        card->longname, sizeof(card->longname));
+               else
+                       len = 0;
+               /* we don't really care if there isn't any vendor string */
+       }
+       if (len > 0)
+               strlcat(card->longname, " ", sizeof(card->longname));
+
+       strlcat(card->longname, card->shortname, sizeof(card->longname));
+
+       len = strlcat(card->longname, " at ", sizeof(card->longname));
+
+       if (len < sizeof(card->longname))
+               usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
+
+       strlcat(card->longname,
+               snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
+               snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
+               ", high speed",
+               sizeof(card->longname));
+
+       snd_usb_audio_create_proc(chip);
+
+       *rchip = chip;
+       return 0;
+}
+
+/*
+ * probe the active usb device
+ *
+ * note that this can be called multiple times per a device, when it
+ * includes multiple audio control interfaces.
+ *
+ * thus we check the usb device pointer and creates the card instance
+ * only at the first time.  the successive calls of this function will
+ * append the pcm interface to the corresponding card.
+ */
+static void *snd_usb_audio_probe(struct usb_device *dev,
+                                struct usb_interface *intf,
+                                const struct usb_device_id *usb_id)
+{
+       const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info;
+       int i, err;
+       struct snd_usb_audio *chip;
+       struct usb_host_interface *alts;
+       int ifnum;
+       u32 id;
+
+       alts = &intf->altsetting[0];
+       ifnum = get_iface_desc(alts)->bInterfaceNumber;
+       id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+                   le16_to_cpu(dev->descriptor.idProduct));
+       if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
+               goto __err_val;
+
+       if (snd_usb_apply_boot_quirk(dev, intf, quirk) < 0)
+               goto __err_val;
+
+       /*
+        * found a config.  now register to ALSA
+        */
+
+       /* check whether it's already registered */
+       chip = NULL;
+       mutex_lock(&register_mutex);
+       for (i = 0; i < SNDRV_CARDS; i++) {
+               if (usb_chip[i] && usb_chip[i]->dev == dev) {
+                       if (usb_chip[i]->shutdown) {
+                               snd_printk(KERN_ERR "USB device is in the shutdown state, cannot create a card instance\n");
+                               goto __error;
+                       }
+                       chip = usb_chip[i];
+                       break;
+               }
+       }
+       if (! chip) {
+               /* it's a fresh one.
+                * now look for an empty slot and create a new card instance
+                */
+               for (i = 0; i < SNDRV_CARDS; i++)
+                       if (enable[i] && ! usb_chip[i] &&
+                           (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
+                           (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
+                               if (snd_usb_audio_create(dev, i, quirk, &chip) < 0) {
+                                       goto __error;
+                               }
+                               snd_card_set_dev(chip->card, &intf->dev);
+                               break;
+                       }
+               if (!chip) {
+                       printk(KERN_ERR "no available usb audio device\n");
+                       goto __error;
+               }
+       }
+
+       chip->txfr_quirk = 0;
+       err = 1; /* continue */
+       if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
+               /* need some special handlings */
+               if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0)
+                       goto __error;
+       }
+
+       if (err > 0) {
+               /* create normal USB audio interfaces */
+               if (snd_usb_create_streams(chip, ifnum) < 0 ||
+                   snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) {
+                       goto __error;
+               }
+       }
+
+       /* we are allowed to call snd_card_register() many times */
+       if (snd_card_register(chip->card) < 0) {
+               goto __error;
+       }
+
+       usb_chip[chip->index] = chip;
+       chip->num_interfaces++;
+       mutex_unlock(&register_mutex);
+       return chip;
+
+ __error:
+       if (chip && !chip->num_interfaces)
+               snd_card_free(chip->card);
+       mutex_unlock(&register_mutex);
+ __err_val:
+       return NULL;
+}
+
+/*
+ * we need to take care of counter, since disconnection can be called also
+ * many times as well as usb_audio_probe().
+ */
+static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
+{
+       struct snd_usb_audio *chip;
+       struct snd_card *card;
+       struct list_head *p;
+
+       if (ptr == (void *)-1L)
+               return;
+
+       chip = ptr;
+       card = chip->card;
+       mutex_lock(&register_mutex);
+       chip->shutdown = 1;
+       chip->num_interfaces--;
+       if (chip->num_interfaces <= 0) {
+               snd_card_disconnect(card);
+               /* release the pcm resources */
+               list_for_each(p, &chip->pcm_list) {
+                       snd_usb_stream_disconnect(p);
+               }
+               /* release the midi resources */
+               list_for_each(p, &chip->midi_list) {
+                       snd_usbmidi_disconnect(p);
+               }
+               /* release mixer resources */
+               list_for_each(p, &chip->mixer_list) {
+                       snd_usb_mixer_disconnect(p);
+               }
+               usb_chip[chip->index] = NULL;
+               mutex_unlock(&register_mutex);
+               snd_card_free_when_closed(card);
+       } else {
+               mutex_unlock(&register_mutex);
+       }
+}
+
+/*
+ * new 2.5 USB kernel API
+ */
+static int usb_audio_probe(struct usb_interface *intf,
+                          const struct usb_device_id *id)
+{
+       void *chip;
+       chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id);
+       if (chip) {
+               usb_set_intfdata(intf, chip);
+               return 0;
+       } else
+               return -EIO;
+}
+
+static void usb_audio_disconnect(struct usb_interface *intf)
+{
+       snd_usb_audio_disconnect(interface_to_usbdev(intf),
+                                usb_get_intfdata(intf));
+}
+
+#ifdef CONFIG_PM
+static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct snd_usb_audio *chip = usb_get_intfdata(intf);
+       struct list_head *p;
+       struct snd_usb_stream *as;
+
+       if (chip == (void *)-1L)
+               return 0;
+
+       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+       if (!chip->num_suspended_intf++) {
+               list_for_each(p, &chip->pcm_list) {
+                       as = list_entry(p, struct snd_usb_stream, list);
+                       snd_pcm_suspend_all(as->pcm);
+               }
+       }
+
+       return 0;
+}
+
+static int usb_audio_resume(struct usb_interface *intf)
+{
+       struct snd_usb_audio *chip = usb_get_intfdata(intf);
+
+       if (chip == (void *)-1L)
+               return 0;
+       if (--chip->num_suspended_intf)
+               return 0;
+       /*
+        * ALSA leaves material resumption to user space
+        * we just notify
+        */
+
+       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+
+       return 0;
+}
+#endif         /* CONFIG_PM */
+
+static struct usb_device_id usb_audio_ids [] = {
+#include "quirks-table.h"
+    { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
+      .bInterfaceClass = USB_CLASS_AUDIO,
+      .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL },
+    { }                                                /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usb_audio_ids);
+
+/*
+ * entry point for linux usb interface
+ */
+
+static struct usb_driver usb_audio_driver = {
+       .name =         "snd-usb-audio",
+       .probe =        usb_audio_probe,
+       .disconnect =   usb_audio_disconnect,
+       .suspend =      usb_audio_suspend,
+       .resume =       usb_audio_resume,
+       .id_table =     usb_audio_ids,
+};
+
+static int __init snd_usb_audio_init(void)
+{
+       if (nrpacks < 1 || nrpacks > MAX_PACKS) {
+               printk(KERN_WARNING "invalid nrpacks value.\n");
+               return -EINVAL;
+       }
+       return usb_register(&usb_audio_driver);
+}
+
+static void __exit snd_usb_audio_cleanup(void)
+{
+       usb_deregister(&usb_audio_driver);
+}
+
+module_init(snd_usb_audio_init);
+module_exit(snd_usb_audio_cleanup);
diff --git a/sound/usb/card.h b/sound/usb/card.h
new file mode 100644 (file)
index 0000000..ed92420
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef __USBAUDIO_CARD_H
+#define __USBAUDIO_CARD_H
+
+#define MAX_PACKS      20
+#define MAX_PACKS_HS   (MAX_PACKS * 8) /* in high speed mode */
+#define MAX_URBS       8
+#define SYNC_URBS      4       /* always four urbs for sync */
+#define MAX_QUEUE      24      /* try not to exceed this queue length, in ms */
+
+struct audioformat {
+       struct list_head list;
+       u64 formats;                    /* ALSA format bits */
+       unsigned int channels;          /* # channels */
+       unsigned int fmt_type;          /* USB audio format type (1-3) */
+       unsigned int frame_size;        /* samples per frame for non-audio */
+       int iface;                      /* interface number */
+       unsigned char altsetting;       /* corresponding alternate setting */
+       unsigned char altset_idx;       /* array index of altenate setting */
+       unsigned char attributes;       /* corresponding attributes of cs endpoint */
+       unsigned char endpoint;         /* endpoint */
+       unsigned char ep_attr;          /* endpoint attributes */
+       unsigned char datainterval;     /* log_2 of data packet interval */
+       unsigned int maxpacksize;       /* max. packet size */
+       unsigned int rates;             /* rate bitmasks */
+       unsigned int rate_min, rate_max;        /* min/max rates */
+       unsigned int nr_rates;          /* number of rate table entries */
+       unsigned int *rate_table;       /* rate table */
+};
+
+struct snd_usb_substream;
+
+struct snd_urb_ctx {
+       struct urb *urb;
+       unsigned int buffer_size;       /* size of data buffer, if data URB */
+       struct snd_usb_substream *subs;
+       int index;      /* index for urb array */
+       int packets;    /* number of packets per urb */
+};
+
+struct snd_urb_ops {
+       int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+       int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+       int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+       int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+};
+
+struct snd_usb_substream {
+       struct snd_usb_stream *stream;
+       struct usb_device *dev;
+       struct snd_pcm_substream *pcm_substream;
+       int direction;  /* playback or capture */
+       int interface;  /* current interface */
+       int endpoint;   /* assigned endpoint */
+       struct audioformat *cur_audiofmt;       /* current audioformat pointer (for hw_params callback) */
+       unsigned int cur_rate;          /* current rate (for hw_params callback) */
+       unsigned int period_bytes;      /* current period bytes (for hw_params callback) */
+       unsigned int altset_idx;     /* USB data format: index of alternate setting */
+       unsigned int datapipe;   /* the data i/o pipe */
+       unsigned int syncpipe;   /* 1 - async out or adaptive in */
+       unsigned int datainterval;      /* log_2 of data packet interval */
+       unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
+       unsigned int freqn;      /* nominal sampling rate in fs/fps in Q16.16 format */
+       unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
+       unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
+       unsigned int phase;      /* phase accumulator */
+       unsigned int maxpacksize;       /* max packet size in bytes */
+       unsigned int maxframesize;      /* max packet size in frames */
+       unsigned int curpacksize;       /* current packet size in bytes (for capture) */
+       unsigned int curframesize;      /* current packet size in frames (for capture) */
+       unsigned int fill_max: 1;       /* fill max packet size always */
+       unsigned int txfr_quirk:1;      /* allow sub-frame alignment */
+       unsigned int fmt_type;          /* USB audio format type (1-3) */
+
+       unsigned int running: 1;        /* running status */
+
+       unsigned int hwptr_done;        /* processed byte position in the buffer */
+       unsigned int transfer_done;             /* processed frames since last period update */
+       unsigned long active_mask;      /* bitmask of active urbs */
+       unsigned long unlink_mask;      /* bitmask of unlinked urbs */
+
+       unsigned int nurbs;                     /* # urbs */
+       struct snd_urb_ctx dataurb[MAX_URBS];   /* data urb table */
+       struct snd_urb_ctx syncurb[SYNC_URBS];  /* sync urb table */
+       char *syncbuf;                          /* sync buffer for all sync URBs */
+       dma_addr_t sync_dma;                    /* DMA address of syncbuf */
+
+       u64 formats;                    /* format bitmasks (all or'ed) */
+       unsigned int num_formats;               /* number of supported audio formats (list) */
+       struct list_head fmt_list;      /* format list */
+       struct snd_pcm_hw_constraint_list rate_list;    /* limited rates */
+       spinlock_t lock;
+
+       struct snd_urb_ops ops;         /* callbacks (must be filled at init) */
+};
+
+struct snd_usb_stream {
+       struct snd_usb_audio *chip;
+       struct snd_pcm *pcm;
+       int pcm_index;
+       unsigned int fmt_type;          /* USB audio format type (1-3) */
+       struct snd_usb_substream substream[2];
+       struct list_head list;
+};
+
+#endif /* __USBAUDIO_CARD_H */
diff --git a/sound/usb/debug.h b/sound/usb/debug.h
new file mode 100644 (file)
index 0000000..343ec2d
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __USBAUDIO_DEBUG_H
+#define __USBAUDIO_DEBUG_H
+
+/*
+ * h/w constraints
+ */
+
+#ifdef HW_CONST_DEBUG
+#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
+#else
+#define hwc_debug(fmt, args...) /**/
+#endif
+
+#endif /* __USBAUDIO_DEBUG_H */
+
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
new file mode 100644 (file)
index 0000000..b1309cd
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "proc.h"
+#include "quirks.h"
+#include "endpoint.h"
+#include "urb.h"
+#include "pcm.h"
+#include "helper.h"
+#include "format.h"
+
+/*
+ * free a substream
+ */
+static void free_substream(struct snd_usb_substream *subs)
+{
+       struct list_head *p, *n;
+
+       if (!subs->num_formats)
+               return; /* not initialized */
+       list_for_each_safe(p, n, &subs->fmt_list) {
+               struct audioformat *fp = list_entry(p, struct audioformat, list);
+               kfree(fp->rate_table);
+               kfree(fp);
+       }
+       kfree(subs->rate_list.list);
+}
+
+
+/*
+ * free a usb stream instance
+ */
+static void snd_usb_audio_stream_free(struct snd_usb_stream *stream)
+{
+       free_substream(&stream->substream[0]);
+       free_substream(&stream->substream[1]);
+       list_del(&stream->list);
+       kfree(stream);
+}
+
+static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
+{
+       struct snd_usb_stream *stream = pcm->private_data;
+       if (stream) {
+               stream->pcm = NULL;
+               snd_usb_audio_stream_free(stream);
+       }
+}
+
+
+/*
+ * add this endpoint to the chip instance.
+ * if a stream with the same endpoint already exists, append to it.
+ * if not, create a new pcm stream.
+ */
+int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct audioformat *fp)
+{
+       struct list_head *p;
+       struct snd_usb_stream *as;
+       struct snd_usb_substream *subs;
+       struct snd_pcm *pcm;
+       int err;
+
+       list_for_each(p, &chip->pcm_list) {
+               as = list_entry(p, struct snd_usb_stream, list);
+               if (as->fmt_type != fp->fmt_type)
+                       continue;
+               subs = &as->substream[stream];
+               if (!subs->endpoint)
+                       continue;
+               if (subs->endpoint == fp->endpoint) {
+                       list_add_tail(&fp->list, &subs->fmt_list);
+                       subs->num_formats++;
+                       subs->formats |= fp->formats;
+                       return 0;
+               }
+       }
+       /* look for an empty stream */
+       list_for_each(p, &chip->pcm_list) {
+               as = list_entry(p, struct snd_usb_stream, list);
+               if (as->fmt_type != fp->fmt_type)
+                       continue;
+               subs = &as->substream[stream];
+               if (subs->endpoint)
+                       continue;
+               err = snd_pcm_new_stream(as->pcm, stream, 1);
+               if (err < 0)
+                       return err;
+               snd_usb_init_substream(as, stream, fp);
+               return 0;
+       }
+
+       /* create a new pcm */
+       as = kzalloc(sizeof(*as), GFP_KERNEL);
+       if (!as)
+               return -ENOMEM;
+       as->pcm_index = chip->pcm_devs;
+       as->chip = chip;
+       as->fmt_type = fp->fmt_type;
+       err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
+                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
+                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
+                         &pcm);
+       if (err < 0) {
+               kfree(as);
+               return err;
+       }
+       as->pcm = pcm;
+       pcm->private_data = as;
+       pcm->private_free = snd_usb_audio_pcm_free;
+       pcm->info_flags = 0;
+       if (chip->pcm_devs > 0)
+               sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
+       else
+               strcpy(pcm->name, "USB Audio");
+
+       snd_usb_init_substream(as, stream, fp);
+
+       list_add(&as->list, &chip->pcm_list);
+       chip->pcm_devs++;
+
+       snd_usb_proc_pcm_format_add(as);
+
+       return 0;
+}
+
+int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
+{
+       struct usb_device *dev;
+       struct usb_interface *iface;
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       int i, altno, err, stream;
+       int format = 0, num_channels = 0;
+       struct audioformat *fp = NULL;
+       unsigned char *fmt, *csep;
+       int num, protocol;
+
+       dev = chip->dev;
+
+       /* parse the interface's altsettings */
+       iface = usb_ifnum_to_if(dev, iface_no);
+
+       num = iface->num_altsetting;
+
+       /*
+        * Dallas DS4201 workaround: It presents 5 altsettings, but the last
+        * one misses syncpipe, and does not produce any sound.
+        */
+       if (chip->usb_id == USB_ID(0x04fa, 0x4201))
+               num = 4;
+
+       for (i = 0; i < num; i++) {
+               alts = &iface->altsetting[i];
+               altsd = get_iface_desc(alts);
+               protocol = altsd->bInterfaceProtocol;
+               /* skip invalid one */
+               if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
+                    altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+                   (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
+                    altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) ||
+                   altsd->bNumEndpoints < 1 ||
+                   le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
+                       continue;
+               /* must be isochronous */
+               if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
+                   USB_ENDPOINT_XFER_ISOC)
+                       continue;
+               /* check direction */
+               stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
+                       SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+               altno = altsd->bAlternateSetting;
+
+               if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
+                       continue;
+
+               /* get audio formats */
+               switch (protocol) {
+               case UAC_VERSION_1: {
+                       struct uac_as_header_descriptor_v1 *as =
+                               snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+
+                       if (!as) {
+                               snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
+                                          dev->devnum, iface_no, altno);
+                               continue;
+                       }
+
+                       if (as->bLength < sizeof(*as)) {
+                               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
+                                          dev->devnum, iface_no, altno);
+                               continue;
+                       }
+
+                       format = le16_to_cpu(as->wFormatTag); /* remember the format value */
+                       break;
+               }
+
+               case UAC_VERSION_2: {
+                       struct uac_as_header_descriptor_v2 *as =
+                               snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+
+                       if (!as) {
+                               snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
+                                          dev->devnum, iface_no, altno);
+                               continue;
+                       }
+
+                       if (as->bLength < sizeof(*as)) {
+                               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
+                                          dev->devnum, iface_no, altno);
+                               continue;
+                       }
+
+                       num_channels = as->bNrChannels;
+                       format = le32_to_cpu(as->bmFormats);
+
+                       break;
+               }
+
+               default:
+                       snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n",
+                                  dev->devnum, iface_no, altno, protocol);
+                       continue;
+               }
+
+               /* get format type */
+               fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
+               if (!fmt) {
+                       snd_printk(KERN_ERR "%d:%u:%d : no UAC_FORMAT_TYPE desc\n",
+                                  dev->devnum, iface_no, altno);
+                       continue;
+               }
+               if (((protocol == UAC_VERSION_1) && (fmt[0] < 8)) ||
+                   ((protocol == UAC_VERSION_2) && (fmt[0] != 6))) {
+                       snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
+                                  dev->devnum, iface_no, altno);
+                       continue;
+               }
+
+               /*
+                * Blue Microphones workaround: The last altsetting is identical
+                * with the previous one, except for a larger packet size, but
+                * is actually a mislabeled two-channel setting; ignore it.
+                */
+               if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 &&
+                   fp && fp->altsetting == 1 && fp->channels == 1 &&
+                   fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
+                   protocol == UAC_VERSION_1 &&
+                   le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
+                                                       fp->maxpacksize * 2)
+                       continue;
+
+               csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
+               /* Creamware Noah has this descriptor after the 2nd endpoint */
+               if (!csep && altsd->bNumEndpoints >= 2)
+                       csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
+               if (!csep || csep[0] < 7 || csep[2] != UAC_EP_GENERAL) {
+                       snd_printk(KERN_WARNING "%d:%u:%d : no or invalid"
+                                  " class specific endpoint descriptor\n",
+                                  dev->devnum, iface_no, altno);
+                       csep = NULL;
+               }
+
+               fp = kzalloc(sizeof(*fp), GFP_KERNEL);
+               if (! fp) {
+                       snd_printk(KERN_ERR "cannot malloc\n");
+                       return -ENOMEM;
+               }
+
+               fp->iface = iface_no;
+               fp->altsetting = altno;
+               fp->altset_idx = i;
+               fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
+               fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
+               fp->datainterval = snd_usb_parse_datainterval(chip, alts);
+               fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+               /* num_channels is only set for v2 interfaces */
+               fp->channels = num_channels;
+               if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
+                       fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
+                                       * (fp->maxpacksize & 0x7ff);
+               fp->attributes = csep ? csep[3] : 0;
+
+               /* some quirks for attributes here */
+
+               switch (chip->usb_id) {
+               case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
+                       /* Optoplay sets the sample rate attribute although
+                        * it seems not supporting it in fact.
+                        */
+                       fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
+                       break;
+               case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
+               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+                       /* doesn't set the sample rate attribute, but supports it */
+                       fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
+                       break;
+               case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
+               case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
+                                               an older model 77d:223) */
+               /*
+                * plantronics headset and Griffin iMic have set adaptive-in
+                * although it's really not...
+                */
+                       fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
+                       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
+                       else
+                               fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
+                       break;
+               }
+
+               /* ok, let's parse further... */
+               if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
+                       kfree(fp->rate_table);
+                       kfree(fp);
+                       continue;
+               }
+
+               snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint);
+               err = snd_usb_add_audio_endpoint(chip, stream, fp);
+               if (err < 0) {
+                       kfree(fp->rate_table);
+                       kfree(fp);
+                       return err;
+               }
+               /* try to set the interface... */
+               usb_set_interface(chip->dev, iface_no, altno);
+               snd_usb_init_pitch(chip, iface_no, alts, fp);
+               snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max);
+       }
+       return 0;
+}
+
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
new file mode 100644 (file)
index 0000000..64dd0db
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __USBAUDIO_ENDPOINT_H
+#define __USBAUDIO_ENDPOINT_H
+
+int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip,
+                                 int iface_no);
+
+int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip,
+                              int stream,
+                              struct audioformat *fp);
+
+#endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/format.c b/sound/usb/format.c
new file mode 100644 (file)
index 0000000..fcadedd
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "quirks.h"
+#include "helper.h"
+#include "debug.h"
+
+/*
+ * parse the audio format type I descriptor
+ * and returns the corresponding pcm format
+ *
+ * @dev: usb device
+ * @fp: audioformat record
+ * @format: the format tag (wFormatTag)
+ * @fmt: the format type descriptor
+ */
+static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
+                                    struct audioformat *fp,
+                                    int format, void *_fmt,
+                                    int protocol)
+{
+       int sample_width, sample_bytes;
+       u64 pcm_formats;
+
+       switch (protocol) {
+       case UAC_VERSION_1: {
+               struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
+               sample_width = fmt->bBitResolution;
+               sample_bytes = fmt->bSubframeSize;
+               format = 1 << format;
+               break;
+       }
+
+       case UAC_VERSION_2: {
+               struct uac_format_type_i_ext_descriptor *fmt = _fmt;
+               sample_width = fmt->bBitResolution;
+               sample_bytes = fmt->bSubslotSize;
+               format <<= 1;
+               break;
+       }
+
+       default:
+               return -EINVAL;
+       }
+
+       pcm_formats = 0;
+
+       if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) {
+               /* some devices don't define this correctly... */
+               snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
+                           chip->dev->devnum, fp->iface, fp->altsetting);
+               format = 1 << UAC_FORMAT_TYPE_I_PCM;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
+               if (sample_width > sample_bytes * 8) {
+                       snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
+                                  chip->dev->devnum, fp->iface, fp->altsetting,
+                                  sample_width, sample_bytes);
+               }
+               /* check the format byte size */
+               switch (sample_bytes) {
+               case 1:
+                       pcm_formats |= SNDRV_PCM_FMTBIT_S8;
+                       break;
+               case 2:
+                       if (snd_usb_is_big_endian_format(chip, fp))
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S16_BE; /* grrr, big endian!! */
+                       else
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S16_LE;
+                       break;
+               case 3:
+                       if (snd_usb_is_big_endian_format(chip, fp))
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S24_3BE; /* grrr, big endian!! */
+                       else
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S24_3LE;
+                       break;
+               case 4:
+                       pcm_formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                       break;
+               default:
+                       snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
+                                  chip->dev->devnum, fp->iface, fp->altsetting,
+                                  sample_width, sample_bytes);
+                       break;
+               }
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) {
+               /* Dallas DS4201 workaround: it advertises U8 format, but really
+                  supports S8. */
+               if (chip->usb_id == USB_ID(0x04fa, 0x4201))
+                       pcm_formats |= SNDRV_PCM_FMTBIT_S8;
+               else
+                       pcm_formats |= SNDRV_PCM_FMTBIT_U8;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) {
+               pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) {
+               pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) {
+               pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
+       }
+       if (format & ~0x3f) {
+               snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n",
+                          chip->dev->devnum, fp->iface, fp->altsetting, format);
+       }
+       return pcm_formats;
+}
+
+
+/*
+ * parse the format descriptor and stores the possible sample rates
+ * on the audioformat table (audio class v1).
+ *
+ * @dev: usb device
+ * @fp: audioformat record
+ * @fmt: the format descriptor
+ * @offset: the start offset of descriptor pointing the rate type
+ *          (7 for type I and II, 8 for type II)
+ */
+static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audioformat *fp,
+                                      unsigned char *fmt, int offset)
+{
+       int nr_rates = fmt[offset];
+
+       if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
+               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
+                                  chip->dev->devnum, fp->iface, fp->altsetting);
+               return -1;
+       }
+
+       if (nr_rates) {
+               /*
+                * build the rate table and bitmap flags
+                */
+               int r, idx;
+
+               fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
+               if (fp->rate_table == NULL) {
+                       snd_printk(KERN_ERR "cannot malloc\n");
+                       return -1;
+               }
+
+               fp->nr_rates = 0;
+               fp->rate_min = fp->rate_max = 0;
+               for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) {
+                       unsigned int rate = combine_triple(&fmt[idx]);
+                       if (!rate)
+                               continue;
+                       /* C-Media CM6501 mislabels its 96 kHz altsetting */
+                       if (rate == 48000 && nr_rates == 1 &&
+                           (chip->usb_id == USB_ID(0x0d8c, 0x0201) ||
+                            chip->usb_id == USB_ID(0x0d8c, 0x0102)) &&
+                           fp->altsetting == 5 && fp->maxpacksize == 392)
+                               rate = 96000;
+                       /* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */
+                       if (rate == 16000 && chip->usb_id == USB_ID(0x041e, 0x4068))
+                               rate = 8000;
+
+                       fp->rate_table[fp->nr_rates] = rate;
+                       if (!fp->rate_min || rate < fp->rate_min)
+                               fp->rate_min = rate;
+                       if (!fp->rate_max || rate > fp->rate_max)
+                               fp->rate_max = rate;
+                       fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+                       fp->nr_rates++;
+               }
+               if (!fp->nr_rates) {
+                       hwc_debug("All rates were zero. Skipping format!\n");
+                       return -1;
+               }
+       } else {
+               /* continuous rates */
+               fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
+               fp->rate_min = combine_triple(&fmt[offset + 1]);
+               fp->rate_max = combine_triple(&fmt[offset + 4]);
+       }
+       return 0;
+}
+
+/*
+ * parse the format descriptor and stores the possible sample rates
+ * on the audioformat table (audio class v2).
+ */
+static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
+                                      struct audioformat *fp,
+                                      struct usb_host_interface *iface)
+{
+       struct usb_device *dev = chip->dev;
+       unsigned char tmp[2], *data;
+       int i, nr_rates, data_size, ret = 0;
+
+       /* get the number of sample rates first by only fetching 2 bytes */
+       ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
+                             USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                             UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                             tmp, sizeof(tmp), 1000);
+
+       if (ret < 0) {
+               snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
+               goto err;
+       }
+
+       nr_rates = (tmp[1] << 8) | tmp[0];
+       data_size = 2 + 12 * nr_rates;
+       data = kzalloc(data_size, GFP_KERNEL);
+       if (!data) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       /* now get the full information */
+       ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
+                              USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                              UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                              data, data_size, 1000);
+
+       if (ret < 0) {
+               snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
+               ret = -EINVAL;
+               goto err_free;
+       }
+
+       fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
+       if (!fp->rate_table) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
+       fp->nr_rates = 0;
+       fp->rate_min = fp->rate_max = 0;
+
+       for (i = 0; i < nr_rates; i++) {
+               int rate = combine_quad(&data[2 + 12 * i]);
+
+               fp->rate_table[fp->nr_rates] = rate;
+               if (!fp->rate_min || rate < fp->rate_min)
+                       fp->rate_min = rate;
+               if (!fp->rate_max || rate > fp->rate_max)
+                       fp->rate_max = rate;
+               fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+               fp->nr_rates++;
+       }
+
+err_free:
+       kfree(data);
+err:
+       return ret;
+}
+
+/*
+ * parse the format type I and III descriptors
+ */
+static int parse_audio_format_i(struct snd_usb_audio *chip,
+                               struct audioformat *fp,
+                               int format, void *_fmt,
+                               struct usb_host_interface *iface)
+{
+       struct usb_interface_descriptor *altsd = get_iface_desc(iface);
+       struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
+       int protocol = altsd->bInterfaceProtocol;
+       int pcm_format, ret;
+
+       if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
+               /* FIXME: the format type is really IECxxx
+                *        but we give normal PCM format to get the existing
+                *        apps working...
+                */
+               switch (chip->usb_id) {
+
+               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+                       if (chip->setup == 0x00 && 
+                           fp->altsetting == 6)
+                               pcm_format = SNDRV_PCM_FORMAT_S16_BE;
+                       else
+                               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+                       break;
+               default:
+                       pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+               }
+               fp->formats = 1uLL << pcm_format;
+       } else {
+               fp->formats = parse_audio_format_i_type(chip, fp, format,
+                                                       fmt, protocol);
+               if (!fp->formats)
+                       return -1;
+       }
+
+       /* gather possible sample rates */
+       /* audio class v1 reports possible sample rates as part of the
+        * proprietary class specific descriptor.
+        * audio class v2 uses class specific EP0 range requests for that.
+        */
+       switch (protocol) {
+       case UAC_VERSION_1:
+               fp->channels = fmt->bNrChannels;
+               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7);
+               break;
+       case UAC_VERSION_2:
+               /* fp->channels is already set in this case */
+               ret = parse_audio_format_rates_v2(chip, fp, iface);
+               break;
+       }
+
+       if (fp->channels < 1) {
+               snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
+                          chip->dev->devnum, fp->iface, fp->altsetting, fp->channels);
+               return -1;
+       }
+
+       return ret;
+}
+
+/*
+ * parse the format type II descriptor
+ */
+static int parse_audio_format_ii(struct snd_usb_audio *chip,
+                                struct audioformat *fp,
+                                int format, void *_fmt,
+                                struct usb_host_interface *iface)
+{
+       int brate, framesize, ret;
+       struct usb_interface_descriptor *altsd = get_iface_desc(iface);
+       int protocol = altsd->bInterfaceProtocol;
+
+       switch (format) {
+       case UAC_FORMAT_TYPE_II_AC3:
+               /* FIXME: there is no AC3 format defined yet */
+               // fp->formats = SNDRV_PCM_FMTBIT_AC3;
+               fp->formats = SNDRV_PCM_FMTBIT_U8; /* temporary hack to receive byte streams */
+               break;
+       case UAC_FORMAT_TYPE_II_MPEG:
+               fp->formats = SNDRV_PCM_FMTBIT_MPEG;
+               break;
+       default:
+               snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected.  processed as MPEG.\n",
+                          chip->dev->devnum, fp->iface, fp->altsetting, format);
+               fp->formats = SNDRV_PCM_FMTBIT_MPEG;
+               break;
+       }
+
+       fp->channels = 1;
+
+       switch (protocol) {
+       case UAC_VERSION_1: {
+               struct uac_format_type_ii_discrete_descriptor *fmt = _fmt;
+               brate = le16_to_cpu(fmt->wMaxBitRate);
+               framesize = le16_to_cpu(fmt->wSamplesPerFrame);
+               snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
+               fp->frame_size = framesize;
+               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 8); /* fmt[8..] sample rates */
+               break;
+       }
+       case UAC_VERSION_2: {
+               struct uac_format_type_ii_ext_descriptor *fmt = _fmt;
+               brate = le16_to_cpu(fmt->wMaxBitRate);
+               framesize = le16_to_cpu(fmt->wSamplesPerFrame);
+               snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
+               fp->frame_size = framesize;
+               ret = parse_audio_format_rates_v2(chip, fp, iface);
+               break;
+       }
+       }
+
+       return ret;
+}
+
+int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
+                      int format, unsigned char *fmt, int stream,
+                      struct usb_host_interface *iface)
+{
+       int err;
+
+       switch (fmt[3]) {
+       case UAC_FORMAT_TYPE_I:
+       case UAC_FORMAT_TYPE_III:
+               err = parse_audio_format_i(chip, fp, format, fmt, iface);
+               break;
+       case UAC_FORMAT_TYPE_II:
+               err = parse_audio_format_ii(chip, fp, format, fmt, iface);
+               break;
+       default:
+               snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
+                          chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]);
+               return -1;
+       }
+       fp->fmt_type = fmt[3];
+       if (err < 0)
+               return err;
+#if 1
+       /* FIXME: temporary hack for extigy/audigy 2 nx/zs */
+       /* extigy apparently supports sample rates other than 48k
+        * but not in ordinary way.  so we enable only 48k atm.
+        */
+       if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
+           chip->usb_id == USB_ID(0x041e, 0x3020) ||
+           chip->usb_id == USB_ID(0x041e, 0x3061)) {
+               if (fmt[3] == UAC_FORMAT_TYPE_I &&
+                   fp->rates != SNDRV_PCM_RATE_48000 &&
+                   fp->rates != SNDRV_PCM_RATE_96000)
+                       return -1;
+       }
+#endif
+       return 0;
+}
+
diff --git a/sound/usb/format.h b/sound/usb/format.h
new file mode 100644 (file)
index 0000000..8298c4e
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __USBAUDIO_FORMAT_H
+#define __USBAUDIO_FORMAT_H
+
+int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
+                              int format, unsigned char *fmt, int stream,
+                              struct usb_host_interface *iface);
+
+#endif /*  __USBAUDIO_FORMAT_H */
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
new file mode 100644 (file)
index 0000000..ba7dba4
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+
+#include "usbaudio.h"
+#include "helper.h"
+
+/*
+ * combine bytes and get an integer value
+ */
+unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size)
+{
+       switch (size) {
+       case 1:  return *bytes;
+       case 2:  return combine_word(bytes);
+       case 3:  return combine_triple(bytes);
+       case 4:  return combine_quad(bytes);
+       default: return 0;
+       }
+}
+
+/*
+ * parse descriptor buffer and return the pointer starting the given
+ * descriptor type.
+ */
+void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype)
+{
+       u8 *p, *end, *next;
+
+       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] == dtype && (!after || (void *)p > after)) {
+                       return p;
+               }
+               p = next;
+       }
+       return NULL;
+}
+
+/*
+ * find a class-specified interface descriptor with the given subtype.
+ */
+void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype)
+{
+       unsigned char *p = after;
+
+       while ((p = snd_usb_find_desc(buffer, buflen, p,
+                                     USB_DT_CS_INTERFACE)) != NULL) {
+               if (p[0] >= 3 && p[2] == dsubtype)
+                       return p;
+       }
+       return NULL;
+}
+
+/*
+ * Wrapper for usb_control_msg().
+ * Allocates a temp buffer to prevent dmaing from/to the stack.
+ */
+int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
+                   __u8 requesttype, __u16 value, __u16 index, void *data,
+                   __u16 size, int timeout)
+{
+       int err;
+       void *buf = NULL;
+
+       if (size > 0) {
+               buf = kmemdup(data, size, GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
+       }
+       err = usb_control_msg(dev, pipe, request, requesttype,
+                             value, index, buf, size, timeout);
+       if (size > 0) {
+               memcpy(data, buf, size);
+               kfree(buf);
+       }
+       return err;
+}
+
+unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
+                                        struct usb_host_interface *alts)
+{
+       if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH &&
+           get_endpoint(alts, 0)->bInterval >= 1 &&
+           get_endpoint(alts, 0)->bInterval <= 4)
+               return get_endpoint(alts, 0)->bInterval - 1;
+       else
+               return 0;
+}
+
diff --git a/sound/usb/helper.h b/sound/usb/helper.h
new file mode 100644 (file)
index 0000000..a6b0e51
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __USBAUDIO_HELPER_H
+#define __USBAUDIO_HELPER_H
+
+unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size);
+
+void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype);
+void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype);
+
+int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
+                   __u8 request, __u8 requesttype, __u16 value, __u16 index,
+                   void *data, __u16 size, int timeout);
+
+unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
+                                        struct usb_host_interface *alts);
+
+/*
+ * retrieve usb_interface descriptor from the host interface
+ * (conditional for compatibility with the older API)
+ */
+#ifndef get_iface_desc
+#define get_iface_desc(iface)  (&(iface)->desc)
+#define get_endpoint(alt,ep)   (&(alt)->endpoint[ep].desc)
+#define get_ep_desc(ep)                (&(ep)->desc)
+#define get_cfg_desc(cfg)      (&(cfg)->desc)
+#endif
+
+#ifndef snd_usb_get_speed
+#define snd_usb_get_speed(dev) ((dev)->speed)
+#endif
+
+
+#endif /* __USBAUDIO_HELPER_H */
similarity index 99%
rename from sound/usb/usbmidi.c
rename to sound/usb/midi.c
index 2c59afd99611b363651d77afe0e0f2ef26be0ebb..c6ee4a18e513c43aff45a127a00eb9f30c947cc0 100644 (file)
@@ -53,7 +53,8 @@
 #include <sound/rawmidi.h>
 #include <sound/asequencer.h>
 #include "usbaudio.h"
-
+#include "midi.h"
+#include "helper.h"
 
 /*
  * define this to log all USB packets
diff --git a/sound/usb/midi.h b/sound/usb/midi.h
new file mode 100644 (file)
index 0000000..2089ec9
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef __USBMIDI_H
+#define __USBMIDI_H
+
+/* maximum number of endpoints per interface */
+#define MIDI_MAX_ENDPOINTS 2
+
+/* data for QUIRK_MIDI_FIXED_ENDPOINT */
+struct snd_usb_midi_endpoint_info {
+       int8_t   out_ep;        /* ep number, 0 autodetect */
+       uint8_t  out_interval;  /* interval for interrupt endpoints */
+       int8_t   in_ep;
+       uint8_t  in_interval;
+       uint16_t out_cables;    /* bitmask */
+       uint16_t in_cables;     /* bitmask */
+};
+
+/* for QUIRK_MIDI_YAMAHA, data is NULL */
+
+/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info
+ * structure (out_cables and in_cables only) */
+
+/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk
+ * structures, terminated with .ifnum = -1 */
+
+/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */
+
+/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */
+
+/* for QUIRK_AUDIO_EDIROL_UA700_UA25/UA1000, data is NULL */
+
+/* for QUIRK_IGNORE_INTERFACE, data is NULL */
+
+/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */
+
+/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info
+ * structure (out_cables and in_cables only) */
+
+/* for QUIRK_MIDI_CME, data is NULL */
+
+int snd_usbmidi_create(struct snd_card *card,
+                      struct usb_interface *iface,
+                      struct list_head *midi_list,
+                      const struct snd_usb_audio_quirk *quirk);
+void snd_usbmidi_input_stop(struct list_head* p);
+void snd_usbmidi_input_start(struct list_head* p);
+void snd_usbmidi_disconnect(struct list_head *p);
+
+#endif /* __USBMIDI_H */
diff --git a/sound/usb/misc/Makefile b/sound/usb/misc/Makefile
new file mode 100644 (file)
index 0000000..ccefd81
--- /dev/null
@@ -0,0 +1,2 @@
+snd-ua101-objs := ua101.o
+obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o
similarity index 99%
rename from sound/usb/ua101.c
rename to sound/usb/misc/ua101.c
index 3d458d3b99621209905c60213f2b86ad3f7aaa9b..796d8b25ee8959a91377361359f9cb2111a45a8f 100644 (file)
@@ -23,7 +23,8 @@
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include "usbaudio.h"
+#include "../usbaudio.h"
+#include "../midi.h"
 
 MODULE_DESCRIPTION("Edirol UA-101/1000 driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
similarity index 72%
rename from sound/usb/usbmixer.c
rename to sound/usb/mixer.c
index 8e8f871b74ca477dc26053b5b0dbf74136d3bdc0..1deef623c0810e36af237893e264d8813046848a 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/string.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
 
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/tlv.h>
 
 #include "usbaudio.h"
-
-/*
- */
-
-/* ignore error from controls - for debugging */
-/* #define IGNORE_CTL_ERROR */
-
-/*
- * Sound Blaster remote control configuration
- *
- * format of remote control data:
- * Extigy:       xx 00
- * Audigy 2 NX:  06 80 xx 00 00 00
- * Live! 24-bit: 06 80 xx yy 22 83
- */
-static const struct rc_config {
-       u32 usb_id;
-       u8  offset;
-       u8  length;
-       u8  packet_length;
-       u8  min_packet_length; /* minimum accepted length of the URB result */
-       u8  mute_mixer_id;
-       u32 mute_code;
-} rc_configs[] = {
-       { USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
-       { USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
-       { USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
-       { USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
-};
+#include "mixer.h"
+#include "helper.h"
+#include "mixer_quirks.h"
 
 #define MAX_ID_ELEMS   256
 
-struct usb_mixer_interface {
-       struct snd_usb_audio *chip;
-       unsigned int ctrlif;
-       struct list_head list;
-       unsigned int ignore_ctl_error;
-       struct urb *urb;
-       /* array[MAX_ID_ELEMS], indexed by unit id */
-       struct usb_mixer_elem_info **id_elems;
-
-       /* Sound Blaster remote control stuff */
-       const struct rc_config *rc_cfg;
-       u32 rc_code;
-       wait_queue_head_t rc_waitq;
-       struct urb *rc_urb;
-       struct usb_ctrlrequest *rc_setup_packet;
-       u8 rc_buffer[6];
-
-       u8 audigy2nx_leds[3];
-       u8 xonar_u1_status;
-};
-
-
 struct usb_audio_term {
        int id;
        int type;
@@ -116,39 +69,6 @@ struct mixer_build {
        const struct usbmix_selector_map *selector_map;
 };
 
-#define MAX_CHANNELS   10      /* max logical channels */
-
-struct usb_mixer_elem_info {
-       struct usb_mixer_interface *mixer;
-       struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
-       struct snd_ctl_elem_id *elem_id;
-       unsigned int id;
-       unsigned int control;   /* CS or ICN (high byte) */
-       unsigned int cmask; /* channel mask bitmap: 0 = master */
-       int channels;
-       int val_type;
-       int min, max, res;
-       int dBmin, dBmax;
-       int cached;
-       int cache_val[MAX_CHANNELS];
-       u8 initialized;
-};
-
-
-enum {
-       USB_FEATURE_NONE = 0,
-       USB_FEATURE_MUTE = 1,
-       USB_FEATURE_VOLUME,
-       USB_FEATURE_BASS,
-       USB_FEATURE_MID,
-       USB_FEATURE_TREBLE,
-       USB_FEATURE_GEQ,
-       USB_FEATURE_AGC,
-       USB_FEATURE_DELAY,
-       USB_FEATURE_BASSBOOST,
-       USB_FEATURE_LOUDNESS
-};
-
 enum {
        USB_MIXER_BOOLEAN,
        USB_MIXER_INV_BOOLEAN,
@@ -213,7 +133,7 @@ enum {
  * if the mixer topology is too complicated and the parsed names are
  * ambiguous, add the entries in usbmixer_maps.c.
  */
-#include "usbmixer_maps.c"
+#include "mixer_maps.c"
 
 static const struct usbmix_name_map *
 find_map(struct mixer_build *state, int unitid, int control)
@@ -278,6 +198,7 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid,
 
 /*
  * find an audio control unit with the given unit id
+ * this doesn't return any clock related units, so they need to be handled elsewhere
  */
 static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit)
 {
@@ -286,7 +207,7 @@ static void *find_audio_control_unit(struct mixer_build *state, unsigned char un
        p = NULL;
        while ((p = snd_usb_find_desc(state->buffer, state->buflen, p,
                                      USB_DT_CS_INTERFACE)) != NULL) {
-               if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC_EXTENSION_UNIT_V1 && p[3] == unit)
+               if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC2_EXTENSION_UNIT_V2 && p[3] == unit)
                        return p;
        }
        return NULL;
@@ -383,7 +304,7 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
  * retrieve a mixer value
  */
 
-static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
 {
        unsigned char buf[2];
        int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
@@ -405,6 +326,58 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali
        return -EINVAL;
 }
 
+static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+{
+       unsigned char buf[14]; /* enough space for one range of 4 bytes */
+       unsigned char *val;
+       int ret;
+       __u8 bRequest;
+
+       bRequest = (request == UAC_GET_CUR) ?
+               UAC2_CS_CUR : UAC2_CS_RANGE;
+
+       ret = snd_usb_ctl_msg(cval->mixer->chip->dev,
+                             usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
+                             bRequest,
+                             USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+                             validx, cval->mixer->ctrlif | (cval->id << 8),
+                             buf, sizeof(buf), 1000);
+
+       if (ret < 0) {
+               snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
+                           request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
+               return ret;
+       }
+
+       switch (request) {
+       case UAC_GET_CUR:
+               val = buf;
+               break;
+       case UAC_GET_MIN:
+               val = buf + sizeof(__u16);
+               break;
+       case UAC_GET_MAX:
+               val = buf + sizeof(__u16) * 2;
+               break;
+       case UAC_GET_RES:
+               val = buf + sizeof(__u16) * 3;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(val, sizeof(__u16)));
+
+       return 0;
+}
+
+static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+{
+       return (cval->mixer->protocol == UAC_VERSION_1) ?
+               get_ctl_value_v1(cval, request, validx, value_ret) :
+               get_ctl_value_v2(cval, request, validx, value_ret);
+}
+
 static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value)
 {
        return get_ctl_value(cval, UAC_GET_CUR, validx, value);
@@ -429,8 +402,7 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
        err = get_cur_mix_raw(cval, channel, value);
        if (err < 0) {
                if (!cval->mixer->ignore_ctl_error)
-                       snd_printd(KERN_ERR "cannot get current value for "
-                                  "control %d ch %d: err = %d\n",
+                       snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n",
                                   cval->control, channel, err);
                return err;
        }
@@ -444,11 +416,26 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
  * set a mixer value
  */
 
-static int set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set)
+int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
+                               int request, int validx, int value_set)
 {
        unsigned char buf[2];
-       int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
-       int timeout = 10;
+       int val_len, timeout = 10;
+
+       if (cval->mixer->protocol == UAC_VERSION_1) {
+               val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
+       } else { /* UAC_VERSION_2 */
+               /* audio class v2 controls are always 2 bytes in size */
+               val_len = sizeof(__u16);
+
+               /* FIXME */
+               if (request != UAC_SET_CUR) {
+                       snd_printdd(KERN_WARNING "RANGE setting not yet supported\n");
+                       return -EINVAL;
+               }
+
+               request = UAC2_CS_CUR;
+       }
 
        value_set = convert_bytes_value(cval, value_set);
        buf[0] = value_set & 0xff;
@@ -468,14 +455,14 @@ static int set_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali
 
 static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value)
 {
-       return set_ctl_value(cval, UAC_SET_CUR, validx, value);
+       return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value);
 }
 
 static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
                             int index, int value)
 {
        int err;
-       err = set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
+       err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
                            value);
        if (err < 0)
                return err;
@@ -644,46 +631,65 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
  */
 static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term)
 {
-       unsigned char *p1;
+       void *p1;
 
        memset(term, 0, sizeof(*term));
        while ((p1 = find_audio_control_unit(state, id)) != NULL) {
+               unsigned char *hdr = p1;
                term->id = id;
-               switch (p1[2]) {
+               switch (hdr[2]) {
                case UAC_INPUT_TERMINAL:
-                       term->type = combine_word(p1 + 4);
-                       term->channels = p1[7];
-                       term->chconfig = combine_word(p1 + 8);
-                       term->name = p1[11];
+                       if (state->mixer->protocol == UAC_VERSION_1) {
+                               struct uac_input_terminal_descriptor *d = p1;
+                               term->type = le16_to_cpu(d->wTerminalType);
+                               term->channels = d->bNrChannels;
+                               term->chconfig = le16_to_cpu(d->wChannelConfig);
+                               term->name = d->iTerminal;
+                       } else { /* UAC_VERSION_2 */
+                               struct uac2_input_terminal_descriptor *d = p1;
+                               term->type = le16_to_cpu(d->wTerminalType);
+                               term->channels = d->bNrChannels;
+                               term->chconfig = le32_to_cpu(d->bmChannelConfig);
+                               term->name = d->iTerminal;
+                       }
                        return 0;
-               case UAC_FEATURE_UNIT:
-                       id = p1[4];
+               case UAC_FEATURE_UNIT: {
+                       /* the header is the same for v1 and v2 */
+                       struct uac_feature_unit_descriptor *d = p1;
+                       id = d->bUnitID;
                        break; /* continue to parse */
-               case UAC_MIXER_UNIT:
-                       term->type = p1[2] << 16; /* virtual type */
-                       term->channels = p1[5 + p1[4]];
-                       term->chconfig = combine_word(p1 + 6 + p1[4]);
-                       term->name = p1[p1[0] - 1];
+               }
+               case UAC_MIXER_UNIT: {
+                       struct uac_mixer_unit_descriptor *d = p1;
+                       term->type = d->bDescriptorSubtype << 16; /* virtual type */
+                       term->channels = uac_mixer_unit_bNrChannels(d);
+                       term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->protocol);
+                       term->name = uac_mixer_unit_iMixer(d);
                        return 0;
-               case UAC_SELECTOR_UNIT:
+               }
+               case UAC_SELECTOR_UNIT: {
+                       struct uac_selector_unit_descriptor *d = p1;
                        /* call recursively to retrieve the channel info */
-                       if (check_input_term(state, p1[5], term) < 0)
+                       if (check_input_term(state, d->baSourceID[0], term) < 0)
                                return -ENODEV;
-                       term->type = p1[2] << 16; /* virtual type */
+                       term->type = d->bDescriptorSubtype << 16; /* virtual type */
                        term->id = id;
-                       term->name = p1[9 + p1[0] - 1];
+                       term->name = uac_selector_unit_iSelector(d);
                        return 0;
+               }
                case UAC_PROCESSING_UNIT_V1:
-               case UAC_EXTENSION_UNIT_V1:
-                       if (p1[6] == 1) {
-                               id = p1[7];
+               case UAC_EXTENSION_UNIT_V1: {
+                       struct uac_processing_unit_descriptor *d = p1;
+                       if (d->bNrInPins) {
+                               id = d->baSourceID[0];
                                break; /* continue to parse */
                        }
-                       term->type = p1[2] << 16; /* virtual type */
-                       term->channels = p1[7 + p1[6]];
-                       term->chconfig = combine_word(p1 + 8 + p1[6]);
-                       term->name = p1[12 + p1[6] + p1[11 + p1[6]]];
+                       term->type = d->bDescriptorSubtype << 16; /* virtual type */
+                       term->channels = uac_processing_unit_bNrChannels(d);
+                       term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol);
+                       term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
                        return 0;
+               }
                default:
                        return -ENODEV;
                }
@@ -764,7 +770,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
                        int last_valid_res = cval->res;
 
                        while (cval->res > 1) {
-                               if (set_ctl_value(cval, UAC_SET_RES, (cval->control << 8) | minchn, cval->res / 2) < 0)
+                               if (snd_usb_mixer_set_ctl_value(cval, UAC_SET_RES,
+                                                               (cval->control << 8) | minchn, cval->res / 2) < 0)
                                        break;
                                cval->res /= 2;
                        }
@@ -929,6 +936,15 @@ static struct snd_kcontrol_new usb_feature_unit_ctl = {
        .put = mixer_ctl_feature_put,
 };
 
+/* the read-only variant */
+static struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "", /* will be filled later manually */
+       .info = mixer_ctl_feature_info,
+       .get = mixer_ctl_feature_get,
+       .put = NULL,
+};
+
 
 /*
  * build a feature control
@@ -939,20 +955,22 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str)
        return strlcat(kctl->id.name, str, sizeof(kctl->id.name));
 }
 
-static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
+static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
                              unsigned int ctl_mask, int control,
-                             struct usb_audio_term *iterm, int unitid)
+                             struct usb_audio_term *iterm, int unitid,
+                             int read_only)
 {
+       struct uac_feature_unit_descriptor *desc = raw_desc;
        unsigned int len = 0;
        int mapped_name = 0;
-       int nameid = desc[desc[0] - 1];
+       int nameid = uac_feature_unit_iFeature(desc);
        struct snd_kcontrol *kctl;
        struct usb_mixer_elem_info *cval;
        const struct usbmix_name_map *map;
 
        control++; /* change from zero-based to 1-based value */
 
-       if (control == USB_FEATURE_GEQ) {
+       if (control == UAC_GRAPHIC_EQUALIZER_CONTROL) {
                /* FIXME: not supported yet */
                return;
        }
@@ -984,7 +1002,11 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
        /* get min/max values */
        get_min_max(cval, 0);
 
-       kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+       if (read_only)
+               kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
+       else
+               kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+
        if (! kctl) {
                snd_printk(KERN_ERR "cannot malloc kcontrol\n");
                kfree(cval);
@@ -999,8 +1021,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
                                kctl->id.name, sizeof(kctl->id.name));
 
        switch (control) {
-       case USB_FEATURE_MUTE:
-       case USB_FEATURE_VOLUME:
+       case UAC_MUTE_CONTROL:
+       case UAC_VOLUME_CONTROL:
                /* determine the control name.  the rule is:
                 * - if a name id is given in descriptor, use it.
                 * - if the connected input can be determined, then use the name
@@ -1027,9 +1049,9 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
                                len = append_ctl_name(kctl, " Playback");
                        }
                }
-               append_ctl_name(kctl, control == USB_FEATURE_MUTE ?
+               append_ctl_name(kctl, control == UAC_MUTE_CONTROL ?
                                " Switch" : " Volume");
-               if (control == USB_FEATURE_VOLUME) {
+               if (control == UAC_VOLUME_CONTROL) {
                        kctl->tlv.c = mixer_vol_tlv;
                        kctl->vd[0].access |= 
                                SNDRV_CTL_ELEM_ACCESS_TLV_READ |
@@ -1094,49 +1116,92 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
        struct usb_audio_term iterm;
        unsigned int master_bits, first_ch_bits;
        int err, csize;
-       struct uac_feature_unit_descriptor *ftr = _ftr;
+       struct uac_feature_unit_descriptor *hdr = _ftr;
+       __u8 *bmaControls;
+
+       if (state->mixer->protocol == UAC_VERSION_1) {
+               csize = hdr->bControlSize;
+               channels = (hdr->bLength - 7) / csize - 1;
+               bmaControls = hdr->bmaControls;
+       } else {
+               struct uac2_feature_unit_descriptor *ftr = _ftr;
+               csize = 4;
+               channels = (hdr->bLength - 6) / 4;
+               bmaControls = ftr->bmaControls;
+       }
 
-       if (ftr->bLength < 7 || ! (csize = ftr->bControlSize) || ftr->bLength < 7 + csize) {
+       if (hdr->bLength < 7 || !csize || hdr->bLength < 7 + csize) {
                snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid);
                return -EINVAL;
        }
 
        /* parse the source unit */
-       if ((err = parse_audio_unit(state, ftr->bSourceID)) < 0)
+       if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0)
                return err;
 
        /* determine the input source type and name */
-       if (check_input_term(state, ftr->bSourceID, &iterm) < 0)
+       if (check_input_term(state, hdr->bSourceID, &iterm) < 0)
                return -EINVAL;
 
-       channels = (ftr->bLength - 7) / csize - 1;
-
-       master_bits = snd_usb_combine_bytes(ftr->controls, csize);
+       master_bits = snd_usb_combine_bytes(bmaControls, csize);
        /* master configuration quirks */
        switch (state->chip->usb_id) {
        case USB_ID(0x08bb, 0x2702):
                snd_printk(KERN_INFO
                           "usbmixer: master volume quirk for PCM2702 chip\n");
                /* disable non-functional volume control */
-               master_bits &= ~(1 << (USB_FEATURE_VOLUME - 1));
+               master_bits &= ~UAC_FU_VOLUME;
                break;
        }
        if (channels > 0)
-               first_ch_bits = snd_usb_combine_bytes(ftr->controls + csize, csize);
+               first_ch_bits = snd_usb_combine_bytes(bmaControls + csize, csize);
        else
                first_ch_bits = 0;
-       /* check all control types */
-       for (i = 0; i < 10; i++) {
-               unsigned int ch_bits = 0;
-               for (j = 0; j < channels; j++) {
-                       unsigned int mask = snd_usb_combine_bytes(ftr->controls + csize * (j+1), csize);
-                       if (mask & (1 << i))
-                               ch_bits |= (1 << j);
+
+       if (state->mixer->protocol == UAC_VERSION_1) {
+               /* check all control types */
+               for (i = 0; i < 10; i++) {
+                       unsigned int ch_bits = 0;
+                       for (j = 0; j < channels; j++) {
+                               unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+                               if (mask & (1 << i))
+                                       ch_bits |= (1 << j);
+                       }
+                       /* audio class v1 controls are never read-only */
+                       if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
+                               build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, 0);
+                       if (master_bits & (1 << i))
+                               build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0);
+               }
+       } else { /* UAC_VERSION_2 */
+               for (i = 0; i < 30/2; i++) {
+                       /* From the USB Audio spec v2.0:
+                          bmaControls() is a (ch+1)-element array of 4-byte bitmaps,
+                          each containing a set of bit pairs. If a Control is present,
+                          it must be Host readable. If a certain Control is not
+                          present then the bit pair must be set to 0b00.
+                          If a Control is present but read-only, the bit pair must be
+                          set to 0b01. If a Control is also Host programmable, the bit
+                          pair must be set to 0b11. The value 0b10 is not allowed. */
+                       unsigned int ch_bits = 0;
+                       unsigned int ch_read_only = 0;
+
+                       for (j = 0; j < channels; j++) {
+                               unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+                               if (mask & (1 << (i * 2))) {
+                                       ch_bits |= (1 << j);
+                                       if (~mask & (1 << ((i * 2) + 1)))
+                                               ch_read_only |= (1 << j);
+                               }
+                       }
+
+                       /* FIXME: the whole unit is read-only if any of the channels is marked read-only */
+                       if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
+                               build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, !!ch_read_only);
+                       if (master_bits & (1 << i * 2))
+                               build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
+                                                 ~master_bits & (1 << ((i * 2) + 1)));
                }
-               if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
-                       build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid);
-               if (master_bits & (1 << i))
-                       build_feature_ctl(state, _ftr, 0, i, &iterm, unitid);
        }
 
        return 0;
@@ -1154,13 +1219,13 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
  * input channel number (zero based) is given in control field instead.
  */
 
-static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
+static void build_mixer_unit_ctl(struct mixer_build *state,
+                                struct uac_mixer_unit_descriptor *desc,
                                 int in_pin, int in_ch, int unitid,
                                 struct usb_audio_term *iterm)
 {
        struct usb_mixer_elem_info *cval;
-       unsigned int input_pins = desc[4];
-       unsigned int num_outs = desc[5 + input_pins];
+       unsigned int num_outs = uac_mixer_unit_bNrChannels(desc);
        unsigned int i, len;
        struct snd_kcontrol *kctl;
        const struct usbmix_name_map *map;
@@ -1178,7 +1243,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
        cval->control = in_ch + 1; /* based on 1 */
        cval->val_type = USB_MIXER_S16;
        for (i = 0; i < num_outs; i++) {
-               if (check_matrix_bitmap(desc + 9 + input_pins, in_ch, i, num_outs)) {
+               if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol), in_ch, i, num_outs)) {
                        cval->cmask |= (1 << i);
                        cval->channels++;
                }
@@ -1211,18 +1276,19 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
 /*
  * parse a mixer unit
  */
-static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
+       struct uac_mixer_unit_descriptor *desc = raw_desc;
        struct usb_audio_term iterm;
        int input_pins, num_ins, num_outs;
        int pin, ich, err;
 
-       if (desc[0] < 11 || ! (input_pins = desc[4]) || ! (num_outs = desc[5 + input_pins])) {
+       if (desc->bLength < 11 || ! (input_pins = desc->bNrInPins) || ! (num_outs = uac_mixer_unit_bNrChannels(desc))) {
                snd_printk(KERN_ERR "invalid MIXER UNIT descriptor %d\n", unitid);
                return -EINVAL;
        }
        /* no bmControls field (e.g. Maya44) -> ignore */
-       if (desc[0] <= 10 + input_pins) {
+       if (desc->bLength <= 10 + input_pins) {
                snd_printdd(KERN_INFO "MU %d has no bmControls field\n", unitid);
                return 0;
        }
@@ -1230,10 +1296,10 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigne
        num_ins = 0;
        ich = 0;
        for (pin = 0; pin < input_pins; pin++) {
-               err = parse_audio_unit(state, desc[5 + pin]);
+               err = parse_audio_unit(state, desc->baSourceID[pin]);
                if (err < 0)
                        return err;
-               err = check_input_term(state, desc[5 + pin], &iterm);
+               err = check_input_term(state, desc->baSourceID[pin], &iterm);
                if (err < 0)
                        return err;
                num_ins += iterm.channels;
@@ -1241,7 +1307,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigne
                        int och, ich_has_controls = 0;
 
                        for (och = 0; och < num_outs; ++och) {
-                               if (check_matrix_bitmap(desc + 9 + input_pins,
+                               if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol),
                                                        ich, och, num_outs)) {
                                        ich_has_controls = 1;
                                        break;
@@ -1402,9 +1468,10 @@ static struct procunit_info extunits[] = {
 /*
  * build a processing/extension unit
  */
-static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned char *dsc, struct procunit_info *list, char *name)
+static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw_desc, struct procunit_info *list, char *name)
 {
-       int num_ins = dsc[6];
+       struct uac_processing_unit_descriptor *desc = raw_desc;
+       int num_ins = desc->bNrInPins;
        struct usb_mixer_elem_info *cval;
        struct snd_kcontrol *kctl;
        int i, err, nameid, type, len;
@@ -1419,17 +1486,18 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
                0, NULL, default_value_info
        };
 
-       if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) {
+       if (desc->bLength < 13 || desc->bLength < 13 + num_ins ||
+           desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) {
                snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid);
                return -EINVAL;
        }
 
        for (i = 0; i < num_ins; i++) {
-               if ((err = parse_audio_unit(state, dsc[7 + i])) < 0)
+               if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0)
                        return err;
        }
 
-       type = combine_word(&dsc[4]);
+       type = le16_to_cpu(desc->wProcessType);
        for (info = list; info && info->type; info++)
                if (info->type == type)
                        break;
@@ -1437,8 +1505,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
                info = &default_info;
 
        for (valinfo = info->values; valinfo->control; valinfo++) {
-               /* FIXME: bitmap might be longer than 8bit */
-               if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
+               __u8 *controls = uac_processing_unit_bmControls(desc, state->mixer->protocol);
+
+               if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
                        continue;
                map = find_map(state, unitid, valinfo->control);
                if (check_ignored_ctl(map))
@@ -1456,9 +1525,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 
                /* get min/max values */
                if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) {
+                       __u8 *control_spec = uac_processing_unit_specific(desc, state->mixer->protocol);
                        /* FIXME: hard-coded */
                        cval->min = 1;
-                       cval->max = dsc[15];
+                       cval->max = control_spec[0];
                        cval->res = 1;
                        cval->initialized = 1;
                } else {
@@ -1488,7 +1558,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
                else if (info->name)
                        strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
                else {
-                       nameid = dsc[12 + num_ins + dsc[11 + num_ins]];
+                       nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol);
                        len = 0;
                        if (nameid)
                                len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
@@ -1507,14 +1577,16 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 }
 
 
-static int parse_audio_processing_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_processing_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
-       return build_audio_procunit(state, unitid, desc, procunits, "Processing Unit");
+       return build_audio_procunit(state, unitid, raw_desc, procunits, "Processing Unit");
 }
 
-static int parse_audio_extension_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_extension_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
-       return build_audio_procunit(state, unitid, desc, extunits, "Extension Unit");
+       /* Note that we parse extension units with processing unit descriptors.
+        * That's ok as the layout is the same */
+       return build_audio_procunit(state, unitid, raw_desc, extunits, "Extension Unit");
 }
 
 
@@ -1616,9 +1688,9 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
 /*
  * parse a selector unit
  */
-static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
-       unsigned int num_ins = desc[4];
+       struct uac_selector_unit_descriptor *desc = raw_desc;
        unsigned int i, nameid, len;
        int err;
        struct usb_mixer_elem_info *cval;
@@ -1626,17 +1698,17 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        const struct usbmix_name_map *map;
        char **namelist;
 
-       if (! num_ins || desc[0] < 5 + num_ins) {
+       if (!desc->bNrInPins || desc->bLength < 5 + desc->bNrInPins) {
                snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid);
                return -EINVAL;
        }
 
-       for (i = 0; i < num_ins; i++) {
-               if ((err = parse_audio_unit(state, desc[5 + i])) < 0)
+       for (i = 0; i < desc->bNrInPins; i++) {
+               if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0)
                        return err;
        }
 
-       if (num_ins == 1) /* only one ? nonsense! */
+       if (desc->bNrInPins == 1) /* only one ? nonsense! */
                return 0;
 
        map = find_map(state, unitid, 0);
@@ -1653,18 +1725,18 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        cval->val_type = USB_MIXER_U8;
        cval->channels = 1;
        cval->min = 1;
-       cval->max = num_ins;
+       cval->max = desc->bNrInPins;
        cval->res = 1;
        cval->initialized = 1;
 
-       namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL);
+       namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
        if (! namelist) {
                snd_printk(KERN_ERR "cannot malloc\n");
                kfree(cval);
                return -ENOMEM;
        }
 #define MAX_ITEM_NAME_LEN      64
-       for (i = 0; i < num_ins; i++) {
+       for (i = 0; i < desc->bNrInPins; i++) {
                struct usb_audio_term iterm;
                len = 0;
                namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
@@ -1678,7 +1750,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
                }
                len = check_mapped_selector_name(state, unitid, i, namelist[i],
                                                 MAX_ITEM_NAME_LEN);
-               if (! len && check_input_term(state, desc[5 + i], &iterm) >= 0)
+               if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0)
                        len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
                if (! len)
                        sprintf(namelist[i], "Input %d", i);
@@ -1694,7 +1766,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        kctl->private_value = (unsigned long)namelist;
        kctl->private_free = usb_mixer_selector_elem_free;
 
-       nameid = desc[desc[0] - 1];
+       nameid = uac_selector_unit_iSelector(desc);
        len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
        if (len)
                ;
@@ -1713,7 +1785,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        }
 
        snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n",
-                   cval->id, kctl->id.name, num_ins);
+                   cval->id, kctl->id.name, desc->bNrInPins);
        if ((err = add_control_to_empty(state, kctl)) < 0)
                return err;
 
@@ -1748,9 +1820,17 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
        case UAC_FEATURE_UNIT:
                return parse_audio_feature_unit(state, unitid, p1);
        case UAC_PROCESSING_UNIT_V1:
-               return parse_audio_processing_unit(state, unitid, p1);
+       /*   UAC2_EFFECT_UNIT has the same value */
+               if (state->mixer->protocol == UAC_VERSION_1)
+                       return parse_audio_processing_unit(state, unitid, p1);
+               else
+                       return 0; /* FIXME - effect units not implemented yet */
        case UAC_EXTENSION_UNIT_V1:
-               return parse_audio_extension_unit(state, unitid, p1);
+       /*   UAC2_PROCESSING_UNIT_V2 has the same value */
+               if (state->mixer->protocol == UAC_VERSION_1)
+                       return parse_audio_extension_unit(state, unitid, p1);
+               else /* UAC_VERSION_2 */
+                       return parse_audio_processing_unit(state, unitid, p1);
        default:
                snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
                return -EINVAL;
@@ -1783,11 +1863,11 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
  */
 static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 {
-       struct uac_output_terminal_descriptor_v1 *desc;
        struct mixer_build state;
        int err;
        const struct usbmix_ctl_map *map;
        struct usb_host_interface *hostif;
+       void *p;
 
        hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
        memset(&state, 0, sizeof(state));
@@ -1806,23 +1886,39 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
                }
        }
 
-       desc = NULL;
-       while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, UAC_OUTPUT_TERMINAL)) != NULL) {
-               if (desc->bLength < 9)
-                       continue; /* invalid descriptor? */
-               set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
-               state.oterm.id = desc->bTerminalID;
-               state.oterm.type = le16_to_cpu(desc->wTerminalType);
-               state.oterm.name = desc->iTerminal;
-               err = parse_audio_unit(&state, desc->bSourceID);
-               if (err < 0)
-                       return err;
+       p = NULL;
+       while ((p = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) {
+               if (mixer->protocol == UAC_VERSION_1) {
+                       struct uac_output_terminal_descriptor_v1 *desc = p;
+
+                       if (desc->bLength < sizeof(*desc))
+                               continue; /* invalid descriptor? */
+                       set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
+                       state.oterm.id = desc->bTerminalID;
+                       state.oterm.type = le16_to_cpu(desc->wTerminalType);
+                       state.oterm.name = desc->iTerminal;
+                       err = parse_audio_unit(&state, desc->bSourceID);
+                       if (err < 0)
+                               return err;
+               } else { /* UAC_VERSION_2 */
+                       struct uac2_output_terminal_descriptor *desc = p;
+
+                       if (desc->bLength < sizeof(*desc))
+                               continue; /* invalid descriptor? */
+                       set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
+                       state.oterm.id = desc->bTerminalID;
+                       state.oterm.type = le16_to_cpu(desc->wTerminalType);
+                       state.oterm.name = desc->iTerminal;
+                       err = parse_audio_unit(&state, desc->bSourceID);
+                       if (err < 0)
+                               return err;
+               }
        }
+
        return 0;
 }
 
-static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer,
-                                   int unitid)
+void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
 {
        struct usb_mixer_elem_info *info;
 
@@ -1871,34 +1967,6 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
        }
 }
 
-static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
-                                       int unitid)
-{
-       if (!mixer->rc_cfg)
-               return;
-       /* unit ids specific to Extigy/Audigy 2 NX: */
-       switch (unitid) {
-       case 0: /* remote control */
-               mixer->rc_urb->dev = mixer->chip->dev;
-               usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
-               break;
-       case 4: /* digital in jack */
-       case 7: /* line in jacks */
-       case 19: /* speaker out jacks */
-       case 20: /* headphones out jack */
-               break;
-       /* live24ext: 4 = line-in jack */
-       case 3: /* hp-out jack (may actuate Mute) */
-               if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-                   mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
-                       snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
-               break;
-       default:
-               snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
-               break;
-       }
-}
-
 static void snd_usb_mixer_status_complete(struct urb *urb)
 {
        struct usb_mixer_interface *mixer = urb->context;
@@ -1916,7 +1984,7 @@ static void snd_usb_mixer_status_complete(struct urb *urb)
                        if (!(buf[0] & 0x40))
                                snd_usb_mixer_notify_id(mixer, buf[1]);
                        else
-                               snd_usb_mixer_memory_change(mixer, buf[1]);
+                               snd_usb_mixer_rc_memory_change(mixer, buf[1]);
                }
        }
        if (urb->status != -ENOENT && urb->status != -ECONNRESET) {
@@ -1960,296 +2028,6 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
        return 0;
 }
 
-static void snd_usb_soundblaster_remote_complete(struct urb *urb)
-{
-       struct usb_mixer_interface *mixer = urb->context;
-       const struct rc_config *rc = mixer->rc_cfg;
-       u32 code;
-
-       if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
-               return;
-
-       code = mixer->rc_buffer[rc->offset];
-       if (rc->length == 2)
-               code |= mixer->rc_buffer[rc->offset + 1] << 8;
-
-       /* the Mute button actually changes the mixer control */
-       if (code == rc->mute_code)
-               snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
-       mixer->rc_code = code;
-       wmb();
-       wake_up(&mixer->rc_waitq);
-}
-
-static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
-                                    long count, loff_t *offset)
-{
-       struct usb_mixer_interface *mixer = hw->private_data;
-       int err;
-       u32 rc_code;
-
-       if (count != 1 && count != 4)
-               return -EINVAL;
-       err = wait_event_interruptible(mixer->rc_waitq,
-                                      (rc_code = xchg(&mixer->rc_code, 0)) != 0);
-       if (err == 0) {
-               if (count == 1)
-                       err = put_user(rc_code, buf);
-               else
-                       err = put_user(rc_code, (u32 __user *)buf);
-       }
-       return err < 0 ? err : count;
-}
-
-static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
-                                           poll_table *wait)
-{
-       struct usb_mixer_interface *mixer = hw->private_data;
-
-       poll_wait(file, &mixer->rc_waitq, wait);
-       return mixer->rc_code ? POLLIN | POLLRDNORM : 0;
-}
-
-static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
-{
-       struct snd_hwdep *hwdep;
-       int err, len, i;
-
-       for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
-               if (rc_configs[i].usb_id == mixer->chip->usb_id)
-                       break;
-       if (i >= ARRAY_SIZE(rc_configs))
-               return 0;
-       mixer->rc_cfg = &rc_configs[i];
-
-       len = mixer->rc_cfg->packet_length;
-       
-       init_waitqueue_head(&mixer->rc_waitq);
-       err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
-       if (err < 0)
-               return err;
-       snprintf(hwdep->name, sizeof(hwdep->name),
-                "%s remote control", mixer->chip->card->shortname);
-       hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
-       hwdep->private_data = mixer;
-       hwdep->ops.read = snd_usb_sbrc_hwdep_read;
-       hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
-       hwdep->exclusive = 1;
-
-       mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (!mixer->rc_urb)
-               return -ENOMEM;
-       mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
-       if (!mixer->rc_setup_packet) {
-               usb_free_urb(mixer->rc_urb);
-               mixer->rc_urb = NULL;
-               return -ENOMEM;
-       }
-       mixer->rc_setup_packet->bRequestType =
-               USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
-       mixer->rc_setup_packet->bRequest = UAC_GET_MEM;
-       mixer->rc_setup_packet->wValue = cpu_to_le16(0);
-       mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
-       mixer->rc_setup_packet->wLength = cpu_to_le16(len);
-       usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
-                            usb_rcvctrlpipe(mixer->chip->dev, 0),
-                            (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
-                            snd_usb_soundblaster_remote_complete, mixer);
-       return 0;
-}
-
-#define snd_audigy2nx_led_info         snd_ctl_boolean_mono_info
-
-static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       int index = kcontrol->private_value;
-
-       ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index];
-       return 0;
-}
-
-static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       int index = kcontrol->private_value;
-       int value = ucontrol->value.integer.value[0];
-       int err, changed;
-
-       if (value > 1)
-               return -EINVAL;
-       changed = value != mixer->audigy2nx_leds[index];
-       err = snd_usb_ctl_msg(mixer->chip->dev,
-                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
-                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-                             value, index + 2, NULL, 0, 100);
-       if (err < 0)
-               return err;
-       mixer->audigy2nx_leds[index] = value;
-       return changed;
-}
-
-static struct snd_kcontrol_new snd_audigy2nx_controls[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "CMSS LED Switch",
-               .info = snd_audigy2nx_led_info,
-               .get = snd_audigy2nx_led_get,
-               .put = snd_audigy2nx_led_put,
-               .private_value = 0,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Power LED Switch",
-               .info = snd_audigy2nx_led_info,
-               .get = snd_audigy2nx_led_get,
-               .put = snd_audigy2nx_led_put,
-               .private_value = 1,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Dolby Digital LED Switch",
-               .info = snd_audigy2nx_led_info,
-               .get = snd_audigy2nx_led_get,
-               .put = snd_audigy2nx_led_put,
-               .private_value = 2,
-       },
-};
-
-static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
-{
-       int i, err;
-
-       for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
-               if (i > 1 && /* Live24ext has 2 LEDs only */
-                       (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-                        mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
-                       break; 
-               err = snd_ctl_add(mixer->chip->card,
-                                 snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
-               if (err < 0)
-                       return err;
-       }
-       mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */
-       return 0;
-}
-
-static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
-                                   struct snd_info_buffer *buffer)
-{
-       static const struct sb_jack {
-               int unitid;
-               const char *name;
-       }  jacks_audigy2nx[] = {
-               {4,  "dig in "},
-               {7,  "line in"},
-               {19, "spk out"},
-               {20, "hph out"},
-               {-1, NULL}
-       }, jacks_live24ext[] = {
-               {4,  "line in"}, /* &1=Line, &2=Mic*/
-               {3,  "hph out"}, /* headphones */
-               {0,  "RC     "}, /* last command, 6 bytes see rc_config above */
-               {-1, NULL}
-       };
-       const struct sb_jack *jacks;
-       struct usb_mixer_interface *mixer = entry->private_data;
-       int i, err;
-       u8 buf[3];
-
-       snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
-       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
-               jacks = jacks_audigy2nx;
-       else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-                mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
-               jacks = jacks_live24ext;
-       else
-               return;
-
-       for (i = 0; jacks[i].name; ++i) {
-               snd_iprintf(buffer, "%s: ", jacks[i].name);
-               err = snd_usb_ctl_msg(mixer->chip->dev,
-                                     usb_rcvctrlpipe(mixer->chip->dev, 0),
-                                     UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
-                                     USB_RECIP_INTERFACE, 0,
-                                     jacks[i].unitid << 8, buf, 3, 100);
-               if (err == 3 && (buf[0] == 3 || buf[0] == 6))
-                       snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
-               else
-                       snd_iprintf(buffer, "?\n");
-       }
-}
-
-static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_value *ucontrol)
-{
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-
-       ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02);
-       return 0;
-}
-
-static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_value *ucontrol)
-{
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       u8 old_status, new_status;
-       int err, changed;
-
-       old_status = mixer->xonar_u1_status;
-       if (ucontrol->value.integer.value[0])
-               new_status = old_status | 0x02;
-       else
-               new_status = old_status & ~0x02;
-       changed = new_status != old_status;
-       err = snd_usb_ctl_msg(mixer->chip->dev,
-                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
-                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-                             50, 0, &new_status, 1, 100);
-       if (err < 0)
-               return err;
-       mixer->xonar_u1_status = new_status;
-       return changed;
-}
-
-static struct snd_kcontrol_new snd_xonar_u1_output_switch = {
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "Digital Playback Switch",
-       .info = snd_ctl_boolean_mono_info,
-       .get = snd_xonar_u1_switch_get,
-       .put = snd_xonar_u1_switch_put,
-};
-
-static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
-{
-       int err;
-
-       err = snd_ctl_add(mixer->chip->card,
-                         snd_ctl_new1(&snd_xonar_u1_output_switch, mixer));
-       if (err < 0)
-               return err;
-       mixer->xonar_u1_status = 0x05;
-       return 0;
-}
-
-void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
-                              unsigned char samplerate_id)
-{
-       struct usb_mixer_interface *mixer;
-       struct usb_mixer_elem_info *cval;
-       int unitid = 12; /* SamleRate ExtensionUnit ID */
-
-       list_for_each_entry(mixer, &chip->mixer_list, list) {
-               cval = mixer->id_elems[unitid];
-               if (cval) {
-                       set_cur_ctl_value(cval, cval->control << 8,
-                                         samplerate_id);
-                       snd_usb_mixer_notify_id(mixer, unitid);
-               }
-               break;
-       }
-}
-
 int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
                         int ignore_error)
 {
@@ -2259,7 +2037,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
        struct usb_mixer_interface *mixer;
        struct snd_info_entry *entry;
        struct usb_host_interface *host_iface;
-       int err, protocol;
+       int err;
 
        strcpy(chip->card->mixername, "USB Mixer");
 
@@ -2277,38 +2055,13 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
        }
 
        host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0];
-       protocol = host_iface->desc.bInterfaceProtocol;
-
-       /* FIXME! */
-       if (protocol != UAC_VERSION_1) {
-               snd_printk(KERN_WARNING "mixer interface protocol 0x%02x not yet supported\n",
-                                       protocol);
-               return 0;
-       }
+       mixer->protocol = host_iface->desc.bInterfaceProtocol;
 
        if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
            (err = snd_usb_mixer_status_create(mixer)) < 0)
                goto _error;
 
-       if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
-               goto _error;
-
-       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
-           mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-           mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
-               if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
-                       goto _error;
-               if (!snd_card_proc_new(chip->card, "audigy2nx", &entry))
-                       snd_info_set_text_ops(entry, mixer,
-                                             snd_audigy2nx_proc_read);
-       }
-
-       if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) ||
-           mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) {
-               err = snd_xonar_u1_controls_create(mixer);
-               if (err < 0)
-                       goto _error;
-       }
+       snd_usb_mixer_apply_create_quirk(mixer);
 
        err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);
        if (err < 0)
@@ -2329,7 +2082,7 @@ _error:
 void snd_usb_mixer_disconnect(struct list_head *p)
 {
        struct usb_mixer_interface *mixer;
-       
+
        mixer = list_entry(p, struct usb_mixer_interface, list);
        usb_kill_urb(mixer->urb);
        usb_kill_urb(mixer->rc_urb);
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
new file mode 100644 (file)
index 0000000..1301238
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __USBMIXER_H
+#define __USBMIXER_H
+
+struct usb_mixer_interface {
+       struct snd_usb_audio *chip;
+       unsigned int ctrlif;
+       struct list_head list;
+       unsigned int ignore_ctl_error;
+       struct urb *urb;
+       /* array[MAX_ID_ELEMS], indexed by unit id */
+       struct usb_mixer_elem_info **id_elems;
+
+       /* the usb audio specification version this interface complies to */
+       int protocol;
+
+       /* Sound Blaster remote control stuff */
+       const struct rc_config *rc_cfg;
+       u32 rc_code;
+       wait_queue_head_t rc_waitq;
+       struct urb *rc_urb;
+       struct usb_ctrlrequest *rc_setup_packet;
+       u8 rc_buffer[6];
+
+       u8 audigy2nx_leds[3];
+       u8 xonar_u1_status;
+};
+
+#define MAX_CHANNELS   10      /* max logical channels */
+
+struct usb_mixer_elem_info {
+       struct usb_mixer_interface *mixer;
+       struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
+       struct snd_ctl_elem_id *elem_id;
+       unsigned int id;
+       unsigned int control;   /* CS or ICN (high byte) */
+       unsigned int cmask; /* channel mask bitmap: 0 = master */
+       int channels;
+       int val_type;
+       int min, max, res;
+       int dBmin, dBmax;
+       int cached;
+       int cache_val[MAX_CHANNELS];
+       u8 initialized;
+};
+
+int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
+                        int ignore_error);
+void snd_usb_mixer_disconnect(struct list_head *p);
+
+void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid);
+
+int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
+                               int request, int validx, int value_set);
+
+#endif /* __USBMIXER_H */
similarity index 98%
rename from sound/usb/usbmixer_maps.c
rename to sound/usb/mixer_maps.c
index 79e903a6086207f24f60b3d8233f75187072b2ad..d93fc89beba8aee1040164bccff3935f0a78524d 100644 (file)
@@ -85,8 +85,8 @@ static struct usbmix_name_map extigy_map[] = {
        /* 16: MU (w/o controls) */
        { 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */
        { 17, "Channel Routing", 2 },   /* PU: mode select */
-       { 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */
-       { 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */
+       { 18, "Tone Control - Bass", UAC_BASS_CONTROL }, /* FU */
+       { 18, "Tone Control - Treble", UAC_TREBLE_CONTROL }, /* FU */
        { 18, "Master Playback" }, /* FU; others */
        /* 19: OT speaker */
        /* 20: OT headphone */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
new file mode 100644 (file)
index 0000000..56b6659
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ *   USB Audio Driver for ALSA
+ *
+ *   Quirks and vendor-specific extensions for mixer interfaces
+ *
+ *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   Many codes borrowed from audio.c by
+ *         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.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+
+#include "usbaudio.h"
+#include "mixer.h"
+#include "mixer_quirks.h"
+#include "helper.h"
+
+/*
+ * Sound Blaster remote control configuration
+ *
+ * format of remote control data:
+ * Extigy:       xx 00
+ * Audigy 2 NX:  06 80 xx 00 00 00
+ * Live! 24-bit: 06 80 xx yy 22 83
+ */
+static const struct rc_config {
+       u32 usb_id;
+       u8  offset;
+       u8  length;
+       u8  packet_length;
+       u8  min_packet_length; /* minimum accepted length of the URB result */
+       u8  mute_mixer_id;
+       u32 mute_code;
+} rc_configs[] = {
+       { USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
+       { USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
+       { USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
+       { USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
+};
+
+static void snd_usb_soundblaster_remote_complete(struct urb *urb)
+{
+       struct usb_mixer_interface *mixer = urb->context;
+       const struct rc_config *rc = mixer->rc_cfg;
+       u32 code;
+
+       if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
+               return;
+
+       code = mixer->rc_buffer[rc->offset];
+       if (rc->length == 2)
+               code |= mixer->rc_buffer[rc->offset + 1] << 8;
+
+       /* the Mute button actually changes the mixer control */
+       if (code == rc->mute_code)
+               snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
+       mixer->rc_code = code;
+       wmb();
+       wake_up(&mixer->rc_waitq);
+}
+
+static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
+                                    long count, loff_t *offset)
+{
+       struct usb_mixer_interface *mixer = hw->private_data;
+       int err;
+       u32 rc_code;
+
+       if (count != 1 && count != 4)
+               return -EINVAL;
+       err = wait_event_interruptible(mixer->rc_waitq,
+                                      (rc_code = xchg(&mixer->rc_code, 0)) != 0);
+       if (err == 0) {
+               if (count == 1)
+                       err = put_user(rc_code, buf);
+               else
+                       err = put_user(rc_code, (u32 __user *)buf);
+       }
+       return err < 0 ? err : count;
+}
+
+static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
+                                           poll_table *wait)
+{
+       struct usb_mixer_interface *mixer = hw->private_data;
+
+       poll_wait(file, &mixer->rc_waitq, wait);
+       return mixer->rc_code ? POLLIN | POLLRDNORM : 0;
+}
+
+static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
+{
+       struct snd_hwdep *hwdep;
+       int err, len, i;
+
+       for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
+               if (rc_configs[i].usb_id == mixer->chip->usb_id)
+                       break;
+       if (i >= ARRAY_SIZE(rc_configs))
+               return 0;
+       mixer->rc_cfg = &rc_configs[i];
+
+       len = mixer->rc_cfg->packet_length;
+
+       init_waitqueue_head(&mixer->rc_waitq);
+       err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
+       if (err < 0)
+               return err;
+       snprintf(hwdep->name, sizeof(hwdep->name),
+                "%s remote control", mixer->chip->card->shortname);
+       hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
+       hwdep->private_data = mixer;
+       hwdep->ops.read = snd_usb_sbrc_hwdep_read;
+       hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
+       hwdep->exclusive = 1;
+
+       mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!mixer->rc_urb)
+               return -ENOMEM;
+       mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
+       if (!mixer->rc_setup_packet) {
+               usb_free_urb(mixer->rc_urb);
+               mixer->rc_urb = NULL;
+               return -ENOMEM;
+       }
+       mixer->rc_setup_packet->bRequestType =
+               USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+       mixer->rc_setup_packet->bRequest = UAC_GET_MEM;
+       mixer->rc_setup_packet->wValue = cpu_to_le16(0);
+       mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
+       mixer->rc_setup_packet->wLength = cpu_to_le16(len);
+       usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
+                            usb_rcvctrlpipe(mixer->chip->dev, 0),
+                            (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
+                            snd_usb_soundblaster_remote_complete, mixer);
+       return 0;
+}
+
+#define snd_audigy2nx_led_info         snd_ctl_boolean_mono_info
+
+static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       int index = kcontrol->private_value;
+
+       ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index];
+       return 0;
+}
+
+static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       int index = kcontrol->private_value;
+       int value = ucontrol->value.integer.value[0];
+       int err, changed;
+
+       if (value > 1)
+               return -EINVAL;
+       changed = value != mixer->audigy2nx_leds[index];
+       err = snd_usb_ctl_msg(mixer->chip->dev,
+                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                             value, index + 2, NULL, 0, 100);
+       if (err < 0)
+               return err;
+       mixer->audigy2nx_leds[index] = value;
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_audigy2nx_controls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "CMSS LED Switch",
+               .info = snd_audigy2nx_led_info,
+               .get = snd_audigy2nx_led_get,
+               .put = snd_audigy2nx_led_put,
+               .private_value = 0,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Power LED Switch",
+               .info = snd_audigy2nx_led_info,
+               .get = snd_audigy2nx_led_get,
+               .put = snd_audigy2nx_led_put,
+               .private_value = 1,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Dolby Digital LED Switch",
+               .info = snd_audigy2nx_led_info,
+               .get = snd_audigy2nx_led_get,
+               .put = snd_audigy2nx_led_put,
+               .private_value = 2,
+       },
+};
+
+static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
+{
+       int i, err;
+
+       for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
+               if (i > 1 && /* Live24ext has 2 LEDs only */
+                       (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+                        mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
+                       break; 
+               err = snd_ctl_add(mixer->chip->card,
+                                 snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
+               if (err < 0)
+                       return err;
+       }
+       mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */
+       return 0;
+}
+
+static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
+                                   struct snd_info_buffer *buffer)
+{
+       static const struct sb_jack {
+               int unitid;
+               const char *name;
+       }  jacks_audigy2nx[] = {
+               {4,  "dig in "},
+               {7,  "line in"},
+               {19, "spk out"},
+               {20, "hph out"},
+               {-1, NULL}
+       }, jacks_live24ext[] = {
+               {4,  "line in"}, /* &1=Line, &2=Mic*/
+               {3,  "hph out"}, /* headphones */
+               {0,  "RC     "}, /* last command, 6 bytes see rc_config above */
+               {-1, NULL}
+       };
+       const struct sb_jack *jacks;
+       struct usb_mixer_interface *mixer = entry->private_data;
+       int i, err;
+       u8 buf[3];
+
+       snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
+       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
+               jacks = jacks_audigy2nx;
+       else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+                mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
+               jacks = jacks_live24ext;
+       else
+               return;
+
+       for (i = 0; jacks[i].name; ++i) {
+               snd_iprintf(buffer, "%s: ", jacks[i].name);
+               err = snd_usb_ctl_msg(mixer->chip->dev,
+                                     usb_rcvctrlpipe(mixer->chip->dev, 0),
+                                     UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
+                                     USB_RECIP_INTERFACE, 0,
+                                     jacks[i].unitid << 8, buf, 3, 100);
+               if (err == 3 && (buf[0] == 3 || buf[0] == 6))
+                       snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
+               else
+                       snd_iprintf(buffer, "?\n");
+       }
+}
+
+static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02);
+       return 0;
+}
+
+static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       u8 old_status, new_status;
+       int err, changed;
+
+       old_status = mixer->xonar_u1_status;
+       if (ucontrol->value.integer.value[0])
+               new_status = old_status | 0x02;
+       else
+               new_status = old_status & ~0x02;
+       changed = new_status != old_status;
+       err = snd_usb_ctl_msg(mixer->chip->dev,
+                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
+                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                             50, 0, &new_status, 1, 100);
+       if (err < 0)
+               return err;
+       mixer->xonar_u1_status = new_status;
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_xonar_u1_output_switch = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Digital Playback Switch",
+       .info = snd_ctl_boolean_mono_info,
+       .get = snd_xonar_u1_switch_get,
+       .put = snd_xonar_u1_switch_put,
+};
+
+static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
+{
+       int err;
+
+       err = snd_ctl_add(mixer->chip->card,
+                         snd_ctl_new1(&snd_xonar_u1_output_switch, mixer));
+       if (err < 0)
+               return err;
+       mixer->xonar_u1_status = 0x05;
+       return 0;
+}
+
+void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
+                              unsigned char samplerate_id)
+{
+       struct usb_mixer_interface *mixer;
+       struct usb_mixer_elem_info *cval;
+       int unitid = 12; /* SamleRate ExtensionUnit ID */
+
+       list_for_each_entry(mixer, &chip->mixer_list, list) {
+               cval = mixer->id_elems[unitid];
+               if (cval) {
+                       snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
+                                                   cval->control << 8,
+                                                   samplerate_id);
+                       snd_usb_mixer_notify_id(mixer, unitid);
+               }
+               break;
+       }
+}
+
+int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
+{
+       int err;
+       struct snd_info_entry *entry;
+
+       if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
+               return err;
+
+       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
+           mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+           mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
+               if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
+                       return err;
+               if (!snd_card_proc_new(mixer->chip->card, "audigy2nx", &entry))
+                       snd_info_set_text_ops(entry, mixer,
+                                             snd_audigy2nx_proc_read);
+       }
+
+       if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) ||
+           mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) {
+               err = snd_xonar_u1_controls_create(mixer);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
+                                   int unitid)
+{
+       if (!mixer->rc_cfg)
+               return;
+       /* unit ids specific to Extigy/Audigy 2 NX: */
+       switch (unitid) {
+       case 0: /* remote control */
+               mixer->rc_urb->dev = mixer->chip->dev;
+               usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
+               break;
+       case 4: /* digital in jack */
+       case 7: /* line in jacks */
+       case 19: /* speaker out jacks */
+       case 20: /* headphones out jack */
+               break;
+       /* live24ext: 4 = line-in jack */
+       case 3: /* hp-out jack (may actuate Mute) */
+               if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+                   mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
+                       snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
+               break;
+       default:
+               snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
+               break;
+       }
+}
+
diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h
new file mode 100644 (file)
index 0000000..bdbfab0
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef SND_USB_MIXER_QUIRKS_H
+#define SND_USB_MIXER_QUIRKS_H
+
+int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer);
+
+void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
+                              unsigned char samplerate_id);
+
+void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
+                                   int unitid);
+
+#endif /* SND_USB_MIXER_QUIRKS_H */
+
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
new file mode 100644 (file)
index 0000000..630e220
--- /dev/null
@@ -0,0 +1,934 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "quirks.h"
+#include "debug.h"
+#include "urb.h"
+#include "helper.h"
+#include "pcm.h"
+
+/*
+ * return the current pcm pointer.  just based on the hwptr_done value.
+ */
+static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_usb_substream *subs;
+       unsigned int hwptr_done;
+
+       subs = (struct snd_usb_substream *)substream->runtime->private_data;
+       spin_lock(&subs->lock);
+       hwptr_done = subs->hwptr_done;
+       spin_unlock(&subs->lock);
+       return hwptr_done / (substream->runtime->frame_bits >> 3);
+}
+
+/*
+ * find a matching audio format
+ */
+static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format,
+                                      unsigned int rate, unsigned int channels)
+{
+       struct list_head *p;
+       struct audioformat *found = NULL;
+       int cur_attr = 0, attr;
+
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               if (!(fp->formats & (1uLL << format)))
+                       continue;
+               if (fp->channels != channels)
+                       continue;
+               if (rate < fp->rate_min || rate > fp->rate_max)
+                       continue;
+               if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
+                       unsigned int i;
+                       for (i = 0; i < fp->nr_rates; i++)
+                               if (fp->rate_table[i] == rate)
+                                       break;
+                       if (i >= fp->nr_rates)
+                               continue;
+               }
+               attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE;
+               if (! found) {
+                       found = fp;
+                       cur_attr = attr;
+                       continue;
+               }
+               /* avoid async out and adaptive in if the other method
+                * supports the same format.
+                * this is a workaround for the case like
+                * M-audio audiophile USB.
+                */
+               if (attr != cur_attr) {
+                       if ((attr == USB_ENDPOINT_SYNC_ASYNC &&
+                            subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
+                           (attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
+                            subs->direction == SNDRV_PCM_STREAM_CAPTURE))
+                               continue;
+                       if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC &&
+                            subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
+                           (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
+                            subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {
+                               found = fp;
+                               cur_attr = attr;
+                               continue;
+                       }
+               }
+               /* find the format with the largest max. packet size */
+               if (fp->maxpacksize > found->maxpacksize) {
+                       found = fp;
+                       cur_attr = attr;
+               }
+       }
+       return found;
+}
+
+static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
+                        struct usb_host_interface *alts,
+                        struct audioformat *fmt)
+{
+       struct usb_device *dev = chip->dev;
+       unsigned int ep;
+       unsigned char data[1];
+       int err;
+
+       ep = get_endpoint(alts, 0)->bEndpointAddress;
+
+       /* if endpoint doesn't have pitch control, bail out */
+       if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
+               return 0;
+
+       data[0] = 1;
+       if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
+                                  USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
+                                  UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
+                          dev->devnum, iface, ep);
+               return err;
+       }
+
+       return 0;
+}
+
+/*
+ * initialize the picth control and sample rate
+ */
+int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
+                      struct usb_host_interface *alts,
+                      struct audioformat *fmt)
+{
+       struct usb_interface_descriptor *altsd = get_iface_desc(alts);
+
+       switch (altsd->bInterfaceProtocol) {
+       case UAC_VERSION_1:
+               return init_pitch_v1(chip, iface, alts, fmt);
+
+       case UAC_VERSION_2:
+               /* not implemented yet */
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
+                             struct usb_host_interface *alts,
+                             struct audioformat *fmt, int rate)
+{
+       struct usb_device *dev = chip->dev;
+       unsigned int ep;
+       unsigned char data[3];
+       int err, crate;
+
+       ep = get_endpoint(alts, 0)->bEndpointAddress;
+       /* if endpoint doesn't have sampling rate control, bail out */
+       if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
+               snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
+                                  dev->devnum, iface, fmt->altsetting);
+               return 0;
+       }
+
+       data[0] = rate;
+       data[1] = rate >> 8;
+       data[2] = rate >> 16;
+       if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
+                                  USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
+                                  UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
+                          dev->devnum, iface, fmt->altsetting, rate, ep);
+               return err;
+       }
+       if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
+                                  USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
+                                  UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
+                          dev->devnum, iface, fmt->altsetting, ep);
+               return 0; /* some devices don't support reading */
+       }
+       crate = data[0] | (data[1] << 8) | (data[2] << 16);
+       if (crate != rate) {
+               snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+               // runtime->rate = crate;
+       }
+
+       return 0;
+}
+
+static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+                             struct usb_host_interface *alts,
+                             struct audioformat *fmt, int rate)
+{
+       struct usb_device *dev = chip->dev;
+       unsigned char data[4];
+       int err, crate;
+
+       data[0] = rate;
+       data[1] = rate >> 8;
+       data[2] = rate >> 16;
+       data[3] = rate >> 24;
+       if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
+                                  USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                  UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
+                          dev->devnum, iface, fmt->altsetting, rate);
+               return err;
+       }
+       if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
+                                  USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                                  UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
+                          dev->devnum, iface, fmt->altsetting);
+               return err;
+       }
+       crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+       if (crate != rate)
+               snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+
+       return 0;
+}
+
+int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
+                            struct usb_host_interface *alts,
+                            struct audioformat *fmt, int rate)
+{
+       struct usb_interface_descriptor *altsd = get_iface_desc(alts);
+
+       switch (altsd->bInterfaceProtocol) {
+       case UAC_VERSION_1:
+               return set_sample_rate_v1(chip, iface, alts, fmt, rate);
+
+       case UAC_VERSION_2:
+               return set_sample_rate_v2(chip, iface, alts, fmt, rate);
+       }
+
+       return -EINVAL;
+}
+
+/*
+ * find a matching format and set up the interface
+ */
+static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
+{
+       struct usb_device *dev = subs->dev;
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       struct usb_interface *iface;
+       unsigned int ep, attr;
+       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+       int err;
+
+       iface = usb_ifnum_to_if(dev, fmt->iface);
+       if (WARN_ON(!iface))
+               return -EINVAL;
+       alts = &iface->altsetting[fmt->altset_idx];
+       altsd = get_iface_desc(alts);
+       if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
+               return -EINVAL;
+
+       if (fmt == subs->cur_audiofmt)
+               return 0;
+
+       /* close the old interface */
+       if (subs->interface >= 0 && subs->interface != fmt->iface) {
+               if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
+                       snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
+                               dev->devnum, fmt->iface, fmt->altsetting);
+                       return -EIO;
+               }
+               subs->interface = -1;
+               subs->altset_idx = 0;
+       }
+
+       /* set interface */
+       if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) {
+               if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
+                       snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
+                                  dev->devnum, fmt->iface, fmt->altsetting);
+                       return -EIO;
+               }
+               snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
+               subs->interface = fmt->iface;
+               subs->altset_idx = fmt->altset_idx;
+       }
+
+       /* create a data pipe */
+       ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
+       if (is_playback)
+               subs->datapipe = usb_sndisocpipe(dev, ep);
+       else
+               subs->datapipe = usb_rcvisocpipe(dev, ep);
+       subs->datainterval = fmt->datainterval;
+       subs->syncpipe = subs->syncinterval = 0;
+       subs->maxpacksize = fmt->maxpacksize;
+       subs->fill_max = 0;
+
+       /* we need a sync pipe in async OUT or adaptive IN mode */
+       /* check the number of EP, since some devices have broken
+        * descriptors which fool us.  if it has only one EP,
+        * assume it as adaptive-out or sync-in.
+        */
+       attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
+       if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
+            (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
+           altsd->bNumEndpoints >= 2) {
+               /* check sync-pipe endpoint */
+               /* ... and check descriptor size before accessing bSynchAddress
+                  because there is a version of the SB Audigy 2 NX firmware lacking
+                  the audio fields in the endpoint descriptors */
+               if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
+                   (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+                    get_endpoint(alts, 1)->bSynchAddress != 0)) {
+                       snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
+                                  dev->devnum, fmt->iface, fmt->altsetting);
+                       return -EINVAL;
+               }
+               ep = get_endpoint(alts, 1)->bEndpointAddress;
+               if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+                   (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
+                    (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
+                       snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
+                                  dev->devnum, fmt->iface, fmt->altsetting);
+                       return -EINVAL;
+               }
+               ep &= USB_ENDPOINT_NUMBER_MASK;
+               if (is_playback)
+                       subs->syncpipe = usb_rcvisocpipe(dev, ep);
+               else
+                       subs->syncpipe = usb_sndisocpipe(dev, ep);
+               if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+                   get_endpoint(alts, 1)->bRefresh >= 1 &&
+                   get_endpoint(alts, 1)->bRefresh <= 9)
+                       subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
+               else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
+                       subs->syncinterval = 1;
+               else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+                        get_endpoint(alts, 1)->bInterval <= 16)
+                       subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+               else
+                       subs->syncinterval = 3;
+       }
+
+       /* always fill max packet size */
+       if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
+               subs->fill_max = 1;
+
+       if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
+               return err;
+
+       subs->cur_audiofmt = fmt;
+
+       snd_usb_set_format_quirk(subs, fmt);
+
+#if 0
+       printk(KERN_DEBUG
+              "setting done: format = %d, rate = %d..%d, channels = %d\n",
+              fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
+       printk(KERN_DEBUG
+              "  datapipe = 0x%0x, syncpipe = 0x%0x\n",
+              subs->datapipe, subs->syncpipe);
+#endif
+
+       return 0;
+}
+
+/*
+ * hw_params callback
+ *
+ * allocate a buffer and set the given audio format.
+ *
+ * so far we use a physically linear buffer although packetize transfer
+ * doesn't need a continuous area.
+ * if sg buffer is supported on the later version of alsa, we'll follow
+ * that.
+ */
+static int snd_usb_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+       struct audioformat *fmt;
+       unsigned int channels, rate, format;
+       int ret, changed;
+
+       ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (ret < 0)
+               return ret;
+
+       format = params_format(hw_params);
+       rate = params_rate(hw_params);
+       channels = params_channels(hw_params);
+       fmt = find_format(subs, format, rate, channels);
+       if (!fmt) {
+               snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
+                          format, rate, channels);
+               return -EINVAL;
+       }
+
+       changed = subs->cur_audiofmt != fmt ||
+               subs->period_bytes != params_period_bytes(hw_params) ||
+               subs->cur_rate != rate;
+       if ((ret = set_format(subs, fmt)) < 0)
+               return ret;
+
+       if (subs->cur_rate != rate) {
+               struct usb_host_interface *alts;
+               struct usb_interface *iface;
+               iface = usb_ifnum_to_if(subs->dev, fmt->iface);
+               alts = &iface->altsetting[fmt->altset_idx];
+               ret = snd_usb_init_sample_rate(subs->stream->chip, subs->interface, alts, fmt, rate);
+               if (ret < 0)
+                       return ret;
+               subs->cur_rate = rate;
+       }
+
+       if (changed) {
+               /* format changed */
+               snd_usb_release_substream_urbs(subs, 0);
+               /* influenced: period_bytes, channels, rate, format, */
+               ret = snd_usb_init_substream_urbs(subs, params_period_bytes(hw_params),
+                                                 params_rate(hw_params),
+                                                 snd_pcm_format_physical_width(params_format(hw_params)) *
+                                                       params_channels(hw_params));
+       }
+
+       return ret;
+}
+
+/*
+ * hw_free callback
+ *
+ * reset the audio format and release the buffer
+ */
+static int snd_usb_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+
+       subs->cur_audiofmt = NULL;
+       subs->cur_rate = 0;
+       subs->period_bytes = 0;
+       if (!subs->stream->chip->shutdown)
+               snd_usb_release_substream_urbs(subs, 0);
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+/*
+ * prepare callback
+ *
+ * only a few subtle things...
+ */
+static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_usb_substream *subs = runtime->private_data;
+
+       if (! subs->cur_audiofmt) {
+               snd_printk(KERN_ERR "usbaudio: no format is specified!\n");
+               return -ENXIO;
+       }
+
+       /* some unit conversions in runtime */
+       subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
+       subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
+
+       /* reset the pointer */
+       subs->hwptr_done = 0;
+       subs->transfer_done = 0;
+       subs->phase = 0;
+       runtime->delay = 0;
+
+       return snd_usb_substream_prepare(subs, runtime);
+}
+
+static struct snd_pcm_hardware snd_usb_hardware =
+{
+       .info =                 SNDRV_PCM_INFO_MMAP |
+                               SNDRV_PCM_INFO_MMAP_VALID |
+                               SNDRV_PCM_INFO_BATCH |
+                               SNDRV_PCM_INFO_INTERLEAVED |
+                               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                               SNDRV_PCM_INFO_PAUSE,
+       .buffer_bytes_max =     1024 * 1024,
+       .period_bytes_min =     64,
+       .period_bytes_max =     512 * 1024,
+       .periods_min =          2,
+       .periods_max =          1024,
+};
+
+static int hw_check_valid_format(struct snd_usb_substream *subs,
+                                struct snd_pcm_hw_params *params,
+                                struct audioformat *fp)
+{
+       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
+       struct snd_mask check_fmts;
+       unsigned int ptime;
+
+       /* check the format */
+       snd_mask_none(&check_fmts);
+       check_fmts.bits[0] = (u32)fp->formats;
+       check_fmts.bits[1] = (u32)(fp->formats >> 32);
+       snd_mask_intersect(&check_fmts, fmts);
+       if (snd_mask_empty(&check_fmts)) {
+               hwc_debug("   > check: no supported format %d\n", fp->format);
+               return 0;
+       }
+       /* check the channels */
+       if (fp->channels < ct->min || fp->channels > ct->max) {
+               hwc_debug("   > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max);
+               return 0;
+       }
+       /* check the rate is within the range */
+       if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) {
+               hwc_debug("   > check: rate_min %d > max %d\n", fp->rate_min, it->max);
+               return 0;
+       }
+       if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) {
+               hwc_debug("   > check: rate_max %d < min %d\n", fp->rate_max, it->min);
+               return 0;
+       }
+       /* check whether the period time is >= the data packet interval */
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
+               ptime = 125 * (1 << fp->datainterval);
+               if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
+                       hwc_debug("   > check: ptime %u > max %u\n", ptime, pt->max);
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+                       struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       struct list_head *p;
+       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       unsigned int rmin, rmax;
+       int changed;
+
+       hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max);
+       changed = 0;
+       rmin = rmax = 0;
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               if (changed++) {
+                       if (rmin > fp->rate_min)
+                               rmin = fp->rate_min;
+                       if (rmax < fp->rate_max)
+                               rmax = fp->rate_max;
+               } else {
+                       rmin = fp->rate_min;
+                       rmax = fp->rate_max;
+               }
+       }
+
+       if (!changed) {
+               hwc_debug("  --> get empty\n");
+               it->empty = 1;
+               return -EINVAL;
+       }
+
+       changed = 0;
+       if (it->min < rmin) {
+               it->min = rmin;
+               it->openmin = 0;
+               changed = 1;
+       }
+       if (it->max > rmax) {
+               it->max = rmax;
+               it->openmax = 0;
+               changed = 1;
+       }
+       if (snd_interval_checkempty(it)) {
+               it->empty = 1;
+               return -EINVAL;
+       }
+       hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
+       return changed;
+}
+
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+                           struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       struct list_head *p;
+       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       unsigned int rmin, rmax;
+       int changed;
+
+       hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max);
+       changed = 0;
+       rmin = rmax = 0;
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               if (changed++) {
+                       if (rmin > fp->channels)
+                               rmin = fp->channels;
+                       if (rmax < fp->channels)
+                               rmax = fp->channels;
+               } else {
+                       rmin = fp->channels;
+                       rmax = fp->channels;
+               }
+       }
+
+       if (!changed) {
+               hwc_debug("  --> get empty\n");
+               it->empty = 1;
+               return -EINVAL;
+       }
+
+       changed = 0;
+       if (it->min < rmin) {
+               it->min = rmin;
+               it->openmin = 0;
+               changed = 1;
+       }
+       if (it->max > rmax) {
+               it->max = rmax;
+               it->openmax = 0;
+               changed = 1;
+       }
+       if (snd_interval_checkempty(it)) {
+               it->empty = 1;
+               return -EINVAL;
+       }
+       hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
+       return changed;
+}
+
+static int hw_rule_format(struct snd_pcm_hw_params *params,
+                         struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       struct list_head *p;
+       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       u64 fbits;
+       u32 oldbits[2];
+       int changed;
+
+       hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
+       fbits = 0;
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               fbits |= fp->formats;
+       }
+
+       oldbits[0] = fmt->bits[0];
+       oldbits[1] = fmt->bits[1];
+       fmt->bits[0] &= (u32)fbits;
+       fmt->bits[1] &= (u32)(fbits >> 32);
+       if (!fmt->bits[0] && !fmt->bits[1]) {
+               hwc_debug("  --> get empty\n");
+               return -EINVAL;
+       }
+       changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]);
+       hwc_debug("  --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed);
+       return changed;
+}
+
+static int hw_rule_period_time(struct snd_pcm_hw_params *params,
+                              struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       struct audioformat *fp;
+       struct snd_interval *it;
+       unsigned char min_datainterval;
+       unsigned int pmin;
+       int changed;
+
+       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
+       hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max);
+       min_datainterval = 0xff;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               min_datainterval = min(min_datainterval, fp->datainterval);
+       }
+       if (min_datainterval == 0xff) {
+               hwc_debug("  --> get emtpy\n");
+               it->empty = 1;
+               return -EINVAL;
+       }
+       pmin = 125 * (1 << min_datainterval);
+       changed = 0;
+       if (it->min < pmin) {
+               it->min = pmin;
+               it->openmin = 0;
+               changed = 1;
+       }
+       if (snd_interval_checkempty(it)) {
+               it->empty = 1;
+               return -EINVAL;
+       }
+       hwc_debug("  --> (%u,%u) (changed = %d)\n", it->min, it->max, changed);
+       return changed;
+}
+
+/*
+ *  If the device supports unusual bit rates, does the request meet these?
+ */
+static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
+                                 struct snd_usb_substream *subs)
+{
+       struct audioformat *fp;
+       int count = 0, needs_knot = 0;
+       int err;
+
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+                       return 0;
+               count += fp->nr_rates;
+               if (fp->rates & SNDRV_PCM_RATE_KNOT)
+                       needs_knot = 1;
+       }
+       if (!needs_knot)
+               return 0;
+
+       subs->rate_list.count = count;
+       subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
+       subs->rate_list.mask = 0;
+       count = 0;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               int i;
+               for (i = 0; i < fp->nr_rates; i++)
+                       subs->rate_list.list[count++] = fp->rate_table[i];
+       }
+       err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                        &subs->rate_list);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+
+/*
+ * set up the runtime hardware information.
+ */
+
+static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
+{
+       struct list_head *p;
+       unsigned int pt, ptmin;
+       int param_period_time_if_needed;
+       int err;
+
+       runtime->hw.formats = subs->formats;
+
+       runtime->hw.rate_min = 0x7fffffff;
+       runtime->hw.rate_max = 0;
+       runtime->hw.channels_min = 256;
+       runtime->hw.channels_max = 0;
+       runtime->hw.rates = 0;
+       ptmin = UINT_MAX;
+       /* check min/max rates and channels */
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               runtime->hw.rates |= fp->rates;
+               if (runtime->hw.rate_min > fp->rate_min)
+                       runtime->hw.rate_min = fp->rate_min;
+               if (runtime->hw.rate_max < fp->rate_max)
+                       runtime->hw.rate_max = fp->rate_max;
+               if (runtime->hw.channels_min > fp->channels)
+                       runtime->hw.channels_min = fp->channels;
+               if (runtime->hw.channels_max < fp->channels)
+                       runtime->hw.channels_max = fp->channels;
+               if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) {
+                       /* FIXME: there might be more than one audio formats... */
+                       runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
+                               fp->frame_size;
+               }
+               pt = 125 * (1 << fp->datainterval);
+               ptmin = min(ptmin, pt);
+       }
+
+       param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
+       if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
+               /* full speed devices have fixed data packet interval */
+               ptmin = 1000;
+       if (ptmin == 1000)
+               /* if period time doesn't go below 1 ms, no rules needed */
+               param_period_time_if_needed = -1;
+       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                    ptmin, UINT_MAX);
+
+       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                      hw_rule_rate, subs,
+                                      SNDRV_PCM_HW_PARAM_FORMAT,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      param_period_time_if_needed,
+                                      -1)) < 0)
+               return err;
+       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      hw_rule_channels, subs,
+                                      SNDRV_PCM_HW_PARAM_FORMAT,
+                                      SNDRV_PCM_HW_PARAM_RATE,
+                                      param_period_time_if_needed,
+                                      -1)) < 0)
+               return err;
+       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+                                      hw_rule_format, subs,
+                                      SNDRV_PCM_HW_PARAM_RATE,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      param_period_time_if_needed,
+                                      -1)) < 0)
+               return err;
+       if (param_period_time_if_needed >= 0) {
+               err = snd_pcm_hw_rule_add(runtime, 0,
+                                         SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                         hw_rule_period_time, subs,
+                                         SNDRV_PCM_HW_PARAM_FORMAT,
+                                         SNDRV_PCM_HW_PARAM_CHANNELS,
+                                         SNDRV_PCM_HW_PARAM_RATE,
+                                         -1);
+               if (err < 0)
+                       return err;
+       }
+       if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
+               return err;
+       return 0;
+}
+
+static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
+{
+       struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_usb_substream *subs = &as->substream[direction];
+
+       subs->interface = -1;
+       subs->altset_idx = 0;
+       runtime->hw = snd_usb_hardware;
+       runtime->private_data = subs;
+       subs->pcm_substream = substream;
+       return setup_hw_info(runtime, subs);
+}
+
+static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
+{
+       struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
+       struct snd_usb_substream *subs = &as->substream[direction];
+
+       if (!as->chip->shutdown && subs->interface >= 0) {
+               usb_set_interface(subs->dev, subs->interface, 0);
+               subs->interface = -1;
+       }
+       subs->pcm_substream = NULL;
+       return 0;
+}
+
+static int snd_usb_playback_open(struct snd_pcm_substream *substream)
+{
+       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static int snd_usb_playback_close(struct snd_pcm_substream *substream)
+{
+       return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static int snd_usb_capture_open(struct snd_pcm_substream *substream)
+{
+       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE);
+}
+
+static int snd_usb_capture_close(struct snd_pcm_substream *substream)
+{
+       return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
+}
+
+static struct snd_pcm_ops snd_usb_playback_ops = {
+       .open =         snd_usb_playback_open,
+       .close =        snd_usb_playback_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_usb_hw_params,
+       .hw_free =      snd_usb_hw_free,
+       .prepare =      snd_usb_pcm_prepare,
+       .trigger =      snd_usb_substream_playback_trigger,
+       .pointer =      snd_usb_pcm_pointer,
+       .page =         snd_pcm_lib_get_vmalloc_page,
+       .mmap =         snd_pcm_lib_mmap_vmalloc,
+};
+
+static struct snd_pcm_ops snd_usb_capture_ops = {
+       .open =         snd_usb_capture_open,
+       .close =        snd_usb_capture_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_usb_hw_params,
+       .hw_free =      snd_usb_hw_free,
+       .prepare =      snd_usb_pcm_prepare,
+       .trigger =      snd_usb_substream_capture_trigger,
+       .pointer =      snd_usb_pcm_pointer,
+       .page =         snd_pcm_lib_get_vmalloc_page,
+       .mmap =         snd_pcm_lib_mmap_vmalloc,
+};
+
+void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream)
+{
+       snd_pcm_set_ops(pcm, stream,
+                       stream == SNDRV_PCM_STREAM_PLAYBACK ?
+                       &snd_usb_playback_ops : &snd_usb_capture_ops);
+}
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
new file mode 100644 (file)
index 0000000..1c931b6
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __USBAUDIO_PCM_H
+#define __USBAUDIO_PCM_H
+
+void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
+
+int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
+                      struct usb_host_interface *alts,
+                      struct audioformat *fmt);
+
+int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
+                            struct usb_host_interface *alts,
+                            struct audioformat *fmt, int rate);
+
+#endif /* __USBAUDIO_PCM_H */
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
new file mode 100644 (file)
index 0000000..f5e3f35
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "helper.h"
+#include "card.h"
+#include "proc.h"
+
+/* convert our full speed USB rate into sampling rate in Hz */
+static inline unsigned get_full_speed_hz(unsigned int usb_rate)
+{
+       return (usb_rate * 125 + (1 << 12)) >> 13;
+}
+
+/* convert our high speed USB rate into sampling rate in Hz */
+static inline unsigned get_high_speed_hz(unsigned int usb_rate)
+{
+       return (usb_rate * 125 + (1 << 9)) >> 10;
+}
+
+/*
+ * common proc files to show the usb device info
+ */
+static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+       struct snd_usb_audio *chip = entry->private_data;
+       if (!chip->shutdown)
+               snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum);
+}
+
+static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+       struct snd_usb_audio *chip = entry->private_data;
+       if (!chip->shutdown)
+               snd_iprintf(buffer, "%04x:%04x\n", 
+                           USB_ID_VENDOR(chip->usb_id),
+                           USB_ID_PRODUCT(chip->usb_id));
+}
+
+void snd_usb_audio_create_proc(struct snd_usb_audio *chip)
+{
+       struct snd_info_entry *entry;
+       if (!snd_card_proc_new(chip->card, "usbbus", &entry))
+               snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read);
+       if (!snd_card_proc_new(chip->card, "usbid", &entry))
+               snd_info_set_text_ops(entry, chip, proc_audio_usbid_read);
+}
+
+/*
+ * proc interface for list the supported pcm formats
+ */
+static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
+{
+       struct list_head *p;
+       static char *sync_types[4] = {
+               "NONE", "ASYNC", "ADAPTIVE", "SYNC"
+       };
+
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               snd_pcm_format_t fmt;
+               fp = list_entry(p, struct audioformat, list);
+               snd_iprintf(buffer, "  Interface %d\n", fp->iface);
+               snd_iprintf(buffer, "    Altset %d\n", fp->altsetting);
+               snd_iprintf(buffer, "    Format:");
+               for (fmt = 0; fmt <= SNDRV_PCM_FORMAT_LAST; ++fmt)
+                       if (fp->formats & (1uLL << fmt))
+                               snd_iprintf(buffer, " %s",
+                                           snd_pcm_format_name(fmt));
+               snd_iprintf(buffer, "\n");
+               snd_iprintf(buffer, "    Channels: %d\n", fp->channels);
+               snd_iprintf(buffer, "    Endpoint: %d %s (%s)\n",
+                           fp->endpoint & USB_ENDPOINT_NUMBER_MASK,
+                           fp->endpoint & USB_DIR_IN ? "IN" : "OUT",
+                           sync_types[(fp->ep_attr & USB_ENDPOINT_SYNCTYPE) >> 2]);
+               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) {
+                       snd_iprintf(buffer, "    Rates: %d - %d (continuous)\n",
+                                   fp->rate_min, fp->rate_max);
+               } else {
+                       unsigned int i;
+                       snd_iprintf(buffer, "    Rates: ");
+                       for (i = 0; i < fp->nr_rates; i++) {
+                               if (i > 0)
+                                       snd_iprintf(buffer, ", ");
+                               snd_iprintf(buffer, "%d", fp->rate_table[i]);
+                       }
+                       snd_iprintf(buffer, "\n");
+               }
+               if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+                       snd_iprintf(buffer, "    Data packet interval: %d us\n",
+                                   125 * (1 << fp->datainterval));
+               // snd_iprintf(buffer, "    Max Packet Size = %d\n", fp->maxpacksize);
+               // snd_iprintf(buffer, "    EP Attribute = %#x\n", fp->attributes);
+       }
+}
+
+static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
+{
+       if (subs->running) {
+               unsigned int i;
+               snd_iprintf(buffer, "  Status: Running\n");
+               snd_iprintf(buffer, "    Interface = %d\n", subs->interface);
+               snd_iprintf(buffer, "    Altset = %d\n", subs->altset_idx);
+               snd_iprintf(buffer, "    URBs = %d [ ", subs->nurbs);
+               for (i = 0; i < subs->nurbs; i++)
+                       snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
+               snd_iprintf(buffer, "]\n");
+               snd_iprintf(buffer, "    Packet Size = %d\n", subs->curpacksize);
+               snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
+                           snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
+                           ? get_full_speed_hz(subs->freqm)
+                           : get_high_speed_hz(subs->freqm),
+                           subs->freqm >> 16, subs->freqm & 0xffff);
+       } else {
+               snd_iprintf(buffer, "  Status: Stop\n");
+       }
+}
+
+static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+       struct snd_usb_stream *stream = entry->private_data;
+
+       snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name);
+
+       if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) {
+               snd_iprintf(buffer, "\nPlayback:\n");
+               proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
+               proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
+       }
+       if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) {
+               snd_iprintf(buffer, "\nCapture:\n");
+               proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
+               proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
+       }
+}
+
+void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream)
+{
+       struct snd_info_entry *entry;
+       char name[32];
+       struct snd_card *card = stream->chip->card;
+
+       sprintf(name, "stream%d", stream->pcm_index);
+       if (!snd_card_proc_new(card, name, &entry))
+               snd_info_set_text_ops(entry, stream, proc_pcm_format_read);
+}
+
diff --git a/sound/usb/proc.h b/sound/usb/proc.h
new file mode 100644 (file)
index 0000000..a45b765
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __USBAUDIO_PROC_H
+#define __USBAUDIO_PROC_H
+
+void snd_usb_audio_create_proc(struct snd_usb_audio *chip);
+void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream);
+
+#endif /* __USBAUDIO_PROC_H */
+
similarity index 99%
rename from sound/usb/usbquirks.h
rename to sound/usb/quirks-table.h
index 2b426c1fd0e8e838ff4949d16bb0485107944ecd..81c5f8a312cea61703c37065a9a0a9e78b08d174 100644 (file)
@@ -279,7 +279,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 0,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S16_LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
                                        .channels = 4,
                                        .iface = 0,
                                        .altsetting = 1,
@@ -296,7 +296,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 1,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S16_LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
                                        .channels = 2,
                                        .iface = 1,
                                        .altsetting = 1,
@@ -580,7 +580,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 0,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
                                        .channels = 2,
                                        .iface = 0,
                                        .altsetting = 1,
@@ -597,7 +597,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 1,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
                                        .channels = 2,
                                        .iface = 1,
                                        .altsetting = 1,
@@ -793,7 +793,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 1,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
                                        .channels = 2,
                                        .iface = 1,
                                        .altsetting = 1,
@@ -810,7 +810,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 2,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
                                        .channels = 2,
                                        .iface = 2,
                                        .altsetting = 1,
@@ -2203,7 +2203,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 1,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = &(const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3BE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3BE,
                                        .channels = 2,
                                        .iface = 1,
                                        .altsetting = 1,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
new file mode 100644 (file)
index 0000000..d4ced64
--- /dev/null
@@ -0,0 +1,593 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "mixer.h"
+#include "mixer_quirks.h"
+#include "midi.h"
+#include "quirks.h"
+#include "helper.h"
+#include "endpoint.h"
+#include "pcm.h"
+
+/*
+ * handle the quirks for the contained interfaces
+ */
+static int create_composite_quirk(struct snd_usb_audio *chip,
+                                 struct usb_interface *iface,
+                                 struct usb_driver *driver,
+                                 const struct snd_usb_audio_quirk *quirk)
+{
+       int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
+       int err;
+
+       for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) {
+               iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
+               if (!iface)
+                       continue;
+               if (quirk->ifnum != probed_ifnum &&
+                   usb_interface_claimed(iface))
+                       continue;
+               err = snd_usb_create_quirk(chip, iface, driver, quirk);
+               if (err < 0)
+                       return err;
+               if (quirk->ifnum != probed_ifnum)
+                       usb_driver_claim_interface(driver, iface, (void *)-1L);
+       }
+       return 0;
+}
+
+static int ignore_interface_quirk(struct snd_usb_audio *chip,
+                                 struct usb_interface *iface,
+                                 struct usb_driver *driver,
+                                 const struct snd_usb_audio_quirk *quirk)
+{
+       return 0;
+}
+
+
+/*
+ * Allow alignment on audio sub-slot (channel samples) rather than
+ * on audio slots (audio frames)
+ */
+static int create_align_transfer_quirk(struct snd_usb_audio *chip,
+                                      struct usb_interface *iface,
+                                      struct usb_driver *driver,
+                                      const struct snd_usb_audio_quirk *quirk)
+{
+       chip->txfr_quirk = 1;
+       return 1;       /* Continue with creating streams and mixer */
+}
+
+static int create_any_midi_quirk(struct snd_usb_audio *chip,
+                                struct usb_interface *intf,
+                                struct usb_driver *driver,
+                                const struct snd_usb_audio_quirk *quirk)
+{
+       return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk);
+}
+
+/*
+ * create a stream for an interface with proper descriptors
+ */
+static int create_standard_audio_quirk(struct snd_usb_audio *chip,
+                                      struct usb_interface *iface,
+                                      struct usb_driver *driver,
+                                      const struct snd_usb_audio_quirk *quirk)
+{
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       int err;
+
+       alts = &iface->altsetting[0];
+       altsd = get_iface_desc(alts);
+       err = snd_usb_parse_audio_endpoints(chip, altsd->bInterfaceNumber);
+       if (err < 0) {
+               snd_printk(KERN_ERR "cannot setup if %d: error %d\n",
+                          altsd->bInterfaceNumber, err);
+               return err;
+       }
+       /* reset the current interface */
+       usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0);
+       return 0;
+}
+
+/*
+ * create a stream for an endpoint/altsetting without proper descriptors
+ */
+static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
+                                    struct usb_interface *iface,
+                                    struct usb_driver *driver,
+                                    const struct snd_usb_audio_quirk *quirk)
+{
+       struct audioformat *fp;
+       struct usb_host_interface *alts;
+       int stream, err;
+       unsigned *rate_table = NULL;
+
+       fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
+       if (! fp) {
+               snd_printk(KERN_ERR "cannot memdup\n");
+               return -ENOMEM;
+       }
+       if (fp->nr_rates > 0) {
+               rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL);
+               if (!rate_table) {
+                       kfree(fp);
+                       return -ENOMEM;
+               }
+               memcpy(rate_table, fp->rate_table, sizeof(int) * fp->nr_rates);
+               fp->rate_table = rate_table;
+       }
+
+       stream = (fp->endpoint & USB_DIR_IN)
+               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+       err = snd_usb_add_audio_endpoint(chip, stream, fp);
+       if (err < 0) {
+               kfree(fp);
+               kfree(rate_table);
+               return err;
+       }
+       if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
+           fp->altset_idx >= iface->num_altsetting) {
+               kfree(fp);
+               kfree(rate_table);
+               return -EINVAL;
+       }
+       alts = &iface->altsetting[fp->altset_idx];
+       fp->datainterval = snd_usb_parse_datainterval(chip, alts);
+       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+       usb_set_interface(chip->dev, fp->iface, 0);
+       snd_usb_init_pitch(chip, fp->iface, alts, fp);
+       snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
+       return 0;
+}
+
+/*
+ * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.  
+ * The only way to detect the sample rate is by looking at wMaxPacketSize.
+ */
+static int create_uaxx_quirk(struct snd_usb_audio *chip,
+                            struct usb_interface *iface,
+                            struct usb_driver *driver,
+                            const struct snd_usb_audio_quirk *quirk)
+{
+       static const struct audioformat ua_format = {
+               .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+               .channels = 2,
+               .fmt_type = UAC_FORMAT_TYPE_I,
+               .altsetting = 1,
+               .altset_idx = 1,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+       };
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       struct audioformat *fp;
+       int stream, err;
+
+       /* both PCM and MIDI interfaces have 2 or more altsettings */
+       if (iface->num_altsetting < 2)
+               return -ENXIO;
+       alts = &iface->altsetting[1];
+       altsd = get_iface_desc(alts);
+
+       if (altsd->bNumEndpoints == 2) {
+               static const struct snd_usb_midi_endpoint_info ua700_ep = {
+                       .out_cables = 0x0003,
+                       .in_cables  = 0x0003
+               };
+               static const struct snd_usb_audio_quirk ua700_quirk = {
+                       .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                       .data = &ua700_ep
+               };
+               static const struct snd_usb_midi_endpoint_info uaxx_ep = {
+                       .out_cables = 0x0001,
+                       .in_cables  = 0x0001
+               };
+               static const struct snd_usb_audio_quirk uaxx_quirk = {
+                       .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                       .data = &uaxx_ep
+               };
+               const struct snd_usb_audio_quirk *quirk =
+                       chip->usb_id == USB_ID(0x0582, 0x002b)
+                       ? &ua700_quirk : &uaxx_quirk;
+               return snd_usbmidi_create(chip->card, iface,
+                                         &chip->midi_list, quirk);
+       }
+
+       if (altsd->bNumEndpoints != 1)
+               return -ENXIO;
+
+       fp = kmalloc(sizeof(*fp), GFP_KERNEL);
+       if (!fp)
+               return -ENOMEM;
+       memcpy(fp, &ua_format, sizeof(*fp));
+
+       fp->iface = altsd->bInterfaceNumber;
+       fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
+       fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
+       fp->datainterval = 0;
+       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+
+       switch (fp->maxpacksize) {
+       case 0x120:
+               fp->rate_max = fp->rate_min = 44100;
+               break;
+       case 0x138:
+       case 0x140:
+               fp->rate_max = fp->rate_min = 48000;
+               break;
+       case 0x258:
+       case 0x260:
+               fp->rate_max = fp->rate_min = 96000;
+               break;
+       default:
+               snd_printk(KERN_ERR "unknown sample rate\n");
+               kfree(fp);
+               return -ENXIO;
+       }
+
+       stream = (fp->endpoint & USB_DIR_IN)
+               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+       err = snd_usb_add_audio_endpoint(chip, stream, fp);
+       if (err < 0) {
+               kfree(fp);
+               return err;
+       }
+       usb_set_interface(chip->dev, fp->iface, 0);
+       return 0;
+}
+
+/*
+ * audio-interface quirks
+ *
+ * returns zero if no standard audio/MIDI parsing is needed.
+ * returns a postive value if standard audio/midi interfaces are parsed
+ * after this.
+ * returns a negative value at error.
+ */
+int snd_usb_create_quirk(struct snd_usb_audio *chip,
+                        struct usb_interface *iface,
+                        struct usb_driver *driver,
+                        const struct snd_usb_audio_quirk *quirk)
+{
+       typedef int (*quirk_func_t)(struct snd_usb_audio *,
+                                   struct usb_interface *,
+                                   struct usb_driver *,
+                                   const struct snd_usb_audio_quirk *);
+       static const quirk_func_t quirk_funcs[] = {
+               [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
+               [QUIRK_COMPOSITE] = create_composite_quirk,
+               [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
+               [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
+               [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
+               [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
+               [QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
+               [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk,
+               [QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
+               [QUIRK_MIDI_CME] = create_any_midi_quirk,
+               [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
+               [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
+               [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
+               [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk
+       };
+
+       if (quirk->type < QUIRK_TYPE_COUNT) {
+               return quirk_funcs[quirk->type](chip, iface, driver, quirk);
+       } else {
+               snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
+               return -ENXIO;
+       }
+}
+
+/*
+ * boot quirks
+ */
+
+#define EXTIGY_FIRMWARE_SIZE_OLD 794
+#define EXTIGY_FIRMWARE_SIZE_NEW 483
+
+static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
+{
+       struct usb_host_config *config = dev->actconfig;
+       int err;
+
+       if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
+           le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) {
+               snd_printdd("sending Extigy boot sequence...\n");
+               /* Send message to force it to reconnect with full interface. */
+               err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0),
+                                     0x10, 0x43, 0x0001, 0x000a, NULL, 0, 1000);
+               if (err < 0) snd_printdd("error sending boot message: %d\n", err);
+               err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
+                               &dev->descriptor, sizeof(dev->descriptor));
+               config = dev->actconfig;
+               if (err < 0) snd_printdd("error usb_get_descriptor: %d\n", err);
+               err = usb_reset_configuration(dev);
+               if (err < 0) snd_printdd("error usb_reset_configuration: %d\n", err);
+               snd_printdd("extigy_boot: new boot length = %d\n",
+                           le16_to_cpu(get_cfg_desc(config)->wTotalLength));
+               return -ENODEV; /* quit this anyway */
+       }
+       return 0;
+}
+
+static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
+{
+       u8 buf = 1;
+
+       snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                       0, 0, &buf, 1, 1000);
+       if (buf == 0) {
+               snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
+                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                               1, 2000, NULL, 0, 1000);
+               return -ENODEV;
+       }
+       return 0;
+}
+
+/*
+ * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
+ * documented in the device's data sheet.
+ */
+static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value)
+{
+       u8 buf[4];
+       buf[0] = 0x20;
+       buf[1] = value & 0xff;
+       buf[2] = (value >> 8) & 0xff;
+       buf[3] = reg;
+       return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
+                              USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
+                              0, 0, &buf, 4, 1000);
+}
+
+static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
+{
+       /*
+        * Enable line-out driver mode, set headphone source to front
+        * channels, enable stereo mic.
+        */
+       return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
+}
+
+/*
+ * C-Media CM6206 is based on CM106 with two additional
+ * registers that are not documented in the data sheet.
+ * Values here are chosen based on sniffing USB traffic
+ * under Windows.
+ */
+static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
+{
+       int err, reg;
+       int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
+
+       for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
+               err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
+               if (err < 0)
+                       return err;
+       }
+
+       return err;
+}
+
+/*
+ * This call will put the synth in "USB send" mode, i.e it will send MIDI
+ * messages through USB (this is disabled at startup). The synth will
+ * acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB
+ * sign on its LCD. Values here are chosen based on sniffing USB traffic
+ * under Windows.
+ */
+static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
+{
+       int err, actual_length;
+
+       /* "midi send" enable */
+       static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 };
+
+       void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf,
+                       ARRAY_SIZE(seq), &actual_length, 1000);
+       kfree(buf);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+/*
+ * Setup quirks
+ */
+#define AUDIOPHILE_SET                 0x01 /* if set, parse device_setup */
+#define AUDIOPHILE_SET_DTS              0x02 /* if set, enable DTS Digital Output */
+#define AUDIOPHILE_SET_96K              0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */
+#define AUDIOPHILE_SET_24B             0x08 /* 24bits sample if set, 16bits otherwise */
+#define AUDIOPHILE_SET_DI              0x10 /* if set, enable Digital Input */
+#define AUDIOPHILE_SET_MASK            0x1F /* bit mask for setup value */
+#define AUDIOPHILE_SET_24B_48K_DI      0x19 /* value for 24bits+48KHz+Digital Input */
+#define AUDIOPHILE_SET_24B_48K_NOTDI   0x09 /* value for 24bits+48KHz+No Digital Input */
+#define AUDIOPHILE_SET_16B_48K_DI      0x11 /* value for 16bits+48KHz+Digital Input */
+#define AUDIOPHILE_SET_16B_48K_NOTDI   0x01 /* value for 16bits+48KHz+No Digital Input */
+
+static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
+                                        int iface,
+                                        int altno)
+{
+       /* Reset ALL ifaces to 0 altsetting.
+        * Call it for every possible altsetting of every interface.
+        */
+       usb_set_interface(chip->dev, iface, 0);
+
+       if (chip->setup & AUDIOPHILE_SET) {
+               if ((chip->setup & AUDIOPHILE_SET_DTS)
+                   && altno != 6)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_96K)
+                   && altno != 1)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_MASK) ==
+                   AUDIOPHILE_SET_24B_48K_DI && altno != 2)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_MASK) ==
+                   AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_MASK) ==
+                   AUDIOPHILE_SET_16B_48K_DI && altno != 4)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_MASK) ==
+                   AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5)
+                       return 1; /* skip this altsetting */
+       }
+
+       return 0; /* keep this altsetting */
+}
+
+int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
+                                 int iface,
+                                 int altno)
+{
+       /* audiophile usb: skip altsets incompatible with device_setup */
+       if (chip->usb_id == USB_ID(0x0763, 0x2003))
+               return audiophile_skip_setting_quirk(chip, iface, altno);
+
+       return 0;
+}
+
+int snd_usb_apply_boot_quirk(struct usb_device *dev,
+                            struct usb_interface *intf,
+                            const struct snd_usb_audio_quirk *quirk)
+{
+       u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+                       le16_to_cpu(dev->descriptor.idProduct));
+
+       /* SB Extigy needs special boot-up sequence */
+       /* if more models come, this will go to the quirk list. */
+       if (id == USB_ID(0x041e, 0x3000))
+               return snd_usb_extigy_boot_quirk(dev, intf);
+
+       /* SB Audigy 2 NX needs its own boot-up magic, too */
+       if (id == USB_ID(0x041e, 0x3020))
+               return snd_usb_audigy2nx_boot_quirk(dev);
+
+       /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
+       if (id == USB_ID(0x10f5, 0x0200))
+               return snd_usb_cm106_boot_quirk(dev);
+
+       /* C-Media CM6206 / CM106-Like Sound Device */
+       if (id == USB_ID(0x0d8c, 0x0102))
+               return snd_usb_cm6206_boot_quirk(dev);
+
+       /* Access Music VirusTI Desktop */
+       if (id == USB_ID(0x133e, 0x0815))
+               return snd_usb_accessmusic_boot_quirk(dev);
+
+       return 0;
+}
+
+/*
+ * check if the device uses big-endian samples
+ */
+int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp)
+{
+       switch (chip->usb_id) {
+       case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
+               if (fp->endpoint & USB_DIR_IN)
+                       return 1;
+               break;
+       case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+               if (chip->setup == 0x00 ||
+                   fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device,
+ * not for interface.
+ */
+
+enum {
+       EMU_QUIRK_SR_44100HZ = 0,
+       EMU_QUIRK_SR_48000HZ,
+       EMU_QUIRK_SR_88200HZ,
+       EMU_QUIRK_SR_96000HZ,
+       EMU_QUIRK_SR_176400HZ,
+       EMU_QUIRK_SR_192000HZ
+};
+
+static void set_format_emu_quirk(struct snd_usb_substream *subs,
+                                struct audioformat *fmt)
+{
+       unsigned char emu_samplerate_id = 0;
+
+       /* When capture is active
+        * sample rate shouldn't be changed
+        * by playback substream
+        */
+       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1)
+                       return;
+       }
+
+       switch (fmt->rate_min) {
+       case 48000:
+               emu_samplerate_id = EMU_QUIRK_SR_48000HZ;
+               break;
+       case 88200:
+               emu_samplerate_id = EMU_QUIRK_SR_88200HZ;
+               break;
+       case 96000:
+               emu_samplerate_id = EMU_QUIRK_SR_96000HZ;
+               break;
+       case 176400:
+               emu_samplerate_id = EMU_QUIRK_SR_176400HZ;
+               break;
+       case 192000:
+               emu_samplerate_id = EMU_QUIRK_SR_192000HZ;
+               break;
+       default:
+               emu_samplerate_id = EMU_QUIRK_SR_44100HZ;
+               break;
+       }
+       snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
+}
+
+void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
+                             struct audioformat *fmt)
+{
+       switch (subs->stream->chip->usb_id) {
+       case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
+       case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
+       case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
+               set_format_emu_quirk(subs, fmt);
+               break;
+       }
+}
+
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
new file mode 100644 (file)
index 0000000..03e5e94
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef __USBAUDIO_QUIRKS_H
+#define __USBAUDIO_QUIRKS_H
+
+int snd_usb_create_quirk(struct snd_usb_audio *chip,
+                        struct usb_interface *iface,
+                        struct usb_driver *driver,
+                        const struct snd_usb_audio_quirk *quirk);
+
+int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
+                                 int iface,
+                                 int altno);
+
+int snd_usb_apply_boot_quirk(struct usb_device *dev,
+                            struct usb_interface *intf,
+                            const struct snd_usb_audio_quirk *quirk);
+
+void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
+                             struct audioformat *fmt);
+
+int snd_usb_is_big_endian_format(struct snd_usb_audio *chip,
+                                struct audioformat *fp);
+
+#endif /* __USBAUDIO_QUIRKS_H */
diff --git a/sound/usb/urb.c b/sound/usb/urb.c
new file mode 100644 (file)
index 0000000..ad50d43
--- /dev/null
@@ -0,0 +1,989 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "helper.h"
+#include "card.h"
+#include "urb.h"
+#include "pcm.h"
+
+/*
+ * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
+ * this will overflow at approx 524 kHz
+ */
+static inline unsigned get_usb_full_speed_rate(unsigned int rate)
+{
+       return ((rate << 13) + 62) / 125;
+}
+
+/*
+ * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
+ * this will overflow at approx 4 MHz
+ */
+static inline unsigned get_usb_high_speed_rate(unsigned int rate)
+{
+       return ((rate << 10) + 62) / 125;
+}
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
+{
+       struct snd_usb_audio *chip = subs->stream->chip;
+       unsigned int i;
+       int async;
+
+       subs->running = 0;
+
+       if (!force && subs->stream->chip->shutdown) /* to be sure... */
+               return -EBADFD;
+
+       async = !can_sleep && chip->async_unlink;
+
+       if (!async && in_interrupt())
+               return 0;
+
+       for (i = 0; i < subs->nurbs; i++) {
+               if (test_bit(i, &subs->active_mask)) {
+                       if (!test_and_set_bit(i, &subs->unlink_mask)) {
+                               struct urb *u = subs->dataurb[i].urb;
+                               if (async)
+                                       usb_unlink_urb(u);
+                               else
+                                       usb_kill_urb(u);
+                       }
+               }
+       }
+       if (subs->syncpipe) {
+               for (i = 0; i < SYNC_URBS; i++) {
+                       if (test_bit(i+16, &subs->active_mask)) {
+                               if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
+                                       struct urb *u = subs->syncurb[i].urb;
+                                       if (async)
+                                               usb_unlink_urb(u);
+                                       else
+                                               usb_kill_urb(u);
+                               }
+                       }
+               }
+       }
+       return 0;
+}
+
+
+/*
+ * release a urb data
+ */
+static void release_urb_ctx(struct snd_urb_ctx *u)
+{
+       if (u->urb) {
+               if (u->buffer_size)
+                       usb_buffer_free(u->subs->dev, u->buffer_size,
+                                       u->urb->transfer_buffer,
+                                       u->urb->transfer_dma);
+               usb_free_urb(u->urb);
+               u->urb = NULL;
+       }
+}
+
+/*
+ *  wait until all urbs are processed.
+ */
+static int wait_clear_urbs(struct snd_usb_substream *subs)
+{
+       unsigned long end_time = jiffies + msecs_to_jiffies(1000);
+       unsigned int i;
+       int alive;
+
+       do {
+               alive = 0;
+               for (i = 0; i < subs->nurbs; i++) {
+                       if (test_bit(i, &subs->active_mask))
+                               alive++;
+               }
+               if (subs->syncpipe) {
+                       for (i = 0; i < SYNC_URBS; i++) {
+                               if (test_bit(i + 16, &subs->active_mask))
+                                       alive++;
+                       }
+               }
+               if (! alive)
+                       break;
+               schedule_timeout_uninterruptible(1);
+       } while (time_before(jiffies, end_time));
+       if (alive)
+               snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+       return 0;
+}
+
+/*
+ * release a substream
+ */
+void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
+{
+       int i;
+
+       /* stop urbs (to be sure) */
+       deactivate_urbs(subs, force, 1);
+       wait_clear_urbs(subs);
+
+       for (i = 0; i < MAX_URBS; i++)
+               release_urb_ctx(&subs->dataurb[i]);
+       for (i = 0; i < SYNC_URBS; i++)
+               release_urb_ctx(&subs->syncurb[i]);
+       usb_buffer_free(subs->dev, SYNC_URBS * 4,
+                       subs->syncbuf, subs->sync_dma);
+       subs->syncbuf = NULL;
+       subs->nurbs = 0;
+}
+
+/*
+ * complete callback from data urb
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+       struct snd_urb_ctx *ctx = urb->context;
+       struct snd_usb_substream *subs = ctx->subs;
+       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
+       int err = 0;
+
+       if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
+           !subs->running || /* can be stopped during retire callback */
+           (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
+           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+               clear_bit(ctx->index, &subs->active_mask);
+               if (err < 0) {
+                       snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
+                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+               }
+       }
+}
+
+
+/*
+ * complete callback from sync urb
+ */
+static void snd_complete_sync_urb(struct urb *urb)
+{
+       struct snd_urb_ctx *ctx = urb->context;
+       struct snd_usb_substream *subs = ctx->subs;
+       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
+       int err = 0;
+
+       if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
+           !subs->running || /* can be stopped during retire callback */
+           (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
+           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+               clear_bit(ctx->index + 16, &subs->active_mask);
+               if (err < 0) {
+                       snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
+                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+               }
+       }
+}
+
+
+/*
+ * initialize a substream for plaback/capture
+ */
+int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
+                               unsigned int period_bytes,
+                               unsigned int rate,
+                               unsigned int frame_bits)
+{
+       unsigned int maxsize, i;
+       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+       unsigned int urb_packs, total_packs, packs_per_ms;
+       struct snd_usb_audio *chip = subs->stream->chip;
+
+       /* calculate the frequency in 16.16 format */
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
+               subs->freqn = get_usb_full_speed_rate(rate);
+       else
+               subs->freqn = get_usb_high_speed_rate(rate);
+       subs->freqm = subs->freqn;
+       /* calculate max. frequency */
+       if (subs->maxpacksize) {
+               /* whatever fits into a max. size packet */
+               maxsize = subs->maxpacksize;
+               subs->freqmax = (maxsize / (frame_bits >> 3))
+                               << (16 - subs->datainterval);
+       } else {
+               /* no max. packet size: just take 25% higher than nominal */
+               subs->freqmax = subs->freqn + (subs->freqn >> 2);
+               maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
+                               >> (16 - subs->datainterval);
+       }
+       subs->phase = 0;
+
+       if (subs->fill_max)
+               subs->curpacksize = subs->maxpacksize;
+       else
+               subs->curpacksize = maxsize;
+
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+               packs_per_ms = 8 >> subs->datainterval;
+       else
+               packs_per_ms = 1;
+
+       if (is_playback) {
+               urb_packs = max(chip->nrpacks, 1);
+               urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
+       } else
+               urb_packs = 1;
+       urb_packs *= packs_per_ms;
+       if (subs->syncpipe)
+               urb_packs = min(urb_packs, 1U << subs->syncinterval);
+
+       /* decide how many packets to be used */
+       if (is_playback) {
+               unsigned int minsize, maxpacks;
+               /* determine how small a packet can be */
+               minsize = (subs->freqn >> (16 - subs->datainterval))
+                         * (frame_bits >> 3);
+               /* with sync from device, assume it can be 12% lower */
+               if (subs->syncpipe)
+                       minsize -= minsize >> 3;
+               minsize = max(minsize, 1u);
+               total_packs = (period_bytes + minsize - 1) / minsize;
+               /* we need at least two URBs for queueing */
+               if (total_packs < 2) {
+                       total_packs = 2;
+               } else {
+                       /* and we don't want too long a queue either */
+                       maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
+                       total_packs = min(total_packs, maxpacks);
+               }
+       } else {
+               while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+                       urb_packs >>= 1;
+               total_packs = MAX_URBS * urb_packs;
+       }
+       subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+       if (subs->nurbs > MAX_URBS) {
+               /* too much... */
+               subs->nurbs = MAX_URBS;
+               total_packs = MAX_URBS * urb_packs;
+       } else if (subs->nurbs < 2) {
+               /* too little - we need at least two packets
+                * to ensure contiguous playback/capture
+                */
+               subs->nurbs = 2;
+       }
+
+       /* allocate and initialize data urbs */
+       for (i = 0; i < subs->nurbs; i++) {
+               struct snd_urb_ctx *u = &subs->dataurb[i];
+               u->index = i;
+               u->subs = subs;
+               u->packets = (i + 1) * total_packs / subs->nurbs
+                       - i * total_packs / subs->nurbs;
+               u->buffer_size = maxsize * u->packets;
+               if (subs->fmt_type == UAC_FORMAT_TYPE_II)
+                       u->packets++; /* for transfer delimiter */
+               u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+               if (!u->urb)
+                       goto out_of_memory;
+               u->urb->transfer_buffer =
+                       usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL,
+                                        &u->urb->transfer_dma);
+               if (!u->urb->transfer_buffer)
+                       goto out_of_memory;
+               u->urb->pipe = subs->datapipe;
+               u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+               u->urb->interval = 1 << subs->datainterval;
+               u->urb->context = u;
+               u->urb->complete = snd_complete_urb;
+       }
+
+       if (subs->syncpipe) {
+               /* allocate and initialize sync urbs */
+               subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4,
+                                                GFP_KERNEL, &subs->sync_dma);
+               if (!subs->syncbuf)
+                       goto out_of_memory;
+               for (i = 0; i < SYNC_URBS; i++) {
+                       struct snd_urb_ctx *u = &subs->syncurb[i];
+                       u->index = i;
+                       u->subs = subs;
+                       u->packets = 1;
+                       u->urb = usb_alloc_urb(1, GFP_KERNEL);
+                       if (!u->urb)
+                               goto out_of_memory;
+                       u->urb->transfer_buffer = subs->syncbuf + i * 4;
+                       u->urb->transfer_dma = subs->sync_dma + i * 4;
+                       u->urb->transfer_buffer_length = 4;
+                       u->urb->pipe = subs->syncpipe;
+                       u->urb->transfer_flags = URB_ISO_ASAP |
+                                                URB_NO_TRANSFER_DMA_MAP;
+                       u->urb->number_of_packets = 1;
+                       u->urb->interval = 1 << subs->syncinterval;
+                       u->urb->context = u;
+                       u->urb->complete = snd_complete_sync_urb;
+               }
+       }
+       return 0;
+
+out_of_memory:
+       snd_usb_release_substream_urbs(subs, 0);
+       return -ENOMEM;
+}
+
+/*
+ * prepare urb for full speed capture sync pipe
+ *
+ * fill the length and offset of each urb descriptor.
+ * the fixed 10.14 frequency is passed through the pipe.
+ */
+static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
+                                   struct snd_pcm_runtime *runtime,
+                                   struct urb *urb)
+{
+       unsigned char *cp = urb->transfer_buffer;
+       struct snd_urb_ctx *ctx = urb->context;
+
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->iso_frame_desc[0].length = 3;
+       urb->iso_frame_desc[0].offset = 0;
+       cp[0] = subs->freqn >> 2;
+       cp[1] = subs->freqn >> 10;
+       cp[2] = subs->freqn >> 18;
+       return 0;
+}
+
+/*
+ * prepare urb for high speed capture sync pipe
+ *
+ * fill the length and offset of each urb descriptor.
+ * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+ */
+static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
+                                      struct snd_pcm_runtime *runtime,
+                                      struct urb *urb)
+{
+       unsigned char *cp = urb->transfer_buffer;
+       struct snd_urb_ctx *ctx = urb->context;
+
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->iso_frame_desc[0].length = 4;
+       urb->iso_frame_desc[0].offset = 0;
+       cp[0] = subs->freqn;
+       cp[1] = subs->freqn >> 8;
+       cp[2] = subs->freqn >> 16;
+       cp[3] = subs->freqn >> 24;
+       return 0;
+}
+
+/*
+ * process after capture sync complete
+ * - nothing to do
+ */
+static int retire_capture_sync_urb(struct snd_usb_substream *subs,
+                                  struct snd_pcm_runtime *runtime,
+                                  struct urb *urb)
+{
+       return 0;
+}
+
+/*
+ * prepare urb for capture data pipe
+ *
+ * fill the offset and length of each descriptor.
+ *
+ * we use a temporary buffer to write the captured data.
+ * since the length of written data is determined by host, we cannot
+ * write onto the pcm buffer directly...  the data is thus copied
+ * later at complete callback to the global buffer.
+ */
+static int prepare_capture_urb(struct snd_usb_substream *subs,
+                              struct snd_pcm_runtime *runtime,
+                              struct urb *urb)
+{
+       int i, offs;
+       struct snd_urb_ctx *ctx = urb->context;
+
+       offs = 0;
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       for (i = 0; i < ctx->packets; i++) {
+               urb->iso_frame_desc[i].offset = offs;
+               urb->iso_frame_desc[i].length = subs->curpacksize;
+               offs += subs->curpacksize;
+       }
+       urb->transfer_buffer_length = offs;
+       urb->number_of_packets = ctx->packets;
+       return 0;
+}
+
+/*
+ * process after capture complete
+ *
+ * copy the data from each desctiptor to the pcm buffer, and
+ * update the current position.
+ */
+static int retire_capture_urb(struct snd_usb_substream *subs,
+                             struct snd_pcm_runtime *runtime,
+                             struct urb *urb)
+{
+       unsigned long flags;
+       unsigned char *cp;
+       int i;
+       unsigned int stride, frames, bytes, oldptr;
+       int period_elapsed = 0;
+
+       stride = runtime->frame_bits >> 3;
+
+       for (i = 0; i < urb->number_of_packets; i++) {
+               cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+               if (urb->iso_frame_desc[i].status) {
+                       snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
+                       // continue;
+               }
+               bytes = urb->iso_frame_desc[i].actual_length;
+               frames = bytes / stride;
+               if (!subs->txfr_quirk)
+                       bytes = frames * stride;
+               if (bytes % (runtime->sample_bits >> 3) != 0) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                       int oldbytes = bytes;
+#endif
+                       bytes = frames * stride;
+                       snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
+                                                       oldbytes, bytes);
+               }
+               /* update the current pointer */
+               spin_lock_irqsave(&subs->lock, flags);
+               oldptr = subs->hwptr_done;
+               subs->hwptr_done += bytes;
+               if (subs->hwptr_done >= runtime->buffer_size * stride)
+                       subs->hwptr_done -= runtime->buffer_size * stride;
+               frames = (bytes + (oldptr % stride)) / stride;
+               subs->transfer_done += frames;
+               if (subs->transfer_done >= runtime->period_size) {
+                       subs->transfer_done -= runtime->period_size;
+                       period_elapsed = 1;
+               }
+               spin_unlock_irqrestore(&subs->lock, flags);
+               /* copy a data chunk */
+               if (oldptr + bytes > runtime->buffer_size * stride) {
+                       unsigned int bytes1 =
+                                       runtime->buffer_size * stride - oldptr;
+                       memcpy(runtime->dma_area + oldptr, cp, bytes1);
+                       memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
+               } else {
+                       memcpy(runtime->dma_area + oldptr, cp, bytes);
+               }
+       }
+       if (period_elapsed)
+               snd_pcm_period_elapsed(subs->pcm_substream);
+       return 0;
+}
+
+/*
+ * Process after capture complete when paused.  Nothing to do.
+ */
+static int retire_paused_capture_urb(struct snd_usb_substream *subs,
+                                    struct snd_pcm_runtime *runtime,
+                                    struct urb *urb)
+{
+       return 0;
+}
+
+
+/*
+ * prepare urb for full speed playback sync pipe
+ *
+ * set up the offset and length to receive the current frequency.
+ */
+
+static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
+                                    struct snd_pcm_runtime *runtime,
+                                    struct urb *urb)
+{
+       struct snd_urb_ctx *ctx = urb->context;
+
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->iso_frame_desc[0].length = 3;
+       urb->iso_frame_desc[0].offset = 0;
+       return 0;
+}
+
+/*
+ * prepare urb for high speed playback sync pipe
+ *
+ * set up the offset and length to receive the current frequency.
+ */
+
+static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
+                                       struct snd_pcm_runtime *runtime,
+                                       struct urb *urb)
+{
+       struct snd_urb_ctx *ctx = urb->context;
+
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->iso_frame_desc[0].length = 4;
+       urb->iso_frame_desc[0].offset = 0;
+       return 0;
+}
+
+/*
+ * process after full speed playback sync complete
+ *
+ * retrieve the current 10.14 frequency from pipe, and set it.
+ * the value is referred in prepare_playback_urb().
+ */
+static int retire_playback_sync_urb(struct snd_usb_substream *subs,
+                                   struct snd_pcm_runtime *runtime,
+                                   struct urb *urb)
+{
+       unsigned int f;
+       unsigned long flags;
+
+       if (urb->iso_frame_desc[0].status == 0 &&
+           urb->iso_frame_desc[0].actual_length == 3) {
+               f = combine_triple((u8*)urb->transfer_buffer) << 2;
+               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+                       spin_lock_irqsave(&subs->lock, flags);
+                       subs->freqm = f;
+                       spin_unlock_irqrestore(&subs->lock, flags);
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * process after high speed playback sync complete
+ *
+ * retrieve the current 12.13 frequency from pipe, and set it.
+ * the value is referred in prepare_playback_urb().
+ */
+static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
+                                      struct snd_pcm_runtime *runtime,
+                                      struct urb *urb)
+{
+       unsigned int f;
+       unsigned long flags;
+
+       if (urb->iso_frame_desc[0].status == 0 &&
+           urb->iso_frame_desc[0].actual_length == 4) {
+               f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
+               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+                       spin_lock_irqsave(&subs->lock, flags);
+                       subs->freqm = f;
+                       spin_unlock_irqrestore(&subs->lock, flags);
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
+ *
+ * These devices return the number of samples per packet instead of the number
+ * of samples per microframe.
+ */
+static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs,
+                                          struct snd_pcm_runtime *runtime,
+                                          struct urb *urb)
+{
+       unsigned int f;
+       unsigned long flags;
+
+       if (urb->iso_frame_desc[0].status == 0 &&
+           urb->iso_frame_desc[0].actual_length == 4) {
+               f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
+               f >>= subs->datainterval;
+               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+                       spin_lock_irqsave(&subs->lock, flags);
+                       subs->freqm = f;
+                       spin_unlock_irqrestore(&subs->lock, flags);
+               }
+       }
+
+       return 0;
+}
+
+/* determine the number of frames in the next packet */
+static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
+{
+       if (subs->fill_max)
+               return subs->maxframesize;
+       else {
+               subs->phase = (subs->phase & 0xffff)
+                       + (subs->freqm << subs->datainterval);
+               return min(subs->phase >> 16, subs->maxframesize);
+       }
+}
+
+/*
+ * Prepare urb for streaming before playback starts or when paused.
+ *
+ * We don't have any data, so we send silence.
+ */
+static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
+                                      struct snd_pcm_runtime *runtime,
+                                      struct urb *urb)
+{
+       unsigned int i, offs, counts;
+       struct snd_urb_ctx *ctx = urb->context;
+       int stride = runtime->frame_bits >> 3;
+
+       offs = 0;
+       urb->dev = ctx->subs->dev;
+       for (i = 0; i < ctx->packets; ++i) {
+               counts = snd_usb_audio_next_packet_size(subs);
+               urb->iso_frame_desc[i].offset = offs * stride;
+               urb->iso_frame_desc[i].length = counts * stride;
+               offs += counts;
+       }
+       urb->number_of_packets = ctx->packets;
+       urb->transfer_buffer_length = offs * stride;
+       memset(urb->transfer_buffer,
+              runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
+              offs * stride);
+       return 0;
+}
+
+/*
+ * prepare urb for playback data pipe
+ *
+ * Since a URB can handle only a single linear buffer, we must use double
+ * buffering when the data to be transferred overflows the buffer boundary.
+ * To avoid inconsistencies when updating hwptr_done, we use double buffering
+ * for all URBs.
+ */
+static int prepare_playback_urb(struct snd_usb_substream *subs,
+                               struct snd_pcm_runtime *runtime,
+                               struct urb *urb)
+{
+       int i, stride;
+       unsigned int counts, frames, bytes;
+       unsigned long flags;
+       int period_elapsed = 0;
+       struct snd_urb_ctx *ctx = urb->context;
+
+       stride = runtime->frame_bits >> 3;
+
+       frames = 0;
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->number_of_packets = 0;
+       spin_lock_irqsave(&subs->lock, flags);
+       for (i = 0; i < ctx->packets; i++) {
+               counts = snd_usb_audio_next_packet_size(subs);
+               /* set up descriptor */
+               urb->iso_frame_desc[i].offset = frames * stride;
+               urb->iso_frame_desc[i].length = counts * stride;
+               frames += counts;
+               urb->number_of_packets++;
+               subs->transfer_done += counts;
+               if (subs->transfer_done >= runtime->period_size) {
+                       subs->transfer_done -= runtime->period_size;
+                       period_elapsed = 1;
+                       if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
+                               if (subs->transfer_done > 0) {
+                                       /* FIXME: fill-max mode is not
+                                        * supported yet */
+                                       frames -= subs->transfer_done;
+                                       counts -= subs->transfer_done;
+                                       urb->iso_frame_desc[i].length =
+                                               counts * stride;
+                                       subs->transfer_done = 0;
+                               }
+                               i++;
+                               if (i < ctx->packets) {
+                                       /* add a transfer delimiter */
+                                       urb->iso_frame_desc[i].offset =
+                                               frames * stride;
+                                       urb->iso_frame_desc[i].length = 0;
+                                       urb->number_of_packets++;
+                               }
+                               break;
+                       }
+               }
+               if (period_elapsed) /* finish at the period boundary */
+                       break;
+       }
+       bytes = frames * stride;
+       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+               /* err, the transferred area goes over buffer boundary. */
+               unsigned int bytes1 =
+                       runtime->buffer_size * stride - subs->hwptr_done;
+               memcpy(urb->transfer_buffer,
+                      runtime->dma_area + subs->hwptr_done, bytes1);
+               memcpy(urb->transfer_buffer + bytes1,
+                      runtime->dma_area, bytes - bytes1);
+       } else {
+               memcpy(urb->transfer_buffer,
+                      runtime->dma_area + subs->hwptr_done, bytes);
+       }
+       subs->hwptr_done += bytes;
+       if (subs->hwptr_done >= runtime->buffer_size * stride)
+               subs->hwptr_done -= runtime->buffer_size * stride;
+       runtime->delay += frames;
+       spin_unlock_irqrestore(&subs->lock, flags);
+       urb->transfer_buffer_length = bytes;
+       if (period_elapsed)
+               snd_pcm_period_elapsed(subs->pcm_substream);
+       return 0;
+}
+
+/*
+ * process after playback data complete
+ * - decrease the delay count again
+ */
+static int retire_playback_urb(struct snd_usb_substream *subs,
+                              struct snd_pcm_runtime *runtime,
+                              struct urb *urb)
+{
+       unsigned long flags;
+       int stride = runtime->frame_bits >> 3;
+       int processed = urb->transfer_buffer_length / stride;
+
+       spin_lock_irqsave(&subs->lock, flags);
+       if (processed > runtime->delay)
+               runtime->delay = 0;
+       else
+               runtime->delay -= processed;
+       spin_unlock_irqrestore(&subs->lock, flags);
+       return 0;
+}
+
+static const char *usb_error_string(int err)
+{
+       switch (err) {
+       case -ENODEV:
+               return "no device";
+       case -ENOENT:
+               return "endpoint not enabled";
+       case -EPIPE:
+               return "endpoint stalled";
+       case -ENOSPC:
+               return "not enough bandwidth";
+       case -ESHUTDOWN:
+               return "device disabled";
+       case -EHOSTUNREACH:
+               return "device suspended";
+       case -EINVAL:
+       case -EAGAIN:
+       case -EFBIG:
+       case -EMSGSIZE:
+               return "internal error";
+       default:
+               return "unknown error";
+       }
+}
+
+/*
+ * set up and start data/sync urbs
+ */
+static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
+{
+       unsigned int i;
+       int err;
+
+       if (subs->stream->chip->shutdown)
+               return -EBADFD;
+
+       for (i = 0; i < subs->nurbs; i++) {
+               if (snd_BUG_ON(!subs->dataurb[i].urb))
+                       return -EINVAL;
+               if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
+                       snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
+                       goto __error;
+               }
+       }
+       if (subs->syncpipe) {
+               for (i = 0; i < SYNC_URBS; i++) {
+                       if (snd_BUG_ON(!subs->syncurb[i].urb))
+                               return -EINVAL;
+                       if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
+                               snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
+                               goto __error;
+                       }
+               }
+       }
+
+       subs->active_mask = 0;
+       subs->unlink_mask = 0;
+       subs->running = 1;
+       for (i = 0; i < subs->nurbs; i++) {
+               err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
+               if (err < 0) {
+                       snd_printk(KERN_ERR "cannot submit datapipe "
+                                  "for urb %d, error %d: %s\n",
+                                  i, err, usb_error_string(err));
+                       goto __error;
+               }
+               set_bit(i, &subs->active_mask);
+       }
+       if (subs->syncpipe) {
+               for (i = 0; i < SYNC_URBS; i++) {
+                       err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
+                       if (err < 0) {
+                               snd_printk(KERN_ERR "cannot submit syncpipe "
+                                          "for urb %d, error %d: %s\n",
+                                          i, err, usb_error_string(err));
+                               goto __error;
+                       }
+                       set_bit(i + 16, &subs->active_mask);
+               }
+       }
+       return 0;
+
+ __error:
+       // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
+       deactivate_urbs(subs, 0, 0);
+       return -EPIPE;
+}
+
+
+/*
+ */
+static struct snd_urb_ops audio_urb_ops[2] = {
+       {
+               .prepare =      prepare_nodata_playback_urb,
+               .retire =       retire_playback_urb,
+               .prepare_sync = prepare_playback_sync_urb,
+               .retire_sync =  retire_playback_sync_urb,
+       },
+       {
+               .prepare =      prepare_capture_urb,
+               .retire =       retire_capture_urb,
+               .prepare_sync = prepare_capture_sync_urb,
+               .retire_sync =  retire_capture_sync_urb,
+       },
+};
+
+static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
+       {
+               .prepare =      prepare_nodata_playback_urb,
+               .retire =       retire_playback_urb,
+               .prepare_sync = prepare_playback_sync_urb_hs,
+               .retire_sync =  retire_playback_sync_urb_hs,
+       },
+       {
+               .prepare =      prepare_capture_urb,
+               .retire =       retire_capture_urb,
+               .prepare_sync = prepare_capture_sync_urb_hs,
+               .retire_sync =  retire_capture_sync_urb,
+       },
+};
+
+/*
+ * initialize the substream instance.
+ */
+
+void snd_usb_init_substream(struct snd_usb_stream *as,
+                           int stream, struct audioformat *fp)
+{
+       struct snd_usb_substream *subs = &as->substream[stream];
+
+       INIT_LIST_HEAD(&subs->fmt_list);
+       spin_lock_init(&subs->lock);
+
+       subs->stream = as;
+       subs->direction = stream;
+       subs->dev = as->chip->dev;
+       subs->txfr_quirk = as->chip->txfr_quirk;
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
+               subs->ops = audio_urb_ops[stream];
+       } else {
+               subs->ops = audio_urb_ops_high_speed[stream];
+               switch (as->chip->usb_id) {
+               case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
+               case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
+               case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
+                       subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
+                       break;
+               }
+       }
+
+       snd_usb_set_pcm_ops(as->pcm, stream);
+
+       list_add_tail(&fp->list, &subs->fmt_list);
+       subs->formats |= fp->formats;
+       subs->endpoint = fp->endpoint;
+       subs->num_formats++;
+       subs->fmt_type = fp->fmt_type;
+}
+
+int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               subs->ops.prepare = prepare_playback_urb;
+               return 0;
+       case SNDRV_PCM_TRIGGER_STOP:
+               return deactivate_urbs(subs, 0, 0);
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               subs->ops.prepare = prepare_nodata_playback_urb;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               subs->ops.retire = retire_capture_urb;
+               return start_urbs(subs, substream->runtime);
+       case SNDRV_PCM_TRIGGER_STOP:
+               return deactivate_urbs(subs, 0, 0);
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               subs->ops.retire = retire_paused_capture_urb;
+               return 0;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               subs->ops.retire = retire_capture_urb;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+int snd_usb_substream_prepare(struct snd_usb_substream *subs,
+                             struct snd_pcm_runtime *runtime)
+{
+       /* clear urbs (to be sure) */
+       deactivate_urbs(subs, 0, 1);
+       wait_clear_urbs(subs);
+
+       /* for playback, submit the URBs now; otherwise, the first hwptr_done
+        * updates for all URBs would happen at the same time when starting */
+       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+               subs->ops.prepare = prepare_nodata_playback_urb;
+               return start_urbs(subs, runtime);
+       }
+
+       return 0;
+}
+
diff --git a/sound/usb/urb.h b/sound/usb/urb.h
new file mode 100644 (file)
index 0000000..888da38
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __USBAUDIO_URB_H
+#define __USBAUDIO_URB_H
+
+void snd_usb_init_substream(struct snd_usb_stream *as,
+                           int stream,
+                           struct audioformat *fp);
+
+int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
+                               unsigned int period_bytes,
+                               unsigned int rate,
+                               unsigned int frame_bits);
+
+void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
+
+int snd_usb_substream_prepare(struct snd_usb_substream *subs,
+                             struct snd_pcm_runtime *runtime);
+
+int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
+int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
+
+#endif /* __USBAUDIO_URB_H */
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
deleted file mode 100644 (file)
index 11b0826..0000000
+++ /dev/null
@@ -1,4050 +0,0 @@
-/*
- *   (Tentative) USB Audio Driver for ALSA
- *
- *   Main and PCM part
- *
- *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- *   Many codes borrowed from audio.c by
- *         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.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- *
- *  NOTES:
- *
- *   - async unlink should be used for avoiding the sleep inside lock.
- *     2.4.22 usb-uhci seems buggy for async unlinking and results in
- *     oops.  in such a cse, pass async_unlink=0 option.
- *   - the linked URBs would be preferred but not used so far because of
- *     the instability of unlinking.
- *   - type II is not supported properly.  there is no device which supports
- *     this type *correctly*.  SB extigy looks as if it supports, but it's
- *     indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream).
- */
-
-
-#include <linux/bitops.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/usb.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
-#include <linux/usb/audio.h>
-#include <linux/usb/ch9.h>
-
-#include <sound/core.h>
-#include <sound/info.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-
-#include "usbaudio.h"
-
-
-MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
-MODULE_DESCRIPTION("USB Audio");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}");
-
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
-/* Vendor/product IDs for this card */
-static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
-static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
-static int nrpacks = 8;                /* max. number of packets per urb */
-static int async_unlink = 1;
-static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/
-static int ignore_ctl_error;
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for the USB audio adapter.");
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable USB audio adapter.");
-module_param_array(vid, int, NULL, 0444);
-MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");
-module_param_array(pid, int, NULL, 0444);
-MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");
-module_param(nrpacks, int, 0644);
-MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB.");
-module_param(async_unlink, bool, 0444);
-MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
-module_param_array(device_setup, int, NULL, 0444);
-MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
-module_param(ignore_ctl_error, bool, 0444);
-MODULE_PARM_DESC(ignore_ctl_error,
-                "Ignore errors from USB controller for mixer interfaces.");
-
-/*
- * debug the h/w constraints
- */
-/* #define HW_CONST_DEBUG */
-
-
-/*
- *
- */
-
-#define MAX_PACKS      20
-#define MAX_PACKS_HS   (MAX_PACKS * 8) /* in high speed mode */
-#define MAX_URBS       8
-#define SYNC_URBS      4       /* always four urbs for sync */
-#define MAX_QUEUE      24      /* try not to exceed this queue length, in ms */
-
-struct audioformat {
-       struct list_head list;
-       snd_pcm_format_t format;        /* format type */
-       unsigned int channels;          /* # channels */
-       unsigned int fmt_type;          /* USB audio format type (1-3) */
-       unsigned int frame_size;        /* samples per frame for non-audio */
-       int iface;                      /* interface number */
-       unsigned char altsetting;       /* corresponding alternate setting */
-       unsigned char altset_idx;       /* array index of altenate setting */
-       unsigned char attributes;       /* corresponding attributes of cs endpoint */
-       unsigned char endpoint;         /* endpoint */
-       unsigned char ep_attr;          /* endpoint attributes */
-       unsigned char datainterval;     /* log_2 of data packet interval */
-       unsigned int maxpacksize;       /* max. packet size */
-       unsigned int rates;             /* rate bitmasks */
-       unsigned int rate_min, rate_max;        /* min/max rates */
-       unsigned int nr_rates;          /* number of rate table entries */
-       unsigned int *rate_table;       /* rate table */
-};
-
-struct snd_usb_substream;
-
-struct snd_urb_ctx {
-       struct urb *urb;
-       unsigned int buffer_size;       /* size of data buffer, if data URB */
-       struct snd_usb_substream *subs;
-       int index;      /* index for urb array */
-       int packets;    /* number of packets per urb */
-};
-
-struct snd_urb_ops {
-       int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-       int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-       int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-       int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-};
-
-struct snd_usb_substream {
-       struct snd_usb_stream *stream;
-       struct usb_device *dev;
-       struct snd_pcm_substream *pcm_substream;
-       int direction;  /* playback or capture */
-       int interface;  /* current interface */
-       int endpoint;   /* assigned endpoint */
-       struct audioformat *cur_audiofmt;       /* current audioformat pointer (for hw_params callback) */
-       unsigned int cur_rate;          /* current rate (for hw_params callback) */
-       unsigned int period_bytes;      /* current period bytes (for hw_params callback) */
-       unsigned int format;     /* USB data format */
-       unsigned int datapipe;   /* the data i/o pipe */
-       unsigned int syncpipe;   /* 1 - async out or adaptive in */
-       unsigned int datainterval;      /* log_2 of data packet interval */
-       unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
-       unsigned int freqn;      /* nominal sampling rate in fs/fps in Q16.16 format */
-       unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
-       unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
-       unsigned int phase;      /* phase accumulator */
-       unsigned int maxpacksize;       /* max packet size in bytes */
-       unsigned int maxframesize;      /* max packet size in frames */
-       unsigned int curpacksize;       /* current packet size in bytes (for capture) */
-       unsigned int curframesize;      /* current packet size in frames (for capture) */
-       unsigned int fill_max: 1;       /* fill max packet size always */
-       unsigned int txfr_quirk:1;      /* allow sub-frame alignment */
-       unsigned int fmt_type;          /* USB audio format type (1-3) */
-
-       unsigned int running: 1;        /* running status */
-
-       unsigned int hwptr_done;        /* processed byte position in the buffer */
-       unsigned int transfer_done;             /* processed frames since last period update */
-       unsigned long active_mask;      /* bitmask of active urbs */
-       unsigned long unlink_mask;      /* bitmask of unlinked urbs */
-
-       unsigned int nurbs;                     /* # urbs */
-       struct snd_urb_ctx dataurb[MAX_URBS];   /* data urb table */
-       struct snd_urb_ctx syncurb[SYNC_URBS];  /* sync urb table */
-       char *syncbuf;                          /* sync buffer for all sync URBs */
-       dma_addr_t sync_dma;                    /* DMA address of syncbuf */
-
-       u64 formats;                    /* format bitmasks (all or'ed) */
-       unsigned int num_formats;               /* number of supported audio formats (list) */
-       struct list_head fmt_list;      /* format list */
-       struct snd_pcm_hw_constraint_list rate_list;    /* limited rates */
-       spinlock_t lock;
-
-       struct snd_urb_ops ops;         /* callbacks (must be filled at init) */
-};
-
-
-struct snd_usb_stream {
-       struct snd_usb_audio *chip;
-       struct snd_pcm *pcm;
-       int pcm_index;
-       unsigned int fmt_type;          /* USB audio format type (1-3) */
-       struct snd_usb_substream substream[2];
-       struct list_head list;
-};
-
-
-/*
- * we keep the snd_usb_audio_t instances by ourselves for merging
- * the all interfaces on the same card as one sound device.
- */
-
-static DEFINE_MUTEX(register_mutex);
-static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
-
-
-/*
- * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
- * this will overflow at approx 524 kHz
- */
-static inline unsigned get_usb_full_speed_rate(unsigned int rate)
-{
-       return ((rate << 13) + 62) / 125;
-}
-
-/*
- * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
- * this will overflow at approx 4 MHz
- */
-static inline unsigned get_usb_high_speed_rate(unsigned int rate)
-{
-       return ((rate << 10) + 62) / 125;
-}
-
-/* convert our full speed USB rate into sampling rate in Hz */
-static inline unsigned get_full_speed_hz(unsigned int usb_rate)
-{
-       return (usb_rate * 125 + (1 << 12)) >> 13;
-}
-
-/* convert our high speed USB rate into sampling rate in Hz */
-static inline unsigned get_high_speed_hz(unsigned int usb_rate)
-{
-       return (usb_rate * 125 + (1 << 9)) >> 10;
-}
-
-
-/*
- * prepare urb for full speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 10.14 frequency is passed through the pipe.
- */
-static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
-                                   struct snd_pcm_runtime *runtime,
-                                   struct urb *urb)
-{
-       unsigned char *cp = urb->transfer_buffer;
-       struct snd_urb_ctx *ctx = urb->context;
-
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 3;
-       urb->iso_frame_desc[0].offset = 0;
-       cp[0] = subs->freqn >> 2;
-       cp[1] = subs->freqn >> 10;
-       cp[2] = subs->freqn >> 18;
-       return 0;
-}
-
-/*
- * prepare urb for high speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 12.13 frequency is passed as 16.16 through the pipe.
- */
-static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
-                                      struct snd_pcm_runtime *runtime,
-                                      struct urb *urb)
-{
-       unsigned char *cp = urb->transfer_buffer;
-       struct snd_urb_ctx *ctx = urb->context;
-
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 4;
-       urb->iso_frame_desc[0].offset = 0;
-       cp[0] = subs->freqn;
-       cp[1] = subs->freqn >> 8;
-       cp[2] = subs->freqn >> 16;
-       cp[3] = subs->freqn >> 24;
-       return 0;
-}
-
-/*
- * process after capture sync complete
- * - nothing to do
- */
-static int retire_capture_sync_urb(struct snd_usb_substream *subs,
-                                  struct snd_pcm_runtime *runtime,
-                                  struct urb *urb)
-{
-       return 0;
-}
-
-/*
- * prepare urb for capture data pipe
- *
- * fill the offset and length of each descriptor.
- *
- * we use a temporary buffer to write the captured data.
- * since the length of written data is determined by host, we cannot
- * write onto the pcm buffer directly...  the data is thus copied
- * later at complete callback to the global buffer.
- */
-static int prepare_capture_urb(struct snd_usb_substream *subs,
-                              struct snd_pcm_runtime *runtime,
-                              struct urb *urb)
-{
-       int i, offs;
-       struct snd_urb_ctx *ctx = urb->context;
-
-       offs = 0;
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       for (i = 0; i < ctx->packets; i++) {
-               urb->iso_frame_desc[i].offset = offs;
-               urb->iso_frame_desc[i].length = subs->curpacksize;
-               offs += subs->curpacksize;
-       }
-       urb->transfer_buffer_length = offs;
-       urb->number_of_packets = ctx->packets;
-       return 0;
-}
-
-/*
- * process after capture complete
- *
- * copy the data from each desctiptor to the pcm buffer, and
- * update the current position.
- */
-static int retire_capture_urb(struct snd_usb_substream *subs,
-                             struct snd_pcm_runtime *runtime,
-                             struct urb *urb)
-{
-       unsigned long flags;
-       unsigned char *cp;
-       int i;
-       unsigned int stride, frames, bytes, oldptr;
-       int period_elapsed = 0;
-
-       stride = runtime->frame_bits >> 3;
-
-       for (i = 0; i < urb->number_of_packets; i++) {
-               cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-               if (urb->iso_frame_desc[i].status) {
-                       snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
-                       // continue;
-               }
-               bytes = urb->iso_frame_desc[i].actual_length;
-               frames = bytes / stride;
-               if (!subs->txfr_quirk)
-                       bytes = frames * stride;
-               if (bytes % (runtime->sample_bits >> 3) != 0) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-                       int oldbytes = bytes;
-#endif
-                       bytes = frames * stride;
-                       snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
-                                                       oldbytes, bytes);
-               }
-               /* update the current pointer */
-               spin_lock_irqsave(&subs->lock, flags);
-               oldptr = subs->hwptr_done;
-               subs->hwptr_done += bytes;
-               if (subs->hwptr_done >= runtime->buffer_size * stride)
-                       subs->hwptr_done -= runtime->buffer_size * stride;
-               frames = (bytes + (oldptr % stride)) / stride;
-               subs->transfer_done += frames;
-               if (subs->transfer_done >= runtime->period_size) {
-                       subs->transfer_done -= runtime->period_size;
-                       period_elapsed = 1;
-               }
-               spin_unlock_irqrestore(&subs->lock, flags);
-               /* copy a data chunk */
-               if (oldptr + bytes > runtime->buffer_size * stride) {
-                       unsigned int bytes1 =
-                                       runtime->buffer_size * stride - oldptr;
-                       memcpy(runtime->dma_area + oldptr, cp, bytes1);
-                       memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
-               } else {
-                       memcpy(runtime->dma_area + oldptr, cp, bytes);
-               }
-       }
-       if (period_elapsed)
-               snd_pcm_period_elapsed(subs->pcm_substream);
-       return 0;
-}
-
-/*
- * Process after capture complete when paused.  Nothing to do.
- */
-static int retire_paused_capture_urb(struct snd_usb_substream *subs,
-                                    struct snd_pcm_runtime *runtime,
-                                    struct urb *urb)
-{
-       return 0;
-}
-
-
-/*
- * prepare urb for full speed playback sync pipe
- *
- * set up the offset and length to receive the current frequency.
- */
-
-static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
-                                    struct snd_pcm_runtime *runtime,
-                                    struct urb *urb)
-{
-       struct snd_urb_ctx *ctx = urb->context;
-
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 3;
-       urb->iso_frame_desc[0].offset = 0;
-       return 0;
-}
-
-/*
- * prepare urb for high speed playback sync pipe
- *
- * set up the offset and length to receive the current frequency.
- */
-
-static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
-                                       struct snd_pcm_runtime *runtime,
-                                       struct urb *urb)
-{
-       struct snd_urb_ctx *ctx = urb->context;
-
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 4;
-       urb->iso_frame_desc[0].offset = 0;
-       return 0;
-}
-
-/*
- * process after full speed playback sync complete
- *
- * retrieve the current 10.14 frequency from pipe, and set it.
- * the value is referred in prepare_playback_urb().
- */
-static int retire_playback_sync_urb(struct snd_usb_substream *subs,
-                                   struct snd_pcm_runtime *runtime,
-                                   struct urb *urb)
-{
-       unsigned int f;
-       unsigned long flags;
-
-       if (urb->iso_frame_desc[0].status == 0 &&
-           urb->iso_frame_desc[0].actual_length == 3) {
-               f = combine_triple((u8*)urb->transfer_buffer) << 2;
-               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
-                       spin_lock_irqsave(&subs->lock, flags);
-                       subs->freqm = f;
-                       spin_unlock_irqrestore(&subs->lock, flags);
-               }
-       }
-
-       return 0;
-}
-
-/*
- * process after high speed playback sync complete
- *
- * retrieve the current 12.13 frequency from pipe, and set it.
- * the value is referred in prepare_playback_urb().
- */
-static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
-                                      struct snd_pcm_runtime *runtime,
-                                      struct urb *urb)
-{
-       unsigned int f;
-       unsigned long flags;
-
-       if (urb->iso_frame_desc[0].status == 0 &&
-           urb->iso_frame_desc[0].actual_length == 4) {
-               f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
-               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
-                       spin_lock_irqsave(&subs->lock, flags);
-                       subs->freqm = f;
-                       spin_unlock_irqrestore(&subs->lock, flags);
-               }
-       }
-
-       return 0;
-}
-
-/*
- * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
- *
- * These devices return the number of samples per packet instead of the number
- * of samples per microframe.
- */
-static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs,
-                                          struct snd_pcm_runtime *runtime,
-                                          struct urb *urb)
-{
-       unsigned int f;
-       unsigned long flags;
-
-       if (urb->iso_frame_desc[0].status == 0 &&
-           urb->iso_frame_desc[0].actual_length == 4) {
-               f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
-               f >>= subs->datainterval;
-               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
-                       spin_lock_irqsave(&subs->lock, flags);
-                       subs->freqm = f;
-                       spin_unlock_irqrestore(&subs->lock, flags);
-               }
-       }
-
-       return 0;
-}
-
-/* determine the number of frames in the next packet */
-static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
-{
-       if (subs->fill_max)
-               return subs->maxframesize;
-       else {
-               subs->phase = (subs->phase & 0xffff)
-                       + (subs->freqm << subs->datainterval);
-               return min(subs->phase >> 16, subs->maxframesize);
-       }
-}
-
-/*
- * Prepare urb for streaming before playback starts or when paused.
- *
- * We don't have any data, so we send silence.
- */
-static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
-                                      struct snd_pcm_runtime *runtime,
-                                      struct urb *urb)
-{
-       unsigned int i, offs, counts;
-       struct snd_urb_ctx *ctx = urb->context;
-       int stride = runtime->frame_bits >> 3;
-
-       offs = 0;
-       urb->dev = ctx->subs->dev;
-       for (i = 0; i < ctx->packets; ++i) {
-               counts = snd_usb_audio_next_packet_size(subs);
-               urb->iso_frame_desc[i].offset = offs * stride;
-               urb->iso_frame_desc[i].length = counts * stride;
-               offs += counts;
-       }
-       urb->number_of_packets = ctx->packets;
-       urb->transfer_buffer_length = offs * stride;
-       memset(urb->transfer_buffer,
-              subs->cur_audiofmt->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
-              offs * stride);
-       return 0;
-}
-
-/*
- * prepare urb for playback data pipe
- *
- * Since a URB can handle only a single linear buffer, we must use double
- * buffering when the data to be transferred overflows the buffer boundary.
- * To avoid inconsistencies when updating hwptr_done, we use double buffering
- * for all URBs.
- */
-static int prepare_playback_urb(struct snd_usb_substream *subs,
-                               struct snd_pcm_runtime *runtime,
-                               struct urb *urb)
-{
-       int i, stride;
-       unsigned int counts, frames, bytes;
-       unsigned long flags;
-       int period_elapsed = 0;
-       struct snd_urb_ctx *ctx = urb->context;
-
-       stride = runtime->frame_bits >> 3;
-
-       frames = 0;
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->number_of_packets = 0;
-       spin_lock_irqsave(&subs->lock, flags);
-       for (i = 0; i < ctx->packets; i++) {
-               counts = snd_usb_audio_next_packet_size(subs);
-               /* set up descriptor */
-               urb->iso_frame_desc[i].offset = frames * stride;
-               urb->iso_frame_desc[i].length = counts * stride;
-               frames += counts;
-               urb->number_of_packets++;
-               subs->transfer_done += counts;
-               if (subs->transfer_done >= runtime->period_size) {
-                       subs->transfer_done -= runtime->period_size;
-                       period_elapsed = 1;
-                       if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
-                               if (subs->transfer_done > 0) {
-                                       /* FIXME: fill-max mode is not
-                                        * supported yet */
-                                       frames -= subs->transfer_done;
-                                       counts -= subs->transfer_done;
-                                       urb->iso_frame_desc[i].length =
-                                               counts * stride;
-                                       subs->transfer_done = 0;
-                               }
-                               i++;
-                               if (i < ctx->packets) {
-                                       /* add a transfer delimiter */
-                                       urb->iso_frame_desc[i].offset =
-                                               frames * stride;
-                                       urb->iso_frame_desc[i].length = 0;
-                                       urb->number_of_packets++;
-                               }
-                               break;
-                       }
-               }
-               if (period_elapsed) /* finish at the period boundary */
-                       break;
-       }
-       bytes = frames * stride;
-       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
-               /* err, the transferred area goes over buffer boundary. */
-               unsigned int bytes1 =
-                       runtime->buffer_size * stride - subs->hwptr_done;
-               memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done, bytes1);
-               memcpy(urb->transfer_buffer + bytes1,
-                      runtime->dma_area, bytes - bytes1);
-       } else {
-               memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done, bytes);
-       }
-       subs->hwptr_done += bytes;
-       if (subs->hwptr_done >= runtime->buffer_size * stride)
-               subs->hwptr_done -= runtime->buffer_size * stride;
-       runtime->delay += frames;
-       spin_unlock_irqrestore(&subs->lock, flags);
-       urb->transfer_buffer_length = bytes;
-       if (period_elapsed)
-               snd_pcm_period_elapsed(subs->pcm_substream);
-       return 0;
-}
-
-/*
- * process after playback data complete
- * - decrease the delay count again
- */
-static int retire_playback_urb(struct snd_usb_substream *subs,
-                              struct snd_pcm_runtime *runtime,
-                              struct urb *urb)
-{
-       unsigned long flags;
-       int stride = runtime->frame_bits >> 3;
-       int processed = urb->transfer_buffer_length / stride;
-
-       spin_lock_irqsave(&subs->lock, flags);
-       if (processed > runtime->delay)
-               runtime->delay = 0;
-       else
-               runtime->delay -= processed;
-       spin_unlock_irqrestore(&subs->lock, flags);
-       return 0;
-}
-
-
-/*
- */
-static struct snd_urb_ops audio_urb_ops[2] = {
-       {
-               .prepare =      prepare_nodata_playback_urb,
-               .retire =       retire_playback_urb,
-               .prepare_sync = prepare_playback_sync_urb,
-               .retire_sync =  retire_playback_sync_urb,
-       },
-       {
-               .prepare =      prepare_capture_urb,
-               .retire =       retire_capture_urb,
-               .prepare_sync = prepare_capture_sync_urb,
-               .retire_sync =  retire_capture_sync_urb,
-       },
-};
-
-static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
-       {
-               .prepare =      prepare_nodata_playback_urb,
-               .retire =       retire_playback_urb,
-               .prepare_sync = prepare_playback_sync_urb_hs,
-               .retire_sync =  retire_playback_sync_urb_hs,
-       },
-       {
-               .prepare =      prepare_capture_urb,
-               .retire =       retire_capture_urb,
-               .prepare_sync = prepare_capture_sync_urb_hs,
-               .retire_sync =  retire_capture_sync_urb,
-       },
-};
-
-/*
- * complete callback from data urb
- */
-static void snd_complete_urb(struct urb *urb)
-{
-       struct snd_urb_ctx *ctx = urb->context;
-       struct snd_usb_substream *subs = ctx->subs;
-       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
-       int err = 0;
-
-       if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
-           !subs->running || /* can be stopped during retire callback */
-           (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
-           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-               clear_bit(ctx->index, &subs->active_mask);
-               if (err < 0) {
-                       snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
-                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-               }
-       }
-}
-
-
-/*
- * complete callback from sync urb
- */
-static void snd_complete_sync_urb(struct urb *urb)
-{
-       struct snd_urb_ctx *ctx = urb->context;
-       struct snd_usb_substream *subs = ctx->subs;
-       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
-       int err = 0;
-
-       if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
-           !subs->running || /* can be stopped during retire callback */
-           (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
-           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-               clear_bit(ctx->index + 16, &subs->active_mask);
-               if (err < 0) {
-                       snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
-                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-               }
-       }
-}
-
-
-/*
- * unlink active urbs.
- */
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
-{
-       unsigned int i;
-       int async;
-
-       subs->running = 0;
-
-       if (!force && subs->stream->chip->shutdown) /* to be sure... */
-               return -EBADFD;
-
-       async = !can_sleep && async_unlink;
-
-       if (!async && in_interrupt())
-               return 0;
-
-       for (i = 0; i < subs->nurbs; i++) {
-               if (test_bit(i, &subs->active_mask)) {
-                       if (!test_and_set_bit(i, &subs->unlink_mask)) {
-                               struct urb *u = subs->dataurb[i].urb;
-                               if (async)
-                                       usb_unlink_urb(u);
-                               else
-                                       usb_kill_urb(u);
-                       }
-               }
-       }
-       if (subs->syncpipe) {
-               for (i = 0; i < SYNC_URBS; i++) {
-                       if (test_bit(i+16, &subs->active_mask)) {
-                               if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
-                                       struct urb *u = subs->syncurb[i].urb;
-                                       if (async)
-                                               usb_unlink_urb(u);
-                                       else
-                                               usb_kill_urb(u);
-                               }
-                       }
-               }
-       }
-       return 0;
-}
-
-
-static const char *usb_error_string(int err)
-{
-       switch (err) {
-       case -ENODEV:
-               return "no device";
-       case -ENOENT:
-               return "endpoint not enabled";
-       case -EPIPE:
-               return "endpoint stalled";
-       case -ENOSPC:
-               return "not enough bandwidth";
-       case -ESHUTDOWN:
-               return "device disabled";
-       case -EHOSTUNREACH:
-               return "device suspended";
-       case -EINVAL:
-       case -EAGAIN:
-       case -EFBIG:
-       case -EMSGSIZE:
-               return "internal error";
-       default:
-               return "unknown error";
-       }
-}
-
-/*
- * set up and start data/sync urbs
- */
-static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
-{
-       unsigned int i;
-       int err;
-
-       if (subs->stream->chip->shutdown)
-               return -EBADFD;
-
-       for (i = 0; i < subs->nurbs; i++) {
-               if (snd_BUG_ON(!subs->dataurb[i].urb))
-                       return -EINVAL;
-               if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
-                       snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
-                       goto __error;
-               }
-       }
-       if (subs->syncpipe) {
-               for (i = 0; i < SYNC_URBS; i++) {
-                       if (snd_BUG_ON(!subs->syncurb[i].urb))
-                               return -EINVAL;
-                       if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
-                               snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
-                               goto __error;
-                       }
-               }
-       }
-
-       subs->active_mask = 0;
-       subs->unlink_mask = 0;
-       subs->running = 1;
-       for (i = 0; i < subs->nurbs; i++) {
-               err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
-               if (err < 0) {
-                       snd_printk(KERN_ERR "cannot submit datapipe "
-                                  "for urb %d, error %d: %s\n",
-                                  i, err, usb_error_string(err));
-                       goto __error;
-               }
-               set_bit(i, &subs->active_mask);
-       }
-       if (subs->syncpipe) {
-               for (i = 0; i < SYNC_URBS; i++) {
-                       err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
-                       if (err < 0) {
-                               snd_printk(KERN_ERR "cannot submit syncpipe "
-                                          "for urb %d, error %d: %s\n",
-                                          i, err, usb_error_string(err));
-                               goto __error;
-                       }
-                       set_bit(i + 16, &subs->active_mask);
-               }
-       }
-       return 0;
-
- __error:
-       // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
-       deactivate_urbs(subs, 0, 0);
-       return -EPIPE;
-}
-
-
-/*
- *  wait until all urbs are processed.
- */
-static int wait_clear_urbs(struct snd_usb_substream *subs)
-{
-       unsigned long end_time = jiffies + msecs_to_jiffies(1000);
-       unsigned int i;
-       int alive;
-
-       do {
-               alive = 0;
-               for (i = 0; i < subs->nurbs; i++) {
-                       if (test_bit(i, &subs->active_mask))
-                               alive++;
-               }
-               if (subs->syncpipe) {
-                       for (i = 0; i < SYNC_URBS; i++) {
-                               if (test_bit(i + 16, &subs->active_mask))
-                                       alive++;
-                       }
-               }
-               if (! alive)
-                       break;
-               schedule_timeout_uninterruptible(1);
-       } while (time_before(jiffies, end_time));
-       if (alive)
-               snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
-       return 0;
-}
-
-
-/*
- * return the current pcm pointer.  just based on the hwptr_done value.
- */
-static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream)
-{
-       struct snd_usb_substream *subs;
-       unsigned int hwptr_done;
-       
-       subs = (struct snd_usb_substream *)substream->runtime->private_data;
-       spin_lock(&subs->lock);
-       hwptr_done = subs->hwptr_done;
-       spin_unlock(&subs->lock);
-       return hwptr_done / (substream->runtime->frame_bits >> 3);
-}
-
-
-/*
- * start/stop playback substream
- */
-static int snd_usb_pcm_playback_trigger(struct snd_pcm_substream *substream,
-                                       int cmd)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               subs->ops.prepare = prepare_playback_urb;
-               return 0;
-       case SNDRV_PCM_TRIGGER_STOP:
-               return deactivate_urbs(subs, 0, 0);
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               subs->ops.prepare = prepare_nodata_playback_urb;
-               return 0;
-       default:
-               return -EINVAL;
-       }
-}
-
-/*
- * start/stop capture substream
- */
-static int snd_usb_pcm_capture_trigger(struct snd_pcm_substream *substream,
-                                      int cmd)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               subs->ops.retire = retire_capture_urb;
-               return start_urbs(subs, substream->runtime);
-       case SNDRV_PCM_TRIGGER_STOP:
-               return deactivate_urbs(subs, 0, 0);
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               subs->ops.retire = retire_paused_capture_urb;
-               return 0;
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               subs->ops.retire = retire_capture_urb;
-               return 0;
-       default:
-               return -EINVAL;
-       }
-}
-
-
-/*
- * release a urb data
- */
-static void release_urb_ctx(struct snd_urb_ctx *u)
-{
-       if (u->urb) {
-               if (u->buffer_size)
-                       usb_buffer_free(u->subs->dev, u->buffer_size,
-                                       u->urb->transfer_buffer,
-                                       u->urb->transfer_dma);
-               usb_free_urb(u->urb);
-               u->urb = NULL;
-       }
-}
-
-/*
- * release a substream
- */
-static void release_substream_urbs(struct snd_usb_substream *subs, int force)
-{
-       int i;
-
-       /* stop urbs (to be sure) */
-       deactivate_urbs(subs, force, 1);
-       wait_clear_urbs(subs);
-
-       for (i = 0; i < MAX_URBS; i++)
-               release_urb_ctx(&subs->dataurb[i]);
-       for (i = 0; i < SYNC_URBS; i++)
-               release_urb_ctx(&subs->syncurb[i]);
-       usb_buffer_free(subs->dev, SYNC_URBS * 4,
-                       subs->syncbuf, subs->sync_dma);
-       subs->syncbuf = NULL;
-       subs->nurbs = 0;
-}
-
-/*
- * initialize a substream for plaback/capture
- */
-static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int period_bytes,
-                              unsigned int rate, unsigned int frame_bits)
-{
-       unsigned int maxsize, i;
-       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-       unsigned int urb_packs, total_packs, packs_per_ms;
-
-       /* calculate the frequency in 16.16 format */
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-               subs->freqn = get_usb_full_speed_rate(rate);
-       else
-               subs->freqn = get_usb_high_speed_rate(rate);
-       subs->freqm = subs->freqn;
-       /* calculate max. frequency */
-       if (subs->maxpacksize) {
-               /* whatever fits into a max. size packet */
-               maxsize = subs->maxpacksize;
-               subs->freqmax = (maxsize / (frame_bits >> 3))
-                               << (16 - subs->datainterval);
-       } else {
-               /* no max. packet size: just take 25% higher than nominal */
-               subs->freqmax = subs->freqn + (subs->freqn >> 2);
-               maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
-                               >> (16 - subs->datainterval);
-       }
-       subs->phase = 0;
-
-       if (subs->fill_max)
-               subs->curpacksize = subs->maxpacksize;
-       else
-               subs->curpacksize = maxsize;
-
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
-               packs_per_ms = 8 >> subs->datainterval;
-       else
-               packs_per_ms = 1;
-
-       if (is_playback) {
-               urb_packs = max(nrpacks, 1);
-               urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
-       } else
-               urb_packs = 1;
-       urb_packs *= packs_per_ms;
-       if (subs->syncpipe)
-               urb_packs = min(urb_packs, 1U << subs->syncinterval);
-
-       /* decide how many packets to be used */
-       if (is_playback) {
-               unsigned int minsize, maxpacks;
-               /* determine how small a packet can be */
-               minsize = (subs->freqn >> (16 - subs->datainterval))
-                         * (frame_bits >> 3);
-               /* with sync from device, assume it can be 12% lower */
-               if (subs->syncpipe)
-                       minsize -= minsize >> 3;
-               minsize = max(minsize, 1u);
-               total_packs = (period_bytes + minsize - 1) / minsize;
-               /* we need at least two URBs for queueing */
-               if (total_packs < 2) {
-                       total_packs = 2;
-               } else {
-                       /* and we don't want too long a queue either */
-                       maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
-                       total_packs = min(total_packs, maxpacks);
-               }
-       } else {
-               while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
-                       urb_packs >>= 1;
-               total_packs = MAX_URBS * urb_packs;
-       }
-       subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
-       if (subs->nurbs > MAX_URBS) {
-               /* too much... */
-               subs->nurbs = MAX_URBS;
-               total_packs = MAX_URBS * urb_packs;
-       } else if (subs->nurbs < 2) {
-               /* too little - we need at least two packets
-                * to ensure contiguous playback/capture
-                */
-               subs->nurbs = 2;
-       }
-
-       /* allocate and initialize data urbs */
-       for (i = 0; i < subs->nurbs; i++) {
-               struct snd_urb_ctx *u = &subs->dataurb[i];
-               u->index = i;
-               u->subs = subs;
-               u->packets = (i + 1) * total_packs / subs->nurbs
-                       - i * total_packs / subs->nurbs;
-               u->buffer_size = maxsize * u->packets;
-               if (subs->fmt_type == UAC_FORMAT_TYPE_II)
-                       u->packets++; /* for transfer delimiter */
-               u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
-               if (!u->urb)
-                       goto out_of_memory;
-               u->urb->transfer_buffer =
-                       usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL,
-                                        &u->urb->transfer_dma);
-               if (!u->urb->transfer_buffer)
-                       goto out_of_memory;
-               u->urb->pipe = subs->datapipe;
-               u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-               u->urb->interval = 1 << subs->datainterval;
-               u->urb->context = u;
-               u->urb->complete = snd_complete_urb;
-       }
-
-       if (subs->syncpipe) {
-               /* allocate and initialize sync urbs */
-               subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4,
-                                                GFP_KERNEL, &subs->sync_dma);
-               if (!subs->syncbuf)
-                       goto out_of_memory;
-               for (i = 0; i < SYNC_URBS; i++) {
-                       struct snd_urb_ctx *u = &subs->syncurb[i];
-                       u->index = i;
-                       u->subs = subs;
-                       u->packets = 1;
-                       u->urb = usb_alloc_urb(1, GFP_KERNEL);
-                       if (!u->urb)
-                               goto out_of_memory;
-                       u->urb->transfer_buffer = subs->syncbuf + i * 4;
-                       u->urb->transfer_dma = subs->sync_dma + i * 4;
-                       u->urb->transfer_buffer_length = 4;
-                       u->urb->pipe = subs->syncpipe;
-                       u->urb->transfer_flags = URB_ISO_ASAP |
-                                                URB_NO_TRANSFER_DMA_MAP;
-                       u->urb->number_of_packets = 1;
-                       u->urb->interval = 1 << subs->syncinterval;
-                       u->urb->context = u;
-                       u->urb->complete = snd_complete_sync_urb;
-               }
-       }
-       return 0;
-
-out_of_memory:
-       release_substream_urbs(subs, 0);
-       return -ENOMEM;
-}
-
-
-/*
- * find a matching audio format
- */
-static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format,
-                                      unsigned int rate, unsigned int channels)
-{
-       struct list_head *p;
-       struct audioformat *found = NULL;
-       int cur_attr = 0, attr;
-
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               if (fp->format != format || fp->channels != channels)
-                       continue;
-               if (rate < fp->rate_min || rate > fp->rate_max)
-                       continue;
-               if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
-                       unsigned int i;
-                       for (i = 0; i < fp->nr_rates; i++)
-                               if (fp->rate_table[i] == rate)
-                                       break;
-                       if (i >= fp->nr_rates)
-                               continue;
-               }
-               attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE;
-               if (! found) {
-                       found = fp;
-                       cur_attr = attr;
-                       continue;
-               }
-               /* avoid async out and adaptive in if the other method
-                * supports the same format.
-                * this is a workaround for the case like
-                * M-audio audiophile USB.
-                */
-               if (attr != cur_attr) {
-                       if ((attr == USB_ENDPOINT_SYNC_ASYNC &&
-                            subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
-                           (attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
-                            subs->direction == SNDRV_PCM_STREAM_CAPTURE))
-                               continue;
-                       if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC &&
-                            subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
-                           (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
-                            subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {
-                               found = fp;
-                               cur_attr = attr;
-                               continue;
-                       }
-               }
-               /* find the format with the largest max. packet size */
-               if (fp->maxpacksize > found->maxpacksize) {
-                       found = fp;
-                       cur_attr = attr;
-               }
-       }
-       return found;
-}
-
-
-/*
- * initialize the picth control and sample rate
- */
-static int init_usb_pitch(struct usb_device *dev, int iface,
-                         struct usb_host_interface *alts,
-                         struct audioformat *fmt)
-{
-       unsigned int ep;
-       unsigned char data[1];
-       int err;
-
-       ep = get_endpoint(alts, 0)->bEndpointAddress;
-       /* if endpoint has pitch control, enable it */
-       if (fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL) {
-               data[0] = 1;
-               if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
-                                          USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
-                                          UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
-                                  dev->devnum, iface, ep);
-                       return err;
-               }
-       }
-       return 0;
-}
-
-static int init_usb_sample_rate(struct usb_device *dev, int iface,
-                               struct usb_host_interface *alts,
-                               struct audioformat *fmt, int rate)
-{
-       unsigned int ep;
-       unsigned char data[3];
-       int err;
-
-       ep = get_endpoint(alts, 0)->bEndpointAddress;
-       /* if endpoint has sampling rate control, set it */
-       if (fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE) {
-               int crate;
-               data[0] = rate;
-               data[1] = rate >> 8;
-               data[2] = rate >> 16;
-               if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
-                                          USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
-                                          UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
-                                  dev->devnum, iface, fmt->altsetting, rate, ep);
-                       return err;
-               }
-               if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
-                                          USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
-                                          UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) {
-                       snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
-                                  dev->devnum, iface, fmt->altsetting, ep);
-                       return 0; /* some devices don't support reading */
-               }
-               crate = data[0] | (data[1] << 8) | (data[2] << 16);
-               if (crate != rate) {
-                       snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
-                       // runtime->rate = crate;
-               }
-       }
-       return 0;
-}
-
-/*
- * For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device,
- * not for interface.
- */
-static void set_format_emu_quirk(struct snd_usb_substream *subs,
-                                struct audioformat *fmt)
-{
-       unsigned char emu_samplerate_id = 0;
-
-       /* When capture is active
-        * sample rate shouldn't be changed
-        * by playback substream
-        */
-       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1)
-                       return;
-       }
-
-       switch (fmt->rate_min) {
-       case 48000:
-               emu_samplerate_id = EMU_QUIRK_SR_48000HZ;
-               break;
-       case 88200:
-               emu_samplerate_id = EMU_QUIRK_SR_88200HZ;
-               break;
-       case 96000:
-               emu_samplerate_id = EMU_QUIRK_SR_96000HZ;
-               break;
-       case 176400:
-               emu_samplerate_id = EMU_QUIRK_SR_176400HZ;
-               break;
-       case 192000:
-               emu_samplerate_id = EMU_QUIRK_SR_192000HZ;
-               break;
-       default:
-               emu_samplerate_id = EMU_QUIRK_SR_44100HZ;
-               break;
-       }
-       snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
-}
-
-/*
- * find a matching format and set up the interface
- */
-static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
-{
-       struct usb_device *dev = subs->dev;
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       struct usb_interface *iface;
-       unsigned int ep, attr;
-       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-       int err;
-
-       iface = usb_ifnum_to_if(dev, fmt->iface);
-       if (WARN_ON(!iface))
-               return -EINVAL;
-       alts = &iface->altsetting[fmt->altset_idx];
-       altsd = get_iface_desc(alts);
-       if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
-               return -EINVAL;
-
-       if (fmt == subs->cur_audiofmt)
-               return 0;
-
-       /* close the old interface */
-       if (subs->interface >= 0 && subs->interface != fmt->iface) {
-               if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
-                               dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EIO;
-               }
-               subs->interface = -1;
-               subs->format = 0;
-       }
-
-       /* set interface */
-       if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) {
-               if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
-                                  dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EIO;
-               }
-               snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
-               subs->interface = fmt->iface;
-               subs->format = fmt->altset_idx;
-       }
-
-       /* create a data pipe */
-       ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
-       if (is_playback)
-               subs->datapipe = usb_sndisocpipe(dev, ep);
-       else
-               subs->datapipe = usb_rcvisocpipe(dev, ep);
-       subs->datainterval = fmt->datainterval;
-       subs->syncpipe = subs->syncinterval = 0;
-       subs->maxpacksize = fmt->maxpacksize;
-       subs->fill_max = 0;
-
-       /* we need a sync pipe in async OUT or adaptive IN mode */
-       /* check the number of EP, since some devices have broken
-        * descriptors which fool us.  if it has only one EP,
-        * assume it as adaptive-out or sync-in.
-        */
-       attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
-       if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
-            (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
-           altsd->bNumEndpoints >= 2) {
-               /* check sync-pipe endpoint */
-               /* ... and check descriptor size before accessing bSynchAddress
-                  because there is a version of the SB Audigy 2 NX firmware lacking
-                  the audio fields in the endpoint descriptors */
-               if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
-                   (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                    get_endpoint(alts, 1)->bSynchAddress != 0)) {
-                       snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
-                                  dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EINVAL;
-               }
-               ep = get_endpoint(alts, 1)->bEndpointAddress;
-               if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                   (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
-                    (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
-                       snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
-                                  dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EINVAL;
-               }
-               ep &= USB_ENDPOINT_NUMBER_MASK;
-               if (is_playback)
-                       subs->syncpipe = usb_rcvisocpipe(dev, ep);
-               else
-                       subs->syncpipe = usb_sndisocpipe(dev, ep);
-               if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                   get_endpoint(alts, 1)->bRefresh >= 1 &&
-                   get_endpoint(alts, 1)->bRefresh <= 9)
-                       subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
-               else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-                       subs->syncinterval = 1;
-               else if (get_endpoint(alts, 1)->bInterval >= 1 &&
-                        get_endpoint(alts, 1)->bInterval <= 16)
-                       subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
-               else
-                       subs->syncinterval = 3;
-       }
-
-       /* always fill max packet size */
-       if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
-               subs->fill_max = 1;
-
-       if ((err = init_usb_pitch(dev, subs->interface, alts, fmt)) < 0)
-               return err;
-
-       subs->cur_audiofmt = fmt;
-
-       switch (subs->stream->chip->usb_id) {
-       case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
-       case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
-       case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
-               set_format_emu_quirk(subs, fmt);
-               break;
-       }
-
-#if 0
-       printk(KERN_DEBUG
-              "setting done: format = %d, rate = %d..%d, channels = %d\n",
-              fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
-       printk(KERN_DEBUG
-              "  datapipe = 0x%0x, syncpipe = 0x%0x\n",
-              subs->datapipe, subs->syncpipe);
-#endif
-
-       return 0;
-}
-
-/*
- * hw_params callback
- *
- * allocate a buffer and set the given audio format.
- *
- * so far we use a physically linear buffer although packetize transfer
- * doesn't need a continuous area.
- * if sg buffer is supported on the later version of alsa, we'll follow
- * that.
- */
-static int snd_usb_hw_params(struct snd_pcm_substream *substream,
-                            struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-       struct audioformat *fmt;
-       unsigned int channels, rate, format;
-       int ret, changed;
-
-       ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                              params_buffer_bytes(hw_params));
-       if (ret < 0)
-               return ret;
-
-       format = params_format(hw_params);
-       rate = params_rate(hw_params);
-       channels = params_channels(hw_params);
-       fmt = find_format(subs, format, rate, channels);
-       if (!fmt) {
-               snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
-                          format, rate, channels);
-               return -EINVAL;
-       }
-
-       changed = subs->cur_audiofmt != fmt ||
-               subs->period_bytes != params_period_bytes(hw_params) ||
-               subs->cur_rate != rate;
-       if ((ret = set_format(subs, fmt)) < 0)
-               return ret;
-
-       if (subs->cur_rate != rate) {
-               struct usb_host_interface *alts;
-               struct usb_interface *iface;
-               iface = usb_ifnum_to_if(subs->dev, fmt->iface);
-               alts = &iface->altsetting[fmt->altset_idx];
-               ret = init_usb_sample_rate(subs->dev, subs->interface, alts, fmt, rate);
-               if (ret < 0)
-                       return ret;
-               subs->cur_rate = rate;
-       }
-
-       if (changed) {
-               /* format changed */
-               release_substream_urbs(subs, 0);
-               /* influenced: period_bytes, channels, rate, format, */
-               ret = init_substream_urbs(subs, params_period_bytes(hw_params),
-                                         params_rate(hw_params),
-                                         snd_pcm_format_physical_width(params_format(hw_params)) * params_channels(hw_params));
-       }
-
-       return ret;
-}
-
-/*
- * hw_free callback
- *
- * reset the audio format and release the buffer
- */
-static int snd_usb_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-
-       subs->cur_audiofmt = NULL;
-       subs->cur_rate = 0;
-       subs->period_bytes = 0;
-       if (!subs->stream->chip->shutdown)
-               release_substream_urbs(subs, 0);
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
-/*
- * prepare callback
- *
- * only a few subtle things...
- */
-static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_usb_substream *subs = runtime->private_data;
-
-       if (! subs->cur_audiofmt) {
-               snd_printk(KERN_ERR "usbaudio: no format is specified!\n");
-               return -ENXIO;
-       }
-
-       /* some unit conversions in runtime */
-       subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
-       subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
-
-       /* reset the pointer */
-       subs->hwptr_done = 0;
-       subs->transfer_done = 0;
-       subs->phase = 0;
-       runtime->delay = 0;
-
-       /* clear urbs (to be sure) */
-       deactivate_urbs(subs, 0, 1);
-       wait_clear_urbs(subs);
-
-       /* for playback, submit the URBs now; otherwise, the first hwptr_done
-        * updates for all URBs would happen at the same time when starting */
-       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
-               subs->ops.prepare = prepare_nodata_playback_urb;
-               return start_urbs(subs, runtime);
-       } else
-               return 0;
-}
-
-static struct snd_pcm_hardware snd_usb_hardware =
-{
-       .info =                 SNDRV_PCM_INFO_MMAP |
-                               SNDRV_PCM_INFO_MMAP_VALID |
-                               SNDRV_PCM_INFO_BATCH |
-                               SNDRV_PCM_INFO_INTERLEAVED |
-                               SNDRV_PCM_INFO_BLOCK_TRANSFER |
-                               SNDRV_PCM_INFO_PAUSE,
-       .buffer_bytes_max =     1024 * 1024,
-       .period_bytes_min =     64,
-       .period_bytes_max =     512 * 1024,
-       .periods_min =          2,
-       .periods_max =          1024,
-};
-
-/*
- * h/w constraints
- */
-
-#ifdef HW_CONST_DEBUG
-#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
-#else
-#define hwc_debug(fmt, args...) /**/
-#endif
-
-static int hw_check_valid_format(struct snd_usb_substream *subs,
-                                struct snd_pcm_hw_params *params,
-                                struct audioformat *fp)
-{
-       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-       struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
-       unsigned int ptime;
-
-       /* check the format */
-       if (!snd_mask_test(fmts, fp->format)) {
-               hwc_debug("   > check: no supported format %d\n", fp->format);
-               return 0;
-       }
-       /* check the channels */
-       if (fp->channels < ct->min || fp->channels > ct->max) {
-               hwc_debug("   > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max);
-               return 0;
-       }
-       /* check the rate is within the range */
-       if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) {
-               hwc_debug("   > check: rate_min %d > max %d\n", fp->rate_min, it->max);
-               return 0;
-       }
-       if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) {
-               hwc_debug("   > check: rate_max %d < min %d\n", fp->rate_max, it->min);
-               return 0;
-       }
-       /* check whether the period time is >= the data packet interval */
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
-               ptime = 125 * (1 << fp->datainterval);
-               if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
-                       hwc_debug("   > check: ptime %u > max %u\n", ptime, pt->max);
-                       return 0;
-               }
-       }
-       return 1;
-}
-
-static int hw_rule_rate(struct snd_pcm_hw_params *params,
-                       struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       struct list_head *p;
-       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       unsigned int rmin, rmax;
-       int changed;
-
-       hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max);
-       changed = 0;
-       rmin = rmax = 0;
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               if (!hw_check_valid_format(subs, params, fp))
-                       continue;
-               if (changed++) {
-                       if (rmin > fp->rate_min)
-                               rmin = fp->rate_min;
-                       if (rmax < fp->rate_max)
-                               rmax = fp->rate_max;
-               } else {
-                       rmin = fp->rate_min;
-                       rmax = fp->rate_max;
-               }
-       }
-
-       if (!changed) {
-               hwc_debug("  --> get empty\n");
-               it->empty = 1;
-               return -EINVAL;
-       }
-
-       changed = 0;
-       if (it->min < rmin) {
-               it->min = rmin;
-               it->openmin = 0;
-               changed = 1;
-       }
-       if (it->max > rmax) {
-               it->max = rmax;
-               it->openmax = 0;
-               changed = 1;
-       }
-       if (snd_interval_checkempty(it)) {
-               it->empty = 1;
-               return -EINVAL;
-       }
-       hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
-       return changed;
-}
-
-
-static int hw_rule_channels(struct snd_pcm_hw_params *params,
-                           struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       struct list_head *p;
-       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       unsigned int rmin, rmax;
-       int changed;
-
-       hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max);
-       changed = 0;
-       rmin = rmax = 0;
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               if (!hw_check_valid_format(subs, params, fp))
-                       continue;
-               if (changed++) {
-                       if (rmin > fp->channels)
-                               rmin = fp->channels;
-                       if (rmax < fp->channels)
-                               rmax = fp->channels;
-               } else {
-                       rmin = fp->channels;
-                       rmax = fp->channels;
-               }
-       }
-
-       if (!changed) {
-               hwc_debug("  --> get empty\n");
-               it->empty = 1;
-               return -EINVAL;
-       }
-
-       changed = 0;
-       if (it->min < rmin) {
-               it->min = rmin;
-               it->openmin = 0;
-               changed = 1;
-       }
-       if (it->max > rmax) {
-               it->max = rmax;
-               it->openmax = 0;
-               changed = 1;
-       }
-       if (snd_interval_checkempty(it)) {
-               it->empty = 1;
-               return -EINVAL;
-       }
-       hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
-       return changed;
-}
-
-static int hw_rule_format(struct snd_pcm_hw_params *params,
-                         struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       struct list_head *p;
-       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-       u64 fbits;
-       u32 oldbits[2];
-       int changed;
-
-       hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
-       fbits = 0;
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               if (!hw_check_valid_format(subs, params, fp))
-                       continue;
-               fbits |= (1ULL << fp->format);
-       }
-
-       oldbits[0] = fmt->bits[0];
-       oldbits[1] = fmt->bits[1];
-       fmt->bits[0] &= (u32)fbits;
-       fmt->bits[1] &= (u32)(fbits >> 32);
-       if (!fmt->bits[0] && !fmt->bits[1]) {
-               hwc_debug("  --> get empty\n");
-               return -EINVAL;
-       }
-       changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]);
-       hwc_debug("  --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed);
-       return changed;
-}
-
-static int hw_rule_period_time(struct snd_pcm_hw_params *params,
-                              struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       struct audioformat *fp;
-       struct snd_interval *it;
-       unsigned char min_datainterval;
-       unsigned int pmin;
-       int changed;
-
-       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
-       hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max);
-       min_datainterval = 0xff;
-       list_for_each_entry(fp, &subs->fmt_list, list) {
-               if (!hw_check_valid_format(subs, params, fp))
-                       continue;
-               min_datainterval = min(min_datainterval, fp->datainterval);
-       }
-       if (min_datainterval == 0xff) {
-               hwc_debug("  --> get emtpy\n");
-               it->empty = 1;
-               return -EINVAL;
-       }
-       pmin = 125 * (1 << min_datainterval);
-       changed = 0;
-       if (it->min < pmin) {
-               it->min = pmin;
-               it->openmin = 0;
-               changed = 1;
-       }
-       if (snd_interval_checkempty(it)) {
-               it->empty = 1;
-               return -EINVAL;
-       }
-       hwc_debug("  --> (%u,%u) (changed = %d)\n", it->min, it->max, changed);
-       return changed;
-}
-
-/*
- *  If the device supports unusual bit rates, does the request meet these?
- */
-static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
-                                 struct snd_usb_substream *subs)
-{
-       struct audioformat *fp;
-       int count = 0, needs_knot = 0;
-       int err;
-
-       list_for_each_entry(fp, &subs->fmt_list, list) {
-               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
-                       return 0;
-               count += fp->nr_rates;
-               if (fp->rates & SNDRV_PCM_RATE_KNOT)
-                       needs_knot = 1;
-       }
-       if (!needs_knot)
-               return 0;
-
-       subs->rate_list.count = count;
-       subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
-       subs->rate_list.mask = 0;
-       count = 0;
-       list_for_each_entry(fp, &subs->fmt_list, list) {
-               int i;
-               for (i = 0; i < fp->nr_rates; i++)
-                       subs->rate_list.list[count++] = fp->rate_table[i];
-       }
-       err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-                                        &subs->rate_list);
-       if (err < 0)
-               return err;
-
-       return 0;
-}
-
-
-/*
- * set up the runtime hardware information.
- */
-
-static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
-{
-       struct list_head *p;
-       unsigned int pt, ptmin;
-       int param_period_time_if_needed;
-       int err;
-
-       runtime->hw.formats = subs->formats;
-
-       runtime->hw.rate_min = 0x7fffffff;
-       runtime->hw.rate_max = 0;
-       runtime->hw.channels_min = 256;
-       runtime->hw.channels_max = 0;
-       runtime->hw.rates = 0;
-       ptmin = UINT_MAX;
-       /* check min/max rates and channels */
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               runtime->hw.rates |= fp->rates;
-               if (runtime->hw.rate_min > fp->rate_min)
-                       runtime->hw.rate_min = fp->rate_min;
-               if (runtime->hw.rate_max < fp->rate_max)
-                       runtime->hw.rate_max = fp->rate_max;
-               if (runtime->hw.channels_min > fp->channels)
-                       runtime->hw.channels_min = fp->channels;
-               if (runtime->hw.channels_max < fp->channels)
-                       runtime->hw.channels_max = fp->channels;
-               if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) {
-                       /* FIXME: there might be more than one audio formats... */
-                       runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
-                               fp->frame_size;
-               }
-               pt = 125 * (1 << fp->datainterval);
-               ptmin = min(ptmin, pt);
-       }
-
-       param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
-       if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
-               /* full speed devices have fixed data packet interval */
-               ptmin = 1000;
-       if (ptmin == 1000)
-               /* if period time doesn't go below 1 ms, no rules needed */
-               param_period_time_if_needed = -1;
-       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
-                                    ptmin, UINT_MAX);
-
-       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-                                      hw_rule_rate, subs,
-                                      SNDRV_PCM_HW_PARAM_FORMAT,
-                                      SNDRV_PCM_HW_PARAM_CHANNELS,
-                                      param_period_time_if_needed,
-                                      -1)) < 0)
-               return err;
-       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                      hw_rule_channels, subs,
-                                      SNDRV_PCM_HW_PARAM_FORMAT,
-                                      SNDRV_PCM_HW_PARAM_RATE,
-                                      param_period_time_if_needed,
-                                      -1)) < 0)
-               return err;
-       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
-                                      hw_rule_format, subs,
-                                      SNDRV_PCM_HW_PARAM_RATE,
-                                      SNDRV_PCM_HW_PARAM_CHANNELS,
-                                      param_period_time_if_needed,
-                                      -1)) < 0)
-               return err;
-       if (param_period_time_if_needed >= 0) {
-               err = snd_pcm_hw_rule_add(runtime, 0,
-                                         SNDRV_PCM_HW_PARAM_PERIOD_TIME,
-                                         hw_rule_period_time, subs,
-                                         SNDRV_PCM_HW_PARAM_FORMAT,
-                                         SNDRV_PCM_HW_PARAM_CHANNELS,
-                                         SNDRV_PCM_HW_PARAM_RATE,
-                                         -1);
-               if (err < 0)
-                       return err;
-       }
-       if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
-               return err;
-       return 0;
-}
-
-static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
-{
-       struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_usb_substream *subs = &as->substream[direction];
-
-       subs->interface = -1;
-       subs->format = 0;
-       runtime->hw = snd_usb_hardware;
-       runtime->private_data = subs;
-       subs->pcm_substream = substream;
-       return setup_hw_info(runtime, subs);
-}
-
-static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
-{
-       struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
-       struct snd_usb_substream *subs = &as->substream[direction];
-
-       if (!as->chip->shutdown && subs->interface >= 0) {
-               usb_set_interface(subs->dev, subs->interface, 0);
-               subs->interface = -1;
-       }
-       subs->pcm_substream = NULL;
-       return 0;
-}
-
-static int snd_usb_playback_open(struct snd_pcm_substream *substream)
-{
-       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);
-}
-
-static int snd_usb_playback_close(struct snd_pcm_substream *substream)
-{
-       return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK);
-}
-
-static int snd_usb_capture_open(struct snd_pcm_substream *substream)
-{
-       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE);
-}
-
-static int snd_usb_capture_close(struct snd_pcm_substream *substream)
-{
-       return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
-}
-
-static struct snd_pcm_ops snd_usb_playback_ops = {
-       .open =         snd_usb_playback_open,
-       .close =        snd_usb_playback_close,
-       .ioctl =        snd_pcm_lib_ioctl,
-       .hw_params =    snd_usb_hw_params,
-       .hw_free =      snd_usb_hw_free,
-       .prepare =      snd_usb_pcm_prepare,
-       .trigger =      snd_usb_pcm_playback_trigger,
-       .pointer =      snd_usb_pcm_pointer,
-       .page =         snd_pcm_lib_get_vmalloc_page,
-       .mmap =         snd_pcm_lib_mmap_vmalloc,
-};
-
-static struct snd_pcm_ops snd_usb_capture_ops = {
-       .open =         snd_usb_capture_open,
-       .close =        snd_usb_capture_close,
-       .ioctl =        snd_pcm_lib_ioctl,
-       .hw_params =    snd_usb_hw_params,
-       .hw_free =      snd_usb_hw_free,
-       .prepare =      snd_usb_pcm_prepare,
-       .trigger =      snd_usb_pcm_capture_trigger,
-       .pointer =      snd_usb_pcm_pointer,
-       .page =         snd_pcm_lib_get_vmalloc_page,
-       .mmap =         snd_pcm_lib_mmap_vmalloc,
-};
-
-
-
-/*
- * helper functions
- */
-
-/*
- * combine bytes and get an integer value
- */
-unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size)
-{
-       switch (size) {
-       case 1:  return *bytes;
-       case 2:  return combine_word(bytes);
-       case 3:  return combine_triple(bytes);
-       case 4:  return combine_quad(bytes);
-       default: return 0;
-       }
-}
-
-/*
- * parse descriptor buffer and return the pointer starting the given
- * descriptor type.
- */
-void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype)
-{
-       u8 *p, *end, *next;
-
-       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] == dtype && (!after || (void *)p > after)) {
-                       return p;
-               }
-               p = next;
-       }
-       return NULL;
-}
-
-/*
- * find a class-specified interface descriptor with the given subtype.
- */
-void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype)
-{
-       unsigned char *p = after;
-
-       while ((p = snd_usb_find_desc(buffer, buflen, p,
-                                     USB_DT_CS_INTERFACE)) != NULL) {
-               if (p[0] >= 3 && p[2] == dsubtype)
-                       return p;
-       }
-       return NULL;
-}
-
-/*
- * Wrapper for usb_control_msg().
- * Allocates a temp buffer to prevent dmaing from/to the stack.
- */
-int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
-                   __u8 requesttype, __u16 value, __u16 index, void *data,
-                   __u16 size, int timeout)
-{
-       int err;
-       void *buf = NULL;
-
-       if (size > 0) {
-               buf = kmemdup(data, size, GFP_KERNEL);
-               if (!buf)
-                       return -ENOMEM;
-       }
-       err = usb_control_msg(dev, pipe, request, requesttype,
-                             value, index, buf, size, timeout);
-       if (size > 0) {
-               memcpy(data, buf, size);
-               kfree(buf);
-       }
-       return err;
-}
-
-
-/*
- * entry point for linux usb interface
- */
-
-static int usb_audio_probe(struct usb_interface *intf,
-                          const struct usb_device_id *id);
-static void usb_audio_disconnect(struct usb_interface *intf);
-
-#ifdef CONFIG_PM
-static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message);
-static int usb_audio_resume(struct usb_interface *intf);
-#else
-#define usb_audio_suspend NULL
-#define usb_audio_resume NULL
-#endif
-
-static struct usb_device_id usb_audio_ids [] = {
-#include "usbquirks.h"
-    { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
-      .bInterfaceClass = USB_CLASS_AUDIO,
-      .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL },
-    { }                                                /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, usb_audio_ids);
-
-static struct usb_driver usb_audio_driver = {
-       .name =         "snd-usb-audio",
-       .probe =        usb_audio_probe,
-       .disconnect =   usb_audio_disconnect,
-       .suspend =      usb_audio_suspend,
-       .resume =       usb_audio_resume,
-       .id_table =     usb_audio_ids,
-};
-
-
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_SND_VERBOSE_PROCFS)
-
-/*
- * proc interface for list the supported pcm formats
- */
-static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
-{
-       struct list_head *p;
-       static char *sync_types[4] = {
-               "NONE", "ASYNC", "ADAPTIVE", "SYNC"
-       };
-
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               snd_iprintf(buffer, "  Interface %d\n", fp->iface);
-               snd_iprintf(buffer, "    Altset %d\n", fp->altsetting);
-               snd_iprintf(buffer, "    Format: %s\n",
-                           snd_pcm_format_name(fp->format));
-               snd_iprintf(buffer, "    Channels: %d\n", fp->channels);
-               snd_iprintf(buffer, "    Endpoint: %d %s (%s)\n",
-                           fp->endpoint & USB_ENDPOINT_NUMBER_MASK,
-                           fp->endpoint & USB_DIR_IN ? "IN" : "OUT",
-                           sync_types[(fp->ep_attr & USB_ENDPOINT_SYNCTYPE) >> 2]);
-               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) {
-                       snd_iprintf(buffer, "    Rates: %d - %d (continuous)\n",
-                                   fp->rate_min, fp->rate_max);
-               } else {
-                       unsigned int i;
-                       snd_iprintf(buffer, "    Rates: ");
-                       for (i = 0; i < fp->nr_rates; i++) {
-                               if (i > 0)
-                                       snd_iprintf(buffer, ", ");
-                               snd_iprintf(buffer, "%d", fp->rate_table[i]);
-                       }
-                       snd_iprintf(buffer, "\n");
-               }
-               if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
-                       snd_iprintf(buffer, "    Data packet interval: %d us\n",
-                                   125 * (1 << fp->datainterval));
-               // snd_iprintf(buffer, "    Max Packet Size = %d\n", fp->maxpacksize);
-               // snd_iprintf(buffer, "    EP Attribute = %#x\n", fp->attributes);
-       }
-}
-
-static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
-{
-       if (subs->running) {
-               unsigned int i;
-               snd_iprintf(buffer, "  Status: Running\n");
-               snd_iprintf(buffer, "    Interface = %d\n", subs->interface);
-               snd_iprintf(buffer, "    Altset = %d\n", subs->format);
-               snd_iprintf(buffer, "    URBs = %d [ ", subs->nurbs);
-               for (i = 0; i < subs->nurbs; i++)
-                       snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
-               snd_iprintf(buffer, "]\n");
-               snd_iprintf(buffer, "    Packet Size = %d\n", subs->curpacksize);
-               snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
-                           snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
-                           ? get_full_speed_hz(subs->freqm)
-                           : get_high_speed_hz(subs->freqm),
-                           subs->freqm >> 16, subs->freqm & 0xffff);
-       } else {
-               snd_iprintf(buffer, "  Status: Stop\n");
-       }
-}
-
-static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
-{
-       struct snd_usb_stream *stream = entry->private_data;
-
-       snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name);
-
-       if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) {
-               snd_iprintf(buffer, "\nPlayback:\n");
-               proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
-               proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
-       }
-       if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) {
-               snd_iprintf(buffer, "\nCapture:\n");
-               proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
-               proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
-       }
-}
-
-static void proc_pcm_format_add(struct snd_usb_stream *stream)
-{
-       struct snd_info_entry *entry;
-       char name[32];
-       struct snd_card *card = stream->chip->card;
-
-       sprintf(name, "stream%d", stream->pcm_index);
-       if (!snd_card_proc_new(card, name, &entry))
-               snd_info_set_text_ops(entry, stream, proc_pcm_format_read);
-}
-
-#else
-
-static inline void proc_pcm_format_add(struct snd_usb_stream *stream)
-{
-}
-
-#endif
-
-/*
- * initialize the substream instance.
- */
-
-static void init_substream(struct snd_usb_stream *as, int stream, struct audioformat *fp)
-{
-       struct snd_usb_substream *subs = &as->substream[stream];
-
-       INIT_LIST_HEAD(&subs->fmt_list);
-       spin_lock_init(&subs->lock);
-
-       subs->stream = as;
-       subs->direction = stream;
-       subs->dev = as->chip->dev;
-       subs->txfr_quirk = as->chip->txfr_quirk;
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
-               subs->ops = audio_urb_ops[stream];
-       } else {
-               subs->ops = audio_urb_ops_high_speed[stream];
-               switch (as->chip->usb_id) {
-               case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
-               case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
-               case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
-                       subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
-                       break;
-               }
-       }
-       snd_pcm_set_ops(as->pcm, stream,
-                       stream == SNDRV_PCM_STREAM_PLAYBACK ?
-                       &snd_usb_playback_ops : &snd_usb_capture_ops);
-
-       list_add_tail(&fp->list, &subs->fmt_list);
-       subs->formats |= 1ULL << fp->format;
-       subs->endpoint = fp->endpoint;
-       subs->num_formats++;
-       subs->fmt_type = fp->fmt_type;
-}
-
-
-/*
- * free a substream
- */
-static void free_substream(struct snd_usb_substream *subs)
-{
-       struct list_head *p, *n;
-
-       if (!subs->num_formats)
-               return; /* not initialized */
-       list_for_each_safe(p, n, &subs->fmt_list) {
-               struct audioformat *fp = list_entry(p, struct audioformat, list);
-               kfree(fp->rate_table);
-               kfree(fp);
-       }
-       kfree(subs->rate_list.list);
-}
-
-
-/*
- * free a usb stream instance
- */
-static void snd_usb_audio_stream_free(struct snd_usb_stream *stream)
-{
-       free_substream(&stream->substream[0]);
-       free_substream(&stream->substream[1]);
-       list_del(&stream->list);
-       kfree(stream);
-}
-
-static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
-{
-       struct snd_usb_stream *stream = pcm->private_data;
-       if (stream) {
-               stream->pcm = NULL;
-               snd_usb_audio_stream_free(stream);
-       }
-}
-
-
-/*
- * add this endpoint to the chip instance.
- * if a stream with the same endpoint already exists, append to it.
- * if not, create a new pcm stream.
- */
-static int add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct audioformat *fp)
-{
-       struct list_head *p;
-       struct snd_usb_stream *as;
-       struct snd_usb_substream *subs;
-       struct snd_pcm *pcm;
-       int err;
-
-       list_for_each(p, &chip->pcm_list) {
-               as = list_entry(p, struct snd_usb_stream, list);
-               if (as->fmt_type != fp->fmt_type)
-                       continue;
-               subs = &as->substream[stream];
-               if (!subs->endpoint)
-                       continue;
-               if (subs->endpoint == fp->endpoint) {
-                       list_add_tail(&fp->list, &subs->fmt_list);
-                       subs->num_formats++;
-                       subs->formats |= 1ULL << fp->format;
-                       return 0;
-               }
-       }
-       /* look for an empty stream */
-       list_for_each(p, &chip->pcm_list) {
-               as = list_entry(p, struct snd_usb_stream, list);
-               if (as->fmt_type != fp->fmt_type)
-                       continue;
-               subs = &as->substream[stream];
-               if (subs->endpoint)
-                       continue;
-               err = snd_pcm_new_stream(as->pcm, stream, 1);
-               if (err < 0)
-                       return err;
-               init_substream(as, stream, fp);
-               return 0;
-       }
-
-       /* create a new pcm */
-       as = kzalloc(sizeof(*as), GFP_KERNEL);
-       if (!as)
-               return -ENOMEM;
-       as->pcm_index = chip->pcm_devs;
-       as->chip = chip;
-       as->fmt_type = fp->fmt_type;
-       err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
-                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
-                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
-                         &pcm);
-       if (err < 0) {
-               kfree(as);
-               return err;
-       }
-       as->pcm = pcm;
-       pcm->private_data = as;
-       pcm->private_free = snd_usb_audio_pcm_free;
-       pcm->info_flags = 0;
-       if (chip->pcm_devs > 0)
-               sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
-       else
-               strcpy(pcm->name, "USB Audio");
-
-       init_substream(as, stream, fp);
-
-       list_add(&as->list, &chip->pcm_list);
-       chip->pcm_devs++;
-
-       proc_pcm_format_add(as);
-
-       return 0;
-}
-
-
-/*
- * check if the device uses big-endian samples
- */
-static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp)
-{
-       switch (chip->usb_id) {
-       case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
-               if (fp->endpoint & USB_DIR_IN)
-                       return 1;
-               break;
-       case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
-               if (device_setup[chip->index] == 0x00 ||
-                   fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
-                       return 1;
-       }
-       return 0;
-}
-
-/*
- * parse the audio format type I descriptor
- * and returns the corresponding pcm format
- *
- * @dev: usb device
- * @fp: audioformat record
- * @format: the format tag (wFormatTag)
- * @fmt: the format type descriptor
- */
-static int parse_audio_format_i_type(struct snd_usb_audio *chip,
-                                    struct audioformat *fp,
-                                    int format, void *_fmt,
-                                    int protocol)
-{
-       int pcm_format, i;
-       int sample_width, sample_bytes;
-
-       switch (protocol) {
-       case UAC_VERSION_1: {
-               struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
-               sample_width = fmt->bBitResolution;
-               sample_bytes = fmt->bSubframeSize;
-               break;
-       }
-
-       case UAC_VERSION_2: {
-               struct uac_format_type_i_ext_descriptor *fmt = _fmt;
-               sample_width = fmt->bBitResolution;
-               sample_bytes = fmt->bSubslotSize;
-
-               /*
-                * FIXME
-                * USB audio class v2 devices specify a bitmap of possible
-                * audio formats rather than one fix value. For now, we just
-                * pick one of them and report that as the only possible
-                * value for this setting.
-                * The bit allocation map is in fact compatible to the
-                * wFormatTag of the v1 AS streaming descriptors, which is why
-                * we can simply map the matrix.
-                */
-
-               for (i = 0; i < 5; i++)
-                       if (format & (1UL << i)) {
-                               format = i + 1;
-                               break;
-                       }
-
-               break;
-       }
-
-       default:
-               return -EINVAL;
-       }
-
-       /* FIXME: correct endianess and sign? */
-       pcm_format = -1;
-
-       switch (format) {
-       case UAC_FORMAT_TYPE_I_UNDEFINED: /* some devices don't define this correctly... */
-               snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
-                           chip->dev->devnum, fp->iface, fp->altsetting);
-               /* fall-through */
-       case UAC_FORMAT_TYPE_I_PCM:
-               if (sample_width > sample_bytes * 8) {
-                       snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
-                                  chip->dev->devnum, fp->iface, fp->altsetting,
-                                  sample_width, sample_bytes);
-               }
-               /* check the format byte size */
-               switch (sample_bytes) {
-               case 1:
-                       pcm_format = SNDRV_PCM_FORMAT_S8;
-                       break;
-               case 2:
-                       if (is_big_endian_format(chip, fp))
-                               pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */
-                       else
-                               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
-                       break;
-               case 3:
-                       if (is_big_endian_format(chip, fp))
-                               pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */
-                       else
-                               pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
-                       break;
-               case 4:
-                       pcm_format = SNDRV_PCM_FORMAT_S32_LE;
-                       break;
-               default:
-                       snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
-                                  chip->dev->devnum, fp->iface, fp->altsetting,
-                                  sample_width, sample_bytes);
-                       break;
-               }
-               break;
-       case UAC_FORMAT_TYPE_I_PCM8:
-               pcm_format = SNDRV_PCM_FORMAT_U8;
-
-               /* Dallas DS4201 workaround: it advertises U8 format, but really
-                  supports S8. */
-               if (chip->usb_id == USB_ID(0x04fa, 0x4201))
-                       pcm_format = SNDRV_PCM_FORMAT_S8;
-               break;
-       case UAC_FORMAT_TYPE_I_IEEE_FLOAT:
-               pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
-               break;
-       case UAC_FORMAT_TYPE_I_ALAW:
-               pcm_format = SNDRV_PCM_FORMAT_A_LAW;
-               break;
-       case UAC_FORMAT_TYPE_I_MULAW:
-               pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
-               break;
-       default:
-               snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
-                          chip->dev->devnum, fp->iface, fp->altsetting, format);
-               break;
-       }
-       return pcm_format;
-}
-
-
-/*
- * parse the format descriptor and stores the possible sample rates
- * on the audioformat table (audio class v1).
- *
- * @dev: usb device
- * @fp: audioformat record
- * @fmt: the format descriptor
- * @offset: the start offset of descriptor pointing the rate type
- *          (7 for type I and II, 8 for type II)
- */
-static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audioformat *fp,
-                                      unsigned char *fmt, int offset)
-{
-       int nr_rates = fmt[offset];
-
-       if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
-               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
-                                  chip->dev->devnum, fp->iface, fp->altsetting);
-               return -1;
-       }
-
-       if (nr_rates) {
-               /*
-                * build the rate table and bitmap flags
-                */
-               int r, idx;
-
-               fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
-               if (fp->rate_table == NULL) {
-                       snd_printk(KERN_ERR "cannot malloc\n");
-                       return -1;
-               }
-
-               fp->nr_rates = 0;
-               fp->rate_min = fp->rate_max = 0;
-               for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) {
-                       unsigned int rate = combine_triple(&fmt[idx]);
-                       if (!rate)
-                               continue;
-                       /* C-Media CM6501 mislabels its 96 kHz altsetting */
-                       if (rate == 48000 && nr_rates == 1 &&
-                           (chip->usb_id == USB_ID(0x0d8c, 0x0201) ||
-                            chip->usb_id == USB_ID(0x0d8c, 0x0102)) &&
-                           fp->altsetting == 5 && fp->maxpacksize == 392)
-                               rate = 96000;
-                       /* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */
-                       if (rate == 16000 && chip->usb_id == USB_ID(0x041e, 0x4068))
-                               rate = 8000;
-                       fp->rate_table[fp->nr_rates] = rate;
-                       if (!fp->rate_min || rate < fp->rate_min)
-                               fp->rate_min = rate;
-                       if (!fp->rate_max || rate > fp->rate_max)
-                               fp->rate_max = rate;
-                       fp->rates |= snd_pcm_rate_to_rate_bit(rate);
-                       fp->nr_rates++;
-               }
-               if (!fp->nr_rates) {
-                       hwc_debug("All rates were zero. Skipping format!\n");
-                       return -1;
-               }
-       } else {
-               /* continuous rates */
-               fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
-               fp->rate_min = combine_triple(&fmt[offset + 1]);
-               fp->rate_max = combine_triple(&fmt[offset + 4]);
-       }
-       return 0;
-}
-
-/*
- * parse the format descriptor and stores the possible sample rates
- * on the audioformat table (audio class v2).
- */
-static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
-                                      struct audioformat *fp,
-                                      struct usb_host_interface *iface)
-{
-       struct usb_device *dev = chip->dev;
-       unsigned char tmp[2], *data;
-       int i, nr_rates, data_size, ret = 0;
-
-       /* get the number of sample rates first by only fetching 2 bytes */
-       ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
-                              USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-                              0x0100, chip->clock_id << 8, tmp, sizeof(tmp), 1000);
-
-       if (ret < 0) {
-               snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
-               goto err;
-       }
-
-       nr_rates = (tmp[1] << 8) | tmp[0];
-       data_size = 2 + 12 * nr_rates;
-       data = kzalloc(data_size, GFP_KERNEL);
-       if (!data) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       /* now get the full information */
-       ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
-                              USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-                              0x0100, chip->clock_id << 8, data, data_size, 1000);
-
-       if (ret < 0) {
-               snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
-               ret = -EINVAL;
-               goto err_free;
-       }
-
-       fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
-       if (!fp->rate_table) {
-               ret = -ENOMEM;
-               goto err_free;
-       }
-
-       fp->nr_rates = 0;
-       fp->rate_min = fp->rate_max = 0;
-
-       for (i = 0; i < nr_rates; i++) {
-               int rate = combine_quad(&data[2 + 12 * i]);
-
-               fp->rate_table[fp->nr_rates] = rate;
-               if (!fp->rate_min || rate < fp->rate_min)
-                       fp->rate_min = rate;
-               if (!fp->rate_max || rate > fp->rate_max)
-                       fp->rate_max = rate;
-               fp->rates |= snd_pcm_rate_to_rate_bit(rate);
-               fp->nr_rates++;
-       }
-
-err_free:
-       kfree(data);
-err:
-       return ret;
-}
-
-/*
- * parse the format type I and III descriptors
- */
-static int parse_audio_format_i(struct snd_usb_audio *chip,
-                               struct audioformat *fp,
-                               int format, void *_fmt,
-                               struct usb_host_interface *iface)
-{
-       struct usb_interface_descriptor *altsd = get_iface_desc(iface);
-       struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
-       int protocol = altsd->bInterfaceProtocol;
-       int pcm_format, ret;
-
-       if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
-               /* FIXME: the format type is really IECxxx
-                *        but we give normal PCM format to get the existing
-                *        apps working...
-                */
-               switch (chip->usb_id) {
-
-               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
-                       if (device_setup[chip->index] == 0x00 && 
-                           fp->altsetting == 6)
-                               pcm_format = SNDRV_PCM_FORMAT_S16_BE;
-                       else
-                               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
-                       break;
-               default:
-                       pcm_format = SNDRV_PCM_FORMAT_S16_LE;
-               }
-       } else {
-               pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol);
-               if (pcm_format < 0)
-                       return -1;
-       }
-
-       fp->format = pcm_format;
-
-       /* gather possible sample rates */
-       /* audio class v1 reports possible sample rates as part of the
-        * proprietary class specific descriptor.
-        * audio class v2 uses class specific EP0 range requests for that.
-        */
-       switch (protocol) {
-       case UAC_VERSION_1:
-               fp->channels = fmt->bNrChannels;
-               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7);
-               break;
-       case UAC_VERSION_2:
-               /* fp->channels is already set in this case */
-               ret = parse_audio_format_rates_v2(chip, fp, iface);
-               break;
-       }
-
-       if (fp->channels < 1) {
-               snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
-                          chip->dev->devnum, fp->iface, fp->altsetting, fp->channels);
-               return -1;
-       }
-
-       return ret;
-}
-
-/*
- * parse the format type II descriptor
- */
-static int parse_audio_format_ii(struct snd_usb_audio *chip,
-                                struct audioformat *fp,
-                                int format, void *_fmt,
-                                struct usb_host_interface *iface)
-{
-       int brate, framesize, ret;
-       struct usb_interface_descriptor *altsd = get_iface_desc(iface);
-       int protocol = altsd->bInterfaceProtocol;
-
-       switch (format) {
-       case UAC_FORMAT_TYPE_II_AC3:
-               /* FIXME: there is no AC3 format defined yet */
-               // fp->format = SNDRV_PCM_FORMAT_AC3;
-               fp->format = SNDRV_PCM_FORMAT_U8; /* temporarily hack to receive byte streams */
-               break;
-       case UAC_FORMAT_TYPE_II_MPEG:
-               fp->format = SNDRV_PCM_FORMAT_MPEG;
-               break;
-       default:
-               snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected.  processed as MPEG.\n",
-                          chip->dev->devnum, fp->iface, fp->altsetting, format);
-               fp->format = SNDRV_PCM_FORMAT_MPEG;
-               break;
-       }
-
-       fp->channels = 1;
-
-       switch (protocol) {
-       case UAC_VERSION_1: {
-               struct uac_format_type_ii_discrete_descriptor *fmt = _fmt;
-               brate = le16_to_cpu(fmt->wMaxBitRate);
-               framesize = le16_to_cpu(fmt->wSamplesPerFrame);
-               snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
-               fp->frame_size = framesize;
-               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 8); /* fmt[8..] sample rates */
-               break;
-       }
-       case UAC_VERSION_2: {
-               struct uac_format_type_ii_ext_descriptor *fmt = _fmt;
-               brate = le16_to_cpu(fmt->wMaxBitRate);
-               framesize = le16_to_cpu(fmt->wSamplesPerFrame);
-               snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
-               fp->frame_size = framesize;
-               ret = parse_audio_format_rates_v2(chip, fp, iface);
-               break;
-       }
-       }
-
-       return ret;
-}
-
-static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
-                             int format, unsigned char *fmt, int stream,
-                             struct usb_host_interface *iface)
-{
-       int err;
-
-       switch (fmt[3]) {
-       case UAC_FORMAT_TYPE_I:
-       case UAC_FORMAT_TYPE_III:
-               err = parse_audio_format_i(chip, fp, format, fmt, iface);
-               break;
-       case UAC_FORMAT_TYPE_II:
-               err = parse_audio_format_ii(chip, fp, format, fmt, iface);
-               break;
-       default:
-               snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
-                          chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]);
-               return -1;
-       }
-       fp->fmt_type = fmt[3];
-       if (err < 0)
-               return err;
-#if 1
-       /* FIXME: temporary hack for extigy/audigy 2 nx/zs */
-       /* extigy apparently supports sample rates other than 48k
-        * but not in ordinary way.  so we enable only 48k atm.
-        */
-       if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
-           chip->usb_id == USB_ID(0x041e, 0x3020) ||
-           chip->usb_id == USB_ID(0x041e, 0x3061)) {
-               if (fmt[3] == UAC_FORMAT_TYPE_I &&
-                   fp->rates != SNDRV_PCM_RATE_48000 &&
-                   fp->rates != SNDRV_PCM_RATE_96000)
-                       return -1;
-       }
-#endif
-       return 0;
-}
-
-static unsigned char parse_datainterval(struct snd_usb_audio *chip,
-                                       struct usb_host_interface *alts)
-{
-       if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH &&
-           get_endpoint(alts, 0)->bInterval >= 1 &&
-           get_endpoint(alts, 0)->bInterval <= 4)
-               return get_endpoint(alts, 0)->bInterval - 1;
-       else
-               return 0;
-}
-
-static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
-                                        int iface, int altno);
-static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
-{
-       struct usb_device *dev;
-       struct usb_interface *iface;
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       int i, altno, err, stream;
-       int format = 0, num_channels = 0;
-       struct audioformat *fp = NULL;
-       unsigned char *fmt, *csep;
-       int num, protocol;
-
-       dev = chip->dev;
-
-       /* parse the interface's altsettings */
-       iface = usb_ifnum_to_if(dev, iface_no);
-
-       num = iface->num_altsetting;
-
-       /*
-        * Dallas DS4201 workaround: It presents 5 altsettings, but the last
-        * one misses syncpipe, and does not produce any sound.
-        */
-       if (chip->usb_id == USB_ID(0x04fa, 0x4201))
-               num = 4;
-
-       for (i = 0; i < num; i++) {
-               alts = &iface->altsetting[i];
-               altsd = get_iface_desc(alts);
-               protocol = altsd->bInterfaceProtocol;
-               /* skip invalid one */
-               if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
-                    altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
-                   (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
-                    altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) ||
-                   altsd->bNumEndpoints < 1 ||
-                   le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
-                       continue;
-               /* must be isochronous */
-               if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
-                   USB_ENDPOINT_XFER_ISOC)
-                       continue;
-               /* check direction */
-               stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
-                       SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-               altno = altsd->bAlternateSetting;
-       
-               /* audiophile usb: skip altsets incompatible with device_setup
-                */
-               if (chip->usb_id == USB_ID(0x0763, 0x2003) && 
-                   audiophile_skip_setting_quirk(chip, iface_no, altno))
-                       continue;
-
-               /* get audio formats */
-               switch (protocol) {
-               case UAC_VERSION_1: {
-                       struct uac_as_header_descriptor_v1 *as =
-                               snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
-
-                       if (!as) {
-                               snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
-                                          dev->devnum, iface_no, altno);
-                               continue;
-                       }
-
-                       if (as->bLength < sizeof(*as)) {
-                               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
-                                          dev->devnum, iface_no, altno);
-                               continue;
-                       }
-
-                       format = le16_to_cpu(as->wFormatTag); /* remember the format value */
-                       break;
-               }
-
-               case UAC_VERSION_2: {
-                       struct uac_as_header_descriptor_v2 *as =
-                               snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
-
-                       if (!as) {
-                               snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
-                                          dev->devnum, iface_no, altno);
-                               continue;
-                       }
-
-                       if (as->bLength < sizeof(*as)) {
-                               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
-                                          dev->devnum, iface_no, altno);
-                               continue;
-                       }
-
-                       num_channels = as->bNrChannels;
-                       format = le32_to_cpu(as->bmFormats);
-
-                       break;
-               }
-
-               default:
-                       snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n",
-                                  dev->devnum, iface_no, altno, protocol);
-                       continue;
-               }
-
-               /* get format type */
-               fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
-               if (!fmt) {
-                       snd_printk(KERN_ERR "%d:%u:%d : no UAC_FORMAT_TYPE desc\n",
-                                  dev->devnum, iface_no, altno);
-                       continue;
-               }
-               if (((protocol == UAC_VERSION_1) && (fmt[0] < 8)) ||
-                   ((protocol == UAC_VERSION_2) && (fmt[0] != 6))) {
-                       snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
-                                  dev->devnum, iface_no, altno);
-                       continue;
-               }
-
-               /*
-                * Blue Microphones workaround: The last altsetting is identical
-                * with the previous one, except for a larger packet size, but
-                * is actually a mislabeled two-channel setting; ignore it.
-                */
-               if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 &&
-                   fp && fp->altsetting == 1 && fp->channels == 1 &&
-                   fp->format == SNDRV_PCM_FORMAT_S16_LE &&
-                   protocol == UAC_VERSION_1 &&
-                   le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
-                                                       fp->maxpacksize * 2)
-                       continue;
-
-               csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
-               /* Creamware Noah has this descriptor after the 2nd endpoint */
-               if (!csep && altsd->bNumEndpoints >= 2)
-                       csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
-               if (!csep || csep[0] < 7 || csep[2] != UAC_EP_GENERAL) {
-                       snd_printk(KERN_WARNING "%d:%u:%d : no or invalid"
-                                  " class specific endpoint descriptor\n",
-                                  dev->devnum, iface_no, altno);
-                       csep = NULL;
-               }
-
-               fp = kzalloc(sizeof(*fp), GFP_KERNEL);
-               if (! fp) {
-                       snd_printk(KERN_ERR "cannot malloc\n");
-                       return -ENOMEM;
-               }
-
-               fp->iface = iface_no;
-               fp->altsetting = altno;
-               fp->altset_idx = i;
-               fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
-               fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
-               fp->datainterval = parse_datainterval(chip, alts);
-               fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
-               /* num_channels is only set for v2 interfaces */
-               fp->channels = num_channels;
-               if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
-                       fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
-                                       * (fp->maxpacksize & 0x7ff);
-               fp->attributes = csep ? csep[3] : 0;
-
-               /* some quirks for attributes here */
-
-               switch (chip->usb_id) {
-               case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
-                       /* Optoplay sets the sample rate attribute although
-                        * it seems not supporting it in fact.
-                        */
-                       fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
-                       break;
-               case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
-               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
-                       /* doesn't set the sample rate attribute, but supports it */
-                       fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
-                       break;
-               case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
-               case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
-                                               an older model 77d:223) */
-               /*
-                * plantronics headset and Griffin iMic have set adaptive-in
-                * although it's really not...
-                */
-                       fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
-                       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-                               fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
-                       else
-                               fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
-                       break;
-               }
-
-               /* ok, let's parse further... */
-               if (parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
-                       kfree(fp->rate_table);
-                       kfree(fp);
-                       continue;
-               }
-
-               snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint);
-               err = add_audio_endpoint(chip, stream, fp);
-               if (err < 0) {
-                       kfree(fp->rate_table);
-                       kfree(fp);
-                       return err;
-               }
-               /* try to set the interface... */
-               usb_set_interface(chip->dev, iface_no, altno);
-               init_usb_pitch(chip->dev, iface_no, alts, fp);
-               init_usb_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max);
-       }
-       return 0;
-}
-
-
-/*
- * disconnect streams
- * called from snd_usb_audio_disconnect()
- */
-static void snd_usb_stream_disconnect(struct list_head *head)
-{
-       int idx;
-       struct snd_usb_stream *as;
-       struct snd_usb_substream *subs;
-
-       as = list_entry(head, struct snd_usb_stream, list);
-       for (idx = 0; idx < 2; idx++) {
-               subs = &as->substream[idx];
-               if (!subs->num_formats)
-                       return;
-               release_substream_urbs(subs, 1);
-               subs->interface = -1;
-       }
-}
-
-static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface)
-{
-       struct usb_device *dev = chip->dev;
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       struct usb_interface *iface = usb_ifnum_to_if(dev, interface);
-
-       if (!iface) {
-               snd_printk(KERN_ERR "%d:%u:%d : does not exist\n",
-                          dev->devnum, ctrlif, interface);
-               return -EINVAL;
-       }
-
-       if (usb_interface_claimed(iface)) {
-               snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n",
-                                               dev->devnum, ctrlif, interface);
-               return -EINVAL;
-       }
-
-       alts = &iface->altsetting[0];
-       altsd = get_iface_desc(alts);
-       if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
-            altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
-           altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
-               int err = snd_usbmidi_create(chip->card, iface,
-                                            &chip->midi_list, NULL);
-               if (err < 0) {
-                       snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n",
-                                               dev->devnum, ctrlif, interface);
-                       return -EINVAL;
-               }
-               usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
-
-               return 0;
-       }
-
-       if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
-            altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
-           altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) {
-               snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n",
-                                       dev->devnum, ctrlif, interface, altsd->bInterfaceClass);
-               /* skip non-supported classes */
-               return -EINVAL;
-       }
-
-       if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
-               snd_printk(KERN_ERR "low speed audio streaming not supported\n");
-               return -EINVAL;
-       }
-
-       if (! parse_audio_endpoints(chip, interface)) {
-               usb_set_interface(dev, interface, 0); /* reset the current interface */
-               usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/*
- * parse audio control descriptor and create pcm/midi streams
- */
-static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
-{
-       struct usb_device *dev = chip->dev;
-       struct usb_host_interface *host_iface;
-       struct usb_interface_descriptor *altsd;
-       void *control_header;
-       int i, protocol;
-
-       /* find audiocontrol interface */
-       host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
-       control_header = snd_usb_find_csint_desc(host_iface->extra,
-                                                host_iface->extralen,
-                                                NULL, UAC_HEADER);
-       altsd = get_iface_desc(host_iface);
-       protocol = altsd->bInterfaceProtocol;
-
-       if (!control_header) {
-               snd_printk(KERN_ERR "cannot find UAC_HEADER\n");
-               return -EINVAL;
-       }
-
-       switch (protocol) {
-       case UAC_VERSION_1: {
-               struct uac_ac_header_descriptor_v1 *h1 = control_header;
-
-               if (!h1->bInCollection) {
-                       snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
-                       return -EINVAL;
-               }
-
-               if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
-                       snd_printk(KERN_ERR "invalid UAC_HEADER (v1)\n");
-                       return -EINVAL;
-               }
-
-               for (i = 0; i < h1->bInCollection; i++)
-                       snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);
-
-               break;
-       }
-
-       case UAC_VERSION_2: {
-               struct uac_clock_source_descriptor *cs;
-               struct usb_interface_assoc_descriptor *assoc =
-                       usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
-
-               if (!assoc) {
-                       snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n");
-                       return -EINVAL;
-               }
-
-               /* FIXME: for now, we expect there is at least one clock source
-                * descriptor and we always take the first one.
-                * We should properly support devices with multiple clock sources,
-                * clock selectors and sample rate conversion units. */
-
-               cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
-                                               NULL, UAC_CLOCK_SOURCE);
-
-               if (!cs) {
-                       snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
-                       return -EINVAL;
-               }
-
-               chip->clock_id = cs->bClockID;
-
-               for (i = 0; i < assoc->bInterfaceCount; i++) {
-                       int intf = assoc->bFirstInterface + i;
-
-                       if (intf != ctrlif)
-                               snd_usb_create_stream(chip, ctrlif, intf);
-               }
-
-               break;
-       }
-
-       default:
-               snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/*
- * create a stream for an endpoint/altsetting without proper descriptors
- */
-static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
-                                    struct usb_interface *iface,
-                                    const struct snd_usb_audio_quirk *quirk)
-{
-       struct audioformat *fp;
-       struct usb_host_interface *alts;
-       int stream, err;
-       unsigned *rate_table = NULL;
-
-       fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
-       if (! fp) {
-               snd_printk(KERN_ERR "cannot memdup\n");
-               return -ENOMEM;
-       }
-       if (fp->nr_rates > 0) {
-               rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL);
-               if (!rate_table) {
-                       kfree(fp);
-                       return -ENOMEM;
-               }
-               memcpy(rate_table, fp->rate_table, sizeof(int) * fp->nr_rates);
-               fp->rate_table = rate_table;
-       }
-
-       stream = (fp->endpoint & USB_DIR_IN)
-               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-       err = add_audio_endpoint(chip, stream, fp);
-       if (err < 0) {
-               kfree(fp);
-               kfree(rate_table);
-               return err;
-       }
-       if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
-           fp->altset_idx >= iface->num_altsetting) {
-               kfree(fp);
-               kfree(rate_table);
-               return -EINVAL;
-       }
-       alts = &iface->altsetting[fp->altset_idx];
-       fp->datainterval = parse_datainterval(chip, alts);
-       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
-       usb_set_interface(chip->dev, fp->iface, 0);
-       init_usb_pitch(chip->dev, fp->iface, alts, fp);
-       init_usb_sample_rate(chip->dev, fp->iface, alts, fp, fp->rate_max);
-       return 0;
-}
-
-/*
- * create a stream for an interface with proper descriptors
- */
-static int create_standard_audio_quirk(struct snd_usb_audio *chip,
-                                      struct usb_interface *iface,
-                                      const struct snd_usb_audio_quirk *quirk)
-{
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       int err;
-
-       alts = &iface->altsetting[0];
-       altsd = get_iface_desc(alts);
-       err = parse_audio_endpoints(chip, altsd->bInterfaceNumber);
-       if (err < 0) {
-               snd_printk(KERN_ERR "cannot setup if %d: error %d\n",
-                          altsd->bInterfaceNumber, err);
-               return err;
-       }
-       /* reset the current interface */
-       usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0);
-       return 0;
-}
-
-/*
- * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.  
- * The only way to detect the sample rate is by looking at wMaxPacketSize.
- */
-static int create_uaxx_quirk(struct snd_usb_audio *chip,
-                             struct usb_interface *iface,
-                             const struct snd_usb_audio_quirk *quirk)
-{
-       static const struct audioformat ua_format = {
-               .format = SNDRV_PCM_FORMAT_S24_3LE,
-               .channels = 2,
-               .fmt_type = UAC_FORMAT_TYPE_I,
-               .altsetting = 1,
-               .altset_idx = 1,
-               .rates = SNDRV_PCM_RATE_CONTINUOUS,
-       };
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       struct audioformat *fp;
-       int stream, err;
-
-       /* both PCM and MIDI interfaces have 2 or more altsettings */
-       if (iface->num_altsetting < 2)
-               return -ENXIO;
-       alts = &iface->altsetting[1];
-       altsd = get_iface_desc(alts);
-
-       if (altsd->bNumEndpoints == 2) {
-               static const struct snd_usb_midi_endpoint_info ua700_ep = {
-                       .out_cables = 0x0003,
-                       .in_cables  = 0x0003
-               };
-               static const struct snd_usb_audio_quirk ua700_quirk = {
-                       .type = QUIRK_MIDI_FIXED_ENDPOINT,
-                       .data = &ua700_ep
-               };
-               static const struct snd_usb_midi_endpoint_info uaxx_ep = {
-                       .out_cables = 0x0001,
-                       .in_cables  = 0x0001
-               };
-               static const struct snd_usb_audio_quirk uaxx_quirk = {
-                       .type = QUIRK_MIDI_FIXED_ENDPOINT,
-                       .data = &uaxx_ep
-               };
-               const struct snd_usb_audio_quirk *quirk =
-                       chip->usb_id == USB_ID(0x0582, 0x002b)
-                       ? &ua700_quirk : &uaxx_quirk;
-               return snd_usbmidi_create(chip->card, iface,
-                                         &chip->midi_list, quirk);
-       }
-
-       if (altsd->bNumEndpoints != 1)
-               return -ENXIO;
-
-       fp = kmalloc(sizeof(*fp), GFP_KERNEL);
-       if (!fp)
-               return -ENOMEM;
-       memcpy(fp, &ua_format, sizeof(*fp));
-
-       fp->iface = altsd->bInterfaceNumber;
-       fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
-       fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
-       fp->datainterval = 0;
-       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
-
-       switch (fp->maxpacksize) {
-       case 0x120:
-               fp->rate_max = fp->rate_min = 44100;
-               break;
-       case 0x138:
-       case 0x140:
-               fp->rate_max = fp->rate_min = 48000;
-               break;
-       case 0x258:
-       case 0x260:
-               fp->rate_max = fp->rate_min = 96000;
-               break;
-       default:
-               snd_printk(KERN_ERR "unknown sample rate\n");
-               kfree(fp);
-               return -ENXIO;
-       }
-
-       stream = (fp->endpoint & USB_DIR_IN)
-               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-       err = add_audio_endpoint(chip, stream, fp);
-       if (err < 0) {
-               kfree(fp);
-               return err;
-       }
-       usb_set_interface(chip->dev, fp->iface, 0);
-       return 0;
-}
-
-static int snd_usb_create_quirk(struct snd_usb_audio *chip,
-                               struct usb_interface *iface,
-                               const struct snd_usb_audio_quirk *quirk);
-
-/*
- * handle the quirks for the contained interfaces
- */
-static int create_composite_quirk(struct snd_usb_audio *chip,
-                                 struct usb_interface *iface,
-                                 const struct snd_usb_audio_quirk *quirk)
-{
-       int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
-       int err;
-
-       for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) {
-               iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
-               if (!iface)
-                       continue;
-               if (quirk->ifnum != probed_ifnum &&
-                   usb_interface_claimed(iface))
-                       continue;
-               err = snd_usb_create_quirk(chip, iface, quirk);
-               if (err < 0)
-                       return err;
-               if (quirk->ifnum != probed_ifnum)
-                       usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
-       }
-       return 0;
-}
-
-static int ignore_interface_quirk(struct snd_usb_audio *chip,
-                                 struct usb_interface *iface,
-                                 const struct snd_usb_audio_quirk *quirk)
-{
-       return 0;
-}
-
-/*
- * Allow alignment on audio sub-slot (channel samples) rather than
- * on audio slots (audio frames)
- */
-static int create_align_transfer_quirk(struct snd_usb_audio *chip,
-                                 struct usb_interface *iface,
-                                 const struct snd_usb_audio_quirk *quirk)
-{
-       chip->txfr_quirk = 1;
-       return 1;       /* Continue with creating streams and mixer */
-}
-
-
-/*
- * boot quirks
- */
-
-#define EXTIGY_FIRMWARE_SIZE_OLD 794
-#define EXTIGY_FIRMWARE_SIZE_NEW 483
-
-static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
-{
-       struct usb_host_config *config = dev->actconfig;
-       int err;
-
-       if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
-           le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) {
-               snd_printdd("sending Extigy boot sequence...\n");
-               /* Send message to force it to reconnect with full interface. */
-               err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0),
-                                     0x10, 0x43, 0x0001, 0x000a, NULL, 0, 1000);
-               if (err < 0) snd_printdd("error sending boot message: %d\n", err);
-               err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
-                               &dev->descriptor, sizeof(dev->descriptor));
-               config = dev->actconfig;
-               if (err < 0) snd_printdd("error usb_get_descriptor: %d\n", err);
-               err = usb_reset_configuration(dev);
-               if (err < 0) snd_printdd("error usb_reset_configuration: %d\n", err);
-               snd_printdd("extigy_boot: new boot length = %d\n",
-                           le16_to_cpu(get_cfg_desc(config)->wTotalLength));
-               return -ENODEV; /* quit this anyway */
-       }
-       return 0;
-}
-
-static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
-{
-       u8 buf = 1;
-
-       snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
-                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-                       0, 0, &buf, 1, 1000);
-       if (buf == 0) {
-               snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
-                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-                               1, 2000, NULL, 0, 1000);
-               return -ENODEV;
-       }
-       return 0;
-}
-
-/*
- * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
- * documented in the device's data sheet.
- */
-static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value)
-{
-       u8 buf[4];
-       buf[0] = 0x20;
-       buf[1] = value & 0xff;
-       buf[2] = (value >> 8) & 0xff;
-       buf[3] = reg;
-       return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
-                              USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
-                              0, 0, &buf, 4, 1000);
-}
-
-static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
-{
-       /*
-        * Enable line-out driver mode, set headphone source to front
-        * channels, enable stereo mic.
-        */
-       return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
-}
-
-/*
- * C-Media CM6206 is based on CM106 with two additional
- * registers that are not documented in the data sheet.
- * Values here are chosen based on sniffing USB traffic
- * under Windows.
- */
-static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
-{
-       int err, reg;
-       int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
-
-       for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
-               err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
-               if (err < 0)
-                       return err;
-       }
-
-       return err;
-}
-
-/*
- * This call will put the synth in "USB send" mode, i.e it will send MIDI
- * messages through USB (this is disabled at startup). The synth will
- * acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB
- * sign on its LCD. Values here are chosen based on sniffing USB traffic
- * under Windows.
- */
-static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
-{
-       int err, actual_length;
-
-       /* "midi send" enable */
-       static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 };
-
-       void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-       err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf,
-                       ARRAY_SIZE(seq), &actual_length, 1000);
-       kfree(buf);
-       if (err < 0)
-               return err;
-
-       return 0;
-}
-
-/*
- * Setup quirks
- */
-#define AUDIOPHILE_SET                 0x01 /* if set, parse device_setup */
-#define AUDIOPHILE_SET_DTS              0x02 /* if set, enable DTS Digital Output */
-#define AUDIOPHILE_SET_96K              0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */
-#define AUDIOPHILE_SET_24B             0x08 /* 24bits sample if set, 16bits otherwise */
-#define AUDIOPHILE_SET_DI              0x10 /* if set, enable Digital Input */
-#define AUDIOPHILE_SET_MASK            0x1F /* bit mask for setup value */
-#define AUDIOPHILE_SET_24B_48K_DI      0x19 /* value for 24bits+48KHz+Digital Input */
-#define AUDIOPHILE_SET_24B_48K_NOTDI   0x09 /* value for 24bits+48KHz+No Digital Input */
-#define AUDIOPHILE_SET_16B_48K_DI      0x11 /* value for 16bits+48KHz+Digital Input */
-#define AUDIOPHILE_SET_16B_48K_NOTDI   0x01 /* value for 16bits+48KHz+No Digital Input */
-
-static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
-                                        int iface, int altno)
-{
-       /* Reset ALL ifaces to 0 altsetting.
-        * Call it for every possible altsetting of every interface.
-        */
-       usb_set_interface(chip->dev, iface, 0);
-
-       if (device_setup[chip->index] & AUDIOPHILE_SET) {
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS)
-                   && altno != 6)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_96K)
-                   && altno != 1)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) ==
-                   AUDIOPHILE_SET_24B_48K_DI && altno != 2)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) ==
-                   AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) ==
-                   AUDIOPHILE_SET_16B_48K_DI && altno != 4)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) ==
-                   AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5)
-                       return 1; /* skip this altsetting */
-       }       
-       return 0; /* keep this altsetting */
-}
-
-static int create_any_midi_quirk(struct snd_usb_audio *chip,
-                                struct usb_interface *intf,
-                                const struct snd_usb_audio_quirk *quirk)
-{
-       return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk);
-}
-
-/*
- * audio-interface quirks
- *
- * returns zero if no standard audio/MIDI parsing is needed.
- * returns a postive value if standard audio/midi interfaces are parsed
- * after this.
- * returns a negative value at error.
- */
-static int snd_usb_create_quirk(struct snd_usb_audio *chip,
-                               struct usb_interface *iface,
-                               const struct snd_usb_audio_quirk *quirk)
-{
-       typedef int (*quirk_func_t)(struct snd_usb_audio *, struct usb_interface *,
-                                   const struct snd_usb_audio_quirk *);
-       static const quirk_func_t quirk_funcs[] = {
-               [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
-               [QUIRK_COMPOSITE] = create_composite_quirk,
-               [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
-               [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
-               [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
-               [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
-               [QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
-               [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk,
-               [QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
-               [QUIRK_MIDI_CME] = create_any_midi_quirk,
-               [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
-               [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
-               [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
-               [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk
-       };
-
-       if (quirk->type < QUIRK_TYPE_COUNT) {
-               return quirk_funcs[quirk->type](chip, iface, quirk);
-       } else {
-               snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
-               return -ENXIO;
-       }
-}
-
-
-/*
- * common proc files to show the usb device info
- */
-static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
-{
-       struct snd_usb_audio *chip = entry->private_data;
-       if (!chip->shutdown)
-               snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum);
-}
-
-static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
-{
-       struct snd_usb_audio *chip = entry->private_data;
-       if (!chip->shutdown)
-               snd_iprintf(buffer, "%04x:%04x\n", 
-                           USB_ID_VENDOR(chip->usb_id),
-                           USB_ID_PRODUCT(chip->usb_id));
-}
-
-static void snd_usb_audio_create_proc(struct snd_usb_audio *chip)
-{
-       struct snd_info_entry *entry;
-       if (!snd_card_proc_new(chip->card, "usbbus", &entry))
-               snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read);
-       if (!snd_card_proc_new(chip->card, "usbid", &entry))
-               snd_info_set_text_ops(entry, chip, proc_audio_usbid_read);
-}
-
-/*
- * free the chip instance
- *
- * here we have to do not much, since pcm and controls are already freed
- *
- */
-
-static int snd_usb_audio_free(struct snd_usb_audio *chip)
-{
-       kfree(chip);
-       return 0;
-}
-
-static int snd_usb_audio_dev_free(struct snd_device *device)
-{
-       struct snd_usb_audio *chip = device->device_data;
-       return snd_usb_audio_free(chip);
-}
-
-
-/*
- * create a chip instance and set its names.
- */
-static int snd_usb_audio_create(struct usb_device *dev, int idx,
-                               const struct snd_usb_audio_quirk *quirk,
-                               struct snd_usb_audio **rchip)
-{
-       struct snd_card *card;
-       struct snd_usb_audio *chip;
-       int err, len;
-       char component[14];
-       static struct snd_device_ops ops = {
-               .dev_free =     snd_usb_audio_dev_free,
-       };
-
-       *rchip = NULL;
-
-       if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
-           snd_usb_get_speed(dev) != USB_SPEED_FULL &&
-           snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
-               snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
-               return -ENXIO;
-       }
-
-       err = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card);
-       if (err < 0) {
-               snd_printk(KERN_ERR "cannot create card instance %d\n", idx);
-               return err;
-       }
-
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-       if (! chip) {
-               snd_card_free(card);
-               return -ENOMEM;
-       }
-
-       chip->index = idx;
-       chip->dev = dev;
-       chip->card = card;
-       chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
-                             le16_to_cpu(dev->descriptor.idProduct));
-       INIT_LIST_HEAD(&chip->pcm_list);
-       INIT_LIST_HEAD(&chip->midi_list);
-       INIT_LIST_HEAD(&chip->mixer_list);
-
-       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-               snd_usb_audio_free(chip);
-               snd_card_free(card);
-               return err;
-       }
-
-       strcpy(card->driver, "USB-Audio");
-       sprintf(component, "USB%04x:%04x",
-               USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
-       snd_component_add(card, component);
-
-       /* retrieve the device string as shortname */
-       if (quirk && quirk->product_name) {
-               strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname));
-       } else {
-               if (!dev->descriptor.iProduct ||
-                   usb_string(dev, dev->descriptor.iProduct,
-                              card->shortname, sizeof(card->shortname)) <= 0) {
-                       /* no name available from anywhere, so use ID */
-                       sprintf(card->shortname, "USB Device %#04x:%#04x",
-                               USB_ID_VENDOR(chip->usb_id),
-                               USB_ID_PRODUCT(chip->usb_id));
-               }
-       }
-
-       /* retrieve the vendor and device strings as longname */
-       if (quirk && quirk->vendor_name) {
-               len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname));
-       } else {
-               if (dev->descriptor.iManufacturer)
-                       len = usb_string(dev, dev->descriptor.iManufacturer,
-                                        card->longname, sizeof(card->longname));
-               else
-                       len = 0;
-               /* we don't really care if there isn't any vendor string */
-       }
-       if (len > 0)
-               strlcat(card->longname, " ", sizeof(card->longname));
-
-       strlcat(card->longname, card->shortname, sizeof(card->longname));
-
-       len = strlcat(card->longname, " at ", sizeof(card->longname));
-
-       if (len < sizeof(card->longname))
-               usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
-
-       strlcat(card->longname,
-               snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
-               snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
-               ", high speed",
-               sizeof(card->longname));
-
-       snd_usb_audio_create_proc(chip);
-
-       *rchip = chip;
-       return 0;
-}
-
-
-/*
- * probe the active usb device
- *
- * note that this can be called multiple times per a device, when it
- * includes multiple audio control interfaces.
- *
- * thus we check the usb device pointer and creates the card instance
- * only at the first time.  the successive calls of this function will
- * append the pcm interface to the corresponding card.
- */
-static void *snd_usb_audio_probe(struct usb_device *dev,
-                                struct usb_interface *intf,
-                                const struct usb_device_id *usb_id)
-{
-       const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info;
-       int i, err;
-       struct snd_usb_audio *chip;
-       struct usb_host_interface *alts;
-       int ifnum;
-       u32 id;
-
-       alts = &intf->altsetting[0];
-       ifnum = get_iface_desc(alts)->bInterfaceNumber;
-       id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
-                   le16_to_cpu(dev->descriptor.idProduct));
-       if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
-               goto __err_val;
-
-       /* SB Extigy needs special boot-up sequence */
-       /* if more models come, this will go to the quirk list. */
-       if (id == USB_ID(0x041e, 0x3000)) {
-               if (snd_usb_extigy_boot_quirk(dev, intf) < 0)
-                       goto __err_val;
-       }
-       /* SB Audigy 2 NX needs its own boot-up magic, too */
-       if (id == USB_ID(0x041e, 0x3020)) {
-               if (snd_usb_audigy2nx_boot_quirk(dev) < 0)
-                       goto __err_val;
-       }
-
-       /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
-       if (id == USB_ID(0x10f5, 0x0200)) {
-               if (snd_usb_cm106_boot_quirk(dev) < 0)
-                       goto __err_val;
-       }
-
-       /* C-Media CM6206 / CM106-Like Sound Device */
-       if (id == USB_ID(0x0d8c, 0x0102)) {
-               if (snd_usb_cm6206_boot_quirk(dev) < 0)
-                       goto __err_val;
-       }
-
-       /* Access Music VirusTI Desktop */
-       if (id == USB_ID(0x133e, 0x0815)) {
-               if (snd_usb_accessmusic_boot_quirk(dev) < 0)
-                       goto __err_val;
-       }
-
-       /*
-        * found a config.  now register to ALSA
-        */
-
-       /* check whether it's already registered */
-       chip = NULL;
-       mutex_lock(&register_mutex);
-       for (i = 0; i < SNDRV_CARDS; i++) {
-               if (usb_chip[i] && usb_chip[i]->dev == dev) {
-                       if (usb_chip[i]->shutdown) {
-                               snd_printk(KERN_ERR "USB device is in the shutdown state, cannot create a card instance\n");
-                               goto __error;
-                       }
-                       chip = usb_chip[i];
-                       break;
-               }
-       }
-       if (! chip) {
-               /* it's a fresh one.
-                * now look for an empty slot and create a new card instance
-                */
-               for (i = 0; i < SNDRV_CARDS; i++)
-                       if (enable[i] && ! usb_chip[i] &&
-                           (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
-                           (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
-                               if (snd_usb_audio_create(dev, i, quirk, &chip) < 0) {
-                                       goto __error;
-                               }
-                               snd_card_set_dev(chip->card, &intf->dev);
-                               break;
-                       }
-               if (!chip) {
-                       printk(KERN_ERR "no available usb audio device\n");
-                       goto __error;
-               }
-       }
-
-       chip->txfr_quirk = 0;
-       err = 1; /* continue */
-       if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
-               /* need some special handlings */
-               if ((err = snd_usb_create_quirk(chip, intf, quirk)) < 0)
-                       goto __error;
-       }
-
-       if (err > 0) {
-               /* create normal USB audio interfaces */
-               if (snd_usb_create_streams(chip, ifnum) < 0 ||
-                   snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) {
-                       goto __error;
-               }
-       }
-
-       /* we are allowed to call snd_card_register() many times */
-       if (snd_card_register(chip->card) < 0) {
-               goto __error;
-       }
-
-       usb_chip[chip->index] = chip;
-       chip->num_interfaces++;
-       mutex_unlock(&register_mutex);
-       return chip;
-
- __error:
-       if (chip && !chip->num_interfaces)
-               snd_card_free(chip->card);
-       mutex_unlock(&register_mutex);
- __err_val:
-       return NULL;
-}
-
-/*
- * we need to take care of counter, since disconnection can be called also
- * many times as well as usb_audio_probe().
- */
-static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
-{
-       struct snd_usb_audio *chip;
-       struct snd_card *card;
-       struct list_head *p;
-
-       if (ptr == (void *)-1L)
-               return;
-
-       chip = ptr;
-       card = chip->card;
-       mutex_lock(&register_mutex);
-       chip->shutdown = 1;
-       chip->num_interfaces--;
-       if (chip->num_interfaces <= 0) {
-               snd_card_disconnect(card);
-               /* release the pcm resources */
-               list_for_each(p, &chip->pcm_list) {
-                       snd_usb_stream_disconnect(p);
-               }
-               /* release the midi resources */
-               list_for_each(p, &chip->midi_list) {
-                       snd_usbmidi_disconnect(p);
-               }
-               /* release mixer resources */
-               list_for_each(p, &chip->mixer_list) {
-                       snd_usb_mixer_disconnect(p);
-               }
-               usb_chip[chip->index] = NULL;
-               mutex_unlock(&register_mutex);
-               snd_card_free_when_closed(card);
-       } else {
-               mutex_unlock(&register_mutex);
-       }
-}
-
-/*
- * new 2.5 USB kernel API
- */
-static int usb_audio_probe(struct usb_interface *intf,
-                          const struct usb_device_id *id)
-{
-       void *chip;
-       chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id);
-       if (chip) {
-               usb_set_intfdata(intf, chip);
-               return 0;
-       } else
-               return -EIO;
-}
-
-static void usb_audio_disconnect(struct usb_interface *intf)
-{
-       snd_usb_audio_disconnect(interface_to_usbdev(intf),
-                                usb_get_intfdata(intf));
-}
-
-#ifdef CONFIG_PM
-static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
-{
-       struct snd_usb_audio *chip = usb_get_intfdata(intf);
-       struct list_head *p;
-       struct snd_usb_stream *as;
-
-       if (chip == (void *)-1L)
-               return 0;
-
-       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
-       if (!chip->num_suspended_intf++) {
-               list_for_each(p, &chip->pcm_list) {
-                       as = list_entry(p, struct snd_usb_stream, list);
-                       snd_pcm_suspend_all(as->pcm);
-               }
-       }
-
-       return 0;
-}
-
-static int usb_audio_resume(struct usb_interface *intf)
-{
-       struct snd_usb_audio *chip = usb_get_intfdata(intf);
-
-       if (chip == (void *)-1L)
-               return 0;
-       if (--chip->num_suspended_intf)
-               return 0;
-       /*
-        * ALSA leaves material resumption to user space
-        * we just notify
-        */
-
-       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
-
-       return 0;
-}
-#endif         /* CONFIG_PM */
-
-static int __init snd_usb_audio_init(void)
-{
-       if (nrpacks < 1 || nrpacks > MAX_PACKS) {
-               printk(KERN_WARNING "invalid nrpacks value.\n");
-               return -EINVAL;
-       }
-       return usb_register(&usb_audio_driver);
-}
-
-
-static void __exit snd_usb_audio_cleanup(void)
-{
-       usb_deregister(&usb_audio_driver);
-}
-
-module_init(snd_usb_audio_init);
-module_exit(snd_usb_audio_cleanup);
index 42c299cbf63aefbae36d2c42679d7ba9ebd0a930..d679e72a3e5c921184fbea1d0cbffc94393b176b 100644 (file)
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-/* maximum number of endpoints per interface */
-#define MIDI_MAX_ENDPOINTS 2
-
 /* handling of USB vendor/product ID pairs as 32-bit numbers */
 #define USB_ID(vendor, product) (((vendor) << 16) | (product))
 #define USB_ID_VENDOR(id) ((id) >> 16)
 #define USB_ID_PRODUCT(id) ((u16)(id))
 
 /*
+ *
  */
 
 struct snd_usb_audio {
@@ -51,6 +49,10 @@ struct snd_usb_audio {
        struct list_head midi_list;     /* list of midi interfaces */
 
        struct list_head mixer_list;    /* list of mixer interfaces */
+
+       int setup;                      /* from the 'device_setup' module param */
+       int nrpacks;                    /* from the 'nrpacks' module param */
+       int async_unlink;               /* from the 'async_unlink' module param */
 };
 
 /*
@@ -89,93 +91,8 @@ struct snd_usb_audio_quirk {
        const void *data;
 };
 
-/* data for QUIRK_MIDI_FIXED_ENDPOINT */
-struct snd_usb_midi_endpoint_info {
-       int8_t   out_ep;        /* ep number, 0 autodetect */
-       uint8_t  out_interval;  /* interval for interrupt endpoints */
-       int8_t   in_ep; 
-       uint8_t  in_interval;
-       uint16_t out_cables;    /* bitmask */
-       uint16_t in_cables;     /* bitmask */
-};
-
-/* for QUIRK_MIDI_YAMAHA, data is NULL */
-
-/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info
- * structure (out_cables and in_cables only) */
-
-/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk
- * structures, terminated with .ifnum = -1 */
-
-/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */
-
-/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */
-
-/* for QUIRK_AUDIO_EDIROL_UAXX, data is NULL */
-
-/* for QUIRK_IGNORE_INTERFACE, data is NULL */
-
-/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */
-
-/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info
- * structure (out_cables and in_cables only) */
-
-/* for QUIRK_MIDI_CME, data is NULL */
-
-/*
- */
-
-/*E-mu USB samplerate control quirk*/
-enum {
-       EMU_QUIRK_SR_44100HZ = 0,
-       EMU_QUIRK_SR_48000HZ,
-       EMU_QUIRK_SR_88200HZ,
-       EMU_QUIRK_SR_96000HZ,
-       EMU_QUIRK_SR_176400HZ,
-       EMU_QUIRK_SR_192000HZ
-};
-
 #define combine_word(s)    ((*(s)) | ((unsigned int)(s)[1] << 8))
 #define combine_triple(s)  (combine_word(s) | ((unsigned int)(s)[2] << 16))
 #define combine_quad(s)    (combine_triple(s) | ((unsigned int)(s)[3] << 24))
 
-unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size);
-
-void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype);
-void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype);
-
-int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
-                   __u8 request, __u8 requesttype, __u16 value, __u16 index,
-                   void *data, __u16 size, int timeout);
-
-int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
-                        int ignore_error);
-void snd_usb_mixer_disconnect(struct list_head *p);
-
-int snd_usbmidi_create(struct snd_card *card,
-                      struct usb_interface *iface,
-                      struct list_head *midi_list,
-                      const struct snd_usb_audio_quirk *quirk);
-void snd_usbmidi_input_stop(struct list_head* p);
-void snd_usbmidi_input_start(struct list_head* p);
-void snd_usbmidi_disconnect(struct list_head *p);
-
-void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
-                       unsigned char samplerate_id);
-
-/*
- * retrieve usb_interface descriptor from the host interface
- * (conditional for compatibility with the older API)
- */
-#ifndef get_iface_desc
-#define get_iface_desc(iface)  (&(iface)->desc)
-#define get_endpoint(alt,ep)   (&(alt)->endpoint[ep].desc)
-#define get_ep_desc(ep)                (&(ep)->desc)
-#define get_cfg_desc(cfg)      (&(cfg)->desc)
-#endif
-
-#ifndef snd_usb_get_speed
-#define snd_usb_get_speed(dev) ((dev)->speed)
-#endif
-
 #endif /* __USBAUDIO_H */
index 44deb21b17770b8d063793c979fdab72a5b0ecd1..5f7b942ff577eea90e141b0dc7e9cdde7182f23a 100644 (file)
@@ -25,6 +25,7 @@
 #define MODNAME "US122L"
 #include "usb_stream.c"
 #include "../usbaudio.h"
+#include "../midi.h"
 #include "us122l.h"
 
 MODULE_AUTHOR("Karsten Wiese <fzu@wemgehoertderstaat.de>");
index 1d174cea352b5b29f43cfdb3d584dc4310bf5d49..e43c0a86441ae600c2725356e1cbf3c27ecb81a3 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef USBUSX2Y_H
 #define USBUSX2Y_H
 #include "../usbaudio.h"
+#include "../midi.h"
 #include "usbus428ctldefs.h" 
 
 #define NRURBS         2