Linux内核版本:4.14.2
本文基于itop4412开发板,编写驱动程序响应HOME按键中断,编写这个按键驱动程序需要做如下几个工作:
1. 在原理图中确定HOME按键的引脚
2. 在设备树文件中添加节点描述HOME引脚
3. 重新编译烧写设备树
4. 编写驱动程序,调用设备树接口函数获取HOME引脚的中断号,使用中断号注册按键中断处理程序
1. 在原理图中确定HOME按键的引脚
在原理图中找到HOME按键对应的引脚是GPX1_1
2. 在设备树文件中添加节点描述HOME引脚
itop4412开发板中将GPIO分成多个组,GPX1_1在gpx1组中,设备树文件exynos4412-pinctrl.dtsi中用如下节点描述gpx1组:
我们需要在设备树文件exynos4412-itop-elite.dts中,根据exynos4412-pinctrl.dtsi文件对gpx1组的描述添加HOME引脚的描述节点,如下
3. 重新编译烧写设备树
4. 编写驱动程序,调用设备树接口函数获取HOME引脚的中断号,使用中断号注册按键中断处理程序
驱动程序按照平台总线的架构编写,用platform_driver_register注册驱动;
4.1 设备和驱动匹配实现过程
在设备树文件中添加了HOME按键的描述节点后,内核在解析设备树时,就会将上述节点转换成一个平台设备platform_device;在struct platform_driver结构体中,将compatible属性设置为”irq-keys”,那么,加载驱动后,就会和内核生成的这个平台设备platform_device匹配,从而进入probe函数中
驱动程序中指定compatible的程序如下: |
static const struct of_device_id irq_test_of_match[] = { { .compatible = "irq-keys", }, { }, }; MODULE_DEVICE_TABLE(of, irq_test_of_match); static struct platform_driver irq_test_device_driver = { .probe = irq_test_probe, .driver = { .name = "irqtest_keys", .of_match_table = irq_test_of_match, } }; |
4.2 HOME引脚信息读取过程
上述驱动和设备匹配后,程序就进入probe函数中,在probe函数中,可以使用设备树相关的接口读取HOME引脚的信息:
读取子节点的个数,irqtest_keys只有一个子节点key-home
device_get_child_node_count
获取每个子节点的结构体
device_for_each_child_node(dev, child)
获取子节点中引脚的虚拟中断号,这个虚拟中断号是内核自动生成的
irq_of_parse_and_map(to_of_node(child), 0)
获取子节点gpio描述符gpiod
devm_fwnode_get_gpiod_from_child
使用虚拟中断号注册中断处理函数
devm_request_any_context_irq
测试驱动程序如下:
1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/fs.h> 4 #include <linux/interrupt.h> 5 #include <linux/irq.h> 6 #include <linux/sched.h> 7 #include <linux/pm.h> 8 #include <linux/slab.h> 9 #include <linux/sysctl.h> 10 #include <linux/proc_fs.h> 11 #include <linux/delay.h> 12 #include <linux/platform_device.h> 13 #include <linux/input.h> 14 #include <linux/gpio_keys.h> 15 #include <linux/workqueue.h> 16 #include <linux/gpio.h> 17 #include <linux/gpio/consumer.h> 18 #include <linux/of.h> 19 #include <linux/of_irq.h> 20 #include <linux/spinlock.h> 21 22 /* 设备树节点 */ 23 #if 0 24 irqtest_keys { 25 compatible = "irq-keys"; 26 key-home { 27 label = "GPIO key-home"; 28 gpios = <&gpx1 1 GPIO_ACTIVE_LOW>; 29 pinctrl-0 = <&my_irq_key>; 30 pinctrl-names = "default"; 31 }; 32 }; 33 34 my_irq_key: my-irq-key { 35 samsung,pins = "gpx1-1"; 36 samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>; 37 // samsung,pin-drv = <EXYNOS4_PIN_DRV_LV4>; 38 }; 39 #endif 40 41 struct my_gpio_keys_button { 42 unsigned int code; 43 int gpio; 44 int active_low; 45 const char *desc; 46 unsigned int type; 47 int wakeup; 48 int debounce_interval; 49 bool can_disable; 50 int value; 51 unsigned int irq; 52 struct gpio_desc *gpiod; 53 }; 54 55 static char *label[2]; 56 static struct device *dev; 57 static struct my_gpio_keys_button *button; 58 59 static irqreturn_t irq_test_irq_isr(int irq, void *dev_id) 60 { 61 printk(KERN_INFO "get irq --> irq_test_irq_isr. "); 62 63 return IRQ_HANDLED; 64 } 65 66 static int irq_test_probe(struct platform_device *pdev) 67 { 68 /* 获取节点信息,注册中断 */ 69 dev = &pdev->dev; 70 struct fwnode_handle *child = NULL; 71 int nbuttons; 72 int irq, error; 73 irq_handler_t isr; 74 unsigned long irqflags; 75 76 nbuttons = device_get_child_node_count(dev); 77 if (nbuttons == 0) { 78 printk(KERN_INFO "no child exist, return "); 79 return ERR_PTR(-ENODEV); 80 } 81 82 printk(KERN_INFO "child num is %d. ", nbuttons); 83 button = devm_kzalloc(dev, sizeof(struct my_gpio_keys_button) * nbuttons, GFP_KERNEL); 84 85 /* 获取lable参数,父节点没有lable属性 */ 86 device_property_read_string(dev, "label", label[0]); 87 printk(KERN_INFO "parent lable %s ", label[0]); 88 89 /* 扫描处理每个子节点 */ 90 device_for_each_child_node(dev, child) { 91 /* 获取虚拟中断号virq */ 92 if (is_of_node(child)) { 93 button->irq = irq_of_parse_and_map(to_of_node(child), 0); 94 } 95 96 fwnode_property_read_string(child, "label", &button->desc); 97 /* 获取gpio描述符gpiod */ 98 button->gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, 99 child, 100 GPIOD_IN, 101 button->desc); 102 if (IS_ERR(button->gpiod)) { 103 printk(KERN_INFO "get gpiod failed, return. "); 104 return -ENOENT; 105 } 106 107 /* 检查虚拟中断号,可不使用 */ 108 if (!button->irq) { 109 irq = gpiod_to_irq(button->gpiod); 110 if (irq < 0) { 111 error = irq; 112 dev_err(dev, 113 "Unable to get irq number for GPIO %d, error %d ", 114 button->gpio, error); 115 return error; 116 } 117 button->irq = irq; 118 } 119 120 printk(KERN_INFO "get virq %d for key. ", button->irq); 121 isr = irq_test_irq_isr; 122 irqflags = 0; 123 irqflags |= IRQF_SHARED; 124 //devm_request_any_context_irq(class_dev, data->irq,int26_irq, IRQF_TRIGGER_FALLING, data->name, data); 125 126 /* 设置引脚为输入模式 */ 127 gpiod_set_value(button->gpiod, 1); 128 gpiod_direction_input(button->gpiod); 129 130 /* 注册中断 */ 131 /* 最后一个参数是传给中断函数的参数 */ 132 error = devm_request_any_context_irq(dev, button->irq, isr, IRQF_TRIGGER_FALLING, button->desc, NULL); 133 if (error < 0) { 134 dev_err(dev, "Unable to claim irq %d; error %d ", button->irq, error); 135 return error; 136 } 137 } 138 139 return 0; 140 } 141 142 static const struct of_device_id irq_test_of_match[] = { 143 { .compatible = "irq-keys", }, 144 { }, 145 }; 146 147 MODULE_DEVICE_TABLE(of, irq_test_of_match); 148 149 static struct platform_driver irq_test_device_driver = { 150 .probe = irq_test_probe, 151 .driver = { 152 .name = "irqtest_keys", 153 .of_match_table = irq_test_of_match, 154 } 155 }; 156 157 static int __init irq_test_init(void) 158 { 159 return platform_driver_register(&irq_test_device_driver); 160 } 161 162 static void __exit irq_test_exit(void) 163 { 164 devm_free_irq(dev, button->irq, NULL); 165 platform_driver_unregister(&irq_test_device_driver); 166 } 167 168 module_init(irq_test_init); 169 module_exit(irq_test_exit); 170 171 MODULE_LICENSE("GPL");