]> Git Repo - linux.git/commitdiff
Merge branch 'uaccess.comedi' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <[email protected]>
Wed, 3 Jun 2020 22:55:45 +0000 (15:55 -0700)
committerLinus Torvalds <[email protected]>
Wed, 3 Jun 2020 22:55:45 +0000 (15:55 -0700)
Pull comedi uaccess cleanups from Al Viro:
 "Comedi compat ioctls done saner - killing the single biggest pile of
  __get_user/__put_user outside of arch/* in the process"

* 'uaccess.comedi' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  comedi: get rid of compat_alloc_user_space() mess in COMEDI_CMD{,TEST} compat
  comedi: do_cmd_ioctl(): lift copyin/copyout into the caller
  comedi: do_cmdtest_ioctl(): lift copyin/copyout into the caller
  comedi: lift copy_from_user() into callers of __comedi_get_user_cmd()
  comedi: get rid of compat_alloc_user_space() mess in COMEDI_INSNLIST compat
  comedi: get rid of compat_alloc_user_space() mess in COMEDI_INSN compat
  comedi: get rid of compat_alloc_user_space() mess in COMEDI_RANGEINFO compat
  comedi: get rid of compat_alloc_user_space() mess in COMEDI_CHANINFO compat
  comedi: get rid of indirection via translated_ioctl()
  comedi: move compat ioctl handling to native fops

1  2 
drivers/staging/comedi/comedi_fops.c

index e84b4fb493d627d5a17f183d99978ce2029fe376,dd14c2935292fbe8bc61dbb4ea71cbbb003c6a9c..a56c8f74a27b676a4324c6a41249383978c564d7
@@@ -4,13 -4,14 +4,14 @@@
   * comedi kernel module
   *
   * COMEDI - Linux Control and Measurement Device Interface
-  * Copyright (C) 1997-2000 David A. Schleef <[email protected]>
+  * Copyright (C) 1997-2007 David A. Schleef <[email protected]>
+  * compat ioctls:
+  * Author: Ian Abbott, MEV Ltd. <[email protected]>
+  * Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
   */
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  
- #include "comedi_compat32.h"
  #include <linux/module.h>
  #include <linux/errno.h>
  #include <linux/kernel.h>
@@@ -27,6 -28,7 +28,7 @@@
  
  #include <linux/io.h>
  #include <linux/uaccess.h>
+ #include <linux/compat.h>
  
  #include "comedi_internal.h"
  
