📄 i2c-core.c
字号:
/* legacy drivers create and remove clients themselves */ if ((res = driver->detach_client(client))) { dev_err(&adap->dev, "detach_client failed for client " "[%s] at address 0x%02x\n", client->name, client->addr); goto out_unlock; } } /* clean up the sysfs representation */ init_completion(&adap->dev_released); device_unregister(&adap->dev); list_del(&adap->list); /* wait for sysfs to drop all references */ wait_for_completion(&adap->dev_released); /* free bus id */ idr_remove(&i2c_adapter_idr, adap->nr); dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); out_unlock: mutex_unlock(&core_lists); return res;}EXPORT_SYMBOL(i2c_del_adapter);/* ------------------------------------------------------------------------- *//* * An i2c_driver is used with one or more i2c_client (device) nodes to access * i2c slave chips, on a bus instance associated with some i2c_adapter. There * are two models for binding the driver to its device: "new style" drivers * follow the standard Linux driver model and just respond to probe() calls * issued if the driver core sees they match(); "legacy" drivers create device * nodes themselves. */int i2c_register_driver(struct module *owner, struct i2c_driver *driver){ int res; /* new style driver methods can't mix with legacy ones */ if (is_newstyle_driver(driver)) { if (driver->attach_adapter || driver->detach_adapter || driver->detach_client) { printk(KERN_WARNING "i2c-core: driver [%s] is confused\n", driver->driver.name); return -EINVAL; } } /* add the driver to the list of i2c drivers in the driver core */ driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type; /* for new style drivers, when registration returns the driver core * will have called probe() for all matching-but-unbound devices. */ res = driver_register(&driver->driver); if (res) return res; mutex_lock(&core_lists); list_add_tail(&driver->list,&drivers); pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name); /* legacy drivers scan i2c busses directly */ if (driver->attach_adapter) { struct i2c_adapter *adapter; list_for_each_entry(adapter, &adapters, list) { driver->attach_adapter(adapter); } } mutex_unlock(&core_lists); return 0;}EXPORT_SYMBOL(i2c_register_driver);/** * i2c_del_driver - unregister I2C driver * @driver: the driver being unregistered * Context: can sleep */void i2c_del_driver(struct i2c_driver *driver){ struct list_head *item1, *item2, *_n; struct i2c_client *client; struct i2c_adapter *adap; mutex_lock(&core_lists); /* new-style driver? */ if (is_newstyle_driver(driver)) goto unregister; /* Have a look at each adapter, if clients of this driver are still * attached. If so, detach them to be able to kill the driver * afterwards. */ list_for_each(item1,&adapters) { adap = list_entry(item1, struct i2c_adapter, list); if (driver->detach_adapter) { if (driver->detach_adapter(adap)) { dev_err(&adap->dev, "detach_adapter failed " "for driver [%s]\n", driver->driver.name); } } else { list_for_each_safe(item2, _n, &adap->clients) { client = list_entry(item2, struct i2c_client, list); if (client->driver != driver) continue; dev_dbg(&adap->dev, "detaching client [%s] " "at 0x%02x\n", client->name, client->addr); if (driver->detach_client(client)) { dev_err(&adap->dev, "detach_client " "failed for client [%s] at " "0x%02x\n", client->name, client->addr); } } } } unregister: driver_unregister(&driver->driver); list_del(&driver->list); pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name); mutex_unlock(&core_lists);}EXPORT_SYMBOL(i2c_del_driver);/* ------------------------------------------------------------------------- */static int __i2c_check_addr(struct i2c_adapter *adapter, unsigned int addr){ struct list_head *item; struct i2c_client *client; list_for_each(item,&adapter->clients) { client = list_entry(item, struct i2c_client, list); if (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;}int i2c_attach_client(struct i2c_client *client){ struct i2c_adapter *adapter = client->adapter; int res = 0; mutex_lock(&adapter->clist_lock); if (__i2c_check_addr(client->adapter, client->addr)) { res = -EBUSY; goto out_unlock; } list_add_tail(&client->list,&adapter->clients); client->usage_count = 0; client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; if (client->driver) client->dev.driver = &client->driver->driver; if (client->driver && !is_newstyle_driver(client->driver)) { client->dev.release = i2c_client_release; client->dev.uevent_suppress = 1; } else client->dev.release = i2c_client_dev_release; snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id), "%d-%04x", i2c_adapter_id(adapter), client->addr); dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n", client->name, client->dev.bus_id); res = device_register(&client->dev); if (res) goto out_list; mutex_unlock(&adapter->clist_lock); if (adapter->client_register) { if (adapter->client_register(client)) { dev_dbg(&adapter->dev, "client_register " "failed for client [%s] at 0x%02x\n", client->name, client->addr); } } return 0;out_list: list_del(&client->list); dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x " "(%d)\n", client->name, client->addr, res);out_unlock: mutex_unlock(&adapter->clist_lock); return res;}EXPORT_SYMBOL(i2c_attach_client);int i2c_detach_client(struct i2c_client *client){ struct i2c_adapter *adapter = client->adapter; int res = 0; if (client->usage_count > 0) { dev_warn(&client->dev, "Client [%s] still busy, " "can't detach\n", client->name); return -EBUSY; } if (adapter->client_unregister) { res = adapter->client_unregister(client); if (res) { dev_err(&client->dev, "client_unregister [%s] failed, " "client not detached\n", client->name); goto out; } } mutex_lock(&adapter->clist_lock); list_del(&client->list); init_completion(&client->released); device_unregister(&client->dev); mutex_unlock(&adapter->clist_lock); wait_for_completion(&client->released); out: return res;}EXPORT_SYMBOL(i2c_detach_client);static int i2c_inc_use_client(struct i2c_client *client){ if (!try_module_get(client->driver->driver.owner)) return -ENODEV; if (!try_module_get(client->adapter->owner)) { module_put(client->driver->driver.owner); return -ENODEV; } return 0;}static void i2c_dec_use_client(struct i2c_client *client){ module_put(client->driver->driver.owner); module_put(client->adapter->owner);}int i2c_use_client(struct i2c_client *client){ int ret; ret = i2c_inc_use_client(client); if (ret) return ret; client->usage_count++; return 0;}EXPORT_SYMBOL(i2c_use_client);int i2c_release_client(struct i2c_client *client){ if (!client->usage_count) { pr_debug("i2c-core: %s used one too many times\n", __FUNCTION__); return -EPERM; } client->usage_count--; i2c_dec_use_client(client); return 0;}EXPORT_SYMBOL(i2c_release_client);void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg){ struct list_head *item; struct i2c_client *client; 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);}EXPORT_SYMBOL(i2c_clients_command);static int __init i2c_init(void){ int retval; retval = bus_register(&i2c_bus_type); if (retval) return retval; return class_register(&i2c_adapter_class);}static void __exit i2c_exit(void){ class_unregister(&i2c_adapter_class); bus_unregister(&i2c_bus_type);}subsys_initcall(i2c_init);module_exit(i2c_exit);/* ---------------------------------------------------- * the functional interface to the i2c busses. * ---------------------------------------------------- */int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num){ int ret; if (adap->algo->master_xfer) {#ifdef DEBUG for (ret = 0; ret < num; ret++) { dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, " "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W', msgs[ret].addr, msgs[ret].len, (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); }#endif mutex_lock_nested(&adap->bus_lock, adap->level); ret = adap->algo->master_xfer(adap,msgs,num); mutex_unlock(&adap->bus_lock); return ret; } else { dev_dbg(&adap->dev, "I2C level transfers not supported\n"); return -ENOSYS; }}EXPORT_SYMBOL(i2c_transfer);int i2c_master_send(struct i2c_client *client,const char *buf ,int count){ int ret; struct i2c_adapter *adap=client->adapter; struct i2c_msg msg; msg.addr = client->addr; msg.flags = client->flags & I2C_M_TEN; msg.len = count; msg.buf = (char *)buf; ret = i2c_transfer(adap, &msg, 1); /* If everything went ok (i.e. 1 msg transmitted), return #bytes transmitted, else error code. */ return (ret == 1) ? count : ret;}EXPORT_SYMBOL(i2c_master_send);int i2c_master_recv(struct i2c_client *client, char *buf ,int count){ struct i2c_adapter *adap=client->adapter; struct i2c_msg msg; int ret; msg.addr = client->addr; msg.flags = client->flags & I2C_M_TEN; msg.flags |= I2C_M_RD; msg.len = count; msg.buf = buf; ret = i2c_transfer(adap, &msg, 1); /* If everything went ok (i.e. 1 msg transmitted), return #bytes transmitted, else error code. */ return (ret == 1) ? count : ret;}EXPORT_SYMBOL(i2c_master_recv);/* ---------------------------------------------------- * the i2c address scanning function * Will not work for 10-bit addresses! * ---------------------------------------------------- */static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind, int (*found_proc) (struct i2c_adapter *, int, int)){ int err; /* Make sure the address is valid */ if (addr < 0x03 || addr > 0x77) { dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n", addr); return -EINVAL; } /* Skip if already in use */ if (i2c_check_addr(adapter, addr)) return 0; /* Make sure there is something at this address, unless forced */ if (kind < 0) { if (i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) < 0) return 0; /* prevent 24RF08 corruption */ if ((addr & ~0x0f) == 0x50) i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL); } /* Finally call the custom detection function */ err = found_proc(adapter, addr, kind); /* -ENODEV can be returned if there is a chip at the given address but it isn't supported by this chip driver. We catch it here as this isn't an error. */ if (err == -ENODEV) err = 0; if (err) dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n", addr, err); return err;}int i2c_probe(struct i2c_adapter *adapter, struct i2c_client_address_data *address_data, int (*found_proc) (struct i2c_adapter *, int, int)){ int i, err; int adap_id = i2c_adapter_id(adapter); /* Force entries are done first, and are not affected by ignore entries */ if (address_data->forces) { unsigned short **forces = address_data->forces; int kind; for (kind = 0; forces[kind]; kind++) { for (i = 0; forces[kind][i] != I2C_CLIENT_END; i += 2) { if (forces[kind][i] == adap_id || forces[kind][i] == ANY_I2C_BUS) { dev_dbg(&adapter->dev, "found force " "parameter for adapter %d, " "addr 0x%02x, kind %d\n", adap_id, forces[kind][i + 1], kind); err = i2c_probe_address(adapter, forces[kind][i + 1], kind, found_proc); if (err) return err; } } } } /* Stop here if we can't use SMBUS_QUICK */ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) { if (address_data->probe[0] == I2C_CLIENT_END && address_data->normal_i2c[0] == I2C_CLIENT_END) return 0; dev_warn(&adapter->dev, "SMBus Quick command not supported, " "can't probe for chips\n"); return -1; } /* Probe entries are done second, and are not affected by ignore entries either */ for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) { if (address_data->probe[i] == adap_id || address_data->probe[i] == ANY_I2C_BUS) { dev_dbg(&adapter->dev, "found probe parameter for " "adapter %d, addr 0x%02x\n", adap_id, address_data->probe[i + 1]); err = i2c_probe_address(adapter, address_data->probe[i + 1], -1, found_proc); if (err) return err; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -