Linux驱动学习day11(定时器)

发布于:2025-07-01 ⋅ 阅读:(17) ⋅ 点赞:(0)

定时器

定时器主要作用就是:设置超时时间,执行超时函数。

按键按下存在抖动,为了消除抖动可以设置定时器,如上图所示,按下一次按键会产生多次抖动,即会产生多次中断,在每次中断产生的时候,设置定时器,定时器时间是当前时间+超时时间,这样每次中断产生都会重新设置定时器时间,等到按键不抖动稳定的时候,就不会再改变定时器时间,这时候我们再记录按键值,就很稳定了。

内核中使用定时器的主要函数 

timer_setup(老版本setup_timer)

设置定时器,主要是初始化timer_list结构体,设置其中参数和函数

#define timer_setup(timer, callback, flags)			\
	__init_timer((timer), (callback), (flags))

add_timer

向内核添加定时器,timer->expires表示超时时间,时间到了内核会自动调用timer->function。

void add_timer(struct timer_list *timer)
{
	BUG_ON(timer_pending(timer));
	mod_timer(timer, timer->expires);
}

mod_timer

修改定时器超时时间

int mod_timer(struct timer_list *timer, unsigned long expires)
{
	return __mod_timer(timer, expires, 0);
}

del_timer

删除定时器

int del_timer(struct timer_list *timer)
{
	struct timer_base *base;
	unsigned long flags;
	int ret = 0;

	debug_assert_init(timer);

	if (timer_pending(timer)) {
		base = lock_timer_base(timer, &flags);
		ret = detach_if_pending(timer, base, true);
		raw_spin_unlock_irqrestore(&base->lock, flags);
	}

	return ret;
}

查看系统定时器时间:进入到内核目录,vi .config 搜索/CONFIG_HZ

3568系统tick如上图所示 3.33ms发生一次中断。每发生一次中断,全局变量jiffies会加1,所以定时器时间都是基于jiffies的。

修改时间有下面两种方法

/* add_timer之前 */
timer.expires = jiffies + xxx; /* xxx * 3.33ms */
timer.expires = jiffies + 2*HZ;/* jiffies 再加上 2 秒的时间 */

/* add_timer之后 */
mod_timer(&timer , jiffies + xxx);
mod_timer(&timer , jiffies + 2*HZ);

 含定时器的驱动代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/gfp.h>
#include <linux/of_irq.h>
#include <linux/poll.h>
#include <linux/timer.h>

#define BUF_LEN 128
#define NEXT_POS(x) ((x + 1) % BUF_LEN)

static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
static struct fasync_struct *button_fasync;

static struct class *mybutton_class;
static struct gpio_inf *gpio_if;
static int major;
static int g_key[BUF_LEN];
static int r, w;

struct gpio_inf {
    int gpio;
    int irq;
    struct gpio_desc *gpiod;
    enum of_gpio_flags flag;
    struct timer_list my_button_timer;
};

static int is_key_buf_empty(void) {
    return (r == w);
}

static int is_key_buf_full(void) {
    return (r == NEXT_POS(w));
}

static void put_key(int key) {
    if (!is_key_buf_full()) {
        g_key[w] = key;
        w = NEXT_POS(w);
    }
}

static int get_key(void) {
    int key = 0;
    if (!is_key_buf_empty()) {
        key = g_key[r];
        r = NEXT_POS(r);
    }
    return key;
}

static void mybutton_keys_timer(struct timer_list *t)
{
    int val, key;
    struct gpio_inf *gf = from_timer(gf, t, my_button_timer);
    if(!gf)
        return;
    val = gpio_get_value(gf->gpio);

    printk("mybutton_keys_timer key %d value%d\n", gf->gpio, val);

    key = (gf->gpio << 8) | val;

    put_key(key);

    wake_up_interruptible(&gpio_key_wait);

    kill_fasync(&button_fasync, SIGIO, POLL_IN);

    return;
}

