Blame view

sound/core/pcm_compat.c 21.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  /*
   *   32bit -> 64bit ioctl wrapper for PCM API
   *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
  
  /* This file included from pcm_native.c */
  
  #include <linux/compat.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
24
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25

877211f5e   Takashi Iwai   [ALSA] Remove xxx...
26
  static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
  				      s32 __user *src)
  {
  	snd_pcm_sframes_t delay;
00e0495d8   Jeffery Miller   ALSA: pcm: Return...
30
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31

00e0495d8   Jeffery Miller   ALSA: pcm: Return...
32
33
34
  	err = snd_pcm_delay(substream, &delay);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
  	if (put_user(delay, src))
  		return -EFAULT;
c2c86a971   Takashi Iwai   ALSA: pcm: Remove...
37
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
39
  static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  				       u32 __user *src)
  {
  	snd_pcm_uframes_t frames;
  	int err;
  
  	if (get_user(frames, src))
  		return -EFAULT;
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		err = snd_pcm_playback_rewind(substream, frames);
  	else
  		err = snd_pcm_capture_rewind(substream, frames);
  	if (put_user(err, src))
  		return -EFAULT;
  	return err < 0 ? err : 0;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
55
  static int snd_pcm_ioctl_forward_compat(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  				       u32 __user *src)
  {
  	snd_pcm_uframes_t frames;
  	int err;
  
  	if (get_user(frames, src))
  		return -EFAULT;
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		err = snd_pcm_playback_forward(substream, frames);
  	else
  		err = snd_pcm_capture_forward(substream, frames);
  	if (put_user(err, src))
  		return -EFAULT;
  	return err < 0 ? err : 0;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
71
  struct snd_pcm_hw_params32 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  	u32 flags;
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
73
74
75
76
  	struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */
  	struct snd_mask mres[5];	/* reserved masks */
  	struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
  	struct snd_interval ires[9];	/* reserved intervals */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
81
82
83
84
85
  	u32 rmask;
  	u32 cmask;
  	u32 info;
  	u32 msbits;
  	u32 rate_num;
  	u32 rate_den;
  	u32 fifo_size;
  	unsigned char reserved[64];
  };
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
86
  struct snd_pcm_sw_params32 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
89
90
91
92
93
94
95
96
  	s32 tstamp_mode;
  	u32 period_step;
  	u32 sleep_min;
  	u32 avail_min;
  	u32 xfer_align;
  	u32 start_threshold;
  	u32 stop_threshold;
  	u32 silence_threshold;
  	u32 silence_size;
  	u32 boundary;
e58c295c0   Takashi Iwai   ALSA: pcm: Add ts...
97
98
99
  	u32 proto;
  	u32 tstamp_type;
  	unsigned char reserved[56];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  };
b27113102   Takashi Iwai   [ALSA] Fix PCM 32...
101
  /* recalcuate the boundary within 32bit */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
102
  static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