@@@ -1047,31 -1049,28 +1049,28 @@@ static int do_subdinfo_ioctl(struct com
   *    array of range table lengths to chaninfo->range_table_list if requested
   */
  static int do_chaninfo_ioctl(struct comedi_device *dev,
-                            struct comedi_chaninfo __user *arg)
+                            struct comedi_chaninfo *it)
  {
        struct comedi_subdevice *s;
-       struct comedi_chaninfo it;
  
        lockdep_assert_held(&dev->mutex);
-       if (copy_from_user(&it, arg, sizeof(it)))
-               return -EFAULT;
  
-       if (it.subdev >= dev->n_subdevices)
+       if (it->subdev >= dev->n_subdevices)
                return -EINVAL;
-       s = &dev->subdevices[it.subdev];
+       s = &dev->subdevices[it->subdev];
  
-       if (it.maxdata_list) {
+       if (it->maxdata_list) {
                if (s->maxdata || !s->maxdata_list)
                        return -EINVAL;
-               if (copy_to_user(it.maxdata_list, s->maxdata_list,
+               if (copy_to_user(it->maxdata_list, s->maxdata_list,
                                 s->n_chan * sizeof(unsigned int)))
                        return -EFAULT;
        }
  
-       if (it.flaglist)
+       if (it->flaglist)
                return -EINVAL; /* flaglist not supported */
  
-       if (it.rangelist) {
+       if (it->rangelist) {
                int i;
  
                if (!s->range_table_list)
                for (i = 0; i < s->n_chan; i++) {
                        int x;
  
-                       x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) |
+                       x = (dev->minor << 28) | (it->subdev << 24) | (i << 16) |
                            (s->range_table_list[i]->length);
-                       if (put_user(x, it.rangelist + i))
+                       if (put_user(x, it->rangelist + i))
                                return -EFAULT;
                }
        }
  #define MIN_SAMPLES 16
  #define MAX_SAMPLES 65536
  static int do_insnlist_ioctl(struct comedi_device *dev,
-                            struct comedi_insnlist __user *arg, void *file)
+                            struct comedi_insn *insns,
+                            unsigned int n_insns,
+                            void *file)
  {
-       struct comedi_insnlist insnlist;
-       struct comedi_insn *insns = NULL;
        unsigned int *data = NULL;
        unsigned int max_n_data_required = MIN_SAMPLES;
        int i = 0;
        int ret = 0;
  
        lockdep_assert_held(&dev->mutex);
-       if (copy_from_user(&insnlist, arg, sizeof(insnlist)))
-               return -EFAULT;
-       insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL);
-       if (!insns) {
-               ret = -ENOMEM;
-               goto error;
-       }
-       if (copy_from_user(insns, insnlist.insns,
-                          sizeof(*insns) * insnlist.n_insns)) {
-               dev_dbg(dev->class_dev, "copy_from_user failed\n");
-               ret = -EFAULT;
-               goto error;
-       }
  
        /* Determine maximum memory needed for all instructions. */
-       for (i = 0; i < insnlist.n_insns; ++i) {
+       for (i = 0; i < n_insns; ++i) {
                if (insns[i].n > MAX_SAMPLES) {
                        dev_dbg(dev->class_dev,
                                "number of samples too large\n");
                goto error;
        }
  
-       for (i = 0; i < insnlist.n_insns; ++i) {
+       for (i = 0; i < n_insns; ++i) {
                if (insns[i].insn & INSN_MASK_WRITE) {
                        if (copy_from_user(data, insns[i].data,
                                           insns[i].n * sizeof(unsigned int))) {
        }
  
  error:
-       kfree(insns);
        kfree(data);
  
        if (ret < 0)
   *    data (for reads) to insn->data pointer
   */
  static int do_insn_ioctl(struct comedi_device *dev,
-                        struct comedi_insn __user *arg, void *file)
+                        struct comedi_insn *insn, void *file)
  {
-       struct comedi_insn insn;
        unsigned int *data = NULL;
        unsigned int n_data = MIN_SAMPLES;
        int ret = 0;
  
        lockdep_assert_held(&dev->mutex);
-       if (copy_from_user(&insn, arg, sizeof(insn)))
-               return -EFAULT;
  
-       n_data = max(n_data, insn.n);
+       n_data = max(n_data, insn->n);
  
        /* This is where the behavior of insn and insnlist deviate. */
-       if (insn.n > MAX_SAMPLES) {
-               insn.n = MAX_SAMPLES;
+       if (insn->n > MAX_SAMPLES) {
+               insn->n = MAX_SAMPLES;
                n_data = MAX_SAMPLES;
        }
  
                goto error;
        }
  
-       if (insn.insn & INSN_MASK_WRITE) {
+       if (insn->insn & INSN_MASK_WRITE) {
                if (copy_from_user(data,
-                                  insn.data,
-                                  insn.n * sizeof(unsigned int))) {
+                                  insn->data,
+                                  insn->n * sizeof(unsigned int))) {
                        ret = -EFAULT;
                        goto error;
                }
        }
-       ret = parse_insn(dev, &insn, data, file);
+       ret = parse_insn(dev, insn, data, file);
        if (ret < 0)
                goto error;
-       if (insn.insn & INSN_MASK_READ) {
-               if (copy_to_user(insn.data,
+       if (insn->insn & INSN_MASK_READ) {
+               if (copy_to_user(insn->data,
                                 data,
-                                insn.n * sizeof(unsigned int))) {
+                                insn->n * sizeof(unsigned int))) {
                        ret = -EFAULT;
                        goto error;
                }
        }
-       ret = insn.n;
+       ret = insn->n;
  
  error:
        kfree(data);
  }
  
  static int __comedi_get_user_cmd(struct comedi_device *dev,
-                                struct comedi_cmd __user *arg,
                                 struct comedi_cmd *cmd)
  {
        struct comedi_subdevice *s;
  
        lockdep_assert_held(&dev->mutex);
-       if (copy_from_user(cmd, arg, sizeof(*cmd))) {
-               dev_dbg(dev->class_dev, "bad cmd address\n");
-               return -EFAULT;
-       }
        if (cmd->subdev >= dev->n_subdevices) {
                dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd->subdev);
                return -ENODEV;
@@@ -1767,9 -1741,8 +1741,8 @@@ static int __comedi_get_user_chanlist(s
   *    possibly modified comedi_cmd structure (when -EAGAIN returned)
   */
  static int do_cmd_ioctl(struct comedi_device *dev,
-                       struct comedi_cmd __user *arg, void *file)
+                       struct comedi_cmd *cmd, bool *copy, void *file)
  {
-       struct comedi_cmd cmd;
        struct comedi_subdevice *s;
        struct comedi_async *async;
        unsigned int __user *user_chanlist;
  
        lockdep_assert_held(&dev->mutex);
  
-       /* get the user's cmd and do some simple validation */
-       ret = __comedi_get_user_cmd(dev, arg, &cmd);
+       /* do some simple cmd validation */
+       ret = __comedi_get_user_cmd(dev, cmd);
        if (ret)
                return ret;
  
        /* save user's chanlist pointer so it can be restored later */
-       user_chanlist = (unsigned int __user *)cmd.chanlist;
+       user_chanlist = (unsigned int __user *)cmd->chanlist;
  
-       s = &dev->subdevices[cmd.subdev];
+       s = &dev->subdevices[cmd->subdev];
        async = s->async;
  
        /* are we locked? (ioctl lock) */
        }
  
        /* make sure channel/gain list isn't too short */
-       if (cmd.chanlist_len < 1) {
+       if (cmd->chanlist_len < 1) {
                dev_dbg(dev->class_dev, "channel/gain list too short %u < 1\n",
-                       cmd.chanlist_len);
+                       cmd->chanlist_len);
                return -EINVAL;
        }
  
-       async->cmd = cmd;
+       async->cmd = *cmd;
        async->cmd.data = NULL;
  
        /* load channel/gain list */
  
        if (async->cmd.flags & CMDF_BOGUS || ret) {
                dev_dbg(dev->class_dev, "test returned %d\n", ret);
-               cmd = async->cmd;
+               *cmd = async->cmd;
                /* restore chanlist pointer before copying back */
-               cmd.chanlist = (unsigned int __force *)user_chanlist;
-               cmd.data = NULL;
-               if (copy_to_user(arg, &cmd, sizeof(cmd))) {
-                       dev_dbg(dev->class_dev, "fault writing cmd\n");
-                       ret = -EFAULT;
-                       goto cleanup;
-               }
+               cmd->chanlist = (unsigned int __force *)user_chanlist;
+               cmd->data = NULL;
+               *copy = true;
                ret = -EAGAIN;
                goto cleanup;
        }
@@@ -1877,44 -1846,39 +1846,39 @@@ cleanup
   *    possibly modified comedi_cmd structure
   */
  static int do_cmdtest_ioctl(struct comedi_device *dev,
-                           struct comedi_cmd __user *arg, void *file)
+                           struct comedi_cmd *cmd, bool *copy, void *file)
  {
-       struct comedi_cmd cmd;
        struct comedi_subdevice *s;
        unsigned int __user *user_chanlist;
        int ret;
  
        lockdep_assert_held(&dev->mutex);
  
-       /* get the user's cmd and do some simple validation */
-       ret = __comedi_get_user_cmd(dev, arg, &cmd);
+       /* do some simple cmd validation */
+       ret = __comedi_get_user_cmd(dev, cmd);
        if (ret)
                return ret;
  
        /* save user's chanlist pointer so it can be restored later */
-       user_chanlist = (unsigned int __user *)cmd.chanlist;
+       user_chanlist = (unsigned int __user *)cmd->chanlist;
  
-       s = &dev->subdevices[cmd.subdev];
+       s = &dev->subdevices[cmd->subdev];
  
        /* user_chanlist can be NULL for COMEDI_CMDTEST ioctl */
        if (user_chanlist) {
                /* load channel/gain list */
-               ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &cmd);
+               ret = __comedi_get_user_chanlist(dev, s, user_chanlist, cmd);
                if (ret)
                        return ret;
        }
  
-       ret = s->do_cmdtest(dev, s, &cmd);
+       ret = s->do_cmdtest(dev, s, cmd);
  
-       kfree(cmd.chanlist);    /* free kernel copy of user chanlist */
+       kfree(cmd->chanlist);   /* free kernel copy of user chanlist */
  
        /* restore chanlist pointer before copying back */
-       cmd.chanlist = (unsigned int __force *)user_chanlist;
-       if (copy_to_user(arg, &cmd, sizeof(cmd))) {
-               dev_dbg(dev->class_dev, "bad cmd address\n");
-               ret = -EFAULT;
-       }
+       cmd->chanlist = (unsigned int __force *)user_chanlist;
+       *copy = true;
  
        return ret;
  }
@@@ -2203,12 -2167,22 +2167,22 @@@ static long comedi_unlocked_ioctl(struc
                                       (struct comedi_subdinfo __user *)arg,
                                       file);
                break;
-       case COMEDI_CHANINFO:
-               rc = do_chaninfo_ioctl(dev, (void __user *)arg);
+       case COMEDI_CHANINFO: {
+               struct comedi_chaninfo it;
+               if (copy_from_user(&it, (void __user *)arg, sizeof(it)))
+                       rc = -EFAULT;
+               else
+                       rc = do_chaninfo_ioctl(dev, &it);
                break;
-       case COMEDI_RANGEINFO:
-               rc = do_rangeinfo_ioctl(dev, (void __user *)arg);
+       }
+       case COMEDI_RANGEINFO: {
+               struct comedi_rangeinfo it;
+               if (copy_from_user(&it, (void __user *)arg, sizeof(it)))
+                       rc = -EFAULT;
+               else
+                       rc = do_rangeinfo_ioctl(dev, &it);
                break;
+       }
        case COMEDI_BUFINFO:
                rc = do_bufinfo_ioctl(dev,
                                      (struct comedi_bufinfo __user *)arg,
        case COMEDI_CANCEL:
                rc = do_cancel_ioctl(dev, arg, file);
                break;
-       case COMEDI_CMD:
-               rc = do_cmd_ioctl(dev, (struct comedi_cmd __user *)arg, file);
+       case COMEDI_CMD: {
+               struct comedi_cmd cmd;
+               bool copy = false;
+               if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) {
+                       rc = -EFAULT;
+                       break;
+               }
+               rc = do_cmd_ioctl(dev, &cmd, &copy, file);
+               if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd)))
+                       rc = -EFAULT;
                break;
-       case COMEDI_CMDTEST:
-               rc = do_cmdtest_ioctl(dev, (struct comedi_cmd __user *)arg,
-                                     file);
+       }
+       case COMEDI_CMDTEST: {
+               struct comedi_cmd cmd;
+               bool copy = false;
+               if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) {
+                       rc = -EFAULT;
+                       break;
+               }
+               rc = do_cmdtest_ioctl(dev, &cmd, &copy, file);
+               if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd)))
+                       rc = -EFAULT;
                break;
-       case COMEDI_INSNLIST:
-               rc = do_insnlist_ioctl(dev,
-                                      (struct comedi_insnlist __user *)arg,
-                                      file);
+       }
+       case COMEDI_INSNLIST: {
+               struct comedi_insnlist insnlist;
+               struct comedi_insn *insns = NULL;
+               if (copy_from_user(&insnlist, (void __user *)arg,
+                                  sizeof(insnlist))) {
+                       rc = -EFAULT;
+                       break;
+               }
+               insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL);
+               if (!insns) {
+                       rc = -ENOMEM;
+                       break;
+               }
+               if (copy_from_user(insns, insnlist.insns,
+                                  sizeof(*insns) * insnlist.n_insns)) {
+                       rc = -EFAULT;
+                       kfree(insns);
+                       break;
+               }
+               rc = do_insnlist_ioctl(dev, insns, insnlist.n_insns, file);
+               kfree(insns);
                break;
-       case COMEDI_INSN:
-               rc = do_insn_ioctl(dev, (struct comedi_insn __user *)arg,
-                                  file);
+       }
+       case COMEDI_INSN: {
+               struct comedi_insn insn;
+               if (copy_from_user(&insn, (void __user *)arg, sizeof(insn)))
+                       rc = -EFAULT;
+               else
+                       rc = do_insn_ioctl(dev, &insn, file);
                break;
+       }
        case COMEDI_POLL:
                rc = do_poll_ioctl(dev, arg, file);
                break;