static irqreturn_t my_key_handler(int irq, void *dev_id) 
{
    struct gpio_inf * inf = (struct gpio_inf *)dev_id;
    printk("my_key_handler %s %s %d key:%d\n", __FILE__, __FUNCTION__, __LINE__ , inf->gpio);

    mod_timer(&inf->my_button_timer, jiffies + HZ/50);

    return IRQ_HANDLED;
}

static ssize_t gpio_button_read(struct file *file, char __user *buf, size_t size, loff_t *off) {
    int err, key;
    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    
    if(is_key_buf_empty() && (file->f_flags & O_NONBLOCK))
        return -EAGAIN;

    wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());

    key = get_key();

    err = copy_to_user(buf, &key, 4);
    
    return 4;
}

static unsigned int gpio_button_poll(struct file *fp, poll_table *wait) {
    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    poll_wait(fp, &gpio_key_wait, wait);
    return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

static int gpio_button_fasync(int fd, struct file *file, int on)
{
    //printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);
    if(fasync_helper(fd, file, on , &button_fasync) >= 0)
        return 0;
    else
        return -EIO;
}

static struct file_operations button_opr = {
    .owner  = THIS_MODULE,
    .read   = gpio_button_read,
    .poll   = gpio_button_poll,
    .fasync = gpio_button_fasync,
};

static const struct of_device_id my_key[] = {
    { .compatible = "my,mybutton" },
    {},
};

MODULE_DEVICE_TABLE(of, my_key);

static int chip_demo_gpio_probe(struct platform_device *pdev) {
    int count, i, err;
    struct device_node *node;
    enum of_gpio_flags flag;

    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    node = pdev->dev.of_node;
    count = of_gpio_count(node);
    if (count <= 0) {
        dev_err(&pdev->dev, "Invalid GPIO count: %d\n", count);
        return -EINVAL;
    }

    gpio_if = kzalloc(count * sizeof(struct gpio_inf), GFP_KERNEL);
    if (!gpio_if) {
        dev_err(&pdev->dev, "Failed to allocate memory\n");
        return -ENOMEM;
    }

    for (i = 0; i < count; i++) {
        gpio_if[i].gpio  = of_get_gpio_flags(node, i, &flag);
        gpio_if[i].irq   = gpio_to_irq(gpio_if[i].gpio);
        gpio_if[i].gpiod = gpio_to_desc(gpio_if[i].gpio);
        gpio_if[i].flag  = flag & OF_GPIO_ACTIVE_LOW;

        err = request_irq(gpio_if[i].irq, my_key_handler,
                          IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                          "my_key", &gpio_if[i]);
        if (err) {
            printk("request_irq %d failed\n", gpio_if[i].irq);
        }
        timer_setup(&gpio_if[i].my_button_timer, mybutton_keys_timer , 0);
        gpio_if[i].my_button_timer.expires = ~0; /* 最大超时时间 */
        add_timer(&gpio_if[i].my_button_timer);
    }

    return 0;
}

static int chip_demo_gpio_remove(struct platform_device *pdev) {
    int count, i;
    struct device_node *node = pdev->dev.of_node;
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    count = of_gpio_count(node);

    for (i = 0; i < count; i++) {
        del_timer(&gpio_if[i].my_button_timer);
        free_irq(gpio_if[i].irq, &gpio_if[i]);
    }

    kfree(gpio_if);
    return 0;
}

static struct platform_driver my_key_drv = {
    .probe = chip_demo_gpio_probe,
    .remove = chip_demo_gpio_remove,
    .driver = {
        .name = "my_key_drv",
        .of_match_table = my_key,
    }
};

static int __init gpio_key_drv_init(void) {
    int err;

    // 注册字符设备
    major = register_chrdev(0, "my_button", &button_opr);
    if (major < 0) {
        printk("register_chrdev failed: %d\n", major);
        return major;
    }

    mybutton_class = class_create(THIS_MODULE, "mybutton_class");
    if (IS_ERR(mybutton_class)) {
        unregister_chrdev(major, "my_button");
        printk("class_create failed\n");
        return PTR_ERR(mybutton_class);
    }

    device_create(mybutton_class, NULL, MKDEV(major, 0), NULL, "my_button");
    printk("char device /dev/my_button created, major=%d\n", major);

    // 注册 platform 驱动
    err = platform_driver_register(&my_key_drv);
    if (err) {
        device_destroy(mybutton_class, MKDEV(major, 0));
        class_destroy(mybutton_class);
        unregister_chrdev(major, "my_button");
        return err;
    }

    return 0;
}

static void __exit gpio_key_drv_exit(void) {
    platform_driver_unregister(&my_key_drv);

    device_destroy(mybutton_class, MKDEV(major, 0));

    class_destroy(mybutton_class);

    unregister_chrdev(major, "my_button");
    
    printk("char device /dev/my_button removed\n");
}

module_init(gpio_key_drv_init);
module_exit(gpio_key_drv_exit);
MODULE_LICENSE("GPL");

 tasklet

当硬件中断发生时,系统首先调用对应的硬件中断处理函数(ISR),该函数完成紧急任务后迅速返回。随后,系统会处理软中断(softirq),内核维护了一个软中断处理函数数组 softirq_vec[],其中包含了用于执行延后任务的函数。作为软中断的一种实现,tasklet被安排在软中断中执行;当中断处理函数通过 tasklet_schedule() 调度tasklet时,该tasklet被加入执行链表。软中断触发时,系统调用 tasklet_action() 遍历tasklet链表,依次执行每个tasklet的处理函数,从而完成硬件中断的后续工作。

根据上述流程,可以得出:

1、为每个按键添加tasklet。tasklet_init()

2、写软中断执行函数

2、在request_irq的中断处理函数中,调度tasklet。tasklet_schedule()。将tasklet加入软中断执行链表。

驱动程序代码

这里我在结构体里面添加了last_val,为了判断按键按下是否发生变化,变化则记录其值,没变化就不记录,这是因为正点原子RK3568中使用GPIO引脚电平来模拟按键按下和松开,我的板子在这块贼不稳定,一会就跳出一大堆信息,如下图所示:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/gfp.h>
#include <linux/of_irq.h>
#include <linux/poll.h>
#include <linux/timer.h>

#define BUF_LEN 128
#define NEXT_POS(x) ((x + 1) % BUF_LEN)

static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
static struct fasync_struct *button_fasync;

static struct class *mybutton_class;
static struct gpio_inf *gpio_if;
static int major;
static int g_key[BUF_LEN];
static int r, w;

struct gpio_inf {
    int gpio;
    int irq;
    int last_val;
    struct gpio_desc *gpiod;
    enum of_gpio_flags flag;
    struct timer_list my_button_timer;
    struct tasklet_struct task;
};

static int is_key_buf_empty(void) {
    return (r == w);
}

static int is_key_buf_full(void) {
    return (r == NEXT_POS(w));
}

static void put_key(int key) {
    if (!is_key_buf_full()) {
        g_key[w] = key;
        w = NEXT_POS(w);
    }
}

static int get_key(void) {
    int key = 0;
    if (!is_key_buf_empty()) {
        key = g_key[r];
        r = NEXT_POS(r);
    }
    return key;
}

static void mybutton_keys_timer(struct timer_list *t)
{
    int val, key;
    struct gpio_inf *gf = from_timer(gf, t, my_button_timer);
    if(!gf)
        return;
    val = gpio_get_value(gf->gpio);

    if (val != gf->last_val) {
        gf->last_val = val;  // 更新记录值
        return;  // 状态不稳定,忽略这次
    }

    printk("mybutton_keys_timer key %d value%d\n", gf->gpio, val);


    key = (gf->gpio << 8) | val;

    put_key(key);

    wake_up_interruptible(&gpio_key_wait);

    kill_fasync(&button_fasync, SIGIO, POLL_IN);

    return;
}

static void my_button_tasklet(unsigned long data)
{
    int val, key;
    struct gpio_inf *gf = (struct gpio_inf *)data;
    if(!gf)
        return;
    val = gpio_get_value(gf->gpio);

    if (val != gf->last_val) {
        gf->last_val = val;  
        return;  
    }

    printk("my_button_tasklet key %d value%d\n", gf->gpio, val);


    key = (gf->gpio << 8) | val;

    put_key(key);

    wake_up_interruptible(&gpio_key_wait);

    kill_fasync(&button_fasync, SIGIO, POLL_IN);
    return;
}

static irqreturn_t my_key_handler(int irq, void *dev_id) 
{
    struct gpio_inf * inf = (struct gpio_inf *)dev_id;
    //printk("my_key_handler %s %s %d key:%d\n", __FILE__, __FUNCTION__, __LINE__ , inf->gpio);
    tasklet_schedule(&inf->task);
    mod_timer(&inf->my_button_timer, jiffies + HZ/50);

    return IRQ_HANDLED;
}

static ssize_t gpio_button_read(struct file *file, char __user *buf, size_t size, loff_t *off) {
    int err, key;
    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    
    if(is_key_buf_empty() && (file->f_flags & O_NONBLOCK))
        return -EAGAIN;

    wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());

    key = get_key();

    err = copy_to_user(buf, &key, 4);
    
    return 4;
}

