7.1.I/O口函数
无论驱动程序多么复杂,归根结底,无非还是向某个端口或者某个寄存器位赋值,这个值只能是0或1。接收值的就是I/O口。与中断和内存不同,使用一个没有 申请的I/O端口不会使处理器产生异常,也就不会导致诸如“segmentationfault”一类的错误发生。由于任何进程都可以访问任何一个I/O 端口,此时系统无法保证对I/O端口的操作不会发生冲突,甚至因此而使系统崩溃。因此,在使用I/O端口前,也应该检查此I/O端口是否已有别的程序在使 用,若没有,再把此端口标记为正在使用,在使用完以后释放它。
这样需要用到如下几个函数:
int check_region(unsigned int from,unsigned int extent);
void request_region(unsigned int from,unsigned int extent,const char *name);
void release_region(unsigned int from, unsignedint extent);
调用这些函数时的参数为:
— from表示所申请的I/O端口的起始地址;
— extent为所要申请的从from开始的端口数;
— name为设备名,将会出现在/proc/ioports文件里;
— check_region返回0表示I/O端口空闲,否则为正在被使用。
在申请了I/O端口之后,可以借助asm/io.h中的如下几个函数来访问I/O端口:
inline unsigned int inb(unsigned shortport);
inline unsigned int inb_p(unsigned shortport);
inline void outb(char value, unsigned shortport);
inline void outb_p(char value,unsigned short port);
其中inb_p和outb_p插入了一定的延时以适应某些低速的I/O端口。
7.2.时钟函数
在设备驱动程序中,一般都需要用到计时机制。在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 timer_list *next;
struct timer_list *prev;
unsigned long expires;
unsigned long data;
void (*function)(unsigned long d);
};
其中,expires是要执行function的时间。系统核心有一个全局变量jiffies表示当前时间,一般在调用add_timer时 jiffies=JIFFIES+num,表示在num个系统最小时间间隔后执行function函数。系统最小时间间隔与所用的硬件平台有关,在核心里 定义了常数HZ表示一秒内最小时间间隔的数目,则num*HZ表示num秒。系统计时到预定时间就调用function,并把此子程序从定时队列里删除, 可见,如果想要每隔一定时间间隔执行一次的话,就必须在function里再一次调用add_timer。function的参数d即为timer里面的 data项。
7.3.内存操作函数
作为系统核心的一部分,设备驱动程序在申请和释放内存时不是调用malloc和free,而代之以调用kmalloc和kfree,它们在linux/kernel.h中被定义为:
void * kmalloc(unsigned int len, intpriority);
void kfree(void * obj);
参数len为希望申请的字节数,obj为要释放的内存指针。priority为分配内存操作的优先级,即在没有足够空闲内存时如何操作,一般由取值GFP_KERNEL解决即可。
7.4.复制函数
在用户程序调用read、write时,因为进程的运行状态由用户态变为核心态,地址空间也变为核心地址空间。由于read、write中参数buf是指向用户程序的私有地址空间的,所以不能直接访问,必须通过下面两个系统函数来访问用户程序的私有地址空间。
#include <asm/segment.h>
void memcpy_fromfs(void * to,const void *from,unsigned long n);
void memcpy_tofs(void * to,const void *from,unsigned long n);
memcpy_fromfs由用户程序地址空间往核心地址空间复制,memcpy_tofs则反之。参数to为复制的目的指针,from为源指针,n为要复制的字节数。
在设备驱动程序里,可以调用printk来打印一些调试信息,printk的用法与printf类似。printk打印的信息不仅出现在屏幕上,同时还记录在文件syslog里。