[태그:] timer

  • 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