linux内核驱动学习(八)

驱动分类:

对于驱动,我们一般按两种方法进行分类:常规分类法和总线分类法。

按照常规分类法,可以分为以下三类:

1、字符设备:

以字节为最小访问单位的设备。一般通过字符设备文件来访问字符设备驱动程序。字符驱动程序则负责驱动字符设备, ,这样的驱动通常支持open、close、read、write系统调用,,应用程序可以通过设备文件(比如/dev/ttySAC0等)来访问字符设备(串口)。例如:串口\led\按键

2、块设备:

以块(一般512字节)为最 小传输单位的设备。大多数UNIX系统中,块设备不能按字节处理数据。常见的块设备包括硬盘、Flash、sd卡。

在Linux追踪则允许块设备传送任意数目的字节。

块设备的特别之处:

a)操作硬件的接口的实现方式不一样

块设备驱动程序是先将用户发来的数据组织成块,再写入设备;或从设备中读出若干块数据,再从中挑出用户需要的

b)数据块上的数据可以有一定的格式。

通常在快设备中按一定的格式存放数据,不同的文件系统类型就是定义这些格式的。内核中,文件系统的层次位于块设备驱动程序上面的,这意味着块设备驱动程序除了向用户层提供与字符设备一 样的接口外,还要向内核其他部件提供一些接口,这些接口用户看不到,但是可以使用这些接口是的可以在块设备上存放文件系统,挂载块设备。

块设备与字符设备的区别仅仅在于驱动向内核提供的接口不一样,而向用户层提供的接口是一样的。

3、网络接口

即可以是一个硬件设备,如网卡;也可以是纯软件的设备。比如回环接口(lo)。一个网络接口负责发送和接收数据报文。

对于网络驱动程序并不同于字符设备和块设备,库、内核提供了一套和数据包传输相关的函数,而不是普通的系统调用(open\write)

按照总线分类法,也可分为以下三类:

1、USB 设备

2、PCI设备

3、平台总线设备

譬如USB无线网卡:按常规分类为网络接口,按总线分类:USB设备。

驱动学习方法:

我们知道Linux内核就是由各种驱动组成的,内核源码中大约85%都为驱动程序的代码。内核中实现的驱动程序种类齐全,我们可以在通类型驱动的基础上进行修改以符合具体的设备。

学习驱动更重要的是搞清楚现有驱动的框架,在这个框架上添加相应硬件。

对于硬件操作,可参考ARM裸机代码,将其移植到驱动框架中去。

还有一点就是在驱动学习初期最好不要过多去阅读内核代码,以免造成混乱。

硬件访问:

硬件访问的实质:驱动程序控制设备,主要是通过设备内的寄存器来达到控制的目的,因此我们讨论如何访问硬件,就成了如何访问这些寄存器了。

一、地址映射

裸机直接使用物理地址去操作寄存器;而在Linux系统中使用的为虚拟地址(无论内核程序还是应用程序)。则当操作寄存器时,需要完成物理地址到虚拟地址的映射。

地址映射又分为:

1.1动态映射:

在驱动程序中采用ioremap函数将物理地址映射为虚拟地址。

函数原型:void * ioremap(physaddr, size)

参数:physaddr:待映射的物理地址

size:映射的区域长度

返回值:映射后的虚拟地址

1.2静态映射:是指Linux系统根据用户事先指定的映射关系,在内核启动时,自动地将物理地址映射为虚拟地址。

映射的举例:IO内存的静态映射,linux系统在建立IO内存物理地址到虚拟地址的映射时,映射关系是怎么指定的呢?这就需要map_desc这个结构数组了,映射就是在这个结构数组中添加新成员来完成的。

即在静态映射中,用户 是通过map_desc结构来指明物理地址与虚拟地址的映射关系 。

文件Map.h中 (linux-ok6410\arch\arm\include\asm\mach)在静态映射中,用 户 是通过map_desc结构来指明物理地址与虚拟地址的映射关系 。

struct map_desc {unsigned long virtual; /* 映射后的虚拟地址 */unsigned long pfn; /* 物理地址所在的页帧号 */unsigned long length; /* 映射长度 */unsigned int type; /* 映射的设备类型 */};pfn:利用__phys_to_pfn(物理地址)可以计算出物理地址所在的物理页帧号

对于6410处理器,关于该结构的填充如下:

Cpu.c (linux-ok6410\arch\arm\mach-s3c64xx)

/* minimal IO mapping */static struct map_desc s3c_iodesc[] __initdata = {{.virtual= (unsigned long)S3C_VA_SYS,.pfn= __phys_to_pfn(S3C64XX_PA_SYSCON),.length= SZ_4K,.type= MT_DEVICE,}, {.virtual= (unsigned long)S3C_VA_MEM,.pfn= __phys_to_pfn(S3C64XX_PA_SROM),.length= SZ_4K,.type= MT_DEVICE,}, {.virtual= (unsigned long)(S3C_VA_UART + UART_OFFS),.pfn= __phys_to_pfn(S3C_PA_UART),.length= SZ_4K,.type= MT_DEVICE,}, {.virtual= (unsigned long)VA_VIC0,.pfn= __phys_to_pfn(S3C64XX_PA_VIC0),.length= SZ_16K,.type= MT_DEVICE,}, {.virtual= (unsigned long)VA_VIC1,.pfn= __phys_to_pfn(S3C64XX_PA_VIC1),.length= SZ_16K,.type= MT_DEVICE,}, {.virtual= (unsigned long)S3C_VA_TIMER,.pfn= __phys_to_pfn(S3C_PA_TIMER),.length= SZ_16K,.type= MT_DEVICE,}, {.virtual= (unsigned long)S3C64XX_VA_GPIO,.pfn= __phys_to_pfn(S3C64XX_PA_GPIO),.length= SZ_4K,.type= MT_DEVICE,}, {.virtual= (unsigned long)S3C64XX_VA_MODEM,.pfn= __phys_to_pfn(S3C64XX_PA_MODEM),.length= SZ_4K,.type= MT_DEVICE,}, {.virtual= (unsigned long)S3C_VA_WATCHDOG,.pfn= __phys_to_pfn(S3C64XX_PA_WATCHDOG),.length= SZ_4K,.type= MT_DEVICE,}, {.virtual= (unsigned long)S3C_VA_USB_HSPHY,.pfn= __phys_to_pfn(S3C64XX_PA_USB_HSPHY),.length= SZ_1K,.type= MT_DEVICE,},};

内核启动时,在以下函数内完成自动映射

孤单寂寞与被遗弃感是最可怕的贫穷

linux内核驱动学习(八)

相关文章:

你感兴趣的文章:

标签云: