Chinaunix首页 | 论坛 | 博客
  • 博客访问: 808416
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 项目管理

2017-01-19 14:03:01

                                  
S3C2440内核IIC设备驱动建立流程:
由内核打印信息,追索驱动流程:
参考的内核版本为: linux
-2.6.22.6
----------------------------------------
内核注册流程:
由打印信息:

1. 先注册 i2c_driver:i2cdev_driver                --- \drivers\i2c\i2c-dev.c

前面分析过,内核启动初始化会执行函数:i2c_dev_init
i2c_dev_init(void)
    register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);       // 注册字符设备,主设备号为:I2C_MAJOR,应用层统一的操作接口为 i2cdev_fops
    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");  // 构建一个class:i2c_dev_class
    
    i2c_add_driver(&i2cdev_driver);
        i2c_register_driver(THIS_MODULE, driver);
            driver_register(&driver->driver);
            list_add_tail(&driver->list,&drivers);   // 这里将 i2cdev_driver 挂接到 drivers 链表上
            
            // 遍历adapters链表中的adapter,调用函数 i2cdev_attach_adapter(adapter)
            if (driver->attach_adapter) {   // 这里 driver->attach_adapter = i2cdev_attach_adapter
                struct i2c_adapter *adapter;

                // 在系统初始化的时候,adapters链表上,还没有初始化adapter元素,所以找不到adapter
                list_for_each_entry(adapter, &adapters, list) {
                    driver->attach_adapter(adapter);  // 初始化的时候,找不到adapter,所以该句不会执行。
                }
            }
            
            
    static struct i2c_driver i2cdev_driver = {
        .driver = {
            .name    = "dev_driver",
        },
        .id        = I2C_DRIVERID_I2CDEV,
        .attach_adapter    = i2cdev_attach_adapter,
        .detach_adapter    = i2cdev_detach_adapter,
        .detach_client    = i2cdev_detach_client,
    };

    注:
        这个代码是linux内核做好的框架,内核启动,就会注册一个i2c字符设备以及类,i2c-driver链表。
        并且为这个i2c字符设备提供了统一的应用层的操作接口函数。
    
打印信息:
    i2c /dev entries driver
    i2c_dev_init, i2c_add_driver: i2cdev_driver
-------------------------------------------------
    

2. 然后注册i2c_adapter:  s3c24xx_i2c.adap       --- \drivers\i2c\busses\i2c-s3c2410.c

注:这里就是针对不同的Soc注册i2c adapter,adapter是针对硬件i2c设备来说的。

在S3C2440中,使用平台总线驱动来注册I2C设备的。

2.1 初始化注册i2c平台设备资源:

    linux-2.6.22.6\arch\arm\plat-s3c24xx\devs.c
    在这个文件中定义了i2c设备资源:
        struct platform_device s3c_device_i2c = {
            .name          = "s3c2410-i2c",
            .id          = -1,
            .num_resources      = ARRAY_SIZE(s3c_i2c_resource),
            .resource      = s3c_i2c_resource,
        };

    linux-2.6.22.6\arch\arm\mach-s3c2440\mach-smdk2440.c
    在这个文件中注册i2c平台设备:
        static struct platform_device *smdk2440_devices[] __initdata = {
            &s3c_device_usb,
            &s3c_device_lcd,
            &s3c_device_wdt,
            &s3c_device_i2c,  
            &s3c_device_iis,
            &s3c2440_device_sdi,
        };

        // 初始化函数
        static void __init smdk2440_machine_init(void)
            platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
                platform_device_register(devs[i]);  // 这里真正注册 s3c_device_i2c 设备

