Commit dba7997a authored by Jean Delvare's avatar Jean Delvare Committed by Jean Delvare

i2c-dev: Split i2cdev_ioctl

Split the handling of the I2C_RDWR and I2C_SMBUS ioctls to their own
functions. This limits the stack usage, saves one level of indentation
and makes the code more readable.
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent 838349b5
...@@ -200,59 +200,14 @@ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) ...@@ -200,59 +200,14 @@ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)
return device_for_each_child(&adapter->dev, &addr, i2cdev_check); return device_for_each_child(&adapter->dev, &addr, i2cdev_check);
} }
static int i2cdev_ioctl(struct inode *inode, struct file *file, static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
unsigned int cmd, unsigned long arg) unsigned long arg)
{ {
struct i2c_client *client = (struct i2c_client *)file->private_data;
struct i2c_rdwr_ioctl_data rdwr_arg; struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp;
struct i2c_msg *rdwr_pa; struct i2c_msg *rdwr_pa;
u8 __user **data_ptrs; u8 __user **data_ptrs;
int i,datasize,res; int i, res;
unsigned long funcs;
dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);
switch ( cmd ) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
/* NOTE: devices set up to work with "new style" drivers
* can't use I2C_SLAVE, even when the device node is not
* bound to a driver. Only I2C_SLAVE_FORCE will work.
*
* Setting the PEC flag here won't affect kernel drivers,
* which will be using the i2c_client node registered with
* the driver model core. Likewise, when that client has
* the PEC flag already set, the i2c-dev driver won't see
* (or use) this setting.
*/
if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
client->addr = arg;
return 0;
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_PEC:
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg);
case I2C_RDWR:
if (copy_from_user(&rdwr_arg, if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg, (struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg))) sizeof(rdwr_arg)))
...@@ -266,8 +221,8 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file, ...@@ -266,8 +221,8 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
rdwr_pa = (struct i2c_msg *) rdwr_pa = (struct i2c_msg *)
kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
GFP_KERNEL); GFP_KERNEL);
if (!rdwr_pa)
if (rdwr_pa == NULL) return -ENOMEM; return -ENOMEM;
if (copy_from_user(rdwr_pa, rdwr_arg.msgs, if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))) { rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
...@@ -282,7 +237,7 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file, ...@@ -282,7 +237,7 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
} }
res = 0; res = 0;
for( i=0; i<rdwr_arg.nmsgs; i++ ) { for (i = 0; i < rdwr_arg.nmsgs; i++) {
/* Limit the size of the message to a sane amount; /* Limit the size of the message to a sane amount;
* and don't let length change either. */ * and don't let length change either. */
if ((rdwr_pa[i].len > 8192) || if ((rdwr_pa[i].len > 8192) ||
...@@ -292,12 +247,11 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file, ...@@ -292,12 +247,11 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
} }
data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL); rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
if(rdwr_pa[i].buf == NULL) { if (rdwr_pa[i].buf == NULL) {
res = -ENOMEM; res = -ENOMEM;
break; break;
} }
if(copy_from_user(rdwr_pa[i].buf, if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],
data_ptrs[i],
rdwr_pa[i].len)) { rdwr_pa[i].len)) {
++i; /* Needs to be kfreed too */ ++i; /* Needs to be kfreed too */
res = -EFAULT; res = -EFAULT;
...@@ -313,25 +267,27 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file, ...@@ -313,25 +267,27 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
return res; return res;
} }
res = i2c_transfer(client->adapter, res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
rdwr_pa, while (i-- > 0) {
rdwr_arg.nmsgs); if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
while(i-- > 0) { if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) { rdwr_pa[i].len))
if(copy_to_user(
data_ptrs[i],
rdwr_pa[i].buf,
rdwr_pa[i].len)) {
res = -EFAULT; res = -EFAULT;
} }
}
kfree(rdwr_pa[i].buf); kfree(rdwr_pa[i].buf);
} }
kfree(data_ptrs); kfree(data_ptrs);
kfree(rdwr_pa); kfree(rdwr_pa);
return res; return res;
}
static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
unsigned long arg)
{
struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp;
int datasize, res;
case I2C_SMBUS:
if (copy_from_user(&data_arg, if (copy_from_user(&data_arg,
(struct i2c_smbus_ioctl_data __user *) arg, (struct i2c_smbus_ioctl_data __user *) arg,
sizeof(struct i2c_smbus_ioctl_data))) sizeof(struct i2c_smbus_ioctl_data)))
...@@ -367,10 +323,8 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file, ...@@ -367,10 +323,8 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
(data_arg.read_write == I2C_SMBUS_WRITE))) (data_arg.read_write == I2C_SMBUS_WRITE)))
/* These are special: we do not use data */ /* These are special: we do not use data */
return i2c_smbus_xfer(client->adapter, client->addr, return i2c_smbus_xfer(client->adapter, client->addr,
client->flags, client->flags, data_arg.read_write,
data_arg.read_write, data_arg.command, data_arg.size, NULL);
data_arg.command,
data_arg.size, NULL);
if (data_arg.data == NULL) { if (data_arg.data == NULL) {
dev_dbg(&client->adapter->dev, dev_dbg(&client->adapter->dev,
...@@ -401,16 +355,69 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file, ...@@ -401,16 +355,69 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
if (data_arg.read_write == I2C_SMBUS_READ) if (data_arg.read_write == I2C_SMBUS_READ)
temp.block[0] = I2C_SMBUS_BLOCK_MAX; temp.block[0] = I2C_SMBUS_BLOCK_MAX;
} }
res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, res = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
data_arg.read_write, data_arg.read_write, data_arg.command, data_arg.size, &temp);
data_arg.command,data_arg.size,&temp); if (!res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_READ))) { (data_arg.read_write == I2C_SMBUS_READ))) {
if (copy_to_user(data_arg.data, &temp, datasize)) if (copy_to_user(data_arg.data, &temp, datasize))
return -EFAULT; return -EFAULT;
} }
return res; return res;
}
static int i2cdev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
unsigned long funcs;
dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);
switch ( cmd ) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
/* NOTE: devices set up to work with "new style" drivers
* can't use I2C_SLAVE, even when the device node is not
* bound to a driver. Only I2C_SLAVE_FORCE will work.
*
* Setting the PEC flag here won't affect kernel drivers,
* which will be using the i2c_client node registered with
* the driver model core. Likewise, when that client has
* the PEC flag already set, the i2c-dev driver won't see
* (or use) this setting.
*/
if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
client->addr = arg;
return 0;
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_PEC:
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg);
case I2C_RDWR:
return i2cdev_ioctl_rdrw(client, arg);
case I2C_SMBUS:
return i2cdev_ioctl_smbus(client, arg);
case I2C_RETRIES: case I2C_RETRIES:
client->adapter->retries = arg; client->adapter->retries = arg;
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment