kernel timer를 실습했다. callback 함수 argrument로 timer를 가지고 있는 구조체 데이터를 전달할 수 있다. work queue와 같은 방식이다. 커널 특정 버전부터 이런 식으로 변경되었는 듯 하다. 타이머가 만료되면 다시 등록하도록 했다.
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/gpio.h> //GPIO #include <linux/cdev.h> #include <linux/device.h> #include <linux/uaccess.h> #include <linux/fcntl.h> #include <linux/timer.h> #include <linux/jiffies.h> #define GPIO_10_OUT (10) #define DEVICE_DATA_MAX 256 unsigned int GPIO_irqNumber; /*\uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \ubd80\ubd84*/ static struct my_device_data{ struct cdev cdev; int index; char my_string[DEVICE_DATA_MAX]; struct timer_list simple_timer; } my_data1; void timer_action_fn(struct timer_list *t); void timer_action_fn(struct timer_list *t) { struct my_device_data *ptr1; pr_info("%ld: timer function was activated\n", jiffies); ptr1=from_timer(ptr1, t, simple_timer); ptr1->index++; pr_info("index is %d\n", ptr1->index); mod_timer(&my_data1.simple_timer, jiffies+msecs_to_jiffies(1000)); } static int init_timer(void){ timer_setup(&my_data1.simple_timer, timer_action_fn, 0); pr_info("%ld: timer was setup\n", jiffies); mod_timer(&my_data1.simple_timer, jiffies+msecs_to_jiffies(1000)); return 0; } static irqreturn_t gpio_irq_handler(int irq, void *dev_id) { /*Scheduling work queue*/ return IRQ_HANDLED; } //device driver \uc791\uc131 \ubd80\ubd84. /*************\ub4dc\ub77c\uc774\ubc84 \ud568\uc218 ******************/ static int mydriver_open(struct inode *inode, struct file *file); static int mydriver_release(struct inode *inode, struct file *file); static ssize_t mydriver_read(struct file *flip, char *buf, size_t len, loff_t *off); static ssize_t mydriver_write(struct file *flip, const char *buf, size_t len, loff_t *off); /********************************************/ //file operation structure static struct file_operations fops = { .owner = THIS_MODULE, .read = mydriver_read, .write = mydriver_write, .open = mydriver_open, .release = mydriver_release, }; static int mydriver_open(struct inode *inode, struct file *file) { pr_info("Deviced file was opend.\n"); return 0; } static int mydriver_release(struct inode *inode, struct file *file) { pr_info("Deviced file was closed.\n"); return 0; } static int mydriver_read(struct file *file, char *buf, size_t len, loff_t *off) { pr_info("read\n"); return 0; } static int mydriver_write(struct file *flip, const char *buf, size_t len, loff_t *off) { return 0; } dev_t dev = 0; static struct cdev my_cdev; static struct class *dev_class; static int __init init_hw(void) { //\ub514\ubc14\uc774\uc2a4 \ub4f1\ub85d if(( alloc_chrdev_region(&dev, 0, 1, "test_device") < 0)) { pr_err("[!]character device was not allocated\n"); goto r_unreg; } pr_info("[=]%d-%d, was allocated\n", MAJOR(dev), MINOR(dev)); //\ucd08\uae30\ud654 cdev_init(&my_cdev, &fops); pr_info("[=]driver was initialized\n"); //\uc2dc\uc2a4\ud15c\uc5d0 \ucd94\uac00 if((cdev_add(&my_cdev, dev, 1)) < 0) { pr_err("[!]cannot add device to kernel\n"); goto r_del; } //class \ub9cc\ub4e6. if((dev_class=class_create(THIS_MODULE, "my_class")) == NULL) { pr_err("[!]cannot add class\n"); goto r_class; } if((device_create(dev_class, NULL, dev, NULL, "my_device")) == NULL) { pr_err("[!]cannot create device\n"); goto r_device; } //gpio 10\ubc88\uc744 \uc0ac\uc6a9. //export\ud558\uc5ec \uac04\ub2e8\ud788 \uc0ac\uc6a9. //\uc785\ub825\uc740 \uac12\uc744 \uc368 \ub123\uc744 \uc218 \uc5c6\uc74c. \ucd9c\ub825\uc73c\ub85c \uc124\uc815. GPIO_irqNumber = gpio_to_irq(GPIO_10_OUT); pr_info("[=]irq %d was assinged\n",GPIO_irqNumber); //interrupt \ub4f1\ub85d \ud544\uc694 if (request_irq(GPIO_irqNumber, (void*)gpio_irq_handler, IRQF_TRIGGER_RISING, "my_device", NULL)) { pr_err("[!]my_device: cannot register IRQ\n"); goto r_gpio; } pr_info("[=]module was installed\n"); //timer setup init_timer(); return 0; r_gpio: gpio_free(GPIO_10_OUT); r_device: device_destroy(dev_class,dev); r_class: class_destroy(dev_class); r_del: cdev_del(&my_cdev); r_unreg: unregister_chrdev_region(dev,1); return -1; } static void __exit exit_hw(void) { free_irq(GPIO_irqNumber, NULL); gpio_free(GPIO_10_OUT); //flush_work(struct work_struct *work); device_destroy(dev_class,dev); //class_unregister(dev_class); class_destroy(dev_class); cdev_del(&my_cdev); unregister_chrdev_region(dev,1); //timer delete del_timer(&my_data1.simple_timer); printk(KERN_INFO "module was removed\n"); } module_init(init_hw); module_exit(exit_hw); MODULE_LICENSE("GPL"); MODULE_AUTHOR("noname"); MODULE_DESCRIPTION("Hello, world!");
[146685.757495] [=]236-0, was allocated [146685.757504] [=]driver was initialized [146685.757960] [=]irq 54 was assinged [146685.757984] [=]module was installed [146685.757991] 14638325: timer was setup [146686.818385] 14638432: timer function was activated [146686.818398] index is 1 [146687.858398] 14638536: timer function was activated [146687.858402] index is 2 [146688.898415] 14638640: timer function was activated [146688.898428] index is 3 [146689.938432] 14638744: timer function was activated [146689.938445] index is 4 [146690.978452] 14638848: timer function was activated [146690.978457] index is 5 [146692.018469] 14638952: timer function was activated [146692.018483] index is 6 [146693.058494] 14639056: timer function was activated [146693.058500] index is 7 [146694.098512] 14639160: timer function was activated [146694.098548] index is 8 [146695.138529] 14639264: timer function was activated [146695.138563] index is 9 [146696.178545] 14639368: timer function was activated [146696.178573] index is 10 [146697.218575] 14639472: timer function was activated [146697.218607] index is 11 [146698.258571] 14639576: timer function was activated [146698.258587] index is 12
https://embetronicx.com/tutorials/linux/device-drivers/using-kernel-timer-in-linux-device-driver/
https://stackoverflow.com/questions/14953871/how-to-pass-custom-argument-to-linux-timers-function