2.2 初始化注册i2c平台驱动:

    linux-2.6.22.6\drivers\i2c\busses\i2c-s3c2410.c
    
    // 注册i2c平台驱动:
    static int __init i2c_adap_s3c_init(void)
        platform_driver_register(&s3c2440_i2c_driver);

    // s3c2440的i2c平台设备驱动结构体:
    static struct platform_driver s3c2440_i2c_driver = {
        .probe        = s3c24xx_i2c_probe,
        .remove        = s3c24xx_i2c_remove,
        .resume        = s3c24xx_i2c_resume,
        .driver        = {
            .owner    = THIS_MODULE,
            .name    = "s3c2440-i2c",
        },
    };
    
    而这里要问:那么 i2c_adapter 在哪里设置的呢?
    回答:
        当初始化i2c平台驱动程序时,就会去找与平台驱动同名的平台设备,如果找到匹配的设备,则会调用
        平台驱动中定义的probe函数。
        在这个probe函数中,将会获取该SoC的i2c设备的相关资源(也就是前面定义的i2c平台设备资源),并且构造一个
        i2c_adapter 结构体,设置填充,最后调用函数i2c_add_adapter(该i2c_adapter结构体),向系统注册。
    
        // 平台设备驱动:注册driver的时候,当找到匹配的设备时,调用driver里面的probe函数
        s3c24xx_i2c_probe(struct platform_device *pdev)
            struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
            i2c->adap.algo_data = i2c;
            i2c->adap.dev.parent = &pdev->dev;
            s3c24xx_i2c_init(i2c);                            // 初始化IIC控制器
            i2c_add_adapter(&i2c->adap);
                i2c_register_adapter(adapter);
                    list_add_tail(&adap->list, &adapters);   // 将该adapter挂接到adapters链表上
                    
                    // 遍历drivers链表中的i2c_driver,调用函数:driver->attach_adapter
                    list_for_each(item,&drivers) {
                        driver = list_entry(item, struct i2c_driver, list);
                        if (driver->attach_adapter)
                            /* We ignore the return code; if it fails, too bad */
                            driver->attach_adapter(adap);  // 这里由于前面内核启动的时候,初始化了一个 i2cdev_driver 结构体
                                                           // 所以这里将执行: i2cdev_attach_adapter(adap)
                                
                                i2cdev_attach_adapter(注册的adapter结构体)
                                    // 在类 i2c_dev_class 下面注册一个device: i2c_dev->dev,
                                    // 在应用层的文件为: /dev/i2c-0,/dev/i2c-1,... /dev/i2c-n
                                    // 这个类: i2c_dev_class 也是在内核启动的时候注册的
                                    i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
                                                     MKDEV(I2C_MAJOR, adap->nr),
                                                     "i2c-%d", adap->nr);
                    }
                    
        // 结构体:i2c_adapter,这里用结构体 struct s3c24xx_i2c 做了一层封装:
        static struct s3c24xx_i2c s3c24xx_i2c = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
            .wait        = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
            .tx_setup    = 50,
            
            // 这里才是真正的 i2c_adapter
            .adap        = {
                .name            = "s3c2410-i2c",
                .owner            = THIS_MODULE,
                .algo            = &s3c24xx_i2c_algorithm,   // 这里定义了i2c设备通过I2C接口如何发送,接收数据,
                                                            // 至于数据啥意思,它不管。
                .retries        = 2,
                .class            = I2C_CLASS_HWMON,
            },
        };
            
打印信息:              
    cnt: 1, i2c_adapter name: s3c2410-i2c
-------------------------------------------
    

3. 进一步解释:

    当注册 i2c_adapter:  s3c24xx_i2c.adap 的时候,就会遍历drivers链表,把drivers链表中的 i2c_driver
    一个一个取出来,然后调用 i2c_driver.attach_adapter(adapt)函数。
    
    因为前面只注册了一个 i2c_driver结构体: i2cdev_driver,所以就将调用函数:
    i2cdev_driver.attach_adapter(adapter) = i2cdev_attach_adapter(s3c24xx_i2c.adap)
    
    i2cdev_attach_adapter(s3c24xx_i2c.adap)
        device_create(i2c_dev_class, &adap->dev, MKDEV(I2C_MAJOR, adap->nr), "i2c-%d", adap->nr);
        这个函数前面已经分析了,将会创建设备文件:"/dev/i2c-0"
    所以当内核启动后,可以查看是否建立的设备文件:
    # ls /dev/i2c* -l
    crw-rw----    1 0        0         89,   0 Jan  1 00:00 /dev/i2c-0
    可以看到,主设备号为89,次设备号为0,建立了设备文件: "/dev/i2c-0"

    内核打印信息:
        cnt: 1, i2cdev_attach_adapter,adap.name:s3c2410-i2c
        cnt: 1, i2c_register_driver, i2c_driver->name: dev_driver
        

