• Linux: IO中断驱动开发教程


    本文章详细介绍了,在Linux内核开发IO中断驱动的流程。主要分为四部分:配置设备树DTS、驱动模块编写、配置Linux内核、应用程序测试。

    1. 配置设备树DTS

    (1)屏蔽原按键设备树IO配置

    打开stm32mp15xx-ya157c.dtsi文件(/arch/arm/boot/dts/stm32mp15xx-ya157c.dtsi),屏蔽如下代码。屏蔽下面的button,是为了方便测试。button按下改成了打印我们的消息。

    图 1. 屏蔽原按键设备树IO配置 

     

    (2)增加外部中断按键配置

    在上一个按键驱动实验的基础上修改即可

    1. gpio_XXXX {
    2. compatible = "gpio, XXXX";
    3. status = "okay";
    4. label = "XXXX_notifier";
    5. gpio_XXXX-gpios = <&gpioi 11 (GPIO_ACTIVE_LOW )>;
    6. interrupt-parent = <&gpioi>;
    7. interrupts = <11 IRQ_TYPE_EDGE_RISING>;
    8. };

    编译和更新DTB文件以后,运行arm板卡,可以发现设备文件,在默认目录下产生。

     2. 驱动模块编写

     

    1. // SPDX-License-Identifier: GPL-2.0-only
    2. /*
    3. * Driver for XXXX on GPIO line capable of generating interrupt.
    4. *
    5. * Copyright 2022 Allen Sun
    6. */
    7. /*---- Kernel includes ----*/
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include
    17. #include
    18. #include
    19. #include
    20. #include
    21. dev_t dev = 0;
    22. static struct class *dev_class;
    23. static struct cdev cdev;
    24. struct gpio_desc *gpio_XXXX;
    25. static int gpio_XXXX_pin_number = 0;
    26. static int gpio_XXXX_irq_number = 0;
    27. #define SIGETX 44
    28. static struct task_struct *task = NULL;
    29. // application pid, application use ioctl method to set this value
    30. static int app_pid = 0;
    31. typedef enum{
    32. IOCTL_SET_APP_PID = 0x111,
    33. } MODULE_CMD;
    34. static int gpio_XXXX_open(struct inode *inode, struct file *file)
    35. {
    36. printk(KERN_INFO "XXXX_NOTIFIER: gpio_XXXX_open\n");
    37. return 0;
    38. }
    39. static ssize_t gpio_XXXX_read(struct file *file, char *userbuf, size_t count, loff_t * ppos)
    40. {
    41. unsigned char temp = gpio_get_value(gpio_XXXX_pin_number);
    42. if(copy_to_user(userbuf, &temp, 1))
    43. {
    44. printk(KERN_ALERT "gpio_XXXX_read copy_to_user failed\n");
    45. return -EINVAL;
    46. }
    47. return count;
    48. }
    49. static long gpio_XXXX_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    50. {
    51. printk(KERN_INFO "gpio_XXXX_ioctl:%s cmd=0x%x,arg=0x%lx\n", __func__, cmd, arg);
    52. if(cmd == IOCTL_SET_APP_PID)
    53. {
    54. printk(KERN_INFO "%s, gpio_XXXX_ioctl: owner pid at 0x%lx\n", __func__, arg);
    55. if(copy_from_user(&app_pid, (int *)arg, sizeof(int)))
    56. {
    57. printk(KERN_ALERT "gpio_XXXX_ioctl:copy from user failed\n");
    58. return -EFAULT;
    59. }
    60. printk(KERN_INFO "gpio_XXXX_ioctl: app_pid is %d\n", app_pid);
    61. }
    62. return 0;
    63. }
    64. static struct file_operations gpio_XXXX_fops = {
    65. .owner = THIS_MODULE,
    66. .read = gpio_XXXX_read,
    67. .open = gpio_XXXX_open,
    68. .unlocked_ioctl = gpio_XXXX_ioctl,
    69. .compat_ioctl = gpio_XXXX_ioctl,
    70. };
    71. // Create the interrupt handler
    72. static irqreturn_t gpio_XXXX_handler(int irq, void * ident)
    73. {
    74. struct kernel_siginfo info;
    75. if (app_pid == 0)
    76. {
    77. printk(KERN_ALERT "XXXX_NOTIFIER: gpio_XXXX_handler not set user pid\n");
    78. return IRQ_HANDLED;
    79. }
    80. //Sending signal to app
    81. memset(&info, 0, sizeof(struct kernel_siginfo));
    82. info.si_signo = SIGETX;
    83. info.si_code = 0;
    84. info.si_int = 1234;
    85. printk(KERN_INFO "gpio_XXXX_handler Interrupt received from GPIO XXXX pin\n");
    86. rcu_read_lock();
    87. task = pid_task(find_vpid(app_pid), PIDTYPE_PID);
    88. rcu_read_unlock();
    89. if (task == NULL) {
    90. printk(KERN_ALERT "XXXX_NOTIFIER: get_current failed\n");
    91. return -EINVAL;
    92. }
    93. else
    94. {
    95. printk(KERN_INFO "Sending signal to app\n");
    96. if(send_sig_info(SIGETX, &info, task) < 0) {
    97. printk(KERN_ALERT "gpio_XXXX_handler Unable to send signal\n");
    98. }
    99. }
    100. return IRQ_HANDLED;
    101. }
    102. static const struct of_device_id gpio_XXXX_of_match[] = {
    103. // Used to probe, match the DTS file parameter: compatible = "gpio, XXXX";
    104. { .compatible = "gpio, XXXX", },
    105. { },
    106. };
    107. MODULE_DEVICE_TABLE(of, gpio_XXXX_of_match);
    108. static int gpio_XXXX_probe(struct platform_device *pdev)
    109. {
    110. // "gpio_XXXX" match the DTS file parameter name: gpio_XXXX-gpios = <&gpioi 11 (GPIO_ACTIVE_LOW )>;
    111. gpio_XXXX = devm_gpiod_get(&pdev->dev, "gpio_XXXX", GPIOD_ASIS);
    112. if (IS_ERR(gpio_XXXX))
    113. {
    114. dev_err(&pdev->dev, "failed to get gpio_XXXX GPIO\n");
    115. return PTR_ERR(gpio_XXXX);
    116. }
    117. gpio_XXXX_pin_number = desc_to_gpio(gpio_XXXX);
    118. gpio_XXXX_irq_number = gpiod_to_irq(gpio_XXXX);
    119. if(alloc_chrdev_region(&dev, 0, 1, "gpio_XXXX_drv") < 0)
    120. {
    121. printk(KERN_ALERT "gpio_XXXX_probe alloc_chrdev_region failed\n");
    122. return -1;
    123. }
    124. printk(KERN_INFO "XXXX_NOTIFIER Major = %d Minor = %d \n", MAJOR(dev), MINOR(dev));
    125. dev_class = class_create(THIS_MODULE, "gpio_XXXX_chr_class");
    126. if(dev_class == NULL)
    127. {
    128. printk(KERN_ALERT "gpio_XXXX_probe class_create failed\n");
    129. unregister_chrdev_region(dev, 1);
    130. return -1;
    131. }
    132. if(device_create(dev_class, NULL, dev, NULL, "gpio_XXXX_chr_device") == NULL)
    133. {
    134. printk(KERN_ALERT "gpio_XXXX_probe device_create failed\n");
    135. class_destroy(dev_class);
    136. unregister_chrdev_region(dev, 1);
    137. return -1;
    138. }
    139. cdev_init(&cdev, &gpio_XXXX_fops);
    140. if(cdev_add(&cdev, dev, 1) < 0)
    141. {
    142. printk(KERN_ALERT "gpio_XXXX_probe cdev_add failed\n");
    143. device_destroy(dev_class, dev);
    144. class_destroy(dev_class);
    145. unregister_chrdev_region(dev, 1);
    146. return -1;
    147. }
    148. if(request_irq(gpio_XXXX_irq_number, gpio_XXXX_handler, IRQF_TRIGGER_RISING, "gpio_XXXX", NULL) != 0)
    149. {
    150. printk(KERN_INFO "could not request irq: %d\n", gpio_XXXX_irq_number);
    151. gpio_free(gpio_XXXX_pin_number);
    152. return -1;
    153. }
    154. printk(KERN_INFO "Waiting for interrupts ... \n");
    155. return 0;
    156. }
    157. static void gpio_XXXX_shutdown(struct platform_device *pdev)
    158. {
    159. free_irq(gpio_XXXX_irq_number, THIS_MODULE->name);
    160. gpio_free(gpio_XXXX_pin_number);
    161. }
    162. static struct platform_driver gpio_XXXX_device_driver = {
    163. .probe = gpio_XXXX_probe,
    164. .shutdown = gpio_XXXX_shutdown,
    165. .driver = {
    166. .name = "XXXX_notifier",
    167. .of_match_table = gpio_XXXX_of_match,
    168. }
    169. };
    170. static int __init gpio_XXXX_init (void)
    171. {
    172. printk("XXXX_NOTIFIER: gpio_XXXX_init \n");
    173. return platform_driver_register(&gpio_XXXX_device_driver);
    174. }
    175. static void __exit gpio_XXXX_exit(void)
    176. {
    177. platform_driver_unregister(&gpio_XXXX_device_driver);
    178. }
    179. module_init(gpio_XXXX_init);
    180. module_exit(gpio_XXXX_exit);
    181. MODULE_AUTHOR("Allen Sun");
    182. MODULE_DESCRIPTION("GPIO interrupt test module for embedded Linux");
    183. MODULE_LICENSE("GPL");
    184. /*
    185. * End of file
    186. */

     Kconfig

    1. # SPDX-License-Identifier: GPL-2.0-only
    2. #
    3. # Input core configuration
    4. #
    5. menuconfig INPUT_XXXX_NOTIFIER
    6. bool "XXXXNotifier"
    7. default y
    8. help
    9. Say Y here, and a list of supported IpccNotifier will be displayed.
    10. This option doesn't affect the kernel.
    11. If unsure, say Y.
    12. if INPUT_XXXX_NOTIFIER
    13. config XXXX_NOTIFIER_GPIO
    14. tristate "XXXX NOTIFIER GPIO"
    15. depends on GPIOLIB
    16. default m
    17. help
    18. This driver implements support for XXXX connected
    19. to GPIO pins of various CPUs (and some other chips).
    20. Say Y here if your device has XXXX connected
    21. directly to such GPIO pins. Your board-specific
    22. setup logic must also provide a platform device,
    23. with configuration data saying which GPIOs are used.
    24. To compile this driver as a module, choose M here: the
    25. module will be called xxxx_notifier.
    26. endif

     Makefile

    1. # SPDX-License-Identifier: GPL-2.0
    2. #
    3. # Makefile for the input core drivers.
    4. #
    5. # Each configuration option enables a list of files.
    6. obj-$(CONFIG_XXXX_NOTIFIER_GPIO) += xxxx_notifier.o

    3. 配置Linux内核

     make menuconfig:设置编译为模块。M:生成.ko Y:编译进内核,不生成.ko文件

     将编译好的驱动文件和内核拷贝到arm开发板上面。重启运行,会看到/dev目录下面有对应的设备节点产生。

     4. 应用程序测试

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #define SIGETX 44
    14. int check = 0;
    15. typedef enum{
    16. IOCTL_SET_APP_PID = 0x111,
    17. } APP_CMD;
    18. void sig_event_handler(int n, siginfo_t *info, void *unused)
    19. {
    20. printf ("sig_event_handler Received signal from kernel");
    21. if (n == SIGETX) {
    22. check = info->si_int;
    23. printf ("Received signal from kernel : Value = %u\n", check);
    24. }
    25. }
    26. int main(int argc, char **argv)
    27. {
    28. int fd = -1;
    29. int count = 1;
    30. int app_pid;
    31. unsigned char pin_level;
    32. struct sigaction act;
    33. /* install custom signal handler */
    34. sigemptyset(&act.sa_mask);
    35. act.sa_flags = SA_NODEFER;
    36. act.sa_sigaction = sig_event_handler;
    37. sigaction(SIGETX, &act, NULL);
    38. printf("Installed signal handler for SIGETX = %d\n", SIGETX);
    39. fd = open("/dev/gpio_XXXX_chr_device", O_RDWR);
    40. if (fd == -1)
    41. {
    42. perror("XXXX GPIO Open\n");
    43. return -1;
    44. }
    45. count = read(fd, &pin_level, 1);
    46. if(count == -1)
    47. {
    48. perror("XXXX GPIO read\n");
    49. }
    50. else
    51. {
    52. printf("XXXX GPIO level is %d\n", pin_level);
    53. }
    54. app_pid = getpid();
    55. ioctl(fd, IOCTL_SET_APP_PID, &app_pid);
    56. while(1)
    57. {
    58. printf("XXXX notifier heartbeat..\n");
    59. sleep(10);
    60. }
    61. if (close(fd) == -1)
    62. {
    63. perror("Failed to close GPIO character device file");
    64. return -1;
    65. }
    66. return 0;
    67. }

    测试结果:按下用户按钮,app对应的中断处理函数就会执行。

  • 相关阅读:
    教你一文解决 js 数字精度丢失问题
    Struts2+Maven+表单标签向MySql表插入数据实现简单注册
    ThreadLocal源码解析学习
    【C++】模板(初级)
    SpringBoot解决跨域问题的六种方式
    5G工业互联网的“下半场”该拼什么?
    JS操作字符串方法学习系列(5)-每天学习10个方法
    Android 10.0 系统设置蓝牙配对时去掉配对框实现直接配对功能
    设备指纹之安全性详解
    基于ISO14229协议的单帧以及多帧Can发送代码
  • 原文地址:https://blog.csdn.net/xikangsoon/article/details/126260217