赞
踩
Linux代码版本:linux3.0
开发板环境: tiny4412
导读:i2c控制器作为platform_device挂接在platform总线上,在《嵌入式软件开发之------浅谈linux驱动模型(四)device和driver》以i2c控制器为例,分析了
s3c_device_i2c1和s3c24xx_i2c_driver的注册过程,总结下来就是下图:
每个i2c控制器都是一个i2c 总线,i2c控制器即挂接在platform总线上,也为i2c提供总线。platform总线有platform_device和platform_driver,相应的i2c总线也有对应的device和driver,只不过是i2c_client和i2c_driver.
一、i2c_adapter的注册及 i2c_client 的实例化
下面看i2c_client,有没有觉得和platform_device很像?都是封装了devcie后添加了部分各自特点的成员:
- struct i2c_client {
- unsigned short flags; /* div., see below */
- unsigned short addr; /* chip address - NOTE: 7bit */
- /* addresses are stored in the */
- /* _LOWER_ 7 bits */
- char name[I2C_NAME_SIZE];
- struct i2c_adapter *adapter; /* the adapter we sit on */
- struct i2c_driver *driver; /* and our access routines */
- struct device dev; /* the device structure */
- int irq; /* irq issued by device */
- struct list_head detected;
- };
unsigned short flags;
标志,用 I2C_CLIENT_TEN 表示 10 bit 地址,用 I2C_CLIENT_PEC 表示SMBus的错误数据检测
unsigned short addr;
器件的地址
char name[I2C_NAME_SIZE];
设备的名字,在 new_device 属性文件中,低7位代表地址
struct i2c_adapter *adapter; /* the adapter we sit on */
虽然是挂接在I2C总线上的,client->dev.bus = &i2c_bus_type,可是 总要 指向所在的i2c控制器吗
struct i2c_driver *driver; /* and our access routines */
对应的驱动
struct device dev; /* the device structure */
封装的device结构体
int irq; /* irq issued by device */
用来表示此设备产生的IRQ
struct list_head detected;
用于插入 i2c_driver.clients 的节点,在遍历 i2c_drive r探测到实际挂接在 i2c_adapter 但又未注册
的 i2c 设备时,实例化成 i2c_client 后以此成员插入 i2c_driver.clients。
看到i2c_client就不得不看i2c设备的注册,这个时候可能有人会说,大概和platform设备注册差不多吧,确实 差不多,可还是有点区别下面就以mma7660为例,
- static struct i2c_board_info i2c_devs3[] __initdata = {
- {
- I2C_BOARD_INFO("mma7660", 0x4c),
- .platform_data = &mma7660_pdata,
- },
- };
展开
- static struct i2c_board_info i2c_devs3[] __initdata = {
- {
- .type = "mma7660",
- .addr = 0x4c,
- .platform_data = &mma7660_pdata,
- },
- };
有没有发现什么不对?不是说i2c设备的结构体说 i2c_client ?咋变成了 i2c_board_info ?platform_device就是对应的啊。这里面肯定有蹊跷,i2c_board_info 最后肯定还得转成成 i2c_client,是的,这既是i2c设备的实例化,从定义的 i2c_board_info 组装成 i2c_client,这个过程将在代码中分析。接下来看一下 platform_device通过 platform_device_register来注册 ,那么 i2c_board_info 设备是通过i2c_register_board_info。
先看下 i2c_board_info 结构体
- struct i2c_board_info {
- char type[I2C_NAME_SIZE];
- unsigned short flags;
- unsigned short addr;
- void *platform_data;
- struct dev_archdata *archdata;
- struct device_node *of_node;
- int irq;
- };
再看一个结构体,下面会用到
- struct i2c_devinfo {
- struct list_head list;
- int busnum;
- struct i2c_board_info board_info;
- };
下面再看 i2c_register_board_info 实例
- i2c_register_board_info(3, i2c_devs3, ARRAY_SIZE(i2c_devs3));
- {
- int status;
-
- down_write(&__i2c_board_lock);
-
- /* dynamic bus numbers will be assigned after the last static one */
- /*__i2c_first_dynamic_bus_num总是要比最大的bus号大1,后面会用到*/
- if (busnum >= __i2c_first_dynamic_bus_num)
- __i2c_first_dynamic_bus_num = busnum + 1;
-
- for (status = 0; len; len--, info++) {
- struct i2c_devinfo *devinfo;
-
- devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
- if (!devinfo) {
- pr_debug("i2c-core: can't register boardinfo!\n");
- status = -ENOMEM;
- break;
- }
- /*将i2c_board_info的信息赋值给 devinfo ,然后将devinfo插入 __i2c_board_list ,所有的i2c设备都会插入
- __i2c_board_list,所以一定要记住__i2c_board_list,
- devinfo->busnum = 3
- devinfo->board_info = &i2c_devs3 */
-
- devinfo->busnum = busnum;
- devinfo->board_info = *info;
- list_add_tail(&devinfo->list, &__i2c_board_list);
- }
-
- up_write(&__i2c_board_lock);
-
- return status;
- }
上面的知识将在下面的代码中用到,下面接着 分析 s3c24xx_i2c_probe,由于是在分析驱动框架,不是分析BSP,
硬件设备千变万化,看芯片手册写或者调试驱动是一个驱动工程师的基本功,所以对于硬件相关的设置将省略。
下面接着分析经过platform总线匹配后调用的 s3c24xx_i2c_probe
- static int s3c24xx_i2c_probe(&s3c_device_i2c3)
- {
- struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
- struct resource *res;
- int ret;
-
- ......
-
- /*赋值 i2c->adap.name = "s3c2410-i2c",后面会用到*/
- strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
- /*i2c->adap进行复制,又看到了 THIS_MODULE 只有当此驱动编译成module的时候指向此模块
- ,如果没有编译成module则为NULL,绝大多数owner都是 THIS_MODULE */
- i2c->adap.owner = THIS_MODULE;
- /*i2c->adap.algo,对应实际硬件的通信方法,控制硬件产生读写时序的函数,adapter是对硬件控制器的抽象,
- 实际产生硬件通信时序的s3c24xx_i2c_algorithm*/
- i2c->adap.algo = &s3c24xx_i2c_algorithm;
- /*通信失败重新尝试的次数*/
- i2c->adap.retries = 2;
- /*此adapter支持的硬件类型,公共也就是三种 I2C_CLASS_HWMON、I2C_CLASS_SPD 和 I2C_CLASS_DDC
- 相应的,i2c_client或者i2c_driver肯定也会有相应的class来和 i2c->adap.class 匹配 */
- i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
- ......
-
- /* setup info block for the i2c core */
-
- i2c->adap.algo_data = i2c;
- /*i2c->adap.dev.parent 指向 s3c_device_i2c1 ,
- 意味着i2c->adap对应的目录会在/sys/devices/platform/s3c2440-i2c.3下*/
- i2c->adap.dev.parent = &pdev->dev;
-
- ......
-
- /* Note, previous versions of the driver used i2c_add_adapter()
- * to add the bus at any number. We now pass the bus number via
- * the platform data, so if unset it will now default to always
- * being bus 0.
- */
- /*此adapter所对应的i2c总线号,这里是通过 paltform_device的私有结构体传递,实际还有另一个代表
- /sys/devices/platform/s3c2440-i2c.3
- i2c总线号的,还记得 s3c_device_i2c3.id = 3吗*/
- i2c->adap.nr = pdata->bus_num;
- /*注册adapter*/
- ret = i2c_add_numbered_adapter(&i2c->adap);
- {
- int id;
- int status;
-
- if (adap->nr & ~MAX_ID_MASK)
- return -EINVAL;
- /*下面涉及idr机制,简单的说idr机制就是将一个整数id和特定指针映射起来,然后可以通过id号
- 找到对应的指针*/
- retry:
- /*为idr分配内存,通过idr获取id之前需要先分配内存*/
- if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
- return -ENOMEM;
-
- mutex_lock(&core_lock);
- /* "above" here means "above or equal to", sigh;
- * we need the "equal to" result to force the result
- */
- /*分配id号并和adap指针关联,对于i2c adapter分配的id就是bus号,所以下面会有
- id和adap->nr的判断*/
- status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
- if (status == 0 && id != adap->nr) {
- status = -EBUSY;
- idr_remove(&i2c_adapter_idr, id);
- }
- mutex_unlock(&core_lock);
- if (status == -EAGAIN)
- goto retry;
-
- if (status == 0)
- /*开始注册adapter*/
- status = i2c_register_adapter(adap);
- {
- int res = 0;
-
- /* Can't register until after driver model init */
- /*i2c被初始化过才能注册adapter,显然注册 i2c_bus_type 就已经初始化过*/
- if (unlikely(WARN_ON(!i2c_bus_type.p))) {
- res = -EAGAIN;
- goto out_list;
- }
-
- /* Sanity checks */
- /*前面已经初始化 adap->name[0] = "s3c2410-i2c" */
- if (unlikely(adap->name[0] == '\0')) {
- pr_err("i2c-core: Attempt to register an adapter with "
- "no name!\n");
- return -EINVAL;
- }
- /*上面已经初始化过 i2c->adap.algo = &s3c24xx_i2c_algorithm */
- if (unlikely(!adap->algo)) {
- pr_err("i2c-core: Attempt to register adapter '%s' with "
- "no algo!\n", adap->name);
- return -EINVAL;
- }
-
- rt_mutex_init(&adap->bus_lock);
- mutex_init(&adap->userspace_clients_lock);
- INIT_LIST_HEAD(&adap->userspace_clients);
-
- /* Set default timeout to 1 second if not already set */
- /*上面没有初始化超时时间,所以默认设置为1s*/
- if (adap->timeout == 0)
- adap->timeout = HZ;
- /*赋值 i2c->adap->dev->kobj->name = "i2c-3",意味着将来创建目录的名字为 i2c-3 */
- dev_set_name(&adap->dev, "i2c-%d", adap->nr);
- /*adapter也是一个device,类型为 i2c_adapter_type ,所以指向i2c_bus_type,其本身提供具体的i2c总线*/
- adap->dev.bus = &i2c_bus_type;
- adap->dev.type = &i2c_adapter_type;
- /*下面即使device的注册过程*/
- res = device_register(&adap->dev);
- {
- device_initialize(dev);
- {
- /*adapter也是一个device,所以其 kobject 属于 devices_kset,类型为 device_ktype*/
- dev->kobj.kset = devices_kset;
- kobject_init(&dev->kobj, &device_ktype);
- INIT_LIST_HEAD(&dev->dma_pools);
- mutex_init(&dev->mutex);
- lockdep_set_novalidate_class(&dev->mutex);
- spin_lock_init(&dev->devres_lock);
- INIT_LIST_HEAD(&dev->devres_head);
- device_pm_init(dev);
- set_dev_node(dev, -1);
- }
- return device_add(dev);
- {
- struct device *parent = NULL;
- struct class_interface *class_intf;
- int error = -EINVAL;
-
- dev = get_device(dev);
- if (!dev)
- goto done;
-
- if (!dev->p) {
- error = device_private_init(dev);
- {
- dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
- if (!dev->p)
- return -ENOMEM;
- dev->p->device = dev;
- klist_init(&dev->p->klist_children, klist_children_get,
- klist_children_put);
- return 0;
- }
- if (error)
- goto done;
- }
-
- /*
- * for statically allocated devices, which should all be converted
- * some day, we need to initialize the name. We prevent reading back
- * the name, and force the use of dev_name()
- */
- /* i2c->adap->dev->init_name未被赋值,也就是为NULL*/
- if (dev->init_name) {
- dev_set_name(dev, "%s", dev->init_name);
- dev->init_name = NULL;
- }
- /* i2c->adap->dev->kobj->name = "i2c-3" ,所以此条件不会被满足 */
- if (!dev_name(dev)) {
- error = -EINVAL;
- goto name_error;
- }
-
- pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
- /*前面初始化 i2c->adap.dev.parent = &pdev->dev */
- parent = get_device(dev->parent);
- /*device层面的parent已经建立,setup_parent要建立kobject 层面 的parent关系*/
- setup_parent(dev, parent);
-
- /* use parent numa_node */
- /*NUMA架构一般应用于大型多CPU的设备,嵌入式单个多核CPU使用SMP架构*/
- if (parent)
- set_dev_node(dev, dev_to_node(parent));
-
- /* first, register with generic layer. */
- /* we require the name to be set before, and pass NULL */
- /*又看见kobject_add,在sys/devices/platform/s3c2440-i2c.3创建i2c-3目录*/
- error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
- if (error)
- goto Error;
-
- /* notify platform of device entry */
- /*暂时不知道具体用途,代码中极少给platform_notify赋值*/
- if (platform_notify)
- platform_notify(dev);
- /*在sys/devices/platform/s3c2440-i2c.1/i2c-3创建uevent文件*/
- error = device_create_file(dev, &uevent_attr);
- if (error)
- goto attrError;
- /*此时 i2c->adap->dev 还没有设备号,所以执行不到,等到 device_creat 的时候再详细分析*/
- if (MAJOR(dev->devt)) {
- error = device_create_file(dev, &devt_attr);
- if (error)
- goto ueventattrError;
-
- error = device_create_sys_dev_entry(dev);
- if (error)
- goto devtattrError;
-
- devtmpfs_create_node(dev);
- }
-
- error = device_add_class_symlinks(dev);
- {
- int error;
- /*i2c->adap->dev->class未赋值,也就是为NULL,在此返回*/
- if (!dev->class)
- return 0;
-
- error = sysfs_create_link(&dev->kobj,
- &dev->class->p->subsys.kobj,
- "subsystem");
- if (error)
- goto out;
-
- if (dev->parent && device_is_not_partition(dev)) {
- error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
- "device");
- if (error)
- goto out_subsys;
- }
-
- #ifdef CONFIG_BLOCK
- /* /sys/block has directories and does not need symlinks */
- if (sysfs_deprecated && dev->class == &block_class)
- return 0;
- #endif
-
- /* link in the class directory pointing to the device */
- error = sysfs_create_link(&dev->class->p->subsys.kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_device;
-
- return 0;
-
- out_device:
- sysfs_remove_link(&dev->kobj, "device");
-
- out_subsys:
- sysfs_remove_link(&dev->kobj, "subsystem");
- out:
- return error;
- }
- if (error)
- goto SymlinkError;
- /*为 i2c->adap 创建属性文件*/
- error = device_add_attrs(dev);
- {
- struct class *class = dev->class;
- const struct device_type *type = dev->type;
- int error;
- /*i2c->adap->dev->class未赋值,也就是为NULL,在此返回*/
- if (class) {
- error = device_add_attributes(dev, class->dev_attrs);
- {
- int error = 0;
- int i;
-
- if (attrs) {
- for (i = 0; attr_name(attrs[i]); i++) {
- error = device_create_file(dev, &attrs[i]);
- if (error)
- break;
- }
- if (error)
- while (--i >= 0)
- device_remove_file(dev, &attrs[i]);
- }
- return error;
- }
- if (error)
- return error;
- error = device_add_bin_attributes(dev, class->dev_bin_attrs);
- if (error)
- goto err_remove_class_attrs;
- }
- /*前面赋值 i2c->adap->dev->type = &i2c_adapter_type,其中
- i2c_adapter_type->groups = i2c_adapter_attr_groups,
- i2c_adapter_attr_groups = &i2c_adapter_attr_group,
- i2c_adapter_attr_group->attr = i2c_adapter_attrs,
- i2c_adapter_attrs[] = {
- &dev_attr_name.attr,
- &dev_attr_new_device.attr,
- &dev_attr_delete_device.attr,
- NULL
- };
- 所以最终将在/sys/devices/platform/s3c2440-i2c.3/i2c-3下创建 name、new_device和delete_device的属性文件*/
- if (type) {
- error = device_add_groups(dev, type->groups);
- {
- int error = 0;
- int i;
-
- if (groups) {
- for (i = 0; groups[i]; i++) {
- error = sysfs_create_group(&dev->kobj, groups[i]);
- if (error) {
- while (--i >= 0)
- sysfs_remove_group(&dev->kobj,
- groups[i]);
- break;
- }
- }
- }
- return error;
- }
- if (error)
- goto err_remove_class_bin_attrs;
- }
- /*i2c->adap->dev->groups也是未初始化的*/
- error = device_add_groups(dev, dev->groups);
- if (error)
- goto err_remove_type_groups;
-
- return 0;
-
- err_remove_type_groups:
- if (type)
- device_remove_groups(dev, type->groups);
- err_remove_class_bin_attrs:
- if (class)
- device_remove_bin_attributes(dev, class->dev_bin_attrs);
- err_remove_class_attrs:
- if (class)
- device_remove_attributes(dev, class->dev_attrs);
-
- return error;
- }
- if (error)
- goto AttrsError;
- /*i2c->adap 添加到 i2c_bus_type*/
- error = bus_add_device(dev);
- {
- struct bus_type *bus = bus_get(dev->bus);
- int error = 0;
-
- if (bus) {
- pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
- error = device_add_attrs(bus, dev);
- {
- int error = 0;
- int i;
- /*由于i2c_bus_type->dev_attrs = NULL,
- 所以不会创建任何属性文件*/
- if (!bus->dev_attrs)
- return 0;
-
- for (i = 0; attr_name(bus->dev_attrs[i]); i++) {
- error = device_create_file(dev, &bus->dev_attrs[i]);
- if (error) {
- while (--i >= 0)
- device_remove_file(dev, &bus->dev_attrs[i]);
- break;
- }
- }
- return error;
- }
- if (error)
- goto out_put;
- /*在注册i2c_bus_type的时候
- priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);
- 下面就是要在/sys/bus/i2c/devices创建指向 /sys/devices/platform/s3c2440-i2c.3/i2c-3
- 的链接,名字也为i2c-3*/
- error = sysfs_create_link(&bus->p->devices_kset->kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_id;
- /*在/sys/devices/platform/s3c2440-i2c.3/i2c-3创建指向/sys/bus/i2c/的链接,
- 名字为subsystem*/
- error = sysfs_create_link(&dev->kobj,
- &dev->bus->p->subsys.kobj, "subsystem");
- if (error)
- goto out_subsys;
- /*将 i2c->adap 加入到i2c_bus_type->p->klist_devices链表中*/
- klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
- }
- return 0;
-
- out_subsys:
- sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
- out_id:
- device_remove_attrs(bus, dev);
- out_put:
- bus_put(dev->bus);
- return error;
- }
- if (error)
- goto BusError;
- /*电源管理相关,后面分析的电源管理机制的时候再详细分析*/
- error = dpm_sysfs_add(dev);
- if (error)
- goto DPMError;
- device_pm_add(dev);
-
- /* Notify clients of device addition. This call must come
- * after dpm_sysf_add() and before kobject_uevent().
- */
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_ADD_DEVICE, dev);
-
- kobject_uevent(&dev->kobj, KOBJ_ADD);
- bus_probe_device(dev);
- {
- struct bus_type *bus = dev->bus;
- int ret;
- /* i2c_bus_type 注册的时候初始化 i2c_bus_type->p->drivers_autoprobe = 1
- /sys/bus/i2c 下的drivers_autoprobe,drivers_autoprobe被置1
- 则自动进行驱动匹配*/
- if (bus && bus->p->drivers_autoprobe) {
- ret = device_attach(dev);
- {
- int ret = 0;
-
- device_lock(dev);
- /*现在只注册 i2c->adap , i2c->adap ->dev->driver = NULL*/
- if (dev->driver) {
- if (klist_node_attached(&dev->p->knode_driver)) {
- ret = 1;
- goto out_unlock;
- }
- ret = device_bind_driver(dev);
- {
- int ret;
-
- ret = driver_sysfs_add(dev);
- if (!ret)
- driver_bound(dev);
- return ret;
- }
- if (ret == 0)
- ret = 1;
- else {
- dev->driver = NULL;
- ret = 0;
- }
- }
- else {
- pm_runtime_get_noresume(dev);
- /*通过 __device_attach 函数逐个匹配 i2c_bus_type->p->klist_drivers成员
- i2c->adap 只是作为一个device注册,没有所谓的驱动,类似 platform_bus 一样,*/
- ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
-
- pm_runtime_put_sync(dev);
- }
- out_unlock:
- device_unlock(dev);
- return ret;
- }
- WARN_ON(ret < 0);
- }
- }
- /*将 i2c->adap 加入 s3c_device_i2c3 的子设备链表*/
- if (parent)
- klist_add_tail(&dev->p->knode_parent,
- &parent->p->klist_children);
-
- /*将 i2c->adap 加入到class的设备链表中,目前 i2c->adap->dev->class = NULL */
- if (dev->class) {
- mutex_lock(&dev->class->p->class_mutex);
- /* tie the class to the device */
- klist_add_tail(&dev->knode_class,
- &dev->class->p->klist_devices);
-
- /* notify any interfaces that the device is here */
- /*暂时还没弄清class_interface的具体用途*/
- list_for_each_entry(class_intf,
- &dev->class->p->class_interfaces, node)
- if (class_intf->add_dev)
- class_intf->add_dev(dev, class_intf);
- mutex_unlock(&dev->class->p->class_mutex);
- }
- done:
- put_device(dev);
- return error;
- DPMError:
- bus_remove_device(dev);
- BusError:
- device_remove_attrs(dev);
- AttrsError:
- device_remove_class_symlinks(dev);
- SymlinkError:
- if (MAJOR(dev->devt))
- devtmpfs_delete_node(dev);
- if (MAJOR(dev->devt))
- device_remove_sys_dev_entry(dev);
- devtattrError:
- if (MAJOR(dev->devt))
- device_remove_file(dev, &devt_attr);
- ueventattrError:
- device_remove_file(dev, &uevent_attr);
- attrError:
- kobject_uevent(&dev->kobj, KOBJ_REMOVE);
- kobject_del(&dev->kobj);
- Error:
- cleanup_device_parent(dev);
- if (parent)
- put_device(parent);
- name_error:
- kfree(dev->p);
- dev->p = NULL;
- goto done;
- }
- }
- if (res)
- goto out_list;
-
- dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
-
- #ifdef CONFIG_I2C_COMPAT
- /* i2c_adapter_compat_class = class_compat_register("i2c-adapter");
- 在 sys/calss/i2c-adapter 目录下创建链接*/
- res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,adap->dev.parent);
- {
- int error;
- /* 在 sys/calss/i2c-adapter 目录下创建链接 指向 sys/devices/platform/s3c2440-i2c.3/i2c-3 的链接,名字仍为 i2c-3 */
- error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));
- {
- return sysfs_do_create_link(kobj, target, name, 1);
- }
- if (error)
- return error;
-
- /*
- * Optionally add a "device" link (typically to the parent), as a
- * class device would have one and we want to provide as much
- * backwards compatibility as possible.
- */
- if (device_link) {
- /*在 sys/devices/platform/s3c2440-i2c.3/i2c-3 目录下创建指向 sys/devices/platform/s3c2440-i2c.3 ,名字为device */
- error = sysfs_create_link(&dev->kobj, &device_link->kobj,
- "device");
- if (error)
- sysfs_remove_link(cls->kobj, dev_name(dev));
- }
-
- return error;
- }
- if (res)
- dev_warn(&adap->dev,
- "Failed to create compatibility class link\n");
- #endif
-
- /* create pre-declared device nodes */
- /* 重点来了, i2c_client 的实例化,由 i2c_board_info 生成 i2c_client */
- if (adap->nr < __i2c_first_dynamic_bus_num)
- i2c_scan_static_board_info(adap);
- {
- struct i2c_devinfo *devinfo;
-
- down_read(&__i2c_board_lock);
- /*还记得前面 i2c_register_board_info 把所有的 i2c_board_info 都注册到 __i2c_board_list ,
- 下面就是遍历 __i2c_board_list ,查找 属于 i2c->adap 的设备 */
- list_for_each_entry(devinfo, &__i2c_board_list, list) {
- if (devinfo->busnum == adapter->nr
- && !i2c_new_device(adapter,&devinfo->board_info))
- {
- /*每查到一个 属于 i2c->adap 的设备 ,声明一个 i2c_client ,这里就以前面注册的 i2c_devs3 为例*/
- struct i2c_client *client;
- int status;
-
- client = kzalloc(sizeof *client, GFP_KERNEL);
- if (!client)
- return NULL;
- /*初始化该 i2c_client 所属的 adap */
- client->adapter = adap;
- /* client->dev.platform_data = i2c_devs3->platform_data = &mma7660_pdata*/
- client->dev.platform_data = info->platform_data;
-
- if (info->archdata)
- client->dev.archdata = *info->archdata;
- /*i2c_devs3->flags 未初始化,也就是为 0 */
- client->flags = info->flags;
- /*client->addr = 0x4c*/
- client->addr = info->addr;
-
- client->irq = info->irq;
- /* client->name = "mma7660" */
- strlcpy(client->name, info->type, sizeof(client->name));
-
- /* Check for address validity */
- /*检查 client->addr 的合法性 */
- 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;
- }
-
- /* Check for address business */
- /*检查 i2c->adap 是否注册过 相同 address 的 i2c 期间,一个 i2c 总线上的器件address 必须唯一*/
- status = i2c_check_addr_busy(adap, client->addr);
- {
- struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
- {
- /*前面初始化 i2c->adap->dev->parent = s3c_device_i2c3->dev,所以 parent = NULL */
- struct device *parent = adapter->dev.parent;
-
- if (parent != NULL && parent->type == &i2c_adapter_type)
- return to_i2c_adapter(parent);
- else
- return NULL;
- }
- int result = 0;
- /* parent = NULL */
- if (parent)
- result = i2c_check_mux_parents(parent, addr);
- {
- struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
- int result;
-
- result = device_for_each_child(&adapter->dev, &addr,
- __i2c_check_addr_busy);
-
- if (!result && parent)
- result = i2c_check_mux_parents(parent, addr);
-
- return result;
- }
-
- if (!result)
- result = device_for_each_child(&adapter->dev, &addr,i2c_check_mux_children);
- {
- struct klist_iter i;
- struct device *child;
- int error = 0;
-
- if (!parent->p)
- return 0;
-
- klist_iter_init(&parent->p->klist_children, &i);
- while ((child = next_device(&i)) && !error)
- error = fn(child, data); //fn = i2c_check_mux_children
- {
- int result;
-
- if (dev->type == &i2c_adapter_type)
- result = device_for_each_child(dev, addrp,
- i2c_check_mux_children);
- else
- /*此地址已经被注册过,则返回 -EBUSY */
- result = __i2c_check_addr_busy(dev, addrp);
- {
- struct i2c_client *client = i2c_verify_client(dev);
- int addr = *(int *)addrp;
-
- if (client && client->addr == addr)
- return -EBUSY;
- return 0;
- }
-
- return result;
- }
- klist_iter_exit(&i);
- return error;
- }
-
- return result;
- }
- if (status)
- goto out_err;
-
- client->dev.parent = &client->adapter->dev;
- client->dev.bus = &i2c_bus_type;
- client->dev.type = &i2c_client_type;
- client->dev.of_node = info->of_node;
- /* client->dev->kobj = "3-004c" */
- dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
- client->addr);
- /*将 client 注册到 /sys/devices/platform/s3c2440-i2c.3/i2c-3 目录下,并创建 3-004c 目录,同事创建
- uevent、name和 new_device 属性文件和一些链接文件等等 */
- 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;
- }
- dev_err(&adapter->dev,
- "Can't create device at 0x%02x\n",
- devinfo->board_info.addr);
- }
- up_read(&__i2c_board_lock);
- }
-
- /* Notify drivers */
- mutex_lock(&core_lock);
- /*遍历已经注册 i2c_driver 和 i2c->adap ,然后探测 i2c->adap 上i2c_driver支持的地址,
- 如果已经注册则跳过,未注册但有该 i2c 设备,则进行实例化*/
- bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
- {
- struct klist_iter i;
- struct device_driver *drv;
- int error = 0;
-
- if (!bus)
- return -EINVAL;
- /*遍历 i2c_bus_type->p->klist_drivers ,然后调用 __process_new_adapter*/
- klist_iter_init_node(&bus->p->klist_drivers, &i,
- start ? &start->p->knode_bus : NULL);
- while ((drv = next_driver(&i)) && !error)
- error = fn(drv, data); //fn = __process_new_adapter
- {
- return i2c_do_add_adapter(to_i2c_driver(d), data);
- {
- /* Detect supported devices on that bus, and instantiate them */
- i2c_detect(adap, driver);
- { /* driver 所支持i2c设备的 address 列表*/
- const unsigned short *address_list;
- /* 声明一个临时的 i2c_client */
- 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;
-
- /* Stop here if the classes do not match */
- if (!(adapter->class & driver->class))
- return 0;
-
- /* Set up a temporary client to help detect callback */
- temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!temp_client)
- return -ENOMEM;
- temp_client->adapter = adapter;
-
- 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);
- {
- struct i2c_board_info info;
- struct i2c_adapter *adapter = temp_client->adapter;
- int addr = temp_client->addr;
- int err;
-
- /* Make sure the address is valid */
- err = i2c_check_addr_validity(addr);
- {
- /*
- * Reserved addresses per I2C specification:
- * 0x00 General call address / START byte
- * 0x01 CBUS address
- * 0x02 Reserved for different bus format
- * 0x03 Reserved for future purposes
- * 0x04-0x07 Hs-mode master code
- * 0x78-0x7b 10-bit slave addressing
- * 0x7c-0x7f Reserved for future purposes
- */
- if (addr < 0x08 || addr > 0x77)
- return -EINVAL;
- return 0;
- }
- if (err) {
- dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
- addr);
- return err;
- }
-
- /* Skip if already in use */
- if (i2c_check_addr_busy(adapter, addr))
- {
- /*前面初始化 i2c->adap->dev->parent = s3c_device_i2c3->dev,所以 parent = NULL */
- struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
- int result = 0;
-
- if (parent)
- result = i2c_check_mux_parents(parent, addr);
-
- if (!result)
- result = device_for_each_child(&adapter->dev, &addr,i2c_check_mux_children);
- {
- struct klist_iter i;
- struct device *child;
- int error = 0;
-
- if (!parent->p)
- return 0;
- /*遍历 查询 i2c->adap 下的设备,也就是 i2c _client */
- klist_iter_init(&parent, &i);
- while ((child = next_device(&i)) && !error)
- error = fn(child, data); //fn = i2c_check_mux_children
- {
- int result;
- if (dev->type == &i2c_adapter_type)
- result = device_for_each_child(dev, addrp,i2c_check_mux_children);
-
- else
- result = __i2c_check_addr_busy(dev, addrp);
- {
- struct i2c_client *client = i2c_verify_client(dev);
- int addr = *(int *)addrp;
-
- if (client && client->addr == addr)
- return -EBUSY;
- return 0;
- }
-
- return result;
- }
- klist_iter_exit(&i);
- return error;
- }
- return result;
- }
- return 0;
-
- /* Make sure there is something at this address */
- if (!i2c_default_probe(adapter, 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;
- }
- return 0;
-
- /* Finally call the custom detection function */
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = addr;
- err = driver->detect(temp_client, &info);
- if (err) {
- /* -ENODEV is returned if the detection fails. We catch it
- here as this isn't an error. */
- return err == -ENODEV ? 0 : err;
- }
-
- /* Consistency check */
- 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;
-
- /* Detection succeeded, instantiate the device */
- dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
- info.type, info.addr);
- /*驱动探测到 adapter 上有该驱动支持的设备但又未 注册 ,则给其实例化*/
- 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;
- }
- if (unlikely(err))
- break;
- }
-
- kfree(temp_client);
- return err;
- }
-
- /* Let legacy drivers scan this bus for matching devices */
- if (driver->attach_adapter) {
- dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
- driver->driver.name);
- dev_warn(&adap->dev, "Please use another way to instantiate "
- "your i2c_client\n");
- /* We ignore the return code; if it fails, too bad */
- driver->attach_adapter(adap);
- }
- return 0;
- }
- }
- klist_iter_exit(&i);
- return error;
- }
- 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;
- }
- return status;
- }
-
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to add bus to i2c core\n");
- goto err_cpufreq;
- }
-
- platform_set_drvdata(pdev, i2c);
-
- dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
- clk_disable(i2c->clk);
- return 0;
-
- err_cpufreq:
- s3c24xx_i2c_deregister_cpufreq(i2c);
-
- err_irq:
- free_irq(i2c->irq, i2c);
-
- err_iomap:
- iounmap(i2c->regs);
-
- err_ioarea:
- release_resource(i2c->ioarea);
- kfree(i2c->ioarea);
-
- err_clk:
- clk_disable(i2c->clk);
- clk_put(i2c->clk);
-
- err_noclk:
- kfree(i2c);
- return ret;
- }
上面的主要工作,就是初始化 i2c_adapter 的硬件,并且注册 i2c_adapter ,注册后将会 遍历 __i2c_board_list 查找挂接在此 i2c_adapter 的 i2c_board_info ,然后实例化 成 i2c_client ,然后 遍历 已经注册的 i2c_driver ,查找此 i2c_adapter 是否有 已经注册的 i2c_driver 支持的 但又未 注册的 i2c 器件,查到后实例化成 i2c_client 并注册。
整个过程下来,仍然会生成一些 属性文件 和一些链接文件,下面关注 new_device 和 delete_device 两个属性文件。
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
展开
- struct device_attribute dev_attr_new_device = {
- .attr = {
- .name = "new_device",
- .mode = S_IWUSR
- },
- .show = NULL,
- .store = i2c_sysfs_new_device,
- }
看store函数
- static ssize_t i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
- {
- struct i2c_adapter *adap = to_i2c_adapter(dev);
- struct i2c_board_info info;
- struct i2c_client *client;
- char *blank, end;
- int res;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
-
- blank = strchr(buf, ' ');
- if (!blank) {
- dev_err(dev, "%s: Missing parameters\n", "new_device");
- return -EINVAL;
- }
- if (blank - buf > I2C_NAME_SIZE - 1) {
- dev_err(dev, "%s: Invalid device name\n", "new_device");
- return -EINVAL;
- }
- memcpy(info.type, buf, blank - buf);
-
- /* Parse remaining parameters, reject extra parameters */
- res = sscanf(++blank, "%hi%c", &info.addr, &end);
- if (res < 1) {
- dev_err(dev, "%s: Can't parse I2C address\n", "new_device");
- return -EINVAL;
- }
- if (res > 1 && end != '\n') {
- dev_err(dev, "%s: Extra parameters\n", "new_device");
- return -EINVAL;
- }
-
- client = i2c_new_device(adap, &info);
- if (!client)
- return -EINVAL;
-
- /* Keep track of the added device */
- mutex_lock(&adap->userspace_clients_lock);
- list_add_tail(&client->detected, &adap->userspace_clients);
- mutex_unlock(&adap->userspace_clients_lock);
- dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
- info.type, info.addr);
-
- return count;
- }
new_device 文件就是为用户空间提供 一种添加 i2c 设备的接口,在不管 系统没有注册 此设备的时候 ,可以尝试通过 new_device 写入 i2c 期间的名字 添加该设备。
static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);
展开
- struct device_attribute dev_attr_delete_device = {
- .attr = {
- .name = "delete_device",
- .mode = S_IWUSR
- },
- .show = NULL,
- .store = i2c_sysfs_delete_device,
- }
查看 store 函数
- static ssize_t i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
- {
- struct i2c_adapter *adap = to_i2c_adapter(dev);
- struct i2c_client *client, *next;
- unsigned short addr;
- char end;
- int res;
-
- /* Parse parameters, reject extra parameters */
- res = sscanf(buf, "%hi%c", &addr, &end);
- if (res < 1) {
- dev_err(dev, "%s: Can't parse I2C address\n", "delete_device");
- return -EINVAL;
- }
- if (res > 1 && end != '\n') {
- dev_err(dev, "%s: Extra parameters\n", "delete_device");
- return -EINVAL;
- }
-
- /* Make sure the device was added through sysfs */
- res = -ENOENT;
- mutex_lock(&adap->userspace_clients_lock);
- list_for_each_entry_safe(client, next, &adap->userspace_clients,
- detected) {
- if (client->addr == addr) {
- dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
- "delete_device", client->name, client->addr);
-
- list_del(&client->detected);
- i2c_unregister_device(client);
- res = count;
- break;
- }
- }
- mutex_unlock(&adap->userspace_clients_lock);
-
- if (res < 0)
- dev_err(dev, "%s: Can't find device in list\n",
- "delete_device");
- return res;
- }
二、i2c_driver的注册
上面的代码已经 实例化 i2c_client 并且注册,下面就该注册相应的 i2c_driver ,然后通过 i2c_bus_type 的 match 调用 到
i2c_driver->probe 即 mma7660_probe 。
- struct i2c_driver {
- unsigned int class;
-
- /* Notifies the driver that a new bus has appeared or is about to be
- * removed. You should avoid using this, it will be removed in a
- * near future.
- */
- int (*attach_adapter)(struct i2c_adapter *) __deprecated;
- int (*detach_adapter)(struct i2c_adapter *) __deprecated;
-
- /* Standard driver model interfaces */
- int (*probe)(struct i2c_client *, const struct i2c_device_id *);
- int (*remove)(struct i2c_client *);
-
- /* driver model interfaces that don't relate to enumeration */
- void (*shutdown)(struct i2c_client *);
- int (*suspend)(struct i2c_client *, pm_message_t mesg);
- int (*resume)(struct i2c_client *);
-
- /* Alert callback, for example for the SMBus alert protocol.
- * The format and meaning of the data value depends on the protocol.
- * For the SMBus alert protocol, there is a single bit of data passed
- * as the alert response's low bit ("event flag").
- */
- void (*alert)(struct i2c_client *, unsigned int data);
-
- /* a ioctl like command that can be used to perform specific functions
- * with the device.
- */
- int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
-
- struct device_driver driver;
- const struct i2c_device_id *id_table;
-
- /* Device detection callback for automatic device creation */
- int (*detect)(struct i2c_client *, struct i2c_board_info *);
- const unsigned short *address_list;
- struct list_head clients;
- };
unsigned int class;
此driver支持的i2c设备种类,用来和i2c_adapter中的class做对比,共三种类型I2C_CLASS_HWMON、I2C_CLASS_SPD 和 I2C_CLASS_DDC
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
i2c 总线增加设备时的回调函数,已经弃用
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
i2c 总线移除设备时的回调函数,已经弃用
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;
此driver兼容的设备列表,i2c_device_match 时会调用
int (*detect)(struct i2c_client *, struct i2c_board_info *);
发现设备时的回调函数,在代码中发现 未注册 设备时会调用
const unsigned short *address_list;
此驱动支持的地址列表,注册 i2c_adapter 或 i2c_driver 时会调用此 列表判断
i2c_adapter 是否挂有此驱动兼容的设备
struct list_head clients;
遍历驱动发现的i2c设备将挂入此链表中,和i2c_client的detected对应
上面以 mma7660 为例实例化的 i2c_client,下面还以 mma7660 为例分析 i2c_driver 的注册
i2c_mma7660_driver的定义
- static struct i2c_driver i2c_mma7660_driver = {
- .driver = {
- .name = MMA7660_NAME,
- },
-
- .probe = mma7660_probe,
- .remove = __devexit_p(mma7660_remove),
- .suspend = mma7660_suspend,
- .resume = mma7660_resume,
- .id_table = mma7660_ids,
- };
其中的 id_table 为
- static const struct i2c_device_id mma7660_ids[] = {
- { "mma7660", 0 },
- { },
- };
下面就看 i2c_mma7660_driver 的注册过程。
- static int __init init_mma7660(void)
- {
- int ret;
-
- ret = i2c_add_driver(&i2c_mma7660_driver);
- {
- return i2c_register_driver(THIS_MODULE, driver);
- {
- int res;
-
- /* Can't register until after driver model init */
- if (unlikely(WARN_ON(!i2c_bus_type.p)))
- return -EAGAIN;
-
- /* add the driver to the list of i2c drivers in the driver core */
- driver->driver.owner = owner;
- driver->driver.bus = &i2c_bus_type;
-
- /* When registration returns, the driver core
- * will have called probe() for all matching-but-unbound devices.
- */
- res = driver_register(&driver->driver);
- {
- int ret;
- struct device_driver *other;
-
- BUG_ON(!drv->bus->p);
- /* i2c_bus_type->probe = i2c_device_probe
- i2c_bus_type->remove = i2c_device_remove
- i2c_bus_type->shutdown = i2c_device_shutdown
- i2c_mma7660_driver->driver->probe = NULL
- i2c_mma7660_driver->driver->remove = NULL
- i2c_mma7660_driver->driver->shutdown = NULL*/
- if ((drv->bus->probe && drv->probe) ||
- (drv->bus->remove && drv->remove) ||
- (drv->bus->shutdown && drv->shutdown))
- printk(KERN_WARNING "Driver '%s' needs updating - please use "
- "bus_type methods\n", drv->name);
- /*通过name查找是否已经注册过此driver*/
- other = driver_find(drv->name, drv->bus);
- {
- struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
- struct driver_private *priv;
-
- if (k) {
- priv = to_driver(k);
- return priv->driver;
- }
- return NULL;
- }
- if (other) {
- put_driver(other);
- printk(KERN_ERR "Error: Driver '%s' is already registered, "
- "aborting...\n", drv->name);
- return -EBUSY;
- }
-
- ret = bus_add_driver(drv);
- {
- struct bus_type *bus;
- struct driver_private *priv;
- int error = 0;
- /*前面初始化 i2c_mma7660_driver->driver.bus = &i2c_bus_type*/
- bus = bus_get(drv->bus);
- if (!bus)
- return -EINVAL;
-
- pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
-
- /*为 i2c_mma7660_driver->driver申请私有结构体*/
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- error = -ENOMEM;
- goto out_put_bus;
- }
- klist_init(&priv->klist_devices, NULL, NULL);
- priv->driver = drv;
- drv->p = priv;
- /*i2c_mma7660_driver->driver->priv->kobj.kset = i2c_bus_type->p->drivers_kset
- 还记得sys/bus/i2c 下的drivers目录吗
- priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);*/
- priv->kobj.kset = bus->p->drivers_kset;
- /*在sys/bus/i2c/drivers目录下创建 mma7660 的目录*/
- error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
- "%s", drv->name);
- if (error)
- goto out_unregister;
- /*又见到了drivers_autoprobe,还记得/sys/bus/i2c 下的drivers_autoprobe文件吗
- i2c_type->p->drivers_autoprobe = 1,自动匹配device和driver*/
- if (drv->bus->p->drivers_autoprobe) {
- error = driver_attach(drv);
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- {
- struct klist_iter i;
- struct device *dev;
- int error = 0;
-
- if (!bus || !bus->p)
- return -EINVAL;
- /*device 注册的时候就是挂接到 i2c_bus_type->p->klist_devices
- 下面就是遍历 i2c_bus_type 的device 链表,然后通过__driver_attach和driver
- 做匹配*/
-
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)) && !error)
- error = fn(dev, data); //fn = __driver_attach
- {
- struct device_driver *drv = data;
-
- /*
- * Lock device and try to bind to it. We drop the error
- * here and always return 0, because we need to keep trying
- * to bind to devices and some drivers will return an error
- * simply if it didn't support the device.
- *
- * driver_probe_device() will spit a warning if there
- * is an error.
- */
-
- if (!driver_match_device(drv, dev))
- { /* drv->bus->match = i2c_device_match ,最终还是调用到 i2c 总线 的match函数
- 最终会匹配到 mma7660 的 i2c_client*/
- return drv->bus->match ? drv->bus->match(dev, drv) : 1;
- }
- return 0;
- /*dev = client->dev
- client->dev.parent = &client->adapter->dev */
-
- if (dev->parent) /* Needed for USB */
- device_lock(dev->parent);
- device_lock(dev);
- /*虽然device和driver都找到了,可还未将driver赋值给device呢*/
- if (!dev->driver)
- driver_probe_device(drv, dev);
- {
- int ret = 0;
-
- if (!device_is_registered(dev))
- return -ENODEV;
-
- pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
-
- pm_runtime_get_noresume(dev);
- pm_runtime_barrier(dev);
- ret = really_probe(dev, drv);
- {
- int ret = 0;
-
- atomic_inc(&probe_count);
- pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
- drv->bus->name, __func__, drv->name, dev_name(dev));
- WARN_ON(!list_empty(&dev->devres_head));
- /*这里才将driver和device关联起来*/
- dev->driver = drv;
- if (driver_sysfs_add(dev)) {
- {
- int ret;
-
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_BIND_DRIVER, dev);
- /*sys/bus/i2c/drivers/mma7660创建指向 3-004c 的链接,
- 名字仍然为 3-004c */
- ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
- kobject_name(&dev->kobj));
- if (ret == 0) {
- /*在 /sys/devices/platform/s3c2440-i2c.3/i2c-3/3-004c/ 下
- 创建指向 /sys/bus/i2c/drivers/mma7660 的链接,名字为driver*/
- ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
- "driver");
- if (ret)
- sysfs_remove_link(&dev->driver->p->kobj,
- kobject_name(&dev->kobj));
- }
- return ret;
- }
- printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
- __func__, dev_name(dev));
- goto probe_failed;
- }
- /*i2c_bus_type->probe = i2c_device_probe ,所以调用
- i2c_device_probe ,最终调用 mma7660_probe*/
- if (dev->bus->probe) {
- ret = dev->bus->probe(dev);
- if (ret)
- goto probe_failed;
- } else if (drv->probe) {
- ret = drv->probe(dev);
- if (ret)
- goto probe_failed;
- }
-
- driver_bound(dev);
- { /*前面 client->dev->p->knode_driver未初始化,也就是为NULL*/
- if (klist_node_attached(&dev->p->knode_driver)) {
- printk(KERN_WARNING "%s: device %s already bound\n",
- __func__, kobject_name(&dev->kobj));
- return;
- }
-
- pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
- __func__, dev->driver->name);
- /*将device挂接在driver的设备链表中*/
- klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
-
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_BOUND_DRIVER, dev);
- }
- ret = 1;
- pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- goto done;
-
- probe_failed:
- devres_release_all(dev);
- driver_sysfs_remove(dev);
- dev->driver = NULL;
-
- if (ret != -ENODEV && ret != -ENXIO) {
- /* driver matched but the probe failed */
- printk(KERN_WARNING
- "%s: probe of %s failed with error %d\n",
- drv->name, dev_name(dev), ret);
- }
- /*
- * Ignore errors returned by ->probe so that the next driver can try
- * its luck.
- */
- ret = 0;
- done:
- atomic_dec(&probe_count);
- wake_up(&probe_waitqueue);
- return ret;
- }
- pm_runtime_put_sync(dev);
-
- return ret;
- }
- device_unlock(dev);
- if (dev->parent)
- device_unlock(dev->parent);
-
- return 0;
- }
- klist_iter_exit(&i);
- return error;
- }
- }
- if (error)
- goto out_unregister;
- }
- /*将 i2c_mma7660_driver 挂接在 i2c_bus_type 的driver链表中*/
- klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
- module_add_driver(drv->owner, drv);
- /*在/sys/bus/i2c/drivers/mma7660 创建 uevent 文件*/
- error = driver_create_file(drv, &driver_attr_uevent);
- if (error) {
- printk(KERN_ERR "%s: uevent attr (%s) failed\n",
- __func__, drv->name);
- }
- /*为 i2c_mma7660_driver 创建默认的属性文件,可是i2c_bus_type->drv_attrs = NULL,所以此例中没有
- 没有创建任何文件*/
- error = driver_add_attrs(bus, drv);
- {
- int error = 0;
- int i;
-
- if (bus->drv_attrs) {
- for (i = 0; attr_name(bus->drv_attrs[i]); i++) {
- error = driver_create_file(drv, &bus->drv_attrs[i]);
- if (error)
- goto err;
- }
- }
- done:
- return error;
- err:
- while (--i >= 0)
- driver_remove_file(drv, &bus->drv_attrs[i]);
- goto done;
- }
- if (error) {
- /* How the hell do we get out of this pickle? Give up */
- printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
- __func__, drv->name);
- }
- /*i2c_mma7660_driver->drv->suppress_bind_attrs 未赋值,也就是为0*/
- if (!drv->suppress_bind_attrs) {
- error = add_bind_files(drv);
- {
- int ret;
- /*在/sys/bus/i2c/drivers/mma7660 目录下创建 ubind 文件*/
- ret = driver_create_file(drv, &driver_attr_unbind);
- if (ret == 0) {
- /*在/sys/bus/i2c/drivers/mma7660目录下创建 bind 文件*/
- ret = driver_create_file(drv, &driver_attr_bind);
- if (ret)
- driver_remove_file(drv, &driver_attr_unbind);
- }
- return ret;
- }
- if (error) {
- /* Ditto */
- printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
- __func__, drv->name);
- }
- }
- /*发送 KOBJ_ADD */
- kobject_uevent(&priv->kobj, KOBJ_ADD);
- return 0;
-
- out_unregister:
- kobject_put(&priv->kobj);
- kfree(drv->p);
- drv->p = NULL;
- out_put_bus:
- bus_put(bus);
- return error;
- }
- if (ret)
- return ret;
- ret = driver_add_groups(drv, drv->groups);
- if (ret)
- bus_remove_driver(drv);
- return ret;
- }
- if (res)
- return res;
-
- /* Drivers should switch to dev_pm_ops instead. */
- if (driver->suspend)
- pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
- driver->driver.name);
- if (driver->resume)
- pr_warn("i2c-core: driver [%s] using legacy resume method\n",
- driver->driver.name);
-
- pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
-
- INIT_LIST_HEAD(&driver->clients);
- /* Walk the adapters that are already present */
- i2c_for_each_dev(driver, __process_new_driver);
- {
- int res;
-
- mutex_lock(&core_lock);
- res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
- {
- struct klist_iter i;
- struct device *dev;
- int error = 0;
-
- if (!bus || !bus->p)
- return -EINVAL;
-
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)) && !error)
- error = fn(dev, data); //fn = __process_new_driver
- {
- if (dev->type != &i2c_adapter_type)
- return 0;
- return i2c_do_add_adapter(data, to_i2c_adapter(dev));
- {
- /* Detect supported devices on that bus, and instantiate them */
- i2c_detect(adap, driver);
- { /* driver 所支持i2c设备的 address 列表*/
- const unsigned short *address_list;
- /* 声明一个临时的 i2c_client */
- struct i2c_client *temp_client;
- int i, err = 0;
- int adap_id = i2c_adapter_id(adapter);
- /* i2c_mma7660_driver->address_list 未赋值,也就是为 NULL 函数在此返回*/
- address_list = driver->address_list;
- if (!driver->detect || !address_list)
- return 0;
-
- ......
-
- kfree(temp_client);
- return err;
- }
-
- /* Let legacy drivers scan this bus for matching devices */
- if (driver->attach_adapter) {
- dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
- driver->driver.name);
- dev_warn(&adap->dev, "Please use another way to instantiate "
- "your i2c_client\n");
- /* We ignore the return code; if it fails, too bad */
- driver->attach_adapter(adap);
- }
- return 0;
- }
- }
- klist_iter_exit(&i);
- return error;
- }
- mutex_unlock(&core_lock);
-
- return res;
- }
-
- return 0;
- }
- }
- printk(KERN_INFO "MMA7660 sensor driver registered.\n");
-
- return ret;
- }
从上面的代码中可以看出,其实和platform的那一套机制是一回事,驱动框架完成了大量的工作,然后实际驱动只需要注册device和driver,驱动框架就会调用到probe函数,开始真正的硬件初始化相关的工作。
三、i2c-dev
不是说 linux 秉承一切皆文件吗(网络设备除外),驱动不是应该完成open、close、read和write等函数吗?到目前为止不管platform还是i2c都没有这些信息。不要忘了,bus、device和driver驱动模型,只是驱动的一种框架,并没有完成任何实质性工作,真正的注册char、block还是网络设备都是开发者自己的事,驱动模型不是最终保证会调用到probe函数吗,对的,你可以在probe函数中完成实际的设备驱动注册,如 register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops),驱动模型只是套了个架子而已,实际驱动工作还得自己做。
对于实际的 i2c 器件,只是将 i2c 作为通信通道而已,再简单的说实际设备功能并不关心是通过spi还是 i2c 进行,比如驱动中的 read 函数,按顺序读写寄存器,并不关心 是 i2c还是spi甚至usb,只要进行数据传输达到目的就行。对于 i2c 器件一般会是input、rtc或者misc,文件系统的接口自然由 input、rtc或misc模型提供,当然你也可以专门为此期间写一个char类型的驱动,完成open、close、read和write等函数,但对于i2c部分,只是利用 i2c_client->adapter->i2c_algorithm->master_xfer和器件进行数据交互。
下面看一下i2c-dev,linux提供的 i2c-dev 并不是实际存在具体的一个i2c器件,而是代表一个i2c总线的设备,可以通过 i2c-dev 提供的 read、write和ioctl来访问总线上的 i2c 器件(根据指定的器件地址)。由于不同的 i2c 器件的 read 和 write 实现各不相同,所以linux提供的 i2c-dev 并不是很通用。
下面简单看一下i2c-dev的注册过程
- 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,
- };
- static int __init i2c_dev_init(void)
- {
- int res;
-
- printk(KERN_INFO "i2c /dev entries driver\n");
- /*注册为char型设备,主设备号为 I2C_MAJOR(123)(终于见到设备号了) */
- 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;
- }
-
- /* 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:
- 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;
- }
其中 i2cdev_attach_adapter中会创建设备文件,也就是在/dev/目录下创建 i2c- %d的文件,如 i2c-3 就代表第三个i2c 上的设备或者说i2c_adapter,可通过/dev/i2c-3 来访问该总线上设备(如open、close、read和write等)。
- static int i2cdev_attach_adapter(struct device *dev, void *dummy)
- {
- ......
-
- /* register this i2c device with the driver core */
- /*创建设备文件,这回 是带 设备号的,所以也会在 /sys/dev/目录下创建文件,如i2c-3*/
- i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
- MKDEV(I2C_MAJOR, adap->nr), NULL,
- "i2c-%d", adap->nr);
- ......
- return res;
- }
上面就是i2c驱动框架的大致内容了,整体来说还是比较复杂的,不过从代码一点一点分析,还是可以理解的,但是鉴于个人的水平有限,可能会出现理解错误的地方,欢迎交流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。