秒字符设备
秒字符设备也是字符驱动,所以与之前的字符设备驱动程序的框架类似,模块编译,模块加载与卸载也与内核模块的编译,加载卸载一致。秒字符设备驱(second)的实现,创建目录(second),在该目录下创建设备文件(second.c),内容按下面的代码写即可,在同一目录下编写相应的Makefile文件,然后make就可以编译模块了,然后插入模块(可以带参数,如:sudo insmod second.ko second_major=249),设备驱动程序就好了,但是现在在/dev中并没有”second”这个字符设备驱动,必须创建设备节点才行,创建设备节点的命令是:mknod /dev/chardev0 c 250 0其中chardev0是设备名,c说明设备是字符设备,250是主设备号,0是次设备号,这些都要根据自己的设备驱动程序来赋予相应的值(此处为:mknod /dev/second c 249 0)。设备节点创建成功后在/dev中就可以看到自己创建的设备了或者使用ls-l second(设备名)来查看详细信息。
秒字符设备驱动实现后要进行测试,创建测试文件(test.c)内容按下面的代码写即可。然后编译运行测试文件,就可以看到效果了。
秒字符设备(second.c)源码:
#include<linux/module.h>
#include<linux/types.h>
#include<linux/fs.h>
#include<linux/errno.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<asm/io.h>
#include<asm/system.h>
#include<asm/uaccess.h>
#define SECOND_MAJOR 249//宏定义主设备号
static int second_major =SECOND_MAJOR; //模块参数,可以输入主设备号
struct second_dev//second设备结构体
{
struct cdev cdev;//cdev结构体
atomic_t counter;//一共经历的秒数
struct timer_lists_timer;//设备要使用的定时器
};
struct second_dev*second_devp; //设备结构体指针
//定时器处理函数
static voidsecond_timer_handle(unsigned long arg)
{
mod_timer(&second_devp->s_timer,jiffies+HZ);
atomic_inc(&second_devp->counter);//counter加1
printk(KERN_NOTICE "currentjiffies is %d \n",jiffies);
}
//文件打开
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初值为0
return 0;
}
/文件释放
int second_release(structinode *inode,struct file *filp)
{
del_timer(&second_devp->s_timer);//释放定时器
return 0;
}
//读函数
static ssize_tsecond_read(struct file *filp,char __user *buf,size_t count,loff_t*ppos)
{
int counter;
counter=atomic_read(&second_devp->counter);//获得counter 的当前值
if(put_user(counter,(int*)buf)) //由于内核空间和用户空间不能直接互访,所以用put_user函数将counter的值从内核复制到用户buf所指的空间。
return -EFAULT;
else
return sizeof(unsigned int);
}
//文件操作结构体
static const structfile_operations second_fops =
{
.owner=THIS_MODULE,
.open=second_open,
.release=second_release,
.read=second_read
};
//初始化并注册cdev
static voidsecond_setup_cdev(struct second_dev *dev,int index)
{
interr,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);//注册设备号
if(err)
printk(KERN_NOTICE "error %dadding led %d",err,index);
}
//模块加载函数
intsecond_init(void)
{
int ret;
dev_tdevno=MKDEV(second_major,0);
if(second_major)
ret=register_chrdev_region(devno,1,"second");//申请设备号
else
{
ret=alloc_chrdev_region(&devno,0,1,"second");//动态申请设备号
second_major=MAJOR(devno);//获得申请到的设备号
}
if(ret<0)
return ret;
//动态申请设备结构体的内存
second_devp=kmalloc(sizeof(structsecond_dev),GFP_KERNEL);
if(!second_devp)//申请失败
{
ret = -ENOMEM;
goto fail_malloc;
}
//设置second_devp所指的大小为sizeof(strcutsecond_dev)的内存均为0
memset(second_devp,0,sizeof(struct second_dev));
second_setup_cdev(second_devp,0);//初始化并注册cdev
return 0;
fail_malloc:unregister_chrdev_region(devno,1);
}
//模块卸载函数
voidsecond_exit(void)
{
cdev_del(&second_devp->cdev);//注销cdev
kfree(second_devp);//释放设备结构体内存
unregister_chrdev_region(MKDEV(second_major,0),1);//释放设备号
}
MODULE_AUTHOR("wangfangyong");
MODULE_LICENSE("GPL");
module_param(second_major,int,S_IRUGO);
module_init(second_init);
module_exit(second_exit);
Makefile文件源码:
obj-m := second.o
path =/usr/src/linux-headers-$(shell uname -r)
all:
make -C $(path) M=$(PWD)modules
clean:
make -C $(path) M=$(PWD)clean
测试文件(test.c)源码:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd;
int counter=0;
int old_counter=0;
fd=open("/dev/second",O_RDONLY); //打开设备文件
if(fd!=-1)
{
while(1)
{
read(fd,&counter,sizeof(unsigned int ));//读目前经历的秒数
if(counter!=old_counter)
{
printf("seconds after open/dev/second:%d\n",counter);
old_counter=counter;
}
}
}
else
printf("device openfail\n");
}