mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 11:19:37 +00:00
Merge branch 'uaccess.comedi' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
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
This commit is contained in:
commit
2e63f6ce7e
6 changed files with 467 additions and 600 deletions
|
@ -4,7 +4,6 @@ ccflags-$(CONFIG_COMEDI_DEBUG) := -DDEBUG
|
|||
comedi-y := comedi_fops.o range.o drivers.o \
|
||||
comedi_buf.o
|
||||
comedi-$(CONFIG_PROC_FS) += proc.o
|
||||
comedi-$(CONFIG_COMPAT) += comedi_compat32.o
|
||||
|
||||
obj-$(CONFIG_COMEDI_PCI_DRIVERS) += comedi_pci.o
|
||||
obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += comedi_pcmcia.o
|
||||
|
|
|
@ -1,455 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* comedi/comedi_compat32.c
|
||||
* 32-bit ioctl compatibility for 64-bit comedi kernel module.
|
||||
*
|
||||
* Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
|
||||
* Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
|
||||
*
|
||||
* COMEDI - Linux Control and Measurement Device Interface
|
||||
* Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
|
||||
*/
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/fs.h>
|
||||
#include "comedi.h"
|
||||
#include "comedi_compat32.h"
|
||||
|
||||
#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 translated ioctl. */
|
||||
static int translated_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
if (file->f_op->unlocked_ioctl)
|
||||
return file->f_op->unlocked_ioctl(file, cmd, arg);
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
/* Handle 32-bit COMEDI_CHANINFO ioctl. */
|
||||
static int compat_chaninfo(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct comedi_chaninfo __user *chaninfo;
|
||||
struct comedi32_chaninfo_struct __user *chaninfo32;
|
||||
int err;
|
||||
union {
|
||||
unsigned int uint;
|
||||
compat_uptr_t uptr;
|
||||
} temp;
|
||||
|
||||
chaninfo32 = compat_ptr(arg);
|
||||
chaninfo = compat_alloc_user_space(sizeof(*chaninfo));
|
||||
|
||||
/* Copy chaninfo structure. Ignore unused members. */
|
||||
if (!access_ok(chaninfo32, sizeof(*chaninfo32)) ||
|
||||
!access_ok(chaninfo, sizeof(*chaninfo)))
|
||||
return -EFAULT;
|
||||
|
||||
err = 0;
|
||||
err |= __get_user(temp.uint, &chaninfo32->subdev);
|
||||
err |= __put_user(temp.uint, &chaninfo->subdev);
|
||||
err |= __get_user(temp.uptr, &chaninfo32->maxdata_list);
|
||||
err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list);
|
||||
err |= __get_user(temp.uptr, &chaninfo32->flaglist);
|
||||
err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist);
|
||||
err |= __get_user(temp.uptr, &chaninfo32->rangelist);
|
||||
err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist);
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo);
|
||||
}
|
||||
|
||||
/* Handle 32-bit COMEDI_RANGEINFO ioctl. */
|
||||
static int compat_rangeinfo(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct comedi_rangeinfo __user *rangeinfo;
|
||||
struct comedi32_rangeinfo_struct __user *rangeinfo32;
|
||||
int err;
|
||||
union {
|
||||
unsigned int uint;
|
||||
compat_uptr_t uptr;
|
||||
} temp;
|
||||
|
||||
rangeinfo32 = compat_ptr(arg);
|
||||
rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo));
|
||||
|
||||
/* Copy rangeinfo structure. */
|
||||
if (!access_ok(rangeinfo32, sizeof(*rangeinfo32)) ||
|
||||
!access_ok(rangeinfo, sizeof(*rangeinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
err = 0;
|
||||
err |= __get_user(temp.uint, &rangeinfo32->range_type);
|
||||
err |= __put_user(temp.uint, &rangeinfo->range_type);
|
||||
err |= __get_user(temp.uptr, &rangeinfo32->range_ptr);
|
||||
err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr);
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
return translated_ioctl(file, COMEDI_RANGEINFO,
|
||||
(unsigned long)rangeinfo);
|
||||
}
|
||||
|
||||
/* Copy 32-bit cmd structure to native cmd structure. */
|
||||
static int get_compat_cmd(struct comedi_cmd __user *cmd,
|
||||
struct comedi32_cmd_struct __user *cmd32)
|
||||
{
|
||||
int err;
|
||||
union {
|
||||
unsigned int uint;
|
||||
compat_uptr_t uptr;
|
||||
} temp;
|
||||
|
||||
/* Copy cmd structure. */
|
||||
if (!access_ok(cmd32, sizeof(*cmd32)) ||
|
||||
!access_ok(cmd, sizeof(*cmd)))
|
||||
return -EFAULT;
|
||||
|
||||
err = 0;
|
||||
err |= __get_user(temp.uint, &cmd32->subdev);
|
||||
err |= __put_user(temp.uint, &cmd->subdev);
|
||||
err |= __get_user(temp.uint, &cmd32->flags);
|
||||
err |= __put_user(temp.uint, &cmd->flags);
|
||||
err |= __get_user(temp.uint, &cmd32->start_src);
|
||||
err |= __put_user(temp.uint, &cmd->start_src);
|
||||
err |= __get_user(temp.uint, &cmd32->start_arg);
|
||||
err |= __put_user(temp.uint, &cmd->start_arg);
|
||||
err |= __get_user(temp.uint, &cmd32->scan_begin_src);
|
||||
err |= __put_user(temp.uint, &cmd->scan_begin_src);
|
||||
err |= __get_user(temp.uint, &cmd32->scan_begin_arg);
|
||||
err |= __put_user(temp.uint, &cmd->scan_begin_arg);
|
||||
err |= __get_user(temp.uint, &cmd32->convert_src);
|
||||
err |= __put_user(temp.uint, &cmd->convert_src);
|
||||
err |= __get_user(temp.uint, &cmd32->convert_arg);
|
||||
err |= __put_user(temp.uint, &cmd->convert_arg);
|
||||
err |= __get_user(temp.uint, &cmd32->scan_end_src);
|
||||
err |= __put_user(temp.uint, &cmd->scan_end_src);
|
||||
err |= __get_user(temp.uint, &cmd32->scan_end_arg);
|
||||
err |= __put_user(temp.uint, &cmd->scan_end_arg);
|
||||
err |= __get_user(temp.uint, &cmd32->stop_src);
|
||||
err |= __put_user(temp.uint, &cmd->stop_src);
|
||||
err |= __get_user(temp.uint, &cmd32->stop_arg);
|
||||
err |= __put_user(temp.uint, &cmd->stop_arg);
|
||||
err |= __get_user(temp.uptr, &cmd32->chanlist);
|
||||
err |= __put_user((unsigned int __force *)compat_ptr(temp.uptr),
|
||||
&cmd->chanlist);
|
||||
err |= __get_user(temp.uint, &cmd32->chanlist_len);
|
||||
err |= __put_user(temp.uint, &cmd->chanlist_len);
|
||||
err |= __get_user(temp.uptr, &cmd32->data);
|
||||
err |= __put_user(compat_ptr(temp.uptr), &cmd->data);
|
||||
err |= __get_user(temp.uint, &cmd32->data_len);
|
||||
err |= __put_user(temp.uint, &cmd->data_len);
|
||||
return err ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/* Copy native cmd structure to 32-bit cmd structure. */
|
||||
static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
|
||||
struct comedi_cmd __user *cmd)
|
||||
{
|
||||
int err;
|
||||
unsigned int temp;
|
||||
|
||||
/*
|
||||
* Copy back most of cmd structure.
|
||||
*
|
||||
* Assume the pointer values are already valid.
|
||||
* (Could use ptr_to_compat() to set them.)
|
||||
*/
|
||||
if (!access_ok(cmd, sizeof(*cmd)) ||
|
||||
!access_ok(cmd32, sizeof(*cmd32)))
|
||||
return -EFAULT;
|
||||
|
||||
err = 0;
|
||||
err |= __get_user(temp, &cmd->subdev);
|
||||
err |= __put_user(temp, &cmd32->subdev);
|
||||
err |= __get_user(temp, &cmd->flags);
|
||||
err |= __put_user(temp, &cmd32->flags);
|
||||
err |= __get_user(temp, &cmd->start_src);
|
||||
err |= __put_user(temp, &cmd32->start_src);
|
||||
err |= __get_user(temp, &cmd->start_arg);
|
||||
err |= __put_user(temp, &cmd32->start_arg);
|
||||
err |= __get_user(temp, &cmd->scan_begin_src);
|
||||
err |= __put_user(temp, &cmd32->scan_begin_src);
|
||||
err |= __get_user(temp, &cmd->scan_begin_arg);
|
||||
err |= __put_user(temp, &cmd32->scan_begin_arg);
|
||||
err |= __get_user(temp, &cmd->convert_src);
|
||||
err |= __put_user(temp, &cmd32->convert_src);
|
||||
err |= __get_user(temp, &cmd->convert_arg);
|
||||
err |= __put_user(temp, &cmd32->convert_arg);
|
||||
err |= __get_user(temp, &cmd->scan_end_src);
|
||||
err |= __put_user(temp, &cmd32->scan_end_src);
|
||||
err |= __get_user(temp, &cmd->scan_end_arg);
|
||||
err |= __put_user(temp, &cmd32->scan_end_arg);
|
||||
err |= __get_user(temp, &cmd->stop_src);
|
||||
err |= __put_user(temp, &cmd32->stop_src);
|
||||
err |= __get_user(temp, &cmd->stop_arg);
|
||||
err |= __put_user(temp, &cmd32->stop_arg);
|
||||
/* Assume chanlist pointer is unchanged. */
|
||||
err |= __get_user(temp, &cmd->chanlist_len);
|
||||
err |= __put_user(temp, &cmd32->chanlist_len);
|
||||
/* Assume data pointer is unchanged. */
|
||||
err |= __get_user(temp, &cmd->data_len);
|
||||
err |= __put_user(temp, &cmd32->data_len);
|
||||
return err ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/* Handle 32-bit COMEDI_CMD ioctl. */
|
||||
static int compat_cmd(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct comedi_cmd __user *cmd;
|
||||
struct comedi32_cmd_struct __user *cmd32;
|
||||
int rc, err;
|
||||
|
||||
cmd32 = compat_ptr(arg);
|
||||
cmd = compat_alloc_user_space(sizeof(*cmd));
|
||||
|
||||
rc = get_compat_cmd(cmd, cmd32);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd);
|
||||
if (rc == -EAGAIN) {
|
||||
/* Special case: copy cmd back to user. */
|
||||
err = put_compat_cmd(cmd32, 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_cmd __user *cmd;
|
||||
struct comedi32_cmd_struct __user *cmd32;
|
||||
int rc, err;
|
||||
|
||||
cmd32 = compat_ptr(arg);
|
||||
cmd = compat_alloc_user_space(sizeof(*cmd));
|
||||
|
||||
rc = get_compat_cmd(cmd, cmd32);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
err = put_compat_cmd(cmd32, cmd);
|
||||
if (err)
|
||||
rc = err;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Copy 32-bit insn structure to native insn structure. */
|
||||
static int get_compat_insn(struct comedi_insn __user *insn,
|
||||
struct comedi32_insn_struct __user *insn32)
|
||||
{
|
||||
int err;
|
||||
union {
|
||||
unsigned int uint;
|
||||
compat_uptr_t uptr;
|
||||
} temp;
|
||||
|
||||
/* Copy insn structure. Ignore the unused members. */
|
||||
err = 0;
|
||||
if (!access_ok(insn32, sizeof(*insn32)) ||
|
||||
!access_ok(insn, sizeof(*insn)))
|
||||
return -EFAULT;
|
||||
|
||||
err |= __get_user(temp.uint, &insn32->insn);
|
||||
err |= __put_user(temp.uint, &insn->insn);
|
||||
err |= __get_user(temp.uint, &insn32->n);
|
||||
err |= __put_user(temp.uint, &insn->n);
|
||||
err |= __get_user(temp.uptr, &insn32->data);
|
||||
err |= __put_user(compat_ptr(temp.uptr), &insn->data);
|
||||
err |= __get_user(temp.uint, &insn32->subdev);
|
||||
err |= __put_user(temp.uint, &insn->subdev);
|
||||
err |= __get_user(temp.uint, &insn32->chanspec);
|
||||
err |= __put_user(temp.uint, &insn->chanspec);
|
||||
return err ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/* Handle 32-bit COMEDI_INSNLIST ioctl. */
|
||||
static int compat_insnlist(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct combined_insnlist {
|
||||
struct comedi_insnlist insnlist;
|
||||
struct comedi_insn insn[1];
|
||||
} __user *s;
|
||||
struct comedi32_insnlist_struct __user *insnlist32;
|
||||
struct comedi32_insn_struct __user *insn32;
|
||||
compat_uptr_t uptr;
|
||||
unsigned int n_insns, n;
|
||||
int err, rc;
|
||||
|
||||
insnlist32 = compat_ptr(arg);
|
||||
|
||||
/* Get 32-bit insnlist structure. */
|
||||
if (!access_ok(insnlist32, sizeof(*insnlist32)))
|
||||
return -EFAULT;
|
||||
|
||||
err = 0;
|
||||
err |= __get_user(n_insns, &insnlist32->n_insns);
|
||||
err |= __get_user(uptr, &insnlist32->insns);
|
||||
insn32 = compat_ptr(uptr);
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
/* Allocate user memory to copy insnlist and insns into. */
|
||||
s = compat_alloc_user_space(offsetof(struct combined_insnlist,
|
||||
insn[n_insns]));
|
||||
|
||||
/* Set native insnlist structure. */
|
||||
if (!access_ok(&s->insnlist, sizeof(s->insnlist)))
|
||||
return -EFAULT;
|
||||
|
||||
err |= __put_user(n_insns, &s->insnlist.n_insns);
|
||||
err |= __put_user(&s->insn[0], &s->insnlist.insns);
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
/* Copy insn structures. */
|
||||
for (n = 0; n < n_insns; n++) {
|
||||
rc = get_compat_insn(&s->insn[n], &insn32[n]);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return translated_ioctl(file, COMEDI_INSNLIST,
|
||||
(unsigned long)&s->insnlist);
|
||||
}
|
||||
|
||||
/* Handle 32-bit COMEDI_INSN ioctl. */
|
||||
static int compat_insn(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct comedi_insn __user *insn;
|
||||
struct comedi32_insn_struct __user *insn32;
|
||||
int rc;
|
||||
|
||||
insn32 = compat_ptr(arg);
|
||||
insn = compat_alloc_user_space(sizeof(*insn));
|
||||
|
||||
rc = get_compat_insn(insn, insn32);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn);
|
||||
}
|
||||
|
||||
/*
|
||||
* compat_ioctl file operation.
|
||||
*
|
||||
* Returns -ENOIOCTLCMD for unrecognised ioctl codes.
|
||||
*/
|
||||
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 = translated_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 = translated_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;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* comedi/comedi_compat32.h
|
||||
* 32-bit ioctl compatibility for 64-bit comedi kernel module.
|
||||
*
|
||||
* Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
|
||||
* Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
|
||||
*
|
||||
* COMEDI - Linux Control and Measurement Device Interface
|
||||
* Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
|
||||
*/
|
||||
|
||||
#ifndef _COMEDI_COMPAT32_H
|
||||
#define _COMEDI_COMPAT32_H
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
struct file;
|
||||
long comedi_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
|
||||
#else /* CONFIG_COMPAT */
|
||||
|
||||
#define comedi_compat_ioctl NULL
|
||||
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
#endif /* _COMEDI_COMPAT32_H */
|
|
@ -4,13 +4,14 @@
|
|||
* comedi kernel module
|
||||
*
|
||||
* COMEDI - Linux Control and Measurement Device Interface
|
||||
* Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
|
||||
* Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
|
||||
* compat ioctls:
|
||||
* Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
|
||||
* 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 @@
|
|||
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include "comedi_internal.h"
|
||||
|
||||
|
@ -1047,31 +1049,28 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
|
|||
* 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)
|
||||
|
@ -1079,9 +1078,9 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1521,34 +1520,19 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
|
|||
#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");
|
||||
|
@ -1566,7 +1550,7 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
|
|||
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))) {
|
||||
|
@ -1593,7 +1577,6 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
|
|||
}
|
||||
|
||||
error:
|
||||
kfree(insns);
|
||||
kfree(data);
|
||||
|
||||
if (ret < 0)
|
||||
|
@ -1616,22 +1599,19 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
|
|||
* 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;
|
||||
}
|
||||
|
||||
|
@ -1641,26 +1621,26 @@ static int do_insn_ioctl(struct comedi_device *dev,
|
|||
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);
|
||||
|
@ -1669,17 +1649,11 @@ static int do_insn_ioctl(struct comedi_device *dev,
|
|||
}
|
||||
|
||||
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 @@ static int __comedi_get_user_chanlist(struct comedi_device *dev,
|
|||
* 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;
|
||||
|
@ -1777,15 +1750,15 @@ static int do_cmd_ioctl(struct comedi_device *dev,
|
|||
|
||||
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) */
|
||||
|
@ -1801,13 +1774,13 @@ static int do_cmd_ioctl(struct comedi_device *dev,
|
|||
}
|
||||
|
||||
/* 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 */
|
||||
|
@ -1819,15 +1792,11 @@ static int do_cmd_ioctl(struct comedi_device *dev,
|
|||
|
||||
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 @@ static int do_cmd_ioctl(struct comedi_device *dev,
|
|||
* 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 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
|
|||
(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,
|
||||
|
@ -2223,22 +2197,64 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
|
|||
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, ©, 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, ©, 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;
|
||||
|
@ -2808,6 +2824,344 @@ static int comedi_close(struct inode *inode, struct file *file)
|
|||
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, ©, 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, ©, 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,
|
||||
|
|
|
@ -18,7 +18,7 @@ struct comedi_subdevice;
|
|||
struct device;
|
||||
|
||||
int do_rangeinfo_ioctl(struct comedi_device *dev,
|
||||
struct comedi_rangeinfo __user *arg);
|
||||
struct comedi_rangeinfo *it);
|
||||
struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device);
|
||||
void comedi_release_hardware_device(struct device *hardware_device);
|
||||
int comedi_alloc_subdevice_minor(struct comedi_subdevice *s);
|
||||
|
|
|
@ -46,17 +46,14 @@ EXPORT_SYMBOL_GPL(range_unknown);
|
|||
* array of comedi_krange structures to rangeinfo->range_ptr pointer
|
||||
*/
|
||||
int do_rangeinfo_ioctl(struct comedi_device *dev,
|
||||
struct comedi_rangeinfo __user *arg)
|
||||
struct comedi_rangeinfo *it)
|
||||
{
|
||||
struct comedi_rangeinfo it;
|
||||
int subd, chan;
|
||||
const struct comedi_lrange *lr;
|
||||
struct comedi_subdevice *s;
|
||||
|
||||
if (copy_from_user(&it, arg, sizeof(struct comedi_rangeinfo)))
|
||||
return -EFAULT;
|
||||
subd = (it.range_type >> 24) & 0xf;
|
||||
chan = (it.range_type >> 16) & 0xff;
|
||||
subd = (it->range_type >> 24) & 0xf;
|
||||
chan = (it->range_type >> 16) & 0xff;
|
||||
|
||||
if (!dev->attached)
|
||||
return -EINVAL;
|
||||
|
@ -73,15 +70,15 @@ int do_rangeinfo_ioctl(struct comedi_device *dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (RANGE_LENGTH(it.range_type) != lr->length) {
|
||||
if (RANGE_LENGTH(it->range_type) != lr->length) {
|
||||
dev_dbg(dev->class_dev,
|
||||
"wrong length %d should be %d (0x%08x)\n",
|
||||
RANGE_LENGTH(it.range_type),
|
||||
lr->length, it.range_type);
|
||||
RANGE_LENGTH(it->range_type),
|
||||
lr->length, it->range_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (copy_to_user(it.range_ptr, lr->range,
|
||||
if (copy_to_user(it->range_ptr, lr->range,
|
||||
sizeof(struct comedi_krange) * lr->length))
|
||||
return -EFAULT;
|
||||
|
||||
|
|
Loading…
Reference in a new issue