Hi, I have made a new attempt at adding support for dB gain reports in alsamixer. See the attached diffs against the current alsa hg repository. 27-5-2006 It shows an example of support for the snd-ca0106 driver. As one can see, this adds minimal amounts of code to each sound card driver. 22 new lines + 1 per volume control making 33 lines total. Any comments? James
diff -r 551da56a592c core/control.c --- a/core/control.c Fri May 19 19:22:34 2006 +0200 +++ b/core/control.c Sat May 27 19:53:59 2006 +0100 @@ -241,6 +241,7 @@ struct snd_kcontrol *snd_ctl_new1(const kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put; + kctl.info2 = ncontrol->info2; kctl.private_value = ncontrol->private_value; kctl.private_data = private_data; return snd_ctl_new(&kctl, access); @@ -570,6 +571,172 @@ static int snd_ctl_card_info(struct snd_ } kfree(info); return 0; +} + +static int uint32_to_message(struct snd_ctl_misc *misc, u32 value) +{ + unsigned int pointer = misc->length; + u32 *store = (u32*) &misc->message[pointer]; + *store = value; + misc->length += sizeof(u32); + return 0; +} + +static int message_to_uint32(struct snd_ctl_misc *misc, unsigned int *value) +{ + unsigned int pointer = misc->length; + u32 *store = (u32*) &misc->message[pointer]; + *value = *store; + misc->length += sizeof(u32); + return 0; +} + + +static int add_int32_tlv_to_container(struct snd_ctl_misc *misc, unsigned int type, unsigned int value) +{ + uint32_to_message(misc, type); + uint32_to_message(misc, 4); + uint32_to_message(misc, value); + /* FIXME: Check for overflows */ + return 0; +} + +static int add_message_tlv_to_container( + struct snd_ctl_misc *misc, unsigned int length, unsigned char *message) +{ + unsigned int pointer = misc->length; + unsigned int n; + for(n=0; n<length; n++) { + misc->message[pointer++] = message[n]; + } + misc->length = pointer; + /* FIXME: Check for overflows */ + return 0; +} + + +static int container_open(struct snd_ctl_misc *misc, unsigned int type) +{ + uint32_to_message(misc, type); + uint32_to_message(misc, 0); /* This gets updated at container close */ + return 0; +} + +static int container_close(struct snd_ctl_misc *misc) +{ + u32 *store = (u32*) &misc->message[sizeof(u32)]; + *store = misc->length; + return 0; +} + +static int snd_ctl_misc_user(struct snd_ctl_file *ctl, + struct snd_ctl_misc __user *_misc) +{ + struct snd_ctl_misc *misc; + struct snd_card *card = ctl->card; + struct snd_kcontrol *kctl; + struct snd_ctl_elem_info2 info2; + int result=0; + int numid; + unsigned int received_length; + unsigned int tlv_type; + unsigned int tlv_length; + unsigned int tlv_value; + unsigned int container_type; + unsigned int container_length; + + misc = kmalloc(2048, GFP_KERNEL); + if (misc == NULL) + return -ENOMEM; + + /* Only copy _misc: version and length from user-space */ + if (copy_from_user(misc, _misc, 8)) { + result = -EFAULT; + goto exit_free; + } + /* Sanity check */ + snd_printk("misc->version=%d\n",misc->version); + if (misc->version!=1) { + result = -EINVAL; + goto exit_free; + } + received_length = misc->length; + snd_printk("misc->length=%d\n",misc->length); + if (received_length>2040 || received_length<8) { + result = -EINVAL; + goto exit_free; + } + /* Copy the entire _misc from user-space */ + if (copy_from_user(misc, _misc, received_length)) { + result = -EFAULT; + goto exit_free; + } + misc->length=0; + message_to_uint32(misc, &container_type); + snd_printk("container_type=%d\n",container_type); + switch (container_type) { + case SND_MISC_DB_SCALE: + message_to_uint32(misc, &container_length); + /* FIXME: Do more sanity checks here */ + message_to_uint32(misc, &tlv_type); + snd_printk("tlv_type=%d\n",tlv_type); + switch (tlv_type) { + case SND_MISC_ELEM_NUMID: + message_to_uint32(misc, &tlv_length); + snd_printk("tlv_length=%d\n",tlv_length); + if (tlv_length != 4) { + result = -EINVAL; + goto exit_free; + } + message_to_uint32(misc, &tlv_value); + snd_printk("tlv_value=%d\n",tlv_value); + break; + default: + result = -EINVAL; + goto exit_free; + break; + } + break; + default: + result = -EINVAL; + goto exit_free; + } + numid=tlv_value; + //snd_printk("numid=%d\n", tlv_value); + down_read(&card->controls_rwsem); + kctl = snd_ctl_find_numid(card, numid); + if (kctl == NULL) { + snd_printk("stop 1\n"); + result = -ENOENT; + goto exit_up; + } + if ( !(kctl->info2) ) { + snd_printk("stop 2\n"); + result = -EINVAL; + goto exit_up; + } + if (kctl->info2(kctl, 1, &info2)) { + snd_printk("stop 3\n"); + result = -EINVAL; + goto exit_up; + } + ; + snd_printk("ok 1\n"); + misc->version=1; + misc->length=0; + container_open(misc, SND_MISC_DB_SCALE); + add_message_tlv_to_container(misc, info2.length, info2.message); + container_close(misc); + if (copy_to_user(_misc, misc, received_length)) + result = -EFAULT; + snd_printk("ok 2\n"); + +exit_up: + up_read(&card->controls_rwsem); +exit_free: + kfree(misc); +exit: + return result; } static int snd_ctl_elem_list(struct snd_card *card, @@ -1103,6 +1270,8 @@ static long snd_ctl_ioctl(struct file *f return snd_ctl_elem_add_user(ctl, argp, 1); case SNDRV_CTL_IOCTL_ELEM_REMOVE: return snd_ctl_elem_remove(ctl, argp); + case SNDRV_CTL_IOCTL_MISC: + return snd_ctl_misc_user(ctl, argp); case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: return snd_ctl_subscribe_events(ctl, ip); case SNDRV_CTL_IOCTL_POWER: diff -r 551da56a592c include/asound.h --- a/include/asound.h Fri May 19 19:22:34 2006 +0200 +++ b/include/asound.h Sat May 27 19:53:59 2006 +0100 @@ -818,6 +818,44 @@ struct snd_ctl_elem_value { unsigned char reserved[128-sizeof(struct timespec)]; }; +struct snd_ctl_misc { + u32 version; + u32 length; + unsigned char message[]; +}; + +struct snd_ctl_elem_db_scale_type1 { + /* Type=1 for simple + * dB = (X * db_per_devision) - db_offset + * This equation is used instead of a more normal + * dB = (( X - db_offset) * db_numerator) / db_denominator + * in order to avoid any devision by zero possibilities, + * save 4 bytes per db_scale and avoid floating point maths. + * + * Types >= 2 are for future more complex conversion functions. + * All types have to have a length value, in case the user land + * alsa lib does not yet understand the type. The length field allows + * user land to safely skip this TLV. + */ + u32 type; /* 1 */ + u32 length; /* 12 */ + s32 db_per_devision; /* Use db per devision * 100 */ + s32 db_offset; /* Use dB offset * 100. This identifies the 0dB point */ + s32 db_mute; /* The value, before conversion, that is equivalent to MUTE */ +}; + +struct snd_ctl_elem_info2 { + u32 length; + unsigned char *message; +}; + + +#define SND_MISC_DB_SCALE 2 +/* Request */ +#define SND_MISC_ELEM_NUMID 2 +/* Response */ +#define SND_MISC_ELEM_DB_SCALE 2 + enum { SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info), @@ -833,6 +871,7 @@ enum { SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id), SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info), + SNDRV_CTL_IOCTL_MISC = _IOR('U', 0x22, struct snd_ctl_misc), SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), SNDRV_CTL_IOCTL_PCM_INFO = _IOWR('U', 0x31, struct snd_pcm_info), SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE = _IOW('U', 0x32, int), diff -r 551da56a592c include/control.h --- a/include/control.h Fri May 19 19:22:34 2006 +0200 +++ b/include/control.h Sat May 27 19:53:59 2006 +0100 @@ -30,6 +30,7 @@ typedef int (snd_kcontrol_info_t) (struc typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo); typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); +typedef int (snd_kcontrol_info2_t) (struct snd_kcontrol * kcontrol, int type, struct snd_ctl_elem_info2 * uinfo); struct snd_kcontrol_new { snd_ctl_elem_iface_t iface; /* interface identifier */ @@ -42,6 +43,7 @@ struct snd_kcontrol_new { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + snd_kcontrol_info2_t *info2; unsigned long private_value; }; @@ -58,6 +60,7 @@ struct snd_kcontrol { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + snd_kcontrol_info2_t *info2; unsigned long private_value; void *private_data; void (*private_free)(struct snd_kcontrol *kcontrol); diff -r 551da56a592c pci/ca0106/ca0106_mixer.c --- a/pci/ca0106/ca0106_mixer.c Fri May 19 19:22:34 2006 +0200 +++ b/pci/ca0106/ca0106_mixer.c Sat May 27 19:53:59 2006 +0100 @@ -362,6 +362,28 @@ static int snd_ca0106_spdif_put(struct s return change; } +static struct snd_ctl_elem_db_scale_type1 db_scale_1 = +{ + /* Scale from -51.50 dB to +12.00 dB in steps of 0.25 dB. 256 steps. */ + .type=1, + .length=12, + .db_per_devision=25, + .db_offset=5175, + .db_mute=0 +}; + +static int snd_ca0106_info2_db_scale_1(struct snd_kcontrol *kcontrol, int type, + struct snd_ctl_elem_info2 *uinfo) +{ + if (type==1) { + uinfo->length=sizeof(struct snd_ctl_elem_db_scale_type1); + uinfo->message=(unsigned char*) &db_scale_1; + return 0; + } + return -EINVAL; /* type not implemented */ +} + + static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -466,13 +488,14 @@ static int snd_ca0106_i2c_volume_put(str return change; } -#define CA_VOLUME(xname,chid,reg) \ +#define CA_VOLUME(xname,chid,reg,db_function) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_ca0106_volume_info, \ .get = snd_ca0106_volume_get, \ .put = snd_ca0106_volume_put, \ - .private_value = ((chid) << 8) | (reg) \ + .private_value = ((chid) << 8) | (reg), \ + .info2 = db_function \ } #define I2C_VOLUME(xname,chid) \ @@ -487,25 +510,33 @@ static int snd_ca0106_i2c_volume_put(str static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { CA_VOLUME("Analog Front Playback Volume", - CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2), + CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2, + &snd_ca0106_info2_db_scale_1), CA_VOLUME("Analog Rear Playback Volume", - CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2), + CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2, + &snd_ca0106_info2_db_scale_1), CA_VOLUME("Analog Center/LFE Playback Volume", - CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2), + CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2, + &snd_ca0106_info2_db_scale_1), CA_VOLUME("Analog Side Playback Volume", - CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2), - + CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2, + &snd_ca0106_info2_db_scale_1), CA_VOLUME("IEC958 Front Playback Volume", - CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1), + CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1, + &snd_ca0106_info2_db_scale_1), CA_VOLUME("IEC958 Rear Playback Volume", - CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1), + CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1, + &snd_ca0106_info2_db_scale_1), CA_VOLUME("IEC958 Center/LFE Playback Volume", - CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1), + CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1, + &snd_ca0106_info2_db_scale_1), CA_VOLUME("IEC958 Unknown Playback Volume", - CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1), + CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1, + &snd_ca0106_info2_db_scale_1), CA_VOLUME("CAPTURE feedback Playback Volume", - 1, CAPTURE_CONTROL), + 1, CAPTURE_CONTROL, + &snd_ca0106_info2_db_scale_1), I2C_VOLUME("Phone Capture Volume", 0), I2C_VOLUME("Mic Capture Volume", 1),
diff -r d5a6770e7faf include/control.h --- a/include/control.h Fri May 19 18:26:41 2006 +0200 +++ b/include/control.h Sat May 27 19:54:17 2006 +0100 @@ -52,6 +52,9 @@ typedef struct snd_aes_iec958 { /** CTL card info container */ typedef struct _snd_ctl_card_info snd_ctl_card_info_t; + +/** CTL misc container */ +typedef struct _snd_ctl_misc snd_ctl_misc_t; /** CTL element identifier container */ typedef struct _snd_ctl_elem_id snd_ctl_elem_id_t; @@ -212,6 +215,7 @@ int snd_ctl_poll_descriptors_revents(snd int snd_ctl_poll_descriptors_revents(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); int snd_ctl_subscribe_events(snd_ctl_t *ctl, int subscribe); int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info); +int snd_ctl_misc(snd_ctl_t *ctl, snd_ctl_misc_t *misc); int snd_ctl_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t * list); int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info); int snd_ctl_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *value); @@ -485,6 +489,8 @@ int snd_hctl_elem_info(snd_hctl_elem_t * int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t * info); int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value); int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value); +int snd_hctl_elem_misc(snd_hctl_elem_t *elem, snd_ctl_misc_t * misc); +int snd_hctl_elem_get_db_gain(snd_hctl_elem_t *elem, long volume, long *db_gain); snd_hctl_t *snd_hctl_elem_get_hctl(snd_hctl_elem_t *elem); diff -r d5a6770e7faf include/local.h --- a/include/local.h Fri May 19 18:26:41 2006 +0200 +++ b/include/local.h Sat May 27 19:54:17 2006 +0100 @@ -46,6 +46,7 @@ #define _snd_pcm_status sndrv_pcm_status #define _snd_ctl_card_info sndrv_ctl_card_info +#define _snd_ctl_misc sndrv_ctl_misc #define _snd_ctl_elem_id sndrv_ctl_elem_id #define _snd_ctl_elem_list sndrv_ctl_elem_list #define _snd_ctl_elem_info sndrv_ctl_elem_info diff -r d5a6770e7faf include/sound/asound.h --- a/include/sound/asound.h Fri May 19 18:26:41 2006 +0200 +++ b/include/sound/asound.h Sat May 27 19:54:17 2006 +0100 @@ -26,6 +26,7 @@ #if defined(LINUX) || defined(__LINUX__) || defined(__linux__) #include <linux/ioctl.h> +#include <inttypes.h> #ifdef __KERNEL__ @@ -847,6 +848,18 @@ struct sndrv_ctl_elem_value { unsigned char reserved[128-sizeof(struct timespec)]; }; +struct sndrv_ctl_misc { + uint32_t version; /* This should be uint32_t */ + uint32_t length; /* This should be uint32_t */ + unsigned char message[]; +}; + +#define SND_MISC_DB_SCALE 2 +/* Request */ +#define SND_MISC_ELEM_NUMID 2 +/* Response */ +#define SND_MISC_ELEM_DB_SCALE_TYPE1 1 + enum { SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct sndrv_ctl_card_info), @@ -862,6 +875,7 @@ enum { SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct sndrv_ctl_elem_id), SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct sndrv_hwdep_info), + SNDRV_CTL_IOCTL_MISC = _IOR('U', 0x22, struct sndrv_ctl_misc), SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), SNDRV_CTL_IOCTL_PCM_INFO = _IOWR('U', 0x31, struct sndrv_pcm_info), SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE = _IOW('U', 0x32, int), diff -r d5a6770e7faf src/control/control.c --- a/src/control/control.c Fri May 19 18:26:41 2006 +0200 +++ b/src/control/control.c Sat May 27 19:54:17 2006 +0100 @@ -234,6 +234,20 @@ int snd_ctl_card_info(snd_ctl_t *ctl, sn { assert(ctl && info); return ctl->ops->card_info(ctl, info); +} + +/** + * \brief Get/Set misc related information + * \param ctl CTL handle + * \param misc pointer + * \return 0 on success otherwise a negative error code + */ +int snd_ctl_misc(snd_ctl_t *ctl, snd_ctl_misc_t *misc) +{ + int err; + assert(ctl && misc); + err = ctl->ops->misc(ctl, misc); + return err; } /** diff -r d5a6770e7faf src/control/control_hw.c --- a/src/control/control_hw.c Fri May 19 18:26:41 2006 +0200 +++ b/src/control/control_hw.c Sat May 27 19:54:17 2006 +0100 @@ -128,6 +128,16 @@ static int snd_ctl_hw_card_info(snd_ctl_ return 0; } +static int snd_ctl_hw_misc(snd_ctl_t *handle, snd_ctl_misc_t *misc) +{ + snd_ctl_hw_t *hw = handle->private_data; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_MISC, misc) < 0) { + //SYSERR("SNDRV_CTL_IOCTL_MISC failed"); + return -errno; + } + return 0; +} + static int snd_ctl_hw_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list) { snd_ctl_hw_t *hw = handle->private_data; @@ -296,6 +306,7 @@ snd_ctl_ops_t snd_ctl_hw_ops = { .async = snd_ctl_hw_async, .subscribe_events = snd_ctl_hw_subscribe_events, .card_info = snd_ctl_hw_card_info, + .misc = snd_ctl_hw_misc, .element_list = snd_ctl_hw_elem_list, .element_info = snd_ctl_hw_elem_info, .element_add = snd_ctl_hw_elem_add, diff -r d5a6770e7faf src/control/control_local.h --- a/src/control/control_local.h Fri May 19 18:26:41 2006 +0200 +++ b/src/control/control_local.h Sat May 27 19:54:17 2006 +0100 @@ -27,6 +27,7 @@ typedef struct _snd_ctl_ops { int (*async)(snd_ctl_t *handle, int sig, pid_t pid); int (*subscribe_events)(snd_ctl_t *handle, int subscribe); int (*card_info)(snd_ctl_t *handle, snd_ctl_card_info_t *info); + int (*misc)(snd_ctl_t *handle, snd_ctl_misc_t *info); int (*element_list)(snd_ctl_t *handle, snd_ctl_elem_list_t *list); int (*element_info)(snd_ctl_t *handle, snd_ctl_elem_info_t *info); int (*element_add)(snd_ctl_t *handle, snd_ctl_elem_info_t *info); diff -r d5a6770e7faf src/control/hcontrol.c --- a/src/control/hcontrol.c Fri May 19 18:26:41 2006 +0200 +++ b/src/control/hcontrol.c Sat May 27 19:54:17 2006 +0100 @@ -49,6 +49,7 @@ to reduce overhead accessing the real co #include <fcntl.h> #include <sys/ioctl.h> #include <pthread.h> +#include <inttypes.h> #ifndef DOC_HIDDEN #define __USE_GNU #endif @@ -757,6 +758,181 @@ int snd_hctl_handle_events(snd_hctl_t *h return count; } + +static int uint32_to_message(snd_ctl_misc_t *misc, uint32_t value) +{ + unsigned int pointer = misc->length; + uint32_t *store = (uint32_t*) &misc->message[pointer]; + *store = value; + misc->length += sizeof(uint32_t); + return 0; +} + +static int message_to_uint32(snd_ctl_misc_t *misc, uint32_t *value) +{ + unsigned int pointer = misc->length; + uint32_t *store = (uint32_t*) &misc->message[pointer]; + *value = *store; + misc->length += sizeof(uint32_t); + return 0; +} + + +static int add_int32_tlv_to_container(snd_ctl_misc_t *misc, uint32_t type, uint32_t value) +{ + uint32_to_message(misc, type); + uint32_to_message(misc, 4); + uint32_to_message(misc, value); + /* FIXME: Check for overflows */ + return 0; +} + +static int add_message_tlv_to_container( + snd_ctl_misc_t *misc, uint32_t length, unsigned char *message) +{ + unsigned int pointer = misc->length; + unsigned int n; + for(n=0; n<length; n++) { + misc->message[pointer++] = message[n]; + } + misc->length = pointer; + /* FIXME: Check for overflows */ + return 0; +} + + +static int container_open(snd_ctl_misc_t *misc, uint32_t type) +{ + uint32_to_message(misc, type); + uint32_to_message(misc, 0); /* This gets updated at container close */ + return 0; +} + +static int container_close(snd_ctl_misc_t *misc) +{ + uint32_t *store = (uint32_t*) &misc->message[sizeof(uint32_t)]; + *store = misc->length; + return 0; +} + + +struct snd_ctl_elem_db_scale_type1 { + /* Type=1 for simple + * dB = (X * db_per_devision) - db_offset + * This equation is used instead of a more normal + * dB = (( X - db_offset) * db_numerator) / db_denominator + * in order to avoid any devision by zero possibilities, + * save 4 bytes per db_scale and avoid floating point maths. + * + * Types >= 2 are for future more complex conversion functions. + * All types have to have a length value, in case the user land + * alsa lib does not yet understand the type. The length field allows + * user land to safely skip this TLV. + */ + uint32_t type; /* 1 */ + uint32_t length; /* 12 */ + int32_t db_per_devision; /* Use db per devision * 100 */ + int32_t db_offset; /* Use dB offset * 100. This identifies the 0dB point */ + int32_t db_mute; /* The value, before conversion, that is equivalent to MUTE */ +}; + +/** + * \brief Get misc information for an HCTL element + * \param elem HCTL element + * \param info HCTL element information + * \return 0 otherwise a negative error code on failure + */ +int snd_hctl_elem_get_db_gain(snd_hctl_elem_t *elem, long volume, long *db_gain) +{ + snd_ctl_misc_t *misc; + int err=0; + unsigned int received_length; + unsigned int tlv_type; + unsigned int tlv_length; + unsigned int container_type; + unsigned int container_length; + struct snd_ctl_elem_db_scale_type1 *db_scale_1; + + assert(elem); + assert(elem->hctl); + misc=malloc(2048); + if (misc == NULL) { + err = -ENOMEM; + return err; + } + misc->version=1; + misc->length=0; + container_open(misc, SND_MISC_DB_SCALE); + add_int32_tlv_to_container(misc, SND_MISC_ELEM_NUMID, elem->id.numid); + container_close(misc); + misc->length=2040; + + err = snd_ctl_misc(elem->hctl->ctl, misc); + if (err) goto _err; + + received_length = misc->length; + misc->length=0; + message_to_uint32(misc, &container_type); + switch (container_type) { + case SND_MISC_DB_SCALE: + message_to_uint32(misc, &container_length); + db_scale_1 = (struct snd_ctl_elem_db_scale_type1*) &misc->message[misc->length]; + /* FIXME: Do more sanity checks here */ + message_to_uint32(misc, &tlv_type); + switch (tlv_type) { + case SND_MISC_ELEM_DB_SCALE_TYPE1: + message_to_uint32(misc, &tlv_length); + if (tlv_length != 12) { + err = -EINVAL; + goto _err; + } + misc->length+=tlv_length; + if (volume == db_scale_1->db_mute) { + *db_gain=-9999999; /* Muted */ + break; + } + /* Scale ca0106: from -51.50 dB to +12.00 dB in steps of 0.75 dB. 256 steps. */ + /* Scale emu10k1 dsp controls: from -39.60 dB to 0.00 dB in steps of 0.4 dB. 100 steps. */ + *db_gain=(volume * db_scale_1->db_per_devision) - db_scale_1->db_offset; + break; + default: + err = -EINVAL; + goto _err; + break; + } + break; + default: + err = -EINVAL; + goto _err; + } + +_err: + free(misc); + return err; +#if 0 + switch (tlv_value) { + case 1: /* Scale from -51.50 dB to +12.00 dB in steps of 0.75 dB. 256 steps. */ + if (volume == 0) { + *db_gain=-9999999; /* Muted */ + break; + } + *db_gain=(volume * 25) - 5175; /* For ca0106 controls */ + break; + case 2: /* Scale from -39.60 dB to 0.00 dB in steps of 0.4 dB. 100 steps. */ + if (volume == 0) { + *db_gain=-9999999; /* Muted */ + break; + } + *db_gain=(volume * 40) - 4000; /* For emu10k1 dsp controls */ + break; + default: + *db_gain=-9999999; /* Muted */ + return 1; + } + return 0; +#endif +} + /** * \brief Get information for an HCTL element * \param elem HCTL element diff -r d5a6770e7faf src/mixer/simple_none.c --- a/src/mixer/simple_none.c Fri May 19 18:26:41 2006 +0200 +++ b/src/mixer/simple_none.c Sat May 27 19:54:17 2006 +0100 @@ -971,12 +971,41 @@ static int get_volume_ops(snd_mixer_elem return 0; } -static int get_dB_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED, - int dir ATTRIBUTE_UNUSED, - snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED, - long *value ATTRIBUTE_UNUSED) -{ - return -ENXIO; +static int get_dB_ops(snd_mixer_elem_t *elem, + int dir, + snd_mixer_selem_channel_id_t channel, + long *value) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + selem_ctl_t *c; + int err=-EINVAL; + long volume, db_gain; + if (dir==SM_PLAY) { + c = &s->ctls[CTL_PLAYBACK_VOLUME]; + if (c->type==2) { + if (get_volume_ops(elem, dir, channel, &volume)) + goto _err; + if ((err = snd_hctl_elem_get_db_gain(c->elem, volume, &db_gain)) < 0) + goto _err; + } else + goto _err; + } else if (dir==SM_CAPT) { + c = &s->ctls[CTL_CAPTURE_VOLUME]; + if (c->type==2) { + if (get_volume_ops(elem, dir, channel, &volume)) + goto _err; + if ((err = snd_hctl_elem_get_db_gain(c->elem, volume, &db_gain)) < 0) + goto _err; + } else + goto _err; + + } else + goto _err; + err=0; + *value=db_gain; +_err: + //if (err) printf("get_dB_ops:err=%d\n",err); + return err; } static int get_switch_ops(snd_mixer_elem_t *elem, int dir,
_______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.sourceforge.net/lists/listinfo/alsa-devel