aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/asound.h10
-rw-r--r--include/sound/control.h16
-rw-r--r--sound/core/control.c139
3 files changed, 132 insertions, 33 deletions
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 76a20406bd18..c1621c650a9a 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -727,10 +727,15 @@ typedef int __bitwise snd_ctl_elem_iface_t;
727#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) 727#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1)
728#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) 728#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE)
729#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */ 729#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */
730#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<2) /* when was control changed */ 730#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<3) /* when was control changed */
731#define SNDRV_CTL_ELEM_ACCESS_TLV_READ (1<<4) /* TLV read is possible */
732#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE (1<<5) /* TLV write is possible */
733#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
734#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND (1<<6) /* TLV command is possible */
731#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */ 735#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */
732#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */ 736#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */
733#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */ 737#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */
738#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK (1<<28) /* kernel use a TLV callback */
734#define SNDRV_CTL_ELEM_ACCESS_USER (1<<29) /* user space element */ 739#define SNDRV_CTL_ELEM_ACCESS_USER (1<<29) /* user space element */
735#define SNDRV_CTL_ELEM_ACCESS_DINDIRECT (1<<30) /* indirect access for matrix dimensions in the info structure */ 740#define SNDRV_CTL_ELEM_ACCESS_DINDIRECT (1<<30) /* indirect access for matrix dimensions in the info structure */
736#define SNDRV_CTL_ELEM_ACCESS_INDIRECT (1<<31) /* indirect access for element value in the value structure */ 741#define SNDRV_CTL_ELEM_ACCESS_INDIRECT (1<<31) /* indirect access for element value in the value structure */
@@ -838,6 +843,8 @@ enum {
838 SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info), 843 SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info),
839 SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id), 844 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), 845 SNDRV_CTL_IOCTL_TLV_READ = _IOWR('U', 0x1a, struct snd_ctl_tlv),
846 SNDRV_CTL_IOCTL_TLV_WRITE = _IOWR('U', 0x1b, struct snd_ctl_tlv),
847 SNDRV_CTL_IOCTL_TLV_COMMAND = _IOWR('U', 0x1c, struct snd_ctl_tlv),
841 SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), 848 SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int),
842 SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info), 849 SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info),
843 SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), 850 SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int),
@@ -862,6 +869,7 @@ enum sndrv_ctl_event_type {
862#define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) /* element value was changed */ 869#define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) /* element value was changed */
863#define SNDRV_CTL_EVENT_MASK_INFO (1<<1) /* element info was changed */ 870#define SNDRV_CTL_EVENT_MASK_INFO (1<<1) /* element info was changed */
864#define SNDRV_CTL_EVENT_MASK_ADD (1<<2) /* element was added */ 871#define SNDRV_CTL_EVENT_MASK_ADD (1<<2) /* element was added */
872#define SNDRV_CTL_EVENT_MASK_TLV (1<<3) /* element TLV tree was changed */
865#define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) /* element was removed */ 873#define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) /* element was removed */
866 874
867struct snd_ctl_event { 875struct snd_ctl_event {
diff --git a/include/sound/control.h b/include/sound/control.h
index a93a58d0e688..e3905c5a0950 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -30,6 +30,11 @@ struct snd_kcontrol;
30typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo); 30typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo);
31typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); 31typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
32typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); 32typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
33typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol,
34 int op_flag, /* 0=read,1=write,-1=command */
35 unsigned int size,
36 unsigned int __user *tlv);
37
33 38
34struct snd_kcontrol_new { 39struct snd_kcontrol_new {
35 snd_ctl_elem_iface_t iface; /* interface identifier */ 40 snd_ctl_elem_iface_t iface; /* interface identifier */
@@ -42,7 +47,10 @@ struct snd_kcontrol_new {
42 snd_kcontrol_info_t *info; 47 snd_kcontrol_info_t *info;
43 snd_kcontrol_get_t *get; 48 snd_kcontrol_get_t *get;
44 snd_kcontrol_put_t *put; 49 snd_kcontrol_put_t *put;
45 unsigned int *tlv; 50 union {
51 snd_kcontrol_tlv_rw_t *c;
52 unsigned int *p;
53 } tlv;
46 unsigned long private_value; 54 unsigned long private_value;
47}; 55};
48 56
@@ -59,7 +67,11 @@ struct snd_kcontrol {
59 snd_kcontrol_info_t *info; 67 snd_kcontrol_info_t *info;
60 snd_kcontrol_get_t *get; 68 snd_kcontrol_get_t *get;
61 snd_kcontrol_put_t *put; 69 snd_kcontrol_put_t *put;
62 unsigned int *tlv; 70 snd_kcontrol_tlv_rw_t *tlv_rw;
71 union {
72 snd_kcontrol_tlv_rw_t *c;
73 unsigned int *p;
74 } tlv;
63 unsigned long private_value; 75 unsigned long private_value;
64 void *private_data; 76 void *private_data;
65 void (*private_free)(struct snd_kcontrol *kcontrol); 77 void (*private_free)(struct snd_kcontrol *kcontrol);
diff --git a/sound/core/control.c b/sound/core/control.c
index f0c7272a2d48..31ad58154c06 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -236,12 +236,16 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
236 kctl.id.index = ncontrol->index; 236 kctl.id.index = ncontrol->index;
237 kctl.count = ncontrol->count ? ncontrol->count : 1; 237 kctl.count = ncontrol->count ? ncontrol->count : 1;
238 access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : 238 access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
239 (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE| 239 (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
240 SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); 240 SNDRV_CTL_ELEM_ACCESS_INACTIVE|
241 SNDRV_CTL_ELEM_ACCESS_DINDIRECT|
242 SNDRV_CTL_ELEM_ACCESS_INDIRECT|
243 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
244 SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
241 kctl.info = ncontrol->info; 245 kctl.info = ncontrol->info;
242 kctl.get = ncontrol->get; 246 kctl.get = ncontrol->get;
243 kctl.put = ncontrol->put; 247 kctl.put = ncontrol->put;
244 kctl.tlv = ncontrol->tlv; 248 kctl.tlv.p = ncontrol->tlv.p;
245 kctl.private_value = ncontrol->private_value; 249 kctl.private_value = ncontrol->private_value;
246 kctl.private_data = private_data; 250 kctl.private_data = private_data;
247 return snd_ctl_new(&kctl, access); 251 return snd_ctl_new(&kctl, access);
@@ -883,6 +887,8 @@ struct user_element {
883 struct snd_ctl_elem_info info; 887 struct snd_ctl_elem_info info;
884 void *elem_data; /* element data */ 888 void *elem_data; /* element data */
885 unsigned long elem_data_size; /* size of element data in bytes */ 889 unsigned long elem_data_size; /* size of element data in bytes */
890 void *tlv_data; /* TLV data */
891 unsigned long tlv_data_size; /* TLV data size */
886 void *priv_data; /* private data (like strings for enumerated type) */ 892 void *priv_data; /* private data (like strings for enumerated type) */
887 unsigned long priv_data_size; /* size of private data in bytes */ 893 unsigned long priv_data_size; /* size of private data in bytes */
888}; 894};
@@ -917,9 +923,46 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
917 return change; 923 return change;
918} 924}
919 925
926static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
927 int op_flag,
928 unsigned int size,
929 unsigned int __user *tlv)
930{
931 struct user_element *ue = kcontrol->private_data;
932 int change = 0;
933 void *new_data;
934
935 if (op_flag > 0) {
936 if (size > 1024 * 128) /* sane value */
937 return -EINVAL;
938 new_data = kmalloc(size, GFP_KERNEL);
939 if (new_data == NULL)
940 return -ENOMEM;
941 if (copy_from_user(new_data, tlv, size)) {
942 kfree(new_data);
943 return -EFAULT;
944 }
945 change = ue->tlv_data_size != size;
946 if (!change)
947 change = memcmp(ue->tlv_data, new_data, size);
948 kfree(ue->tlv_data);
949 ue->tlv_data = new_data;
950 ue->tlv_data_size = size;
951 } else {
952 if (size < ue->tlv_data_size)
953 return -ENOSPC;
954 if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
955 return -EFAULT;
956 }
957 return change;
958}
959
920static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) 960static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
921{ 961{
922 kfree(kcontrol->private_data); 962 struct user_element *ue = kcontrol->private_data;
963 if (ue->tlv_data)
964 kfree(ue->tlv_data);
965 kfree(ue);
923} 966}
924 967
925static int snd_ctl_elem_add(struct snd_ctl_file *file, 968static int snd_ctl_elem_add(struct snd_ctl_file *file,
@@ -938,7 +981,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
938 return -EINVAL; 981 return -EINVAL;
939 access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : 982 access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
940 (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| 983 (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
941 SNDRV_CTL_ELEM_ACCESS_INACTIVE)); 984 SNDRV_CTL_ELEM_ACCESS_INACTIVE|
985 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
942 info->id.numid = 0; 986 info->id.numid = 0;
943 memset(&kctl, 0, sizeof(kctl)); 987 memset(&kctl, 0, sizeof(kctl));
944 down_write(&card->controls_rwsem); 988 down_write(&card->controls_rwsem);
@@ -964,6 +1008,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
964 kctl.get = snd_ctl_elem_user_get; 1008 kctl.get = snd_ctl_elem_user_get;
965 if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) 1009 if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
966 kctl.put = snd_ctl_elem_user_put; 1010 kctl.put = snd_ctl_elem_user_put;
1011 if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
1012 kctl.tlv.c = snd_ctl_elem_user_tlv;
1013 access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
1014 }
967 switch (info->type) { 1015 switch (info->type) {
968 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 1016 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
969 private_size = sizeof(char); 1017 private_size = sizeof(char);
@@ -1068,38 +1116,65 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
1068 return 0; 1116 return 0;
1069} 1117}
1070 1118
1071static int snd_ctl_tlv_read(struct snd_card *card, 1119static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
1072 struct snd_ctl_tlv __user *_tlv) 1120 struct snd_ctl_tlv __user *_tlv,
1121 int op_flag)
1073{ 1122{
1123 struct snd_card *card = file->card;
1074 struct snd_ctl_tlv tlv; 1124 struct snd_ctl_tlv tlv;
1075 struct snd_kcontrol *kctl; 1125 struct snd_kcontrol *kctl;
1126 struct snd_kcontrol_volatile *vd;
1076 unsigned int len; 1127 unsigned int len;
1077 int err = 0; 1128 int err = 0;
1078 1129
1079 if (copy_from_user(&tlv, _tlv, sizeof(tlv))) 1130 if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
1080 return -EFAULT; 1131 return -EFAULT;
1081 if (tlv.length < sizeof(unsigned int) * 3) 1132 if (tlv.length < sizeof(unsigned int) * 3)
1082 return -EINVAL; 1133 return -EINVAL;
1083 down_read(&card->controls_rwsem); 1134 down_read(&card->controls_rwsem);
1084 kctl = snd_ctl_find_numid(card, tlv.numid); 1135 kctl = snd_ctl_find_numid(card, tlv.numid);
1085 if (kctl == NULL) { 1136 if (kctl == NULL) {
1086 err = -ENOENT; 1137 err = -ENOENT;
1087 goto __kctl_end; 1138 goto __kctl_end;
1088 } 1139 }
1089 if (kctl->tlv == NULL) { 1140 if (kctl->tlv.p == NULL) {
1090 err = -ENXIO; 1141 err = -ENXIO;
1091 goto __kctl_end; 1142 goto __kctl_end;
1092 } 1143 }
1093 len = kctl->tlv[1] + 2 * sizeof(unsigned int); 1144 vd = &kctl->vd[tlv.numid - kctl->id.numid];
1094 if (tlv.length < len) { 1145 if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
1095 err = -ENOMEM; 1146 (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
1096 goto __kctl_end; 1147 (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
1097 } 1148 err = -ENXIO;
1098 if (copy_to_user(_tlv->tlv, kctl->tlv, len)) 1149 goto __kctl_end;
1099 err = -EFAULT; 1150 }
1151 if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
1152 if (file && vd->owner != NULL && vd->owner != file) {
1153 err = -EPERM;
1154 goto __kctl_end;
1155 }
1156 err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
1157 if (err > 0) {
1158 up_read(&card->controls_rwsem);
1159 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
1160 return 0;
1161 }
1162 } else {
1163 if (op_flag) {
1164 err = -ENXIO;
1165 goto __kctl_end;
1166 }
1167 len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
1168 if (tlv.length < len) {
1169 err = -ENOMEM;
1170 goto __kctl_end;
1171 }
1172 if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))
1173 err = -EFAULT;
1174 }
1100 __kctl_end: 1175 __kctl_end:
1101 up_read(&card->controls_rwsem); 1176 up_read(&card->controls_rwsem);
1102 return err; 1177 return err;
1103} 1178}
1104 1179
1105static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 1180static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -1140,8 +1215,12 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
1140 return snd_ctl_elem_remove(ctl, argp); 1215 return snd_ctl_elem_remove(ctl, argp);
1141 case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: 1216 case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
1142 return snd_ctl_subscribe_events(ctl, ip); 1217 return snd_ctl_subscribe_events(ctl, ip);
1143 case SNDRV_CTL_IOCTL_TLV_READ: 1218 case SNDRV_CTL_IOCTL_TLV_READ:
1144 return snd_ctl_tlv_read(card, argp); 1219 return snd_ctl_tlv_ioctl(ctl, argp, 0);
1220 case SNDRV_CTL_IOCTL_TLV_WRITE:
1221 return snd_ctl_tlv_ioctl(ctl, argp, 1);
1222 case SNDRV_CTL_IOCTL_TLV_COMMAND:
1223 return snd_ctl_tlv_ioctl(ctl, argp, -1);
1145 case SNDRV_CTL_IOCTL_POWER: 1224 case SNDRV_CTL_IOCTL_POWER:
1146 return -ENOPROTOOPT; 1225 return -ENOPROTOOPT;
1147 case SNDRV_CTL_IOCTL_POWER_STATE: 1226 case SNDRV_CTL_IOCTL_POWER_STATE: