1
0
Fork 0

Merge branch 'topic/misc' into for-linus

hifive-unleashed-5.1
Takashi Iwai 2010-05-20 11:59:29 +02:00
commit 5e8aa85253
75 changed files with 24893 additions and 5152 deletions

View File

@ -227,6 +227,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
The power-management is supported.
Module snd-asihpi
-----------------
Module for AudioScience ASI soundcards
enable_hpi_hwdep - enable HPI hwdep for AudioScience soundcard
This module supports multiple cards.
The driver requires the firmware loader support on kernel.
Module snd-atiixp
-----------------
@ -622,28 +632,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
The power-management is supported.
Module snd-es968
----------------
Module for sound cards based on ESS ES968 chip (PnP only).
This module supports multiple cards, PnP and autoprobe.
The power-management is supported.
Module snd-es1688
-----------------
Module for ESS AudioDrive ES-1688 and ES-688 sound cards.
port - port # for ES-1688 chip (0x220,0x240,0x260)
fm_port - port # for OPL3 (option; share the same port as default)
isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default)
irq - IRQ # for ES-1688 chip (5,7,9,10)
mpu_irq - IRQ # for MPU-401 port (5,7,9,10)
fm_port - port # for OPL3 (option; share the same port as default)
with isapnp=0, the following additional options are available:
port - port # for ES-1688 chip (0x220,0x240,0x260)
irq - IRQ # for ES-1688 chip (5,7,9,10)
dma8 - DMA # for ES-1688 chip (0,1,3)
This module supports multiple cards and autoprobe (without MPU-401 port).
This module supports multiple cards and autoprobe (without MPU-401 port)
and PnP with the ES968 chip.
Module snd-es18xx
-----------------

View File

@ -0,0 +1,366 @@
/*
* Copyright (c) 2010 Daniel Mack <daniel@caiaq.de>
*
* This software is distributed under the terms of the GNU General Public
* License ("GPL") version 2, as published by the Free Software Foundation.
*
* This file holds USB constants and structures defined
* by the USB Device Class Definition for Audio Devices in version 2.0.
* Comments below reference relevant sections of the documents contained
* in http://www.usb.org/developers/devclass_docs/Audio2.0_final.zip
*/
#ifndef __LINUX_USB_AUDIO_V2_H
#define __LINUX_USB_AUDIO_V2_H
#include <linux/types.h>
/* v1.0 and v2.0 of this standard have many things in common. For the rest
* of the definitions, please refer to audio.h */
/* 4.7.2.1 Clock Source Descriptor */
struct uac_clock_source_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bClockID;
__u8 bmAttributes;
__u8 bmControls;
__u8 bAssocTerminal;
__u8 iClockSource;
} __attribute__((packed));
/* 4.7.2.2 Clock Source Descriptor */
struct uac_clock_selector_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bClockID;
__u8 bNrInPins;
__u8 bmControls;
__u8 baCSourceID[];
} __attribute__((packed));
/* 4.7.2.4 Input terminal descriptor */
struct uac2_input_terminal_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bTerminalID;
__u16 wTerminalType;
__u8 bAssocTerminal;
__u8 bCSourceID;
__u8 bNrChannels;
__u32 bmChannelConfig;
__u8 iChannelNames;
__u16 bmControls;
__u8 iTerminal;
} __attribute__((packed));
/* 4.7.2.5 Output terminal descriptor */
struct uac2_output_terminal_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bTerminalID;
__u16 wTerminalType;
__u8 bAssocTerminal;
__u8 bSourceID;
__u8 bCSourceID;
__u16 bmControls;
__u8 iTerminal;
} __attribute__((packed));
/* 4.7.2.8 Feature Unit Descriptor */
struct uac2_feature_unit_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bUnitID;
__u8 bSourceID;
/* bmaControls is actually u32,
* but u8 is needed for the hybrid parser */
__u8 bmaControls[0]; /* variable length */
} __attribute__((packed));
/* 4.9.2 Class-Specific AS Interface Descriptor */
struct uac_as_header_descriptor_v2 {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u8 bTerminalLink;
__u8 bmControls;
__u8 bFormatType;
__u32 bmFormats;
__u8 bNrChannels;
__u32 bmChannelConfig;
__u8 iChannelNames;
} __attribute__((packed));
/* A.7 Audio Function Category Codes */
#define UAC2_FUNCTION_SUBCLASS_UNDEFINED 0x00
#define UAC2_FUNCTION_DESKTOP_SPEAKER 0x01
#define UAC2_FUNCTION_HOME_THEATER 0x02
#define UAC2_FUNCTION_MICROPHONE 0x03
#define UAC2_FUNCTION_HEADSET 0x04
#define UAC2_FUNCTION_TELEPHONE 0x05
#define UAC2_FUNCTION_CONVERTER 0x06
#define UAC2_FUNCTION_SOUND_RECORDER 0x07
#define UAC2_FUNCTION_IO_BOX 0x08
#define UAC2_FUNCTION_MUSICAL_INSTRUMENT 0x09
#define UAC2_FUNCTION_PRO_AUDIO 0x0a
#define UAC2_FUNCTION_AUDIO_VIDEO 0x0b
#define UAC2_FUNCTION_CONTROL_PANEL 0x0c
#define UAC2_FUNCTION_OTHER 0xff
/* A.9 Audio Class-Specific AC Interface Descriptor Subtypes */
/* see audio.h for the rest, which is identical to v1 */
#define UAC2_EFFECT_UNIT 0x07
#define UAC2_PROCESSING_UNIT_V2 0x08
#define UAC2_EXTENSION_UNIT_V2 0x09
#define UAC2_CLOCK_SOURCE 0x0a
#define UAC2_CLOCK_SELECTOR 0x0b
#define UAC2_CLOCK_MULTIPLIER 0x0c
#define UAC2_SAMPLE_RATE_CONVERTER 0x0d
/* A.10 Audio Class-Specific AS Interface Descriptor Subtypes */
/* see audio.h for the rest, which is identical to v1 */
#define UAC2_ENCODER 0x03
#define UAC2_DECODER 0x04
/* A.11 Effect Unit Effect Types */
#define UAC2_EFFECT_UNDEFINED 0x00
#define UAC2_EFFECT_PARAM_EQ 0x01
#define UAC2_EFFECT_REVERB 0x02
#define UAC2_EFFECT_MOD_DELAY 0x03
#define UAC2_EFFECT_DYN_RANGE_COMP 0x04
/* A.12 Processing Unit Process Types */
#define UAC2_PROCESS_UNDEFINED 0x00
#define UAC2_PROCESS_UP_DOWNMIX 0x01
#define UAC2_PROCESS_DOLBY_PROLOCIC 0x02
#define UAC2_PROCESS_STEREO_EXTENDER 0x03
/* A.14 Audio Class-Specific Request Codes */
#define UAC2_CS_CUR 0x01
#define UAC2_CS_RANGE 0x02
/* A.15 Encoder Type Codes */
#define UAC2_ENCODER_UNDEFINED 0x00
#define UAC2_ENCODER_OTHER 0x01
#define UAC2_ENCODER_MPEG 0x02
#define UAC2_ENCODER_AC3 0x03
#define UAC2_ENCODER_WMA 0x04
#define UAC2_ENCODER_DTS 0x05
/* A.16 Decoder Type Codes */
#define UAC2_DECODER_UNDEFINED 0x00
#define UAC2_DECODER_OTHER 0x01
#define UAC2_DECODER_MPEG 0x02
#define UAC2_DECODER_AC3 0x03
#define UAC2_DECODER_WMA 0x04
#define UAC2_DECODER_DTS 0x05
/* A.17.1 Clock Source Control Selectors */
#define UAC2_CS_UNDEFINED 0x00
#define UAC2_CS_CONTROL_SAM_FREQ 0x01
#define UAC2_CS_CONTROL_CLOCK_VALID 0x02
/* A.17.2 Clock Selector Control Selectors */
#define UAC2_CX_UNDEFINED 0x00
#define UAC2_CX_CLOCK_SELECTOR 0x01
/* A.17.3 Clock Multiplier Control Selectors */
#define UAC2_CM_UNDEFINED 0x00
#define UAC2_CM_NUMERATOR 0x01
#define UAC2_CM_DENOMINTATOR 0x02
/* A.17.4 Terminal Control Selectors */
#define UAC2_TE_UNDEFINED 0x00
#define UAC2_TE_COPY_PROTECT 0x01
#define UAC2_TE_CONNECTOR 0x02
#define UAC2_TE_OVERLOAD 0x03
#define UAC2_TE_CLUSTER 0x04
#define UAC2_TE_UNDERFLOW 0x05
#define UAC2_TE_OVERFLOW 0x06
#define UAC2_TE_LATENCY 0x07
/* A.17.5 Mixer Control Selectors */
#define UAC2_MU_UNDEFINED 0x00
#define UAC2_MU_MIXER 0x01
#define UAC2_MU_CLUSTER 0x02
#define UAC2_MU_UNDERFLOW 0x03
#define UAC2_MU_OVERFLOW 0x04
#define UAC2_MU_LATENCY 0x05
/* A.17.6 Selector Control Selectors */
#define UAC2_SU_UNDEFINED 0x00
#define UAC2_SU_SELECTOR 0x01
#define UAC2_SU_LATENCY 0x02
/* A.17.7 Feature Unit Control Selectors */
/* see audio.h for the rest, which is identical to v1 */
#define UAC2_FU_INPUT_GAIN 0x0b
#define UAC2_FU_INPUT_GAIN_PAD 0x0c
#define UAC2_FU_PHASE_INVERTER 0x0d
#define UAC2_FU_UNDERFLOW 0x0e
#define UAC2_FU_OVERFLOW 0x0f
#define UAC2_FU_LATENCY 0x10
/* A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors */
#define UAC2_PE_UNDEFINED 0x00
#define UAC2_PE_ENABLE 0x01
#define UAC2_PE_CENTERFREQ 0x02
#define UAC2_PE_QFACTOR 0x03
#define UAC2_PE_GAIN 0x04
#define UAC2_PE_UNDERFLOW 0x05
#define UAC2_PE_OVERFLOW 0x06
#define UAC2_PE_LATENCY 0x07
/* A.17.8.2 Reverberation Effect Unit Control Selectors */
#define UAC2_RV_UNDEFINED 0x00
#define UAC2_RV_ENABLE 0x01
#define UAC2_RV_TYPE 0x02
#define UAC2_RV_LEVEL 0x03
#define UAC2_RV_TIME 0x04
#define UAC2_RV_FEEDBACK 0x05
#define UAC2_RV_PREDELAY 0x06
#define UAC2_RV_DENSITY 0x07
#define UAC2_RV_HIFREQ_ROLLOFF 0x08
#define UAC2_RV_UNDERFLOW 0x09
#define UAC2_RV_OVERFLOW 0x0a
#define UAC2_RV_LATENCY 0x0b
/* A.17.8.3 Modulation Delay Effect Control Selectors */
#define UAC2_MD_UNDEFINED 0x00
#define UAC2_MD_ENABLE 0x01
#define UAC2_MD_BALANCE 0x02
#define UAC2_MD_RATE 0x03
#define UAC2_MD_DEPTH 0x04
#define UAC2_MD_TIME 0x05
#define UAC2_MD_FEEDBACK 0x06
#define UAC2_MD_UNDERFLOW 0x07
#define UAC2_MD_OVERFLOW 0x08
#define UAC2_MD_LATENCY 0x09
/* A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors */
#define UAC2_DR_UNDEFINED 0x00
#define UAC2_DR_ENABLE 0x01
#define UAC2_DR_COMPRESSION_RATE 0x02
#define UAC2_DR_MAXAMPL 0x03
#define UAC2_DR_THRESHOLD 0x04
#define UAC2_DR_ATTACK_TIME 0x05
#define UAC2_DR_RELEASE_TIME 0x06
#define UAC2_DR_UNDEFLOW 0x07
#define UAC2_DR_OVERFLOW 0x08
#define UAC2_DR_LATENCY 0x09
/* A.17.9.1 Up/Down-mix Processing Unit Control Selectors */
#define UAC2_UD_UNDEFINED 0x00
#define UAC2_UD_ENABLE 0x01
#define UAC2_UD_MODE_SELECT 0x02
#define UAC2_UD_CLUSTER 0x03
#define UAC2_UD_UNDERFLOW 0x04
#define UAC2_UD_OVERFLOW 0x05
#define UAC2_UD_LATENCY 0x06
/* A.17.9.2 Dolby Prologic[tm] Processing Unit Control Selectors */
#define UAC2_DP_UNDEFINED 0x00
#define UAC2_DP_ENABLE 0x01
#define UAC2_DP_MODE_SELECT 0x02
#define UAC2_DP_CLUSTER 0x03
#define UAC2_DP_UNDERFFLOW 0x04
#define UAC2_DP_OVERFLOW 0x05
#define UAC2_DP_LATENCY 0x06
/* A.17.9.3 Stereo Expander Processing Unit Control Selectors */
#define UAC2_ST_EXT_UNDEFINED 0x00
#define UAC2_ST_EXT_ENABLE 0x01
#define UAC2_ST_EXT_WIDTH 0x02
#define UAC2_ST_EXT_UNDEFLOW 0x03
#define UAC2_ST_EXT_OVERFLOW 0x04
#define UAC2_ST_EXT_LATENCY 0x05
/* A.17.10 Extension Unit Control Selectors */
#define UAC2_XU_UNDEFINED 0x00
#define UAC2_XU_ENABLE 0x01
#define UAC2_XU_CLUSTER 0x02
#define UAC2_XU_UNDERFLOW 0x03
#define UAC2_XU_OVERFLOW 0x04
#define UAC2_XU_LATENCY 0x05
/* A.17.11 AudioStreaming Interface Control Selectors */
#define UAC2_AS_UNDEFINED 0x00
#define UAC2_AS_ACT_ALT_SETTING 0x01
#define UAC2_AS_VAL_ALT_SETTINGS 0x02
#define UAC2_AS_AUDIO_DATA_FORMAT 0x03
/* A.17.12 Encoder Control Selectors */
#define UAC2_EN_UNDEFINED 0x00
#define UAC2_EN_BIT_RATE 0x01
#define UAC2_EN_QUALITY 0x02
#define UAC2_EN_VBR 0x03
#define UAC2_EN_TYPE 0x04
#define UAC2_EN_UNDERFLOW 0x05
#define UAC2_EN_OVERFLOW 0x06
#define UAC2_EN_ENCODER_ERROR 0x07
#define UAC2_EN_PARAM1 0x08
#define UAC2_EN_PARAM2 0x09
#define UAC2_EN_PARAM3 0x0a
#define UAC2_EN_PARAM4 0x0b
#define UAC2_EN_PARAM5 0x0c
#define UAC2_EN_PARAM6 0x0d
#define UAC2_EN_PARAM7 0x0e
#define UAC2_EN_PARAM8 0x0f
/* A.17.13.1 MPEG Decoder Control Selectors */
#define UAC2_MPEG_UNDEFINED 0x00
#define UAC2_MPEG_DUAL_CHANNEL 0x01
#define UAC2_MPEG_SECOND_STEREO 0x02
#define UAC2_MPEG_MULTILINGUAL 0x03
#define UAC2_MPEG_DYN_RANGE 0x04
#define UAC2_MPEG_SCALING 0x05
#define UAC2_MPEG_HILO_SCALING 0x06
#define UAC2_MPEG_UNDERFLOW 0x07
#define UAC2_MPEG_OVERFLOW 0x08
#define UAC2_MPEG_DECODER_ERROR 0x09
/* A17.13.2 AC3 Decoder Control Selectors */
#define UAC2_AC3_UNDEFINED 0x00
#define UAC2_AC3_MODE 0x01
#define UAC2_AC3_DYN_RANGE 0x02
#define UAC2_AC3_SCALING 0x03
#define UAC2_AC3_HILO_SCALING 0x04
#define UAC2_AC3_UNDERFLOW 0x05
#define UAC2_AC3_OVERFLOW 0x06
#define UAC2_AC3_DECODER_ERROR 0x07
/* A17.13.3 WMA Decoder Control Selectors */
#define UAC2_WMA_UNDEFINED 0x00
#define UAC2_WMA_UNDERFLOW 0x01
#define UAC2_WMA_OVERFLOW 0x02
#define UAC2_WMA_DECODER_ERROR 0x03
/* A17.13.4 DTS Decoder Control Selectors */
#define UAC2_DTS_UNDEFINED 0x00
#define UAC2_DTS_UNDERFLOW 0x01
#define UAC2_DTS_OVERFLOW 0x02
#define UAC2_DTS_DECODER_ERROR 0x03
/* A17.14 Endpoint Control Selectors */
#define UAC2_EP_CS_UNDEFINED 0x00
#define UAC2_EP_CS_PITCH 0x01
#define UAC2_EP_CS_DATA_OVERRUN 0x02
#define UAC2_EP_CS_DATA_UNDERRUN 0x03
#endif /* __LINUX_USB_AUDIO_V2_H */

View File

@ -13,6 +13,9 @@
* Comments below reference relevant sections of that document:
*
* http://www.usb.org/developers/devclass_docs/audio10.pdf
*
* Types and defines in this file are either specific to version 1.0 of
* this standard or common for newer versions.
*/
#ifndef __LINUX_USB_AUDIO_H
@ -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

View File

@ -574,7 +574,7 @@ enum {
#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */
struct snd_timer_id {
int dev_class;
int dev_class;
int dev_sclass;
int card;
int device;
@ -762,7 +762,7 @@ struct snd_ctl_elem_id {
snd_ctl_elem_iface_t iface; /* interface identifier */
unsigned int device; /* device/client number */
unsigned int subdevice; /* subdevice (substream) number */
unsigned char name[44]; /* ASCII name of item */
unsigned char name[44]; /* ASCII name of item */
unsigned int index; /* index of item */
};
@ -809,7 +809,7 @@ struct snd_ctl_elem_info {
struct snd_ctl_elem_value {
struct snd_ctl_elem_id id; /* W: element ID */
unsigned int indirect: 1; /* W: indirect access - obsoleted */
union {
union {
union {
long value[128];
long *value_ptr; /* obsoleted */
@ -827,15 +827,15 @@ struct snd_ctl_elem_value {
unsigned char *data_ptr; /* obsoleted */
} bytes;
struct snd_aes_iec958 iec958;
} value; /* RO */
} value; /* RO */
struct timespec tstamp;
unsigned char reserved[128-sizeof(struct timespec)];
unsigned char reserved[128-sizeof(struct timespec)];
};
struct snd_ctl_tlv {
unsigned int numid; /* control element numeric identification */
unsigned int length; /* in bytes aligned to 4 */
unsigned int tlv[0]; /* first TLV */
unsigned int numid; /* control element numeric identification */
unsigned int length; /* in bytes aligned to 4 */
unsigned int tlv[0]; /* first TLV */
};
#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int)
@ -886,8 +886,8 @@ struct snd_ctl_event {
unsigned int mask;
struct snd_ctl_elem_id id;
} elem;
unsigned char data8[60];
} data;
unsigned char data8[60];
} data;
};
/*

View File

@ -44,7 +44,6 @@ struct snd_es1688 {
unsigned char pad;
unsigned int dma_size;
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_pcm_substream *playback_substream;
struct snd_pcm_substream *capture_substream;
@ -108,14 +107,16 @@ struct snd_es1688 {
void snd_es1688_mixer_write(struct snd_es1688 *chip, unsigned char reg, unsigned char data);
int snd_es1688_create(struct snd_card *card,
struct snd_es1688 *chip,
unsigned long port,
unsigned long mpu_port,
int irq,
int mpu_irq,
int dma8,
unsigned short hardware,
struct snd_es1688 ** rchip);
int snd_es1688_pcm(struct snd_es1688 *chip, int device, struct snd_pcm ** rpcm);
int snd_es1688_mixer(struct snd_es1688 *chip);
unsigned short hardware);
int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device,
struct snd_pcm **rpcm);
int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip);
int snd_es1688_reset(struct snd_es1688 *chip);
#endif /* __SOUND_ES1688_H */

View File

@ -1,3 +1,3 @@
/* include/version.h */
#define CONFIG_SND_VERSION "1.0.22.1"
#define CONFIG_SND_VERSION "1.0.23"
#define CONFIG_SND_DATE ""

View File

@ -98,7 +98,8 @@ int snd_i2c_bus_create(struct snd_card *card, const char *name,
bus->master = master;
}
strlcpy(bus->name, name, sizeof(bus->name));
if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &ops)) < 0) {
err = snd_device_new(card, SNDRV_DEV_BUS, bus, &ops);
if (err < 0) {
snd_i2c_bus_free(bus);
return err;
}
@ -246,7 +247,8 @@ static int snd_i2c_bit_sendbyte(struct snd_i2c_bus *bus, unsigned char data)
for (i = 7; i >= 0; i--)
snd_i2c_bit_send(bus, !!(data & (1 << i)));
if ((err = snd_i2c_bit_ack(bus)) < 0)
err = snd_i2c_bit_ack(bus);
if (err < 0)
return err;
return 0;
}
@ -278,12 +280,14 @@ static int snd_i2c_bit_sendbytes(struct snd_i2c_device *device,
if (device->flags & SND_I2C_DEVICE_ADDRTEN)
return -EIO; /* not yet implemented */
snd_i2c_bit_start(bus);
if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0) {
err = snd_i2c_bit_sendbyte(bus, device->addr << 1);
if (err < 0) {
snd_i2c_bit_hw_stop(bus);
return err;
}
while (count-- > 0) {
if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0) {
err = snd_i2c_bit_sendbyte(bus, *bytes++);
if (err < 0) {
snd_i2c_bit_hw_stop(bus);
return err;
}
@ -302,12 +306,14 @@ static int snd_i2c_bit_readbytes(struct snd_i2c_device *device,
if (device->flags & SND_I2C_DEVICE_ADDRTEN)
return -EIO; /* not yet implemented */
snd_i2c_bit_start(bus);
if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0) {
err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1);
if (err < 0) {
snd_i2c_bit_hw_stop(bus);
return err;
}
while (count-- > 0) {
if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0) {
err = snd_i2c_bit_readbyte(bus, count == 0);
if (err < 0) {
snd_i2c_bit_hw_stop(bus);
return err;
}

View File

@ -128,26 +128,14 @@ config SND_CS4236
To compile this driver as a module, choose M here: the module
will be called snd-cs4236.
config SND_ES968
tristate "Generic ESS ES968 driver"
depends on PNP
select ISAPNP
select SND_MPU401_UART
select SND_SB8_DSP
help
Say Y here to include support for ESS AudioDrive ES968 chips.
To compile this driver as a module, choose M here: the module
will be called snd-es968.
config SND_ES1688
tristate "Generic ESS ES688/ES1688 driver"
tristate "Generic ESS ES688/ES1688 and ES968 PnP driver"
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
help
Say Y here to include support for ESS AudioDrive ES688 or
ES1688 chips.
ES1688 chips. Also, this module support cards with ES968 PnP chip.
To compile this driver as a module, choose M here: the module
will be called snd-es1688.

View File

@ -22,6 +22,7 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/isapnp.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/moduleparam.h>
@ -45,8 +46,13 @@ MODULE_SUPPORTED_DEVICE("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100},"
"{ESS,ES688 AudioDrive,pnp:ESS6881},"
"{ESS,ES1688 AudioDrive,pnp:ESS1681}}");
MODULE_ALIAS("snd_es968");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
#ifdef CONFIG_PNP
static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
#endif
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* Usually 0x388 */
@ -60,6 +66,10 @@ MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
#ifdef CONFIG_PNP
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
module_param_array(port, long, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
@ -74,14 +84,21 @@ MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
module_param_array(dma8, int, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
#ifdef CONFIG_PNP
#define is_isapnp_selected(dev) isapnp[dev]
#else
#define is_isapnp_selected(dev) 0
#endif
static int __devinit snd_es1688_match(struct device *dev, unsigned int n)
{
return enable[n];
return enable[n] && !is_isapnp_selected(n);
}
static int __devinit snd_es1688_legacy_create(struct snd_card *card,
struct device *dev, unsigned int n, struct snd_es1688 **rchip)
static int __devinit snd_es1688_legacy_create(struct snd_card *card,
struct device *dev, unsigned int n)
{
struct snd_es1688 *chip = card->private_data;
static long possible_ports[] = {0x220, 0x240, 0x260};
static int possible_irqs[] = {5, 9, 10, 7, -1};
static int possible_dmas[] = {1, 3, 0, -1};
@ -104,47 +121,39 @@ static int __devinit snd_es1688_legacy_create(struct snd_card *card,
}
if (port[n] != SNDRV_AUTO_PORT)
return snd_es1688_create(card, port[n], mpu_port[n], irq[n],
mpu_irq[n], dma8[n], ES1688_HW_AUTO, rchip);
return snd_es1688_create(card, chip, port[n], mpu_port[n],
irq[n], mpu_irq[n], dma8[n], ES1688_HW_AUTO);
i = 0;
do {
port[n] = possible_ports[i];
error = snd_es1688_create(card, port[n], mpu_port[n], irq[n],
mpu_irq[n], dma8[n], ES1688_HW_AUTO, rchip);
error = snd_es1688_create(card, chip, port[n], mpu_port[n],
irq[n], mpu_irq[n], dma8[n], ES1688_HW_AUTO);
} while (error < 0 && ++i < ARRAY_SIZE(possible_ports));
return error;
}
static int __devinit snd_es1688_probe(struct device *dev, unsigned int n)
static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
{
struct snd_card *card;
struct snd_es1688 *chip;
struct snd_es1688 *chip = card->private_data;
struct snd_opl3 *opl3;
struct snd_pcm *pcm;
int error;
error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
error = snd_es1688_pcm(card, chip, 0, &pcm);
if (error < 0)
return error;
error = snd_es1688_legacy_create(card, dev, n, &chip);
error = snd_es1688_mixer(card, chip);
if (error < 0)
goto out;
return error;
error = snd_es1688_pcm(chip, 0, &pcm);
if (error < 0)
goto out;
error = snd_es1688_mixer(chip);
if (error < 0)
goto out;
strcpy(card->driver, "ES1688");
strcpy(card->shortname, pcm->name);
sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name,
chip->port, chip->irq, chip->dma8);
strlcpy(card->driver, "ES1688", sizeof(card->driver));
strlcpy(card->shortname, pcm->name, sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port,
chip->irq, chip->dma8);
if (fm_port[n] == SNDRV_AUTO_PORT)
fm_port[n] = port[n]; /* share the same port */
@ -152,12 +161,12 @@ static int __devinit snd_es1688_probe(struct device *dev, unsigned int n)
if (fm_port[n] > 0) {
if (snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
OPL3_HW_OPL3, 0, &opl3) < 0)
dev_warn(dev,
dev_warn(card->dev,
"opl3 not detected at 0x%lx\n", fm_port[n]);
else {
error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (error < 0)
goto out;
return error;
}
}
@ -167,23 +176,41 @@ static int __devinit snd_es1688_probe(struct device *dev, unsigned int n)
chip->mpu_port, 0,
mpu_irq[n], IRQF_DISABLED, NULL);
if (error < 0)
goto out;
return error;
}
return snd_card_register(card);
}
static int __devinit snd_es1688_isa_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
int error;
error = snd_card_create(index[n], id[n], THIS_MODULE,
sizeof(struct snd_es1688), &card);
if (error < 0)
return error;
error = snd_es1688_legacy_create(card, dev, n);
if (error < 0)
goto out;
snd_card_set_dev(card, dev);
error = snd_card_register(card);
error = snd_es1688_probe(card, n);
if (error < 0)
goto out;
dev_set_drvdata(dev, card);
return 0;
out: snd_card_free(card);
return 0;
out:
snd_card_free(card);
return error;
}
static int __devexit snd_es1688_remove(struct device *dev, unsigned int n)
static int __devexit snd_es1688_isa_remove(struct device *dev, unsigned int n)
{
snd_card_free(dev_get_drvdata(dev));
dev_set_drvdata(dev, NULL);
@ -192,8 +219,8 @@ static int __devexit snd_es1688_remove(struct device *dev, unsigned int n)
static struct isa_driver snd_es1688_driver = {
.match = snd_es1688_match,
.probe = snd_es1688_probe,
.remove = __devexit_p(snd_es1688_remove),
.probe = snd_es1688_isa_probe,
.remove = __devexit_p(snd_es1688_isa_remove),
#if 0 /* FIXME */
.suspend = snd_es1688_suspend,
.resume = snd_es1688_resume,
@ -203,14 +230,142 @@ static struct isa_driver snd_es1688_driver = {
}
};
static int snd_es968_pnp_is_probed;
#ifdef CONFIG_PNP
static int __devinit snd_card_es968_pnp(struct snd_card *card, unsigned int n,
struct pnp_card_link *pcard,
const struct pnp_card_device_id *pid)
{
struct snd_es1688 *chip = card->private_data;
struct pnp_dev *pdev;
int error;
pdev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
if (pdev == NULL)
return -ENODEV;
error = pnp_activate_dev(pdev);
if (error < 0) {
snd_printk(KERN_ERR "ES968 pnp configure failure\n");
return error;
}
port[n] = pnp_port_start(pdev, 0);
dma8[n] = pnp_dma(pdev, 0);
irq[n] = pnp_irq(pdev, 0);
return snd_es1688_create(card, chip, port[n], mpu_port[n], irq[n],
mpu_irq[n], dma8[n], ES1688_HW_AUTO);
}
static int __devinit snd_es968_pnp_detect(struct pnp_card_link *pcard,
const struct pnp_card_device_id *pid)
{
struct snd_card *card;
static unsigned int dev;
int error;
struct snd_es1688 *chip;
if (snd_es968_pnp_is_probed)
return -EBUSY;
for ( ; dev < SNDRV_CARDS; dev++) {
if (enable[dev] && isapnp[dev])
break;
}
if (dev == SNDRV_CARDS)
return -ENODEV;
error = snd_card_create(index[dev], id[dev], THIS_MODULE,
sizeof(struct snd_es1688), &card);
if (error < 0)
return error;
chip = card->private_data;
error = snd_card_es968_pnp(card, dev, pcard, pid);
if (error < 0) {
snd_card_free(card);
return error;
}
snd_card_set_dev(card, &pcard->card->dev);
error = snd_es1688_probe(card, dev);
if (error < 0)
return error;
pnp_set_card_drvdata(pcard, card);
snd_es968_pnp_is_probed = 1;
return 0;
}
static void __devexit snd_es968_pnp_remove(struct pnp_card_link * pcard)
{
snd_card_free(pnp_get_card_drvdata(pcard));
pnp_set_card_drvdata(pcard, NULL);
snd_es968_pnp_is_probed = 0;
}
#ifdef CONFIG_PM
static int snd_es968_pnp_suspend(struct pnp_card_link *pcard,
pm_message_t state)
{
struct snd_card *card = pnp_get_card_drvdata(pcard);
struct snd_es1688 *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
return 0;
}
static int snd_es968_pnp_resume(struct pnp_card_link *pcard)
{
struct snd_card *card = pnp_get_card_drvdata(pcard);
struct snd_es1688 *chip = card->private_data;
snd_es1688_reset(chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif
static struct pnp_card_device_id snd_es968_pnpids[] = {
{ .id = "ESS0968", .devs = { { "@@@0968" }, } },
{ .id = "ESS0968", .devs = { { "ESS0968" }, } },
{ .id = "", } /* end */
};
MODULE_DEVICE_TABLE(pnp_card, snd_es968_pnpids);
static struct pnp_card_driver es968_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = DEV_NAME " PnP",
.id_table = snd_es968_pnpids,
.probe = snd_es968_pnp_detect,
.remove = __devexit_p(snd_es968_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_es968_pnp_suspend,
.resume = snd_es968_pnp_resume,
#endif
};
#endif
static int __init alsa_card_es1688_init(void)
{
#ifdef CONFIG_PNP
pnp_register_card_driver(&es968_pnpc_driver);
if (snd_es968_pnp_is_probed)
return 0;
pnp_unregister_card_driver(&es968_pnpc_driver);
#endif
return isa_register_driver(&snd_es1688_driver, SNDRV_CARDS);
}
static void __exit alsa_card_es1688_exit(void)
{
isa_unregister_driver(&snd_es1688_driver);
if (!snd_es968_pnp_is_probed) {
isa_unregister_driver(&snd_es1688_driver);
return;
}
#ifdef CONFIG_PNP
pnp_unregister_card_driver(&es968_pnpc_driver);
#endif
}
module_init(alsa_card_es1688_init);

View File

@ -99,7 +99,7 @@ static unsigned char snd_es1688_mixer_read(struct snd_es1688 *chip, unsigned cha
return result;
}
static int snd_es1688_reset(struct snd_es1688 *chip)
int snd_es1688_reset(struct snd_es1688 *chip)
{
int i;
@ -115,6 +115,7 @@ static int snd_es1688_reset(struct snd_es1688 *chip)
snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */
return 0;
}
EXPORT_SYMBOL(snd_es1688_reset);
static int snd_es1688_probe(struct snd_es1688 *chip)
{
@ -620,7 +621,6 @@ static int snd_es1688_free(struct snd_es1688 *chip)
disable_dma(chip->dma8);
free_dma(chip->dma8);
}
kfree(chip);
return 0;
}
@ -638,23 +638,20 @@ static const char *snd_es1688_chip_id(struct snd_es1688 *chip)
}
int snd_es1688_create(struct snd_card *card,
struct snd_es1688 *chip,
unsigned long port,
unsigned long mpu_port,
int irq,
int mpu_irq,
int dma8,
unsigned short hardware,
struct snd_es1688 **rchip)
unsigned short hardware)
{
static struct snd_device_ops ops = {
.dev_free = snd_es1688_dev_free,
};
struct snd_es1688 *chip;
int err;
*rchip = NULL;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->irq = -1;
@ -662,25 +659,21 @@ int snd_es1688_create(struct snd_card *card,
if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) {
snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4);
snd_es1688_free(chip);
return -EBUSY;
}
if (request_irq(irq, snd_es1688_interrupt, IRQF_DISABLED, "ES1688", (void *) chip)) {
snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq);
snd_es1688_free(chip);
return -EBUSY;
}
chip->irq = irq;
if (request_dma(dma8, "ES1688")) {
snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8);
snd_es1688_free(chip);
return -EBUSY;
}
chip->dma8 = dma8;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
chip->card = card;
chip->port = port;
mpu_port &= ~0x000f;
if (mpu_port < 0x300 || mpu_port > 0x330)
@ -689,23 +682,16 @@ int snd_es1688_create(struct snd_card *card,
chip->mpu_irq = mpu_irq;
chip->hardware = hardware;
if ((err = snd_es1688_probe(chip)) < 0) {
snd_es1688_free(chip);
err = snd_es1688_probe(chip);
if (err < 0)
return err;
}
if ((err = snd_es1688_init(chip, 1)) < 0) {
snd_es1688_free(chip);
err = snd_es1688_init(chip, 1);
if (err < 0)
return err;
}
/* Register device */
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
snd_es1688_free(chip);
return err;
}
*rchip = chip;
return 0;
return snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
}
static struct snd_pcm_ops snd_es1688_playback_ops = {
@ -730,12 +716,14 @@ static struct snd_pcm_ops snd_es1688_capture_ops = {
.pointer = snd_es1688_capture_pointer,
};
int snd_es1688_pcm(struct snd_es1688 * chip, int device, struct snd_pcm ** rpcm)
int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip,
int device, struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
if ((err = snd_pcm_new(chip->card, "ESx688", device, 1, 1, &pcm)) < 0)
err = snd_pcm_new(card, "ESx688", device, 1, 1, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops);
@ -1009,18 +997,15 @@ static unsigned char snd_es1688_init_table[][2] = {
{ ES1688_REC_DEV, 0x17 }
};
int snd_es1688_mixer(struct snd_es1688 *chip)
int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip)
{
struct snd_card *card;
unsigned int idx;
int err;
unsigned char reg, val;
if (snd_BUG_ON(!chip || !chip->card))
if (snd_BUG_ON(!chip || !card))
return -EINVAL;
card = chip->card;
strcpy(card->mixername, snd_es1688_chip_id(chip));
for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) {

View File

@ -95,7 +95,7 @@ static int __devinit snd_gusextreme_match(struct device *dev, unsigned int n)
}
static int __devinit snd_gusextreme_es1688_create(struct snd_card *card,
struct device *dev, unsigned int n, struct snd_es1688 **rchip)
struct snd_es1688 *chip, struct device *dev, unsigned int n)
{
static long possible_ports[] = {0x220, 0x240, 0x260};
static int possible_irqs[] = {5, 9, 10, 7, -1};
@ -119,14 +119,14 @@ static int __devinit snd_gusextreme_es1688_create(struct snd_card *card,
}
if (port[n] != SNDRV_AUTO_PORT)
return snd_es1688_create(card, port[n], mpu_port[n], irq[n],
mpu_irq[n], dma8[n], ES1688_HW_1688, rchip);
return snd_es1688_create(card, chip, port[n], mpu_port[n],
irq[n], mpu_irq[n], dma8[n], ES1688_HW_1688);
i = 0;
do {
port[n] = possible_ports[i];
error = snd_es1688_create(card, port[n], mpu_port[n], irq[n],
mpu_irq[n], dma8[n], ES1688_HW_1688, rchip);
error = snd_es1688_create(card, chip, port[n], mpu_port[n],
irq[n], mpu_irq[n], dma8[n], ES1688_HW_1688);
} while (error < 0 && ++i < ARRAY_SIZE(possible_ports));
return error;
@ -206,9 +206,8 @@ static int __devinit snd_gusextreme_detect(struct snd_gus_card *gus,
return 0;
}
static int __devinit snd_gusextreme_mixer(struct snd_es1688 *chip)
static int __devinit snd_gusextreme_mixer(struct snd_card *card)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
int error;
@ -241,17 +240,20 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
struct snd_opl3 *opl3;
int error;
error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
error = snd_card_create(index[n], id[n], THIS_MODULE,
sizeof(struct snd_es1688), &card);
if (error < 0)
return error;
es1688 = card->private_data;
if (mpu_port[n] == SNDRV_AUTO_PORT)
mpu_port[n] = 0;
if (mpu_irq[n] > 15)
mpu_irq[n] = -1;
error = snd_gusextreme_es1688_create(card, dev, n, &es1688);
error = snd_gusextreme_es1688_create(card, es1688, dev, n);
if (error < 0)
goto out;
@ -280,11 +282,11 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
}
gus->codec_flag = 1;
error = snd_es1688_pcm(es1688, 0, NULL);
error = snd_es1688_pcm(card, es1688, 0, NULL);
if (error < 0)
goto out;
error = snd_es1688_mixer(es1688);
error = snd_es1688_mixer(card, es1688);
if (error < 0)
goto out;
@ -300,7 +302,7 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
if (error < 0)
goto out;
error = snd_gusextreme_mixer(es1688);
error = snd_gusextreme_mixer(card);
if (error < 0)
goto out;