b27113102   Takashi Iwai   [ALSA] Fix PCM 32...
103
104
105
106
107
108
109
110
111
112
  {
  	snd_pcm_uframes_t boundary;
  
  	if (! runtime->buffer_size)
  		return 0;
  	boundary = runtime->buffer_size;
  	while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
  		boundary *= 2;
  	return boundary;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
113
114
  static int snd_pcm_ioctl_sw_params_compat(struct snd_pcm_substream *substream,
  					  struct snd_pcm_sw_params32 __user *src)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
116
  	struct snd_pcm_sw_params params;
b27113102   Takashi Iwai   [ALSA] Fix PCM 32...
117
  	snd_pcm_uframes_t boundary;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
125
126
127
128
  	int err;
  
  	memset(&params, 0, sizeof(params));
  	if (get_user(params.tstamp_mode, &src->tstamp_mode) ||
  	    get_user(params.period_step, &src->period_step) ||
  	    get_user(params.sleep_min, &src->sleep_min) ||
  	    get_user(params.avail_min, &src->avail_min) ||
  	    get_user(params.xfer_align, &src->xfer_align) ||
  	    get_user(params.start_threshold, &src->start_threshold) ||
  	    get_user(params.stop_threshold, &src->stop_threshold) ||
  	    get_user(params.silence_threshold, &src->silence_threshold) ||
e58c295c0   Takashi Iwai   ALSA: pcm: Add ts...
129
130
131
  	    get_user(params.silence_size, &src->silence_size) ||
  	    get_user(params.tstamp_type, &src->tstamp_type) ||
  	    get_user(params.proto, &src->proto))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  		return -EFAULT;
b27113102   Takashi Iwai   [ALSA] Fix PCM 32...
133
134
135
136
137
138
139
  	/*
  	 * Check silent_size parameter.  Since we have 64bit boundary,
  	 * silence_size must be compared with the 32bit boundary.
  	 */
  	boundary = recalculate_boundary(substream->runtime);
  	if (boundary && params.silence_size >= boundary)
  		params.silence_size = substream->runtime->boundary;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
  	err = snd_pcm_sw_params(substream, &params);
  	if (err < 0)
  		return err;
7153a558a   Takashi Iwai   [ALSA] pcm - Fix ...
143
  	if (boundary && put_user(boundary, &src->boundary))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
  		return -EFAULT;
  	return err;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
147
  struct snd_pcm_channel_info32 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
150
151
152
  	u32 channel;
  	u32 offset;
  	u32 first;
  	u32 step;
  };
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
153
154
  static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream,
  					     struct snd_pcm_channel_info32 __user *src)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
156
  	struct snd_pcm_channel_info info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  	int err;
  
  	if (get_user(info.channel, &src->channel) ||
  	    get_user(info.offset, &src->offset) ||
  	    get_user(info.first, &src->first) ||
  	    get_user(info.step, &src->step))
  		return -EFAULT;
  	err = snd_pcm_channel_info(substream, &info);
  	if (err < 0)
  		return err;
  	if (put_user(info.channel, &src->channel) ||
  	    put_user(info.offset, &src->offset) ||
  	    put_user(info.first, &src->first) ||
  	    put_user(info.step, &src->step))
  		return -EFAULT;
  	return err;
  }
513ace79b   Takashi Iwai   ALSA: pcm: Fix io...
174
175
176
177
178
179
180
  #ifdef CONFIG_X86_X32
  /* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */
  static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
  				     struct snd_pcm_channel_info __user *src);
  #define snd_pcm_ioctl_channel_info_x32(s, p)	\
  	snd_pcm_channel_info_user(s, p)
  #endif /* CONFIG_X86_X32 */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
181
  struct snd_pcm_status32 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
185
186
187
188
189
190
191
  	s32 state;
  	struct compat_timespec trigger_tstamp;
  	struct compat_timespec tstamp;
  	u32 appl_ptr;
  	u32 hw_ptr;
  	s32 delay;
  	u32 avail;
  	u32 avail_max;
  	u32 overrange;
  	s32 suspended_state;
5442a73a0   Pierre-Louis Bossart   ALSA: core: pass ...
192
  	u32 audio_tstamp_data;
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
193
  	struct compat_timespec audio_tstamp;
5442a73a0   Pierre-Louis Bossart   ALSA: core: pass ...
194
195
196
  	struct compat_timespec driver_tstamp;
  	u32 audio_tstamp_accuracy;
  	unsigned char reserved[52-2*sizeof(struct compat_timespec)];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  } __attribute__((packed));
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
198
  static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
5442a73a0   Pierre-Louis Bossart   ALSA: core: pass ...
199
200
  				      struct snd_pcm_status32 __user *src,
  				      bool ext)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
202
  	struct snd_pcm_status status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  	int err;
5442a73a0   Pierre-Louis Bossart   ALSA: core: pass ...
204
205
206
207
208
209
210
211
212
  	memset(&status, 0, sizeof(status));
  	/*
  	 * with extension, parameters are read/write,
  	 * get audio_tstamp_data from user,
  	 * ignore rest of status structure
  	 */
  	if (ext && get_user(status.audio_tstamp_data,
  				(u32 __user *)(&src->audio_tstamp_data)))
  		return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
  	err = snd_pcm_status(substream, &status);
  	if (err < 0)
  		return err;
