I2C驱动框架理解


相关对象

1、 I2C总线
2、 I2C设备驱动
4、 I2C设备
3、 I2C适配器

-------------------------------------------------------------------------------------------------------

I2C总线
struct bus_type变量i2c_bus_type定义了I2C总线
    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总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备与I2C驱动的匹配,删除等操作,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,如果匹配就调用i2c_device_probe函数,进而调用I2C驱动的probe函数;

I2C总线注册
    I2C总线属于I2C核心层,在driver/i2c/i2c-core.c中postcore_initcall(i2c_init)开始I2C总线注册,调用过程如下:
    postcore_initcall(i2c_init);
            i2c_init(void)
                bus_register(&i2c_bus_type);
        
    i2c_init()实现如下:
    static int __init i2c_init(void)
    {
        ... ...
        retval = bus_register(&i2c_bus_type);
        ... ...
    }

-------------------------------------------------------------------------------------------------------

I2C设备驱动
    定义在include/linux/i2c.h中的struct i2c_driver结构描述I2C驱动,
    struct i2c_driver
    {
        unsigned int class;

        int (*attach_adapter)(struct i2c_adapter *) __deprecated;
        int (*detach_adapter)(struct i2c_adapter *) __deprecated;

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

-------------------------------------------------------------------------------------------------------

I2C设备
    struct i2c_client结构体描述一个I2C设备,定义在include/linux/i2c.h中
    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;    // 适配器
        struct i2c_driver *driver;        // 设备对应驱动
        struct device dev;                // 表明这个是一个设备
        int irq;                        // 中断号
        struct list_head detected;
    };

-------------------------------------------------------------------------------------------------------

I2C适配器
    I2C适配器就是SOC上的I2C控制器
    struct i2c_adapter {
        ... ...
        const struct i2c_algorithm *algo;
        struct device dev;
        ... ...
    };
    
    struct i2c_algorithm {
        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
                   int num);
        int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
                   unsigned short flags, char read_write,
                   u8 command, int size, union i2c_smbus_data *data);
        u32 (*functionality) (struct i2c_adapter *);
    };

