i2c驅動程序全面分析,從adapter驅動程序到設備驅動程序


開發板    :mini2440
內核版本:linux2.6.32.2
驅動程序參考:韋東山老師畢業班i2c


內容概括:

    1、adapter client 簡介
   2、adapter 驅動框架

      2.1 設備側
      2.2 驅動側
         2.2.1 probe 函數
         2.2.1.1 注冊adapter
            new_device del_device
            board_info
            i2c_detect
            i2c_new_device

   3、i2c 設備驅動框架
      3.1 i2c_bus_type
      3.2 i2c_driver
      3.3 i2c_device

   4、寫設備驅動程序

   5、寫adapter驅動程序


1、adapter client 簡介
  在內核里,i2c 驅動框架大概分為兩層,adapter 驅動 和 設備驅動,adapter 英文翻譯過來為 “適配器”,適配器並不恰當,根據我的理解,adapter 指的是我們 mcu 里的 i2c 控制模塊,就是那堆寄存器,因為一個 mcu 里的i2c控制模塊是固定的(寄存器參數、以及收發數據的方法),因此大多數情況下,它們都有芯片廠商寫好了,然而我們學習的過程中自己動手寫一寫也並不困難。對於s3c2440僅僅有一個i2c_adapter,但是別的Mcu可能有多個。至於Client,它對應於muc外圍的I2c設備,每一個i2c設備都由一個唯一的client來描述。


[cpp]  view plain  copy
  1. struct i2c_adapter {  
  2.     struct module *owner;  
  3.     unsigned int id;  
  4.     unsigned int class;       /* classes to allow probing for */  
  5.     const struct i2c_algorithm *algo; /* the algorithm to access the bus */  
  6.     void *algo_data;  
  7.   
  8.     /* data fields that are valid for all devices   */  
  9.     u8 level;           /* nesting level for lockdep */  
  10.     struct mutex bus_lock;  
  11.   
  12.     int timeout;            /* in jiffies */  
  13.     int retries;  
  14.     struct device dev;      /* the adapter device */  
  15.   
  16.     int nr;  
  17.     char name[48];  
  18.     struct completion dev_released;  
  19. };  
    簡單掃一眼,i2c_adapter 封裝了 struct device ,因此它是作為一個設備注冊到內核中去的(稍后我們會知道,它是注冊到i2c_bus_type里),此外非常重要的一個成員struct i2c_algorithm *algo ,這就是我們上邊提到的 i2c 控制器收發數據的方法。
[cpp]  view plain  copy
  1. struct i2c_algorithm {  
  2.   
  3.     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,  
  4.                int num);  
  5.     int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,  
  6.                unsigned short flags, char read_write,  
  7.                u8 command, int size, union i2c_smbus_data *data);  
  8.   
  9.     /* To determine what the adapter supports */  
  10.     u32 (*functionality) (struct i2c_adapter *);  
  11. };  

    master_xfer 對應於i2c協議子集 smbus ,有些設備只支持這個協議

    smbus_xfer 對應於普通的 i2c 傳輸協議

    functionality 用來描述,adapter 所具有的功能,比如是否支持 smbus

[cpp]  view plain  copy
  1. struct i2c_client {  
  2.     unsigned short flags;       /* div., see below      */  
  3.     unsigned short addr;        /* chip address - NOTE: 7bit    */  
  4.                     /* addresses are stored in the  */  
  5.                     /* _LOWER_ 7 bits       */  
  6.     char name[I2C_NAME_SIZE];  
  7.     struct i2c_adapter *adapter;    /* the adapter we sit on    */  
  8.     struct i2c_driver *driver;  /* and our access routines  */  
  9.     struct device dev;      /* the device structure     */  
  10.     int irq;            /* irq issued by device     */  
  11.     struct list_head detected;  
  12. };  
    i2c_client 本質上是一個 i2c_"dev", 它包含了與它配對的 driver ,以及它所在的 adapter(i2c設備在物理連接上,連接到了哪個adapter),后面分析時會看到,它也是作為設備注冊到i2c_bus_type


2、adapter 驅動框架
    在我所使用的這個內核里,2440的i2c_adapter框架是基於 platform_bus_type 的,關於 platform_bus_type 別的文章已經分析過了,這里不做贅述,只簡單提一下,當設備或驅動注冊到 platform_bus_type 時,首先會查找驅動是否有id_table,如果有根據id_table進行匹配(就是看id_table里有無設備的名字),否則匹配設備名字和驅動名字。匹配成功則調用驅動里的probe函數。
    2.1 設備側
        根據設備總線驅動模型的分層思想,將一個驅動程序分為 device 和 driver 兩層,那么 device 里提供底層的硬件資源,在 driver 中取出這些資源進行使用。那么我們就可以猜測到 i2c_adapter 驅動的設備側 至少應該含有哪些資源?

        1、存器地址必須有吧,因為我們要使用這些寄存器,不然怎么傳輸。
        2、中斷必須有吧,i2c傳輸過程中可是離不開中斷的。
        下面,我們就開詳細的看一看,i2c_adapter 驅動的設備側提供了哪些設備資源。
        mach-smdk2410.c (arch\arm\mach-s3c2410) 中定義了個指針數組,這里面有我們想要的 s3c_device_i2c0

[cpp]  view plain  copy
  1. static struct platform_device *smdk2410_devices[] __initdata = {  
  2.     &s3c_device_usb,  
  3.     &s3c_device_lcd,  
  4.     &s3c_device_wdt,  
  5.     &s3c_device_i2c0,  
  6.     &s3c_device_iis,  
  7. };  
  8. dev-i2c0.c (arch\arm\plat-s3c)  
  9. struct platform_device s3c_device_i2c0 = {  
  10.     .name         = "s3c2410-i2c",  
  11. #ifdef CONFIG_S3C_DEV_I2C1  
  12.     .id       = 0,  
  13. #else  
  14.     .id       = -1,  
  15. #endif  
  16.     .num_resources    = ARRAY_SIZE(s3c_i2c_resource),  
  17.     .resource     = s3c_i2c_resource,  
  18. };  
  19. static struct resource s3c_i2c_resource[] = {  
  20.     [0] = {  
  21.         .start = S3C_PA_IIC1,  
  22.         .end   = S3C_PA_IIC1 + SZ_4K - 1,  
  23.         .flags = IORESOURCE_MEM,  
  24.     },  
  25.     [1] = {  
  26.         .start = IRQ_IIC1,  
  27.         .end   = IRQ_IIC1,  
  28.         .flags = IORESOURCE_IRQ,  
  29.     },  
  30. };  
    是不是正如我們所料,在資源文件中提供了 物理寄存器 以及 中斷資源。
    Mach-smdk2410.c (arch\arm\mach-s3c2410),將 s3c_device_i2c0 注冊到 平台設備總線上去
[cpp]  view plain  copy
  1. static void __init smdk2410_init(void)  
  2. {  
  3.     s3c_i2c0_set_platdata(NULL);  
  4.     platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));  
  5.     smdk_machine_init();  
  6. }  
  7. dev-i2c0.c (arch\arm\plat-s3c)  
  8. static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {  
  9.     .flags      = 0,  
  10.     .slave_addr = 0x10,  
  11.     .frequency  = 100*1000,  
  12.     .sda_delay  = 100,  
  13. };        
  14. void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)  
  15. {  
  16.     struct s3c2410_platform_i2c *npd;  
  17.   
  18.     if (!pd)  
  19.         pd = &default_i2c_data0;  
  20.   
  21.     npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);  
  22.     if (!npd)  
  23.         printk(KERN_ERR "%s: no memory for platform data\n", __func__);  
  24.     else if (!npd->cfg_gpio)  
  25.         npd->cfg_gpio = s3c_i2c0_cfg_gpio;  
  26.   
  27.     s3c_device_i2c0.dev.platform_data = npd;  
  28. }  
  29. setup-i2c.c (arch\arm\plat-s3c24xx)  
  30. void s3c_i2c0_cfg_gpio(struct platform_device *dev)  
  31. {  
  32.     s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA);  
  33.     s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL);  
  34. }  
  35. S3c244x.c (arch\arm\plat-s3c24xx)  
  36. void __init s3c244x_map_io(void)  
  37. {  
  38.     /* register our io-tables */  
  39.   
  40.     iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc));  
  41.   
  42.     /* rename any peripherals used differing from the s3c2410 */  
  43.   
  44.     s3c_device_sdi.name  = "s3c2440-sdi";  
  45.     s3c_device_i2c0.name  = "s3c2440-i2c";  
  46.     s3c_device_nand.name = "s3c2440-nand";  
  47.     s3c_device_usbgadget.name = "s3c2440-usbgadget";  
  48. }  
    在將 s3c_device_i2c0 注冊到 平台設備總線上去之前,還提供了以上的其它信息,包括i2c控制器作為從機的默認slave_addr等,以及引腳的配置函數。注意,s3c_device_i2c0.name  = "s3c2440-i2c";
    

    2.2 驅動側
        驅動側的工作大概是取出設備側的資源進行利用,比如Ioremap,配置寄存器,注冊中斷等等

[cpp]  view plain  copy
  1. i2c-s3c2410.c (drivers\i2c\busses)  
  2. static struct platform_driver s3c24xx_i2c_driver = {  
  3.     .probe      = s3c24xx_i2c_probe,  
  4.     .remove     = s3c24xx_i2c_remove,  
  5.     .id_table   = s3c24xx_driver_ids,  
  6.     .driver     = {  
  7.         .owner  = THIS_MODULE,  
  8.         .name   = "s3c-i2c",  
  9.         .pm = S3C24XX_DEV_PM_OPS,  
  10.     },  
  11. };  
  12. static struct platform_device_id s3c24xx_driver_ids[] = {  
  13.     {  
  14.         .name       = "s3c2410-i2c",  
  15.         .driver_data    = TYPE_S3C2410,  
  16.     }, {  
  17.         .name       = "s3c2440-i2c",  
  18.         .driver_data    = TYPE_S3C2440,  
  19.     }, { },  
  20. };  
  21. static int __init i2c_adap_s3c_init(void)  
  22. {  
  23.     return platform_driver_register(&s3c24xx_i2c_driver);  
  24. }  
  25. subsys_initcall(i2c_adap_s3c_init);  
        我們在分析platform總線模型的時候,我們知道platform_bus_type->match函數是首先根據 driver->id_table 來進行匹配device的,前面講了s3c_device_i2c0.name  = "s3c2440-i2c",因此,匹配成功會調用 s3c24xx_i2c_driver->probe 函數,也就是 s3c24xx_i2c_probe ,它是個重點。

    2.3 probe 函數分析

