分享

Linux设备驱动程序中的一些具体问题

 lchjczw 2012-04-06

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端口被映射到一段内存中去,要访问这些端口就相当于访问一段内存。经常性的,我们要获得一块内存的物理地址。

2。内存操作

在设备驱动程序中动态开辟内存,不是用malloc,而是kmalloc,或者用get_free_pages直接申请页。释放内存用的是kfree,或free_pages。 请注意,kmalloc等函数返回的是物理地址!

注意,kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。

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

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

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

 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的一些扩展特性,而这些扩展特性必须在加了优化选项之后才能体现出来。

 

由于设备种类繁多,相应的设备驱动程序也非常之多。尽管设备驱动程序是内核的一部分,但设备驱动程序的开发往往由很多人来完成,如业余编程高手、设备厂商等。为了让设备驱动程序的开发建立在规范的基础上,就必须在驱动程序和内核之间有一个严格定义和管理的接口,例如SVR4提出了DDI/DDK规范,其含义就是设备与驱动程序接口/设备驱动程序与内核接口(Device-Driver Interface/Driver-Kernel Interface)。通过这个规范,可以规范设备驱动程序与内核之间的接口。

 

Linux的设备驱动程序与外接的接口与DDI/DKI规范相似,可以分为三部分:

(1)   驱动程序与内核的接口,这是通过数据结构file_operations来完成的。

(2)   驱动程序与系统引导的接口,这部分利用驱动程序对设备进行初始化。

(3)   驱动程序与设备的接口,这部分描述了驱动程序如何与设备进行交互,这与具体设备密切相关。

 

根据功能,驱动程序的代码可以分为如下几个部分:

(1)驱动程序的注册和注销

(2)设备的打开与释放

(3)设备的读和写操作

(4)设备的控制操作

(5)设备的中断和查询处理

前三点我们已经给予简单说明,后面我们还会结合具体程序给出进一步的说明。关于设备的控制操作可以通过驱动程序中的ioctl()来完成,例如,对光驱的控制可以使用cdrom_ ioctl()。

与读写操作不同,ioctl()的用法与具体设备密切相关,例如,对于软驱的控制可以使用floppy_ioctl(),其调用形式为:

static int floppy_ioctl(struct inode *inode, struct file *filp,

unsigned int cmd, unsigned long param)

其中cmd的取值及含义与软驱有关,例如,FDEJECT表示弹出软盘。

除了ioctl(),设备驱动程序还可能有其他控制函数,如lseek()等。

对于不支持中断的设备,读写时需要轮流查询设备的状态,以便决定是否继续进行数据传输,例如,打印机驱动程序在缺省时轮流查询打印机的状态。如果设备支持中断,则可按中断方式进行

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多