2005-04-16 15:20:36 -07:00
/*
* ( Tentative ) USB Audio Driver for ALSA
*
* Mixer control part
*
* Copyright ( c ) 2002 by Takashi Iwai < tiwai @ suse . de >
*
* Many codes borrowed from audio . c by
* Alan Cox ( alan @ lxorguk . ukuu . org . uk )
* Thomas Sailer ( sailer @ ife . ee . ethz . ch )
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <sound/driver.h>
# 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 <sound/core.h>
# include <sound/control.h>
2005-04-29 16:29:28 +02:00
# include <sound/hwdep.h>
2005-05-10 14:47:38 +02:00
# include <sound/info.h>
2005-04-16 15:20:36 -07:00
# include "usbaudio.h"
/*
*/
/* ignore error from controls - for debugging */
/* #define IGNORE_CTL_ERROR */
typedef struct usb_mixer_build mixer_build_t ;
typedef struct usb_audio_term usb_audio_term_t ;
typedef struct usb_mixer_elem_info usb_mixer_elem_info_t ;
2005-04-29 16:23:13 +02:00
struct usb_mixer_interface {
snd_usb_audio_t * chip ;
unsigned int ctrlif ;
struct list_head list ;
unsigned int ignore_ctl_error ;
2005-04-29 16:26:14 +02:00
struct urb * urb ;
usb_mixer_elem_info_t * * id_elems ; /* array[256], indexed by unit id */
2005-04-29 16:29:28 +02:00
/* Sound Blaster remote control stuff */
enum {
RC_NONE ,
RC_EXTIGY ,
RC_AUDIGY2NX ,
} rc_type ;
unsigned long rc_hwdep_open ;
u32 rc_code ;
wait_queue_head_t rc_waitq ;
struct urb * rc_urb ;
struct usb_ctrlrequest * rc_setup_packet ;
u8 rc_buffer [ 6 ] ;
2005-05-03 08:02:40 +02:00
u8 audigy2nx_leds [ 3 ] ;
2005-04-29 16:23:13 +02:00
} ;
2005-04-16 15:20:36 -07:00
struct usb_audio_term {
int id ;
int type ;
int channels ;
unsigned int chconfig ;
int name ;
} ;
struct usbmix_name_map ;
struct usb_mixer_build {
snd_usb_audio_t * chip ;
2005-04-29 16:23:13 +02:00
struct usb_mixer_interface * mixer ;
2005-04-16 15:20:36 -07:00
unsigned char * buffer ;
unsigned int buflen ;
2005-04-29 09:56:17 +02:00
DECLARE_BITMAP ( unitbitmap , 256 ) ;
2005-04-16 15:20:36 -07:00
usb_audio_term_t oterm ;
const struct usbmix_name_map * map ;
2005-04-22 15:49:52 +02:00
const struct usbmix_selector_map * selector_map ;
2005-04-16 15:20:36 -07:00
} ;
struct usb_mixer_elem_info {
2005-04-29 16:23:13 +02:00
struct usb_mixer_interface * mixer ;
2005-04-29 16:26:14 +02:00
usb_mixer_elem_info_t * next_id_elem ; /* list of controls with same id */
snd_ctl_elem_id_t * elem_id ;
2005-04-16 15:20:36 -07:00
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 ;
2005-04-29 16:26:14 +02:00
u8 initialized ;
2005-04-16 15:20:36 -07:00
} ;
enum {
USB_FEATURE_NONE = 0 ,
USB_FEATURE_MUTE = 1 ,
USB_FEATURE_VOLUME ,
USB_FEATURE_BASS ,
USB_FEATURE_MID ,
USB_FEATURE_TREBLE ,
USB_FEATURE_GEQ ,
USB_FEATURE_AGC ,
USB_FEATURE_DELAY ,
USB_FEATURE_BASSBOOST ,
USB_FEATURE_LOUDNESS
} ;
enum {
USB_MIXER_BOOLEAN ,
USB_MIXER_INV_BOOLEAN ,
USB_MIXER_S8 ,
USB_MIXER_U8 ,
USB_MIXER_S16 ,
USB_MIXER_U16 ,
} ;
enum {
USB_PROC_UPDOWN = 1 ,
USB_PROC_UPDOWN_SWITCH = 1 ,
USB_PROC_UPDOWN_MODE_SEL = 2 ,
USB_PROC_PROLOGIC = 2 ,
USB_PROC_PROLOGIC_SWITCH = 1 ,
USB_PROC_PROLOGIC_MODE_SEL = 2 ,
USB_PROC_3DENH = 3 ,
USB_PROC_3DENH_SWITCH = 1 ,
USB_PROC_3DENH_SPACE = 2 ,
USB_PROC_REVERB = 4 ,
USB_PROC_REVERB_SWITCH = 1 ,
USB_PROC_REVERB_LEVEL = 2 ,
USB_PROC_REVERB_TIME = 3 ,
USB_PROC_REVERB_DELAY = 4 ,
USB_PROC_CHORUS = 5 ,
USB_PROC_CHORUS_SWITCH = 1 ,
USB_PROC_CHORUS_LEVEL = 2 ,
USB_PROC_CHORUS_RATE = 3 ,
USB_PROC_CHORUS_DEPTH = 4 ,
USB_PROC_DCR = 6 ,
USB_PROC_DCR_SWITCH = 1 ,
USB_PROC_DCR_RATIO = 2 ,
USB_PROC_DCR_MAX_AMP = 3 ,
USB_PROC_DCR_THRESHOLD = 4 ,
USB_PROC_DCR_ATTACK = 5 ,
USB_PROC_DCR_RELEASE = 6 ,
} ;
# define MAX_CHANNELS 10 /* max logical channels */
/*
* manual mapping of mixer names
* if the mixer topology is too complicated and the parsed names are
* ambiguous , add the entries in usbmixer_maps . c .
*/
# include "usbmixer_maps.c"
/* get the mapped name if the unit matches */
static int check_mapped_name ( mixer_build_t * state , int unitid , int control , char * buf , int buflen )
{
const struct usbmix_name_map * p ;
if ( ! state - > map )
return 0 ;
for ( p = state - > map ; p - > id ; p + + ) {
if ( p - > id = = unitid & & p - > name & &
( ! control | | ! p - > control | | control = = p - > control ) ) {
buflen - - ;
return strlcpy ( buf , p - > name , buflen ) ;
}
}
return 0 ;
}
/* check whether the control should be ignored */
static int check_ignored_ctl ( mixer_build_t * state , int unitid , int control )
{
const struct usbmix_name_map * p ;
if ( ! state - > map )
return 0 ;
for ( p = state - > map ; p - > id ; p + + ) {
if ( p - > id = = unitid & & ! p - > name & &
( ! control | | ! p - > control | | control = = p - > control ) ) {
// printk("ignored control %d:%d\n", unitid, control);
return 1 ;
}
}
return 0 ;
}
2005-04-22 15:49:52 +02:00
/* get the mapped selector source name */
static int check_mapped_selector_name ( mixer_build_t * state , int unitid ,
int index , char * buf , int buflen )
{
const struct usbmix_selector_map * p ;
if ( ! state - > selector_map )
return 0 ;
for ( p = state - > selector_map ; p - > id ; p + + ) {
if ( p - > id = = unitid & & index < p - > count )
return strlcpy ( buf , p - > names [ index ] , buflen ) ;
}
return 0 ;
}
2005-04-16 15:20:36 -07:00
/*
* find an audio control unit with the given unit id
*/
static void * find_audio_control_unit ( mixer_build_t * state , unsigned char unit )
{
unsigned char * p ;
p = NULL ;
while ( ( p = snd_usb_find_desc ( state - > buffer , state - > buflen , p ,
USB_DT_CS_INTERFACE ) ) ! = NULL ) {
if ( p [ 0 ] > = 4 & & p [ 2 ] > = INPUT_TERMINAL & & p [ 2 ] < = EXTENSION_UNIT & & p [ 3 ] = = unit )
return p ;
}
return NULL ;
}
/*
* copy a string with the given id
*/
static int snd_usb_copy_string_desc ( mixer_build_t * state , int index , char * buf , int maxlen )
{
int len = usb_string ( state - > chip - > dev , index , buf , maxlen - 1 ) ;
buf [ len ] = 0 ;
return len ;
}
/*
* convert from the byte / word on usb descriptor to the zero - based integer
*/
static int convert_signed_value ( usb_mixer_elem_info_t * cval , int val )
{
switch ( cval - > val_type ) {
case USB_MIXER_BOOLEAN :
return ! ! val ;
case USB_MIXER_INV_BOOLEAN :
return ! val ;
case USB_MIXER_U8 :
val & = 0xff ;
break ;
case USB_MIXER_S8 :
val & = 0xff ;
if ( val > = 0x80 )
val - = 0x100 ;
break ;
case USB_MIXER_U16 :
val & = 0xffff ;
break ;
case USB_MIXER_S16 :
val & = 0xffff ;
if ( val > = 0x8000 )
val - = 0x10000 ;
break ;
}
return val ;
}
/*
* convert from the zero - based int to the byte / word for usb descriptor
*/
static int convert_bytes_value ( usb_mixer_elem_info_t * cval , int val )
{
switch ( cval - > val_type ) {
case USB_MIXER_BOOLEAN :
return ! ! val ;
case USB_MIXER_INV_BOOLEAN :
return ! val ;
case USB_MIXER_S8 :
case USB_MIXER_U8 :
return val & 0xff ;
case USB_MIXER_S16 :
case USB_MIXER_U16 :
return val & 0xffff ;
}
return 0 ; /* not reached */
}
static int get_relative_value ( usb_mixer_elem_info_t * cval , int val )
{
if ( ! cval - > res )
cval - > res = 1 ;
if ( val < cval - > min )
return 0 ;
else if ( val > cval - > max )
return ( cval - > max - cval - > min ) / cval - > res ;
else
return ( val - cval - > min ) / cval - > res ;
}
static int get_abs_value ( usb_mixer_elem_info_t * cval , int val )
{
if ( val < 0 )
return cval - > min ;
if ( ! cval - > res )
cval - > res = 1 ;
val * = cval - > res ;
val + = cval - > min ;
if ( val > cval - > max )
return cval - > max ;
return val ;
}
/*
* retrieve a mixer value
*/
static int get_ctl_value ( usb_mixer_elem_info_t * cval , int request , int validx , int * value_ret )
{
unsigned char buf [ 2 ] ;
int val_len = cval - > val_type > = USB_MIXER_S16 ? 2 : 1 ;
int timeout = 10 ;
while ( timeout - - > 0 ) {
2005-04-29 16:23:13 +02:00
if ( snd_usb_ctl_msg ( cval - > mixer - > chip - > dev ,
usb_rcvctrlpipe ( cval - > mixer - > chip - > dev , 0 ) ,
2005-04-16 15:20:36 -07:00
request ,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN ,
2005-04-29 16:23:13 +02:00
validx , cval - > mixer - > ctrlif | ( cval - > id < < 8 ) ,
2005-04-16 15:20:36 -07:00
buf , val_len , 100 ) > = 0 ) {
* value_ret = convert_signed_value ( cval , snd_usb_combine_bytes ( buf , val_len ) ) ;
return 0 ;
}
}
2005-04-29 16:23:13 +02:00
snd_printdd ( KERN_ERR " cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d \n " ,
request , validx , cval - > mixer - > ctrlif | ( cval - > id < < 8 ) , cval - > val_type ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
static int get_cur_ctl_value ( usb_mixer_elem_info_t * cval , int validx , int * value )
{
return get_ctl_value ( cval , GET_CUR , validx , value ) ;
}
/* channel = 0: master, 1 = first channel */
inline static int get_cur_mix_value ( usb_mixer_elem_info_t * cval , int channel , int * value )
{
return get_ctl_value ( cval , GET_CUR , ( cval - > control < < 8 ) | channel , value ) ;
}
/*
* set a mixer value
*/
static int set_ctl_value ( usb_mixer_elem_info_t * cval , int request , int validx , int value_set )
{
unsigned char buf [ 2 ] ;
int val_len = cval - > val_type > = USB_MIXER_S16 ? 2 : 1 ;
int timeout = 10 ;
value_set = convert_bytes_value ( cval , value_set ) ;
buf [ 0 ] = value_set & 0xff ;
buf [ 1 ] = ( value_set > > 8 ) & 0xff ;
while ( timeout - - > 0 )
2005-04-29 16:23:13 +02:00
if ( snd_usb_ctl_msg ( cval - > mixer - > chip - > dev ,
usb_sndctrlpipe ( cval - > mixer - > chip - > dev , 0 ) ,
2005-04-16 15:20:36 -07:00
request ,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT ,
2005-04-29 16:23:13 +02:00
validx , cval - > mixer - > ctrlif | ( cval - > id < < 8 ) ,
2005-04-16 15:20:36 -07:00
buf , val_len , 100 ) > = 0 )
return 0 ;
2005-04-29 16:23:13 +02:00
snd_printdd ( KERN_ERR " cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x \n " ,
request , validx , cval - > mixer - > ctrlif | ( cval - > id < < 8 ) , cval - > val_type , buf [ 0 ] , buf [ 1 ] ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
static int set_cur_ctl_value ( usb_mixer_elem_info_t * cval , int validx , int value )
{
return set_ctl_value ( cval , SET_CUR , validx , value ) ;
}
inline static int set_cur_mix_value ( usb_mixer_elem_info_t * cval , int channel , int value )
{
return set_ctl_value ( cval , SET_CUR , ( cval - > control < < 8 ) | channel , value ) ;
}
/*
* parser routines begin here . . .
*/
static int parse_audio_unit ( mixer_build_t * state , int unitid ) ;
/*
* check if the input / output channel routing is enabled on the given bitmap .
* used for mixer unit parser
*/
static int check_matrix_bitmap ( unsigned char * bmap , int ich , int och , int num_outs )
{
int idx = ich * num_outs + och ;
return bmap [ idx > > 3 ] & ( 0x80 > > ( idx & 7 ) ) ;
}
/*
* add an alsa control element
* search and increment the index until an empty slot is found .
*
* if failed , give up and free the control instance .
*/
2005-04-29 16:23:13 +02:00
static int add_control_to_empty ( mixer_build_t * state , snd_kcontrol_t * kctl )
2005-04-16 15:20:36 -07:00
{
2005-04-29 16:26:14 +02:00
usb_mixer_elem_info_t * cval = kctl - > private_data ;
2005-04-16 15:20:36 -07:00
int err ;
2005-04-29 16:23:13 +02:00
while ( snd_ctl_find_id ( state - > chip - > card , & kctl - > id ) )
2005-04-16 15:20:36 -07:00
kctl - > id . index + + ;
2005-04-29 16:23:13 +02:00
if ( ( err = snd_ctl_add ( state - > chip - > card , kctl ) ) < 0 ) {
2005-04-16 15:20:36 -07:00
snd_printd ( KERN_ERR " cannot add control (err = %d) \n " , err ) ;
snd_ctl_free_one ( kctl ) ;
2005-04-29 16:26:14 +02:00
return err ;
2005-04-16 15:20:36 -07:00
}
2005-04-29 16:26:14 +02:00
cval - > elem_id = & kctl - > id ;
cval - > next_id_elem = state - > mixer - > id_elems [ cval - > id ] ;
state - > mixer - > id_elems [ cval - > id ] = cval ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
/*
* get a terminal name string
*/
static struct iterm_name_combo {
int type ;
char * name ;
} iterm_names [ ] = {
{ 0x0300 , " Output " } ,
{ 0x0301 , " Speaker " } ,
{ 0x0302 , " Headphone " } ,
{ 0x0303 , " HMD Audio " } ,
{ 0x0304 , " Desktop Speaker " } ,
{ 0x0305 , " Room Speaker " } ,
{ 0x0306 , " Com Speaker " } ,
{ 0x0307 , " LFE " } ,
{ 0x0600 , " External In " } ,
{ 0x0601 , " Analog In " } ,
{ 0x0602 , " Digital In " } ,
{ 0x0603 , " Line " } ,
{ 0x0604 , " Legacy In " } ,
{ 0x0605 , " IEC958 In " } ,
{ 0x0606 , " 1394 DA Stream " } ,
{ 0x0607 , " 1394 DV Stream " } ,
{ 0x0700 , " Embedded " } ,
{ 0x0701 , " Noise Source " } ,
{ 0x0702 , " Equalization Noise " } ,
{ 0x0703 , " CD " } ,
{ 0x0704 , " DAT " } ,
{ 0x0705 , " DCC " } ,
{ 0x0706 , " MiniDisk " } ,
{ 0x0707 , " Analog Tape " } ,
{ 0x0708 , " Phonograph " } ,
{ 0x0709 , " VCR Audio " } ,
{ 0x070a , " Video Disk Audio " } ,
{ 0x070b , " DVD Audio " } ,
{ 0x070c , " TV Tuner Audio " } ,
{ 0x070d , " Satellite Rec Audio " } ,
{ 0x070e , " Cable Tuner Audio " } ,
{ 0x070f , " DSS Audio " } ,
{ 0x0710 , " Radio Receiver " } ,
{ 0x0711 , " Radio Transmitter " } ,
{ 0x0712 , " Multi-Track Recorder " } ,
{ 0x0713 , " Synthesizer " } ,
{ 0 } ,
} ;
static int get_term_name ( mixer_build_t * state , usb_audio_term_t * iterm ,
unsigned char * name , int maxlen , int term_only )
{
struct iterm_name_combo * names ;
if ( iterm - > name )
return snd_usb_copy_string_desc ( state , iterm - > name , name , maxlen ) ;
/* virtual type - not a real terminal */
if ( iterm - > type > > 16 ) {
if ( term_only )
return 0 ;
switch ( iterm - > type > > 16 ) {
case SELECTOR_UNIT :
strcpy ( name , " Selector " ) ; return 8 ;
case PROCESSING_UNIT :
strcpy ( name , " Process Unit " ) ; return 12 ;
case EXTENSION_UNIT :
strcpy ( name , " Ext Unit " ) ; return 8 ;
case MIXER_UNIT :
strcpy ( name , " Mixer " ) ; return 5 ;
default :
return sprintf ( name , " Unit %d " , iterm - > id ) ;
}
}
switch ( iterm - > type & 0xff00 ) {
case 0x0100 :
strcpy ( name , " PCM " ) ; return 3 ;
case 0x0200 :
strcpy ( name , " Mic " ) ; return 3 ;
case 0x0400 :
strcpy ( name , " Headset " ) ; return 7 ;
case 0x0500 :
strcpy ( name , " Phone " ) ; return 5 ;
}
for ( names = iterm_names ; names - > type ; names + + )
if ( names - > type = = iterm - > type ) {
strcpy ( name , names - > name ) ;
return strlen ( names - > name ) ;
}
return 0 ;
}
/*
* parse the source unit recursively until it reaches to a terminal
* or a branched unit .
*/
static int check_input_term ( mixer_build_t * state , int id , usb_audio_term_t * term )
{
unsigned char * p1 ;
memset ( term , 0 , sizeof ( * term ) ) ;
while ( ( p1 = find_audio_control_unit ( state , id ) ) ! = NULL ) {
term - > id = id ;
switch ( p1 [ 2 ] ) {
case INPUT_TERMINAL :
term - > type = combine_word ( p1 + 4 ) ;
term - > channels = p1 [ 7 ] ;
term - > chconfig = combine_word ( p1 + 8 ) ;
term - > name = p1 [ 11 ] ;
return 0 ;
case FEATURE_UNIT :
id = p1 [ 4 ] ;
break ; /* continue to parse */
case MIXER_UNIT :
term - > type = p1 [ 2 ] < < 16 ; /* virtual type */
term - > channels = p1 [ 5 + p1 [ 4 ] ] ;
term - > chconfig = combine_word ( p1 + 6 + p1 [ 4 ] ) ;
term - > name = p1 [ p1 [ 0 ] - 1 ] ;
return 0 ;
case SELECTOR_UNIT :
/* call recursively to retrieve the channel info */
if ( check_input_term ( state , p1 [ 5 ] , term ) < 0 )
return - ENODEV ;
term - > type = p1 [ 2 ] < < 16 ; /* virtual type */
term - > id = id ;
term - > name = p1 [ 9 + p1 [ 0 ] - 1 ] ;
return 0 ;
case PROCESSING_UNIT :
case EXTENSION_UNIT :
if ( p1 [ 6 ] = = 1 ) {
id = p1 [ 7 ] ;
break ; /* continue to parse */
}
term - > type = p1 [ 2 ] < < 16 ; /* virtual type */
term - > channels = p1 [ 7 + p1 [ 6 ] ] ;
term - > chconfig = combine_word ( p1 + 8 + p1 [ 6 ] ) ;
term - > name = p1 [ 12 + p1 [ 6 ] + p1 [ 11 + p1 [ 6 ] ] ] ;
return 0 ;
default :
return - ENODEV ;
}
}
return - ENODEV ;
}
/*
* Feature Unit
*/
/* feature unit control information */
struct usb_feature_control_info {
const char * name ;
unsigned int type ; /* control type (mute, volume, etc.) */
} ;
static struct usb_feature_control_info audio_feature_info [ ] = {
{ " Mute " , USB_MIXER_INV_BOOLEAN } ,
{ " Volume " , USB_MIXER_S16 } ,
{ " Tone Control - Bass " , USB_MIXER_S8 } ,
{ " Tone Control - Mid " , USB_MIXER_S8 } ,
{ " Tone Control - Treble " , USB_MIXER_S8 } ,
{ " Graphic Equalizer " , USB_MIXER_S8 } , /* FIXME: not implemeted yet */
{ " Auto Gain Control " , USB_MIXER_BOOLEAN } ,
{ " Delay Control " , USB_MIXER_U16 } ,
{ " Bass Boost " , USB_MIXER_BOOLEAN } ,
{ " Loudness " , USB_MIXER_BOOLEAN } ,
} ;
/* private_free callback */
static void usb_mixer_elem_free ( snd_kcontrol_t * kctl )
{
if ( kctl - > private_data ) {
kfree ( kctl - > private_data ) ;
kctl - > private_data = NULL ;
}
}
/*
* interface to ALSA control for feature / mixer units
*/
/*
* retrieve the minimum and maximum values for the specified control
*/
static int get_min_max ( usb_mixer_elem_info_t * cval , int default_min )
{
/* for failsafe */
cval - > min = default_min ;
cval - > max = cval - > min + 1 ;
cval - > res = 1 ;
if ( cval - > val_type = = USB_MIXER_BOOLEAN | |
cval - > val_type = = USB_MIXER_INV_BOOLEAN ) {
cval - > initialized = 1 ;
} else {
int minchn = 0 ;
if ( cval - > cmask ) {
int i ;
for ( i = 0 ; i < MAX_CHANNELS ; i + + )
if ( cval - > cmask & ( 1 < < i ) ) {
minchn = i + 1 ;
break ;
}
}
if ( get_ctl_value ( cval , GET_MAX , ( cval - > control < < 8 ) | minchn , & cval - > max ) < 0 | |
get_ctl_value ( cval , GET_MIN , ( cval - > control < < 8 ) | minchn , & cval - > min ) < 0 ) {
2005-04-29 16:23:13 +02:00
snd_printd ( KERN_ERR " %d:%d: cannot get min/max values for control %d (id %d) \n " ,
cval - > id , cval - > mixer - > ctrlif , cval - > control , cval - > id ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
if ( get_ctl_value ( cval , GET_RES , ( cval - > control < < 8 ) | minchn , & cval - > res ) < 0 ) {
cval - > res = 1 ;
} else {
int last_valid_res = cval - > res ;
while ( cval - > res > 1 ) {
if ( set_ctl_value ( cval , SET_RES , ( cval - > control < < 8 ) | minchn , cval - > res / 2 ) < 0 )
break ;
cval - > res / = 2 ;
}
if ( get_ctl_value ( cval , GET_RES , ( cval - > control < < 8 ) | minchn , & cval - > res ) < 0 )
cval - > res = last_valid_res ;
}
if ( cval - > res = = 0 )
cval - > res = 1 ;
cval - > initialized = 1 ;
}
return 0 ;
}
/* get a feature/mixer unit info */
static int mixer_ctl_feature_info ( snd_kcontrol_t * kcontrol , snd_ctl_elem_info_t * uinfo )
{
usb_mixer_elem_info_t * cval = kcontrol - > private_data ;
if ( cval - > val_type = = USB_MIXER_BOOLEAN | |
cval - > val_type = = USB_MIXER_INV_BOOLEAN )
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BOOLEAN ;
else
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = cval - > channels ;
if ( cval - > val_type = = USB_MIXER_BOOLEAN | |
cval - > val_type = = USB_MIXER_INV_BOOLEAN ) {
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 1 ;
} else {
if ( ! cval - > initialized )
get_min_max ( cval , 0 ) ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = ( cval - > max - cval - > min ) / cval - > res ;
}
return 0 ;
}
/* get the current value from feature/mixer unit */
static int mixer_ctl_feature_get ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
usb_mixer_elem_info_t * cval = kcontrol - > private_data ;
int c , cnt , val , err ;
if ( cval - > cmask ) {
cnt = 0 ;
for ( c = 0 ; c < MAX_CHANNELS ; c + + ) {
if ( cval - > cmask & ( 1 < < c ) ) {
err = get_cur_mix_value ( cval , c + 1 , & val ) ;
if ( err < 0 ) {
2005-04-29 16:23:13 +02:00
if ( cval - > mixer - > ignore_ctl_error ) {
2005-04-16 15:20:36 -07:00
ucontrol - > value . integer . value [ 0 ] = cval - > min ;
return 0 ;
}
snd_printd ( KERN_ERR " cannot get current value for control %d ch %d: err = %d \n " , cval - > control , c + 1 , err ) ;
return err ;
}
val = get_relative_value ( cval , val ) ;
ucontrol - > value . integer . value [ cnt ] = val ;
cnt + + ;
}
}
} else {
/* master channel */
err = get_cur_mix_value ( cval , 0 , & val ) ;
if ( err < 0 ) {
2005-04-29 16:23:13 +02:00
if ( cval - > mixer - > ignore_ctl_error ) {
2005-04-16 15:20:36 -07:00
ucontrol - > value . integer . value [ 0 ] = cval - > min ;
return 0 ;
}
snd_printd ( KERN_ERR " cannot get current value for control %d master ch: err = %d \n " , cval - > control , err ) ;
return err ;
}
val = get_relative_value ( cval , val ) ;
ucontrol - > value . integer . value [ 0 ] = val ;
}
return 0 ;
}
/* put the current value to feature/mixer unit */
static int mixer_ctl_feature_put ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
usb_mixer_elem_info_t * cval = kcontrol - > private_data ;
int c , cnt , val , oval , err ;
int changed = 0 ;
if ( cval - > cmask ) {
cnt = 0 ;
for ( c = 0 ; c < MAX_CHANNELS ; c + + ) {
if ( cval - > cmask & ( 1 < < c ) ) {
err = get_cur_mix_value ( cval , c + 1 , & oval ) ;
if ( err < 0 ) {
2005-04-29 16:23:13 +02:00
if ( cval - > mixer - > ignore_ctl_error )
2005-04-16 15:20:36 -07:00
return 0 ;
return err ;
}
val = ucontrol - > value . integer . value [ cnt ] ;
val = get_abs_value ( cval , val ) ;
if ( oval ! = val ) {
set_cur_mix_value ( cval , c + 1 , val ) ;
changed = 1 ;
}
get_cur_mix_value ( cval , c + 1 , & val ) ;
cnt + + ;
}
}
} else {
/* master channel */
err = get_cur_mix_value ( cval , 0 , & oval ) ;
2005-04-29 16:23:13 +02:00
if ( err < 0 & & cval - > mixer - > ignore_ctl_error )
2005-04-16 15:20:36 -07:00
return 0 ;
if ( err < 0 )
return err ;
val = ucontrol - > value . integer . value [ 0 ] ;
val = get_abs_value ( cval , val ) ;
if ( val ! = oval ) {
set_cur_mix_value ( cval , 0 , val ) ;
changed = 1 ;
}
}
return changed ;
}
static snd_kcontrol_new_t usb_feature_unit_ctl = {
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " " , /* will be filled later manually */
. info = mixer_ctl_feature_info ,
. get = mixer_ctl_feature_get ,
. put = mixer_ctl_feature_put ,
} ;
/*
* build a feature control
*/
static void build_feature_ctl ( mixer_build_t * state , unsigned char * desc ,
unsigned int ctl_mask , int control ,
usb_audio_term_t * iterm , int unitid )
{
unsigned int len = 0 ;
int mapped_name = 0 ;
int nameid = desc [ desc [ 0 ] - 1 ] ;
snd_kcontrol_t * kctl ;
usb_mixer_elem_info_t * cval ;
control + + ; /* change from zero-based to 1-based value */
if ( control = = USB_FEATURE_GEQ ) {
/* FIXME: not supported yet */
return ;
}
if ( check_ignored_ctl ( state , unitid , control ) )
return ;
cval = kcalloc ( 1 , sizeof ( * cval ) , GFP_KERNEL ) ;
if ( ! cval ) {
snd_printk ( KERN_ERR " cannot malloc kcontrol \n " ) ;
return ;
}
2005-04-29 16:23:13 +02:00
cval - > mixer = state - > mixer ;
2005-04-16 15:20:36 -07:00
cval - > id = unitid ;
cval - > control = control ;
cval - > cmask = ctl_mask ;
cval - > val_type = audio_feature_info [ control - 1 ] . type ;
if ( ctl_mask = = 0 )
cval - > channels = 1 ; /* master channel */
else {
int i , c = 0 ;
for ( i = 0 ; i < 16 ; i + + )
if ( ctl_mask & ( 1 < < i ) )
c + + ;
cval - > channels = c ;
}
/* get min/max values */
get_min_max ( cval , 0 ) ;
kctl = snd_ctl_new1 ( & usb_feature_unit_ctl , cval ) ;
if ( ! kctl ) {
snd_printk ( KERN_ERR " cannot malloc kcontrol \n " ) ;
kfree ( cval ) ;
return ;
}
kctl - > private_free = usb_mixer_elem_free ;
len = check_mapped_name ( state , unitid , control , kctl - > id . name , sizeof ( kctl - > id . name ) ) ;
mapped_name = len ! = 0 ;
if ( ! len & & nameid )
len = snd_usb_copy_string_desc ( state , nameid , kctl - > id . name , sizeof ( kctl - > id . name ) ) ;
switch ( control ) {
case USB_FEATURE_MUTE :
case USB_FEATURE_VOLUME :
/* determine the control name. the rule is:
* - if a name id is given in descriptor , use it .
* - if the connected input can be determined , then use the name
* of terminal type .
* - if the connected output can be determined , use it .
* - otherwise , anonymous name .
*/
if ( ! len ) {
len = get_term_name ( state , iterm , kctl - > id . name , sizeof ( kctl - > id . name ) , 1 ) ;
if ( ! len )
len = get_term_name ( state , & state - > oterm , kctl - > id . name , sizeof ( kctl - > id . name ) , 1 ) ;
if ( ! len )
len = snprintf ( kctl - > id . name , sizeof ( kctl - > id . name ) ,
" Feature %d " , unitid ) ;
}
/* determine the stream direction:
* if the connected output is USB stream , then it ' s likely a
* capture stream . otherwise it should be playback ( hopefully : )
*/
if ( ! mapped_name & & ! ( state - > oterm . type > > 16 ) ) {
if ( ( state - > oterm . type & 0xff00 ) = = 0x0100 ) {
len = strlcat ( kctl - > id . name , " Capture " , sizeof ( kctl - > id . name ) ) ;
} else {
len = strlcat ( kctl - > id . name + len , " Playback " , sizeof ( kctl - > id . name ) ) ;
}
}
strlcat ( kctl - > id . name + len , control = = USB_FEATURE_MUTE ? " Switch " : " Volume " ,
sizeof ( kctl - > id . name ) ) ;
break ;
default :
if ( ! len )
strlcpy ( kctl - > id . name , audio_feature_info [ control - 1 ] . name ,
sizeof ( kctl - > id . name ) ) ;
break ;
}
/* quirk for UDA1321/N101 */
/* note that detection between firmware 2.1.1.7 (N101) and later 2.1.1.21 */
/* is not very clear from datasheets */
/* I hope that the min value is -15360 for newer firmware --jk */
2005-05-02 08:51:26 +02:00
switch ( state - > chip - > usb_id ) {
case USB_ID ( 0x0471 , 0x0101 ) :
case USB_ID ( 0x0471 , 0x0104 ) :
case USB_ID ( 0x0471 , 0x0105 ) :
case USB_ID ( 0x0672 , 0x1041 ) :
if ( ! strcmp ( kctl - > id . name , " PCM Playback Volume " ) & &
cval - > min = = - 15616 ) {
snd_printk ( " using volume control quirk for the UDA1321/N101 chip \n " ) ;
cval - > max = - 256 ;
}
2005-04-16 15:20:36 -07:00
}
snd_printdd ( KERN_INFO " [%d] FU [%s] ch = %d, val = %d/%d/%d \n " ,
cval - > id , kctl - > id . name , cval - > channels , cval - > min , cval - > max , cval - > res ) ;
2005-04-29 16:23:13 +02:00
add_control_to_empty ( state , kctl ) ;
2005-04-16 15:20:36 -07:00
}
/*
* parse a feature unit
*
* most of controlls are defined here .
*/
static int parse_audio_feature_unit ( mixer_build_t * state , int unitid , unsigned char * ftr )
{
int channels , i , j ;
usb_audio_term_t iterm ;
unsigned int master_bits , first_ch_bits ;
int err , csize ;
if ( ftr [ 0 ] < 7 | | ! ( csize = ftr [ 5 ] ) | | ftr [ 0 ] < 7 + csize ) {
snd_printk ( KERN_ERR " usbaudio: unit %u: invalid FEATURE_UNIT descriptor \n " , unitid ) ;
return - EINVAL ;
}
/* parse the source unit */
if ( ( err = parse_audio_unit ( state , ftr [ 4 ] ) ) < 0 )
return err ;
/* determine the input source type and name */
if ( check_input_term ( state , ftr [ 4 ] , & iterm ) < 0 )
return - EINVAL ;
channels = ( ftr [ 0 ] - 7 ) / csize - 1 ;
master_bits = snd_usb_combine_bytes ( ftr + 6 , csize ) ;
if ( channels > 0 )
first_ch_bits = snd_usb_combine_bytes ( ftr + 6 + csize , csize ) ;
else
first_ch_bits = 0 ;
/* check all control types */
for ( i = 0 ; i < 10 ; i + + ) {
unsigned int ch_bits = 0 ;
for ( j = 0 ; j < channels ; j + + ) {
unsigned int mask = snd_usb_combine_bytes ( ftr + 6 + csize * ( j + 1 ) , csize ) ;
if ( mask & ( 1 < < i ) )
ch_bits | = ( 1 < < j ) ;
}
if ( ch_bits & 1 ) /* the first channel must be set (for ease of programming) */
build_feature_ctl ( state , ftr , ch_bits , i , & iterm , unitid ) ;
if ( master_bits & ( 1 < < i ) )
build_feature_ctl ( state , ftr , 0 , i , & iterm , unitid ) ;
}
return 0 ;
}
/*
* Mixer Unit
*/
/*
* build a mixer unit control
*
* the callbacks are identical with feature unit .
* input channel number ( zero based ) is given in control field instead .
*/
static void build_mixer_unit_ctl ( mixer_build_t * state , unsigned char * desc ,
int in_pin , int in_ch , int unitid ,
usb_audio_term_t * iterm )
{
usb_mixer_elem_info_t * cval ;
unsigned int input_pins = desc [ 4 ] ;
unsigned int num_outs = desc [ 5 + input_pins ] ;
unsigned int i , len ;
snd_kcontrol_t * kctl ;
if ( check_ignored_ctl ( state , unitid , 0 ) )
return ;
cval = kcalloc ( 1 , sizeof ( * cval ) , GFP_KERNEL ) ;
if ( ! cval )
return ;
2005-04-29 16:23:13 +02:00
cval - > mixer = state - > mixer ;
2005-04-16 15:20:36 -07:00
cval - > id = unitid ;
cval - > control = in_ch + 1 ; /* based on 1 */
cval - > val_type = USB_MIXER_S16 ;
for ( i = 0 ; i < num_outs ; i + + ) {
if ( check_matrix_bitmap ( desc + 9 + input_pins , in_ch , i , num_outs ) ) {
cval - > cmask | = ( 1 < < i ) ;
cval - > channels + + ;
}
}
/* get min/max values */
get_min_max ( cval , 0 ) ;
kctl = snd_ctl_new1 ( & usb_feature_unit_ctl , cval ) ;
if ( ! kctl ) {
snd_printk ( KERN_ERR " cannot malloc kcontrol \n " ) ;
kfree ( cval ) ;
return ;
}
kctl - > private_free = usb_mixer_elem_free ;
len = check_mapped_name ( state , unitid , 0 , kctl - > id . name , sizeof ( kctl - > id . name ) ) ;
if ( ! len )
len = get_term_name ( state , iterm , kctl - > id . name , sizeof ( kctl - > id . name ) , 0 ) ;
if ( ! len )
len = sprintf ( kctl - > id . name , " Mixer Source %d " , in_ch + 1 ) ;
strlcat ( kctl - > id . name + len , " Volume " , sizeof ( kctl - > id . name ) ) ;
snd_printdd ( KERN_INFO " [%d] MU [%s] ch = %d, val = %d/%d \n " ,
cval - > id , kctl - > id . name , cval - > channels , cval - > min , cval - > max ) ;
2005-04-29 16:23:13 +02:00
add_control_to_empty ( state , kctl ) ;
2005-04-16 15:20:36 -07:00
}
/*
* parse a mixer unit
*/
static int parse_audio_mixer_unit ( mixer_build_t * state , int unitid , unsigned char * desc )
{
usb_audio_term_t iterm ;
int input_pins , num_ins , num_outs ;
int pin , ich , err ;
if ( desc [ 0 ] < 11 | | ! ( input_pins = desc [ 4 ] ) | | ! ( num_outs = desc [ 5 + input_pins ] ) ) {
snd_printk ( KERN_ERR " invalid MIXER UNIT descriptor %d \n " , unitid ) ;
return - EINVAL ;
}
/* no bmControls field (e.g. Maya44) -> ignore */
if ( desc [ 0 ] < = 10 + input_pins ) {
snd_printdd ( KERN_INFO " MU %d has no bmControls field \n " , unitid ) ;
return 0 ;
}
num_ins = 0 ;
ich = 0 ;
for ( pin = 0 ; pin < input_pins ; pin + + ) {
err = parse_audio_unit ( state , desc [ 5 + pin ] ) ;
if ( err < 0 )
return err ;
err = check_input_term ( state , desc [ 5 + pin ] , & iterm ) ;
if ( err < 0 )
return err ;
num_ins + = iterm . channels ;
for ( ; ich < num_ins ; + + ich ) {
int och , ich_has_controls = 0 ;
for ( och = 0 ; och < num_outs ; + + och ) {
if ( check_matrix_bitmap ( desc + 9 + input_pins ,
ich , och , num_outs ) ) {
ich_has_controls = 1 ;
break ;
}
}
if ( ich_has_controls )
build_mixer_unit_ctl ( state , desc , pin , ich ,
unitid , & iterm ) ;
}
}
return 0 ;
}
/*
* Processing Unit / Extension Unit
*/
/* get callback for processing/extension unit */
static int mixer_ctl_procunit_get ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
usb_mixer_elem_info_t * cval = kcontrol - > private_data ;
int err , val ;
err = get_cur_ctl_value ( cval , cval - > control < < 8 , & val ) ;
2005-04-29 16:23:13 +02:00
if ( err < 0 & & cval - > mixer - > ignore_ctl_error ) {
2005-04-16 15:20:36 -07:00
ucontrol - > value . integer . value [ 0 ] = cval - > min ;
return 0 ;
}
if ( err < 0 )
return err ;
val = get_relative_value ( cval , val ) ;
ucontrol - > value . integer . value [ 0 ] = val ;
return 0 ;
}
/* put callback for processing/extension unit */
static int mixer_ctl_procunit_put ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
usb_mixer_elem_info_t * cval = kcontrol - > private_data ;
int val , oval , err ;
err = get_cur_ctl_value ( cval , cval - > control < < 8 , & oval ) ;
if ( err < 0 ) {
2005-04-29 16:23:13 +02:00
if ( cval - > mixer - > ignore_ctl_error )
2005-04-16 15:20:36 -07:00
return 0 ;
return err ;
}
val = ucontrol - > value . integer . value [ 0 ] ;
val = get_abs_value ( cval , val ) ;
if ( val ! = oval ) {
set_cur_ctl_value ( cval , cval - > control < < 8 , val ) ;
return 1 ;
}
return 0 ;
}
/* alsa control interface for processing/extension unit */
static snd_kcontrol_new_t mixer_procunit_ctl = {
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " " , /* will be filled later */
. info = mixer_ctl_feature_info ,
. get = mixer_ctl_procunit_get ,
. put = mixer_ctl_procunit_put ,
} ;
/*
* predefined data for processing units
*/
struct procunit_value_info {
int control ;
char * suffix ;
int val_type ;
int min_value ;
} ;
struct procunit_info {
int type ;
char * name ;
struct procunit_value_info * values ;
} ;
static struct procunit_value_info updown_proc_info [ ] = {
{ USB_PROC_UPDOWN_SWITCH , " Switch " , USB_MIXER_BOOLEAN } ,
{ USB_PROC_UPDOWN_MODE_SEL , " Mode Select " , USB_MIXER_U8 , 1 } ,
{ 0 }
} ;
static struct procunit_value_info prologic_proc_info [ ] = {
{ USB_PROC_PROLOGIC_SWITCH , " Switch " , USB_MIXER_BOOLEAN } ,
{ USB_PROC_PROLOGIC_MODE_SEL , " Mode Select " , USB_MIXER_U8 , 1 } ,
{ 0 }
} ;
static struct procunit_value_info threed_enh_proc_info [ ] = {
{ USB_PROC_3DENH_SWITCH , " Switch " , USB_MIXER_BOOLEAN } ,
{ USB_PROC_3DENH_SPACE , " Spaciousness " , USB_MIXER_U8 } ,
{ 0 }
} ;
static struct procunit_value_info reverb_proc_info [ ] = {
{ USB_PROC_REVERB_SWITCH , " Switch " , USB_MIXER_BOOLEAN } ,
{ USB_PROC_REVERB_LEVEL , " Level " , USB_MIXER_U8 } ,
{ USB_PROC_REVERB_TIME , " Time " , USB_MIXER_U16 } ,
{ USB_PROC_REVERB_DELAY , " Delay " , USB_MIXER_U8 } ,
{ 0 }
} ;
static struct procunit_value_info chorus_proc_info [ ] = {
{ USB_PROC_CHORUS_SWITCH , " Switch " , USB_MIXER_BOOLEAN } ,
{ USB_PROC_CHORUS_LEVEL , " Level " , USB_MIXER_U8 } ,
{ USB_PROC_CHORUS_RATE , " Rate " , USB_MIXER_U16 } ,
{ USB_PROC_CHORUS_DEPTH , " Depth " , USB_MIXER_U16 } ,
{ 0 }
} ;
static struct procunit_value_info dcr_proc_info [ ] = {
{ USB_PROC_DCR_SWITCH , " Switch " , USB_MIXER_BOOLEAN } ,
{ USB_PROC_DCR_RATIO , " Ratio " , USB_MIXER_U16 } ,
{ USB_PROC_DCR_MAX_AMP , " Max Amp " , USB_MIXER_S16 } ,
{ USB_PROC_DCR_THRESHOLD , " Threshold " , USB_MIXER_S16 } ,
{ USB_PROC_DCR_ATTACK , " Attack Time " , USB_MIXER_U16 } ,
{ USB_PROC_DCR_RELEASE , " Release Time " , USB_MIXER_U16 } ,
{ 0 }
} ;
static struct procunit_info procunits [ ] = {
{ USB_PROC_UPDOWN , " Up Down " , updown_proc_info } ,
{ USB_PROC_PROLOGIC , " Dolby Prologic " , prologic_proc_info } ,
{ USB_PROC_3DENH , " 3D Stereo Extender " , threed_enh_proc_info } ,
{ USB_PROC_REVERB , " Reverb " , reverb_proc_info } ,
{ USB_PROC_CHORUS , " Chorus " , chorus_proc_info } ,
{ USB_PROC_DCR , " DCR " , dcr_proc_info } ,
{ 0 } ,
} ;
/*
* build a processing / extension unit
*/
static int build_audio_procunit ( mixer_build_t * state , int unitid , unsigned char * dsc , struct procunit_info * list , char * name )
{
int num_ins = dsc [ 6 ] ;
usb_mixer_elem_info_t * cval ;
snd_kcontrol_t * kctl ;
int i , err , nameid , type , len ;
struct procunit_info * info ;
struct procunit_value_info * valinfo ;
static struct procunit_value_info default_value_info [ ] = {
{ 0x01 , " Switch " , USB_MIXER_BOOLEAN } ,
{ 0 }
} ;
static struct procunit_info default_info = {
0 , NULL , default_value_info
} ;
if ( dsc [ 0 ] < 13 | | dsc [ 0 ] < 13 + num_ins | | dsc [ 0 ] < num_ins + dsc [ 11 + num_ins ] ) {
snd_printk ( KERN_ERR " invalid %s descriptor (id %d) \n " , name , unitid ) ;
return - EINVAL ;
}
for ( i = 0 ; i < num_ins ; i + + ) {
if ( ( err = parse_audio_unit ( state , dsc [ 7 + i ] ) ) < 0 )
return err ;
}
type = combine_word ( & dsc [ 4 ] ) ;
for ( info = list ; info & & info - > type ; info + + )
if ( info - > type = = type )
break ;
if ( ! info | | ! info - > type )
info = & default_info ;
for ( valinfo = info - > values ; valinfo - > control ; valinfo + + ) {
/* FIXME: bitmap might be longer than 8bit */
if ( ! ( dsc [ 12 + num_ins ] & ( 1 < < ( valinfo - > control - 1 ) ) ) )
continue ;
if ( check_ignored_ctl ( state , unitid , valinfo - > control ) )
continue ;
cval = kcalloc ( 1 , sizeof ( * cval ) , GFP_KERNEL ) ;
if ( ! cval ) {
snd_printk ( KERN_ERR " cannot malloc kcontrol \n " ) ;
return - ENOMEM ;
}
2005-04-29 16:23:13 +02:00
cval - > mixer = state - > mixer ;
2005-04-16 15:20:36 -07:00
cval - > id = unitid ;
cval - > control = valinfo - > control ;
cval - > val_type = valinfo - > val_type ;
cval - > channels = 1 ;
/* get min/max values */
if ( type = = USB_PROC_UPDOWN & & cval - > control = = USB_PROC_UPDOWN_MODE_SEL ) {
/* FIXME: hard-coded */
cval - > min = 1 ;
cval - > max = dsc [ 15 ] ;
cval - > res = 1 ;
cval - > initialized = 1 ;
} else
get_min_max ( cval , valinfo - > min_value ) ;
kctl = snd_ctl_new1 ( & mixer_procunit_ctl , cval ) ;
if ( ! kctl ) {
snd_printk ( KERN_ERR " cannot malloc kcontrol \n " ) ;
kfree ( cval ) ;
return - ENOMEM ;
}
kctl - > private_free = usb_mixer_elem_free ;
if ( check_mapped_name ( state , unitid , cval - > control , kctl - > id . name , sizeof ( kctl - > id . name ) ) )
;
else if ( info - > name )
strlcpy ( kctl - > id . name , info - > name , sizeof ( kctl - > id . name ) ) ;
else {
nameid = dsc [ 12 + num_ins + dsc [ 11 + num_ins ] ] ;
len = 0 ;
if ( nameid )
len = snd_usb_copy_string_desc ( state , nameid , kctl - > id . name , sizeof ( kctl - > id . name ) ) ;
if ( ! len )
strlcpy ( kctl - > id . name , name , sizeof ( kctl - > id . name ) ) ;
}
strlcat ( kctl - > id . name , " " , sizeof ( kctl - > id . name ) ) ;
strlcat ( kctl - > id . name , valinfo - > suffix , sizeof ( kctl - > id . name ) ) ;
snd_printdd ( KERN_INFO " [%d] PU [%s] ch = %d, val = %d/%d \n " ,
cval - > id , kctl - > id . name , cval - > channels , cval - > min , cval - > max ) ;
2005-04-29 16:23:13 +02:00
if ( ( err = add_control_to_empty ( state , kctl ) ) < 0 )
2005-04-16 15:20:36 -07:00
return err ;
}
return 0 ;
}
static int parse_audio_processing_unit ( mixer_build_t * state , int unitid , unsigned char * desc )
{
return build_audio_procunit ( state , unitid , desc , procunits , " Processing Unit " ) ;
}
static int parse_audio_extension_unit ( mixer_build_t * state , int unitid , unsigned char * desc )
{
return build_audio_procunit ( state , unitid , desc , NULL , " Extension Unit " ) ;
}
/*
* Selector Unit
*/
/* info callback for selector unit
* use an enumerator type for routing
*/
static int mixer_ctl_selector_info ( snd_kcontrol_t * kcontrol , snd_ctl_elem_info_t * uinfo )
{
usb_mixer_elem_info_t * cval = kcontrol - > private_data ;
char * * itemlist = ( char * * ) kcontrol - > private_value ;
snd_assert ( itemlist , return - EINVAL ) ;
uinfo - > type = SNDRV_CTL_ELEM_TYPE_ENUMERATED ;
uinfo - > count = 1 ;
uinfo - > value . enumerated . items = cval - > max ;
if ( ( int ) uinfo - > value . enumerated . item > = cval - > max )
uinfo - > value . enumerated . item = cval - > max - 1 ;
strcpy ( uinfo - > value . enumerated . name , itemlist [ uinfo - > value . enumerated . item ] ) ;
return 0 ;
}
/* get callback for selector unit */
static int mixer_ctl_selector_get ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
usb_mixer_elem_info_t * cval = kcontrol - > private_data ;
int val , err ;
err = get_cur_ctl_value ( cval , 0 , & val ) ;
if ( err < 0 ) {
2005-04-29 16:23:13 +02:00
if ( cval - > mixer - > ignore_ctl_error ) {
2005-04-16 15:20:36 -07:00
ucontrol - > value . enumerated . item [ 0 ] = 0 ;
return 0 ;
}
return err ;
}
val = get_relative_value ( cval , val ) ;
ucontrol - > value . enumerated . item [ 0 ] = val ;
return 0 ;
}
/* put callback for selector unit */
static int mixer_ctl_selector_put ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
usb_mixer_elem_info_t * cval = kcontrol - > private_data ;
int val , oval , err ;
err = get_cur_ctl_value ( cval , 0 , & oval ) ;
if ( err < 0 ) {
2005-04-29 16:23:13 +02:00
if ( cval - > mixer - > ignore_ctl_error )
2005-04-16 15:20:36 -07:00
return 0 ;
return err ;
}
val = ucontrol - > value . enumerated . item [ 0 ] ;
val = get_abs_value ( cval , val ) ;
if ( val ! = oval ) {
set_cur_ctl_value ( cval , 0 , val ) ;
return 1 ;
}
return 0 ;
}
/* alsa control interface for selector unit */
static snd_kcontrol_new_t mixer_selectunit_ctl = {
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " " , /* will be filled later */
. info = mixer_ctl_selector_info ,
. get = mixer_ctl_selector_get ,
. put = mixer_ctl_selector_put ,
} ;
/* private free callback.
* free both private_data and private_value
*/
static void usb_mixer_selector_elem_free ( snd_kcontrol_t * kctl )
{
int i , num_ins = 0 ;
if ( kctl - > private_data ) {
usb_mixer_elem_info_t * cval = kctl - > private_data ;
num_ins = cval - > max ;
kfree ( cval ) ;
kctl - > private_data = NULL ;
}
if ( kctl - > private_value ) {
char * * itemlist = ( char * * ) kctl - > private_value ;
for ( i = 0 ; i < num_ins ; i + + )
kfree ( itemlist [ i ] ) ;
kfree ( itemlist ) ;
kctl - > private_value = 0 ;
}
}
/*
* parse a selector unit
*/
static int parse_audio_selector_unit ( mixer_build_t * state , int unitid , unsigned char * desc )
{
unsigned int num_ins = desc [ 4 ] ;
unsigned int i , nameid , len ;
int err ;
usb_mixer_elem_info_t * cval ;
snd_kcontrol_t * kctl ;
char * * namelist ;
if ( ! num_ins | | desc [ 0 ] < 6 + num_ins ) {
snd_printk ( KERN_ERR " invalid SELECTOR UNIT descriptor %d \n " , unitid ) ;
return - EINVAL ;
}
for ( i = 0 ; i < num_ins ; i + + ) {
if ( ( err = parse_audio_unit ( state , desc [ 5 + i ] ) ) < 0 )
return err ;
}
if ( num_ins = = 1 ) /* only one ? nonsense! */
return 0 ;
if ( check_ignored_ctl ( state , unitid , 0 ) )
return 0 ;
cval = kcalloc ( 1 , sizeof ( * cval ) , GFP_KERNEL ) ;
if ( ! cval ) {
snd_printk ( KERN_ERR " cannot malloc kcontrol \n " ) ;
return - ENOMEM ;
}
2005-04-29 16:23:13 +02:00
cval - > mixer = state - > mixer ;
2005-04-16 15:20:36 -07:00
cval - > id = unitid ;
cval - > val_type = USB_MIXER_U8 ;
cval - > channels = 1 ;
cval - > min = 1 ;
cval - > max = num_ins ;
cval - > res = 1 ;
cval - > initialized = 1 ;
namelist = kmalloc ( sizeof ( char * ) * num_ins , GFP_KERNEL ) ;
if ( ! namelist ) {
snd_printk ( KERN_ERR " cannot malloc \n " ) ;
kfree ( cval ) ;
return - ENOMEM ;
}
# define MAX_ITEM_NAME_LEN 64
for ( i = 0 ; i < num_ins ; i + + ) {
usb_audio_term_t iterm ;
len = 0 ;
namelist [ i ] = kmalloc ( MAX_ITEM_NAME_LEN , GFP_KERNEL ) ;
if ( ! namelist [ i ] ) {
snd_printk ( KERN_ERR " cannot malloc \n " ) ;
while ( - - i > 0 )
kfree ( namelist [ i ] ) ;
kfree ( namelist ) ;
kfree ( cval ) ;
return - ENOMEM ;
}
2005-04-22 15:49:52 +02:00
len = check_mapped_selector_name ( state , unitid , i , namelist [ i ] ,
MAX_ITEM_NAME_LEN ) ;
if ( ! len & & check_input_term ( state , desc [ 5 + i ] , & iterm ) > = 0 )
2005-04-16 15:20:36 -07:00
len = get_term_name ( state , & iterm , namelist [ i ] , MAX_ITEM_NAME_LEN , 0 ) ;
if ( ! len )
sprintf ( namelist [ i ] , " Input %d " , i ) ;
}
kctl = snd_ctl_new1 ( & mixer_selectunit_ctl , cval ) ;
if ( ! kctl ) {
snd_printk ( KERN_ERR " cannot malloc kcontrol \n " ) ;
kfree ( cval ) ;
return - ENOMEM ;
}
kctl - > private_value = ( unsigned long ) namelist ;
kctl - > private_free = usb_mixer_selector_elem_free ;
nameid = desc [ desc [ 0 ] - 1 ] ;
len = check_mapped_name ( state , unitid , 0 , kctl - > id . name , sizeof ( kctl - > id . name ) ) ;
if ( len )
;
else if ( nameid )
snd_usb_copy_string_desc ( state , nameid , kctl - > id . name , sizeof ( kctl - > id . name ) ) ;
else {
len = get_term_name ( state , & state - > oterm ,
kctl - > id . name , sizeof ( kctl - > id . name ) , 0 ) ;
if ( ! len )
strlcpy ( kctl - > id . name , " USB " , sizeof ( kctl - > id . name ) ) ;
if ( ( state - > oterm . type & 0xff00 ) = = 0x0100 )
strlcat ( kctl - > id . name , " Capture Source " , sizeof ( kctl - > id . name ) ) ;
else
strlcat ( kctl - > id . name , " Playback Source " , sizeof ( kctl - > id . name ) ) ;
}
snd_printdd ( KERN_INFO " [%d] SU [%s] items = %d \n " ,
cval - > id , kctl - > id . name , num_ins ) ;
2005-04-29 16:23:13 +02:00
if ( ( err = add_control_to_empty ( state , kctl ) ) < 0 )
2005-04-16 15:20:36 -07:00
return err ;
return 0 ;
}
/*
* parse an audio unit recursively
*/
static int parse_audio_unit ( mixer_build_t * state , int unitid )
{
unsigned char * p1 ;
if ( test_and_set_bit ( unitid , state - > unitbitmap ) )
return 0 ; /* the unit already visited */
p1 = find_audio_control_unit ( state , unitid ) ;
if ( ! p1 ) {
snd_printk ( KERN_ERR " usbaudio: unit %d not found! \n " , unitid ) ;
return - EINVAL ;
}
switch ( p1 [ 2 ] ) {
case INPUT_TERMINAL :
return 0 ; /* NOP */
case MIXER_UNIT :
return parse_audio_mixer_unit ( state , unitid , p1 ) ;
case SELECTOR_UNIT :
return parse_audio_selector_unit ( state , unitid , p1 ) ;
case FEATURE_UNIT :
return parse_audio_feature_unit ( state , unitid , p1 ) ;
case PROCESSING_UNIT :
return parse_audio_processing_unit ( state , unitid , p1 ) ;
case EXTENSION_UNIT :
return parse_audio_extension_unit ( state , unitid , p1 ) ;
default :
snd_printk ( KERN_ERR " usbaudio: unit %u: unexpected type 0x%02x \n " , unitid , p1 [ 2 ] ) ;
return - EINVAL ;
}
}
2005-04-29 16:23:13 +02:00
static void snd_usb_mixer_free ( struct usb_mixer_interface * mixer )
{
2005-04-29 16:26:14 +02:00
kfree ( mixer - > id_elems ) ;
if ( mixer - > urb ) {
kfree ( mixer - > urb - > transfer_buffer ) ;
usb_free_urb ( mixer - > urb ) ;
}
2005-04-29 16:29:28 +02:00
if ( mixer - > rc_urb )
usb_free_urb ( mixer - > rc_urb ) ;
kfree ( mixer - > rc_setup_packet ) ;
2005-04-29 16:23:13 +02:00
kfree ( mixer ) ;
}
static int snd_usb_mixer_dev_free ( snd_device_t * device )
{
struct usb_mixer_interface * mixer = device - > device_data ;
snd_usb_mixer_free ( mixer ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
/*
* create mixer controls
*
* walk through all OUTPUT_TERMINAL descriptors to search for mixers
*/
2005-04-29 16:23:13 +02:00
static int snd_usb_mixer_controls ( struct usb_mixer_interface * mixer )
2005-04-16 15:20:36 -07:00
{
unsigned char * desc ;
mixer_build_t state ;
int err ;
const struct usbmix_ctl_map * map ;
2005-04-29 16:23:13 +02:00
struct usb_host_interface * hostif ;
2005-04-16 15:20:36 -07:00
2005-04-29 16:23:13 +02:00
hostif = & usb_ifnum_to_if ( mixer - > chip - > dev , mixer - > ctrlif ) - > altsetting [ 0 ] ;
2005-04-16 15:20:36 -07:00
memset ( & state , 0 , sizeof ( state ) ) ;
2005-04-29 16:23:13 +02:00
state . chip = mixer - > chip ;
state . mixer = mixer ;
2005-04-16 15:20:36 -07:00
state . buffer = hostif - > extra ;
state . buflen = hostif - > extralen ;
/* check the mapping table */
2005-05-02 08:51:26 +02:00
for ( map = usbmix_ctl_maps ; map - > id ; map + + ) {
if ( map - > id = = state . chip - > usb_id ) {
2005-04-16 15:20:36 -07:00
state . map = map - > map ;
2005-04-22 15:49:52 +02:00
state . selector_map = map - > selector_map ;
2005-04-29 16:23:13 +02:00
mixer - > ignore_ctl_error = map - > ignore_ctl_error ;
2005-04-16 15:20:36 -07:00
break ;
}
}
desc = NULL ;
while ( ( desc = snd_usb_find_csint_desc ( hostif - > extra , hostif - > extralen , desc , OUTPUT_TERMINAL ) ) ! = NULL ) {
if ( desc [ 0 ] < 9 )
continue ; /* invalid descriptor? */
set_bit ( desc [ 3 ] , state . unitbitmap ) ; /* mark terminal ID as visited */
state . oterm . id = desc [ 3 ] ;
state . oterm . type = combine_word ( & desc [ 4 ] ) ;
state . oterm . name = desc [ 8 ] ;
err = parse_audio_unit ( & state , desc [ 7 ] ) ;
if ( err < 0 )
return err ;
}
return 0 ;
}
2005-04-29 16:23:13 +02:00
2005-04-29 16:26:14 +02:00
static void snd_usb_mixer_notify_id ( struct usb_mixer_interface * mixer ,
int unitid )
{
usb_mixer_elem_info_t * info ;
for ( info = mixer - > id_elems [ unitid ] ; info ; info = info - > next_id_elem )
snd_ctl_notify ( mixer - > chip - > card , SNDRV_CTL_EVENT_MASK_VALUE ,
info - > elem_id ) ;
}
2005-04-29 16:29:28 +02:00
static void snd_usb_mixer_memory_change ( struct usb_mixer_interface * mixer ,
int unitid )
{
2005-05-10 14:47:38 +02:00
if ( mixer - > rc_type = = RC_NONE )
return ;
/* unit ids specific to Extigy/Audigy 2 NX: */
switch ( unitid ) {
case 0 : /* remote control */
2005-04-29 16:29:28 +02:00
mixer - > rc_urb - > dev = mixer - > chip - > dev ;
usb_submit_urb ( mixer - > rc_urb , GFP_ATOMIC ) ;
2005-05-10 14:47:38 +02:00
break ;
case 4 : /* digital in jack */
case 7 : /* line in jacks */
case 19 : /* speaker out jacks */
case 20 : /* headphones out jack */
break ;
default :
snd_printd ( KERN_DEBUG " memory change in unknown unit %d \n " , unitid ) ;
break ;
2005-04-29 16:29:28 +02:00
}
}
2005-04-29 16:26:14 +02:00
static void snd_usb_mixer_status_complete ( struct urb * urb , struct pt_regs * regs )
{
struct usb_mixer_interface * mixer = urb - > context ;
if ( urb - > status = = 0 ) {
u8 * buf = urb - > transfer_buffer ;
int i ;
for ( i = urb - > actual_length ; i > = 2 ; buf + = 2 , i - = 2 ) {
snd_printd ( KERN_DEBUG " status interrupt: %02x %02x \n " ,
buf [ 0 ] , buf [ 1 ] ) ;
/* ignore any notifications not from the control interface */
if ( ( buf [ 0 ] & 0x0f ) ! = 0 )
continue ;
if ( ! ( buf [ 0 ] & 0x40 ) )
snd_usb_mixer_notify_id ( mixer , buf [ 1 ] ) ;
2005-04-29 16:29:28 +02:00
else
snd_usb_mixer_memory_change ( mixer , buf [ 1 ] ) ;
2005-04-29 16:26:14 +02:00
}
}
if ( urb - > status ! = - ENOENT & & urb - > status ! = - ECONNRESET ) {
urb - > dev = mixer - > chip - > dev ;
usb_submit_urb ( urb , GFP_ATOMIC ) ;
}
}
/* create the handler for the optional status interrupt endpoint */
static int snd_usb_mixer_status_create ( struct usb_mixer_interface * mixer )
{
struct usb_host_interface * hostif ;
struct usb_endpoint_descriptor * ep ;
void * transfer_buffer ;
int buffer_length ;
unsigned int epnum ;
hostif = & usb_ifnum_to_if ( mixer - > chip - > dev , mixer - > ctrlif ) - > altsetting [ 0 ] ;
/* we need one interrupt input endpoint */
if ( get_iface_desc ( hostif ) - > bNumEndpoints < 1 )
return 0 ;
ep = get_endpoint ( hostif , 0 ) ;
if ( ( ep - > bEndpointAddress & USB_ENDPOINT_DIR_MASK ) ! = USB_DIR_IN | |
( ep - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) ! = USB_ENDPOINT_XFER_INT )
return 0 ;
epnum = ep - > bEndpointAddress & USB_ENDPOINT_NUMBER_MASK ;
buffer_length = le16_to_cpu ( ep - > wMaxPacketSize ) ;
transfer_buffer = kmalloc ( buffer_length , GFP_KERNEL ) ;
if ( ! transfer_buffer )
return - ENOMEM ;
mixer - > urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! mixer - > urb ) {
kfree ( transfer_buffer ) ;
return - ENOMEM ;
}
usb_fill_int_urb ( mixer - > urb , mixer - > chip - > dev ,
usb_rcvintpipe ( mixer - > chip - > dev , epnum ) ,
transfer_buffer , buffer_length ,
snd_usb_mixer_status_complete , mixer , ep - > bInterval ) ;
usb_submit_urb ( mixer - > urb , GFP_KERNEL ) ;
return 0 ;
}
2005-04-29 16:29:28 +02:00
static void snd_usb_soundblaster_remote_complete ( struct urb * urb ,
struct pt_regs * regs )
{
struct usb_mixer_interface * mixer = urb - > context ;
/*
* format of remote control data :
* Extigy : xx 00
* Audigy 2 NX : 06 80 xx 00 00 00
*/
int offset = mixer - > rc_type = = RC_EXTIGY ? 0 : 2 ;
u32 code ;
if ( urb - > status < 0 | | urb - > actual_length < = offset )
return ;
code = mixer - > rc_buffer [ offset ] ;
/* the Mute button actually changes the mixer control */
if ( code = = 13 )
snd_usb_mixer_notify_id ( mixer , 18 ) ;
mixer - > rc_code = code ;
wmb ( ) ;
wake_up ( & mixer - > rc_waitq ) ;
}
static int snd_usb_sbrc_hwdep_open ( snd_hwdep_t * hw , struct file * file )
{
struct usb_mixer_interface * mixer = hw - > private_data ;
if ( test_and_set_bit ( 0 , & mixer - > rc_hwdep_open ) )
return - EBUSY ;
return 0 ;
}
static int snd_usb_sbrc_hwdep_release ( snd_hwdep_t * hw , struct file * file )
{
struct usb_mixer_interface * mixer = hw - > private_data ;
clear_bit ( 0 , & mixer - > rc_hwdep_open ) ;
smp_mb__after_clear_bit ( ) ;
return 0 ;
}
static long snd_usb_sbrc_hwdep_read ( snd_hwdep_t * hw , char __user * buf ,
long count , loff_t * offset )
{
struct usb_mixer_interface * mixer = hw - > private_data ;
int err ;
u32 rc_code ;
2005-05-02 08:58:31 +02:00
if ( count ! = 1 & & count ! = 4 )
2005-04-29 16:29:28 +02:00
return - EINVAL ;
err = wait_event_interruptible ( mixer - > rc_waitq ,
( rc_code = xchg ( & mixer - > rc_code , 0 ) ) ! = 0 ) ;
if ( err = = 0 ) {
2005-05-02 08:58:31 +02:00
if ( count = = 1 )
err = put_user ( rc_code , buf ) ;
else
err = put_user ( rc_code , ( u32 __user * ) buf ) ;
2005-04-29 16:29:28 +02:00
}
return err < 0 ? err : count ;
}
static unsigned int snd_usb_sbrc_hwdep_poll ( snd_hwdep_t * 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 )
{
snd_hwdep_t * hwdep ;
int err , len ;
2005-05-02 08:51:26 +02:00
switch ( mixer - > chip - > usb_id ) {
case USB_ID ( 0x041e , 0x3000 ) :
2005-04-29 16:29:28 +02:00
mixer - > rc_type = RC_EXTIGY ;
len = 2 ;
break ;
2005-05-02 08:51:26 +02:00
case USB_ID ( 0x041e , 0x3020 ) :
2005-04-29 16:29:28 +02:00
mixer - > rc_type = RC_AUDIGY2NX ;
len = 6 ;
break ;
default :
return 0 ;
}
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 . open = snd_usb_sbrc_hwdep_open ;
hwdep - > ops . release = snd_usb_sbrc_hwdep_release ;
hwdep - > ops . poll = snd_usb_sbrc_hwdep_poll ;
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 = 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 ;
}
2005-05-03 08:02:40 +02:00
static int snd_audigy2nx_led_info ( snd_kcontrol_t * kcontrol , snd_ctl_elem_info_t * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BOOLEAN ;
uinfo - > count = 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 1 ;
return 0 ;
}
static int snd_audigy2nx_led_get ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * 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 ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * 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 snd_kcontrol_new_t 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 ) {
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 ;
}
2005-05-10 14:47:38 +02:00
static void snd_audigy2nx_proc_read ( snd_info_entry_t * entry ,
snd_info_buffer_t * buffer )
{
static const struct {
int unitid ;
const char * name ;
} jacks [ ] = {
{ 4 , " dig in " } ,
{ 7 , " line in " } ,
{ 19 , " spk out " } ,
{ 20 , " hph out " } ,
} ;
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 ) ;
for ( i = 0 ; i < ARRAY_SIZE ( jacks ) ; + + i ) {
snd_iprintf ( buffer , " %s: " , jacks [ i ] . name ) ;
err = snd_usb_ctl_msg ( mixer - > chip - > dev ,
usb_rcvctrlpipe ( mixer - > chip - > dev , 0 ) ,
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 )
snd_iprintf ( buffer , " %02x %02x \n " , buf [ 1 ] , buf [ 2 ] ) ;
else
snd_iprintf ( buffer , " ? \n " ) ;
}
}
2005-04-29 16:23:13 +02:00
int snd_usb_create_mixer ( snd_usb_audio_t * chip , int ctrlif )
{
static snd_device_ops_t dev_ops = {
. dev_free = snd_usb_mixer_dev_free
} ;
struct usb_mixer_interface * mixer ;
int err ;
strcpy ( chip - > card - > mixername , " USB Mixer " ) ;
mixer = kcalloc ( 1 , sizeof ( * mixer ) , GFP_KERNEL ) ;
if ( ! mixer )
return - ENOMEM ;
mixer - > chip = chip ;
mixer - > ctrlif = ctrlif ;
# ifdef IGNORE_CTL_ERROR
mixer - > ignore_ctl_error = 1 ;
# endif
2005-04-29 16:26:14 +02:00
mixer - > id_elems = kcalloc ( 256 , sizeof ( * mixer - > id_elems ) , GFP_KERNEL ) ;
if ( ! mixer - > id_elems ) {
kfree ( mixer ) ;
return - ENOMEM ;
}
2005-04-29 16:23:13 +02:00
2005-04-29 16:26:14 +02:00
if ( ( err = snd_usb_mixer_controls ( mixer ) ) < 0 | |
2005-05-03 08:02:40 +02:00
( err = snd_usb_mixer_status_create ( mixer ) ) < 0 )
goto _error ;
2005-04-29 16:23:13 +02:00
2005-05-03 08:02:40 +02:00
if ( ( err = snd_usb_soundblaster_remote_init ( mixer ) ) < 0 )
goto _error ;
if ( mixer - > chip - > usb_id = = USB_ID ( 0x041e , 0x3020 ) ) {
2005-05-10 14:47:38 +02:00
snd_info_entry_t * entry ;
2005-05-03 08:02:40 +02:00
if ( ( err = snd_audigy2nx_controls_create ( mixer ) ) < 0 )
goto _error ;
2005-05-10 14:47:38 +02:00
if ( ! snd_card_proc_new ( chip - > card , " audigy2nx " , & entry ) )
snd_info_set_text_ops ( entry , mixer , 1024 ,
snd_audigy2nx_proc_read ) ;
2005-04-29 16:29:28 +02:00
}
2005-04-29 16:23:13 +02:00
err = snd_device_new ( chip - > card , SNDRV_DEV_LOWLEVEL , mixer , & dev_ops ) ;
2005-05-03 08:02:40 +02:00
if ( err < 0 )
goto _error ;
2005-04-29 16:23:13 +02:00
list_add ( & mixer - > list , & chip - > mixer_list ) ;
return 0 ;
2005-05-03 08:02:40 +02:00
_error :
snd_usb_mixer_free ( mixer ) ;
return err ;
2005-04-29 16:23:13 +02:00
}
void snd_usb_mixer_disconnect ( struct list_head * p )
{
2005-04-29 16:26:14 +02:00
struct usb_mixer_interface * mixer ;
mixer = list_entry ( p , struct usb_mixer_interface , list ) ;
if ( mixer - > urb )
usb_kill_urb ( mixer - > urb ) ;
2005-04-29 16:29:28 +02:00
if ( mixer - > rc_urb )
usb_kill_urb ( mixer - > rc_urb ) ;
2005-04-29 16:23:13 +02:00
}