View File

@ -11,7 +11,6 @@ snd-sb8-objs := sb8.o
snd-sb16-objs := sb16.o
snd-sbawe-objs := sbawe.o emu8000.o
snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
snd-es968-objs := es968.o
snd-jazz16-objs := jazz16.o
# Toplevel Module Dependency
@ -21,7 +20,6 @@ obj-$(CONFIG_SND_SB8_DSP) += snd-sb8-dsp.o
obj-$(CONFIG_SND_SB8) += snd-sb8.o
obj-$(CONFIG_SND_SB16) += snd-sb16.o
obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o
obj-$(CONFIG_SND_ES968) += snd-es968.o
obj-$(CONFIG_SND_JAZZ16) += snd-jazz16.o
ifeq ($(CONFIG_SND_SB16_CSP),y)
obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o

View File

@ -1,248 +0,0 @@
/*
card-es968.c - driver for ESS AudioDrive ES968 based soundcards.
Copyright (C) 1999 by Massimo Piccioni <dafastidio@libero.it>
Thanks to Pierfrancesco 'qM2' Passerini.
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/time.h>
#include <linux/pnp.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/sb.h>
#define PFX "es968: "
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_DESCRIPTION("ESS AudioDrive ES968");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{ESS,AudioDrive ES968}}");
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_ISAPNP; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */
static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for es968 based soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for es968 based soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable es968 based soundcard.");
struct snd_card_es968 {
struct pnp_dev *dev;
struct snd_sb *chip;
};
static struct pnp_card_device_id snd_es968_pnpids[] = {
{ .id = "ESS0968", .devs = { { "@@@0968" }, } },
{ .id = "", } /* end */
};
MODULE_DEVICE_TABLE(pnp_card, snd_es968_pnpids);
#define DRIVER_NAME "snd-card-es968"
static irqreturn_t snd_card_es968_interrupt(int irq, void *dev_id)
{
struct snd_sb *chip = dev_id;
if (chip->open & SB_OPEN_PCM) {
return snd_sb8dsp_interrupt(chip);
} else {
return snd_sb8dsp_midi_interrupt(chip);
}
}
static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard,
struct pnp_card_link *card,
const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
if (acard->dev == NULL)
return -ENODEV;
pdev = acard->dev;
err = pnp_activate_dev(pdev);
if (err < 0) {
snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
return err;
}
port[dev] = pnp_port_start(pdev, 0);
dma8[dev] = pnp_dma(pdev, 0);
irq[dev] = pnp_irq(pdev, 0);
return 0;
}
static int __devinit snd_card_es968_probe(int dev,
struct pnp_card_link *pcard,
const struct pnp_card_device_id *pid)
{
int error;
struct snd_sb *chip;
struct snd_card *card;
struct snd_card_es968 *acard;
error = snd_card_create(index[dev], id[dev], THIS_MODULE,
sizeof(struct snd_card_es968), &card);
if (error < 0)
return error;
acard = card->private_data;
if ((error = snd_card_es968_pnp(dev, acard, pcard, pid))) {
snd_card_free(card);
return error;
}
snd_card_set_dev(card, &pcard->card->dev);
if ((error = snd_sbdsp_create(card, port[dev],
irq[dev],
snd_card_es968_interrupt,
dma8[dev],
-1,
SB_HW_AUTO, &chip)) < 0) {
snd_card_free(card);
return error;
}
acard->chip = chip;
if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) {
snd_card_free(card);
return error;
}
if ((error = snd_sbmixer_new(chip)) < 0) {
snd_card_free(card);
return error;
}
if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) {
snd_card_free(card);
return error;
}
strcpy(card->driver, "ES968");
strcpy(card->shortname, "ESS ES968");
sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d",
card->shortname, chip->name, chip->port, irq[dev], dma8[dev]);
if ((error = snd_card_register(card)) < 0) {
snd_card_free(card);
return error;
}
pnp_set_card_drvdata(pcard, card);
return 0;
}
static unsigned int __devinitdata es968_devices;
static int __devinit snd_es968_pnp_detect(struct pnp_card_link *card,
const struct pnp_card_device_id *id)
{
static int dev;
int res;
for ( ; dev < SNDRV_CARDS; dev++) {
if (!enable[dev])
continue;
res = snd_card_es968_probe(dev, card, id);
if (res < 0)
return res;
dev++;
es968_devices++;
return 0;
}
return -ENODEV;
}
static void __devexit snd_es968_pnp_remove(struct pnp_card_link * pcard)
{
snd_card_free(pnp_get_card_drvdata(pcard));
pnp_set_card_drvdata(pcard, NULL);
}
#ifdef CONFIG_PM
static int snd_es968_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
struct snd_card *card = pnp_get_card_drvdata(pcard);
struct snd_card_es968 *acard = card->private_data;
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
static int snd_es968_pnp_resume(struct pnp_card_link *pcard)
{
struct snd_card *card = pnp_get_card_drvdata(pcard);
struct snd_card_es968 *acard = card->private_data;
struct snd_sb *chip = acard->chip;
snd_sbdsp_reset(chip);
snd_sbmixer_resume(chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif
static struct pnp_card_driver es968_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = "es968",
.id_table = snd_es968_pnpids,
.probe = snd_es968_pnp_detect,
.remove = __devexit_p(snd_es968_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_es968_pnp_suspend,
.resume = snd_es968_pnp_resume,
#endif
};
static int __init alsa_card_es968_init(void)
{
int err = pnp_register_card_driver(&es968_pnpc_driver);
if (err)
return err;
if (!es968_devices) {
pnp_unregister_card_driver(&es968_pnpc_driver);
#ifdef MODULE
snd_printk(KERN_ERR "no ES968 based soundcards found\n");
#endif
return -ENODEV;
}
return 0;
}
static void __exit alsa_card_es968_exit(void)
{
pnp_unregister_card_driver(&es968_pnpc_driver);
}
module_init(alsa_card_es968_init)
module_exit(alsa_card_es968_exit)

View File

@ -58,6 +58,18 @@ config SND_ALI5451
To compile this driver as a module, choose M here: the module
will be called snd-ali5451.
config SND_ASIHPI
tristate "AudioScience ASIxxxx"
depends on X86
select FW_LOADER
select SND_PCM
select SND_HWDEP
help
Say Y here to include support for AudioScience ASI sound cards.
To compile this driver as a module, choose M here: the module
will be called snd-asihpi.
config SND_ATIIXP
tristate "ATI IXP AC97 Controller"
select SND_AC97_CODEC
@ -501,6 +513,16 @@ config SND_ES1968
To compile this driver as a module, choose M here: the module
will be called snd-es1968.
config SND_ES1968_INPUT
bool "Enable input device for es1968 volume buttons"
depends on SND_ES1968
depends on INPUT=y || INPUT=SND_ES1968
help
If you say Y here, you will get an input device which reports
keypresses for the volume buttons connected to the es1968 chip.
If you say N the buttons will directly control the master volume.
It is recommended to say Y.
config SND_FM801
tristate "ForteMedia FM801"
select SND_OPL3_LIB
@ -655,6 +677,16 @@ config SND_MAESTRO3
To compile this driver as a module, choose M here: the module
will be called snd-maestro3.
config SND_MAESTRO3_INPUT
bool "Enable input device for maestro3 volume buttons"
depends on SND_MAESTRO3
depends on INPUT=y || INPUT=SND_MAESTRO3
help
If you say Y here, you will get an input device which reports
keypresses for the volume buttons connected to the maestro3 chip.
If you say N the buttons will directly control the master volume.
It is recommended to say Y.
config SND_MIXART
tristate "Digigram miXart"
select SND_HWDEP

View File

@ -57,6 +57,7 @@ obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o
obj-$(CONFIG_SND) += \
ac97/ \
ali5451/ \
asihpi/ \
au88x0/ \
aw2/ \
ctxfi/ \

View File

@ -0,0 +1,5 @@
snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\
hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\
hpios.o hpi6000.o hpi6205.o hpimsgx.o
obj-$(CONFIG_SND_ASIHPI) += snd-asihpi.o

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
/*****************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Public declarations for DSP Proramming Interface to TI C6701
Shared between hpi6000.c and DSP code
(C) Copyright AudioScience Inc. 1998-2003
******************************************************************************/
#ifndef _HPI6000_H_
#define _HPI6000_H_
#define HPI_NMIXER_CONTROLS 200
/*
* Control caching is always supported in the HPI code.
* The DSP should make sure that dwControlCacheSizeInBytes is initialized to 0
* during boot to make it in-active.
*/
struct hpi_hif_6000 {
u32 host_cmd;
u32 dsp_ack;
u32 address;
u32 length;
u32 message_buffer_address;
u32 response_buffer_address;
u32 dsp_number;
u32 adapter_info;
u32 control_cache_is_dirty;
u32 control_cache_address;
u32 control_cache_size_in_bytes;
u32 control_cache_count;
};
#define HPI_HIF_PACK_ADAPTER_INFO(adapter, version_major, version_minor) \
((adapter << 16) | (version_major << 8) | version_minor)
#define HPI_HIF_ADAPTER_INFO_EXTRACT_ADAPTER(adapterinfo) \
((adapterinfo >> 16) & 0xffff)
#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MAJOR(adapterinfo) \
((adapterinfo >> 8) & 0xff)
#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MINOR(adapterinfo) \
(adapterinfo & 0xff)
/* Command/status exchanged between host and DSP */
#define HPI_HIF_IDLE 0
#define HPI_HIF_SEND_MSG 1
#define HPI_HIF_GET_RESP 2
#define HPI_HIF_DATA_MASK 0x10
#define HPI_HIF_SEND_DATA 0x13
#define HPI_HIF_GET_DATA 0x14
#define HPI_HIF_SEND_DONE 5
#define HPI_HIF_RESET 9
#endif /* _HPI6000_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
/*****************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Host Interface module for an ASI6205 based
bus mastering PCI adapter.
Copyright AudioScience, Inc., 2003
******************************************************************************/
#ifndef _HPI6205_H_
#define _HPI6205_H_
/* transitional conditional compile shared between host and DSP */
/* #define HPI6205_NO_HSR_POLL */
#include "hpi_internal.h"
/***********************************************************
Defines used for basic messaging
************************************************************/
#define H620_HIF_RESET 0
#define H620_HIF_IDLE 1
#define H620_HIF_GET_RESP 2
#define H620_HIF_DATA_DONE 3
#define H620_HIF_DATA_MASK 0x10
#define H620_HIF_SEND_DATA 0x14
#define H620_HIF_GET_DATA 0x15
#define H620_HIF_UNKNOWN 0x0000ffff
/***********************************************************
Types used for mixer control caching
************************************************************/
#define H620_MAX_ISTREAMS 32
#define H620_MAX_OSTREAMS 32
#define HPI_NMIXER_CONTROLS 2048
/*********************************************************************
This is used for dynamic control cache allocation
**********************************************************************/
struct controlcache_6205 {
u32 number_of_controls;
u32 physical_address32;
u32 size_in_bytes;
};
/*********************************************************************
This is used for dynamic allocation of async event array
**********************************************************************/
struct async_event_buffer_6205 {
u32 physical_address32;
u32 spare;
struct hpi_fifo_buffer b;
};
/***********************************************************
The Host located memory buffer that the 6205 will bus master
in and out of.
************************************************************/
#define HPI6205_SIZEOF_DATA (16*1024)
struct bus_master_interface {
u32 host_cmd;
u32 dsp_ack;
u32 transfer_size_in_bytes;
union {
struct hpi_message message_buffer;
struct hpi_response response_buffer;
u8 b_data[HPI6205_SIZEOF_DATA];
} u;
struct controlcache_6205 control_cache;
struct async_event_buffer_6205 async_buffer;
struct hpi_hostbuffer_status
instream_host_buffer_status[H620_MAX_ISTREAMS];
struct hpi_hostbuffer_status
outstream_host_buffer_status[H620_MAX_OSTREAMS];
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,643 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
\file hpicmn.c
Common functions used by hpixxxx.c modules
(C) Copyright AudioScience Inc. 1998-2003
*******************************************************************************/
#define SOURCEFILE_NAME "hpicmn.c"
#include "hpi_internal.h"
#include "hpidebug.h"
#include "hpicmn.h"
struct hpi_adapters_list {
struct hpios_spinlock list_lock;
struct hpi_adapter_obj adapter[HPI_MAX_ADAPTERS];
u16 gw_num_adapters;
};
static struct hpi_adapters_list adapters;
/**
* Given an HPI Message that was sent out and a response that was received,
* validate that the response has the correct fields filled in,
* i.e ObjectType, Function etc
**/
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr)
{
u16 error = 0;
if ((phr->type != HPI_TYPE_RESPONSE)
|| (phr->object != phm->object)
|| (phr->function != phm->function))
error = HPI_ERROR_INVALID_RESPONSE;
return error;
}
u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
{
u16 retval = 0;
/*HPI_ASSERT(pao->wAdapterType); */
hpios_alistlock_lock(&adapters);
if (pao->index >= HPI_MAX_ADAPTERS) {
retval = HPI_ERROR_BAD_ADAPTER_NUMBER;
goto unlock;
}
if (adapters.adapter[pao->index].adapter_type) {
{
retval = HPI_DUPLICATE_ADAPTER_NUMBER;
goto unlock;
}
}
adapters.adapter[pao->index] = *pao;
hpios_dsplock_init(&adapters.adapter[pao->index]);
adapters.gw_num_adapters++;
unlock:
hpios_alistlock_un_lock(&adapters);
return retval;
}
void hpi_delete_adapter(struct hpi_adapter_obj *pao)
{
memset(pao, 0, sizeof(struct hpi_adapter_obj));
hpios_alistlock_lock(&adapters);
adapters.gw_num_adapters--; /* dec the number of adapters */
hpios_alistlock_un_lock(&adapters);
}
/**
* FindAdapter returns a pointer to the struct hpi_adapter_obj with
* index wAdapterIndex in an HPI_ADAPTERS_LIST structure.
*
*/
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
{
struct hpi_adapter_obj *pao = NULL;
if (adapter_index >= HPI_MAX_ADAPTERS) {
HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d ",
adapter_index);
return NULL;
}
pao = &adapters.adapter[adapter_index];
if (pao->adapter_type != 0) {
/*
HPI_DEBUG_LOG(VERBOSE, "Found adapter index %d\n",
wAdapterIndex);
*/
return pao;
} else {
/*
HPI_DEBUG_LOG(VERBOSE, "No adapter index %d\n",
wAdapterIndex);
*/
return NULL;
}
}
/**
*
* wipe an HPI_ADAPTERS_LIST structure.
*
**/
static void wipe_adapter_list(void
)
{
memset(&adapters, 0, sizeof(adapters));
}
/**
* SubSysGetAdapters fills awAdapterList in an struct hpi_response structure
* with all adapters in the given HPI_ADAPTERS_LIST.
*
*/
static void subsys_get_adapters(struct hpi_response *phr)
{
/* fill in the response adapter array with the position */
/* identified by the adapter number/index of the adapters in */
/* this HPI */
/* i.e. if we have an A120 with it's jumper set to */
/* Adapter Number 2 then put an Adapter type A120 in the */
/* array in position 1 */
/* NOTE: AdapterNumber is 1..N, Index is 0..N-1 */
/* input: NONE */
/* output: wNumAdapters */
/* awAdapter[] */
/* */
short i;
struct hpi_adapter_obj *pao = NULL;
HPI_DEBUG_LOG(VERBOSE, "subsys_get_adapters\n");
/* for each adapter, place it's type in the position of the array */
/* corresponding to it's adapter number */
for (i = 0; i < adapters.gw_num_adapters; i++) {
pao = &adapters.adapter[i];
if (phr->u.s.aw_adapter_list[pao->index] != 0) {
phr->error = HPI_DUPLICATE_ADAPTER_NUMBER;
phr->specific_error = pao->index;
return;
}
phr->u.s.aw_adapter_list[pao->index] = pao->adapter_type;
}
phr->u.s.num_adapters = adapters.gw_num_adapters;
phr->error = 0; /* the function completed OK; */
}
static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
{
unsigned int i;
int cached = 0;
if (!pC)
return 0;
if ((!pC->init) && (pC->p_cache != NULL) && (pC->control_count)
&& (pC->cache_size_in_bytes)
) {
u32 *p_master_cache;
pC->init = 1;
p_master_cache = (u32 *)pC->p_cache;
HPI_DEBUG_LOG(VERBOSE, "check %d controls\n",
pC->control_count);
for (i = 0; i < pC->control_count; i++) {
struct hpi_control_cache_info *info =
(struct hpi_control_cache_info *)
p_master_cache;
if (info->control_type) {
pC->p_info[i] = info;
cached++;
} else
pC->p_info[i] = NULL;
if (info->size_in32bit_words)
p_master_cache += info->size_in32bit_words;
else
p_master_cache +=
sizeof(struct
hpi_control_cache_single) /
sizeof(u32);
HPI_DEBUG_LOG(VERBOSE,
"cached %d, pinfo %p index %d type %d\n",
cached, pC->p_info[i], info->control_index,
info->control_type);
}
/*
We didn't find anything to cache, so try again later !
*/
if (!cached)
pC->init = 0;
}
return pC->init;
}
/** Find a control.
*/
static short find_control(struct hpi_message *phm,
struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI,
u16 *pw_control_index)
{
*pw_control_index = phm->obj_index;
if (!control_cache_alloc_check(p_cache)) {
HPI_DEBUG_LOG(VERBOSE,
"control_cache_alloc_check() failed. adap%d ci%d\n",
phm->adapter_index, *pw_control_index);
return 0;
}
*pI = p_cache->p_info[*pw_control_index];
if (!*pI) {
HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, control %d\n",
phm->adapter_index, *pw_control_index);
return 0;
} else {
HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n",
(*pI)->control_type);
}
return 1;
}
/** Used by the kernel driver to figure out if a buffer needs mapping.
*/
short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
struct hpi_message *phm, void **p, unsigned int *pN)
{
*pN = 0;
*p = NULL;
if ((phm->function == HPI_CONTROL_GET_STATE)
&& (phm->object == HPI_OBJ_CONTROLEX)
) {
u16 control_index;
struct hpi_control_cache_info *pI;
if (!find_control(phm, p_cache, &pI, &control_index))
return 0;
}
return 0;
}
/* allow unified treatment of several string fields within struct */
#define HPICMN_PAD_OFS_AND_SIZE(m) {\
offsetof(struct hpi_control_cache_pad, m), \
sizeof(((struct hpi_control_cache_pad *)(NULL))->m) }
struct pad_ofs_size {
unsigned int offset;
unsigned int field_size;
};
static struct pad_ofs_size pad_desc[] = {
HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */
HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
HPICMN_PAD_OFS_AND_SIZE(c_comment), /* HPI_PAD_COMMENT */
};
/** CheckControlCache checks the cache and fills the struct hpi_response
* accordingly. It returns one if a cache hit occurred, zero otherwise.
*/
short hpi_check_control_cache(struct hpi_control_cache *p_cache,
struct hpi_message *phm, struct hpi_response *phr)
{
short found = 1;
u16 control_index;
struct hpi_control_cache_info *pI;
struct hpi_control_cache_single *pC;
struct hpi_control_cache_pad *p_pad;
if (!find_control(phm, p_cache, &pI, &control_index))
return 0;
phr->error = 0;
/* pC is the default cached control strucure. May be cast to
something else in the following switch statement.
*/
pC = (struct hpi_control_cache_single *)pI;
p_pad = (struct hpi_control_cache_pad *)pI;
switch (pI->control_type) {
case HPI_CONTROL_METER:
if (phm->u.c.attribute == HPI_METER_PEAK) {
phr->u.c.an_log_value[0] = pC->u.p.an_log_peak[0];
phr->u.c.an_log_value[1] = pC->u.p.an_log_peak[1];
} else if (phm->u.c.attribute == HPI_METER_RMS) {
phr->u.c.an_log_value[0] = pC->u.p.an_logRMS[0];
phr->u.c.an_log_value[1] = pC->u.p.an_logRMS[1];
} else
found = 0;
break;
case HPI_CONTROL_VOLUME:
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
phr->u.c.an_log_value[0] = pC->u.v.an_log[0];
phr->u.c.an_log_value[1] = pC->u.v.an_log[1];
} else
found = 0;
break;
case HPI_CONTROL_MULTIPLEXER:
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
phr->u.c.param1 = pC->u.x.source_node_type;
phr->u.c.param2 = pC->u.x.source_node_index;
} else {
found = 0;
}
break;
case HPI_CONTROL_CHANNEL_MODE:
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
phr->u.c.param1 = pC->u.m.mode;
else
found = 0;
break;
case HPI_CONTROL_LEVEL:
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
phr->u.c.an_log_value[0] = pC->u.l.an_log[0];
phr->u.c.an_log_value[1] = pC->u.l.an_log[1];
} else
found = 0;
break;
case HPI_CONTROL_TUNER:
{
struct hpi_control_cache_single *pCT =
(struct hpi_control_cache_single *)pI;
if (phm->u.c.attribute == HPI_TUNER_FREQ)
phr->u.c.param1 = pCT->u.t.freq_ink_hz;
else if (phm->u.c.attribute == HPI_TUNER_BAND)
phr->u.c.param1 = pCT->u.t.band;
else if ((phm->u.c.attribute == HPI_TUNER_LEVEL)
&& (phm->u.c.param1 ==
HPI_TUNER_LEVEL_AVERAGE))
phr->u.c.param1 = pCT->u.t.level;
else
found = 0;
}
break;
case HPI_CONTROL_AESEBU_RECEIVER:
if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS)
phr->u.c.param1 = pC->u.aes3rx.error_status;
else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
phr->u.c.param1 = pC->u.aes3rx.source;
else
found = 0;
break;
case HPI_CONTROL_AESEBU_TRANSMITTER:
if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
phr->u.c.param1 = pC->u.aes3tx.format;
else
found = 0;
break;
case HPI_CONTROL_TONEDETECTOR:
if (phm->u.c.attribute == HPI_TONEDETECTOR_STATE)
phr->u.c.param1 = pC->u.tone.state;
else
found = 0;
break;
case HPI_CONTROL_SILENCEDETECTOR:
if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) {
phr->u.c.param1 = pC->u.silence.state;
phr->u.c.param2 = pC->u.silence.count;
} else
found = 0;
break;
case HPI_CONTROL_MICROPHONE:
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
phr->u.c.param1 = pC->u.phantom_power.state;
else
found = 0;
break;
case HPI_CONTROL_SAMPLECLOCK:
if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
phr->u.c.param1 = pC->u.clk.source;
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) {
if (pC->u.clk.source_index ==
HPI_ERROR_ILLEGAL_CACHE_VALUE) {
phr->u.c.param1 = 0;
phr->error = HPI_ERROR_INVALID_OPERATION;
} else
phr->u.c.param1 = pC->u.clk.source_index;
} else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE)
phr->u.c.param1 = pC->u.clk.sample_rate;
else
found = 0;
break;
case HPI_CONTROL_PAD:
if (!(p_pad->field_valid_flags & (1 <<
HPI_CTL_ATTR_INDEX(phm->u.c.
attribute)))) {
phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
break;
}
if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)
phr->u.c.param1 = p_pad->pI;
else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)
phr->u.c.param1 = p_pad->pTY;
else {
unsigned int index =
HPI_CTL_ATTR_INDEX(phm->u.c.attribute) - 1;
unsigned int offset = phm->u.c.param1;
unsigned int pad_string_len, field_size;
char *pad_string;
unsigned int tocopy;
HPI_DEBUG_LOG(VERBOSE, "PADS HPI_PADS_ %d\n",
phm->u.c.attribute);
if (index > ARRAY_SIZE(pad_desc) - 1) {
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
break;
}
pad_string = ((char *)p_pad) + pad_desc[index].offset;
field_size = pad_desc[index].field_size;
/* Ensure null terminator */
pad_string[field_size - 1] = 0;
pad_string_len = strlen(pad_string) + 1;
if (offset > pad_string_len) {
phr->error = HPI_ERROR_INVALID_CONTROL_VALUE;
break;
}
tocopy = pad_string_len - offset;
if (tocopy > sizeof(phr->u.cu.chars8.sz_data))
tocopy = sizeof(phr->u.cu.chars8.sz_data);
HPI_DEBUG_LOG(VERBOSE,
"PADS memcpy(%d), offset %d \n", tocopy,
offset);
memcpy(phr->u.cu.chars8.sz_data, &pad_string[offset],
tocopy);
phr->u.cu.chars8.remaining_chars =
pad_string_len - offset - tocopy;
}
break;
default:
found = 0;
break;
}
if (found)
HPI_DEBUG_LOG(VERBOSE,
"cached adap %d, ctl %d, type %d, attr %d\n",
phm->adapter_index, pI->control_index,
pI->control_type, phm->u.c.attribute);
else
HPI_DEBUG_LOG(VERBOSE,
"uncached adap %d, ctl %d, ctl type %d\n",
phm->adapter_index, pI->control_index,
pI->control_type);
if (found)
phr->size =
sizeof(struct hpi_response_header) +
sizeof(struct hpi_control_res);
return found;
}
/** Updates the cache with Set values.
Only update if no error.
Volume and Level return the limited values in the response, so use these
Multiplexer does so use sent values
*/
void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
struct hpi_message *phm, struct hpi_response *phr)
{
u16 control_index;
struct hpi_control_cache_single *pC;
struct hpi_control_cache_info *pI;
if (!find_control(phm, p_cache, &pI, &control_index))
return;
/* pC is the default cached control strucure.
May be cast to something else in the following switch statement.
*/
pC = (struct hpi_control_cache_single *)pI;
switch (pI->control_type) {
case HPI_CONTROL_VOLUME:
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
}
break;
case HPI_CONTROL_MULTIPLEXER:
/* mux does not return its setting on Set command. */
if (phr->error)
return;
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
pC->u.x.source_node_type = (u16)phm->u.c.param1;
pC->u.x.source_node_index = (u16)phm->u.c.param2;
}
break;
case HPI_CONTROL_CHANNEL_MODE:
/* mode does not return its setting on Set command. */
if (phr->error)
return;
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
pC->u.m.mode = (u16)phm->u.c.param1;
break;
case HPI_CONTROL_LEVEL:
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
}
break;
case HPI_CONTROL_MICROPHONE:
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
pC->u.phantom_power.state = (u16)phm->u.c.param1;
break;
case HPI_CONTROL_AESEBU_TRANSMITTER:
if (phr->error)
return;
if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
pC->u.aes3tx.format = phm->u.c.param1;
break;
case HPI_CONTROL_AESEBU_RECEIVER:
if (phr->error)
return;
if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
pC->u.aes3rx.source = phm->u.c.param1;
break;
case HPI_CONTROL_SAMPLECLOCK:
if (phr->error)
return;
if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
pC->u.clk.source = (u16)phm->u.c.param1;
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX)
pC->u.clk.source_index = (u16)phm->u.c.param1;
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE)
pC->u.clk.sample_rate = phm->u.c.param1;
break;
default:
break;
}
}
struct hpi_control_cache *hpi_alloc_control_cache(const u32
number_of_controls, const u32 size_in_bytes,
struct hpi_control_cache_info *pDSP_control_buffer)
{
struct hpi_control_cache *p_cache =
kmalloc(sizeof(*p_cache), GFP_KERNEL);
p_cache->cache_size_in_bytes = size_in_bytes;
p_cache->control_count = number_of_controls;
p_cache->p_cache =
(struct hpi_control_cache_single *)pDSP_control_buffer;
p_cache->init = 0;
p_cache->p_info =
kmalloc(sizeof(*p_cache->p_info) * p_cache->control_count,
GFP_KERNEL);
return p_cache;
}
void hpi_free_control_cache(struct hpi_control_cache *p_cache)
{
if ((p_cache->init) && (p_cache->p_info)) {
kfree(p_cache->p_info);
p_cache->p_info = NULL;
p_cache->init = 0;
kfree(p_cache);
}
}
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_SUBSYS_OPEN:
case HPI_SUBSYS_CLOSE:
case HPI_SUBSYS_DRIVER_UNLOAD:
phr->error = 0;
break;
case HPI_SUBSYS_DRIVER_LOAD:
wipe_adapter_list();
hpios_alistlock_init(&adapters);
phr->error = 0;
break;
case HPI_SUBSYS_GET_INFO:
subsys_get_adapters(phr);
break;
case HPI_SUBSYS_CREATE_ADAPTER:
case HPI_SUBSYS_DELETE_ADAPTER:
phr->error = 0;
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
}
}
void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->type) {
case HPI_TYPE_MESSAGE:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
break;
}
break;
default:
phr->error = HPI_ERROR_INVALID_TYPE;
break;
}
}

