Merge tag 'dlm-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland/linux-dlm
[sfrench/cifs-2.6.git] / sound / core / pcm_compat.c
1 /*
2  *   32bit -> 64bit ioctl wrapper for PCM 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 /* This file included from pcm_native.c */
22
23 #include <linux/compat.h>
24 #include <linux/slab.h>
25
26 static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream,
27                                       s32 __user *src)
28 {
29         snd_pcm_sframes_t delay;
30         int err;
31
32         err = snd_pcm_delay(substream, &delay);
33         if (err)
34                 return err;
35         if (put_user(delay, src))
36                 return -EFAULT;
37         return 0;
38 }
39
40 static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream,
41                                        u32 __user *src)
42 {
43         snd_pcm_uframes_t frames;
44         int err;
45
46         if (get_user(frames, src))
47                 return -EFAULT;
48         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
49                 err = snd_pcm_playback_rewind(substream, frames);
50         else
51                 err = snd_pcm_capture_rewind(substream, frames);
52         if (put_user(err, src))
53                 return -EFAULT;
54         return err < 0 ? err : 0;
55 }
56
57 static int snd_pcm_ioctl_forward_compat(struct snd_pcm_substream *substream,
58                                        u32 __user *src)
59 {
60         snd_pcm_uframes_t frames;
61         int err;
62
63         if (get_user(frames, src))
64                 return -EFAULT;
65         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
66                 err = snd_pcm_playback_forward(substream, frames);
67         else
68                 err = snd_pcm_capture_forward(substream, frames);
69         if (put_user(err, src))
70                 return -EFAULT;
71         return err < 0 ? err : 0;
72 }
73
74 struct snd_pcm_hw_params32 {
75         u32 flags;
76         struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */
77         struct snd_mask mres[5];        /* reserved masks */
78         struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
79         struct snd_interval ires[9];    /* reserved intervals */
80         u32 rmask;
81         u32 cmask;
82         u32 info;
83         u32 msbits;
84         u32 rate_num;
85         u32 rate_den;
86         u32 fifo_size;
87         unsigned char reserved[64];
88 };
89
90 struct snd_pcm_sw_params32 {
91         s32 tstamp_mode;
92         u32 period_step;
93         u32 sleep_min;
94         u32 avail_min;
95         u32 xfer_align;
96         u32 start_threshold;
97         u32 stop_threshold;
98         u32 silence_threshold;
99         u32 silence_size;
100         u32 boundary;
101         u32 proto;
102         u32 tstamp_type;
103         unsigned char reserved[56];
104 };
105
106 /* recalcuate the boundary within 32bit */
107 static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
108 {
109         snd_pcm_uframes_t boundary;
110
111         if (! runtime->buffer_size)
112                 return 0;
113         boundary = runtime->buffer_size;
114         while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
115                 boundary *= 2;
116         return boundary;
117 }
118
119 static int snd_pcm_ioctl_sw_params_compat(struct snd_pcm_substream *substream,
120                                           struct snd_pcm_sw_params32 __user *src)
121 {
122         struct snd_pcm_sw_params params;
123         snd_pcm_uframes_t boundary;
124         int err;
125
126         memset(&params, 0, sizeof(params));
127         if (get_user(params.tstamp_mode, &src->tstamp_mode) ||
128             get_user(params.period_step, &src->period_step) ||
129             get_user(params.sleep_min, &src->sleep_min) ||
130             get_user(params.avail_min, &src->avail_min) ||
131             get_user(params.xfer_align, &src->xfer_align) ||
132             get_user(params.start_threshold, &src->start_threshold) ||
133             get_user(params.stop_threshold, &src->stop_threshold) ||
134             get_user(params.silence_threshold, &src->silence_threshold) ||
135             get_user(params.silence_size, &src->silence_size) ||
136             get_user(params.tstamp_type, &src->tstamp_type) ||
137             get_user(params.proto, &src->proto))
138                 return -EFAULT;
139         /*
140          * Check silent_size parameter.  Since we have 64bit boundary,
141          * silence_size must be compared with the 32bit boundary.
142          */
143         boundary = recalculate_boundary(substream->runtime);
144         if (boundary && params.silence_size >= boundary)
145                 params.silence_size = substream->runtime->boundary;
146         err = snd_pcm_sw_params(substream, &params);
147         if (err < 0)
148                 return err;
149         if (boundary && put_user(boundary, &src->boundary))
150                 return -EFAULT;
151         return err;
152 }
153
154 struct snd_pcm_channel_info32 {
155         u32 channel;
156         u32 offset;
157         u32 first;
158         u32 step;
159 };
160
161 static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream,
162                                              struct snd_pcm_channel_info32 __user *src)
163 {
164         struct snd_pcm_channel_info info;
165         int err;
166
167         if (get_user(info.channel, &src->channel) ||
168             get_user(info.offset, &src->offset) ||
169             get_user(info.first, &src->first) ||
170             get_user(info.step, &src->step))
171                 return -EFAULT;
172         err = snd_pcm_channel_info(substream, &info);
173         if (err < 0)
174                 return err;
175         if (put_user(info.channel, &src->channel) ||
176             put_user(info.offset, &src->offset) ||
177             put_user(info.first, &src->first) ||
178             put_user(info.step, &src->step))
179                 return -EFAULT;
180         return err;
181 }
182
183 #ifdef CONFIG_X86_X32
184 /* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */
185 static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
186                                      struct snd_pcm_channel_info __user *src);
187 #define snd_pcm_ioctl_channel_info_x32(s, p)    \
188         snd_pcm_channel_info_user(s, p)
189 #endif /* CONFIG_X86_X32 */
190
191 struct snd_pcm_status32 {
192         s32 state;
193         struct compat_timespec trigger_tstamp;
194         struct compat_timespec tstamp;
195         u32 appl_ptr;
196         u32 hw_ptr;
197         s32 delay;
198         u32 avail;
199         u32 avail_max;
200         u32 overrange;
201         s32 suspended_state;
202         u32 audio_tstamp_data;
203         struct compat_timespec audio_tstamp;
204         struct compat_timespec driver_tstamp;
205         u32 audio_tstamp_accuracy;
206         unsigned char reserved[52-2*sizeof(struct compat_timespec)];
207 } __attribute__((packed));
208
209
210 static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
211                                       struct snd_pcm_status32 __user *src,
212                                       bool ext)
213 {
214         struct snd_pcm_status status;
215         int err;
216
217         memset(&status, 0, sizeof(status));
218         /*
219          * with extension, parameters are read/write,
220          * get audio_tstamp_data from user,
221          * ignore rest of status structure
222          */
223         if (ext && get_user(status.audio_tstamp_data,
224                                 (u32 __user *)(&src->audio_tstamp_data)))
225                 return -EFAULT;
226         err = snd_pcm_status(substream, &status);
227         if (err < 0)
228                 return err;
229
230         if (clear_user(src, sizeof(*src)))
231                 return -EFAULT;
232         if (put_user(status.state, &src->state) ||
233             compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
234             compat_put_timespec(&status.tstamp, &src->tstamp) ||
235             put_user(status.appl_ptr, &src->appl_ptr) ||
236             put_user(status.hw_ptr, &src->hw_ptr) ||
237             put_user(status.delay, &src->delay) ||
238             put_user(status.avail, &src->avail) ||
239             put_user(status.avail_max, &src->avail_max) ||
240             put_user(status.overrange, &src->overrange) ||
241             put_user(status.suspended_state, &src->suspended_state) ||
242             put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
243             compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
244             compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
245             put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
246                 return -EFAULT;
247
248         return err;
249 }
250
251 #ifdef CONFIG_X86_X32
252 /* X32 ABI has 64bit timespec and 64bit alignment */
253 struct snd_pcm_status_x32 {
254         s32 state;
255         u32 rsvd; /* alignment */
256         struct timespec trigger_tstamp;
257         struct timespec tstamp;
258         u32 appl_ptr;
259         u32 hw_ptr;
260         s32 delay;
261         u32 avail;
262         u32 avail_max;
263         u32 overrange;
264         s32 suspended_state;
265         u32 audio_tstamp_data;
266         struct timespec audio_tstamp;
267         struct timespec driver_tstamp;
268         u32 audio_tstamp_accuracy;
269         unsigned char reserved[52-2*sizeof(struct timespec)];
270 } __packed;
271
272 #define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
273
274 static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
275                                    struct snd_pcm_status_x32 __user *src,
276                                    bool ext)
277 {
278         struct snd_pcm_status status;
279         int err;
280
281         memset(&status, 0, sizeof(status));
282         /*
283          * with extension, parameters are read/write,
284          * get audio_tstamp_data from user,
285          * ignore rest of status structure
286          */
287         if (ext && get_user(status.audio_tstamp_data,
288                                 (u32 __user *)(&src->audio_tstamp_data)))
289                 return -EFAULT;
290         err = snd_pcm_status(substream, &status);
291         if (err < 0)
292                 return err;
293
294         if (clear_user(src, sizeof(*src)))
295                 return -EFAULT;
296         if (put_user(status.state, &src->state) ||
297             put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
298             put_timespec(&status.tstamp, &src->tstamp) ||
299             put_user(status.appl_ptr, &src->appl_ptr) ||
300             put_user(status.hw_ptr, &src->hw_ptr) ||
301             put_user(status.delay, &src->delay) ||
302             put_user(status.avail, &src->avail) ||
303             put_user(status.avail_max, &src->avail_max) ||
304             put_user(status.overrange, &src->overrange) ||
305             put_user(status.suspended_state, &src->suspended_state) ||
306             put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
307             put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
308             put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
309             put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
310                 return -EFAULT;
311
312         return err;
313 }
314 #endif /* CONFIG_X86_X32 */
315
316 /* both for HW_PARAMS and HW_REFINE */
317 static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
318                                           int refine, 
319                                           struct snd_pcm_hw_params32 __user *data32)
320 {
321         struct snd_pcm_hw_params *data;
322         struct snd_pcm_runtime *runtime;
323         int err;
324
325         if (! (runtime = substream->runtime))
326                 return -ENOTTY;
327
328         data = kmalloc(sizeof(*data), GFP_KERNEL);
329         if (!data)
330                 return -ENOMEM;
331
332         /* only fifo_size (RO from userspace) is different, so just copy all */
333         if (copy_from_user(data, data32, sizeof(*data32))) {
334                 err = -EFAULT;
335                 goto error;
336         }
337
338         if (refine)
339                 err = snd_pcm_hw_refine(substream, data);
340         else
341                 err = snd_pcm_hw_params(substream, data);
342         if (err < 0)
343                 goto error;
344         if (copy_to_user(data32, data, sizeof(*data32)) ||
345             put_user(data->fifo_size, &data32->fifo_size)) {
346                 err = -EFAULT;
347                 goto error;
348         }
349
350         if (! refine) {
351                 unsigned int new_boundary = recalculate_boundary(runtime);
352                 if (new_boundary)
353                         runtime->boundary = new_boundary;
354         }
355  error:
356         kfree(data);
357         return err;
358 }
359
360
361 /*
362  */
363 struct snd_xferi32 {
364         s32 result;
365         u32 buf;
366         u32 frames;
367 };
368
369 static int snd_pcm_ioctl_xferi_compat(struct snd_pcm_substream *substream,
370                                       int dir, struct snd_xferi32 __user *data32)
371 {
372         compat_caddr_t buf;
373         u32 frames;
374         int err;
375
376         if (! substream->runtime)
377                 return -ENOTTY;
378         if (substream->stream != dir)
379                 return -EINVAL;
380         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
381                 return -EBADFD;
382
383         if (get_user(buf, &data32->buf) ||
384             get_user(frames, &data32->frames))
385                 return -EFAULT;
386
387         if (dir == SNDRV_PCM_STREAM_PLAYBACK)
388                 err = snd_pcm_lib_write(substream, compat_ptr(buf), frames);
389         else
390                 err = snd_pcm_lib_read(substream, compat_ptr(buf), frames);
391         if (err < 0)
392                 return err;
393         /* copy the result */
394         if (put_user(err, &data32->result))
395                 return -EFAULT;
396         return 0;
397 }
398
399
400 /* snd_xfern needs remapping of bufs */
401 struct snd_xfern32 {
402         s32 result;
403         u32 bufs;  /* this is void **; */
404         u32 frames;
405 };
406
407 /*
408  * xfern ioctl nees to copy (up to) 128 pointers on stack.
409  * although we may pass the copied pointers through f_op->ioctl, but the ioctl
410  * handler there expands again the same 128 pointers on stack, so it is better
411  * to handle the function (calling pcm_readv/writev) directly in this handler.
412  */
413 static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
414                                       int dir, struct snd_xfern32 __user *data32)
415 {
416         compat_caddr_t buf;
417         compat_caddr_t __user *bufptr;
418         u32 frames;
419         void __user **bufs;
420         int err, ch, i;
421
422         if (! substream->runtime)
423                 return -ENOTTY;
424         if (substream->stream != dir)
425                 return -EINVAL;
426         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
427                 return -EBADFD;
428
429         if ((ch = substream->runtime->channels) > 128)
430                 return -EINVAL;
431         if (get_user(buf, &data32->bufs) ||
432             get_user(frames, &data32->frames))
433                 return -EFAULT;
434         bufptr = compat_ptr(buf);
435         bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL);
436         if (bufs == NULL)
437                 return -ENOMEM;
438         for (i = 0; i < ch; i++) {
439                 u32 ptr;
440                 if (get_user(ptr, bufptr)) {
441                         kfree(bufs);
442                         return -EFAULT;
443                 }
444                 bufs[i] = compat_ptr(ptr);
445                 bufptr++;
446         }
447         if (dir == SNDRV_PCM_STREAM_PLAYBACK)
448                 err = snd_pcm_lib_writev(substream, bufs, frames);
449         else
450                 err = snd_pcm_lib_readv(substream, bufs, frames);
451         if (err >= 0) {
452                 if (put_user(err, &data32->result))
453                         err = -EFAULT;
454         }
455         kfree(bufs);
456         return err;
457 }
458
459
460 struct snd_pcm_mmap_status32 {
461         s32 state;
462         s32 pad1;
463         u32 hw_ptr;
464         struct compat_timespec tstamp;
465         s32 suspended_state;
466         struct compat_timespec audio_tstamp;
467 } __attribute__((packed));
468
469 struct snd_pcm_mmap_control32 {
470         u32 appl_ptr;
471         u32 avail_min;
472 };
473
474 struct snd_pcm_sync_ptr32 {
475         u32 flags;
476         union {
477                 struct snd_pcm_mmap_status32 status;
478                 unsigned char reserved[64];
479         } s;
480         union {
481                 struct snd_pcm_mmap_control32 control;
482                 unsigned char reserved[64];
483         } c;
484 } __attribute__((packed));
485
486 static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
487                                          struct snd_pcm_sync_ptr32 __user *src)
488 {
489         struct snd_pcm_runtime *runtime = substream->runtime;
490         volatile struct snd_pcm_mmap_status *status;
491         volatile struct snd_pcm_mmap_control *control;
492         u32 sflags;
493         struct snd_pcm_mmap_control scontrol;
494         struct snd_pcm_mmap_status sstatus;
495         snd_pcm_uframes_t boundary;
496         int err;
497
498         if (snd_BUG_ON(!runtime))
499                 return -EINVAL;
500
501         if (get_user(sflags, &src->flags) ||
502             get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
503             get_user(scontrol.avail_min, &src->c.control.avail_min))
504                 return -EFAULT;
505         if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
506                 err = snd_pcm_hwsync(substream);
507                 if (err < 0)
508                         return err;
509         }
510         status = runtime->status;
511         control = runtime->control;
512         boundary = recalculate_boundary(runtime);
513         if (! boundary)
514                 boundary = 0x7fffffff;
515         snd_pcm_stream_lock_irq(substream);
516         /* FIXME: we should consider the boundary for the sync from app */
517         if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
518                 control->appl_ptr = scontrol.appl_ptr;
519         else
520                 scontrol.appl_ptr = control->appl_ptr % boundary;
521         if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
522                 control->avail_min = scontrol.avail_min;
523         else
524                 scontrol.avail_min = control->avail_min;
525         sstatus.state = status->state;
526         sstatus.hw_ptr = status->hw_ptr % boundary;
527         sstatus.tstamp = status->tstamp;
528         sstatus.suspended_state = status->suspended_state;
529         sstatus.audio_tstamp = status->audio_tstamp;
530         snd_pcm_stream_unlock_irq(substream);
531         if (put_user(sstatus.state, &src->s.status.state) ||
532             put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
533             compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
534             put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
535             compat_put_timespec(&sstatus.audio_tstamp,
536                     &src->s.status.audio_tstamp) ||
537             put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
538             put_user(scontrol.avail_min, &src->c.control.avail_min))
539                 return -EFAULT;
540
541         return 0;
542 }
543
544 #ifdef CONFIG_X86_X32
545 /* X32 ABI has 64bit timespec and 64bit alignment */
546 struct snd_pcm_mmap_status_x32 {
547         s32 state;
548         s32 pad1;
549         u32 hw_ptr;
550         u32 pad2; /* alignment */
551         struct timespec tstamp;
552         s32 suspended_state;
553         s32 pad3;
554         struct timespec audio_tstamp;
555 } __packed;
556
557 struct snd_pcm_mmap_control_x32 {
558         u32 appl_ptr;
559         u32 avail_min;
560 };
561
562 struct snd_pcm_sync_ptr_x32 {
563         u32 flags;
564         u32 rsvd; /* alignment */
565         union {
566                 struct snd_pcm_mmap_status_x32 status;
567                 unsigned char reserved[64];
568         } s;
569         union {
570                 struct snd_pcm_mmap_control_x32 control;
571                 unsigned char reserved[64];
572         } c;
573 } __packed;
574
575 static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
576                                       struct snd_pcm_sync_ptr_x32 __user *src)
577 {
578         struct snd_pcm_runtime *runtime = substream->runtime;
579         volatile struct snd_pcm_mmap_status *status;
580         volatile struct snd_pcm_mmap_control *control;
581         u32 sflags;
582         struct snd_pcm_mmap_control scontrol;
583         struct snd_pcm_mmap_status sstatus;
584         snd_pcm_uframes_t boundary;
585         int err;
586
587         if (snd_BUG_ON(!runtime))
588                 return -EINVAL;
589
590         if (get_user(sflags, &src->flags) ||
591             get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
592             get_user(scontrol.avail_min, &src->c.control.avail_min))
593                 return -EFAULT;
594         if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
595                 err = snd_pcm_hwsync(substream);
596                 if (err < 0)
597                         return err;
598         }
599         status = runtime->status;
600         control = runtime->control;
601         boundary = recalculate_boundary(runtime);
602         if (!boundary)
603                 boundary = 0x7fffffff;
604         snd_pcm_stream_lock_irq(substream);
605         /* FIXME: we should consider the boundary for the sync from app */
606         if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
607                 control->appl_ptr = scontrol.appl_ptr;
608         else
609                 scontrol.appl_ptr = control->appl_ptr % boundary;
610         if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
611                 control->avail_min = scontrol.avail_min;
612         else
613                 scontrol.avail_min = control->avail_min;
614         sstatus.state = status->state;
615         sstatus.hw_ptr = status->hw_ptr % boundary;
616         sstatus.tstamp = status->tstamp;
617         sstatus.suspended_state = status->suspended_state;
618         sstatus.audio_tstamp = status->audio_tstamp;
619         snd_pcm_stream_unlock_irq(substream);
620         if (put_user(sstatus.state, &src->s.status.state) ||
621             put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
622             put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
623             put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
624             put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) ||
625             put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
626             put_user(scontrol.avail_min, &src->c.control.avail_min))
627                 return -EFAULT;
628
629         return 0;
630 }
631 #endif /* CONFIG_X86_X32 */
632
633 /*
634  */
635 enum {
636         SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32),
637         SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
638         SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
639         SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
640         SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
641         SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
642         SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
643         SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
644         SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32),
645         SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct snd_xferi32),
646         SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32),
647         SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
648         SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
649         SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),
650 #ifdef CONFIG_X86_X32
651         SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info),
652         SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32),
653         SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32),
654         SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
655 #endif /* CONFIG_X86_X32 */
656 };
657
658 static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
659 {
660         struct snd_pcm_file *pcm_file;
661         struct snd_pcm_substream *substream;
662         void __user *argp = compat_ptr(arg);
663
664         pcm_file = file->private_data;
665         if (! pcm_file)
666                 return -ENOTTY;
667         substream = pcm_file->substream;
668         if (! substream)
669                 return -ENOTTY;
670
671         /*
672          * When PCM is used on 32bit mode, we need to disable
673          * mmap of PCM status/control records because of the size
674          * incompatibility.
675          */
676         pcm_file->no_compat_mmap = 1;
677
678         switch (cmd) {
679         case SNDRV_PCM_IOCTL_PVERSION:
680         case SNDRV_PCM_IOCTL_INFO:
681         case SNDRV_PCM_IOCTL_TSTAMP:
682         case SNDRV_PCM_IOCTL_TTSTAMP:
683         case SNDRV_PCM_IOCTL_USER_PVERSION:
684         case SNDRV_PCM_IOCTL_HWSYNC:
685         case SNDRV_PCM_IOCTL_PREPARE:
686         case SNDRV_PCM_IOCTL_RESET:
687         case SNDRV_PCM_IOCTL_START:
688         case SNDRV_PCM_IOCTL_DROP:
689         case SNDRV_PCM_IOCTL_DRAIN:
690         case SNDRV_PCM_IOCTL_PAUSE:
691         case SNDRV_PCM_IOCTL_HW_FREE:
692         case SNDRV_PCM_IOCTL_RESUME:
693         case SNDRV_PCM_IOCTL_XRUN:
694         case SNDRV_PCM_IOCTL_LINK:
695         case SNDRV_PCM_IOCTL_UNLINK:
696                 return snd_pcm_common_ioctl(file, substream, cmd, argp);
697         case SNDRV_PCM_IOCTL_HW_REFINE32:
698                 return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
699         case SNDRV_PCM_IOCTL_HW_PARAMS32:
700                 return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
701         case SNDRV_PCM_IOCTL_SW_PARAMS32:
702                 return snd_pcm_ioctl_sw_params_compat(substream, argp);
703         case SNDRV_PCM_IOCTL_STATUS32:
704                 return snd_pcm_status_user_compat(substream, argp, false);
705         case SNDRV_PCM_IOCTL_STATUS_EXT32:
706                 return snd_pcm_status_user_compat(substream, argp, true);
707         case SNDRV_PCM_IOCTL_SYNC_PTR32:
708                 return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
709         case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
710                 return snd_pcm_ioctl_channel_info_compat(substream, argp);
711         case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
712                 return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
713         case SNDRV_PCM_IOCTL_READI_FRAMES32:
714                 return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
715         case SNDRV_PCM_IOCTL_WRITEN_FRAMES32:
716                 return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
717         case SNDRV_PCM_IOCTL_READN_FRAMES32:
718                 return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
719         case SNDRV_PCM_IOCTL_DELAY32:
720                 return snd_pcm_ioctl_delay_compat(substream, argp);
721         case SNDRV_PCM_IOCTL_REWIND32:
722                 return snd_pcm_ioctl_rewind_compat(substream, argp);
723         case SNDRV_PCM_IOCTL_FORWARD32:
724                 return snd_pcm_ioctl_forward_compat(substream, argp);
725 #ifdef CONFIG_X86_X32
726         case SNDRV_PCM_IOCTL_STATUS_X32:
727                 return snd_pcm_status_user_x32(substream, argp, false);
728         case SNDRV_PCM_IOCTL_STATUS_EXT_X32:
729                 return snd_pcm_status_user_x32(substream, argp, true);
730         case SNDRV_PCM_IOCTL_SYNC_PTR_X32:
731                 return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
732         case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
733                 return snd_pcm_ioctl_channel_info_x32(substream, argp);
734 #endif /* CONFIG_X86_X32 */
735         }
736
737         return -ENOIOCTLCMD;
738 }