static unsigned int gpio_button_poll(struct file *fp, poll_table *wait) {
    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    poll_wait(fp, &gpio_key_wait, wait);
    return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

static int gpio_button_fasync(int fd, struct file *file, int on)
{
    //printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);
    if(fasync_helper(fd, file, on , &button_fasync) >= 0)
        return 0;
    else
        return -EIO;
}

static struct file_operations button_opr = {
    .owner  = THIS_MODULE,
    .read   = gpio_button_read,
    .poll   = gpio_button_poll,
    .fasync = gpio_button_fasync,
};

static const struct of_device_id my_key[] = {
    { .compatible = "my,mybutton" },
    {},
};

MODULE_DEVICE_TABLE(of, my_key);

static int chip_demo_gpio_probe(struct platform_device *pdev) {
    int count, i, err;
    struct device_node *node;
    enum of_gpio_flags flag;

    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    node = pdev->dev.of_node;
    count = of_gpio_count(node);
    if (count <= 0) {
        dev_err(&pdev->dev, "Invalid GPIO count: %d\n", count);
        return -EINVAL;
    }

    gpio_if = kzalloc(count * sizeof(struct gpio_inf), GFP_KERNEL);
    if (!gpio_if) {
        dev_err(&pdev->dev, "Failed to allocate memory\n");
        return -ENOMEM;
    }

    for (i = 0; i < count; i++) {
        gpio_if[i].gpio  = of_get_gpio_flags(node, i, &flag);
        gpio_if[i].irq   = gpio_to_irq(gpio_if[i].gpio);
        gpio_if[i].gpiod = gpio_to_desc(gpio_if[i].gpio);
        gpio_if[i].flag  = flag & OF_GPIO_ACTIVE_LOW;
        gpio_if[i].last_val = gpio_get_value(gpio_if[i].gpio);

        err = request_irq(gpio_if[i].irq, my_key_handler,
                          IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                          "my_key", &gpio_if[i]);
        if (err) {
            printk("request_irq %d failed\n", gpio_if[i].irq);
        }
        timer_setup(&gpio_if[i].my_button_timer, mybutton_keys_timer , 0);
        gpio_if[i].my_button_timer.expires = ~0;
        add_timer(&gpio_if[i].my_button_timer);

        tasklet_init(&gpio_if[i].task, my_button_tasklet, (unsigned long)&gpio_if[i]);
    }

    return 0;
}

static int chip_demo_gpio_remove(struct platform_device *pdev) {
    int count, i;
    struct device_node *node = pdev->dev.of_node;
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    count = of_gpio_count(node);

    for (i = 0; i < count; i++) {
        del_timer(&gpio_if[i].my_button_timer);
        free_irq(gpio_if[i].irq, &gpio_if[i]);
        tasklet_kill(&gpio_if[i].task);
    }

    kfree(gpio_if);
    return 0;
}

static struct platform_driver my_key_drv = {
    .probe = chip_demo_gpio_probe,
    .remove = chip_demo_gpio_remove,
    .driver = {
        .name = "my_key_drv",
        .of_match_table = my_key,
    }
};

static int __init gpio_key_drv_init(void) {
    int err;

    // 注册字符设备
    major = register_chrdev(0, "my_button", &button_opr);
    if (major < 0) {
        printk("register_chrdev failed: %d\n", major);
        return major;
    }

    mybutton_class = class_create(THIS_MODULE, "mybutton_class");
    if (IS_ERR(mybutton_class)) {
        unregister_chrdev(major, "my_button");
        printk("class_create failed\n");
        return PTR_ERR(mybutton_class);
    }

    device_create(mybutton_class, NULL, MKDEV(major, 0), NULL, "my_button");
    printk("char device /dev/my_button created, major=%d\n", major);

    // 注册 platform 驱动
    err = platform_driver_register(&my_key_drv);
    if (err) {
        device_destroy(mybutton_class, MKDEV(major, 0));
        class_destroy(mybutton_class);
        unregister_chrdev(major, "my_button");
        return err;
    }

    return 0;
}

static void __exit gpio_key_drv_exit(void) {
    platform_driver_unregister(&my_key_drv);

    device_destroy(mybutton_class, MKDEV(major, 0));

    class_destroy(mybutton_class);

    unregister_chrdev(major, "my_button");
    
    printk("char device /dev/my_button removed\n");
}

module_init(gpio_key_drv_init);
module_exit(gpio_key_drv_exit);
MODULE_LICENSE("GPL");

工作队列

中断下半部(timer,tasklet)都是在中断上下文中执行的,无法休眠,如果处理复杂事情的时候,无法休眠,会将CPU资源占满,无法执行用户程序,这样就会使得系统卡顿。为了解决该问题,可以使用线程处理复杂事情。线程可以休眠。(在内核中,使用工作队列,内核会自动创建内核线程)

缺点:当工作队列中前一个work比较耗时,这样就会影响到之后的work工作。

驱动要做的部分:

1、构造work,.func

2、将work放入队列,wake_up唤醒--->schedule_work();

如果处理的事情非常复杂,就不直接使用系统的内核线程,自己创建一个内核线程单独处理。

container_of() 可以获得结构体的地址,主要采用反推。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/gfp.h>
#include <linux/of_irq.h>
#include <linux/poll.h>
#include <linux/timer.h>
#include <linux/workqueue.h>


#define BUF_LEN 128
#define NEXT_POS(x) ((x + 1) % BUF_LEN)

static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
static struct fasync_struct *button_fasync;

static struct class *mybutton_class;
static struct gpio_inf *gpio_if;
static int major;
static int g_key[BUF_LEN];
static int r, w;

struct gpio_inf {
    int gpio;
    int irq;
    int last_val;
    struct gpio_desc *gpiod;
    enum of_gpio_flags flag;
    struct timer_list my_button_timer;
    struct tasklet_struct task;
    struct work_struct work;
};

static int is_key_buf_empty(void) {
    return (r == w);
}

static int is_key_buf_full(void) {
    return (r == NEXT_POS(w));
}

static void put_key(int key) {
    if (!is_key_buf_full()) {
        g_key[w] = key;
        w = NEXT_POS(w);
    }
}

static int get_key(void) {
    int key = 0;
    if (!is_key_buf_empty()) {
        key = g_key[r];
        r = NEXT_POS(r);
    }
    return key;
}

static void mybutton_keys_timer(struct timer_list *t)
{
    int val, key;
    struct gpio_inf *gf = from_timer(gf, t, my_button_timer);
    if(!gf)
        return;
    val = gpio_get_value(gf->gpio);

    if (val != gf->last_val) {
        gf->last_val = val;  // 更新记录值
        return;  // 状态不稳定,忽略这次
    }

    printk("mybutton_keys_timer key %d value%d\n", gf->gpio, val);


    key = (gf->gpio << 8) | val;

    put_key(key);

    wake_up_interruptible(&gpio_key_wait);

    kill_fasync(&button_fasync, SIGIO, POLL_IN);

    return;
}

static void my_button_tasklet(unsigned long data)
{
    int val, key;
    struct gpio_inf *gf = (struct gpio_inf *)data;
    if(!gf)
        return;
    val = gpio_get_value(gf->gpio);

    if (val != gf->last_val) {
        gf->last_val = val;  
        return;  
    }

    printk("my_button_tasklet key %d value%d\n", gf->gpio, val);


    key = (gf->gpio << 8) | val;

    put_key(key);

    wake_up_interruptible(&gpio_key_wait);

    kill_fasync(&button_fasync, SIGIO, POLL_IN);
    return;
}

static void my_button_work_func(struct work_struct *work)
{
    int val, key;
    struct gpio_inf *gf = container_of(work, struct gpio_inf, work);
    if(!gf)
        return;
    val = gpio_get_value(gf->gpio);

    if (val != gf->last_val) {
        gf->last_val = val;  
        return;  
    }

    printk("my_button_work_func key %d value%d\n", gf->gpio, val);

    key = (gf->gpio << 8) | val;

    put_key(key);

    return;

}

static irqreturn_t my_key_handler(int irq, void *dev_id) 
{
    struct gpio_inf * inf = (struct gpio_inf *)dev_id;
    //printk("my_key_handler %s %s %d key:%d\n", __FILE__, __FUNCTION__, __LINE__ , inf->gpio);
    tasklet_schedule(&inf->task);
    mod_timer(&inf->my_button_timer, jiffies + HZ/50);
    schedule_work(&inf->work);

    return IRQ_HANDLED;
}

static ssize_t gpio_button_read(struct file *file, char __user *buf, size_t size, loff_t *off) {
    int err, key;
    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    
    if(is_key_buf_empty() && (file->f_flags & O_NONBLOCK))
        return -EAGAIN;

    wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());

    key = get_key();

    err = copy_to_user(buf, &key, 4);
    
    return 4;
}