[cpp]  view plain  copy
  1. i2c-s3c2410.c (drivers\i2c\busses)  
  2. static int s3c24xx_i2c_probe(struct platform_device *pdev)  
  3. {  
  4.     struct s3c24xx_i2c *i2c;  
  5.     struct s3c2410_platform_i2c *pdata;  
  6.     struct resource *res;  
  7.     int ret;  
  8.     // 還記得,我們在 device 里放的platform_data么,是時候取出來使用了  
  9.     pdata = pdev->dev.platform_data;  
  10.   
  11.     i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);  
  12.   
  13.     /* 1、使能 i2c 時鍾 */  
  14.     i2c->dev = &pdev->dev;  
  15.     i2c->clk = clk_get(&pdev->dev, "i2c");  
  16.     clk_enable(i2c->clk);  
  17.   
  18.     /* 2、io內存映射 */  
  19.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  20.     i2c->ioarea = request_mem_region(res->start, resource_size(res),  
  21.                      pdev->name);  
  22.     i2c->regs = ioremap(res->start, resource_size(res));  
  23.       
  24.     /* 3、設置adap的相關信息 */  
  25.     strlcpy(i2c->adap.name, "s3c2410-i2c"sizeof(i2c->adap.name));  
  26.     i2c->adap.owner   = THIS_MODULE;  
  27.     i2c->adap.algo    = &s3c24xx_i2c_algorithm;  // i2c控制器的收發函數  
  28.     i2c->adap.retries = 2;  
  29.     i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;  
  30.     i2c->tx_setup     = 50;  
  31.     i2c->adap.algo_data = i2c;  
  32.     i2c->adap.dev.parent = &pdev->dev;  
  33.   
  34.     /* 4、初始化 i2c controller */  
  35.     ret = s3c24xx_i2c_init(i2c);  
  36.     i2c->irq = ret = platform_get_irq(pdev, 0);  
  37.     /* 5、注冊中斷 */  
  38.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,  
  39.               dev_name(&pdev->dev), i2c);  
  40.   
  41.     ret = s3c24xx_i2c_register_cpufreq(i2c);  
  42.   
  43.     /* Note, previous versions of the driver used i2c_add_adapter() 
  44.      * to add the bus at any number. We now pass the bus number via 
  45.      * the platform data, so if unset it will now default to always 
  46.      * being bus 0. 
  47.      */  
  48.     /* 6、適配器編號 */  
  49.     i2c->adap.nr = pdata->bus_num; //閱讀上面的英文,大概意思就是device側pdata中沒設置bus_num,那么就默認為0,顯然這里是0  
  50.       
  51.     /* 7、注冊 adapter */  
  52.     ret = i2c_add_numbered_adapter(&i2c->adap);  // i2c_register_adapter(&i2c->adap);  
  53.   
  54.     platform_set_drvdata(pdev, i2c);  
  55.   
  56.     return 0;  
  57.   
  58. }  
  59. i2c-core.c (drivers\i2c)  
  60. static int i2c_register_adapter(struct i2c_adapter *adap)  
  61. {  
  62.     int res = 0, dummy;  
  63.   
  64.     mutex_init(&adap->bus_lock);  
  65.   
  66.     /* Set default timeout to 1 second if not already set */  
  67.     if (adap->timeout == 0)  
  68.         adap->timeout = HZ;  
  69.       
  70.     /* 設置 adap->dev.kobj.name 為 i2c-0 ,它將出現在 sysfs 中 */  
  71.     dev_set_name(&adap->dev, "i2c-%d", adap->nr);  
  72.       
  73.     /* 設置它所屬的總線 i2c_bus_type */  
  74.     adap->dev.bus = &i2c_bus_type;  
  75.       
  76.     /* 重點: 設置屬性,用戶空間創建 device 就靠它了 */  
  77.     adap->dev.type = &i2c_adapter_type;  
  78.       
  79.     /* 將 adap->dev注冊到 i2c_bus_type */  
  80.     res = device_register(&adap->dev);  
  81. /* 大概是創建 devices 目錄 到class 目錄的符號連接 */  
  82. #ifdef CONFIG_I2C_COMPAT  
  83.     res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,  
  84.                        adap->dev.parent);  
  85.     if (res)  
  86.         dev_warn(&adap->dev,  
  87.              "Failed to create compatibility class link\n");  
  88. #endif  
  89.   
  90.     /* 重點: 掃描 __i2c_board_list 鏈表里的設備信息,自動創建 client ,並注冊到 i2c_bus_type */  
  91.     if (adap->nr < __i2c_first_dynamic_bus_num)  
  92.         i2c_scan_static_board_info(adap);  
  93.   
  94.     /* 重點: 遍歷 i2c_bus_type的driver 鏈表,取出每一個driver 調用 i2c_do_add_adapter */  
  95.     mutex_lock(&core_lock);  
  96.     dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,  
  97.                  i2c_do_add_adapter);  
  98.     mutex_unlock(&core_lock);  
  99.   
  100.     return 0;  
  101.   
  102. }  

        這一個 probe 函數的內容是在是太多了,慢慢來吧,至於 ioremap 申請中斷啥的就不講了,上個圖


        在內核幫助文檔 instantiating-devices 中說,有4種方法可以創建 i2c_device ,其中第四種是在用戶空間創建的:
         Example:
                # echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-0/new_device
                分析過設備模型的都知道,i2c-0 是我們上面設置的 dev_set_name(&adap->dev, "i2c-%d", adap->nr),new_device 就是它的一個屬性了,這個屬性在哪里?在i2c_adapter_type 中

