转了个圈,又回来了
分类: LINUX
2008-07-25 10:49:26
3. 一个具体的I2C设备驱动程序的开发
DS1307是一款小巧的I2C接口的实时时钟芯片,具有低功耗,全BCD码时钟和日历输出, 12 /24小时工作模式,时分秒、星期、年月日计时数据,润年自动补偿,有效期至2100年,外加56 Bytes的NV RAM(非易失性的RAM)等特点[3]。下面以DS1307为例,说明一个具体的I2C设备驱动程序的设计要点。
3.1 I2C设备驱动程序的一般结构
一个具体的I2C设备驱动需要实现两个方面的接口,一个是对I2C core层的接口,用以挂接I2C adapter层来实现对I2C总线及I2C设备具体的访问方法,包括要实现attach_adapter,detach_client,command等接口函数。另一个是对用户应用层的接口,提供用户程序访问I2C设备的接口,包括实现open,release,read,write以及最重要的ioctl等标准文件操作的接口函数。
对I2C core层的接口函数的具体功能解释如下:
attach_adapter:I2C driver在调用I2C_add_driver() 注册时,对发现的每一个I2C adapter(对应一条I2C 总线)都要调用该函数,检查该I2C adapter是否符合I2C driver的特定条件,如果符合条件则连接此I2C adapter,并通过I2C adapter来实现对I2C总线及I2C设备的访问。
detach_client:I2C driver在删除一个I2C device时调用该函数,清除描述这个I2C device的数据结构,这样以后就不能访问该设备了。
command:针对设备的特点,实现一系列的子功能,是用户接口中的ioctl功能的底层实现。
3.2 DS1307驱动程序实现对I2C core层的接口
在驱动中必须实现一个struct i2c_driver 的数据结构,并在驱动模块初始化时向I2C core注册一个I2C驱动,并完成对I2C adapter的相关操作。
struct i2c_driver ds1307_driver =
{
name: "DS1307",
id: I2C_DRIVERID_DS1307,
flags: I2C_DF_NOTIFY,
attach_adapter:ds1307_probe,
detach_client:ds1307_detach,
command: ds1307_command
};
数据结构ds1307_driver中的name:"DS1307",Id:I2C_DRIVERID_DS1307用来标识DS1307驱动程序。flags: I2C_DF_NOTIFY表示在I2C总线发生变化时通知该驱动。
ds1307_probe对应i2c_driver数据结构中的attach_adapter,主要功能:调用 I2C core 层提供的i2c_probe函数查找一条I2C总线,看是否有DS1307的设备存在,如果存在DS1307,则将对应的I2C adapter 和DS1307设备挂接在一起,并通过该I2C adapter来实现对DS1307的访问。同时使能DS1307, 并调用i2c_attach_client()向I2C core层注册DS1307。
ds1307_detach对应i2c_driver数据结构中的detach_client,主要功能:调用i2c_detach_client() 向I2C core层注销DS1307,并不使能DS1307,这样I2C驱动就不能访问DS1307了。
ds1307_command对应i2c_driver数据结构中的command ,主要功能:针对DS1307时钟芯片的特点,实现一系列的诸如DS1307_GETTIME ,DS1307_SETTIME,DS1307_GETDATETIME,DS1307_MEM_READ,DS1307_MEM_WRITE等子功能,是用户接口中的ioctl功能的底层实现。
以上3个接口函数使DS1307的驱动程序实现了对I2C 总线及I2C adpater的挂接,因此就可以通过I2C core的提供对I2C总线读写访问的通用接口,来开发实现DS1037驱动程序对用户应用层的接口函数。
3.3 DS1307驱动程序实现对用户应用层的接口
在驱动中必须实现一个struct file_operations 的数据结构,并向内核注册为一个字符类型的设备(用单独的主设备号来标识),或者注册为一个miscdevice设备(所有miscdevice设备共同一个主设备号,不同的次设备号,所有的miscdevice设备形成一个链表,对设备访问时根据次设备号查找对应的miscdevice设备,然后调用其struct file_operations中注册的应用层接口进行操作)。
struct file_operations rtc_fops =
{
owner: THIS_MODULE,
ioctl: ds1307_rtc_ioctl,
read: ds1307_rtc_read,
write: ds1307_rtc_read,
open: ds1307_rtc_open,
release: ds1307_rtc_release
};
数据结构rtc_fops 中的ds1307_rtc_open 和ds1307_rtc_release对应file_operations中的open和release,分别用来打开和关闭DS1307。
ds1307_rtc_ioctl对应file_operations中的ioctl,对用户提供的一系列控制时钟芯片的具体命令:RTC_GET_TIME: 以固定的数据格式读取实时时钟的时间。RTC_SET_TIME:以固定的数据格式设定实时时钟的时间。RTC_SYNC_TIME:系统时钟和实时时钟之间的时间同步。
ds1307_rtc_read 对应对应file_operations中的read,实现与ds1307_rtc_ioctl 的子功能RTC_GET_TIME相同的功能,以及从NV RAM读取数据。
ds1307_rtc_write 对应file_operations中的write,实现与ds1307_rtc_ioctl的子功能 RTC_SET_TIME相同的功能,以及将数据写入NV RAM。
3.4 DS1307驱动程序的加载和测试
在DS1307驱动模块的初始化函数ds1307_init()中,首先通过i2c_add_driver(&ds1307_driver)向I2C core层注册一个I2C的设备驱动,然后再通过misc_register (&ds1307_rtc_miscdev)将DS1307注册为一个miscdevice设备,这样用户程序就可以通过主设备号10 次设备号 135的设备节点/dev/rtc来访问DS1307了。
将DS1307的驱动程序编译成模块的方式,通过insmod命令加载进内核,然后用测试代码进行测试,DS1307驱动程序中实现的所有功能都达到了预期的效果。
由于DS1307驱动程序在底层实现了对DS1307时钟芯片数据的解释和转换,所以在用户程序中得到的就是有固定格式和意义的数据,这样就方便了用户程序的访问,提高了应用开发的效率。
4.总结
I2C总线是一种结构小巧,协议简单的总线,应用很广泛,访问起来简单方便。linux系统下I2C的驱动程序具有清晰的层次结构,可以很容易地为一个特定的I2C设备开发驱动。本文通过对linux系统下I2C驱动,以及一个具体的DS1307时钟芯片驱动结构的分析,基本上可以很清楚看出一个I2C设备驱动的开发过程。实现的关键分为两个部分,1. 对I2C core的接口,必须实现 struct i2c_drvier数据结构中的几个特定的功能函数。这些函数是I2C驱动与I2C总线物理层(I2C控制器)和I2C设备器件之间通信的基础。2. 对用户应用层的接口,必须实现struct file_operation数据结构中的一些特定功能的函数,如 open ,release , read ,write,lseek等函数。以上两类接口中,对I2C core的接口是对I2C设备访问的基础,实现对I2C总线具体的访问方法;对用户应用层的接口则是方便应用程序开发,实现设备特定功能的必不可少的部