diff options
author | Jaroslav Kysela <perex@suse.cz> | 2006-06-01 12:34:01 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2006-09-23 04:36:42 -0400 |
commit | 42750b04c5baa7c5ffdf0a8be2b9b320efdf069f (patch) | |
tree | 52aea8f1eeb44405b67bc5b381cce6bc20e2bff6 | |
parent | 3eeab61aa3ddd3c0bedb7449ada1599de22fdb5a (diff) |
[ALSA] Control API - TLV implementation for additional information like dB scale
This patch implements a TLV mechanism to transfer an additional information
like dB scale to the user space. The types might be extended in future.
Acked-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
-rw-r--r-- | include/sound/asound.h | 9 | ||||
-rw-r--r-- | include/sound/control.h | 2 | ||||
-rw-r--r-- | include/sound/tlv.h | 43 | ||||
-rw-r--r-- | sound/core/control.c | 41 | ||||
-rw-r--r-- | sound/pci/ca0106/ca0106_mixer.c | 4 |
5 files changed, 96 insertions, 3 deletions
diff --git a/include/sound/asound.h b/include/sound/asound.h index 41885f48ad91..76a20406bd18 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h | |||
@@ -688,7 +688,7 @@ struct snd_timer_tread { | |||
688 | * * | 688 | * * |
689 | ****************************************************************************/ | 689 | ****************************************************************************/ |
690 | 690 | ||
691 | #define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 3) | 691 | #define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 4) |
692 | 692 | ||
693 | struct snd_ctl_card_info { | 693 | struct snd_ctl_card_info { |
694 | int card; /* card number */ | 694 | int card; /* card number */ |
@@ -818,6 +818,12 @@ struct snd_ctl_elem_value { | |||
818 | unsigned char reserved[128-sizeof(struct timespec)]; | 818 | unsigned char reserved[128-sizeof(struct timespec)]; |
819 | }; | 819 | }; |
820 | 820 | ||
821 | struct snd_ctl_tlv { | ||
822 | unsigned int numid; /* control element numeric identification */ | ||
823 | unsigned int length; /* in bytes aligned to 4 */ | ||
824 | unsigned int tlv[0]; /* first TLV */ | ||
825 | }; | ||
826 | |||
821 | enum { | 827 | enum { |
822 | SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), | 828 | SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), |
823 | SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info), | 829 | SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info), |
@@ -831,6 +837,7 @@ enum { | |||
831 | SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct snd_ctl_elem_info), | 837 | SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct snd_ctl_elem_info), |
832 | SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info), | 838 | SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info), |
833 | SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id), | 839 | SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id), |
840 | SNDRV_CTL_IOCTL_TLV_READ = _IOWR('U', 0x1a, struct snd_ctl_tlv), | ||
834 | SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), | 841 | SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), |
835 | SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info), | 842 | SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info), |
836 | SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), | 843 | SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), |
diff --git a/include/sound/control.h b/include/sound/control.h index 2489b1eb0110..a93a58d0e688 100644 --- a/include/sound/control.h +++ b/include/sound/control.h | |||
@@ -42,6 +42,7 @@ struct snd_kcontrol_new { | |||
42 | snd_kcontrol_info_t *info; | 42 | snd_kcontrol_info_t *info; |
43 | snd_kcontrol_get_t *get; | 43 | snd_kcontrol_get_t *get; |
44 | snd_kcontrol_put_t *put; | 44 | snd_kcontrol_put_t *put; |
45 | unsigned int *tlv; | ||
45 | unsigned long private_value; | 46 | unsigned long private_value; |
46 | }; | 47 | }; |
47 | 48 | ||
@@ -58,6 +59,7 @@ struct snd_kcontrol { | |||
58 | snd_kcontrol_info_t *info; | 59 | snd_kcontrol_info_t *info; |
59 | snd_kcontrol_get_t *get; | 60 | snd_kcontrol_get_t *get; |
60 | snd_kcontrol_put_t *put; | 61 | snd_kcontrol_put_t *put; |
62 | unsigned int *tlv; | ||
61 | unsigned long private_value; | 63 | unsigned long private_value; |
62 | void *private_data; | 64 | void *private_data; |
63 | void (*private_free)(struct snd_kcontrol *kcontrol); | 65 | void (*private_free)(struct snd_kcontrol *kcontrol); |
diff --git a/include/sound/tlv.h b/include/sound/tlv.h new file mode 100644 index 000000000000..b826e1df1da6 --- /dev/null +++ b/include/sound/tlv.h | |||
@@ -0,0 +1,43 @@ | |||
1 | #ifndef __SOUND_TLV_H | ||
2 | #define __SOUND_TLV_H | ||
3 | |||
4 | /* | ||
5 | * Advanced Linux Sound Architecture - ALSA - Driver | ||
6 | * Copyright (c) 2006 by Jaroslav Kysela <perex@suse.cz> | ||
7 | * | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | /* | ||
26 | * TLV structure is right behind the struct snd_ctl_tlv: | ||
27 | * unsigned int type - see SNDRV_CTL_TLVT_* | ||
28 | * unsigned int length | ||
29 | * .... data aligned to sizeof(unsigned int), use | ||
30 | * block_length = (length + (sizeof(unsigned int) - 1)) & | ||
31 | * ~(sizeof(unsigned int) - 1)) .... | ||
32 | */ | ||
33 | |||
34 | #define SNDRV_CTL_TLVT_CONTAINER 0 /* one level down - group of TLVs */ | ||
35 | #define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */ | ||
36 | |||
37 | #define DECLARE_TLV_DB_SCALE(name, min, step, mute) \ | ||
38 | unsigned int name[] = { \ | ||
39 | SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \ | ||
40 | (min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0) \ | ||
41 | } | ||
42 | |||
43 | #endif /* __SOUND_TLV_H */ | ||
diff --git a/sound/core/control.c b/sound/core/control.c index bb397eaa7187..e9c8854d2f7b 100644 --- a/sound/core/control.c +++ b/sound/core/control.c | |||
@@ -241,6 +241,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, | |||
241 | kctl.info = ncontrol->info; | 241 | kctl.info = ncontrol->info; |
242 | kctl.get = ncontrol->get; | 242 | kctl.get = ncontrol->get; |
243 | kctl.put = ncontrol->put; | 243 | kctl.put = ncontrol->put; |
244 | kctl.tlv = ncontrol->tlv; | ||
244 | kctl.private_value = ncontrol->private_value; | 245 | kctl.private_value = ncontrol->private_value; |
245 | kctl.private_data = private_data; | 246 | kctl.private_data = private_data; |
246 | return snd_ctl_new(&kctl, access); | 247 | return snd_ctl_new(&kctl, access); |
@@ -1067,6 +1068,40 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr) | |||
1067 | return 0; | 1068 | return 0; |
1068 | } | 1069 | } |
1069 | 1070 | ||
1071 | static int snd_ctl_tlv_read(struct snd_card *card, | ||
1072 | struct snd_ctl_tlv __user *_tlv) | ||
1073 | { | ||
1074 | struct snd_ctl_tlv tlv; | ||
1075 | struct snd_kcontrol *kctl; | ||
1076 | unsigned int len; | ||
1077 | int err = 0; | ||
1078 | |||
1079 | if (copy_from_user(&tlv, _tlv, sizeof(tlv))) | ||
1080 | return -EFAULT; | ||
1081 | if (tlv.length < sizeof(unsigned int) * 3) | ||
1082 | return -EINVAL; | ||
1083 | down_read(&card->controls_rwsem); | ||
1084 | kctl = snd_ctl_find_numid(card, tlv.numid); | ||
1085 | if (kctl == NULL) { | ||
1086 | err = -ENOENT; | ||
1087 | goto __kctl_end; | ||
1088 | } | ||
1089 | if (kctl->tlv == NULL) { | ||
1090 | err = -ENXIO; | ||
1091 | goto __kctl_end; | ||
1092 | } | ||
1093 | len = kctl->tlv[1] + 2 * sizeof(unsigned int); | ||
1094 | if (tlv.length < len) { | ||
1095 | err = -ENOMEM; | ||
1096 | goto __kctl_end; | ||
1097 | } | ||
1098 | if (copy_to_user(_tlv->tlv, kctl->tlv, len)) | ||
1099 | err = -EFAULT; | ||
1100 | __kctl_end: | ||
1101 | up_read(&card->controls_rwsem); | ||
1102 | return err; | ||
1103 | } | ||
1104 | |||
1070 | static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 1105 | static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
1071 | { | 1106 | { |
1072 | struct snd_ctl_file *ctl; | 1107 | struct snd_ctl_file *ctl; |
@@ -1086,11 +1121,11 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg | |||
1086 | case SNDRV_CTL_IOCTL_CARD_INFO: | 1121 | case SNDRV_CTL_IOCTL_CARD_INFO: |
1087 | return snd_ctl_card_info(card, ctl, cmd, argp); | 1122 | return snd_ctl_card_info(card, ctl, cmd, argp); |
1088 | case SNDRV_CTL_IOCTL_ELEM_LIST: | 1123 | case SNDRV_CTL_IOCTL_ELEM_LIST: |
1089 | return snd_ctl_elem_list(ctl->card, argp); | 1124 | return snd_ctl_elem_list(card, argp); |
1090 | case SNDRV_CTL_IOCTL_ELEM_INFO: | 1125 | case SNDRV_CTL_IOCTL_ELEM_INFO: |
1091 | return snd_ctl_elem_info_user(ctl, argp); | 1126 | return snd_ctl_elem_info_user(ctl, argp); |
1092 | case SNDRV_CTL_IOCTL_ELEM_READ: | 1127 | case SNDRV_CTL_IOCTL_ELEM_READ: |
1093 | return snd_ctl_elem_read_user(ctl->card, argp); | 1128 | return snd_ctl_elem_read_user(card, argp); |
1094 | case SNDRV_CTL_IOCTL_ELEM_WRITE: | 1129 | case SNDRV_CTL_IOCTL_ELEM_WRITE: |
1095 | return snd_ctl_elem_write_user(ctl, argp); | 1130 | return snd_ctl_elem_write_user(ctl, argp); |
1096 | case SNDRV_CTL_IOCTL_ELEM_LOCK: | 1131 | case SNDRV_CTL_IOCTL_ELEM_LOCK: |
@@ -1105,6 +1140,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg | |||
1105 | return snd_ctl_elem_remove(ctl, argp); | 1140 | return snd_ctl_elem_remove(ctl, argp); |
1106 | case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: | 1141 | case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: |
1107 | return snd_ctl_subscribe_events(ctl, ip); | 1142 | return snd_ctl_subscribe_events(ctl, ip); |
1143 | case SNDRV_CTL_IOCTL_TLV_READ: | ||
1144 | return snd_ctl_tlv_read(card, argp); | ||
1108 | case SNDRV_CTL_IOCTL_POWER: | 1145 | case SNDRV_CTL_IOCTL_POWER: |
1109 | return -ENOPROTOOPT; | 1146 | return -ENOPROTOOPT; |
1110 | case SNDRV_CTL_IOCTL_POWER_STATE: | 1147 | case SNDRV_CTL_IOCTL_POWER_STATE: |
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 146eed70dce6..35309b3ed8c0 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c | |||
@@ -70,9 +70,12 @@ | |||
70 | #include <sound/pcm.h> | 70 | #include <sound/pcm.h> |
71 | #include <sound/ac97_codec.h> | 71 | #include <sound/ac97_codec.h> |
72 | #include <sound/info.h> | 72 | #include <sound/info.h> |
73 | #include <sound/tlv.h> | ||
73 | 74 | ||
74 | #include "ca0106.h" | 75 | #include "ca0106.h" |
75 | 76 | ||
77 | static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale, -5150, 75, 1); | ||
78 | |||
76 | static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol, | 79 | static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol, |
77 | struct snd_ctl_elem_info *uinfo) | 80 | struct snd_ctl_elem_info *uinfo) |
78 | { | 81 | { |
@@ -472,6 +475,7 @@ static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, | |||
472 | .info = snd_ca0106_volume_info, \ | 475 | .info = snd_ca0106_volume_info, \ |
473 | .get = snd_ca0106_volume_get, \ | 476 | .get = snd_ca0106_volume_get, \ |
474 | .put = snd_ca0106_volume_put, \ | 477 | .put = snd_ca0106_volume_put, \ |
478 | .tlv = snd_ca0106_db_scale, \ | ||
475 | .private_value = ((chid) << 8) | (reg) \ | 479 | .private_value = ((chid) << 8) | (reg) \ |
476 | } | 480 | } |
477 | 481 | ||