i2c设备注册(动态注册和静态注册)以及i2c驱动注册过程

一、i2c设备驱动注册
i2c设备驱动注册的调用流程为:xxx_init(设备驱动里所定义)—–>i2c_add_driver—–>i2c_register_driver
下面直接看源码,xxx_init(设备驱动里所定义)源码如下:

static int egalax_i2c_ts_init(void)
{
    int result;

    result = misc_register(&egalax_misc_dev);
    if(result) 
    {
        EGALAX_DBG(DBG_MODULE, " misc device register failed\n");
        goto fail;
    }

    p_char_dev = setup_chardev(); // allocate the character device
    if(!p_char_dev) 
    {
        result = -ENOMEM;
        goto fail;
    }

    dbgProcFile = proc_create(PROC_FS_NAME, S_IRUGO|S_IWUGO, NULL, &egalax_proc_fops);
    if (dbgProcFile == NULL) 
    {
        remove_proc_entry(PROC_FS_NAME, NULL);
        EGALAX_DBG(DBG_MODULE, " Could not initialize /proc/%s\n", PROC_FS_NAME);
    }

    EGALAX_DBG(DBG_MODULE, " Driver init done!\n");

    return i2c_add_driver(&egalax_i2c_driver);//调用i2c_add_driver函数


fail:   
    egalax_i2c_ts_exit();
    return result;
}

module_init(egalax_i2c_ts_init);//定义驱动的init函数,也是加载驱动的时候第一个执行的函数

i2c_add_driver函数又会调用i2c_register_driver函数。下面看i2c_register_driver函数的源码:

int i2c_register_driver(struct module *owner, struct i2c_driver *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;//设定这个driver说依附的总线

    /* When registration returns, the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
    res = driver_register(&driver->driver);//向i2c_bus注册driver
    /*注意这里调用的driver_register函数,这个函数又会调用其他函数对driver和device进行匹配(其中的匹配过程会在下一篇中详细接收),若匹配成功,i2c_register_driver返回res,结束执行;如不成功,则会往下执行i2c_for_each_dev函数(这个函数接下来调用的其他函数会动态注册i2c设备,这个动态注册设备的过程会在下面介绍);
    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);//当driver_register中device和driver匹配不成功的时候,会执行此函数;

    return 0;
}

至此设备驱动的注册就完成了。

二、i2c的设备注册

1.静态注册

  • 在没有出现dts之前,linux会将一些板级信息写在arch/arm下和你的板所匹配的c文件里,一般定义一个struct i2c_board_info结构体,将i2c的地址以及i2c的名称信息写入到此结构体中。如:
static struct i2c_board_info i2c_ina219_devs[] __initdata = {
    { I2C_BOARD_INFO("egalax_i2c", 0x2a), },
    }
然后在同一c文件里调用i2c_register_board_info函数将设备信息加入到内核的设备链表里,i2c_register_board_info源码如下(源码位于driver/i2c/i2c-boardinfo.c),
int __init i2c_register_board_info(int busnum,
    struct i2c_board_info const *info, unsigned len)
{
    int status;

    down_write(&__i2c_board_lock);

    /* dynamic bus numbers will be assigned after the last static one */
    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;
        }

        devinfo->busnum = busnum;
        devinfo->board_info = *info;
        list_add_tail(&devinfo->list, &__i2c_board_list);//将设备信息添加到__i2c_board_tail设备链表中
    }

    up_write(&__i2c_board_lock);

    return status;
}
函数第一个参数表示添加的i2c总线的组号,第二个参数是要注册的i2c_board_info的结构体指针,第三个参数是指i2c_board_info中成员的个数。
  • 在出现了dts之后,为了去耦合,将这些的板级信息全部都定义在设备树中,在移植的时候只要修改设备树的硬件信息即可,设备树信息位于arch/arm/boots/dts中,以i2c设备为例,其在设备树中的信息如下图:
    这里写图片描述
    此图为在第二组i2c总线下挂接的4个硬件设备信息;
    linux在启动uboot的时候,会自动展开dts上的硬件信息,自动调用i2c_register_board_info函数将设备注册进设备链表中。现在多数的平台都采用dts的方法在定义这些板级的信息;
  • 将硬件设备添加到设备链表之后,现在就分析内核是怎么向i2c总线动态注册这些设备的,下面区分几个概念:
    i2c的硬件设备在内核中表示为:i2c_client;
    i2c的设备驱动程序在内核中表示为:i2c_driver;
    每一组i2c总线对应一个i2c控制器,即i2c_adapter;
    系统中对i2c_client的注册是在i2c_adapter的注册过程中完成的:
    i2c_add_numbered_adapter()函数在注册I2C_adapter驱动的同时会扫描前面提到的I2C设备链表__i2c_board_list,如果该总线上有对应的I2C设备,则创建相应的i2c_client,并将其注册到I2C core中。调用流程如下所示:
    i2c_add_numbered_adapter—–>i2c_register_adapter—–>i2c_scan_static_board_info—–>i2c_new_device—–>i2c_attach_client—–>device_register;
    下面分析源码(源码位置位于driver/i2c/i2c-core.c):
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
    int id;
    int status;

    if (adap->nr == -1) /* -1 means dynamically assign bus id */i2c总线的组有动态分配和静态之分,当adap->nr == -1的时候为静态注册;
        return i2c_add_adapter(adap);
    if (adap->nr & ~MAX_ID_MASK)
        return -EINVAL;