317168d0c   Takashi Iwai   ALSA: pcm: Zero-c...
216
217
  	if (clear_user(src, sizeof(*src)))
  		return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
  	if (put_user(status.state, &src->state) ||
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
219
220
  	    compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
  	    compat_put_timespec(&status.tstamp, &src->tstamp) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
224
225
226
  	    put_user(status.appl_ptr, &src->appl_ptr) ||
  	    put_user(status.hw_ptr, &src->hw_ptr) ||
  	    put_user(status.delay, &src->delay) ||
  	    put_user(status.avail, &src->avail) ||
  	    put_user(status.avail_max, &src->avail_max) ||
  	    put_user(status.overrange, &src->overrange) ||
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
227
  	    put_user(status.suspended_state, &src->suspended_state) ||
5442a73a0   Pierre-Louis Bossart   ALSA: core: pass ...
228
229
230
231
  	    put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
  	    compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
  	    compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
  	    put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
  		return -EFAULT;
  
  	return err;
  }
513ace79b   Takashi Iwai   ALSA: pcm: Fix io...
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  #ifdef CONFIG_X86_X32
  /* X32 ABI has 64bit timespec and 64bit alignment */
  struct snd_pcm_status_x32 {
  	s32 state;
  	u32 rsvd; /* alignment */
  	struct timespec trigger_tstamp;
  	struct timespec tstamp;
  	u32 appl_ptr;
  	u32 hw_ptr;
  	s32 delay;
  	u32 avail;
  	u32 avail_max;
  	u32 overrange;
  	s32 suspended_state;
  	u32 audio_tstamp_data;
  	struct timespec audio_tstamp;
  	struct timespec driver_tstamp;
  	u32 audio_tstamp_accuracy;
  	unsigned char reserved[52-2*sizeof(struct timespec)];
  } __packed;
  
  #define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
  
  static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
  				   struct snd_pcm_status_x32 __user *src,
  				   bool ext)
  {
  	struct snd_pcm_status status;
  	int err;
  
  	memset(&status, 0, sizeof(status));
  	/*
  	 * with extension, parameters are read/write,
  	 * get audio_tstamp_data from user,
  	 * ignore rest of status structure
  	 */
  	if (ext && get_user(status.audio_tstamp_data,
  				(u32 __user *)(&src->audio_tstamp_data)))
  		return -EFAULT;
  	err = snd_pcm_status(substream, &status);
  	if (err < 0)
  		return err;
  
  	if (clear_user(src, sizeof(*src)))
  		return -EFAULT;
  	if (put_user(status.state, &src->state) ||
  	    put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
  	    put_timespec(&status.tstamp, &src->tstamp) ||
  	    put_user(status.appl_ptr, &src->appl_ptr) ||
  	    put_user(status.hw_ptr, &src->hw_ptr) ||
  	    put_user(status.delay, &src->delay) ||
  	    put_user(status.avail, &src->avail) ||
  	    put_user(status.avail_max, &src->avail_max) ||
  	    put_user(status.overrange, &src->overrange) ||
  	    put_user(status.suspended_state, &src->suspended_state) ||
  	    put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
  	    put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
  	    put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
  	    put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
  		return -EFAULT;
  
  	return err;
  }
  #endif /* CONFIG_X86_X32 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
  /* both for HW_PARAMS and HW_REFINE */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
301
  static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  					  int refine, 
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
303
  					  struct snd_pcm_hw_params32 __user *data32)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
305
306
  	struct snd_pcm_hw_params *data;
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
310
  	int err;
  
  	if (! (runtime = substream->runtime))
  		return -ENOTTY;
43c54b8c7   Nicolas Boichat   ALSA: pcm: Fix sn...
311
312
313
314
315
316
317
318
319
  	data = kmalloc(sizeof(*data), GFP_KERNEL);
  	if (!data)
  		return -ENOMEM;
  
  	/* only fifo_size (RO from userspace) is different, so just copy all */
  	if (copy_from_user(data, data32, sizeof(*data32))) {
  		err = -EFAULT;
  		goto error;
  	}
