摘要:介绍了linux下驱动的分类和学习方法,描述了linux下硬件访问的思路和具体实现方法。
一、linux下设备驱动分类
linux系统的设备常规分为字符设备(char device),块设备(block device)和网络设备(network device),对应的驱动也分为这三类。
字符设备:字符设备是一种按照字节来访问的设备,并且存取时没有缓存设备。这样的驱动通常用来实现open,read,write和close系统调用,字符设备包括:串口,LED,按键等。
块设备:linux允许块设备传送任意字节的数据,可以是一个字节一个字节,也可以是一块一块例如512字节,他们的主要区别在于块设备使用了缓存来支持,并且能够随机存取,块设备驱动与内核的接口是和字符设备不同的。常见的块设备包括flash,sd卡,cd等等。
网络接口:网络接口设备驱动主要是基于socket机制,主要是负责发送和接收数据报文,在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据传递,网络接口设备包括例如eth0这样的硬件网卡,也可以是回环loop这样的纯软件。
另外,按照总线方式的不同,还可以分为USB设备,PCI设备和平台总线设备。
二、驱动学习方法
第一:驱动模型
用自己的话理解吧,其实内核里面,各个驱动的模型是比较固定的,学习的时候按照这个模式先去往里填,框架熟悉了,就能摸索出一点心得出来。大致我认为驱动学习,百分之三十要归结到模型上,百分之三十在内核,剩下的百分之四十是要对硬件的操作比较熟悉,因为驱动驱动,驱动到最后一定要操作硬件(也有软件模型比如loop),你对外设有多少的掌握和理解,时序,寄存器,读写方式,中断等等,驱动为什么难,难就难在需要一个系统的思维去学习,对底层硬件的东西掌握需要比较多,还要对内核对驱动的支持机制掌握,然后编写驱动的时候要遵照模型,驱动写完了还得写应用测试程序。
第二:硬件操作
这个刚才已经说了,比如你要驱动一个按键,首先按键的操作方式要了解,起码看得懂硬件连接,高低电平,按键对应的I/O口物理地址,寄存器配置方式,轮询肯定不行了,那就得要用中断,内核对中断的机制是如何的,还有访问硬件有哪些方式,我们按键对应的物理地址如何对应到我在驱动中操作的虚拟地址,等等这些。按键只是个最最简单的比方,后面IIC,SPI,网络驱动,有时候难度并不在驱动本身了,而是你对他们的理解,比如通信协议,需要ACK,需要校验,怎么发,怎么收,发到哪里,谁去接受等等等等。所以好的驱动工程师,个人认为跟你编写过的外设驱动数量和种类是有很大关系的。
第三:测试程序
驱动写好了,就要编写测试程序来测试这个驱动是否可用,这时候就需要对驱动接口比较了解,还要考虑是否有什么小的问题,响应时间,内存占用等等,这部分其实是应用程序开发的功力。
三、硬件访问技术
要对设备进行控制,实际上是控制设备的寄存器,如何访问这些硬件设备,实际上就是探讨如何访问寄存器。做到这样需要两步:第一,地址映射;第二:读写寄存器。
地址映射
因为s3c2440给出的是物理地址,而我们使用的需要虚拟地址,所以必须要先完成物理地址到虚拟地址的转化,转化分为动态映射方式和静态映射方式。
动态映射方式函数:void *ioremap(physaddr,size)
函数原型:void *__ioremap(unsigned longphys_addr,unsigned long size,unsigned long flags)
函数功能:将phys_addr指向的物理地址映射size字节大小到虚拟地址空间,返回值为该段虚拟地址的起始地址。
返回值:映射后的虚拟地址。
phys_addr:要映射的起始的I/O地址。
size:要映射的空间大小。
flags:要映射的I/O空间的权限有关的标志。
静态映射方式就是根据用户指定,在linux内核启动的时候就会自动的将某些物理地址和虚拟地址应设好了。首先是填充一个结构体,然后是通知内核。
用户通过map_desc结构来指明物理地址与虚拟地址的映射关系。
structmap_desc{
unsignedlong virtual;
unsignedlong pfn;
unsignedlong length;
unsignedint type;
}
virtual:映射后的虚拟地址。
pfn:利用__phys_to_pfn(物理地址)可以计算出物理地址所在的物理页帧号。
length:映射的长度。
type:映射的设备类型。
读写寄存器
完成地址映射以后,内核提供了一组用来访问寄存器的函数,如下:
读I/O内存,8,16,32就是一个字节,两个字节,四个字节的意思。
unsignedioread8(void *addr)
unsignedioread16(void *addr)
unsignedioread32(void *addr)
unsignedreadb(address)
unsignedreadw(address)
unsignedreadl(address)
写I/O内存,同上:
voidiowrite8(u8 value,void *addr)
voidiowrite16(u16 value,void *addr)
voidiowrite32(u32 value,void *addr)
voidwriteb(unsigned value,address)
voidwritew(unsigned value,address)
voidwritel(unsigned value,address)
这篇帖子就到这里吧,如有不正确的地方还请指出,大家共同进步。