retry:
    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
     */
    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)
        status = i2c_register_adapter(adap);//调用i2c_register_adapter
    return status;
}

下面看i2c_register_adapter函数源码:
static int i2c_register_adapter(struct i2c_adapter *adap)
{
    int res = 0;

    /* Can't register until after driver model init */
    if (unlikely(WARN_ON(!i2c_bus_type.p))) {
        res = -EAGAIN;
        goto out_list;
    }

    /* Sanity checks */
    if (unlikely(adap->name[0] == '\0')) {
        pr_err("i2c-core: Attempt to register an adapter with "
               "no name!\n");
        return -EINVAL;
    }
    if (unlikely(!adap->algo)) {
        pr_err("i2c-core: Attempt to register adapter '%s' with "
               "no algo!\n", adap->name);
        return -EINVAL;
    }
    //这里的likely()指很大几率执行if分支的代码,编译时提醒编译器放在前面;unlikel指很大几率执行else分支的代码,编译时提醒编译器将else的代码放在前面。这样做为了提高cpu的运行速率;
    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 */
    if (adap->timeout == 0)
        adap->timeout = HZ;

    dev_set_name(&adap->dev, "i2c-%d", adap->nr);//设置 adap->dev.kobj.name 为 i2c-0 ,它将出现在 sysfs 中
    adap->dev.bus = &i2c_bus_type;       //此设备依附的总线类型是i2c总线类型;
    adap->dev.type = &i2c_adapter_type;  //此设备的设备类型是i2c_adapter;
    res = device_register(&adap->dev);  //注册i2c_adapter设备;
    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

    /* create pre-declared device nodes */
    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);//扫描设备链表上的所有硬件设备

    /* Notify drivers */
    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;
}
i2c_register_adapter这个函数主要做两件事:
1.注册自己的i2c_adapter设备;
2.调用i2c_scan_static_board_info函数,创建与i2c_adapter总线号相同的其他设备;

下面主要看i2c_scan_static_board_info函数,源码如下:
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) { //遍历__i2c_board_list,并以此取出每个设备
        if (devinfo->busnum == adapter->nr//adapter->nr==0,当取出设备的busnum==0时,调用i2c_new_device函数;
                && !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);
}


下面来看i2c_new_device函数。源码如下:
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    printk("********************wbx:i2c_new_device begin\n"); 
    struct i2c_client   *client;
    int         status;

    client = kzalloc(sizeof *client, GFP_KERNEL);
    if (!client)
        return NULL;

    client->adapter = adap; //要创建的i2c_client依附到当前的i2c_adapter控制器上
    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;//中断编号默认为0,在启动设备中通过gpio_to_irq会对irq重新赋值

    strlcpy(client->name, info->type, sizeof(client->name));//对i2c_client的名字进行赋值,名字很重要,靠这个名字和驱动的id_table进行匹配

    /* Check for address validity */
    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 */
    status = i2c_check_addr_busy(adap, client->addr);
    if (status)
        goto out_err;

    client->dev.parent = &client->adapter->dev;//设置其父设备为:i2c_adapter设备
    client->dev.bus = &i2c_bus_type; //设备其依附的总线
    client->dev.type = &i2c_client_type;//其属性设置为设备属性
    client->dev.of_node = info->of_node;

    /* For 10-bit clients, add an arbitrary offset to avoid collisions */
    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
             client->addr | ((client->flags & I2C_CLIENT_TEN)
                     ? 0xa000 : 0));
    status = device_register(&client->dev);//注册这个设备
    if (status)
        goto out_err;

    printk("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;
}
此函数主要调用device_register函数来进行设备注册;

