diff options
Diffstat (limited to 'drivers/i2c/i2c-dev.c')
| -rw-r--r-- | drivers/i2c/i2c-dev.c | 129 | 
1 files changed, 83 insertions, 46 deletions
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index cec0f3ba97f..80b47e8ce03 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -17,7 +17,8 @@      You should have received a copy of the GNU General Public License      along with this program; if not, write to the Free Software -    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +    MA 02110-1301 USA.  */  /* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. @@ -28,6 +29,8 @@  #include <linux/kernel.h>  #include <linux/module.h> +#include <linux/device.h> +#include <linux/notifier.h>  #include <linux/fs.h>  #include <linux/slab.h>  #include <linux/init.h> @@ -37,16 +40,13 @@  #include <linux/jiffies.h>  #include <linux/uaccess.h> -static struct i2c_driver i2cdev_driver; -  /*   * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a   * slave (i2c_client) with which messages will be exchanged.  It's coupled   * with a character special file which is accessed by user mode drivers.   *   * The list of i2c_dev structures is parallel to the i2c_adapter lists - * maintained by the driver model, and is updated using notifications - * delivered to the i2cdev_driver. + * maintained by the driver model, and is updated using bus notifications.   */  struct i2c_dev {  	struct list_head list; @@ -102,8 +102,8 @@ static void return_i2c_dev(struct i2c_dev *i2c_dev)  	kfree(i2c_dev);  } -static ssize_t show_adapter_name(struct device *dev, -				 struct device_attribute *attr, char *buf) +static ssize_t name_show(struct device *dev, +			 struct device_attribute *attr, char *buf)  {  	struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt)); @@ -111,7 +111,13 @@ static ssize_t show_adapter_name(struct device *dev,  		return -ENODEV;  	return sprintf(buf, "%s\n", i2c_dev->adap->name);  } -static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL); +static DEVICE_ATTR_RO(name); + +static struct attribute *i2c_attrs[] = { +	&dev_attr_name.attr, +	NULL, +}; +ATTRIBUTE_GROUPS(i2c);  /* ------------------------------------------------------------------------- */ @@ -148,7 +154,7 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,  		return -ENOMEM;  	pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n", -		iminor(file->f_path.dentry->d_inode), count); +		iminor(file_inode(file)), count);  	ret = i2c_master_recv(client, tmp, count);  	if (ret >= 0) @@ -172,7 +178,7 @@ static ssize_t i2cdev_write(struct file *file, const char __user *buf,  		return PTR_ERR(tmp);  	pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n", -		iminor(file->f_path.dentry->d_inode), count); +		iminor(file_inode(file)), count);  	ret = i2c_master_send(client, tmp, count);  	kfree(tmp); @@ -252,15 +258,10 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,  	if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)  		return -EINVAL; -	rdwr_pa = kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL); -	if (!rdwr_pa) -		return -ENOMEM; - -	if (copy_from_user(rdwr_pa, rdwr_arg.msgs, -			   rdwr_arg.nmsgs * sizeof(struct i2c_msg))) { -		kfree(rdwr_pa); -		return -EFAULT; -	} +	rdwr_pa = memdup_user(rdwr_arg.msgs, +			      rdwr_arg.nmsgs * sizeof(struct i2c_msg)); +	if (IS_ERR(rdwr_pa)) +		return PTR_ERR(rdwr_pa);  	data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);  	if (data_ptrs == NULL) { @@ -270,19 +271,41 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,  	res = 0;  	for (i = 0; i < rdwr_arg.nmsgs; i++) { -		/* Limit the size of the message to a sane amount; -		 * and don't let length change either. */ -		if ((rdwr_pa[i].len > 8192) || -		    (rdwr_pa[i].flags & I2C_M_RECV_LEN)) { +		/* Limit the size of the message to a sane amount */ +		if (rdwr_pa[i].len > 8192) {  			res = -EINVAL;  			break;  		} +  		data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;  		rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);  		if (IS_ERR(rdwr_pa[i].buf)) {  			res = PTR_ERR(rdwr_pa[i].buf);  			break;  		} + +		/* +		 * If the message length is received from the slave (similar +		 * to SMBus block read), we must ensure that the buffer will +		 * be large enough to cope with a message length of +		 * I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus +		 * drivers allow. The first byte in the buffer must be +		 * pre-filled with the number of extra bytes, which must be +		 * at least one to hold the message length, but can be +		 * greater (for example to account for a checksum byte at +		 * the end of the message.) +		 */ +		if (rdwr_pa[i].flags & I2C_M_RECV_LEN) { +			if (!(rdwr_pa[i].flags & I2C_M_RD) || +			    rdwr_pa[i].buf[0] < 1 || +			    rdwr_pa[i].len < rdwr_pa[i].buf[0] + +					     I2C_SMBUS_BLOCK_MAX) { +				res = -EINVAL; +				break; +			} + +			rdwr_pa[i].len = rdwr_pa[i].buf[0]; +		}  	}  	if (res < 0) {  		int j; @@ -491,7 +514,6 @@ static int i2cdev_open(struct inode *inode, struct file *file)  		return -ENOMEM;  	}  	snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); -	client->driver = &i2cdev_driver;  	client->adapter = adap;  	file->private_data = client; @@ -522,19 +544,18 @@ static const struct file_operations i2cdev_fops = {  /* ------------------------------------------------------------------------- */ -/* - * The legacy "i2cdev_driver" is used primarily to get notifications when - * I2C adapters are added or removed, so that each one gets an i2c_dev - * and is thus made available to userspace driver code. - */ -  static struct class *i2c_dev_class; -static int i2cdev_attach_adapter(struct i2c_adapter *adap) +static int i2cdev_attach_adapter(struct device *dev, void *dummy)  { +	struct i2c_adapter *adap;  	struct i2c_dev *i2c_dev;  	int res; +	if (dev->type != &i2c_adapter_type) +		return 0; +	adap = to_i2c_adapter(dev); +  	i2c_dev = get_free_i2c_dev(adap);  	if (IS_ERR(i2c_dev))  		return PTR_ERR(i2c_dev); @@ -547,29 +568,28 @@ static int i2cdev_attach_adapter(struct i2c_adapter *adap)  		res = PTR_ERR(i2c_dev->dev);  		goto error;  	} -	res = device_create_file(i2c_dev->dev, &dev_attr_name); -	if (res) -		goto error_destroy;  	pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",  		 adap->name, adap->nr);  	return 0; -error_destroy: -	device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));  error:  	return_i2c_dev(i2c_dev);  	return res;  } -static int i2cdev_detach_adapter(struct i2c_adapter *adap) +static int i2cdev_detach_adapter(struct device *dev, void *dummy)  { +	struct i2c_adapter *adap;  	struct i2c_dev *i2c_dev; +	if (dev->type != &i2c_adapter_type) +		return 0; +	adap = to_i2c_adapter(dev); +  	i2c_dev = i2c_dev_get_by_minor(adap->nr);  	if (!i2c_dev) /* attach_adapter must have failed */  		return 0; -	device_remove_file(i2c_dev->dev, &dev_attr_name);  	return_i2c_dev(i2c_dev);  	device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); @@ -577,12 +597,23 @@ static int i2cdev_detach_adapter(struct i2c_adapter *adap)  	return 0;  } -static struct i2c_driver i2cdev_driver = { -	.driver = { -		.name	= "dev_driver", -	}, -	.attach_adapter	= i2cdev_attach_adapter, -	.detach_adapter	= i2cdev_detach_adapter, +static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action, +			 void *data) +{ +	struct device *dev = data; + +	switch (action) { +	case BUS_NOTIFY_ADD_DEVICE: +		return i2cdev_attach_adapter(dev, NULL); +	case BUS_NOTIFY_DEL_DEVICE: +		return i2cdev_detach_adapter(dev, NULL); +	} + +	return 0; +} + +static struct notifier_block i2cdev_notifier = { +	.notifier_call = i2cdev_notifier_call,  };  /* ------------------------------------------------------------------------- */ @@ -606,11 +637,16 @@ static int __init i2c_dev_init(void)  		res = PTR_ERR(i2c_dev_class);  		goto out_unreg_chrdev;  	} +	i2c_dev_class->dev_groups = i2c_groups; -	res = i2c_add_driver(&i2cdev_driver); +	/* Keep track of adapters which will be added or removed later */ +	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);  	if (res)  		goto out_unreg_class; +	/* Bind to already existing adapters right away */ +	i2c_for_each_dev(NULL, i2cdev_attach_adapter); +  	return 0;  out_unreg_class: @@ -624,7 +660,8 @@ out:  static void __exit i2c_dev_exit(void)  { -	i2c_del_driver(&i2cdev_driver); +	bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier); +	i2c_for_each_dev(NULL, i2cdev_detach_adapter);  	class_destroy(i2c_dev_class);  	unregister_chrdev(I2C_MAJOR, "i2c");  }  | 
