diff options
Diffstat (limited to 'drivers/i2c/i2c-core.c')
-rw-r--r-- | drivers/i2c/i2c-core.c | 78 |
1 files changed, 46 insertions, 32 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index ddd1b83..c8bbd67 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -199,6 +199,25 @@ static struct bus_type i2c_bus_type = { .resume = i2c_device_resume, }; + +/** + * i2c_verify_client - return parameter as i2c_client, or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-I2C device as an i2c_client. + */ +struct i2c_client *i2c_verify_client(struct device *dev) +{ + return (dev->bus == &i2c_bus_type) + ? to_i2c_client(dev) + : NULL; +} +EXPORT_SYMBOL(i2c_verify_client); + + /** * i2c_new_device - instantiate an i2c device for use with a new style driver * @adap: the adapter managing the device @@ -670,28 +689,19 @@ EXPORT_SYMBOL(i2c_del_driver); /* ------------------------------------------------------------------------- */ -static int __i2c_check_addr(struct i2c_adapter *adapter, unsigned int addr) +static int __i2c_check_addr(struct device *dev, void *addrp) { - struct list_head *item; - struct i2c_client *client; + struct i2c_client *client = i2c_verify_client(dev); + int addr = *(int *)addrp; - list_for_each(item,&adapter->clients) { - client = list_entry(item, struct i2c_client, list); - if (client->addr == addr) - return -EBUSY; - } + if (client && client->addr == addr) + return -EBUSY; return 0; } static int i2c_check_addr(struct i2c_adapter *adapter, int addr) { - int rval; - - mutex_lock(&adapter->clist_lock); - rval = __i2c_check_addr(adapter, addr); - mutex_unlock(&adapter->clist_lock); - - return rval; + return device_for_each_child(&adapter->dev, &addr, __i2c_check_addr); } int i2c_attach_client(struct i2c_client *client) @@ -700,7 +710,7 @@ int i2c_attach_client(struct i2c_client *client) int res = 0; mutex_lock(&adapter->clist_lock); - if (__i2c_check_addr(client->adapter, client->addr)) { + if (i2c_check_addr(client->adapter, client->addr)) { res = -EBUSY; goto out_unlock; } @@ -804,24 +814,28 @@ void i2c_release_client(struct i2c_client *client) } EXPORT_SYMBOL(i2c_release_client); +struct i2c_cmd_arg { + unsigned cmd; + void *arg; +}; + +static int i2c_cmd(struct device *dev, void *_arg) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_cmd_arg *arg = _arg; + + if (client && client->driver && client->driver->command) + client->driver->command(client, arg->cmd, arg->arg); + return 0; +} + void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg) { - struct list_head *item; - struct i2c_client *client; + struct i2c_cmd_arg cmd_arg; - mutex_lock(&adap->clist_lock); - list_for_each(item,&adap->clients) { - client = list_entry(item, struct i2c_client, list); - if (!try_module_get(client->driver->driver.owner)) - continue; - if (NULL != client->driver->command) { - mutex_unlock(&adap->clist_lock); - client->driver->command(client,cmd,arg); - mutex_lock(&adap->clist_lock); - } - module_put(client->driver->driver.owner); - } - mutex_unlock(&adap->clist_lock); + cmd_arg.cmd = cmd; + cmd_arg.arg = arg; + device_for_each_child(&adap->dev, &cmd_arg, i2c_cmd); } EXPORT_SYMBOL(i2c_clients_command); @@ -1087,7 +1101,7 @@ i2c_new_probed_device(struct i2c_adapter *adap, } /* Check address availability */ - if (__i2c_check_addr(adap, addr_list[i])) { + if (i2c_check_addr(adap, addr_list[i])) { dev_dbg(&adap->dev, "Address 0x%02x already in " "use, not probing\n", addr_list[i]); continue; |