Linux I2C核心、总线与设备驱动(一)

来源:互联网 发布:ip的端口是否打开 编辑:程序博客网 时间:2024/05/21 11:20

本章导读

I2C总线仅仅使用SCLSDA两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和PCB板布线空间的占用。因此,I2C总线被非常广泛地应用在EEPROM、实时钟、小型LCD等设备与CPU的接口中。

Linux定义了系统的I2C驱动体系结构,在Linux系统中,I2C驱动由3部分组成,即I2C核心、I2C总线驱动和I2C设备驱动。这3部分相互协作,形成了非常通用、可适应性很强的I2C框架。
  
本章第1节将对Linux I2C体系结构进行分析,讲明3个组成部分各自的功能及相互联系。第2节将对Linux I2C核心进行分析,解释i2c-core.c文件的功能和主要函数的实现。第34节将分别详细介绍I2C总线驱动和I2C设备驱动的编写方法,给出可供参考的设计模板。第56节将以第34节给出的设计模板为基础,讲解S3C2410 ARM处理器I2C总线驱动及挂接在上的SAA7113H视频模拟/数字转换芯片设备驱动的编写方法。
15.1 Linux I2C
体系结构
Linux
I2C体系结构分为3个组成部分:
?  I2C
核心
I2C
核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(即“algorithm”,笔者认为直译为运算方法并不合适,为免引起误解,下文将直接使用“algorithm”)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
?  I2C
总线驱动
I2C
总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至直接集成在CPU内部。
I2C
总线驱动主要包含了I2C适配器数据结构i2c_adapterI2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。
经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。
?  I2C
设备驱动
I2C
设备驱动是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。
I2C
设备驱动主要包含了数据结构i2c_driveri2c_client,我们需要根据具体设备实现其中的成员函数。

15.1 Linux I2C体系结构
Linux 2.6内核中,所有的I2C设备都被在sysfs文件系统中显示,存在于/sys/bus/i2c/目录,以适配器地址和芯片地址的形式列出,如:
$ tree /sys/bus/i2c/
/sys/bus/i2c/
|-- devices
|   |-- 0-0048 -> ../../../devices/legacy/i2c-0/0-0048
|   |-- 0-0049 -> ../../../devices/legacy/i2c-0/0-0049
|   |-- 0-004a -> ../../../devices/legacy/i2c-0/0-004a
|   |-- 0-004b -> ../../../devices/legacy/i2c-0/0-004b
|   |-- 0-004c -> ../../../devices/legacy/i2c-0/0-004c
|   |-- 0-004d -> ../../../devices/legacy/i2c-0/0-004d
|   |-- 0-004e -> ../../../devices/legacy/i2c-0/0-004e
|   `-- 0-004f -> ../../../devices/legacy/i2c-0/0-004f
`-- drivers
    |-- i2c_adapter
    `-- lm75
        |-- 0-0048 -> ../../../../devices/legacy/i2c-0/0-0048
        |-- 0-0049 -> ../../../../devices/legacy/i2c-0/0-0049
        |-- 0-004a -> ../../../../devices/legacy/i2c-0/0-004a
        |-- 0-004b -> ../../../../devices/legacy/i2c-0/0-004b
        |-- 0-004c -> ../../../../devices/legacy/i2c-0/0-004c
        |-- 0-004d -> ../../../../devices/legacy/i2c-0/0-004d
        |-- 0-004e -> ../../../../devices/legacy/i2c-0/0-004e
        `-- 0-004f -> ../../../../devices/legacy/i2c-0/0-004f