static unsigned int gpio_button_poll(struct file *fp, poll_table *wait) {
    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    poll_wait(fp, &gpio_key_wait, wait);
    return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

static int gpio_button_fasync(int fd, struct file *file, int on)
{
    //printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);
    if(fasync_helper(fd, file, on , &button_fasync) >= 0)
        return 0;
    else
        return -EIO;
}

static struct file_operations button_opr = {
    .owner  = THIS_MODULE,
    .read   = gpio_button_read,
    .poll   = gpio_button_poll,
    .fasync = gpio_button_fasync,
};

static const struct of_device_id my_key[] = {
    { .compatible = "my,mybutton" },
    {},
};

MODULE_DEVICE_TABLE(of, my_key);

static int chip_demo_gpio_probe(struct platform_device *pdev) {
    int count, i, err;
    struct device_node *node;
    enum of_gpio_flags flag;

    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    node = pdev->dev.of_node;
    count = of_gpio_count(node);
    if (count <= 0) {
        dev_err(&pdev->dev, "Invalid GPIO count: %d\n", count);
        return -EINVAL;
    }

    gpio_if = kzalloc(count * sizeof(struct gpio_inf), GFP_KERNEL);
    if (!gpio_if) {
        dev_err(&pdev->dev, "Failed to allocate memory\n");
        return -ENOMEM;
    }

    for (i = 0; i < count; i++) {
        gpio_if[i].gpio  = of_get_gpio_flags(node, i, &flag);
        gpio_if[i].irq   = gpio_to_irq(gpio_if[i].gpio);
        gpio_if[i].gpiod = gpio_to_desc(gpio_if[i].gpio);
        gpio_if[i].flag  = flag & OF_GPIO_ACTIVE_LOW;
        gpio_if[i].last_val = gpio_get_value(gpio_if[i].gpio);

        err = request_irq(gpio_if[i].irq, my_key_handler,
                          IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                          "my_key", &gpio_if[i]);
        if (err) {
            printk("request_irq %d failed\n", gpio_if[i].irq);
        }
        timer_setup(&gpio_if[i].my_button_timer, mybutton_keys_timer , 0);
        gpio_if[i].my_button_timer.expires = ~0;
        add_timer(&gpio_if[i].my_button_timer);

        tasklet_init(&gpio_if[i].task, my_button_tasklet, (unsigned long)&gpio_if[i]);

        INIT_WORK(&gpio_if[i].work, my_button_work_func);
    }

    return 0;
}

static int chip_demo_gpio_remove(struct platform_device *pdev) {
    int count, i;
    struct device_node *node = pdev->dev.of_node;
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    count = of_gpio_count(node);

    for (i = 0; i < count; i++) {
        del_timer(&gpio_if[i].my_button_timer);
        free_irq(gpio_if[i].irq, &gpio_if[i]);
        tasklet_kill(&gpio_if[i].task);
    }

    kfree(gpio_if);
    return 0;
}

static struct platform_driver my_key_drv = {
    .probe = chip_demo_gpio_probe,
    .remove = chip_demo_gpio_remove,
    .driver = {
        .name = "my_key_drv",
        .of_match_table = my_key,
    }
};

static int __init gpio_key_drv_init(void) {
    int err;

    // 注册字符设备
    major = register_chrdev(0, "my_button", &button_opr);
    if (major < 0) {
        printk("register_chrdev failed: %d\n", major);
        return major;
    }

    mybutton_class = class_create(THIS_MODULE, "mybutton_class");
    if (IS_ERR(mybutton_class)) {
        unregister_chrdev(major, "my_button");
        printk("class_create failed\n");
        return PTR_ERR(mybutton_class);
    }

    device_create(mybutton_class, NULL, MKDEV(major, 0), NULL, "my_button");
    printk("char device /dev/my_button created, major=%d\n", major);

    // 注册 platform 驱动
    err = platform_driver_register(&my_key_drv);
    if (err) {
        device_destroy(mybutton_class, MKDEV(major, 0));
        class_destroy(mybutton_class);
        unregister_chrdev(major, "my_button");
        return err;
    }

    return 0;
}

static void __exit gpio_key_drv_exit(void) {
    platform_driver_unregister(&my_key_drv);

    device_destroy(mybutton_class, MKDEV(major, 0));

    class_destroy(mybutton_class);

    unregister_chrdev(major, "my_button");
    
    printk("char device /dev/my_button removed\n");
}

module_init(gpio_key_drv_init);
module_exit(gpio_key_drv_exit);
MODULE_LICENSE("GPL");

内核线程

 中断的线程化处理

主要程序代码

/* 注册irq时,使用request_threaded_irq */
err = request_threaded_irq(gpio_if[i].irq , my_key_handler , my_key_threaded_func , 
        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT , 
        "my_key" , &gpio_if[i]);


static irqreturn_t my_key_threaded_func(int irq, void *data)
{
    int val, key;
    struct gpio_inf *gf = (struct gpio_inf *)data ;
    
    val = gpio_get_value(gf->gpio);

    printk("my_key_threaded_func key %d value%d\n", gf->gpio, val);
    printk("my_key_threaded_func: the process is %s pid %d\n" , current->comm , current->pid);
    key = (gf->gpio << 8) | val;

    put_key(key);

	return IRQ_HANDLED;
}

结果如图所示


网站公告

今日签到

点亮在社区的每一天
去签到