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