2010-03-11 23:13:22 +03:00
/*
* USB Audio Driver for ALSA
*
* Quirks and vendor - specific extensions for mixer interfaces
*
* Copyright ( c ) 2002 by Takashi Iwai < tiwai @ suse . de >
*
* Many codes borrowed from audio . c by
* Alan Cox ( alan @ lxorguk . ukuu . org . uk )
* Thomas Sailer ( sailer @ ife . ee . ethz . ch )
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/init.h>
# include <linux/usb.h>
# include <linux/usb/audio.h>
# include <sound/core.h>
# include <sound/control.h>
# include <sound/hwdep.h>
# include <sound/info.h>
# include "usbaudio.h"
2010-03-11 23:13:23 +03:00
# include "mixer.h"
2010-03-11 23:13:22 +03:00
# 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 ;
}
}