[cpp]  view plain  copy
  1. static struct device_type i2c_adapter_type = {  
  2.     .groups     = i2c_adapter_attr_groups,  
  3.     .release    = i2c_adapter_dev_release,  
  4. };  
  5. static const struct attribute_group *i2c_adapter_attr_groups[] = {  
  6.     &i2c_adapter_attr_group,  
  7.     NULL  
  8. };  
  9. static struct attribute_group i2c_adapter_attr_group = {  
  10.     .attrs      = i2c_adapter_attrs,  
  11. };  
  12.   
  13. static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);  
  14. static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);  
  15. static struct attribute *i2c_adapter_attrs[] = {  
  16.     &dev_attr_name.attr,  
  17.     &dev_attr_new_device.attr,  
  18.     &dev_attr_delete_device.attr,  
  19.     NULL  
  20. };  
        這里,我就不將宏展開了,大概說一下,當 device_register 注冊 device 時,會設置dev.kobj.ktype = device_ktype, device_ktype 提供通用的屬性show 與 store函數,當用戶空間訪問屬性文件時,通用的 show 與 store 就會 調用具體的show 與 store。
         DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device),就會創建一個device_attribute結構體,
                name = “new_device”,.mode = S_IWUSR , show = NULL ,store = i2c_sysfs_new_device 
         顯然上面 echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-0/new_device ,就會調用 i2c_sysfs_new_device 函數了。

[cpp]  view plain  copy
  1. static ssize_t  
  2. i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,  
  3.              const char *buf, size_t count)  
  4. {  
  5.     struct i2c_adapter *adap = to_i2c_adapter(dev);  
  6.     struct i2c_board_info info;  
  7.     struct i2c_client *client;  
  8.     char *blank, end;  
  9.     int res;  
  10.   
  11.     memset(&info, 0, sizeof(struct i2c_board_info));  
  12.   
  13.     blank = strchr(buf, ' ');  
  14.   
  15.     memcpy(info.type, buf, blank - buf);  
  16.   
  17.     /* Parse remaining parameters, reject extra parameters */  
  18.     res = sscanf(++blank, "%hi%c", &info.addr, &end);  
  19.   
  20.     client = i2c_new_device(adap, &info);  
  21.   
  22.     return count;  
  23. }  
  24. struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)  
  25. {  
  26.     struct i2c_client   *client;  
  27.     int         status;  
  28.   
  29.     client = kzalloc(sizeof *client, GFP_KERNEL);  
  30.   
  31.     client->adapter = adap;  
  32.   
  33.     client->dev.platform_data = info->platform_data;  
  34.   
  35.     if (info->archdata)  
  36.         client->dev.archdata = *info->archdata;  
  37.   
  38.     client->flags = info->flags;  
  39.     client->addr = info->addr;    // 設備地址   
  40.     client->irq = info->irq;  
  41.   
  42.     strlcpy(client->name, info->type, sizeof(client->name)); // 名字很重要  
  43.   
  44.     /* Check for address business */  
  45.     status = i2c_check_addr(adap, client->addr);  
  46.   
  47.     client->dev.parent = &client->adapter->dev;  
  48.     client->dev.bus = &i2c_bus_type;  
  49.     client->dev.type = &i2c_client_type;  
  50.   
  51.     dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),  
  52.              client->addr);  
  53.     status = device_register(&client->dev);  
  54.   
  55.     return client;  
  56. }  
        i2c_sysfs_new_device 函數,將用戶空間傳遞進來的命令進行解析 生成info結構體(addr ,type),然后調用 i2c_new_device, 在 i2c_new_device 中構造client ,設置它的屬性並將它注冊到 i2c_bus_type,其中兩個必須提的屬性 client->name = info->type ,為什么說名字重要,如果看i2c_bus_type的match函數就可以知道,driver是根據client的名字是否在其idtable中判斷是否支持這個client的。另外一個就是addr了,不用多說,每一個i2c設備必須都有個地址。空說無憑,看代碼。

[cpp]  view plain  copy
  1. static int i2c_device_match(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     struct i2c_client   *client = i2c_verify_client(dev);  
  4.     struct i2c_driver   *driver;  
  5.   
  6.     if (!client)  
  7.         return 0;  
  8.   
  9.     driver = to_i2c_driver(drv);  
  10.     /* match on an id table if there is one */  
  11.     if (driver->id_table)  
  12.         return i2c_match_id(driver->id_table, client) != NULL;  
  13.   
  14.     return 0;  
  15. }  
  16. static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,  
  17.         const struct i2c_client *client)  
  18. {  
  19.     while (id->name[0]) {  
  20.         if (strcmp(client->name, id->name) == 0)  
  21.             return id;  
  22.         id++;  
  23.     }  
  24.     return NULL;  
  25. }  

    分析一下 i2c_scan_static_board_info 第二種創建 device 的方式 

[cpp]  view plain  copy
  1. static void i2c_scan_static_board_info(struct i2c_adapter *adapter)  
  2. {  
  3.     struct i2c_devinfo  *devinfo;  
  4.   
  5.     down_read(&__i2c_board_lock);  
  6.     /* 遍歷__i2c_board_list鏈表 取出每一個 devinfo */  
  7.     list_for_each_entry(devinfo, &__i2c_board_list, list) {  
  8.         /* adapter->nr == 0 devinfo->busnum 還不知道,如果相等 
  9.            取出 devinfo->board_info 調用 i2c_new_device ,前面分析過了哦 */  
  10.         if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info))  
  11.             dev_err(&adapter->dev,  
  12.                 "Can't create device at 0x%02x\n",  
  13.                 devinfo->board_info.addr);  
  14.     }  
  15.     up_read(&__i2c_board_lock);  
  16. }  

    來看一下 __i2c_board_list 這個鏈表是哪里填充的。
