This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / sound / core / control_compat.c
1 /*
2  * compat ioctls for control API
3  *
4  *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19  */
20
21 /* this file included from control.c */
22
23 #include <linux/compat.h>
24
25 struct sndrv_ctl_elem_list32 {
26         u32 offset;
27         u32 space;
28         u32 used;
29         u32 count;
30         u32 pids;
31         unsigned char reserved[50];
32 } /* don't set packed attribute here */;
33
34 static int snd_ctl_elem_list_compat(snd_card_t *card, struct sndrv_ctl_elem_list32 __user *data32)
35 {
36         struct sndrv_ctl_elem_list __user *data;
37         compat_caddr_t ptr;
38         int err;
39
40         data = compat_alloc_user_space(sizeof(*data));
41
42         /* offset, space, used, count */
43         if (copy_in_user(data, data32, 4 * sizeof(u32)))
44                 return -EFAULT;
45         /* pids */
46         if (get_user(ptr, &data32->pids) ||
47             put_user(compat_ptr(ptr), &data->pids))
48                 return -EFAULT;
49         err = snd_ctl_elem_list(card, data);
50         if (err < 0)
51                 return err;
52         /* copy the result */
53         if (copy_in_user(data32, data, 4 * sizeof(u32)))
54                 return -EFAULT;
55         return 0;
56 }
57
58 /*
59  * control element info
60  * it uses union, so the things are not easy..
61  */
62
63 struct sndrv_ctl_elem_info32 {
64         struct sndrv_ctl_elem_id id; // the size of struct is same
65         s32 type;
66         u32 access;
67         u32 count;
68         s32 owner;
69         union {
70                 struct {
71                         s32 min;
72                         s32 max;
73                         s32 step;
74                 } integer;
75                 struct {
76                         u64 min;
77                         u64 max;
78                         u64 step;
79                 } integer64;
80                 struct {
81                         u32 items;
82                         u32 item;
83                         char name[64];
84                 } enumerated;
85                 unsigned char reserved[128];
86         } value;
87         unsigned char reserved[64];
88 } __attribute__((packed));
89
90 static int snd_ctl_elem_info_compat(snd_ctl_file_t *ctl, struct sndrv_ctl_elem_info32 __user *data32)
91 {
92         struct sndrv_ctl_elem_info *data;
93         int err;
94
95         data = kcalloc(1, sizeof(*data), GFP_KERNEL);
96         if (! data)
97                 return -ENOMEM;
98
99         err = -EFAULT;
100         /* copy id */
101         if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
102                 goto error;
103         /* we need to copy the item index.
104          * hope this doesn't break anything..
105          */
106         if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
107                 goto error;
108         err = snd_ctl_elem_info(ctl, data);
109         if (err < 0)
110                 goto error;
111         /* restore info to 32bit */
112         err = -EFAULT;
113         /* id, type, access, count */
114         if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
115             copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
116                 goto error;
117         if (put_user(data->owner, &data32->owner))
118                 goto error;
119         switch (data->type) {
120         case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
121         case SNDRV_CTL_ELEM_TYPE_INTEGER:
122                 if (put_user(data->value.integer.min, &data32->value.integer.min) ||
123                     put_user(data->value.integer.max, &data32->value.integer.max) ||
124                     put_user(data->value.integer.step, &data32->value.integer.step))
125                         goto error;
126                 break;
127         case SNDRV_CTL_ELEM_TYPE_INTEGER64:
128                 if (copy_to_user(&data32->value.integer64,
129                                  &data->value.integer64,
130                                  sizeof(data->value.integer64)))
131                         goto error;
132                 break;
133         case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
134                 if (copy_to_user(&data32->value.enumerated,
135                                  &data->value.enumerated,
136                                  sizeof(data->value.enumerated)))
137                         goto error;
138                 break;
139         default:
140                 break;
141         }
142         err = 0;
143  error:
144         kfree(data);
145         return err;
146 }
147
148 /* read / write */
149 struct sndrv_ctl_elem_value32 {
150         struct sndrv_ctl_elem_id id;
151         unsigned int indirect;  /* bit-field causes misalignment */
152         union {
153                 s32 integer[128];
154                 unsigned char data[512];
155 #ifndef CONFIG_X86_64
156                 s64 integer64[64];
157 #endif
158         } value;
159         unsigned char reserved[128];
160 };
161
162
163 /* get the value type and count of the control */
164 static int get_ctl_type(snd_card_t *card, snd_ctl_elem_id_t *id, int *countp)
165 {
166         snd_kcontrol_t *kctl;
167         snd_ctl_elem_info_t info;
168         int err;
169
170         down_read(&card->controls_rwsem);
171         kctl = snd_ctl_find_id(card, id);
172         if (! kctl) {
173                 up_read(&card->controls_rwsem);
174                 return -ENXIO;
175         }
176         info.id = *id;
177         err = kctl->info(kctl, &info);
178         up_read(&card->controls_rwsem);
179         if (err >= 0) {
180                 err = info.type;
181                 *countp = info.count;
182         }
183         return err;
184 }
185
186 static int get_elem_size(int type, int count)
187 {
188         switch (type) {
189         case SNDRV_CTL_ELEM_TYPE_INTEGER64:
190                 return sizeof(s64) * count;
191         case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
192                 return sizeof(int) * count;
193         case SNDRV_CTL_ELEM_TYPE_BYTES:
194                 return 512;
195         case SNDRV_CTL_ELEM_TYPE_IEC958:
196                 return sizeof(struct sndrv_aes_iec958);
197         default:
198                 return -1;
199         }
200 }
201
202 static int copy_ctl_value_from_user(snd_card_t *card,
203                                     struct sndrv_ctl_elem_value *data,
204                                     struct sndrv_ctl_elem_value32 __user *data32,
205                                     int *typep, int *countp)
206 {
207         int i, type, count, size;
208         unsigned int indirect;
209
210         if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
211                 return -EFAULT;
212         if (get_user(indirect, &data32->indirect))
213                 return -EFAULT;
214         if (indirect)
215                 return -EINVAL;
216         type = get_ctl_type(card, &data->id, &count);
217         if (type < 0)
218                 return type;
219
220         if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
221             type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
222                 for (i = 0; i < count; i++) {
223                         int val;
224                         if (get_user(val, &data32->value.integer[i]))
225                                 return -EFAULT;
226                         data->value.integer.value[i] = val;
227                 }
228         } else {
229                 size = get_elem_size(type, count);
230                 if (size < 0) {
231                         printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
232                         return -EINVAL;
233                 }
234                 if (copy_from_user(data->value.bytes.data,
235                                    data32->value.data, size))
236                         return -EFAULT;
237         }
238
239         *typep = type;
240         *countp = count;
241         return 0;
242 }
243
244 /* restore the value to 32bit */
245 static int copy_ctl_value_to_user(struct sndrv_ctl_elem_value32 __user *data32,
246                                   struct sndrv_ctl_elem_value *data,
247                                   int type, int count)
248 {
249         int i, size;
250
251         if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
252             type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
253                 for (i = 0; i < count; i++) {
254                         int val;
255                         val = data->value.integer.value[i];
256                         if (put_user(val, &data32->value.integer[i]))
257                                 return -EFAULT;
258                 }
259         } else {
260                 size = get_elem_size(type, count);
261                 if (copy_to_user(data32->value.data,
262                                  data->value.bytes.data, size))
263                         return -EFAULT;
264         }
265         return 0;
266 }
267
268 static int snd_ctl_elem_read_user_compat(snd_card_t *card, 
269                                          struct sndrv_ctl_elem_value32 __user *data32)
270 {
271         struct sndrv_ctl_elem_value *data;
272         int err, type, count;
273
274         data = kcalloc(1, sizeof(*data), GFP_KERNEL);
275         if (data == NULL)
276                 return -ENOMEM;
277
278         if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
279                 goto error;
280         if ((err = snd_ctl_elem_read(card, data)) < 0)
281                 goto error;
282         err = copy_ctl_value_to_user(data32, data, type, count);
283  error:
284         kfree(data);
285         return err;
286 }
287
288 static int snd_ctl_elem_write_user_compat(snd_ctl_file_t *file,
289                                           struct sndrv_ctl_elem_value32 __user *data32)
290 {
291         struct sndrv_ctl_elem_value *data;
292         int err, type, count;
293
294         data = kcalloc(1, sizeof(*data), GFP_KERNEL);
295         if (data == NULL)
296                 return -ENOMEM;
297
298         if ((err = copy_ctl_value_from_user(file->card, data, data32, &type, &count)) < 0)
299                 goto error;
300         if ((err = snd_ctl_elem_write(file->card, file, data)) < 0)
301                 goto error;
302         err = copy_ctl_value_to_user(data32, data, type, count);
303  error:
304         kfree(data);
305         return err;
306 }
307
308 /* add or replace a user control */
309 static int snd_ctl_elem_add_compat(snd_ctl_file_t *file,
310                                    struct sndrv_ctl_elem_info32 __user *data32,
311                                    int replace)
312 {
313         struct sndrv_ctl_elem_info *data;
314         int err;
315
316         data = kcalloc(1, sizeof(*data), GFP_KERNEL);
317         if (! data)
318                 return -ENOMEM;
319
320         err = -EFAULT;
321         /* id, type, access, count */ \
322         if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
323             copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
324                 goto error;
325         if (get_user(data->owner, &data32->owner) ||
326             get_user(data->type, &data32->type))
327                 goto error;
328         switch (data->type) {
329         case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
330         case SNDRV_CTL_ELEM_TYPE_INTEGER:
331                 if (get_user(data->value.integer.min, &data32->value.integer.min) ||
332                     get_user(data->value.integer.max, &data32->value.integer.max) ||
333                     get_user(data->value.integer.step, &data32->value.integer.step))
334                         goto error;
335                 break;
336         case SNDRV_CTL_ELEM_TYPE_INTEGER64:
337                 if (copy_from_user(&data->value.integer64,
338                                    &data32->value.integer64,
339                                    sizeof(data->value.integer64)))
340                         goto error;
341                 break;
342         case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
343                 if (copy_from_user(&data->value.enumerated,
344                                    &data32->value.enumerated,
345                                    sizeof(data->value.enumerated)))
346                         goto error;
347                 break;
348         default:
349                 break;
350         }
351         err = snd_ctl_elem_add(file, data, replace);
352  error:
353         kfree(data);
354         return err;
355 }  
356
357 enum {
358         SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32),
359         SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32),
360         SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32),
361         SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32),
362         SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct sndrv_ctl_elem_info32),
363         SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct sndrv_ctl_elem_info32),
364 };
365
366 static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
367 {
368         snd_ctl_file_t *ctl;
369         struct list_head *list;
370         void __user *argp = compat_ptr(arg);
371         int err;
372
373         ctl = file->private_data;
374         snd_assert(ctl && ctl->card, return -ENXIO);
375
376         switch (cmd) {
377         case SNDRV_CTL_IOCTL_PVERSION:
378         case SNDRV_CTL_IOCTL_CARD_INFO:
379         case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
380         case SNDRV_CTL_IOCTL_POWER:
381         case SNDRV_CTL_IOCTL_POWER_STATE:
382         case SNDRV_CTL_IOCTL_ELEM_LOCK:
383         case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
384                 return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
385         case SNDRV_CTL_IOCTL_ELEM_LIST32:
386                 return snd_ctl_elem_list_compat(ctl->card, argp);
387         case SNDRV_CTL_IOCTL_ELEM_INFO32:
388                 return snd_ctl_elem_info_compat(ctl, argp);
389         case SNDRV_CTL_IOCTL_ELEM_READ32:
390                 return snd_ctl_elem_read_user_compat(ctl->card, argp);
391         case SNDRV_CTL_IOCTL_ELEM_WRITE32:
392                 return snd_ctl_elem_write_user_compat(ctl, argp);
393         case SNDRV_CTL_IOCTL_ELEM_ADD32:
394                 return snd_ctl_elem_add_compat(ctl, argp, 0);
395         case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
396                 return snd_ctl_elem_add_compat(ctl, argp, 1);
397         }
398
399         down_read(&snd_ioctl_rwsem);
400         list_for_each(list, &snd_control_compat_ioctls) {
401                 snd_kctl_ioctl_t *p = list_entry(list, snd_kctl_ioctl_t, list);
402                 if (p->fioctl) {
403                         err = p->fioctl(ctl->card, ctl, cmd, arg);
404                         if (err != -ENOIOCTLCMD) {
405                                 up_read(&snd_ioctl_rwsem);
406                                 return err;
407                         }
408                 }
409         }
410         up_read(&snd_ioctl_rwsem);
411         return -ENOIOCTLCMD;
412 }