Kernel driver example - timer
来自个人维基
2020年4月26日 (日) 18:01free6d1823(讨论 | 贡献)的版本
- Environment:
Host PC: 4.15.0-96-generic #97~16.04.1-Ubuntu x86_64 GNU/Linux
- driver - mytimer.c
#include <linux/module.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/timer.h> //init_timer static int mytimer_major = 0; module_param(mytimer_major, int, S_IRUGO); struct mytimer_dev { struct cdev cdev; atomic_t counter; struct timer_list s_timer; }; static struct mytimer_dev *mytimer_devp; static void mytimer_timer_handler(struct timer_list * pTimer) { mod_timer(pTimer, jiffies + HZ); /* 触发下一次定时 */ atomic_inc(&mytimer_devp->counter); /* 增加秒计数 */ printk(KERN_INFO "current jiffies is %ld\n", jiffies); } static int mytimer_open(struct inode *inode, struct file *filp) { printk(KERN_INFO "mytimer_open:\n"); mytimer_devp->s_timer.function = &mytimer_timer_handler; mytimer_devp->s_timer.expires = jiffies + HZ; timer_setup(&mytimer_devp->s_timer, &mytimer_timer_handler, 0); add_timer(&mytimer_devp->s_timer); atomic_set(&mytimer_devp->counter, 0); /* 初始化秒计数为0 */ return 0; } static int mytimer_release(struct inode *inode, struct file *filp) { printk(KERN_INFO "mytimer_release:\n"); del_timer(&mytimer_devp->s_timer); return 0; } static ssize_t mytimer_read(struct file *filp, char __user * buf, size_t count, loff_t * ppos) { int counter; printk(KERN_INFO "mytimer_read:\n"); counter = atomic_read(&mytimer_devp->counter); if (put_user(counter, (int *)buf)) /* 拷贝counter到userspace */ return -EFAULT; else return sizeof(unsigned int); } static const struct file_operations mytimer_fops = { .owner = THIS_MODULE, .open = mytimer_open, .release = mytimer_release, .read = mytimer_read, }; static void mytimer_setup_cdev(struct mytimer_dev *dev, int index) { int err, devno = MKDEV(mytimer_major, index); cdev_init(&dev->cdev, &mytimer_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add(&dev->cdev, devno, 1); if (err) printk(KERN_ERR "Failed to add mytimer device\n"); } static int __init mytimer_init(void) { int ret; dev_t devno = MKDEV(mytimer_major, 0); printk(KERN_INFO "mytimer_init:\n"); if (mytimer_major) ret = register_chrdev_region(devno, 1, "mytimer"); else { ret = alloc_chrdev_region(&devno, 0, 1, "mytimer"); mytimer_major = MAJOR(devno); } if (ret < 0) { printk(KERN_ERR "mytimer: Failed alloc_chrdev_region!\n"); return ret; } mytimer_devp = kzalloc(sizeof(*mytimer_devp), GFP_KERNEL); if (!mytimer_devp) { ret = -ENOMEM; printk(KERN_ERR "kzalloc error\n"); goto fail_malloc; } mytimer_setup_cdev(mytimer_devp, 0); printk(KERN_INFO "mytimer_init OK\n"); return 0; fail_malloc: unregister_chrdev_region(devno, 1); return ret; } module_init(mytimer_init); static void __exit mytimer_exit(void) { printk(KERN_INFO "mytimer_exit:\n"); cdev_del(&mytimer_devp->cdev); kfree(mytimer_devp); unregister_chrdev_region(MKDEV(mytimer_major, 0), 1); } module_exit(mytimer_exit); MODULE_AUTHOR("free6d1823"); MODULE_LICENSE("GPL v2");
- test program test.c
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/time.h> int main(int argc, char **argv){ int fd; int c1 = 0; int c2 = -1; struct timeval tvStart; struct timeval tvEnd; gettimeofday(&tvStart, NULL); fd = open("/dev/mytimer", O_RDONLY); if (fd != - 1) { while (1) { if(c1!=c2) { gettimeofday(&tvEnd, NULL); long int dt = (tvEnd.tv_sec-tvStart.tv_sec)*1000000+(tvEnd.tv_usec - tvStart.tv_usec); printf("%3ld.%06ld: seconds after open /dev/mytimer :%d\n", dt/1000000, dt%1000000, c1); c2 = c1; } read(fd,&c1, sizeof(unsigned int)); } } else { printf("Device open failed\n"); } }
- Makefile
PWD := $(shell pwd) obj-m := mytimer.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean test: test.c gcc -o test test.c
- Build
make
- Execution
sudo insmod mytimer.ko cat /proc/devices |grep "mytimer" #242 mytimer mknod /dev/mytimer c 242 0 sudo ./test
- Test Result