13-实例_秒字符设备
10.5.3 实例:秒字符设备
下面我们编写一个字符设备“second”(即“秒”)的驱动,它在被打开的时候初始化一个定时器并将其添加到内核定时器链表,每秒输出一次当前的jiffies(为此,定时器处理函数中每次都要修改新的expires),整个程序如代码清单10.11所示。
代码清单10.12 使用内核定时器的second字符设备驱动
1 #include <linux/module.h>
2 #include <linux/types.h>
3 #include <linux/fs.h>
4 #include <linux/errno.h>
5 #include <linux/mm.h>
6 #include <linux/sched.h>
7 #include <linux/init.h>
8 #include <linux/cdev.h>
9 #include <asm/io.h>
10 #include <asm/system.h>
11 #include <asm/uaccess.h>
12
13 #define SECOND_MAJOR 248 /预设的second的主设备号/
14
15 static int second_major = SECOND_MAJOR;
16
17 /second设备结构体/
18 struct second_dev {
19 struct cdev cdev; /cdev结构体/
20 atomic_t counter;/ 一共经历了多少秒?/
21 struct timer_list s_timer; /设备要使用的定时器/
22 };
23
24 struct second_dev second_devp; /设备结构体指针*/
25
26 /定时器处理函数/
27 static void second_timer_handle(unsigned long arg)
28 {
29 mod_timer(&second_devp->s_timer,jiffies + Hz);
30 atomic_inc(&second_devp->counter);
31
32 printk(KERN_NOTICE "current jiffies is %ld\n", jiffies);
33 }
34
35 /文件打开函数/
36 int second_open(struct inode inode, struct file filp)
37 {
38 /初始化定时器/
39 init_timer(&second_devp->s_timer);
40 second_devp->s_timer.function = &second_timer_handle;
41 second_devp->s_timer.expires = jiffies + Hz;
42
43 add_timer(&second_devp->s_timer); /添加(注册)定时器/
44
45 atomic_set(&second_devp->counter,0); //计数清0
46
47 return 0;
48 }
49 /文件释放函数/
50 int second_release(struct inode inode, struct file filp)
51 {
52 del_timer(&second_devp->s_timer);
53
54 return 0;
55 }
56
57 /读函数/
58 static ssize_t second_read(struct file filp, char - -user buf, size_t count,
59 loff_t *ppos)
60 {
61 int counter;
62
63 counter = atomic_read(&second_devp->counter);
64 if(put_user(counter, (int*)buf))
65 return - EFAULT;
66 else
67 return sizeof(unsigned int);
68 }
69
70 /文件操作结构体/
71 static const struct file_operations second_fops = {
72 .owner = THIS_MODULE,
73 .open = second_open,
74 .release = second_release,
75 .read = second_read,
76 };
77
78 /初始化并注册cdev/
79 static void second_setup_cdev(struct second_dev *dev, int index)
80 {
81 int err, devno = MKDEV(second_major, index);
82
83 cdev_init(&dev->cdev, &second_fops);
84 dev->cdev.owner = THIS_MODULE;
85 err = cdev_add(&dev->cdev, devno, 1);
86 if (err)
87 printk(KERN_NOTICE "Error %d adding LED%d", err, index);
88 }
89
90 /设备驱动模块加载函数/
91 int second_init(void)
92 {
93 int ret;
94 dev_t devno = MKDEV(second_major, 0);
95
96 / 申请设备号/
97 if (second_major)
98 ret = register_chrdev_region(devno, 1, "second");
99 else { / 动态申请设备号 /
100 ret = alloc_chrdev_region(&devno, 0, 1, "second");
101 second_major = MAJOR(devno);
102 }
103 if (ret < 0)
104 return ret;
105 / 动态申请设备结构体的内存/
106 second_devp = kmalloc(sizeof(struct second_dev), GFP_KERNEL);
107 if (!second_devp) { /申请失败/
108 ret = - ENOMEM;
109 goto fail_malloc;
110 }
111
112 memset(second_devp, 0, sizeof(struct second_dev));
113
114 second_setup_cdev(second_devp, 0);
115
116 return 0;
117
118 fail_malloc:
119 unregister_chrdev_region(devno, 1);
120 return ret;
121 }
122
123 /模块卸载函数/
124 void second_exit(void)
125 {
126 cdev_del(&second_devp->cdev); /注销cdev/
127 kfree(second_devp); /释放设备结构体内存/
128 unregister_chrdev_region(MKDEV(second_major, 0), 1); /释放设备号/
129 }
130
131 MODULE_AUTHOR("Barry Song [email protected]");
132 MODULE_LICENSE("Dual BSD/GPL");
133
134 module_param(second_major, int, S_IRUGO);
135
136 module_init(second_init);
137 module_exit(second_exit);
在second的open()函数中,将启动定时器,此后每1s会再次运行定时器处理函数,在second 的release()函数中,定时器被删除。
second_dev结构体中的原子变量counter用于秒计数,每次在定时器处理函数中将被atomic_inc()调用原子的增1,second的read()函数会将这个值返回给用户空间。
/home/lihacker/develop/svn/ldd6410-read-only/training/kernel/drivers/second包含了second设备驱动以及second_test.c用户空间测试程序,运行make命令编译得到second.ko和second_test,加载second.ko内核模块并创建“/dev/second”设备文件结点:
root@lihacker-laptop:/home/lihacker/develop/svn/ldd6410-read-only/training/kernel/d
rivers/second# mknod /dev/second c 248 0
代码清单10.13给出了second_test.c这个应用程序,它打开“/dev/second”,其后不断地读取自打开“/dev/second”设备文件以来经历的秒数。
代码清单10.13 second设备用户空间测试程序
1 #include ...
2
3 main()
4 {
5 int fd;
6 int counter = 0;
7 int old_counter = 0;
8
9 /打开/dev/second设备文件/
10 fd = open("/dev/second", O_RDONLY);
11 if (fd != - 1) {
13 while (1) {
15 read(fd,&counter, sizeof(unsigned int));/ 读目前经历的秒数 /
16 if(counter!=old_counter) {
18 printf("seconds after open /dev/second :%d\n",counter);
19 old_counter = counter;
20 }
21 }
22 } else {
25 printf("Device open failure\n");
26 }
27 }
运行second_test后,内核将不断地输出目前的jiffies值:
[44335.554313] current jiffies is 11008888
[44336.553166] current jiffies is 11009138
[44337.553175] current jiffies is 11009388
[44338.552383] current jiffies is 11009638
[44339.552321] current jiffies is 11009888
...
而应用程序将不断输出自打开/dec/second以来经历的秒数:
root@lihacker-laptop:/home/lihacker/develop/svn/ldd6410-read-only/training/kernel/d
rivers/second# ./second_test
seconds after open /dev/second :1
seconds after open /dev/second :2
seconds after open /dev/second :3
seconds after open /dev/second :4
seconds after open /dev/second :5
...