Commit | Line | Data |
---|---|---|
cf664a64 PDM |
1 | /* |
2 | * ioctl32.c: Conversion between 32bit and 64bit native ioctls. | |
3 | * Separated from fs stuff by Arnd Bergmann <arnd@arndb.de> | |
4 | * | |
5 | * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) | |
6 | * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) | |
7 | * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs | |
a2531293 | 8 | * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz) |
cf664a64 | 9 | * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be) |
92f45bad | 10 | * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> |
cf664a64 PDM |
11 | * |
12 | * These routines maintain argument size conversion between 32bit and 64bit | |
13 | * ioctls. | |
14 | */ | |
15 | ||
0d0fbf81 | 16 | #include <linux/compat.h> |
133b7354 | 17 | #include <linux/module.h> |
b9d0aa6e | 18 | #include <linux/videodev2.h> |
ed45ce2c | 19 | #include <linux/v4l2-subdev.h> |
b9d0aa6e | 20 | #include <media/v4l2-dev.h> |
b8c601e8 HV |
21 | #include <media/v4l2-fh.h> |
22 | #include <media/v4l2-ctrls.h> | |
2864462e | 23 | #include <media/v4l2-ioctl.h> |
0d0fbf81 | 24 | |
a1dfb4c4 DM |
25 | /* Use the same argument order as copy_in_user */ |
26 | #define assign_in_user(to, from) \ | |
27 | ({ \ | |
28 | typeof(*from) __assign_tmp; \ | |
29 | \ | |
30 | get_user(__assign_tmp, from) || put_user(__assign_tmp, to); \ | |
31 | }) | |
32 | ||
069b7479 | 33 | static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
92f45bad | 34 | { |
069b7479 | 35 | long ret = -ENOIOCTLCMD; |
92f45bad HV |
36 | |
37 | if (file->f_op->unlocked_ioctl) | |
38 | ret = file->f_op->unlocked_ioctl(file, cmd, arg); | |
92f45bad HV |
39 | |
40 | return ret; | |
41 | } | |
42 | ||
43 | ||
d1f81da2 | 44 | struct v4l2_clip32 { |
cf664a64 | 45 | struct v4l2_rect c; |
6e6a8b5a | 46 | compat_caddr_t next; |
cf664a64 PDM |
47 | }; |
48 | ||
d1f81da2 | 49 | struct v4l2_window32 { |
cf664a64 | 50 | struct v4l2_rect w; |
4a3fad70 | 51 | __u32 field; /* enum v4l2_field */ |
cf664a64 PDM |
52 | __u32 chromakey; |
53 | compat_caddr_t clips; /* actually struct v4l2_clip32 * */ | |
54 | __u32 clipcount; | |
55 | compat_caddr_t bitmap; | |
025a26fa | 56 | __u8 global_alpha; |
cf664a64 PDM |
57 | }; |
58 | ||
a1dfb4c4 DM |
59 | static int get_v4l2_window32(struct v4l2_window __user *kp, |
60 | struct v4l2_window32 __user *up, | |
61 | void __user *aux_buf, u32 aux_space) | |
cf664a64 | 62 | { |
a751be5b HV |
63 | struct v4l2_clip32 __user *uclips; |
64 | struct v4l2_clip __user *kclips; | |
65 | compat_caddr_t p; | |
a1dfb4c4 | 66 | u32 clipcount; |
a751be5b | 67 | |
333b1e9f | 68 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || |
a1dfb4c4 DM |
69 | copy_in_user(&kp->w, &up->w, sizeof(up->w)) || |
70 | assign_in_user(&kp->field, &up->field) || | |
71 | assign_in_user(&kp->chromakey, &up->chromakey) || | |
72 | assign_in_user(&kp->global_alpha, &up->global_alpha) || | |
73 | get_user(clipcount, &up->clipcount) || | |
74 | put_user(clipcount, &kp->clipcount)) | |
b7b957d4 | 75 | return -EFAULT; |
a1dfb4c4 | 76 | if (clipcount > 2048) |
cf664a64 | 77 | return -EINVAL; |
a1dfb4c4 DM |
78 | if (!clipcount) |
79 | return put_user(NULL, &kp->clips); | |
cf664a64 | 80 | |
a751be5b HV |
81 | if (get_user(p, &up->clips)) |
82 | return -EFAULT; | |
83 | uclips = compat_ptr(p); | |
a1dfb4c4 DM |
84 | if (aux_space < clipcount * sizeof(*kclips)) |
85 | return -EFAULT; | |
86 | kclips = aux_buf; | |
87 | if (put_user(kclips, &kp->clips)) | |
88 | return -EFAULT; | |
89 | ||
90 | while (clipcount--) { | |
a751be5b | 91 | if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c))) |
5b1a43d7 | 92 | return -EFAULT; |
a1dfb4c4 | 93 | if (put_user(clipcount ? kclips + 1 : NULL, &kclips->next)) |
a751be5b HV |
94 | return -EFAULT; |
95 | uclips++; | |
96 | kclips++; | |
97 | } | |
cf664a64 PDM |
98 | return 0; |
99 | } | |
100 | ||
a1dfb4c4 DM |
101 | static int put_v4l2_window32(struct v4l2_window __user *kp, |
102 | struct v4l2_window32 __user *up) | |
cf664a64 | 103 | { |
85ea29f1 | 104 | struct v4l2_clip __user *kclips; |
a751be5b | 105 | struct v4l2_clip32 __user *uclips; |
a751be5b | 106 | compat_caddr_t p; |
a1dfb4c4 DM |
107 | u32 clipcount; |
108 | ||
109 | if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) || | |
110 | assign_in_user(&up->field, &kp->field) || | |
111 | assign_in_user(&up->chromakey, &kp->chromakey) || | |
112 | assign_in_user(&up->global_alpha, &kp->global_alpha) || | |
113 | get_user(clipcount, &kp->clipcount) || | |
114 | put_user(clipcount, &up->clipcount)) | |
b7b957d4 | 115 | return -EFAULT; |
a1dfb4c4 | 116 | if (!clipcount) |
a751be5b HV |
117 | return 0; |
118 | ||
85ea29f1 MCC |
119 | if (get_user(kclips, &kp->clips)) |
120 | return -EFAULT; | |
a751be5b HV |
121 | if (get_user(p, &up->clips)) |
122 | return -EFAULT; | |
123 | uclips = compat_ptr(p); | |
a1dfb4c4 | 124 | while (clipcount--) { |
a751be5b HV |
125 | if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c))) |
126 | return -EFAULT; | |
127 | uclips++; | |
128 | kclips++; | |
129 | } | |
cf664a64 PDM |
130 | return 0; |
131 | } | |
132 | ||
d1f81da2 | 133 | struct v4l2_format32 { |
6016af82 | 134 | __u32 type; /* enum v4l2_buf_type */ |
d1f81da2 | 135 | union { |
92f45bad | 136 | struct v4l2_pix_format pix; |
52a3082f | 137 | struct v4l2_pix_format_mplane pix_mp; |
92f45bad HV |
138 | struct v4l2_window32 win; |
139 | struct v4l2_vbi_format vbi; | |
140 | struct v4l2_sliced_vbi_format sliced; | |
d5beb67b | 141 | struct v4l2_sdr_format sdr; |
fb9ffa6a | 142 | struct v4l2_meta_format meta; |
d1f81da2 | 143 | __u8 raw_data[200]; /* user-defined */ |
cf664a64 PDM |
144 | } fmt; |
145 | }; | |
146 | ||
09362ec2 GL |
147 | /** |
148 | * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument | |
149 | * @index: on return, index of the first created buffer | |
150 | * @count: entry: number of requested buffers, | |
151 | * return: number of created buffers | |
152 | * @memory: buffer memory type | |
153 | * @format: frame format, for which buffers are requested | |
154 | * @reserved: future extensions | |
155 | */ | |
2150158b | 156 | struct v4l2_create_buffers32 { |
09362ec2 | 157 | __u32 index; |
2150158b | 158 | __u32 count; |
6016af82 | 159 | __u32 memory; /* enum v4l2_memory */ |
09362ec2 | 160 | struct v4l2_format32 format; |
2150158b GL |
161 | __u32 reserved[8]; |
162 | }; | |
163 | ||
a1dfb4c4 DM |
164 | static int __bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size) |
165 | { | |
166 | u32 type; | |
167 | ||
168 | if (get_user(type, &up->type)) | |
169 | return -EFAULT; | |
170 | ||
171 | switch (type) { | |
172 | case V4L2_BUF_TYPE_VIDEO_OVERLAY: | |
173 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: { | |
174 | u32 clipcount; | |
175 | ||
176 | if (get_user(clipcount, &up->fmt.win.clipcount)) | |
177 | return -EFAULT; | |
178 | if (clipcount > 2048) | |
179 | return -EINVAL; | |
180 | *size = clipcount * sizeof(struct v4l2_clip); | |
181 | return 0; | |
182 | } | |
183 | default: | |
184 | *size = 0; | |
185 | return 0; | |
186 | } | |
187 | } | |
188 | ||
189 | static int bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size) | |
190 | { | |
191 | if (!access_ok(VERIFY_READ, up, sizeof(*up))) | |
192 | return -EFAULT; | |
193 | return __bufsize_v4l2_format(up, size); | |
194 | } | |
195 | ||
196 | static int __get_v4l2_format32(struct v4l2_format __user *kp, | |
197 | struct v4l2_format32 __user *up, | |
198 | void __user *aux_buf, u32 aux_space) | |
cf664a64 | 199 | { |
a1dfb4c4 DM |
200 | u32 type; |
201 | ||
202 | if (get_user(type, &up->type) || put_user(type, &kp->type)) | |
ec77581a GL |
203 | return -EFAULT; |
204 | ||
a1dfb4c4 | 205 | switch (type) { |
cf664a64 | 206 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
92f45bad | 207 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
a1dfb4c4 DM |
208 | return copy_in_user(&kp->fmt.pix, &up->fmt.pix, |
209 | sizeof(kp->fmt.pix)) ? -EFAULT : 0; | |
52a3082f PO |
210 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: |
211 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | |
a1dfb4c4 DM |
212 | return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp, |
213 | sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0; | |
cf664a64 | 214 | case V4L2_BUF_TYPE_VIDEO_OVERLAY: |
92f45bad | 215 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: |
a1dfb4c4 DM |
216 | return get_v4l2_window32(&kp->fmt.win, &up->fmt.win, |
217 | aux_buf, aux_space); | |
cf664a64 | 218 | case V4L2_BUF_TYPE_VBI_CAPTURE: |
92f45bad | 219 | case V4L2_BUF_TYPE_VBI_OUTPUT: |
a1dfb4c4 DM |
220 | return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi, |
221 | sizeof(kp->fmt.vbi)) ? -EFAULT : 0; | |
92f45bad HV |
222 | case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: |
223 | case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: | |
a1dfb4c4 DM |
224 | return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced, |
225 | sizeof(kp->fmt.sliced)) ? -EFAULT : 0; | |
d5beb67b | 226 | case V4L2_BUF_TYPE_SDR_CAPTURE: |
9effc72f | 227 | case V4L2_BUF_TYPE_SDR_OUTPUT: |
a1dfb4c4 DM |
228 | return copy_in_user(&kp->fmt.sdr, &up->fmt.sdr, |
229 | sizeof(kp->fmt.sdr)) ? -EFAULT : 0; | |
fb9ffa6a | 230 | case V4L2_BUF_TYPE_META_CAPTURE: |
a1dfb4c4 DM |
231 | return copy_in_user(&kp->fmt.meta, &up->fmt.meta, |
232 | sizeof(kp->fmt.meta)) ? -EFAULT : 0; | |
cf664a64 | 233 | default: |
92f45bad | 234 | return -EINVAL; |
cf664a64 PDM |
235 | } |
236 | } | |
237 | ||
a1dfb4c4 DM |
238 | static int get_v4l2_format32(struct v4l2_format __user *kp, |
239 | struct v4l2_format32 __user *up, | |
240 | void __user *aux_buf, u32 aux_space) | |
2150158b | 241 | { |
333b1e9f | 242 | if (!access_ok(VERIFY_READ, up, sizeof(*up))) |
ec77581a | 243 | return -EFAULT; |
a1dfb4c4 | 244 | return __get_v4l2_format32(kp, up, aux_buf, aux_space); |
2150158b GL |
245 | } |
246 | ||
a1dfb4c4 DM |
247 | static int bufsize_v4l2_create(struct v4l2_create_buffers32 __user *up, |
248 | u32 *size) | |
249 | { | |
250 | if (!access_ok(VERIFY_READ, up, sizeof(*up))) | |
251 | return -EFAULT; | |
252 | return __bufsize_v4l2_format(&up->format, size); | |
253 | } | |
254 | ||
255 | static int get_v4l2_create32(struct v4l2_create_buffers __user *kp, | |
256 | struct v4l2_create_buffers32 __user *up, | |
257 | void __user *aux_buf, u32 aux_space) | |
2150158b | 258 | { |
333b1e9f | 259 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || |
a1dfb4c4 DM |
260 | copy_in_user(kp, up, |
261 | offsetof(struct v4l2_create_buffers32, format))) | |
ec77581a | 262 | return -EFAULT; |
a1dfb4c4 DM |
263 | return __get_v4l2_format32(&kp->format, &up->format, |
264 | aux_buf, aux_space); | |
2150158b GL |
265 | } |
266 | ||
a1dfb4c4 DM |
267 | static int __put_v4l2_format32(struct v4l2_format __user *kp, |
268 | struct v4l2_format32 __user *up) | |
cf664a64 | 269 | { |
a1dfb4c4 DM |
270 | u32 type; |
271 | ||
272 | if (get_user(type, &kp->type)) | |
6ed9b285 GL |
273 | return -EFAULT; |
274 | ||
a1dfb4c4 | 275 | switch (type) { |
cf664a64 | 276 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
92f45bad | 277 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: |
a1dfb4c4 | 278 | return copy_in_user(&up->fmt.pix, &kp->fmt.pix, |
486c5215 | 279 | sizeof(kp->fmt.pix)) ? -EFAULT : 0; |
52a3082f PO |
280 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: |
281 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | |
a1dfb4c4 | 282 | return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp, |
486c5215 | 283 | sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0; |
cf664a64 | 284 | case V4L2_BUF_TYPE_VIDEO_OVERLAY: |
92f45bad | 285 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: |
cf664a64 PDM |
286 | return put_v4l2_window32(&kp->fmt.win, &up->fmt.win); |
287 | case V4L2_BUF_TYPE_VBI_CAPTURE: | |
92f45bad | 288 | case V4L2_BUF_TYPE_VBI_OUTPUT: |
a1dfb4c4 | 289 | return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi, |
486c5215 | 290 | sizeof(kp->fmt.vbi)) ? -EFAULT : 0; |
92f45bad HV |
291 | case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: |
292 | case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: | |
a1dfb4c4 | 293 | return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced, |
486c5215 | 294 | sizeof(kp->fmt.sliced)) ? -EFAULT : 0; |
d5beb67b | 295 | case V4L2_BUF_TYPE_SDR_CAPTURE: |
9effc72f | 296 | case V4L2_BUF_TYPE_SDR_OUTPUT: |
a1dfb4c4 | 297 | return copy_in_user(&up->fmt.sdr, &kp->fmt.sdr, |
486c5215 | 298 | sizeof(kp->fmt.sdr)) ? -EFAULT : 0; |
fb9ffa6a | 299 | case V4L2_BUF_TYPE_META_CAPTURE: |
a1dfb4c4 | 300 | return copy_in_user(&up->fmt.meta, &kp->fmt.meta, |
486c5215 | 301 | sizeof(kp->fmt.meta)) ? -EFAULT : 0; |
cf664a64 | 302 | default: |
92f45bad | 303 | return -EINVAL; |
cf664a64 PDM |
304 | } |
305 | } | |
306 | ||
a1dfb4c4 DM |
307 | static int put_v4l2_format32(struct v4l2_format __user *kp, |
308 | struct v4l2_format32 __user *up) | |
2150158b | 309 | { |
333b1e9f | 310 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) |
2150158b GL |
311 | return -EFAULT; |
312 | return __put_v4l2_format32(kp, up); | |
313 | } | |
314 | ||
a1dfb4c4 DM |
315 | static int put_v4l2_create32(struct v4l2_create_buffers __user *kp, |
316 | struct v4l2_create_buffers32 __user *up) | |
2150158b | 317 | { |
333b1e9f | 318 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || |
a1dfb4c4 DM |
319 | copy_in_user(up, kp, |
320 | offsetof(struct v4l2_create_buffers32, format)) || | |
321 | copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved))) | |
6ed9b285 | 322 | return -EFAULT; |
2150158b GL |
323 | return __put_v4l2_format32(&kp->format, &up->format); |
324 | } | |
325 | ||
d1f81da2 | 326 | struct v4l2_standard32 { |
cf664a64 | 327 | __u32 index; |
655e9780 | 328 | compat_u64 id; |
cf664a64 PDM |
329 | __u8 name[24]; |
330 | struct v4l2_fract frameperiod; /* Frames, not fields */ | |
331 | __u32 framelines; | |
332 | __u32 reserved[4]; | |
333 | }; | |
334 | ||
a1dfb4c4 DM |
335 | static int get_v4l2_standard32(struct v4l2_standard __user *kp, |
336 | struct v4l2_standard32 __user *up) | |
cf664a64 PDM |
337 | { |
338 | /* other fields are not set by the user, nor used by the driver */ | |
333b1e9f | 339 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || |
a1dfb4c4 | 340 | assign_in_user(&kp->index, &up->index)) |
a113bc78 GM |
341 | return -EFAULT; |
342 | return 0; | |
cf664a64 PDM |
343 | } |
344 | ||
a1dfb4c4 DM |
345 | static int put_v4l2_standard32(struct v4l2_standard __user *kp, |
346 | struct v4l2_standard32 __user *up) | |
cf664a64 | 347 | { |
333b1e9f | 348 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || |
a1dfb4c4 DM |
349 | assign_in_user(&up->index, &kp->index) || |
350 | assign_in_user(&up->id, &kp->id) || | |
351 | copy_in_user(up->name, kp->name, sizeof(up->name)) || | |
352 | copy_in_user(&up->frameperiod, &kp->frameperiod, | |
353 | sizeof(up->frameperiod)) || | |
354 | assign_in_user(&up->framelines, &kp->framelines) || | |
355 | copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved))) | |
b7b957d4 | 356 | return -EFAULT; |
a113bc78 GM |
357 | return 0; |
358 | } | |
359 | ||
52a3082f PO |
360 | struct v4l2_plane32 { |
361 | __u32 bytesused; | |
362 | __u32 length; | |
363 | union { | |
364 | __u32 mem_offset; | |
365 | compat_long_t userptr; | |
051c7788 | 366 | __s32 fd; |
52a3082f PO |
367 | } m; |
368 | __u32 data_offset; | |
369 | __u32 reserved[11]; | |
370 | }; | |
371 | ||
d1f81da2 | 372 | struct v4l2_buffer32 { |
cf664a64 | 373 | __u32 index; |
6016af82 | 374 | __u32 type; /* enum v4l2_buf_type */ |
cf664a64 PDM |
375 | __u32 bytesused; |
376 | __u32 flags; | |
6016af82 | 377 | __u32 field; /* enum v4l2_field */ |
cf664a64 PDM |
378 | struct compat_timeval timestamp; |
379 | struct v4l2_timecode timecode; | |
380 | __u32 sequence; | |
381 | ||
382 | /* memory location */ | |
6016af82 | 383 | __u32 memory; /* enum v4l2_memory */ |
cf664a64 PDM |
384 | union { |
385 | __u32 offset; | |
386 | compat_long_t userptr; | |
52a3082f | 387 | compat_caddr_t planes; |
051c7788 | 388 | __s32 fd; |
cf664a64 PDM |
389 | } m; |
390 | __u32 length; | |
2b719d7b | 391 | __u32 reserved2; |
cf664a64 PDM |
392 | __u32 reserved; |
393 | }; | |
394 | ||
a1dfb4c4 DM |
395 | static int get_v4l2_plane32(struct v4l2_plane __user *up, |
396 | struct v4l2_plane32 __user *up32, | |
b7b957d4 | 397 | enum v4l2_memory memory) |
52a3082f | 398 | { |
a1dfb4c4 | 399 | compat_ulong_t p; |
52a3082f PO |
400 | |
401 | if (copy_in_user(up, up32, 2 * sizeof(__u32)) || | |
b7b957d4 | 402 | copy_in_user(&up->data_offset, &up32->data_offset, |
333b1e9f | 403 | sizeof(up->data_offset))) |
52a3082f PO |
404 | return -EFAULT; |
405 | ||
8ed5a59d HV |
406 | switch (memory) { |
407 | case V4L2_MEMORY_MMAP: | |
408 | case V4L2_MEMORY_OVERLAY: | |
409 | if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset, | |
410 | sizeof(up32->m.mem_offset))) | |
411 | return -EFAULT; | |
412 | break; | |
413 | case V4L2_MEMORY_USERPTR: | |
a1dfb4c4 DM |
414 | if (get_user(p, &up32->m.userptr) || |
415 | put_user((unsigned long)compat_ptr(p), &up->m.userptr)) | |
52a3082f | 416 | return -EFAULT; |
8ed5a59d HV |
417 | break; |
418 | case V4L2_MEMORY_DMABUF: | |
333b1e9f | 419 | if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(up32->m.fd))) |
051c7788 | 420 | return -EFAULT; |
8ed5a59d | 421 | break; |
52a3082f PO |
422 | } |
423 | ||
424 | return 0; | |
425 | } | |
426 | ||
a1dfb4c4 DM |
427 | static int put_v4l2_plane32(struct v4l2_plane __user *up, |
428 | struct v4l2_plane32 __user *up32, | |
b7b957d4 | 429 | enum v4l2_memory memory) |
52a3082f | 430 | { |
8ed5a59d HV |
431 | unsigned long p; |
432 | ||
52a3082f | 433 | if (copy_in_user(up32, up, 2 * sizeof(__u32)) || |
b7b957d4 | 434 | copy_in_user(&up32->data_offset, &up->data_offset, |
333b1e9f | 435 | sizeof(up->data_offset))) |
52a3082f PO |
436 | return -EFAULT; |
437 | ||
8ed5a59d HV |
438 | switch (memory) { |
439 | case V4L2_MEMORY_MMAP: | |
440 | case V4L2_MEMORY_OVERLAY: | |
52a3082f | 441 | if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset, |
333b1e9f | 442 | sizeof(up->m.mem_offset))) |
52a3082f | 443 | return -EFAULT; |
8ed5a59d HV |
444 | break; |
445 | case V4L2_MEMORY_USERPTR: | |
446 | if (get_user(p, &up->m.userptr) || | |
447 | put_user((compat_ulong_t)ptr_to_compat((__force void *)p), | |
448 | &up32->m.userptr)) | |
449 | return -EFAULT; | |
450 | break; | |
451 | case V4L2_MEMORY_DMABUF: | |
a1dfb4c4 | 452 | if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(up->m.fd))) |
051c7788 | 453 | return -EFAULT; |
8ed5a59d HV |
454 | break; |
455 | } | |
52a3082f PO |
456 | |
457 | return 0; | |
458 | } | |
459 | ||
a1dfb4c4 DM |
460 | static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *up, u32 *size) |
461 | { | |
462 | u32 type; | |
463 | u32 length; | |
464 | ||
465 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || | |
466 | get_user(type, &up->type) || | |
467 | get_user(length, &up->length)) | |
468 | return -EFAULT; | |
469 | ||
470 | if (V4L2_TYPE_IS_MULTIPLANAR(type)) { | |
471 | if (length > VIDEO_MAX_PLANES) | |
472 | return -EINVAL; | |
473 | ||
474 | /* | |
475 | * We don't really care if userspace decides to kill itself | |
476 | * by passing a very big length value | |
477 | */ | |
478 | *size = length * sizeof(struct v4l2_plane); | |
479 | } else { | |
480 | *size = 0; | |
481 | } | |
482 | return 0; | |
483 | } | |
484 | ||
485 | static int get_v4l2_buffer32(struct v4l2_buffer __user *kp, | |
486 | struct v4l2_buffer32 __user *up, | |
487 | void __user *aux_buf, u32 aux_space) | |
cf664a64 | 488 | { |
a1dfb4c4 DM |
489 | u32 type; |
490 | u32 length; | |
491 | enum v4l2_memory memory; | |
52a3082f PO |
492 | struct v4l2_plane32 __user *uplane32; |
493 | struct v4l2_plane __user *uplane; | |
494 | compat_caddr_t p; | |
52a3082f | 495 | int ret; |
cf664a64 | 496 | |
333b1e9f | 497 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || |
a1dfb4c4 DM |
498 | assign_in_user(&kp->index, &up->index) || |
499 | get_user(type, &up->type) || | |
500 | put_user(type, &kp->type) || | |
501 | assign_in_user(&kp->flags, &up->flags) || | |
502 | get_user(memory, &up->memory) || | |
503 | put_user(memory, &kp->memory) || | |
504 | get_user(length, &up->length) || | |
505 | put_user(length, &kp->length)) | |
b7b957d4 | 506 | return -EFAULT; |
52a3082f | 507 | |
a1dfb4c4 DM |
508 | if (V4L2_TYPE_IS_OUTPUT(type)) |
509 | if (assign_in_user(&kp->bytesused, &up->bytesused) || | |
510 | assign_in_user(&kp->field, &up->field) || | |
511 | assign_in_user(&kp->timestamp.tv_sec, | |
512 | &up->timestamp.tv_sec) || | |
513 | assign_in_user(&kp->timestamp.tv_usec, | |
514 | &up->timestamp.tv_usec)) | |
22c859fa | 515 | return -EFAULT; |
cf664a64 | 516 | |
a1dfb4c4 DM |
517 | if (V4L2_TYPE_IS_MULTIPLANAR(type)) { |
518 | u32 num_planes = length; | |
a56bc171 | 519 | |
a1dfb4c4 DM |
520 | if (num_planes == 0) { |
521 | /* | |
522 | * num_planes == 0 is legal, e.g. when userspace doesn't | |
523 | * need planes array on DQBUF | |
524 | */ | |
525 | return put_user(NULL, &kp->m.planes); | |
cf664a64 | 526 | } |
a1dfb4c4 DM |
527 | if (num_planes > VIDEO_MAX_PLANES) |
528 | return -EINVAL; | |
52a3082f PO |
529 | |
530 | if (get_user(p, &up->m.planes)) | |
a113bc78 | 531 | return -EFAULT; |
52a3082f PO |
532 | |
533 | uplane32 = compat_ptr(p); | |
534 | if (!access_ok(VERIFY_READ, uplane32, | |
a1dfb4c4 | 535 | num_planes * sizeof(*uplane32))) |
52a3082f PO |
536 | return -EFAULT; |
537 | ||
a1dfb4c4 DM |
538 | /* |
539 | * We don't really care if userspace decides to kill itself | |
540 | * by passing a very big num_planes value | |
541 | */ | |
542 | if (aux_space < num_planes * sizeof(*uplane)) | |
543 | return -EFAULT; | |
52a3082f | 544 | |
a1dfb4c4 DM |
545 | uplane = aux_buf; |
546 | if (put_user((__force struct v4l2_plane *)uplane, | |
547 | &kp->m.planes)) | |
548 | return -EFAULT; | |
549 | ||
550 | while (num_planes--) { | |
551 | ret = get_v4l2_plane32(uplane, uplane32, memory); | |
52a3082f PO |
552 | if (ret) |
553 | return ret; | |
a1dfb4c4 DM |
554 | uplane++; |
555 | uplane32++; | |
52a3082f PO |
556 | } |
557 | } else { | |
a1dfb4c4 | 558 | switch (memory) { |
52a3082f | 559 | case V4L2_MEMORY_MMAP: |
8ed5a59d | 560 | case V4L2_MEMORY_OVERLAY: |
a1dfb4c4 | 561 | if (assign_in_user(&kp->m.offset, &up->m.offset)) |
52a3082f PO |
562 | return -EFAULT; |
563 | break; | |
a1dfb4c4 DM |
564 | case V4L2_MEMORY_USERPTR: { |
565 | compat_ulong_t userptr; | |
52a3082f | 566 | |
a1dfb4c4 DM |
567 | if (get_user(userptr, &up->m.userptr) || |
568 | put_user((unsigned long)compat_ptr(userptr), | |
569 | &kp->m.userptr)) | |
570 | return -EFAULT; | |
52a3082f | 571 | break; |
a1dfb4c4 | 572 | } |
051c7788 | 573 | case V4L2_MEMORY_DMABUF: |
a1dfb4c4 | 574 | if (assign_in_user(&kp->m.fd, &up->m.fd)) |
051c7788 SS |
575 | return -EFAULT; |
576 | break; | |
52a3082f | 577 | } |
cf664a64 | 578 | } |
52a3082f | 579 | |
cf664a64 PDM |
580 | return 0; |
581 | } | |
582 | ||
a1dfb4c4 DM |
583 | static int put_v4l2_buffer32(struct v4l2_buffer __user *kp, |
584 | struct v4l2_buffer32 __user *up) | |
cf664a64 | 585 | { |
a1dfb4c4 DM |
586 | u32 type; |
587 | u32 length; | |
588 | enum v4l2_memory memory; | |
52a3082f PO |
589 | struct v4l2_plane32 __user *uplane32; |
590 | struct v4l2_plane __user *uplane; | |
591 | compat_caddr_t p; | |
52a3082f PO |
592 | int ret; |
593 | ||
333b1e9f | 594 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || |
a1dfb4c4 DM |
595 | assign_in_user(&up->index, &kp->index) || |
596 | get_user(type, &kp->type) || | |
597 | put_user(type, &up->type) || | |
598 | assign_in_user(&up->flags, &kp->flags) || | |
599 | get_user(memory, &kp->memory) || | |
600 | put_user(memory, &up->memory)) | |
b7b957d4 | 601 | return -EFAULT; |
52a3082f | 602 | |
a1dfb4c4 DM |
603 | if (assign_in_user(&up->bytesused, &kp->bytesused) || |
604 | assign_in_user(&up->field, &kp->field) || | |
605 | assign_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) || | |
606 | assign_in_user(&up->timestamp.tv_usec, &kp->timestamp.tv_usec) || | |
607 | copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) || | |
608 | assign_in_user(&up->sequence, &kp->sequence) || | |
609 | assign_in_user(&up->reserved2, &kp->reserved2) || | |
610 | assign_in_user(&up->reserved, &kp->reserved) || | |
611 | get_user(length, &kp->length) || | |
612 | put_user(length, &up->length)) | |
b7b957d4 | 613 | return -EFAULT; |
52a3082f | 614 | |
a1dfb4c4 DM |
615 | if (V4L2_TYPE_IS_MULTIPLANAR(type)) { |
616 | u32 num_planes = length; | |
617 | ||
52a3082f PO |
618 | if (num_planes == 0) |
619 | return 0; | |
620 | ||
a1dfb4c4 DM |
621 | if (get_user(uplane, ((__force struct v4l2_plane __user **)&kp->m.planes))) |
622 | return -EFAULT; | |
52a3082f PO |
623 | if (get_user(p, &up->m.planes)) |
624 | return -EFAULT; | |
625 | uplane32 = compat_ptr(p); | |
626 | ||
a1dfb4c4 DM |
627 | while (num_planes--) { |
628 | ret = put_v4l2_plane32(uplane, uplane32, memory); | |
52a3082f PO |
629 | if (ret) |
630 | return ret; | |
631 | ++uplane; | |
632 | ++uplane32; | |
633 | } | |
634 | } else { | |
a1dfb4c4 | 635 | switch (memory) { |
52a3082f | 636 | case V4L2_MEMORY_MMAP: |
8ed5a59d | 637 | case V4L2_MEMORY_OVERLAY: |
a1dfb4c4 | 638 | if (assign_in_user(&up->m.offset, &kp->m.offset)) |
52a3082f PO |
639 | return -EFAULT; |
640 | break; | |
641 | case V4L2_MEMORY_USERPTR: | |
a1dfb4c4 | 642 | if (assign_in_user(&up->m.userptr, &kp->m.userptr)) |
52a3082f PO |
643 | return -EFAULT; |
644 | break; | |
051c7788 | 645 | case V4L2_MEMORY_DMABUF: |
a1dfb4c4 | 646 | if (assign_in_user(&up->m.fd, &kp->m.fd)) |
051c7788 SS |
647 | return -EFAULT; |
648 | break; | |
52a3082f PO |
649 | } |
650 | } | |
651 | ||
cf664a64 PDM |
652 | return 0; |
653 | } | |
654 | ||
d1f81da2 | 655 | struct v4l2_framebuffer32 { |
cf664a64 PDM |
656 | __u32 capability; |
657 | __u32 flags; | |
6e6a8b5a | 658 | compat_caddr_t base; |
d52e2381 LP |
659 | struct { |
660 | __u32 width; | |
661 | __u32 height; | |
662 | __u32 pixelformat; | |
663 | __u32 field; | |
664 | __u32 bytesperline; | |
665 | __u32 sizeimage; | |
666 | __u32 colorspace; | |
667 | __u32 priv; | |
668 | } fmt; | |
cf664a64 PDM |
669 | }; |
670 | ||
a1dfb4c4 DM |
671 | static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp, |
672 | struct v4l2_framebuffer32 __user *up) | |
13d133bc | 673 | { |
a1dfb4c4 | 674 | compat_caddr_t tmp; |
13d133bc | 675 | |
333b1e9f | 676 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || |
b7b957d4 | 677 | get_user(tmp, &up->base) || |
a1dfb4c4 DM |
678 | put_user((__force void *)compat_ptr(tmp), &kp->base) || |
679 | assign_in_user(&kp->capability, &up->capability) || | |
680 | assign_in_user(&kp->flags, &up->flags) || | |
681 | copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt))) | |
b7b957d4 | 682 | return -EFAULT; |
13d133bc PDM |
683 | return 0; |
684 | } | |
685 | ||
a1dfb4c4 DM |
686 | static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp, |
687 | struct v4l2_framebuffer32 __user *up) | |
cf664a64 | 688 | { |
a1dfb4c4 | 689 | void *base; |
cf664a64 | 690 | |
333b1e9f | 691 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || |
a1dfb4c4 DM |
692 | get_user(base, &kp->base) || |
693 | put_user(ptr_to_compat(base), &up->base) || | |
694 | assign_in_user(&up->capability, &kp->capability) || | |
695 | assign_in_user(&up->flags, &kp->flags) || | |
696 | copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt))) | |
b7b957d4 | 697 | return -EFAULT; |
cf664a64 PDM |
698 | return 0; |
699 | } | |
700 | ||
92f45bad HV |
701 | struct v4l2_input32 { |
702 | __u32 index; /* Which input */ | |
703 | __u8 name[32]; /* Label */ | |
704 | __u32 type; /* Type of input */ | |
705 | __u32 audioset; /* Associated audios (bitfield) */ | |
706 | __u32 tuner; /* Associated tuner */ | |
655e9780 | 707 | compat_u64 std; |
92f45bad | 708 | __u32 status; |
037e0865 HV |
709 | __u32 capabilities; |
710 | __u32 reserved[3]; | |
655e9780 | 711 | }; |
92f45bad | 712 | |
a1dfb4c4 DM |
713 | /* |
714 | * The 64-bit v4l2_input struct has extra padding at the end of the struct. | |
715 | * Otherwise it is identical to the 32-bit version. | |
716 | */ | |
717 | static inline int get_v4l2_input32(struct v4l2_input __user *kp, | |
718 | struct v4l2_input32 __user *up) | |
a113bc78 | 719 | { |
a1dfb4c4 | 720 | if (copy_in_user(kp, up, sizeof(*up))) |
5b1a43d7 | 721 | return -EFAULT; |
a113bc78 GM |
722 | return 0; |
723 | } | |
724 | ||
a1dfb4c4 DM |
725 | static inline int put_v4l2_input32(struct v4l2_input __user *kp, |
726 | struct v4l2_input32 __user *up) | |
a113bc78 | 727 | { |
a1dfb4c4 | 728 | if (copy_in_user(up, kp, sizeof(*up))) |
5b1a43d7 | 729 | return -EFAULT; |
a113bc78 GM |
730 | return 0; |
731 | } | |
732 | ||
92f45bad | 733 | struct v4l2_ext_controls32 { |
0f8017be | 734 | __u32 which; |
28c50292 RR |
735 | __u32 count; |
736 | __u32 error_idx; | |
737 | __u32 reserved[2]; | |
738 | compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ | |
eb4eeccc MCC |
739 | }; |
740 | ||
6b5a9492 HV |
741 | struct v4l2_ext_control32 { |
742 | __u32 id; | |
743 | __u32 size; | |
744 | __u32 reserved2[1]; | |
745 | union { | |
746 | __s32 value; | |
747 | __s64 value64; | |
748 | compat_caddr_t string; /* actually char * */ | |
749 | }; | |
750 | } __attribute__ ((packed)); | |
751 | ||
b8c601e8 HV |
752 | /* Return true if this control is a pointer type. */ |
753 | static inline bool ctrl_is_pointer(struct file *file, u32 id) | |
6b5a9492 | 754 | { |
b8c601e8 HV |
755 | struct video_device *vdev = video_devdata(file); |
756 | struct v4l2_fh *fh = NULL; | |
757 | struct v4l2_ctrl_handler *hdl = NULL; | |
758 | struct v4l2_query_ext_ctrl qec = { id }; | |
759 | const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops; | |
760 | ||
761 | if (test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags)) | |
762 | fh = file->private_data; | |
763 | ||
764 | if (fh && fh->ctrl_handler) | |
765 | hdl = fh->ctrl_handler; | |
766 | else if (vdev->ctrl_handler) | |
767 | hdl = vdev->ctrl_handler; | |
768 | ||
769 | if (hdl) { | |
770 | struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, id); | |
771 | ||
772 | return ctrl && ctrl->is_ptr; | |
fdf82dc2 | 773 | } |
b8c601e8 | 774 | |
273caa26 | 775 | if (!ops || !ops->vidioc_query_ext_ctrl) |
b8c601e8 HV |
776 | return false; |
777 | ||
778 | return !ops->vidioc_query_ext_ctrl(file, fh, &qec) && | |
779 | (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD); | |
6b5a9492 HV |
780 | } |
781 | ||
a1dfb4c4 DM |
782 | static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *up, |
783 | u32 *size) | |
784 | { | |
785 | u32 count; | |
786 | ||
787 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || | |
788 | get_user(count, &up->count)) | |
789 | return -EFAULT; | |
790 | if (count > V4L2_CID_MAX_CTRLS) | |
791 | return -EINVAL; | |
792 | *size = count * sizeof(struct v4l2_ext_control); | |
793 | return 0; | |
794 | } | |
795 | ||
b8c601e8 | 796 | static int get_v4l2_ext_controls32(struct file *file, |
a1dfb4c4 DM |
797 | struct v4l2_ext_controls __user *kp, |
798 | struct v4l2_ext_controls32 __user *up, | |
799 | void __user *aux_buf, u32 aux_space) | |
eb4eeccc | 800 | { |
6b5a9492 | 801 | struct v4l2_ext_control32 __user *ucontrols; |
92f45bad | 802 | struct v4l2_ext_control __user *kcontrols; |
a1dfb4c4 DM |
803 | u32 count; |
804 | u32 n; | |
92f45bad HV |
805 | compat_caddr_t p; |
806 | ||
333b1e9f | 807 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || |
a1dfb4c4 DM |
808 | assign_in_user(&kp->which, &up->which) || |
809 | get_user(count, &up->count) || | |
810 | put_user(count, &kp->count) || | |
811 | assign_in_user(&kp->error_idx, &up->error_idx) || | |
812 | copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved))) | |
b7b957d4 | 813 | return -EFAULT; |
a1dfb4c4 DM |
814 | |
815 | if (count == 0) | |
816 | return put_user(NULL, &kp->controls); | |
817 | if (count > V4L2_CID_MAX_CTRLS) | |
a56bc171 | 818 | return -EINVAL; |
92f45bad HV |
819 | if (get_user(p, &up->controls)) |
820 | return -EFAULT; | |
821 | ucontrols = compat_ptr(p); | |
a1dfb4c4 | 822 | if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols))) |
92f45bad | 823 | return -EFAULT; |
a1dfb4c4 DM |
824 | if (aux_space < count * sizeof(*kcontrols)) |
825 | return -EFAULT; | |
826 | kcontrols = aux_buf; | |
827 | if (put_user((__force struct v4l2_ext_control *)kcontrols, | |
828 | &kp->controls)) | |
829 | return -EFAULT; | |
830 | ||
831 | for (n = 0; n < count; n++) { | |
8ae632b1 HV |
832 | u32 id; |
833 | ||
52a3082f | 834 | if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols))) |
92f45bad | 835 | return -EFAULT; |
a1dfb4c4 | 836 | |
8ae632b1 HV |
837 | if (get_user(id, &kcontrols->id)) |
838 | return -EFAULT; | |
a1dfb4c4 | 839 | |
b8c601e8 | 840 | if (ctrl_is_pointer(file, id)) { |
6b5a9492 HV |
841 | void __user *s; |
842 | ||
843 | if (get_user(p, &ucontrols->string)) | |
844 | return -EFAULT; | |
845 | s = compat_ptr(p); | |
846 | if (put_user(s, &kcontrols->string)) | |
847 | return -EFAULT; | |
848 | } | |
92f45bad HV |
849 | ucontrols++; |
850 | kcontrols++; | |
851 | } | |
eb4eeccc MCC |
852 | return 0; |
853 | } | |
854 | ||
b8c601e8 | 855 | static int put_v4l2_ext_controls32(struct file *file, |
a1dfb4c4 | 856 | struct v4l2_ext_controls __user *kp, |
b8c601e8 | 857 | struct v4l2_ext_controls32 __user *up) |
0d0fbf81 | 858 | { |
6b5a9492 | 859 | struct v4l2_ext_control32 __user *ucontrols; |
a1dfb4c4 DM |
860 | struct v4l2_ext_control __user *kcontrols; |
861 | u32 count; | |
862 | u32 n; | |
92f45bad HV |
863 | compat_caddr_t p; |
864 | ||
333b1e9f | 865 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || |
a1dfb4c4 DM |
866 | assign_in_user(&up->which, &kp->which) || |
867 | get_user(count, &kp->count) || | |
868 | put_user(count, &up->count) || | |
869 | assign_in_user(&up->error_idx, &kp->error_idx) || | |
870 | copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)) || | |
871 | get_user(kcontrols, &kp->controls)) | |
b7b957d4 | 872 | return -EFAULT; |
a113bc78 | 873 | |
a1dfb4c4 DM |
874 | if (!count) |
875 | return 0; | |
92f45bad | 876 | if (get_user(p, &up->controls)) |
0d0fbf81 | 877 | return -EFAULT; |
92f45bad | 878 | ucontrols = compat_ptr(p); |
a1dfb4c4 | 879 | if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols))) |
0d0fbf81 AB |
880 | return -EFAULT; |
881 | ||
a1dfb4c4 DM |
882 | for (n = 0; n < count; n++) { |
883 | unsigned int size = sizeof(*ucontrols); | |
8ae632b1 | 884 | u32 id; |
6b5a9492 | 885 | |
a1dfb4c4 DM |
886 | if (get_user(id, &kcontrols->id) || |
887 | put_user(id, &ucontrols->id) || | |
888 | assign_in_user(&ucontrols->size, &kcontrols->size) || | |
889 | copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2, | |
890 | sizeof(ucontrols->reserved2))) | |
8ae632b1 | 891 | return -EFAULT; |
a1dfb4c4 DM |
892 | |
893 | /* | |
894 | * Do not modify the pointer when copying a pointer control. | |
895 | * The contents of the pointer was changed, not the pointer | |
896 | * itself. | |
897 | */ | |
b8c601e8 | 898 | if (ctrl_is_pointer(file, id)) |
6b5a9492 | 899 | size -= sizeof(ucontrols->value64); |
a1dfb4c4 | 900 | |
6b5a9492 | 901 | if (copy_in_user(ucontrols, kcontrols, size)) |
92f45bad | 902 | return -EFAULT; |
a1dfb4c4 | 903 | |
92f45bad HV |
904 | ucontrols++; |
905 | kcontrols++; | |
0d0fbf81 | 906 | } |
92f45bad | 907 | return 0; |
0d0fbf81 | 908 | } |
92f45bad | 909 | |
2330fb82 HV |
910 | struct v4l2_event32 { |
911 | __u32 type; | |
912 | union { | |
655e9780 | 913 | compat_s64 value64; |
2330fb82 HV |
914 | __u8 data[64]; |
915 | } u; | |
916 | __u32 pending; | |
917 | __u32 sequence; | |
918 | struct compat_timespec timestamp; | |
919 | __u32 id; | |
920 | __u32 reserved[8]; | |
921 | }; | |
922 | ||
a1dfb4c4 DM |
923 | static int put_v4l2_event32(struct v4l2_event __user *kp, |
924 | struct v4l2_event32 __user *up) | |
2330fb82 | 925 | { |
333b1e9f | 926 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || |
a1dfb4c4 DM |
927 | assign_in_user(&up->type, &kp->type) || |
928 | copy_in_user(&up->u, &kp->u, sizeof(kp->u)) || | |
929 | assign_in_user(&up->pending, &kp->pending) || | |
930 | assign_in_user(&up->sequence, &kp->sequence) || | |
931 | assign_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) || | |
932 | assign_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) || | |
933 | assign_in_user(&up->id, &kp->id) || | |
934 | copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved))) | |
b7b957d4 | 935 | return -EFAULT; |
2330fb82 HV |
936 | return 0; |
937 | } | |
938 | ||
dd519bb3 | 939 | struct v4l2_edid32 { |
ed45ce2c HV |
940 | __u32 pad; |
941 | __u32 start_block; | |
942 | __u32 blocks; | |
943 | __u32 reserved[5]; | |
944 | compat_caddr_t edid; | |
945 | }; | |
946 | ||
a1dfb4c4 DM |
947 | static int get_v4l2_edid32(struct v4l2_edid __user *kp, |
948 | struct v4l2_edid32 __user *up) | |
ed45ce2c | 949 | { |
a1dfb4c4 | 950 | compat_uptr_t tmp; |
ed45ce2c | 951 | |
333b1e9f | 952 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || |
a1dfb4c4 DM |
953 | assign_in_user(&kp->pad, &up->pad) || |
954 | assign_in_user(&kp->start_block, &up->start_block) || | |
955 | assign_in_user(&kp->blocks, &up->blocks) || | |
b7b957d4 | 956 | get_user(tmp, &up->edid) || |
a1dfb4c4 DM |
957 | put_user(compat_ptr(tmp), &kp->edid) || |
958 | copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved))) | |
b7b957d4 | 959 | return -EFAULT; |
ed45ce2c HV |
960 | return 0; |
961 | } | |
962 | ||
a1dfb4c4 DM |
963 | static int put_v4l2_edid32(struct v4l2_edid __user *kp, |
964 | struct v4l2_edid32 __user *up) | |
ed45ce2c | 965 | { |
a1dfb4c4 | 966 | void *edid; |
ed45ce2c | 967 | |
333b1e9f | 968 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || |
a1dfb4c4 DM |
969 | assign_in_user(&up->pad, &kp->pad) || |
970 | assign_in_user(&up->start_block, &kp->start_block) || | |
971 | assign_in_user(&up->blocks, &kp->blocks) || | |
972 | get_user(edid, &kp->edid) || | |
973 | put_user(ptr_to_compat(edid), &up->edid) || | |
974 | copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved))) | |
b7b957d4 | 975 | return -EFAULT; |
ed45ce2c HV |
976 | return 0; |
977 | } | |
978 | ||
979 | ||
92f45bad HV |
980 | #define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32) |
981 | #define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32) | |
982 | #define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32) | |
983 | #define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) | |
984 | #define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) | |
985 | #define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32) | |
986 | #define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32) | |
987 | #define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32) | |
988 | #define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32) | |
dd519bb3 HV |
989 | #define VIDIOC_G_EDID32 _IOWR('V', 40, struct v4l2_edid32) |
990 | #define VIDIOC_S_EDID32 _IOWR('V', 41, struct v4l2_edid32) | |
6e6a8b5a | 991 | #define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32) |
92f45bad HV |
992 | #define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32) |
993 | #define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32) | |
994 | #define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) | |
2330fb82 | 995 | #define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32) |
2150158b GL |
996 | #define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32) |
997 | #define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32) | |
92f45bad HV |
998 | |
999 | #define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) | |
92f45bad HV |
1000 | #define VIDIOC_STREAMON32 _IOW ('V', 18, s32) |
1001 | #define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) | |
1002 | #define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) | |
1003 | #define VIDIOC_S_INPUT32 _IOWR('V', 39, s32) | |
1004 | #define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) | |
1005 | #define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) | |
0d0fbf81 | 1006 | |
a1dfb4c4 DM |
1007 | static int alloc_userspace(unsigned int size, u32 aux_space, |
1008 | void __user **up_native) | |
1009 | { | |
1010 | *up_native = compat_alloc_user_space(size + aux_space); | |
1011 | if (!*up_native) | |
1012 | return -ENOMEM; | |
1013 | if (clear_user(*up_native, size)) | |
1014 | return -EFAULT; | |
1015 | return 0; | |
1016 | } | |
1017 | ||
069b7479 | 1018 | static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
0d0fbf81 | 1019 | { |
0d0fbf81 | 1020 | void __user *up = compat_ptr(arg); |
a1dfb4c4 DM |
1021 | void __user *up_native = NULL; |
1022 | void __user *aux_buf; | |
1023 | u32 aux_space; | |
cf664a64 | 1024 | int compatible_arg = 1; |
069b7479 | 1025 | long err = 0; |
0d0fbf81 AB |
1026 | |
1027 | /* First, convert the command. */ | |
d1f81da2 | 1028 | switch (cmd) { |
92f45bad HV |
1029 | case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; |
1030 | case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; | |
1031 | case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break; | |
1032 | case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break; | |
1033 | case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break; | |
1034 | case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break; | |
1035 | case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break; | |
1036 | case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break; | |
1037 | case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break; | |
1038 | case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break; | |
1039 | case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break; | |
1040 | case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; | |
1041 | case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; | |
2330fb82 | 1042 | case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break; |
92f45bad | 1043 | case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; |
92f45bad HV |
1044 | case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; |
1045 | case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; | |
1046 | case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; | |
1047 | case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; | |
1048 | case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; | |
1049 | case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; | |
2150158b GL |
1050 | case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break; |
1051 | case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break; | |
dd519bb3 HV |
1052 | case VIDIOC_G_EDID32: cmd = VIDIOC_G_EDID; break; |
1053 | case VIDIOC_S_EDID32: cmd = VIDIOC_S_EDID; break; | |
92f45bad | 1054 | } |
0d0fbf81 | 1055 | |
d1f81da2 | 1056 | switch (cmd) { |
cf664a64 | 1057 | case VIDIOC_OVERLAY: |
13d133bc PDM |
1058 | case VIDIOC_STREAMON: |
1059 | case VIDIOC_STREAMOFF: | |
92f45bad HV |
1060 | case VIDIOC_S_INPUT: |
1061 | case VIDIOC_S_OUTPUT: | |
a1dfb4c4 DM |
1062 | err = alloc_userspace(sizeof(unsigned int), 0, &up_native); |
1063 | if (!err && assign_in_user((unsigned int __user *)up_native, | |
1064 | (compat_uint_t __user *)up)) | |
1065 | err = -EFAULT; | |
92f45bad | 1066 | compatible_arg = 0; |
0d0fbf81 | 1067 | break; |
13d133bc | 1068 | |
92f45bad HV |
1069 | case VIDIOC_G_INPUT: |
1070 | case VIDIOC_G_OUTPUT: | |
a1dfb4c4 | 1071 | err = alloc_userspace(sizeof(unsigned int), 0, &up_native); |
13d133bc PDM |
1072 | compatible_arg = 0; |
1073 | break; | |
cf664a64 | 1074 | |
dd519bb3 HV |
1075 | case VIDIOC_G_EDID: |
1076 | case VIDIOC_S_EDID: | |
a1dfb4c4 DM |
1077 | err = alloc_userspace(sizeof(struct v4l2_edid), 0, &up_native); |
1078 | if (!err) | |
1079 | err = get_v4l2_edid32(up_native, up); | |
ed45ce2c HV |
1080 | compatible_arg = 0; |
1081 | break; | |
1082 | ||
cf664a64 PDM |
1083 | case VIDIOC_G_FMT: |
1084 | case VIDIOC_S_FMT: | |
1085 | case VIDIOC_TRY_FMT: | |
a1dfb4c4 DM |
1086 | err = bufsize_v4l2_format(up, &aux_space); |
1087 | if (!err) | |
1088 | err = alloc_userspace(sizeof(struct v4l2_format), | |
1089 | aux_space, &up_native); | |
1090 | if (!err) { | |
1091 | aux_buf = up_native + sizeof(struct v4l2_format); | |
1092 | err = get_v4l2_format32(up_native, up, | |
1093 | aux_buf, aux_space); | |
1094 | } | |
cf664a64 PDM |
1095 | compatible_arg = 0; |
1096 | break; | |
1097 | ||
2150158b | 1098 | case VIDIOC_CREATE_BUFS: |
a1dfb4c4 DM |
1099 | err = bufsize_v4l2_create(up, &aux_space); |
1100 | if (!err) | |
1101 | err = alloc_userspace(sizeof(struct v4l2_create_buffers), | |
1102 | aux_space, &up_native); | |
1103 | if (!err) { | |
1104 | aux_buf = up_native + sizeof(struct v4l2_create_buffers); | |
1105 | err = get_v4l2_create32(up_native, up, | |
1106 | aux_buf, aux_space); | |
1107 | } | |
2150158b GL |
1108 | compatible_arg = 0; |
1109 | break; | |
1110 | ||
1111 | case VIDIOC_PREPARE_BUF: | |
cf664a64 PDM |
1112 | case VIDIOC_QUERYBUF: |
1113 | case VIDIOC_QBUF: | |
1114 | case VIDIOC_DQBUF: | |
a1dfb4c4 DM |
1115 | err = bufsize_v4l2_buffer(up, &aux_space); |
1116 | if (!err) | |
1117 | err = alloc_userspace(sizeof(struct v4l2_buffer), | |
1118 | aux_space, &up_native); | |
1119 | if (!err) { | |
1120 | aux_buf = up_native + sizeof(struct v4l2_buffer); | |
1121 | err = get_v4l2_buffer32(up_native, up, | |
1122 | aux_buf, aux_space); | |
1123 | } | |
cf664a64 PDM |
1124 | compatible_arg = 0; |
1125 | break; | |
1126 | ||
92f45bad | 1127 | case VIDIOC_S_FBUF: |
a1dfb4c4 DM |
1128 | err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0, |
1129 | &up_native); | |
1130 | if (!err) | |
1131 | err = get_v4l2_framebuffer32(up_native, up); | |
a113bc78 GM |
1132 | compatible_arg = 0; |
1133 | break; | |
1134 | ||
92f45bad | 1135 | case VIDIOC_G_FBUF: |
a1dfb4c4 DM |
1136 | err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0, |
1137 | &up_native); | |
cf664a64 PDM |
1138 | compatible_arg = 0; |
1139 | break; | |
1140 | ||
92f45bad | 1141 | case VIDIOC_ENUMSTD: |
a1dfb4c4 DM |
1142 | err = alloc_userspace(sizeof(struct v4l2_standard), 0, |
1143 | &up_native); | |
1144 | if (!err) | |
1145 | err = get_v4l2_standard32(up_native, up); | |
a113bc78 GM |
1146 | compatible_arg = 0; |
1147 | break; | |
1148 | ||
92f45bad | 1149 | case VIDIOC_ENUMINPUT: |
a1dfb4c4 DM |
1150 | err = alloc_userspace(sizeof(struct v4l2_input), 0, &up_native); |
1151 | if (!err) | |
1152 | err = get_v4l2_input32(up_native, up); | |
a113bc78 GM |
1153 | compatible_arg = 0; |
1154 | break; | |
1155 | ||
92f45bad HV |
1156 | case VIDIOC_G_EXT_CTRLS: |
1157 | case VIDIOC_S_EXT_CTRLS: | |
1158 | case VIDIOC_TRY_EXT_CTRLS: | |
a1dfb4c4 DM |
1159 | err = bufsize_v4l2_ext_controls(up, &aux_space); |
1160 | if (!err) | |
1161 | err = alloc_userspace(sizeof(struct v4l2_ext_controls), | |
1162 | aux_space, &up_native); | |
1163 | if (!err) { | |
1164 | aux_buf = up_native + sizeof(struct v4l2_ext_controls); | |
1165 | err = get_v4l2_ext_controls32(file, up_native, up, | |
1166 | aux_buf, aux_space); | |
1167 | } | |
a113bc78 GM |
1168 | compatible_arg = 0; |
1169 | break; | |
2330fb82 | 1170 | case VIDIOC_DQEVENT: |
a1dfb4c4 | 1171 | err = alloc_userspace(sizeof(struct v4l2_event), 0, &up_native); |
2330fb82 HV |
1172 | compatible_arg = 0; |
1173 | break; | |
92f45bad | 1174 | } |
d1f81da2 | 1175 | if (err) |
92f45bad | 1176 | return err; |
0d0fbf81 | 1177 | |
d1f81da2 | 1178 | if (compatible_arg) |
92f45bad | 1179 | err = native_ioctl(file, cmd, (unsigned long)up); |
a1dfb4c4 DM |
1180 | else |
1181 | err = native_ioctl(file, cmd, (unsigned long)up_native); | |
92f45bad | 1182 | |
d83a8243 HV |
1183 | if (err == -ENOTTY) |
1184 | return err; | |
1185 | ||
a1dfb4c4 DM |
1186 | /* |
1187 | * Special case: even after an error we need to put the | |
1188 | * results back for these ioctls since the error_idx will | |
1189 | * contain information on which control failed. | |
1190 | */ | |
92f45bad HV |
1191 | switch (cmd) { |
1192 | case VIDIOC_G_EXT_CTRLS: | |
1193 | case VIDIOC_S_EXT_CTRLS: | |
1194 | case VIDIOC_TRY_EXT_CTRLS: | |
a1dfb4c4 | 1195 | if (put_v4l2_ext_controls32(file, up_native, up)) |
92f45bad HV |
1196 | err = -EFAULT; |
1197 | break; | |
ba7ed691 | 1198 | case VIDIOC_S_EDID: |
a1dfb4c4 | 1199 | if (put_v4l2_edid32(up_native, up)) |
ba7ed691 HV |
1200 | err = -EFAULT; |
1201 | break; | |
92f45bad HV |
1202 | } |
1203 | if (err) | |
1204 | return err; | |
1205 | ||
1206 | switch (cmd) { | |
92f45bad HV |
1207 | case VIDIOC_S_INPUT: |
1208 | case VIDIOC_S_OUTPUT: | |
1209 | case VIDIOC_G_INPUT: | |
1210 | case VIDIOC_G_OUTPUT: | |
a1dfb4c4 DM |
1211 | if (assign_in_user((compat_uint_t __user *)up, |
1212 | ((unsigned int __user *)up_native))) | |
1213 | err = -EFAULT; | |
92f45bad | 1214 | break; |
a113bc78 | 1215 | |
92f45bad | 1216 | case VIDIOC_G_FBUF: |
a1dfb4c4 | 1217 | err = put_v4l2_framebuffer32(up_native, up); |
92f45bad HV |
1218 | break; |
1219 | ||
2330fb82 | 1220 | case VIDIOC_DQEVENT: |
a1dfb4c4 | 1221 | err = put_v4l2_event32(up_native, up); |
2330fb82 HV |
1222 | break; |
1223 | ||
dd519bb3 | 1224 | case VIDIOC_G_EDID: |
a1dfb4c4 | 1225 | err = put_v4l2_edid32(up_native, up); |
ed45ce2c HV |
1226 | break; |
1227 | ||
92f45bad HV |
1228 | case VIDIOC_G_FMT: |
1229 | case VIDIOC_S_FMT: | |
1230 | case VIDIOC_TRY_FMT: | |
a1dfb4c4 | 1231 | err = put_v4l2_format32(up_native, up); |
92f45bad HV |
1232 | break; |
1233 | ||
2150158b | 1234 | case VIDIOC_CREATE_BUFS: |
a1dfb4c4 | 1235 | err = put_v4l2_create32(up_native, up); |
2150158b GL |
1236 | break; |
1237 | ||
3ee6d040 | 1238 | case VIDIOC_PREPARE_BUF: |
92f45bad HV |
1239 | case VIDIOC_QUERYBUF: |
1240 | case VIDIOC_QBUF: | |
1241 | case VIDIOC_DQBUF: | |
a1dfb4c4 | 1242 | err = put_v4l2_buffer32(up_native, up); |
92f45bad HV |
1243 | break; |
1244 | ||
1245 | case VIDIOC_ENUMSTD: | |
a1dfb4c4 | 1246 | err = put_v4l2_standard32(up_native, up); |
92f45bad HV |
1247 | break; |
1248 | ||
1249 | case VIDIOC_ENUMINPUT: | |
a1dfb4c4 | 1250 | err = put_v4l2_input32(up_native, up); |
92f45bad | 1251 | break; |
0d0fbf81 | 1252 | } |
0d0fbf81 AB |
1253 | return err; |
1254 | } | |
1255 | ||
9bb7cde7 | 1256 | long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) |
0d0fbf81 | 1257 | { |
b9d0aa6e | 1258 | struct video_device *vdev = video_devdata(file); |
069b7479 | 1259 | long ret = -ENOIOCTLCMD; |
0d0fbf81 | 1260 | |
c6d7ba8b | 1261 | if (!file->f_op->unlocked_ioctl) |
0d0fbf81 AB |
1262 | return ret; |
1263 | ||
ab58a301 | 1264 | if (_IOC_TYPE(cmd) == 'V' && _IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) |
0d0fbf81 | 1265 | ret = do_video_ioctl(file, cmd, arg); |
ab58a301 HV |
1266 | else if (vdev->fops->compat_ioctl32) |
1267 | ret = vdev->fops->compat_ioctl32(file, cmd, arg); | |
0d0fbf81 | 1268 | |
ab58a301 | 1269 | if (ret == -ENOIOCTLCMD) |
9ec32cc7 HV |
1270 | pr_debug("compat_ioctl32: unknown ioctl '%c', dir=%d, #%d (0x%08x)\n", |
1271 | _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd); | |
e8efb71d | 1272 | return ret; |
0d0fbf81 | 1273 | } |
9bb7cde7 | 1274 | EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32); |