刚开始学习驱动的时候,每次都需要mknod /dev/timer c 500 0 这样手动去创建一个设备节点;实际上Linux内核提供一组函数,可以用来在驱动模块加载的时候自动在/dev目录下创建相应的设备节点,并在下载的时候删该节点。
device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)和device_destroy(struct class *class,dev_t devt)配对使用
class_create(owner, name)和class_destroy(struct class *cls);配对使用
代码实现:
/*
*************************************************************************
环境:ubuntu 12.04
交叉编译工具:arm-none-linux-gnueabi-
开发板:exynos4412
***********************************************************************************
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h> //字符设备的头文件
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <linux/device.h>
#define MA 300 //主设备号
struct class *my_class; //定义一个class 用于udev自动创建一个节点
MODULE_LICENSE("Dual BSD/GPL"); //模块许可声明 三要素之一
int timer_major=MA; //主设备号 用于区分不同种类的设备
//timer设备结构体
struct timer_dev {
struct cdev cdev; //cdev结构体
int counter; //一共经历了多少秒
struct timer_list s_timer;//设备要使用定时器
}timer_dev;
//定时器处理函数
static void timer_handle(unsigned long arg) {
mod_timer(&timer_dev.s_timer,jiffies+HZ);
timer_dev.counter++;
printk(KERN_NOTICE"current jiffies %d\n",jiffies);
}
//文件打开函数
int timer_open (struct inode *inode,struct file *filp) {
//初始化定时器
init_timer(&timer_dev.s_timer); //初始化定时器
timer_dev.s_timer.function=timer_handle; //指定定时器处理函数
timer_dev.s_timer.expires=jiffies+HZ; //
add_timer(&timer_dev.s_timer);//添加注册定时器
timer_dev.counter=0; //计数清零
return 0;
}
//文件关闭函数
int timer_release(struct inode *inode,struct file *filp) {
del_timer(&timer_dev.s_timer);
printk("timer release \n");
return 0;
}
//文件读函数
static ssize_t timer_read (struct file * filp,char __user *buf,size_t count,loff_t *ppos) {
if(put_user(timer_dev.counter,(int *)buf))
return -EFAULT;
else
return sizeof(unsigned int);
}
//文件操作结构体
static const struct file_operations timer_fops = {
.owner = THIS_MODULE,
.open = timer_open,
.release = timer_release,
.read = timer_read,
};
//初始化并注册cdev
static void timer_setup_cdev (struct timer_dev *dev,int index) {
int err,devno =MKDEV(timer_major,index); //获取设备号
cdev_init(&dev->cdev,&timer_fops); //字符设备初始化
err=cdev_add(&dev->cdev,devno,1); //添加一个字符设备到系统中
if(err) //异常判断
{
printk(KERN_NOTICE"Error %d adding LED%d",err,index);
}
}
//设备驱动模块加载函数
int timer_init(void) {
int ret;
dev_t devno =MKDEV(timer_major,0); //得到设备号
/*
*MKDEV(MAJOR,MIN);
*MAJOR:主设备号 指是哪一类设备
*MIN: 次设备号 指具体哪一个设备
*/
ret=register_chrdev_region(devno,1,"timmer");//注册设备号
/*
*注册一个字符设备号(静态分配)
*为一个字符驱动获取一个或多个设备编号
*register_chrdev_region(dev_id,DEVICE_NUM,DEVICE_NAME);
*dev_id 分配的起始设备编号(一般是0)
*DEVICE_NUM: 请求的连接设备编号的总数(不能太大,避免别的主设备号冲突)
*DEVICE_NAME: 是应当连接到这个编号的设备名字
*/
if(ret<0)
return ret;
timer_setup_cdev(&timer_dev,0); //初始化处理函数
//自动创建一个设备节点
my_class=class_create(THIS_MODULE,"timer");
if(IS_ERR(my_class)) {
printk("Err:failed in creating class.\n");
return -1;
}
//注册这个设备节点
device_create(my_class,NULL,devno,NULL,"timer_dev");
printk("timer init two ok\n");
return 0;
}
//设备驱动模块卸载函数
void timer_exit(void) {
dev_t devno =MKDEV(timer_major,0); //得到设备号
cdev_del(&timer_dev.cdev);
device_destroy(my_class,devno); //注销这个设备节点
class_destroy(my_class);//删除一个设备节点
unregister_chrdev_region(MKDEV(timer_major,0),1);//释放设备号
printk("timer exit ok\n");
}
module_init(timer_init); //模块加载入口声明 三要素之一
module_exit(timer_exit); //模块卸载入口声明 三要素之一
执行结果:
[root@farsight]#ls
a.out dev lib mnt root sys timer.o usr
bin etc linuxrc proc sbin timer.ko tmp var
加载驱动模块:
[root@farsight]#insmod timer.ko
[ 47.760000] timer init two ok
查看驱动模块加载信息
[root@farsight]#lsmod
timer 2172 0 - Live 0xbf000000 (O)
查看自动创建的设备节点
[root@farsight]#ls -l /dev/timer_dev
crw-rw---- 1 0 0 300, 0 Jan 1 00:00 /dev/timer_dev
可以看到自动创建设备节点成功
卸载驱动模块:
[root@farsight]#rmmod timer
[ 243.960000] timer exit ok
再次加载也能成功
[root@farsight]#insmod timer.ko
[ 304.940000] timer init two ok
如果没有配对使用,重新加载的时候会报错
//class_destroy(my_class);//删除一个设备节点
我将这一行屏蔽掉,看运行结果
[root@farsight]#insmod timer.ko
[ 445.950000] timer init two ok
[root@farsight]#rmmod timer.ko
[root@farsight]#insmod timer.ko
insmod: can't insert 'timer.ko': File exists
当在加载的时候就出现这样的错误
将下面两行屏蔽掉
//device_destroy(my_class,devno); //注销这个设备节点
//class_destroy(my_class);//删除一个设备节点
结果:
[root@farsight]#insmod timer.ko
[ 58.595000] timer init two ok
[root@farsight]#ls -l /dev/timer_dev
crw-rw---- 1 0 0 300, 0 Jan 1 00:00 /dev/timer_dev
[root@farsight]#rmmod timer
[ 84.430000] timer exit ok
第一次加载时没有问题的,卸载的时候问题就来了,可以看到,显示是成功的的,但是看下面的查询,是没有删除成功的
[root@farsight]#ls -l /dev/timer_dev
crw-rw---- 1 0 0 300, 0 Jan 1 00:00 /dev/timer_dev
在此加载就会出现下面的问题
[root@farsight]#insmod timer.ko
[ 103.780000] ------------[ cut here ]------------
[ 103.785000] WARNING: CPU: 0 PID: 1203 at fs/sysfs/dir.c:52 sysfs_warn_dup+0x68/0x84()
[ 103.795000] sysfs: cannot create duplicate filename '/class/timer'
[ 103.800000] Modules linked in: timer(O+) [last unloaded: timer]
[ 103.805000] CPU: 0 PID: 1203 Comm: insmod Tainted: G O 3.14.0 #2
[ 103.810000] [<c0013e10>] (unwind_backtrace) from [<c0011240>] (show_stack+0x10/0x14)
[ 103.820000] [<c0011240>] (show_stack) from [<c03b874c>] (dump_stack+0x64/0xb4)
[ 103.825000] [<c03b874c>] (dump_stack) from [<c001ce74>] (warn_slowpath_common+0x68/0x88)
[ 103.835000] [<c001ce74>] (warn_slowpath_common) from [<c001cf28>] (warn_slowpath_fmt+0x30/0x40)
[ 103.845000] [<c001cf28>] (warn_slowpath_fmt) from [<c01092e8>] (sysfs_warn_dup+0x68/0x84)
[ 103.850000] [<c01092e8>] (sysfs_warn_dup) from [<c0109388>] (sysfs_create_dir_ns+0x84/0x8c)
[ 103.860000] [<c0109388>] (sysfs_create_dir_ns) from [<c01d6240>] (kobject_add_internal+0x9c/0x2c0)
[ 103.870000] [<c01d6240>] (kobject_add_internal) from [<c01d6484>] (kset_register+0x20/0x3c)
[ 103.880000] [<c01d6484>] (kset_register) from [<c02482b8>] (__class_register+0xac/0x198)
[ 103.885000] [<c02482b8>] (__class_register) from [<c02483e4>] (__class_create+0x40/0x6c)
[ 103.895000] [<c02483e4>] (__class_create) from [<bf004168>] (init_module+0x6c/0xec [timer])
[ 103.900000] [<bf004168>] (init_module [timer]) from [<c00087b4>] (do_one_initcall+0x30/0x144)
[ 103.910000] [<c00087b4>] (do_one_initcall) from [<c0074f9c>] (load_module+0x173c/0x1c68)
[ 103.920000] [<c0074f9c>] (load_module) from [<c00755a4>] (SyS_init_module+0xdc/0xe0)
[ 103.925000] [<c00755a4>] (SyS_init_module) from [<c000e420>] (ret_fast_syscall+0x0/0x30)
[ 103.935000] ---[ end trace 11ebc48fbb16ce8a ]---
[ 103.940000] ------------[ cut here ]------------
[ 103.945000] WARNING: CPU: 0 PID: 1203 at lib/kobject.c:240 kobject_add_internal+0x238/0x2c0()
[ 103.950000] kobject_add_internal failed for timer with -EEXIST, don't try to register things with the same name in the same directory.
[ 103.965000] Modules linked in: timer(O+) [last unloaded: timer]
[ 103.970000] CPU: 0 PID: 1203 Comm: insmod Tainted: G W O 3.14.0 #2
[ 103.975000] [<c0013e10>] (unwind_backtrace) from [<c0011240>] (show_stack+0x10/0x14)
[ 103.985000] [<c0011240>] (show_stack) from [<c03b874c>] (dump_stack+0x64/0xb4)
[ 103.990000] [<c03b874c>] (dump_stack) from [<c001ce74>] (warn_slowpath_common+0x68/0x88)
[ 104.000000] [<c001ce74>] (warn_slowpath_common) from [<c001cf28>] (warn_slowpath_fmt+0x30/0x40)
[ 104.010000] [<c001cf28>] (warn_slowpath_fmt) from [<c01d63dc>] (kobject_add_internal+0x238/0x2c0)
[ 104.020000] [<c01d63dc>] (kobject_add_internal) from [<c01d6484>] (kset_register+0x20/0x3c)
[ 104.025000] [<c01d6484>] (kset_register) from [<c02482b8>] (__class_register+0xac/0x198)
[ 104.035000] [<c02482b8>] (__class_register) from [<c02483e4>] (__class_create+0x40/0x6c)
[ 104.040000] [<c02483e4>] (__class_create) from [<bf004168>] (init_module+0x6c/0xec [timer])
[ 104.050000] [<bf004168>] (init_module [timer]) from [<c00087b4>] (do_one_initcall+0x30/0x144)
[ 104.060000] [<c00087b4>] (do_one_initcall) from [<c0074f9c>] (load_module+0x173c/0x1c68)
[ 104.065000] [<c0074f9c>] (load_module) from [<c00755a4>] (SyS_init_module+0xdc/0xe0)
[ 104.075000] [<c00755a4>] (SyS_init_module) from [<c000e420>] (ret_fast_syscall+0x0/0x30)
[ 104.085000] ---[ end trace 11ebc48fbb16ce8b ]---
[ 104.085000] Err:failed in creating class.
insmod: can't insert 'timer.ko': Operation not permitted
[root@farsight]#
总结:在驱动编写中,很多函数是成对使用的,需要特别注意,一般有申请就有释放,有新增就有删除