[cpp]  view plain  copy
  1. mach-mini2440.c (arch\arm\mach-s3c2440)  
  2. static struct i2c_board_info i2c_devs[] __initdata = {  
  3.     { I2C_BOARD_INFO("eeprom", 0x50), },  
  4. };  
  5. #define I2C_BOARD_INFO(dev_type, dev_addr) \  
  6.     .type = dev_type, .addr = (dev_addr)  
  7. static void __init mini2440_machine_init(void)  
  8. {  
  9.     i2c_register_board_info(0, i2c_devs, ARRAY_SIZE(i2c_devs));  
  10.     ....  
  11. }  
  12. int __init i2c_register_board_info(int busnum,  
  13.     struct i2c_board_info const *info, unsigned len)  
  14. {  
  15.     int status;  
  16.   
  17.     down_write(&__i2c_board_lock);  
  18.   
  19.     if (busnum >= __i2c_first_dynamic_bus_num)  
  20.         __i2c_first_dynamic_bus_num = busnum + 1;  
  21.   
  22.     for (status = 0; len; len--, info++) {  
  23.         struct i2c_devinfo  *devinfo;  
  24.   
  25.         devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);  
  26.   
  27.         devinfo->busnum = busnum;    // busnum == 0  
  28.         devinfo->board_info = *info;  
  29.         list_add_tail(&devinfo->list, &__i2c_board_list); // 添加到 __i2c_board_list 鏈表中  
  30.     }  
  31.   
  32.     up_write(&__i2c_board_lock);  
  33.   
  34.     return status;  
  35. }  

        這種方法與第一種用戶空間創建device的方法相類似,都是提供一個addr 和 名字,只不過這種方法還有一個限制,前面看代碼的時候我們知道,在注冊adapter的時候,它會去訪問__i2c_board_list鏈表,那么如果想成功創建,你必須在注冊adater之前i2c_register_board_info

        第二種創建 device 的方式 

[cpp]  view plain  copy
  1. bus_for_each_drv(&i2c_bus_type, NULL, adap, i2c_do_add_adapter) 分析  
  2.     取出 i2c_bus_type 每一個driver 調用 i2c_do_add_adapter  
  3. static int i2c_do_add_adapter(struct device_driver *d, void *data)  
  4. {  
  5.     struct i2c_driver *driver = to_i2c_driver(d);  
  6.     struct i2c_adapter *adap = data;  
  7.   
  8.     /* Detect supported devices on that bus, and instantiate them */  
  9.     i2c_detect(adap, driver);  
  10.   
  11.     /* Let legacy drivers scan this bus for matching devices */  
  12.     if (driver->attach_adapter) {  
  13.         /* We ignore the return code; if it fails, too bad */  
  14.         driver->attach_adapter(adap);  
  15.     }  
  16.     return 0;  
  17. }  
  18. static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)  
  19. {  
  20.     const struct i2c_client_address_data *address_data;  
  21.     struct i2c_client *temp_client;  
  22.     int i, err = 0;  
  23.     int adap_id = i2c_adapter_id(adapter);  
  24.     // driver中 設置了 address_data 是創建device的前提,因為 address_data 中保存了設備的addr 與 名字,看設備驅動的時候會知道  
  25.     address_data = driver->address_data;  
  26.     if (!driver->detect || !address_data)  
  27.         return 0;  
  28.   
  29.     /* Set up a temporary client to help detect callback */  
  30.     temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);  
  31.     if (!temp_client)  
  32.         return -ENOMEM;  
  33.     temp_client->adapter = adapter;  
  34.   
  35.     /* Force entries are done first, and are not affected by ignore 
  36.        entries */  
  37.     if (address_data->forces) {  
  38.         .....  
  39.     }  
  40.   
  41.     /* Stop here if the classes do not match */  
  42.     if (!(adapter->class & driver->class))  
  43.         goto exit_free;  
  44.   
  45.     /* Stop here if we can't use SMBUS_QUICK */  
  46.     if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {  
  47.         ......  
  48.     }  
  49.   
  50.     /* Probe entries are done second, and are not affected by ignore 
  51.        entries either */  
  52.     for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {  
  53.         ......  
  54.     }  
  55.   
  56.     /* Normal entries are done last, unless shadowed by an ignore entry */  
  57.     for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {  
  58.         int j, ignore;  
  59.   
  60.         ignore = 0;  
  61.         ......  
  62.         temp_client->addr = address_data->normal_i2c[i];  
  63.         err = i2c_detect_address(temp_client, -1, driver);  
  64.     }  
  65. }  
  66. static int i2c_detect_address(struct i2c_client *temp_client, int kind,  
  67.   struct i2c_driver *driver)  
  68. {  
  69.     struct i2c_board_info info;  
  70.     struct i2c_adapter *adapter = temp_client->adapter;  
  71.     int addr = temp_client->addr;  
  72.     int err;  
  73.   
  74.     /* 發送 start 信號 以及 i2c設備地址,看是否能收到 ack 信號,判斷設備是否存在,不存在返回 */  
  75.     if (kind < 0) {  
  76.         if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,  
  77.                    I2C_SMBUS_QUICK, NULL) < 0)   // 最終就會調用到adapter驅動中我們設置的 i2c_algorithm  
  78.             return 0;  
  79.     }  
  80.   
  81.     /* Finally call the custom detection function */  
  82.     memset(&info, 0, sizeof(struct i2c_board_info));  
  83.     info.addr = addr;  
  84.     // 要在 driver->detect 設置 info->type   
  85.     err = driver->detect(temp_client, kind, &info);  
  86.   
  87.     /* 如果設置了 info.type,創建 client 調用 i2c_new_device */  
  88.     if (info.type[0] == '\0') {  
  89.         ......  
  90.     } else {  
  91.         struct i2c_client *client;  
  92.   
  93.         /* Detection succeeded, instantiate the device */                         
  94.         client = i2c_new_device(adapter, &info);  
  95.         if (client)  
  96.             list_add_tail(&client->detected, &driver->clients);  
  97.     }  
  98.     return 0;  
  99. }  
        我們在 向i2c_bus_type 注冊driver時,與上面的方法是一樣的,因此,我們可以動態加載driver時,創建對應的device,但是並不推薦這樣做。
         i2c_add_driver-》i2c_register_driver-》bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
         __attach_adapter 和 i2c_do_add_adapter 內容是同理的。

3、i2c 設備驅動框架
  3.1 i2c_bus_type
            前面已經分析過了它的 match 函數,稍后我們會注意一下它的probe函數。
  3.2 i2c_driver
            注冊driver的過程,創建 device 前面也分析過了
  3.3 i2c_device
            device 就更不用提了,前面講了3中創建它的方法,還有第四種 直接 i2c_new_device ,豈不是更來得痛快。
            關於以上3點,就不再分析了,后面直接看 代碼更直接,這里再記錄一下 我分析這里時遇到的一個小問題。

[cpp]  view plain  copy
  1. static struct i2c_driver eeprom_driver = {  
  2.     .driver = {  
  3.         .name   = "eeprom",  
  4.     },  
  5.     .probe      = eeprom_probe,  
  6.     .remove     = eeprom_remove,  
  7.     .id_table   = eeprom_id,  
  8.   
  9.     .class      = I2C_CLASS_DDC | I2C_CLASS_SPD,  
  10.     .detect     = eeprom_detect,  
  11.     .address_data   = &addr_data,   // 由I2C_CLIENT_INSMOD_1(eeprom)宏定義  
  12. };  

        我們在寫i2c_driver時,在 .driver 中沒有指定 probe函數,那么配對成功后是如何調用到 eeprom_probe 的,對於platform平台,它是在注冊platform_driver時,給.driver設置了通用的probe接口,platform_probe,使用它跳轉到 上層的 probe 也就類似於這里的 eeprom_probe。但是搜遍代碼 i2c_bus_type 並沒有這樣做呀,奇怪奇怪。回想在分析設備總線驅動模型,設備與驅動的配對過程,在調用probe函數時,首先會看bus->probe有沒有,有就調用,沒有才會調用driver->probe,platform_bus_type 是沒有Probe函數的,但是i2c_bus_type有!!!所以,來看看 i2c_bus_type->probe吧。

[cpp]  view plain  copy
  1. static int i2c_device_probe(struct device *dev)  
  2. {  
  3.     struct i2c_client   *client = i2c_verify_client(dev);  
  4.     struct i2c_driver   *driver;  
  5.     int status;  
  6.   
  7.     if (!client)  
  8.         return 0;  
  9.   
  10.     driver = to_i2c_driver(dev->driver);  
  11.     if (!driver->probe || !driver->id_table)  
  12.         return -ENODEV;  
  13.     client->driver = driver;  
  14.     if (!device_can_wakeup(&client->dev))  
  15.         device_init_wakeup(&client->dev,  
  16.                     client->flags & I2C_CLIENT_WAKE);  
  17.     dev_dbg(dev, "probe\n");  
  18.   
  19.     status = driver->probe(client, i2c_match_id(driver->id_table, client));  
  20.     if (status)  
  21.         client->driver = NULL;  
  22.     return status;  
  23. }  
        不難分析,原來是通過 Bus->probe 函數進行了跳轉,以后分析別的總線模型又漲知識了。

4、寫設備驅動程序

        我們使用最簡單粗暴的方法,直接在設備側使用 i2c_new_device 創建一個設備注冊到 i2c_bus_type里,再寫個驅動與它配對。

[cpp]  view plain  copy
  1. #include <linux/kernel.h>  
  2. #include <linux/module.h>  
  3. #include <linux/platform_device.h>  
  4. #include <linux/i2c.h>  
  5. #include <linux/err.h>  
  6. #include <linux/regmap.h>  
  7. #include <linux/slab.h>  
  8.   
  9.   
  10. static struct i2c_board_info at24cxx_info = {     
  11.     I2C_BOARD_INFO("at24c08", 0x50),  
  12. };  
  13.   
  14. static struct i2c_client *at24cxx_client;  
  15.   
  16. static int at24cxx_dev_init(void)  
  17. {  
  18.     struct i2c_adapter *i2c_adap;  
  19.     // 獲取設備號為 0 的adpter ,也就是adapter->nr == 0  
  20.     i2c_adap = i2c_get_adapter(0);  
  21.     // 直接使用 i2c_new_device 創建 client 自動注冊到i2c_bus_type 中去,client->name == "at24c08" ,client->addr = 0x50  
  22.     at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info);  
  23.     // 釋放掉 adapter  
  24.     i2c_put_adapter(i2c_adap);    
  25.     return 0;  
  26. }  
  27.   
  28. static void at24cxx_dev_exit(void)  
  29. {  
  30.     i2c_unregister_device(at24cxx_client);  
  31. }  
  32.   
  33. module_init(at24cxx_dev_init);  
  34. module_exit(at24cxx_dev_exit);  
  35. MODULE_LICENSE("GPL");  
     設備側的程序相對簡單,我們只需要構造一個board_info結構體,設置名字Info->type 以及 地址 info->addr,然后使用 i2c_new_device 將其注冊到I2c_bus_type 即可。

