[xhr4412][practice] 设备树 GPIO 中断

    科技2022-07-13  132

    文章目录

    1. introduction2. makefile3. key_drv.c4. test5. issue1. unexpected IRQ trap at vector 00 reference

    1. introduction

       前面简单使用了 GPIO 的输出功能,wifi 模块的中断是由一个 GPIO 中断实现,所以这里还需要尝试如何使用 GPIO 中断。

       主要使用设备树中 gpio-keys 节点,并仿照 drivers/input/keyboard/gpio_keys.c 来写我们自己的 driver。

       linux 官方驱动使用的是 struct input_dev 输入子系统。我们这里只简单使用中断,不使用这些完整的框架。

    2. makefile

    MODULE := xhrkey obj-m := $(MODULE).o KDIR := /home/xhr/iTop4412/xhr4412/linux/xhr4412-linux-5.8.5 PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules .PHONY:clean clean: rm -f *.o *.ko *.symvers *.order *.mod.c *.mod .*.cmd $(MODULE)-objs += key_drv.o

    3. key_drv.c

    #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/gpio_keys.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_gpio.h> #include <linux/spinlock.h> #include <linux/input.h> #include <dt-bindings/input/gpio-keys.h> #define xprint(fmt, args...) printk("[xhr4412][%s][%d] " fmt, __func__, __LINE__, ##args) struct gpio_button_data { const struct gpio_keys_button *button; }; struct gpio_keys_drvdata { const struct gpio_keys_platform_data *pdata; int btn_num; struct gpio_button_data btns[]; }; /* * Translate properties into platform_data */ static struct gpio_keys_platform_data *gpio_keys_get_devtree_pdata(struct device *dev) { struct gpio_keys_platform_data *pdata; struct gpio_keys_button *button; struct fwnode_handle *child; int nbuttons; nbuttons = device_get_child_node_count(dev); if (nbuttons == 0) return ERR_PTR(-ENODEV); pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * sizeof(*button), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); button = (struct gpio_keys_button *)(pdata + 1); pdata->buttons = button; pdata->nbuttons = nbuttons; pdata->rep = device_property_read_bool(dev, "autorepeat"); device_property_read_string(dev, "label", &pdata->name); device_for_each_child_node(dev, child) { button->gpio = of_get_gpio(to_of_node(child), 0); button->irq = gpio_to_irq(button->gpio); if (fwnode_property_read_u32(child, "linux,code", &button->code)) { dev_err(dev, "Button without keycode\n"); fwnode_handle_put(child); return ERR_PTR(-EINVAL); } fwnode_property_read_string(child, "label", &button->desc); if (fwnode_property_read_u32(child, "linux,input-type", &button->type)) button->type = EV_KEY; button->wakeup = fwnode_property_read_bool(child, "wakeup-source") || /* legacy name */ fwnode_property_read_bool(child, "gpio-key,wakeup"); fwnode_property_read_u32(child, "wakeup-event-action", &button->wakeup_event_action); button->can_disable = fwnode_property_read_bool(child, "linux,can-disable"); if (fwnode_property_read_u32(child, "debounce-interval", &button->debounce_interval)) button->debounce_interval = 5; button++; } return pdata; } static irqreturn_t gpio_keys_irq_handler(int irqnum, void *data) { static int count = 0; struct gpio_button_data *btn = data; xprint("gpio=%d, irqnum=%d, btn=%s, count=%d\n", btn->button->gpio, irqnum, btn->button->desc, count++); return IRQ_HANDLED; } static int gpio_keys_probe(struct platform_device * pdev) { struct device *dev = &pdev->dev; const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); struct gpio_keys_drvdata *ddata; struct gpio_keys_button *button; int i, ret; if (!pdata) { pdata = gpio_keys_get_devtree_pdata(dev); if (IS_ERR(pdata)) { xprint("get devtree pdata error !\n"); return PTR_ERR(pdata); } } else { xprint("not support !!\n"); return -EFAULT; } ddata = devm_kzalloc(dev, struct_size(ddata, btns, pdata->nbuttons), GFP_KERNEL); if (!ddata) { dev_err(dev, "failed to allocate state\n"); return -ENOMEM; } ddata->btn_num = pdata->nbuttons; ddata->pdata = pdata; platform_set_drvdata(pdev, ddata); button = (void *)(pdata + 1); for (i = 0; i < ddata->btn_num; i++) { if (button->irq) { ret = devm_request_irq(dev, button->irq, gpio_keys_irq_handler, IRQF_TRIGGER_FALLING, "test", &ddata->btns[i]); xprint("btn=%s devm_request_irq %d ret = %d\n", button->desc, button->irq, ret); } ddata->btns[i].button = button++; } return 0; } static int gpio_keys_remove(struct platform_device *pdev) { return 0; } static const struct of_device_id gpio_keys_of_match[] = { { .compatible = "xhr-gpio-keys---", }, { }, }; MODULE_DEVICE_TABLE(of, gpio_keys_of_match); static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, .remove = gpio_keys_remove, .driver = { .name = "xgpio-keys", .of_match_table = gpio_keys_of_match, } }; static int __init gpio_keys_init(void) { return platform_driver_register(&gpio_keys_device_driver); } static void __exit gpio_keys_exit(void) { platform_driver_unregister(&gpio_keys_device_driver); } late_initcall(gpio_keys_init); module_exit(gpio_keys_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("xhr <xhr@xhr.com>"); MODULE_DESCRIPTION("Keyboard driver for GPIOs"); MODULE_ALIAS("xhr platform:gpio-keys");

    4. test

    5. issue

    1. unexpected IRQ trap at vector 00

       用的 api 不对,拿到的 irq num 是错误的。

    reference

    addr2line内核模块(addr2line on kernel module)c – addr2line在内核模块上插入内核模块死机调试Linux设备驱动(五)—— 中断机制Linux kernel中断子系统之(五):驱动申请中断API【转】韦东山:在Linux设备树(DTS)中指定中断_在代码中获得中断
    Processed: 0.010, SQL: 8