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