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