1999-05-02 18:21:40 +02:00
/*
* ALSA command line mixer utility
2007-10-15 10:25:17 +02:00
* Copyright ( c ) 1999 - 2000 by Jaroslav Kysela < perex @ perex . cz >
1999-05-02 18:21:40 +02:00
*
* 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
2017-11-14 14:28:51 +01:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
1999-05-02 18:21:40 +02:00
*
*/
2023-08-30 18:48:23 +02:00
# include "aconfig.h"
1999-05-02 18:21:40 +02:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <getopt.h>
# include <stdarg.h>
# include <ctype.h>
# include <math.h>
2000-08-11 21:29:55 +02:00
# include <errno.h>
2000-08-12 17:34:17 +02:00
# include <assert.h>
2001-09-11 11:34:14 +02:00
# include <alsa/asoundlib.h>
2019-11-20 05:28:53 +01:00
# include <poll.h>
2012-03-12 10:56:22 +01:00
# include <stdint.h>
1999-05-02 18:21:40 +02:00
# include "amixer.h"
2012-03-12 10:56:22 +01:00
# include "../alsamixer/volume_mapping.h"
1999-05-02 18:21:40 +02:00
2001-07-23 15:26:34 +02:00
# define LEVEL_BASIC (1<<0)
# define LEVEL_INACTIVE (1<<1)
# define LEVEL_ID (1<<2)
1999-05-02 18:21:40 +02:00
2005-12-01 12:32:28 +01:00
static int quiet = 0 ;
static int debugflag = 0 ;
static int no_check = 0 ;
static int smixer_level = 0 ;
static int ignore_error = 0 ;
static struct snd_mixer_selem_regopt smixer_options ;
static char card [ 64 ] = " default " ;
1999-05-02 18:21:40 +02:00
2000-08-12 17:34:17 +02:00
static void error ( const char * fmt , . . . )
1999-05-02 18:21:40 +02:00
{
va_list va ;
va_start ( va , fmt ) ;
fprintf ( stderr , " amixer: " ) ;
vfprintf ( stderr , fmt , va ) ;
fprintf ( stderr , " \n " ) ;
va_end ( va ) ;
}
2000-08-11 21:29:55 +02:00
static int help ( void )
1999-05-02 18:21:40 +02:00
{
2005-12-01 12:32:28 +01:00
printf ( " Usage: amixer <options> [command] \n " ) ;
1999-05-02 18:21:40 +02:00
printf ( " \n Available options: \n " ) ;
printf ( " -h,--help this help \n " ) ;
2002-05-07 14:20:55 +02:00
printf ( " -c,--card N select the card \n " ) ;
2002-04-07 22:42:20 +02:00
printf ( " -D,--device N select the device, default '%s' \n " , card ) ;
printf ( " -d,--debug debug mode \n " ) ;
2005-02-04 16:35:27 +01:00
printf ( " -n,--nocheck do not perform range checking \n " ) ;
1999-05-02 18:21:40 +02:00
printf ( " -v,--version print version of this program \n " ) ;
2001-07-23 15:26:34 +02:00
printf ( " -q,--quiet be quiet \n " ) ;
printf ( " -i,--inactive show also inactive controls \n " ) ;
2005-03-27 14:50:34 +02:00
printf ( " -a,--abstract L select abstraction level (none or basic) \n " ) ;
2005-12-01 12:32:28 +01:00
printf ( " -s,--stdin Read and execute commands from stdin sequentially \n " ) ;
2012-03-12 10:56:22 +01:00
printf ( " -R,--raw-volume Use the raw value (default) \n " ) ;
printf ( " -M,--mapped-volume Use the mapped volume \n " ) ;
1999-05-02 18:21:40 +02:00
printf ( " \n Available commands: \n " ) ;
2000-08-11 21:29:55 +02:00
printf ( " scontrols show all mixer simple controls \n " ) ;
printf ( " scontents show contents of all mixer simple controls (default command) \n " ) ;
printf ( " sset sID P set contents for one mixer simple control \n " ) ;
2001-03-16 08:35:25 +01:00
printf ( " sget sID get contents for one mixer simple control \n " ) ;
2000-08-11 21:29:55 +02:00
printf ( " controls show all controls for given card \n " ) ;
printf ( " contents show contents of all controls for given card \n " ) ;
2001-03-16 08:35:25 +01:00
printf ( " cset cID P set control contents for one control \n " ) ;
printf ( " cget cID get control contents for one control \n " ) ;
2021-10-04 10:40:07 +02:00
printf ( " \n Available advanced commands: \n " ) ;
printf ( " sevents show the mixer events for simple controls \n " ) ;
2022-08-25 09:33:58 +02:00
printf ( " events show the mixer events for controls \n " ) ;
2000-08-11 21:29:55 +02:00
return 0 ;
1999-05-02 18:21:40 +02:00
}
2000-08-12 17:34:17 +02:00
static int info ( void )
1999-05-02 18:21:40 +02:00
{
int err ;
2000-07-15 12:21:59 +02:00
snd_ctl_t * handle ;
snd_mixer_t * mhandle ;
2001-02-07 16:13:17 +01:00
snd_ctl_card_info_t * info ;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_list_t * clist ;
2001-02-07 16:13:17 +01:00
snd_ctl_card_info_alloca ( & info ) ;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_list_alloca ( & clist ) ;
1999-05-02 18:21:40 +02:00
2001-03-26 14:45:50 +02:00
if ( ( err = snd_ctl_open ( & handle , card , 0 ) ) < 0 ) {
2003-04-01 16:09:09 +02:00
error ( " Control device %s open error: %s " , card , snd_strerror ( err ) ) ;
2000-08-11 21:29:55 +02:00
return err ;
1999-05-02 18:21:40 +02:00
}
2001-09-05 11:23:31 +02:00
2001-02-07 16:13:17 +01:00
if ( ( err = snd_ctl_card_info ( handle , info ) ) < 0 ) {
2003-04-01 16:09:09 +02:00
error ( " Control device %s hw info error: %s " , card , snd_strerror ( err ) ) ;
2000-08-11 21:29:55 +02:00
return err ;
1999-05-02 18:21:40 +02:00
}
2001-02-07 16:13:17 +01:00
printf ( " Card %s '%s'/'%s' \n " , card , snd_ctl_card_info_get_id ( info ) ,
snd_ctl_card_info_get_longname ( info ) ) ;
printf ( " Mixer name : '%s' \n " , snd_ctl_card_info_get_mixername ( info ) ) ;
2001-06-25 15:14:51 +02:00
printf ( " Components : '%s' \n " , snd_ctl_card_info_get_components ( info ) ) ;
2001-02-09 12:20:32 +01:00
if ( ( err = snd_ctl_elem_list ( handle , clist ) ) < 0 ) {
error ( " snd_ctl_elem_list failure: %s " , snd_strerror ( err ) ) ;
2000-07-15 12:21:59 +02:00
} else {
2001-02-09 12:20:32 +01:00
printf ( " Controls : %i \n " , snd_ctl_elem_list_get_count ( clist ) ) ;
2000-07-15 12:21:59 +02:00
}
snd_ctl_close ( handle ) ;
2001-03-26 14:45:50 +02:00
if ( ( err = snd_mixer_open ( & mhandle , 0 ) ) < 0 ) {
2001-02-11 16:45:44 +01:00
error ( " Mixer open error: %s " , snd_strerror ( err ) ) ;
return err ;
}
2005-06-06 16:02:10 +02:00
if ( smixer_level = = 0 & & ( err = snd_mixer_attach ( mhandle , card ) ) < 0 ) {
2001-02-11 16:45:44 +01:00
error ( " Mixer attach %s error: %s " , card , snd_strerror ( err ) ) ;
snd_mixer_close ( mhandle ) ;
2000-08-11 21:29:55 +02:00
return err ;
2000-07-15 12:21:59 +02:00
}
2005-03-27 14:50:34 +02:00
if ( ( err = snd_mixer_selem_register ( mhandle , smixer_level > 0 ? & smixer_options : NULL , NULL ) ) < 0 ) {
2001-02-11 16:45:44 +01:00
error ( " Mixer register error: %s " , snd_strerror ( err ) ) ;
snd_mixer_close ( mhandle ) ;
return err ;
}
err = snd_mixer_load ( mhandle ) ;
2001-02-09 12:20:32 +01:00
if ( err < 0 ) {
2004-02-21 19:44:18 +01:00
error ( " Mixer load %s error: %s " , card , snd_strerror ( err ) ) ;
2001-02-11 16:45:44 +01:00
snd_mixer_close ( mhandle ) ;
return err ;
2000-07-15 12:21:59 +02:00
}
2001-02-11 16:45:44 +01:00
printf ( " Simple ctrls : %i \n " , snd_mixer_get_count ( mhandle ) ) ;
2000-07-15 12:21:59 +02:00
snd_mixer_close ( mhandle ) ;
1999-05-02 18:21:40 +02:00
return 0 ;
}
2001-02-09 12:20:32 +01:00
static const char * control_type ( snd_ctl_elem_info_t * info )
2000-08-11 21:29:55 +02:00
{
2001-02-09 12:20:32 +01:00
return snd_ctl_elem_type_name ( snd_ctl_elem_info_get_type ( info ) ) ;
2000-08-11 21:29:55 +02:00
}
2001-02-09 12:20:32 +01:00
static const char * control_access ( snd_ctl_elem_info_t * info )
2000-08-11 21:29:55 +02:00
{
2001-02-07 00:48:29 +01:00
static char result [ 10 ] ;
2000-08-11 21:29:55 +02:00
char * res = result ;
2001-02-09 12:20:32 +01:00
* res + + = snd_ctl_elem_info_is_readable ( info ) ? ' r ' : ' - ' ;
* res + + = snd_ctl_elem_info_is_writable ( info ) ? ' w ' : ' - ' ;
* res + + = snd_ctl_elem_info_is_inactive ( info ) ? ' i ' : ' - ' ;
* res + + = snd_ctl_elem_info_is_volatile ( info ) ? ' v ' : ' - ' ;
* res + + = snd_ctl_elem_info_is_locked ( info ) ? ' l ' : ' - ' ;
2006-07-05 17:46:10 +02:00
* res + + = snd_ctl_elem_info_is_tlv_readable ( info ) ? ' R ' : ' - ' ;
* res + + = snd_ctl_elem_info_is_tlv_writable ( info ) ? ' W ' : ' - ' ;
* res + + = snd_ctl_elem_info_is_tlv_commandable ( info ) ? ' C ' : ' - ' ;
2000-08-11 21:29:55 +02:00
* res + + = ' \0 ' ;
return result ;
}
2006-08-25 12:00:14 +02:00
# define check_range(val, min, max) \
( no_check ? ( val ) : ( ( val < min ) ? ( min ) : ( val > max ) ? ( max ) : ( val ) ) )
2000-08-11 21:29:55 +02:00
#if 0
1999-05-02 18:21:40 +02:00
static int convert_range ( int val , int omin , int omax , int nmin , int nmax )
{
int orange = omax - omin , nrange = nmax - nmin ;
if ( orange = = 0 )
return 0 ;
1999-07-30 23:58:34 +02:00
return rint ( ( ( ( double ) nrange * ( ( double ) val - ( double ) omin ) ) + ( ( double ) orange / 2.0 ) ) / ( ( double ) orange + ( double ) nmin ) ) ;
1999-05-02 18:21:40 +02:00
}
2000-08-11 21:29:55 +02:00
# endif
1999-05-02 18:21:40 +02:00
2000-08-11 21:29:55 +02:00
#if 0
1999-05-02 18:21:40 +02:00
static int convert_db_range ( int val , int omin , int omax , int nmin , int nmax )
{
int orange = omax - omin , nrange = nmax - nmin ;
if ( orange = = 0 )
return 0 ;
2000-08-11 21:29:55 +02:00
return rint ( ( ( ( double ) nrange * ( ( double ) val - ( double ) omin ) ) + ( ( double ) orange / 2.0 ) ) / ( double ) orange + ( double ) nmin ) ;
1999-05-02 18:21:40 +02:00
}
2000-08-11 21:29:55 +02:00
# endif
1999-05-02 18:21:40 +02:00
1999-07-30 23:58:34 +02:00
/* Fuction to convert from volume to percentage. val = volume */
2012-03-12 10:56:22 +01:00
static int convert_prange ( long val , long min , long max )
1999-05-02 18:21:40 +02:00
{
2012-03-12 10:56:22 +01:00
long range = max - min ;
1999-07-31 00:24:24 +02:00
int tmp ;
if ( range = = 0 )
2020-06-05 13:34:37 +02:00
return min ;
1999-07-31 00:24:24 +02:00
val - = min ;
tmp = rint ( ( double ) val / ( double ) range * 100 ) ;
return tmp ;
1999-07-30 23:58:34 +02:00
}
2020-06-05 13:34:37 +02:00
/* Function to convert from percentage to volume. perc = percentage */
static long convert_prange1 ( long perc , long min , long max )
{
long tmp ;
1999-07-30 23:58:34 +02:00
2020-06-05 17:59:31 +02:00
tmp = rint ( ( double ) perc * ( double ) ( max - min ) * 0.01 ) ;
2020-06-05 13:34:37 +02:00
if ( tmp = = 0 & & perc > 0 )
tmp + + ;
return tmp + min ;
}
1999-05-02 18:21:40 +02:00
2006-08-25 12:00:14 +02:00
struct volume_ops {
int ( * get_range ) ( snd_mixer_elem_t * elem , long * min , long * max ) ;
int ( * get ) ( snd_mixer_elem_t * elem , snd_mixer_selem_channel_id_t c ,
long * value ) ;
int ( * set ) ( snd_mixer_elem_t * elem , snd_mixer_selem_channel_id_t c ,
2012-03-12 10:56:22 +01:00
long value , int dir ) ;
2006-08-25 12:00:14 +02:00
} ;
2012-03-12 10:56:22 +01:00
enum { VOL_RAW , VOL_DB , VOL_MAP } ;
2006-08-25 12:00:14 +02:00
struct volume_ops_set {
int ( * has_volume ) ( snd_mixer_elem_t * elem ) ;
2012-03-12 10:56:22 +01:00
struct volume_ops v [ 3 ] ;
2006-08-25 12:00:14 +02:00
} ;
static int set_playback_dB ( snd_mixer_elem_t * elem ,
2012-03-12 10:56:22 +01:00
snd_mixer_selem_channel_id_t c , long value , int dir )
1999-07-21 13:52:55 +02:00
{
2012-03-12 10:56:22 +01:00
return snd_mixer_selem_set_playback_dB ( elem , c , value , dir ) ;
2006-08-25 12:00:14 +02:00
}
1999-07-21 13:52:55 +02:00
2006-08-25 12:00:14 +02:00
static int set_capture_dB ( snd_mixer_elem_t * elem ,
2012-03-12 10:56:22 +01:00
snd_mixer_selem_channel_id_t c , long value , int dir )
{
return snd_mixer_selem_set_capture_dB ( elem , c , value , dir ) ;
}
static int set_playback_raw_volume ( snd_mixer_elem_t * elem ,
snd_mixer_selem_channel_id_t c ,
2023-08-30 12:35:03 +02:00
long value , int dir ATTRIBUTE_UNUSED )
2012-03-12 10:56:22 +01:00
{
return snd_mixer_selem_set_playback_volume ( elem , c , value ) ;
}
static int set_capture_raw_volume ( snd_mixer_elem_t * elem ,
snd_mixer_selem_channel_id_t c ,
2023-08-30 12:35:03 +02:00
long value , int dir ATTRIBUTE_UNUSED )
2012-03-12 10:56:22 +01:00
{
return snd_mixer_selem_set_capture_volume ( elem , c , value ) ;
}
/* FIXME: normalize to int32 space to be compatible with other types */
# define MAP_VOL_RES (INT32_MAX / 100)
2023-08-30 12:35:03 +02:00
static int get_mapped_volume_range ( snd_mixer_elem_t * elem ATTRIBUTE_UNUSED ,
2012-03-12 10:56:22 +01:00
long * pmin , long * pmax )
{
* pmin = 0 ;
* pmax = MAP_VOL_RES ;
return 0 ;
}
static int get_playback_mapped_volume ( snd_mixer_elem_t * elem ,
snd_mixer_selem_channel_id_t c ,
long * value )
{
* value = ( rint ) ( get_normalized_playback_volume ( elem , c ) * MAP_VOL_RES ) ;
return 0 ;
}
static int set_playback_mapped_volume ( snd_mixer_elem_t * elem ,
snd_mixer_selem_channel_id_t c ,
long value , int dir )
2006-08-25 12:00:14 +02:00
{
2012-03-12 10:56:22 +01:00
return set_normalized_playback_volume ( elem , c ,
( double ) value / MAP_VOL_RES , dir ) ;
}
static int get_capture_mapped_volume ( snd_mixer_elem_t * elem ,
snd_mixer_selem_channel_id_t c ,
long * value )
{
* value = ( rint ) ( get_normalized_capture_volume ( elem , c ) * MAP_VOL_RES ) ;
return 0 ;
}
static int set_capture_mapped_volume ( snd_mixer_elem_t * elem ,
snd_mixer_selem_channel_id_t c ,
long value , int dir )
{
return set_normalized_capture_volume ( elem , c ,
( double ) value / MAP_VOL_RES , dir ) ;
2006-08-25 12:00:14 +02:00
}
2008-11-21 13:10:02 +01:00
static const struct volume_ops_set vol_ops [ 2 ] = {
2006-08-25 12:00:14 +02:00
{
. has_volume = snd_mixer_selem_has_playback_volume ,
. v = { { snd_mixer_selem_get_playback_volume_range ,
snd_mixer_selem_get_playback_volume ,
2012-03-12 10:56:22 +01:00
set_playback_raw_volume } ,
2006-08-25 12:00:14 +02:00
{ snd_mixer_selem_get_playback_dB_range ,
snd_mixer_selem_get_playback_dB ,
2012-03-12 10:56:22 +01:00
set_playback_dB } ,
{ get_mapped_volume_range ,
get_playback_mapped_volume ,
set_playback_mapped_volume } ,
} ,
2006-08-25 12:00:14 +02:00
} ,
{
. has_volume = snd_mixer_selem_has_capture_volume ,
. v = { { snd_mixer_selem_get_capture_volume_range ,
snd_mixer_selem_get_capture_volume ,
2012-03-12 10:56:22 +01:00
set_capture_raw_volume } ,
2006-08-25 12:00:14 +02:00
{ snd_mixer_selem_get_capture_dB_range ,
snd_mixer_selem_get_capture_dB ,
2012-03-12 10:56:22 +01:00
set_capture_dB } ,
{ get_mapped_volume_range ,
get_capture_mapped_volume ,
set_capture_mapped_volume } ,
} ,
2006-08-25 12:00:14 +02:00
} ,
} ;
2012-03-12 10:56:22 +01:00
static int std_vol_type = VOL_RAW ;
2006-08-25 12:00:14 +02:00
static int set_volume_simple ( snd_mixer_elem_t * elem ,
snd_mixer_selem_channel_id_t chn ,
char * * ptr , int dir )
{
long val , orig , pmin , pmax ;
char * p = * ptr , * s ;
2012-03-12 10:56:22 +01:00
int invalid = 0 , percent = 0 , err = 0 ;
2014-11-23 10:04:24 +01:00
int vol_type ;
2012-03-12 10:56:22 +01:00
double scale = 1.0 ;
int correct = 0 ;
2006-08-25 12:00:14 +02:00
if ( ! vol_ops [ dir ] . has_volume ( elem ) )
invalid = 1 ;
if ( * p = = ' : ' )
p + + ;
if ( * p = = ' \0 ' | | ( ! isdigit ( * p ) & & * p ! = ' - ' ) )
goto skip ;
s = p ;
val = strtol ( s , & p , 10 ) ;
if ( * p = = ' . ' ) {
p + + ;
strtol ( p , & p , 10 ) ;
1999-07-21 13:52:55 +02:00
}
2006-08-25 12:00:14 +02:00
if ( * p = = ' % ' ) {
2014-11-23 10:04:24 +01:00
vol_type = std_vol_type ;
2012-03-12 10:56:22 +01:00
percent = 1 ;
2006-08-25 12:00:14 +02:00
p + + ;
2014-11-23 09:40:07 +01:00
} else if ( toupper ( p [ 0 ] ) = = ' D ' & & toupper ( p [ 1 ] ) = = ' B ' ) {
2012-03-12 10:56:22 +01:00
vol_type = VOL_DB ;
2006-08-25 12:00:14 +02:00
p + = 2 ;
2012-03-12 10:56:22 +01:00
scale = 100 ;
2014-11-23 10:04:24 +01:00
} else {
2012-03-12 10:56:22 +01:00
vol_type = VOL_RAW ;
2014-11-23 10:04:24 +01:00
}
if ( * p & & ! strchr ( " ,:+- " , * p ) )
invalid = 1 ;
2012-03-12 10:56:22 +01:00
val = ( long ) ( strtod ( s , NULL ) * scale ) ;
if ( vol_ops [ dir ] . v [ vol_type ] . get_range ( elem , & pmin , & pmax ) < 0 )
invalid = 1 ;
if ( percent )
val = ( long ) convert_prange1 ( val , pmin , pmax ) ;
2006-08-25 12:00:14 +02:00
if ( * p = = ' + ' | | * p = = ' - ' ) {
if ( ! invalid ) {
if ( vol_ops [ dir ] . v [ vol_type ] . get ( elem , chn , & orig ) < 0 )
invalid = 1 ;
2012-03-12 10:56:22 +01:00
if ( * p = = ' + ' ) {
2006-08-25 12:00:14 +02:00
val = orig + val ;
2012-11-13 21:54:20 +01:00
correct = 1 ;
2012-03-12 10:56:22 +01:00
} else {
2006-08-25 12:00:14 +02:00
val = orig - val ;
2012-11-13 21:54:20 +01:00
correct = - 1 ;
2012-03-12 10:56:22 +01:00
}
2006-08-25 12:00:14 +02:00
}
p + + ;
2000-03-18 19:00:37 +01:00
}
2014-11-23 10:04:24 +01:00
if ( * p & & ! strchr ( " ,: " , * p ) )
invalid = 1 ;
2006-08-25 12:00:14 +02:00
if ( ! invalid ) {
val = check_range ( val , pmin , pmax ) ;
2012-03-12 10:56:22 +01:00
err = vol_ops [ dir ] . v [ vol_type ] . set ( elem , chn , val , correct ) ;
2006-08-25 12:00:14 +02:00
}
skip :
if ( * p = = ' , ' )
p + + ;
* ptr = p ;
2006-10-04 09:47:41 +02:00
return err ? err : ( invalid ? - ENOENT : 0 ) ;
1999-07-21 13:52:55 +02:00
}
2001-08-27 11:15:44 +02:00
static int get_bool_simple ( char * * ptr , char * str , int invert , int orig )
{
if ( * * ptr = = ' : ' )
( * ptr ) + + ;
if ( ! strncasecmp ( * ptr , str , strlen ( str ) ) ) {
orig = 1 ^ ( invert ? 1 : 0 ) ;
while ( * * ptr ! = ' \0 ' & & * * ptr ! = ' , ' & & * * ptr ! = ' : ' )
( * ptr ) + + ;
}
if ( * * ptr = = ' , ' | | * * ptr = = ' : ' )
( * ptr ) + + ;
return orig ;
}
static int simple_skip_word ( char * * ptr , char * str )
{
char * xptr = * ptr ;
if ( * xptr = = ' : ' )
xptr + + ;
if ( ! strncasecmp ( xptr , str , strlen ( str ) ) ) {
while ( * xptr ! = ' \0 ' & & * xptr ! = ' , ' & & * xptr ! = ' : ' )
xptr + + ;
if ( * xptr = = ' , ' | | * xptr = = ' : ' )
xptr + + ;
* ptr = xptr ;
return 1 ;
}
return 0 ;
}
2001-02-09 12:20:32 +01:00
static void show_control_id ( snd_ctl_elem_id_t * id )
1999-05-02 18:21:40 +02:00
{
2013-04-09 18:38:58 +02:00
char * str ;
str = snd_ctl_ascii_elem_id_get ( id ) ;
if ( str )
printf ( " %s " , str ) ;
free ( str ) ;
2000-08-11 21:29:55 +02:00
}
2006-07-05 17:46:10 +02:00
static void print_spaces ( unsigned int spaces )
{
while ( spaces - - > 0 )
putc ( ' ' , stdout ) ;
}
2006-07-27 11:02:00 +02:00
static void print_dB ( long dB )
2006-07-05 17:46:10 +02:00
{
2013-05-28 08:43:30 +02:00
if ( dB < 0 ) {
printf ( " -%li.%02lidB " , - dB / 100 , - dB % 100 ) ;
} else {
printf ( " %li.%02lidB " , dB / 100 , dB % 100 ) ;
}
2006-07-05 17:46:10 +02:00
}
static void decode_tlv ( unsigned int spaces , unsigned int * tlv , unsigned int tlv_size )
{
unsigned int type = tlv [ 0 ] ;
unsigned int size ;
unsigned int idx = 0 ;
2013-06-14 11:41:01 +02:00
const char * chmap_type = NULL ;
2019-10-31 17:33:00 +01:00
int lf = 1 ;
2008-08-14 14:57:19 +02:00
2006-07-05 17:46:10 +02:00
if ( tlv_size < 2 * sizeof ( unsigned int ) ) {
printf ( " TLV size error! \n " ) ;
return ;
}
print_spaces ( spaces ) ;
printf ( " | " ) ;
type = tlv [ idx + + ] ;
size = tlv [ idx + + ] ;
tlv_size - = 2 * sizeof ( unsigned int ) ;
if ( size > tlv_size ) {
2019-11-20 05:28:55 +01:00
printf ( " TLV size error (%u, %u, %u)! \n " , type , size , tlv_size ) ;
2006-07-05 17:46:10 +02:00
return ;
}
switch ( type ) {
case SND_CTL_TLVT_CONTAINER :
amixer: fix indentation when printing container TLV contents
decode_tlv() adds indentation in the beginning, with the expectation
that the TLV will be printed on the line afterwards in a switch-case.
However, in the case of a container TLV the switch-case simply adds
another level of indentation and calls decode_tlv() for the inner TLVs.
This causes the first inner TLV to be printed with too much indentation
and double '|'.
Fix that by printing "container" and a newline for container TLVs, so
that the result is as follows:
: values=0,0,0,0,0,0,0,0
| container
| chmap-variable=FL,FR
| chmap-variable=FL,FR,LFE
| chmap-variable=FL,FR,FC
Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-10-01 22:46:53 +02:00
printf ( " container \n " ) ;
2006-07-05 17:46:10 +02:00
size + = sizeof ( unsigned int ) - 1 ;
size / = sizeof ( unsigned int ) ;
while ( idx < size ) {
if ( tlv [ idx + 1 ] > ( size - idx ) * sizeof ( unsigned int ) ) {
printf ( " TLV size error in compound! \n " ) ;
return ;
}
2012-11-26 16:13:57 +01:00
decode_tlv ( spaces + 2 , tlv + idx , tlv [ idx + 1 ] + 8 ) ;
2013-09-30 22:46:50 +02:00
idx + = 2 + ( tlv [ idx + 1 ] + sizeof ( unsigned int ) - 1 ) / sizeof ( unsigned int ) ;
2006-07-05 17:46:10 +02:00
}
2019-10-31 17:33:00 +01:00
lf = 0 ;
2006-07-05 17:46:10 +02:00
break ;
case SND_CTL_TLVT_DB_SCALE :
printf ( " dBscale- " ) ;
if ( size ! = 2 * sizeof ( unsigned int ) ) {
while ( size > 0 ) {
2008-08-14 14:57:19 +02:00
printf ( " 0x%08x, " , tlv [ idx + + ] ) ;
2006-07-05 17:46:10 +02:00
size - = sizeof ( unsigned int ) ;
}
} else {
printf ( " min= " ) ;
2008-08-14 14:57:19 +02:00
print_dB ( ( int ) tlv [ 2 ] ) ;
2006-07-05 17:46:10 +02:00
printf ( " ,step= " ) ;
print_dB ( tlv [ 3 ] & 0xffff ) ;
printf ( " ,mute=%i " , ( tlv [ 3 ] > > 16 ) & 1 ) ;
}
break ;
2006-08-28 13:28:46 +02:00
# ifdef SND_CTL_TLVT_DB_LINEAR
case SND_CTL_TLVT_DB_LINEAR :
printf ( " dBlinear- " ) ;
if ( size ! = 2 * sizeof ( unsigned int ) ) {
while ( size > 0 ) {
2008-08-14 14:57:19 +02:00
printf ( " 0x%08x, " , tlv [ idx + + ] ) ;
2006-08-28 13:28:46 +02:00
size - = sizeof ( unsigned int ) ;
}
} else {
printf ( " min= " ) ;
2013-01-31 17:47:50 +01:00
print_dB ( ( int ) tlv [ 2 ] ) ;
2006-08-28 13:28:46 +02:00
printf ( " ,max= " ) ;
2013-01-31 17:47:50 +01:00
print_dB ( ( int ) tlv [ 3 ] ) ;
2006-08-28 13:28:46 +02:00
}
break ;
2008-08-14 14:57:19 +02:00
# endif
# ifdef SND_CTL_TLVT_DB_RANGE
case SND_CTL_TLVT_DB_RANGE :
printf ( " dBrange- \n " ) ;
2011-06-16 14:19:18 +02:00
if ( ( size % ( 6 * sizeof ( unsigned int ) ) ) ! = 0 ) {
2008-08-14 14:57:19 +02:00
while ( size > 0 ) {
printf ( " 0x%08x, " , tlv [ idx + + ] ) ;
size - = sizeof ( unsigned int ) ;
}
break ;
}
2011-06-16 14:19:18 +02:00
while ( size > 0 ) {
2008-08-14 14:57:19 +02:00
print_spaces ( spaces + 2 ) ;
2011-06-16 14:19:18 +02:00
printf ( " rangemin=%i, " , tlv [ idx + + ] ) ;
printf ( " ,rangemax=%i \n " , tlv [ idx + + ] ) ;
decode_tlv ( spaces + 4 , tlv + idx , 4 * sizeof ( unsigned int ) ) ;
idx + = 4 ;
size - = 6 * sizeof ( unsigned int ) ;
2008-08-14 14:57:19 +02:00
}
break ;
2010-02-10 11:52:53 +01:00
# endif
# ifdef SND_CTL_TLVT_DB_MINMAX
case SND_CTL_TLVT_DB_MINMAX :
case SND_CTL_TLVT_DB_MINMAX_MUTE :
if ( type = = SND_CTL_TLVT_DB_MINMAX_MUTE )
printf ( " dBminmaxmute- " ) ;
else
printf ( " dBminmax- " ) ;
if ( size ! = 2 * sizeof ( unsigned int ) ) {
while ( size > 0 ) {
printf ( " 0x%08x, " , tlv [ idx + + ] ) ;
size - = sizeof ( unsigned int ) ;
}
} else {
printf ( " min= " ) ;
2013-01-31 17:47:50 +01:00
print_dB ( ( int ) tlv [ 2 ] ) ;
2010-02-10 11:52:53 +01:00
printf ( " ,max= " ) ;
2013-01-31 17:47:50 +01:00
print_dB ( ( int ) tlv [ 3 ] ) ;
2010-02-10 11:52:53 +01:00
}
break ;
2013-06-14 11:41:01 +02:00
# endif
# ifdef SND_CTL_TLVT_CHMAP_FIXED
case SND_CTL_TLVT_CHMAP_FIXED :
chmap_type = " fixed " ;
/* Fall through */
case SND_CTL_TLVT_CHMAP_VAR :
if ( ! chmap_type )
chmap_type = " variable " ;
/* Fall through */
case SND_CTL_TLVT_CHMAP_PAIRED :
if ( ! chmap_type )
chmap_type = " paired " ;
printf ( " chmap-%s= " , chmap_type ) ;
while ( size > 0 ) {
printf ( " %s " , snd_pcm_chmap_name ( tlv [ idx + + ] ) ) ;
size - = sizeof ( unsigned int ) ;
if ( size > 0 )
printf ( " , " ) ;
}
break ;
2006-08-28 13:28:46 +02:00
# endif
2006-07-05 17:46:10 +02:00
default :
2019-11-20 05:28:55 +01:00
printf ( " unk-%u- " , type ) ;
2006-07-05 17:46:10 +02:00
while ( size > 0 ) {
2008-08-14 14:57:19 +02:00
printf ( " 0x%08x, " , tlv [ idx + + ] ) ;
2006-07-05 17:46:10 +02:00
size - = sizeof ( unsigned int ) ;
}
break ;
}
2019-10-31 17:33:00 +01:00
if ( lf )
putc ( ' \n ' , stdout ) ;
2006-07-05 17:46:10 +02:00
}
2001-02-11 16:45:44 +01:00
static int show_control ( const char * space , snd_hctl_elem_t * elem ,
int level )
2000-08-11 21:29:55 +02:00
{
int err ;
2006-07-05 17:46:10 +02:00
unsigned int item , idx , count , * tlv ;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_type_t type ;
2001-02-11 16:45:44 +01:00
snd_ctl_elem_id_t * id ;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_t * info ;
2001-02-11 16:45:44 +01:00
snd_ctl_elem_value_t * control ;
2007-04-05 17:23:42 +02:00
snd_aes_iec958_t iec958 ;
2001-02-11 16:45:44 +01:00
snd_ctl_elem_id_alloca ( & id ) ;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_alloca ( & info ) ;
2001-02-11 16:45:44 +01:00
snd_ctl_elem_value_alloca ( & control ) ;
if ( ( err = snd_hctl_elem_info ( elem , info ) ) < 0 ) {
2001-07-23 15:26:34 +02:00
error ( " Control %s snd_hctl_elem_info error: %s \n " , card , snd_strerror ( err ) ) ;
2000-08-11 21:29:55 +02:00
return err ;
}
2001-07-23 15:26:34 +02:00
if ( level & LEVEL_ID ) {
2001-02-11 16:45:44 +01:00
snd_hctl_elem_get_id ( elem , id ) ;
2001-02-07 00:48:29 +01:00
show_control_id ( id ) ;
2000-08-11 21:29:55 +02:00
printf ( " \n " ) ;
1999-05-02 18:21:40 +02:00
}
2001-02-09 12:20:32 +01:00
count = snd_ctl_elem_info_get_count ( info ) ;
type = snd_ctl_elem_info_get_type ( info ) ;
2019-11-20 05:28:55 +01:00
printf ( " %s; type=%s,access=%s,values=%u " , space , control_type ( info ) , control_access ( info ) , count ) ;
2001-07-11 17:00:59 +02:00
switch ( type ) {
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_INTEGER :
2001-02-07 00:48:29 +01:00
printf ( " ,min=%li,max=%li,step=%li \n " ,
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_get_min ( info ) ,
snd_ctl_elem_info_get_max ( info ) ,
snd_ctl_elem_info_get_step ( info ) ) ;
2000-08-11 21:29:55 +02:00
break ;
2002-05-13 11:54:24 +02:00
case SND_CTL_ELEM_TYPE_INTEGER64 :
2020-02-05 09:12:20 +01:00
printf ( " ,min=%lli,max=%lli,step=%lli \n " ,
2002-05-13 11:54:24 +02:00
snd_ctl_elem_info_get_min64 ( info ) ,
snd_ctl_elem_info_get_max64 ( info ) ,
snd_ctl_elem_info_get_step64 ( info ) ) ;
break ;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_ENUMERATED :
2001-02-07 00:48:29 +01:00
{
2001-02-09 12:20:32 +01:00
unsigned int items = snd_ctl_elem_info_get_items ( info ) ;
2001-02-07 00:48:29 +01:00
printf ( " ,items=%u \n " , items ) ;
for ( item = 0 ; item < items ; item + + ) {
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_set_item ( info , item ) ;
2001-02-11 16:45:44 +01:00
if ( ( err = snd_hctl_elem_info ( elem , info ) ) < 0 ) {
2001-02-09 12:20:32 +01:00
error ( " Control %s element info error: %s \n " , card , snd_strerror ( err ) ) ;
2000-08-11 21:29:55 +02:00
return err ;
}
2001-02-09 12:20:32 +01:00
printf ( " %s; Item #%u '%s' \n " , space , item , snd_ctl_elem_info_get_item_name ( info ) ) ;
2000-08-11 21:29:55 +02:00
}
break ;
2001-02-07 00:48:29 +01:00
}
2000-08-11 21:29:55 +02:00
default :
printf ( " \n " ) ;
break ;
1999-05-02 18:21:40 +02:00
}
2001-07-23 15:26:34 +02:00
if ( level & LEVEL_BASIC ) {
2010-02-01 13:22:39 +01:00
if ( ! snd_ctl_elem_info_is_readable ( info ) )
goto __skip_read ;
2001-02-11 16:45:44 +01:00
if ( ( err = snd_hctl_elem_read ( elem , control ) ) < 0 ) {
2001-02-09 12:20:32 +01:00
error ( " Control %s element read error: %s \n " , card , snd_strerror ( err ) ) ;
2000-08-11 21:29:55 +02:00
return err ;
}
printf ( " %s: values= " , space ) ;
2001-02-07 00:48:29 +01:00
for ( idx = 0 ; idx < count ; idx + + ) {
2000-08-11 21:29:55 +02:00
if ( idx > 0 )
printf ( " , " ) ;
2001-07-11 17:00:59 +02:00
switch ( type ) {
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BOOLEAN :
2001-02-11 16:45:44 +01:00
printf ( " %s " , snd_ctl_elem_value_get_boolean ( control , idx ) ? " on " : " off " ) ;
2000-08-11 21:29:55 +02:00
break ;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_INTEGER :
2001-02-11 16:45:44 +01:00
printf ( " %li " , snd_ctl_elem_value_get_integer ( control , idx ) ) ;
2000-08-11 21:29:55 +02:00
break ;
2002-05-13 11:54:24 +02:00
case SND_CTL_ELEM_TYPE_INTEGER64 :
2020-02-05 09:12:20 +01:00
printf ( " %lli " , snd_ctl_elem_value_get_integer64 ( control , idx ) ) ;
2002-05-13 11:54:24 +02:00
break ;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_ENUMERATED :
2001-02-11 16:45:44 +01:00
printf ( " %u " , snd_ctl_elem_value_get_enumerated ( control , idx ) ) ;
2000-08-11 21:29:55 +02:00
break ;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BYTES :
2001-02-11 16:45:44 +01:00
printf ( " 0x%02x " , snd_ctl_elem_value_get_byte ( control , idx ) ) ;
2000-08-11 21:29:55 +02:00
break ;
2007-04-05 17:23:42 +02:00
case SND_CTL_ELEM_TYPE_IEC958 :
snd_ctl_elem_value_get_iec958 ( control , & iec958 ) ;
printf ( " [AES0=0x%02x AES1=0x%02x AES2=0x%02x AES3=0x%02x] " ,
iec958 . status [ 0 ] , iec958 . status [ 1 ] ,
iec958 . status [ 2 ] , iec958 . status [ 3 ] ) ;
break ;
2000-08-11 21:29:55 +02:00
default :
printf ( " ? " ) ;
break ;
}
}
printf ( " \n " ) ;
2010-02-01 13:22:39 +01:00
__skip_read :
2006-07-05 17:46:10 +02:00
if ( ! snd_ctl_elem_info_is_tlv_readable ( info ) )
goto __skip_tlv ;
2016-01-28 09:32:18 +01:00
/* skip ASoC ext bytes controls that may have huge binary TLV data */
if ( type = = SND_CTL_ELEM_TYPE_BYTES & &
! snd_ctl_elem_info_is_readable ( info ) & &
! snd_ctl_elem_info_is_writable ( info ) ) {
printf ( " %s; ASoC TLV Byte control, skipping bytes dump \n " , space ) ;
goto __skip_tlv ;
}
2006-07-05 17:46:10 +02:00
tlv = malloc ( 4096 ) ;
if ( ( err = snd_hctl_elem_tlv_read ( elem , tlv , 4096 ) ) < 0 ) {
error ( " Control %s element TLV read error: %s \n " , card , snd_strerror ( err ) ) ;
free ( tlv ) ;
return err ;
}
decode_tlv ( strlen ( space ) , tlv , 4096 ) ;
free ( tlv ) ;
1999-05-02 18:21:40 +02:00
}
2006-07-05 17:46:10 +02:00
__skip_tlv :
1999-05-02 18:21:40 +02:00
return 0 ;
}
2000-08-11 21:29:55 +02:00
static int controls ( int level )
1999-05-02 18:21:40 +02:00
{
2001-02-05 16:44:45 +01:00
int err ;
2001-02-09 16:38:59 +01:00
snd_hctl_t * handle ;
2001-02-09 12:20:32 +01:00
snd_hctl_elem_t * elem ;
snd_ctl_elem_id_t * id ;
2001-07-23 15:26:34 +02:00
snd_ctl_elem_info_t * info ;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_id_alloca ( & id ) ;
2001-07-23 15:26:34 +02:00
snd_ctl_elem_info_alloca ( & info ) ;
1999-05-02 18:21:40 +02:00
2001-03-26 14:45:50 +02:00
if ( ( err = snd_hctl_open ( & handle , card , 0 ) ) < 0 ) {
2000-09-18 11:47:01 +02:00
error ( " Control %s open error: %s " , card , snd_strerror ( err ) ) ;
2000-08-11 21:29:55 +02:00
return err ;
}
2001-02-11 16:45:44 +01:00
if ( ( err = snd_hctl_load ( handle ) ) < 0 ) {
error ( " Control %s local error: %s \n " , card , snd_strerror ( err ) ) ;
return err ;
}
2001-02-09 12:20:32 +01:00
for ( elem = snd_hctl_first_elem ( handle ) ; elem ; elem = snd_hctl_elem_next ( elem ) ) {
2001-07-23 15:26:34 +02:00
if ( ( err = snd_hctl_elem_info ( elem , info ) ) < 0 ) {
error ( " Control %s snd_hctl_elem_info error: %s \n " , card , snd_strerror ( err ) ) ;
return err ;
}
if ( ! ( level & LEVEL_INACTIVE ) & & snd_ctl_elem_info_is_inactive ( info ) )
continue ;
2001-02-09 12:20:32 +01:00
snd_hctl_elem_get_id ( elem , id ) ;
2001-02-07 00:48:29 +01:00
show_control_id ( id ) ;
2000-08-11 21:29:55 +02:00
printf ( " \n " ) ;
2001-07-23 15:26:34 +02:00
if ( level & LEVEL_BASIC )
2001-02-11 16:45:44 +01:00
show_control ( " " , elem , 1 ) ;
1999-05-02 18:21:40 +02:00
}
2001-02-09 16:38:59 +01:00
snd_hctl_close ( handle ) ;
1999-05-02 18:21:40 +02:00
return 0 ;
}
2012-03-12 10:56:22 +01:00
static void show_selem_volume ( snd_mixer_elem_t * elem ,
snd_mixer_selem_channel_id_t chn , int dir ,
long min , long max )
{
long raw , val ;
vol_ops [ dir ] . v [ VOL_RAW ] . get ( elem , chn , & raw ) ;
if ( std_vol_type = = VOL_RAW )
val = convert_prange ( raw , min , max ) ;
else {
vol_ops [ dir ] . v [ std_vol_type ] . get ( elem , chn , & val ) ;
val = convert_prange ( val , 0 , MAP_VOL_RES ) ;
}
printf ( " %li [%li%%] " , raw , val ) ;
if ( ! vol_ops [ dir ] . v [ VOL_DB ] . get ( elem , chn , & val ) ) {
printf ( " [ " ) ;
print_dB ( val ) ;
printf ( " ] " ) ;
}
}
2001-02-09 12:20:32 +01:00
static int show_selem ( snd_mixer_t * handle , snd_mixer_selem_id_t * id , const char * space , int level )
1999-05-02 18:21:40 +02:00
{
2001-02-11 16:45:44 +01:00
snd_mixer_selem_channel_id_t chn ;
2001-02-16 00:05:42 +01:00
long pmin = 0 , pmax = 0 ;
long cmin = 0 , cmax = 0 ;
int psw , csw ;
2001-07-23 15:26:34 +02:00
int pmono , cmono , mono_ok = 0 ;
2001-02-09 12:20:32 +01:00
snd_mixer_elem_t * elem ;
1999-05-02 18:21:40 +02:00
2001-02-09 12:20:32 +01:00
elem = snd_mixer_find_selem ( handle , id ) ;
if ( ! elem ) {
error ( " Mixer %s simple element not found " , card ) ;
return - ENOENT ;
}
2001-07-23 15:26:34 +02:00
if ( level & LEVEL_BASIC ) {
1999-07-21 13:52:55 +02:00
printf ( " %sCapabilities: " , space ) ;
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_has_common_volume ( elem ) ) {
1999-07-21 00:00:59 +02:00
printf ( " volume " ) ;
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_has_playback_volume_joined ( elem ) )
printf ( " volume-joined " ) ;
2001-07-23 15:26:34 +02:00
} else {
if ( snd_mixer_selem_has_playback_volume ( elem ) ) {
printf ( " pvolume " ) ;
if ( snd_mixer_selem_has_playback_volume_joined ( elem ) )
printf ( " pvolume-joined " ) ;
}
if ( snd_mixer_selem_has_capture_volume ( elem ) ) {
printf ( " cvolume " ) ;
if ( snd_mixer_selem_has_capture_volume_joined ( elem ) )
printf ( " cvolume-joined " ) ;
}
2001-02-16 00:05:42 +01:00
}
if ( snd_mixer_selem_has_common_switch ( elem ) ) {
printf ( " switch " ) ;
if ( snd_mixer_selem_has_playback_switch_joined ( elem ) )
printf ( " switch-joined " ) ;
2001-07-23 15:26:34 +02:00
} else {
if ( snd_mixer_selem_has_playback_switch ( elem ) ) {
printf ( " pswitch " ) ;
if ( snd_mixer_selem_has_playback_switch_joined ( elem ) )
printf ( " pswitch-joined " ) ;
}
if ( snd_mixer_selem_has_capture_switch ( elem ) ) {
printf ( " cswitch " ) ;
if ( snd_mixer_selem_has_capture_switch_joined ( elem ) )
printf ( " cswitch-joined " ) ;
if ( snd_mixer_selem_has_capture_switch_exclusive ( elem ) )
printf ( " cswitch-exclusive " ) ;
}
1999-07-21 00:00:59 +02:00
}
2006-10-13 23:25:33 +02:00
if ( snd_mixer_selem_is_enum_playback ( elem ) ) {
printf ( " penum " ) ;
} else if ( snd_mixer_selem_is_enum_capture ( elem ) ) {
printf ( " cenum " ) ;
} else if ( snd_mixer_selem_is_enumerated ( elem ) ) {
2005-06-15 16:55:37 +02:00
printf ( " enum " ) ;
}
1999-07-21 00:00:59 +02:00
printf ( " \n " ) ;
2005-06-15 16:55:37 +02:00
if ( snd_mixer_selem_is_enumerated ( elem ) ) {
2005-09-22 14:47:02 +02:00
int i , items ;
unsigned int idx ;
2015-04-08 18:30:56 +02:00
/*
* See snd_ctl_elem_init_enum_names ( ) in
* sound / core / control . c .
*/
char itemname [ 64 ] ;
2005-06-15 16:55:37 +02:00
items = snd_mixer_selem_get_enum_items ( elem ) ;
printf ( " Items: " ) ;
for ( i = 0 ; i < items ; i + + ) {
snd_mixer_selem_get_enum_item_name ( elem , i , sizeof ( itemname ) - 1 , itemname ) ;
printf ( " '%s' " , itemname ) ;
}
printf ( " \n " ) ;
for ( i = 0 ; ! snd_mixer_selem_get_enum_item ( elem , i , & idx ) ; i + + ) {
snd_mixer_selem_get_enum_item_name ( elem , idx , sizeof ( itemname ) - 1 , itemname ) ;
printf ( " Item%d: '%s' \n " , i , itemname ) ;
}
return 0 ; /* no more thing to do */
}
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_has_capture_switch_exclusive ( elem ) )
2001-02-11 16:45:44 +01:00
printf ( " %sCapture exclusive group: %i \n " , space ,
2001-02-16 00:05:42 +01:00
snd_mixer_selem_get_capture_group ( elem ) ) ;
if ( snd_mixer_selem_has_playback_volume ( elem ) | |
snd_mixer_selem_has_playback_switch ( elem ) ) {
2003-02-24 14:51:15 +01:00
printf ( " %sPlayback channels: " , space ) ;
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_is_playback_mono ( elem ) ) {
2003-02-24 14:51:15 +01:00
printf ( " Mono " ) ;
2001-02-16 00:05:42 +01:00
} else {
2001-07-23 15:26:34 +02:00
int first = 1 ;
2001-07-11 17:00:59 +02:00
for ( chn = 0 ; chn < = SND_MIXER_SCHN_LAST ; chn + + ) {
2001-02-16 00:05:42 +01:00
if ( ! snd_mixer_selem_has_playback_channel ( elem , chn ) )
continue ;
2001-07-23 15:26:34 +02:00
if ( ! first )
2003-02-24 14:51:15 +01:00
printf ( " - " ) ;
printf ( " %s " , snd_mixer_selem_channel_name ( chn ) ) ;
2001-07-23 15:26:34 +02:00
first = 0 ;
2001-02-16 00:05:42 +01:00
}
1999-07-22 20:10:19 +02:00
}
2001-02-16 00:05:42 +01:00
printf ( " \n " ) ;
1999-07-21 00:00:59 +02:00
}
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_has_capture_volume ( elem ) | |
snd_mixer_selem_has_capture_switch ( elem ) ) {
2003-02-24 14:51:15 +01:00
printf ( " %sCapture channels: " , space ) ;
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_is_capture_mono ( elem ) ) {
2003-02-24 14:51:15 +01:00
printf ( " Mono " ) ;
2001-02-16 00:05:42 +01:00
} else {
2001-07-23 15:26:34 +02:00
int first = 1 ;
2001-07-11 17:00:59 +02:00
for ( chn = 0 ; chn < = SND_MIXER_SCHN_LAST ; chn + + ) {
2001-02-16 00:05:42 +01:00
if ( ! snd_mixer_selem_has_capture_channel ( elem , chn ) )
continue ;
2001-07-23 15:26:34 +02:00
if ( ! first )
2003-02-24 14:51:15 +01:00
printf ( " - " ) ;
printf ( " %s " , snd_mixer_selem_channel_name ( chn ) ) ;
2001-07-23 15:26:34 +02:00
first = 0 ;
2001-02-16 00:05:42 +01:00
}
}
printf ( " \n " ) ;
}
if ( snd_mixer_selem_has_playback_volume ( elem ) | |
snd_mixer_selem_has_capture_volume ( elem ) ) {
2003-02-24 14:51:15 +01:00
printf ( " %sLimits: " , space ) ;
2001-07-23 15:26:34 +02:00
if ( snd_mixer_selem_has_common_volume ( elem ) ) {
2001-03-30 11:43:30 +02:00
snd_mixer_selem_get_playback_volume_range ( elem , & pmin , & pmax ) ;
snd_mixer_selem_get_capture_volume_range ( elem , & cmin , & cmax ) ;
2003-02-24 14:51:15 +01:00
printf ( " %li - %li " , pmin , pmax ) ;
2001-07-23 15:26:34 +02:00
} else {
if ( snd_mixer_selem_has_playback_volume ( elem ) ) {
snd_mixer_selem_get_playback_volume_range ( elem , & pmin , & pmax ) ;
2003-02-24 14:51:15 +01:00
printf ( " Playback %li - %li " , pmin , pmax ) ;
2001-07-23 15:26:34 +02:00
}
if ( snd_mixer_selem_has_capture_volume ( elem ) ) {
snd_mixer_selem_get_capture_volume_range ( elem , & cmin , & cmax ) ;
2003-02-24 14:51:15 +01:00
printf ( " Capture %li - %li " , cmin , cmax ) ;
2001-07-23 15:26:34 +02:00
}
2001-02-16 00:05:42 +01:00
}
printf ( " \n " ) ;
}
2001-07-23 15:26:34 +02:00
pmono = snd_mixer_selem_has_playback_channel ( elem , SND_MIXER_SCHN_MONO ) & &
( snd_mixer_selem_is_playback_mono ( elem ) | |
2001-02-16 00:05:42 +01:00
( ! snd_mixer_selem_has_playback_volume ( elem ) & &
2001-07-23 15:26:34 +02:00
! snd_mixer_selem_has_playback_switch ( elem ) ) ) ;
cmono = snd_mixer_selem_has_capture_channel ( elem , SND_MIXER_SCHN_MONO ) & &
( snd_mixer_selem_is_capture_mono ( elem ) | |
2001-02-16 00:05:42 +01:00
( ! snd_mixer_selem_has_capture_volume ( elem ) & &
2001-07-23 15:26:34 +02:00
! snd_mixer_selem_has_capture_switch ( elem ) ) ) ;
#if 0
printf ( " pmono = %i, cmono = %i (%i, %i, %i, %i) \n " , pmono , cmono ,
snd_mixer_selem_has_capture_channel ( elem , SND_MIXER_SCHN_MONO ) ,
snd_mixer_selem_is_capture_mono ( elem ) ,
snd_mixer_selem_has_capture_volume ( elem ) ,
snd_mixer_selem_has_capture_switch ( elem ) ) ;
# endif
if ( pmono | | cmono ) {
if ( ! mono_ok ) {
2003-02-24 14:51:15 +01:00
printf ( " %s%s: " , space , " Mono " ) ;
2001-07-23 15:26:34 +02:00
mono_ok = 1 ;
}
if ( snd_mixer_selem_has_common_volume ( elem ) ) {
2012-03-12 10:56:22 +01:00
show_selem_volume ( elem , SND_MIXER_SCHN_MONO , 0 , pmin , pmax ) ;
2001-07-23 15:26:34 +02:00
}
if ( snd_mixer_selem_has_common_switch ( elem ) ) {
snd_mixer_selem_get_playback_switch ( elem , SND_MIXER_SCHN_MONO , & psw ) ;
2003-02-24 14:51:15 +01:00
printf ( " [%s] " , psw ? " on " : " off " ) ;
2001-07-23 15:26:34 +02:00
}
}
if ( pmono & & snd_mixer_selem_has_playback_channel ( elem , SND_MIXER_SCHN_MONO ) ) {
int title = 0 ;
if ( ! mono_ok ) {
2003-02-24 14:51:15 +01:00
printf ( " %s%s: " , space , " Mono " ) ;
2001-07-23 15:26:34 +02:00
mono_ok = 1 ;
}
if ( ! snd_mixer_selem_has_common_volume ( elem ) ) {
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_has_playback_volume ( elem ) ) {
2003-02-24 14:51:15 +01:00
printf ( " Playback " ) ;
2001-07-23 15:26:34 +02:00
title = 1 ;
2012-03-12 10:56:22 +01:00
show_selem_volume ( elem , SND_MIXER_SCHN_MONO , 0 , pmin , pmax ) ;
2001-02-16 00:05:42 +01:00
}
2001-07-23 15:26:34 +02:00
}
if ( ! snd_mixer_selem_has_common_switch ( elem ) ) {
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_has_playback_switch ( elem ) ) {
2001-07-23 15:26:34 +02:00
if ( ! title )
2003-02-24 14:51:15 +01:00
printf ( " Playback " ) ;
2001-07-23 15:26:34 +02:00
snd_mixer_selem_get_playback_switch ( elem , SND_MIXER_SCHN_MONO , & psw ) ;
2003-02-24 14:51:15 +01:00
printf ( " [%s] " , psw ? " on " : " off " ) ;
2001-02-16 00:05:42 +01:00
}
}
2001-07-23 15:26:34 +02:00
}
if ( cmono & & snd_mixer_selem_has_capture_channel ( elem , SND_MIXER_SCHN_MONO ) ) {
int title = 0 ;
if ( ! mono_ok ) {
2003-02-24 14:51:15 +01:00
printf ( " %s%s: " , space , " Mono " ) ;
2001-07-23 15:26:34 +02:00
mono_ok = 1 ;
}
if ( ! snd_mixer_selem_has_common_volume ( elem ) ) {
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_has_capture_volume ( elem ) ) {
2003-02-24 14:51:15 +01:00
printf ( " Capture " ) ;
2001-07-23 15:26:34 +02:00
title = 1 ;
2012-03-12 10:56:22 +01:00
show_selem_volume ( elem , SND_MIXER_SCHN_MONO , 1 , cmin , cmax ) ;
2001-02-16 00:05:42 +01:00
}
2001-07-23 15:26:34 +02:00
}
if ( ! snd_mixer_selem_has_common_switch ( elem ) ) {
2001-02-16 00:05:42 +01:00
if ( snd_mixer_selem_has_capture_switch ( elem ) ) {
2001-07-23 15:26:34 +02:00
if ( ! title )
2003-02-24 14:51:15 +01:00
printf ( " Capture " ) ;
2001-07-23 15:26:34 +02:00
snd_mixer_selem_get_capture_switch ( elem , SND_MIXER_SCHN_MONO , & csw ) ;
2003-02-24 14:51:15 +01:00
printf ( " [%s] " , csw ? " on " : " off " ) ;
2001-02-16 00:05:42 +01:00
}
1999-07-22 20:10:19 +02:00
}
2001-07-23 15:26:34 +02:00
}
if ( pmono | | cmono )
2001-02-16 00:05:42 +01:00
printf ( " \n " ) ;
2001-07-23 15:26:34 +02:00
if ( ! pmono | | ! cmono ) {
for ( chn = 0 ; chn < = SND_MIXER_SCHN_LAST ; chn + + ) {
if ( ( pmono | | ! snd_mixer_selem_has_playback_channel ( elem , chn ) ) & &
( cmono | | ! snd_mixer_selem_has_capture_channel ( elem , chn ) ) )
continue ;
2003-02-24 14:51:15 +01:00
printf ( " %s%s: " , space , snd_mixer_selem_channel_name ( chn ) ) ;
2001-07-25 13:49:32 +02:00
if ( ! pmono & & ! cmono & & snd_mixer_selem_has_common_volume ( elem ) ) {
2012-03-12 10:56:22 +01:00
show_selem_volume ( elem , chn , 0 , pmin , pmax ) ;
2001-07-23 15:26:34 +02:00
}
2001-07-25 13:49:32 +02:00
if ( ! pmono & & ! cmono & & snd_mixer_selem_has_common_switch ( elem ) ) {
2001-07-23 15:26:34 +02:00
snd_mixer_selem_get_playback_switch ( elem , chn , & psw ) ;
2003-02-24 14:51:15 +01:00
printf ( " [%s] " , psw ? " on " : " off " ) ;
2001-07-23 15:26:34 +02:00
}
if ( ! pmono & & snd_mixer_selem_has_playback_channel ( elem , chn ) ) {
int title = 0 ;
if ( ! snd_mixer_selem_has_common_volume ( elem ) ) {
if ( snd_mixer_selem_has_playback_volume ( elem ) ) {
2003-02-24 14:51:15 +01:00
printf ( " Playback " ) ;
2001-07-23 15:26:34 +02:00
title = 1 ;
2012-03-12 10:56:22 +01:00
show_selem_volume ( elem , chn , 0 , pmin , pmax ) ;
2001-07-23 15:26:34 +02:00
}
}
if ( ! snd_mixer_selem_has_common_switch ( elem ) ) {
if ( snd_mixer_selem_has_playback_switch ( elem ) ) {
if ( ! title )
2003-02-24 14:51:15 +01:00
printf ( " Playback " ) ;
2001-07-23 15:26:34 +02:00
snd_mixer_selem_get_playback_switch ( elem , chn , & psw ) ;
2003-02-24 14:51:15 +01:00
printf ( " [%s] " , psw ? " on " : " off " ) ;
2001-07-23 15:26:34 +02:00
}
}
}
if ( ! cmono & & snd_mixer_selem_has_capture_channel ( elem , chn ) ) {
int title = 0 ;
if ( ! snd_mixer_selem_has_common_volume ( elem ) ) {
if ( snd_mixer_selem_has_capture_volume ( elem ) ) {
2003-02-24 14:51:15 +01:00
printf ( " Capture " ) ;
2001-07-23 15:26:34 +02:00
title = 1 ;
2012-03-12 10:56:22 +01:00
show_selem_volume ( elem , chn , 1 , cmin , cmax ) ;
2001-07-23 15:26:34 +02:00
}
}
if ( ! snd_mixer_selem_has_common_switch ( elem ) ) {
if ( snd_mixer_selem_has_capture_switch ( elem ) ) {
if ( ! title )
2003-02-24 14:51:15 +01:00
printf ( " Capture " ) ;
2001-07-23 15:26:34 +02:00
snd_mixer_selem_get_capture_switch ( elem , chn , & csw ) ;
2003-02-24 14:51:15 +01:00
printf ( " [%s] " , csw ? " on " : " off " ) ;
2001-07-23 15:26:34 +02:00
}
}
}
printf ( " \n " ) ;
}
1999-07-21 00:00:59 +02:00
}
}
1999-05-02 18:21:40 +02:00
return 0 ;
}
2001-02-09 12:20:32 +01:00
static int selems ( int level )
1999-05-02 18:21:40 +02:00
{
2001-02-05 16:44:45 +01:00
int err ;
1999-06-02 02:40:31 +02:00
snd_mixer_t * handle ;
2001-02-09 12:20:32 +01:00
snd_mixer_selem_id_t * sid ;
snd_mixer_elem_t * elem ;
snd_mixer_selem_id_alloca ( & sid ) ;
1999-05-02 18:21:40 +02:00
2001-03-26 14:45:50 +02:00
if ( ( err = snd_mixer_open ( & handle , 0 ) ) < 0 ) {
2000-09-18 11:47:01 +02:00
error ( " Mixer %s open error: %s " , card , snd_strerror ( err ) ) ;
2000-08-11 21:29:55 +02:00
return err ;
1999-05-02 18:21:40 +02:00
}
2005-06-06 16:02:10 +02:00
if ( smixer_level = = 0 & & ( err = snd_mixer_attach ( handle , card ) ) < 0 ) {
2001-02-11 16:45:44 +01:00
error ( " Mixer attach %s error: %s " , card , snd_strerror ( err ) ) ;
snd_mixer_close ( handle ) ;
return err ;
}
2005-03-27 14:50:34 +02:00
if ( ( err = snd_mixer_selem_register ( handle , smixer_level > 0 ? & smixer_options : NULL , NULL ) ) < 0 ) {
2001-02-11 16:45:44 +01:00
error ( " Mixer register error: %s " , snd_strerror ( err ) ) ;
snd_mixer_close ( handle ) ;
return err ;
}
err = snd_mixer_load ( handle ) ;
2001-02-09 12:20:32 +01:00
if ( err < 0 ) {
2004-02-24 10:19:31 +01:00
error ( " Mixer %s load error: %s " , card , snd_strerror ( err ) ) ;
2001-02-11 16:45:44 +01:00
snd_mixer_close ( handle ) ;
2000-08-11 21:29:55 +02:00
return err ;
1999-05-02 18:21:40 +02:00
}
2001-02-09 12:20:32 +01:00
for ( elem = snd_mixer_first_elem ( handle ) ; elem ; elem = snd_mixer_elem_next ( elem ) ) {
snd_mixer_selem_get_id ( elem , sid ) ;
2001-07-23 15:26:34 +02:00
if ( ! ( level & LEVEL_INACTIVE ) & & ! snd_mixer_selem_is_active ( elem ) )
continue ;
2001-02-09 12:20:32 +01:00
printf ( " Simple mixer control '%s',%i \n " , snd_mixer_selem_id_get_name ( sid ) , snd_mixer_selem_id_get_index ( sid ) ) ;
show_selem ( handle , sid , " " , level ) ;
1999-05-02 18:21:40 +02:00
}
snd_mixer_close ( handle ) ;
return 0 ;
}
2001-02-09 12:20:32 +01:00
static int parse_simple_id ( const char * str , snd_mixer_selem_id_t * sid )
1999-07-21 13:52:55 +02:00
{
int c , size ;
2001-02-09 12:20:32 +01:00
char buf [ 128 ] ;
char * ptr = buf ;
1999-07-21 13:52:55 +02:00
while ( * str = = ' ' | | * str = = ' \t ' )
str + + ;
if ( ! ( * str ) )
2000-08-11 21:29:55 +02:00
return - EINVAL ;
2001-06-25 11:16:27 +02:00
size = 1 ; /* for '\0' */
1999-07-21 13:52:55 +02:00
if ( * str ! = ' " ' & & * str ! = ' \' ' ) {
while ( * str & & * str ! = ' , ' ) {
2003-02-04 17:03:36 +01:00
if ( size < ( int ) sizeof ( buf ) ) {
1999-07-21 13:52:55 +02:00
* ptr + + = * str ;
size + + ;
}
str + + ;
}
} else {
c = * str + + ;
while ( * str & & * str ! = c ) {
2003-02-04 17:03:36 +01:00
if ( size < ( int ) sizeof ( buf ) ) {
1999-07-21 13:52:55 +02:00
* ptr + + = * str ;
size + + ;
}
str + + ;
}
if ( * str = = c )
str + + ;
}
2001-06-25 11:16:27 +02:00
if ( * str = = ' \0 ' ) {
snd_mixer_selem_id_set_index ( sid , 0 ) ;
* ptr = 0 ;
goto _set ;
}
1999-07-21 13:52:55 +02:00
if ( * str ! = ' , ' )
2000-08-11 21:29:55 +02:00
return - EINVAL ;
2001-02-12 14:33:25 +01:00
* ptr = 0 ; /* terminate the string */
1999-07-21 13:52:55 +02:00
str + + ;
if ( ! isdigit ( * str ) )
2000-08-11 21:29:55 +02:00
return - EINVAL ;
2001-02-09 12:20:32 +01:00
snd_mixer_selem_id_set_index ( sid , atoi ( str ) ) ;
2001-06-25 11:16:27 +02:00
_set :
2001-02-09 12:20:32 +01:00
snd_mixer_selem_id_set_name ( sid , buf ) ;
1999-05-02 18:21:40 +02:00
return 0 ;
}
2005-12-01 12:32:28 +01:00
static int cset ( int argc , char * argv [ ] , int roflag , int keep_handle )
1999-05-02 18:21:40 +02:00
{
int err ;
2005-12-01 12:32:28 +01:00
static snd_ctl_t * handle = NULL ;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_t * info ;
snd_ctl_elem_id_t * id ;
2001-02-11 16:45:44 +01:00
snd_ctl_elem_value_t * control ;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_alloca ( & info ) ;
snd_ctl_elem_id_alloca ( & id ) ;
2001-02-11 16:45:44 +01:00
snd_ctl_elem_value_alloca ( & control ) ;
1999-05-02 18:21:40 +02:00
if ( argc < 1 ) {
2000-08-11 21:29:55 +02:00
fprintf ( stderr , " Specify a full control identifier: [[iface=<iface>,][name='name',][index=<index>,][device=<device>,][subdevice=<subdevice>]]|[numid=<numid>] \n " ) ;
return - EINVAL ;
1999-05-02 18:21:40 +02:00
}
2013-04-09 18:38:58 +02:00
if ( snd_ctl_ascii_elem_id_parse ( id , argv [ 0 ] ) ) {
2000-08-11 21:29:55 +02:00
fprintf ( stderr , " Wrong control identifier: %s \n " , argv [ 0 ] ) ;
return - EINVAL ;
1999-05-02 18:21:40 +02:00
}
2000-08-11 21:29:55 +02:00
if ( debugflag ) {
printf ( " VERIFY ID: " ) ;
2001-02-07 00:48:29 +01:00
show_control_id ( id ) ;
2000-08-11 21:29:55 +02:00
printf ( " \n " ) ;
1999-05-02 18:21:40 +02:00
}
2005-12-01 12:32:28 +01:00
if ( handle = = NULL & &
( err = snd_ctl_open ( & handle , card , 0 ) ) < 0 ) {
2000-09-18 11:47:01 +02:00
error ( " Control %s open error: %s \n " , card , snd_strerror ( err ) ) ;
2000-08-11 21:29:55 +02:00
return err ;
}
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_set_id ( info , id ) ;
if ( ( err = snd_ctl_elem_info ( handle , info ) ) < 0 ) {
2005-12-01 12:32:28 +01:00
if ( ignore_error )
return 0 ;
2005-06-15 16:55:37 +02:00
error ( " Cannot find the given element from control %s \n " , card ) ;
2006-10-04 09:47:41 +02:00
if ( ! keep_handle ) {
snd_ctl_close ( handle ) ;
handle = NULL ;
}
2000-08-11 21:29:55 +02:00
return err ;
}
2013-05-27 16:40:26 +02:00
snd_ctl_elem_info_get_id ( info , id ) ; /* FIXME: Remove it when hctl find works ok !!! */
2000-08-11 21:29:55 +02:00
if ( ! roflag ) {
2013-04-09 18:38:58 +02:00
snd_ctl_elem_value_set_id ( control , id ) ;
if ( ( err = snd_ctl_elem_read ( handle , control ) ) < 0 ) {
if ( ignore_error )
return 0 ;
error ( " Cannot read the given element from control %s \n " , card ) ;
if ( ! keep_handle ) {
snd_ctl_close ( handle ) ;
handle = NULL ;
}
return err ;
}
err = snd_ctl_ascii_value_parse ( handle , control , info , argv [ 1 ] ) ;
if ( err < 0 ) {
if ( ! ignore_error )
error ( " Control %s parse error: %s \n " , card , snd_strerror ( err ) ) ;
if ( ! keep_handle ) {
snd_ctl_close ( handle ) ;
handle = NULL ;
2000-08-11 21:29:55 +02:00
}
2013-04-09 18:38:58 +02:00
return ignore_error ? 0 : err ;
1999-05-02 18:21:40 +02:00
}
2001-02-09 12:20:32 +01:00
if ( ( err = snd_ctl_elem_write ( handle , control ) ) < 0 ) {
2006-10-04 09:47:41 +02:00
if ( ! ignore_error )
error ( " Control %s element write error: %s \n " , card , snd_strerror ( err ) ) ;
if ( ! keep_handle ) {
snd_ctl_close ( handle ) ;
handle = NULL ;
}
return ignore_error ? 0 : err ;
1999-05-02 18:21:40 +02:00
}
}
2005-12-01 12:32:28 +01:00
if ( ! keep_handle ) {
snd_ctl_close ( handle ) ;
handle = NULL ;
}
2001-02-09 16:38:59 +01:00
if ( ! quiet ) {
snd_hctl_t * hctl ;
2001-02-15 10:14:28 +01:00
snd_hctl_elem_t * elem ;
2001-03-26 14:45:50 +02:00
if ( ( err = snd_hctl_open ( & hctl , card , 0 ) ) < 0 ) {
2001-02-09 16:38:59 +01:00
error ( " Control %s open error: %s \n " , card , snd_strerror ( err ) ) ;
return err ;
}
2001-02-15 10:14:28 +01:00
if ( ( err = snd_hctl_load ( hctl ) ) < 0 ) {
2001-02-11 16:45:44 +01:00
error ( " Control %s load error: %s \n " , card , snd_strerror ( err ) ) ;
return err ;
}
elem = snd_hctl_find_elem ( hctl , id ) ;
2001-02-15 10:14:28 +01:00
if ( elem )
2001-07-23 15:26:34 +02:00
show_control ( " " , elem , LEVEL_BASIC | LEVEL_ID ) ;
2001-02-15 10:14:28 +01:00
else
printf ( " Could not find the specified element \n " ) ;
2001-02-09 16:38:59 +01:00
snd_hctl_close ( hctl ) ;
}
1999-05-02 18:21:40 +02:00
return 0 ;
}
2000-03-18 19:00:37 +01:00
typedef struct channel_mask {
char * name ;
unsigned int mask ;
} channel_mask_t ;
2008-11-21 13:10:02 +01:00
static const channel_mask_t chanmask [ ] = {
2001-02-11 16:45:44 +01:00
{ " frontleft " , 1 < < SND_MIXER_SCHN_FRONT_LEFT } ,
{ " frontright " , 1 < < SND_MIXER_SCHN_FRONT_RIGHT } ,
{ " frontcenter " , 1 < < SND_MIXER_SCHN_FRONT_CENTER } ,
{ " front " , ( ( 1 < < SND_MIXER_SCHN_FRONT_LEFT ) |
( 1 < < SND_MIXER_SCHN_FRONT_RIGHT ) ) } ,
{ " center " , 1 < < SND_MIXER_SCHN_FRONT_CENTER } ,
{ " rearleft " , 1 < < SND_MIXER_SCHN_REAR_LEFT } ,
{ " rearright " , 1 < < SND_MIXER_SCHN_REAR_RIGHT } ,
{ " rear " , ( ( 1 < < SND_MIXER_SCHN_REAR_LEFT ) |
( 1 < < SND_MIXER_SCHN_REAR_RIGHT ) ) } ,
{ " woofer " , 1 < < SND_MIXER_SCHN_WOOFER } ,
2001-02-05 16:44:45 +01:00
{ NULL , 0 }
2000-03-18 19:00:37 +01:00
} ;
2001-08-27 11:15:44 +02:00
static unsigned int channels_mask ( char * * arg , unsigned int def )
2000-03-18 19:00:37 +01:00
{
2008-11-21 13:10:02 +01:00
const channel_mask_t * c ;
2000-03-18 19:00:37 +01:00
for ( c = chanmask ; c - > name ; c + + ) {
2001-08-27 11:15:44 +02:00
if ( strncasecmp ( * arg , c - > name , strlen ( c - > name ) ) = = 0 ) {
while ( * * arg ! = ' \0 ' & & * * arg ! = ' , ' & & * * arg ! = ' ' & & * * arg ! = ' \t ' )
( * arg ) + + ;
if ( * * arg = = ' , ' | | * * arg = = ' ' | | * * arg = = ' \t ' )
( * arg ) + + ;
2001-02-09 12:20:32 +01:00
return c - > mask ;
2001-08-27 11:15:44 +02:00
}
2000-03-18 19:00:37 +01:00
}
2001-08-27 11:15:44 +02:00
return def ;
}
static unsigned int dir_mask ( char * * arg , unsigned int def )
{
int findend = 0 ;
if ( strncasecmp ( * arg , " playback " , 8 ) = = 0 )
def = findend = 1 ;
else if ( strncasecmp ( * arg , " capture " , 8 ) = = 0 )
def = findend = 2 ;
if ( findend ) {
while ( * * arg ! = ' \0 ' & & * * arg ! = ' , ' & & * * arg ! = ' ' & & * * arg ! = ' \t ' )
( * arg ) + + ;
if ( * * arg = = ' , ' | | * * arg = = ' ' | | * * arg = = ' \t ' )
( * arg ) + + ;
}
return def ;
2000-03-18 19:00:37 +01:00
}
2005-06-15 16:55:37 +02:00
static int get_enum_item_index ( snd_mixer_elem_t * elem , char * * ptrp )
{
char * ptr = * ptrp ;
int items , i , len ;
2015-04-08 18:30:56 +02:00
/* See snd_ctl_elem_init_enum_names() in sound/core/control.c. */
char name [ 64 ] ;
2005-06-15 16:55:37 +02:00
items = snd_mixer_selem_get_enum_items ( elem ) ;
if ( items < = 0 )
return - 1 ;
for ( i = 0 ; i < items ; i + + ) {
if ( snd_mixer_selem_get_enum_item_name ( elem , i , sizeof ( name ) - 1 , name ) < 0 )
continue ;
2015-04-08 18:30:56 +02:00
2005-06-15 16:55:37 +02:00
len = strlen ( name ) ;
if ( ! strncmp ( name , ptr , len ) ) {
if ( ! ptr [ len ] | | ptr [ len ] = = ' , ' | | ptr [ len ] = = ' \n ' ) {
ptr + = len ;
* ptrp = ptr ;
return i ;
}
}
}
return - 1 ;
}
2007-07-09 16:37:35 +02:00
static int sset_enum ( snd_mixer_elem_t * elem , unsigned int argc , char * * argv )
1999-07-21 13:52:55 +02:00
{
2015-03-03 18:39:52 +01:00
unsigned int idx , item = 0 ;
2007-07-24 15:19:34 +02:00
int check_flag = ignore_error ? 0 : - 1 ;
1999-07-21 13:52:55 +02:00
2007-07-09 16:37:35 +02:00
for ( idx = 1 ; idx < argc ; idx + + ) {
char * ptr = argv [ idx ] ;
while ( * ptr ) {
int ival = get_enum_item_index ( elem , & ptr ) ;
if ( ival < 0 )
return check_flag ;
2015-03-03 18:39:52 +01:00
if ( snd_mixer_selem_set_enum_item ( elem , item + + , ival ) > = 0 )
2007-07-09 16:37:35 +02:00
check_flag = 1 ;
/* skip separators */
while ( * ptr = = ' , ' | | isspace ( * ptr ) )
ptr + + ;
2005-12-01 12:32:28 +01:00
}
2001-02-11 16:45:44 +01:00
}
2007-07-09 16:37:35 +02:00
return check_flag ;
}
static int sset_channels ( snd_mixer_elem_t * elem , unsigned int argc , char * * argv )
{
unsigned int channels = ~ 0U ;
unsigned int dir = 3 , okflag = 3 ;
unsigned int idx ;
snd_mixer_selem_channel_id_t chn ;
2007-07-24 15:19:34 +02:00
int check_flag = ignore_error ? 0 : - 1 ;
2007-07-09 16:37:35 +02:00
1999-07-21 13:52:55 +02:00
for ( idx = 1 ; idx < argc ; idx + + ) {
2001-08-27 11:15:44 +02:00
char * ptr = argv [ idx ] , * optr ;
2001-09-20 10:31:12 +02:00
int multi , firstchn = 1 ;
2001-08-27 11:15:44 +02:00
channels = channels_mask ( & ptr , channels ) ;
if ( * ptr = = ' \0 ' )
2000-03-18 19:00:37 +01:00
continue ;
2001-08-27 11:15:44 +02:00
dir = dir_mask ( & ptr , dir ) ;
if ( * ptr = = ' \0 ' )
2000-03-18 19:00:37 +01:00
continue ;
2001-08-27 11:15:44 +02:00
multi = ( strchr ( ptr , ' , ' ) ! = NULL ) ;
optr = ptr ;
for ( chn = 0 ; chn < = SND_MIXER_SCHN_LAST ; chn + + ) {
char * sptr = NULL ;
int ival ;
2000-03-18 19:00:37 +01:00
2001-08-27 11:15:44 +02:00
if ( ! ( channels & ( 1 < < chn ) ) )
continue ;
2005-06-15 16:55:37 +02:00
2001-08-27 11:15:44 +02:00
if ( ( dir & 1 ) & & snd_mixer_selem_has_playback_channel ( elem , chn ) ) {
sptr = ptr ;
if ( ! strncmp ( ptr , " mute " , 4 ) & & snd_mixer_selem_has_playback_switch ( elem ) ) {
snd_mixer_selem_get_playback_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_playback_switch ( elem , chn , get_bool_simple ( & ptr , " mute " , 1 , ival ) ) > = 0 )
check_flag = 1 ;
2001-08-27 11:15:44 +02:00
} else if ( ! strncmp ( ptr , " off " , 3 ) & & snd_mixer_selem_has_playback_switch ( elem ) ) {
snd_mixer_selem_get_playback_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_playback_switch ( elem , chn , get_bool_simple ( & ptr , " off " , 1 , ival ) ) > = 0 )
check_flag = 1 ;
2001-08-27 11:15:44 +02:00
} else if ( ! strncmp ( ptr , " unmute " , 6 ) & & snd_mixer_selem_has_playback_switch ( elem ) ) {
snd_mixer_selem_get_playback_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_playback_switch ( elem , chn , get_bool_simple ( & ptr , " unmute " , 0 , ival ) ) > = 0 )
check_flag = 1 ;
2001-08-27 11:15:44 +02:00
} else if ( ! strncmp ( ptr , " on " , 2 ) & & snd_mixer_selem_has_playback_switch ( elem ) ) {
snd_mixer_selem_get_playback_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_playback_switch ( elem , chn , get_bool_simple ( & ptr , " on " , 0 , ival ) ) > = 0 )
check_flag = 1 ;
2001-09-20 10:31:12 +02:00
} else if ( ! strncmp ( ptr , " toggle " , 6 ) & & snd_mixer_selem_has_playback_switch ( elem ) ) {
if ( firstchn | | ! snd_mixer_selem_has_playback_switch_joined ( elem ) ) {
snd_mixer_selem_get_playback_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_playback_switch ( elem , chn , ( ival ? 1 : 0 ) ^ 1 ) > = 0 )
check_flag = 1 ;
2001-09-20 10:31:12 +02:00
}
simple_skip_word ( & ptr , " toggle " ) ;
} else if ( isdigit ( * ptr ) | | * ptr = = ' - ' | | * ptr = = ' + ' ) {
2006-10-04 09:47:41 +02:00
if ( set_volume_simple ( elem , chn , & ptr , 0 ) > = 0 )
check_flag = 1 ;
2001-08-27 11:15:44 +02:00
} else if ( simple_skip_word ( & ptr , " cap " ) | | simple_skip_word ( & ptr , " rec " ) | |
simple_skip_word ( & ptr , " nocap " ) | | simple_skip_word ( & ptr , " norec " ) ) {
/* nothing */
} else {
2005-09-22 14:47:02 +02:00
okflag & = ~ 1 ;
2001-08-27 11:15:44 +02:00
}
1999-07-22 20:10:19 +02:00
}
2001-08-27 11:15:44 +02:00
if ( ( dir & 2 ) & & snd_mixer_selem_has_capture_channel ( elem , chn ) ) {
if ( sptr ! = NULL )
ptr = sptr ;
sptr = ptr ;
if ( ! strncmp ( ptr , " cap " , 3 ) & & snd_mixer_selem_has_capture_switch ( elem ) ) {
snd_mixer_selem_get_capture_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_capture_switch ( elem , chn , get_bool_simple ( & ptr , " cap " , 0 , ival ) ) > = 0 )
check_flag = 1 ;
2001-08-27 11:15:44 +02:00
} else if ( ! strncmp ( ptr , " rec " , 3 ) & & snd_mixer_selem_has_capture_switch ( elem ) ) {
snd_mixer_selem_get_capture_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_capture_switch ( elem , chn , get_bool_simple ( & ptr , " rec " , 0 , ival ) ) > = 0 )
check_flag = 1 ;
2001-08-27 11:15:44 +02:00
} else if ( ! strncmp ( ptr , " nocap " , 5 ) & & snd_mixer_selem_has_capture_switch ( elem ) ) {
snd_mixer_selem_get_capture_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_capture_switch ( elem , chn , get_bool_simple ( & ptr , " nocap " , 1 , ival ) ) > = 0 )
check_flag = 1 ;
2001-08-27 11:15:44 +02:00
} else if ( ! strncmp ( ptr , " norec " , 5 ) & & snd_mixer_selem_has_capture_switch ( elem ) ) {
snd_mixer_selem_get_capture_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_capture_switch ( elem , chn , get_bool_simple ( & ptr , " norec " , 1 , ival ) ) > = 0 )
check_flag = 1 ;
2001-09-20 10:31:12 +02:00
} else if ( ! strncmp ( ptr , " toggle " , 6 ) & & snd_mixer_selem_has_capture_switch ( elem ) ) {
if ( firstchn | | ! snd_mixer_selem_has_capture_switch_joined ( elem ) ) {
snd_mixer_selem_get_capture_switch ( elem , chn , & ival ) ;
2006-10-04 09:47:41 +02:00
if ( snd_mixer_selem_set_capture_switch ( elem , chn , ( ival ? 1 : 0 ) ^ 1 ) > = 0 )
check_flag = 1 ;
2001-09-20 10:31:12 +02:00
}
simple_skip_word ( & ptr , " toggle " ) ;
} else if ( isdigit ( * ptr ) | | * ptr = = ' - ' | | * ptr = = ' + ' ) {
2006-10-04 09:47:41 +02:00
if ( set_volume_simple ( elem , chn , & ptr , 1 ) > = 0 )
check_flag = 1 ;
2001-08-27 11:15:44 +02:00
} else if ( simple_skip_word ( & ptr , " mute " ) | | simple_skip_word ( & ptr , " off " ) | |
simple_skip_word ( & ptr , " unmute " ) | | simple_skip_word ( & ptr , " on " ) ) {
/* nothing */
} else {
2005-09-22 14:39:26 +02:00
okflag & = ~ 2 ;
2001-08-27 11:15:44 +02:00
}
}
2005-09-22 14:39:26 +02:00
if ( okflag = = 0 ) {
2005-12-01 12:32:28 +01:00
if ( debugflag ) {
if ( dir & 1 )
2006-10-04 09:47:41 +02:00
error ( " Unknown playback setup '%s'.. " , ptr ) ;
2005-12-01 12:32:28 +01:00
if ( dir & 2 )
2006-10-04 09:47:41 +02:00
error ( " Unknown capture setup '%s'.. " , ptr ) ;
2005-12-01 12:32:28 +01:00
}
2007-07-09 16:37:35 +02:00
return 0 ; /* just skip it */
2005-09-22 14:39:26 +02:00
}
2001-08-27 11:15:44 +02:00
if ( ! multi )
ptr = optr ;
2001-09-20 10:31:12 +02:00
firstchn = 0 ;
1999-07-21 13:52:55 +02:00
}
2006-10-04 09:47:41 +02:00
}
2007-07-09 16:37:35 +02:00
return check_flag ;
}
static int sset ( unsigned int argc , char * argv [ ] , int roflag , int keep_handle )
{
int err = 0 ;
static snd_mixer_t * handle = NULL ;
snd_mixer_elem_t * elem ;
snd_mixer_selem_id_t * sid ;
snd_mixer_selem_id_alloca ( & sid ) ;
if ( argc < 1 ) {
fprintf ( stderr , " Specify a scontrol identifier: 'name',index \n " ) ;
return 1 ;
}
if ( parse_simple_id ( argv [ 0 ] , sid ) ) {
fprintf ( stderr , " Wrong scontrol identifier: %s \n " , argv [ 0 ] ) ;
return 1 ;
}
if ( ! roflag & & argc < 2 ) {
fprintf ( stderr , " Specify what you want to set... \n " ) ;
return 1 ;
}
if ( handle = = NULL ) {
if ( ( err = snd_mixer_open ( & handle , 0 ) ) < 0 ) {
error ( " Mixer %s open error: %s \n " , card , snd_strerror ( err ) ) ;
return err ;
}
if ( smixer_level = = 0 & & ( err = snd_mixer_attach ( handle , card ) ) < 0 ) {
error ( " Mixer attach %s error: %s " , card , snd_strerror ( err ) ) ;
2006-10-04 09:47:41 +02:00
snd_mixer_close ( handle ) ;
handle = NULL ;
2007-07-09 16:37:35 +02:00
return err ;
}
if ( ( err = snd_mixer_selem_register ( handle , smixer_level > 0 ? & smixer_options : NULL , NULL ) ) < 0 ) {
error ( " Mixer register error: %s " , snd_strerror ( err ) ) ;
snd_mixer_close ( handle ) ;
handle = NULL ;
return err ;
}
err = snd_mixer_load ( handle ) ;
if ( err < 0 ) {
error ( " Mixer %s load error: %s " , card , snd_strerror ( err ) ) ;
snd_mixer_close ( handle ) ;
handle = NULL ;
return err ;
}
}
elem = snd_mixer_find_selem ( handle , sid ) ;
if ( ! elem ) {
if ( ignore_error )
return 0 ;
error ( " Unable to find simple control '%s',%i \n " , snd_mixer_selem_id_get_name ( sid ) , snd_mixer_selem_id_get_index ( sid ) ) ;
snd_mixer_close ( handle ) ;
handle = NULL ;
return - ENOENT ;
}
if ( ! roflag ) {
/* enum control */
if ( snd_mixer_selem_is_enumerated ( elem ) )
err = sset_enum ( elem , argc , argv ) ;
else
err = sset_channels ( elem , argc , argv ) ;
if ( ! err )
goto done ;
if ( err < 0 ) {
error ( " Invalid command! " ) ;
goto done ;
2006-10-04 09:47:41 +02:00
}
}
1999-07-21 13:52:55 +02:00
if ( ! quiet ) {
2001-02-09 12:20:32 +01:00
printf ( " Simple mixer control '%s',%i \n " , snd_mixer_selem_id_get_name ( sid ) , snd_mixer_selem_id_get_index ( sid ) ) ;
show_selem ( handle , sid , " " , 1 ) ;
1999-07-21 13:52:55 +02:00
}
2007-07-09 16:37:35 +02:00
done :
2005-12-01 12:32:28 +01:00
if ( ! keep_handle ) {
snd_mixer_close ( handle ) ;
handle = NULL ;
}
2007-07-09 16:37:35 +02:00
return err < 0 ? 1 : 0 ;
1999-07-21 13:52:55 +02:00
}
2001-02-11 16:45:44 +01:00
static void events_info ( snd_hctl_elem_t * helem )
2000-08-12 17:34:17 +02:00
{
2001-02-09 12:20:32 +01:00
snd_ctl_elem_id_t * id ;
snd_ctl_elem_id_alloca ( & id ) ;
snd_hctl_elem_get_id ( helem , id ) ;
2001-02-11 16:45:44 +01:00
printf ( " event info: " ) ;
2001-02-07 00:48:29 +01:00
show_control_id ( id ) ;
2000-08-12 17:34:17 +02:00
printf ( " \n " ) ;
}
2001-02-09 12:20:32 +01:00
static void events_value ( snd_hctl_elem_t * helem )
2000-08-12 17:34:17 +02:00
{
2001-02-09 12:20:32 +01:00
snd_ctl_elem_id_t * id ;
snd_ctl_elem_id_alloca ( & id ) ;
snd_hctl_elem_get_id ( helem , id ) ;
2000-08-12 17:34:17 +02:00
printf ( " event value: " ) ;
2001-02-07 00:48:29 +01:00
show_control_id ( id ) ;
2000-08-12 17:34:17 +02:00
printf ( " \n " ) ;
}
2001-02-09 12:20:32 +01:00
static void events_remove ( snd_hctl_elem_t * helem )
2000-08-12 17:34:17 +02:00
{
2001-02-09 12:20:32 +01:00
snd_ctl_elem_id_t * id ;
snd_ctl_elem_id_alloca ( & id ) ;
snd_hctl_elem_get_id ( helem , id ) ;
2000-08-12 17:34:17 +02:00
printf ( " event remove: " ) ;
2001-02-07 00:48:29 +01:00
show_control_id ( id ) ;
2000-08-12 17:34:17 +02:00
printf ( " \n " ) ;
}
2008-11-21 13:09:56 +01:00
static int element_callback ( snd_hctl_elem_t * elem , unsigned int mask )
2000-08-12 17:34:17 +02:00
{
2001-02-13 22:29:38 +01:00
if ( mask = = SND_CTL_EVENT_MASK_REMOVE ) {
2001-02-09 12:20:32 +01:00
events_remove ( elem ) ;
2001-02-13 22:29:38 +01:00
return 0 ;
2001-02-09 12:20:32 +01:00
}
2001-02-13 22:29:38 +01:00
if ( mask & SND_CTL_EVENT_MASK_INFO )
events_info ( elem ) ;
if ( mask & SND_CTL_EVENT_MASK_VALUE )
events_value ( elem ) ;
2001-02-09 12:20:32 +01:00
return 0 ;
}
static void events_add ( snd_hctl_elem_t * helem )
{
snd_ctl_elem_id_t * id ;
snd_ctl_elem_id_alloca ( & id ) ;
snd_hctl_elem_get_id ( helem , id ) ;
2000-08-12 17:34:17 +02:00
printf ( " event add: " ) ;
2001-02-07 00:48:29 +01:00
show_control_id ( id ) ;
2000-08-12 17:34:17 +02:00
printf ( " \n " ) ;
2001-02-09 12:20:32 +01:00
snd_hctl_elem_set_callback ( helem , element_callback ) ;
}
2023-08-30 12:35:03 +02:00
static int ctl_callback ( snd_hctl_t * ctl ATTRIBUTE_UNUSED , unsigned int mask ,
snd_hctl_elem_t * elem )
2001-02-09 12:20:32 +01:00
{
2001-02-13 22:29:38 +01:00
if ( mask & SND_CTL_EVENT_MASK_ADD )
2001-02-09 12:20:32 +01:00
events_add ( elem ) ;
return 0 ;
2000-08-12 17:34:17 +02:00
}
2001-02-05 16:44:45 +01:00
static int events ( int argc ATTRIBUTE_UNUSED , char * argv [ ] ATTRIBUTE_UNUSED )
2000-08-12 17:34:17 +02:00
{
2001-02-09 16:38:59 +01:00
snd_hctl_t * handle ;
2001-02-09 12:20:32 +01:00
snd_hctl_elem_t * helem ;
2000-08-12 17:34:17 +02:00
int err ;
2001-03-26 14:45:50 +02:00
if ( ( err = snd_hctl_open ( & handle , card , 0 ) ) < 0 ) {
2000-09-18 11:47:01 +02:00
error ( " Control %s open error: %s \n " , card , snd_strerror ( err ) ) ;
2000-08-12 17:34:17 +02:00
return err ;
}
2001-02-09 12:20:32 +01:00
snd_hctl_set_callback ( handle , ctl_callback ) ;
2001-02-11 16:45:44 +01:00
if ( ( err = snd_hctl_load ( handle ) ) < 0 ) {
error ( " Control %s hbuild error: %s \n " , card , snd_strerror ( err ) ) ;
return err ;
}
2001-02-09 12:20:32 +01:00
for ( helem = snd_hctl_first_elem ( handle ) ; helem ; helem = snd_hctl_elem_next ( helem ) ) {
snd_hctl_elem_set_callback ( helem , element_callback ) ;
2000-08-12 17:34:17 +02:00
}
printf ( " Ready to listen... \n " ) ;
while ( 1 ) {
2001-02-13 00:51:53 +01:00
int res = snd_hctl_wait ( handle , - 1 ) ;
if ( res > = 0 ) {
2000-08-16 15:36:24 +02:00
printf ( " Poll ok: %i \n " , res ) ;
2001-02-11 16:45:44 +01:00
res = snd_hctl_handle_events ( handle ) ;
2021-03-29 11:36:17 +02:00
if ( res < 0 )
printf ( " ERR: %s (%d) \n " , snd_strerror ( res ) , res ) ;
2000-08-12 17:34:17 +02:00
}
}
2001-02-09 16:38:59 +01:00
snd_hctl_close ( handle ) ;
2005-05-13 19:30:00 +02:00
return 0 ;
2000-08-12 17:34:17 +02:00
}
2001-02-09 12:20:32 +01:00
static void sevents_value ( snd_mixer_selem_id_t * sid )
2000-08-16 15:36:24 +02:00
{
2001-02-09 12:20:32 +01:00
printf ( " event value: '%s',%i \n " , snd_mixer_selem_id_get_name ( sid ) , snd_mixer_selem_id_get_index ( sid ) ) ;
2000-08-16 15:36:24 +02:00
}
2001-02-11 16:45:44 +01:00
static void sevents_info ( snd_mixer_selem_id_t * sid )
2000-08-16 15:36:24 +02:00
{
2001-02-11 16:45:44 +01:00
printf ( " event info: '%s',%i \n " , snd_mixer_selem_id_get_name ( sid ) , snd_mixer_selem_id_get_index ( sid ) ) ;
2000-08-16 15:36:24 +02:00
}
2001-02-09 12:20:32 +01:00
static void sevents_remove ( snd_mixer_selem_id_t * sid )
2000-08-16 15:36:24 +02:00
{
2001-02-09 12:20:32 +01:00
printf ( " event remove: '%s',%i \n " , snd_mixer_selem_id_get_name ( sid ) , snd_mixer_selem_id_get_index ( sid ) ) ;
2000-08-16 15:36:24 +02:00
}
2008-11-21 13:09:56 +01:00
static int melem_event ( snd_mixer_elem_t * elem , unsigned int mask )
2000-08-16 15:36:24 +02:00
{
2001-02-09 12:20:32 +01:00
snd_mixer_selem_id_t * sid ;
snd_mixer_selem_id_alloca ( & sid ) ;
snd_mixer_selem_get_id ( elem , sid ) ;
2001-02-13 22:29:38 +01:00
if ( mask = = SND_CTL_EVENT_MASK_REMOVE ) {
2001-02-09 12:20:32 +01:00
sevents_remove ( sid ) ;
2001-02-13 22:29:38 +01:00
return 0 ;
2001-02-09 12:20:32 +01:00
}
2001-02-13 22:29:38 +01:00
if ( mask & SND_CTL_EVENT_MASK_INFO )
sevents_info ( sid ) ;
if ( mask & SND_CTL_EVENT_MASK_VALUE )
sevents_value ( sid ) ;
2001-02-09 12:20:32 +01:00
return 0 ;
}
2000-08-16 15:36:24 +02:00
2001-02-09 12:20:32 +01:00
static void sevents_add ( snd_mixer_elem_t * elem )
2000-08-16 15:36:24 +02:00
{
2001-02-09 12:20:32 +01:00
snd_mixer_selem_id_t * sid ;
snd_mixer_selem_id_alloca ( & sid ) ;
snd_mixer_selem_get_id ( elem , sid ) ;
printf ( " event add: '%s',%i \n " , snd_mixer_selem_id_get_name ( sid ) , snd_mixer_selem_id_get_index ( sid ) ) ;
2001-02-11 16:45:44 +01:00
snd_mixer_elem_set_callback ( elem , melem_event ) ;
2001-02-09 12:20:32 +01:00
}
2000-08-16 15:36:24 +02:00
2023-08-30 12:35:03 +02:00
static int mixer_event ( snd_mixer_t * mixer ATTRIBUTE_UNUSED , unsigned int mask ,
snd_mixer_elem_t * elem )
2001-02-09 12:20:32 +01:00
{
2001-02-13 22:29:38 +01:00
if ( mask & SND_CTL_EVENT_MASK_ADD )
2001-02-09 12:20:32 +01:00
sevents_add ( elem ) ;
return 0 ;
2000-08-16 15:36:24 +02:00
}
2001-02-05 16:44:45 +01:00
static int sevents ( int argc ATTRIBUTE_UNUSED , char * argv [ ] ATTRIBUTE_UNUSED )
2000-08-16 15:36:24 +02:00
{
snd_mixer_t * handle ;
int err ;
2001-03-26 14:45:50 +02:00
if ( ( err = snd_mixer_open ( & handle , 0 ) ) < 0 ) {
2001-02-11 16:45:44 +01:00
error ( " Mixer %s open error: %s " , card , snd_strerror ( err ) ) ;
return err ;
}
2005-06-06 16:02:10 +02:00
if ( smixer_level = = 0 & & ( err = snd_mixer_attach ( handle , card ) ) < 0 ) {
2001-02-11 16:45:44 +01:00
error ( " Mixer attach %s error: %s " , card , snd_strerror ( err ) ) ;
snd_mixer_close ( handle ) ;
return err ;
}
2005-03-27 14:50:34 +02:00
if ( ( err = snd_mixer_selem_register ( handle , smixer_level > 0 ? & smixer_options : NULL , NULL ) ) < 0 ) {
2001-02-11 16:45:44 +01:00
error ( " Mixer register error: %s " , snd_strerror ( err ) ) ;
snd_mixer_close ( handle ) ;
2000-08-16 15:36:24 +02:00
return err ;
}
2001-02-09 12:20:32 +01:00
snd_mixer_set_callback ( handle , mixer_event ) ;
2001-02-11 16:45:44 +01:00
err = snd_mixer_load ( handle ) ;
2001-02-09 12:20:32 +01:00
if ( err < 0 ) {
2004-02-24 10:19:31 +01:00
error ( " Mixer %s load error: %s " , card , snd_strerror ( err ) ) ;
2001-02-09 12:20:32 +01:00
snd_mixer_close ( handle ) ;
return err ;
}
2000-08-16 15:36:24 +02:00
printf ( " Ready to listen... \n " ) ;
while ( 1 ) {
int res ;
2001-02-13 00:51:53 +01:00
res = snd_mixer_wait ( handle , - 1 ) ;
if ( res > = 0 ) {
2000-08-16 15:36:24 +02:00
printf ( " Poll ok: %i \n " , res ) ;
2001-02-11 16:45:44 +01:00
res = snd_mixer_handle_events ( handle ) ;
assert ( res > = 0 ) ;
2000-08-16 15:36:24 +02:00
}
}
snd_mixer_close ( handle ) ;
2005-05-13 19:30:00 +02:00
return 0 ;
2000-08-16 15:36:24 +02:00
}
2005-12-01 12:32:28 +01:00
/*
* split a line into tokens
* the content in the line buffer is modified
*/
static int split_line ( char * buf , char * * token , int max_token )
{
char * dst ;
int n , esc , quote ;
for ( n = 0 ; n < max_token ; n + + ) {
while ( isspace ( * buf ) )
buf + + ;
if ( ! * buf | | * buf = = ' \n ' )
return n ;
/* skip comments */
if ( * buf = = ' # ' | | * buf = = ' ! ' )
return n ;
esc = 0 ;
quote = 0 ;
token [ n ] = buf ;
for ( dst = buf ; * buf & & * buf ! = ' \n ' ; buf + + ) {
if ( esc )
esc = 0 ;
else if ( isspace ( * buf ) & & ! quote ) {
buf + + ;
break ;
} else if ( * buf = = ' \\ ' ) {
esc = 1 ;
continue ;
} else if ( * buf = = ' \' ' | | * buf = = ' " ' ) {
if ( ! quote ) {
quote = * buf ;
continue ;
} else if ( * buf = = quote ) {
quote = 0 ;
continue ;
}
}
* dst + + = * buf ;
}
* dst = 0 ;
}
return n ;
}
# define MAX_ARGS 32
static int exec_stdin ( void )
{
int narg ;
char buf [ 256 ] , * args [ MAX_ARGS ] ;
int err = 0 ;
/* quiet = 1; */
ignore_error = 1 ;
while ( fgets ( buf , sizeof ( buf ) , stdin ) ) {
narg = split_line ( buf , args , MAX_ARGS ) ;
if ( narg > 0 ) {
if ( ! strcmp ( args [ 0 ] , " sset " ) | | ! strcmp ( args [ 0 ] , " set " ) )
err = sset ( narg - 1 , args + 1 , 0 , 1 ) ;
else if ( ! strcmp ( args [ 0 ] , " cset " ) )
err = cset ( narg - 1 , args + 1 , 0 , 1 ) ;
if ( err < 0 )
return 1 ;
}
}
return 0 ;
}
1999-05-02 18:21:40 +02:00
int main ( int argc , char * argv [ ] )
{
2021-05-10 16:31:06 +02:00
int badopt , retval , level = 0 ;
2005-12-01 12:32:28 +01:00
int read_stdin = 0 ;
2008-11-21 13:10:02 +01:00
static const struct option long_option [ ] =
1999-05-02 18:21:40 +02:00
{
2005-12-01 12:32:28 +01:00
{ " help " , 0 , NULL , ' h ' } ,
{ " card " , 1 , NULL , ' c ' } ,
{ " device " , 1 , NULL , ' D ' } ,
{ " quiet " , 0 , NULL , ' q ' } ,
{ " inactive " , 0 , NULL , ' i ' } ,
{ " debug " , 0 , NULL , ' d ' } ,
{ " nocheck " , 0 , NULL , ' n ' } ,
{ " version " , 0 , NULL , ' v ' } ,
{ " abstract " , 1 , NULL , ' a ' } ,
{ " stdin " , 0 , NULL , ' s ' } ,
2012-03-12 10:56:22 +01:00
{ " raw-volume " , 0 , NULL , ' R ' } ,
{ " mapped-volume " , 0 , NULL , ' M ' } ,
1999-05-02 18:21:40 +02:00
{ NULL , 0 , NULL , 0 } ,
} ;
2021-05-10 16:31:06 +02:00
badopt = 0 ;
1999-05-02 18:21:40 +02:00
while ( 1 ) {
int c ;
2012-03-12 10:56:22 +01:00
if ( ( c = getopt_long ( argc , argv , " hc:D:qidnva:sRM " , long_option , NULL ) ) < 0 )
1999-05-02 18:21:40 +02:00
break ;
switch ( c ) {
case ' h ' :
2005-01-31 14:52:09 +01:00
help ( ) ;
return 0 ;
1999-05-02 18:21:40 +02:00
case ' c ' :
2002-04-07 22:42:20 +02:00
{
int i ;
i = snd_card_get_index ( optarg ) ;
if ( i > = 0 & & i < 32 )
2021-04-06 18:38:18 +02:00
# if defined(SND_LIB_VER) && SND_LIB_VER(1, 2, 5) <= SND_LIB_VERSION
sprintf ( card , " sysdefault:%i " , i ) ;
# else
2002-04-07 22:42:20 +02:00
sprintf ( card , " hw:%i " , i ) ;
2021-04-06 18:38:18 +02:00
# endif
2002-04-07 22:42:20 +02:00
else {
2021-05-10 16:31:06 +02:00
fprintf ( stderr , " Invalid card number '%s'. \n " , optarg ) ;
badopt + + ;
2002-04-07 22:42:20 +02:00
}
}
break ;
case ' D ' :
2004-08-10 11:46:19 +02:00
strncpy ( card , optarg , sizeof ( card ) - 1 ) ;
2003-10-17 09:34:19 +02:00
card [ sizeof ( card ) - 1 ] = ' \0 ' ;
1999-05-02 18:21:40 +02:00
break ;
case ' q ' :
quiet = 1 ;
break ;
2001-07-23 15:26:34 +02:00
case ' i ' :
level | = LEVEL_INACTIVE ;
break ;
2002-04-07 22:42:20 +02:00
case ' d ' :
1999-05-02 18:21:40 +02:00
debugflag = 1 ;
break ;
2005-02-04 16:35:27 +01:00
case ' n ' :
no_check = 1 ;
break ;
1999-05-02 18:21:40 +02:00
case ' v ' :
printf ( " amixer version " SND_UTIL_VERSION_STR " \n " ) ;
2021-05-10 16:31:06 +02:00
return 0 ;
2005-03-27 14:50:34 +02:00
case ' a ' :
smixer_level = 1 ;
memset ( & smixer_options , 0 , sizeof ( smixer_options ) ) ;
smixer_options . ver = 1 ;
if ( ! strcmp ( optarg , " none " ) )
smixer_options . abstract = SND_MIXER_SABSTRACT_NONE ;
else if ( ! strcmp ( optarg , " basic " ) )
2005-03-29 16:06:29 +02:00
smixer_options . abstract = SND_MIXER_SABSTRACT_BASIC ;
2005-03-27 14:50:34 +02:00
else {
fprintf ( stderr , " Select correct abstraction level (none or basic)... \n " ) ;
2021-05-10 16:31:06 +02:00
badopt + + ;
2005-03-27 14:50:34 +02:00
}
break ;
2005-12-01 12:32:28 +01:00
case ' s ' :
read_stdin = 1 ;
break ;
2012-03-12 10:56:22 +01:00
case ' R ' :
std_vol_type = VOL_RAW ;
break ;
case ' M ' :
std_vol_type = VOL_MAP ;
break ;
1999-05-02 18:21:40 +02:00
default :
2021-05-10 16:31:06 +02:00
fprintf ( stderr , " Invalid switch or option -%c needs an argument. \n " , c ) ;
badopt + + ;
1999-05-02 18:21:40 +02:00
}
}
2021-05-10 16:31:06 +02:00
if ( badopt )
2005-01-31 14:52:09 +01:00
return 1 ;
2021-05-10 16:31:06 +02:00
2005-06-06 16:02:10 +02:00
smixer_options . device = card ;
2005-12-01 12:32:28 +01:00
2021-03-30 13:31:13 +02:00
if ( read_stdin ) {
retval = exec_stdin ( ) ;
goto finish ;
}
2005-12-01 12:32:28 +01:00
1999-05-02 18:21:40 +02:00
if ( argc - optind < = 0 ) {
2021-03-30 13:31:13 +02:00
retval = selems ( LEVEL_BASIC | level ) ? 1 : 0 ;
goto finish ;
1999-05-02 18:21:40 +02:00
}
2000-08-11 21:29:55 +02:00
if ( ! strcmp ( argv [ optind ] , " help " ) ) {
2021-03-30 13:31:13 +02:00
retval = help ( ) ? 1 : 0 ;
2000-08-11 21:29:55 +02:00
} else if ( ! strcmp ( argv [ optind ] , " info " ) ) {
2021-03-30 13:31:13 +02:00
retval = info ( ) ? 1 : 0 ;
2000-07-15 12:21:59 +02:00
} else if ( ! strcmp ( argv [ optind ] , " controls " ) ) {
2021-03-30 13:31:13 +02:00
retval = controls ( level ) ? 1 : 0 ;
1999-05-02 18:21:40 +02:00
} else if ( ! strcmp ( argv [ optind ] , " contents " ) ) {
2021-03-30 13:31:13 +02:00
retval = controls ( LEVEL_BASIC | level ) ? 1 : 0 ;
2000-08-11 21:29:55 +02:00
} else if ( ! strcmp ( argv [ optind ] , " scontrols " ) | | ! strcmp ( argv [ optind ] , " simple " ) ) {
2021-03-30 13:31:13 +02:00
retval = selems ( level ) ? 1 : 0 ;
2000-07-15 12:21:59 +02:00
} else if ( ! strcmp ( argv [ optind ] , " scontents " ) ) {
2021-03-30 13:31:13 +02:00
retval = selems ( LEVEL_BASIC | level ) ? 1 : 0 ;
2000-08-11 21:29:55 +02:00
} else if ( ! strcmp ( argv [ optind ] , " sset " ) | | ! strcmp ( argv [ optind ] , " set " ) ) {
2021-03-30 13:31:13 +02:00
retval = sset ( argc - optind - 1 , argc - optind > 1 ? argv + optind + 1 : NULL , 0 , 0 ) ? 1 : 0 ;
2000-08-11 21:29:55 +02:00
} else if ( ! strcmp ( argv [ optind ] , " sget " ) | | ! strcmp ( argv [ optind ] , " get " ) ) {
2021-03-30 13:31:13 +02:00
retval = sset ( argc - optind - 1 , argc - optind > 1 ? argv + optind + 1 : NULL , 1 , 0 ) ? 1 : 0 ;
2000-07-15 12:21:59 +02:00
} else if ( ! strcmp ( argv [ optind ] , " cset " ) ) {
2021-03-30 13:31:13 +02:00
retval = cset ( argc - optind - 1 , argc - optind > 1 ? argv + optind + 1 : NULL , 0 , 0 ) ? 1 : 0 ;
2000-07-15 12:21:59 +02:00
} else if ( ! strcmp ( argv [ optind ] , " cget " ) ) {
2021-03-30 13:31:13 +02:00
retval = cset ( argc - optind - 1 , argc - optind > 1 ? argv + optind + 1 : NULL , 1 , 0 ) ? 1 : 0 ;
2000-08-12 17:34:17 +02:00
} else if ( ! strcmp ( argv [ optind ] , " events " ) ) {
2021-03-30 13:31:13 +02:00
retval = events ( argc - optind - 1 , argc - optind > 1 ? argv + optind + 1 : NULL ) ;
2000-08-16 15:36:24 +02:00
} else if ( ! strcmp ( argv [ optind ] , " sevents " ) ) {
2021-03-30 13:31:13 +02:00
retval = sevents ( argc - optind - 1 , argc - optind > 1 ? argv + optind + 1 : NULL ) ;
1999-05-02 18:21:40 +02:00
} else {
fprintf ( stderr , " amixer: Unknown command '%s'... \n " , argv [ optind ] ) ;
2021-03-30 13:31:13 +02:00
retval = 0 ;
1999-05-02 18:21:40 +02:00
}
2021-03-30 13:31:13 +02:00
finish :
snd_config_update_free_global ( ) ;
return retval ;
1999-05-02 18:21:40 +02:00
}