4.  问题:上面只是生成了一个/dev/i2c-0设备文件,那真正的IIC从设备如何注册驱动呢?

    回答:
        可以参考例子程序:linux-2.6.22.6\drivers\i2c\chips\ds1374.c
        static int __init ds1374_init(void)
            i2c_add_driver(&ds1374_driver)
                i2c_register_driver(THIS_MODULE, driver);
                    driver->driver.owner = owner;
                    driver->driver.bus = &i2c_bus_type;
                    driver_register(&driver->driver);
                    
                    // 将注册的 i2c_driver 挂接到drivers链表上
                    list_add_tail(&driver->list,&drivers);
                    
                    // 遍历adapters链表下的 adapter 结构体,调用新注册的 i2c_driver 下面的attach_adapter函数
                    if (driver->attach_adapter) {
                        struct i2c_adapter *adapter;

                        list_for_each_entry(adapter, &adapters, list) {
                            driver->attach_adapter(adapter);  // 这里函数 attach_adapter = ds1374_attach
                                                              // adapter 就是之前初始化注册的 s3c24xx_i2c.adap
                            // 相当于执行函数:
                            ds1374_attach(s3c24xx_i2c.adap)
                                i2c_probe(s3c24xx_i2c.adap, &addr_data, ds1374_probe);
                                // 在这里才真正的和底层设备扯上关系了
                                    i2c_probe_address(s3c24xx_i2c.adap, 从设备的IIC地址, -1, 传递进来的函数 ds1374_probe );
                                        i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL)
                                            // 1. 探测是否真正存在该地址的IIC从设备
                                            i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data);
                                            
                                            // 2. 如果存在该IIC从设备,则调用真正的IIC从设备probe函数,
                                            //    这个函数是传递进来的: ds1374_probe
                                            found_proc(adapter, addr, kind);    
                                            ds1374_probe(struct i2c_adapter *adap, int addr, int kind)    
                                                // 2.1 建立一个i2c_client结构体,这个就表示一个外部的IIC从设备
                                                //  可以看到这个client结构体才是真正的操作源头。
                                                struct i2c_client *client;
                                                client->addr = addr;
                                                client->adapter = adap;
                                                client->driver = &ds1374_driver;    
                                                i2c_attach_client(client);
                                                    // 2.2 将这个i2c_client结构体,挂接到该 i2c_adapter 的clients链表下面
                                                    list_add_tail(&client->list,&adapter->clients);
                                                    client->dev.bus = &i2c_bus_type;
                                                    device_register(&client->dev);
                        }
                    }
                    
        static struct i2c_driver ds1374_driver = {
            .driver = {
                .name    = DS1374_DRV_NAME,
            },
            .id = I2C_DRIVERID_DS1374,
            .attach_adapter = ds1374_attach,
            .detach_client = ds1374_detach,
        };
    
    总结:
        当注册 i2c_driver 到内核时,
        (1)会从adapters链表下取出一个个adapter,
        (2)然后调用新注册i2c_driver结构体总定义的 attach_adapter 函数,
        (3)在这个 attach_adapter 函数中,会调用内核定义的统一的接口函数: i2c_probe
            i2c_probe函数原型:
            int i2c_probe(struct i2c_adapter *adapter, struct i2c_client_address_data *address_data,
                            int (*found_proc) (struct i2c_adapter *, int, int))
                            
        (4)在i2c_probe函数中,做两件事:
            a. 探测该IIC从设备是否真的存在,这是有内核定义的框架来自动probe的;
            b. 传递给 i2c_probe 函数的参数中带有一个真正的 IIC从设备的probe函数,在这一步得到调用
                在这个真正的IIC从设备probe函数中,将建立一个I2c_client结构体,这个结构体就是IIC从设备
                和外界进行IIC通信的载体(各种不同的操作:比如读写IIC从设备)。
                
    
    

5. 总结如何编写i2c驱动

    5.1 构造一个i2c_driver结构体

        该结构体包括函数:
            attach_adapter
            detach_client

    5.2 注册i2c_driver结构体

        当向内核注册i2c_driver,将会调用该结构体下面的attach_adapter函数
        

    5.3 所以当内核注册i2c_driver,会先探测IIC设备时候存在,如果存在,则调用attach_adapter函数来probe设备,来建立某种联系:

        5.3.1 在attach_adapter函数中,调用统一的函数接口:i2c_probe(adapter, 设备地址, "设备真正的probe函数");
        5.3.2 在"设备真正的probe函数"中,构造一个:struct i2c_client结构体:
            该结构体一端连着"adapter",一端连着"i2c_driver",并保存IIC设备地址;最后调用函数 i2c_attach_client
            将该client注册到内核的 i2c_bus_type 上。
        5.3.3 运行到这里,表示已经找到IIC设备,并且已经利用client建立了某种联系,就可以创建与IIC设备对应的字符
            设备文件,这个创建过程和前面讲的建立字符设备驱动一样。以后IIC设备文件的读写等操作,都是借助于client
            这个桥梁。
        注:
            5.3.3 这一步在有的driver里面并没有做。注意在源代码中查看。
阅读(1313) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~