diff options
Diffstat (limited to 'drivers/i2c/i2c-dev.c')
| -rw-r--r-- | drivers/i2c/i2c-dev.c | 517 |
1 files changed, 292 insertions, 225 deletions
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 393e679d9fa..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,15 +29,16 @@ #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> #include <linux/list.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> -#include <asm/uaccess.h> - -static struct i2c_driver i2cdev_driver; +#include <linux/jiffies.h> +#include <linux/uaccess.h> /* * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a @@ -44,8 +46,7 @@ static struct i2c_driver i2cdev_driver; * 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; @@ -101,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)); @@ -110,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); /* ------------------------------------------------------------------------- */ @@ -131,53 +138,49 @@ static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL); * needed by those system calls and by this SMBus interface. */ -static ssize_t i2cdev_read (struct file *file, char __user *buf, size_t count, - loff_t *offset) +static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, + loff_t *offset) { char *tmp; int ret; - struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_client *client = file->private_data; if (count > 8192) count = 8192; - tmp = kmalloc(count,GFP_KERNEL); - if (tmp==NULL) + tmp = kmalloc(count, GFP_KERNEL); + if (tmp == NULL) return -ENOMEM; - pr_debug("i2c-dev: i2c-%d reading %zd bytes.\n", - iminor(file->f_path.dentry->d_inode), count); + pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n", + iminor(file_inode(file)), count); - ret = i2c_master_recv(client,tmp,count); + ret = i2c_master_recv(client, tmp, count); if (ret >= 0) - ret = copy_to_user(buf,tmp,count)?-EFAULT:ret; + ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; kfree(tmp); return ret; } -static ssize_t i2cdev_write (struct file *file, const char __user *buf, size_t count, - loff_t *offset) +static ssize_t i2cdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) { int ret; char *tmp; - struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_client *client = file->private_data; if (count > 8192) count = 8192; - tmp = kmalloc(count,GFP_KERNEL); - if (tmp==NULL) - return -ENOMEM; - if (copy_from_user(tmp,buf,count)) { - kfree(tmp); - return -EFAULT; - } + tmp = memdup_user(buf, count); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); - pr_debug("i2c-dev: i2c-%d writing %zd bytes.\n", - iminor(file->f_path.dentry->d_inode), count); + pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n", + iminor(file_inode(file)), count); - ret = i2c_master_send(client,tmp,count); + ret = i2c_master_send(client, tmp, count); kfree(tmp); return ret; } @@ -192,30 +195,235 @@ static int i2cdev_check(struct device *dev, void *addrp) return dev->driver ? -EBUSY : 0; } +/* walk up mux tree */ +static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result; + + result = device_for_each_child(&adapter->dev, &addr, i2cdev_check); + if (!result && parent) + result = i2cdev_check_mux_parents(parent, addr); + + return result; +} + +/* recurse down mux tree */ +static int i2cdev_check_mux_children(struct device *dev, void *addrp) +{ + int result; + + if (dev->type == &i2c_adapter_type) + result = device_for_each_child(dev, addrp, + i2cdev_check_mux_children); + else + result = i2cdev_check(dev, addrp); + + return result; +} + /* This address checking function differs from the one in i2c-core in that it considers an address with a registered device, but no driver bound to it, as NOT busy. */ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) { - return device_for_each_child(&adapter->dev, &addr, i2cdev_check); + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result = 0; + + if (parent) + result = i2cdev_check_mux_parents(parent, addr); + + if (!result) + result = device_for_each_child(&adapter->dev, &addr, + i2cdev_check_mux_children); + + return result; } -static int i2cdev_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client, + unsigned long arg) { - struct i2c_client *client = (struct i2c_client *)file->private_data; struct i2c_rdwr_ioctl_data rdwr_arg; - struct i2c_smbus_ioctl_data data_arg; - union i2c_smbus_data temp; struct i2c_msg *rdwr_pa; u8 __user **data_ptrs; - int i,datasize,res; + int i, res; + + if (copy_from_user(&rdwr_arg, + (struct i2c_rdwr_ioctl_data __user *)arg, + sizeof(rdwr_arg))) + return -EFAULT; + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) + return -EINVAL; + + 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) { + kfree(rdwr_pa); + return -ENOMEM; + } + + res = 0; + for (i = 0; i < rdwr_arg.nmsgs; i++) { + /* 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; + for (j = 0; j < i; ++j) + kfree(rdwr_pa[j].buf); + kfree(data_ptrs); + kfree(rdwr_pa); + return res; + } + + res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs); + while (i-- > 0) { + if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) { + if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf, + rdwr_pa[i].len)) + res = -EFAULT; + } + kfree(rdwr_pa[i].buf); + } + kfree(data_ptrs); + kfree(rdwr_pa); + 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; + + if (copy_from_user(&data_arg, + (struct i2c_smbus_ioctl_data __user *) arg, + sizeof(struct i2c_smbus_ioctl_data))) + return -EFAULT; + if ((data_arg.size != I2C_SMBUS_BYTE) && + (data_arg.size != I2C_SMBUS_QUICK) && + (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA) && + (data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) && + (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) && + (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) { + dev_dbg(&client->adapter->dev, + "size out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.size); + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((data_arg.read_write != I2C_SMBUS_READ) && + (data_arg.read_write != I2C_SMBUS_WRITE)) { + dev_dbg(&client->adapter->dev, + "read_write out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.read_write); + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((data_arg.size == I2C_SMBUS_QUICK) || + ((data_arg.size == I2C_SMBUS_BYTE) && + (data_arg.read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags, data_arg.read_write, + data_arg.command, data_arg.size, NULL); + + if (data_arg.data == NULL) { + dev_dbg(&client->adapter->dev, + "data is NULL pointer in ioctl I2C_SMBUS.\n"); + return -EINVAL; + } + + if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || + (data_arg.size == I2C_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == smbus block, i2c block, or block proc. call */ + datasize = sizeof(data_arg.data->block); + + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || + (data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) || + (data_arg.read_write == I2C_SMBUS_WRITE)) { + if (copy_from_user(&temp, data_arg.data, datasize)) + return -EFAULT; + } + if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) { + /* Convert old I2C block commands to the new + convention. This preserves binary compatibility. */ + data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA; + if (data_arg.read_write == I2C_SMBUS_READ) + temp.block[0] = I2C_SMBUS_BLOCK_MAX; + } + res = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + data_arg.read_write, data_arg.command, data_arg.size, &temp); + if (!res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) { + if (copy_to_user(data_arg.data, &temp, datasize)) + return -EFAULT; + } + return res; +} + +static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = file->private_data; unsigned long funcs; dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", cmd, arg); - switch ( cmd ) { + switch (cmd) { case I2C_SLAVE: case I2C_SLAVE_FORCE: /* NOTE: devices set up to work with "new style" drivers @@ -253,169 +461,19 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file, return put_user(funcs, (unsigned long __user *)arg); case I2C_RDWR: - if (copy_from_user(&rdwr_arg, - (struct i2c_rdwr_ioctl_data __user *)arg, - sizeof(rdwr_arg))) - return -EFAULT; - - /* Put an arbitrary limit on the number of messages that can - * be sent at once */ - if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) - return -EINVAL; - - rdwr_pa = (struct i2c_msg *) - kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), - GFP_KERNEL); - - if (rdwr_pa == NULL) return -ENOMEM; - - if (copy_from_user(rdwr_pa, rdwr_arg.msgs, - rdwr_arg.nmsgs * sizeof(struct i2c_msg))) { - kfree(rdwr_pa); - return -EFAULT; - } - - data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL); - if (data_ptrs == NULL) { - kfree(rdwr_pa); - return -ENOMEM; - } - - 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)) { - res = -EINVAL; - break; - } - data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; - rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL); - if(rdwr_pa[i].buf == NULL) { - res = -ENOMEM; - break; - } - if(copy_from_user(rdwr_pa[i].buf, - data_ptrs[i], - rdwr_pa[i].len)) { - ++i; /* Needs to be kfreed too */ - res = -EFAULT; - break; - } - } - if (res < 0) { - int j; - for (j = 0; j < i; ++j) - kfree(rdwr_pa[j].buf); - kfree(data_ptrs); - kfree(rdwr_pa); - return res; - } - - res = i2c_transfer(client->adapter, - rdwr_pa, - rdwr_arg.nmsgs); - while(i-- > 0) { - if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) { - if(copy_to_user( - data_ptrs[i], - rdwr_pa[i].buf, - rdwr_pa[i].len)) { - res = -EFAULT; - } - } - kfree(rdwr_pa[i].buf); - } - kfree(data_ptrs); - kfree(rdwr_pa); - return res; + return i2cdev_ioctl_rdrw(client, arg); case I2C_SMBUS: - if (copy_from_user(&data_arg, - (struct i2c_smbus_ioctl_data __user *) arg, - sizeof(struct i2c_smbus_ioctl_data))) - return -EFAULT; - if ((data_arg.size != I2C_SMBUS_BYTE) && - (data_arg.size != I2C_SMBUS_QUICK) && - (data_arg.size != I2C_SMBUS_BYTE_DATA) && - (data_arg.size != I2C_SMBUS_WORD_DATA) && - (data_arg.size != I2C_SMBUS_PROC_CALL) && - (data_arg.size != I2C_SMBUS_BLOCK_DATA) && - (data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) && - (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) && - (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) { - dev_dbg(&client->adapter->dev, - "size out of range (%x) in ioctl I2C_SMBUS.\n", - data_arg.size); - return -EINVAL; - } - /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, - so the check is valid if size==I2C_SMBUS_QUICK too. */ - if ((data_arg.read_write != I2C_SMBUS_READ) && - (data_arg.read_write != I2C_SMBUS_WRITE)) { - dev_dbg(&client->adapter->dev, - "read_write out of range (%x) in ioctl I2C_SMBUS.\n", - data_arg.read_write); - return -EINVAL; - } - - /* Note that command values are always valid! */ - - if ((data_arg.size == I2C_SMBUS_QUICK) || - ((data_arg.size == I2C_SMBUS_BYTE) && - (data_arg.read_write == I2C_SMBUS_WRITE))) - /* These are special: we do not use data */ - return i2c_smbus_xfer(client->adapter, client->addr, - client->flags, - data_arg.read_write, - data_arg.command, - data_arg.size, NULL); - - if (data_arg.data == NULL) { - dev_dbg(&client->adapter->dev, - "data is NULL pointer in ioctl I2C_SMBUS.\n"); - return -EINVAL; - } + return i2cdev_ioctl_smbus(client, arg); - if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || - (data_arg.size == I2C_SMBUS_BYTE)) - datasize = sizeof(data_arg.data->byte); - else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || - (data_arg.size == I2C_SMBUS_PROC_CALL)) - datasize = sizeof(data_arg.data->word); - else /* size == smbus block, i2c block, or block proc. call */ - datasize = sizeof(data_arg.data->block); - - if ((data_arg.size == I2C_SMBUS_PROC_CALL) || - (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || - (data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) || - (data_arg.read_write == I2C_SMBUS_WRITE)) { - if (copy_from_user(&temp, data_arg.data, datasize)) - return -EFAULT; - } - if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) { - /* Convert old I2C block commands to the new - convention. This preserves binary compatibility. */ - data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA; - if (data_arg.read_write == I2C_SMBUS_READ) - temp.block[0] = I2C_SMBUS_BLOCK_MAX; - } - res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, - data_arg.read_write, - data_arg.command,data_arg.size,&temp); - if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || - (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || - (data_arg.read_write == I2C_SMBUS_READ))) { - if (copy_to_user(data_arg.data, &temp, datasize)) - return -EFAULT; - } - return res; case I2C_RETRIES: client->adapter->retries = arg; break; case I2C_TIMEOUT: - client->adapter->timeout = arg; + /* For historical reasons, user-space sets the timeout + * value in units of 10 ms. + */ + client->adapter->timeout = msecs_to_jiffies(arg * 10); break; default: /* NOTE: returning a fault code here could cause trouble @@ -456,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; @@ -480,61 +537,59 @@ static const struct file_operations i2cdev_fops = { .llseek = no_llseek, .read = i2cdev_read, .write = i2cdev_write, - .ioctl = i2cdev_ioctl, + .unlocked_ioctl = i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, }; /* ------------------------------------------------------------------------- */ -/* - * 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); /* register this i2c device with the driver core */ i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, - MKDEV(I2C_MAJOR, adap->nr), + MKDEV(I2C_MAJOR, adap->nr), NULL, "i2c-%d", adap->nr); if (IS_ERR(i2c_dev->dev)) { 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)); @@ -542,19 +597,23 @@ static int i2cdev_detach_adapter(struct i2c_adapter *adap) return 0; } -static int i2cdev_detach_client(struct i2c_client *client) +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 i2c_driver i2cdev_driver = { - .driver = { - .name = "dev_driver", - }, - .id = I2C_DRIVERID_I2CDEV, - .attach_adapter = i2cdev_attach_adapter, - .detach_adapter = i2cdev_detach_adapter, - .detach_client = i2cdev_detach_client, +static struct notifier_block i2cdev_notifier = { + .notifier_call = i2cdev_notifier_call, }; /* ------------------------------------------------------------------------- */ @@ -574,13 +633,20 @@ static int __init i2c_dev_init(void) goto out; i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); - if (IS_ERR(i2c_dev_class)) + if (IS_ERR(i2c_dev_class)) { + 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: @@ -594,9 +660,10 @@ 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"); + unregister_chrdev(I2C_MAJOR, "i2c"); } MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " |
