精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>电脑技术>>● Linux>>有待整理文章>>如何编写Linux操作系统下的设备驱动程序(2)

主题:如何编写Linux操作系统下的设备驱动程序(2)
发信人: r_hayes(Hayes)
整理人: qiaoqian(2002-05-13 06:53:32), 站内信件
三 设备驱动程序中的一些具体问题。 

1. I/O Port. 

和硬件打交道离不开I/O Port,老的ISA设备经常是占用实际的I/O端口,在linux下,操作系统没有对I/O口屏蔽,也就是说,任何驱动程序都可对任意的I/O口操作,这样就很容易引起混乱。每个驱动程序应该自己避免误用端口。 

有两个重要的kernel函数可以保证驱动程序做到这一点。 

1)check_region(int io_port, int off_set) 

这个函数察看系统的I/O表,看是否有别的驱动程序占用某一段I/O口。 

参数1:io端口的基地址, 

参数2:io端口占用的范围。 

返回值:0 没有占用, 非0,已经被占用。 

2)request_region(int io_port, int off_set,char *devname) 

如果这段I/O端口没有被占用,在我们的驱动程序中就可以使用它。在使用之前,必须向系统登记,以防止被其他程序占用。登记后,在/proc/ioports文件中可以看到你登记的io口。 

参数1:io端口的基地址。 

参数2:io端口占用的范围。 

参数3:使用这段io地址的设备名。 

在对I/O口登记后,就可以放心地用inb(), outb()之类的函来访问了。 

在一些pci设备中,I/O端口被映射到一段内存中去,要访问这些端口就相当于访问一段内存。经常性的,我们要获得一块内存的物理地址。在dos环境下,(之所以不说是dos操作系统是因为我认为DOS根本就不是一个操作系统,它实在是太简单,太不安全了)只要用段:偏移就可以了。在window95中,95ddk提供了一个vmm 调用 _MapLinearToPhys,用以把线性地址转化为物理地址。但在Linux中是怎样做的呢? 

2 内存操作 

在设备驱动程序中动态开辟内存,不是用malloc,而是kmalloc,或者用get_free_pages直接申请页。释放内存用的是kfree,或free_pages. 请注意,kmalloc等函数返回的是物理地址!而malloc等返回的是线性地址!关于kmalloc返回的是物理地址这一点本人有点不太明白:既然从线性地址到物理地址的转换是由386cpu硬件完成的,那样汇编指令的操作数应该是线性地址,驱动程序同样也不能直接使用物理地址而是线性地址。但是事实上kmalloc返回的确实是物理地址,而且也可以直接通过它访问实际的RAM,我想这样可以由两种解释,一种是在核心态禁止分页,但是这好像不太现实;另一种是linux的页目录和页表项设计得正好使得物理地址等同于线性地址。我的想法不知对不对,还请高手指教。 

言归正传,要注意kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。kmalloc用法参见khg. 

内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用F0000000以上的地址空间。在驱动程序中不能直接访问,要通过kernel函数vremap获得重新映射以后的地址。 

另外,很多硬件需要一块比较大的连续内存用作DMA传送。这块内存需要一直驻留在内存,不能被交换到文件中去。但是kmalloc最多只能开辟128k的内存。 

这可以通过牺牲一些系统内存的方法来解决。 

具体做法是:比如说你的机器由32M的内存,在lilo.conf的启动参数中加上mem=30M,这样linux就认为你的机器只有30M的内存,剩下的2M内存在vremap之后就可以为DMA所用了。 

请记住,用vremap映射后的内存,不用时应用unremap释放,否则会浪费页表。 

3 中断处理 

同处理I/O端口一样,要使用一个中断,必须先向系统登记。 

int request_irq(unsigned int irq , 

void(*handle)(int,void *,struct pt_regs *), 

unsigned int long flags, 

const char *device); 

irq: 是要申请的中断。 

handle:中断处理函数指针。 

flags:SA_INTERRUPT 请求一个快速中断,0 正常中断。 

device:设备名。 

如果登记成功,返回0,这时在/proc/interrupts文件中可以看你请求的中断。 

4一些常见的问题。 

对硬件操作,有时时序很重要。但是如果用C语言写一些低级的硬件操作的话,gcc往往会对你的程序进行优化,这样时序就错掉了。如果用汇编写呢,gcc同样会对汇编代码进行优化,除非你用volatile关键字修饰。最保险的办法是禁止优化。这当然只能对一部分你自己编写的代码。如果对所有的代码都不优化,你会发现驱动程序根本无法装载。这是因为在编译驱动程序时要用到gcc的一些扩展特性,而这些扩展特性必须在加了优化选项之后才能体现出来。 

关于kernel的调试工具,我现在还没有发现有合适的。有谁知道请告诉我,不胜感激。我一直都在printk打印调试信息,倒也还凑合。 

关于设备驱动程序还有很多内容,如等待/唤醒机制,块设备的编写等。 

我还不是很明白,不敢乱说。 

欢迎大家批评指正。 



----

                    灌灌 
                    灌灌 
                    灌灌 
                    灌灌          灌灌 
                    灌灌      灌灌灌 
    灌灌灌灌灌灌灌  灌灌  灌灌灌 
      灌灌灌灌灌灌灌灌灌灌灌灌 
                灌灌灌灌灌灌 
               灌灌灌灌灌灌灌  
              灌灌  灌灌  灌灌 
             灌灌   灌灌    灌灌 
            灌灌    灌灌      灌灌 
          灌灌      灌灌      灌灌灌 
          灌灌      灌灌        灌灌灌灌 
        灌灌        灌灌        灌灌灌灌灌 
      灌灌          灌灌          灌灌灌灌灌  
    灌灌            灌灌            灌灌 
                灌灌灌灌 
                  灌灌灌 
                    灌    

[关闭][返回]