ef44a1ec6   Li Zefan   ALSA: sound/core:...
320

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
323
324
325
326
327
328
329
330
331
  	if (refine)
  		err = snd_pcm_hw_refine(substream, data);
  	else
  		err = snd_pcm_hw_params(substream, data);
  	if (err < 0)
  		goto error;
  	if (copy_to_user(data32, data, sizeof(*data32)) ||
  	    put_user(data->fifo_size, &data32->fifo_size)) {
  		err = -EFAULT;
  		goto error;
  	}
7153a558a   Takashi Iwai   [ALSA] pcm - Fix ...
332
333
334
335
336
  	if (! refine) {
  		unsigned int new_boundary = recalculate_boundary(runtime);
  		if (new_boundary)
  			runtime->boundary = new_boundary;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
339
340
341
342
343
344
   error:
  	kfree(data);
  	return err;
  }
  
  
  /*
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
345
  struct snd_xferi32 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
349
  	s32 result;
  	u32 buf;
  	u32 frames;
  };
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
350
351
  static int snd_pcm_ioctl_xferi_compat(struct snd_pcm_substream *substream,
  				      int dir, struct snd_xferi32 __user *data32)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
  {
  	compat_caddr_t buf;
  	u32 frames;
  	int err;
  
  	if (! substream->runtime)
  		return -ENOTTY;
  	if (substream->stream != dir)
  		return -EINVAL;
  	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
  		return -EBADFD;
  
  	if (get_user(buf, &data32->buf) ||
  	    get_user(frames, &data32->frames))
  		return -EFAULT;
  
  	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
  		err = snd_pcm_lib_write(substream, compat_ptr(buf), frames);
  	else
  		err = snd_pcm_lib_read(substream, compat_ptr(buf), frames);
  	if (err < 0)
  		return err;
  	/* copy the result */
  	if (put_user(err, &data32->result))
  		return -EFAULT;
  	return 0;
  }
  
  
  /* snd_xfern needs remapping of bufs */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
382
  struct snd_xfern32 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
385
386
387
388
389
390
391
392
393
  	s32 result;
  	u32 bufs;  /* this is void **; */
  	u32 frames;
  };
  
  /*
   * xfern ioctl nees to copy (up to) 128 pointers on stack.
   * although we may pass the copied pointers through f_op->ioctl, but the ioctl
   * handler there expands again the same 128 pointers on stack, so it is better
   * to handle the function (calling pcm_readv/writev) directly in this handler.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
394
395
  static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
  				      int dir, struct snd_xfern32 __user *data32)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
400
401
402
403
404
405
406
  {
  	compat_caddr_t buf;
  	compat_caddr_t __user *bufptr;
  	u32 frames;
  	void __user **bufs;
  	int err, ch, i;
  
  	if (! substream->runtime)
  		return -ENOTTY;
  	if (substream->stream != dir)
  		return -EINVAL;
e5e9a770c   Takashi Iwai   ALSA: pcm: Check ...
407
408
  	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
  		return -EBADFD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  
  	if ((ch = substream->runtime->channels) > 128)
  		return -EINVAL;
  	if (get_user(buf, &data32->bufs) ||
  	    get_user(frames, &data32->frames))
  		return -EFAULT;
  	bufptr = compat_ptr(buf);
  	bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL);
  	if (bufs == NULL)
  		return -ENOMEM;
  	for (i = 0; i < ch; i++) {
  		u32 ptr;
  		if (get_user(ptr, bufptr)) {
  			kfree(bufs);
  			return -EFAULT;
  		}
ca9380fd6   Julia Lawall   ALSA: sound/core/...
425
  		bufs[i] = compat_ptr(ptr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
428
429
430
431
432
433
434
435
436
437
438
  		bufptr++;
  	}
  	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
  		err = snd_pcm_lib_writev(substream, bufs, frames);
  	else
  		err = snd_pcm_lib_readv(substream, bufs, frames);
  	if (err >= 0) {
  		if (put_user(err, &data32->result))
  			err = -EFAULT;
  	}
  	kfree(bufs);
  	return err;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
439
  struct snd_pcm_mmap_status32 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
442
443
444
  	s32 state;
  	s32 pad1;
  	u32 hw_ptr;
  	struct compat_timespec tstamp;
  	s32 suspended_state;
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
445
  	struct compat_timespec audio_tstamp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
  } __attribute__((packed));
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
447
  struct snd_pcm_mmap_control32 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
  	u32 appl_ptr;
  	u32 avail_min;
  };
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
451
  struct snd_pcm_sync_ptr32 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
453
  	u32 flags;
  	union {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
454
  		struct snd_pcm_mmap_status32 status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
456
457
  		unsigned char reserved[64];
  	} s;
  	union {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
458
  		struct snd_pcm_mmap_control32 control;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
  		unsigned char reserved[64];
  	} c;
  } __attribute__((packed));
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
462
463
  static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
  					 struct snd_pcm_sync_ptr32 __user *src)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
465
466
467
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	volatile struct snd_pcm_mmap_status *status;
  	volatile struct snd_pcm_mmap_control *control;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
  	u32 sflags;
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
469
470
  	struct snd_pcm_mmap_control scontrol;
  	struct snd_pcm_mmap_status sstatus;
b27113102   Takashi Iwai   [ALSA] Fix PCM 32...
471
  	snd_pcm_uframes_t boundary;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
  	int err;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
473
474
  	if (snd_BUG_ON(!runtime))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
478
479
480
481
482
483
484
485
486
  
  	if (get_user(sflags, &src->flags) ||
  	    get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
  	    get_user(scontrol.avail_min, &src->c.control.avail_min))
  		return -EFAULT;
  	if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
  		err = snd_pcm_hwsync(substream);
  		if (err < 0)
  			return err;
  	}
  	status = runtime->status;
  	control = runtime->control;
b27113102   Takashi Iwai   [ALSA] Fix PCM 32...
487
  	boundary = recalculate_boundary(runtime);
7153a558a   Takashi Iwai   [ALSA] pcm - Fix ...
488
489
  	if (! boundary)
  		boundary = 0x7fffffff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
  	snd_pcm_stream_lock_irq(substream);
b27113102   Takashi Iwai   [ALSA] Fix PCM 32...
491
  	/* FIXME: we should consider the boundary for the sync from app */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
493
494
  	if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
  		control->appl_ptr = scontrol.appl_ptr;
  	else
b27113102   Takashi Iwai   [ALSA] Fix PCM 32...
495
  		scontrol.appl_ptr = control->appl_ptr % boundary;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
498
499
500
  	if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
  		control->avail_min = scontrol.avail_min;
  	else
  		scontrol.avail_min = control->avail_min;
  	sstatus.state = status->state;
b27113102   Takashi Iwai   [ALSA] Fix PCM 32...
501
  	sstatus.hw_ptr = status->hw_ptr % boundary;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
  	sstatus.tstamp = status->tstamp;
  	sstatus.suspended_state = status->suspended_state;
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
504
  	sstatus.audio_tstamp = status->audio_tstamp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
  	snd_pcm_stream_unlock_irq(substream);
  	if (put_user(sstatus.state, &src->s.status.state) ||
  	    put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
508
  	    compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
  	    put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
510
511
  	    compat_put_timespec(&sstatus.audio_tstamp,
  		    &src->s.status.audio_tstamp) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
514
515
516
517
  	    put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
  	    put_user(scontrol.avail_min, &src->c.control.avail_min))
  		return -EFAULT;
  
  	return 0;
  }