-------------------------------------------------------------------------------------------------------
I2C总线:维护两个链表(I2C驱动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等
I2C驱动:I2C设备驱动程序
I2C设备:具体硬件设备的一个抽象
I2C适配器:用于I2C驱动和I2C设备间的通道,SOC上I2C控制器的抽象

-------------------------------------------------------------------------------------------------------

I2C驱动注册
    通过调用i2c_add_driver函数注册I2C驱动
    static inline int i2c_add_driver(struct i2c_driver *driver)
    {
        return i2c_register_driver(THIS_MODULE, driver);
    }
    再调用i2c_register_driver注册
    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    {    
        /*        
        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,
        };
        */
        ... ...
        driver->driver.bus = &i2c_bus_type;  //绑定总线
        ... ...
        res = driver_register(&driver->driver);    //向总线注册驱动
        ... ...
        /* 遍历I2C总线上的所有设备,调用__process_new_driver函数 */
        i2c_for_each_dev(driver, __process_new_driver);

        return 0;
    }

    进入driver_register函数
    int driver_register(struct device_driver *drv)
    {
        ... ...
        ret = bus_add_driver(drv); // 将驱动添加到总线
    }

    进入bus_add_driver函数:
    int bus_add_driver(struct device_driver *drv)
    {    
        ... ...
        if (drv->bus->p->drivers_autoprobe) {
            error = driver_attach(drv);
            if (error)
                goto out_unregister;
        }

        klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
        ... ...
    }

    进入driver_attach函数:
    int driver_attach(struct device_driver *drv)
    {
        return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
    }

    int bus_for_each_dev(struct bus_type *bus, struct device *start,
             void *data, int (*fn)(struct device *, void *))
    {    
        /* 遍历总线的所有设备链表(bus->p->klist_devices)的所有设备,执行fn函数 */
        while ((dev = next_device(&i)) && !error)
            error = fn(dev, data);
    }
    对于bus_for_each_dev(drv->bus, NULL, drv, __driver_attach),fn对应的函数就是__driver_attach

    进入__driver_attach函数:
    static int __driver_attach(struct device *dev, void *data)
    {
        // 判断驱动与设备是否匹配
        if (!driver_match_device(drv, dev))
            return 0;

        // 如果匹配,调用driver_probe_device函数            
        if (!dev->driver)
            driver_probe_device(drv, dev);
    }

    进入driver_match_device函数
    static inline int driver_match_device(struct device_driver *drv, struct device *dev)
    {
        // 调用bus总线的match函数,判断是否匹配
        return drv->bus->match ? drv->bus->match(dev, drv) : 1;
    }

    match函数在哪里?在最开始的i2c_register_driver函数中,driver->driver.bus = &i2c_bus_type;
    i2c_bus_type被赋值给了bus
        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,
        };

        drv->bus->match对应的就是i2c_device_match函数;

    进入i2c_device_match函数:
    static int i2c_device_match(struct device *dev, struct device_driver *drv)
    {
        struct i2c_client    *client = i2c_verify_client(dev); // 由device获得i2c_client
        struct i2c_driver    *driver = to_i2c_driver(drv); // 由device_driver获取到i2c_driver
        
        return i2c_match_id(driver->id_table, client) != NULL;
    }

    进入i2c_match_id函数:
    static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
                        const struct i2c_client *client)
    {
        while (id->name[0]) {
            if (strcmp(client->name, id->name) == 0)
                return id;
            id++;
        }
        return NULL;
    }

    strcmp(client->name, id->name)    
    比较i2c_client的char name[I2C_NAME_SIZE]与i2c_driver的i2c_device_id中的char name[I2C_NAME_SIZE];
    
    回到__driver_attach函数:
    static int __driver_attach(struct device *dev, void *data)
    {
        // 判断驱动与设备是否匹配
        if (!driver_match_device(drv, dev))
            return 0;

        // 如果匹配,调用driver_probe_device函数            
        if (!dev->driver)
            driver_probe_device(drv, dev);
    }
    driver_match_device匹配成功后,调用driver_probe_device函数

    进入driver_probe_device函数
    int driver_probe_device(struct device_driver *drv, struct device *dev)
    {
        ret = really_probe(dev, drv);
    }

    进入really_probe函数
    static int really_probe(struct device *dev, struct device_driver *drv)
    {
        if (dev->bus->probe) {
            ret = dev->bus->probe(dev); //优先调用总线的probe函数
        } else if (drv->probe) {
            ret = drv->probe(dev);
        }
    }
    
    优先调用总线的probe函数,由总线定义:
    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,
    };
    
    dev->bus->probe就是i2c_device_probe函数;
    
    进入i2c_device_probe函数
    static int i2c_device_probe(struct device *dev)
    {
        struct i2c_driver    *driver = to_i2c_driver(dev->driver);
        // 调用i2c驱动i2c_driver的probe函数
        status = driver->probe(client, i2c_match_id(driver->id_table, client));
    }

    通过上面的分析,可以知道i2c总线控制,当向内核注册i2c驱动时,会将i2c驱动添加到总线的链表中,遍历总线上所有设备,通过i2c_client->name, i2c_driver->i2c_device_id->name进行字符串匹配,如果匹配,就调用驱动程序的probe函数;

    继续回到i2c_register_driver函数
    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    {    
        /*        
        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,
        };
        */
        ... ...
        driver->driver.bus = &i2c_bus_type;  //绑定总线
        ... ...
        res = driver_register(&driver->driver);    //向总线注册驱动
        ... ...
        /* 遍历I2C总线上的所有设备,调用__process_new_driver函数 */
        i2c_for_each_dev(driver, __process_new_driver);
    }
    
    分析i2c_for_each_dev(driver, __process_new_driver),进入i2c_for_each_dev函数
    int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
    {
        res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
    }

    进入bus_for_each_dev函数:
    int bus_for_each_dev(struct bus_type *bus, struct device *start,
             void *data, int (*fn)(struct device *, void *))
    {
        struct device *dev;
        /* 遍历I2C总线上所有的device,调用fn函数 */
        while ((dev = next_device(&i)) && !error)
            error = fn(dev, data);
    }
    bus_for_each_dev遍历总线上所有的device,调用fn函数,fn由i2c_for_each_dev传入的__process_new_driver函数

    进入__process_new_driver函数
    static int __process_new_driver(struct device *dev, void *data)
    {
        return i2c_do_add_adapter(data, to_i2c_adapter(dev));
    }
    
    进入i2c_do_add_adapter函数
    static int i2c_do_add_adapter(struct i2c_driver *driver,
                  struct i2c_adapter *adap)
    {
        /* 探测I2C总线上能支持的设备 */
        i2c_detect(adap, driver);

        /* Let legacy drivers scan this bus for matching devices */
        if (driver->attach_adapter) {
            driver->attach_adapter(adap);
        }
        return 0;
    }

    进入i2c_detect函数:
    static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
    {
        ......
        for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
            err = i2c_detect_address(temp_client, driver);
        }
    }

    进入i2c_detect_address函数
    static int i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver)
    {
        i2c_check_addr_validity(addr);

        i2c_check_addr_busy(adapter, addr);
            
        i2c_default_probe(adapter, addr);
        
        driver->detect(temp_client, &info);
    
        client = i2c_new_device(adapter, &info);
        list_add_tail(&client->detected, &driver->clients);
    }