[cpp]  view plain  copy
  1. #include <linux/kernel.h>  
  2. #include <linux/module.h>  
  3. #include <linux/platform_device.h>  
  4. #include <linux/i2c.h>  
  5. #include <linux/err.h>  
  6. #include <linux/regmap.h>  
  7. #include <linux/slab.h>  
  8. #include <linux/fs.h>  
  9. #include <asm/uaccess.h>  
  10.   
  11.   
  12. static int major;  
  13. static struct class *class;  
  14. static struct i2c_client *at24cxx_client;  
  15.   
  16. /* 傳入: buf[0] : addr 
  17.  * 輸出: buf[0] : data 
  18.  */  
  19. static ssize_t at24cxx_read(struct file * file, char __user *buf, size_t count, loff_t *off)  
  20. {  
  21.     unsigned char addr, data;  
  22.       
  23.     copy_from_user(&addr, buf, 1);  
  24.     data = i2c_smbus_read_byte_data(at24cxx_client, addr);  
  25.     copy_to_user(buf, &data, 1);  
  26.     return 1;  
  27. }  
  28.   
  29. /* buf[0] : addr 
  30.  * buf[1] : data 
  31.  */  
  32. static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off)  
  33. {  
  34.     unsigned char ker_buf[2];  
  35.     unsigned char addr, data;  
  36.   
  37.     copy_from_user(ker_buf, buf, 2);  
  38.     addr = ker_buf[0];  
  39.     data = ker_buf[1];  
  40.   
  41.     printk("addr = 0x%02x, data = 0x%02x\n", addr, data);  
  42.   
  43.     if (!i2c_smbus_write_byte_data(at24cxx_client, addr, data))  
  44.         return 2;  
  45.     else  
  46.         return -EIO;      
  47. }  
  48.   
  49. static struct file_operations at24cxx_fops = {  
  50.     .owner = THIS_MODULE,  
  51.     .read  = at24cxx_read,  
  52.     .write = at24cxx_write,  
  53. };  
  54.   
  55. static int __devinit at24cxx_probe(struct i2c_client *client,  
  56.                   const struct i2c_device_id *id)  
  57. {  
  58.     at24cxx_client = client;  
  59.           
  60.     //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);  
  61.     major = register_chrdev(0, "at24cxx", &at24cxx_fops);  
  62.     class = class_create(THIS_MODULE, "at24cxx");  
  63.     device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */  
  64.       
  65.     return 0;  
  66. }  
  67.   
  68. static int __devexit at24cxx_remove(struct i2c_client *client)  
  69. {  
  70.     //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);  
  71.     device_destroy(class, MKDEV(major, 0));  
  72.     class_destroy(class);  
  73.     unregister_chrdev(major, "at24cxx");  
  74.           
  75.     return 0;  
  76. }  
  77.   
  78. static const struct i2c_device_id at24cxx_id_table[] = {  
  79.     { "at24c08", 0 },  
  80.     {}  
  81. };  
  82.   
  83.   
  84. /* 1. 分配/設置i2c_driver */  
  85. static struct i2c_driver at24cxx_driver = {  
  86.     .driver = {  
  87.         .name   = "100ask",  
  88.         .owner  = THIS_MODULE,  
  89.     },  
  90.     .probe      = at24cxx_probe,  
  91.     .remove     = __devexit_p(at24cxx_remove),  
  92.     .id_table   = at24cxx_id_table,  
  93. };  
  94.   
  95. static int at24cxx_drv_init(void)  
  96. {  
  97.     /* 2. 注冊i2c_driver */  
  98.     i2c_add_driver(&at24cxx_driver);  
  99.       
  100.     return 0;  
  101. }  
  102.   
  103. static void at24cxx_drv_exit(void)  
  104. {  
  105.     i2c_del_driver(&at24cxx_driver);  
  106. }  
  107.   
  108.   
  109. module_init(at24cxx_drv_init);  
  110. module_exit(at24cxx_drv_exit);  
  111. MODULE_LICENSE("GPL");  
    驅動側的程序,思路:

        1、分配一個 i2c_driver 結構體

        2、設置它的名字,僅僅是出現在 sysfs 的目錄名

        3、設置 id_table ,根據 id_table 與 client 的名字進行匹配

        4、設置 probe 函數,配對成功后調用它,我們往往在這里面創建類,在類下面創建設備,讓Mdev自動幫我們創建設備節點。

        5、設備 remove 函數  ,與 i2c_add_driver 相反,我們在remove函數里 將driver刪除掉,使用 i2c_del_driver 。

    還有需要注意的是,我們在Probe函數里,注冊了一個字符設備驅動,在它的 read 和 write 函數里,用到了兩個簡單的函數,i2c_smbus_read_byte_data 與 i2c_smbus_write_byte_data,這兩個函數是什么東西,我們之前用過 i2c_transfer 使用 mesg 結構體進行傳輸過,i2c_transfer 最終會調用到 adapter 里我們設置的傳輸函數。這里就不再分析代碼了,根據我個人的理解,我們現在使用的 i2c_smbus_read_byte_data 等函數,是對 i2c_transfer 與 meag 進行了更深層次的封裝,我們以前向讀數據時,需要用兩個mesg 結構體,因為要先發送地址,再讀取數據,i2c_smbus_read_byte_data 函數我們只需要提供一個要讀取的地址,它就會幫我們去構造那兩個Mesg 結構體,總而言之,我們寫程序更方便了。那么,i2c_smbus_read_byte_data 等函數對應的時序是怎樣的,是否符合我們的要求,如何選取?看幫助文檔 smbus-protocol 

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

SMBus Protocol Summary

======================

The following is a summary of the SMBus protocol. It applies to
all revisions of the protocol (1.0, 1.1, and 2.0).
Certain protocol features which are not supported by
this package are briefly described at the end of this document.

Some adapters understand only the SMBus (System Management Bus) protocol,
which is a subset from the I2C protocol. Fortunately, many devices use
only the same subset, which makes it possible to put them on an SMBus.

