1/* 2 comedi/comedi_compat32.c 3 32-bit ioctl compatibility for 64-bit comedi kernel module. 4 5 Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk> 6 Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/> 7 8 COMEDI - Linux Control and Measurement Device Interface 9 Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org> 10 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. 15 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. 20 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. 24 25*/ 26 27#define __NO_VERSION__ 28#include <linux/uaccess.h> 29#include "comedi.h" 30#include "comedi_compat32.h" 31 32#ifdef CONFIG_COMPAT 33 34#define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct) 35#define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct) 36/* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR. 37 * It's too late to change it now, but it only affects the command number. */ 38#define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct) 39/* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR. 40 * It's too late to change it now, but it only affects the command number. */ 41#define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct) 42#define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct) 43#define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct) 44 45struct comedi32_chaninfo_struct { 46 unsigned int subdev; 47 compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */ 48 compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */ 49 compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */ 50 unsigned int unused[4]; 51}; 52 53struct comedi32_rangeinfo_struct { 54 unsigned int range_type; 55 compat_uptr_t range_ptr; /* 32-bit 'void *' */ 56}; 57 58struct comedi32_cmd_struct { 59 unsigned int subdev; 60 unsigned int flags; 61 unsigned int start_src; 62 unsigned int start_arg; 63 unsigned int scan_begin_src; 64 unsigned int scan_begin_arg; 65 unsigned int convert_src; 66 unsigned int convert_arg; 67 unsigned int scan_end_src; 68 unsigned int scan_end_arg; 69 unsigned int stop_src; 70 unsigned int stop_arg; 71 compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */ 72 unsigned int chanlist_len; 73 compat_uptr_t data; /* 32-bit 'short *' */ 74 unsigned int data_len; 75}; 76 77struct comedi32_insn_struct { 78 unsigned int insn; 79 unsigned int n; 80 compat_uptr_t data; /* 32-bit 'unsigned int *' */ 81 unsigned int subdev; 82 unsigned int chanspec; 83 unsigned int unused[3]; 84}; 85 86struct comedi32_insnlist_struct { 87 unsigned int n_insns; 88 compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */ 89}; 90 91/* Handle translated ioctl. */ 92static int translated_ioctl(struct file *file, unsigned int cmd, 93 unsigned long arg) 94{ 95 if (!file->f_op) 96 return -ENOTTY; 97 98 if (file->f_op->unlocked_ioctl) 99 return file->f_op->unlocked_ioctl(file, cmd, arg); 100 101 return -ENOTTY; 102} 103 104/* Handle 32-bit COMEDI_CHANINFO ioctl. */ 105static int compat_chaninfo(struct file *file, unsigned long arg) 106{ 107 struct comedi_chaninfo __user *chaninfo; 108 struct comedi32_chaninfo_struct __user *chaninfo32; 109 int err; 110 union { 111 unsigned int uint; 112 compat_uptr_t uptr; 113 } temp; 114 115 chaninfo32 = compat_ptr(arg); 116 chaninfo = compat_alloc_user_space(sizeof(*chaninfo)); 117 118 /* Copy chaninfo structure. Ignore unused members. */ 119 if (!access_ok(VERIFY_READ, chaninfo32, sizeof(*chaninfo32)) 120 || !access_ok(VERIFY_WRITE, chaninfo, sizeof(*chaninfo))) { 121 return -EFAULT; 122 } 123 err = 0; 124 err |= __get_user(temp.uint, &chaninfo32->subdev); 125 err |= __put_user(temp.uint, &chaninfo->subdev); 126 err |= __get_user(temp.uptr, &chaninfo32->maxdata_list); 127 err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list); 128 err |= __get_user(temp.uptr, &chaninfo32->flaglist); 129 err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist); 130 err |= __get_user(temp.uptr, &chaninfo32->rangelist); 131 err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist); 132 if (err) 133 return -EFAULT; 134 135 return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo); 136} 137 138/* Handle 32-bit COMEDI_RANGEINFO ioctl. */ 139static int compat_rangeinfo(struct file *file, unsigned long arg) 140{ 141 struct comedi_rangeinfo __user *rangeinfo; 142 struct comedi32_rangeinfo_struct __user *rangeinfo32; 143 int err; 144 union { 145 unsigned int uint; 146 compat_uptr_t uptr; 147 } temp; 148 149 rangeinfo32 = compat_ptr(arg); 150 rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo)); 151 152 /* Copy rangeinfo structure. */ 153 if (!access_ok(VERIFY_READ, rangeinfo32, sizeof(*rangeinfo32)) 154 || !access_ok(VERIFY_WRITE, rangeinfo, sizeof(*rangeinfo))) { 155 return -EFAULT; 156 } 157 err = 0; 158 err |= __get_user(temp.uint, &rangeinfo32->range_type); 159 err |= __put_user(temp.uint, &rangeinfo->range_type); 160 err |= __get_user(temp.uptr, &rangeinfo32->range_ptr); 161 err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr); 162 if (err) 163 return -EFAULT; 164 165 return translated_ioctl(file, COMEDI_RANGEINFO, 166 (unsigned long)rangeinfo); 167} 168 169/* Copy 32-bit cmd structure to native cmd structure. */ 170static int get_compat_cmd(struct comedi_cmd __user *cmd, 171 struct comedi32_cmd_struct __user *cmd32) 172{ 173 int err; 174 union { 175 unsigned int uint; 176 compat_uptr_t uptr; 177 } temp; 178 179 /* Copy cmd structure. */ 180 if (!access_ok(VERIFY_READ, cmd32, sizeof(*cmd32)) 181 || !access_ok(VERIFY_WRITE, cmd, sizeof(*cmd))) { 182 return -EFAULT; 183 } 184 err = 0; 185 err |= __get_user(temp.uint, &cmd32->subdev); 186 err |= __put_user(temp.uint, &cmd->subdev); 187 err |= __get_user(temp.uint, &cmd32->flags); 188 err |= __put_user(temp.uint, &cmd->flags); 189 err |= __get_user(temp.uint, &cmd32->start_src); 190 err |= __put_user(temp.uint, &cmd->start_src); 191 err |= __get_user(temp.uint, &cmd32->start_arg); 192 err |= __put_user(temp.uint, &cmd->start_arg); 193 err |= __get_user(temp.uint, &cmd32->scan_begin_src); 194 err |= __put_user(temp.uint, &cmd->scan_begin_src); 195 err |= __get_user(temp.uint, &cmd32->scan_begin_arg); 196 err |= __put_user(temp.uint, &cmd->scan_begin_arg); 197 err |= __get_user(temp.uint, &cmd32->convert_src); 198 err |= __put_user(temp.uint, &cmd->convert_src); 199 err |= __get_user(temp.uint, &cmd32->convert_arg); 200 err |= __put_user(temp.uint, &cmd->convert_arg); 201 err |= __get_user(temp.uint, &cmd32->scan_end_src); 202 err |= __put_user(temp.uint, &cmd->scan_end_src); 203 err |= __get_user(temp.uint, &cmd32->scan_end_arg); 204 err |= __put_user(temp.uint, &cmd->scan_end_arg); 205 err |= __get_user(temp.uint, &cmd32->stop_src); 206 err |= __put_user(temp.uint, &cmd->stop_src); 207 err |= __get_user(temp.uint, &cmd32->stop_arg); 208 err |= __put_user(temp.uint, &cmd->stop_arg); 209 err |= __get_user(temp.uptr, &cmd32->chanlist); 210 err |= __put_user(compat_ptr(temp.uptr), &cmd->chanlist); 211 err |= __get_user(temp.uint, &cmd32->chanlist_len); 212 err |= __put_user(temp.uint, &cmd->chanlist_len); 213 err |= __get_user(temp.uptr, &cmd32->data); 214 err |= __put_user(compat_ptr(temp.uptr), &cmd->data); 215 err |= __get_user(temp.uint, &cmd32->data_len); 216 err |= __put_user(temp.uint, &cmd->data_len); 217 return err ? -EFAULT : 0; 218} 219 220/* Copy native cmd structure to 32-bit cmd structure. */ 221static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32, 222 struct comedi_cmd __user *cmd) 223{ 224 int err; 225 unsigned int temp; 226 227 /* Copy back most of cmd structure. */ 228 /* Assume the pointer values are already valid. */ 229 /* (Could use ptr_to_compat() to set them, but that wasn't implemented 230 * until kernel version 2.6.11.) */ 231 if (!access_ok(VERIFY_READ, cmd, sizeof(*cmd)) 232 || !access_ok(VERIFY_WRITE, cmd32, sizeof(*cmd32))) { 233 return -EFAULT; 234 } 235 err = 0; 236 err |= __get_user(temp, &cmd->subdev); 237 err |= __put_user(temp, &cmd32->subdev); 238 err |= __get_user(temp, &cmd->flags); 239 err |= __put_user(temp, &cmd32->flags); 240 err |= __get_user(temp, &cmd->start_src); 241 err |= __put_user(temp, &cmd32->start_src); 242 err |= __get_user(temp, &cmd->start_arg); 243 err |= __put_user(temp, &cmd32->start_arg); 244 err |= __get_user(temp, &cmd->scan_begin_src); 245 err |= __put_user(temp, &cmd32->scan_begin_src); 246 err |= __get_user(temp, &cmd->scan_begin_arg); 247 err |= __put_user(temp, &cmd32->scan_begin_arg); 248 err |= __get_user(temp, &cmd->convert_src); 249 err |= __put_user(temp, &cmd32->convert_src); 250 err |= __get_user(temp, &cmd->convert_arg); 251 err |= __put_user(temp, &cmd32->convert_arg); 252 err |= __get_user(temp, &cmd->scan_end_src); 253 err |= __put_user(temp, &cmd32->scan_end_src); 254 err |= __get_user(temp, &cmd->scan_end_arg); 255 err |= __put_user(temp, &cmd32->scan_end_arg); 256 err |= __get_user(temp, &cmd->stop_src); 257 err |= __put_user(temp, &cmd32->stop_src); 258 err |= __get_user(temp, &cmd->stop_arg); 259 err |= __put_user(temp, &cmd32->stop_arg); 260 /* Assume chanlist pointer is unchanged. */ 261 err |= __get_user(temp, &cmd->chanlist_len); 262 err |= __put_user(temp, &cmd32->chanlist_len); 263 /* Assume data pointer is unchanged. */ 264 err |= __get_user(temp, &cmd->data_len); 265 err |= __put_user(temp, &cmd32->data_len); 266 return err ? -EFAULT : 0; 267} 268 269/* Handle 32-bit COMEDI_CMD ioctl. */ 270static int compat_cmd(struct file *file, unsigned long arg) 271{ 272 struct comedi_cmd __user *cmd; 273 struct comedi32_cmd_struct __user *cmd32; 274 int rc; 275 276 cmd32 = compat_ptr(arg); 277 cmd = compat_alloc_user_space(sizeof(*cmd)); 278 279 rc = get_compat_cmd(cmd, cmd32); 280 if (rc) 281 return rc; 282 283 return translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd); 284} 285 286/* Handle 32-bit COMEDI_CMDTEST ioctl. */ 287static int compat_cmdtest(struct file *file, unsigned long arg) 288{ 289 struct comedi_cmd __user *cmd; 290 struct comedi32_cmd_struct __user *cmd32; 291 int rc, err; 292 293 cmd32 = compat_ptr(arg); 294 cmd = compat_alloc_user_space(sizeof(*cmd)); 295 296 rc = get_compat_cmd(cmd, cmd32); 297 if (rc) 298 return rc; 299 300 rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd); 301 if (rc < 0) 302 return rc; 303 304 err = put_compat_cmd(cmd32, cmd); 305 if (err) 306 rc = err; 307 308 return rc; 309} 310 311/* Copy 32-bit insn structure to native insn structure. */ 312static int get_compat_insn(struct comedi_insn __user *insn, 313 struct comedi32_insn_struct __user *insn32) 314{ 315 int err; 316 union { 317 unsigned int uint; 318 compat_uptr_t uptr; 319 } temp; 320 321 /* Copy insn structure. Ignore the unused members. */ 322 err = 0; 323 if (!access_ok(VERIFY_READ, insn32, sizeof(*insn32)) 324 || !access_ok(VERIFY_WRITE, insn, sizeof(*insn))) 325 return -EFAULT; 326 327 err |= __get_user(temp.uint, &insn32->insn); 328 err |= __put_user(temp.uint, &insn->insn); 329 err |= __get_user(temp.uint, &insn32->n); 330 err |= __put_user(temp.uint, &insn->n); 331 err |= __get_user(temp.uptr, &insn32->data); 332 err |= __put_user(compat_ptr(temp.uptr), &insn->data); 333 err |= __get_user(temp.uint, &insn32->subdev); 334 err |= __put_user(temp.uint, &insn->subdev); 335 err |= __get_user(temp.uint, &insn32->chanspec); 336 err |= __put_user(temp.uint, &insn->chanspec); 337 return err ? -EFAULT : 0; 338} 339 340/* Handle 32-bit COMEDI_INSNLIST ioctl. */ 341static int compat_insnlist(struct file *file, unsigned long arg) 342{ 343 struct combined_insnlist { 344 struct comedi_insnlist insnlist; 345 struct comedi_insn insn[1]; 346 } __user *s; 347 struct comedi32_insnlist_struct __user *insnlist32; 348 struct comedi32_insn_struct __user *insn32; 349 compat_uptr_t uptr; 350 unsigned int n_insns, n; 351 int err, rc; 352 353 insnlist32 = compat_ptr(arg); 354 355 /* Get 32-bit insnlist structure. */ 356 if (!access_ok(VERIFY_READ, insnlist32, sizeof(*insnlist32))) 357 return -EFAULT; 358 359 err = 0; 360 err |= __get_user(n_insns, &insnlist32->n_insns); 361 err |= __get_user(uptr, &insnlist32->insns); 362 insn32 = compat_ptr(uptr); 363 if (err) 364 return -EFAULT; 365 366 /* Allocate user memory to copy insnlist and insns into. */ 367 s = compat_alloc_user_space(offsetof(struct combined_insnlist, 368 insn[n_insns])); 369 370 /* Set native insnlist structure. */ 371 if (!access_ok(VERIFY_WRITE, &s->insnlist, sizeof(s->insnlist))) 372 return -EFAULT; 373 374 err |= __put_user(n_insns, &s->insnlist.n_insns); 375 err |= __put_user(&s->insn[0], &s->insnlist.insns); 376 if (err) 377 return -EFAULT; 378 379 /* Copy insn structures. */ 380 for (n = 0; n < n_insns; n++) { 381 rc = get_compat_insn(&s->insn[n], &insn32[n]); 382 if (rc) 383 return rc; 384 } 385 386 return translated_ioctl(file, COMEDI_INSNLIST, 387 (unsigned long)&s->insnlist); 388} 389 390/* Handle 32-bit COMEDI_INSN ioctl. */ 391static int compat_insn(struct file *file, unsigned long arg) 392{ 393 struct comedi_insn __user *insn; 394 struct comedi32_insn_struct __user *insn32; 395 int rc; 396 397 insn32 = compat_ptr(arg); 398 insn = compat_alloc_user_space(sizeof(*insn)); 399 400 rc = get_compat_insn(insn, insn32); 401 if (rc) 402 return rc; 403 404 return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn); 405} 406 407/* Process untranslated ioctl. */ 408/* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */ 409static inline int raw_ioctl(struct file *file, unsigned int cmd, 410 unsigned long arg) 411{ 412 int rc; 413 414 switch (cmd) { 415 case COMEDI_DEVCONFIG: 416 case COMEDI_DEVINFO: 417 case COMEDI_SUBDINFO: 418 case COMEDI_BUFCONFIG: 419 case COMEDI_BUFINFO: 420 /* Just need to translate the pointer argument. */ 421 arg = (unsigned long)compat_ptr(arg); 422 rc = translated_ioctl(file, cmd, arg); 423 break; 424 case COMEDI_LOCK: 425 case COMEDI_UNLOCK: 426 case COMEDI_CANCEL: 427 case COMEDI_POLL: 428 /* No translation needed. */ 429 rc = translated_ioctl(file, cmd, arg); 430 break; 431 case COMEDI32_CHANINFO: 432 rc = compat_chaninfo(file, arg); 433 break; 434 case COMEDI32_RANGEINFO: 435 rc = compat_rangeinfo(file, arg); 436 break; 437 case COMEDI32_CMD: 438 rc = compat_cmd(file, arg); 439 break; 440 case COMEDI32_CMDTEST: 441 rc = compat_cmdtest(file, arg); 442 break; 443 case COMEDI32_INSNLIST: 444 rc = compat_insnlist(file, arg); 445 break; 446 case COMEDI32_INSN: 447 rc = compat_insn(file, arg); 448 break; 449 default: 450 rc = -ENOIOCTLCMD; 451 break; 452 } 453 return rc; 454} 455 456/* compat_ioctl file operation. */ 457/* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */ 458long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 459{ 460 return raw_ioctl(file, cmd, arg); 461} 462 463#endif /* CONFIG_COMPAT */ 464