completed the code

This commit is contained in:
paul-loedige 2022-11-28 11:12:08 +09:00
parent 3e5ec66892
commit 809bfbd627
7 changed files with 610 additions and 0 deletions

10
Code/Makefile Normal file
View File

@ -0,0 +1,10 @@
obj-m += vinput.o
obj-m += vkbd.o
PWD := $(CURDIR)
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

403
Code/vinput.c Normal file
View File

@ -0,0 +1,403 @@
/*
* vinput.c
*/
#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");
err = vinput_dev;
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");

49
Code/vinput.h Normal file
View File

@ -0,0 +1,49 @@
/*
* vinput.h
*/
#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

110
Code/vkbd.c Normal file
View File

@ -0,0 +1,110 @@
/*
* vkbd.c
*/
#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");

View File

@ -1,2 +1,9 @@
# Homework_Lesson_8 # Homework_Lesson_8
## Assignment
Create a virtual keyboard using the kernel module,
which simulates a key press on a virtual input device /dev/vinput0.
Echo some keycode to the virtual input device and get the key message on the standard output.
Compile and run the program.
Submit the source code and the screen shots of the standard output and the kernel log message,
respectively, when the keycode is echoed to the virtual input device.

13
output.txt Normal file
View File

@ -0,0 +1,13 @@
vkbd
+36
-36
0
==================================================
[Mon Nov 28 11:09:58 2022] vinput: Loading virtual input driver
[Mon Nov 28 11:09:58 2022] vinput: registered new virtual input device 'vkbd'
[Mon Nov 28 11:09:58 2022] input: vkbd as /devices/virtual/vinput/vinput0/input19
[Mon Nov 28 11:09:58 2022] vinput vinput0: Registered virtual input vkbd 0
[Mon Nov 28 11:09:59 2022] vinput vinput0: Event VINPUT_PRESS code 36
[Mon Nov 28 11:10:04 2022] vinput vinput0: Event VINPUT_RELEASE code 36
[Mon Nov 28 11:10:05 2022] vinput: unregistered virtual input device 'vkbd'
[Mon Nov 28 11:10:05 2022] vinput: Unloading virtual input driver

18
run.sh Normal file
View File

@ -0,0 +1,18 @@
cd ./Code
make
sudo insmod vinput.ko
sudo insmod vkbd.ko
echo "vkbd" | sudo tee /sys/class/vinput/export > ../output.txt
sleep 1
echo "+36" | sudo tee /dev/vinput0 >> ../output.txt
sleep 5
echo "-36" | sudo tee /dev/vinput0 >> ../output.txt
sleep 1
echo "0" | sudo tee /sys/class/vinput/unexport >> ../output.txt
echo "==================================================" >> ../output.txt
sudo rmmod vkbd
sudo rmmod vinput
sleep 5
cd ../
sudo dmesg -T -l info | tail -8 >> output.txt
echo "Done"