cl===============================
本文系本站原创,欢迎转载!
转载请注明出处:http://blog.csdn.net/gdt_a20
===============================
I2c子系统将i2c控制器(i2c寄存器所在的那块电路)抽象出来,用adapter(适配器)这个结构来描述,可以说一个适配器就代表一条i2c总
线,而挂接在i2c总线上的设备是用client这个结构体来表述,另外i2c_bus上的设备链表挂接的不单单是连接的这条i2c上的client,同
样adapter也作为一个设备挂在其所在的i2c_bus,也就是说控制器和设备都作为i2c_bus上的设备连接在设备链表,他们用内嵌的
device的type这个成员来区分,适配器的类型为i2c_adapter_type,client的类型为i2c_client_type。
一、i2c相关的描述结构
首先看一下i2c子系统给adapter定义的描述结构:
- struct i2c_adapter {
- struct module *owner;
- unsigned int id;
- unsigned int class;
- const struct i2c_algorithm *algo;
- void *algo_data;
-
- struct rt_mutex bus_lock;
- int timeout;
- int retries;
-
-
-
-
- struct device dev;
-
- int nr;
- char name[48];
- struct completion dev_released;
- struct mutex userspace_clients_lock;
- struct list_head userspace_clients;
- };
再来看一下client的描述结构:
- struct i2c_client {
- unsigned short flags;
-
-
-
-
- unsigned short addr;
- char name[I2C_NAME_SIZE];
- struct i2c_adapter *adapter;
- struct i2c_driver *driver;
-
-
-
-
-
- struct device dev;
- int irq;
- struct list_head detected;
- };
下面是driver的表述结构i2c_driver:
- struct i2c_driver {
- unsigned int class;
-
-
-
-
-
- int (*attach_adapter)(struct i2c_adapter *);
- int (*detach_adapter)(struct i2c_adapter *);
-
- int (*probe)(struct i2c_client *, const struct i2c_device_id *);
- int (*remove)(struct i2c_client *);
-
- void (*shutdown)(struct i2c_client *);
- int (*suspend)(struct i2c_client *, pm_message_t mesg);
- int (*resume)(struct i2c_client *);
-
-
-
-
-
- void (*alert)(struct i2c_client *, unsigned int data);
-
-
-
- int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
-
-
-
-
- struct device_driver driver;
-
- const struct i2c_device_id *id_table;
-
-
- int (*detect)(struct i2c_client *, struct i2c_board_info *);
- const unsigned short *address_list;
- struct list_head clients;
- ;
另外client端有一条全局链表,用于串联所有i2c的client设备,为__i2c_board_list,也就是说client可以静态注册亦可动态
被探测,静态注册挂接在该链表上的结构为:
- struct i2c_devinfo {
- struct list_head list;
- int busnum;
- struct i2c_board_info board_info;
- };
-
- struct i2c_board_info {
- char type[I2C_NAME_SIZE];
- unsigned short flags;
- unsigned short addr;
- void *platform_data;
- struct dev_archdata *archdata;
- #ifdef CONFIG_OF
- struct device_node *of_node;
- #endi
- int irq;
- };
i2c_devinfo结构静态注册的信息最后都会被整合集成到client中,形成一个标准的i2c_client设备并注册。
二、i2c核心初始化代码分析
首先看一下i2c平台无关的核心初始化,代码位于drivers/i2c/i2c-core.c下:
- static int __init i2c_init(void)
- {
- int retval;
-
-
-
- retval = bus_register(&i2c_bus_type);
- if (retval)
- return retval;
- #ifdef CONFIG_I2C_COMPAT
-
-
-
- i2c_adapter_compat_class = class_compat_register("i2c-adapter");
- if (!i2c_adapter_compat_class) {
- retval = -ENOMEM;
- goto bus_err;
- }
-
- #endif
-
-
-
- retval = i2c_add_driver(&dummy_driver);
- if (retval)
- goto class_err;
- return 0;
- class_err:
- #ifdef CONFIG_I2C_COMPAT
- class_compat_unregister(i2c_adapter_compat_class);
- bus_err:
- #endif
- bus_unregister(&i2c_bus_type);
- return retval;
- }
-
- struct bus_type i2c_bus_type = {
- .name = "i2c",
- .match = i2c_device_match,
- .probe = i2c_device_probe,
- .remove = i2c_device_remove,
- .shutdown = i2c_device_shutdown,
- .pm = &i2c_device_pm_ops,
- };
三、i2c_add_driver分析
驱动端的统一接口为i2c_add_driver:
- static inline int i2c_add_driver(struct i2c_driver *driver)
- {
-
-
-
- return i2c_register_driver(THIS_MODULE, driver);
- }
- int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- {
- int res;
-
- if (unlikely(WARN_ON(!i2c_bus_type.p)))
- return -EAGAIN;
-
-
-
-
-
- driver->driver.owner = owner;
- driver->driver.bus = &i2c_bus_type;
-
-
-
-
-
-
-
-
-
- res = driver_register(&driver->driver);
- if (res)
- return res;
- pr_debug("i2c-core: driver [%s] registered/n", driver->driver.name);
-
-
-
-
- INIT_LIST_HEAD(&driver->clients);
-
- mutex_lock(&core_lock);
-
-
-
-
-
- bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
- mutex_unlock(&core_lock);
- return 0;
- }
-
-
-
- static int i2c_device_match(struct device *dev, struct device_driver *drv)
- {
-
-
-
-
-
-
- struct i2c_client *client = i2c_verify_client(dev);
- struct i2c_driver *driver;
-
-
-
- if (!client)
- return 0;
-
-
-
-
-
-
- if (of_driver_match_device(dev, drv))
- return 1;
-
-
-
- driver = to_i2c_driver(drv);
-
-
-
-
-
-
-
- if (driver->id_table)
- return i2c_match_id(driver->id_table, client) != NULL;
- return 0;
- }
-
-
-
- static int i2c_device_probe(struct device *dev)
- {
-
-
-
- struct i2c_client *client = i2c_verify_client(dev);
- struct i2c_driver *driver;
- int status;
- if (!client)
- return 0;
-
-
-
-
-
- driver = to_i2c_driver(dev->driver);
-
-
-
-
-
- if (!driver->probe || !driver->id_table)
- return -ENODEV;
-
-
-
-
- client->driver = driver;
-
-
-
-
- if (!device_can_wakeup(&client->dev))
- device_init_wakeup(&client->dev,
- client->flags & I2C_CLIENT_WAKE);
- dev_dbg(dev, "probe/n");
-
-
-
-
- status = driver->probe(client, i2c_match_id(driver->id_table, client));
-
-
-
-
- if (status) {
- client->driver = NULL;
- i2c_set_clientdata(client, NULL);
- }
- return status;
- }
-
-
-
- static int __process_new_driver(struct device *dev, void *data)
- {
-
-
-
-
- if (dev->type != &i2c_adapter_type)
- return 0;
-
-
-
-
-
- return i2c_do_add_adapter(data, to_i2c_adapter(dev));
-
- }
-
-
-
- static int i2c_do_add_adapter(struct i2c_driver *driver,
- struct i2c_adapter *adap)
- {
-
-
-
-
-
-
- i2c_detect(adap, driver);
-
-
-
-
-
-
- if (driver->attach_adapter) {
-
- driver->attach_adapter(adap);
- }
- return 0;
- }
-
-
-
- static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
- {
- const unsigned short *address_list;
- struct i2c_client *temp_client;
- int i, err = 0;
- int adap_id = i2c_adapter_id(adapter);
-
-
-
- address_list = driver->address_list;
-
-
-
-
- if (!driver->detect || !address_list)
- return 0;
-
-
-
-
-
- temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!temp_client)
- return -ENOMEM;
-
-
-
-
- temp_client->adapter = adapter;
-
-
-
-
-
-
-
- if (!(adapter->class & driver->class))
- goto exit_free;
-
-
-
- for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
- dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
- "addr 0x%02x/n", adap_id, address_list[i]);
-
-
-
- temp_client->addr = address_list[i];
- err = i2c_detect_address(temp_client, driver);
- if (err)
- goto exit_free;
- }
- exit_free:
- kfree(temp_client);
- return err;
- }
-
-
-
- static int i2c_detect_address(struct i2c_client *temp_client,
- struct i2c_driver *driver)
- {
- struct i2c_board_info info;
- struct i2c_adapter *adapter = temp_client->adapter;
- int addr = temp_client->addr;
- int err;
-
-
-
-
-
-
- err = i2c_check_addr_validity(addr);
- if (err) {
- dev_warn(&adapter->dev, "Invalid probe address 0x%02x/n",
- addr);
- return err;
- }
-
-
-
-
-
- if (i2c_check_addr_busy(adapter, addr))
- return 0;
-
-
-
-
-
- if (!i2c_default_probe(adapter, addr))
- return 0;
-
-
-
-
-
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = addr;
- err = driver->detect(temp_client, &info);
- if (err) {
-
-
- return err == -ENODEV ? 0 : err;
- }
-
-
-
-
-
- if (info.type[0] == '/0') {
- dev_err(&adapter->dev, "%s detection function provided "
- "no name for 0x%x/n", driver->driver.name,
- addr);
- } else {
- struct i2c_client *client;
-
- dev_dbg(&adapter->dev, "Creating %s at 0x%02x/n",
- info.type, info.addr);
-
-
-
-
- client = i2c_new_device(adapter, &info);
-
-
-
-
- if (client)
- list_add_tail(&client->detected, &driver->clients);
- else
- dev_err(&adapter->dev, "Failed creating %s at 0x%02x/n",
- info.type, info.addr);
- }
- return 0;
- }
-
-
-
- static int i2c_check_addr_validity(unsigned short addr)
- {
-
-
-
-
-
-
-
-
-
-
- if (addr < 0x08 || addr > 0x77)
- return -EINVAL;
- return 0;
- }
-
-
-
- static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr)
- {
- int err;
- union i2c_smbus_data dummy;
- #ifdef CONFIG_X86
-
-
-
- if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON)
- && i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA))
- err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
- I2C_SMBUS_BYTE_DATA, &dummy);
- else
- #endif
-
-
-
- if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50)
- && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))
- err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0,
- I2C_SMBUS_QUICK, NULL);
-
-
-
-
- else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE))
- err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
- I2C_SMBUS_BYTE, &dummy);
- else {
- dev_warn(&adap->dev, "No suitable probing method supported/n");
- err = -EOPNOTSUPP;
- }
- return err >= 0;
- }
-
-
-
- static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
- {
- return (func & i2c_get_functionality(adap)) == func;
- }
- static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
- {
-
-
-
-
- return adap->algo->functionality(adap);
- }
-
-
-
- s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
- char read_write, u8 command, int protocol,
- union i2c_smbus_data *data)
- {
- unsigned long orig_jiffies;
- int try;
- s32 res;
- flags &= I2C_M_TEN | I2C_CLIENT_PEC;
-
-
-
-
- if (adapter->algo->smbus_xfer) {
- i2c_lock_adapter(adapter);
-
- orig_jiffies = jiffies;
- for (res = 0, try = 0; try <= adapter->retries; try++) {
- res = adapter->algo->smbus_xfer(adapter, addr, flags,
- read_write, command,
- protocol, data);
- if (res != -EAGAIN)
- break;
- if (time_after(jiffies,
- orig_jiffies + adapter->timeout))
- break;
- }
- i2c_unlock_adapter(adapter);
- } else
-
-
-
-
-
- res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
- command, protocol, data);
- return res;
- }
-
-
-
- static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
- unsigned short flags,
- char read_write, u8 command, int size,
- union i2c_smbus_data *data)
- {
-
-
-
-
-
-
-
-
-
- unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
- unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
-
-
-
-
- int num = read_write == I2C_SMBUS_READ ? 2 : 1;
-
-
-
-
- struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },
- { addr, flags | I2C_M_RD, 0, msgbuf1 }
- };
- int i;
- u8 partial_pec = 0;
- int status;
-
-
-
- msgbuf0[0] = command;
- switch (size) {
-
-
-
-
- case I2C_SMBUS_QUICK:
- msg[0].len = 0;
-
- msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?
- I2C_M_RD : 0);
- num = 1;
- break;
-
-
-
-
- case I2C_SMBUS_BYTE:
- if (read_write == I2C_SMBUS_READ) {
-
- msg[0].flags = I2C_M_RD | flags;
- num = 1;
- }
- break;
-
-
-
-
- case I2C_SMBUS_BYTE_DATA:
- if (read_write == I2C_SMBUS_READ)
- msg[1].len = 1;
- else {
- msg[0].len = 2;
- msgbuf0[1] = data->byte;
- }
- break;
-
-
-
-
- case I2C_SMBUS_WORD_DATA:
- if (read_write == I2C_SMBUS_READ)
- msg[1].len = 2;
- else {
- msg[0].len = 3;
- msgbuf0[1] = data->word & 0xff;
- msgbuf0[2] = data->word >> 8;
- }
- break;
-
-
-
-
- case I2C_SMBUS_PROC_CALL:
- num = 2;
- read_write = I2C_SMBUS_READ;
- msg[0].len = 3;
- msg[1].len = 2;
- msgbuf0[1] = data->word & 0xff;
- msgbuf0[2] = data->word >> 8;
- break;
-
-
-
-
- case I2C_SMBUS_BLOCK_DATA:
- if (read_write == I2C_SMBUS_READ) {
- msg[1].flags |= I2C_M_RECV_LEN;
- msg[1].len = 1;
-
- } else {
- msg[0].len = data->block[0] + 2;
- if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
- dev_err(&adapter->dev,
- "Invalid block write size %d/n",
- data->block[0]);
- return -EINVAL;
- }
- for (i = 1; i < msg[0].len; i++)
- msgbuf0[i] = data->block[i-1];
- }
- break;
-
-
-
-
- case I2C_SMBUS_BLOCK_PROC_CALL:
- num = 2;
- read_write = I2C_SMBUS_READ;
- if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
- dev_err(&adapter->dev,
- "Invalid block write size %d/n",
- data->block[0]);
- return -EINVAL;
- }
- msg[0].len = data->block[0] + 2;
- for (i = 1; i < msg[0].len; i++)
- msgbuf0[i] = data->block[i-1];
- msg[1].flags |= I2C_M_RECV_LEN;
- msg[1].len = 1;
-
- break;
-
-
-
-
- case I2C_SMBUS_I2C_BLOCK_DATA:
- if (read_write == I2C_SMBUS_READ) {
- msg[1].len = data->block[0];
- } else {
- msg[0].len = data->block[0] + 1;
- if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {
- dev_err(&adapter->dev,
- "Invalid block write size %d/n",
- data->block[0]);
- return -EINVAL;
- }
- for (i = 1; i <= data->block[0]; i++)
- msgbuf0[i] = data->block[i];
- }
- break;
- default:
- dev_err(&adapter->dev, "Unsupported transaction %d/n", size);
- return -EOPNOTSUPP;
- }
- i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
- && size != I2C_SMBUS_I2C_BLOCK_DATA);
- if (i) {
-
- if (!(msg[0].flags & I2C_M_RD)) {
- if (num == 1)
- i2c_smbus_add_pec(&msg[0]);
- else
- partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
- }
-
- if (msg[num-1].flags & I2C_M_RD)
- msg[num-1].len++;
- }
-
-
-
- status = i2c_transfer(adapter, msg, num);
- if (status < 0)
- return status;
-
- if (i && (msg[num-1].flags & I2C_M_RD)) {
- status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
- if (status < 0)
- return status;
- }
-
-
-
-
- if (read_write == I2C_SMBUS_READ)
- switch (size) {
- case I2C_SMBUS_BYTE:
- data->byte = msgbuf0[0];
- break;
- case I2C_SMBUS_BYTE_DATA:
- data->byte = msgbuf1[0];
- break;
- case I2C_SMBUS_WORD_DATA:
- case I2C_SMBUS_PROC_CALL:
- data->word = msgbuf1[0] | (msgbuf1[1] << 8);
- break;
- case I2C_SMBUS_I2C_BLOCK_DATA:
- for (i = 0; i < data->block[0]; i++)
- data->block[i+1] = msgbuf1[i];
- break;
- case I2C_SMBUS_BLOCK_DATA:
- case I2C_SMBUS_BLOCK_PROC_CALL:
- for (i = 0; i < msgbuf1[0] + 1; i++)
- data->block[i] = msgbuf1[i];
- break;
- }
- return 0;
- }
-
-
-
- int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
- {
- unsigned long orig_jiffies;
- int ret, try;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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
- if (in_atomic() || irqs_disabled()) {
- ret = i2c_trylock_adapter(adap);
- if (!ret)
-
- return -EAGAIN;
- } else {
- i2c_lock_adapter(adap);
- }
-
- orig_jiffies = jiffies;
- for (ret = 0, try = 0; try <= adap->retries; try++) {
- ret = adap->algo->master_xfer(adap, msgs, num);
- if (ret != -EAGAIN)
- break;
- if (time_after(jiffies, orig_jiffies + adap->timeout))
- break;
- }
- i2c_unlock_adapter(adap);
- return ret;
- } else {
- dev_dbg(&adap->dev, "I2C level transfers not supported/n");
- return -EOPNOTSUPP;
- }
- }
-
-
-
- struct i2c_client *
- i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
- {
- struct i2c_client *client;
- int status;
-
-
-
-
- client = kzalloc(sizeof *client, GFP_KERNEL);
- if (!client)
- return NULL;
-
-
-
-
- client->adapter = adap;
- client->dev.platform_data = info->platform_data;
-
-
-
-
- if (info->archdata)
- client->dev.archdata = *info->archdata;
-
-
-
- client->flags = info->flags;
- client->addr = info->addr;
- client->irq = info->irq;
- strlcpy(client->name, info->type, sizeof(client->name));
-
-
-
-
-
- status = i2c_check_client_addr_validity(client);
- if (status) {
- dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx/n",
- client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
- goto out_err_silent;
- }
-
-
-
-
-
- status = i2c_check_addr_busy(adap, client->addr);
- if (status)
- goto out_err;
-
-
-
- client->dev.parent = &client->adapter->dev;
- client->dev.bus = &i2c_bus_type;
- client->dev.type = &i2c_client_type;
- #ifdef CONFIG_OF
- client->dev.of_node = info->of_node;
- #endif
- dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
- client->addr);
-
-
-
- status = device_register(&client->dev);
- if (status)
- goto out_err;
- dev_dbg(&adap->dev, "client [%s] registered with bus id %s/n",
- client->name, dev_name(&client->dev));
- return client;
- out_err:
- dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
- "(%d)/n", client->name, client->addr, status);
- out_err_silent:
- kfree(client);
- return NULL;
- }
以上就是i2c通用driver添加的流程,下面看一下设备端,适配器的流程
四、i2c_add_adapter分析
- int i2c_add_adapter(struct i2c_adapter *adapter)
- {
- int id, res = 0;
- retry:
-
-
-
-
- if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
- return -ENOMEM;
- mutex_lock(&core_lock);
-
- res = idr_get_new_above(&i2c_adapter_idr, adapter,
- __i2c_first_dynamic_bus_num, &id);
- mutex_unlock(&core_lock);
- if (res < 0) {
- if (res == -EAGAIN)
- goto retry;
- return res;
- }
-
-
-
-
- adapter->nr = id;
- return i2c_register_adapter(adapter);
- }
-
-
-
- static int i2c_register_adapter(struct i2c_adapter *adap)
- {
- int res = 0;
-
-
-
-
-
- if (unlikely(WARN_ON(!i2c_bus_type.p))) {
- res = -EAGAIN;
- goto out_list;
- }
- rt_mutex_init(&adap->bus_lock);
- mutex_init(&adap->userspace_clients_lock);
- INIT_LIST_HEAD(&adap->userspace_clients);
-
-
-
-
-
- if (adap->timeout == 0)
- adap->timeout = HZ;
-
-
-
-
- dev_set_name(&adap->dev, "i2c-%d", adap->nr);
- adap->dev.bus = &i2c_bus_type;
- adap->dev.type = &i2c_adapter_type;
-
-
-
-
- res = device_register(&adap->dev);
- if (res)
- goto out_list;
- dev_dbg(&adap->dev, "adapter [%s] registered/n", adap->name);
- #ifdef CONFIG_I2C_COMPAT
- res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
- adap->dev.parent);
- if (res)
- dev_warn(&adap->dev,
- "Failed to create compatibility class link/n");
- #endif
-
-
-
-
-
-
- if (adap->nr < __i2c_first_dynamic_bus_num)
- i2c_scan_static_board_info(adap);
-
- mutex_lock(&core_lock);
-
-
-
-
- bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
- mutex_unlock(&core_lock);
- return 0;
- out_list:
- mutex_lock(&core_lock);
- idr_remove(&i2c_adapter_idr, adap->nr);
- mutex_unlock(&core_lock);
- return res;
- }
-
-
-
- static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
- {
- struct i2c_devinfo *devinfo;
- down_read(&__i2c_board_lock);
-
-
-
-
-
- list_for_each_entry(devinfo, &__i2c_board_list, list) {
- if (devinfo->busnum == adapter->nr
- && !i2c_new_device(adapter,
- &devinfo->board_info))
- dev_err(&adapter->dev,
- "Can't create device at 0x%02x/n",
- devinfo->board_info.addr);
- }
- up_read(&__i2c_board_lock);
- }
-
-
-
- static int __process_new_adapter(struct device_driver *d, void *data)
- {
-
-
-
-
-
- return i2c_do_add_adapter(to_i2c_driver(d), data);
- }
依据以上的分析画出流程图如下:
i2c_driver依据内部成员的设定,会走不同的分支,产生不同的作用,下面根据流程前后顺序总结一下:
当发现的是i2c_bus上的一个client时(发生在标准driver注册的匹配):
1、首先会进入到bus定义的匹配函数i2c_device_match如果定义了CONFIG_OF_DEVICE宏并且内部的标准
driver结构定义了of_match_table成员,则利用其进行匹配;
2、否则如果driver->id_table成员设定,则利用其进行匹配,否则匹配失败。
3、如果匹配成功会调用i2c_bus的i2c_device_probe函数,该函数会判断,如果该i2c_driver的probe成员
或者id_table成员为NULL,则返回,否则利用i2c_driver->probe初始化这个client。
当发现的是代表该i2c_bus的上的adapter时(发生在bus的遍历):
1、如果该driver的driver->detect或者address_list为NULL退出
2、如果该adapter->class和driver->class不匹配也退出
3、如果以上都成立最终会调用driver->detect函数,实例化支持的client
4、如果driver->attach_adapter也被设定,含会走旧式的路线,直接利用driver->attach_adapter进行探测
不过,一般不会让3&&4这种结果出现
可见,i2c_driver的设置非常灵活,抓住关键成员就不难掌握其流程。
五、i2c关于dev下节点的产生及其操作
该部分的代码位于rivers/i2c/i2c-dev.c下,我们从头看起:
- static int __init i2c_dev_init(void)
- {
- int res;
- printk(KERN_INFO "i2c /dev entries driver/n");
-
-
-
- res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
- if (res)
- goto out;
-
-
-
- i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
- if (IS_ERR(i2c_dev_class)) {
- res = PTR_ERR(i2c_dev_class);
- goto out_unreg_chrdev;
- }
-
-
-
-
- res = i2c_add_driver(&i2cdev_driver);
- if (res)
- goto out_unreg_class;
- return 0;
- out_unreg_class:
- class_destroy(i2c_dev_class);
- out_unreg_chrdev:
- unregister_chrdev(I2C_MAJOR, "i2c");
- out:
- printk(KERN_ERR "%s: Driver Initialisation failed/n", __FILE__);
- return res;
- }
-
- static struct i2c_driver i2cdev_driver = {
- .driver = {
- .name = "dev_driver",
- },
- .attach_adapter = i2cdev_attach_adapter,
- .detach_adapter = i2cdev_detach_adapter,
- };
由于没有关于client的支持表的定义,因此匹配client时就会直接返回, 由于存在成员attach_adapter,因此当匹配adapter时会进入该函数。
-
-
-
- static int i2cdev_attach_adapter(struct i2c_adapter *adap)
- {
- struct i2c_dev *i2c_dev;
- int res;
- i2c_dev = get_free_i2c_dev(adap);
- if (IS_ERR(i2c_dev))
- return PTR_ERR(i2c_dev);
-
-
-
-
-
-
-
- i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
- 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;
- }
通过以上分析可以看到i2c-dev层,找到一个adapter就会自动为其创建设备节点,形式类似于i2c-*,那么当应用层open对应的设备节点的时候,内核会自动调用刚才注册的字符设备的操作函数, 我们先来看一下刚才注册的字符设备的操作集:
- static const struct file_operations i2cdev_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = i2cdev_read,
- .write = i2cdev_write,
- .unlocked_ioctl = i2cdev_ioctl,
- .open = i2cdev_open,
- .release = i2cdev_release,
- };
按照用户层的流程先看一下open函数i2cdev_open:
- static int i2cdev_open(struct inode *inode, struct file *file)
- {
- unsigned int minor = iminor(inode);
- struct i2c_client *client;
- struct i2c_adapter *adap;
- struct i2c_dev *i2c_dev;
-
-
-
-
-
- i2c_dev = i2c_dev_get_by_minor(minor);
-
- if (!i2c_dev)
- return -ENODEV;
- adap = i2c_get_adapter(i2c_dev->adap->nr);
- if (!adap)
- return -ENODEV;
-
-
-
-
-
-
-
- client = kzalloc(sizeof(*client), GFP_KERNEL);
- if (!client) {
- i2c_put_adapter(adap);
- return -ENOMEM;
- }
-
-
- snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
-
-
- client->driver = &i2cdev_driver;
-
-
- client->adapter = adap;
-
- file->private_data = client;
- return 0;
- }
由open可见,我们要操作i2c下的设备,始终是需要通过adapter,物理上也是如此,操作设备都是通过控制器进行读写的,因此我们打开的始终是
adapter而open过程中会创建client,来表述我们主观上是要操作设备。下面在看一下read函数:
- 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 = file->private_data;
-
-
- if (count > 8192)
- count = 8192;
-
-
- tmp = kmalloc(count, GFP_KERNEL);
- if (tmp == NULL)
- return -ENOMEM;
- pr_debug("i2c-dev: i2c-%d reading %zu bytes./n",
- iminor(file->f_path.dentry->d_inode), count);
-
-
- ret = i2c_master_recv(client, tmp, count);
- if (ret >= 0)
-
- ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
- kfree(tmp);
- return ret;
- }
-
-
-
- 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);
-
-
- return (ret == 1) ? count : ret;
- }
i2c_transfer函数上面已经分析过了,其会最终调用client所在adapter的adap->algo->master_xfer函数发送。
六、总结
分析了linux下i2c子系统模型及其关键点,针对核心的平台无关代码进行了描述,以上为个人观点,如有不妥,还望指正 ^_^
阅读(901) | 评论(0) | 转发(0) |