2 comedi/comedi_compat32.c
3 32-bit ioctl compatibility for 64-bit comedi kernel module.
5 Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
6 Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
8 COMEDI - Linux Control and Measurement Device Interface
9 Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #define __NO_VERSION__
29 #include <linux/smp_lock.h>
30 #include <asm/uaccess.h>
32 #include "comedi_compat32.h"
36 #ifndef HAVE_COMPAT_IOCTL
37 #include <linux/ioctl32.h> /* for (un)register_ioctl32_conversion */
40 #define COMEDI32_CHANINFO _IOR(CIO, 3, comedi32_chaninfo)
41 #define COMEDI32_RANGEINFO _IOR(CIO, 8, (struct comedi32_rangeinfo_struct))
42 /* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
43 * It's too late to change it now, but it only affects the command number. */
44 #define COMEDI32_CMD _IOR(CIO, 9, (struct comedi32_cmd_struct))
45 /* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
46 * It's too late to change it now, but it only affects the command number. */
47 #define COMEDI32_CMDTEST _IOR(CIO, 10, (struct comedi32_cmd_struct))
48 #define COMEDI32_INSNLIST _IOR(CIO, 11, (struct comedi32_insnlist_struct))
49 #define COMEDI32_INSN _IOR(CIO, 12, (struct comedi32_insn_struct))
51 struct comedi32_chaninfo_struct
{
53 compat_uptr_t maxdata_list
; /* 32-bit 'unsigned int *' */
54 compat_uptr_t flaglist
; /* 32-bit 'unsigned int *' */
55 compat_uptr_t rangelist
; /* 32-bit 'unsigned int *' */
56 unsigned int unused
[4];
59 struct comedi32_rangeinfo_struct
{
60 unsigned int range_type
;
61 compat_uptr_t range_ptr
; /* 32-bit 'void *' */
64 struct comedi32_cmd_struct
{
67 unsigned int start_src
;
68 unsigned int start_arg
;
69 unsigned int scan_begin_src
;
70 unsigned int scan_begin_arg
;
71 unsigned int convert_src
;
72 unsigned int convert_arg
;
73 unsigned int scan_end_src
;
74 unsigned int scan_end_arg
;
75 unsigned int stop_src
;
76 unsigned int stop_arg
;
77 compat_uptr_t chanlist
; /* 32-bit 'unsigned int *' */
78 unsigned int chanlist_len
;
79 compat_uptr_t data
; /* 32-bit 'short *' */
80 unsigned int data_len
;
83 struct comedi32_insn_struct
{
86 compat_uptr_t data
; /* 32-bit 'unsigned int *' */
88 unsigned int chanspec
;
89 unsigned int unused
[3];
92 struct comedi32_insnlist_struct
{
94 compat_uptr_t insns
; /* 32-bit 'comedi_insn *' */
97 /* Handle translated ioctl. */
98 static int translated_ioctl(struct file
*file
, unsigned int cmd
,
104 #ifdef HAVE_UNLOCKED_IOCTL
105 if (file
->f_op
->unlocked_ioctl
) {
106 int rc
= (int)(*file
->f_op
->unlocked_ioctl
)(file
, cmd
, arg
);
107 if (rc
== -ENOIOCTLCMD
)
112 if (file
->f_op
->ioctl
) {
115 rc
= (*file
->f_op
->ioctl
)(file
->f_dentry
->d_inode
,
123 /* Handle 32-bit COMEDI_CHANINFO ioctl. */
124 static int compat_chaninfo(struct file
*file
, unsigned long arg
)
126 struct comedi_chaninfo_struct __user
*chaninfo
;
127 struct comedi32_chaninfo_struct __user
*chaninfo32
;
134 chaninfo32
= compat_ptr(arg
);
135 chaninfo
= compat_alloc_user_space(sizeof(*chaninfo
));
137 /* Copy chaninfo structure. Ignore unused members. */
138 if (!access_ok(VERIFY_READ
, chaninfo32
, sizeof(*chaninfo32
))
139 || !access_ok(VERIFY_WRITE
, chaninfo
,
140 sizeof(*chaninfo
))) {
144 err
|= __get_user(temp
.uint
, &chaninfo32
->subdev
);
145 err
|= __put_user(temp
.uint
, &chaninfo
->subdev
);
146 err
|= __get_user(temp
.uptr
, &chaninfo32
->maxdata_list
);
147 err
|= __put_user(compat_ptr(temp
.uptr
), &chaninfo
->maxdata_list
);
148 err
|= __get_user(temp
.uptr
, &chaninfo32
->flaglist
);
149 err
|= __put_user(compat_ptr(temp
.uptr
), &chaninfo
->flaglist
);
150 err
|= __get_user(temp
.uptr
, &chaninfo32
->rangelist
);
151 err
|= __put_user(compat_ptr(temp
.uptr
), &chaninfo
->rangelist
);
155 return translated_ioctl(file
, COMEDI_CHANINFO
, (unsigned long)chaninfo
);
158 /* Handle 32-bit COMEDI_RANGEINFO ioctl. */
159 static int compat_rangeinfo(struct file
*file
, unsigned long arg
)
161 comedi_rangeinfo __user
*rangeinfo
;
162 struct comedi32_rangeinfo_struct __user
*rangeinfo32
;
169 rangeinfo32
= compat_ptr(arg
);
170 rangeinfo
= compat_alloc_user_space(sizeof(*rangeinfo
));
172 /* Copy rangeinfo structure. */
173 if (!access_ok(VERIFY_READ
, rangeinfo32
, sizeof(*rangeinfo32
))
174 || !access_ok(VERIFY_WRITE
, rangeinfo
,
175 sizeof(*rangeinfo
))) {
179 err
|= __get_user(temp
.uint
, &rangeinfo32
->range_type
);
180 err
|= __put_user(temp
.uint
, &rangeinfo
->range_type
);
181 err
|= __get_user(temp
.uptr
, &rangeinfo32
->range_ptr
);
182 err
|= __put_user(compat_ptr(temp
.uptr
), &rangeinfo
->range_ptr
);
186 return translated_ioctl(file
, COMEDI_RANGEINFO
,
187 (unsigned long)rangeinfo
);
190 /* Copy 32-bit cmd structure to native cmd structure. */
191 static int get_compat_cmd(comedi_cmd __user
*cmd
,
192 struct comedi32_cmd_struct __user
*cmd32
)
200 /* Copy cmd structure. */
201 if (!access_ok(VERIFY_READ
, cmd32
, sizeof(*cmd32
))
202 || !access_ok(VERIFY_WRITE
, cmd
, sizeof(*cmd
))) {
206 err
|= __get_user(temp
.uint
, &cmd32
->subdev
);
207 err
|= __put_user(temp
.uint
, &cmd
->subdev
);
208 err
|= __get_user(temp
.uint
, &cmd32
->flags
);
209 err
|= __put_user(temp
.uint
, &cmd
->flags
);
210 err
|= __get_user(temp
.uint
, &cmd32
->start_src
);
211 err
|= __put_user(temp
.uint
, &cmd
->start_src
);
212 err
|= __get_user(temp
.uint
, &cmd32
->start_arg
);
213 err
|= __put_user(temp
.uint
, &cmd
->start_arg
);
214 err
|= __get_user(temp
.uint
, &cmd32
->scan_begin_src
);
215 err
|= __put_user(temp
.uint
, &cmd
->scan_begin_src
);
216 err
|= __get_user(temp
.uint
, &cmd32
->scan_begin_arg
);
217 err
|= __put_user(temp
.uint
, &cmd
->scan_begin_arg
);
218 err
|= __get_user(temp
.uint
, &cmd32
->convert_src
);
219 err
|= __put_user(temp
.uint
, &cmd
->convert_src
);
220 err
|= __get_user(temp
.uint
, &cmd32
->convert_arg
);
221 err
|= __put_user(temp
.uint
, &cmd
->convert_arg
);
222 err
|= __get_user(temp
.uint
, &cmd32
->scan_end_src
);
223 err
|= __put_user(temp
.uint
, &cmd
->scan_end_src
);
224 err
|= __get_user(temp
.uint
, &cmd32
->scan_end_arg
);
225 err
|= __put_user(temp
.uint
, &cmd
->scan_end_arg
);
226 err
|= __get_user(temp
.uint
, &cmd32
->stop_src
);
227 err
|= __put_user(temp
.uint
, &cmd
->stop_src
);
228 err
|= __get_user(temp
.uint
, &cmd32
->stop_arg
);
229 err
|= __put_user(temp
.uint
, &cmd
->stop_arg
);
230 err
|= __get_user(temp
.uptr
, &cmd32
->chanlist
);
231 err
|= __put_user(compat_ptr(temp
.uptr
), &cmd
->chanlist
);
232 err
|= __get_user(temp
.uint
, &cmd32
->chanlist_len
);
233 err
|= __put_user(temp
.uint
, &cmd
->chanlist_len
);
234 err
|= __get_user(temp
.uptr
, &cmd32
->data
);
235 err
|= __put_user(compat_ptr(temp
.uptr
), &cmd
->data
);
236 err
|= __get_user(temp
.uint
, &cmd32
->data_len
);
237 err
|= __put_user(temp
.uint
, &cmd
->data_len
);
238 return err
? -EFAULT
: 0;
241 /* Copy native cmd structure to 32-bit cmd structure. */
242 static int put_compat_cmd(struct comedi32_cmd_struct __user
*cmd32
, comedi_cmd __user
*cmd
)
247 /* Copy back most of cmd structure. */
248 /* Assume the pointer values are already valid. */
249 /* (Could use ptr_to_compat() to set them, but that wasn't implemented
250 * until kernel version 2.6.11.) */
251 if (!access_ok(VERIFY_READ
, cmd
, sizeof(*cmd
))
252 || !access_ok(VERIFY_WRITE
, cmd32
, sizeof(*cmd32
))) {
256 err
|= __get_user(temp
, &cmd
->subdev
);
257 err
|= __put_user(temp
, &cmd32
->subdev
);
258 err
|= __get_user(temp
, &cmd
->flags
);
259 err
|= __put_user(temp
, &cmd32
->flags
);
260 err
|= __get_user(temp
, &cmd
->start_src
);
261 err
|= __put_user(temp
, &cmd32
->start_src
);
262 err
|= __get_user(temp
, &cmd
->start_arg
);
263 err
|= __put_user(temp
, &cmd32
->start_arg
);
264 err
|= __get_user(temp
, &cmd
->scan_begin_src
);
265 err
|= __put_user(temp
, &cmd32
->scan_begin_src
);
266 err
|= __get_user(temp
, &cmd
->scan_begin_arg
);
267 err
|= __put_user(temp
, &cmd32
->scan_begin_arg
);
268 err
|= __get_user(temp
, &cmd
->convert_src
);
269 err
|= __put_user(temp
, &cmd32
->convert_src
);
270 err
|= __get_user(temp
, &cmd
->convert_arg
);
271 err
|= __put_user(temp
, &cmd32
->convert_arg
);
272 err
|= __get_user(temp
, &cmd
->scan_end_src
);
273 err
|= __put_user(temp
, &cmd32
->scan_end_src
);
274 err
|= __get_user(temp
, &cmd
->scan_end_arg
);
275 err
|= __put_user(temp
, &cmd32
->scan_end_arg
);
276 err
|= __get_user(temp
, &cmd
->stop_src
);
277 err
|= __put_user(temp
, &cmd32
->stop_src
);
278 err
|= __get_user(temp
, &cmd
->stop_arg
);
279 err
|= __put_user(temp
, &cmd32
->stop_arg
);
280 /* Assume chanlist pointer is unchanged. */
281 err
|= __get_user(temp
, &cmd
->chanlist_len
);
282 err
|= __put_user(temp
, &cmd32
->chanlist_len
);
283 /* Assume data pointer is unchanged. */
284 err
|= __get_user(temp
, &cmd
->data_len
);
285 err
|= __put_user(temp
, &cmd32
->data_len
);
286 return err
? -EFAULT
: 0;
289 /* Handle 32-bit COMEDI_CMD ioctl. */
290 static int compat_cmd(struct file
*file
, unsigned long arg
)
292 comedi_cmd __user
*cmd
;
293 struct comedi32_cmd_struct __user
*cmd32
;
296 cmd32
= compat_ptr(arg
);
297 cmd
= compat_alloc_user_space(sizeof(*cmd
));
299 rc
= get_compat_cmd(cmd
, cmd32
);
303 return translated_ioctl(file
, COMEDI_CMD
, (unsigned long)cmd
);
306 /* Handle 32-bit COMEDI_CMDTEST ioctl. */
307 static int compat_cmdtest(struct file
*file
, unsigned long arg
)
309 comedi_cmd __user
*cmd
;
310 struct comedi32_cmd_struct __user
*cmd32
;
313 cmd32
= compat_ptr(arg
);
314 cmd
= compat_alloc_user_space(sizeof(*cmd
));
316 rc
= get_compat_cmd(cmd
, cmd32
);
320 rc
= translated_ioctl(file
, COMEDI_CMDTEST
, (unsigned long)cmd
);
324 err
= put_compat_cmd(cmd32
, cmd
);
331 /* Copy 32-bit insn structure to native insn structure. */
332 static int get_compat_insn(comedi_insn __user
*insn
,
333 struct comedi32_insn_struct __user
*insn32
)
341 /* Copy insn structure. Ignore the unused members. */
343 if (!access_ok(VERIFY_READ
, insn32
, sizeof(*insn32
))
344 || !access_ok(VERIFY_WRITE
, insn
, sizeof(*insn
)))
347 err
|= __get_user(temp
.uint
, &insn32
->insn
);
348 err
|= __put_user(temp
.uint
, &insn
->insn
);
349 err
|= __get_user(temp
.uint
, &insn32
->n
);
350 err
|= __put_user(temp
.uint
, &insn
->n
);
351 err
|= __get_user(temp
.uptr
, &insn32
->data
);
352 err
|= __put_user(compat_ptr(temp
.uptr
), &insn
->data
);
353 err
|= __get_user(temp
.uint
, &insn32
->subdev
);
354 err
|= __put_user(temp
.uint
, &insn
->subdev
);
355 err
|= __get_user(temp
.uint
, &insn32
->chanspec
);
356 err
|= __put_user(temp
.uint
, &insn
->chanspec
);
357 return err
? -EFAULT
: 0;
360 /* Handle 32-bit COMEDI_INSNLIST ioctl. */
361 static int compat_insnlist(struct file
*file
, unsigned long arg
)
363 struct combined_insnlist
{
364 comedi_insnlist insnlist
;
367 struct comedi32_insnlist_struct __user
*insnlist32
;
368 struct comedi32_insn_struct __user
*insn32
;
370 unsigned int n_insns
, n
;
373 insnlist32
= compat_ptr(arg
);
375 /* Get 32-bit insnlist structure. */
376 if (!access_ok(VERIFY_READ
, insnlist32
, sizeof(*insnlist32
))) {
380 err
|= __get_user(n_insns
, &insnlist32
->n_insns
);
381 err
|= __get_user(uptr
, &insnlist32
->insns
);
382 insn32
= compat_ptr(uptr
);
386 /* Allocate user memory to copy insnlist and insns into. */
387 s
= compat_alloc_user_space(offsetof(struct combined_insnlist
,
390 /* Set native insnlist structure. */
391 if (!access_ok(VERIFY_WRITE
, &s
->insnlist
, sizeof(s
->insnlist
))) {
394 err
|= __put_user(n_insns
, &s
->insnlist
.n_insns
);
395 err
|= __put_user(&s
->insn
[0], &s
->insnlist
.insns
);
399 /* Copy insn structures. */
400 for (n
= 0; n
< n_insns
; n
++) {
401 rc
= get_compat_insn(&s
->insn
[n
], &insn32
[n
]);
406 return translated_ioctl(file
, COMEDI_INSNLIST
,
407 (unsigned long)&s
->insnlist
);
410 /* Handle 32-bit COMEDI_INSN ioctl. */
411 static int compat_insn(struct file
*file
, unsigned long arg
)
413 comedi_insn __user
*insn
;
414 struct comedi32_insn_struct __user
*insn32
;
417 insn32
= compat_ptr(arg
);
418 insn
= compat_alloc_user_space(sizeof(*insn
));
420 rc
= get_compat_insn(insn
, insn32
);
424 return translated_ioctl(file
, COMEDI_INSN
, (unsigned long)insn
);
427 /* Process untranslated ioctl. */
428 /* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
429 static inline int raw_ioctl(struct file
*file
, unsigned int cmd
,
435 case COMEDI_DEVCONFIG
:
437 case COMEDI_SUBDINFO
:
438 case COMEDI_BUFCONFIG
:
440 /* Just need to translate the pointer argument. */
441 arg
= (unsigned long)compat_ptr(arg
);
442 rc
= translated_ioctl(file
, cmd
, arg
);
448 /* No translation needed. */
449 rc
= translated_ioctl(file
, cmd
, arg
);
451 case COMEDI32_CHANINFO
:
452 rc
= compat_chaninfo(file
, arg
);
454 case COMEDI32_RANGEINFO
:
455 rc
= compat_rangeinfo(file
, arg
);
458 rc
= compat_cmd(file
, arg
);
460 case COMEDI32_CMDTEST
:
461 rc
= compat_cmdtest(file
, arg
);
463 case COMEDI32_INSNLIST
:
464 rc
= compat_insnlist(file
, arg
);
467 rc
= compat_insn(file
, arg
);
476 #ifdef HAVE_COMPAT_IOCTL /* defined in <linux/fs.h> 2.6.11 onwards */
478 /* compat_ioctl file operation. */
479 /* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
480 long comedi_compat_ioctl(struct file
*file
, unsigned int cmd
,
483 return raw_ioctl(file
, cmd
, arg
);
486 #else /* HAVE_COMPAT_IOCTL */
489 * Brain-dead ioctl compatibility for 2.6.10 and earlier.
491 * It's brain-dead because cmd numbers need to be unique system-wide!
492 * The comedi driver could end up attempting to execute ioctls for non-Comedi
493 * devices because it registered the system-wide cmd code first. Similarly,
494 * another driver could end up attempting to execute ioctls for a Comedi
495 * device because it registered the cmd code first. Chaos ensues.
498 /* Handler for all 32-bit ioctl codes registered by this driver. */
499 static int mapped_ioctl(unsigned int fd
, unsigned int cmd
, unsigned long arg
,
504 /* Make sure we are dealing with a Comedi device. */
505 if (imajor(file
->f_dentry
->d_inode
) != COMEDI_MAJOR
)
508 rc
= raw_ioctl(file
, cmd
, arg
);
509 /* Do not return -ENOIOCTLCMD. */
510 if (rc
== -ENOIOCTLCMD
)
518 int (*handler
)(unsigned int, unsigned int, unsigned long,
523 static struct ioctl32_map comedi_ioctl32_map
[] = {
524 { COMEDI_DEVCONFIG
, mapped_ioctl
, 0 },
525 { COMEDI_DEVINFO
, mapped_ioctl
, 0 },
526 { COMEDI_SUBDINFO
, mapped_ioctl
, 0 },
527 { COMEDI_BUFCONFIG
, mapped_ioctl
, 0 },
528 { COMEDI_BUFINFO
, mapped_ioctl
, 0 },
529 { COMEDI_LOCK
, mapped_ioctl
, 0 },
530 { COMEDI_UNLOCK
, mapped_ioctl
, 0 },
531 { COMEDI_CANCEL
, mapped_ioctl
, 0 },
532 { COMEDI_POLL
, mapped_ioctl
, 0 },
533 { COMEDI32_CHANINFO
, mapped_ioctl
, 0 },
534 { COMEDI32_RANGEINFO
, mapped_ioctl
, 0 },
535 { COMEDI32_CMD
, mapped_ioctl
, 0 },
536 { COMEDI32_CMDTEST
, mapped_ioctl
, 0 },
537 { COMEDI32_INSNLIST
, mapped_ioctl
, 0 },
538 { COMEDI32_INSN
, mapped_ioctl
, 0 },
541 #define NUM_IOCTL32_MAPS ARRAY_SIZE(comedi_ioctl32_map)
543 /* Register system-wide 32-bit ioctl handlers. */
544 void comedi_register_ioctl32(void)
548 for (n
= 0; n
< NUM_IOCTL32_MAPS
; n
++) {
549 rc
= register_ioctl32_conversion(comedi_ioctl32_map
[n
].cmd
,
550 comedi_ioctl32_map
[n
].handler
);
553 "comedi: failed to register 32-bit "
554 "compatible ioctl handler for 0x%X - "
555 "expect bad things to happen!\n",
556 comedi_ioctl32_map
[n
].cmd
);
558 comedi_ioctl32_map
[n
].registered
= !rc
;
562 /* Unregister system-wide 32-bit ioctl translations. */
563 void comedi_unregister_ioctl32(void)
567 for (n
= 0; n
< NUM_IOCTL32_MAPS
; n
++) {
568 if (comedi_ioctl32_map
[n
].registered
) {
569 rc
= unregister_ioctl32_conversion(
570 comedi_ioctl32_map
[n
].cmd
,
571 comedi_ioctl32_map
[n
].handler
);
574 "comedi: failed to unregister 32-bit "
575 "compatible ioctl handler for 0x%X - "
576 "expect kernel Oops!\n",
577 comedi_ioctl32_map
[n
].cmd
);
579 comedi_ioctl32_map
[n
].registered
= 0;
585 #endif /* HAVE_COMPAT_IOCTL */
587 #endif /* CONFIG_COMPAT */