到这里静态注册设备的分析以及完成了,通过i2c_board_info这个结构体或者dts来配置硬件信息,再通过调用i2c_register_board_info函数来向设备链表来注册硬件设备,然后系统在注册i2c_adapter完成后,取出链表中的每一个设备,当设备的i2c的编号和i2c_adapter相同时,在调用device_register对此设备进行注册;

二、动态注册(以i2c设备为例)
另外还有一种设备注册的方法是动态注册,这种动态注册方法需要配合设备驱动程序进行,如果没有第三方的的配置文件的话,要将部分硬件信息写到设备驱动中,这里以eeti公司的egalax_i2c.c触摸屏设备驱动程序移植到全志R16平台上例:
前面以及分析过了i2c_register_driver这个函数,在i2c_register_driver函数中会调用driver_register函数,若此函数不能正确的匹配dirver和device,则向下执行i2c_for_each_dev这个函数;下面就以i2c_for_each_dev这个函数为入口点,来看设备是怎么进行动态注册的。
先看i2c_register_driver源码(driver/i2c/i2c-core.c):

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res;
    printk("********************wbx:i2c_register_driver begin\n");  
    /* 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);
    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);//driver_register中device和driver匹配不错的时候,会调用此函数,**注意这里的__process_new_driver,这是一个函数指针**,也传递进了i2c_for_each_dev函数,下面会讲解这边函数指针的用法,这里先提醒

    return 0;
}

再看i2c_for_each_dev源码:

int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
    //函数的形参data就是上层函数i2c_register_driver传递过来的driver
    int res;
    mutex_lock(&core_lock);
    res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);//在调用此函数
    mutex_unlock(&core_lock);

    return res;
}

看bus_for_each_dev函数源码(driver/base/bus.c):

int bus_for_each_dev(struct bus_type *bus, struct device *start,
             void *data, int (*fn)(struct device *, void *))
{
    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));//遍历链表上的所有设备
    //通过next_device函数将链表上的设备一样取出然后传递给fn,也就是前面提到的__process_new_driver函数
    while ((dev = next_device(&i)) && !error)
        error = fn(dev, data);
    klist_iter_exit(&i);
    return error;
}

下面再看__process_new_driver源码拿到driver和device做了什么(这部分源码在driver/i2c/i2c-core.c):

static int __process_new_driver(struct device *dev, void *data)
{
//判断拿到的设备是否是i2c总线设备,即是不是i2c_adapter,返回0,结束词函数;继续拿下一个device,在进行判断是不是i2c_adapter,如实是则执行i2c_do_add_adapter函数
    if (dev->type != &i2c_adapter_type)
        return 0;}
    return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}

下面看调用的i2c_do_add_adapter源码:

扫描二维码关注公众号,回复: 1811150 查看本文章
static int i2c_do_add_adapter(struct i2c_driver *driver,
                  struct i2c_adapter *adap)
{

    /* Detect supported devices on that bus, and instantiate them */
    //进入i2c_do_add_adapter函数后先调用i2c_detect函数
    i2c_detect(adap, driver);

    /* 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;
}

看i2c_detect的源码:

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;
    //获取i2c_adapter设备的i2c总线号
    int adap_id = i2c_adapter_id(adapter);
    //获取设备驱动程序中定义的address_list 
    address_list = driver->address_list;
    if (!driver->detect || !address_list)
        { 
        return 0;}
//这里在判断驱动程序中是定义了detect 函数和address_list,其中一个没定义,则失败,返回0;
    /* Stop here if the classes do not match */
    if (!(adapter->class & driver->class))
        {
        printk("********************wbx:2_error\n");
        return 0;}
