提交 f09d045e 编写于 作者: J Jaroslav Kysela

Merge branch 'topic/usb' of...

Merge branch 'topic/usb' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6 into devel
/*
* 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 */
......@@ -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
......@@ -20,14 +23,15 @@
#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
......@@ -38,15 +42,6 @@
#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
......@@ -78,10 +73,6 @@
#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
......
......@@ -22,8 +22,7 @@ config SND_USB_AUDIO
will be called snd-usb-audio.
config SND_USB_UA101
tristate "Edirol UA-101/UA-1000 driver (EXPERIMENTAL)"
depends on EXPERIMENTAL
tristate "Edirol UA-101/UA-1000 driver"
select SND_PCM
select SND_RAWMIDI
help
......@@ -65,6 +64,7 @@ config SND_USB_CAIAQ
* Native Instruments Audio 8 DJ
* Native Instruments Guitar Rig Session I/O
* Native Instruments Guitar Rig mobile
* Native Instruments Traktor Kontrol X1
To compile this driver as a module, choose M here: the module
will be called snd-usb-caiaq.
......
......@@ -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/
......@@ -35,33 +35,41 @@ static int control_info(struct snd_kcontrol *kcontrol,
struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
int is_intval = pos & CNT_INTVAL;
unsigned int id = dev->chip.usb_id;
int maxval = 63;
uinfo->count = 1;
pos &= ~CNT_INTVAL;
if (id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)
&& (pos == 0)) {
/* current input mode of A8DJ */
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 2;
return 0;
}
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
if (pos == 0) {
/* current input mode of A8DJ */
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 2;
return 0;
}
break;
if (id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)
&& (pos == 0)) {
/* current input mode of A4DJ */
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
if (pos == 0) {
/* current input mode of A4DJ */
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
maxval = 127;
break;
}
if (is_intval) {
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 64;
uinfo->value.integer.max = maxval;
} else {
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->value.integer.min = 0;
......@@ -102,9 +110,10 @@ static int control_put(struct snd_kcontrol *kcontrol,
struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
unsigned char cmd = EP1_CMD_WRITE_IO;
if (dev->chip.usb_id ==
USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)) {
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): {
/* A4DJ has only one control */
/* do not expose hardware input mode 0 */
dev->control_state[0] = ucontrol->value.integer.value[0] + 1;
......@@ -113,10 +122,15 @@ static int control_put(struct snd_kcontrol *kcontrol,
return 1;
}
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
cmd = EP1_CMD_DIMM_LEDS;
break;
}
if (pos & CNT_INTVAL) {
dev->control_state[pos & ~CNT_INTVAL]
= ucontrol->value.integer.value[0];
snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
snd_usb_caiaq_send_command(dev, cmd,
dev->control_state, sizeof(dev->control_state));
} else {
if (ucontrol->value.integer.value[0])
......@@ -124,7 +138,7 @@ static int control_put(struct snd_kcontrol *kcontrol,
else
dev->control_state[pos / 8] &= ~(1 << (pos % 8));
snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
snd_usb_caiaq_send_command(dev, cmd,
dev->control_state, sizeof(dev->control_state));
}
......@@ -273,6 +287,43 @@ static struct caiaq_controller a4dj_controller[] = {
{ "Current input mode", 0 | CNT_INTVAL }
};
static struct caiaq_controller kontrolx1_controller[] = {
{ "LED FX A: ON", 7 | CNT_INTVAL },
{ "LED FX A: 1", 6 | CNT_INTVAL },
{ "LED FX A: 2", 5 | CNT_INTVAL },
{ "LED FX A: 3", 4 | CNT_INTVAL },
{ "LED FX B: ON", 3 | CNT_INTVAL },
{ "LED FX B: 1", 2 | CNT_INTVAL },
{ "LED FX B: 2", 1 | CNT_INTVAL },
{ "LED FX B: 3", 0 | CNT_INTVAL },
{ "LED Hotcue", 28 | CNT_INTVAL },
{ "LED Shift (white)", 29 | CNT_INTVAL },
{ "LED Shift (green)", 30 | CNT_INTVAL },
{ "LED Deck A: FX1", 24 | CNT_INTVAL },
{ "LED Deck A: FX2", 25 | CNT_INTVAL },
{ "LED Deck A: IN", 17 | CNT_INTVAL },
{ "LED Deck A: OUT", 16 | CNT_INTVAL },
{ "LED Deck A: < BEAT", 19 | CNT_INTVAL },
{ "LED Deck A: BEAT >", 18 | CNT_INTVAL },
{ "LED Deck A: CUE/ABS", 21 | CNT_INTVAL },
{ "LED Deck A: CUP/REL", 20 | CNT_INTVAL },
{ "LED Deck A: PLAY", 23 | CNT_INTVAL },
{ "LED Deck A: SYNC", 22 | CNT_INTVAL },
{ "LED Deck B: FX1", 26 | CNT_INTVAL },
{ "LED Deck B: FX2", 27 | CNT_INTVAL },
{ "LED Deck B: IN", 15 | CNT_INTVAL },
{ "LED Deck B: OUT", 14 | CNT_INTVAL },
{ "LED Deck B: < BEAT", 13 | CNT_INTVAL },
{ "LED Deck B: BEAT >", 12 | CNT_INTVAL },
{ "LED Deck B: CUE/ABS", 11 | CNT_INTVAL },
{ "LED Deck B: CUP/REL", 10 | CNT_INTVAL },
{ "LED Deck B: PLAY", 9 | CNT_INTVAL },
{ "LED Deck B: SYNC", 8 | CNT_INTVAL },
};
static int __devinit add_controls(struct caiaq_controller *c, int num,
struct snd_usb_caiaqdev *dev)
{
......@@ -321,10 +372,16 @@ int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
ret = add_controls(a8dj_controller,
ARRAY_SIZE(a8dj_controller), dev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
ret = add_controls(a4dj_controller,
ARRAY_SIZE(a4dj_controller), dev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
ret = add_controls(kontrolx1_controller,
ARRAY_SIZE(kontrolx1_controller), dev);
break;
}
return ret;
......
......@@ -47,7 +47,8 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
"{Native Instruments, Audio 4 DJ},"
"{Native Instruments, Audio 8 DJ},"
"{Native Instruments, Session I/O},"
"{Native Instruments, GuitarRig mobile}");
"{Native Instruments, GuitarRig mobile}"
"{Native Instruments, Traktor Kontrol X1}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
......@@ -128,6 +129,11 @@ static struct usb_device_id snd_usb_id_table[] = {
.idVendor = USB_VID_NATIVEINSTRUMENTS,
.idProduct = USB_PID_AUDIO2DJ
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
.idVendor = USB_VID_NATIVEINSTRUMENTS,
.idProduct = USB_PID_TRAKTORKONTROLX1
},
{ /* terminator */ }
};
......
......@@ -5,18 +5,20 @@
#define USB_VID_NATIVEINSTRUMENTS 0x17cc
#define USB_PID_RIGKONTROL2 0x1969
#define USB_PID_RIGKONTROL3 0x1940
#define USB_PID_KORECONTROLLER 0x4711
#define USB_PID_KORECONTROLLER2 0x4712
#define USB_PID_AK1 0x0815
#define USB_PID_AUDIO2DJ 0x041c
#define USB_PID_AUDIO4DJ 0x0839
#define USB_PID_AUDIO8DJ 0x1978
#define USB_PID_SESSIONIO 0x1915
#define USB_PID_GUITARRIGMOBILE 0x0d8d
#define USB_PID_RIGKONTROL2 0x1969
#define USB_PID_RIGKONTROL3 0x1940
#define USB_PID_KORECONTROLLER 0x4711
#define USB_PID_KORECONTROLLER2 0x4712
#define USB_PID_AK1 0x0815
#define USB_PID_AUDIO2DJ 0x041c
#define USB_PID_AUDIO4DJ 0x0839
#define USB_PID_AUDIO8DJ 0x1978
#define USB_PID_SESSIONIO 0x1915
#define USB_PID_GUITARRIGMOBILE 0x0d8d
#define USB_PID_TRAKTORKONTROLX1 0x2305
#define EP1_BUFSIZE 64
#define EP4_BUFSIZE 512
#define CAIAQ_USB_STR_LEN 0xff
#define MAX_STREAMS 32
......@@ -104,6 +106,8 @@ struct snd_usb_caiaqdev {
struct input_dev *input_dev;
char phys[64]; /* physical device path */
unsigned short keycode[64];
struct urb *ep4_in_urb;
unsigned char ep4_in_buf[EP4_BUFSIZE];
#endif
/* ALSA */
......
......@@ -16,9 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include "device.h"
......@@ -65,6 +67,8 @@ static unsigned short keycode_kore[] = {
KEY_BRL_DOT5
};
#define KONTROLX1_INPUTS 40
#define DEG90 (range / 2)
#define DEG180 (range)
#define DEG270 (DEG90 + DEG180)
......@@ -162,6 +166,17 @@ static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev,
input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
input_sync(input_dev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
input_report_abs(input_dev, ABS_HAT0X, (buf[8] << 8) | buf[9]);
input_report_abs(input_dev, ABS_HAT0Y, (buf[4] << 8) | buf[5]);
input_report_abs(input_dev, ABS_HAT1X, (buf[12] << 8) | buf[13]);
input_report_abs(input_dev, ABS_HAT1Y, (buf[2] << 8) | buf[3]);
input_report_abs(input_dev, ABS_HAT2X, (buf[15] << 8) | buf[15]);
input_report_abs(input_dev, ABS_HAT2Y, (buf[0] << 8) | buf[1]);
input_report_abs(input_dev, ABS_HAT3X, (buf[10] << 8) | buf[11]);
input_report_abs(input_dev, ABS_HAT3Y, (buf[6] << 8) | buf[7]);
input_sync(input_dev);
break;
}
}
......@@ -201,7 +216,7 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
}
static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
char *buf, unsigned int len)
unsigned char *buf, unsigned int len)
{
struct input_dev *input_dev = dev->input_dev;
unsigned short *keycode = input_dev->keycode;
......@@ -218,15 +233,84 @@ static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
input_report_key(input_dev, keycode[i],
buf[i / 8] & (1 << (i % 8)));
if (dev->chip.usb_id ==
USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER) ||
dev->chip.usb_id ==
USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2))
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
/* rotary encoders */
input_report_abs(dev->input_dev, ABS_X, buf[5] & 0xf);
input_report_abs(dev->input_dev, ABS_Y, buf[5] >> 4);
input_report_abs(dev->input_dev, ABS_Z, buf[6] & 0xf);
input_report_abs(dev->input_dev, ABS_MISC, buf[6] >> 4);
break;
}
input_sync(input_dev);
}
static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
{
struct snd_usb_caiaqdev *dev = urb->context;
unsigned char *buf = urb->transfer_buffer;
int ret;
if (urb->status || !dev || urb != dev->ep4_in_urb)
return;
if (urb->actual_length < 24)
goto requeue;
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
if (buf[0] & 0x3)
snd_caiaq_input_read_io(dev, buf + 1, 7);
if (buf[0] & 0x4)
snd_caiaq_input_read_analog(dev, buf + 8, 16);
break;
}
requeue:
dev->ep4_in_urb->actual_length = 0;
ret = usb_submit_urb(dev->ep4_in_urb, GFP_ATOMIC);
if (ret < 0)
log("unable to submit urb. OOM!?\n");
}
static int snd_usb_caiaq_input_open(struct input_dev *idev)
{
struct snd_usb_caiaqdev *dev = input_get_drvdata(idev);
if (!dev)
return -EINVAL;
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0)
return -EIO;
break;
}
return 0;
}
static void snd_usb_caiaq_input_close(struct input_dev *idev)
{
struct snd_usb_caiaqdev *dev = input_get_drvdata(idev);
if (!dev)
return;
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
usb_kill_urb(dev->ep4_in_urb);
break;
}
}
void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev,
char *buf,
unsigned int len)
......@@ -251,7 +335,7 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
{
struct usb_device *usb_dev = dev->chip.dev;
struct input_dev *input;
int i, ret;
int i, ret = 0;
input = input_allocate_device();
if (!input)
......@@ -265,7 +349,9 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
usb_to_input_id(usb_dev, &input->id);
input->dev.parent = &usb_dev->dev;
switch (dev->chip.usb_id) {
input_set_drvdata(input, dev);
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
......@@ -325,26 +411,73 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1);
snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->absbit[0] = BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) |
BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y) |
BIT_MASK(ABS_HAT2X) | BIT_MASK(ABS_HAT2Y) |
BIT_MASK(ABS_HAT3X) | BIT_MASK(ABS_HAT3Y) |
BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_Z);
input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
BUILD_BUG_ON(sizeof(dev->keycode) < KONTROLX1_INPUTS);
for (i = 0; i < KONTROLX1_INPUTS; i++)
dev->keycode[i] = BTN_MISC + i;
input->keycodemax = KONTROLX1_INPUTS;
/* analog potentiometers */
input_set_abs_params(input, ABS_HAT0X, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_HAT0Y, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_HAT1X, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_HAT1Y, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_HAT2X, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_HAT2Y, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_HAT3X, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_HAT3Y, 0, 4096, 0, 10);
/* rotary encoders */
input_set_abs_params(input, ABS_X, 0, 0xf, 0, 1);
input_set_abs_params(input, ABS_Y, 0, 0xf, 0, 1);
input_set_abs_params(input, ABS_Z, 0, 0xf, 0, 1);
input_set_abs_params(input, ABS_MISC, 0, 0xf, 0, 1);
dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->ep4_in_urb) {
ret = -ENOMEM;
goto exit_free_idev;
}
usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev,
usb_rcvbulkpipe(usb_dev, 0x4),
dev->ep4_in_buf, EP4_BUFSIZE,
snd_usb_caiaq_ep4_reply_dispatch, dev);
snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
break;
default:
/* no input methods supported on this device */
input_free_device(input);
return 0;
goto exit_free_idev;
}
input->open = snd_usb_caiaq_input_open;
input->close = snd_usb_caiaq_input_close;
input->keycode = dev->keycode;
input->keycodesize = sizeof(unsigned short);
for (i = 0; i < input->keycodemax; i++)
__set_bit(dev->keycode[i], input->keybit);
ret = input_register_device(input);
if (ret < 0) {
input_free_device(input);
return ret;
}
if (ret < 0)
goto exit_free_idev;
dev->input_dev = input;
return 0;
exit_free_idev:
input_free_device(input);
return ret;
}
void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev)
......@@ -352,6 +485,10 @@ void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev)
if (!dev || !dev->input_dev)
return;
usb_kill_urb(dev->ep4_in_urb);
usb_free_urb(dev->ep4_in_urb);
dev->ep4_in_urb = NULL;
input_unregister_device(dev->input_dev);
dev->input_dev = NULL;
}
......
/*
* (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;
}
#else
#define usb_audio_suspend NULL
#define usb_audio_resume NULL
#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);
#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 */
#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 */
/*
* 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/slab.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 */
case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */
case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
/* 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;
}
#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 */
/*
* 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/slab.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;
}
#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 */
/*
* 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/slab.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;
}
#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 */
......@@ -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
......@@ -986,6 +987,8 @@ static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream)
DEFINE_WAIT(wait);
long timeout = msecs_to_jiffies(50);
if (ep->umidi->disconnected)
return;
/*
* The substream buffer is empty, but some data might still be in the
* currently active URBs, so we have to wait for those to complete.
......@@ -1123,14 +1126,21 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,
* Frees an output endpoint.
* May be called when ep hasn't been initialized completely.
*/
static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint* ep)
static void snd_usbmidi_out_endpoint_clear(struct snd_usb_midi_out_endpoint *ep)
{
unsigned int i;
for (i = 0; i < OUTPUT_URBS; ++i)
if (ep->urbs[i].urb)
if (ep->urbs[i].urb) {
free_urb_and_buffer(ep->umidi, ep->urbs[i].urb,
ep->max_transfer);
ep->urbs[i].urb = NULL;
}
}
static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint *ep)
{
snd_usbmidi_out_endpoint_clear(ep);
kfree(ep);
}
......@@ -1262,15 +1272,18 @@ void snd_usbmidi_disconnect(struct list_head* p)
usb_kill_urb(ep->out->urbs[j].urb);
if (umidi->usb_protocol_ops->finish_out_endpoint)
umidi->usb_protocol_ops->finish_out_endpoint(ep->out);
ep->out->active_urbs = 0;
if (ep->out->drain_urbs) {
ep->out->drain_urbs = 0;
wake_up(&ep->out->drain_wait);
}
}
if (ep->in)
for (j = 0; j < INPUT_URBS; ++j)
usb_kill_urb(ep->in->urbs[j]);
/* free endpoints here; later call can result in Oops */
if (ep->out) {
snd_usbmidi_out_endpoint_delete(ep->out);
ep->out = NULL;
}
if (ep->out)
snd_usbmidi_out_endpoint_clear(ep->out);
if (ep->in) {
snd_usbmidi_in_endpoint_delete(ep->in);
ep->in = NULL;
......
#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 */
snd-ua101-objs := ua101.o
obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o
......@@ -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>");
......
#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 */
......@@ -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 */
......
/*
* 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/slab.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;
}
}
#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 */
此差异已折叠。
#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 */
/*
* 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);
}
#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 */
此差异已折叠。
#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 */
此差异已折叠。
#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 */
此差异已折叠。
此差异已折叠。
......@@ -26,6 +26,7 @@
#define MODNAME "US122L"
#include "usb_stream.c"
#include "../usbaudio.h"
#include "../midi.h"
#include "us122l.h"
MODULE_AUTHOR("Karsten Wiese <fzu@wemgehoertderstaat.de>");
......
#ifndef USBUSX2Y_H
#define USBUSX2Y_H
#include "../usbaudio.h"
#include "../midi.h"
#include "usbus428ctldefs.h"
#define NRURBS 2
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册