Index: alsa-driver/alsa-kernel/core/control.c =================================================================== RCS file: /cvsroot/alsa/alsa-kernel/core/control.c,v retrieving revision 1.69 diff -u -r1.69 control.c --- alsa-driver/alsa-kernel/core/control.c 20 Nov 2005 14:06:59 -0000 1.69 +++ alsa-driver/alsa-kernel/core/control.c 15 Dec 2005 18:16:43 -0000 @@ -237,6 +237,7 @@ kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put; + kctl.db_scale = ncontrol->db_scale; kctl.private_value = ncontrol->private_value; kctl.private_data = private_data; return snd_ctl_new(&kctl, access); @@ -547,6 +548,138 @@ return 0; } +static int uint32_to_message(struct snd_ctl_misc *misc, unsigned int value) +{ + unsigned int pointer = misc->length; + /* If unsigned int is a 64bit value, we will just ignore the high 32bits. */ + misc->message[pointer++] = value & 0xff; + misc->message[pointer++] = (value >> 8) & 0xff; + misc->message[pointer++] = (value >> 16) & 0xff; + misc->message[pointer++] = (value >> 24) & 0xff; + misc->length = pointer; + return 0; +} + +static int message_to_uint32(struct snd_ctl_misc *misc, unsigned int *value) +{ + unsigned int pointer = misc->length; + unsigned int val; + val = misc->message[pointer++] ; + val |= ((misc->message[pointer++]) << 8); + val |= ((misc->message[pointer++]) << 16); + val |= ((misc->message[pointer++]) << 24); + misc->length = pointer; + *value = val; + 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 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) +{ + unsigned int pointer = 4; + unsigned int value = misc->length; + misc->message[pointer++] = value & 0xff; + misc->message[pointer++] = (value >> 8) & 0xff; + misc->message[pointer++] = (value >> 16) & 0xff; + misc->message[pointer++] = (value >> 24) & 0xff; + 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; + int result; + int err=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(sizeof(*misc), GFP_KERNEL); + if (misc == NULL) + return -ENOMEM; + if (copy_from_user(misc, _misc, sizeof(*misc))) { + result = -EFAULT; + goto exit_free; + } + 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); + /* FIXME: Do more sanity checks here */ + message_to_uint32(misc, &tlv_type); + switch (tlv_type) { + case SND_MISC_ELEM_NUMID: + message_to_uint32(misc, &tlv_length); + if (tlv_length != 4) { + err = -EINVAL; + goto exit_free; + } + message_to_uint32(misc, &tlv_value); + break; + default: + err = -EINVAL; + goto exit_free; + break; + } + break; + default: + err = -EINVAL; + goto exit_free; + } + numid=tlv_value; + down_read(&card->controls_rwsem); + kctl = snd_ctl_find_numid(card, numid); + if (kctl == NULL) { + result = -ENOENT; + snd_printk("exit_up1\n"); + goto exit_up; + } + if (kctl->db_scale == 0) { /* Value defaults to zero when not implemented. */ + err = -EINVAL; + goto exit_up; + } + misc->type=1; + misc->length=0; + container_open(misc, SND_MISC_DB_SCALE); + add_int32_tlv_to_container(misc, SND_MISC_ELEM_DB_SCALE, kctl->db_scale); + container_close(misc); + if (copy_to_user(_misc, misc, sizeof(*misc))) + result = -EFAULT; + +exit_up: + up_read(&card->controls_rwsem); +exit_free: + kfree(misc); +exit: + return result; +} + static int snd_ctl_elem_list(struct snd_card *card, struct snd_ctl_elem_list __user *_list) { @@ -1062,6 +1195,8 @@ 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: Index: alsa-driver/alsa-kernel/include/asound.h =================================================================== RCS file: /cvsroot/alsa/alsa-kernel/include/asound.h,v retrieving revision 1.57 diff -u -r1.57 asound.h --- alsa-driver/alsa-kernel/include/asound.h 17 Nov 2005 13:51:18 -0000 1.57 +++ alsa-driver/alsa-kernel/include/asound.h 15 Dec 2005 18:16:43 -0000 @@ -818,6 +818,18 @@ unsigned char reserved[128-sizeof(struct timespec)]; }; +struct snd_ctl_misc { + u32 type; + u32 length; + unsigned char message[2040]; +}; + +#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 +845,7 @@ 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), Index: alsa-driver/alsa-kernel/include/control.h =================================================================== RCS file: /cvsroot/alsa/alsa-kernel/include/control.h,v retrieving revision 1.14 diff -u -r1.14 control.h --- alsa-driver/alsa-kernel/include/control.h 17 Nov 2005 13:53:23 -0000 1.14 +++ alsa-driver/alsa-kernel/include/control.h 15 Dec 2005 18:16:43 -0000 @@ -42,6 +42,7 @@ snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + unsigned int db_scale; unsigned long private_value; }; @@ -58,6 +59,7 @@ snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + unsigned int db_scale; unsigned long private_value; void *private_data; void (*private_free)(struct snd_kcontrol *kcontrol); Index: alsa-driver/alsa-kernel/pci/ca0106/ca0106_mixer.c =================================================================== RCS file: /cvsroot/alsa/alsa-kernel/pci/ca0106/ca0106_mixer.c,v retrieving revision 1.9 diff -u -r1.9 ca0106_mixer.c --- alsa-driver/alsa-kernel/pci/ca0106/ca0106_mixer.c 17 Nov 2005 14:55:40 -0000 1.9 +++ alsa-driver/alsa-kernel/pci/ca0106/ca0106_mixer.c 15 Dec 2005 18:16:43 -0000 @@ -329,37 +329,38 @@ return 1; } -#define CA_VOLUME(xname,chid,reg) \ +#define CA_VOLUME(xname,chid,reg,db) \ { \ .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), \ + .db_scale = db \ } 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, 1), CA_VOLUME("Analog Rear Playback Volume", - CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2), + CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2, 1), CA_VOLUME("Analog Center/LFE Playback Volume", - CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2), + CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2, 1), CA_VOLUME("Analog Side Playback Volume", - CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2), + CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2, 1), CA_VOLUME("SPDIF Front Playback Volume", - CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1), + CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1, 1), CA_VOLUME("SPDIF Rear Playback Volume", - CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1), + CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1, 1), CA_VOLUME("SPDIF Center/LFE Playback Volume", - CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1), + CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1, 1), CA_VOLUME("SPDIF Unknown Playback Volume", - CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1), + CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1, 1), CA_VOLUME("CAPTURE feedback Playback Volume", - 1, CAPTURE_CONTROL), + 1, CAPTURE_CONTROL, 1), { .access = SNDRV_CTL_ELEM_ACCESS_READ,