Linux内核源代码中的drivers目录下包含一个i2c目录,而在i2c目录下又包含如下文件和文件夹:
?  i2c-core.c
这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。
?  i2c-dev.c
实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访问设备时的主设备号都为89,次设备号为0255。应用程序通过 “i2c-%d” (i2c-0, i2c-1, ..., i2c-10, ...)文件名并使用文件操作接口open()write()read()ioctl()close()等来访问这个设备。
i2c-dev.c
并没有针对特定的设备而设计,只是提供了通用的read()write()ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器并控制I2C设备的工作方式。
?  chips
文件夹
这个目录中包含了一些特定的I2C设备驱动,如Dallas公司的DS1337实时钟芯片、EPSON公司的RTC8564实时钟芯片和I2C接口的EEPROM驱动等。
?  busses
文件夹
这个文件中包含了一些I2C总线的驱动,如S3C2410I2C控制器驱动为i2c-s3c2410.c
?  algos
文件夹
实现了一些I2C总线适配器的algorithm
此外,内核中的i2c.h这个头文件对i2c_driveri2c_clienti2c_adapteri2c_algorithm4个数据结构进行了定义。理解这4个结构体的作用十分关键,代码清单15.115.215.315.4分别给出了它们的定义。
代码清单15.1 i2c_adapter结构体
1  struct i2c_adapter {
2   struct module *owner;/*
所属模块*/
3  unsigned int id;   /*algorithm
的类型,定义于i2c-id.h,以I2C_ALGO_开始*/
4  unsigned int class;
5  struct i2c_algorithm *algo;/*
总线通信方法结构体指针 */
6  void *algo_data; /* algorithm
数据 */
7  int (*client_register)(struct i2c_client *);  /*client
注册时调用*/
8  int (*client_unregister)(struct i2c_client *); /*client
注销时调用*/
9  struct semaphore bus_lock;    /*
控制并发访问的自旋锁*/
10 struct semaphore clist_lock;
11 int timeout;
12 int retries;    /*
重试次数*/
13 struct device dev;  /*
适配器设备 */
14 struct class_device class_dev; /*
类设备 */
15 int nr;
16 struct list_head clients;  /* client
链表头*/
17 struct list_head list;
18 char name[I2C_NAME_SIZE];  /*
适配器名称*/
19 struct completion dev_released;    /*
用于同步*/
20 struct completion class_dev_released;
21};
代码清单15.2 i2c_algorithm结构体
1  struct i2c_algorithm {
2   int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,
3                      int num);  /*i2c
传输函数指针*/
4   int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,   /*smbus
传输函数指针*/
5                      unsigned short flags, char read_write,
6                      u8 command, int size, union i2c_smbus_data * data);
7   int (*slave_send)(struct i2c_adapter *,char*,int);/*
i2c适配器为slave时,发送函数*/
8   int (*slave_recv)(struct i2c_adapter *,char*,int); /*
i2c适配器为slave时,接收函数*/
9   int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long); /*
类似ioctl*/
10  u32 (*functionality) (struct i2c_adapter *);/*
返回适配器支持的功能*/
11 };
上述代码第4行对应为SMBus传输函数指针,SMBus大部分基于I2C总线规范,SMBus不需增加额外引脚。与I2C总线相比,SMBus增加了一些新的功能特性,在访问时序也有一定的差异。
代码清单15.3 i2c_driver结构体
1  struct i2c_driver {
2   int id;
3   unsigned int class;
4   int (*attach_adapter)(struct i2c_adapter *); /*
依附i2c_adapter函数指针 */
5   int (*detach_adapter)(struct i2c_adapter *); /*
脱离i2c_adapter函数指针*/
6   int (*detach_client)(struct i2c_client *);  /*i2c client
脱离函数指针*/
7   int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); /*
类似ioctl*/
8   struct device_driver driver;    /*
设备驱动结构体*/
9   struct list_head list;         /*
链表头*/
10 };
代码清单15.4 i2c_client结构体
1  struct i2c_client {
2   unsigned int flags;  /*
标志 */
3   unsigned short addr;     /*
7位为芯片地址 */
4   struct i2c_adapter *adapter; /*
依附的i2c_adapter*/
5   struct i2c_driver *driver;    /*
依附的i2c_driver */
6   int usage_count;     /*
访问计数  */
7   struct device dev;     /*
设备结构体 */
8   struct list_head list;       /*
链表头 */
9   char name[I2C_NAME_SIZE]; /*
设备名称 */
10  struct completion released;   /*
用于同步 */
11 };
下面分析一下i2c_driveri2c_clienti2c_adapteri2c_algorithm4个数据结构的作用及其盘根错节的关系。
?  i2c_adapter
i2c_algorithm
i2c_adapter
对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithmi2c_adapter什么也做不了,因此i2c_adapter中包含其使用的 i2c_algorithm的指针。
i2c_algorithm
中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位。i2c_msg结构体也非常关键,代码清单15.5给出了它的定义。
代码清单15.5 i2c_msg结构体
1 struct i2c_msg {
2  __u16 addr; /*
设备地址*/
3   __u16 flags; /*
标志 */ 
4   __u16 len;  /*
消息长度*/
5   __u8 *buf;  /*
消息数据*/
6 };
?  i2c_driver
i2c_client
i2c_driver
对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client一般被包含在i2c字符设备的私有信息结构体中。
i2c_driver
i2c_client发生关联的时刻在i2c_driverattach_adapter()函数被运行时。attach_adapter()会探测物理设备,当确定一个client存在时,把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapterdriver指针指向该i2c_driver,并会调用i2c_adapterclient_register()函数。相反的过程发生在 i2c_driver detach_client()函数被调用的时候。
?  i2c_adpater
i2c_client
i2c_adpater
i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adpater。由于一个适配器上可以连接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,i2c_adpater中包括依附于它的i2c_client的链表。
假设I2C总线适配器xxx上有两个使用相同驱动程序的yyy I2C设备,在打开该I2C总线的设备结点后相关数据结构之间的逻辑组织关系将如图15.2所示。


15.2 I2C驱动各数据结构关系