一个字符设备“second”的驱动,它在被打开的时候初始化一个定时器并将其添加到内核定时器链表,每秒输出一次当前的jifies(为此,定时器处理函数中每次都要修改新的expires)。代码如下:second_timer.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/jiffies.h>
#define SECOND_MAJOR 88 /*预设second的主设备号*/
static int second_major = SECOND_MAJOR;
/*second 设备结构体*/
struct second_dev
{
struct cdev cdev; //cdev结构体
atomic_t counter; //原子变量,一共经历多少秒?
struct timer_list s_timer; //设备要使用的定时器
};
struct second_dev *second_devp; //设备结构体指针
/*定时器处理函数*/
static void second_timer_handle(unsigned long arg)
{
mod_timer(&second_devp->s_timer,jiffies + HZ);
atomic_inc(&second_devp->counter); //原子变量递增,相当于counter++
printk(KERN_NOTICE "current jiffies is %ld\n",jiffies);//KERN_NOTICE 正常但又重要的条件,用于提醒
}
/*文件打开函数*/
int second_open(struct inode *inode,struct file *filp)
{
/*初始化定时器*/
init_timer(&second_devp->s_timer); //初始化定时器对象中与内核实现相关的成员
second_devp->s_timer.function = &second_timer_handle;
second_devp->s_timer.expires = jiffies + HZ;
add_timer(&second_devp->s_timer); //添加(注册)定时器
atomic_set(&second_devp->counter,0);//原子操作,设置counter的值,计数清零
return 0;
}
/*文件释放函数*/
int second_release(struct inode *inode,struct file *filp)
{
del_timer(&second_devp->s_timer);
return 0;
}
/*globalfifo读函数*/
static ssize_t second_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{
int counter;
counter = atomic_read(&second_devp->counter);//原子读操作
/*
copy_to_user -- Copy a block of data into user space.
put_user ( x, ptr);//Write a simple value into user space.
x Value to copy to user space.
ptr Destination address, in user space.
*/
if(put_user(counter,(int *)buf))
return - EFAULT;//内存空间访问错误
else
return sizeof(unsigned int);
}
/*文件操作结构体*/
static const struct file_operations second_fops =
{
.owner = THIS_MODULE,
.open = second_open,
.release= second_release,
.read = second_read,
};
/*初始化并注册cdev*/
static void second_setup_cdev(struct second_dev *dev,int index)
{
/*
函数:MKDEV(int major,int monor)
头文件:<linux/kdev.h>
参数:major为主设备号 ,monor为次设备号
功能:将主设备号和次设备号转换成dev_t类型
返回:dev_t类型的设备编号
说明:dev_t dev; //设备号,int 类型,高12位为主设备号,低20位为次设备号
可以使用如下宏调用来获得主、次设备号:
MAJOR(dev_t dev)
MINOR(dev_t dev)
*/
int err,devno = MKDEV(second_major,index);
cdev_init(&dev->cdev,&second_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &second_fops;
err = cdev_add(&dev->cdev,devno,1);//传入 cdev 结构的指针,起始设备编号,以及设备编号范围。
if(err)
printk(KERN_NOTICE "Error %d adding LED%d",err,index);
}
/*设备驱动模块加载函数*/
int second_init(void)
{
int ret;
dev_t devno = MKDEV(second_major,0);
/*申请设备号*/
if(second_major)
ret = register_chrdev_region(devno,1,"second");
/*
register_chrdev_region(dev_t first,unsigned int count,char *name)
First :要分配的设备编号范围的初始值(次设备号常设为0);
Count:连续编号范围.
Name:编号相关联的设备名称. (/proc/devices);
*/
else /*动态申请设备号*/
{
/*
alloc_chrdev_region() 是动态分配主次设备号。
register_chrdev()。是老版本的设备号注册方式,他只分配主设备号。从设备号在mknod的时候指定。
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
firstminor : 通常为0;
*dev:存放返回的设备号;
*/
ret = alloc_chrdev_region(&devno,0,1,"second");
second_major = MAJOR(devno);
}
if(ret < 0)
return ret;
/*动态申请设备结构体内存*/
second_devp = kmalloc(sizeof(struct second_dev),GFP_KERNEL);//GFP_KERNEL,他表示内存分配
if(!second_devp) /*申请失败*/
{
ret = - ENOMEM; //核心内存不足
goto fail_malloc;
}
memset(second_devp,0,sizeof(struct second_dev));
second_setup_cdev(second_devp,0);
return 0;
fail_malloc: unregister_chrdev_region(devno,1);
}
/*模块卸载函数*/
void second_exit(void)
{
cdev_del(&second_devp->cdev); //注销cdev
kfree(second_devp); //释放设备结构体内存
unregister_chrdev_region(MKDEV(second_major,0),1);//释放设备号
}
MODULE_AUTHOR("Huang Dezhi");
MODULE_LICENSE("GPL");
module_param(second_major,int,S_IRUGO);
module_init(second_init);
module_exit(second_exit);
在second的open()函数中,将启动定时器,此后每秒会再次运行定时器处理函数,在second的release()函数中,定时器被删除。
second_dev结构体中的原子变量counter用于秒计数,每次在定时器处理函数中将被atomic_inc()调用原子增1,second的read()函数会将这个值返回给用户空间。
编译驱动,加载该内核模块并创建“second”设备文件节点后,使用用户空间测试程序打开“second”。
用户测试应用程序会不断地读取自打开“second”设备文件以来经历的秒数。
second设备用户空间测设程序:second_test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int fd;
int counter = 0;
int old_counter = 0;
/*打开second设备文件*/
fd = open("second",O_RDONLY);
if(fd != -1)
{
while(1)
{
read(fd,&counter,sizeof(unsigned int));//读目前经历的秒数
if(counter != old_counter)
{
printf("second after open sencond : %d\n",counter);
old_counter = counter;
}
}
}
else
{
printf("Device open failure\n");
}
return 0;
}
运行second_test.c后,内核将不断地输出目前的jiffies值。