在Linux系统中,设备驱动程序初始化的时候向系统进行登记,并进行系统资源的申请。
初始化部分负责给设备驱动程序申请的系统资源,包括内存、中断、时钟、I/O端口等,同样,这些资源也可以在open子程序或别的地方申请。并且值得注意的是,由于系统资源紧张,在这些资源不用的时候,应该释放它们,以利于资源的共享。
下面来依次介绍系统资源的申请与释放问题:
设备驱动程序申请内存调用的是kmaloc与kfree,而不是malloc与free,前者被定义为:
#include<linux/kernel>
void * kmalloc(size_t size,int flags);
void kfree(const void *ptr);
其中参数size为要分配的内存大小,flags为分配标志,一般用 GFP_KERNEL,ptr为要释放的内存指针。
值得注意的是kmalloc与vmalloc的区别:kamlloc申请的内存位于内核空间物理内存映射区域,而且在物理上也是连续的,与真实得物理地址只有一个固定的偏移,是一种比较简单的转换关系。而vmalloc申请的内存同样位于内核空间,并且在物理上也是连续的,但是这片连续的虚拟内存在物理内存上却并不一定连续。malloc申请的内存位于用户空间。
设备驱动程序通过调用request_irq函数来申请中断,通过free_irq来释放中断。
#include <linux/sched.h>
int request_irq(unsigned int irq,
void (*handler)(int irq,void dev_id,struct pt_regs *regs),
unsigned long irqflags,
const char *devname,
void *dev_id);
void free_irq(unsigned int irq, void *dev_id);
参数irq是要申请中断的硬件中断号,handler是向系统登记的中断处理函数,irqflags是中断的属性,devname为设备名,dev_id为中断的系统标识
在设备驱动程序里,一般都需要用到计时机制。在LINUX系统中,时钟是由系统接管,设备驱动程序可以向系统申请时钟。
#include <asm/param.h>
#include <linux/timer.h>
void add_timer(struct timer_list * timer); //增加化定时器
int del_timer(struct timer_list * timer); //删除定时器
inline void init_timer(struct timer_list * timer); //初始化定时器
struct timer_list的定义为:
struct timer_list {
struct list_head entry; //定时器列表
unsigned long expires; // 定时器到期时间
void (*function)(unsigned long data); //定时器处理函数
unsigned long data; //作为参数传入定时器处理函数
struct timer_base_s *base;
};
其中expires是系统要执行function所需要的时间。系统核心有一个全局变量JIFFIES表示当前时间,一般在调用add_timer时jiffies =JIFFIES+num,表示在num个系统最小时间间隔后执行function。系统最小时间间隔与所用的硬件平台有关,在核心里定义了常数HZ表示一秒内最小时间间隔的数目,则num*HZ表示num秒。系统计时到预定时间就调用function,并把此子程序从定时队列里删除,因此如果想要每隔一定时间间隔执行一次的话,就必须在function里再一次调用add_timer。function的参数data即为timer_list结构体
与中断和内存不同,使用一个没有申请的I/O端口不会使CPU产生异常,也就不会导致诸如“segmentation fault"一类的错误发生。任何进程都可以访问任何一个I/O端口。此时系统无法保证对I/O端口的操作不会发生冲突,甚至会因此而使系统崩溃。因此,在使用I/O端口前,也应该检查此I/O端口是否已有别的程序在使用,若没有,再把此端口标记为正在使用,在使用完以后释放它。
这样需要用到如下几个函数:
int check_region(unsigned int from, unsigned long n);
void request_region(unsigned int from, unsigned long n, const char *name);
void release_region(unsigned int from, unsign long n);
调用这些函数时的参数为:from表示所申请的I/O端口的起始地址;n为所要申请的从from开始的端口数;name为设备名,将会出现在/proc/ioports文件里。check_region返回0表示I/O端口空闲,否则为正在被使用。
里面的data项。