1 中断编程

语雀内容

2 驱动中断处理模板

中断的基本知识在第一节已经有文档记录,这里我们给出中断处理在驱动中的使用模板

2.1 tasklet与底半部中断

  1. /* 定义tasklet和中断底半部函数并将它们关联 */
  2. void xxx_do_tasklet(unsigned long);
  3. DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);
  4. /* 中断处理底半部函数 */
  5. void xxx_do_tasklet(unsigned long)
  6. {
  7. // ...
  8. }
  9. /* 中断处理顶半部 */
  10. irqreturn_t xxx_interrupt(int irq, void *dev_id)
  11. {
  12. // ...
  13. tasklet_schedule(&xxx_tasklet);//调度底半部处理
  14. // ...
  15. }
  16. /* 设备驱动模块加载函数 */
  17. int __init xxx_init(void)
  18. {
  19. // ...
  20. /* 申请中断 */
  21. result = request_irq(xxx_irq, xxx_interrupt, 0, "xxx", NULL);
  22. // ...
  23. return IRQ_HANDLED;
  24. }
  25. /* 设备驱动模块卸载函数 */
  26. void __exit xxx_exit(void)
  27. {
  28. // ...
  29. /* 释放中断 */
  30. free_irq(xxx_irq, xxx_interrupt);
  31. // ...
  32. }

2.2 工作队列与底半部中断

  1. /* 定义工作队列和关联函数 */
  2. struct work_struct xxx_wq;
  3. void xxx_do_work(struct work_struct *work);
  4. /* 中断处理底半部 */
  5. void xxx_do_work(struct work_struct *work)
  6. {
  7. //...
  8. }
  9. /* 中断处理顶半部 */
  10. irqreturn_t xxx_interrupt(int irq, void *dev_id)
  11. {
  12. //...
  13. schedule_work(&xxx_wq); //调度底半部处理
  14. //...
  15. return IRQ_HANDLED;
  16. }
  17. /* 设备驱动模块加载函数 */
  18. int xxx_init(void)
  19. {
  20. //...
  21. /* 申请中断 */
  22. result = request_irq(xxx_irq, xxx_interrupt, 0, "xxx", NULL);
  23. //...
  24. /* 初始化工作队列 */
  25. INIT_WORK(&xxx_wq, xxx_do_work);
  26. //...
  27. }
  28. /* 设备驱动模块卸载函数 */
  29. void xxx_exit(void)
  30. {
  31. //...
  32. /* 释放中断 */
  33. free_irq(xxx_irq, xxx_interrupt);
  34. //...
  35. }

2.3 多设备共享中断

  1. /* 中断处理顶半部 */
  2. irqreturn_t xxx_interrupt(int irq, void *dev_id)
  3. {
  4. //...
  5. int status = read_int_status(); /* 获知中断源 */
  6. if (!is_myint(dev_id, status)) /* 判断是否为本设备中断 */
  7. return IRQ_NONE; /* 不是本设备中断, 立即返回 */
  8. /* 是本设备中断, 进行处理 */
  9. //...
  10. return IRQ_HANDLED; /* 返回 IRQ_HANDLED 表明中断已被处理 */
  11. }
  12. /* 设备驱动模块加载函数 */
  13. int xxx_init(void)
  14. {
  15. //...
  16. /* 申请共享中断 */
  17. result = request_irq(sh_irq, xxx_interrupt,
  18. IRQF_SHARED, "xxx", xxx_dev);
  19. //...
  20. }
  21. /* 设备驱动模块卸载函数 */
  22. void xxx_exit(void)
  23. {
  24. //...
  25. /* 释放中断 */
  26. free_irq(xxx_irq, xxx_interrupt);
  27. //...
  28. }

3 内核定时器编程

语雀内容

4 驱动中定时器处理模板

  1. /* xxx 设备结构体 */
  2. struct xxx_dev
  3. {
  4. struct cdev cdev;
  5. //...
  6. timer_list xxx_timer; /* 设备要使用的定时器 */
  7. };
  8. /* xxx 驱动中的某函数 */
  9. xxx_func1( )
  10. {
  11. struct xxx_dev *dev = filp->private_data;
  12. //...
  13. /* 初始化定时器 */
  14. init_timer(&dev->xxx_timer);
  15. dev->xxx_timer.function = &xxx_do_timer;
  16. dev->xxx_timer.data = (unsigned long)dev;
  17. /* 设备结构体指针作为定时器处理函数参数 */
  18. dev->xxx_timer.expires = jiffies + delay;
  19. /* 添加(注册) 定时器 */
  20. add_timer(&dev->xxx_timer);
  21. //...
  22. }
  23. /* xxx 驱动中的某函数 */
  24. xxx_func2( )
  25. {
  26. //...
  27. /* 删除定时器 */
  28. del_timer(&dev->xxx_timer);
  29. //...
  30. }
  31. /* 定时器处理函数 */
  32. static void xxx_do_timer(unsigned long arg)
  33. {
  34. struct xxx_device *dev = (struct xxx_device *)(arg);
  35. //...
  36. /* 调度定时器再执行 */
  37. dev->xxx_timer.expires = jiffies + delay;
  38. add_timer(&dev->xxx_timer);
  39. //...
  40. }

5 驱动定时器处理示例

  1. //秒字符设备
  2. //在打开时初始化定时器并添加到内核的定时器链表中,每秒输出一次当前jiffies
  3. #include <linux/module.h>
  4. #include <linux/fs.h>
  5. #include <linux/mm.h>
  6. #include <linux/init.h>
  7. #include <linux/cdev.h>
  8. #include <linux/slab.h>
  9. #include <linux/uaccess.h>
  10. #define SECOND_MAJOR 232
  11. static int second_major = SECOND_MAJOR;
  12. module_param(second_major, int, S_IRUGO);
  13. struct second_dev
  14. {
  15. struct cdev cdev;
  16. atomic_t counter; //原子类型计数
  17. struct timer_list s_timer; //内核定时器
  18. };
  19. static struct second_dev *second_devp;
  20. static void second_timer_handler(struct timer_list* arg)
  21. {
  22. mod_timer(&(second_devp->s_timer), jiffies + HZ); //触发下一次定时
  23. atomic_inc(&(second_devp->counter));
  24. printk(KERN_INFO "current jiffies is %ld\n", jiffies);
  25. }
  26. static int second_open(struct inode *inode, struct file *filp)
  27. {
  28. //打开设备时,初始化定时器,设置处理函数
  29. second_devp->s_timer.function = &second_timer_handler;
  30. second_devp->s_timer.expires = jiffies + HZ;
  31. add_timer(&(second_devp->s_timer)); //添加timer到内核
  32. atomic_set(&(second_devp->counter), 0);
  33. return 0;
  34. }
  35. static int second_release(struct inode *inode, struct file *filp)
  36. {
  37. del_timer(&(second_devp->s_timer)); //释放设备时,从内核删除定时器
  38. return 0;
  39. }
  40. static ssize_t second_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
  41. {
  42. //读操作
  43. int counter = atomic_read(&(second_devp->counter));
  44. if (put_user(counter, (int *)buf)) //复制counter到用户空间
  45. return -EFAULT;
  46. else
  47. return sizeof(unsigned int);
  48. }
  49. static const struct file_operations second_fops =
  50. {
  51. .owner = THIS_MODULE,
  52. .open = second_open,
  53. .release = second_release,
  54. .read = second_read
  55. };
  56. //驱动模块加载函数
  57. static void second_setup_cdev(struct second_dev* dev, int index)
  58. {
  59. int err, devno = MKDEV(second_major, index); //获得dev_t对象
  60. cdev_init(&(dev->cdev), &second_fops);//初始化设备
  61. dev->cdev.owner = THIS_MODULE;
  62. //注册设备
  63. err = cdev_add(&dev->cdev, devno, 1);
  64. if (err)
  65. {
  66. printk(KERN_NOTICE"Error %d adding second %d", err, index);
  67. }
  68. }
  69. static int __init second_init(void)
  70. {
  71. int ret;
  72. dev_t devno = MKDEV(second_major, 0);
  73. //申请设备号
  74. if (second_major)
  75. {
  76. ret = register_chrdev_region(devno, 1, "second");
  77. }
  78. else
  79. {
  80. ret = alloc_chrdev_region(&devno, 0, 1, "second");
  81. second_major = MAJOR(devno);
  82. }
  83. if (ret < 0)
  84. return ret;
  85. second_devp = kzalloc(sizeof(struct second_dev), GFP_KERNEL);
  86. if (!second_devp)
  87. {
  88. //空间申请失败
  89. ret = -ENOMEM;
  90. goto fail_malloc;
  91. }
  92. second_setup_cdev(second_devp, 0);
  93. //初始化互斥量
  94. return 0;
  95. fail_malloc:
  96. unregister_chrdev_region(devno, 1);
  97. return ret;
  98. }
  99. module_init(second_init);
  100. //驱动模块卸载函数
  101. static void __exit second_exit(void)
  102. {
  103. cdev_del(&second_devp->cdev);//注销设备
  104. kfree(second_devp);
  105. unregister_chrdev_region(MKDEV(second_major, 0), 1);//释放设备号
  106. }
  107. module_exit(second_exit);
  108. //模块声明
  109. MODULE_AUTHOR("BARRET REN <barret.ren@outlook.com>");
  110. MODULE_LICENSE("GPL v2");
  111. MODULE_DESCRIPTION("A driver for virtual second charactor device");
  112. MODULE_ALIAS("second device driver");

测试程序很简单,只有触发驱动的read就可以:

  1. //second cdev的用户空间测试程序
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. int main()
  8. {
  9. int fd;
  10. int counter = 0;
  11. int old_counter = 0;
  12. /* 打开 /dev/second 设备文件 */
  13. fd = open("/dev/second", O_RDONLY);
  14. if (fd != -1)
  15. {
  16. while (1)
  17. {
  18. read(fd, &counter, sizeof(unsigned int)); /* 读目前经历的秒数 */
  19. if (counter != old_counter)
  20. {
  21. printf("seconds after open /dev/second :%d\n", counter);
  22. old_counter = counter;
  23. }
  24. }
  25. }
  26. else
  27. {
  28. printf("Device open failure\n");
  29. }
  30. }

之后创建设备号为232的/dev/second设备节点,运行测试程序就会看待控制台打印,同时模块在dmesg也会打印jiffies的值。