mirror of
https://github.com/sysprog21/lkmpg.git
synced 2024-12-01 05:04:15 +08:00
Introduce Virtual Input Device Driver
Add the new section of input device driver, vinput[1]. Also, update the Acknowledgements. [1] https://github.com/sysprog21/vinput
This commit is contained in:
parent
85ee0ec67b
commit
636c1e702d
|
@ -1,2 +1,3 @@
|
||||||
bottomhalf
|
bottomhalf
|
||||||
intrpt
|
intrpt
|
||||||
|
vkbd
|
||||||
|
|
1
.mailmap
1
.mailmap
|
@ -6,3 +6,4 @@ Jim Huang <jserv.tw@gmail.com> Jim Huang <jserv@biilabs.io>
|
||||||
linD026 <shiyn.lin@gmail.com> linD026 <66012716+linD026@users.noreply.github.com>
|
linD026 <shiyn.lin@gmail.com> linD026 <66012716+linD026@users.noreply.github.com>
|
||||||
linD026 <shiyn.lin@gmail.com> linD026 <0086d026@email.ntou.edu.tw>
|
linD026 <shiyn.lin@gmail.com> linD026 <0086d026@email.ntou.edu.tw>
|
||||||
linD026 <shiyn.lin@gmail.com> linzhien <0086d026@email.ntou.edu.tw>
|
linD026 <shiyn.lin@gmail.com> linzhien <0086d026@email.ntou.edu.tw>
|
||||||
|
mengxinayan <31788564+mengxinayan@users.noreply.github.com> 萌新阿岩 <31788564+mengxinayan@users.noreply.github.com>
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
Arush Sharma, % <46960231+arushsharma24@users.noreply.github.com>
|
Arush Sharma, % <46960231+arushsharma24@users.noreply.github.com>
|
||||||
asas1asas200, % <asas1asas200@gmail.com>
|
asas1asas200, % <asas1asas200@gmail.com>
|
||||||
Benno Bielmeier, % <32938211+bbenno@users.noreply.github.com>
|
Benno Bielmeier, % <32938211+bbenno@users.noreply.github.com>
|
||||||
|
Bob Lee, % <defru04002@gmail.com>
|
||||||
Brad Baker, % <brad@brdbkr.com>
|
Brad Baker, % <brad@brdbkr.com>
|
||||||
ccs100203, % <ccs100203@gmail.com>
|
ccs100203, % <ccs100203@gmail.com>
|
||||||
Chih-Yu Chen, % <34228283+chihyu1206@users.noreply.github.com>
|
Chih-Yu Chen, % <34228283+chihyu1206@users.noreply.github.com>
|
||||||
|
Ching-Hua (Vivian) Lin, % <jkrvivian@gmail.com>
|
||||||
ChinYikMing, % <yikming2222@gmail.com>
|
ChinYikMing, % <yikming2222@gmail.com>
|
||||||
Cyril Brulebois, % <cyril@debamax.com>
|
Cyril Brulebois, % <cyril@debamax.com>
|
||||||
Daniele Paolo Scarpazza, % <>
|
Daniele Paolo Scarpazza, % <>
|
||||||
|
@ -22,10 +24,15 @@ Hsin-Hsiang Peng, % <hsinspeng@gmail.com>
|
||||||
Ignacio Martin, % <>
|
Ignacio Martin, % <>
|
||||||
JianXing Wu, % <fdgkhdkgh@gmail.com>
|
JianXing Wu, % <fdgkhdkgh@gmail.com>
|
||||||
linD026, % <shiyn.lin@gmail.com>
|
linD026, % <shiyn.lin@gmail.com>
|
||||||
|
lyctw, % <lyctw.ee@gmail.com>
|
||||||
|
manbing, % <manbing3@gmail.com>
|
||||||
Marconi Jiang, % <marconi1964@yahoo.com>
|
Marconi Jiang, % <marconi1964@yahoo.com>
|
||||||
|
mengxinayan, % <31788564+mengxinayan@users.noreply.github.com>
|
||||||
RinHizakura, % <s921975628@gmail.com>
|
RinHizakura, % <s921975628@gmail.com>
|
||||||
Roman Lakeev, % <>
|
Roman Lakeev, % <>
|
||||||
Stacy Prowell, % <sprowell@gmail.com>
|
Stacy Prowell, % <sprowell@gmail.com>
|
||||||
|
Steven Lung, % <1030steven@gmail.com>
|
||||||
|
Tristan Lelong, % <tristan.lelong@blunderer.org>
|
||||||
Tucker Polomik, % <tucker.polomik@inficon.com>
|
Tucker Polomik, % <tucker.polomik@inficon.com>
|
||||||
VxTeemo, % <tcccvvv123@gmail.com>
|
VxTeemo, % <tcccvvv123@gmail.com>
|
||||||
Wei-Lun Tsai, % <alan23273850@gmail.com>
|
Wei-Lun Tsai, % <alan23273850@gmail.com>
|
||||||
|
|
|
@ -56,6 +56,10 @@ DisableFormat: false
|
||||||
ExperimentalAutoDetectBinPacking: false
|
ExperimentalAutoDetectBinPacking: false
|
||||||
FixNamespaceComments: false
|
FixNamespaceComments: false
|
||||||
|
|
||||||
|
ForEachMacros:
|
||||||
|
- 'list_for_each'
|
||||||
|
- 'list_for_each_safe'
|
||||||
|
|
||||||
IncludeBlocks: Preserve
|
IncludeBlocks: Preserve
|
||||||
IncludeCategories:
|
IncludeCategories:
|
||||||
- Regex: '.*'
|
- Regex: '.*'
|
||||||
|
|
|
@ -29,6 +29,8 @@ obj-m += example_atomic.o
|
||||||
obj-m += example_mutex.o
|
obj-m += example_mutex.o
|
||||||
obj-m += bottomhalf.o
|
obj-m += bottomhalf.o
|
||||||
obj-m += ioctl.o
|
obj-m += ioctl.o
|
||||||
|
obj-m += vinput.o
|
||||||
|
obj-m += vkbd.o
|
||||||
|
|
||||||
PWD := $(CURDIR)
|
PWD := $(CURDIR)
|
||||||
|
|
||||||
|
|
398
examples/vinput.c
Normal file
398
examples/vinput.c
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
#include <linux/cdev.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
|
#include "vinput.h"
|
||||||
|
|
||||||
|
#define DRIVER_NAME "vinput"
|
||||||
|
|
||||||
|
#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
|
||||||
|
|
||||||
|
static DECLARE_BITMAP(vinput_ids, VINPUT_MINORS);
|
||||||
|
|
||||||
|
static LIST_HEAD(vinput_devices);
|
||||||
|
static LIST_HEAD(vinput_vdevices);
|
||||||
|
|
||||||
|
static int vinput_dev;
|
||||||
|
static struct spinlock vinput_lock;
|
||||||
|
static struct class vinput_class;
|
||||||
|
|
||||||
|
/* Search the name of vinput device in the vinput_devices linked list,
|
||||||
|
* which added at vinput_register().
|
||||||
|
*/
|
||||||
|
static struct vinput_device *vinput_get_device_by_type(const char *type)
|
||||||
|
{
|
||||||
|
int found = 0;
|
||||||
|
struct vinput_device *vinput;
|
||||||
|
struct list_head *curr;
|
||||||
|
|
||||||
|
spin_lock(&vinput_lock);
|
||||||
|
list_for_each (curr, &vinput_devices) {
|
||||||
|
vinput = list_entry(curr, struct vinput_device, list);
|
||||||
|
if (vinput && strncmp(type, vinput->name, strlen(vinput->name)) == 0) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&vinput_lock);
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
return vinput;
|
||||||
|
return ERR_PTR(-ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search the id of virtual device in the vinput_vdevices linked list,
|
||||||
|
* which added at vinput_alloc_vdevice().
|
||||||
|
*/
|
||||||
|
static struct vinput *vinput_get_vdevice_by_id(long id)
|
||||||
|
{
|
||||||
|
struct vinput *vinput = NULL;
|
||||||
|
struct list_head *curr;
|
||||||
|
|
||||||
|
spin_lock(&vinput_lock);
|
||||||
|
list_for_each (curr, &vinput_vdevices) {
|
||||||
|
vinput = list_entry(curr, struct vinput, list);
|
||||||
|
if (vinput && vinput->id == id)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spin_unlock(&vinput_lock);
|
||||||
|
|
||||||
|
if (vinput && vinput->id == id)
|
||||||
|
return vinput;
|
||||||
|
return ERR_PTR(-ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vinput_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct vinput *vinput = NULL;
|
||||||
|
|
||||||
|
vinput = vinput_get_vdevice_by_id(iminor(inode));
|
||||||
|
|
||||||
|
if (IS_ERR(vinput))
|
||||||
|
err = PTR_ERR(vinput);
|
||||||
|
else
|
||||||
|
file->private_data = vinput;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vinput_release(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t vinput_read(struct file *file, char __user *buffer, size_t count,
|
||||||
|
loff_t *offset)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
char buff[VINPUT_MAX_LEN + 1];
|
||||||
|
struct vinput *vinput = file->private_data;
|
||||||
|
|
||||||
|
len = vinput->type->ops->read(vinput, buff, count);
|
||||||
|
|
||||||
|
if (*offset > len)
|
||||||
|
count = 0;
|
||||||
|
else if (count + *offset > VINPUT_MAX_LEN)
|
||||||
|
count = len - *offset;
|
||||||
|
|
||||||
|
if (raw_copy_to_user(buffer, buff + *offset, count))
|
||||||
|
count = -EFAULT;
|
||||||
|
|
||||||
|
*offset += count;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t vinput_write(struct file *file, const char __user *buffer,
|
||||||
|
size_t count, loff_t *offset)
|
||||||
|
{
|
||||||
|
char buff[VINPUT_MAX_LEN + 1];
|
||||||
|
struct vinput *vinput = file->private_data;
|
||||||
|
|
||||||
|
memset(buff, 0, sizeof(char) * (VINPUT_MAX_LEN + 1));
|
||||||
|
|
||||||
|
if (count > VINPUT_MAX_LEN) {
|
||||||
|
dev_warn(&vinput->dev, "Too long. %d bytes allowed\n", VINPUT_MAX_LEN);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw_copy_from_user(buff, buffer, count))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return vinput->type->ops->send(vinput, buff, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations vinput_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.open = vinput_open,
|
||||||
|
.release = vinput_release,
|
||||||
|
.read = vinput_read,
|
||||||
|
.write = vinput_write,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void vinput_unregister_vdevice(struct vinput *vinput)
|
||||||
|
{
|
||||||
|
input_unregister_device(vinput->input);
|
||||||
|
if (vinput->type->ops->kill)
|
||||||
|
vinput->type->ops->kill(vinput);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vinput_destroy_vdevice(struct vinput *vinput)
|
||||||
|
{
|
||||||
|
/* Remove from the list first */
|
||||||
|
spin_lock(&vinput_lock);
|
||||||
|
list_del(&vinput->list);
|
||||||
|
clear_bit(vinput->id, vinput_ids);
|
||||||
|
spin_unlock(&vinput_lock);
|
||||||
|
|
||||||
|
module_put(THIS_MODULE);
|
||||||
|
|
||||||
|
kfree(vinput);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vinput_release_dev(struct device *dev)
|
||||||
|
{
|
||||||
|
struct vinput *vinput = dev_to_vinput(dev);
|
||||||
|
int id = vinput->id;
|
||||||
|
|
||||||
|
vinput_destroy_vdevice(vinput);
|
||||||
|
|
||||||
|
pr_debug("released vinput%d.\n", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct vinput *vinput_alloc_vdevice(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct vinput *vinput = kzalloc(sizeof(struct vinput), GFP_KERNEL);
|
||||||
|
|
||||||
|
try_module_get(THIS_MODULE);
|
||||||
|
|
||||||
|
memset(vinput, 0, sizeof(struct vinput));
|
||||||
|
|
||||||
|
spin_lock_init(&vinput->lock);
|
||||||
|
|
||||||
|
spin_lock(&vinput_lock);
|
||||||
|
vinput->id = find_first_zero_bit(vinput_ids, VINPUT_MINORS);
|
||||||
|
if (vinput->id >= VINPUT_MINORS) {
|
||||||
|
err = -ENOBUFS;
|
||||||
|
goto fail_id;
|
||||||
|
}
|
||||||
|
set_bit(vinput->id, vinput_ids);
|
||||||
|
list_add(&vinput->list, &vinput_vdevices);
|
||||||
|
spin_unlock(&vinput_lock);
|
||||||
|
|
||||||
|
/* allocate the input device */
|
||||||
|
vinput->input = input_allocate_device();
|
||||||
|
if (vinput->input == NULL) {
|
||||||
|
pr_err("vinput: Cannot allocate vinput input device\n");
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto fail_input_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialize device */
|
||||||
|
vinput->dev.class = &vinput_class;
|
||||||
|
vinput->dev.release = vinput_release_dev;
|
||||||
|
vinput->dev.devt = MKDEV(vinput_dev, vinput->id);
|
||||||
|
dev_set_name(&vinput->dev, DRIVER_NAME "%lu", vinput->id);
|
||||||
|
|
||||||
|
return vinput;
|
||||||
|
|
||||||
|
fail_input_dev:
|
||||||
|
spin_lock(&vinput_lock);
|
||||||
|
list_del(&vinput->list);
|
||||||
|
fail_id:
|
||||||
|
spin_unlock(&vinput_lock);
|
||||||
|
module_put(THIS_MODULE);
|
||||||
|
kfree(vinput);
|
||||||
|
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vinput_register_vdevice(struct vinput *vinput)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* register the input device */
|
||||||
|
vinput->input->name = vinput->type->name;
|
||||||
|
vinput->input->phys = "vinput";
|
||||||
|
vinput->input->dev.parent = &vinput->dev;
|
||||||
|
|
||||||
|
vinput->input->id.bustype = BUS_VIRTUAL;
|
||||||
|
vinput->input->id.product = 0x0000;
|
||||||
|
vinput->input->id.vendor = 0x0000;
|
||||||
|
vinput->input->id.version = 0x0000;
|
||||||
|
|
||||||
|
err = vinput->type->ops->init(vinput);
|
||||||
|
|
||||||
|
if (err == 0)
|
||||||
|
dev_info(&vinput->dev, "Registered virtual input %s %ld\n",
|
||||||
|
vinput->type->name, vinput->id);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t export_store(struct class *class, struct class_attribute *attr,
|
||||||
|
const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct vinput *vinput;
|
||||||
|
struct vinput_device *device;
|
||||||
|
|
||||||
|
device = vinput_get_device_by_type(buf);
|
||||||
|
if (IS_ERR(device)) {
|
||||||
|
pr_info("vinput: This virtual device isn't registered\n");
|
||||||
|
err = PTR_ERR(device);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
vinput = vinput_alloc_vdevice();
|
||||||
|
if (IS_ERR(vinput)) {
|
||||||
|
err = PTR_ERR(vinput);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
vinput->type = device;
|
||||||
|
err = device_register(&vinput->dev);
|
||||||
|
if (err < 0)
|
||||||
|
goto fail_register;
|
||||||
|
|
||||||
|
err = vinput_register_vdevice(vinput);
|
||||||
|
if (err < 0)
|
||||||
|
goto fail_register_vinput;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
|
||||||
|
fail_register_vinput:
|
||||||
|
device_unregister(&vinput->dev);
|
||||||
|
fail_register:
|
||||||
|
vinput_destroy_vdevice(vinput);
|
||||||
|
fail:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
/* This macro generates class_attr_export structure and export_store() */
|
||||||
|
static CLASS_ATTR_WO(export);
|
||||||
|
|
||||||
|
static ssize_t unexport_store(struct class *class, struct class_attribute *attr,
|
||||||
|
const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
unsigned long id;
|
||||||
|
struct vinput *vinput;
|
||||||
|
|
||||||
|
err = kstrtol(buf, 10, &id);
|
||||||
|
if (err) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
vinput = vinput_get_vdevice_by_id(id);
|
||||||
|
if (IS_ERR(vinput)) {
|
||||||
|
pr_err("vinput: No such vinput device %ld\n", id);
|
||||||
|
err = PTR_ERR(vinput);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
vinput_unregister_vdevice(vinput);
|
||||||
|
device_unregister(&vinput->dev);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
failed:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
/* This macro generates class_attr_unexport structure and unexport_store() */
|
||||||
|
static CLASS_ATTR_WO(unexport);
|
||||||
|
|
||||||
|
static struct attribute *vinput_class_attrs[] = {
|
||||||
|
&class_attr_export.attr,
|
||||||
|
&class_attr_unexport.attr,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* This macro generates vinput_class_groups structure */
|
||||||
|
ATTRIBUTE_GROUPS(vinput_class);
|
||||||
|
|
||||||
|
static struct class vinput_class = {
|
||||||
|
.name = "vinput",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.class_groups = vinput_class_groups,
|
||||||
|
};
|
||||||
|
|
||||||
|
int vinput_register(struct vinput_device *dev)
|
||||||
|
{
|
||||||
|
spin_lock(&vinput_lock);
|
||||||
|
list_add(&dev->list, &vinput_devices);
|
||||||
|
spin_unlock(&vinput_lock);
|
||||||
|
|
||||||
|
pr_info("vinput: registered new virtual input device '%s'\n", dev->name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(vinput_register);
|
||||||
|
|
||||||
|
void vinput_unregister(struct vinput_device *dev)
|
||||||
|
{
|
||||||
|
struct list_head *curr, *next;
|
||||||
|
|
||||||
|
/* Remove from the list first */
|
||||||
|
spin_lock(&vinput_lock);
|
||||||
|
list_del(&dev->list);
|
||||||
|
spin_unlock(&vinput_lock);
|
||||||
|
|
||||||
|
/* unregister all devices of this type */
|
||||||
|
list_for_each_safe (curr, next, &vinput_vdevices) {
|
||||||
|
struct vinput *vinput = list_entry(curr, struct vinput, list);
|
||||||
|
if (vinput && vinput->type == dev) {
|
||||||
|
vinput_unregister_vdevice(vinput);
|
||||||
|
device_unregister(&vinput->dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("vinput: unregistered virtual input device '%s'\n", dev->name);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(vinput_unregister);
|
||||||
|
|
||||||
|
static int __init vinput_init(void)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
pr_info("vinput: Loading virtual input driver\n");
|
||||||
|
|
||||||
|
vinput_dev = register_chrdev(0, DRIVER_NAME, &vinput_fops);
|
||||||
|
if (vinput_dev < 0) {
|
||||||
|
pr_err("vinput: Unable to allocate char dev region\n");
|
||||||
|
goto failed_alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&vinput_lock);
|
||||||
|
|
||||||
|
err = class_register(&vinput_class);
|
||||||
|
if (err < 0) {
|
||||||
|
pr_err("vinput: Unable to register vinput class\n");
|
||||||
|
goto failed_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
failed_class:
|
||||||
|
class_unregister(&vinput_class);
|
||||||
|
failed_alloc:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit vinput_end(void)
|
||||||
|
{
|
||||||
|
pr_info("vinput: Unloading virtual input driver\n");
|
||||||
|
|
||||||
|
unregister_chrdev(vinput_dev, DRIVER_NAME);
|
||||||
|
class_unregister(&vinput_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(vinput_init);
|
||||||
|
module_exit(vinput_end);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("Emulate input events");
|
45
examples/vinput.h
Normal file
45
examples/vinput.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef VINPUT_H
|
||||||
|
#define VINPUT_H
|
||||||
|
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#define VINPUT_MAX_LEN 128
|
||||||
|
#define MAX_VINPUT 32
|
||||||
|
#define VINPUT_MINORS MAX_VINPUT
|
||||||
|
|
||||||
|
#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
|
||||||
|
|
||||||
|
struct vinput_device;
|
||||||
|
|
||||||
|
struct vinput {
|
||||||
|
long id;
|
||||||
|
long devno;
|
||||||
|
long last_entry;
|
||||||
|
spinlock_t lock;
|
||||||
|
|
||||||
|
void *priv_data;
|
||||||
|
|
||||||
|
struct device dev;
|
||||||
|
struct list_head list;
|
||||||
|
struct input_dev *input;
|
||||||
|
struct vinput_device *type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vinput_ops {
|
||||||
|
int (*init)(struct vinput *);
|
||||||
|
int (*kill)(struct vinput *);
|
||||||
|
int (*send)(struct vinput *, char *, int);
|
||||||
|
int (*read)(struct vinput *, char *, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vinput_device {
|
||||||
|
char name[16];
|
||||||
|
struct list_head list;
|
||||||
|
struct vinput_ops *ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
int vinput_register(struct vinput_device *dev);
|
||||||
|
void vinput_unregister(struct vinput_device *dev);
|
||||||
|
|
||||||
|
#endif
|
106
examples/vkbd.c
Normal file
106
examples/vkbd.c
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#include "vinput.h"
|
||||||
|
|
||||||
|
#define VINPUT_KBD "vkbd"
|
||||||
|
#define VINPUT_RELEASE 0
|
||||||
|
#define VINPUT_PRESS 1
|
||||||
|
|
||||||
|
static unsigned short vkeymap[KEY_MAX];
|
||||||
|
|
||||||
|
static int vinput_vkbd_init(struct vinput *vinput)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Set up the input bitfield */
|
||||||
|
vinput->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
|
||||||
|
vinput->input->keycodesize = sizeof(unsigned short);
|
||||||
|
vinput->input->keycodemax = KEY_MAX;
|
||||||
|
vinput->input->keycode = vkeymap;
|
||||||
|
|
||||||
|
for (i = 0; i < KEY_MAX; i++)
|
||||||
|
set_bit(vkeymap[i], vinput->input->keybit);
|
||||||
|
|
||||||
|
/* vinput will help us allocate new input device structure via
|
||||||
|
* input_allocate_device(). So, we can register it straightforwardly.
|
||||||
|
*/
|
||||||
|
return input_register_device(vinput->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vinput_vkbd_read(struct vinput *vinput, char *buff, int len)
|
||||||
|
{
|
||||||
|
spin_lock(&vinput->lock);
|
||||||
|
len = snprintf(buff, len, "%+ld\n", vinput->last_entry);
|
||||||
|
spin_unlock(&vinput->lock);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vinput_vkbd_send(struct vinput *vinput, char *buff, int len)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
long key = 0;
|
||||||
|
short type = VINPUT_PRESS;
|
||||||
|
|
||||||
|
/* Determine which event was received (press or release)
|
||||||
|
* and store the state.
|
||||||
|
*/
|
||||||
|
if (buff[0] == '+')
|
||||||
|
ret = kstrtol(buff + 1, 10, &key);
|
||||||
|
else
|
||||||
|
ret = kstrtol(buff, 10, &key);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&vinput->dev, "error during kstrtol: -%d\n", ret);
|
||||||
|
spin_lock(&vinput->lock);
|
||||||
|
vinput->last_entry = key;
|
||||||
|
spin_unlock(&vinput->lock);
|
||||||
|
|
||||||
|
if (key < 0) {
|
||||||
|
type = VINPUT_RELEASE;
|
||||||
|
key = -key;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&vinput->dev, "Event %s code %ld\n",
|
||||||
|
(type == VINPUT_RELEASE) ? "VINPUT_RELEASE" : "VINPUT_PRESS", key);
|
||||||
|
|
||||||
|
/* Report the state received to input subsystem. */
|
||||||
|
input_report_key(vinput->input, key, type);
|
||||||
|
/* Tell input subsystem that it finished the report. */
|
||||||
|
input_sync(vinput->input);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct vinput_ops vkbd_ops = {
|
||||||
|
.init = vinput_vkbd_init,
|
||||||
|
.send = vinput_vkbd_send,
|
||||||
|
.read = vinput_vkbd_read,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct vinput_device vkbd_dev = {
|
||||||
|
.name = VINPUT_KBD,
|
||||||
|
.ops = &vkbd_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init vkbd_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < KEY_MAX; i++)
|
||||||
|
vkeymap[i] = i;
|
||||||
|
return vinput_register(&vkbd_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit vkbd_end(void)
|
||||||
|
{
|
||||||
|
vinput_unregister(&vkbd_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(vkbd_init);
|
||||||
|
module_exit(vkbd_end);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("Emulate keyboard input events through /dev/vinput");
|
82
lkmpg.tex
82
lkmpg.tex
|
@ -1857,6 +1857,88 @@ Here is an example of symmetrically encrypting a string using the AES algorithm
|
||||||
|
|
||||||
\samplec{examples/cryptosk.c}
|
\samplec{examples/cryptosk.c}
|
||||||
|
|
||||||
|
\section{Virtual Input Device Driver}
|
||||||
|
\label{sec:vinput}
|
||||||
|
The input device driver is a module that provides a way to communicate with the interaction device via the event.
|
||||||
|
For example, the keyboard can send the press or release event to tell the kernel what we want to do.
|
||||||
|
The input device driver will allocate a new input structure with \cpp|input_allocate_device()| and sets up input bitfields, device id, version, etc.
|
||||||
|
After that, registers it by calling \cpp|input_register_device()|.
|
||||||
|
|
||||||
|
Here is an example, vinput,
|
||||||
|
It is an API to allow easy development of virtual input drivers.
|
||||||
|
The drivers needs to export a \cpp|vinput_device()| that contains the virtual device name and \cpp|vinput_ops| structure that describes:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item the init function: \cpp|init()|
|
||||||
|
\item the input event injection function: \cpp|send()|
|
||||||
|
\item the readback function: \cpp|read()|
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Then using \cpp|vinput_register_device()| and \cpp|vinput_unregister_device()| will add a new device to the list of support virtual input devices.
|
||||||
|
|
||||||
|
\begin{code}
|
||||||
|
int init(struct vinput *);
|
||||||
|
\end{code}
|
||||||
|
|
||||||
|
This function is passed a \cpp|struct vinput| already initialized with an allocated \cpp|struct input_dev|.
|
||||||
|
The \cpp|init()| function is responsible for initializing the capabilities of the input device and register it.
|
||||||
|
|
||||||
|
\begin{code}
|
||||||
|
int send(struct vinput *, char *, int);
|
||||||
|
\end{code}
|
||||||
|
|
||||||
|
This function will receive a user string to interpret and inject the event using the \cpp|input_report_XXXX| or \cpp|input_event| call.
|
||||||
|
The string is already copied from user.
|
||||||
|
|
||||||
|
\begin{code}
|
||||||
|
int read(struct vinput *, char *, int);
|
||||||
|
\end{code}
|
||||||
|
|
||||||
|
This function is used for debugging and should fill the buffer parameter with the last event sent in the virtual input device format.
|
||||||
|
The buffer will then be copied to user.
|
||||||
|
|
||||||
|
vinput devices are created and destroyed using sysfs.
|
||||||
|
And, event injection is done through a \verb|/dev| node.
|
||||||
|
The device name will be used by the userland to export a new virtual input device.
|
||||||
|
To create a \verb|vinputX| sysfs entry and \verb|/dev| node.
|
||||||
|
|
||||||
|
\begin{codebash}
|
||||||
|
echo "vkbd" | sudo tee /sys/class/vinput/export
|
||||||
|
\end{codebash}
|
||||||
|
|
||||||
|
To unexport the device, just echo its id in unexport:
|
||||||
|
|
||||||
|
\begin{codebash}
|
||||||
|
echo "0" | sudo tee /sys/class/vinput/unexport
|
||||||
|
\end{codebash}
|
||||||
|
|
||||||
|
\samplec{examples/vinput.h}
|
||||||
|
\samplec{examples/vinput.c}
|
||||||
|
|
||||||
|
Here the virtual keyboard is one of example to use vinput.
|
||||||
|
It supports all \cpp|KEY_MAX| keycodes.
|
||||||
|
The injection format is the \cpp|KEY_CODE| such as defined in \src{include/linux/input.h}.
|
||||||
|
A positive value means \cpp|KEY_PRESS| while a negative value is a \cpp|KEY_RELEASE|.
|
||||||
|
The keyboard supports repetition when the key stays pressed for too long.
|
||||||
|
The following demonstrates how simulation work.
|
||||||
|
|
||||||
|
Simulate a key press on "g" (\cpp|KEY_G| = 34):
|
||||||
|
|
||||||
|
\begin{codebash}
|
||||||
|
echo "+34" | sudo tee /dev/vinput0
|
||||||
|
\end{codebash}
|
||||||
|
|
||||||
|
Simulate a key release on "g" (\cpp|KEY_G| = 34:)
|
||||||
|
|
||||||
|
\begin{codebash}
|
||||||
|
echo "-34" | sudo tee /dev/vinput0
|
||||||
|
\end{codebash}
|
||||||
|
|
||||||
|
\samplec{examples/vkbd.c}
|
||||||
|
|
||||||
|
% TODO: Add description of attribute
|
||||||
|
% TODO: Add vts.c and vmouse.c example
|
||||||
|
|
||||||
\section{Standardizing the interfaces: The Device Model}
|
\section{Standardizing the interfaces: The Device Model}
|
||||||
\label{sec:device_model}
|
\label{sec:device_model}
|
||||||
Up to this point we have seen all kinds of modules doing all kinds of things, but there was no consistency in their interfaces with the rest of the kernel.
|
Up to this point we have seen all kinds of modules doing all kinds of things, but there was no consistency in their interfaces with the rest of the kernel.
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
Arush Sharma,<46960231+arushsharma24@users.noreply.github.com>
|
Arush Sharma,<46960231+arushsharma24@users.noreply.github.com>
|
||||||
asas1asas200,<asas1asas200@gmail.com>
|
asas1asas200,<asas1asas200@gmail.com>
|
||||||
Benno Bielmeier,<32938211+bbenno@users.noreply.github.com>
|
Benno Bielmeier,<32938211+bbenno@users.noreply.github.com>
|
||||||
|
Bob Lee,<defru04002@gmail.com>
|
||||||
Brad Baker,<brad@brdbkr.com>
|
Brad Baker,<brad@brdbkr.com>
|
||||||
ccs100203,<ccs100203@gmail.com>
|
ccs100203,<ccs100203@gmail.com>
|
||||||
Chih-Yu Chen,<34228283+chihyu1206@users.noreply.github.com>
|
Chih-Yu Chen,<34228283+chihyu1206@users.noreply.github.com>
|
||||||
|
Ching-Hua (Vivian) Lin,<jkrvivian@gmail.com>
|
||||||
ChinYikMing,<yikming2222@gmail.com>
|
ChinYikMing,<yikming2222@gmail.com>
|
||||||
Cyril Brulebois,<cyril@debamax.com>
|
Cyril Brulebois,<cyril@debamax.com>
|
||||||
Daniele Paolo Scarpazza,<>
|
Daniele Paolo Scarpazza,<>
|
||||||
|
@ -22,10 +24,15 @@ Hsin-Hsiang Peng,<hsinspeng@gmail.com>
|
||||||
Ignacio Martin,<>
|
Ignacio Martin,<>
|
||||||
JianXing Wu,<fdgkhdkgh@gmail.com>
|
JianXing Wu,<fdgkhdkgh@gmail.com>
|
||||||
linD026,<shiyn.lin@gmail.com>,<0086d026@email.ntou.edu.tw>,<66012716+linD026@users.noreply.github.com>
|
linD026,<shiyn.lin@gmail.com>,<0086d026@email.ntou.edu.tw>,<66012716+linD026@users.noreply.github.com>
|
||||||
|
lyctw,<lyctw.ee@gmail.com>
|
||||||
|
manbing,<manbing3@gmail.com>
|
||||||
Marconi Jiang,<marconi1964@yahoo.com>
|
Marconi Jiang,<marconi1964@yahoo.com>
|
||||||
|
mengxinayan,<31788564+mengxinayan@users.noreply.github.com>
|
||||||
RinHizakura,<s921975628@gmail.com>
|
RinHizakura,<s921975628@gmail.com>
|
||||||
Roman Lakeev,<>
|
Roman Lakeev,<>
|
||||||
Stacy Prowell,<sprowell@gmail.com>
|
Stacy Prowell,<sprowell@gmail.com>
|
||||||
|
Steven Lung,<1030steven@gmail.com>
|
||||||
|
Tristan Lelong,<tristan.lelong@blunderer.org>
|
||||||
Tucker Polomik,<tucker.polomik@inficon.com>
|
Tucker Polomik,<tucker.polomik@inficon.com>
|
||||||
VxTeemo,<tcccvvv123@gmail.com>
|
VxTeemo,<tcccvvv123@gmail.com>
|
||||||
Wei-Lun Tsai,<alan23273850@gmail.com>
|
Wei-Lun Tsai,<alan23273850@gmail.com>
|
||||||
|
|
|
@ -5,3 +5,4 @@ Francois Audeon,<>
|
||||||
Horst Schirmeier,<>
|
Horst Schirmeier,<>
|
||||||
Ignacio Martin,<>
|
Ignacio Martin,<>
|
||||||
Roman Lakeev,<>
|
Roman Lakeev,<>
|
||||||
|
Tristan Lelong,<tristan.lelong@blunderer.org>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user