View File

@ -0,0 +1,64 @@
/**
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
*/
struct hpi_adapter_obj {
struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */
u16 adapter_type; /* ASI6701 etc */
u16 index; /* */
u16 open; /* =1 when adapter open */
u16 mixer_open;
struct hpios_spinlock dsp_lock;
u16 dsp_crashed;
u16 has_control_cache;
void *priv;
};
struct hpi_control_cache {
u32 init; /**< indicates whether the
structures are initialized */
u32 control_count;
u32 cache_size_in_bytes;
struct hpi_control_cache_info
**p_info; /**< pointer to allocated memory of
lookup pointers. */
struct hpi_control_cache_single
*p_cache; /**< pointer to DSP's control cache. */
};
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index);
u16 hpi_add_adapter(struct hpi_adapter_obj *pao);
void hpi_delete_adapter(struct hpi_adapter_obj *pao);
short hpi_check_control_cache(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
struct hpi_control_cache *hpi_alloc_control_cache(const u32
number_of_controls, const u32 size_in_bytes,
struct hpi_control_cache_info
*pDSP_control_buffer);
void hpi_free_control_cache(struct hpi_control_cache *p_cache);
void hpi_sync_control_cache(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
struct hpi_message *phm, void **p, unsigned int *pN);

View File

@ -0,0 +1,225 @@
/************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Debug macro translation.
************************************************************************/
#include "hpi_internal.h"
#include "hpidebug.h"
/* Debug level; 0 quiet; 1 informative, 2 debug, 3 verbose debug. */
int hpi_debug_level = HPI_DEBUG_LEVEL_DEFAULT;
void hpi_debug_init(void)
{
printk(KERN_INFO "debug start\n");
}
int hpi_debug_level_set(int level)
{
int old_level;
old_level = hpi_debug_level;
hpi_debug_level = level;
return old_level;
}
int hpi_debug_level_get(void)
{
return hpi_debug_level;
}
#ifdef HPIOS_DEBUG_PRINT
/* implies OS has no printf-like function */
#include <stdarg.h>
void hpi_debug_printf(char *fmt, ...)
{
va_list arglist;
char buffer[128];
va_start(arglist, fmt);
if (buffer[0])
HPIOS_DEBUG_PRINT(buffer);
va_end(arglist);
}
#endif
struct treenode {
void *array;
unsigned int num_elements;
};
#define make_treenode_from_array(nodename, array) \
static void *tmp_strarray_##nodename[] = array; \
static struct treenode nodename = { \
&tmp_strarray_##nodename, \
ARRAY_SIZE(tmp_strarray_##nodename) \
};
#define get_treenode_elem(node_ptr, idx, type) \
(&(*((type *)(node_ptr)->array)[idx]))
make_treenode_from_array(hpi_control_type_strings, HPI_CONTROL_TYPE_STRINGS)
make_treenode_from_array(hpi_subsys_strings, HPI_SUBSYS_STRINGS)
make_treenode_from_array(hpi_adapter_strings, HPI_ADAPTER_STRINGS)
make_treenode_from_array(hpi_istream_strings, HPI_ISTREAM_STRINGS)
make_treenode_from_array(hpi_ostream_strings, HPI_OSTREAM_STRINGS)
make_treenode_from_array(hpi_mixer_strings, HPI_MIXER_STRINGS)
make_treenode_from_array(hpi_node_strings,
{
"NODE is invalid object"})
make_treenode_from_array(hpi_control_strings, HPI_CONTROL_STRINGS)
make_treenode_from_array(hpi_nvmemory_strings, HPI_OBJ_STRINGS)
make_treenode_from_array(hpi_digitalio_strings, HPI_DIGITALIO_STRINGS)
make_treenode_from_array(hpi_watchdog_strings, HPI_WATCHDOG_STRINGS)
make_treenode_from_array(hpi_clock_strings, HPI_CLOCK_STRINGS)
make_treenode_from_array(hpi_profile_strings, HPI_PROFILE_STRINGS)
make_treenode_from_array(hpi_asyncevent_strings, HPI_ASYNCEVENT_STRINGS)
#define HPI_FUNCTION_STRINGS \
{ \
&hpi_subsys_strings,\
&hpi_adapter_strings,\
&hpi_ostream_strings,\
&hpi_istream_strings,\
&hpi_mixer_strings,\
&hpi_node_strings,\
&hpi_control_strings,\
&hpi_nvmemory_strings,\
&hpi_digitalio_strings,\
&hpi_watchdog_strings,\
&hpi_clock_strings,\
&hpi_profile_strings,\
&hpi_control_strings, \
&hpi_asyncevent_strings \
};
make_treenode_from_array(hpi_function_strings, HPI_FUNCTION_STRINGS)
compile_time_assert(HPI_OBJ_MAXINDEX == 14, obj_list_doesnt_match);
static char *hpi_function_string(unsigned int function)
{
unsigned int object;
struct treenode *tmp;
object = function / HPI_OBJ_FUNCTION_SPACING;
function = function - object * HPI_OBJ_FUNCTION_SPACING;
if (object == 0 || object == HPI_OBJ_NODE
|| object > hpi_function_strings.num_elements)
return "invalid object";
tmp = get_treenode_elem(&hpi_function_strings, object - 1,
struct treenode *);
if (function == 0 || function > tmp->num_elements)
return "invalid function";
return get_treenode_elem(tmp, function - 1, char *);
}
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline)
{
if (phm) {
if ((phm->object <= HPI_OBJ_MAXINDEX) && phm->object) {
u16 index = 0;
u16 attrib = 0;
int is_control = 0;
index = phm->obj_index;
switch (phm->object) {
case HPI_OBJ_ADAPTER:
case HPI_OBJ_PROFILE:
break;
case HPI_OBJ_MIXER:
if (phm->function ==
HPI_MIXER_GET_CONTROL_BY_INDEX)
index = phm->u.m.control_index;
break;
case HPI_OBJ_OSTREAM:
case HPI_OBJ_ISTREAM:
break;
case HPI_OBJ_CONTROLEX:
case HPI_OBJ_CONTROL:
if (phm->version == 1)
attrib = HPI_CTL_ATTR(UNIVERSAL, 1);
else
attrib = phm->u.c.attribute;
is_control = 1;
break;
default:
break;
}
if (is_control && (attrib & 0xFF00)) {
int control_type = (attrib & 0xFF00) >> 8;
int attr_index = HPI_CTL_ATTR_INDEX(attrib);
/* note the KERN facility level
is in szFileline already */
printk("%s adapter %d %s "
"ctrl_index x%04x %s %d\n",
sz_fileline, phm->adapter_index,
hpi_function_string(phm->function),
index,
get_treenode_elem
(&hpi_control_type_strings,
control_type, char *),
attr_index);
} else
printk("%s adapter %d %s "
"idx x%04x attr x%04x \n",
sz_fileline, phm->adapter_index,
hpi_function_string(phm->function),
index, attrib);
} else {
printk("adap=%d, invalid obj=%d, func=0x%x\n",
phm->adapter_index, phm->object,
phm->function);
}
} else
printk(KERN_ERR
"NULL message pointer to hpi_debug_message!\n");
}
void hpi_debug_data(u16 *pdata, u32 len)
{
u32 i;
int j;
int k;
int lines;
int cols = 8;
lines = (len + cols - 1) / cols;
if (lines > 8)
lines = 8;
for (i = 0, j = 0; j < lines; j++) {
printk(KERN_DEBUG "%p:", (pdata + i));
for (k = 0; k < cols && i < len; i++, k++)
printk("%s%04x", k == 0 ? "" : " ", pdata[i]);
printk("\n");
}
}

View File

@ -0,0 +1,385 @@
/*****************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Debug macros.
*****************************************************************************/
#ifndef _HPIDEBUG_H
#define _HPIDEBUG_H
#include "hpi_internal.h"
/* Define debugging levels. */
enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
HPI_DEBUG_LEVEL_WARNING = 1,
HPI_DEBUG_LEVEL_NOTICE = 2,
HPI_DEBUG_LEVEL_INFO = 3,
HPI_DEBUG_LEVEL_DEBUG = 4,
HPI_DEBUG_LEVEL_VERBOSE = 5 /* same printk level as DEBUG */
};
#define HPI_DEBUG_LEVEL_DEFAULT HPI_DEBUG_LEVEL_NOTICE
/* an OS can define an extra flag string that is appended to
the start of each message, eg see hpios_linux.h */
#ifdef SOURCEFILE_NAME
#define FILE_LINE SOURCEFILE_NAME ":" __stringify(__LINE__) " "
#else
#define FILE_LINE __FILE__ ":" __stringify(__LINE__) " "
#endif
#if defined(HPI_DEBUG) && defined(_WINDOWS)
#define HPI_DEBUGBREAK() debug_break()
#else
#define HPI_DEBUGBREAK()
#endif
#define HPI_DEBUG_ASSERT(expression) \
do { \
if (!(expression)) {\
printk(KERN_ERR FILE_LINE\
"ASSERT " __stringify(expression));\
HPI_DEBUGBREAK();\
} \
} while (0)
#define HPI_DEBUG_LOG(level, ...) \
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
printk(HPI_DEBUG_FLAG_##level \
FILE_LINE __VA_ARGS__); \
} \
} while (0)
void hpi_debug_init(void);
int hpi_debug_level_set(int level);
int hpi_debug_level_get(void);
/* needed by Linux driver for dynamic debug level changes */
extern int hpi_debug_level;
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline);
void hpi_debug_data(u16 *pdata, u32 len);
#define HPI_DEBUG_DATA(pdata, len) \
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
hpi_debug_data(pdata, len); \
} while (0)
#define HPI_DEBUG_MESSAGE(level, phm) \
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
hpi_debug_message(phm,HPI_DEBUG_FLAG_##level \
FILE_LINE __stringify(level));\
} \
} while (0)
#define HPI_DEBUG_RESPONSE(phr) \
do { \
if ((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && (phr->error))\
HPI_DEBUG_LOG(ERROR, \
"HPI response - error# %d\n", \
phr->error); \
else if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
HPI_DEBUG_LOG(VERBOSE, "HPI response OK\n");\
} while (0)
#ifndef compile_time_assert
#define compile_time_assert(cond, msg) \
typedef char msg[(cond) ? 1 : -1]
#endif
/* check that size is exactly some number */
#define function_count_check(sym, size) \
compile_time_assert((sym##_FUNCTION_COUNT) == (size),\
strings_match_defs_##sym)
/* These strings should be generated using a macro which defines
the corresponding symbol values. */
#define HPI_OBJ_STRINGS \
{ \
"HPI_OBJ_SUBSYSTEM", \
"HPI_OBJ_ADAPTER", \
"HPI_OBJ_OSTREAM", \
"HPI_OBJ_ISTREAM", \
"HPI_OBJ_MIXER", \
"HPI_OBJ_NODE", \
"HPI_OBJ_CONTROL", \
"HPI_OBJ_NVMEMORY", \
"HPI_OBJ_DIGITALIO", \
"HPI_OBJ_WATCHDOG", \
"HPI_OBJ_CLOCK", \
"HPI_OBJ_PROFILE", \
"HPI_OBJ_CONTROLEX" \
}
#define HPI_SUBSYS_STRINGS \
{ \
"HPI_SUBSYS_OPEN", \
"HPI_SUBSYS_GET_VERSION", \
"HPI_SUBSYS_GET_INFO", \
"HPI_SUBSYS_FIND_ADAPTERS", \
"HPI_SUBSYS_CREATE_ADAPTER",\
"HPI_SUBSYS_CLOSE", \
"HPI_SUBSYS_DELETE_ADAPTER", \
"HPI_SUBSYS_DRIVER_LOAD", \
"HPI_SUBSYS_DRIVER_UNLOAD", \
"HPI_SUBSYS_READ_PORT_8", \
"HPI_SUBSYS_WRITE_PORT_8", \
"HPI_SUBSYS_GET_NUM_ADAPTERS",\
"HPI_SUBSYS_GET_ADAPTER", \
"HPI_SUBSYS_SET_NETWORK_INTERFACE"\
}
function_count_check(HPI_SUBSYS, 14);
#define HPI_ADAPTER_STRINGS \
{ \
"HPI_ADAPTER_OPEN", \
"HPI_ADAPTER_CLOSE", \
"HPI_ADAPTER_GET_INFO", \
"HPI_ADAPTER_GET_ASSERT", \
"HPI_ADAPTER_TEST_ASSERT", \
"HPI_ADAPTER_SET_MODE", \
"HPI_ADAPTER_GET_MODE", \
"HPI_ADAPTER_ENABLE_CAPABILITY",\
"HPI_ADAPTER_SELFTEST", \
"HPI_ADAPTER_FIND_OBJECT", \
"HPI_ADAPTER_QUERY_FLASH", \
"HPI_ADAPTER_START_FLASH", \
"HPI_ADAPTER_PROGRAM_FLASH", \
"HPI_ADAPTER_SET_PROPERTY", \
"HPI_ADAPTER_GET_PROPERTY", \
"HPI_ADAPTER_ENUM_PROPERTY", \
"HPI_ADAPTER_MODULE_INFO", \
"HPI_ADAPTER_DEBUG_READ" \
}
function_count_check(HPI_ADAPTER, 18);
#define HPI_OSTREAM_STRINGS \
{ \
"HPI_OSTREAM_OPEN", \
"HPI_OSTREAM_CLOSE", \
"HPI_OSTREAM_WRITE", \
"HPI_OSTREAM_START", \
"HPI_OSTREAM_STOP", \
"HPI_OSTREAM_RESET", \
"HPI_OSTREAM_GET_INFO", \
"HPI_OSTREAM_QUERY_FORMAT", \
"HPI_OSTREAM_DATA", \
"HPI_OSTREAM_SET_VELOCITY", \
"HPI_OSTREAM_SET_PUNCHINOUT", \
"HPI_OSTREAM_SINEGEN", \
"HPI_OSTREAM_ANC_RESET", \
"HPI_OSTREAM_ANC_GET_INFO", \
"HPI_OSTREAM_ANC_READ", \
"HPI_OSTREAM_SET_TIMESCALE",\
"HPI_OSTREAM_SET_FORMAT", \
"HPI_OSTREAM_HOSTBUFFER_ALLOC", \
"HPI_OSTREAM_HOSTBUFFER_FREE", \
"HPI_OSTREAM_GROUP_ADD",\
"HPI_OSTREAM_GROUP_GETMAP", \
"HPI_OSTREAM_GROUP_RESET", \
"HPI_OSTREAM_HOSTBUFFER_GET_INFO", \
"HPI_OSTREAM_WAIT_START", \
}
function_count_check(HPI_OSTREAM, 24);
#define HPI_ISTREAM_STRINGS \
{ \
"HPI_ISTREAM_OPEN", \
"HPI_ISTREAM_CLOSE", \
"HPI_ISTREAM_SET_FORMAT", \
"HPI_ISTREAM_READ", \
"HPI_ISTREAM_START", \
"HPI_ISTREAM_STOP", \
"HPI_ISTREAM_RESET", \
"HPI_ISTREAM_GET_INFO", \
"HPI_ISTREAM_QUERY_FORMAT", \
"HPI_ISTREAM_ANC_RESET", \
"HPI_ISTREAM_ANC_GET_INFO", \
"HPI_ISTREAM_ANC_WRITE", \
"HPI_ISTREAM_HOSTBUFFER_ALLOC",\
"HPI_ISTREAM_HOSTBUFFER_FREE", \
"HPI_ISTREAM_GROUP_ADD", \
"HPI_ISTREAM_GROUP_GETMAP", \
"HPI_ISTREAM_GROUP_RESET", \
"HPI_ISTREAM_HOSTBUFFER_GET_INFO", \
"HPI_ISTREAM_WAIT_START", \
}
function_count_check(HPI_ISTREAM, 19);
#define HPI_MIXER_STRINGS \
{ \
"HPI_MIXER_OPEN", \
"HPI_MIXER_CLOSE", \
"HPI_MIXER_GET_INFO", \
"HPI_MIXER_GET_NODE_INFO", \
"HPI_MIXER_GET_CONTROL", \
"HPI_MIXER_SET_CONNECTION", \
"HPI_MIXER_GET_CONNECTIONS", \
"HPI_MIXER_GET_CONTROL_BY_INDEX", \
"HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX", \
"HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES", \
"HPI_MIXER_STORE", \
}
function_count_check(HPI_MIXER, 11);
#define HPI_CONTROL_STRINGS \
{ \
"HPI_CONTROL_GET_INFO", \
"HPI_CONTROL_GET_STATE", \
"HPI_CONTROL_SET_STATE" \
}
function_count_check(HPI_CONTROL, 3);
#define HPI_NVMEMORY_STRINGS \
{ \
"HPI_NVMEMORY_OPEN", \
"HPI_NVMEMORY_READ_BYTE", \
"HPI_NVMEMORY_WRITE_BYTE" \
}
function_count_check(HPI_NVMEMORY, 3);
#define HPI_DIGITALIO_STRINGS \
{ \
"HPI_GPIO_OPEN", \
"HPI_GPIO_READ_BIT", \
"HPI_GPIO_WRITE_BIT", \
"HPI_GPIO_READ_ALL", \
"HPI_GPIO_WRITE_STATUS"\
}
function_count_check(HPI_GPIO, 5);
#define HPI_WATCHDOG_STRINGS \
{ \
"HPI_WATCHDOG_OPEN", \
"HPI_WATCHDOG_SET_TIME", \
"HPI_WATCHDOG_PING" \
}
#define HPI_CLOCK_STRINGS \
{ \
"HPI_CLOCK_OPEN", \
"HPI_CLOCK_SET_TIME", \
"HPI_CLOCK_GET_TIME" \
}
#define HPI_PROFILE_STRINGS \
{ \
"HPI_PROFILE_OPEN_ALL", \
"HPI_PROFILE_START_ALL", \
"HPI_PROFILE_STOP_ALL", \
"HPI_PROFILE_GET", \
"HPI_PROFILE_GET_IDLECOUNT", \
"HPI_PROFILE_GET_NAME", \
"HPI_PROFILE_GET_UTILIZATION" \
}
function_count_check(HPI_PROFILE, 7);
#define HPI_ASYNCEVENT_STRINGS \
{ \
"HPI_ASYNCEVENT_OPEN",\
"HPI_ASYNCEVENT_CLOSE ",\
"HPI_ASYNCEVENT_WAIT",\
"HPI_ASYNCEVENT_GETCOUNT",\
"HPI_ASYNCEVENT_GET",\
"HPI_ASYNCEVENT_SENDEVENTS"\
}
function_count_check(HPI_ASYNCEVENT, 6);
#define HPI_CONTROL_TYPE_STRINGS \
{ \
"null control", \
"HPI_CONTROL_CONNECTION", \
"HPI_CONTROL_VOLUME", \
"HPI_CONTROL_METER", \
"HPI_CONTROL_MUTE", \
"HPI_CONTROL_MULTIPLEXER", \
"HPI_CONTROL_AESEBU_TRANSMITTER", \
"HPI_CONTROL_AESEBU_RECEIVER", \
"HPI_CONTROL_LEVEL", \
"HPI_CONTROL_TUNER", \
"HPI_CONTROL_ONOFFSWITCH", \
"HPI_CONTROL_VOX", \
"HPI_CONTROL_AES18_TRANSMITTER", \
"HPI_CONTROL_AES18_RECEIVER", \
"HPI_CONTROL_AES18_BLOCKGENERATOR", \
"HPI_CONTROL_CHANNEL_MODE", \
"HPI_CONTROL_BITSTREAM", \
"HPI_CONTROL_SAMPLECLOCK", \
"HPI_CONTROL_MICROPHONE", \
"HPI_CONTROL_PARAMETRIC_EQ", \
"HPI_CONTROL_COMPANDER", \
"HPI_CONTROL_COBRANET", \
"HPI_CONTROL_TONE_DETECT", \
"HPI_CONTROL_SILENCE_DETECT", \
"HPI_CONTROL_PAD", \
"HPI_CONTROL_SRC" ,\
"HPI_CONTROL_UNIVERSAL" \
}
compile_time_assert((HPI_CONTROL_LAST_INDEX + 1 == 27),
controltype_strings_match_defs);
#define HPI_SOURCENODE_STRINGS \
{ \
"no source", \
"HPI_SOURCENODE_OSTREAM", \
"HPI_SOURCENODE_LINEIN", \
"HPI_SOURCENODE_AESEBU_IN", \
"HPI_SOURCENODE_TUNER", \
"HPI_SOURCENODE_RF", \
"HPI_SOURCENODE_CLOCK_SOURCE", \
"HPI_SOURCENODE_RAW_BITSTREAM", \
"HPI_SOURCENODE_MICROPHONE", \
"HPI_SOURCENODE_COBRANET", \
"HPI_SOURCENODE_ANALOG", \
"HPI_SOURCENODE_ADAPTER" \
}
compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_BASE + 1) ==
(12), sourcenode_strings_match_defs);
#define HPI_DESTNODE_STRINGS \
{ \
"no destination", \
"HPI_DESTNODE_ISTREAM", \
"HPI_DESTNODE_LINEOUT", \
"HPI_DESTNODE_AESEBU_OUT", \
"HPI_DESTNODE_RF", \
"HPI_DESTNODE_SPEAKER", \
"HPI_DESTNODE_COBRANET", \
"HPI_DESTNODE_ANALOG" \
}
compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_BASE + 1) == (8),
destnode_strings_match_defs);
#define HPI_CONTROL_CHANNEL_MODE_STRINGS \
{ \
"XXX HPI_CHANNEL_MODE_ERROR XXX", \
"HPI_CHANNEL_MODE_NORMAL", \
"HPI_CHANNEL_MODE_SWAP", \
"HPI_CHANNEL_MODE_LEFT_ONLY", \
"HPI_CHANNEL_MODE_RIGHT_ONLY" \
}
#endif /* _HPIDEBUG_H */

View File

@ -0,0 +1,172 @@
/***********************************************************************/
/*!
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
\file
Functions for reading DSP code to load into DSP
(Linux only:) If DSPCODE_FIRMWARE_LOADER is defined, code is read using
hotplug firmware loader from individual dsp code files
If neither of the above is defined, code is read from linked arrays.
DSPCODE_ARRAY is defined.
HPI_INCLUDE_**** must be defined
and the appropriate hzz?????.c or hex?????.c linked in
*/
/***********************************************************************/
#define SOURCEFILE_NAME "hpidspcd.c"
#include "hpidspcd.h"
#include "hpidebug.h"
/**
Header structure for binary dsp code file (see asidsp.doc)
This structure must match that used in s2bin.c for generation of asidsp.bin
*/
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
#endif
struct code_header {
u32 size;
char type[4];
u32 adapter;
u32 version;
u32 crc;
};
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
#define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \
HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER)))
/***********************************************************************/
#include "linux/pci.h"
/*-------------------------------------------------------------------*/
short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code,
u32 *pos_error_code)
{
const struct firmware *ps_firmware = ps_dsp_code->ps_firmware;
struct code_header header;
char fw_name[20];
int err;
sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
HPI_DEBUG_LOG(INFO, "requesting firmware for %s\n", fw_name);
err = request_firmware(&ps_firmware, fw_name,
&ps_dsp_code->ps_dev->dev);
if (err != 0) {
HPI_DEBUG_LOG(ERROR, "%d, request_firmware failed for %s\n",
err, fw_name);
goto error1;
}
if (ps_firmware->size < sizeof(header)) {
HPI_DEBUG_LOG(ERROR, "header size too small %s\n", fw_name);
goto error2;
}
memcpy(&header, ps_firmware->data, sizeof(header));
if (header.adapter != adapter) {
HPI_DEBUG_LOG(ERROR, "adapter type incorrect %4x != %4x\n",
header.adapter, adapter);
goto error2;
}
if (header.size != ps_firmware->size) {
HPI_DEBUG_LOG(ERROR, "code size wrong %d != %ld\n",
header.size, (unsigned long)ps_firmware->size);
goto error2;
}
if (header.version / 10000 != HPI_VER_DECIMAL / 10000) {
HPI_DEBUG_LOG(ERROR,
"firmware major version mismatch "
"DSP image %d != driver %d\n", header.version,
HPI_VER_DECIMAL);
goto error2;
}
if (header.version != HPI_VER_DECIMAL) {
HPI_DEBUG_LOG(WARNING,
"version mismatch DSP image %d != driver %d\n",
header.version, HPI_VER_DECIMAL);
/* goto error2; still allow driver to load */
}
HPI_DEBUG_LOG(INFO, "dsp code %s opened\n", fw_name);
ps_dsp_code->ps_firmware = ps_firmware;
ps_dsp_code->block_length = header.size / sizeof(u32);
ps_dsp_code->word_count = sizeof(header) / sizeof(u32);
ps_dsp_code->version = header.version;
ps_dsp_code->crc = header.crc;
return 0;
error2:
release_firmware(ps_firmware);
error1:
ps_dsp_code->ps_firmware = NULL;
ps_dsp_code->block_length = 0;
return HPI_ERROR_DSP_FILE_NOT_FOUND;
}
/*-------------------------------------------------------------------*/
void hpi_dsp_code_close(struct dsp_code *ps_dsp_code)
{
if (ps_dsp_code->ps_firmware != NULL) {
HPI_DEBUG_LOG(DEBUG, "dsp code closed\n");
release_firmware(ps_dsp_code->ps_firmware);
ps_dsp_code->ps_firmware = NULL;
}
}
/*-------------------------------------------------------------------*/
void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code)
{
/* Go back to start of data, after header */
ps_dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
}
/*-------------------------------------------------------------------*/
short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword)
{
if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length)
return (HPI_ERROR_DSP_FILE_FORMAT);
*pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code->
word_count];
ps_dsp_code->word_count++;
return 0;
}
/*-------------------------------------------------------------------*/
short hpi_dsp_code_read_block(size_t words_requested,
struct dsp_code *ps_dsp_code, u32 **ppblock)
{
if (ps_dsp_code->word_count + words_requested >
ps_dsp_code->block_length)
return HPI_ERROR_DSP_FILE_FORMAT;
*ppblock =
((u32 *)(ps_dsp_code->ps_firmware->data)) +
ps_dsp_code->word_count;
ps_dsp_code->word_count += words_requested;
return 0;
}

View File

@ -0,0 +1,104 @@
/***********************************************************************/
/**
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
\file
Functions for reading DSP code to load into DSP
hpi_dspcode_defines HPI DSP code loading method
Define exactly one of these to select how the DSP code is supplied to
the adapter.
End users writing applications that use the HPI interface do not have to
use any of the below defines; they are only necessary for building drivers
HPI_DSPCODE_FILE:
DSP code is supplied as a file that is opened and read from by the driver.
HPI_DSPCODE_FIRMWARE:
DSP code is read using the hotplug firmware loader module.
Only valid when compiling the HPI kernel driver under Linux.
*/
/***********************************************************************/
#ifndef _HPIDSPCD_H_
#define _HPIDSPCD_H_
#include "hpi_internal.h"
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
#endif
/** Descriptor for dspcode from firmware loader */
struct dsp_code {
/** Firmware descriptor */
const struct firmware *ps_firmware;
struct pci_dev *ps_dev;
/** Expected number of words in the whole dsp code,INCL header */
long int block_length;
/** Number of words read so far */
long int word_count;
/** Version read from dsp code file */
u32 version;
/** CRC read from dsp code file */
u32 crc;
};
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
/** Prepare *psDspCode to refer to the requuested adapter.
Searches the file, or selects the appropriate linked array
\return 0 for success, or error code if requested code is not available
*/
short hpi_dsp_code_open(
/** Code identifier, usually adapter family */
u32 adapter,
/** Pointer to DSP code control structure */
struct dsp_code *ps_dsp_code,
/** Pointer to dword to receive OS specific error code */
u32 *pos_error_code);
/** Close the DSP code file */
void hpi_dsp_code_close(struct dsp_code *ps_dsp_code);
/** Rewind to the beginning of the DSP code file (for verify) */
void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code);
/** Read one word from the dsp code file
\return 0 for success, or error code if eof, or block length exceeded
*/
short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code,
/**< DSP code descriptor */
u32 *pword /**< where to store the read word */
);
/** Get a block of dsp code into an internal buffer, and provide a pointer to
that buffer. (If dsp code is already an array in memory, it is referenced,
not copied.)
\return Error if requested number of words are not available
*/
short hpi_dsp_code_read_block(size_t words_requested,
struct dsp_code *ps_dsp_code,
/* Pointer to store (Pointer to code buffer) */
u32 **ppblock);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,130 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Hardware Programming Interface (HPI) Utility functions.
(C) Copyright AudioScience Inc. 2007
*******************************************************************************/
#include "hpi_internal.h"
#include "hpimsginit.h"
/* The actual message size for each object type */
static u16 msg_size[HPI_OBJ_MAXINDEX + 1] = HPI_MESSAGE_SIZE_BY_OBJECT;
/* The actual response size for each object type */
static u16 res_size[HPI_OBJ_MAXINDEX + 1] = HPI_RESPONSE_SIZE_BY_OBJECT;
/* Flag to enable alternate message type for SSX2 bypass. */
static u16 gwSSX2_bypass;
/** \internal
* Used by ASIO driver to disable SSX2 for a single process
* \param phSubSys Pointer to HPI subsystem handle.
* \param wBypass New bypass setting 0 = off, nonzero = on
* \return Previous bypass setting.
*/
u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass)
{
u16 old_value = gwSSX2_bypass;
gwSSX2_bypass = bypass;
return old_value;
}
/** \internal
* initialize the HPI message structure
*/
static void hpi_init_message(struct hpi_message *phm, u16 object,
u16 function)
{
memset(phm, 0, sizeof(*phm));
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
phm->size = msg_size[object];
else
phm->size = sizeof(*phm);
if (gwSSX2_bypass)
phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE;
else
phm->type = HPI_TYPE_MESSAGE;
phm->object = object;
phm->function = function;
phm->version = 0;
/* Expect adapter index to be set by caller */
}
/** \internal
* initialize the HPI response structure
*/
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error)
{
memset(phr, 0, sizeof(*phr));
phr->type = HPI_TYPE_RESPONSE;
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
phr->size = res_size[object];
else
phr->size = sizeof(*phr);
phr->object = object;
phr->function = function;
phr->error = error;
phr->specific_error = 0;
phr->version = 0;
}
void hpi_init_message_response(struct hpi_message *phm,
struct hpi_response *phr, u16 object, u16 function)
{
hpi_init_message(phm, object, function);
/* default error return if the response is
not filled in by the callee */
hpi_init_response(phr, object, function,
HPI_ERROR_PROCESSING_MESSAGE);
}
static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
u16 object, u16 function)
{
memset(phm, 0, sizeof(*phm));
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
phm->size = size;
phm->type = HPI_TYPE_MESSAGE;
phm->object = object;
phm->function = function;
phm->version = 1;
/* Expect adapter index to be set by caller */
}
}
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
u16 object, u16 function)
{
memset(phr, 0, sizeof(*phr));
phr->size = size;
phr->version = 1;
phr->type = HPI_TYPE_RESPONSE;
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
}
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
struct hpi_response_header *phr, u16 res_size, u16 object,
u16 function)
{
hpi_init_messageV1(phm, msg_size, object, function);
hpi_init_responseV1(phr, res_size, object, function);
}

View File

@ -0,0 +1,40 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Hardware Programming Interface (HPI) Utility functions
(C) Copyright AudioScience Inc. 2007
*******************************************************************************/
/* Initialise response headers, or msg/response pairs.
Note that it is valid to just init a response e.g. when a lower level is preparing
a response to a message.
However, when sending a message, a matching response buffer always must be prepared
*/
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error);
void hpi_init_message_response(struct hpi_message *phm,
struct hpi_response *phr, u16 object, u16 function);
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
u16 object, u16 function);
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
struct hpi_response_header *phr, u16 res_size, u16 object,
u16 function);

View File

@ -0,0 +1,907 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Extended Message Function With Response Cacheing
(C) Copyright AudioScience Inc. 2002
*****************************************************************************/
#define SOURCEFILE_NAME "hpimsgx.c"
#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpimsgx.h"
#include "hpidebug.h"
static struct pci_device_id asihpi_pci_tbl[] = {
#include "hpipcida.h"
};
static struct hpios_spinlock msgx_lock;
static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS];
static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
*pci_info)
{
int i;
for (i = 0; asihpi_pci_tbl[i].vendor != 0; i++) {
if (asihpi_pci_tbl[i].vendor != PCI_ANY_ID
&& asihpi_pci_tbl[i].vendor != pci_info->vendor_id)
continue;
if (asihpi_pci_tbl[i].device != PCI_ANY_ID
&& asihpi_pci_tbl[i].device != pci_info->device_id)
continue;
if (asihpi_pci_tbl[i].subvendor != PCI_ANY_ID
&& asihpi_pci_tbl[i].subvendor !=
pci_info->subsys_vendor_id)
continue;
if (asihpi_pci_tbl[i].subdevice != PCI_ANY_ID
&& asihpi_pci_tbl[i].subdevice !=
pci_info->subsys_device_id)
continue;
HPI_DEBUG_LOG(DEBUG, " %x,%lu\n", i,
asihpi_pci_tbl[i].driver_data);
return (hpi_handler_func *) asihpi_pci_tbl[i].driver_data;
}
return NULL;
}
static inline void hw_entry_point(struct hpi_message *phm,
struct hpi_response *phr)
{
hpi_handler_func *ep;
if (phm->adapter_index < HPI_MAX_ADAPTERS) {
ep = (hpi_handler_func *) hpi_entry_points[phm->
adapter_index];
if (ep) {
HPI_DEBUG_MESSAGE(DEBUG, phm);
ep(phm, phr);
HPI_DEBUG_RESPONSE(phr);
return;
}
}
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_PROCESSING_MESSAGE);
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr);
static void adapter_close(struct hpi_message *phm, struct hpi_response *phr);
static void mixer_open(struct hpi_message *phm, struct hpi_response *phr);
static void mixer_close(struct hpi_message *phm, struct hpi_response *phr);
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void HPIMSGX__reset(u16 adapter_index);
static u16 HPIMSGX__init(struct hpi_message *phm, struct hpi_response *phr);
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner);
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
#endif
struct hpi_subsys_response {
struct hpi_response_header h;
struct hpi_subsys_res s;
};
struct hpi_adapter_response {
struct hpi_response_header h;
struct hpi_adapter_res a;
};
struct hpi_mixer_response {
struct hpi_response_header h;
struct hpi_mixer_res m;
};
struct hpi_stream_response {
struct hpi_response_header h;
struct hpi_stream_res d;
};
struct adapter_info {
u16 type;
u16 num_instreams;
u16 num_outstreams;
};
struct asi_open_state {
int open_flag;
void *h_owner;
};
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
/* Globals */
static struct hpi_adapter_response rESP_HPI_ADAPTER_OPEN[HPI_MAX_ADAPTERS];
static struct hpi_stream_response
rESP_HPI_OSTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static struct hpi_stream_response
rESP_HPI_ISTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static struct hpi_mixer_response rESP_HPI_MIXER_OPEN[HPI_MAX_ADAPTERS];
static struct hpi_subsys_response gRESP_HPI_SUBSYS_FIND_ADAPTERS;
static struct adapter_info aDAPTER_INFO[HPI_MAX_ADAPTERS];
/* use these to keep track of opens from user mode apps/DLLs */
static struct asi_open_state
outstream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static struct asi_open_state
instream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
switch (phm->function) {
case HPI_SUBSYS_GET_VERSION:
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_GET_VERSION, 0);
phr->u.s.version = HPI_VER >> 8; /* return major.minor */
phr->u.s.data = HPI_VER; /* return major.minor.release */
break;
case HPI_SUBSYS_OPEN:
/*do not propagate the message down the chain */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPEN, 0);
break;
case HPI_SUBSYS_CLOSE:
/*do not propagate the message down the chain */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CLOSE,
0);
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
break;
case HPI_SUBSYS_DRIVER_LOAD:
/* Initialize this module's internal state */
hpios_msgxlock_init(&msgx_lock);
memset(&hpi_entry_points, 0, sizeof(hpi_entry_points));
hpios_locked_mem_init();
/* Init subsys_findadapters response to no-adapters */
HPIMSGX__reset(HPIMSGX_ALLADAPTERS);
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_LOAD, 0);
/* individual HPIs dont implement driver load */
HPI_COMMON(phm, phr);
break;
case HPI_SUBSYS_DRIVER_UNLOAD:
HPI_COMMON(phm, phr);
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
hpios_locked_mem_free_all();
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_UNLOAD, 0);
return;
case HPI_SUBSYS_GET_INFO:
HPI_COMMON(phm, phr);
break;
case HPI_SUBSYS_FIND_ADAPTERS:
memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
break;
case HPI_SUBSYS_GET_NUM_ADAPTERS:
memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
phr->function = HPI_SUBSYS_GET_NUM_ADAPTERS;
break;
case HPI_SUBSYS_GET_ADAPTER:
{
int count = phm->adapter_index;
int index = 0;
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_GET_ADAPTER, 0);
/* This is complicated by the fact that we want to
* "skip" 0's in the adapter list.
* First, make sure we are pointing to a
* non-zero adapter type.
*/
while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[index] == 0) {
index++;
if (index >= HPI_MAX_ADAPTERS)
break;
}
while (count) {
/* move on to the next adapter */
index++;
if (index >= HPI_MAX_ADAPTERS)
break;
while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[index] == 0) {
index++;
if (index >= HPI_MAX_ADAPTERS)
break;
}
count--;
}
if (index < HPI_MAX_ADAPTERS) {
phr->u.s.adapter_index = (u16)index;
phr->u.s.aw_adapter_list[0] =
gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[index];
} else {
phr->u.s.adapter_index = 0;
phr->u.s.aw_adapter_list[0] = 0;
phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER;
}
break;
}
case HPI_SUBSYS_CREATE_ADAPTER:
HPIMSGX__init(phm, phr);
break;
case HPI_SUBSYS_DELETE_ADAPTER:
HPIMSGX__cleanup(phm->adapter_index, h_owner);
{
struct hpi_message hm;
struct hpi_response hr;
/* call to HPI_ADAPTER_CLOSE */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_CLOSE);
hm.adapter_index = phm->adapter_index;
hw_entry_point(&hm, &hr);
}
hw_entry_point(phm, phr);
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.
aw_adapter_list[phm->adapter_index]
= 0;
hpi_entry_points[phm->adapter_index] = NULL;
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void adapter_message(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
switch (phm->function) {
case HPI_ADAPTER_OPEN:
adapter_open(phm, phr);
break;
case HPI_ADAPTER_CLOSE:
adapter_close(phm, phr);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void mixer_message(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_MIXER_OPEN:
mixer_open(phm, phr);
break;
case HPI_MIXER_CLOSE:
mixer_close(phm, phr);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void outstream_message(struct hpi_message *phm,
struct hpi_response *phr, void *h_owner)
{
if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_outstreams) {
hpi_init_response(phr, HPI_OBJ_OSTREAM, phm->function,
HPI_ERROR_INVALID_OBJ_INDEX);
return;
}
switch (phm->function) {
case HPI_OSTREAM_OPEN:
outstream_open(phm, phr, h_owner);
break;
case HPI_OSTREAM_CLOSE:
outstream_close(phm, phr, h_owner);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void instream_message(struct hpi_message *phm,
struct hpi_response *phr, void *h_owner)
{
if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_instreams) {
hpi_init_response(phr, HPI_OBJ_ISTREAM, phm->function,
HPI_ERROR_INVALID_OBJ_INDEX);
return;
}
switch (phm->function) {
case HPI_ISTREAM_OPEN:
instream_open(phm, phr, h_owner);
break;
case HPI_ISTREAM_CLOSE:
instream_close(phm, phr, h_owner);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
/* NOTE: HPI_Message() must be defined in the driver as a wrapper for
* HPI_MessageEx so that functions in hpifunc.c compile.
*/
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
HPI_DEBUG_MESSAGE(DEBUG, phm);
if (phm->type != HPI_TYPE_MESSAGE) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_INVALID_TYPE);
return;
}
if (phm->adapter_index >= HPI_MAX_ADAPTERS
&& phm->adapter_index != HPIMSGX_ALLADAPTERS) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_BAD_ADAPTER_NUMBER);
return;
}
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr, h_owner);
break;
case HPI_OBJ_ADAPTER:
adapter_message(phm, phr, h_owner);
break;
case HPI_OBJ_MIXER:
mixer_message(phm, phr);
break;
case HPI_OBJ_OSTREAM:
outstream_message(phm, phr, h_owner);
break;
case HPI_OBJ_ISTREAM:
instream_message(phm, phr, h_owner);
break;
default:
hw_entry_point(phm, phr);
break;
}
HPI_DEBUG_RESPONSE(phr);
#if 1
if (phr->error >= HPI_ERROR_BACKEND_BASE) {
void *ep = NULL;
char *ep_name;
HPI_DEBUG_MESSAGE(ERROR, phm);
if (phm->adapter_index < HPI_MAX_ADAPTERS)
ep = hpi_entry_points[phm->adapter_index];
/* Don't need this? Have adapter index in debug info
Know at driver load time index->backend mapping */
if (ep == HPI_6000)
ep_name = "HPI_6000";
else if (ep == HPI_6205)
ep_name = "HPI_6205";
else
ep_name = "unknown";
HPI_DEBUG_LOG(ERROR, "HPI %s response - error# %d\n", ep_name,
phr->error);
if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)
hpi_debug_data((u16 *)phm,
sizeof(*phm) / sizeof(u16));
}
#endif
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr)
{
HPI_DEBUG_LOG(VERBOSE, "adapter_open\n");
memcpy(phr, &rESP_HPI_ADAPTER_OPEN[phm->adapter_index],
sizeof(rESP_HPI_ADAPTER_OPEN[0]));
}
static void adapter_close(struct hpi_message *phm, struct hpi_response *phr)
{
HPI_DEBUG_LOG(VERBOSE, "adapter_close\n");
hpi_init_response(phr, HPI_OBJ_ADAPTER, HPI_ADAPTER_CLOSE, 0);
}
static void mixer_open(struct hpi_message *phm, struct hpi_response *phr)
{
memcpy(phr, &rESP_HPI_MIXER_OPEN[phm->adapter_index],
sizeof(rESP_HPI_MIXER_OPEN[0]));
}
static void mixer_close(struct hpi_message *phm, struct hpi_response *phr)
{
hpi_init_response(phr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE, 0);
}
static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_OPEN, 0);
hpios_msgxlock_lock(&msgx_lock);
if (instream_user_open[phm->adapter_index][phm->obj_index].open_flag)
phr->error = HPI_ERROR_OBJ_ALREADY_OPEN;
else if (rESP_HPI_ISTREAM_OPEN[phm->adapter_index]
[phm->obj_index].h.error)
memcpy(phr,
&rESP_HPI_ISTREAM_OPEN[phm->adapter_index][phm->
obj_index],
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
hpios_msgxlock_un_lock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
phr->error = hr.error;
} else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
memcpy(phr,
&rESP_HPI_ISTREAM_OPEN[phm->adapter_index]
[phm->obj_index],
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
}
}
hpios_msgxlock_un_lock(&msgx_lock);
}
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_CLOSE, 0);
hpios_msgxlock_lock(&msgx_lock);
if (h_owner ==
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner) {
/* HPI_DEBUG_LOG(INFO,"closing adapter %d "
"instream %d owned by %p\n",
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
hpios_msgxlock_un_lock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
phr->error = hr.error;
} else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
}
} else {
HPI_DEBUG_LOG(WARNING,
"%p trying to close %d instream %d owned by %p\n",
h_owner, phm->adapter_index, phm->obj_index,
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
hpios_msgxlock_un_lock(&msgx_lock);
}
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_OPEN, 0);
hpios_msgxlock_lock(&msgx_lock);
if (outstream_user_open[phm->adapter_index][phm->obj_index].open_flag)
phr->error = HPI_ERROR_OBJ_ALREADY_OPEN;
else if (rESP_HPI_OSTREAM_OPEN[phm->adapter_index]
[phm->obj_index].h.error)
memcpy(phr,
&rESP_HPI_OSTREAM_OPEN[phm->adapter_index][phm->
obj_index],
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
hpios_msgxlock_un_lock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
phr->error = hr.error;
} else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
memcpy(phr,
&rESP_HPI_OSTREAM_OPEN[phm->adapter_index]
[phm->obj_index],
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
}
}
hpios_msgxlock_un_lock(&msgx_lock);
}
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_CLOSE, 0);
hpios_msgxlock_lock(&msgx_lock);
if (h_owner ==
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner) {
/* HPI_DEBUG_LOG(INFO,"closing adapter %d "
"outstream %d owned by %p\n",
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
hpios_msgxlock_un_lock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
phr->error = hr.error;
} else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
}
} else {
HPI_DEBUG_LOG(WARNING,
"%p trying to close %d outstream %d owned by %p\n",
h_owner, phm->adapter_index, phm->obj_index,
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
hpios_msgxlock_un_lock(&msgx_lock);
}
static u16 adapter_prepare(u16 adapter)
{
struct hpi_message hm;
struct hpi_response hr;
/* Open the adapter and streams */
u16 i;
/* call to HPI_ADAPTER_OPEN */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN);
hm.adapter_index = adapter;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr,
sizeof(rESP_HPI_ADAPTER_OPEN[0]));
if (hr.error)
return hr.error;
/* call to HPI_ADAPTER_GET_INFO */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_GET_INFO);
hm.adapter_index = adapter;
hw_entry_point(&hm, &hr);
if (hr.error)
return hr.error;
aDAPTER_INFO[adapter].num_outstreams = hr.u.a.num_outstreams;
aDAPTER_INFO[adapter].num_instreams = hr.u.a.num_instreams;
aDAPTER_INFO[adapter].type = hr.u.a.adapter_type;
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list[adapter] =
hr.u.a.adapter_type;
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters++;
if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters > HPI_MAX_ADAPTERS)
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters =
HPI_MAX_ADAPTERS;
/* call to HPI_OSTREAM_OPEN */
for (i = 0; i < aDAPTER_INFO[adapter].num_outstreams; i++) {
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_OPEN);
hm.adapter_index = adapter;
hm.obj_index = i;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i], &hr,
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
outstream_user_open[adapter][i].open_flag = 0;
outstream_user_open[adapter][i].h_owner = NULL;
}
/* call to HPI_ISTREAM_OPEN */
for (i = 0; i < aDAPTER_INFO[adapter].num_instreams; i++) {
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_OPEN);
hm.adapter_index = adapter;
hm.obj_index = i;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i], &hr,
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
instream_user_open[adapter][i].open_flag = 0;
instream_user_open[adapter][i].h_owner = NULL;
}
/* call to HPI_MIXER_OPEN */
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN);
hm.adapter_index = adapter;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
sizeof(rESP_HPI_MIXER_OPEN[0]));
return gRESP_HPI_SUBSYS_FIND_ADAPTERS.h.error;
}
static void HPIMSGX__reset(u16 adapter_index)
{
int i;
u16 adapter;
struct hpi_response hr;
if (adapter_index == HPIMSGX_ALLADAPTERS) {
/* reset all responses to contain errors */
hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_FIND_ADAPTERS, 0);
memcpy(&gRESP_HPI_SUBSYS_FIND_ADAPTERS, &hr,
sizeof(&gRESP_HPI_SUBSYS_FIND_ADAPTERS));
for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) {
hpi_init_response(&hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN, HPI_ERROR_BAD_ADAPTER);
memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr,
sizeof(rESP_HPI_ADAPTER_OPEN[adapter]));
hpi_init_response(&hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN,
HPI_ERROR_INVALID_OBJ);
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
sizeof(rESP_HPI_MIXER_OPEN[adapter]));
for (i = 0; i < HPI_MAX_STREAMS; i++) {
hpi_init_response(&hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_OPEN,
HPI_ERROR_INVALID_OBJ);
memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i],
&hr,
sizeof(rESP_HPI_OSTREAM_OPEN[adapter]
[i]));
hpi_init_response(&hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_OPEN,
HPI_ERROR_INVALID_OBJ);
memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i],
&hr,
sizeof(rESP_HPI_ISTREAM_OPEN[adapter]
[i]));
}
}
} else if (adapter_index < HPI_MAX_ADAPTERS) {
rESP_HPI_ADAPTER_OPEN[adapter_index].h.error =
HPI_ERROR_BAD_ADAPTER;
rESP_HPI_MIXER_OPEN[adapter_index].h.error =
HPI_ERROR_INVALID_OBJ;
for (i = 0; i < HPI_MAX_STREAMS; i++) {
rESP_HPI_OSTREAM_OPEN[adapter_index][i].h.error =
HPI_ERROR_INVALID_OBJ;
rESP_HPI_ISTREAM_OPEN[adapter_index][i].h.error =
HPI_ERROR_INVALID_OBJ;
}
if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[adapter_index]) {
gRESP_HPI_SUBSYS_FIND_ADAPTERS.
s.aw_adapter_list[adapter_index] = 0;
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters--;
}
}
}
static u16 HPIMSGX__init(struct hpi_message *phm,
/* HPI_SUBSYS_CREATE_ADAPTER structure with */
/* resource list or NULL=find all */
struct hpi_response *phr
/* response from HPI_ADAPTER_GET_INFO */
)
{
hpi_handler_func *entry_point_func;
struct hpi_response hr;
if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters >= HPI_MAX_ADAPTERS)
return HPI_ERROR_BAD_ADAPTER_NUMBER;
/* Init response here so we can pass in previous adapter list */
hpi_init_response(&hr, phm->object, phm->function,
HPI_ERROR_INVALID_OBJ);
memcpy(hr.u.s.aw_adapter_list,
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list,
sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list));
entry_point_func =
hpi_lookup_entry_point_function(phm->u.s.resource.r.pci);
if (entry_point_func) {
HPI_DEBUG_MESSAGE(DEBUG, phm);
entry_point_func(phm, &hr);
} else {
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
return phr->error;
}
if (hr.error == 0) {
/* the adapter was created succesfully
save the mapping for future use */
hpi_entry_points[hr.u.s.adapter_index] = entry_point_func;
/* prepare adapter (pre-open streams etc.) */
HPI_DEBUG_LOG(DEBUG,
"HPI_SUBSYS_CREATE_ADAPTER successful,"
" preparing adapter\n");
adapter_prepare(hr.u.s.adapter_index);
}
memcpy(phr, &hr, hr.size);
return phr->error;
}
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
{
int i, adapter, adapter_limit;
if (!h_owner)
return;
if (adapter_index == HPIMSGX_ALLADAPTERS) {
adapter = 0;
adapter_limit = HPI_MAX_ADAPTERS;
} else {
adapter = adapter_index;
adapter_limit = adapter + 1;
}
for (; adapter < adapter_limit; adapter++) {
/* printk(KERN_INFO "Cleanup adapter #%d\n",wAdapter); */
for (i = 0; i < HPI_MAX_STREAMS; i++) {
if (h_owner ==
outstream_user_open[adapter][i].h_owner) {
struct hpi_message hm;
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
"close adapter %d ostream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
HPI_OBJ_OSTREAM, HPI_OSTREAM_RESET);
hm.adapter_index = (u16)adapter;
hm.obj_index = (u16)i;
hw_entry_point(&hm, &hr);
hm.function = HPI_OSTREAM_HOSTBUFFER_FREE;
hw_entry_point(&hm, &hr);
hm.function = HPI_OSTREAM_GROUP_RESET;
hw_entry_point(&hm, &hr);
outstream_user_open[adapter][i].open_flag = 0;
outstream_user_open[adapter][i].h_owner =
NULL;
}
if (h_owner == instream_user_open[adapter][i].h_owner) {
struct hpi_message hm;
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
"close adapter %d istream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
HPI_OBJ_ISTREAM, HPI_ISTREAM_RESET);
hm.adapter_index = (u16)adapter;
hm.obj_index = (u16)i;
hw_entry_point(&hm, &hr);
hm.function = HPI_ISTREAM_HOSTBUFFER_FREE;
hw_entry_point(&hm, &hr);
hm.function = HPI_ISTREAM_GROUP_RESET;
hw_entry_point(&hm, &hr);
instream_user_open[adapter][i].open_flag = 0;
instream_user_open[adapter][i].h_owner = NULL;
}
}
}
}