If you write a driver for some I2C device, please try to use the SMBus
commands if at all possible (if the device uses only that subset of the
I2C protocol). This makes it possible to use the device driver on both
SMBus adapters and I2C adapters (the SMBus command set is automatically
translated to I2C on I2C adapters, but plain I2C commands can not be
handled at all on most pure SMBus adapters).

Below is a list of SMBus protocol operations, and the functions executing
them.  Note that the names used in the SMBus protocol specifications usually
don't match these function names.  For some of the operations which pass a
single data byte, the functions using SMBus protocol operation names execute
a different protocol operation entirely.

Key to symbols
==============

S     (1 bit) : Start bit
P     (1 bit) : Stop bit
Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.
A, NA (1 bit) : Accept and reverse accept bit. 
Addr  (7 bits): I2C 7 bit address. Note that this can be expanded as usual to 
                get a 10 bit I2C address.
Comm  (8 bits): Command byte, a data byte which often selects a register on
                the device.
Data  (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh
                for 16 bit data.
Count (8 bits): A data byte containing the length of a block operation.

[..]: Data sent by I2C device, as opposed to data sent by the host adapter.

SMBus Quick Command
===================

This sends a single bit to the device, at the place of the Rd/Wr bit.

A Addr Rd/Wr [A] P

SMBus Receive Byte:  i2c_smbus_read_byte()
==========================================

This reads a single byte from a device, without specifying a device
register. Some devices are so simple that this interface is enough; for
others, it is a shorthand if you want to read the same register as in
the previous SMBus command.

S Addr Rd [A] [Data] NA P

SMBus Send Byte:  i2c_smbus_write_byte()
========================================

This operation is the reverse of Receive Byte: it sends a single byte
to a device.  See Receive Byte for more information.

S Addr Wr [A] Data [A] P

SMBus Read Byte:  i2c_smbus_read_byte_data()
============================================

This reads a single byte from a device, from a designated register.
The register is specified through the Comm byte.


S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P

SMBus Read Word:  i2c_smbus_read_word_data()
============================================

This operation is very like Read Byte; again, data is read from a
device, from a designated register that is specified through the Comm
byte. But this time, the data is a complete word (16 bits).

S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P

SMBus Write Byte:  i2c_smbus_write_byte_data()
==============================================

This writes a single byte to a device, to a designated register. The
register is specified through the Comm byte. This is the opposite of
the Read Byte operation.

S Addr Wr [A] Comm [A] Data [A] P

SMBus Write Word:  i2c_smbus_write_word_data()
==============================================

This is the opposite of the Read Word operation. 16 bits
of data is written to a device, to the designated register that is
specified through the Comm byte. 

S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P

SMBus Process Call:  i2c_smbus_process_call()
=============================================

This command selects a device register (through the Comm byte), sends
16 bits of data to it, and reads 16 bits of data in return.

S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] 
                             S Addr Rd [A] [DataLow] A [DataHigh] NA P


SMBus Block Read:  i2c_smbus_read_block_data()
==============================================

This command reads a block of up to 32 bytes from a device, from a 
designated register that is specified through the Comm byte. The amount
of data is specified by the device in the Count byte.

S Addr Wr [A] Comm [A] 
           S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P


SMBus Block Write:  i2c_smbus_write_block_data()
================================================

The opposite of the Block Read command, this writes up to 32 bytes to 
a device, to a designated register that is specified through the
Comm byte. The amount of data is specified in the Count byte.

S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P

SMBus Block Write - Block Read Process Call
===========================================

SMBus Block Write - Block Read Process Call was introduced in
Revision 2.0 of the specification.

This command selects a device register (through the Comm byte), sends
1 to 31 bytes of data to it, and reads 1 to 31 bytes of data in return.

S Addr Wr [A] Comm [A] Count [A] Data [A] ...
                             S Addr Rd [A] [Count] A [Data] ... A P


SMBus Host Notify
=================

This command is sent from a SMBus device acting as a master to the
SMBus host acting as a slave.
It is the same form as Write Word, with the command code replaced by the
alerting device's address.

[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]

Packet Error Checking (PEC)
===========================

Packet Error Checking was introduced in Revision 1.1 of the specification.

PEC adds a CRC-8 error-checking byte to transfers using it, immediately
before the terminating STOP.

Address Resolution Protocol (ARP)
=================================

The Address Resolution Protocol was introduced in Revision 2.0 of
the specification. It is a higher-layer protocol which uses the
messages above.

ARP adds device enumeration and dynamic address assignment to
the protocol. All ARP communications use slave address 0x61 and
require PEC checksums.

I2C Block Transactions
======================

The following I2C block transactions are supported by the
SMBus layer and are described here for completeness.
They are *NOT* defined by the SMBus specification.

I2C block transactions do not limit the number of bytes transferred
but the SMBus layer places a limit of 32 bytes.

I2C Block Read:  i2c_smbus_read_i2c_block_data()
================================================

This command reads a block of bytes from a device, from a 
designated register that is specified through the Comm byte.


S Addr Wr [A] Comm [A] 
           S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P





I2C Block Read (2 Comm bytes)
=============================


This command reads a block of bytes from a device, from a 
designated register that is specified through the two Comm bytes.


S Addr Wr [A] Comm1 [A] Comm2 [A] 
           S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P





I2C Block Write:  i2c_smbus_write_i2c_block_data()
==================================================


The opposite of the Block Read command, this writes bytes to 
a device, to a designated register that is specified through the
Comm byte. Note that command lengths of 0, 2, or more bytes are
supported as they are indistinguishable from data.


S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P


5、寫adapter驅動程序

        i2c協議的簡單介紹,以及手寫 adapter驅動程序 放到下一篇文章里吧~



轉自:http://blog.csdn.net/lizuobin2/article/details/51694574


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM