From: Benjamin Herrenschmidt I needed those for the G5 on ppc64, so here they are, I was only able to test the SMBUS stuff though. drivers/i2c/i2c-dev.c | 2 fs/compat_ioctl.c | 113 +++++++++++++++++++++++++++++++++++++++++-- include/linux/compat_ioctl.h | 7 ++ include/linux/i2c-dev.h | 2 4 files changed, 120 insertions(+), 4 deletions(-) diff -puN drivers/i2c/i2c-dev.c~compat-ioctl-for-i2c drivers/i2c/i2c-dev.c --- 25/drivers/i2c/i2c-dev.c~compat-ioctl-for-i2c 2003-11-13 21:29:55.000000000 -0800 +++ 25-akpm/drivers/i2c/i2c-dev.c 2003-11-13 21:29:55.000000000 -0800 @@ -223,7 +223,7 @@ int i2cdev_ioctl (struct inode *inode, s /* Put an arbritrary limit on the number of messages that can * be sent at once */ - if (rdwr_arg.nmsgs > 42) + if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) return -EINVAL; rdwr_pa = (struct i2c_msg *) diff -puN fs/compat_ioctl.c~compat-ioctl-for-i2c fs/compat_ioctl.c --- 25/fs/compat_ioctl.c~compat-ioctl-for-i2c 2003-11-13 21:29:55.000000000 -0800 +++ 25-akpm/fs/compat_ioctl.c 2003-11-13 21:29:55.000000000 -0800 @@ -63,6 +63,8 @@ #include #include #include +#include +#include #include /* siocdevprivate_ioctl */ #include @@ -128,7 +130,7 @@ static int w_long(unsigned int fd, unsig set_fs (KERNEL_DS); err = sys_ioctl(fd, cmd, (unsigned long)&val); set_fs (old_fs); - if (!err && put_user(val, (u32 *)arg)) + if (!err && put_user(val, (u32 *)compat_ptr(arg))) return -EFAULT; return err; } @@ -136,15 +138,16 @@ static int w_long(unsigned int fd, unsig static int rw_long(unsigned int fd, unsigned int cmd, unsigned long arg) { mm_segment_t old_fs = get_fs(); + u32 *argptr = compat_ptr(arg); int err; unsigned long val; - if(get_user(val, (u32 *)arg)) + if(get_user(val, argptr)) return -EFAULT; set_fs (KERNEL_DS); err = sys_ioctl(fd, cmd, (unsigned long)&val); set_fs (old_fs); - if (!err && put_user(val, (u32 *)arg)) + if (!err && put_user(val, argptr)) return -EFAULT; return err; } @@ -2869,6 +2872,105 @@ static int do_usbdevfs_discsignal(unsign 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 */ +}; + +static int do_i2c_rdwr_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct i2c_rdwr_ioctl_data *tdata; + struct i2c_rdwr_ioctl_data32 *udata; + struct i2c_msg *tmsgs; + struct i2c_msg32 *umsgs; + compat_caddr_t datap; + int nmsgs, i; + + tdata = compat_alloc_user_space(sizeof(*tdata)); + if (tdata == NULL) + return -ENOMEM; + if (verify_area(VERIFY_WRITE, tdata, sizeof(*tdata))) + return -EFAULT; + + udata = (struct i2c_rdwr_ioctl_data32 *)compat_ptr(arg); + if (verify_area(VERIFY_READ, udata, sizeof(*udata))) + return -EFAULT; + if (__get_user(nmsgs, &udata->nmsgs) || __put_user(nmsgs, &tdata->nmsgs)) + return -EFAULT; + if (nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) + return -EINVAL; + if (__get_user(datap, &udata->msgs)) + return -EFAULT; + umsgs = (struct i2c_msg32 *)compat_ptr(datap); + if (verify_area(VERIFY_READ, umsgs, sizeof(struct i2c_msg) * nmsgs)) + return -EFAULT; + + tmsgs = compat_alloc_user_space(sizeof(struct i2c_msg) * nmsgs); + if (tmsgs == NULL) + return -ENOMEM; + if (verify_area(VERIFY_WRITE, tmsgs, sizeof(struct i2c_msg) * nmsgs)) + return -EFAULT; + if (__put_user(tmsgs, &tdata->msgs)) + return -ENOMEM; + 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, unsigned long arg) +{ + struct i2c_smbus_ioctl_data *tdata; + struct i2c_smbus_ioctl_data32 *udata; + compat_caddr_t datap; + + tdata = compat_alloc_user_space(sizeof(*tdata)); + if (tdata == NULL) + return -ENOMEM; + if (verify_area(VERIFY_WRITE, tdata, sizeof(*tdata))) + return -EFAULT; + + udata = (struct i2c_smbus_ioctl_data32 *)compat_ptr(arg); + if (verify_area(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); +} + + #undef CODE #endif @@ -3027,5 +3129,10 @@ HANDLE_IOCTL(USBDEVFS_BULK32, do_usbdevf HANDLE_IOCTL(USBDEVFS_REAPURB32, do_usbdevfs_reapurb) HANDLE_IOCTL(USBDEVFS_REAPURBNDELAY32, do_usbdevfs_reapurb) HANDLE_IOCTL(USBDEVFS_DISCSIGNAL32, do_usbdevfs_discsignal) +/* i2c */ +HANDLE_IOCTL(I2C_FUNCS, w_long) +HANDLE_IOCTL(I2C_RDWR, do_i2c_rdwr_ioctl) +HANDLE_IOCTL(I2C_SMBUS, do_i2c_smbus_ioctl) + #undef DECLARES #endif diff -puN include/linux/compat_ioctl.h~compat-ioctl-for-i2c include/linux/compat_ioctl.h --- 25/include/linux/compat_ioctl.h~compat-ioctl-for-i2c 2003-11-13 21:29:55.000000000 -0800 +++ 25-akpm/include/linux/compat_ioctl.h 2003-11-13 21:29:55.000000000 -0800 @@ -678,3 +678,10 @@ COMPATIBLE_IOCTL(NBD_CLEAR_QUE) COMPATIBLE_IOCTL(NBD_PRINT_DEBUG) COMPATIBLE_IOCTL(NBD_SET_SIZE_BLOCKS) COMPATIBLE_IOCTL(NBD_DISCONNECT) +/* 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) diff -puN include/linux/i2c-dev.h~compat-ioctl-for-i2c include/linux/i2c-dev.h --- 25/include/linux/i2c-dev.h~compat-ioctl-for-i2c 2003-11-13 21:29:55.000000000 -0800 +++ 25-akpm/include/linux/i2c-dev.h 2003-11-13 21:29:55.000000000 -0800 @@ -43,4 +43,6 @@ struct i2c_rdwr_ioctl_data { __u32 nmsgs; /* number of i2c_msgs */ }; +#define I2C_RDRW_IOCTL_MAX_MSGS 42 + #endif /* _LINUX_I2C_DEV_H */ _