View File

@ -0,0 +1,36 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
HPI Extended Message Handler Functions
(C) Copyright AudioScience Inc. 1997-2003
******************************************************************************/
#ifndef _HPIMSGX_H_
#define _HPIMSGX_H_
#include "hpi_internal.h"
#define HPIMSGX_ALLADAPTERS (0xFFFF)
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
#define HPI_MESSAGE_LOWER_LAYER hpi_send_recv_ex
#endif /* _HPIMSGX_H_ */

View File

@ -0,0 +1,484 @@
/*******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Common Linux HPI ioctl and module probe/remove functions
*******************************************************************************/
#define SOURCEFILE_NAME "hpioctl.c"
#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpidebug.h"
#include "hpimsgx.h"
#include "hpioctl.h"
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <asm/uaccess.h>
#include <linux/stringify.h>
#ifdef MODULE_FIRMWARE
MODULE_FIRMWARE("asihpi/dsp5000.bin");
MODULE_FIRMWARE("asihpi/dsp6200.bin");
MODULE_FIRMWARE("asihpi/dsp6205.bin");
MODULE_FIRMWARE("asihpi/dsp6400.bin");
MODULE_FIRMWARE("asihpi/dsp6600.bin");
MODULE_FIRMWARE("asihpi/dsp8700.bin");
MODULE_FIRMWARE("asihpi/dsp8900.bin");
#endif
static int prealloc_stream_buf;
module_param(prealloc_stream_buf, int, S_IRUGO);
MODULE_PARM_DESC(prealloc_stream_buf,
"preallocate size for per-adapter stream buffer");
/* Allow the debug level to be changed after module load.
E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
*/
module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5");
/* List of adapters found */
static struct hpi_adapter adapters[HPI_MAX_ADAPTERS];
/* Wrapper function to HPI_Message to enable dumping of the
message and response types.
*/
static void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr,
struct file *file)
{
int adapter = phm->adapter_index;
if ((adapter >= HPI_MAX_ADAPTERS || adapter < 0)
&& (phm->object != HPI_OBJ_SUBSYSTEM))
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
else
hpi_send_recv_ex(phm, phr, file);
}
/* This is called from hpifunc.c functions, called by ALSA
* (or other kernel process) In this case there is no file descriptor
* available for the message cache code
*/
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr)
{
hpi_send_recv_f(phm, phr, HOWNER_KERNEL);
}
EXPORT_SYMBOL(hpi_send_recv);
/* for radio-asihpi */
int asihpi_hpi_release(struct file *file)
{
struct hpi_message hm;
struct hpi_response hr;
/* HPI_DEBUG_LOG(INFO,"hpi_release file %p, pid %d\n", file, current->pid); */
/* close the subsystem just in case the application forgot to. */
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_CLOSE);
hpi_send_recv_ex(&hm, &hr, file);
return 0;
}
long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct hpi_ioctl_linux __user *phpi_ioctl_data;
void __user *puhm;
void __user *puhr;
union hpi_message_buffer_v1 *hm;
union hpi_response_buffer_v1 *hr;
u16 res_max_size;
u32 uncopied_bytes;
struct hpi_adapter *pa = NULL;
int err = 0;
if (cmd != HPI_IOCTL_LINUX)
return -EINVAL;
hm = kmalloc(sizeof(*hm), GFP_KERNEL);
hr = kmalloc(sizeof(*hr), GFP_KERNEL);
if (!hm || !hr) {
err = -ENOMEM;
goto out;
}
phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg;
/* Read the message and response pointers from user space. */
get_user(puhm, &phpi_ioctl_data->phm);
get_user(puhr, &phpi_ioctl_data->phr);
/* Now read the message size and data from user space. */
get_user(hm->h.size, (u16 __user *)puhm);
if (hm->h.size > sizeof(*hm))
hm->h.size = sizeof(*hm);
/*printk(KERN_INFO "message size %d\n", hm->h.wSize); */
uncopied_bytes = copy_from_user(hm, puhm, hm->h.size);
if (uncopied_bytes) {
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
err = -EFAULT;
goto out;
}
get_user(res_max_size, (u16 __user *)puhr);
/* printk(KERN_INFO "user response size %d\n", res_max_size); */
if (res_max_size < sizeof(struct hpi_response_header)) {
HPI_DEBUG_LOG(WARNING, "small res size %d\n", res_max_size);
err = -EFAULT;
goto out;
}
pa = &adapters[hm->h.adapter_index];
hr->h.size = 0;
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
switch (hm->h.function) {
case HPI_SUBSYS_CREATE_ADAPTER:
case HPI_SUBSYS_DELETE_ADAPTER:
/* Application must not use these functions! */
hr->h.size = sizeof(hr->h);
hr->h.error = HPI_ERROR_INVALID_OPERATION;
hr->h.function = hm->h.function;
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
if (uncopied_bytes)
err = -EFAULT;
else
err = 0;
goto out;
default:
hpi_send_recv_f(&hm->m0, &hr->r0, file);
}
} else {
u16 __user *ptr = NULL;
u32 size = 0;
/* -1=no data 0=read from user mem, 1=write to user mem */
int wrflag = -1;
u32 adapter = hm->h.adapter_index;
if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) {
hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN,
HPI_ERROR_BAD_ADAPTER_NUMBER);
uncopied_bytes =
copy_to_user(puhr, hr, sizeof(hr->h));
if (uncopied_bytes)
err = -EFAULT;
else
err = 0;
goto out;
}
if (mutex_lock_interruptible(&adapters[adapter].mutex)) {
err = -EINTR;
goto out;
}
/* Dig out any pointers embedded in the message. */
switch (hm->h.function) {
case HPI_OSTREAM_WRITE:
case HPI_ISTREAM_READ:{
/* Yes, sparse, this is correct. */
ptr = (u16 __user *)hm->m0.u.d.u.data.pb_data;
size = hm->m0.u.d.u.data.data_size;
/* Allocate buffer according to application request.
?Is it better to alloc/free for the duration
of the transaction?
*/
if (pa->buffer_size < size) {
HPI_DEBUG_LOG(DEBUG,
"realloc adapter %d stream "
"buffer from %zd to %d\n",
hm->h.adapter_index,
pa->buffer_size, size);
if (pa->p_buffer) {
pa->buffer_size = 0;
vfree(pa->p_buffer);
}
pa->p_buffer = vmalloc(size);
if (pa->p_buffer)
pa->buffer_size = size;
else {
HPI_DEBUG_LOG(ERROR,
"HPI could not allocate "
"stream buffer size %d\n",
size);
mutex_unlock(&adapters
[adapter].mutex);
err = -EINVAL;
goto out;
}
}
hm->m0.u.d.u.data.pb_data = pa->p_buffer;
if (hm->h.function == HPI_ISTREAM_READ)
/* from card, WRITE to user mem */
wrflag = 1;
else
wrflag = 0;
break;
}
default:
size = 0;
break;
}
if (size && (wrflag == 0)) {
uncopied_bytes =
copy_from_user(pa->p_buffer, ptr, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
"missed %d of %d "
"bytes from user\n", uncopied_bytes,
size);
}
hpi_send_recv_f(&hm->m0, &hr->r0, file);
if (size && (wrflag == 1)) {
uncopied_bytes =
copy_to_user(ptr, pa->p_buffer, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
"missed %d of %d " "bytes to user\n",
uncopied_bytes, size);
}
mutex_unlock(&adapters[adapter].mutex);
}
/* on return response size must be set */
/*printk(KERN_INFO "response size %d\n", hr->h.wSize); */
if (!hr->h.size) {
HPI_DEBUG_LOG(ERROR, "response zero size\n");
err = -EFAULT;
goto out;
}
if (hr->h.size > res_max_size) {
HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
res_max_size);
/*HPI_DEBUG_MESSAGE(ERROR, hm); */
err = -EFAULT;
goto out;
}
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
if (uncopied_bytes) {
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
err = -EFAULT;
goto out;
}
out:
kfree(hm);
kfree(hr);
return err;
}
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
int err, idx, nm;
unsigned int memlen;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter adapter;
struct hpi_pci pci;
memset(&adapter, 0, sizeof(adapter));
printk(KERN_DEBUG "probe PCI device (%04x:%04x,%04x:%04x,%04x)\n",
pci_dev->vendor, pci_dev->device, pci_dev->subsystem_vendor,
pci_dev->subsystem_device, pci_dev->devfn);
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_CREATE_ADAPTER);
hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER,
HPI_ERROR_PROCESSING_MESSAGE);
hm.adapter_index = -1; /* an invalid index */
/* fill in HPI_PCI information from kernel provided information */
adapter.pci = pci_dev;
nm = HPI_MAX_ADAPTER_MEM_SPACES;
for (idx = 0; idx < nm; idx++) {
HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
idx, pci_dev->resource[idx].name,
(unsigned long long)pci_resource_start(pci_dev, idx),
(unsigned long long)pci_resource_end(pci_dev, idx),
(unsigned long long)pci_resource_flags(pci_dev, idx));
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
memlen = pci_resource_len(pci_dev, idx);
adapter.ap_remapped_mem_base[idx] =
ioremap(pci_resource_start(pci_dev, idx),
memlen);
if (!adapter.ap_remapped_mem_base[idx]) {
HPI_DEBUG_LOG(ERROR,
"ioremap failed, aborting\n");
/* unmap previously mapped pci mem space */
goto err;
}
}
pci.ap_mem_base[idx] = adapter.ap_remapped_mem_base[idx];
}
/* could replace Pci with direct pointer to pci_dev for linux
Instead wrap accessor functions for IDs etc.
Would it work for windows?
*/
pci.bus_number = pci_dev->bus->number;
pci.vendor_id = (u16)pci_dev->vendor;
pci.device_id = (u16)pci_dev->device;
pci.subsys_vendor_id = (u16)(pci_dev->subsystem_vendor & 0xffff);
pci.subsys_device_id = (u16)(pci_dev->subsystem_device & 0xffff);
pci.device_number = pci_dev->devfn;
pci.interrupt = pci_dev->irq;
pci.p_os_data = pci_dev;
hm.u.s.resource.bus_type = HPI_BUS_PCI;
hm.u.s.resource.r.pci = &pci;
/* call CreateAdapterObject on the relevant hpi module */
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
if (hr.error)
goto err;
if (prealloc_stream_buf) {
adapter.p_buffer = vmalloc(prealloc_stream_buf);
if (!adapter.p_buffer) {
HPI_DEBUG_LOG(ERROR,
"HPI could not allocate "
"kernel buffer size %d\n",
prealloc_stream_buf);
goto err;
}
}
adapter.index = hr.u.s.adapter_index;
adapter.type = hr.u.s.aw_adapter_list[adapter.index];
hm.adapter_index = adapter.index;
err = hpi_adapter_open(NULL, adapter.index);
if (err)
goto err;
adapter.snd_card_asihpi = NULL;
/* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?!
*/
adapters[hr.u.s.adapter_index] = adapter;
mutex_init(&adapters[adapter.index].mutex);
pci_set_drvdata(pci_dev, &adapters[adapter.index]);
printk(KERN_INFO "probe found adapter ASI%04X HPI index #%d.\n",
adapter.type, adapter.index);
return 0;
err:
for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
if (adapter.ap_remapped_mem_base[idx]) {
iounmap(adapter.ap_remapped_mem_base[idx]);
adapter.ap_remapped_mem_base[idx] = NULL;
}
}
if (adapter.p_buffer) {
adapter.buffer_size = 0;
vfree(adapter.p_buffer);
}
HPI_DEBUG_LOG(ERROR, "adapter_probe failed\n");
return -ENODEV;
}
void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
{
int idx;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter *pa;
pa = (struct hpi_adapter *)pci_get_drvdata(pci_dev);
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DELETE_ADAPTER);
hm.adapter_index = pa->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
/* unmap PCI memory space, mapped during device init. */
for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
if (pa->ap_remapped_mem_base[idx]) {
iounmap(pa->ap_remapped_mem_base[idx]);
pa->ap_remapped_mem_base[idx] = NULL;
}
}
if (pa->p_buffer) {
pa->buffer_size = 0;
vfree(pa->p_buffer);
}
pci_set_drvdata(pci_dev, NULL);
/*
printk(KERN_INFO "PCI device (%04x:%04x,%04x:%04x,%04x),"
" HPI index # %d, removed.\n",
pci_dev->vendor, pci_dev->device,
pci_dev->subsystem_vendor,
pci_dev->subsystem_device, pci_dev->devfn,
pa->index);
*/
}
void __init asihpi_init(void)
{
struct hpi_message hm;
struct hpi_response hr;
memset(adapters, 0, sizeof(adapters));
printk(KERN_INFO "ASIHPI driver %d.%02d.%02d\n",
HPI_VER_MAJOR(HPI_VER), HPI_VER_MINOR(HPI_VER),
HPI_VER_RELEASE(HPI_VER));
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_LOAD);
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
}
void asihpi_exit(void)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_UNLOAD);
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
}

View File

@ -0,0 +1,38 @@
/*******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Linux HPI ioctl, and shared module init functions
*******************************************************************************/
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id);
void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev);
void __init asihpi_init(void);
void __exit asihpi_exit(void);
int asihpi_hpi_release(struct file *file);
long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/* This is called from hpifunc.c functions, called by ALSA
* (or other kernel process) In this case there is no file descriptor
* available for the message cache code
*/
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
#define HOWNER_KERNEL ((void *)-1)

View File

@ -0,0 +1,114 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
HPI Operating System function implementation for Linux
(C) Copyright AudioScience Inc. 1997-2003
******************************************************************************/
#define SOURCEFILE_NAME "hpios.c"
#include "hpi_internal.h"
#include "hpidebug.h"
#include <linux/delay.h>
#include <linux/sched.h>
void hpios_delay_micro_seconds(u32 num_micro_sec)
{
if ((usecs_to_jiffies(num_micro_sec) > 1) && !in_interrupt()) {
/* MUST NOT SCHEDULE IN INTERRUPT CONTEXT! */
schedule_timeout_uninterruptible(usecs_to_jiffies
(num_micro_sec));
} else if (num_micro_sec <= 2000)
udelay(num_micro_sec);
else
mdelay(num_micro_sec / 1000);
}
void hpios_locked_mem_init(void)
{
}
/** Allocated an area of locked memory for bus master DMA operations.
On error, return -ENOMEM, and *pMemArea.size = 0
*/
u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
struct pci_dev *pdev)
{
/*?? any benefit in using managed dmam_alloc_coherent? */
p_mem_area->vaddr =
dma_alloc_coherent(&pdev->dev, size, &p_mem_area->dma_handle,
GFP_DMA32 | GFP_KERNEL);
if (p_mem_area->vaddr) {
HPI_DEBUG_LOG(DEBUG, "allocated %d bytes, dma 0x%x vma %p\n",
size, (unsigned int)p_mem_area->dma_handle,
p_mem_area->vaddr);
p_mem_area->pdev = &pdev->dev;
p_mem_area->size = size;
return 0;
} else {
HPI_DEBUG_LOG(WARNING,
"failed to allocate %d bytes locked memory\n", size);
p_mem_area->size = 0;
return -ENOMEM;
}
}
u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area)
{
if (p_mem_area->size) {
dma_free_coherent(p_mem_area->pdev, p_mem_area->size,
p_mem_area->vaddr, p_mem_area->dma_handle);
HPI_DEBUG_LOG(DEBUG, "freed %lu bytes, dma 0x%x vma %p\n",
(unsigned long)p_mem_area->size,
(unsigned int)p_mem_area->dma_handle,
p_mem_area->vaddr);
p_mem_area->size = 0;
return 0;
} else {
return 1;
}
}
void hpios_locked_mem_free_all(void)
{
}
void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx,
unsigned int length)
{
HPI_DEBUG_LOG(DEBUG, "mapping %d %s %08llx-%08llx %04llx len 0x%x\n",
idx, pci_dev->resource[idx].name,
(unsigned long long)pci_resource_start(pci_dev, idx),
(unsigned long long)pci_resource_end(pci_dev, idx),
(unsigned long long)pci_resource_flags(pci_dev, idx), length);
if (!(pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM)) {
HPI_DEBUG_LOG(ERROR, "not an io memory resource\n");
return NULL;
}
if (length > pci_resource_len(pci_dev, idx)) {
HPI_DEBUG_LOG(ERROR, "resource too small for requested %d \n",
length);
return NULL;
}
return ioremap(pci_resource_start(pci_dev, idx), length);
}

View File

@ -0,0 +1,178 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
HPI Operating System Specific macros for Linux Kernel driver
(C) Copyright AudioScience Inc. 1997-2003
******************************************************************************/
#ifndef _HPIOS_H_
#define _HPIOS_H_
#undef HPI_OS_LINUX_KERNEL
#define HPI_OS_LINUX_KERNEL
#define HPI_OS_DEFINED
#define HPI_KERNEL_MODE
#define HPI_REASSIGN_DUPLICATE_ADAPTER_IDX
#include <linux/io.h>
#include <asm/system.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#define HPI_NO_OS_FILE_OPS
#ifdef CONFIG_64BIT
#define HPI64BIT
#endif
/** Details of a memory area allocated with pci_alloc_consistent
Need all info for parameters to pci_free_consistent
*/
struct consistent_dma_area {
struct device *pdev;
/* looks like dma-mapping dma_devres ?! */
size_t size;
void *vaddr;
dma_addr_t dma_handle;
};
static inline u16 hpios_locked_mem_get_phys_addr(struct consistent_dma_area
*locked_mem_handle, u32 *p_physical_addr)
{
*p_physical_addr = locked_mem_handle->dma_handle;
return 0;
}
static inline u16 hpios_locked_mem_get_virt_addr(struct consistent_dma_area
*locked_mem_handle, void **pp_virtual_addr)
{
*pp_virtual_addr = locked_mem_handle->vaddr;
return 0;
}
static inline u16 hpios_locked_mem_valid(struct consistent_dma_area
*locked_mem_handle)
{
return locked_mem_handle->size != 0;
}
struct hpi_ioctl_linux {
void __user *phm;
void __user *phr;
};
/* Conflict?: H is already used by a number of drivers hid, bluetooth hci,
and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is ununsed command
*/
#define HPI_IOCTL_LINUX _IOWR('H', 0xFC, struct hpi_ioctl_linux)
#define HPI_DEBUG_FLAG_ERROR KERN_ERR
#define HPI_DEBUG_FLAG_WARNING KERN_WARNING
#define HPI_DEBUG_FLAG_NOTICE KERN_NOTICE
#define HPI_DEBUG_FLAG_INFO KERN_INFO
#define HPI_DEBUG_FLAG_DEBUG KERN_DEBUG
#define HPI_DEBUG_FLAG_VERBOSE KERN_DEBUG /* kernel has no verbose */
#include <linux/spinlock.h>
#define HPI_LOCKING
struct hpios_spinlock {
spinlock_t lock; /* SEE hpios_spinlock */
int lock_context;
};
/* The reason for all this evilness is that ALSA calls some of a drivers
* operators in atomic context, and some not. But all our functions channel
* through the HPI_Message conduit, so we can't handle the different context
* per function
*/
#define IN_LOCK_BH 1
#define IN_LOCK_IRQ 0
static inline void cond_lock(struct hpios_spinlock *l)
{
if (irqs_disabled()) {
/* NO bh or isr can execute on this processor,
so ordinary lock will do
*/
spin_lock(&((l)->lock));
l->lock_context = IN_LOCK_IRQ;
} else {
spin_lock_bh(&((l)->lock));
l->lock_context = IN_LOCK_BH;
}
}
static inline void cond_unlock(struct hpios_spinlock *l)
{
if (l->lock_context == IN_LOCK_BH)
spin_unlock_bh(&((l)->lock));
else
spin_unlock(&((l)->lock));
}
#define hpios_msgxlock_init(obj) spin_lock_init(&(obj)->lock)
#define hpios_msgxlock_lock(obj) cond_lock(obj)
#define hpios_msgxlock_un_lock(obj) cond_unlock(obj)
#define hpios_dsplock_init(obj) spin_lock_init(&(obj)->dsp_lock.lock)
#define hpios_dsplock_lock(obj) cond_lock(&(obj)->dsp_lock)
#define hpios_dsplock_unlock(obj) cond_unlock(&(obj)->dsp_lock)
#ifdef CONFIG_SND_DEBUG
#define HPI_DEBUG
#endif
#define HPI_ALIST_LOCKING
#define hpios_alistlock_init(obj) spin_lock_init(&((obj)->list_lock.lock))
#define hpios_alistlock_lock(obj) spin_lock(&((obj)->list_lock.lock))
#define hpios_alistlock_un_lock(obj) spin_unlock(&((obj)->list_lock.lock))
struct hpi_adapter {
/* mutex prevents contention for one card
between multiple user programs (via ioctl) */
struct mutex mutex;
u16 index;
u16 type;
/* ALSA card structure */
void *snd_card_asihpi;
char *p_buffer;
size_t buffer_size;
struct pci_dev *pci;
void __iomem *ap_remapped_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
};
static inline void hpios_unmap_io(void __iomem *addr,
unsigned long size)
{
iounmap(addr);
}
void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx,
unsigned int length);
#endif

View File

@ -0,0 +1,37 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation;
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
Array initializer for PCI card IDs
(C) Copyright AudioScience Inc. 1998-2003
*******************************************************************************/
/*NOTE: when adding new lines to this header file
they MUST be grouped by HPI entry point.
*/
{
HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t) HPI_6205}
, {
HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t) HPI_6000}
, {
0}

View File