//这里在判断驱动程序中定义的class和adapter中定义的class是否相同,不相同则失败返回0;
/*我在移植egalax_i2c这个驱动程序时,驱动中没有定义address_list,也没有定义detect 函数和class,我是仿照默认驱动的gt82x.c,进行添加的。
    /* 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;//这里定义了一个i2c_client,下面的程序会对这个进行赋值,然后再注册

    for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
        printk("found normal entry for adapter %d, "
            "addr 0x%02x\n", adap_id, address_list[i]);
        temp_client->addr = address_list[i];//将检测到的i2c地址赋值给i2c_client的addr
        err = i2c_detect_address(temp_client, driver);//调用此函数
        if (unlikely(err))
            break;
    }

    kfree(temp_client);
    return err;
}

下面看i2c_detect_address的源码:

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;

    /* Make sure the address is valid */
    err = i2c_check_addr_validity(addr);//检测i2c地址是否有效
    if (err) {
        printk(&adapter->dev, "Invalid probe address 0x%02x\n",
             addr);
        return err;
    }

    /* Skip if already in use */
    if (i2c_check_addr_busy(adapter, addr))//检测i2c地址是否busy
        return 0;

#ifndef CONFIG_ARCH_SUNXI
    /* Make sure there is something at this address */
    if (!i2c_default_probe(adapter, addr))
        return 0;
#endif

    /* Finally call the custom detection function */
    memset(&info, 0, sizeof(struct i2c_board_info));
    info.addr = addr;
    err = driver->detect(temp_client, &info);
    /*这里定义了结构体i2c_board_info info,看了静态注册设备的话,对这个结构体很熟悉了,没有使用dts的时候必须要用i2c_board_info 结构体存放硬件信息,动态注册也一样,只是定义这个结构体的位置不同而已。
    这里把在i2c_detect函数中检测到的i2c地址赋值给了info这个结构体,并将temp_client和info一起传入了驱动程序中定义的detect函数,那么detect函数拿到这两个参数做了什么呢?,看下**驱动程序中**detect函数的源码:

`static int ctp_detect(struct i2c_client *client, struct i2c_board_info *info)
{
    //首先定义了一个结构体i2c_adapter 保存传递进来的temp_client结构体中的adapter成员
    struct i2c_adapter *adapter = client->adapter;

    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)){
        printk("======return=====\n");
        return -ENODEV;
    }//检测i2c_apater(i2c控制器的驱动能力)
    /*twi_id == adapter->nr这个判断语句是重点,我们都知道每一组i2c总线挂接的有自己的i2c设备这是由硬件连接决定的,那么决定了在软件上每一个i2c_driver挂载到相应的i2c_adapter,这里进行判读,传入进来的i2c_adapter的总线号是否和我这个i2c_client设备要挂载的总线号一致;
    if(twi_id == adapter->nr){
                strlcpy(info->type, "egalax_i2c", 11);//若总线号相同,赋值名称,这里的名称一定要和id_table当中的相同;
                return 0;
    }else{
        return -ENODEV;
    }
}`//这里的detect函数主要就是判断总线号,然后对即将注册的i2c_client的名称赋值


    if (err) {
         /*-ENODEV is returned if the detection fails. We catch it
           here as this isn't an error. */
           printk("******************error\n");
        return err == -ENODEV ? 0 : err;
    }



    /* Consistency check */
    if (info.type[0] == '\0') {
        printk( "%s detection function provided "
            "no name for 0x%x\n", driver->driver.name,
            addr);
    } else {
        struct i2c_client *client;

        /* Detection succeeded, instantiate the device */
        printk("Creating %s at 0x%02x\n",
            info.type, info.addr);
        client = i2c_new_device(adapter, &info);
        /*然后在调用i2c_new_device函数,i2c_new_device在静态注册中已经分析过了,主要是调用driver_register向i2c总线注册设备;
        if (client)
            {list_add_tail(&client->detected, &driver->clients);
            printk("add_client\n");
            }//注册成功后将i2c_client加到设备链表中
        else
            printk(&adapter->dev, "Failed creating %s at 0x%02x\n",
                info.type, info.addr);
    }
    return 0;
}

至此动态注册分析完成。全志的R16平台是采用此种方法进行注册硬件设备的,但是这种方法需要在驱动中写入相应的硬件信息,而且在移植的时候不一目了然。我是花了好几天才跟踪到这种动态注册的方法;

猜你喜欢

转载自blog.csdn.net/chihunqi5879/article/details/79971034
I2C