文章目录
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
[];
};
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") ||
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)中指定中断_在代码中获得中断