|
使用内核定时器控制打印机端口上的 LED 。程序代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/time.h>
#include <linux/timer.h>
#define KERNELTIMER_WRITE_ADDR 0x378
#define TIME_STEP (2*HZ / 10)
typedef struct {
struct timer_list timer;
unsigned long led;
}__attribute__((packed)) KERNEL_TIMER_MANAGER;
static KERNEL_TIMER_MANAGER *ptrmng = NULL;
void kerneltimer_timeover (unsigned long arg);
void kerneltimer_registertimer (KERNEL_TIMER_MANAGER *pdata, unsigned long timeover)
{
init_timer (&(pdata->timer)); /*初始化 timer_list 结构*/
pdata->timer.expires = get_jiffies_64() + timeover;
pdata->timer.data = (unsigned long) pdata; /*指针地址转换为整数暂存*/
/*设置超时后处理函数,(unsigned long)型pdata变量超时后自动传入kerneltimer_timerover函数作为其参数*/
pdata->timer.function = kerneltimer_timeover;
add_timer (&(pdata->timer)); /*注册 timer 结构体*/
}
void kerneltimer_timeover (unsigned long arg)
{
KERNEL_TIMER_MANAGER *pdata = NULL;
if (arg) {
pdata = (KERNEL_TIMER_MANAGER *) arg;
outb ((unsigned char) (pdata->led & 0xFF), KERNELTIMER_WRITE_ADDR);
pdata->led = ~(pdata->led);
kerneltimer_registertimer(pdata, TIME_STEP);
}
}
int kerneltimer_init (void)
{
ptrmng = kmalloc (sizeof (KERNEL_TIMER_MANAGER), GFP_KERNEL);
if (ptrmng == NULL) return -ENOMEM;
memset (ptrmng, 0, sizeof (KERNEL_TIMER_MANAGER));
ptrmng->led = 0;
kerneltimer_registertimer (ptrmng, TIME_STEP);
return 0;
}
void kerneltimer_exit (void)
{
if (ptrmng != NULL) {
del_timer (&(ptrmng->timer));
kfree (ptrmng);
}
outb (0x00, KERNELTIMER_WRITE_ADDR);
}
module_init (kerneltimer_init);
module_exit (kerneltimer_exit);
MODULE_LICENSE ("Dual BSD/GPL"); 说明:
在 kerneltimer_init() 函数中:
ptrmng 指向地址上分配了 KERNEL_TIMER_MANAGER 结构体内存空间。
ptrmng->led = 0 表示一开始 LED 设置为关闭状态。
最后调用 kerneltimer_registertimer() 函数,此函数的主要目的是初始化内核定时器相关变量,并在内核定时器目录上注册内核定时器函数。
在 kerneltimer_registertimer() 函数中:
首先使用 init_timer() 函数,该函数初始化 struct timer_list 结构,这个结构一般在设备驱动程序里无需关心其内部结构。
pdata->timer.expires = get_jiffies_64() + timeover; 这条语句设定了超时时间,get_jiffies_64() 得到当前时间,timerover 是超时时间,这里由 TIME_STEP 宏指定,是 0.2s 时长。
pdata->timer.data = (unsigned long) pdata; 这条语句把原来指向结构体的指针强制转换为 unsigned long 类型存到 pdata->timer.data 中,这个 timer.data 的值在发生内核超时中断时,会自动作为超时处理函数的参数传入超时处理函数中。
pdata->timer.function = kerneltimer_timeover; //设置超时候处理函数 kerneltimer_timerover ,并通过 add_timer() 函数实现注册。当 jiffies_64 的值大于或等于 expires 域时,会调用到这个函数。kerneltimer_timerover() 会最先确认其参数 arg 上有无传送的值(在本例中,arg 的值就是注册到内核定时器和包含 led 状态结构体的地址)。当检查到 arg 上有传送值时,就把该值强制转换(casting)后再代入到 pdata 变量上。
最后,再调用 kerneltimer_registertimer() 函数,实现自身的再次注册,于是又一轮新的循环开始,这个循环周期就是 TIME_STEP 为 0.2s 。
在 insmod 模块到内核中时,会看到并口上的 LED 灯以 0.2s 的间隔闪烁。当 rmmod 模块时,就会相应的调用 kerneltimer_exit() 函数,实现了内核模块的注销。kerneltimer_exit() 函数里要检查 ptrmng 全局变量上是否有分配内存,如果有,则要通过 kfree() 将其释放,以免内存泄露。最后调用 del_timer() 函数终止掉 kerneltimer_timerover() 函数的调用,kerneltimer_timerover() 函数就会处于结束运行并已注销的状态。 |
|