513ace79b   Takashi Iwai   ALSA: pcm: Fix io...
518
519
520
521
522
523
524
525
526
  #ifdef CONFIG_X86_X32
  /* X32 ABI has 64bit timespec and 64bit alignment */
  struct snd_pcm_mmap_status_x32 {
  	s32 state;
  	s32 pad1;
  	u32 hw_ptr;
  	u32 pad2; /* alignment */
  	struct timespec tstamp;
  	s32 suspended_state;
c9adcdbc6   Baolin Wang   ALSA: pcm: Fix st...
527
  	s32 pad3;
513ace79b   Takashi Iwai   ALSA: pcm: Fix io...
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
  	struct timespec audio_tstamp;
  } __packed;
  
  struct snd_pcm_mmap_control_x32 {
  	u32 appl_ptr;
  	u32 avail_min;
  };
  
  struct snd_pcm_sync_ptr_x32 {
  	u32 flags;
  	u32 rsvd; /* alignment */
  	union {
  		struct snd_pcm_mmap_status_x32 status;
  		unsigned char reserved[64];
  	} s;
  	union {
  		struct snd_pcm_mmap_control_x32 control;
  		unsigned char reserved[64];
  	} c;
  } __packed;
  
  static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
  				      struct snd_pcm_sync_ptr_x32 __user *src)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	volatile struct snd_pcm_mmap_status *status;
  	volatile struct snd_pcm_mmap_control *control;
  	u32 sflags;
  	struct snd_pcm_mmap_control scontrol;
  	struct snd_pcm_mmap_status sstatus;
  	snd_pcm_uframes_t boundary;
  	int err;
  
  	if (snd_BUG_ON(!runtime))
  		return -EINVAL;
  
  	if (get_user(sflags, &src->flags) ||
  	    get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
  	    get_user(scontrol.avail_min, &src->c.control.avail_min))
  		return -EFAULT;
  	if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
  		err = snd_pcm_hwsync(substream);
  		if (err < 0)
  			return err;
  	}
  	status = runtime->status;
  	control = runtime->control;
  	boundary = recalculate_boundary(runtime);
  	if (!boundary)
  		boundary = 0x7fffffff;
  	snd_pcm_stream_lock_irq(substream);
  	/* FIXME: we should consider the boundary for the sync from app */
  	if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
  		control->appl_ptr = scontrol.appl_ptr;
  	else
  		scontrol.appl_ptr = control->appl_ptr % boundary;
  	if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
  		control->avail_min = scontrol.avail_min;
  	else
  		scontrol.avail_min = control->avail_min;
  	sstatus.state = status->state;
  	sstatus.hw_ptr = status->hw_ptr % boundary;
  	sstatus.tstamp = status->tstamp;
  	sstatus.suspended_state = status->suspended_state;
  	sstatus.audio_tstamp = status->audio_tstamp;
  	snd_pcm_stream_unlock_irq(substream);
  	if (put_user(sstatus.state, &src->s.status.state) ||
  	    put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
  	    put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
  	    put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
  	    put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) ||
  	    put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
  	    put_user(scontrol.avail_min, &src->c.control.avail_min))
  		return -EFAULT;
  
  	return 0;
  }
  #endif /* CONFIG_X86_X32 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
607
608
609
  
  /*
   */
  enum {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
610
611
612
613
  	SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32),
  	SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
  	SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
  	SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
5442a73a0   Pierre-Louis Bossart   ALSA: core: pass ...
614
  	SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
  	SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
616
  	SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
  	SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
  	SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32),
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
619
620
621
622
623
  	SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct snd_xferi32),
  	SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32),
  	SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
  	SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
  	SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),
513ace79b   Takashi Iwai   ALSA: pcm: Fix io...
624
625
626
627
628
629
  #ifdef CONFIG_X86_X32
  	SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info),
  	SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32),
  	SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32),
  	SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
  #endif /* CONFIG_X86_X32 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
632
633
  };
  
  static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
634
635
  	struct snd_pcm_file *pcm_file;
  	struct snd_pcm_substream *substream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
640
641
642
643
644
645
646
647
648
649
  	void __user *argp = compat_ptr(arg);
  
  	pcm_file = file->private_data;
  	if (! pcm_file)
  		return -ENOTTY;
  	substream = pcm_file->substream;
  	if (! substream)
  		return -ENOTTY;
  
  	/*
  	 * When PCM is used on 32bit mode, we need to disable
  	 * mmap of PCM status/control records because of the size
  	 * incompatibility.
  	 */