@@@ -2725,10 -2741,8 +2741,10 @@@ static int comedi_open(struct inode *in
        }
  
        cfp = kzalloc(sizeof(*cfp), GFP_KERNEL);
 -      if (!cfp)
 +      if (!cfp) {
 +              comedi_dev_put(dev);
                return -ENOMEM;
 +      }
  
        cfp->dev = dev;
  
@@@ -2808,6 -2822,344 +2824,344 @@@ static int comedi_close(struct inode *i
        return 0;
  }
  
+ #ifdef CONFIG_COMPAT
+ #define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct)
+ #define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct)
+ /*
+  * N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
+  * It's too late to change it now, but it only affects the command number.
+  */
+ #define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct)
+ /*
+  * N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
+  * It's too late to change it now, but it only affects the command number.
+  */
+ #define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct)
+ #define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct)
+ #define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct)
+ struct comedi32_chaninfo_struct {
+       unsigned int subdev;
+       compat_uptr_t maxdata_list;     /* 32-bit 'unsigned int *' */
+       compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
+       compat_uptr_t rangelist;        /* 32-bit 'unsigned int *' */
+       unsigned int unused[4];
+ };
+ struct comedi32_rangeinfo_struct {
+       unsigned int range_type;
+       compat_uptr_t range_ptr;        /* 32-bit 'void *' */
+ };
+ struct comedi32_cmd_struct {
+       unsigned int subdev;
+       unsigned int flags;
+       unsigned int start_src;
+       unsigned int start_arg;
+       unsigned int scan_begin_src;
+       unsigned int scan_begin_arg;
+       unsigned int convert_src;
+       unsigned int convert_arg;
+       unsigned int scan_end_src;
+       unsigned int scan_end_arg;
+       unsigned int stop_src;
+       unsigned int stop_arg;
+       compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
+       unsigned int chanlist_len;
+       compat_uptr_t data;     /* 32-bit 'short *' */
+       unsigned int data_len;
+ };
+ struct comedi32_insn_struct {
+       unsigned int insn;
+       unsigned int n;
+       compat_uptr_t data;     /* 32-bit 'unsigned int *' */
+       unsigned int subdev;
+       unsigned int chanspec;
+       unsigned int unused[3];
+ };
+ struct comedi32_insnlist_struct {
+       unsigned int n_insns;
+       compat_uptr_t insns;    /* 32-bit 'struct comedi_insn *' */
+ };
+ /* Handle 32-bit COMEDI_CHANINFO ioctl. */
+ static int compat_chaninfo(struct file *file, unsigned long arg)
+ {
+       struct comedi_file *cfp = file->private_data;
+       struct comedi_device *dev = cfp->dev;
+       struct comedi32_chaninfo_struct chaninfo32;
+       struct comedi_chaninfo chaninfo;
+       int err;
+       if (copy_from_user(&chaninfo32, compat_ptr(arg), sizeof(chaninfo32)))
+               return -EFAULT;
+       memset(&chaninfo, 0, sizeof(chaninfo));
+       chaninfo.subdev = chaninfo32.subdev;
+       chaninfo.maxdata_list = compat_ptr(chaninfo32.maxdata_list);
+       chaninfo.flaglist = compat_ptr(chaninfo32.flaglist);
+       chaninfo.rangelist = compat_ptr(chaninfo32.rangelist);
+       mutex_lock(&dev->mutex);
+       err = do_chaninfo_ioctl(dev, &chaninfo);
+       mutex_unlock(&dev->mutex);
+       return err;
+ }
+ /* Handle 32-bit COMEDI_RANGEINFO ioctl. */
+ static int compat_rangeinfo(struct file *file, unsigned long arg)
+ {
+       struct comedi_file *cfp = file->private_data;
+       struct comedi_device *dev = cfp->dev;
+       struct comedi32_rangeinfo_struct rangeinfo32;
+       struct comedi_rangeinfo rangeinfo;
+       int err;
+       if (copy_from_user(&rangeinfo32, compat_ptr(arg), sizeof(rangeinfo32)))
+               return -EFAULT;
+       memset(&rangeinfo, 0, sizeof(rangeinfo));
+       rangeinfo.range_type = rangeinfo32.range_type;
+       rangeinfo.range_ptr = compat_ptr(rangeinfo32.range_ptr);
+       mutex_lock(&dev->mutex);
+       err = do_rangeinfo_ioctl(dev, &rangeinfo);
+       mutex_unlock(&dev->mutex);
+       return err;
+ }
+ /* Copy 32-bit cmd structure to native cmd structure. */
+ static int get_compat_cmd(struct comedi_cmd *cmd,
+                         struct comedi32_cmd_struct __user *cmd32)
+ {
+       struct comedi32_cmd_struct v32;
+       if (copy_from_user(&v32, cmd32, sizeof(v32)))
+               return -EFAULT;
+       cmd->subdev = v32.subdev;
+       cmd->flags = v32.flags;
+       cmd->start_src = v32.start_src;
+       cmd->start_arg = v32.start_arg;
+       cmd->scan_begin_src = v32.scan_begin_src;
+       cmd->scan_begin_arg = v32.scan_begin_arg;
+       cmd->convert_src = v32.convert_src;
+       cmd->convert_arg = v32.convert_arg;
+       cmd->scan_end_src = v32.scan_end_src;
+       cmd->scan_end_arg = v32.scan_end_arg;
+       cmd->stop_src = v32.stop_src;
+       cmd->stop_arg = v32.stop_arg;
+       cmd->chanlist = compat_ptr(v32.chanlist);
+       cmd->chanlist_len = v32.chanlist_len;
+       cmd->data = compat_ptr(v32.data);
+       cmd->data_len = v32.data_len;
+       return 0;
+ }
+ /* Copy native cmd structure to 32-bit cmd structure. */
+ static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
+                         struct comedi_cmd *cmd)
+ {
+       struct comedi32_cmd_struct v32;
+       memset(&v32, 0, sizeof(v32));
+       v32.subdev = cmd->subdev;
+       v32.flags = cmd->flags;
+       v32.start_src = cmd->start_src;
+       v32.start_arg = cmd->start_arg;
+       v32.scan_begin_src = cmd->scan_begin_src;
+       v32.scan_begin_arg = cmd->scan_begin_arg;
+       v32.convert_src = cmd->convert_src;
+       v32.convert_arg = cmd->convert_arg;
+       v32.scan_end_src = cmd->scan_end_src;
+       v32.scan_end_arg = cmd->scan_end_arg;
+       v32.stop_src = cmd->stop_src;
+       v32.stop_arg = cmd->stop_arg;
+       /* Assume chanlist pointer is unchanged. */
+       v32.chanlist = ptr_to_compat(cmd->chanlist);
+       v32.chanlist_len = cmd->chanlist_len;
+       v32.data = ptr_to_compat(cmd->data);
+       v32.data_len = cmd->data_len;
+       return copy_to_user(cmd32, &v32, sizeof(v32));
+ }
+ /* Handle 32-bit COMEDI_CMD ioctl. */
+ static int compat_cmd(struct file *file, unsigned long arg)
+ {
+       struct comedi_file *cfp = file->private_data;
+       struct comedi_device *dev = cfp->dev;
+       struct comedi_cmd cmd;
+       bool copy = false;
+       int rc, err;
+       rc = get_compat_cmd(&cmd, compat_ptr(arg));
+       if (rc)
+               return rc;
+       mutex_lock(&dev->mutex);
+       rc = do_cmd_ioctl(dev, &cmd, &copy, file);
+       mutex_unlock(&dev->mutex);
+       if (copy) {
+               /* Special case: copy cmd back to user. */
+               err = put_compat_cmd(compat_ptr(arg), &cmd);
+               if (err)
+                       rc = err;
+       }
+       return rc;
+ }
+ /* Handle 32-bit COMEDI_CMDTEST ioctl. */
+ static int compat_cmdtest(struct file *file, unsigned long arg)
+ {
+       struct comedi_file *cfp = file->private_data;
+       struct comedi_device *dev = cfp->dev;
+       struct comedi_cmd cmd;
+       bool copy = false;
+       int rc, err;
+       rc = get_compat_cmd(&cmd, compat_ptr(arg));
+       if (rc)
+               return rc;
+       mutex_lock(&dev->mutex);
+       rc = do_cmdtest_ioctl(dev, &cmd, &copy, file);
+       mutex_unlock(&dev->mutex);
+       if (copy) {
+               err = put_compat_cmd(compat_ptr(arg), &cmd);
+               if (err)
+                       rc = err;
+       }
+       return rc;
+ }
+ /* Copy 32-bit insn structure to native insn structure. */
+ static int get_compat_insn(struct comedi_insn *insn,
+                          struct comedi32_insn_struct __user *insn32)
+ {
+       struct comedi32_insn_struct v32;
+       /* Copy insn structure.  Ignore the unused members. */
+       if (copy_from_user(&v32, insn32, sizeof(v32)))
+               return -EFAULT;
+       memset(insn, 0, sizeof(*insn));
+       insn->insn = v32.insn;
+       insn->n = v32.n;
+       insn->data = compat_ptr(v32.data);
+       insn->subdev = v32.subdev;
+       insn->chanspec = v32.chanspec;
+       return 0;
+ }
+ /* Handle 32-bit COMEDI_INSNLIST ioctl. */
+ static int compat_insnlist(struct file *file, unsigned long arg)
+ {
+       struct comedi_file *cfp = file->private_data;
+       struct comedi_device *dev = cfp->dev;
+       struct comedi32_insnlist_struct insnlist32;
+       struct comedi32_insn_struct __user *insn32;
+       struct comedi_insn *insns;
+       unsigned int n;
+       int rc;
+       if (copy_from_user(&insnlist32, compat_ptr(arg), sizeof(insnlist32)))
+               return -EFAULT;
+       insns = kcalloc(insnlist32.n_insns, sizeof(*insns), GFP_KERNEL);
+       if (!insns)
+               return -ENOMEM;
+       /* Copy insn structures. */
+       insn32 = compat_ptr(insnlist32.insns);
+       for (n = 0; n < insnlist32.n_insns; n++) {
+               rc = get_compat_insn(insns + n, insn32 + n);
+               if (rc) {
+                       kfree(insns);
+                       return rc;
+               }
+       }
+       mutex_lock(&dev->mutex);
+       rc = do_insnlist_ioctl(dev, insns, insnlist32.n_insns, file);
+       mutex_unlock(&dev->mutex);
+       return rc;
+ }
+ /* Handle 32-bit COMEDI_INSN ioctl. */
+ static int compat_insn(struct file *file, unsigned long arg)
+ {
+       struct comedi_file *cfp = file->private_data;
+       struct comedi_device *dev = cfp->dev;
+       struct comedi_insn insn;
+       int rc;
+       rc = get_compat_insn(&insn, (void __user *)arg);
+       if (rc)
+               return rc;
+       mutex_lock(&dev->mutex);
+       rc = do_insn_ioctl(dev, &insn, file);
+       mutex_unlock(&dev->mutex);
+       return rc;
+ }
+ /*
+  * compat_ioctl file operation.
+  *
+  * Returns -ENOIOCTLCMD for unrecognised ioctl codes.
+  */
+ static long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ {
+       int rc;
+       switch (cmd) {
+       case COMEDI_DEVCONFIG:
+       case COMEDI_DEVINFO:
+       case COMEDI_SUBDINFO:
+       case COMEDI_BUFCONFIG:
+       case COMEDI_BUFINFO:
+               /* Just need to translate the pointer argument. */
+               arg = (unsigned long)compat_ptr(arg);
+               rc = comedi_unlocked_ioctl(file, cmd, arg);
+               break;
+       case COMEDI_LOCK:
+       case COMEDI_UNLOCK:
+       case COMEDI_CANCEL:
+       case COMEDI_POLL:
+       case COMEDI_SETRSUBD:
+       case COMEDI_SETWSUBD:
+               /* No translation needed. */
+               rc = comedi_unlocked_ioctl(file, cmd, arg);
+               break;
+       case COMEDI32_CHANINFO:
+               rc = compat_chaninfo(file, arg);
+               break;
+       case COMEDI32_RANGEINFO:
+               rc = compat_rangeinfo(file, arg);
+               break;
+       case COMEDI32_CMD:
+               rc = compat_cmd(file, arg);
+               break;
+       case COMEDI32_CMDTEST:
+               rc = compat_cmdtest(file, arg);
+               break;
+       case COMEDI32_INSNLIST:
+               rc = compat_insnlist(file, arg);
+               break;
+       case COMEDI32_INSN:
+               rc = compat_insn(file, arg);
+               break;
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+       return rc;
+ }
+ #else
+ #define comedi_compat_ioctl NULL
+ #endif
  static const struct file_operations comedi_fops = {
        .owner = THIS_MODULE,
        .unlocked_ioctl = comedi_unlocked_ioctl,
This page took 0.08776 seconds and 4 git commands to generate.