Doing all the compat_ioctl handling in the i2c driver itself removes special cases from fs/compat_ioctl.c and makes it possible to optimize this case better. Signed-off-by: Arnd Bergmann <arnd@xxxxxxxx> Cc: "Jean Delvare (PC drivers, core)" <khali@xxxxxxxxxxxx> Cc: "Ben Dooks (embedded platforms)" <ben-linux@xxxxxxxxx> Cc: Wolfram Sang <w.sang@xxxxxxxxxxxxxx> Cc: linux-i2c@xxxxxxxxxxxxxxx --- drivers/i2c/i2c-dev.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/compat_ioctl.c | 119 ------------------------------------------------- 2 files changed, 117 insertions(+), 119 deletions(-) diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 7e13d2d..fde0c9e 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -35,6 +35,7 @@ #include <linux/i2c.h> #include <linux/i2c-dev.h> #include <linux/smp_lock.h> +#include <linux/compat.h> #include <linux/jiffies.h> #include <asm/uaccess.h> @@ -439,6 +440,119 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; } +#ifdef CONFIG_COMPAT +struct i2c_msg32 { + u16 addr; + u16 flags; + u16 len; + compat_caddr_t buf; +}; + +struct i2c_rdwr_ioctl_data32 { + compat_caddr_t msgs; /* struct i2c_msg __user *msgs */ + u32 nmsgs; +}; + +struct i2c_smbus_ioctl_data32 { + u8 read_write; + u8 command; + u32 size; + compat_caddr_t data; /* union i2c_smbus_data *data */ +}; + +struct i2c_rdwr_aligned { + struct i2c_rdwr_ioctl_data cmd; + struct i2c_msg msgs[0]; +}; + +static int compat_i2c_rdwr_ioctl(struct file *filp, unsigned int cmd, + struct i2c_rdwr_ioctl_data32 __user *udata) +{ + struct i2c_rdwr_aligned __user *tdata; + struct i2c_msg __user *tmsgs; + struct i2c_msg32 __user *umsgs; + compat_caddr_t datap; + int nmsgs, i; + + if (get_user(nmsgs, &udata->nmsgs)) + return -EFAULT; + if (nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) + return -EINVAL; + + if (get_user(datap, &udata->msgs)) + return -EFAULT; + umsgs = compat_ptr(datap); + + tdata = compat_alloc_user_space(sizeof(*tdata) + + nmsgs * sizeof(struct i2c_msg)); + tmsgs = &tdata->msgs[0]; + + if (put_user(nmsgs, &tdata->cmd.nmsgs) || + put_user(tmsgs, &tdata->cmd.msgs)) + return -EFAULT; + + for (i = 0; i < nmsgs; i++) { + if (copy_in_user(&tmsgs[i].addr, &umsgs[i].addr, 3*sizeof(u16))) + return -EFAULT; + if (get_user(datap, &umsgs[i].buf) || + put_user(compat_ptr(datap), &tmsgs[i].buf)) + return -EFAULT; + } + return i2cdev_ioctl(filp, cmd, (unsigned long)tdata); +} + +static int compat_i2c_smbus_ioctl(struct file *filp, unsigned int cmd, + struct i2c_smbus_ioctl_data32 __user *udata) +{ + struct i2c_smbus_ioctl_data __user *tdata; + compat_caddr_t datap; + + tdata = compat_alloc_user_space(sizeof(*tdata)); + if (tdata == NULL) + return -ENOMEM; + if (!access_ok(VERIFY_WRITE, tdata, sizeof(*tdata))) + return -EFAULT; + + if (!access_ok(VERIFY_READ, udata, sizeof(*udata))) + return -EFAULT; + + if (__copy_in_user(&tdata->read_write, &udata->read_write, 2 * sizeof(u8))) + return -EFAULT; + if (__copy_in_user(&tdata->size, &udata->size, 2 * sizeof(u32))) + return -EFAULT; + if (__get_user(datap, &udata->data) || + __put_user(compat_ptr(datap), &tdata->data)) + return -EFAULT; + + return i2cdev_ioctl(filp, cmd, (unsigned long)tdata); +} + +static int compat_i2c_funcs(struct file *filp, unsigned int cmd, + compat_ulong_t __user *argp) +{ + struct i2c_client *client = filp->private_data; + compat_ulong_t funcs; + funcs = i2c_get_functionality(client->adapter); + return put_user(funcs, argp); +} + +static long i2cdev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = compat_ptr(arg); + + switch (cmd) { + case I2C_FUNCS: + return compat_i2c_funcs(filp, cmd, argp); + case I2C_RDWR: + return compat_i2c_rdwr_ioctl(filp, cmd, argp); + case I2C_SMBUS: + return compat_i2c_smbus_ioctl(filp, cmd, argp); + } + + return i2cdev_ioctl(filp, cmd, arg); +} +#endif /* CONFIG_COMPAT */ + static int i2cdev_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); @@ -501,6 +615,9 @@ static const struct file_operations i2cdev_fops = { .read = i2cdev_read, .write = i2cdev_write, .unlocked_ioctl = i2cdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = i2cdev_compat_ioctl, +#endif .open = i2cdev_open, .release = i2cdev_release, }; diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index a762fb1..b419459 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -117,21 +117,6 @@ #include <asm/fbio.h> #endif -static int w_long(unsigned int fd, unsigned int cmd, - compat_ulong_t __user *argp) -{ - mm_segment_t old_fs = get_fs(); - int err; - unsigned long val; - - set_fs (KERNEL_DS); - err = sys_ioctl(fd, cmd, (unsigned long)&val); - set_fs (old_fs); - if (!err && put_user(val, argp)) - return -EFAULT; - return err; -} - struct compat_video_event { int32_t type; compat_time_t timestamp; @@ -691,96 +676,6 @@ static int do_usbdevfs_discsignal(unsigned int fd, unsigned int cmd, return err; } -/* - * I2C layer ioctls - */ - -struct i2c_msg32 { - u16 addr; - u16 flags; - u16 len; - compat_caddr_t buf; -}; - -struct i2c_rdwr_ioctl_data32 { - compat_caddr_t msgs; /* struct i2c_msg __user *msgs */ - u32 nmsgs; -}; - -struct i2c_smbus_ioctl_data32 { - u8 read_write; - u8 command; - u32 size; - compat_caddr_t data; /* union i2c_smbus_data *data */ -}; - -struct i2c_rdwr_aligned { - struct i2c_rdwr_ioctl_data cmd; - struct i2c_msg msgs[0]; -}; - -static int do_i2c_rdwr_ioctl(unsigned int fd, unsigned int cmd, - struct i2c_rdwr_ioctl_data32 __user *udata) -{ - struct i2c_rdwr_aligned __user *tdata; - struct i2c_msg __user *tmsgs; - struct i2c_msg32 __user *umsgs; - compat_caddr_t datap; - int nmsgs, i; - - if (get_user(nmsgs, &udata->nmsgs)) - return -EFAULT; - if (nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) - return -EINVAL; - - if (get_user(datap, &udata->msgs)) - return -EFAULT; - umsgs = compat_ptr(datap); - - tdata = compat_alloc_user_space(sizeof(*tdata) + - nmsgs * sizeof(struct i2c_msg)); - tmsgs = &tdata->msgs[0]; - - if (put_user(nmsgs, &tdata->cmd.nmsgs) || - put_user(tmsgs, &tdata->cmd.msgs)) - return -EFAULT; - - for (i = 0; i < nmsgs; i++) { - if (copy_in_user(&tmsgs[i].addr, &umsgs[i].addr, 3*sizeof(u16))) - return -EFAULT; - if (get_user(datap, &umsgs[i].buf) || - put_user(compat_ptr(datap), &tmsgs[i].buf)) - return -EFAULT; - } - return sys_ioctl(fd, cmd, (unsigned long)tdata); -} - -static int do_i2c_smbus_ioctl(unsigned int fd, unsigned int cmd, - struct i2c_smbus_ioctl_data32 __user *udata) -{ - struct i2c_smbus_ioctl_data __user *tdata; - compat_caddr_t datap; - - tdata = compat_alloc_user_space(sizeof(*tdata)); - if (tdata == NULL) - return -ENOMEM; - if (!access_ok(VERIFY_WRITE, tdata, sizeof(*tdata))) - return -EFAULT; - - if (!access_ok(VERIFY_READ, udata, sizeof(*udata))) - return -EFAULT; - - if (__copy_in_user(&tdata->read_write, &udata->read_write, 2 * sizeof(u8))) - return -EFAULT; - if (__copy_in_user(&tdata->size, &udata->size, 2 * sizeof(u32))) - return -EFAULT; - if (__get_user(datap, &udata->data) || - __put_user(compat_ptr(datap), &tdata->data)) - return -EFAULT; - - return sys_ioctl(fd, cmd, (unsigned long)tdata); -} - #define RTC_IRQP_READ32 _IOR('p', 0x0b, compat_ulong_t) #define RTC_IRQP_SET32 _IOW('p', 0x0c, compat_ulong_t) #define RTC_EPOCH_READ32 _IOR('p', 0x0d, compat_ulong_t) @@ -1341,13 +1236,6 @@ COMPATIBLE_IOCTL(USBDEVFS_SUBMITURB32) COMPATIBLE_IOCTL(USBDEVFS_REAPURB32) COMPATIBLE_IOCTL(USBDEVFS_REAPURBNDELAY32) COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT) -/* i2c */ -COMPATIBLE_IOCTL(I2C_SLAVE) -COMPATIBLE_IOCTL(I2C_SLAVE_FORCE) -COMPATIBLE_IOCTL(I2C_TENBIT) -COMPATIBLE_IOCTL(I2C_PEC) -COMPATIBLE_IOCTL(I2C_RETRIES) -COMPATIBLE_IOCTL(I2C_TIMEOUT) /* hiddev */ COMPATIBLE_IOCTL(HIDIOCGVERSION) COMPATIBLE_IOCTL(HIDIOCAPPLICATION) @@ -1533,13 +1421,6 @@ static long do_ioctl_trans(int fd, unsigned int cmd, return do_usbdevfs_bulk(fd, cmd, argp); case USBDEVFS_DISCSIGNAL32: return do_usbdevfs_discsignal(fd, cmd, argp); - /* i2c */ - case I2C_FUNCS: - return w_long(fd, cmd, argp); - case I2C_RDWR: - return do_i2c_rdwr_ioctl(fd, cmd, argp); - case I2C_SMBUS: - return do_i2c_smbus_ioctl(fd, cmd, argp); /* Not implemented in the native kernel */ case RTC_IRQP_READ32: case RTC_IRQP_SET32: -- 1.6.3.3 -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html