适用场景 当应用程序必须等待某个事件发生,比如必须等待按键被按下时,可以使用“休眠-唤醒”机制: APP 调用 read 等函数试图读取数据,比如读取按键; APP 进入内核态,也就是调用驱动中的对应函数,发现有数据则复制到用户空间并马上返回; 如果 APP 在内核态,也就是在驱动程序中发现没有数据,则 APP 休眠; 当有数据时,比如当按下按键时,驱动程序的中断服务程序被调用,它会记录数据、唤醒 APP; APP 继续运行它的内核态代码,也就是驱动程序中的函数,复制数据到用户空间并马上返回。 休眠函数 //includelinuxwait.h。 //带interruptible的,是休眠期间是可被打断的,可以被信号打断。 //带timeout的,是有超时机制的 //休眠到wq队列,直到condition为真 wait_event_interruptible(wq, condition) wait_event(wq, condition) wait_event_interruptible_timeout(wq, condition, timeout) wait_event_timeout(wq, condition, timeout) 1 wq:waitqueue,等待队列 休眠时除了把程序状态改为非 RUNNING 之外,还要把进程/进程放入 wq 中,以后中断服务程序要从 wq中把它取出来唤醒。 没有 wq 的话,茫茫人海中,中断服务程序去哪里找到你? 2 condition 这可以是一个变量,也可以是任何表达式。表示“一直等待,直到 condition 为真”。 唤醒函数 //includelinuxwait.h。 //唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”或“TASK_UNINTERRUPTIBLE”的线程,只唤醒其中的一个线程 #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL) //唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”或“TASK_UNINTERRUPTIBLE”的线程,只唤醒其中 nr个线程 #define wake_up_nr(x, nr) __wake_up(x, TASK_NORMAL, nr, NULL) //唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”或“TASK_UNINTERRUPTIBLE”的线程,唤醒其中的所有线程 #define wake_up_all(x) __wake_up(x, TASK_NORMAL, 0, NULL) #define wake_up_locked(x) __wake_up_locked((x), TASK_NORMAL, 1) #define wake_up_all_locked(x) __wake_up_locked((x), TASK_NORMAL, 0) //唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程,只唤醒其中的一个线程 #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL) //唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程,只唤醒其中的 nr 个线程 #define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL) //唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程,唤醒其中的所有线程 #define wake_up_interruptible_all(x) __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL) #define wake_up_interruptible_sync(x) __wake_up_sync((x), TASK_INTERRUPTIBLE, 1) 编写思路 初始化wq队列 在驱动的 read 函数中,调用 wait_event_interruptible:它本身会判断 event 是否为 FALSE,如果为 FASLE 表示无数据,则休眠。当从 wait_event_interruptible 返回后,把数据复制回用户空间。 在中断服务程序里:设置 event 为 TRUE,并调用 wake_up_interruptible 唤醒线程。 源码 //button_irq.c #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct gpio_key { int gpio; struct gpio_desc *gpiod; int flag; int irq; }; static struct gpio_key *myBtn_key; static int button_major = 0; static struct class *button_class; static int g_key = 0; static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait); //1. 初始化gpio_key_wait队列 static ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { int err; //2. 放进gpio_key_wait队列等待,直到g_key != 0 wait_event_interruptible(gpio_key_wait, g_key); err = copy_to_user(buf, &g_key, 4); g_key = 0; if(err != 4) { return -1; } return 4; } static struct file_operations button_ops = { .owner = THIS_MODULE, .read = button_read, }; static irqreturn_t myBtn_irq_request(int irq, void *dev_id) { struct gpio_key *gpio_key = dev_id; int val; val = gpiod_get_value(gpio_key->gpiod); printk(KERN_WARNING"key %d %dn", gpio_key->gpio, val); g_key = (myBtn_key->gpio << 8)|val; //中断中调用唤醒函数 wake_up_interruptible(&gpio_key_wait); return IRQ_HANDLED; } static int my_button_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; int count; enum of_gpio_flags flag; int i, err; count = of_gpio_count(node); if(!count) { printk("%s,there isn't any gpio availiablen", __FUNCTION__); return -1; } myBtn_key = (struct gpio_key*)kzalloc(sizeof(struct gpio_key)*count, GFP_KERNEL); if(!myBtn_key) { printk("%s,kzalloc malloc failedn", __FUNCTION__); return -1; } for(i=0;i myBtn_key.gpio = of_get_gpio_flags(node, i, &flag); if(myBtn_key.gpio < 0) { printk("%s, of_get_gpio_flags failedn", __FUNCTION__); return -1; } myBtn_key.gpiod = gpio_to_desc(myBtn_key.gpio); myBtn_key.flag = flag & OF_GPIO_ACTIVE_LOW; myBtn_key.irq = gpio_to_irq(myBtn_key.gpio); err = request_irq(myBtn_key.irq, myBtn_irq_request, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "myBtn_key", &myBtn_key); } button_major = register_chrdev(0, "mybutton", &button_ops); if (button_major < 0) { printk(KERN_ERR "button : couldn't get a major number.n"); return -1; } button_class = class_create(THIS_MODULE, "button_class"); if(IS_ERR(button_class)) { printk(KERN_ERR "button class: create failedn"); unregister_chrdev(button_major, "mybutton"); return -1; } device_create(button_class, NULL, MKDEV(button_major, 0), NULL, "mybutton%d", 0); return 0; } static int my_button_remove(struct platform_device *pdev) { struct device_node *node= pdev->dev.of_node; int count; int i; device_destroy(button_class, MKDEV(button_major, 0)); class_destroy(button_class); unregister_chrdev(button_major, "mybutton"); count = of_gpio_count(node); for(i=0;i free_irq(myBtn_key.irq, &myBtn_key); } kfree(myBtn_key); return 0; } static struct of_device_id mybuttons[] = { { .compatible = "mybtn,btn_drv" }, { }, }; static struct platform_driver my_button_driver = { .probe = my_button_probe, .remove = my_button_remove, .driver = { .name = "button_dirver", .of_match_table = mybuttons, }, }; static int gpio_button_init(void) { int err; err = platform_driver_register(&my_button_driver); printk(KERN_WARNING"my button dirver initn"); return 0; } static void gpio_button_exit(void) { platform_driver_unregister(&my_button_driver); printk(KERN_WARNING"my button dirver exitn"); } module_init(gpio_button_init); module_exit(gpio_button_exit); MODULE_LICENSE("GPL");
原作者:习惯就好zz
|