@ -104,6 +104,7 @@
#include <linux/gameport.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/input.h>
#include <sound/core.h>
#include <sound/pcm.h>
@ -517,14 +518,9 @@ struct es1968 {
/* ALSA Stuff */
struct snd_ac97 *ac97;
struct snd_kcontrol *master_switch; /* for h/w volume control */
struct snd_kcontrol *master_volume;
struct snd_rawmidi *rmidi;
spinlock_t reg_lock;
spinlock_t ac97_lock;
struct tasklet_struct hwvol_tq;
unsigned int in_suspend;
/* Maestro Stuff */
@ -547,6 +543,16 @@ struct es1968 {
#ifdef SUPPORT_JOYSTICK
struct gameport *gameport;
#endif
#ifdef CONFIG_SND_ES1968_INPUT
struct input_dev *input_dev;
char phys[64]; /* physical device path */
#else
struct snd_kcontrol *master_switch; /* for h/w volume control */
struct snd_kcontrol *master_volume;
spinlock_t ac97_lock;
struct tasklet_struct hwvol_tq;
#endif
};
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
@ -632,28 +638,38 @@ static int snd_es1968_ac97_wait_poll(struct es1968 *chip)
static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct es1968 *chip = ac97->private_data;
#ifndef CONFIG_SND_ES1968_INPUT
unsigned long flags;
#endif
snd_es1968_ac97_wait(chip);
/* Write the bus */
#ifndef CONFIG_SND_ES1968_INPUT
spin_lock_irqsave(&chip->ac97_lock, flags);
#endif
outw(val, chip->io_port + ESM_AC97_DATA);
/*msleep(1);*/
outb(reg, chip->io_port + ESM_AC97_INDEX);
/*msleep(1);*/
#ifndef CONFIG_SND_ES1968_INPUT
spin_unlock_irqrestore(&chip->ac97_lock, flags);
#endif
}
static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
u16 data = 0;
struct es1968 *chip = ac97->private_data;
#ifndef CONFIG_SND_ES1968_INPUT
unsigned long flags;
#endif
snd_es1968_ac97_wait(chip);
#ifndef CONFIG_SND_ES1968_INPUT
spin_lock_irqsave(&chip->ac97_lock, flags);
#endif
outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
/*msleep(1);*/
@ -661,7 +677,9 @@ static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short
data = inw(chip->io_port + ESM_AC97_DATA);
/*msleep(1);*/
}
#ifndef CONFIG_SND_ES1968_INPUT
spin_unlock_irqrestore(&chip->ac97_lock, flags);
#endif
return data;
}
@ -1874,13 +1892,17 @@ static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es)
}
}
/*
*/
/* The hardware volume works by incrementing / decrementing 2 counters
(without wrap around) in response to volume button presses and then
generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
of a byte wide register. The meaning of bits 0 and 4 is unknown. */
static void es1968_update_hw_volume(unsigned long private_data)
{
struct es1968 *chip = (struct es1968 *) private_data;
int x, val;
#ifndef CONFIG_SND_ES1968_INPUT
unsigned long flags;
#endif
/* Figure out which volume control button was pushed,
based on differences from the default register
@ -1895,6 +1917,7 @@ static void es1968_update_hw_volume(unsigned long private_data)
if (chip->in_suspend)
return;
#ifndef CONFIG_SND_ES1968_INPUT
if (! chip->master_switch || ! chip->master_volume)
return;
@ -1937,6 +1960,35 @@ static void es1968_update_hw_volume(unsigned long private_data)
break;
}
spin_unlock_irqrestore(&chip->ac97_lock, flags);
#else
if (!chip->input_dev)
return;
val = 0;
switch (x) {
case 0x88:
/* The counters have not changed, yet we've received a HV
interrupt. According to tests run by various people this
happens when pressing the mute button. */
val = KEY_MUTE;
break;
case 0xaa:
/* counters increased by 1 -> volume up */
val = KEY_VOLUMEUP;
break;
case 0x66:
/* counters decreased by 1 -> volume down */
val = KEY_VOLUMEDOWN;
break;
}
if (val) {
input_report_key(chip->input_dev, val, 1);
input_sync(chip->input_dev);
input_report_key(chip->input_dev, val, 0);
input_sync(chip->input_dev);
}
#endif
}
/*
@ -1953,7 +2005,11 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
if (event & ESM_HWVOL_IRQ)
#ifdef CONFIG_SND_ES1968_INPUT
es1968_update_hw_volume((unsigned long)chip);
#else
tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */
#endif
/* else ack 'em all, i imagine */
outb(0xFF, chip->io_port + 0x1A);
@ -1993,7 +2049,9 @@ snd_es1968_mixer(struct es1968 *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
#ifndef CONFIG_SND_ES1968_INPUT
struct snd_ctl_elem_id elem_id;
#endif
int err;
static struct snd_ac97_bus_ops ops = {
.write = snd_es1968_ac97_write,
@ -2009,6 +2067,7 @@ snd_es1968_mixer(struct es1968 *chip)
if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
return err;
#ifndef CONFIG_SND_ES1968_INPUT
/* attach master switch / volumes for h/w volume control */
memset(&elem_id, 0, sizeof(elem_id));
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
@ -2018,6 +2077,7 @@ snd_es1968_mixer(struct es1968 *chip)
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(elem_id.name, "Master Playback Volume");
chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
#endif
return 0;
}
@ -2341,6 +2401,7 @@ static void snd_es1968_start_irq(struct es1968 *chip)
w = ESM_HIRQ_DSIE | ESM_HIRQ_HW_VOLUME;
if (chip->rmidi)
w |= ESM_HIRQ_MPU401;
outb(w, chip->io_port + 0x1A);
outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
}
@ -2474,8 +2535,49 @@ static inline int snd_es1968_create_gameport(struct es1968 *chip, int dev) { ret
static inline void snd_es1968_free_gameport(struct es1968 *chip) { }
#endif
#ifdef CONFIG_SND_ES1968_INPUT
static int __devinit snd_es1968_input_register(struct es1968 *chip)
{
struct input_dev *input_dev;
int err;
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0",
pci_name(chip->pci));
input_dev->name = chip->card->driver;
input_dev->phys = chip->phys;
input_dev->id.bustype = BUS_PCI;
input_dev->id.vendor = chip->pci->vendor;
input_dev->id.product = chip->pci->device;
input_dev->dev.parent = &chip->pci->dev;
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(KEY_MUTE, input_dev->keybit);
__set_bit(KEY_VOLUMEDOWN, input_dev->keybit);
__set_bit(KEY_VOLUMEUP, input_dev->keybit);
err = input_register_device(input_dev);
if (err) {
input_free_device(input_dev);
return err;
}
chip->input_dev = input_dev;
return 0;
}
#endif /* CONFIG_SND_ES1968_INPUT */
static int snd_es1968_free(struct es1968 *chip)
{
#ifdef CONFIG_SND_ES1968_INPUT
if (chip->input_dev)
input_unregister_device(chip->input_dev);
#endif
if (chip->io_port) {
if (chip->irq >= 0)
synchronize_irq(chip->irq);
@ -2486,8 +2588,6 @@ static int snd_es1968_free(struct es1968 *chip)
if (chip->irq >= 0)
free_irq(chip->irq, chip);
snd_es1968_free_gameport(chip);
chip->master_switch = NULL;
chip->master_volume = NULL;
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip);
@ -2558,9 +2658,11 @@ static int __devinit snd_es1968_create(struct snd_card *card,
spin_lock_init(&chip->substream_lock);
INIT_LIST_HEAD(&chip->buf_list);
INIT_LIST_HEAD(&chip->substream_list);
spin_lock_init(&chip->ac97_lock);
mutex_init(&chip->memory_mutex);
#ifndef CONFIG_SND_ES1968_INPUT
spin_lock_init(&chip->ac97_lock);
tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
#endif
chip->card = card;
chip->pci = pci;
chip->irq = -1;
@ -2713,6 +2815,13 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
snd_es1968_create_gameport(chip, dev);
#ifdef CONFIG_SND_ES1968_INPUT
err = snd_es1968_input_register(chip);
if (err)
snd_printk(KERN_WARNING "Input device registration "
"failed with error %i", err);
#endif
snd_es1968_start_irq(chip);
chip->clock = clock[dev];

View File

@ -41,6 +41,7 @@
#include <linux/vmalloc.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/input.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@ -844,11 +845,17 @@ struct snd_m3 {
struct m3_dma *substreams;
spinlock_t reg_lock;
spinlock_t ac97_lock;
#ifdef CONFIG_SND_MAESTRO3_INPUT
struct input_dev *input_dev;
char phys[64]; /* physical device path */
#else
spinlock_t ac97_lock;
struct snd_kcontrol *master_switch;
struct snd_kcontrol *master_volume;
struct tasklet_struct hwvol_tq;
#endif
unsigned int in_suspend;
#ifdef CONFIG_PM
@ -1598,18 +1605,32 @@ static void snd_m3_update_ptr(struct snd_m3 *chip, struct m3_dma *s)
}
}
/* The m3's hardware volume works by incrementing / decrementing 2 counters
(without wrap around) in response to volume button presses and then
generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
of a byte wide register. The meaning of bits 0 and 4 is unknown. */
static void snd_m3_update_hw_volume(unsigned long private_data)
{
struct snd_m3 *chip = (struct snd_m3 *) private_data;
int x, val;
#ifndef CONFIG_SND_MAESTRO3_INPUT
unsigned long flags;
#endif
/* Figure out which volume control button was pushed,
based on differences from the default register
values. */
x = inb(chip->iobase + SHADOW_MIX_REG_VOICE) & 0xee;
/* Reset the volume control registers. */
/* Reset the volume counters to 4. Tests on the allegro integrated
into a Compaq N600C laptop, have revealed that:
1) Writing any value will result in the 2 counters being reset to
4 so writing 0x88 is not strictly necessary
2) Writing to any of the 4 involved registers will reset all 4
of them (and reading them always returns the same value for all
of them)
It could be that a maestro deviates from this, so leave the code
as is. */
outb(0x88, chip->iobase + SHADOW_MIX_REG_VOICE);
outb(0x88, chip->iobase + HW_VOL_COUNTER_VOICE);
outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER);
@ -1620,6 +1641,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
if (chip->in_suspend)
return;
#ifndef CONFIG_SND_MAESTRO3_INPUT
if (!chip->master_switch || !chip->master_volume)
return;
@ -1629,7 +1651,9 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
val = chip->ac97->regs[AC97_MASTER_VOL];
switch (x) {
case 0x88:
/* mute */
/* The counters have not changed, yet we've received a HV
interrupt. According to tests run by various people this
happens when pressing the mute button. */
val ^= 0x8000;
chip->ac97->regs[AC97_MASTER_VOL] = val;
outw(val, chip->iobase + CODEC_DATA);
@ -1638,7 +1662,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
&chip->master_switch->id);
break;
case 0xaa:
/* volume up */
/* counters increased by 1 -> volume up */
if ((val & 0x7f) > 0)
val--;
if ((val & 0x7f00) > 0)
@ -1650,7 +1674,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
&chip->master_volume->id);
break;
case 0x66:
/* volume down */
/* counters decreased by 1 -> volume down */
if ((val & 0x7f) < 0x1f)
val++;
if ((val & 0x7f00) < 0x1f00)
@ -1663,6 +1687,35 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
break;
}
spin_unlock_irqrestore(&chip->ac97_lock, flags);
#else
if (!chip->input_dev)
return;
val = 0;
switch (x) {
case 0x88:
/* The counters have not changed, yet we've received a HV
interrupt. According to tests run by various people this
happens when pressing the mute button. */
val = KEY_MUTE;
break;
case 0xaa:
/* counters increased by 1 -> volume up */
val = KEY_VOLUMEUP;
break;
case 0x66:
/* counters decreased by 1 -> volume down */
val = KEY_VOLUMEDOWN;
break;
}
if (val) {
input_report_key(chip->input_dev, val, 1);
input_sync(chip->input_dev);
input_report_key(chip->input_dev, val, 0);
input_sync(chip->input_dev);
}
#endif
}
static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
@ -1677,7 +1730,11 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (status & HV_INT_PENDING)
#ifdef CONFIG_SND_MAESTRO3_INPUT
snd_m3_update_hw_volume((unsigned long)chip);
#else
tasklet_schedule(&chip->hwvol_tq);
#endif
/*
* ack an assp int if its running
@ -1943,18 +2000,24 @@ static unsigned short
snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct snd_m3 *chip = ac97->private_data;
#ifndef CONFIG_SND_MAESTRO3_INPUT
unsigned long flags;
#endif
unsigned short data = 0xffff;
if (snd_m3_ac97_wait(chip))
goto fail;
#ifndef CONFIG_SND_MAESTRO3_INPUT
spin_lock_irqsave(&chip->ac97_lock, flags);
#endif
snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
if (snd_m3_ac97_wait(chip))
goto fail_unlock;
data = snd_m3_inw(chip, CODEC_DATA);
fail_unlock:
#ifndef CONFIG_SND_MAESTRO3_INPUT
spin_unlock_irqrestore(&chip->ac97_lock, flags);
#endif
fail:
return data;
}
@ -1963,14 +2026,20 @@ static void
snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct snd_m3 *chip = ac97->private_data;
#ifndef CONFIG_SND_MAESTRO3_INPUT
unsigned long flags;
#endif
if (snd_m3_ac97_wait(chip))
return;
#ifndef CONFIG_SND_MAESTRO3_INPUT
spin_lock_irqsave(&chip->ac97_lock, flags);
#endif
snd_m3_outw(chip, val, CODEC_DATA);
snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
#ifndef CONFIG_SND_MAESTRO3_INPUT
spin_unlock_irqrestore(&chip->ac97_lock, flags);
#endif
}
@ -2077,7 +2146,9 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
#ifndef CONFIG_SND_MAESTRO3_INPUT
struct snd_ctl_elem_id elem_id;
#endif
int err;
static struct snd_ac97_bus_ops ops = {
.write = snd_m3_ac97_write,
@ -2097,6 +2168,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
schedule_timeout_uninterruptible(msecs_to_jiffies(100));
snd_ac97_write(chip->ac97, AC97_PCM, 0);
#ifndef CONFIG_SND_MAESTRO3_INPUT
memset(&elem_id, 0, sizeof(elem_id));
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(elem_id.name, "Master Playback Switch");
@ -2105,6 +2177,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
strcpy(elem_id.name, "Master Playback Volume");
chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
#endif
return 0;
}
@ -2370,6 +2443,7 @@ snd_m3_enable_ints(struct snd_m3 *chip)
val = ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/;
if (chip->hv_config & HV_CTRL_ENABLE)
val |= HV_INT_ENABLE;
outb(val, chip->iobase + HOST_INT_STATUS);
outw(val, io + HOST_INT_CTRL);
outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
io + ASSP_CONTROL_C);
@ -2384,6 +2458,11 @@ static int snd_m3_free(struct snd_m3 *chip)
struct m3_dma *s;
int i;
#ifdef CONFIG_SND_MAESTRO3_INPUT
if (chip->input_dev)
input_unregister_device(chip->input_dev);
#endif
if (chip->substreams) {
spin_lock_irq(&chip->reg_lock);
for (i = 0; i < chip->num_substreams; i++) {
@ -2510,6 +2589,41 @@ static int m3_resume(struct pci_dev *pci)
}
#endif /* CONFIG_PM */
#ifdef CONFIG_SND_MAESTRO3_INPUT
static int __devinit snd_m3_input_register(struct snd_m3 *chip)
{
struct input_dev *input_dev;
int err;
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0",
pci_name(chip->pci));
input_dev->name = chip->card->driver;
input_dev->phys = chip->phys;
input_dev->id.bustype = BUS_PCI;
input_dev->id.vendor = chip->pci->vendor;
input_dev->id.product = chip->pci->device;
input_dev->dev.parent = &chip->pci->dev;
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(KEY_MUTE, input_dev->keybit);
__set_bit(KEY_VOLUMEDOWN, input_dev->keybit);
__set_bit(KEY_VOLUMEUP, input_dev->keybit);
err = input_register_device(input_dev);
if (err) {
input_free_device(input_dev);
return err;
}
chip->input_dev = input_dev;
return 0;
}
#endif /* CONFIG_INPUT */
/*
*/
@ -2553,7 +2667,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
}
spin_lock_init(&chip->reg_lock);
#ifndef CONFIG_SND_MAESTRO3_INPUT
spin_lock_init(&chip->ac97_lock);
#endif
switch (pci->device) {
case PCI_DEVICE_ID_ESS_ALLEGRO:
@ -2636,7 +2752,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
snd_m3_hv_init(chip);
#ifndef CONFIG_SND_MAESTRO3_INPUT
tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
#endif
if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED,
card->driver, chip)) {
@ -2668,7 +2786,16 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
if ((err = snd_m3_pcm(chip, 0)) < 0)
return err;
#ifdef CONFIG_SND_MAESTRO3_INPUT
if (chip->hv_config & HV_CTRL_ENABLE) {
err = snd_m3_input_register(chip);
if (err)
snd_printk(KERN_WARNING "Input device registration "
"failed with error %i", err);
}
#endif
snd_m3_enable_ints(chip);
snd_m3_assp_continue(chip);

View File

@ -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

View File

@ -2,14 +2,24 @@
# Makefile for ALSA
#
snd-usb-audio-objs := usbaudio.o usbmixer.o
snd-usb-lib-objs := usbmidi.o
snd-ua101-objs := ua101.o
snd-usb-audio-objs := card.o \
mixer.o \
mixer_quirks.o \
proc.o \
quirks.o \
format.o \
endpoint.o \
urb.o \
pcm.o \
helper.o
snd-usbmidi-lib-objs := midi.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o
obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o snd-usb-lib.o
obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o
obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o
obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usbmidi-lib.o
obj-$(CONFIG_SND) += usx2y/ caiaq/
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) += misc/ usx2y/ caiaq/

View File

@ -16,6 +16,7 @@
* 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>

652
sound/usb/card.c 100644
View File

@ -0,0 +1,652 @@
/*
* (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);

105
sound/usb/card.h 100644
View File

@ -0,0 +1,105 @@
#ifndef __USBAUDIO_CARD_H
#define __USBAUDIO_CARD_H
#define MAX_PACKS 20
#define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */
#define MAX_URBS 8
#define SYNC_URBS 4 /* always four urbs for sync */
#define MAX_QUEUE 24 /* try not to exceed this queue length, in ms */
struct audioformat {
struct list_head list;
u64 formats; /* ALSA format bits */
unsigned int channels; /* # channels */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int frame_size; /* samples per frame for non-audio */
int iface; /* interface number */
unsigned char altsetting; /* corresponding alternate setting */
unsigned char altset_idx; /* array index of altenate setting */
unsigned char attributes; /* corresponding attributes of cs endpoint */
unsigned char endpoint; /* endpoint */
unsigned char ep_attr; /* endpoint attributes */
unsigned char datainterval; /* log_2 of data packet interval */
unsigned int maxpacksize; /* max. packet size */
unsigned int rates; /* rate bitmasks */
unsigned int rate_min, rate_max; /* min/max rates */
unsigned int nr_rates; /* number of rate table entries */
unsigned int *rate_table; /* rate table */
};
struct snd_usb_substream;
struct snd_urb_ctx {
struct urb *urb;
unsigned int buffer_size; /* size of data buffer, if data URB */
struct snd_usb_substream *subs;
int index; /* index for urb array */
int packets; /* number of packets per urb */
};
struct snd_urb_ops {
int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
};
struct snd_usb_substream {
struct snd_usb_stream *stream;
struct usb_device *dev;
struct snd_pcm_substream *pcm_substream;
int direction; /* playback or capture */
int interface; /* current interface */
int endpoint; /* assigned endpoint */
struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */
unsigned int cur_rate; /* current rate (for hw_params callback) */
unsigned int period_bytes; /* current period bytes (for hw_params callback) */
unsigned int altset_idx; /* USB data format: index of alternate setting */
unsigned int datapipe; /* the data i/o pipe */
unsigned int syncpipe; /* 1 - async out or adaptive in */
unsigned int datainterval; /* log_2 of data packet interval */
unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */
unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */
unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */
unsigned int freqmax; /* maximum sampling rate, used for buffer management */
unsigned int phase; /* phase accumulator */
unsigned int maxpacksize; /* max packet size in bytes */
unsigned int maxframesize; /* max packet size in frames */
unsigned int curpacksize; /* current packet size in bytes (for capture) */
unsigned int curframesize; /* current packet size in frames (for capture) */
unsigned int fill_max: 1; /* fill max packet size always */
unsigned int txfr_quirk:1; /* allow sub-frame alignment */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int running: 1; /* running status */
unsigned int hwptr_done; /* processed byte position in the buffer */
unsigned int transfer_done; /* processed frames since last period update */
unsigned long active_mask; /* bitmask of active urbs */
unsigned long unlink_mask; /* bitmask of unlinked urbs */
unsigned int nurbs; /* # urbs */
struct snd_urb_ctx dataurb[MAX_URBS]; /* data urb table */
struct snd_urb_ctx syncurb[SYNC_URBS]; /* sync urb table */
char *syncbuf; /* sync buffer for all sync URBs */
dma_addr_t sync_dma; /* DMA address of syncbuf */
u64 formats; /* format bitmasks (all or'ed) */
unsigned int num_formats; /* number of supported audio formats (list) */
struct list_head fmt_list; /* format list */
struct snd_pcm_hw_constraint_list rate_list; /* limited rates */
spinlock_t lock;
struct snd_urb_ops ops; /* callbacks (must be filled at init) */
};
struct snd_usb_stream {
struct snd_usb_audio *chip;
struct snd_pcm *pcm;
int pcm_index;
unsigned int fmt_type; /* USB audio format type (1-3) */
struct snd_usb_substream substream[2];
struct list_head list;
};
#endif /* __USBAUDIO_CARD_H */

15
sound/usb/debug.h 100644
View File

@ -0,0 +1,15 @@
#ifndef __USBAUDIO_DEBUG_H
#define __USBAUDIO_DEBUG_H
/*
* h/w constraints
*/
#ifdef HW_CONST_DEBUG
#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
#else
#define hwc_debug(fmt, args...) /**/
#endif
#endif /* __USBAUDIO_DEBUG_H */

View File

@ -0,0 +1,362 @@
/*
* 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;
}

View File

@ -0,0 +1,11 @@
#ifndef __USBAUDIO_ENDPOINT_H
#define __USBAUDIO_ENDPOINT_H
int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip,
int iface_no);
int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip,
int stream,
struct audioformat *fp);
#endif /* __USBAUDIO_ENDPOINT_H */

432
sound/usb/format.c 100644
View File

@ -0,0 +1,432 @@
/*
* 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;
}

View File

@ -0,0 +1,8 @@
#ifndef __USBAUDIO_FORMAT_H
#define __USBAUDIO_FORMAT_H
int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
int format, unsigned char *fmt, int stream,
struct usb_host_interface *iface);
#endif /* __USBAUDIO_FORMAT_H */

113
sound/usb/helper.c 100644
View File

@ -0,0 +1,113 @@
/*
* 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;
}

32
sound/usb/helper.h 100644
View File

@ -0,0 +1,32 @@
#ifndef __USBAUDIO_HELPER_H
#define __USBAUDIO_HELPER_H
unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size);
void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype);
void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype);
int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value, __u16 index,
void *data, __u16 size, int timeout);
unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
struct usb_host_interface *alts);
/*
* retrieve usb_interface descriptor from the host interface
* (conditional for compatibility with the older API)
*/
#ifndef get_iface_desc
#define get_iface_desc(iface) (&(iface)->desc)
#define get_endpoint(alt,ep) (&(alt)->endpoint[ep].desc)
#define get_ep_desc(ep) (&(ep)->desc)
#define get_cfg_desc(cfg) (&(cfg)->desc)
#endif
#ifndef snd_usb_get_speed
#define snd_usb_get_speed(dev) ((dev)->speed)
#endif
#endif /* __USBAUDIO_HELPER_H */

View File

@ -53,7 +53,8 @@
#include <sound/rawmidi.h>
#include <sound/asequencer.h>
#include "usbaudio.h"
#include "midi.h"
#include "helper.h"
/*
* define this to log all USB packets

48
sound/usb/midi.h 100644
View File

@ -0,0 +1,48 @@
#ifndef __USBMIDI_H
#define __USBMIDI_H
/* maximum number of endpoints per interface */
#define MIDI_MAX_ENDPOINTS 2
/* data for QUIRK_MIDI_FIXED_ENDPOINT */
struct snd_usb_midi_endpoint_info {
int8_t out_ep; /* ep number, 0 autodetect */
uint8_t out_interval; /* interval for interrupt endpoints */
int8_t in_ep;
uint8_t in_interval;
uint16_t out_cables; /* bitmask */
uint16_t in_cables; /* bitmask */
};
/* for QUIRK_MIDI_YAMAHA, data is NULL */
/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info
* structure (out_cables and in_cables only) */
/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk
* structures, terminated with .ifnum = -1 */
/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */
/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */
/* for QUIRK_AUDIO_EDIROL_UA700_UA25/UA1000, data is NULL */
/* for QUIRK_IGNORE_INTERFACE, data is NULL */
/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */
/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info
* structure (out_cables and in_cables only) */
/* for QUIRK_MIDI_CME, data is NULL */
int snd_usbmidi_create(struct snd_card *card,
struct usb_interface *iface,
struct list_head *midi_list,
const struct snd_usb_audio_quirk *quirk);
void snd_usbmidi_input_stop(struct list_head* p);
void snd_usbmidi_input_start(struct list_head* p);
void snd_usbmidi_disconnect(struct list_head *p);
#endif /* __USBMIDI_H */

View File

@ -0,0 +1,2 @@
snd-ua101-objs := ua101.o
obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o

View File

@ -23,7 +23,8 @@
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "usbaudio.h"
#include "../usbaudio.h"
#include "../midi.h"
MODULE_DESCRIPTION("Edirol UA-101/1000 driver");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");

File diff suppressed because it is too large Load Diff

55
sound/usb/mixer.h 100644
View File

@ -0,0 +1,55 @@
#ifndef __USBMIXER_H
#define __USBMIXER_H
struct usb_mixer_interface {
struct snd_usb_audio *chip;
unsigned int ctrlif;
struct list_head list;
unsigned int ignore_ctl_error;
struct urb *urb;
/* array[MAX_ID_ELEMS], indexed by unit id */
struct usb_mixer_elem_info **id_elems;
/* the usb audio specification version this interface complies to */
int protocol;
/* Sound Blaster remote control stuff */
const struct rc_config *rc_cfg;
u32 rc_code;
wait_queue_head_t rc_waitq;
struct urb *rc_urb;
struct usb_ctrlrequest *rc_setup_packet;
u8 rc_buffer[6];
u8 audigy2nx_leds[3];
u8 xonar_u1_status;
};
#define MAX_CHANNELS 10 /* max logical channels */
struct usb_mixer_elem_info {
struct usb_mixer_interface *mixer;
struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
struct snd_ctl_elem_id *elem_id;
unsigned int id;
unsigned int control; /* CS or ICN (high byte) */
unsigned int cmask; /* channel mask bitmap: 0 = master */
int channels;
int val_type;
int min, max, res;
int dBmin, dBmax;
int cached;
int cache_val[MAX_CHANNELS];
u8 initialized;
};
int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
int ignore_error);
void snd_usb_mixer_disconnect(struct list_head *p);
void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid);
int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
int request, int validx, int value_set);
#endif /* __USBMIXER_H */

View File

