timer 실습

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

코멘트

댓글 남기기

이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.