linux/drivers/staging/comedi/comedi_compat32.c
<<
>>
Prefs
   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