ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / sound / core / ioctl32 / ioctl32.c
1 /*
2  *   32bit -> 64bit ioctl wrapper for control API
3  *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18  *
19  */
20
21 #include <sound/driver.h>
22 #include <linux/sched.h>
23 #include <linux/smp_lock.h>
24 #include <linux/init.h>
25 #include <linux/time.h>
26 #include <linux/slab.h>
27 #include <linux/fs.h>
28 #include <sound/core.h>
29 #include <sound/control.h>
30 #include <asm/uaccess.h>
31 #include "ioctl32.h"
32
33 /*
34  * register/unregister mappers
35  * exported for other modules
36  */
37
38 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
39 MODULE_DESCRIPTION("ioctl32 wrapper for ALSA");
40 MODULE_LICENSE("GPL");
41
42 int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
43 int unregister_ioctl32_conversion(unsigned int cmd);
44
45
46 int snd_ioctl32_register(struct ioctl32_mapper *mappers)
47 {
48         int err;
49         struct ioctl32_mapper *m;
50
51         for (m = mappers; m->cmd; m++) {
52                 err = register_ioctl32_conversion(m->cmd, m->handler);
53                 if (err >= 0)
54                         m->registered++;
55         }
56         return 0;
57 }
58
59 void snd_ioctl32_unregister(struct ioctl32_mapper *mappers)
60 {
61         struct ioctl32_mapper *m;
62
63         for (m = mappers; m->cmd; m++) {
64                 if (m->registered) {
65                         unregister_ioctl32_conversion(m->cmd);
66                         m->registered = 0;
67                 }
68         }
69 }
70
71
72 /*
73  * compatible wrapper
74  */
75 int snd_ioctl32_compat(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *filp)
76 {
77         if (! filp->f_op || ! filp->f_op->ioctl)
78                 return -ENOTTY;
79         return filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
80 }
81
82
83 /*
84  * Controls
85  */
86
87 struct sndrv_ctl_elem_list32 {
88         u32 offset;
89         u32 space;
90         u32 used;
91         u32 count;
92         u32 pids;
93         unsigned char reserved[50];
94 } /* don't set packed attribute here */;
95
96 #define CVT_sndrv_ctl_elem_list()\
97 {\
98         COPY(offset);\
99         COPY(space);\
100         COPY(used);\
101         COPY(count);\
102         CPTR(pids);\
103 }
104
105 static int _snd_ioctl32_ctl_elem_list(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
106 {
107         struct sndrv_ctl_elem_list32 data32;
108         struct sndrv_ctl_elem_list data;
109         mm_segment_t oldseg;
110         int err;
111
112         if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
113                 return -EFAULT;
114         memset(&data, 0, sizeof(data));
115         data.offset = data32.offset;
116         data.space = data32.space;
117         data.used = data32.used;
118         data.count = data32.count;
119         data.pids = A(data32.pids);
120         oldseg = get_fs();
121         set_fs(KERNEL_DS);
122         err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
123         set_fs(oldseg);
124         if (err < 0)
125                 return err;
126         /* copy the result */
127         data32.offset = data.offset;
128         data32.space = data.space;
129         data32.used = data.used;
130         data32.count = data.count;
131         //data.pids = data.pids;
132         if (copy_to_user((void*)arg, &data32, sizeof(data32)))
133                 return -EFAULT;
134         return 0;
135 }
136
137 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_list, ctl_elem_list, SNDRV_CTL_IOCTL_ELEM_LIST);
138
139 /*
140  * control element info
141  * it uses union, so the things are not easy..
142  */
143
144 struct sndrv_ctl_elem_info32 {
145         struct sndrv_ctl_elem_id id; // the size of struct is same
146         s32 type;
147         u32 access;
148         u32 count;
149         s32 owner;
150         union {
151                 struct {
152                         s32 min;
153                         s32 max;
154                         s32 step;
155                 } integer;
156                 struct {
157                         u64 min;
158                         u64 max;
159                         u64 step;
160                 } integer64;
161                 struct {
162                         u32 items;
163                         u32 item;
164                         char name[64];
165                 } enumerated;
166                 unsigned char reserved[128];
167         } value;
168         unsigned char reserved[64];
169 } __attribute__((packed));
170
171 static int _snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
172 {
173         struct sndrv_ctl_elem_info data;
174         struct sndrv_ctl_elem_info32 data32;
175         int err;
176         mm_segment_t oldseg;
177
178         if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
179                 return -EFAULT;
180         memset(&data, 0, sizeof(data));
181         data.id = data32.id;
182         /* we need to copy the item index.
183          * hope this doesn't break anything..
184          */
185         data.value.enumerated.item = data32.value.enumerated.item;
186         oldseg = get_fs();
187         set_fs(KERNEL_DS);
188         err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
189         set_fs(oldseg);
190         if (err < 0)
191                 return err;
192         /* restore info to 32bit */
193         data32.id = data.id;
194         data32.type = data.type;
195         data32.access = data.access;
196         data32.count = data.count;
197         data32.owner = data.owner;
198         switch (data.type) {
199         case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
200         case SNDRV_CTL_ELEM_TYPE_INTEGER:
201                 data32.value.integer.min = data.value.integer.min;
202                 data32.value.integer.max = data.value.integer.max;
203                 data32.value.integer.step = data.value.integer.step;
204                 break;
205         case SNDRV_CTL_ELEM_TYPE_INTEGER64:
206                 data32.value.integer64.min = data.value.integer64.min;
207                 data32.value.integer64.max = data.value.integer64.max;
208                 data32.value.integer64.step = data.value.integer64.step;
209                 break;
210         case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
211                 data32.value.enumerated.items = data.value.enumerated.items;
212                 data32.value.enumerated.item = data.value.enumerated.item;
213                 memcpy(data32.value.enumerated.name, data.value.enumerated.name,
214                        sizeof(data.value.enumerated.name));
215                 break;
216         default:
217                 break;
218         }
219         if (copy_to_user((void*)arg, &data32, sizeof(data32)))
220                 return -EFAULT;
221         return 0;
222 }
223
224 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_info, ctl_elem_info, SNDRV_CTL_IOCTL_ELEM_INFO);
225
226 struct sndrv_ctl_elem_value32 {
227         struct sndrv_ctl_elem_id id;
228         unsigned int indirect;  /* bit-field causes misalignment */
229         union {
230                 union {
231                         s32 value[128];
232                         u32 value_ptr;
233                 } integer;
234                 union {
235                         s64 value[64];
236                         u32 value_ptr;
237                 } integer64;
238                 union {
239                         u32 item[128];
240                         u32 item_ptr;
241                 } enumerated;
242                 union {
243                         unsigned char data[512];
244                         u32 data_ptr;
245                 } bytes;
246                 struct sndrv_aes_iec958 iec958;
247         } value;
248         unsigned char reserved[128];
249 } __attribute__((packed));
250
251
252 /* hmm, it's so hard to retrieve the value type from the control id.. */
253 static int get_ctl_type(struct file *file, snd_ctl_elem_id_t *id)
254 {
255         snd_ctl_file_t *ctl;
256         snd_kcontrol_t *kctl;
257         snd_ctl_elem_info_t info;
258         int err;
259
260         ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO);
261
262         down_read(&ctl->card->controls_rwsem);
263         kctl = snd_ctl_find_id(ctl->card, id);
264         if (! kctl) {
265                 up_read(&ctl->card->controls_rwsem);
266                 return -ENXIO;
267         }
268         info.id = *id;
269         err = kctl->info(kctl, &info);
270         up_read(&ctl->card->controls_rwsem);
271         if (err >= 0)
272                 err = info.type;
273         return err;
274 }
275
276
277 static int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
278 {
279         struct sndrv_ctl_elem_value *data;
280         struct sndrv_ctl_elem_value32 *data32;
281         int err, i;
282         int type;
283         mm_segment_t oldseg;
284
285         /* FIXME: check the sane ioctl.. */
286
287         data = kmalloc(sizeof(*data), GFP_KERNEL);
288         data32 = kmalloc(sizeof(*data32), GFP_KERNEL);
289         if (data == NULL || data32 == NULL) {
290                 err = -ENOMEM;
291                 goto __end;
292         }
293
294         if (copy_from_user(data32, (void*)arg, sizeof(*data32))) {
295                 err = -EFAULT;
296                 goto __end;
297         }
298         memset(data, 0, sizeof(*data));
299         data->id = data32->id;
300         data->indirect = data32->indirect;
301         if (data->indirect) /* FIXME: this is not correct for long arrays */
302                 data->value.integer.value_ptr = (void*)TO_PTR(data32->value.integer.value_ptr);
303         type = get_ctl_type(file, &data->id);
304         if (type < 0) {
305                 err = type;
306                 goto __end;
307         }
308         if (! data->indirect) {
309                 switch (type) {
310                 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
311                 case SNDRV_CTL_ELEM_TYPE_INTEGER:
312                         for (i = 0; i < 128; i++)
313                                 data->value.integer.value[i] = data32->value.integer.value[i];
314                         break;
315                 case SNDRV_CTL_ELEM_TYPE_INTEGER64:
316                         for (i = 0; i < 64; i++)
317                                 data->value.integer64.value[i] = data32->value.integer64.value[i];
318                         break;
319                 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
320                         for (i = 0; i < 128; i++)
321                                 data->value.enumerated.item[i] = data32->value.enumerated.item[i];
322                         break;
323                 case SNDRV_CTL_ELEM_TYPE_BYTES:
324                         memcpy(data->value.bytes.data, data32->value.bytes.data,
325                                sizeof(data->value.bytes.data));
326                         break;
327                 case SNDRV_CTL_ELEM_TYPE_IEC958:
328                         data->value.iec958 = data32->value.iec958;
329                         break;
330                 default:
331                         printk("unknown type %d\n", type);
332                         break;
333                 }
334         }
335
336         oldseg = get_fs();
337         set_fs(KERNEL_DS);
338         err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data);
339         set_fs(oldseg);
340         if (err < 0)
341                 goto __end;
342         /* restore info to 32bit */
343         if (! data->indirect) {
344                 switch (type) {
345                 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
346                 case SNDRV_CTL_ELEM_TYPE_INTEGER:
347                         for (i = 0; i < 128; i++)
348                                 data32->value.integer.value[i] = data->value.integer.value[i];
349                         break;
350                 case SNDRV_CTL_ELEM_TYPE_INTEGER64:
351                         for (i = 0; i < 64; i++)
352                                 data32->value.integer64.value[i] = data->value.integer64.value[i];
353                         break;
354                 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
355                         for (i = 0; i < 128; i++)
356                                 data32->value.enumerated.item[i] = data->value.enumerated.item[i];
357                         break;
358                 case SNDRV_CTL_ELEM_TYPE_BYTES:
359                         memcpy(data32->value.bytes.data, data->value.bytes.data,
360                                sizeof(data->value.bytes.data));
361                         break;
362                 case SNDRV_CTL_ELEM_TYPE_IEC958:
363                         data32->value.iec958 = data->value.iec958;
364                         break;
365                 default:
366                         break;
367                 }
368         }
369         err = 0;
370         if (copy_to_user((void*)arg, data32, sizeof(*data32)))
371                 err = -EFAULT;
372       __end:
373         if (data32)
374                 kfree(data32);
375         if (data)
376                 kfree(data);
377         return err;
378 }
379
380 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_read, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_READ);
381 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_write, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_WRITE);
382
383 /*
384  */
385
386 #define AP(x) snd_ioctl32_##x
387
388 enum {
389         SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32),
390         SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32),
391         SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32),
392         SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32),
393 };
394
395 static struct ioctl32_mapper control_mappers[] = {
396         /* controls (without rawmidi, hwdep, timer releated ones) */
397         MAP_COMPAT(SNDRV_CTL_IOCTL_PVERSION),
398         MAP_COMPAT(SNDRV_CTL_IOCTL_CARD_INFO),
399         { SNDRV_CTL_IOCTL_ELEM_LIST32, AP(ctl_elem_list) },
400         { SNDRV_CTL_IOCTL_ELEM_INFO32, AP(ctl_elem_info) },
401         { SNDRV_CTL_IOCTL_ELEM_READ32, AP(ctl_elem_read) },
402         { SNDRV_CTL_IOCTL_ELEM_WRITE32, AP(ctl_elem_write) },
403         MAP_COMPAT(SNDRV_CTL_IOCTL_ELEM_LOCK),
404         MAP_COMPAT(SNDRV_CTL_IOCTL_ELEM_UNLOCK),
405         MAP_COMPAT(SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS),
406         MAP_COMPAT(SNDRV_CTL_IOCTL_HWDEP_INFO),
407         MAP_COMPAT(SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE),
408         MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE),
409         MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_INFO),
410         MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE),
411         MAP_COMPAT(SNDRV_CTL_IOCTL_POWER),
412         MAP_COMPAT(SNDRV_CTL_IOCTL_POWER_STATE),
413         { 0 }
414 };
415
416
417 /*
418  */
419
420 extern struct ioctl32_mapper pcm_mappers[];
421 extern struct ioctl32_mapper rawmidi_mappers[];
422 extern struct ioctl32_mapper timer_mappers[];
423 extern struct ioctl32_mapper hwdep_mappers[];
424 #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
425 extern struct ioctl32_mapper seq_mappers[];
426 #endif
427
428 static void snd_ioctl32_done(void)
429 {
430 #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
431         snd_ioctl32_unregister(seq_mappers);
432 #endif
433         snd_ioctl32_unregister(hwdep_mappers);
434         snd_ioctl32_unregister(timer_mappers);
435         snd_ioctl32_unregister(rawmidi_mappers);
436         snd_ioctl32_unregister(pcm_mappers);
437         snd_ioctl32_unregister(control_mappers);
438 }
439
440 static int __init snd_ioctl32_init(void)
441 {
442         snd_ioctl32_register(control_mappers);
443         snd_ioctl32_register(pcm_mappers);
444         snd_ioctl32_register(rawmidi_mappers);
445         snd_ioctl32_register(timer_mappers);
446         snd_ioctl32_register(hwdep_mappers);
447 #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
448         snd_ioctl32_register(seq_mappers);
449 #endif
450         return 0;
451 }
452
453 module_init(snd_ioctl32_init)
454 module_exit(snd_ioctl32_done)