zoukankan      html  css  js  c++  java
  • linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析

    在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinci.c)。标题党请见谅!

    其实i2c接口非常的简单,即使用51单片的gpio来模拟i2c,编写一个e2prom或者其他i2c接口的驱动程序,也不是什么难事,几百行代码就能搞定。

    但是Linux的i2c驱动体系结构却有相当的复杂度,不管是叫linux i2c驱动还是单片机i2c驱动,其根本还是操作soc芯片内部的i2c模块(也叫i2c adapter)(读写i2c相关的寄存器)来产生start、stop还有ack信号而已。

    linux设备驱动到底复杂在什么地方?

    假设soc芯片dm368有两个i2c adapter(368内部真正只有一个i2c模块):i2c_adapter1,i2c_adapter1;然后外部有三个i2c接口的设备i2c_device1,i2c_device2,i2c_device3。

    现在要求在裸机下写出他们的驱动函数。那么肯定要写出6个不同的驱动函数:

     

    1. i2c_adapter1_ReadWrite_i2c_device1(); 
    2. i2c_adapter1_ReadWrite_i2c_device2() 
    3. i2c_adapter1_ReadWrite_i2c_device3() 
    4. i2c_adapter2_ReadWrite_i2c_device1() 
    5. i2c_adapter2_ReadWrite_i2c_device2() 
    6. i2c_adapter2_ReadWrite_i2c_device3() 
    1. i2c_adapter1_ReadWrite_i2c_device1(); 
    2. i2c_adapter1_ReadWrite_i2c_device2() 
    3. i2c_adapter1_ReadWrite_i2c_device3() 
    4. i2c_adapter2_ReadWrite_i2c_device1() 
    5. i2c_adapter2_ReadWrite_i2c_device2() 
    6. i2c_adapter2_ReadWrite_i2c_device3() 



    设想一共有m个i2c adapter和n个外设i2c device,那么将需要m*n个驱动。并且这m*n个驱动程序必要会有很大部分重复的代码,而且不利于驱动程序的移植。

     

    如果采用adapter和device分离的思想来写这样的驱动会是怎样呢?

    图1

    这样分离之后,只需要m+n个驱动,而且Adapter和Device的几乎没有耦合性,增加一个Adapter或者device并不会影响其余的驱动。

    这就是分离思想带来的好处。除此之外,linux虽然是C写的,但是大量使用了面向对象的变成方法(可以理解为分层的思想),仅仅分离细想和分层思想的引入,就大大增加了linux设备驱动的复杂度。

     

    linux驱动中 i2c驱动架构

    图2

    上图完整的描述了linux i2c驱动架构,虽然I2C硬件体系结构比较简单,但是i2c体系结构在linux中的实现却相当复杂。那么我们如何编写特定i2c接口器件(比如,ov2715,需要i2c来配置寄存器)的驱动程序?就是说上述架构中的那些部分需要我们完成,而哪些是linux内核已经完善的或者是芯片提供商(TI davinci平台已经做好的)已经提供的?

    架构层次分类

    第一层:提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层

    第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层

    第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备的attach_adapter()、detach_adapter()方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中的driver驱动层

    第四层:实现i2c设备所对应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,eeprom和ov2715显然不是同一类的device,所以要实现具体设备device的write()、read()、ioctl()等方法,赋值给file_operations,然后注册字符设备(多数是字符设备)。覆盖图中的driver驱动层

    第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成所有总线bus,如usb、pci、i2c等等。并且总线bus中的【与特定硬件相关的代码】已由芯片提供商编写完成,例如TI davinci平台i2c总线bus与硬件相关的代码在内核目录/drivers/i2c/buses下的i2c-davinci.c源文件中;而三星的s3c-2440平台i2c总线bus为/drivers/i2c/buses/i2c-s3c2410.c

    第三第四层又叫设备驱动层与特定device相干的就需要驱动工程师来实现了。

    明确了方向后,再来具体分析。

    具体分析

    i2c_adapter与i2c_client的关系与i2c硬件体系中设配器与设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备device,所以相应的,i2c_adapter也可以被多个i2c_client依附,在i2c_adapter中包含i2c_client的链表。同一类的i2c设备device对应一个驱动driver。driver与device的关系是一对多的关系。

    现在,我们就来看一下这几个重要的结构体,分别是i2c_driver i2c_client i2c_adapter,也可以先忽略他们,待会回过头来看会更容易理解

    1、i2c_driver

     

    1. struct i2c_driver { 
    2.     int id; 
    3.     unsigned int class
    4.  
    5.     int (*attach_adapter)(struct i2c_adapter *); 
    6.     int (*detach_adapter)(struct i2c_adapter *); 
    7.  
    8.     int (*detach_client)(struct i2c_client *); 
    9.  
    10.     int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); 
    11.     struct device_driver driver; 
    12.     struct list_head list; 
    13. }; 
    1. struct i2c_driver { 
    2.     int id; 
    3.     unsigned int class
    4.  
    5.     int (*attach_adapter)(struct i2c_adapter *); 
    6.     int (*detach_adapter)(struct i2c_adapter *); 
    7.  
    8.     int (*detach_client)(struct i2c_client *); 
    9.  
    10.     int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); 
    11.     struct device_driver driver; 
    12.     struct list_head list; 
    13. }; 



    2、i2c_client

     

    1. struct i2c_client { 
    2.     unsigned int flags;     /* div., see below      */ 
    3.     unsigned short addr;        /* chip address - NOTE: 7bit    */ 
    4.                     /* addresses are stored in the  */ 
    5.                     /* _LOWER_ 7 bits       */ 
    6.     struct i2c_adapter *adapter;    /* the adapter we sit on    */ 
    7.     struct i2c_driver *driver;  /* and our access routines  */ 
    8.     int usage_count;        /* How many accesses currently  */ 
    9.                     /* to the client        */ 
    10.     struct device dev;      /* the device structure     */ 
    11.     struct list_head list; 
    12.     char name[I2C_NAME_SIZE]; 
    13.     struct completion released; 
    14. }; 
    1. struct i2c_client { 
    2.     unsigned int flags;     /* div., see below      */ 
    3.     unsigned short addr;        /* chip address - NOTE: 7bit    */ 
    4.                     /* addresses are stored in the  */ 
    5.                     /* _LOWER_ 7 bits       */ 
    6.     struct i2c_adapter *adapter;    /* the adapter we sit on    */ 
    7.     struct i2c_driver *driver;  /* and our access routines  */ 
    8.     int usage_count;        /* How many accesses currently  */ 
    9.                     /* to the client        */ 
    10.     struct device dev;      /* the device structure     */ 
    11.     struct list_head list; 
    12.     char name[I2C_NAME_SIZE]; 
    13.     struct completion released; 
    14. }; 



    3、i2c_adapter

     

     

    1. struct i2c_adapter { 
    2.     struct module *owner; 
    3.     unsigned int id; 
    4.     unsigned int class
    5.     struct i2c_algorithm *algo;/* the algorithm to access the bus   */ 
    6.     void *algo_data; 
    7.  
    8.     /* --- administration stuff. */ 
    9.     int (*client_register)(struct i2c_client *); 
    10.     int (*client_unregister)(struct i2c_client *); 
    11.  
    12.     /* data fields that are valid for all devices   */ 
    13.     struct mutex bus_lock; 
    14.     struct mutex clist_lock; 
    15.  
    16.     int timeout; 
    17.     int retries; 
    18.     struct device dev;      /* the adapter device */ 
    19.     struct class_device class_dev;  /* the class device */ 
    20.  
    21.     int nr; 
    22.     struct list_head clients; 
    23.     struct list_head list; 
    24.     char name[I2C_NAME_SIZE]; 
    25.     struct completion dev_released; 
    26.     struct completion class_dev_released; 
    27. }; 
    1. struct i2c_adapter { 
    2.     struct module *owner; 
    3.     unsigned int id; 
    4.     unsigned int class
    5.     struct i2c_algorithm *algo;/* the algorithm to access the bus   */ 
    6.     void *algo_data; 
    7.  
    8.     /* --- administration stuff. */ 
    9.     int (*client_register)(struct i2c_client *); 
    10.     int (*client_unregister)(struct i2c_client *); 
    11.  
    12.     /* data fields that are valid for all devices   */ 
    13.     struct mutex bus_lock; 
    14.     struct mutex clist_lock; 
    15.  
    16.     int timeout; 
    17.     int retries; 
    18.     struct device dev;      /* the adapter device */ 
    19.     struct class_device class_dev;  /* the class device */ 
    20.  
    21.     int nr; 
    22.     struct list_head clients; 
    23.     struct list_head list; 
    24.     char name[I2C_NAME_SIZE]; 
    25.     struct completion dev_released; 
    26.     struct completion class_dev_released; 
    27. }; 



     

    4、i2c_algorithm

     

    1. struct i2c_algorithm { 
    2.     int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,  
    3.                        int num); 
    4.     int (*slave_send)(struct i2c_adapter *,char*,int); 
    5.     int (*slave_recv)(struct i2c_adapter *,char*,int); 
    6.     u32 (*functionality) (struct i2c_adapter *); 
    7. }; 
    1. struct i2c_algorithm { 
    2.     int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,  
    3.                        int num); 
    4.     int (*slave_send)(struct i2c_adapter *,char*,int); 
    5.     int (*slave_recv)(struct i2c_adapter *,char*,int); 
    6.     u32 (*functionality) (struct i2c_adapter *); 
    7. }; 



     

    【i2c_adapter与i2c_algorithm】

    i2c_adapter对应与物理上的一个适配器,而i2c_algorithm对应一套通信方法,一个i2c适配器需要i2c_algorithm中提供的(i2c_algorithm中的又是更下层与硬件相关的代码提供)通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指针。

    i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start stop ack信号,以i2c_msg(即i2c消息)为单位发送和接收通信数据。i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体

     

    1. /*
    2. * I2C Message - used for pure i2c transaction, also from /dev interface
    3. */ 
    4. struct i2c_msg { 
    5.     __u16 addr; /* slave address            */ 
    6.     __u16 flags;         
    7.     __u16 len;      /* msg length               */ 
    8.     __u8 *buf;      /* pointer to msg data          */ 
    9. }; 
    1. /*
    2. * I2C Message - used for pure i2c transaction, also from /dev interface
    3. */ 
    4. struct i2c_msg { 
    5.     __u16 addr; /* slave address            */ 
    6.     __u16 flags;         
    7.     __u16 len;      /* msg length               */ 
    8.     __u8 *buf;      /* pointer to msg data          */ 
    9. }; 



    【i2c_driver和i2c_client】

     

    i2c_driver对应一套驱动方法,其主要函数是attach_adapter()和detach_client(),i2c_client对应真实的i2c物理设备device,每个i2c设备都需要一个i2c_client来描述,i2c_driver与i2c_client的关系是一对多。一个i2c_driver上可以支持多个同等类型的i2c_client.

    【i2c_adapter和i2c_client】

    i2c_adapter和i2c_client的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备,所以i2c_adapter中包含依附于它的i2c_client的链表。

    从图1图2中都可以看出,linux内核对i2c架构抽象了一个叫核心层core的中间件,它分离了设备驱动device driver和硬件控制的实现细节(如操作i2c的寄存器),core层不但为上面的设备驱动提供封装后的内核注册函数,而且还为小面的硬件时间提供注册接口(也就是i2c总线注册接口),可以说core层起到了承上启下的作用。

    我们先看一下i2c-core为外部提供的核心函数(选取部分),i2c-core对应的源文件为i2c-core.c,位于内核目录/driver/i2c/i2c-core.c

     

    1. EXPORT_SYMBOL(i2c_add_adapter); 
    2. EXPORT_SYMBOL(i2c_del_adapter); 
    3. EXPORT_SYMBOL(i2c_del_driver); 
    4. EXPORT_SYMBOL(i2c_attach_client); 
    5. EXPORT_SYMBOL(i2c_detach_client); 
    6.  
    7. EXPORT_SYMBOL(i2c_transfer); 
    1. EXPORT_SYMBOL(i2c_add_adapter); 
    2. EXPORT_SYMBOL(i2c_del_adapter); 
    3. EXPORT_SYMBOL(i2c_del_driver); 
    4. EXPORT_SYMBOL(i2c_attach_client); 
    5. EXPORT_SYMBOL(i2c_detach_client); 
    6.  
    7. EXPORT_SYMBOL(i2c_transfer); 



     

    如果看过i2c设备驱动程序的人一定对上面几个函数比较熟悉。

     

    i2c_transfer()函数,i2c_transfer()函数本身并不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正的驱动硬件流程,代码清单如下,不重要的已删除。

     

    1. int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num) 
    2.     int ret; 
    3.     if (adap->algo->master_xfer) {//如果master_xfer函数存在,则调用,否则返回错误 
    4.         ret = adap->algo->master_xfer(adap,msgs,num);//这个函数在硬件相关的代码中给algorithm赋值 
    5.         return ret; 
    6.     } else 
    7.         return -ENOSYS; 
    8.     } 
    1. int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num) 
    2.     int ret; 
    3.     if (adap->algo->master_xfer) {//如果master_xfer函数存在,则调用,否则返回错误 
    4.         ret = adap->algo->master_xfer(adap,msgs,num);//这个函数在硬件相关的代码中给algorithm赋值 
    5.         return ret; 
    6.     } else 
    7.         return -ENOSYS; 
    8.     } 



    当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。相反的,在client被取消关联的时候,sysfs文件和设备也被注销,驱动开发人员需开发i2c设备驱动时,需要调用下列函数。程序清单如下

    1. int i2c_attach_client(struct i2c_client *client) 
    2.     ... 
    3.     device_register(&client->dev); 
    4.     device_create_file(&client->dev, &dev_attr_client_name); 
    5.     ... 
    6.     return 0; 
    1. int i2c_attach_client(struct i2c_client *client) 
    2.     ... 
    3.     device_register(&client->dev); 
    4.     device_create_file(&client->dev, &dev_attr_client_name); 
    5.     ... 
    6.     return 0; 



    1. int i2c_detach_client(struct i2c_client *client) 
    2.     ... 
    3.     device_remove_file(&client->dev, &dev_attr_client_name); 
    4.     device_unregister(&client->dev); 
    5.     ... 
    6.     return res; 
    1. int i2c_detach_client(struct i2c_client *client) 
    2.     ... 
    3.     device_remove_file(&client->dev, &dev_attr_client_name); 
    4.     device_unregister(&client->dev); 
    5.     ... 
    6.     return res; 



    i2c_add_adapter()函数和i2c_del_adapter()在i2c-davinci.c中有调用,稍后分析

    1. /* -----
    2. * i2c_add_adapter is called from within the algorithm layer,
    3. * when a new hw adapter registers. A new device is register to be
    4. * available for clients.
    5. */ 
    6. int i2c_add_adapter(struct i2c_adapter *adap) 
    7.     ... 
    8.     device_register(&adap->dev); 
    9.     device_create_file(&adap->dev, &dev_attr_name); 
    10.     ... 
    11.     /* inform drivers of new adapters */ 
    12.     list_for_each(item,&drivers) { 
    13.         driver = list_entry(item, struct i2c_driver, list); 
    14.         if (driver->attach_adapter) 
    15.             /* We ignore the return code; if it fails, too bad */ 
    16.             driver->attach_adapter(adap); 
    17.     } 
    18.     ... 
    1. /* -----
    2. * i2c_add_adapter is called from within the algorithm layer,
    3. * when a new hw adapter registers. A new device is register to be
    4. * available for clients.
    5. */ 
    6. int i2c_add_adapter(struct i2c_adapter *adap) 
    7.     ... 
    8.     device_register(&adap->dev); 
    9.     device_create_file(&adap->dev, &dev_attr_name); 
    10.     ... 
    11.     /* inform drivers of new adapters */ 
    12.     list_for_each(item,&drivers) { 
    13.         driver = list_entry(item, struct i2c_driver, list); 
    14.         if (driver->attach_adapter) 
    15.             /* We ignore the return code; if it fails, too bad */ 
    16.             driver->attach_adapter(adap); 
    17.     } 
    18.     ... 



    1. int i2c_del_adapter(struct i2c_adapter *adap) 
    2.     ... 
    3.     list_for_each(item,&drivers) { 
    4.         driver = list_entry(item, struct i2c_driver, list); 
    5.         if (driver->detach_adapter) 
    6.             if ((res = driver->detach_adapter(adap))) { 
    7.             } 
    8.     } 
    9.     ... 
    10.     list_for_each_safe(item, _n, &adap->clients) { 
    11.         client = list_entry(item, struct i2c_client, list); 
    12.  
    13.         if ((res=client->driver->detach_client(client))) { 
    14.  
    15.         } 
    16.     } 
    17.     ... 
    18.     device_remove_file(&adap->dev, &dev_attr_name); 
    19.     device_unregister(&adap->dev); 
    20.  
    1. int i2c_del_adapter(struct i2c_adapter *adap) 
    2.     ... 
    3.     list_for_each(item,&drivers) { 
    4.         driver = list_entry(item, struct i2c_driver, list); 
    5.         if (driver->detach_adapter) 
    6.             if ((res = driver->detach_adapter(adap))) { 
    7.             } 
    8.     } 
    9.     ... 
    10.     list_for_each_safe(item, _n, &adap->clients) { 
    11.         client = list_entry(item, struct i2c_client, list); 
    12.  
    13.         if ((res=client->driver->detach_client(client))) { 
    14.  
    15.         } 
    16.     } 
    17.     ... 
    18.     device_remove_file(&adap->dev, &dev_attr_name); 
    19.     device_unregister(&adap->dev); 
    20.  



    i2c-davinci.c是实现与硬件相关功能的代码集合,这部分是与平台相关的,也叫做i2c总线驱动,这部分代码是这样添加到系统中的

    1. static struct platform_driver davinci_i2c_driver = { 
    2.     .probe      = davinci_i2c_probe, 
    3.     .remove     = davinci_i2c_remove, 
    4.     .driver     = { 
    5.         .name   = "i2c_davinci"
    6.         .owner  = THIS_MODULE, 
    7.     }, 
    8. }; 
    9.  
    10. /* I2C may be needed to bring up other drivers */ 
    11. static int __init davinci_i2c_init_driver(void
    12.     return platform_driver_register(&davinci_i2c_driver); 
    13. subsys_initcall(davinci_i2c_init_driver); 
    14.  
    15. static void __exit davinci_i2c_exit_driver(void
    16.     platform_driver_unregister(&davinci_i2c_driver); 
    17. module_exit(davinci_i2c_exit_driver); 
    1. static struct platform_driver davinci_i2c_driver = { 
    2.     .probe      = davinci_i2c_probe, 
    3.     .remove     = davinci_i2c_remove, 
    4.     .driver     = { 
    5.         .name   = "i2c_davinci"
    6.         .owner  = THIS_MODULE, 
    7.     }, 
    8. }; 
    9.  
    10. /* I2C may be needed to bring up other drivers */ 
    11. static int __init davinci_i2c_init_driver(void
    12.     return platform_driver_register(&davinci_i2c_driver); 
    13. subsys_initcall(davinci_i2c_init_driver); 
    14.  
    15. static void __exit davinci_i2c_exit_driver(void
    16.     platform_driver_unregister(&davinci_i2c_driver); 
    17. module_exit(davinci_i2c_exit_driver); 



    并且,i2c适配器控制硬件发送接收数据的函数在这里赋值给i2c-algorithm,i2c_davinci_xfer稍加修改就可以在裸机中控制i2c适配器

    1. static struct i2c_algorithm i2c_davinci_algo = { 
    2.     .master_xfer    = i2c_davinci_xfer, 
    3.     .functionality  = i2c_davinci_func, 
    4. }; 
    1. static struct i2c_algorithm i2c_davinci_algo = { 
    2.     .master_xfer    = i2c_davinci_xfer, 
    3.     .functionality  = i2c_davinci_func, 
    4. }; 



    然后在davinci_i2c_probe函数中,将i2c_davinci_algo添加到添加到algorithm系统中

     

     

    1. adap->algo = &i2c_davinci_algo; 
    1. adap->algo = &i2c_davinci_algo; 



    梳理图

    有时候代码比任何文字描述都来得直接,但是过多的代码展示反而让人觉得枯燥。这个时候,需要一幅图来梳理一下上面的内容,请看图3。

    图3

    好了,上面这些代码的展示是告诉我们,linux内核和芯片提供商为我们的的驱动程序提供了 i2c驱动的框架,以及框架底层与硬件相关的代码的实现。剩下的就是针对挂载在i2c两线上的i2c设备了device,如at24c02,例如ov2715,而编写的具体设备驱动了,这里的设备就是硬件接口外挂载的设备,而非硬件接口本身(soc硬件接口本身的驱动可以理解为总线驱动)。

    在理解了i2c驱动架构后,我们接下来再作两方面的分析工作:一是具体的i2c设备ov2715驱动源码分析,二是davinci平台的i2c总线驱动源码。

    ov2715设备i2c驱动源码分析

    ov2715为200万的CMOS Sensor,芯片的寄存器控制通过i2c接口完成,i2c设备地址为0x6c,寄存器地址为16位两个字节,寄存器值为8位一个字节,可以理解为一般的字符设备。
    该驱动程序并非只能用于ov2715,因此源码中存在支持多个设备地址的机制。
    该字符设备的用到的结构体有两个,如下

     

    1. typedef struct 
    2.  
    3.   int devAddr; 
    4.  
    5.   struct i2c_client client;   //!< Data structure containing general access routines. 
    6.   struct i2c_driver driver;   //!< Data structure containing information specific to each client. 
    7.    
    8.   char name[20]; 
    9.   int nameSize; 
    10.   int users; 
    11.    
    12. } I2C_Obj; 
    1. typedef struct 
    2.  
    3.   int devAddr; 
    4.  
    5.   struct i2c_client client;   //!< Data structure containing general access routines. 
    6.   struct i2c_driver driver;   //!< Data structure containing information specific to each client. 
    7.    
    8.   char name[20]; 
    9.   int nameSize; 
    10.   int users; 
    11.    
    12. } I2C_Obj; 



     

    1. #define I2C_DEV_MAX_ADDR  (0xFF) 
    2. #define I2C_TRANSFER_BUF_SIZE_MAX   (256) 
    3. typedef struct 
    4.  
    5.   struct cdev cdev;             /* Char device structure    */ 
    6.   int     major; 
    7.   struct semaphore semLock; 
    8.      
    9.   I2C_Obj *pObj[I2C_DEV_MAX_ADDR]; 
    10.  
    11.   uint8_t reg[I2C_TRANSFER_BUF_SIZE_MAX]; 
    12.   uint16_t reg16[I2C_TRANSFER_BUF_SIZE_MAX]; 
    13.   uint8_t buffer[I2C_TRANSFER_BUF_SIZE_MAX*4]; 
    14.    
    15. } I2C_Dev; 
    1. #define I2C_DEV_MAX_ADDR  (0xFF) 
    2. #define I2C_TRANSFER_BUF_SIZE_MAX   (256) 
    3. typedef struct 
    4.  
    5.   struct cdev cdev;             /* Char device structure    */ 
    6.   int     major; 
    7.   struct semaphore semLock; 
    8.      
    9.   I2C_Obj *pObj[I2C_DEV_MAX_ADDR]; 
    10.  
    11.   uint8_t reg[I2C_TRANSFER_BUF_SIZE_MAX]; 
    12.   uint16_t reg16[I2C_TRANSFER_BUF_SIZE_MAX]; 
    13.   uint8_t buffer[I2C_TRANSFER_BUF_SIZE_MAX*4]; 
    14.    
    15. } I2C_Dev; 




    一个I2C_Obj描述一个设备,devAddr保存该设备的地址,I2C_Obj内嵌到结构体I2C_Dev,I2C_Dev管理该驱动所支持的所有设备,尽管支持多个设备,但i2c适配器只有一个,因此需要一个信号量semLock来保护该共享资源,同时只能向一个设备读写数据。成员变量cdev是我们所熟知的,每个字符设备驱动中几乎总会有一个结构体包含它,major用于保存该驱动的主设备编号,reg数组为寄存器地址为8位的寄存器地址缓冲区,reg16为寄存器地址为16的寄存器地址缓冲区。同时可以读写多个寄存器地址的值。buffer为读写的寄存器值

     

    使用I2C_Dev构建一个全局变量gI2C_dev,在驱动的多个地方均需要它。
    下面先从字符设备的基本框架入手,然后深入该驱动的细节部分。
    首先是该字符设备的初始化和退出函数

     

     

    1. int I2C_devInit(void
    2.   int     result, i; 
    3.   dev_t   dev = 0; 
    4.  
    5.   result = alloc_chrdev_region(&dev, 0, 1, I2C_DRV_NAME);//分配字符设备空间 
    6.    
    7.   for(i=0; i<I2C_DEV_MAX_ADDR; i++) 
    8.   { 
    9.     gI2C_dev.pObj[i]=NULL; 
    10.   } 
    11.  
    12.   gI2C_dev.major = MAJOR(dev);//保存设备主编号 
    13.   sema_init(&gI2C_dev.semLock, 1);//信号量初始化 
    14.   cdev_init(&gI2C_dev.cdev, &gI2C_devFileOps);//使用gI2C_devFileOps初始化该字符设备,gI2C_devFileOps见下文 
    15.   gI2C_dev.cdev.owner = THIS_MODULE;//常规赋值 
    16. gI2C_dev.cdev.ops = &gI2C_devFileOps;//常规赋值 result = cdev_add(&gI2C_dev.cdev, dev, 1);//添加设备到字符设备中 return result;}void I2C_devExit(void){ dev_t devno = MKDEV(gI2C_dev.major, 0); cdev_del(&gI2C_dev.cdev);//从字符设备中删除该设备 unregister_chrdev_region(devno, 1);//回收空间} 
    17. gI2c_devFileOps全局变量,驱动初始化会用到该结构体变量 
    18. struct file_operations gI2C_devFileOps = { 
    19.   .owner = THIS_MODULE, 
    20.   .open = I2C_devOpen, 
    21.   .release = I2C_devRelease, 
    22.   .ioctl = I2C_devIoctl, 
    23. }; 
    1. int I2C_devInit(void
    2.   int     result, i; 
    3.   dev_t   dev = 0; 
    4.  
    5.   result = alloc_chrdev_region(&dev, 0, 1, I2C_DRV_NAME);//分配字符设备空间 
    6.    
    7.   for(i=0; i<I2C_DEV_MAX_ADDR; i++) 
    8.   { 
    9.     gI2C_dev.pObj[i]=NULL; 
    10.   } 
    11.  
    12.   gI2C_dev.major = MAJOR(dev);//保存设备主编号 
    13.   sema_init(&gI2C_dev.semLock, 1);//信号量初始化 
    14.   cdev_init(&gI2C_dev.cdev, &gI2C_devFileOps);//使用gI2C_devFileOps初始化该字符设备,gI2C_devFileOps见下文 
    15.   gI2C_dev.cdev.owner = THIS_MODULE;//常规赋值 
    16. gI2C_dev.cdev.ops = &gI2C_devFileOps;//常规赋值 result = cdev_add(&gI2C_dev.cdev, dev, 1);//添加设备到字符设备中 return result;}void I2C_devExit(void){ dev_t devno = MKDEV(gI2C_dev.major, 0); cdev_del(&gI2C_dev.cdev);//从字符设备中删除该设备 unregister_chrdev_region(devno, 1);//回收空间} 
    17. gI2c_devFileOps全局变量,驱动初始化会用到该结构体变量 
    18. struct file_operations gI2C_devFileOps = { 
    19.   .owner = THIS_MODULE, 
    20.   .open = I2C_devOpen, 
    21.   .release = I2C_devRelease, 
    22.   .ioctl = I2C_devIoctl, 
    23. }; 



    该驱动只实现了三个函数,open,release和ioctl,对于i2c设备来说,这已经足够了。
    在I2C_devOpen和I2C_devOpen中并没有做实际的工作,重要的工作均在I2C_devIoctl这个ioctl中完成。I2C_devIoctl代码展示(将影响结构条理的代码去掉,稍后在做详细分析)

     

     

    1. int I2C_devIoctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) 
    2.   I2C_Obj *pObj; 
    3.   int status=0; 
    4.   I2C_TransferPrm transferPrm; 
    5.    
    6.   pObj = (I2C_Obj *)filp->private_data; 
    7.  
    8.   if(!I2C_IOCTL_CMD_IS_VALID(cmd)) 
    9.     return -1; 
    10.   cmd = I2C_IOCTL_CMD_GET(cmd);//cmd命令转换,防止混淆,具体原因参见上一篇文章:ioctl中的cmd 
    11.  
    12.   down_interruptible(&gI2C_dev.semLock);      //信号量down 
    13.    
    14.   switch(cmd) 
    15.   { 
    16.     case I2C_CMD_SET_DEV_ADDR://命令1,设置设备地址 
    17.       filp->private_data = I2C_create(arg); 
    18.  
    19.     case I2C_CMD_WRITE:  //命令2,写寄存器值 
    20.        
    21.       status = copy_from_user(&transferPrm, (void *)arg, sizeof(transferPrm)); 
    22.       ... 
    23.              
    24.       break
    25.     case I2C_CMD_READ:  //命令3,读寄存器值 
    26.      
    27.       status = copy_from_user(&transferPrm, (void *)arg, sizeof(transferPrm)); 
    28.       ... 
    29.        
    30.       break
    31.     default
    32.       status = -1; 
    33.       break;     
    34.   } 
    35.  
    36.   up(&gI2C_dev.semLock);      //信号量up 
    37.  
    38.   return status; 
    1. int I2C_devIoctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) 
    2.   I2C_Obj *pObj; 
    3.   int status=0; 
    4.   I2C_TransferPrm transferPrm; 
    5.    
    6.   pObj = (I2C_Obj *)filp->private_data; 
    7.  
    8.   if(!I2C_IOCTL_CMD_IS_VALID(cmd)) 
    9.     return -1; 
    10.   cmd = I2C_IOCTL_CMD_GET(cmd);//cmd命令转换,防止混淆,具体原因参见上一篇文章:ioctl中的cmd 
    11.  
    12.   down_interruptible(&gI2C_dev.semLock);      //信号量down 
    13.    
    14.   switch(cmd) 
    15.   { 
    16.     case I2C_CMD_SET_DEV_ADDR://命令1,设置设备地址 
    17.       filp->private_data = I2C_create(arg); 
    18.  
    19.     case I2C_CMD_WRITE:  //命令2,写寄存器值 
    20.        
    21.       status = copy_from_user(&transferPrm, (void *)arg, sizeof(transferPrm)); 
    22.       ... 
    23.              
    24.       break
    25.     case I2C_CMD_READ:  //命令3,读寄存器值 
    26.      
    27.       status = copy_from_user(&transferPrm, (void *)arg, sizeof(transferPrm)); 
    28.       ... 
    29.        
    30.       break
    31.     default
    32.       status = -1; 
    33.       break;     
    34.   } 
    35.  
    36.   up(&gI2C_dev.semLock);      //信号量up 
    37.  
    38.   return status; 



    以上三个命令中最重要最复杂的是第一个I2C_CMD_SET_DEV_ADDR,设置设备地址,之所以重要和复杂,因为在I2C_create()函数中,将通过i2c-core提供的函数把该驱动程序和底层的i2c_adapter联系起来。下面是I2C_create()函数源码

     

     

    1. void *I2C_create(int devAddr) { 
    2.  
    3.     int ret; 
    4.     struct i2c_driver *driver; 
    5.     struct i2c_client *client = client; 
    6.     I2C_Obj *pObj; 
    7.  
    8.     devAddr >>= 1; 
    9.      
    10.     if(devAddr>I2C_DEV_MAX_ADDR)  //变量合法性判断 
    11.       return NULL; 
    12.     
    13.     if(gI2C_dev.pObj[devAddr]!=NULL) {  //变量合法性判断,如果该地址的设备已经创建,则调过,防止上层错误调用 
    14.       // already allocated, increment user count, and return the allocated handle 
    15.       gI2C_dev.pObj[devAddr]->users++; 
    16.       return gI2C_dev.pObj[devAddr]; 
    17.     } 
    18.      
    19.     pObj = (void*)kmalloc( sizeof(I2C_Obj), GFP_KERNEL); //为pObj分配空间 
    20.     gI2C_dev.pObj[devAddr] = pObj;  //将分配的空间地址保存在全局变量里 
    21.     memset(pObj, 0, sizeof(I2C_Obj)); 
    22.    
    23.     pObj->client.adapter = NULL; 
    24.     pObj->users++;    //用户基数,初始化为0,当前设为1 
    25.     pObj->devAddr = devAddr;  //保存设备地址 
    26.      
    27.     gI2C_curAddr = pObj->devAddr;  //gI2C_curAddr为全局的整型变量,用于保存当前的设备地址 
    28.     driver = &pObj->driver;  //将成员变量driver单独抽取出来,因为线面要使用driver来初始化驱动 
    29.  
    30.     pObj->nameSize=0;//i2c设备名称,注意,这里不是在/dev下面的设备节点名 
    31.     pObj->name[pObj->nameSize++] = 'I'
    32.     pObj->name[pObj->nameSize++] = '2'
    33.     pObj->name[pObj->nameSize++] = 'C'
    34.     pObj->name[pObj->nameSize++] = '_';    
    35.     pObj->name[pObj->nameSize++] = 'A' + ((pObj->devAddr >> 0) & 0xF); 
    36.     pObj->name[pObj->nameSize++] = 'B' + ((pObj->devAddr >> 4) & 0xF); 
    37.     pObj->name[pObj->nameSize++] = 0; 
    38.  
    39.     driver->driver.name = pObj->name; //保存刚才设置的name 
    40.     driver->id = I2C_DRIVERID_MISC; 
    41.     driver->attach_adapter = I2C_attachAdapter;   //这个很重要,将驱动连接到i2c适配器上,在后面分析 
    42.     driver->detach_client = I2C_detachClient;    //这个很重,在后面分析 
    43.  
    44.     if((ret = i2c_add_driver(driver)))  //使用i2c-core(i2c_register_driver函数)的接口,注册该驱动,i2c_add_driver实质调用了driver_register() 
    45.     { 
    46.         printk( KERN_ERR "I2C: ERROR: Driver registration failed (address=%x), module not inserted. ", pObj->devAddr); 
    47.     } 
    48.  
    49.     if(ret<0) { 
    50.  
    51.       gI2C_dev.pObj[pObj->devAddr] = NULL; 
    52.       kfree(pObj);     
    53.       return NULL; 
    54.     } 
    55.     return pObj; 
    1. void *I2C_create(int devAddr) { 
    2.  
    3.     int ret; 
    4.     struct i2c_driver *driver; 
    5.     struct i2c_client *client = client; 
    6.     I2C_Obj *pObj; 
    7.  
    8.     devAddr >>= 1; 
    9.      
    10.     if(devAddr>I2C_DEV_MAX_ADDR)  //变量合法性判断 
    11.       return NULL; 
    12.     
    13.     if(gI2C_dev.pObj[devAddr]!=NULL) {  //变量合法性判断,如果该地址的设备已经创建,则调过,防止上层错误调用 
    14.       // already allocated, increment user count, and return the allocated handle 
    15.       gI2C_dev.pObj[devAddr]->users++; 
    16.       return gI2C_dev.pObj[devAddr]; 
    17.     } 
    18.      
    19.     pObj = (void*)kmalloc( sizeof(I2C_Obj), GFP_KERNEL); //为pObj分配空间 
    20.     gI2C_dev.pObj[devAddr] = pObj;  //将分配的空间地址保存在全局变量里 
    21.     memset(pObj, 0, sizeof(I2C_Obj)); 
    22.    
    23.     pObj->client.adapter = NULL; 
    24.     pObj->users++;    //用户基数,初始化为0,当前设为1 
    25.     pObj->devAddr = devAddr;  //保存设备地址 
    26.      
    27.     gI2C_curAddr = pObj->devAddr;  //gI2C_curAddr为全局的整型变量,用于保存当前的设备地址 
    28.     driver = &pObj->driver;  //将成员变量driver单独抽取出来,因为线面要使用driver来初始化驱动 
    29.  
    30.     pObj->nameSize=0;//i2c设备名称,注意,这里不是在/dev下面的设备节点名 
    31.     pObj->name[pObj->nameSize++] = 'I'
    32.     pObj->name[pObj->nameSize++] = '2'
    33.     pObj->name[pObj->nameSize++] = 'C'
    34.     pObj->name[pObj->nameSize++] = '_';    
    35.     pObj->name[pObj->nameSize++] = 'A' + ((pObj->devAddr >> 0) & 0xF); 
    36.     pObj->name[pObj->nameSize++] = 'B' + ((pObj->devAddr >> 4) & 0xF); 
    37.     pObj->name[pObj->nameSize++] = 0; 
    38.  
    39.     driver->driver.name = pObj->name; //保存刚才设置的name 
    40.     driver->id = I2C_DRIVERID_MISC; 
    41.     driver->attach_adapter = I2C_attachAdapter;   //这个很重要,将驱动连接到i2c适配器上,在后面分析 
    42.     driver->detach_client = I2C_detachClient;    //这个很重,在后面分析 
    43.  
    44.     if((ret = i2c_add_driver(driver)))  //使用i2c-core(i2c_register_driver函数)的接口,注册该驱动,i2c_add_driver实质调用了driver_register() 
    45.     { 
    46.         printk( KERN_ERR "I2C: ERROR: Driver registration failed (address=%x), module not inserted. ", pObj->devAddr); 
    47.     } 
    48.  
    49.     if(ret<0) { 
    50.  
    51.       gI2C_dev.pObj[pObj->devAddr] = NULL; 
    52.       kfree(pObj);     
    53.       return NULL; 
    54.     } 
    55.     return pObj; 



     

    其他两个命令是I2C_CMD_WRITE和I2C_CMD_READ,这个比较简单,只需设置寄存器地址的大小以及寄存器值的大小,然后通过i2c-core 提供的i2c_transfer()函数发送即可。例如I2C_wirte()

     

    1. int I2C_write(I2C_Obj *pObj, uint8_t *reg, uint8_t *buffer, uint8_t count, uint8_t dataSize) 
    2.   uint8_t i; 
    3.   int err; 
    4.   struct i2c_client *client; 
    5.     struct i2c_msg msg[1]; 
    6.     unsigned char data[8]; 
    7.  
    8.   if(pObj==NULL) 
    9.     return -ENODEV; 
    10.  
    11.   client = &pObj->client;//得到client信息 
    12.   if(!client->adapter) 
    13.     return -ENODEV;   
    14.    
    15.   if(dataSize<=0||dataSize>4) 
    16.     return -1; 
    17.      
    18.   for(i=0; i<count; i++) { 
    19.    
    20.     msg->addr = client->addr;//设置要写的i2c设备地址 
    21.     msg->flags= 0;//一直为0 
    22.     msg->buf  = data;//date为准备i2c通信的缓冲区,这个缓冲区除了不包含设备地址外,要包括要目标寄存器地址,和要写入该寄存器的值 
    23.          
    24.     data[0] = reg[i];//寄存器地址赋值 
    25.          
    26.     if(dataSize==1) {//寄存器值长度为1 
    27.        data[1]  = buffer[i];//寄存器值赋值 
    28.        msg->len = 2;     //设置data长度为2     
    29.     }   else if(dataSize==2) {//寄存器值长度为2 
    30.        data[1] = buffer[2*i+1]; 
    31.        data[2] = buffer[2*i]; 
    32.        msg->len = 3; 
    33.     }  
    34.     err = i2c_transfer(client->adapter, msg, 1);//调用i2c-core中的i2c_transfer发送i2c数据 
    35.     if( err < 0 ) 
    36.       return err; 
    37.     } 
    38.    
    39.   return 0; 
    1. int I2C_write(I2C_Obj *pObj, uint8_t *reg, uint8_t *buffer, uint8_t count, uint8_t dataSize) 
    2.   uint8_t i; 
    3.   int err; 
    4.   struct i2c_client *client; 
    5.     struct i2c_msg msg[1]; 
    6.     unsigned char data[8]; 
    7.  
    8.   if(pObj==NULL) 
    9.     return -ENODEV; 
    10.  
    11.   client = &pObj->client;//得到client信息 
    12.   if(!client->adapter) 
    13.     return -ENODEV;   
    14.    
    15.   if(dataSize<=0||dataSize>4) 
    16.     return -1; 
    17.      
    18.   for(i=0; i<count; i++) { 
    19.    
    20.     msg->addr = client->addr;//设置要写的i2c设备地址 
    21.     msg->flags= 0;//一直为0 
    22.     msg->buf  = data;//date为准备i2c通信的缓冲区,这个缓冲区除了不包含设备地址外,要包括要目标寄存器地址,和要写入该寄存器的值 
    23.          
    24.     data[0] = reg[i];//寄存器地址赋值 
    25.          
    26.     if(dataSize==1) {//寄存器值长度为1 
    27.        data[1]  = buffer[i];//寄存器值赋值 
    28.        msg->len = 2;     //设置data长度为2     
    29.     }   else if(dataSize==2) {//寄存器值长度为2 
    30.        data[1] = buffer[2*i+1]; 
    31.        data[2] = buffer[2*i]; 
    32.        msg->len = 3; 
    33.     }  
    34.     err = i2c_transfer(client->adapter, msg, 1);//调用i2c-core中的i2c_transfer发送i2c数据 
    35.     if( err < 0 ) 
    36.       return err; 
    37.     } 
    38.    
    39.   return 0; 



    现在,我们重点分析上一段代码void *I2C_create(int devAddr)函数中的i2c_driver结构体部分的代码,下面的代码是从上面I2C_create抽取出来的

     

    1. driver->driver.name = pObj->name; 
    2. driver->id = I2C_DRIVERID_MISC; 
    3. driver->attach_adapter = I2C_attachAdapter; 
    4. driver->detach_client = I2C_detachClient; 
    1. driver->driver.name = pObj->name; 
    2. driver->id = I2C_DRIVERID_MISC; 
    3. driver->attach_adapter = I2C_attachAdapter; 
    4. driver->detach_client = I2C_detachClient; 



    在i2c_driver结构体中针对attach_adapter有这样的说明:

     

     

    1. /* Notifies the driver that a new bus has appeared. This routine
    2. * can be used by the driver to test if the bus meets its conditions
    3. * & seek for the presence of the chip(s) it supports. If found, it
    4. * registers the client(s) that are on the bus to the i2c admin. via
    5. * i2c_attach_client.
    6. */ 
    1. /* Notifies the driver that a new bus has appeared. This routine
    2. * can be used by the driver to test if the bus meets its conditions
    3. * & seek for the presence of the chip(s) it supports. If found, it
    4. * registers the client(s) that are on the bus to the i2c admin. via
    5. * i2c_attach_client.
    6. */ 



    意思是通知驱动,i2c适配器已经就绪了,这时可以讲device的driver连接到总线bus上。所以I2C_attachAdapter的作用就是检测client,然后将client连接上来。attach_adapter和detach_client由内核驱动自动调用,我们只需在调用的时候实现必要的功能即可,如下代码展示

    1. int I2C_attachAdapter(struct i2c_adapter *adapter) 
    2.     return I2C_detectClient(adapter, gI2C_curAddr); 
    3.  
    4. int I2C_detectClient(struct i2c_adapter *adapter, int address) 
    5.     I2C_Obj *pObj; 
    6.     struct i2c_client *client; 
    7.     int err = 0; 
    8.      
    9.     if(address > I2C_DEV_MAX_ADDR) { 
    10.       printk( KERN_ERR "I2C: ERROR: Invalid device address %x ", address);         
    11.       return -1; 
    12.     } 
    13.        
    14.     pObj = gI2C_dev.pObj[address]; 
    15.     if(pObj==NULL) { 
    16.       printk( KERN_ERR "I2C: ERROR: Object not found for address %x ", address);     
    17.       return -1; 
    18.     } 
    19.  
    20.     client = &pObj->client; 
    21.  
    22.     if(client->adapter) 
    23.       return -EBUSY;  /* our client is already attached */ 
    24.  
    25.     memset(client, 0x00, sizeof(struct i2c_client)); 
    26.     client->addr = pObj->devAddr; 
    27.     client->adapter = adapter; 
    28.     client->driver = &pObj->driver; 
    29.  
    30.     if((err = i2c_attach_client(client))) 
    31.     { 
    32.         printk( KERN_ERR "I2C: ERROR: Couldn't attach %s (address=%x) ", pObj->name, pObj->devAddr); 
    33.         client->adapter = NULL; 
    34.         return err; 
    35.     } 
    36.     return 0; 
    1. int I2C_attachAdapter(struct i2c_adapter *adapter) 
    2.     return I2C_detectClient(adapter, gI2C_curAddr); 
    3.  
    4. int I2C_detectClient(struct i2c_adapter *adapter, int address) 
    5.     I2C_Obj *pObj; 
    6.     struct i2c_client *client; 
    7.     int err = 0; 
    8.      
    9.     if(address > I2C_DEV_MAX_ADDR) { 
    10.       printk( KERN_ERR "I2C: ERROR: Invalid device address %x ", address);         
    11.       return -1; 
    12.     } 
    13.        
    14.     pObj = gI2C_dev.pObj[address]; 
    15.     if(pObj==NULL) { 
    16.       printk( KERN_ERR "I2C: ERROR: Object not found for address %x ", address);     
    17.       return -1; 
    18.     } 
    19.  
    20.     client = &pObj->client; 
    21.  
    22.     if(client->adapter) 
    23.       return -EBUSY;  /* our client is already attached */ 
    24.  
    25.     memset(client, 0x00, sizeof(struct i2c_client)); 
    26.     client->addr = pObj->devAddr; 
    27.     client->adapter = adapter; 
    28.     client->driver = &pObj->driver; 
    29.  
    30.     if((err = i2c_attach_client(client))) 
    31.     { 
    32.         printk( KERN_ERR "I2C: ERROR: Couldn't attach %s (address=%x) ", pObj->name, pObj->devAddr); 
    33.         client->adapter = NULL; 
    34.         return err; 
    35.     } 
    36.     return 0; 



    最终I2C_detectClient()函数调用了i2c-core中的i2c_attach_client,从名字上就能看出什么意思,连接client设备。
    当内核驱动准备删除该驱动时会自动调用i2c_driver的成员函数:detech_client,因此我们需要实现删除client设备的函数然后赋值给改函数指针,detech_client的说明如下:

     

     

    1. /* tells the driver that a client is about to be deleted & gives it
    2. * the chance to remove its private data. Also, if the client struct
    3. * has been dynamically allocated by the driver in the function above,
    4. * it must be freed here.
    5. */ 
    1. /* tells the driver that a client is about to be deleted & gives it
    2. * the chance to remove its private data. Also, if the client struct
    3. * has been dynamically allocated by the driver in the function above,
    4. * it must be freed here.
    5. */ 



    下面是detech_client调用的函数代码清单,该函数最终调用了i2c-core提供的i2c_detach_client,用于取消client设备的连接

     

     

    1. int I2C_detachClient(struct i2c_client *client) 
    2.     int err; 
    3.  
    4.     if(!client->adapter) 
    5.         return -ENODEV; /* our client isn't attached */ 
    6.  
    7.     if((err = i2c_detach_client(client))) { 
    8.         printk( KERN_ERR "Client deregistration failed (address=%x), client not detached. ", client->addr); 
    9.         return err; 
    10.     } 
    11.  
    12.     client->adapter = NULL; 
    13.  
    14.     return 0; 
    1. int I2C_detachClient(struct i2c_client *client) 
    2.     int err; 
    3.  
    4.     if(!client->adapter) 
    5.         return -ENODEV; /* our client isn't attached */ 
    6.  
    7.     if((err = i2c_detach_client(client))) { 
    8.         printk( KERN_ERR "Client deregistration failed (address=%x), client not detached. ", client->addr); 
    9.         return err; 
    10.     } 
    11.  
    12.     client->adapter = NULL; 
    13.  
    14.     return 0; 



     

    ov2715设备的i2c驱动源码的分析就到这里,至于平台相关的i2c总线驱动分析就放到下一篇文章里分析,因为这部分多数情况下并不需要我们亲自去实现。但是对于理解i2c驱动架构来说,还是有很大帮助的。

  • 相关阅读:
    ContentProvider简单介绍
    hdu 1087 Super Jumping! Jumping! Jumping!(dp 最长上升子序列和)
    点点文刊 -- 记录生活的色彩
    【struts2+hibernate4】小型电子商务站点
    IOS学习之蓝牙4.0
    POJ 1988 Cube Stacking (种类并查集)
    Android开发之 shape的使用
    妄想性仮想人格障害 新手教程 +改动器
    Spring 的 ApplicationEvent and ApplicationListener
    Offer是否具有法律效力?
  • 原文地址:https://www.cnblogs.com/heyp/p/3359529.html
Copyright © 2011-2022 走看看