嵌入式内核及驱动开发之学习笔记(八) 阻塞模式+中断实现读取数据

举报
王建峰 发表于 2021/11/19 02:29:37 2021/11/19
【摘要】 目前已经学习两种应用层IO模型的使用 非阻塞:立即返回结果,如果想得到期望的结果,要不停的调用这个方法(轮询),非常耗费资源 阻塞:没有得到真正的数据前,不返回结果。此时,进程进入阻塞(休眠)态,直到有数据唤醒进程,这个过程不耗资源。 PS:linux应用中,大部分的函数接口都是阻塞    驱动程序将进程进...

目前已经学习两种应用层IO模型的使用

非阻塞:立即返回结果,如果想得到期望的结果,要不停的调用这个方法(轮询),非常耗费资源

阻塞:没有得到真正的数据前,不返回结果。此时,进程进入阻塞(休眠)态,直到有数据唤醒进程,这个过程不耗资源。

PS:linux应用中,大部分的函数接口都是阻塞

 

 驱动程序将进程进入休眠状态的过程

        1将当前进程加入到等待队列头中
        2将当前进程状态设置成TASK_INTERRUPTIBLE
        3让出调度--休眠
就是说,将进程加入一个队列中。如果改变它的状态成TASK_INTERRUPTIBLE,并让CPU不再调度它,那这就是进入阻塞(休眠)态了。在linux驱动开发中,通过一个接口完成上述过程

wait_event_interruptible(wq, condition)
 

 

写驱动程序的的步骤


        1等待队列头

init_waitqueue_head(wait_queue_head_t *q);
 


        2在需要等待(没有数据)的时候,进行休眠

wait_event_interruptible(wait_queue_head_t wq, condition) // 内部会构建一个等待队列项/节点wait_queue_t
 


        3在一个合适的时候(有数据),会将进程唤醒

wake_up_interruptible(wait_queue_head_t *q)
 

 

驱动程序

在驱动模块加载时,也就是初始化函数key_drv_init中,加入 等待列队头。当应用程序调用read接口函数时,使用wait_event_interruptible函数,condition参数为1说明有数据就继续执行,没有则阻塞等待。wake_up_interruptible用来唤醒休眠中的进程。


  
  1. //key_drv.c
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/of.h>
  5. #include <linux/of_irq.h>
  6. #include <linux/interrupt.h>
  7. #include <linux/slab.h>
  8. #include <linux/fs.h>
  9. #include <linux/device.h>
  10. #include <linux/wait.h>
  11. #include <linux/sched.h>
  12. #include <asm/io.h>
  13. #include <asm/uaccess.h>
  14. #include <asm/fcntl.h>
  15. irqreturn_t key_irq_handler(int irqno, void *devid);
  16. ssize_t key_drv_read (struct file *, char __user *, size_t, loff_t *);
  17. ssize_t key_drv_write (struct file *, const char __user *, size_t, loff_t *);
  18. int key_drv_open (struct inode *, struct file *);
  19. int key_drv_close (struct inode *, struct file *);
  20. #define GPXCON_REG 0x11000C20
  21. #define KEY_ENTER 28
  22. const struct file_operations key_fops = {
  23. .open = key_drv_open,
  24. .read = key_drv_read,
  25. .write = key_drv_write,
  26. .release = key_drv_close,
  27. };
  28. struct key_event{
  29. int code; // 按键的类型
  30. int value; // 状态
  31. };
  32. struct key_desc{
  33. unsigned int dev_major;
  34. struct class *cls;
  35. struct device *dev;
  36. int irqno;
  37. void *reg_base;
  38. int key_state; //表示是否有数据
  39. struct key_event event;
  40. wait_queue_head_t wq_head;
  41. };
  42. struct key_desc *key_dev;
  43. //static int irqno;
  44. int get_irqno_from_node(void)
  45. {
  46. //从设备树路径,查找节点
  47. struct device_node *np = of_find_node_by_path("/key_int_node");
  48. if(np){
  49. printk("find node ok\n");
  50. }else{
  51. printk("find node failed\n");
  52. }
  53. int irqno = irq_of_parse_and_map(np, 0);
  54. printk("irqno = %d\n", irqno);
  55. return irqno;
  56. }
  57. static int __init key_drv_init(void)
  58. {
  59. int ret;
  60. //对象实例化
  61. key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);
  62. //申请主设备号
  63. key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);
  64. //创建设备结点
  65. key_dev->cls = class_create(THIS_MODULE, "key_cls");
  66. key_dev->dev = device_create(key_dev->cls, NULL,
  67. MKDEV(key_dev->dev_major,0), NULL, "key0");
  68. //硬件初始化
  69. key_dev->irqno = get_irqno_from_node();
  70. ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
  71. "key3_eint10", NULL);
  72. if(ret != 0)
  73. {
  74. printk("request_irq error\n");
  75. return ret;
  76. }
  77. key_dev->reg_base = ioremap(GPXCON_REG, 8);
  78. // 初始化等待队列头
  79. init_waitqueue_head(&key_dev->wq_head);
  80. return 0;
  81. }
  82. static void __exit key_drv_exit(void)
  83. {
  84. iounmap(key_dev->reg_base); //去映射
  85. free_irq(key_dev->irqno, NULL); //释放中断资源
  86. device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0)); //
  87. class_destroy(key_dev->cls); //
  88. unregister_chrdev(key_dev->dev_major, "key_drv"); //注销主设备号
  89. kfree(key_dev); //释放结构体内存
  90. }
  91. irqreturn_t key_irq_handler(int irqno, void *devid)
  92. {
  93. printk("-------%s-------------\n", __FUNCTION__);
  94. int value = readl(key_dev->reg_base + 4) & (1<<2);
  95. if(value){// 1
  96. printk("key3 up\n");
  97. key_dev->event.code = KEY_ENTER;
  98. key_dev->event.value = 0;
  99. }else{// 0
  100. printk("key3 pressed\n");
  101. key_dev->event.code = KEY_ENTER;
  102. key_dev->event.value = 1;
  103. }
  104. // 表示有数据,需要去唤醒整个进程/等待队列
  105. wake_up_interruptible(&key_dev->wq_head);
  106. //同时设置标志位
  107. key_dev->key_state = 1;
  108. return IRQ_HANDLED;
  109. }
  110. ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  111. {
  112. int ret;
  113. //2,在需要等待(没有数据)的时候,进行休眠
  114. wait_event_interruptible(key_dev->wq_head, key_dev->key_state);
  115. // 表示有数据
  116. ret = copy_to_user(buf, &key_dev->event, count);
  117. if(ret > 0)
  118. {
  119. printk("copy_to_user error\n");
  120. return -EFAULT;
  121. }
  122. // 清除key_dev->event的数据记录
  123. memset(&key_dev->event, 0, sizeof(key_dev->event));
  124. key_dev->key_state = 0;
  125. return count;
  126. }
  127. ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  128. {
  129. printk("-------%s-------------\n", __FUNCTION__);
  130. return 0;
  131. }
  132. int key_drv_open(struct inode *inode, struct file *filp)
  133. {
  134. printk("-------%s-------------\n", __FUNCTION__);
  135. return 0;
  136. }
  137. int key_drv_close (struct inode *inode, struct file *filp)
  138. {
  139. printk("-------%s-------------\n", __FUNCTION__);
  140. return 0;
  141. }
  142. module_init(key_drv_init);
  143. module_exit(key_drv_exit);
  144. MODULE_LICENSE("GPL");

 

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/84146463

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。