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.\n"); 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\n"); 79 return ERR_PTR(-ENODEV); 80 } 81 82 printk(KERN_INFO "child num is %d.\n", 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\n", 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.\n"); 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\n", 114 button->gpio, error); 115 return error; 116 } 117 button->irq = irq; 118 } 119 120 printk(KERN_INFO "get virq %d for key.\n", 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\n", 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");