548a648b9   Takashi Iwai   [ALSA] Fix contro...
650
  	pcm_file->no_compat_mmap = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
654
  
  	switch (cmd) {
  	case SNDRV_PCM_IOCTL_PVERSION:
  	case SNDRV_PCM_IOCTL_INFO:
5a7f26192   Takashi Iwai   [ALSA] Add SNDRV_...
655
  	case SNDRV_PCM_IOCTL_TSTAMP:
6b587ef9a   Takashi Iwai   [ALSA] Fix old ts...
656
  	case SNDRV_PCM_IOCTL_TTSTAMP:
4b671f577   Takashi Iwai   ALSA: pcm: Add an...
657
  	case SNDRV_PCM_IOCTL_USER_PVERSION:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
659
660
661
662
663
664
665
666
667
668
669
  	case SNDRV_PCM_IOCTL_HWSYNC:
  	case SNDRV_PCM_IOCTL_PREPARE:
  	case SNDRV_PCM_IOCTL_RESET:
  	case SNDRV_PCM_IOCTL_START:
  	case SNDRV_PCM_IOCTL_DROP:
  	case SNDRV_PCM_IOCTL_DRAIN:
  	case SNDRV_PCM_IOCTL_PAUSE:
  	case SNDRV_PCM_IOCTL_HW_FREE:
  	case SNDRV_PCM_IOCTL_RESUME:
  	case SNDRV_PCM_IOCTL_XRUN:
  	case SNDRV_PCM_IOCTL_LINK:
  	case SNDRV_PCM_IOCTL_UNLINK:
67616feda   Takashi Iwai   ALSA: pcm: Unify ...
670
  		return snd_pcm_common_ioctl(file, substream, cmd, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671
672
673
674
675
676
677
  	case SNDRV_PCM_IOCTL_HW_REFINE32:
  		return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
  	case SNDRV_PCM_IOCTL_HW_PARAMS32:
  		return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
  	case SNDRV_PCM_IOCTL_SW_PARAMS32:
  		return snd_pcm_ioctl_sw_params_compat(substream, argp);
  	case SNDRV_PCM_IOCTL_STATUS32:
5442a73a0   Pierre-Louis Bossart   ALSA: core: pass ...
678
679
680
  		return snd_pcm_status_user_compat(substream, argp, false);
  	case SNDRV_PCM_IOCTL_STATUS_EXT32:
  		return snd_pcm_status_user_compat(substream, argp, true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
  	case SNDRV_PCM_IOCTL_SYNC_PTR32:
  		return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
  	case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
  		return snd_pcm_ioctl_channel_info_compat(substream, argp);
  	case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
  		return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
  	case SNDRV_PCM_IOCTL_READI_FRAMES32:
  		return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
  	case SNDRV_PCM_IOCTL_WRITEN_FRAMES32:
  		return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
  	case SNDRV_PCM_IOCTL_READN_FRAMES32:
  		return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
  	case SNDRV_PCM_IOCTL_DELAY32:
  		return snd_pcm_ioctl_delay_compat(substream, argp);
  	case SNDRV_PCM_IOCTL_REWIND32:
  		return snd_pcm_ioctl_rewind_compat(substream, argp);
  	case SNDRV_PCM_IOCTL_FORWARD32:
  		return snd_pcm_ioctl_forward_compat(substream, argp);
513ace79b   Takashi Iwai   ALSA: pcm: Fix io...
699
700
701
702
703
704
705
706
707
708
  #ifdef CONFIG_X86_X32
  	case SNDRV_PCM_IOCTL_STATUS_X32:
  		return snd_pcm_status_user_x32(substream, argp, false);
  	case SNDRV_PCM_IOCTL_STATUS_EXT_X32:
  		return snd_pcm_status_user_x32(substream, argp, true);
  	case SNDRV_PCM_IOCTL_SYNC_PTR_X32:
  		return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
  	case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
  		return snd_pcm_ioctl_channel_info_x32(substream, argp);
  #endif /* CONFIG_X86_X32 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
711
712
  	}
  
  	return -ENOIOCTLCMD;
  }