您的位置:首页 > 运维架构 > Linux

Linux设备驱动程序--学习笔记(2)

2016-08-31 17:38 232 查看
1.fprintf(stdout,"hello");fprintf(stderr,"world");这样会在屏幕上输出 world hello  因为stdout有缓冲,而stderr无缓冲;如果将含有这两个函数的可执行文件(假设test)./test > abc.c那么stdout中的"hello"会输出到abc.c,而stderr中的"world"会输出到屏幕

2.用户CPU+系统CPU+空闲CPU=100

3.ARM体系的CPU有以下起七种工作模式:(1)用户模式(usr):ARM处理器正常的程序执行状态;(2)快速中断模式(fig):用于高速数据传输或通道处理;(3)中断模式(irq):用于通用的中断处理;(4)管理模式(svc):操作系统使用的保护模式;(5)数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可以用与虚拟存储和保护模式;(6)系统模式(sys):运行具有特权的操作系统任务;(7)未定义指令终止模式(und):当未定义的指令执行时进入该模式,可用于硬件协处理器的软件仿真

4.当应用程序调用C库里的open(),read(),write()函数时,此时会发生一个软中断,系统调用接口根据异常发生的原因,调用VFS(virtual file system)的处理函数(sys_open(),sys_read(),sys_write())--->因为设备在linux中都是文件的形式,VFS根据chrdev数组(下标对应主设备号)找到对应的file_operations

5.file_operations数据结构定义了文件的一些操作,我们需要调register_chrdev(major,"chrdev_name",&first_drv_fops);来注册我们的驱动程序--就是告诉内核这个file_operations结构,其中first_drv_fops是我们的file_operations结构的变量

6.注册驱动的函数register_chrdev由驱动入口函数init调用啦,但是一个驱动中有多个init函数,我们怎么知道调用哪个init呢?由一个宏定义:module_init(init_name)告诉内核驱动的入口函数是这个

7.init函数需要有int返回值,exit函数为void不需要返回

8.exit函数调用unregister_chrdev(major,"驱动名");来卸载驱动

9.源程序->打开设备文件->在/dev里查找有无该设备->有,根据该设备的主设备号在VFS的设备数组中找到该设备的file_operations结构,根据该设备的file_operations结构调用相关的函数执行操作

10.class_device_create();改为device_create();   class_device_destroy();改为device_destroy();

11.写一个点LED驱动的过程:(1)框架;(2)完善硬件的操作:2.1 看原理图;2.2看S3C2440手册;2.3写驱动-->用虚拟地址

12.外设的I/O内存的物理地址是已知的,但是CPU通常并没有为这些已知的外设I/O内存的物理地址预定义虚拟地址范围,驱动程序不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核心虚拟地址范围,通过访问指令访问这些I/O内存。

13.在内核驱动程序初始化阶段,通过ioremap()将外设I/O的物理地址映射到内核虚拟空间,(ioremap()函数定义在io.h中,函数iounmap用于取消ioremp()所做的映射);然后在驱动程序中的mmap系统调用中,使用remap_page_range()将该块ROM映射到用户虚拟空间,这样内核空间和用户空间都能访问这段被映射的虚拟地址

14.将32位的二进制的bit[8,9],[10,11],[12,13]分别设置为[1,0],[1,0],[1,0],但不能影响到其他位:(1)将相应的8,9,10,11,12清零,但不能影响其他的位。iocon&=~((0x3<<(2*4))|(0x3<<(5*2))|(0x3<<(6*2)));   (2)设置相应的位iocon|=((0x1<<(2*4))|(0x1<<(2*5))|(0x1<<(2*6)));

15.< >表示里面的参数不能省略

16.在一个驱动程序中可以根据不同的次设备号对拥有同意主设备号而次设备号不同的设备进行不同的操作

17.编写按键驱动的步骤:(1)写出框架;(2)硬件操作函数实现

18.ARM架构CPU的异常向量基址可以是0x00000000,也可以是0xffff0000,linux内核使用0xffff0000,两个地址都是虚拟地址

19.trap_init函数被用来设置各种硬件异常的处理向量,包括中断向量,将异常向量复制到0xffff0000,搭建了各类异常处理的框架,所谓"向量":就是一些被安装在固定位置的代码,当发送异常时,CPU会自动执行这些固定位置上的指令

20.irq--代表中断号

21.CPU的异常除了外部中断外,还有执行未定义的指令,试图修改只读的数据,执行swi指令(软件中断指令),缺页中断等

22.在arch/arm/kernel/calls.S中,swi异常的处理函数指针被组织成一个表格,swi指令机器码的位【23:0】被用来作为索引,这样,通过不同的"swi index"指令就可以调用不同的swi异常处理函数,它们被称为系统调用,比如sys_open,sys_read,sys_write等,所以swi是内核进行系统调用的方式,导致软中断。

23.中断是异常的一种,但是中断跟其他异常不同的地方是中断与具体的开发版密切相关,大部分的中断处理函数都必须由驱动开发者提供

24.init_IRQ(在irq.c中定义)被用来初始化中断的处理框架,设置各种中断的默认处理函数

25.Linux内核将所有的中断统一编号,使用一个irq_desc结构数组来描述这些中断,每个数组项对应一个中断或一组中断,文件arch/arm/mm/irq.c中定义了中断处理函数,总入口函数为asm_do_IRQ,它通过中断号irq作为irq_desc结构数组的下标,调用里面的handle_irq,handle_irq使用chip结构中的函数来清除,屏蔽,或者重新使能中断,还一一调用用户在action链表中注册的中断处理函数

26.irq_desc结构数组,它的成员"struct irq_chip *chip","struct irqaction *action",这三种数据结构构成了中断处理体系的框架,其中irqaction结构代表驱动程序注册的每个中断处理函数

27.中断处理流程:(1)发生中断时,CPU执行异常向量vector_irq的代码(2)在vector_irq里面,最终会调用中断处理的总入口函数asm_do_IRQ(3)asm_do_IRQ根据中断号调用irq_desc数组项中的handle_irq(4)handle_irq会使用chip成员中的函数来设置硬件,比如清除中断,禁止中断,重新使能中断等(5)handle_irq逐个调用驱动程序在action链表中注册的处理函数-->可见,中断体系结构的初始化就是构造这些数据结构,比如irq_desc数组项中的handle_irq,chip等成员;驱动程序中断时就是构造action链表;用户卸载中断时就是从action链表中去除不需要的项

28.init_IRQ函数执行完后,各个irq_desc数组项的chip,handle_irq成员都被设置好了

29.驱动程序注册的中断名字,可以通过"cat /proc/interrupts"来查看

30.action链表的中断处理函数是驱动程序写的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: