129 lines
3.1 KiB
C
129 lines
3.1 KiB
C
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/input.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
static struct input_dev *kbd_dev;
|
|
static int gpio_num;
|
|
static int irq_num;
|
|
|
|
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
|
|
{
|
|
int state = gpio_get_value(gpio_num);
|
|
|
|
/* Active low button */
|
|
if (state == 0) {
|
|
input_report_key(kbd_dev, KEY_A, 1);
|
|
input_sync(kbd_dev);
|
|
} else {
|
|
input_report_key(kbd_dev, KEY_A, 0);
|
|
input_sync(kbd_dev);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int gpio_kbd_probe(struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
|
|
if (!np)
|
|
return -EINVAL;
|
|
|
|
/* Read GPIO from DT */
|
|
gpio_num = of_get_named_gpio(np, "gpios", 0);
|
|
if (gpio_num < 0) {
|
|
dev_err(dev, "Failed to get GPIO from DT\n");
|
|
return gpio_num;
|
|
}
|
|
|
|
ret = gpio_request(gpio_num, "gpio_kbd_button");
|
|
if (ret) {
|
|
dev_err(dev, "Failed to request GPIO\n");
|
|
return ret;
|
|
}
|
|
|
|
gpio_direction_input(gpio_num);
|
|
|
|
/* Allocate input device */
|
|
kbd_dev = input_allocate_device();
|
|
if (!kbd_dev) {
|
|
dev_err(dev, "Failed to allocate input device\n");
|
|
gpio_free(gpio_num);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
kbd_dev->name = "GPIO Keyboard Device";
|
|
kbd_dev->evbit[0] = BIT_MASK(EV_KEY);
|
|
set_bit(KEY_A, kbd_dev->keybit);
|
|
|
|
ret = input_register_device(kbd_dev);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to register input device\n");
|
|
input_free_device(kbd_dev);
|
|
gpio_free(gpio_num);
|
|
return ret;
|
|
}
|
|
|
|
/* Get IRQ from DT */
|
|
irq_num = of_irq_get(np, 0);
|
|
if (irq_num < 0) {
|
|
dev_err(dev, "Failed to get IRQ from DT\n");
|
|
input_unregister_device(kbd_dev);
|
|
gpio_free(gpio_num);
|
|
return irq_num;
|
|
}
|
|
|
|
ret = request_irq(irq_num,
|
|
gpio_irq_handler,
|
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
|
"gpio_kbd_irq",
|
|
NULL);
|
|
|
|
if (ret) {
|
|
dev_err(dev, "Failed to request IRQ\n");
|
|
input_unregister_device(kbd_dev);
|
|
gpio_free(gpio_num);
|
|
return ret;
|
|
}
|
|
|
|
dev_info(dev, "GPIO keyboard driver loaded\n");
|
|
return 0;
|
|
}
|
|
|
|
static void gpio_kbd_remove(struct platform_device *pdev)
|
|
{
|
|
free_irq(irq_num, NULL);
|
|
input_unregister_device(kbd_dev);
|
|
gpio_free(gpio_num);
|
|
}
|
|
|
|
static const struct of_device_id gpio_kbd_of_match[] = {
|
|
{ .compatible = "arcade,gpio-keyboard" },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, gpio_kbd_of_match);
|
|
|
|
static struct platform_driver gpio_kbd_driver = {
|
|
.probe = gpio_kbd_probe,
|
|
.remove = gpio_kbd_remove,
|
|
.driver = {
|
|
.name = "gpio-keyboard",
|
|
.of_match_table = gpio_kbd_of_match,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(gpio_kbd_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Nicholas Mello");
|
|
MODULE_DESCRIPTION("GPIO-based virtual keyboard driver using Device Tree");
|