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
댓글 남기기