@ -85,8 +85,8 @@ static struct usbmix_name_map extigy_map[] = {
/* 16: MU (w/o controls) */
{ 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */
{ 17, "Channel Routing", 2 }, /* PU: mode select */
{ 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */
{ 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */
{ 18, "Tone Control - Bass", UAC_BASS_CONTROL }, /* FU */
{ 18, "Tone Control - Treble", UAC_TREBLE_CONTROL }, /* FU */
{ 18, "Master Playback" }, /* FU; others */
/* 19: OT speaker */
/* 20: OT headphone */

View File

@ -0,0 +1,412 @@
/*
* 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;
}
}

View File

@ -0,0 +1,13 @@
#ifndef SND_USB_MIXER_QUIRKS_H
#define SND_USB_MIXER_QUIRKS_H
int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer);
void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
unsigned char samplerate_id);
void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
int unitid);
#endif /* SND_USB_MIXER_QUIRKS_H */

935
sound/usb/pcm.c 100644
View File

@ -0,0 +1,935 @@
/*
* 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 <sound/pcm_params.h>
#include "usbaudio.h"
#include "card.h"
#include "quirks.h"
#include "debug.h"
#include "urb.h"
#include "helper.h"
#include "pcm.h"
/*
* return the current pcm pointer. just based on the hwptr_done value.
*/
static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_usb_substream *subs;
unsigned int hwptr_done;
subs = (struct snd_usb_substream *)substream->runtime->private_data;
spin_lock(&subs->lock);
hwptr_done = subs->hwptr_done;
spin_unlock(&subs->lock);
return hwptr_done / (substream->runtime->frame_bits >> 3);
}
/*
* find a matching audio format
*/
static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format,
unsigned int rate, unsigned int channels)
{
struct list_head *p;
struct audioformat *found = NULL;
int cur_attr = 0, attr;
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
if (!(fp->formats & (1uLL << format)))
continue;
if (fp->channels != channels)
continue;
if (rate < fp->rate_min || rate > fp->rate_max)
continue;
if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
unsigned int i;
for (i = 0; i < fp->nr_rates; i++)
if (fp->rate_table[i] == rate)
break;
if (i >= fp->nr_rates)
continue;
}
attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE;
if (! found) {
found = fp;
cur_attr = attr;
continue;
}
/* avoid async out and adaptive in if the other method
* supports the same format.
* this is a workaround for the case like
* M-audio audiophile USB.
*/
if (attr != cur_attr) {
if ((attr == USB_ENDPOINT_SYNC_ASYNC &&
subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
(attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
subs->direction == SNDRV_PCM_STREAM_CAPTURE))
continue;
if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC &&
subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
(cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {
found = fp;
cur_attr = attr;
continue;
}
}
/* find the format with the largest max. packet size */
if (fp->maxpacksize > found->maxpacksize) {
found = fp;
cur_attr = attr;
}
}
return found;
}
static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt)
{
struct usb_device *dev = chip->dev;
unsigned int ep;
unsigned char data[1];
int err;
ep = get_endpoint(alts, 0)->bEndpointAddress;
/* if endpoint doesn't have pitch control, bail out */
if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
return 0;
data[0] = 1;
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep,
data, sizeof(data), 1000)) < 0) {
snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
dev->devnum, iface, ep);
return err;
}
return 0;
}
/*
* initialize the picth control and sample rate
*/
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt)
{
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
switch (altsd->bInterfaceProtocol) {
case UAC_VERSION_1:
return init_pitch_v1(chip, iface, alts, fmt);
case UAC_VERSION_2:
/* not implemented yet */
return 0;
}
return -EINVAL;
}
static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate)
{
struct usb_device *dev = chip->dev;
unsigned int ep;
unsigned char data[3];
int err, crate;
ep = get_endpoint(alts, 0)->bEndpointAddress;
/* if endpoint doesn't have sampling rate control, bail out */
if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
dev->devnum, iface, fmt->altsetting);
return 0;
}
data[0] = rate;
data[1] = rate >> 8;
data[2] = rate >> 16;
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
data, sizeof(data), 1000)) < 0) {
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
dev->devnum, iface, fmt->altsetting, rate, ep);
return err;
}
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
data, sizeof(data), 1000)) < 0) {
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
dev->devnum, iface, fmt->altsetting, ep);
return 0; /* some devices don't support reading */
}
crate = data[0] | (data[1] << 8) | (data[2] << 16);
if (crate != rate) {
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
// runtime->rate = crate;
}
return 0;
}
static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate)
{
struct usb_device *dev = chip->dev;
unsigned char data[4];
int err, crate;
data[0] = rate;
data[1] = rate >> 8;
data[2] = rate >> 16;
data[3] = rate >> 24;
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
data, sizeof(data), 1000)) < 0) {
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
dev->devnum, iface, fmt->altsetting, rate);
return err;
}
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
data, sizeof(data), 1000)) < 0) {
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
dev->devnum, iface, fmt->altsetting);
return err;
}
crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
if (crate != rate)
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
return 0;
}
int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate)
{
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
switch (altsd->bInterfaceProtocol) {
case UAC_VERSION_1:
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
case UAC_VERSION_2:
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
}
return -EINVAL;
}
/*
* find a matching format and set up the interface
*/
static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
{
struct usb_device *dev = subs->dev;
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
struct usb_interface *iface;
unsigned int ep, attr;
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
int err;
iface = usb_ifnum_to_if(dev, fmt->iface);
if (WARN_ON(!iface))
return -EINVAL;
alts = &iface->altsetting[fmt->altset_idx];
altsd = get_iface_desc(alts);
if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
return -EINVAL;
if (fmt == subs->cur_audiofmt)
return 0;
/* close the old interface */
if (subs->interface >= 0 && subs->interface != fmt->iface) {
if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
dev->devnum, fmt->iface, fmt->altsetting);
return -EIO;
}
subs->interface = -1;
subs->altset_idx = 0;
}
/* set interface */
if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) {
if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
dev->devnum, fmt->iface, fmt->altsetting);
return -EIO;
}
snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
subs->interface = fmt->iface;
subs->altset_idx = fmt->altset_idx;
}
/* create a data pipe */
ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
if (is_playback)
subs->datapipe = usb_sndisocpipe(dev, ep);
else
subs->datapipe = usb_rcvisocpipe(dev, ep);
subs->datainterval = fmt->datainterval;
subs->syncpipe = subs->syncinterval = 0;
subs->maxpacksize = fmt->maxpacksize;
subs->fill_max = 0;
/* we need a sync pipe in async OUT or adaptive IN mode */
/* check the number of EP, since some devices have broken
* descriptors which fool us. if it has only one EP,
* assume it as adaptive-out or sync-in.
*/
attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
(! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
altsd->bNumEndpoints >= 2) {
/* check sync-pipe endpoint */
/* ... and check descriptor size before accessing bSynchAddress
because there is a version of the SB Audigy 2 NX firmware lacking
the audio fields in the endpoint descriptors */
if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
(get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
get_endpoint(alts, 1)->bSynchAddress != 0)) {
snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
dev->devnum, fmt->iface, fmt->altsetting);
return -EINVAL;
}
ep = get_endpoint(alts, 1)->bEndpointAddress;
if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
(( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
(!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
dev->devnum, fmt->iface, fmt->altsetting);
return -EINVAL;
}
ep &= USB_ENDPOINT_NUMBER_MASK;
if (is_playback)
subs->syncpipe = usb_rcvisocpipe(dev, ep);
else
subs->syncpipe = usb_sndisocpipe(dev, ep);
if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
get_endpoint(alts, 1)->bRefresh >= 1 &&
get_endpoint(alts, 1)->bRefresh <= 9)
subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
subs->syncinterval = 1;
else if (get_endpoint(alts, 1)->bInterval >= 1 &&
get_endpoint(alts, 1)->bInterval <= 16)
subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
else
subs->syncinterval = 3;
}
/* always fill max packet size */
if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
subs->fill_max = 1;
if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
return err;
subs->cur_audiofmt = fmt;
snd_usb_set_format_quirk(subs, fmt);
#if 0
printk(KERN_DEBUG
"setting done: format = %d, rate = %d..%d, channels = %d\n",
fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
printk(KERN_DEBUG
" datapipe = 0x%0x, syncpipe = 0x%0x\n",
subs->datapipe, subs->syncpipe);
#endif
return 0;
}
/*
* hw_params callback
*
* allocate a buffer and set the given audio format.
*
* so far we use a physically linear buffer although packetize transfer
* doesn't need a continuous area.
* if sg buffer is supported on the later version of alsa, we'll follow
* that.
*/
static int snd_usb_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_usb_substream *subs = substream->runtime->private_data;
struct audioformat *fmt;
unsigned int channels, rate, format;
int ret, changed;
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
format = params_format(hw_params);
rate = params_rate(hw_params);
channels = params_channels(hw_params);
fmt = find_format(subs, format, rate, channels);
if (!fmt) {
snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
format, rate, channels);
return -EINVAL;
}
changed = subs->cur_audiofmt != fmt ||
subs->period_bytes != params_period_bytes(hw_params) ||
subs->cur_rate != rate;
if ((ret = set_format(subs, fmt)) < 0)
return ret;
if (subs->cur_rate != rate) {
struct usb_host_interface *alts;
struct usb_interface *iface;
iface = usb_ifnum_to_if(subs->dev, fmt->iface);
alts = &iface->altsetting[fmt->altset_idx];
ret = snd_usb_init_sample_rate(subs->stream->chip, subs->interface, alts, fmt, rate);
if (ret < 0)
return ret;
subs->cur_rate = rate;
}
if (changed) {
/* format changed */
snd_usb_release_substream_urbs(subs, 0);
/* influenced: period_bytes, channels, rate, format, */
ret = snd_usb_init_substream_urbs(subs, params_period_bytes(hw_params),
params_rate(hw_params),
snd_pcm_format_physical_width(params_format(hw_params)) *
params_channels(hw_params));
}
return ret;
}
/*
* hw_free callback
*
* reset the audio format and release the buffer
*/
static int snd_usb_hw_free(struct snd_pcm_substream *substream)
{
struct snd_usb_substream *subs = substream->runtime->private_data;
subs->cur_audiofmt = NULL;
subs->cur_rate = 0;
subs->period_bytes = 0;
if (!subs->stream->chip->shutdown)
snd_usb_release_substream_urbs(subs, 0);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
/*
* prepare callback
*
* only a few subtle things...
*/
static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usb_substream *subs = runtime->private_data;
if (! subs->cur_audiofmt) {
snd_printk(KERN_ERR "usbaudio: no format is specified!\n");
return -ENXIO;
}
/* some unit conversions in runtime */
subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
/* reset the pointer */
subs->hwptr_done = 0;
subs->transfer_done = 0;
subs->phase = 0;
runtime->delay = 0;
return snd_usb_substream_prepare(subs, runtime);
}
static struct snd_pcm_hardware snd_usb_hardware =
{
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE,
.buffer_bytes_max = 1024 * 1024,
.period_bytes_min = 64,
.period_bytes_max = 512 * 1024,
.periods_min = 2,
.periods_max = 1024,
};
static int hw_check_valid_format(struct snd_usb_substream *subs,
struct snd_pcm_hw_params *params,
struct audioformat *fp)
{
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
struct snd_mask check_fmts;
unsigned int ptime;
/* check the format */
snd_mask_none(&check_fmts);
check_fmts.bits[0] = (u32)fp->formats;
check_fmts.bits[1] = (u32)(fp->formats >> 32);
snd_mask_intersect(&check_fmts, fmts);
if (snd_mask_empty(&check_fmts)) {
hwc_debug(" > check: no supported format %d\n", fp->format);
return 0;
}
/* check the channels */
if (fp->channels < ct->min || fp->channels > ct->max) {
hwc_debug(" > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max);
return 0;
}
/* check the rate is within the range */
if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) {
hwc_debug(" > check: rate_min %d > max %d\n", fp->rate_min, it->max);
return 0;
}
if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) {
hwc_debug(" > check: rate_max %d < min %d\n", fp->rate_max, it->min);
return 0;
}
/* check whether the period time is >= the data packet interval */
if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
ptime = 125 * (1 << fp->datainterval);
if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max);
return 0;
}
}
return 1;
}
static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
struct list_head *p;
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
unsigned int rmin, rmax;
int changed;
hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max);
changed = 0;
rmin = rmax = 0;
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
if (!hw_check_valid_format(subs, params, fp))
continue;
if (changed++) {
if (rmin > fp->rate_min)
rmin = fp->rate_min;
if (rmax < fp->rate_max)
rmax = fp->rate_max;
} else {
rmin = fp->rate_min;
rmax = fp->rate_max;
}
}
if (!changed) {
hwc_debug(" --> get empty\n");
it->empty = 1;
return -EINVAL;
}
changed = 0;
if (it->min < rmin) {
it->min = rmin;
it->openmin = 0;
changed = 1;
}
if (it->max > rmax) {
it->max = rmax;
it->openmax = 0;
changed = 1;
}
if (snd_interval_checkempty(it)) {
it->empty = 1;
return -EINVAL;
}
hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
return changed;
}
static int hw_rule_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
struct list_head *p;
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
unsigned int rmin, rmax;
int changed;
hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max);
changed = 0;
rmin = rmax = 0;
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
if (!hw_check_valid_format(subs, params, fp))
continue;
if (changed++) {
if (rmin > fp->channels)
rmin = fp->channels;
if (rmax < fp->channels)
rmax = fp->channels;
} else {
rmin = fp->channels;
rmax = fp->channels;
}
}
if (!changed) {
hwc_debug(" --> get empty\n");
it->empty = 1;
return -EINVAL;
}
changed = 0;
if (it->min < rmin) {
it->min = rmin;
it->openmin = 0;
changed = 1;
}
if (it->max > rmax) {
it->max = rmax;
it->openmax = 0;
changed = 1;
}
if (snd_interval_checkempty(it)) {
it->empty = 1;
return -EINVAL;
}
hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
return changed;
}
static int hw_rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
struct list_head *p;
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
u64 fbits;
u32 oldbits[2];
int changed;
hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
fbits = 0;
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
if (!hw_check_valid_format(subs, params, fp))
continue;
fbits |= fp->formats;
}
oldbits[0] = fmt->bits[0];
oldbits[1] = fmt->bits[1];
fmt->bits[0] &= (u32)fbits;
fmt->bits[1] &= (u32)(fbits >> 32);
if (!fmt->bits[0] && !fmt->bits[1]) {
hwc_debug(" --> get empty\n");
return -EINVAL;
}
changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]);
hwc_debug(" --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed);
return changed;
}
static int hw_rule_period_time(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
struct audioformat *fp;
struct snd_interval *it;
unsigned char min_datainterval;
unsigned int pmin;
int changed;
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max);
min_datainterval = 0xff;
list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
min_datainterval = min(min_datainterval, fp->datainterval);
}
if (min_datainterval == 0xff) {
hwc_debug(" --> get emtpy\n");
it->empty = 1;
return -EINVAL;
}
pmin = 125 * (1 << min_datainterval);
changed = 0;
if (it->min < pmin) {
it->min = pmin;
it->openmin = 0;
changed = 1;
}
if (snd_interval_checkempty(it)) {
it->empty = 1;
return -EINVAL;
}
hwc_debug(" --> (%u,%u) (changed = %d)\n", it->min, it->max, changed);
return changed;
}
/*
* If the device supports unusual bit rates, does the request meet these?
*/
static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
struct snd_usb_substream *subs)
{
struct audioformat *fp;
int count = 0, needs_knot = 0;
int err;
list_for_each_entry(fp, &subs->fmt_list, list) {
if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
return 0;
count += fp->nr_rates;
if (fp->rates & SNDRV_PCM_RATE_KNOT)
needs_knot = 1;
}
if (!needs_knot)
return 0;
subs->rate_list.count = count;
subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
subs->rate_list.mask = 0;
count = 0;
list_for_each_entry(fp, &subs->fmt_list, list) {
int i;
for (i = 0; i < fp->nr_rates; i++)
subs->rate_list.list[count++] = fp->rate_table[i];
}
err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&subs->rate_list);
if (err < 0)
return err;
return 0;
}
/*
* set up the runtime hardware information.
*/
static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
{
struct list_head *p;
unsigned int pt, ptmin;
int param_period_time_if_needed;
int err;
runtime->hw.formats = subs->formats;
runtime->hw.rate_min = 0x7fffffff;
runtime->hw.rate_max = 0;
runtime->hw.channels_min = 256;
runtime->hw.channels_max = 0;
runtime->hw.rates = 0;
ptmin = UINT_MAX;
/* check min/max rates and channels */
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
runtime->hw.rates |= fp->rates;
if (runtime->hw.rate_min > fp->rate_min)
runtime->hw.rate_min = fp->rate_min;
if (runtime->hw.rate_max < fp->rate_max)
runtime->hw.rate_max = fp->rate_max;
if (runtime->hw.channels_min > fp->channels)
runtime->hw.channels_min = fp->channels;
if (runtime->hw.channels_max < fp->channels)
runtime->hw.channels_max = fp->channels;
if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) {
/* FIXME: there might be more than one audio formats... */
runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
fp->frame_size;
}
pt = 125 * (1 << fp->datainterval);
ptmin = min(ptmin, pt);
}
param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
/* full speed devices have fixed data packet interval */
ptmin = 1000;
if (ptmin == 1000)
/* if period time doesn't go below 1 ms, no rules needed */
param_period_time_if_needed = -1;
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
ptmin, UINT_MAX);
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
hw_rule_rate, subs,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_CHANNELS,
param_period_time_if_needed,
-1)) < 0)
return err;
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
hw_rule_channels, subs,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_RATE,
param_period_time_if_needed,
-1)) < 0)
return err;
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
hw_rule_format, subs,
SNDRV_PCM_HW_PARAM_RATE,
SNDRV_PCM_HW_PARAM_CHANNELS,
param_period_time_if_needed,
-1)) < 0)
return err;
if (param_period_time_if_needed >= 0) {
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
hw_rule_period_time, subs,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_CHANNELS,
SNDRV_PCM_HW_PARAM_RATE,
-1);
if (err < 0)
return err;
}
if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
return err;
return 0;
}
static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
{
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usb_substream *subs = &as->substream[direction];
subs->interface = -1;
subs->altset_idx = 0;
runtime->hw = snd_usb_hardware;
runtime->private_data = subs;
subs->pcm_substream = substream;
return setup_hw_info(runtime, subs);
}
static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
{
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_usb_substream *subs = &as->substream[direction];
if (!as->chip->shutdown && subs->interface >= 0) {
usb_set_interface(subs->dev, subs->interface, 0);
subs->interface = -1;
}
subs->pcm_substream = NULL;
return 0;
}
static int snd_usb_playback_open(struct snd_pcm_substream *substream)
{
return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);
}
static int snd_usb_playback_close(struct snd_pcm_substream *substream)
{
return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK);
}
static int snd_usb_capture_open(struct snd_pcm_substream *substream)
{
return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE);
}
static int snd_usb_capture_close(struct snd_pcm_substream *substream)
{
return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
}
static struct snd_pcm_ops snd_usb_playback_ops = {
.open = snd_usb_playback_open,
.close = snd_usb_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_usb_hw_params,
.hw_free = snd_usb_hw_free,
.prepare = snd_usb_pcm_prepare,
.trigger = snd_usb_substream_playback_trigger,
.pointer = snd_usb_pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
static struct snd_pcm_ops snd_usb_capture_ops = {
.open = snd_usb_capture_open,
.close = snd_usb_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_usb_hw_params,
.hw_free = snd_usb_hw_free,
.prepare = snd_usb_pcm_prepare,
.trigger = snd_usb_substream_capture_trigger,
.pointer = snd_usb_pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream)
{
snd_pcm_set_ops(pcm, stream,
stream == SNDRV_PCM_STREAM_PLAYBACK ?
&snd_usb_playback_ops : &snd_usb_capture_ops);
}

14
sound/usb/pcm.h 100644
View File

@ -0,0 +1,14 @@
#ifndef __USBAUDIO_PCM_H
#define __USBAUDIO_PCM_H
void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt);
int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate);
#endif /* __USBAUDIO_PCM_H */

168
sound/usb/proc.c 100644
View File

@ -0,0 +1,168 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/usb.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include "usbaudio.h"
#include "helper.h"
#include "card.h"
#include "proc.h"
/* convert our full speed USB rate into sampling rate in Hz */
static inline unsigned get_full_speed_hz(unsigned int usb_rate)
{
return (usb_rate * 125 + (1 << 12)) >> 13;
}
/* convert our high speed USB rate into sampling rate in Hz */
static inline unsigned get_high_speed_hz(unsigned int usb_rate)
{
return (usb_rate * 125 + (1 << 9)) >> 10;
}
/*
* common proc files to show the usb device info
*/
static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_usb_audio *chip = entry->private_data;
if (!chip->shutdown)
snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum);
}
static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_usb_audio *chip = entry->private_data;
if (!chip->shutdown)
snd_iprintf(buffer, "%04x:%04x\n",
USB_ID_VENDOR(chip->usb_id),
USB_ID_PRODUCT(chip->usb_id));
}
void snd_usb_audio_create_proc(struct snd_usb_audio *chip)
{
struct snd_info_entry *entry;
if (!snd_card_proc_new(chip->card, "usbbus", &entry))
snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read);
if (!snd_card_proc_new(chip->card, "usbid", &entry))
snd_info_set_text_ops(entry, chip, proc_audio_usbid_read);
}
/*
* proc interface for list the supported pcm formats
*/
static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
{
struct list_head *p;
static char *sync_types[4] = {
"NONE", "ASYNC", "ADAPTIVE", "SYNC"
};
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
snd_pcm_format_t fmt;
fp = list_entry(p, struct audioformat, list);
snd_iprintf(buffer, " Interface %d\n", fp->iface);
snd_iprintf(buffer, " Altset %d\n", fp->altsetting);
snd_iprintf(buffer, " Format:");
for (fmt = 0; fmt <= SNDRV_PCM_FORMAT_LAST; ++fmt)
if (fp->formats & (1uLL << fmt))
snd_iprintf(buffer, " %s",
snd_pcm_format_name(fmt));
snd_iprintf(buffer, "\n");
snd_iprintf(buffer, " Channels: %d\n", fp->channels);
snd_iprintf(buffer, " Endpoint: %d %s (%s)\n",
fp->endpoint & USB_ENDPOINT_NUMBER_MASK,
fp->endpoint & USB_DIR_IN ? "IN" : "OUT",
sync_types[(fp->ep_attr & USB_ENDPOINT_SYNCTYPE) >> 2]);
if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) {
snd_iprintf(buffer, " Rates: %d - %d (continuous)\n",
fp->rate_min, fp->rate_max);
} else {
unsigned int i;
snd_iprintf(buffer, " Rates: ");
for (i = 0; i < fp->nr_rates; i++) {
if (i > 0)
snd_iprintf(buffer, ", ");
snd_iprintf(buffer, "%d", fp->rate_table[i]);
}
snd_iprintf(buffer, "\n");
}
if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
snd_iprintf(buffer, " Data packet interval: %d us\n",
125 * (1 << fp->datainterval));
// snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize);
// snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes);
}
}
static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
{
if (subs->running) {
unsigned int i;
snd_iprintf(buffer, " Status: Running\n");
snd_iprintf(buffer, " Interface = %d\n", subs->interface);
snd_iprintf(buffer, " Altset = %d\n", subs->altset_idx);
snd_iprintf(buffer, " URBs = %d [ ", subs->nurbs);
for (i = 0; i < subs->nurbs; i++)
snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
snd_iprintf(buffer, "]\n");
snd_iprintf(buffer, " Packet Size = %d\n", subs->curpacksize);
snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n",
snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
? get_full_speed_hz(subs->freqm)
: get_high_speed_hz(subs->freqm),
subs->freqm >> 16, subs->freqm & 0xffff);
} else {
snd_iprintf(buffer, " Status: Stop\n");
}
}
static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_usb_stream *stream = entry->private_data;
snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name);
if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) {
snd_iprintf(buffer, "\nPlayback:\n");
proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
}
if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) {
snd_iprintf(buffer, "\nCapture:\n");
proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
}
}
void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream)
{
struct snd_info_entry *entry;
char name[32];
struct snd_card *card = stream->chip->card;
sprintf(name, "stream%d", stream->pcm_index);
if (!snd_card_proc_new(card, name, &entry))
snd_info_set_text_ops(entry, stream, proc_pcm_format_read);
}

8
sound/usb/proc.h 100644
View File

@ -0,0 +1,8 @@
#ifndef __USBAUDIO_PROC_H
#define __USBAUDIO_PROC_H
void snd_usb_audio_create_proc(struct snd_usb_audio *chip);
void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream);
#endif /* __USBAUDIO_PROC_H */

View File

@ -279,7 +279,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.ifnum = 0,
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = & (const struct audioformat) {
.format = SNDRV_PCM_FORMAT_S16_LE,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 4,
.iface = 0,
.altsetting = 1,
@ -296,7 +296,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.ifnum = 1,
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = & (const struct audioformat) {
.format = SNDRV_PCM_FORMAT_S16_LE,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 2,
.iface = 1,
.altsetting = 1,
@ -580,7 +580,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.ifnum = 0,
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = & (const struct audioformat) {
.format = SNDRV_PCM_FORMAT_S24_3LE,
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 0,
.altsetting = 1,
@ -597,7 +597,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.ifnum = 1,
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = & (const struct audioformat) {
.format = SNDRV_PCM_FORMAT_S24_3LE,
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 1,
.altsetting = 1,
@ -793,7 +793,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.ifnum = 1,
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = & (const struct audioformat) {
.format = SNDRV_PCM_FORMAT_S24_3LE,
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 1,
.altsetting = 1,
@ -810,7 +810,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.ifnum = 2,
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = & (const struct audioformat) {
.format = SNDRV_PCM_FORMAT_S24_3LE,
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 2,
.altsetting = 1,
@ -1826,6 +1826,60 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
{
USB_DEVICE(0x0763, 0x2080),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra 8", */
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = & (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
.type = QUIRK_IGNORE_INTERFACE
},
{
.ifnum = 1,
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = 2,
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
/* interface 3 (MIDI) is standard compliant */
{
.ifnum = -1
}
}
}
},
{
USB_DEVICE(0x0763, 0x2081),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra 8R", */
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = & (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
.type = QUIRK_IGNORE_INTERFACE
},
{
.ifnum = 1,
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = 2,
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
/* interface 3 (MIDI) is standard compliant */
{
.ifnum = -1
}
}
}
},
/* Casio devices */
{
@ -2203,7 +2257,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.ifnum = 1,
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = &(const struct audioformat) {
.format = SNDRV_PCM_FORMAT_S24_3BE,
.formats = SNDRV_PCM_FMTBIT_S24_3BE,
.channels = 2,
.iface = 1,
.altsetting = 1,

594
sound/usb/quirks.c 100644
View File

@ -0,0 +1,594 @@
/*
* 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/info.h>
#include <sound/pcm.h>
#include "usbaudio.h"
#include "card.h"
#include "mixer.h"
#include "mixer_quirks.h"
#include "midi.h"
#include "quirks.h"
#include "helper.h"
#include "endpoint.h"
#include "pcm.h"
/*
* handle the quirks for the contained interfaces
*/
static int create_composite_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk)
{
int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
int err;
for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) {
iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
if (!iface)
continue;
if (quirk->ifnum != probed_ifnum &&
usb_interface_claimed(iface))
continue;
err = snd_usb_create_quirk(chip, iface, driver, quirk);
if (err < 0)
return err;
if (quirk->ifnum != probed_ifnum)
usb_driver_claim_interface(driver, iface, (void *)-1L);
}
return 0;
}
static int ignore_interface_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk)
{
return 0;
}
/*
* Allow alignment on audio sub-slot (channel samples) rather than
* on audio slots (audio frames)
*/
static int create_align_transfer_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk)
{
chip->txfr_quirk = 1;
return 1; /* Continue with creating streams and mixer */
}
static int create_any_midi_quirk(struct snd_usb_audio *chip,
struct usb_interface *intf,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk)
{
return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk);
}
/*
* create a stream for an interface with proper descriptors
*/
static int create_standard_audio_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk)
{
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
int err;
alts = &iface->altsetting[0];
altsd = get_iface_desc(alts);
err = snd_usb_parse_audio_endpoints(chip, altsd->bInterfaceNumber);
if (err < 0) {
snd_printk(KERN_ERR "cannot setup if %d: error %d\n",
altsd->bInterfaceNumber, err);
return err;
}
/* reset the current interface */
usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0);
return 0;
}
/*
* create a stream for an endpoint/altsetting without proper descriptors
*/
static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk)
{
struct audioformat *fp;
struct usb_host_interface *alts;
int stream, err;
unsigned *rate_table = NULL;
fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
if (! fp) {
snd_printk(KERN_ERR "cannot memdup\n");
return -ENOMEM;
}
if (fp->nr_rates > 0) {
rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL);
if (!rate_table) {
kfree(fp);
return -ENOMEM;
}
memcpy(rate_table, fp->rate_table, sizeof(int) * fp->nr_rates);
fp->rate_table = rate_table;
}
stream = (fp->endpoint & USB_DIR_IN)
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_endpoint(chip, stream, fp);
if (err < 0) {
kfree(fp);
kfree(rate_table);
return err;
}
if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
fp->altset_idx >= iface->num_altsetting) {
kfree(fp);
kfree(rate_table);
return -EINVAL;
}
alts = &iface->altsetting[fp->altset_idx];
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
usb_set_interface(chip->dev, fp->iface, 0);
snd_usb_init_pitch(chip, fp->iface, alts, fp);
snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
return 0;
}
/*
* Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.
* The only way to detect the sample rate is by looking at wMaxPacketSize.
*/
static int create_uaxx_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk)
{
static const struct audioformat ua_format = {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.fmt_type = UAC_FORMAT_TYPE_I,
.altsetting = 1,
.altset_idx = 1,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
};
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
struct audioformat *fp;
int stream, err;
/* both PCM and MIDI interfaces have 2 or more altsettings */
if (iface->num_altsetting < 2)
return -ENXIO;
alts = &iface->altsetting[1];
altsd = get_iface_desc(alts);
if (altsd->bNumEndpoints == 2) {
static const struct snd_usb_midi_endpoint_info ua700_ep = {
.out_cables = 0x0003,
.in_cables = 0x0003
};
static const struct snd_usb_audio_quirk ua700_quirk = {
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = &ua700_ep
};
static const struct snd_usb_midi_endpoint_info uaxx_ep = {
.out_cables = 0x0001,
.in_cables = 0x0001
};
static const struct snd_usb_audio_quirk uaxx_quirk = {
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = &uaxx_ep
};
const struct snd_usb_audio_quirk *quirk =
chip->usb_id == USB_ID(0x0582, 0x002b)
? &ua700_quirk : &uaxx_quirk;
return snd_usbmidi_create(chip->card, iface,
&chip->midi_list, quirk);
}
if (altsd->bNumEndpoints != 1)
return -ENXIO;
fp = kmalloc(sizeof(*fp), GFP_KERNEL);
if (!fp)
return -ENOMEM;
memcpy(fp, &ua_format, sizeof(*fp));
fp->iface = altsd->bInterfaceNumber;
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = 0;
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
switch (fp->maxpacksize) {
case 0x120:
fp->rate_max = fp->rate_min = 44100;
break;
case 0x138:
case 0x140:
fp->rate_max = fp->rate_min = 48000;
break;
case 0x258:
case 0x260:
fp->rate_max = fp->rate_min = 96000;
break;
default:
snd_printk(KERN_ERR "unknown sample rate\n");
kfree(fp);
return -ENXIO;
}
stream = (fp->endpoint & USB_DIR_IN)
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_endpoint(chip, stream, fp);
if (err < 0) {
kfree(fp);
return err;
}
usb_set_interface(chip->dev, fp->iface, 0);
return 0;
}
/*
* audio-interface quirks
*
* returns zero if no standard audio/MIDI parsing is needed.
* returns a postive value if standard audio/midi interfaces are parsed
* after this.
* returns a negative value at error.
*/
int snd_usb_create_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk)
{
typedef int (*quirk_func_t)(struct snd_usb_audio *,
struct usb_interface *,
struct usb_driver *,
const struct snd_usb_audio_quirk *);
static const quirk_func_t quirk_funcs[] = {
[QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
[QUIRK_COMPOSITE] = create_composite_quirk,
[QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
[QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
[QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
[QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
[QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
[QUIRK_MIDI_FASTLANE] = create_any_midi_quirk,
[QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
[QUIRK_MIDI_CME] = create_any_midi_quirk,
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
[QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk
};
if (quirk->type < QUIRK_TYPE_COUNT) {
return quirk_funcs[quirk->type](chip, iface, driver, quirk);
} else {
snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
return -ENXIO;
}
}
/*
* boot quirks
*/
#define EXTIGY_FIRMWARE_SIZE_OLD 794
#define EXTIGY_FIRMWARE_SIZE_NEW 483
static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
{
struct usb_host_config *config = dev->actconfig;
int err;
if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) {
snd_printdd("sending Extigy boot sequence...\n");
/* Send message to force it to reconnect with full interface. */
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0),
0x10, 0x43, 0x0001, 0x000a, NULL, 0, 1000);
if (err < 0) snd_printdd("error sending boot message: %d\n", err);
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
&dev->descriptor, sizeof(dev->descriptor));
config = dev->actconfig;
if (err < 0) snd_printdd("error usb_get_descriptor: %d\n", err);
err = usb_reset_configuration(dev);
if (err < 0) snd_printdd("error usb_reset_configuration: %d\n", err);
snd_printdd("extigy_boot: new boot length = %d\n",
le16_to_cpu(get_cfg_desc(config)->wTotalLength));
return -ENODEV; /* quit this anyway */
}
return 0;
}
static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
{
u8 buf = 1;
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
0, 0, &buf, 1, 1000);
if (buf == 0) {
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1, 2000, NULL, 0, 1000);
return -ENODEV;
}
return 0;
}
/*
* C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
* documented in the device's data sheet.
*/
static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value)
{
u8 buf[4];
buf[0] = 0x20;
buf[1] = value & 0xff;
buf[2] = (value >> 8) & 0xff;
buf[3] = reg;
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
0, 0, &buf, 4, 1000);
}
static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
{
/*
* Enable line-out driver mode, set headphone source to front
* channels, enable stereo mic.
*/
return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
}
/*
* C-Media CM6206 is based on CM106 with two additional
* registers that are not documented in the data sheet.
* Values here are chosen based on sniffing USB traffic
* under Windows.
*/
static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
{
int err, reg;
int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
if (err < 0)
return err;
}
return err;
}
/*
* This call will put the synth in "USB send" mode, i.e it will send MIDI
* messages through USB (this is disabled at startup). The synth will
* acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB
* sign on its LCD. Values here are chosen based on sniffing USB traffic
* under Windows.
*/
static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
{
int err, actual_length;
/* "midi send" enable */
static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 };
void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
if (!buf)
return -ENOMEM;
err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf,
ARRAY_SIZE(seq), &actual_length, 1000);
kfree(buf);
if (err < 0)
return err;
return 0;
}
/*
* Setup quirks
*/
#define AUDIOPHILE_SET 0x01 /* if set, parse device_setup */
#define AUDIOPHILE_SET_DTS 0x02 /* if set, enable DTS Digital Output */
#define AUDIOPHILE_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */
#define AUDIOPHILE_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */
#define AUDIOPHILE_SET_DI 0x10 /* if set, enable Digital Input */
#define AUDIOPHILE_SET_MASK 0x1F /* bit mask for setup value */
#define AUDIOPHILE_SET_24B_48K_DI 0x19 /* value for 24bits+48KHz+Digital Input */
#define AUDIOPHILE_SET_24B_48K_NOTDI 0x09 /* value for 24bits+48KHz+No Digital Input */
#define AUDIOPHILE_SET_16B_48K_DI 0x11 /* value for 16bits+48KHz+Digital Input */
#define AUDIOPHILE_SET_16B_48K_NOTDI 0x01 /* value for 16bits+48KHz+No Digital Input */
static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
int iface,
int altno)
{
/* Reset ALL ifaces to 0 altsetting.
* Call it for every possible altsetting of every interface.
*/
usb_set_interface(chip->dev, iface, 0);
if (chip->setup & AUDIOPHILE_SET) {
if ((chip->setup & AUDIOPHILE_SET_DTS)
&& altno != 6)
return 1; /* skip this altsetting */
if ((chip->setup & AUDIOPHILE_SET_96K)
&& altno != 1)
return 1; /* skip this altsetting */
if ((chip->setup & AUDIOPHILE_SET_MASK) ==
AUDIOPHILE_SET_24B_48K_DI && altno != 2)
return 1; /* skip this altsetting */
if ((chip->setup & AUDIOPHILE_SET_MASK) ==
AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3)
return 1; /* skip this altsetting */
if ((chip->setup & AUDIOPHILE_SET_MASK) ==
AUDIOPHILE_SET_16B_48K_DI && altno != 4)
return 1; /* skip this altsetting */
if ((chip->setup & AUDIOPHILE_SET_MASK) ==
AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5)
return 1; /* skip this altsetting */
}
return 0; /* keep this altsetting */
}
int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
int iface,
int altno)
{
/* audiophile usb: skip altsets incompatible with device_setup */
if (chip->usb_id == USB_ID(0x0763, 0x2003))
return audiophile_skip_setting_quirk(chip, iface, altno);
return 0;
}
int snd_usb_apply_boot_quirk(struct usb_device *dev,
struct usb_interface *intf,
const struct snd_usb_audio_quirk *quirk)
{
u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
/* SB Extigy needs special boot-up sequence */
/* if more models come, this will go to the quirk list. */
if (id == USB_ID(0x041e, 0x3000))
return snd_usb_extigy_boot_quirk(dev, intf);
/* SB Audigy 2 NX needs its own boot-up magic, too */
if (id == USB_ID(0x041e, 0x3020))
return snd_usb_audigy2nx_boot_quirk(dev);
/* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
if (id == USB_ID(0x10f5, 0x0200))
return snd_usb_cm106_boot_quirk(dev);
/* C-Media CM6206 / CM106-Like Sound Device */
if (id == USB_ID(0x0d8c, 0x0102))
return snd_usb_cm6206_boot_quirk(dev);
/* Access Music VirusTI Desktop */
if (id == USB_ID(0x133e, 0x0815))
return snd_usb_accessmusic_boot_quirk(dev);
return 0;
}
/*
* check if the device uses big-endian samples
*/
int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp)
{
switch (chip->usb_id) {
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
if (fp->endpoint & USB_DIR_IN)
return 1;
break;
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
if (chip->setup == 0x00 ||
fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
return 1;
}
return 0;
}
/*
* For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device,
* not for interface.
*/
enum {
EMU_QUIRK_SR_44100HZ = 0,
EMU_QUIRK_SR_48000HZ,
EMU_QUIRK_SR_88200HZ,
EMU_QUIRK_SR_96000HZ,
EMU_QUIRK_SR_176400HZ,
EMU_QUIRK_SR_192000HZ
};
static void set_format_emu_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt)
{
unsigned char emu_samplerate_id = 0;
/* When capture is active
* sample rate shouldn't be changed
* by playback substream
*/
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1)
return;
}
switch (fmt->rate_min) {
case 48000:
emu_samplerate_id = EMU_QUIRK_SR_48000HZ;
break;
case 88200:
emu_samplerate_id = EMU_QUIRK_SR_88200HZ;
break;
case 96000:
emu_samplerate_id = EMU_QUIRK_SR_96000HZ;
break;
case 176400:
emu_samplerate_id = EMU_QUIRK_SR_176400HZ;
break;
case 192000:
emu_samplerate_id = EMU_QUIRK_SR_192000HZ;
break;
default:
emu_samplerate_id = EMU_QUIRK_SR_44100HZ;
break;
}
snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
}
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt)
{
switch (subs->stream->chip->usb_id) {
case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
set_format_emu_quirk(subs, fmt);
break;
}
}

