3 驱动实现
本节将实现一线制温度传感器网络的驱动模块。驱动从总体上看分为两部分:驱动与内核接口层、硬件设备接口层。
3.1 驱动与内核接口层
驱动与内核接口层主要完成驱动模块在Linux内核的注册加载、卸载清除工作。这部分工作分别由初始化和退出函数完成。
① 初始化函数完成驱动模块加载:
statIC int __init DS18B20_init(void){
……
register_chrdev(DS18B20_MAJOR,DEVICE_NAME, &DS18B20_fops);//完成设备注册
#ifdefCONFIG_DEVFS_FS//创建设备文件系统
devfs_mk_cdev(MKDEV(DS18B20_MAJOR,0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
#endif
……
}
② 退出函数完成驱动模块卸载:
static void __exit DS18B20_exit(void) {
#ifdef CONFIG_DEVFS_FS
devfs_remove(DEVICE_NAME);//移除设备文件
#endif
unregister_chrdev(DS18B20_MAJOR,DEVICE_NAME); //完成设备注销
……
}
3.2 硬件设备接口层
硬件设备接口层用来描述驱动程序与设备的交互。这些工作通过虚拟文件系统与设备驱动程序的接口实现。这个接口由file_operation结构定义,其结构如下:
static struct file_operations DS18B20_fops ={
.owner=THIS_MODULE, //指向拥有该结构的模块,内核使用该结构维护模块使用计数
.open=DS18B20_open, //打开设备函数
.read=DS18B20_read, //读接口函数
.write=DS18B20_write,//写接口函数
.fasync=DS18B20_fasync, //异步通知函数
.poll=DS18B20_poll,//poll函数
.release=DS18B20_release, //释放设备函数
};
3.2.1 打开设备函数
打开设备函数主要完成设备的初始化。
DS18B20_open(struct inode *inode,struct file *filp) {
Initial_Timer( );//初始化定时器,使内核模块按一定周期读温度
Initial_Device_DS18B20();//初始化硬件
readtemperature();//开始读取……
}
void readtemperature(void) {
……Temperature=DS18B20read();//读取2个8位数据,此函数完成的硬件操作时序,由当前读通道号变量指定当前通道
DS_SLOT_NO();//将本次读通道号放入缓冲区
DS18B20Event();//数据放入缓冲区,唤醒等待队列并启动异步通知
if(ReleaseFlag)
CycleTimer_Delay_SOFt(hdelay);//如果没有读停止信号,通过内核定时器延时,进行下一次读,在中断服务程序中再次启动读
……
}
在使用内核定时器之前需定义一个定时器结构体 static struct timer_list CycleTimer。下面是定时器的具体操作:
static void Initial_Timer(void) {
init_timer(&CycleTimer); );//初始化定时器结构
CycleTimer.function=DS18B20_timer; //挂接定时中断服务程序
}
3.2.2 读接口函数
用户程序执行读操作的时候可能没有可以读取的数据,此时需要让read操作等待直到有数据可以读取。在此采用等待队列使进程在无数据读取时进入等待,数据到达时唤醒。等待队列设置成一个循环缓冲区,每放入一个新数据作为缓冲区的头,存放时间最久还未被取走的数据为缓冲区的尾。
DS18B20_read( ) {
DECLARE_WAITQUEUE(wait,current);//声明等待队列……
Next_try:
if(DS18B20dev.head != DS18B20dev.tail) {//等待队列不为空,即有数据
DS18B20_ret=Read_Buffer_DS18B20(); //取走缓冲区的尾
copy_to_user( ); //读取的数据送到用户空间
}
else { ……//等待队列为空,即没有数据
add_wait_queue(&queue,&wait);
current>state=TASK_INTERRUPTIBLE;//添加等待队列,声明状态为任务可中断
while((DS18B20dev.head==DS18B20dev.tail)&&!signal_pending(current) {//进入等待
schedule();
current>state=TASK_INTERRUPTIBLE;
}//如果缓冲区为空,Linux内核调度,等待通知
current>state = TASK_RUNNING;//得到有数据的通知,声明任务状态为运行
remove_wait_queue(&queue,&wait);//删除等待队列
goto Next_try;//返回到读取数据
}
}