|
#include <stdint.h> |
|
#include <linux/kernel.h> |
|
#include <linux/device.h> |
|
#include <linux/module.h> |
|
#include <linux/init.h> |
|
#include <linux/types.h> |
|
#include <linux/errno.h> |
|
#include <linux/fs.h> |
|
#include <linux/mm.h> |
|
#include <linux/sched.h> |
|
#include <linux/cdev.h> |
|
#include <linux/slab.h> |
|
#include <linux/ioctl.h> |
|
#include <linux/uaccess.h> |
|
#include <asm/io.h> |
|
#include <linux/platform_device.h> |
|
#include <linux/version.h> |
|
#include <linux/sysfs.h> |
|
#include <linux/kobject.h> |
|
#include <linux/input.h> |
|
#include <linux/bits.h> |
|
|
|
static struct input_dev *kb_dev; |
|
|
|
// Sysfs write callback |
|
static ssize_t key_event_store(struct device *dev, struct device_attribute *attr, |
|
const char *buf, size_t count) |
|
{ |
|
uint8_t keycode, state; |
|
|
|
if (count < 2) { |
|
pr_err("clemore_virtual_kbd: Need 2 bytes (keycode + state)\n"); |
|
return -EINVAL; |
|
} |
|
|
|
keycode = buf[0]; |
|
state = buf[1]; |
|
|
|
if (keycode >= KEY_CNT) { |
|
pr_err("clemore_virtual_kbd: Invalid keycode %u\n", keycode); |
|
return -EINVAL; |
|
} |
|
if (state != 0 && state != 1) { |
|
pr_err("clemore_virtual_kbd: Invalid state %u (must be 0 or 1)\n", state); |
|
return -EINVAL; |
|
} |
|
|
|
input_report_key(kb_dev, keycode, state); |
|
input_sync(kb_dev); |
|
|
|
pr_debug("clemore_virtual_kbd: Sent key %u %s\n", keycode, state ? "PRESS" : "RELEASE"); |
|
|
|
return count; |
|
} |
|
|
|
static DEVICE_ATTR(key_event, 0220, NULL, key_event_store); |
|
|
|
static int __init virtual_keyboard_init(void) |
|
{ |
|
int ret; |
|
|
|
pr_info("clemore_virtual_kbd: Initializing driver\n"); |
|
|
|
kb_dev = input_allocate_device(); |
|
if (!kb_dev) { |
|
pr_err("clemore_virtual_kbd: Allocation failed\n"); |
|
return -ENOMEM; |
|
} |
|
|
|
kb_dev->name = "Clemore Virtual Keyboard"; |
|
kb_dev->id.bustype = BUS_VIRTUAL; |
|
kb_dev->id.vendor = CLEMORE_VirtDevVID; |
|
kb_dev->id.product = CLEMORE_VirtDevPID; |
|
kb_dev->id.version = CLEMORE_VirtDevVersion; |
|
|
|
__set_bit(EV_KEY, kb_dev->evbit); |
|
bitmap_fill(kb_dev->keybit, KEY_CNT); |
|
|
|
ret = input_register_device(kb_dev); |
|
if (ret) { |
|
pr_err("clemore_virtual_kbd: Registration failed: %d\n", ret); |
|
input_free_device(kb_dev); |
|
return ret; |
|
} |
|
|
|
ret = device_create_file(&kb_dev->dev, &dev_attr_key_event.attr); |
|
if (ret) { |
|
pr_err("clemore_virtual_kbd: Sysfs creation failed: %d\n", ret); |
|
input_unregister_device(kb_dev); |
|
// No need to call input_free_device; input_unregister_device frees it |
|
return ret; |
|
} |
|
|
|
pr_info("clemore_virtual_kbd: Driver loaded\n"); |
|
return 0; |
|
} |
|
|
|
static void __exit virtual_keyboard_exit(void) |
|
{ |
|
pr_info("clemore_virtual_kbd: Unloading driver\n"); |
|
|
|
if (kb_dev) { |
|
device_remove_file(&kb_dev->dev, &dev_attr_key_event.attr); |
|
input_unregister_device(kb_dev); |
|
} |
|
|
|
pr_info("clemore_virtual_kbd: Driver unloaded\n"); |
|
} |
|
|
|
module_init(virtual_keyboard_init); |
|
module_exit(virtual_keyboard_exit); |
|
|
|
MODULE_LICENSE("GPL"); |
|
MODULE_AUTHOR("Jim Jumblaya Jackson aka God of Thunder"); |
|
MODULE_DESCRIPTION("Virtual Keyboard Driver"); |
|
MODULE_VERSION("1.0"); |