23
sound/usb/quirks.h 100644
View File

@ -0,0 +1,23 @@
#ifndef __USBAUDIO_QUIRKS_H
#define __USBAUDIO_QUIRKS_H
int snd_usb_create_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
const struct snd_usb_audio_quirk *quirk);
int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
int iface,
int altno);
int snd_usb_apply_boot_quirk(struct usb_device *dev,
struct usb_interface *intf,
const struct snd_usb_audio_quirk *quirk);
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt);
int snd_usb_is_big_endian_format(struct snd_usb_audio *chip,
struct audioformat *fp);
#endif /* __USBAUDIO_QUIRKS_H */

995
sound/usb/urb.c 100644
View File

@ -0,0 +1,995 @@
/*
* 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/gfp.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include "usbaudio.h"
#include "helper.h"
#include "card.h"
#include "urb.h"
#include "pcm.h"
/*
* convert a sampling rate into our full speed format (fs/1000 in Q16.16)
* this will overflow at approx 524 kHz
*/
static inline unsigned get_usb_full_speed_rate(unsigned int rate)
{
return ((rate << 13) + 62) / 125;
}
/*
* convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
* this will overflow at approx 4 MHz
*/
static inline unsigned get_usb_high_speed_rate(unsigned int rate)
{
return ((rate << 10) + 62) / 125;
}
/*
* unlink active urbs.
*/
static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
{
struct snd_usb_audio *chip = subs->stream->chip;
unsigned int i;
int async;
subs->running = 0;
if (!force && subs->stream->chip->shutdown) /* to be sure... */
return -EBADFD;
async = !can_sleep && chip->async_unlink;
if (!async && in_interrupt())
return 0;
for (i = 0; i < subs->nurbs; i++) {
if (test_bit(i, &subs->active_mask)) {
if (!test_and_set_bit(i, &subs->unlink_mask)) {
struct urb *u = subs->dataurb[i].urb;
if (async)
usb_unlink_urb(u);
else
usb_kill_urb(u);
}
}
}
if (subs->syncpipe) {
for (i = 0; i < SYNC_URBS; i++) {
if (test_bit(i+16, &subs->active_mask)) {
if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
struct urb *u = subs->syncurb[i].urb;
if (async)
usb_unlink_urb(u);
else
usb_kill_urb(u);
}
}
}
}
return 0;
}
/*
* release a urb data
*/
static void release_urb_ctx(struct snd_urb_ctx *u)
{
if (u->urb) {
if (u->buffer_size)
usb_buffer_free(u->subs->dev, u->buffer_size,
u->urb->transfer_buffer,
u->urb->transfer_dma);
usb_free_urb(u->urb);
u->urb = NULL;
}
}
/*
* wait until all urbs are processed.
*/
static int wait_clear_urbs(struct snd_usb_substream *subs)
{
unsigned long end_time = jiffies + msecs_to_jiffies(1000);
unsigned int i;
int alive;
do {
alive = 0;
for (i = 0; i < subs->nurbs; i++) {
if (test_bit(i, &subs->active_mask))
alive++;
}
if (subs->syncpipe) {
for (i = 0; i < SYNC_URBS; i++) {
if (test_bit(i + 16, &subs->active_mask))
alive++;
}
}
if (! alive)
break;
schedule_timeout_uninterruptible(1);
} while (time_before(jiffies, end_time));
if (alive)
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
return 0;
}
/*
* release a substream
*/
void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
{
int i;
/* stop urbs (to be sure) */
deactivate_urbs(subs, force, 1);
wait_clear_urbs(subs);
for (i = 0; i < MAX_URBS; i++)
release_urb_ctx(&subs->dataurb[i]);
for (i = 0; i < SYNC_URBS; i++)
release_urb_ctx(&subs->syncurb[i]);
usb_buffer_free(subs->dev, SYNC_URBS * 4,
subs->syncbuf, subs->sync_dma);
subs->syncbuf = NULL;
subs->nurbs = 0;
}
/*
* complete callback from data urb
*/
static void snd_complete_urb(struct urb *urb)
{
struct snd_urb_ctx *ctx = urb->context;
struct snd_usb_substream *subs = ctx->subs;
struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
int err = 0;
if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
!subs->running || /* can be stopped during retire callback */
(err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
(err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
clear_bit(ctx->index, &subs->active_mask);
if (err < 0) {
snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
}
}
}
/*
* complete callback from sync urb
*/
static void snd_complete_sync_urb(struct urb *urb)
{
struct snd_urb_ctx *ctx = urb->context;
struct snd_usb_substream *subs = ctx->subs;
struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
int err = 0;
if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
!subs->running || /* can be stopped during retire callback */
(err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
(err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
clear_bit(ctx->index + 16, &subs->active_mask);
if (err < 0) {
snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
}
}
}
/*
* initialize a substream for plaback/capture
*/
int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
unsigned int period_bytes,
unsigned int rate,
unsigned int frame_bits)
{
unsigned int maxsize, i;
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int urb_packs, total_packs, packs_per_ms;
struct snd_usb_audio *chip = subs->stream->chip;
/* calculate the frequency in 16.16 format */
if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
subs->freqn = get_usb_full_speed_rate(rate);
else
subs->freqn = get_usb_high_speed_rate(rate);
subs->freqm = subs->freqn;
/* calculate max. frequency */
if (subs->maxpacksize) {
/* whatever fits into a max. size packet */
maxsize = subs->maxpacksize;
subs->freqmax = (maxsize / (frame_bits >> 3))
<< (16 - subs->datainterval);
} else {
/* no max. packet size: just take 25% higher than nominal */
subs->freqmax = subs->freqn + (subs->freqn >> 2);
maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
>> (16 - subs->datainterval);
}
subs->phase = 0;
if (subs->fill_max)
subs->curpacksize = subs->maxpacksize;
else
subs->curpacksize = maxsize;
if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
packs_per_ms = 8 >> subs->datainterval;
else
packs_per_ms = 1;
if (is_playback) {
urb_packs = max(chip->nrpacks, 1);
urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
} else
urb_packs = 1;
urb_packs *= packs_per_ms;
if (subs->syncpipe)
urb_packs = min(urb_packs, 1U << subs->syncinterval);
/* decide how many packets to be used */
if (is_playback) {
unsigned int minsize, maxpacks;
/* determine how small a packet can be */
minsize = (subs->freqn >> (16 - subs->datainterval))
* (frame_bits >> 3);
/* with sync from device, assume it can be 12% lower */
if (subs->syncpipe)
minsize -= minsize >> 3;
minsize = max(minsize, 1u);
total_packs = (period_bytes + minsize - 1) / minsize;
/* we need at least two URBs for queueing */
if (total_packs < 2) {
total_packs = 2;
} else {
/* and we don't want too long a queue either */
maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
total_packs = min(total_packs, maxpacks);
}
} else {
while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
urb_packs >>= 1;
total_packs = MAX_URBS * urb_packs;
}
subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
if (subs->nurbs > MAX_URBS) {
/* too much... */
subs->nurbs = MAX_URBS;
total_packs = MAX_URBS * urb_packs;
} else if (subs->nurbs < 2) {
/* too little - we need at least two packets
* to ensure contiguous playback/capture
*/
subs->nurbs = 2;
}
/* allocate and initialize data urbs */
for (i = 0; i < subs->nurbs; i++) {
struct snd_urb_ctx *u = &subs->dataurb[i];
u->index = i;
u->subs = subs;
u->packets = (i + 1) * total_packs / subs->nurbs
- i * total_packs / subs->nurbs;
u->buffer_size = maxsize * u->packets;
if (subs->fmt_type == UAC_FORMAT_TYPE_II)
u->packets++; /* for transfer delimiter */
u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
if (!u->urb)
goto out_of_memory;
u->urb->transfer_buffer =
usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL,
&u->urb->transfer_dma);
if (!u->urb->transfer_buffer)
goto out_of_memory;
u->urb->pipe = subs->datapipe;
u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
u->urb->interval = 1 << subs->datainterval;
u->urb->context = u;
u->urb->complete = snd_complete_urb;
}
if (subs->syncpipe) {
/* allocate and initialize sync urbs */
subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4,
GFP_KERNEL, &subs->sync_dma);
if (!subs->syncbuf)
goto out_of_memory;
for (i = 0; i < SYNC_URBS; i++) {
struct snd_urb_ctx *u = &subs->syncurb[i];
u->index = i;
u->subs = subs;
u->packets = 1;
u->urb = usb_alloc_urb(1, GFP_KERNEL);
if (!u->urb)
goto out_of_memory;
u->urb->transfer_buffer = subs->syncbuf + i * 4;
u->urb->transfer_dma = subs->sync_dma + i * 4;
u->urb->transfer_buffer_length = 4;
u->urb->pipe = subs->syncpipe;
u->urb->transfer_flags = URB_ISO_ASAP |
URB_NO_TRANSFER_DMA_MAP;
u->urb->number_of_packets = 1;
u->urb->interval = 1 << subs->syncinterval;
u->urb->context = u;
u->urb->complete = snd_complete_sync_urb;
}
}
return 0;
out_of_memory:
snd_usb_release_substream_urbs(subs, 0);
return -ENOMEM;
}
/*
* prepare urb for full speed capture sync pipe
*
* fill the length and offset of each urb descriptor.
* the fixed 10.14 frequency is passed through the pipe.
*/
static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned char *cp = urb->transfer_buffer;
struct snd_urb_ctx *ctx = urb->context;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 3;
urb->iso_frame_desc[0].offset = 0;
cp[0] = subs->freqn >> 2;
cp[1] = subs->freqn >> 10;
cp[2] = subs->freqn >> 18;
return 0;
}
/*
* prepare urb for high speed capture sync pipe
*
* fill the length and offset of each urb descriptor.
* the fixed 12.13 frequency is passed as 16.16 through the pipe.
*/
static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned char *cp = urb->transfer_buffer;
struct snd_urb_ctx *ctx = urb->context;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 4;
urb->iso_frame_desc[0].offset = 0;
cp[0] = subs->freqn;
cp[1] = subs->freqn >> 8;
cp[2] = subs->freqn >> 16;
cp[3] = subs->freqn >> 24;
return 0;
}
/*
* process after capture sync complete
* - nothing to do
*/
static int retire_capture_sync_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
return 0;
}
/*
* prepare urb for capture data pipe
*
* fill the offset and length of each descriptor.
*
* we use a temporary buffer to write the captured data.
* since the length of written data is determined by host, we cannot
* write onto the pcm buffer directly... the data is thus copied
* later at complete callback to the global buffer.
*/
static int prepare_capture_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
int i, offs;
struct snd_urb_ctx *ctx = urb->context;
offs = 0;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
for (i = 0; i < ctx->packets; i++) {
urb->iso_frame_desc[i].offset = offs;
urb->iso_frame_desc[i].length = subs->curpacksize;
offs += subs->curpacksize;
}
urb->transfer_buffer_length = offs;
urb->number_of_packets = ctx->packets;
return 0;
}
/*
* process after capture complete
*
* copy the data from each desctiptor to the pcm buffer, and
* update the current position.
*/
static int retire_capture_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned long flags;
unsigned char *cp;
int i;
unsigned int stride, frames, bytes, oldptr;
int period_elapsed = 0;
stride = runtime->frame_bits >> 3;
for (i = 0; i < urb->number_of_packets; i++) {
cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (urb->iso_frame_desc[i].status) {
snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
// continue;
}
bytes = urb->iso_frame_desc[i].actual_length;
frames = bytes / stride;
if (!subs->txfr_quirk)
bytes = frames * stride;
if (bytes % (runtime->sample_bits >> 3) != 0) {
#ifdef CONFIG_SND_DEBUG_VERBOSE
int oldbytes = bytes;
#endif
bytes = frames * stride;
snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
oldbytes, bytes);
}
/* update the current pointer */
spin_lock_irqsave(&subs->lock, flags);
oldptr = subs->hwptr_done;
subs->hwptr_done += bytes;
if (subs->hwptr_done >= runtime->buffer_size * stride)
subs->hwptr_done -= runtime->buffer_size * stride;
frames = (bytes + (oldptr % stride)) / stride;
subs->transfer_done += frames;
if (subs->transfer_done >= runtime->period_size) {
subs->transfer_done -= runtime->period_size;
period_elapsed = 1;
}
spin_unlock_irqrestore(&subs->lock, flags);
/* copy a data chunk */
if (oldptr + bytes > runtime->buffer_size * stride) {
unsigned int bytes1 =
runtime->buffer_size * stride - oldptr;
memcpy(runtime->dma_area + oldptr, cp, bytes1);
memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
} else {
memcpy(runtime->dma_area + oldptr, cp, bytes);
}
}
if (period_elapsed)
snd_pcm_period_elapsed(subs->pcm_substream);
return 0;
}
/*
* Process after capture complete when paused. Nothing to do.
*/
static int retire_paused_capture_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
return 0;
}
/*
* prepare urb for full speed playback sync pipe
*
* set up the offset and length to receive the current frequency.
*/
static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
struct snd_urb_ctx *ctx = urb->context;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 3;
urb->iso_frame_desc[0].offset = 0;
return 0;
}
/*
* prepare urb for high speed playback sync pipe
*
* set up the offset and length to receive the current frequency.
*/
static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
struct snd_urb_ctx *ctx = urb->context;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 4;
urb->iso_frame_desc[0].offset = 0;
return 0;
}
/*
* process after full speed playback sync complete
*
* retrieve the current 10.14 frequency from pipe, and set it.
* the value is referred in prepare_playback_urb().
*/
static int retire_playback_sync_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned int f;
unsigned long flags;
if (urb->iso_frame_desc[0].status == 0 &&
urb->iso_frame_desc[0].actual_length == 3) {
f = combine_triple((u8*)urb->transfer_buffer) << 2;
if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
spin_lock_irqsave(&subs->lock, flags);
subs->freqm = f;
spin_unlock_irqrestore(&subs->lock, flags);
}
}
return 0;
}
/*
* process after high speed playback sync complete
*
* retrieve the current 12.13 frequency from pipe, and set it.
* the value is referred in prepare_playback_urb().
*/
static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned int f;
unsigned long flags;
if (urb->iso_frame_desc[0].status == 0 &&
urb->iso_frame_desc[0].actual_length == 4) {
f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
spin_lock_irqsave(&subs->lock, flags);
subs->freqm = f;
spin_unlock_irqrestore(&subs->lock, flags);
}
}
return 0;
}
/*
* process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
*
* These devices return the number of samples per packet instead of the number
* of samples per microframe.
*/
static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned int f;
unsigned long flags;
if (urb->iso_frame_desc[0].status == 0 &&
urb->iso_frame_desc[0].actual_length == 4) {
f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
f >>= subs->datainterval;
if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
spin_lock_irqsave(&subs->lock, flags);
subs->freqm = f;
spin_unlock_irqrestore(&subs->lock, flags);
}
}
return 0;
}
/* determine the number of frames in the next packet */
static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
{
if (subs->fill_max)
return subs->maxframesize;
else {
subs->phase = (subs->phase & 0xffff)
+ (subs->freqm << subs->datainterval);
return min(subs->phase >> 16, subs->maxframesize);
}
}
/*
* Prepare urb for streaming before playback starts or when paused.
*
* We don't have any data, so we send silence.
*/
static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned int i, offs, counts;
struct snd_urb_ctx *ctx = urb->context;
int stride = runtime->frame_bits >> 3;
offs = 0;
urb->dev = ctx->subs->dev;
for (i = 0; i < ctx->packets; ++i) {
counts = snd_usb_audio_next_packet_size(subs);
urb->iso_frame_desc[i].offset = offs * stride;
urb->iso_frame_desc[i].length = counts * stride;
offs += counts;
}
urb->number_of_packets = ctx->packets;
urb->transfer_buffer_length = offs * stride;
memset(urb->transfer_buffer,
runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
offs * stride);
return 0;
}
/*
* prepare urb for playback data pipe
*
* Since a URB can handle only a single linear buffer, we must use double
* buffering when the data to be transferred overflows the buffer boundary.
* To avoid inconsistencies when updating hwptr_done, we use double buffering
* for all URBs.
*/
static int prepare_playback_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
int i, stride;
unsigned int counts, frames, bytes;
unsigned long flags;
int period_elapsed = 0;
struct snd_urb_ctx *ctx = urb->context;
stride = runtime->frame_bits >> 3;
frames = 0;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->number_of_packets = 0;
spin_lock_irqsave(&subs->lock, flags);
for (i = 0; i < ctx->packets; i++) {
counts = snd_usb_audio_next_packet_size(subs);
/* set up descriptor */
urb->iso_frame_desc[i].offset = frames * stride;
urb->iso_frame_desc[i].length = counts * stride;
frames += counts;
urb->number_of_packets++;
subs->transfer_done += counts;
if (subs->transfer_done >= runtime->period_size) {
subs->transfer_done -= runtime->period_size;
period_elapsed = 1;
if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
if (subs->transfer_done > 0) {
/* FIXME: fill-max mode is not
* supported yet */
frames -= subs->transfer_done;
counts -= subs->transfer_done;
urb->iso_frame_desc[i].length =
counts * stride;
subs->transfer_done = 0;
}
i++;
if (i < ctx->packets) {
/* add a transfer delimiter */
urb->iso_frame_desc[i].offset =
frames * stride;
urb->iso_frame_desc[i].length = 0;
urb->number_of_packets++;
}
break;
}
}
if (period_elapsed) /* finish at the period boundary */
break;
}
bytes = frames * stride;
if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
/* err, the transferred area goes over buffer boundary. */
unsigned int bytes1 =
runtime->buffer_size * stride - subs->hwptr_done;
memcpy(urb->transfer_buffer,
runtime->dma_area + subs->hwptr_done, bytes1);
memcpy(urb->transfer_buffer + bytes1,
runtime->dma_area, bytes - bytes1);
} else {
memcpy(urb->transfer_buffer,
runtime->dma_area + subs->hwptr_done, bytes);
}
subs->hwptr_done += bytes;
if (subs->hwptr_done >= runtime->buffer_size * stride)
subs->hwptr_done -= runtime->buffer_size * stride;
runtime->delay += frames;
spin_unlock_irqrestore(&subs->lock, flags);
urb->transfer_buffer_length = bytes;
if (period_elapsed)
snd_pcm_period_elapsed(subs->pcm_substream);
return 0;
}
/*
* process after playback data complete
* - decrease the delay count again
*/
static int retire_playback_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned long flags;
int stride = runtime->frame_bits >> 3;
int processed = urb->transfer_buffer_length / stride;
spin_lock_irqsave(&subs->lock, flags);
if (processed > runtime->delay)
runtime->delay = 0;
else
runtime->delay -= processed;
spin_unlock_irqrestore(&subs->lock, flags);
return 0;
}
static const char *usb_error_string(int err)
{
switch (err) {
case -ENODEV:
return "no device";
case -ENOENT:
return "endpoint not enabled";
case -EPIPE:
return "endpoint stalled";
case -ENOSPC:
return "not enough bandwidth";
case -ESHUTDOWN:
return "device disabled";
case -EHOSTUNREACH:
return "device suspended";
case -EINVAL:
case -EAGAIN:
case -EFBIG:
case -EMSGSIZE:
return "internal error";
default:
return "unknown error";
}
}
/*
* set up and start data/sync urbs
*/
static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
{
unsigned int i;
int err;
if (subs->stream->chip->shutdown)
return -EBADFD;
for (i = 0; i < subs->nurbs; i++) {
if (snd_BUG_ON(!subs->dataurb[i].urb))
return -EINVAL;
if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
goto __error;
}
}
if (subs->syncpipe) {
for (i = 0; i < SYNC_URBS; i++) {
if (snd_BUG_ON(!subs->syncurb[i].urb))
return -EINVAL;
if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
goto __error;
}
}
}
subs->active_mask = 0;
subs->unlink_mask = 0;
subs->running = 1;
for (i = 0; i < subs->nurbs; i++) {
err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
if (err < 0) {
snd_printk(KERN_ERR "cannot submit datapipe "
"for urb %d, error %d: %s\n",
i, err, usb_error_string(err));
goto __error;
}
set_bit(i, &subs->active_mask);
}
if (subs->syncpipe) {
for (i = 0; i < SYNC_URBS; i++) {
err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
if (err < 0) {
snd_printk(KERN_ERR "cannot submit syncpipe "
"for urb %d, error %d: %s\n",
i, err, usb_error_string(err));
goto __error;
}
set_bit(i + 16, &subs->active_mask);
}
}
return 0;
__error:
// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
deactivate_urbs(subs, 0, 0);
return -EPIPE;
}
/*
*/
static struct snd_urb_ops audio_urb_ops[2] = {
{
.prepare = prepare_nodata_playback_urb,
.retire = retire_playback_urb,
.prepare_sync = prepare_playback_sync_urb,
.retire_sync = retire_playback_sync_urb,
},
{
.prepare = prepare_capture_urb,
.retire = retire_capture_urb,
.prepare_sync = prepare_capture_sync_urb,
.retire_sync = retire_capture_sync_urb,
},
};
static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
{
.prepare = prepare_nodata_playback_urb,
.retire = retire_playback_urb,
.prepare_sync = prepare_playback_sync_urb_hs,
.retire_sync = retire_playback_sync_urb_hs,
},
{
.prepare = prepare_capture_urb,
.retire = retire_capture_urb,
.prepare_sync = prepare_capture_sync_urb_hs,
.retire_sync = retire_capture_sync_urb,
},
};
/*
* initialize the substream instance.
*/
void snd_usb_init_substream(struct snd_usb_stream *as,
int stream, struct audioformat *fp)
{
struct snd_usb_substream *subs = &as->substream[stream];
INIT_LIST_HEAD(&subs->fmt_list);
spin_lock_init(&subs->lock);
subs->stream = as;
subs->direction = stream;
subs->dev = as->chip->dev;
subs->txfr_quirk = as->chip->txfr_quirk;
if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
subs->ops = audio_urb_ops[stream];
} else {
subs->ops = audio_urb_ops_high_speed[stream];
switch (as->chip->usb_id) {
case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
break;
case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */
case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
subs->ops.prepare_sync = prepare_playback_sync_urb;
subs->ops.retire_sync = retire_playback_sync_urb;
break;
}
}
snd_usb_set_pcm_ops(as->pcm, stream);
list_add_tail(&fp->list, &subs->fmt_list);
subs->formats |= fp->formats;
subs->endpoint = fp->endpoint;
subs->num_formats++;
subs->fmt_type = fp->fmt_type;
}
int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_usb_substream *subs = substream->runtime->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
subs->ops.prepare = prepare_playback_urb;
return 0;
case SNDRV_PCM_TRIGGER_STOP:
return deactivate_urbs(subs, 0, 0);
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
subs->ops.prepare = prepare_nodata_playback_urb;
return 0;
}
return -EINVAL;
}
int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_usb_substream *subs = substream->runtime->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
subs->ops.retire = retire_capture_urb;
return start_urbs(subs, substream->runtime);
case SNDRV_PCM_TRIGGER_STOP:
return deactivate_urbs(subs, 0, 0);
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
subs->ops.retire = retire_paused_capture_urb;
return 0;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
subs->ops.retire = retire_capture_urb;
return 0;
}
return -EINVAL;
}
int snd_usb_substream_prepare(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime)
{
/* clear urbs (to be sure) */
deactivate_urbs(subs, 0, 1);
wait_clear_urbs(subs);
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
subs->ops.prepare = prepare_nodata_playback_urb;
return start_urbs(subs, runtime);
}
return 0;
}

21
sound/usb/urb.h 100644
View File

@ -0,0 +1,21 @@
#ifndef __USBAUDIO_URB_H
#define __USBAUDIO_URB_H
void snd_usb_init_substream(struct snd_usb_stream *as,
int stream,
struct audioformat *fp);
int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
unsigned int period_bytes,
unsigned int rate,
unsigned int frame_bits);
void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
int snd_usb_substream_prepare(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime);
int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
#endif /* __USBAUDIO_URB_H */

File diff suppressed because it is too large Load Diff

View File

@ -21,15 +21,13 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* maximum number of endpoints per interface */
#define MIDI_MAX_ENDPOINTS 2
/* handling of USB vendor/product ID pairs as 32-bit numbers */
#define USB_ID(vendor, product) (((vendor) << 16) | (product))
#define USB_ID_VENDOR(id) ((id) >> 16)
#define USB_ID_PRODUCT(id) ((u16)(id))
/*
*
*/
struct snd_usb_audio {
@ -51,6 +49,10 @@ struct snd_usb_audio {
struct list_head midi_list; /* list of midi interfaces */
struct list_head mixer_list; /* list of mixer interfaces */
int setup; /* from the 'device_setup' module param */
int nrpacks; /* from the 'nrpacks' module param */
int async_unlink; /* from the 'async_unlink' module param */
};
/*
@ -89,93 +91,8 @@ struct snd_usb_audio_quirk {
const void *data;
};
/* data for QUIRK_MIDI_FIXED_ENDPOINT */
struct snd_usb_midi_endpoint_info {
int8_t out_ep; /* ep number, 0 autodetect */
uint8_t out_interval; /* interval for interrupt endpoints */
int8_t in_ep;
uint8_t in_interval;
uint16_t out_cables; /* bitmask */
uint16_t in_cables; /* bitmask */
};
/* for QUIRK_MIDI_YAMAHA, data is NULL */
/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info
* structure (out_cables and in_cables only) */
/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk
* structures, terminated with .ifnum = -1 */
/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */
/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */
/* for QUIRK_AUDIO_EDIROL_UAXX, data is NULL */
/* for QUIRK_IGNORE_INTERFACE, data is NULL */
/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */
/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info
* structure (out_cables and in_cables only) */
/* for QUIRK_MIDI_CME, data is NULL */
/*
*/
/*E-mu USB samplerate control quirk*/
enum {
EMU_QUIRK_SR_44100HZ = 0,
EMU_QUIRK_SR_48000HZ,
EMU_QUIRK_SR_88200HZ,
EMU_QUIRK_SR_96000HZ,
EMU_QUIRK_SR_176400HZ,
EMU_QUIRK_SR_192000HZ
};
#define combine_word(s) ((*(s)) | ((unsigned int)(s)[1] << 8))
#define combine_triple(s) (combine_word(s) | ((unsigned int)(s)[2] << 16))
#define combine_quad(s) (combine_triple(s) | ((unsigned int)(s)[3] << 24))
unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size);
void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype);
void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype);
int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value, __u16 index,
void *data, __u16 size, int timeout);
int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
int ignore_error);
void snd_usb_mixer_disconnect(struct list_head *p);
int snd_usbmidi_create(struct snd_card *card,
struct usb_interface *iface,
struct list_head *midi_list,
const struct snd_usb_audio_quirk *quirk);
void snd_usbmidi_input_stop(struct list_head* p);
void snd_usbmidi_input_start(struct list_head* p);
void snd_usbmidi_disconnect(struct list_head *p);
void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
unsigned char samplerate_id);
/*
* retrieve usb_interface descriptor from the host interface
* (conditional for compatibility with the older API)
*/
#ifndef get_iface_desc
#define get_iface_desc(iface) (&(iface)->desc)
#define get_endpoint(alt,ep) (&(alt)->endpoint[ep].desc)
#define get_ep_desc(ep) (&(ep)->desc)
#define get_cfg_desc(cfg) (&(cfg)->desc)
#endif
#ifndef snd_usb_get_speed
#define snd_usb_get_speed(dev) ((dev)->speed)
#endif
#endif /* __USBAUDIO_H */

View File

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

View File

@ -1,6 +1,7 @@
#ifndef USBUSX2Y_H
#define USBUSX2Y_H
#include "../usbaudio.h"
#include "../midi.h"
#include "usbus428ctldefs.h"
#define NRURBS 2