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; nmessage[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),