I2C设备注册
    内核调用i2c_new_device完成I2C设备注册
    进入i2c_new_device函数:
    struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    {
        struct i2c_client    *client; // i2c_client描述一个I2C设备
    
        client = kzalloc(sizeof *client, GFP_KERNEL);
        client->adapter = adap;    // I2C适配器
        client->dev.platform_data = info->platform_data;
        client->flags = info->flags;
        client->addr = info->addr;
        client->irq = info->irq;
        strlcpy(client->name, info->type, sizeof(client->name));
        i2c_check_client_addr_validity(client);
        i2c_check_addr_busy(adap, client->addr);
        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;

        dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), client->addr);

        device_register(&client->dev);
        .......
    }
        
    进入device_register函数:
    int device_register(struct device *dev)
    {
        device_initialize(dev);
        device_add(dev);
    }

    进入device_add函数:
    int device_add(struct device *dev)
    {    
        bus_add_device(dev);     //将I2C设备加入I2C总线
        bus_probe_device(dev);    //为设备probe对应的驱动
    }

    进入bus_probe_device:
    void bus_probe_device(struct device *dev)
    {
        if (bus && bus->p->drivers_autoprobe) {
            ret = device_attach(dev);
        }
    }

    进入device_attach:
    int device_attach(struct device *dev)
    {
        bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
    }

    进入bus_for_each_drv:
    int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
             void *data, int (*fn)(struct device_driver *, void *))
    {
        struct device_driver *drv;;
        while ((drv = next_driver(&i)) && !error)
            fn(drv, data);
    }
    遍历driver链表,调用fn, fn为device_attach重调用bus_for_each_drv传入的__device_attach函数;

    进入__device_attach函数:
    static int __device_attach(struct device_driver *drv, void *data)
    {
        driver_match_device(drv, dev)
        driver_probe_device(drv, dev);
    }

    进入driver_match_device:
    static inline int driver_match_device(struct device_driver *drv,
                      struct device *dev)
    {
        // 调用i2c_bus_type的i2c_device_match函数,判断是否匹配
        drv->bus->match(dev, drv)
    }

    进入driver_probe_device:
    int driver_probe_device(struct device_driver *drv, struct device *dev)
    {
        ret = really_probe(dev, drv);
    }

    进入really_probe(dev, drv):
    static int really_probe(struct device *dev, struct device_driver *drv)
    {
        if (dev->bus->probe) {
            ret = dev->bus->probe(dev); //优先调用总线的probe函数
        } else if (drv->probe) {
            ret = drv->probe(dev);
        }
    }
    
    优先调用总线的probe函数,由总线定义:
    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,
    };
    
    dev->bus->probe就是i2c_device_probe函数;
    
    进入i2c_device_probe函数
    static int i2c_device_probe(struct device *dev)
    {
        struct i2c_driver    *driver = to_i2c_driver(dev->driver);
        // 调用i2c驱动i2c_driver的probe函数
        status = driver->probe(client, i2c_match_id(driver->id_table, client));
    }

    最终调用driver的probe函数;

    通过i2c_register_board_info注册I2C设备,如在MINI2440Z中:
    
    MACHINE_START(MINI2440, "MINI2440")
        .boot_params    = S3C2410_SDRAM_PA + 0x100,
        .map_io        = mini2440_map_io,
        .init_machine    = mini2440_init,
        .init_irq    = s3c24xx_init_irq,
        .timer        = &s3c24xx_timer,
    MACHINE_END

    进入mini2440_init函数:
    static void __init mini2440_init(void)
    {
        i2c_register_board_info(0, mini2440_i2c_devs, ARRAY_SIZE(mini2440_i2c_devs));
    }

    进入i2c_register_board_info:
    int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
    {
        for (status = 0; len; len--, info++) {
            struct i2c_devinfo    *devinfo;
            devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
            devinfo->busnum = busnum;
            devinfo->board_info = *info;
            /* 将i2c_devinfo加入__i2c_board_list链表 */
            list_add_tail(&devinfo->list, &__i2c_board_list);
        }
    }

    __i2c_board_list调用的另一条线:
    __i2c_board_list在什么用?__i2c_board_list在i2c_scan_static_board_info函数中被使用;

    进入i2c_scan_static_board_info函数:
    static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
    {
        struct i2c_devinfo    *devinfo;
        list_for_each_entry(devinfo, &__i2c_board_list, list) {
            if (devinfo->busnum == adapter->nr
                    && !i2c_new_device(adapter,
                            &devinfo->board_info))
        }
    }
    循环遍历__i2c_board_list,调用i2c_new_device函数,最终还是调用i2c_new_device函数
    
    i2c_scan_static_board_info在哪里调用?i2c_scan_static_board_info在i2c_register_adapter函数被调用:
    
    进入i2c_register_adapter函数:
    static int i2c_register_adapter(struct i2c_adapter *adap)
    {        
        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);

        // 注册I2C适配器到I2C总线,type为i2c_adapter_type
        device_register(&adap->dev);
    
        if (adap->nr < __i2c_first_dynamic_bus_num)
            i2c_scan_static_board_info(adap);

        bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
    }
    在i2c_register_adapter函数中同时调用了__process_new_adapter函数,与i2c_register_driver注册driver一样。
    也就是说,在注册I2C适配器的过程中,通过i2c_scan_static_board_info将静态注册的I2C设备调用i2c_new_device注册到I2C总线上,
    I2C设备注册完成后,在调用__process_new_adapter探测该适配器上所有地址,探测成功,调用i2c_new_device生成I2C设备,整个过程也就进入
    了上面的I2C设备注册过程。
    
    
    
    
    
    

    
        


            

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要模拟Linux I2C驱动,我们需要理解I2C协议和Linux驱动框架的基本原理。 I2C(Inter-Integrated Circuit)是一种串行通信协议,允许多个器件通过共享的两根线(SDA和SCL)进行通信。在Linux中,I2C驱动负责管理I2C总线上的设备与内核的通信。 首先,我们需要创建一个虚拟I2C设备。可以使用I2C-dev驱动模块来创建一个模拟的I2C设备节点文件。这可以通过在终端中运行“modprobe i2c-dev”命令来加载模块。 然后,我们需要编写一个I2C驱动程序,它需要遵循Linux驱动模型。驱动程序需要实现与I2C设备通信的函数,如读取和写入数据、发送和接收命令等。我们可以使用ioctl系统调用来实现这些操作。 另外,我们还需要在Linux的设备树(Device Tree)中添加对虚拟I2C设备的描述。这可以通过编辑设备树文件(如.dts或.dtsi)来完成。设备树会告诉内核如何初始化和配置I2C设备。 最后,我们需要编译和加载驱动程序。可以使用交叉编译工具链来编译驱动程序,并将其加载到Linux系统中。编译和加载驱动程序的具体步骤可以根据具体的开发环境和目标平台来进行配置和调整。 通过以上步骤,我们就可以在Linux系统上模拟一个I2C驱动。这样可以方便地进行I2C设备的开发和调试,而无需实际的硬件设备。同时,这也为Linux内核提供了一个通用的I2C接口,可以方便地与各种I2C设备进行通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值