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
25static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream,
26				      s32 __user *src)
27{
28	snd_pcm_sframes_t delay;
29	mm_segment_t fs;
30	int err;
31
32	fs = snd_enter_user();
33	err = snd_pcm_delay(substream, &delay);
34	snd_leave_user(fs);
35	if (err < 0)
36		return err;
37	if (put_user(delay, src))
38		return -EFAULT;
39	return err;
40}
41
42static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream,
43				       u32 __user *src)
44{
45	snd_pcm_uframes_t frames;
46	int err;
47
48	if (get_user(frames, src))
49		return -EFAULT;
50	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
51		err = snd_pcm_playback_rewind(substream, frames);
52	else
53		err = snd_pcm_capture_rewind(substream, frames);
54	if (put_user(err, src))
55		return -EFAULT;
56	return err < 0 ? err : 0;
57}
58
59static int snd_pcm_ioctl_forward_compat(struct snd_pcm_substream *substream,
60				       u32 __user *src)
61{
62	snd_pcm_uframes_t frames;
63	int err;
64
65	if (get_user(frames, src))
66		return -EFAULT;
67	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
68		err = snd_pcm_playback_forward(substream, frames);
69	else
70		err = snd_pcm_capture_forward(substream, frames);
71	if (put_user(err, src))
72		return -EFAULT;
73	return err < 0 ? err : 0;
74}
75
76struct snd_pcm_hw_params32 {
77	u32 flags;
78	struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */
79	struct snd_mask mres[5];	/* reserved masks */
80	struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
81	struct snd_interval ires[9];	/* reserved intervals */
82	u32 rmask;
83	u32 cmask;
84	u32 info;
85	u32 msbits;
86	u32 rate_num;
87	u32 rate_den;
88	u32 fifo_size;
89	unsigned char reserved[64];
90};
91
92struct snd_pcm_sw_params32 {
93	s32 tstamp_mode;
94	u32 period_step;
95	u32 sleep_min;
96	u32 avail_min;
97	u32 xfer_align;
98	u32 start_threshold;
99	u32 stop_threshold;
100	u32 silence_threshold;
101	u32 silence_size;
102	u32 boundary;
103	unsigned char reserved[64];
104};
105
106/* recalcuate the boundary within 32bit */
107static 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
119static 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		return -EFAULT;
137	/*
138	 * Check silent_size parameter.  Since we have 64bit boundary,
139	 * silence_size must be compared with the 32bit boundary.
140	 */
141	boundary = recalculate_boundary(substream->runtime);
142	if (boundary && params.silence_size >= boundary)
143		params.silence_size = substream->runtime->boundary;
144	err = snd_pcm_sw_params(substream, &params);
145	if (err < 0)
146		return err;
147	if (boundary && put_user(boundary, &src->boundary))
148		return -EFAULT;
149	return err;
150}
151
152struct snd_pcm_channel_info32 {
153	u32 channel;
154	u32 offset;
155	u32 first;
156	u32 step;
157};
158
159static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream,
160					     struct snd_pcm_channel_info32 __user *src)
161{
162	struct snd_pcm_channel_info info;
163	int err;
164
165	if (get_user(info.channel, &src->channel) ||
166	    get_user(info.offset, &src->offset) ||
167	    get_user(info.first, &src->first) ||
168	    get_user(info.step, &src->step))
169		return -EFAULT;
170	err = snd_pcm_channel_info(substream, &info);
171	if (err < 0)
172		return err;
173	if (put_user(info.channel, &src->channel) ||
174	    put_user(info.offset, &src->offset) ||
175	    put_user(info.first, &src->first) ||
176	    put_user(info.step, &src->step))
177		return -EFAULT;
178	return err;
179}
180
181struct snd_pcm_status32 {
182	s32 state;
183	struct compat_timespec trigger_tstamp;
184	struct compat_timespec tstamp;
185	u32 appl_ptr;
186	u32 hw_ptr;
187	s32 delay;
188	u32 avail;
189	u32 avail_max;
190	u32 overrange;
191	s32 suspended_state;
192	unsigned char reserved[60];
193} __attribute__((packed));
194
195
196static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
197				      struct snd_pcm_status32 __user *src)
198{
199	struct snd_pcm_status status;
200	int err;
201
202	err = snd_pcm_status(substream, &status);
203	if (err < 0)
204		return err;
205
206	if (put_user(status.state, &src->state) ||
207	    put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) ||
208	    put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) ||
209	    put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
210	    put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
211	    put_user(status.appl_ptr, &src->appl_ptr) ||
212	    put_user(status.hw_ptr, &src->hw_ptr) ||
213	    put_user(status.delay, &src->delay) ||
214	    put_user(status.avail, &src->avail) ||
215	    put_user(status.avail_max, &src->avail_max) ||
216	    put_user(status.overrange, &src->overrange) ||
217	    put_user(status.suspended_state, &src->suspended_state))
218		return -EFAULT;
219
220	return err;
221}
222
223/* both for HW_PARAMS and HW_REFINE */
224static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
225					  int refine,
226					  struct snd_pcm_hw_params32 __user *data32)
227{
228	struct snd_pcm_hw_params *data;
229	struct snd_pcm_runtime *runtime;
230	int err;
231
232	if (! (runtime = substream->runtime))
233		return -ENOTTY;
234
235	data = kmalloc(sizeof(*data), GFP_KERNEL);
236	if (data == NULL)
237		return -ENOMEM;
238	/* only fifo_size is different, so just copy all */
239	if (copy_from_user(data, data32, sizeof(*data32))) {
240		err = -EFAULT;
241		goto error;
242	}
243	if (refine)
244		err = snd_pcm_hw_refine(substream, data);
245	else
246		err = snd_pcm_hw_params(substream, data);
247	if (err < 0)
248		goto error;
249	if (copy_to_user(data32, data, sizeof(*data32)) ||
250	    put_user(data->fifo_size, &data32->fifo_size)) {
251		err = -EFAULT;
252		goto error;
253	}
254
255	if (! refine) {
256		unsigned int new_boundary = recalculate_boundary(runtime);
257		if (new_boundary)
258			runtime->boundary = new_boundary;
259	}
260 error:
261	kfree(data);
262	return err;
263}
264
265
266/*
267 */
268struct snd_xferi32 {
269	s32 result;
270	u32 buf;
271	u32 frames;
272};
273
274static int snd_pcm_ioctl_xferi_compat(struct snd_pcm_substream *substream,
275				      int dir, struct snd_xferi32 __user *data32)
276{
277	compat_caddr_t buf;
278	u32 frames;
279	int err;
280
281	if (! substream->runtime)
282		return -ENOTTY;
283	if (substream->stream != dir)
284		return -EINVAL;
285	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
286		return -EBADFD;
287
288	if (get_user(buf, &data32->buf) ||
289	    get_user(frames, &data32->frames))
290		return -EFAULT;
291
292	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
293		err = snd_pcm_lib_write(substream, compat_ptr(buf), frames);
294	else
295		err = snd_pcm_lib_read(substream, compat_ptr(buf), frames);
296	if (err < 0)
297		return err;
298	/* copy the result */
299	if (put_user(err, &data32->result))
300		return -EFAULT;
301	return 0;
302}
303
304
305/* snd_xfern needs remapping of bufs */
306struct snd_xfern32 {
307	s32 result;
308	u32 bufs;  /* this is void **; */
309	u32 frames;
310};
311
312/*
313 * xfern ioctl nees to copy (up to) 128 pointers on stack.
314 * although we may pass the copied pointers through f_op->ioctl, but the ioctl
315 * handler there expands again the same 128 pointers on stack, so it is better
316 * to handle the function (calling pcm_readv/writev) directly in this handler.
317 */
318static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
319				      int dir, struct snd_xfern32 __user *data32)
320{
321	compat_caddr_t buf;
322	compat_caddr_t __user *bufptr;
323	u32 frames;
324	void __user **bufs;
325	int err, ch, i;
326
327	if (! substream->runtime)
328		return -ENOTTY;
329	if (substream->stream != dir)
330		return -EINVAL;
331
332	if ((ch = substream->runtime->channels) > 128)
333		return -EINVAL;
334	if (get_user(buf, &data32->bufs) ||
335	    get_user(frames, &data32->frames))
336		return -EFAULT;
337	bufptr = compat_ptr(buf);
338	bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL);
339	if (bufs == NULL)
340		return -ENOMEM;
341	for (i = 0; i < ch; i++) {
342		u32 ptr;
343		if (get_user(ptr, bufptr)) {
344			kfree(bufs);
345			return -EFAULT;
346		}
347		bufs[ch] = compat_ptr(ptr);
348		bufptr++;
349	}
350	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
351		err = snd_pcm_lib_writev(substream, bufs, frames);
352	else
353		err = snd_pcm_lib_readv(substream, bufs, frames);
354	if (err >= 0) {
355		if (put_user(err, &data32->result))
356			err = -EFAULT;
357	}
358	kfree(bufs);
359	return err;
360}
361
362
363struct snd_pcm_mmap_status32 {
364	s32 state;
365	s32 pad1;
366	u32 hw_ptr;
367	struct compat_timespec tstamp;
368	s32 suspended_state;
369} __attribute__((packed));
370
371struct snd_pcm_mmap_control32 {
372	u32 appl_ptr;
373	u32 avail_min;
374};
375
376struct snd_pcm_sync_ptr32 {
377	u32 flags;
378	union {
379		struct snd_pcm_mmap_status32 status;
380		unsigned char reserved[64];
381	} s;
382	union {
383		struct snd_pcm_mmap_control32 control;
384		unsigned char reserved[64];
385	} c;
386} __attribute__((packed));
387
388static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
389					 struct snd_pcm_sync_ptr32 __user *src)
390{
391	struct snd_pcm_runtime *runtime = substream->runtime;
392	volatile struct snd_pcm_mmap_status *status;
393	volatile struct snd_pcm_mmap_control *control;
394	u32 sflags;
395	struct snd_pcm_mmap_control scontrol;
396	struct snd_pcm_mmap_status sstatus;
397	snd_pcm_uframes_t boundary;
398	int err;
399
400	snd_assert(runtime, return -EINVAL);
401
402	if (get_user(sflags, &src->flags) ||
403	    get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
404	    get_user(scontrol.avail_min, &src->c.control.avail_min))
405		return -EFAULT;
406	if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
407		err = snd_pcm_hwsync(substream);
408		if (err < 0)
409			return err;
410	}
411	status = runtime->status;
412	control = runtime->control;
413	boundary = recalculate_boundary(runtime);
414	if (! boundary)
415		boundary = 0x7fffffff;
416	snd_pcm_stream_lock_irq(substream);
417	if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
418		control->appl_ptr = scontrol.appl_ptr;
419	else
420		scontrol.appl_ptr = control->appl_ptr % boundary;
421	if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
422		control->avail_min = scontrol.avail_min;
423	else
424		scontrol.avail_min = control->avail_min;
425	sstatus.state = status->state;
426	sstatus.hw_ptr = status->hw_ptr % boundary;
427	sstatus.tstamp = status->tstamp;
428	sstatus.suspended_state = status->suspended_state;
429	snd_pcm_stream_unlock_irq(substream);
430	if (put_user(sstatus.state, &src->s.status.state) ||
431	    put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
432	    put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) ||
433	    put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
434	    put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
435	    put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
436	    put_user(scontrol.avail_min, &src->c.control.avail_min))
437		return -EFAULT;
438
439	return 0;
440}
441
442
443/*
444 */
445enum {
446	SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32),
447	SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
448	SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
449	SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
450	SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
451	SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
452	SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
453	SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32),
454	SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct snd_xferi32),
455	SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32),
456	SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
457	SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
458	SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),
459
460};
461
462static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
463{
464	struct snd_pcm_file *pcm_file;
465	struct snd_pcm_substream *substream;
466	void __user *argp = compat_ptr(arg);
467
468	pcm_file = file->private_data;
469	if (! pcm_file)
470		return -ENOTTY;
471	substream = pcm_file->substream;
472	if (! substream)
473		return -ENOTTY;
474
475	/*
476	 * When PCM is used on 32bit mode, we need to disable
477	 * mmap of PCM status/control records because of the size
478	 * incompatibility.
479	 */
480	pcm_file->no_compat_mmap = 1;
481
482	switch (cmd) {
483	case SNDRV_PCM_IOCTL_PVERSION:
484	case SNDRV_PCM_IOCTL_INFO:
485	case SNDRV_PCM_IOCTL_TSTAMP:
486	case SNDRV_PCM_IOCTL_HWSYNC:
487	case SNDRV_PCM_IOCTL_PREPARE:
488	case SNDRV_PCM_IOCTL_RESET:
489	case SNDRV_PCM_IOCTL_START:
490	case SNDRV_PCM_IOCTL_DROP:
491	case SNDRV_PCM_IOCTL_DRAIN:
492	case SNDRV_PCM_IOCTL_PAUSE:
493	case SNDRV_PCM_IOCTL_HW_FREE:
494	case SNDRV_PCM_IOCTL_RESUME:
495	case SNDRV_PCM_IOCTL_XRUN:
496	case SNDRV_PCM_IOCTL_LINK:
497	case SNDRV_PCM_IOCTL_UNLINK:
498		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
499			return snd_pcm_playback_ioctl1(file, substream, cmd, argp);
500		else
501			return snd_pcm_capture_ioctl1(file, substream, cmd, argp);
502	case SNDRV_PCM_IOCTL_HW_REFINE32:
503		return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
504	case SNDRV_PCM_IOCTL_HW_PARAMS32:
505		return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
506	case SNDRV_PCM_IOCTL_SW_PARAMS32:
507		return snd_pcm_ioctl_sw_params_compat(substream, argp);
508	case SNDRV_PCM_IOCTL_STATUS32:
509		return snd_pcm_status_user_compat(substream, argp);
510	case SNDRV_PCM_IOCTL_SYNC_PTR32:
511		return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
512	case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
513		return snd_pcm_ioctl_channel_info_compat(substream, argp);
514	case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
515		return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
516	case SNDRV_PCM_IOCTL_READI_FRAMES32:
517		return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
518	case SNDRV_PCM_IOCTL_WRITEN_FRAMES32:
519		return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
520	case SNDRV_PCM_IOCTL_READN_FRAMES32:
521		return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
522	case SNDRV_PCM_IOCTL_DELAY32:
523		return snd_pcm_ioctl_delay_compat(substream, argp);
524	case SNDRV_PCM_IOCTL_REWIND32:
525		return snd_pcm_ioctl_rewind_compat(substream, argp);
526	case SNDRV_PCM_IOCTL_FORWARD32:
527		return snd_pcm_ioctl_forward_compat(substream, argp);
528	}
529
530	return -ENOIOCTLCMD;
531}
532