©2006EmbeddedLinux设备驱动程序设计Linux设备驱动程序设计郗闽军牛建伟©2006Embedded实验目的•学习Linux下进行驱动程序设计的原理•掌握Linux设备驱动程序开发的基本过程和设计方法©2006Embedded实验内容•内核驱动设计入门-模块方式驱动程序(5.1)•内核驱动设计实验-触摸屏驱动(5.2)–写一个简单的应用程序,显示触摸位置的坐标(x,y)•开发一个LED(数码管)驱动程序,并编写一个应用程序对所开发的驱动程序进行测试(大作业)–实验实现的功能是上电复位后,数码管显示数字0-7,然后每一个数字依次闪烁一次,小数点也要点亮,即:0.1.2.3.4.5.6.7.©2006EmbeddedLinux的设备驱动程序•硬件设备与应用程序之间的一个中间软件层•它使得某个特定硬件能够响应一个定义良好的内部编程接口,同时完全隐蔽了设备的工作细节•用户通过一组与具体设备无关的标准化的调用来完成相应的操作•驱动程序的任务就是把这些标准化的系统调用映射到具体设备对于实际硬件的特定操作上•驱动程序是内核的一部分,可以使用中断、DMA等操作•驱动程序在用户态和内核态之间传递数据©2006Embedded设备驱动程序的分类•字符设备–所有能够象字节流一样访问的设备都通过字符设备来实现–它们被映射为文件系统中的节点,通常在/dev/目录下面–一般要包含openreadwriteclose等系统调用的实现•块设备–通常是指诸如磁盘、内存、Flash等可以容纳文件系统的存储设备。–块设备也是通过文件系统来访问,与字符设备的区别是:内核管理数据的方式不同–它允许象字符设备一样以字节流的方式来访问,也可一次传递任意多的字节。•网络接口设备–通常它指的是硬件设备,但有时也可能是一个软件设备(如回环接口loopback),它们由内核中网络子系统驱动,负责发送和接收数据包。–它们的数据传送往往不是面向流的,因此很难将它们映射到一个文件系统的节点上。©2006Embedded基本概念•主设备号和次设备号–主设备号和次设备号能够唯一地标识一个设备•128(V2.0以前),256(V2.0以后)–主设备号相同的设备使用相同的驱动程序,次设备号用于区分具体设备的实例–动态获取主设备号–Linux下对设备号的分配请参考Documentation/devices.txt•设备文件–Linux使用设备文件来统一对设备的访问接口,将设备文件放在/dev/目录下–设备的命名一般为设备文件名+数字或者字母表示的子类,例如/dev/hda1,/dev/hda2等–Linux2.4以后引入了设备文件系统(devfs)的概念,所有的设备文件作为一个可以挂装的文件系统,这样就可以被文件系统统一管理,从而设备文件就可以挂装到任何需要的地方。一般将主设备建立一个目录,再将具体的子设备文件建立在此目录下。例如,/dev/mtdblock0©2006Embedded基本概念•驱动程序使用的2个重要结构–structfile–structfile_operations©2006Embedded基本概念•structfile©2006Embedded基本概念•structfile数据结构–定义位于include/fs.h–structfile结构与驱动相关的成员•mode_tf_mode标识文件的读写权限•loff_tf_pos当前读写位置•unsignedint_f_flag文件标志,主要进行阻塞/非阻塞型操作时检查•structfile_operation*f_op文件操作的结构指针•void*private_data驱动程序一般将它指向已经分配的数据•structdentry*f_dentry文件对应的目录项结构©2006Embedded基本概念•设备驱动程序接口(structfile_operations),标记化方法:staticstructfile_operationsdemo_fops={owner:THIS_MODULE,write:demo_write,read:demo_read,ioctl:demo_ioctl,open:demo_open,release:demo_release,};©2006Embedded基本概念•设备驱动程序接口(structfile_operations)–通常所说的设备驱动程序接口是指structfile_operations{},它的定义位于include/linux/fs.h中。–在嵌入式系统的开发中,通常只要实现如下几个接口函数就能完成系统所需要的功能•init加载驱动程序时,内核自动调用•read从设备中读取数据•write向字符设备中写数据•ioctl控制设备,实现除读写操作以外的其他控制命令•open打开设备并进行初始化•release关闭设备并释放资源•exit卸载驱动程序时,内核自动调用©2006Embedded基本概念•驱动程序注册过程(动态分配主设备号)–insmodmodule_name;加载驱动程序,运行init函数(register_chrdev(dev_Major,“module_name”,*fs))–查看/proc/devices–mknod/dev/module_namec/b主设备号次设备号–rmmodmodule_name;卸载驱动,运行exit函数(unregister_chrdev(dev_Major,“module_name”,*fs))•用户程序调用–Open(“/dev/module_name”,mode);O_RDWR–Ioctl()–Write()–Read()–Close()©2006Embedded开发驱动程序时须注意的事项•中断处理–中断是现代微处理器的一个重要功能–Linux驱动程序中的中断处理函数externintrequest_irq(unsignedintirq,void(*handler)(int,void*,structpt_regs*),unsignedlongflag,constchar*dev_name,void*dev_id);//请求为中断号irq分配中断处理函数externvoidfree_irq(unsignedint,void*);//释放中断–注意事项•不能向用户空间发送或者接收数据•不能执行有睡眠操作的函数•不能调用调度函数•谨慎使用全局变量(可重入)•自旋锁的使用©2006Embedded基本概念•字符设备的管理–驱动程序模块通过函数intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops)完成向内核的注册,其中major是主设备号,name是设备名,fops是针对该设备的驱动程序的接口。–在系统中为驱动程序模块建立一个设备节点[minjun@RedHatAS~]$mknod/dev/democ2540•其中/dev/demo标识设备名为demo,“c”说明是字符设备,254是指定的主设备号,0是次设备号©2006Embedded基本概念•驱动程序的编译–以demo.c为例•Makefile的形式-参考实验指导书•命令行的形式[minjun@RedHatAS~]$armv4l-unknown-linux-gcc-Wall-c-O-D__KERNEL__-I/home/minjun/embedded/kernel-2410s/includedemo.c-odemo.o•加载驱动–[minjun@RedHatAS~]$insmoddemo.o•卸载驱动–[minjun@RedHatAS~]$rmmoddemo.o©2006Embedded基本概念•测试程序实例//test.c#includestdio.h#includefcntl.h#includestdlib.hintmain(){intfd;fd=open(/dev/demo,O_RDWR);if(fd0){exit(fd);}//yourcodehereread(fd,buffer,size);write(fd,buffer,size);......close(fd);return0;}©2006Embedded驱动程序的实现-驱动程序框架#includelinux/config.h#includelinux/module.h#includelinux/init.h#includelinux/kernel.h/*printk()*/#includelinux/fs.h/*everything...*/#includelinux/errno.h/*errorcodes*/#includelinux/types.h/*size_t*/#includelinux/proc_fs.h#includelinux/fcntl.h/*O_ACCMODE*/#includelinux/poll.h/*COPY_TO_USER*/#includeasm/system.h/*cli(),*_flags*/#defineDEVICE_NAMEdemo#definedemo_MAJOR250#definedemo_MINOR0staticssize_tdemo_write(structfile*filp,constchar*buffer,size_tcount){copy_from_user(drv_buf,buffer,count);WRI_LENGTH=count;printk(userwritedatatodriver\n);//yourcodeherereturncount;}©2006Embedded驱动程序的实现-驱动程序框架staticssize_tdemo_read(structfile*filp,char*buffer,size_tcount,loff_t*ppos){if(countMAX_BUF_LEN)count=MAX_BUF_LEN;copy_to_user(buffer,drv_buf,count);printk(userreaddatafromdriver\n);returncount;}staticintdemo_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg){printk(ioctlruning\n);switch(cmd){case1:printk(runingcommand1\n);break;case2:printk(runingcommand2\n);break;default:printk(errorcmdnumber\n);break;}return0;}staticintdemo_open(structinode*inode,structfile*file){MOD_INC_USE_COUNT;sprintf(drv_buf,deviceopensucess!\n);printk(deviceopensucess!\n);return0;}©2006Embedded驱动程序的实现-驱动程序框架staticintdemo_release(structinode*inode,structfile*filp){MOD_DEC_USE_COUNT;printk(devicerelease\n);return0;}staticstructfile_operationsdemo_fops={owner:THIS_MODULE,write:demo_write,read:demo_read,ioctl:demo_ioctl,open:demo_open,release:demo_release,};staticint__initdemo_init(void){SET_MODULE_OWNER