diff --git a/index.html b/index.html index 82e8309f..9efb3f28 100644 --- a/index.html +++ b/index.html @@ -18,7 +18,7 @@
The Linux Kernel Module Programming Guide is a free book; you may reproduce @@ -112,10 +113,10 @@
If you publish or distribute this book commercially, donations, royalties, and/or @@ -189,11 +190,11 @@
On Arch Linux:
1sudo pacman -S gcc kmod-
-
++
To discover what modules are already loaded within your current kernel use the command
+ It is possible that in future tasklets may be replaced by threaded irqs. However,
+discussion about that has been ongoing since 2007 (Eliminating tasklets), so do
+not hold your breath. See the section 15.1 if you wish to avoid the tasklet
+debate.
+
Here is an example tasklet module. The
+ Here is an example tasklet module. The
So with this example loaded So with this example loaded Although tasklet is easy to use, it comes with several drawbacks, and developers are
+ Although tasklet is easy to use, it comes with several drawbacks, and developers are
discussing about getting rid of tasklet in linux kernel. The tasklet callback
runs in atomic context, inside a software interrupt, meaning that it cannot
sleep or access user-space data, so not all work can be done in a tasklet
handler. Also, the kernel only allows one instance of any given tasklet to be
running at any given time; multiple different tasklet callbacks can run in
parallel.
- In recent kernels, tasklets can be replaced by workqueues, timers, or threaded
+ In recent kernels, tasklets can be replaced by workqueues, timers, or threaded
interrupts.1
While the removal of tasklets remains a longer-term goal, the current kernel contains more
than a hundred uses of tasklets. Now developers are proceeding with the API changes and
the macro
+
To add a task to the scheduler we can use a workqueue. The kernel then uses the
+ To add a task to the scheduler we can use a workqueue. The kernel then uses the
Completely Fair Scheduler (CFS) to execute work within the queue.
lsmod
@@ -4877,10 +4878,14 @@
14
14.1 Tasklets
- tasklet_fn
function runs for a few seconds. In the meantime, execution of the
example_tasklet_init
@@ -4932,7 +4937,7 @@
14.1
42
43MODULE_DESCRIPTION("Tasklet example");
44MODULE_LICENSE("GPL");
-
dmesg
+
dmesg
should show:
@@ -4944,23 +4949,23 @@ 14.1
Example tasklet init continues...
Example tasklet ends
-
DECLARE_TASKLET_OLD
exists for compatibility. For further information, see https://lwn.net/Articles/830964/.
-14.2 Work queues
-14.2
+
+
Except for the last chapter, everything we did in the kernel so far we have done as a +
Except for the last chapter, everything we did in the kernel so far we have done as a
response to a process asking for it, either by dealing with a special file, sending an
ioctl()
, or issuing a system call. But the job of the kernel is not just to respond to process
requests. Another job, which is every bit as important, is to speak to the hardware
connected to the machine.
-
There are two types of interaction between the CPU and the rest of the +
There are two types of interaction between the CPU and the rest of the computer’s hardware. The first type is when the CPU gives orders to the hardware, the other is when the hardware needs to tell the CPU something. The second, called interrupts, is much harder to implement because it has to be dealt with when convenient for the hardware, not the CPU. Hardware devices typically have a very small amount of RAM, and if you do not read their information when available, it is lost. -
Under Linux, hardware interrupts are called IRQ’s (Interrupt ReQuests). There +
Under Linux, hardware interrupts are called IRQ’s (Interrupt ReQuests). There are two types of IRQ’s, short and long. A short IRQ is one which is expected to take a very short period of time, during which the rest of the machine will be blocked and no other interrupts will be handled. A long IRQ is one which can take longer, and during which other interrupts may occur (but not interrupts from the same device). If at all possible, it is better to declare an interrupt handler to be long. -
When the CPU receives an interrupt, it stops whatever it is doing (unless it is +
When the CPU receives an interrupt, it stops whatever it is doing (unless it is processing a more important interrupt, in which case it will deal with this one only when the more important one is done), saves certain parameters on the stack and calls the interrupt handler. This means that certain things are not allowed in the @@ -5038,10 +5043,10 @@
The way to implement this is to call +
The way to implement this is to call
request_irq()
to get your interrupt handler called when the relevant IRQ is received.
-
In practice IRQ handling can be a bit more complex. Hardware is often designed +
In practice IRQ handling can be a bit more complex. Hardware is often designed in a way that chains two interrupt controllers, so that all the IRQs from interrupt controller B are cascaded to a certain IRQ from interrupt controller A. Of course, that requires that the kernel finds out which IRQ it really was @@ -5058,11 +5063,11 @@
This function receives the IRQ number, the name of the function, flags, a name +
This function receives the IRQ number, the name of the function, flags, a name for /proc/interrupts and a parameter to be passed to the interrupt handler. Usually there is a certain number of IRQs available. How many IRQs there are is hardware-dependent. -
The flags can be used for specify behaviors of the IRQ. For example, use +
The flags can be used for specify behaviors of the IRQ. For example, use
IRQF_SHARED
to indicate you are willing to share the IRQ with other interrupt handlers
(usually because a number of hardware devices sit on the same IRQ); use the
@@ -5076,16 +5081,16 @@
IRQF
flags are in use. This function will only succeed if there is not already a handler on
this IRQ, or if you are both willing to share.
-+
Many popular single board computers, such as Raspberry Pi or Beagleboards, have a +
Many popular single board computers, such as Raspberry Pi or Beagleboards, have a bunch of GPIO pins. Attaching buttons to those and then having a button press do something is a classic case in which you might need to use interrupts, so that instead of having the CPU waste time and battery power polling for a change in input state, it is better for the input to trigger the CPU to then run a particular handling function. -
Here is an example where buttons are connected to GPIO numbers 17 and 18 and +
Here is an example where buttons are connected to GPIO numbers 17 and 18 and an LED is connected to GPIO 4. You can change those numbers to whatever is appropriate for your board.
@@ -5235,17 +5240,17 @@
+
Suppose you want to do a bunch of stuff inside of an interrupt routine. A common +
Suppose you want to do a bunch of stuff inside of an interrupt routine. A common way to do that without rendering the interrupt unavailable for a significant duration is to combine it with a tasklet. This pushes the bulk of the work off into the scheduler. -
The example below modifies the previous example to also run an additional task +
The example below modifies the previous example to also run an additional task when an interrupt is triggered.
@@ -5417,10 +5422,206 @@
+
-
The input device driver is a module that provides a way to communicate +
Threaded IRQ is a mechanism to organize both top-half and bottom-half
+of an IRQ at once. A threaded IRQ splits the one handler in
+ request_irq()
+
into two: one for the top-half, the other for the bottom-half. The
+ request_threaded_irq()
+
is the function for using threaded IRQs. Two handlers are registered at once in the
+ request_threaded_irq()
+
.
+
Those two handlers run in different context. The top-half handler runs
+in interrupt context. It’s the equivalence of the handler passed to the
+ request_irq()
+
. The bottom-half handler on the other hand runs in its own thread. This
+thread is created on registration of a threaded IRQ. Its sole purpose is to run
+this bottom-half handler. This is where a threaded IRQ is “threaded”. If
+ IRQ_WAKE_THREAD
+
is returned by the top-half handler, that bottom-half serving thread will wake up.
+The thread then runs the bottom-half handler.
+
Here is an example of how to do the same thing as before, with top and bottom +halves, but using threads. +
+
+1/* +2 * bh_thread.c - Top and bottom half interrupt handling +3 * +4 * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de) +5 * from: +6 * https://github.com/wendlers/rpi-kmod-samples +7 * +8 * Press one button to turn on a LED and another to turn it off +9 */ +10 +11#include <linux/module.h> +12#include <linux/kernel.h> +13#include <linux/gpio.h> +14#include <linux/delay.h> +15#include <linux/interrupt.h> +16 +17static int button_irqs[] = { -1, -1 }; +18 +19/* Define GPIOs for LEDs. +20 * FIXME: Change the numbers for the GPIO on your board. +21 */ +22static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } }; +23 +24/* Define GPIOs for BUTTONS +25 * FIXME: Change the numbers for the GPIO on your board. +26 */ +27static struct gpio buttons[] = { +28 { 17, GPIOF_IN, "LED 1 ON BUTTON" }, +29 { 18, GPIOF_IN, "LED 1 OFF BUTTON" }, +30}; +31 +32/* This happens immediately, when the IRQ is triggered */ +33static irqreturn_t button_top_half(int irq, void *ident) +34{ +35 return IRQ_WAKE_THREAD; +36} +37 +38/* This can happen at leisure, freeing up IRQs for other high priority task */ +39static irqreturn_t button_bottom_half(int irq, void *ident) +40{ +41 pr_info("Bottom half task starts\n"); +42 mdelay(500); /* do something which takes a while */ +43 pr_info("Bottom half task ends\n"); +44 return IRQ_HANDLED; +45} +46 +47static int __init bottomhalf_init(void) +48{ +49 int ret = 0; +50 +51 pr_info("%s\n", __func__); +52 +53 /* register LED gpios */ +54 ret = gpio_request_array(leds, ARRAY_SIZE(leds)); +55 +56 if (ret) { +57 pr_err("Unable to request GPIOs for LEDs: %d\n", ret); +58 return ret; +59 } +60 +61 /* register BUTTON gpios */ +62 ret = gpio_request_array(buttons, ARRAY_SIZE(buttons)); +63 +64 if (ret) { +65 pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret); +66 goto fail1; +67 } +68 +69 pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio)); +70 +71 ret = gpio_to_irq(buttons[0].gpio); +72 +73 if (ret < 0) { +74 pr_err("Unable to request IRQ: %d\n", ret); +75 goto fail2; +76 } +77 +78 button_irqs[0] = ret; +79 +80 pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]); +81 +82 ret = request_threaded_irq( +83 button_irqs[0], button_top_half, button_bottom_half, +84 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button1", &buttons[0]); +85 +86 if (ret) { +87 pr_err("Unable to request IRQ: %d\n", ret); +88 goto fail2; +89 } +90 +91 ret = gpio_to_irq(buttons[1].gpio); +92 +93 if (ret < 0) { +94 pr_err("Unable to request IRQ: %d\n", ret); +95 goto fail2; +96 } +97 +98 button_irqs[1] = ret; +99 +100 pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]); +101 +102 ret = request_threaded_irq( +103 button_irqs[1], button_top_half, button_bottom_half, +104 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button2", &buttons[1]); +105 +106 if (ret) { +107 pr_err("Unable to request IRQ: %d\n", ret); +108 goto fail3; +109 } +110 +111 return 0; +112 +113/* cleanup what has been setup so far */ +114fail3: +115 free_irq(button_irqs[0], NULL); +116 +117fail2: +118 gpio_free_array(buttons, ARRAY_SIZE(leds)); +119 +120fail1: +121 gpio_free_array(leds, ARRAY_SIZE(leds)); +122 +123 return ret; +124} +125 +126static void __exit bottomhalf_exit(void) +127{ +128 int i; +129 +130 pr_info("%s\n", __func__); +131 +132 /* free irqs */ +133 free_irq(button_irqs[0], NULL); +134 free_irq(button_irqs[1], NULL); +135 +136 /* turn all LEDs off */ +137 for (i = 0; i < ARRAY_SIZE(leds); i++) +138 gpio_set_value(leds[i].gpio, 0); +139 +140 /* unregister */ +141 gpio_free_array(leds, ARRAY_SIZE(leds)); +142 gpio_free_array(buttons, ARRAY_SIZE(buttons)); +143} +144 +145module_init(bottomhalf_init); +146module_exit(bottomhalf_exit); +147 +148MODULE_LICENSE("GPL"); +149MODULE_DESCRIPTION("Interrupt with top and bottom half");+
A threaded IRQ is registered using request_threaded_irq()
+
. This function only takes one additional parameter than the
+ request_irq()
+
– the bottom-half handling function that runs in its own thread. In this example it is
+the button_bottom_half()
+
. Usage of other parameters are the same as
+ request_irq()
+
+
+
+
.
+
Presence of both handlers is not mandatory. If either of them is not needed, pass
+the NULL
+
instead. A NULL
+
top-half handler implies that no action is taken except to wake up the
+bottom-half serving thread, which runs the bottom-half handler. Similarly, a
+ NULL
+
bottom-half handler effectively acts as if
+ request_irq()
+
were used. In fact, this is how request_irq()
+
is implemented.
+
Note that passing NULL
+
to both handlers is considered an error and will make registration fail.
+
+
+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 @@ -5428,7 +5629,7 @@
input_register_device()
.
-Here is an example, vinput, It is an API to allow easy +
Here is an example, vinput, It is an API to allow easy
development of virtual input drivers. The drivers needs to export a
vinput_device()
that contains the virtual device name and
@@ -5440,546 +5641,546 @@
send()
-
+
read()
Then using Then using
This function is passed a This function is passed a
This function will receive a user string to interpret and inject the event using the
+ This function will receive a user string to interpret and inject the event using the
This function is used for debugging and should fill the buffer parameter with the
+ 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
+ vinput devices are created and destroyed using sysfs. And, event injection is done
through a /dev node. The device name will be used by the userland to export a new
virtual input device.
- The The
In vinput.c, the macro In vinput.c, the macro To create a vinputX sysfs entry and /dev node.
+ To create a vinputX sysfs entry and /dev node.
To unexport the device, just echo its id in unexport:
+ To unexport the device, just echo its id in unexport:
Here the virtual keyboard is one of example to use vinput. It supports all
+ Here the virtual keyboard is one of example to use vinput. It supports all
Simulate a key press on "g" ( Simulate a key press on "g" (
Simulate a key release on "g" ( Simulate a key release on "g" (
-
- Up to this point we have seen all kinds of modules doing all kinds of things, but there
+
+ 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. To impose some
consistency such that there is at minimum a standardized way to start, suspend and
resume a device model was added. An example is shown below, and you can
@@ -6124,111 +6325,111 @@
-
- Sometimes you might want your code to run as quickly as possible,
+
+
+ Sometimes you might want your code to run as quickly as possible,
especially if it is handling an interrupt or doing something which might
cause noticeable latency. If your code contains boolean conditions and if
you know that the conditions are almost always likely to evaluate as either
@@ -6240,40 +6441,40 @@ vinput_register_device()
+
vinput_register_device()
and vinput_unregister_device()
will add a new device to the list of support virtual input devices.
1int init(struct vinput *);
- struct vinput
-
already initialized with an allocated struct input_dev
+
1int init(struct vinput *);
+ struct vinput
+
already initialized with an allocated struct input_dev
. The init()
function is responsible for initializing the capabilities of the input device and register
it.
1int send(struct vinput *, char *, int);
-1int send(struct vinput *, char *, int);
+ input_report_XXXX
or input_event
call. The string is already copied from user.
1int read(struct vinput *, char *, int);
-1int read(struct vinput *, char *, int);
+ class_attribute
+
class_attribute
structure is similar to other attribute types we talked about in section 8:
1struct class_attribute {
-2 struct attribute attr;
-3 ssize_t (*show)(struct class *class, struct class_attribute *attr,
-4 char *buf);
-5 ssize_t (*store)(struct class *class, struct class_attribute *attr,
-6 const char *buf, size_t count);
-7};
- CLASS_ATTR_WO(export/unexport)
-
defined in include/linux/device.h (in this case, device.h is included in include/linux/input.h)
-will generate the class_attribute
-
structures which are named class_attr_export/unexport. Then, put them into
+ 1struct class_attribute {
+2 struct attribute attr;
+3 ssize_t (*show)(struct class *class, struct class_attribute *attr,
+4 char *buf);
+5 ssize_t (*store)(struct class *class, struct class_attribute *attr,
+6 const char *buf, size_t count);
+7};
+ CLASS_ATTR_WO(export/unexport)
+
defined in include/linux/device.h (in this case, device.h is included in include/linux/input.h)
+will generate the class_attribute
+
structures which are named class_attr_export/unexport. Then, put them into
vinput_class_attrs
array and the macro ATTRIBUTE_GROUPS(vinput_class)
-
will generate the struct attribute_group vinput_class_group
+
will generate the struct attribute_group vinput_class_group
that should be assigned in vinput_class
. Finally, call class_register(&vinput_class)
to create attributes in sysfs.
-1echo "vkbd" | sudo tee /sys/class/vinput/export
-1echo "vkbd" | sudo tee /sys/class/vinput/export
+1echo "0" | sudo tee /sys/class/vinput/unexport
+ 1echo "0" | sudo tee /sys/class/vinput/unexport
1/*
-2 * vinput.h
-3 */
-4
-5#ifndef VINPUT_H
-6#define VINPUT_H
-7
-8#include <linux/input.h>
-9#include <linux/spinlock.h>
-10
-11#define VINPUT_MAX_LEN 128
-12#define MAX_VINPUT 32
-13#define VINPUT_MINORS MAX_VINPUT
-14
-15#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
-16
-17struct vinput_device;
-18
-19struct vinput {
-20 long id;
-21 long devno;
-22 long last_entry;
-23 spinlock_t lock;
-24
-25 void *priv_data;
-26
-27 struct device dev;
-28 struct list_head list;
-29 struct input_dev *input;
-30 struct vinput_device *type;
-31};
-32
-33struct vinput_ops {
-34 int (*init)(struct vinput *);
-35 int (*kill)(struct vinput *);
-36 int (*send)(struct vinput *, char *, int);
-37 int (*read)(struct vinput *, char *, int);
-38};
-39
-40struct vinput_device {
-41 char name[16];
-42 struct list_head list;
-43 struct vinput_ops *ops;
-44};
-45
-46int vinput_register(struct vinput_device *dev);
-47void vinput_unregister(struct vinput_device *dev);
-48
-49#endif
+ 1/*
+2 * vinput.h
+3 */
+4
+5#ifndef VINPUT_H
+6#define VINPUT_H
+7
+8#include <linux/input.h>
+9#include <linux/spinlock.h>
+10
+11#define VINPUT_MAX_LEN 128
+12#define MAX_VINPUT 32
+13#define VINPUT_MINORS MAX_VINPUT
+14
+15#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
+16
+17struct vinput_device;
+18
+19struct vinput {
+20 long id;
+21 long devno;
+22 long last_entry;
+23 spinlock_t lock;
+24
+25 void *priv_data;
+26
+27 struct device dev;
+28 struct list_head list;
+29 struct input_dev *input;
+30 struct vinput_device *type;
+31};
+32
+33struct vinput_ops {
+34 int (*init)(struct vinput *);
+35 int (*kill)(struct vinput *);
+36 int (*send)(struct vinput *, char *, int);
+37 int (*read)(struct vinput *, char *, int);
+38};
+39
+40struct vinput_device {
+41 char name[16];
+42 struct list_head list;
+43 struct vinput_ops *ops;
+44};
+45
+46int vinput_register(struct vinput_device *dev);
+47void vinput_unregister(struct vinput_device *dev);
+48
+49#endif
1/*
-2 * vinput.c
-3 */
-4
-5#include <linux/cdev.h>
-6#include <linux/input.h>
-7#include <linux/module.h>
-8#include <linux/slab.h>
-9#include <linux/spinlock.h>
-10#include <linux/version.h>
-11
-12#include <asm/uaccess.h>
-13
-14#include "vinput.h"
-15
-16#define DRIVER_NAME "vinput"
-17
-18#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
-19
-20static DECLARE_BITMAP(vinput_ids, VINPUT_MINORS);
-21
-22static LIST_HEAD(vinput_devices);
-23static LIST_HEAD(vinput_vdevices);
-24
-25static int vinput_dev;
-26static struct spinlock vinput_lock;
-27static struct class vinput_class;
-28
-29/* Search the name of vinput device in the vinput_devices linked list,
-30 * which added at vinput_register().
-31 */
-32static struct vinput_device *vinput_get_device_by_type(const char *type)
-33{
-34 int found = 0;
-35 struct vinput_device *vinput;
-36 struct list_head *curr;
-37
-38 spin_lock(&vinput_lock);
-39 list_for_each (curr, &vinput_devices) {
-40 vinput = list_entry(curr, struct vinput_device, list);
-41 if (vinput && strncmp(type, vinput->name, strlen(vinput->name)) == 0) {
-42 found = 1;
-43 break;
-44 }
-45 }
-46 spin_unlock(&vinput_lock);
-47
-48 if (found)
-49 return vinput;
-50 return ERR_PTR(-ENODEV);
-51}
-52
-53/* Search the id of virtual device in the vinput_vdevices linked list,
-54 * which added at vinput_alloc_vdevice().
-55 */
-56static struct vinput *vinput_get_vdevice_by_id(long id)
-57{
-58 struct vinput *vinput = NULL;
-59 struct list_head *curr;
-60
-61 spin_lock(&vinput_lock);
-62 list_for_each (curr, &vinput_vdevices) {
-63 vinput = list_entry(curr, struct vinput, list);
-64 if (vinput && vinput->id == id)
-65 break;
-66 }
-67 spin_unlock(&vinput_lock);
-68
-69 if (vinput && vinput->id == id)
-70 return vinput;
-71 return ERR_PTR(-ENODEV);
-72}
-73
-74static int vinput_open(struct inode *inode, struct file *file)
-75{
-76 int err = 0;
-77 struct vinput *vinput = NULL;
-78
-79 vinput = vinput_get_vdevice_by_id(iminor(inode));
-80
-81 if (IS_ERR(vinput))
-82 err = PTR_ERR(vinput);
-83 else
-84 file->private_data = vinput;
-85
-86 return err;
-87}
-88
-89static int vinput_release(struct inode *inode, struct file *file)
-90{
-91 return 0;
-92}
-93
-94static ssize_t vinput_read(struct file *file, char __user *buffer, size_t count,
-95 loff_t *offset)
-96{
-97 int len;
-98 char buff[VINPUT_MAX_LEN + 1];
-99 struct vinput *vinput = file->private_data;
-100
-101 len = vinput->type->ops->read(vinput, buff, count);
-102
-103 if (*offset > len)
-104 count = 0;
-105 else if (count + *offset > VINPUT_MAX_LEN)
-106 count = len - *offset;
-107
-108 if (raw_copy_to_user(buffer, buff + *offset, count))
-109 count = -EFAULT;
-110
-111 *offset += count;
-112
-113 return count;
-114}
-115
-116static ssize_t vinput_write(struct file *file, const char __user *buffer,
-117 size_t count, loff_t *offset)
-118{
-119 char buff[VINPUT_MAX_LEN + 1];
-120 struct vinput *vinput = file->private_data;
-121
-122 memset(buff, 0, sizeof(char) * (VINPUT_MAX_LEN + 1));
-123
-124 if (count > VINPUT_MAX_LEN) {
-125 dev_warn(&vinput->dev, "Too long. %d bytes allowed\n", VINPUT_MAX_LEN);
-126 return -EINVAL;
-127 }
-128
-129 if (raw_copy_from_user(buff, buffer, count))
-130 return -EFAULT;
-131
-132 return vinput->type->ops->send(vinput, buff, count);
-133}
-134
-135static const struct file_operations vinput_fops = {
-136#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
-137 .owner = THIS_MODULE,
-138#endif
-139 .open = vinput_open,
-140 .release = vinput_release,
-141 .read = vinput_read,
-142 .write = vinput_write,
-143};
-144
-145static void vinput_unregister_vdevice(struct vinput *vinput)
-146{
-147 input_unregister_device(vinput->input);
-148 if (vinput->type->ops->kill)
-149 vinput->type->ops->kill(vinput);
-150}
-151
-152static void vinput_destroy_vdevice(struct vinput *vinput)
-153{
-154 /* Remove from the list first */
-155 spin_lock(&vinput_lock);
-156 list_del(&vinput->list);
-157 clear_bit(vinput->id, vinput_ids);
-158 spin_unlock(&vinput_lock);
-159
-160 module_put(THIS_MODULE);
-161
-162 kfree(vinput);
-163}
-164
-165static void vinput_release_dev(struct device *dev)
-166{
-167 struct vinput *vinput = dev_to_vinput(dev);
-168 int id = vinput->id;
-169
-170 vinput_destroy_vdevice(vinput);
-171
-172 pr_debug("released vinput%d.\n", id);
-173}
-174
-175static struct vinput *vinput_alloc_vdevice(void)
-176{
-177 int err;
-178 struct vinput *vinput = kzalloc(sizeof(struct vinput), GFP_KERNEL);
-179
-180 try_module_get(THIS_MODULE);
-181
-182 memset(vinput, 0, sizeof(struct vinput));
-183
-184 spin_lock_init(&vinput->lock);
-185
-186 spin_lock(&vinput_lock);
-187 vinput->id = find_first_zero_bit(vinput_ids, VINPUT_MINORS);
-188 if (vinput->id >= VINPUT_MINORS) {
-189 err = -ENOBUFS;
-190 goto fail_id;
-191 }
-192 set_bit(vinput->id, vinput_ids);
-193 list_add(&vinput->list, &vinput_vdevices);
-194 spin_unlock(&vinput_lock);
-195
-196 /* allocate the input device */
-197 vinput->input = input_allocate_device();
-198 if (vinput->input == NULL) {
-199 pr_err("vinput: Cannot allocate vinput input device\n");
-200 err = -ENOMEM;
-201 goto fail_input_dev;
-202 }
-203
-204 /* initialize device */
-205 vinput->dev.class = &vinput_class;
-206 vinput->dev.release = vinput_release_dev;
-207 vinput->dev.devt = MKDEV(vinput_dev, vinput->id);
-208 dev_set_name(&vinput->dev, DRIVER_NAME "%lu", vinput->id);
-209
-210 return vinput;
-211
-212fail_input_dev:
-213 spin_lock(&vinput_lock);
-214 list_del(&vinput->list);
-215fail_id:
-216 spin_unlock(&vinput_lock);
-217 module_put(THIS_MODULE);
-218 kfree(vinput);
-219
-220 return ERR_PTR(err);
-221}
-222
-223static int vinput_register_vdevice(struct vinput *vinput)
-224{
-225 int err = 0;
-226
-227 /* register the input device */
-228 vinput->input->name = vinput->type->name;
-229 vinput->input->phys = "vinput";
-230 vinput->input->dev.parent = &vinput->dev;
-231
-232 vinput->input->id.bustype = BUS_VIRTUAL;
-233 vinput->input->id.product = 0x0000;
-234 vinput->input->id.vendor = 0x0000;
-235 vinput->input->id.version = 0x0000;
-236
-237 err = vinput->type->ops->init(vinput);
-238
-239 if (err == 0)
-240 dev_info(&vinput->dev, "Registered virtual input %s %ld\n",
-241 vinput->type->name, vinput->id);
-242
-243 return err;
-244}
-245
-246#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
-247static ssize_t export_store(const struct class *class,
-248 const struct class_attribute *attr,
-249#else
-250static ssize_t export_store(struct class *class, struct class_attribute *attr,
-251#endif
-252 const char *buf, size_t len)
-253{
-254 int err;
-255 struct vinput *vinput;
-256 struct vinput_device *device;
-257
-258 device = vinput_get_device_by_type(buf);
-259 if (IS_ERR(device)) {
-260 pr_info("vinput: This virtual device isn't registered\n");
-261 err = PTR_ERR(device);
-262 goto fail;
-263 }
-264
-265 vinput = vinput_alloc_vdevice();
-266 if (IS_ERR(vinput)) {
-267 err = PTR_ERR(vinput);
-268 goto fail;
-269 }
-270
-271 vinput->type = device;
-272 err = device_register(&vinput->dev);
-273 if (err < 0)
-274 goto fail_register;
-275
-276 err = vinput_register_vdevice(vinput);
-277 if (err < 0)
-278 goto fail_register_vinput;
-279
-280 return len;
-281
-282fail_register_vinput:
-283 device_unregister(&vinput->dev);
-284fail_register:
-285 vinput_destroy_vdevice(vinput);
-286fail:
-287 return err;
-288}
-289/* This macro generates class_attr_export structure and export_store() */
-290static CLASS_ATTR_WO(export);
-291
-292#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
-293static ssize_t unexport_store(const struct class *class,
-294 const struct class_attribute *attr,
-295#else
-296static ssize_t unexport_store(struct class *class, struct class_attribute *attr,
-297#endif
-298 const char *buf, size_t len)
-299{
-300 int err;
-301 unsigned long id;
-302 struct vinput *vinput;
-303
-304 err = kstrtol(buf, 10, &id);
-305 if (err) {
-306 err = -EINVAL;
-307 goto failed;
-308 }
-309
-310 vinput = vinput_get_vdevice_by_id(id);
-311 if (IS_ERR(vinput)) {
-312 pr_err("vinput: No such vinput device %ld\n", id);
-313 err = PTR_ERR(vinput);
-314 goto failed;
-315 }
-316
-317 vinput_unregister_vdevice(vinput);
-318 device_unregister(&vinput->dev);
-319
-320 return len;
-321failed:
-322 return err;
-323}
-324/* This macro generates class_attr_unexport structure and unexport_store() */
-325static CLASS_ATTR_WO(unexport);
-326
-327static struct attribute *vinput_class_attrs[] = {
-328 &class_attr_export.attr,
-329 &class_attr_unexport.attr,
-330 NULL,
-331};
-332
-333/* This macro generates vinput_class_groups structure */
-334ATTRIBUTE_GROUPS(vinput_class);
-335
-336static struct class vinput_class = {
-337 .name = "vinput",
-338#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
-339 .owner = THIS_MODULE,
-340#endif
-341 .class_groups = vinput_class_groups,
-342};
-343
-344int vinput_register(struct vinput_device *dev)
-345{
-346 spin_lock(&vinput_lock);
-347 list_add(&dev->list, &vinput_devices);
-348 spin_unlock(&vinput_lock);
-349
-350 pr_info("vinput: registered new virtual input device '%s'\n", dev->name);
-351
-352 return 0;
-353}
-354EXPORT_SYMBOL(vinput_register);
-355
-356void vinput_unregister(struct vinput_device *dev)
-357{
-358 struct list_head *curr, *next;
-359
-360 /* Remove from the list first */
-361 spin_lock(&vinput_lock);
-362 list_del(&dev->list);
-363 spin_unlock(&vinput_lock);
-364
-365 /* unregister all devices of this type */
-366 list_for_each_safe (curr, next, &vinput_vdevices) {
-367 struct vinput *vinput = list_entry(curr, struct vinput, list);
-368 if (vinput && vinput->type == dev) {
-369 vinput_unregister_vdevice(vinput);
-370 device_unregister(&vinput->dev);
-371 }
-372 }
-373
-374 pr_info("vinput: unregistered virtual input device '%s'\n", dev->name);
-375}
-376EXPORT_SYMBOL(vinput_unregister);
-377
-378static int __init vinput_init(void)
-379{
-380 int err = 0;
-381
-382 pr_info("vinput: Loading virtual input driver\n");
-383
-384 vinput_dev = register_chrdev(0, DRIVER_NAME, &vinput_fops);
-385 if (vinput_dev < 0) {
-386 pr_err("vinput: Unable to allocate char dev region\n");
-387 err = vinput_dev;
-388 goto failed_alloc;
-389 }
-390
-391 spin_lock_init(&vinput_lock);
-392
-393 err = class_register(&vinput_class);
-394 if (err < 0) {
-395 pr_err("vinput: Unable to register vinput class\n");
-396 goto failed_class;
-397 }
-398
-399 return 0;
-400failed_class:
-401 class_unregister(&vinput_class);
-402failed_alloc:
-403 return err;
-404}
-405
-406static void __exit vinput_end(void)
-407{
-408 pr_info("vinput: Unloading virtual input driver\n");
-409
-410 unregister_chrdev(vinput_dev, DRIVER_NAME);
-411 class_unregister(&vinput_class);
-412}
-413
-414module_init(vinput_init);
-415module_exit(vinput_end);
-416
-417MODULE_LICENSE("GPL");
-418MODULE_DESCRIPTION("Emulate input events");
-1/*
+2 * vinput.c
+3 */
+4
+5#include <linux/cdev.h>
+6#include <linux/input.h>
+7#include <linux/module.h>
+8#include <linux/slab.h>
+9#include <linux/spinlock.h>
+10#include <linux/version.h>
+11
+12#include <asm/uaccess.h>
+13
+14#include "vinput.h"
+15
+16#define DRIVER_NAME "vinput"
+17
+18#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
+19
+20static DECLARE_BITMAP(vinput_ids, VINPUT_MINORS);
+21
+22static LIST_HEAD(vinput_devices);
+23static LIST_HEAD(vinput_vdevices);
+24
+25static int vinput_dev;
+26static struct spinlock vinput_lock;
+27static struct class vinput_class;
+28
+29/* Search the name of vinput device in the vinput_devices linked list,
+30 * which added at vinput_register().
+31 */
+32static struct vinput_device *vinput_get_device_by_type(const char *type)
+33{
+34 int found = 0;
+35 struct vinput_device *vinput;
+36 struct list_head *curr;
+37
+38 spin_lock(&vinput_lock);
+39 list_for_each (curr, &vinput_devices) {
+40 vinput = list_entry(curr, struct vinput_device, list);
+41 if (vinput && strncmp(type, vinput->name, strlen(vinput->name)) == 0) {
+42 found = 1;
+43 break;
+44 }
+45 }
+46 spin_unlock(&vinput_lock);
+47
+48 if (found)
+49 return vinput;
+50 return ERR_PTR(-ENODEV);
+51}
+52
+53/* Search the id of virtual device in the vinput_vdevices linked list,
+54 * which added at vinput_alloc_vdevice().
+55 */
+56static struct vinput *vinput_get_vdevice_by_id(long id)
+57{
+58 struct vinput *vinput = NULL;
+59 struct list_head *curr;
+60
+61 spin_lock(&vinput_lock);
+62 list_for_each (curr, &vinput_vdevices) {
+63 vinput = list_entry(curr, struct vinput, list);
+64 if (vinput && vinput->id == id)
+65 break;
+66 }
+67 spin_unlock(&vinput_lock);
+68
+69 if (vinput && vinput->id == id)
+70 return vinput;
+71 return ERR_PTR(-ENODEV);
+72}
+73
+74static int vinput_open(struct inode *inode, struct file *file)
+75{
+76 int err = 0;
+77 struct vinput *vinput = NULL;
+78
+79 vinput = vinput_get_vdevice_by_id(iminor(inode));
+80
+81 if (IS_ERR(vinput))
+82 err = PTR_ERR(vinput);
+83 else
+84 file->private_data = vinput;
+85
+86 return err;
+87}
+88
+89static int vinput_release(struct inode *inode, struct file *file)
+90{
+91 return 0;
+92}
+93
+94static ssize_t vinput_read(struct file *file, char __user *buffer, size_t count,
+95 loff_t *offset)
+96{
+97 int len;
+98 char buff[VINPUT_MAX_LEN + 1];
+99 struct vinput *vinput = file->private_data;
+100
+101 len = vinput->type->ops->read(vinput, buff, count);
+102
+103 if (*offset > len)
+104 count = 0;
+105 else if (count + *offset > VINPUT_MAX_LEN)
+106 count = len - *offset;
+107
+108 if (raw_copy_to_user(buffer, buff + *offset, count))
+109 count = -EFAULT;
+110
+111 *offset += count;
+112
+113 return count;
+114}
+115
+116static ssize_t vinput_write(struct file *file, const char __user *buffer,
+117 size_t count, loff_t *offset)
+118{
+119 char buff[VINPUT_MAX_LEN + 1];
+120 struct vinput *vinput = file->private_data;
+121
+122 memset(buff, 0, sizeof(char) * (VINPUT_MAX_LEN + 1));
+123
+124 if (count > VINPUT_MAX_LEN) {
+125 dev_warn(&vinput->dev, "Too long. %d bytes allowed\n", VINPUT_MAX_LEN);
+126 return -EINVAL;
+127 }
+128
+129 if (raw_copy_from_user(buff, buffer, count))
+130 return -EFAULT;
+131
+132 return vinput->type->ops->send(vinput, buff, count);
+133}
+134
+135static const struct file_operations vinput_fops = {
+136#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
+137 .owner = THIS_MODULE,
+138#endif
+139 .open = vinput_open,
+140 .release = vinput_release,
+141 .read = vinput_read,
+142 .write = vinput_write,
+143};
+144
+145static void vinput_unregister_vdevice(struct vinput *vinput)
+146{
+147 input_unregister_device(vinput->input);
+148 if (vinput->type->ops->kill)
+149 vinput->type->ops->kill(vinput);
+150}
+151
+152static void vinput_destroy_vdevice(struct vinput *vinput)
+153{
+154 /* Remove from the list first */
+155 spin_lock(&vinput_lock);
+156 list_del(&vinput->list);
+157 clear_bit(vinput->id, vinput_ids);
+158 spin_unlock(&vinput_lock);
+159
+160 module_put(THIS_MODULE);
+161
+162 kfree(vinput);
+163}
+164
+165static void vinput_release_dev(struct device *dev)
+166{
+167 struct vinput *vinput = dev_to_vinput(dev);
+168 int id = vinput->id;
+169
+170 vinput_destroy_vdevice(vinput);
+171
+172 pr_debug("released vinput%d.\n", id);
+173}
+174
+175static struct vinput *vinput_alloc_vdevice(void)
+176{
+177 int err;
+178 struct vinput *vinput = kzalloc(sizeof(struct vinput), GFP_KERNEL);
+179
+180 try_module_get(THIS_MODULE);
+181
+182 memset(vinput, 0, sizeof(struct vinput));
+183
+184 spin_lock_init(&vinput->lock);
+185
+186 spin_lock(&vinput_lock);
+187 vinput->id = find_first_zero_bit(vinput_ids, VINPUT_MINORS);
+188 if (vinput->id >= VINPUT_MINORS) {
+189 err = -ENOBUFS;
+190 goto fail_id;
+191 }
+192 set_bit(vinput->id, vinput_ids);
+193 list_add(&vinput->list, &vinput_vdevices);
+194 spin_unlock(&vinput_lock);
+195
+196 /* allocate the input device */
+197 vinput->input = input_allocate_device();
+198 if (vinput->input == NULL) {
+199 pr_err("vinput: Cannot allocate vinput input device\n");
+200 err = -ENOMEM;
+201 goto fail_input_dev;
+202 }
+203
+204 /* initialize device */
+205 vinput->dev.class = &vinput_class;
+206 vinput->dev.release = vinput_release_dev;
+207 vinput->dev.devt = MKDEV(vinput_dev, vinput->id);
+208 dev_set_name(&vinput->dev, DRIVER_NAME "%lu", vinput->id);
+209
+210 return vinput;
+211
+212fail_input_dev:
+213 spin_lock(&vinput_lock);
+214 list_del(&vinput->list);
+215fail_id:
+216 spin_unlock(&vinput_lock);
+217 module_put(THIS_MODULE);
+218 kfree(vinput);
+219
+220 return ERR_PTR(err);
+221}
+222
+223static int vinput_register_vdevice(struct vinput *vinput)
+224{
+225 int err = 0;
+226
+227 /* register the input device */
+228 vinput->input->name = vinput->type->name;
+229 vinput->input->phys = "vinput";
+230 vinput->input->dev.parent = &vinput->dev;
+231
+232 vinput->input->id.bustype = BUS_VIRTUAL;
+233 vinput->input->id.product = 0x0000;
+234 vinput->input->id.vendor = 0x0000;
+235 vinput->input->id.version = 0x0000;
+236
+237 err = vinput->type->ops->init(vinput);
+238
+239 if (err == 0)
+240 dev_info(&vinput->dev, "Registered virtual input %s %ld\n",
+241 vinput->type->name, vinput->id);
+242
+243 return err;
+244}
+245
+246#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
+247static ssize_t export_store(const struct class *class,
+248 const struct class_attribute *attr,
+249#else
+250static ssize_t export_store(struct class *class, struct class_attribute *attr,
+251#endif
+252 const char *buf, size_t len)
+253{
+254 int err;
+255 struct vinput *vinput;
+256 struct vinput_device *device;
+257
+258 device = vinput_get_device_by_type(buf);
+259 if (IS_ERR(device)) {
+260 pr_info("vinput: This virtual device isn't registered\n");
+261 err = PTR_ERR(device);
+262 goto fail;
+263 }
+264
+265 vinput = vinput_alloc_vdevice();
+266 if (IS_ERR(vinput)) {
+267 err = PTR_ERR(vinput);
+268 goto fail;
+269 }
+270
+271 vinput->type = device;
+272 err = device_register(&vinput->dev);
+273 if (err < 0)
+274 goto fail_register;
+275
+276 err = vinput_register_vdevice(vinput);
+277 if (err < 0)
+278 goto fail_register_vinput;
+279
+280 return len;
+281
+282fail_register_vinput:
+283 device_unregister(&vinput->dev);
+284fail_register:
+285 vinput_destroy_vdevice(vinput);
+286fail:
+287 return err;
+288}
+289/* This macro generates class_attr_export structure and export_store() */
+290static CLASS_ATTR_WO(export);
+291
+292#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
+293static ssize_t unexport_store(const struct class *class,
+294 const struct class_attribute *attr,
+295#else
+296static ssize_t unexport_store(struct class *class, struct class_attribute *attr,
+297#endif
+298 const char *buf, size_t len)
+299{
+300 int err;
+301 unsigned long id;
+302 struct vinput *vinput;
+303
+304 err = kstrtol(buf, 10, &id);
+305 if (err) {
+306 err = -EINVAL;
+307 goto failed;
+308 }
+309
+310 vinput = vinput_get_vdevice_by_id(id);
+311 if (IS_ERR(vinput)) {
+312 pr_err("vinput: No such vinput device %ld\n", id);
+313 err = PTR_ERR(vinput);
+314 goto failed;
+315 }
+316
+317 vinput_unregister_vdevice(vinput);
+318 device_unregister(&vinput->dev);
+319
+320 return len;
+321failed:
+322 return err;
+323}
+324/* This macro generates class_attr_unexport structure and unexport_store() */
+325static CLASS_ATTR_WO(unexport);
+326
+327static struct attribute *vinput_class_attrs[] = {
+328 &class_attr_export.attr,
+329 &class_attr_unexport.attr,
+330 NULL,
+331};
+332
+333/* This macro generates vinput_class_groups structure */
+334ATTRIBUTE_GROUPS(vinput_class);
+335
+336static struct class vinput_class = {
+337 .name = "vinput",
+338#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
+339 .owner = THIS_MODULE,
+340#endif
+341 .class_groups = vinput_class_groups,
+342};
+343
+344int vinput_register(struct vinput_device *dev)
+345{
+346 spin_lock(&vinput_lock);
+347 list_add(&dev->list, &vinput_devices);
+348 spin_unlock(&vinput_lock);
+349
+350 pr_info("vinput: registered new virtual input device '%s'\n", dev->name);
+351
+352 return 0;
+353}
+354EXPORT_SYMBOL(vinput_register);
+355
+356void vinput_unregister(struct vinput_device *dev)
+357{
+358 struct list_head *curr, *next;
+359
+360 /* Remove from the list first */
+361 spin_lock(&vinput_lock);
+362 list_del(&dev->list);
+363 spin_unlock(&vinput_lock);
+364
+365 /* unregister all devices of this type */
+366 list_for_each_safe (curr, next, &vinput_vdevices) {
+367 struct vinput *vinput = list_entry(curr, struct vinput, list);
+368 if (vinput && vinput->type == dev) {
+369 vinput_unregister_vdevice(vinput);
+370 device_unregister(&vinput->dev);
+371 }
+372 }
+373
+374 pr_info("vinput: unregistered virtual input device '%s'\n", dev->name);
+375}
+376EXPORT_SYMBOL(vinput_unregister);
+377
+378static int __init vinput_init(void)
+379{
+380 int err = 0;
+381
+382 pr_info("vinput: Loading virtual input driver\n");
+383
+384 vinput_dev = register_chrdev(0, DRIVER_NAME, &vinput_fops);
+385 if (vinput_dev < 0) {
+386 pr_err("vinput: Unable to allocate char dev region\n");
+387 err = vinput_dev;
+388 goto failed_alloc;
+389 }
+390
+391 spin_lock_init(&vinput_lock);
+392
+393 err = class_register(&vinput_class);
+394 if (err < 0) {
+395 pr_err("vinput: Unable to register vinput class\n");
+396 goto failed_class;
+397 }
+398
+399 return 0;
+400failed_class:
+401 class_unregister(&vinput_class);
+402failed_alloc:
+403 return err;
+404}
+405
+406static void __exit vinput_end(void)
+407{
+408 pr_info("vinput: Unloading virtual input driver\n");
+409
+410 unregister_chrdev(vinput_dev, DRIVER_NAME);
+411 class_unregister(&vinput_class);
+412}
+413
+414module_init(vinput_init);
+415module_exit(vinput_end);
+416
+417MODULE_LICENSE("GPL");
+418MODULE_DESCRIPTION("Emulate input events");
+ KEY_MAX
keycodes. The injection format is the KEY_CODE
such as defined in include/linux/input.h. A positive value means
@@ -5987,136 +6188,136 @@ while a negative value is a
KEY_RELEASE
. The keyboard supports repetition when the key stays pressed for too long. The
following demonstrates how simulation work.
- KEY_G
+
KEY_G
= 34):
1echo "+34" | sudo tee /dev/vinput0
- KEY_G
+
1echo "+34" | sudo tee /dev/vinput0
+ KEY_G
= 34):
1echo "-34" | sudo tee /dev/vinput0
+1echo "-34" | sudo tee /dev/vinput0
1/*
-2 * vkbd.c
-3 */
-4
-5#include <linux/init.h>
-6#include <linux/input.h>
-7#include <linux/module.h>
-8#include <linux/spinlock.h>
-9
-10#include "vinput.h"
-11
-12#define VINPUT_KBD "vkbd"
-13#define VINPUT_RELEASE 0
-14#define VINPUT_PRESS 1
-15
-16static unsigned short vkeymap[KEY_MAX];
-17
-18static int vinput_vkbd_init(struct vinput *vinput)
-19{
-20 int i;
-21
-22 /* Set up the input bitfield */
-23 vinput->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
-24 vinput->input->keycodesize = sizeof(unsigned short);
-25 vinput->input->keycodemax = KEY_MAX;
-26 vinput->input->keycode = vkeymap;
-27
-28 for (i = 0; i < KEY_MAX; i++)
-29 set_bit(vkeymap[i], vinput->input->keybit);
-30
-31 /* vinput will help us allocate new input device structure via
-32 * input_allocate_device(). So, we can register it straightforwardly.
-33 */
-34 return input_register_device(vinput->input);
-35}
-36
-37static int vinput_vkbd_read(struct vinput *vinput, char *buff, int len)
-38{
-39 spin_lock(&vinput->lock);
-40 len = snprintf(buff, len, "%+ld\n", vinput->last_entry);
-41 spin_unlock(&vinput->lock);
-42
-43 return len;
-44}
-45
-46static int vinput_vkbd_send(struct vinput *vinput, char *buff, int len)
-47{
-48 int ret;
-49 long key = 0;
-50 short type = VINPUT_PRESS;
-51
-52 /* Determine which event was received (press or release)
-53 * and store the state.
-54 */
-55 if (buff[0] == '+')
-56 ret = kstrtol(buff + 1, 10, &key);
-57 else
-58 ret = kstrtol(buff, 10, &key);
-59 if (ret)
-60 dev_err(&vinput->dev, "error during kstrtol: -%d\n", ret);
-61 spin_lock(&vinput->lock);
-62 vinput->last_entry = key;
-63 spin_unlock(&vinput->lock);
-64
-65 if (key < 0) {
-66 type = VINPUT_RELEASE;
-67 key = -key;
-68 }
-69
-70 dev_info(&vinput->dev, "Event %s code %ld\n",
-71 (type == VINPUT_RELEASE) ? "VINPUT_RELEASE" : "VINPUT_PRESS", key);
-72
-73 /* Report the state received to input subsystem. */
-74 input_report_key(vinput->input, key, type);
-75 /* Tell input subsystem that it finished the report. */
-76 input_sync(vinput->input);
-77
-78 return len;
-79}
-80
-81static struct vinput_ops vkbd_ops = {
-82 .init = vinput_vkbd_init,
-83 .send = vinput_vkbd_send,
-84 .read = vinput_vkbd_read,
-85};
-86
-87static struct vinput_device vkbd_dev = {
-88 .name = VINPUT_KBD,
-89 .ops = &vkbd_ops,
-90};
-91
-92static int __init vkbd_init(void)
-93{
-94 int i;
-95
-96 for (i = 0; i < KEY_MAX; i++)
-97 vkeymap[i] = i;
-98 return vinput_register(&vkbd_dev);
-99}
-100
-101static void __exit vkbd_end(void)
-102{
-103 vinput_unregister(&vkbd_dev);
-104}
-105
-106module_init(vkbd_init);
-107module_exit(vkbd_end);
-108
-109MODULE_LICENSE("GPL");
-110MODULE_DESCRIPTION("Emulate keyboard input events through /dev/vinput");
-17 Standardizing the interfaces: The Device Model
-1/*
+2 * vkbd.c
+3 */
+4
+5#include <linux/init.h>
+6#include <linux/input.h>
+7#include <linux/module.h>
+8#include <linux/spinlock.h>
+9
+10#include "vinput.h"
+11
+12#define VINPUT_KBD "vkbd"
+13#define VINPUT_RELEASE 0
+14#define VINPUT_PRESS 1
+15
+16static unsigned short vkeymap[KEY_MAX];
+17
+18static int vinput_vkbd_init(struct vinput *vinput)
+19{
+20 int i;
+21
+22 /* Set up the input bitfield */
+23 vinput->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+24 vinput->input->keycodesize = sizeof(unsigned short);
+25 vinput->input->keycodemax = KEY_MAX;
+26 vinput->input->keycode = vkeymap;
+27
+28 for (i = 0; i < KEY_MAX; i++)
+29 set_bit(vkeymap[i], vinput->input->keybit);
+30
+31 /* vinput will help us allocate new input device structure via
+32 * input_allocate_device(). So, we can register it straightforwardly.
+33 */
+34 return input_register_device(vinput->input);
+35}
+36
+37static int vinput_vkbd_read(struct vinput *vinput, char *buff, int len)
+38{
+39 spin_lock(&vinput->lock);
+40 len = snprintf(buff, len, "%+ld\n", vinput->last_entry);
+41 spin_unlock(&vinput->lock);
+42
+43 return len;
+44}
+45
+46static int vinput_vkbd_send(struct vinput *vinput, char *buff, int len)
+47{
+48 int ret;
+49 long key = 0;
+50 short type = VINPUT_PRESS;
+51
+52 /* Determine which event was received (press or release)
+53 * and store the state.
+54 */
+55 if (buff[0] == '+')
+56 ret = kstrtol(buff + 1, 10, &key);
+57 else
+58 ret = kstrtol(buff, 10, &key);
+59 if (ret)
+60 dev_err(&vinput->dev, "error during kstrtol: -%d\n", ret);
+61 spin_lock(&vinput->lock);
+62 vinput->last_entry = key;
+63 spin_unlock(&vinput->lock);
+64
+65 if (key < 0) {
+66 type = VINPUT_RELEASE;
+67 key = -key;
+68 }
+69
+70 dev_info(&vinput->dev, "Event %s code %ld\n",
+71 (type == VINPUT_RELEASE) ? "VINPUT_RELEASE" : "VINPUT_PRESS", key);
+72
+73 /* Report the state received to input subsystem. */
+74 input_report_key(vinput->input, key, type);
+75 /* Tell input subsystem that it finished the report. */
+76 input_sync(vinput->input);
+77
+78 return len;
+79}
+80
+81static struct vinput_ops vkbd_ops = {
+82 .init = vinput_vkbd_init,
+83 .send = vinput_vkbd_send,
+84 .read = vinput_vkbd_read,
+85};
+86
+87static struct vinput_device vkbd_dev = {
+88 .name = VINPUT_KBD,
+89 .ops = &vkbd_ops,
+90};
+91
+92static int __init vkbd_init(void)
+93{
+94 int i;
+95
+96 for (i = 0; i < KEY_MAX; i++)
+97 vkeymap[i] = i;
+98 return vinput_register(&vkbd_dev);
+99}
+100
+101static void __exit vkbd_end(void)
+102{
+103 vinput_unregister(&vkbd_dev);
+104}
+105
+106module_init(vkbd_init);
+107module_exit(vkbd_end);
+108
+109MODULE_LICENSE("GPL");
+110MODULE_DESCRIPTION("Emulate keyboard input events through /dev/vinput");
+17 Standardizing the interfaces: The Device Model
+1/*
-2 * devicemodel.c
-3 */
-4#include <linux/kernel.h>
-5#include <linux/module.h>
-6#include <linux/platform_device.h>
-7
-8struct devicemodel_data {
-9 char *greeting;
-10 int number;
-11};
-12
-13static int devicemodel_probe(struct platform_device *dev)
-14{
-15 struct devicemodel_data *pd =
-16 (struct devicemodel_data *)(dev->dev.platform_data);
-17
-18 pr_info("devicemodel probe\n");
-19 pr_info("devicemodel greeting: %s; %d\n", pd->greeting, pd->number);
-20
-21 /* Your device initialization code */
-22
-23 return 0;
-24}
-25
-26static int devicemodel_remove(struct platform_device *dev)
-27{
-28 pr_info("devicemodel example removed\n");
-29
-30 /* Your device removal code */
-31
-32 return 0;
-33}
-34
-35static int devicemodel_suspend(struct device *dev)
-36{
-37 pr_info("devicemodel example suspend\n");
-38
-39 /* Your device suspend code */
-40
-41 return 0;
-42}
-43
-44static int devicemodel_resume(struct device *dev)
-45{
-46 pr_info("devicemodel example resume\n");
-47
-48 /* Your device resume code */
-49
-50 return 0;
-51}
-52
-53static const struct dev_pm_ops devicemodel_pm_ops = {
-54 .suspend = devicemodel_suspend,
-55 .resume = devicemodel_resume,
-56 .poweroff = devicemodel_suspend,
-57 .freeze = devicemodel_suspend,
-58 .thaw = devicemodel_resume,
-59 .restore = devicemodel_resume,
-60};
-61
-62static struct platform_driver devicemodel_driver = {
-63 .driver =
-64 {
-65 .name = "devicemodel_example",
-66 .pm = &devicemodel_pm_ops,
-67 },
-68 .probe = devicemodel_probe,
-69 .remove = devicemodel_remove,
-70};
-71
-72static int __init devicemodel_init(void)
-73{
-74 int ret;
-75
-76 pr_info("devicemodel init\n");
-77
-78 ret = platform_driver_register(&devicemodel_driver);
-79
-80 if (ret) {
-81 pr_err("Unable to register driver\n");
-82 return ret;
-83 }
-84
-85 return 0;
-86}
-87
-88static void __exit devicemodel_exit(void)
-89{
-90 pr_info("devicemodel exit\n");
-91 platform_driver_unregister(&devicemodel_driver);
-92}
-93
-94module_init(devicemodel_init);
-95module_exit(devicemodel_exit);
-96
-97MODULE_LICENSE("GPL");
-98MODULE_DESCRIPTION("Linux Device Model example");
-18 Optimizations
-18.1 Likely and Unlikely conditions
-1/*
+2 * devicemodel.c
+3 */
+4#include <linux/kernel.h>
+5#include <linux/module.h>
+6#include <linux/platform_device.h>
+7
+8struct devicemodel_data {
+9 char *greeting;
+10 int number;
+11};
+12
+13static int devicemodel_probe(struct platform_device *dev)
+14{
+15 struct devicemodel_data *pd =
+16 (struct devicemodel_data *)(dev->dev.platform_data);
+17
+18 pr_info("devicemodel probe\n");
+19 pr_info("devicemodel greeting: %s; %d\n", pd->greeting, pd->number);
+20
+21 /* Your device initialization code */
+22
+23 return 0;
+24}
+25
+26static int devicemodel_remove(struct platform_device *dev)
+27{
+28 pr_info("devicemodel example removed\n");
+29
+30 /* Your device removal code */
+31
+32 return 0;
+33}
+34
+35static int devicemodel_suspend(struct device *dev)
+36{
+37 pr_info("devicemodel example suspend\n");
+38
+39 /* Your device suspend code */
+40
+41 return 0;
+42}
+43
+44static int devicemodel_resume(struct device *dev)
+45{
+46 pr_info("devicemodel example resume\n");
+47
+48 /* Your device resume code */
+49
+50 return 0;
+51}
+52
+53static const struct dev_pm_ops devicemodel_pm_ops = {
+54 .suspend = devicemodel_suspend,
+55 .resume = devicemodel_resume,
+56 .poweroff = devicemodel_suspend,
+57 .freeze = devicemodel_suspend,
+58 .thaw = devicemodel_resume,
+59 .restore = devicemodel_resume,
+60};
+61
+62static struct platform_driver devicemodel_driver = {
+63 .driver =
+64 {
+65 .name = "devicemodel_example",
+66 .pm = &devicemodel_pm_ops,
+67 },
+68 .probe = devicemodel_probe,
+69 .remove = devicemodel_remove,
+70};
+71
+72static int __init devicemodel_init(void)
+73{
+74 int ret;
+75
+76 pr_info("devicemodel init\n");
+77
+78 ret = platform_driver_register(&devicemodel_driver);
+79
+80 if (ret) {
+81 pr_err("Unable to register driver\n");
+82 return ret;
+83 }
+84
+85 return 0;
+86}
+87
+88static void __exit devicemodel_exit(void)
+89{
+90 pr_info("devicemodel exit\n");
+91 platform_driver_unregister(&devicemodel_driver);
+92}
+93
+94module_init(devicemodel_init);
+95module_exit(devicemodel_exit);
+96
+97MODULE_LICENSE("GPL");
+98MODULE_DESCRIPTION("Linux Device Model example");
+18 Optimizations
+18.1 Likely and Unlikely conditions
+
-
1bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx); -2if (unlikely(!bvl)) { -3 mempool_free(bio, bio_pool); -4 bio = NULL; -5 goto out; -6}-
When the unlikely
+
1bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx); +2if (unlikely(!bvl)) { +3 mempool_free(bio, bio_pool); +4 bio = NULL; +5 goto out; +6}+
When the unlikely
macro is used, the compiler alters its machine instruction output, so that it
continues along the false branch and only jumps if the condition is true. That
avoids flushing the processor pipeline. The opposite happens if you use the
likely
macro.
-
+
-
Static keys allow us to enable or disable kernel code paths based on the runtime state +
Static keys allow us to enable or disable kernel code paths based on the runtime state
of key. Its APIs have been available since 2010 (most architectures are already
supported), use self-modifying code to eliminate the overhead of cache and branch
prediction. The most typical use case of static keys is for performance-sensitive kernel
code, such as tracepoints, context switching, networking, etc. These hot paths of the
kernel often contain branches and can be optimized easily using this technique.
Before we can use static keys in the kernel, we need to make sure that gcc supports
- asm goto
+
asm goto
inline assembly, and the following kernel configurations are set:
-
1CONFIG_JUMP_LABEL=y -2CONFIG_HAVE_ARCH_JUMP_LABEL=y -3CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y-
To declare a static key, we need to define a global variable using the +
1CONFIG_JUMP_LABEL=y +2CONFIG_HAVE_ARCH_JUMP_LABEL=y +3CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y+
To declare a static key, we need to define a global variable using the
DEFINE_STATIC_KEY_FALSE
or DEFINE_STATIC_KEY_TRUE
macro defined in include/linux/jump_label.h. This macro initializes the key with
@@ -6282,277 +6483,277 @@
-
1DEFINE_STATIC_KEY_FALSE(fkey);-
Once the static key has been declared, we need to add branching code to the +
1DEFINE_STATIC_KEY_FALSE(fkey);+
Once the static key has been declared, we need to add branching code to the module that uses the static key. For example, the code includes a fastpath, where a no-op instruction will be generated at compile time as the key is initialized to false and the branch is unlikely to be taken.
-
1pr_info("fastpath 1\n"); -2if (static_branch_unlikely(&fkey)) -3 pr_alert("do unlikely thing\n"); -4pr_info("fastpath 2\n");-
If the key is enabled at runtime by calling
- static_branch_enable(&fkey)
-
, the fastpath will be patched with an unconditional jump instruction to the slowpath
+
1pr_info("fastpath 1\n"); +2if (static_branch_unlikely(&fkey)) +3 pr_alert("do unlikely thing\n"); +4pr_info("fastpath 2\n");+
If the key is enabled at runtime by calling
+ static_branch_enable(&fkey)
+
, the fastpath will be patched with an unconditional jump instruction to the slowpath
code pr_alert
, so the branch will always be taken until the key is disabled again.
-
The following kernel module derived from chardev.c, demonstrates how the +
The following kernel module derived from chardev.c, demonstrates how the static key works.
-
1/* -2 * static_key.c -3 */ -4 -5#include <linux/atomic.h> -6#include <linux/device.h> -7#include <linux/fs.h> -8#include <linux/kernel.h> /* for sprintf() */ -9#include <linux/module.h> -10#include <linux/printk.h> -11#include <linux/types.h> -12#include <linux/uaccess.h> /* for get_user and put_user */ -13#include <linux/jump_label.h> /* for static key macros */ -14#include <linux/version.h> -15 -16#include <asm/errno.h> -17 -18static int device_open(struct inode *inode, struct file *file); -19static int device_release(struct inode *inode, struct file *file); -20static ssize_t device_read(struct file *file, char __user *buf, size_t count, -21 loff_t *ppos); -22static ssize_t device_write(struct file *file, const char __user *buf, -23 size_t count, loff_t *ppos); -24 -25#define SUCCESS 0 -26#define DEVICE_NAME "key_state" -27#define BUF_LEN 10 -28 -29static int major; -30 -31enum { -32 CDEV_NOT_USED = 0, -33 CDEV_EXCLUSIVE_OPEN = 1, -34}; -35 -36static atomic_t already_open = ATOMIC_INIT(CDEV_NOT_USED); -37 -38static char msg[BUF_LEN + 1]; -39 -40static struct class *cls; -41 -42static DEFINE_STATIC_KEY_FALSE(fkey); -43 -44static struct file_operations chardev_fops = { -45#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) -46 .owner = THIS_MODULE, -47#endif -48 .open = device_open, -49 .release = device_release, -50 .read = device_read, -51 .write = device_write, -52}; -53 -54static int __init chardev_init(void) -55{ -56 major = register_chrdev(0, DEVICE_NAME, &chardev_fops); -57 if (major < 0) { -58 pr_alert("Registering char device failed with %d\n", major); -59 return major; -60 } -61 -62 pr_info("I was assigned major number %d\n", major); -63 -64#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) -65 cls = class_create(THIS_MODULE, DEVICE_NAME); -66#else -67 cls = class_create(DEVICE_NAME); -68#endif -69 -70 device_create(cls, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); -71 -72 pr_info("Device created on /dev/%s\n", DEVICE_NAME); -73 -74 return SUCCESS; -75} -76 -77static void __exit chardev_exit(void) -78{ -79 device_destroy(cls, MKDEV(major, 0)); -80 class_destroy(cls); -81 -82 /* Unregister the device */ -83 unregister_chrdev(major, DEVICE_NAME); -84} -85 -86/* Methods */ -87 -88/** -89 * Called when a process tried to open the device file, like -90 * cat /dev/key_state -91 */ -92static int device_open(struct inode *inode, struct file *file) -93{ -94 if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN)) -95 return -EBUSY; -96 -97 sprintf(msg, static_key_enabled(&fkey) ? "enabled\n" : "disabled\n"); -98 -99 pr_info("fastpath 1\n"); -100 if (static_branch_unlikely(&fkey)) -101 pr_alert("do unlikely thing\n"); -102 pr_info("fastpath 2\n"); -103 -104 try_module_get(THIS_MODULE); -105 -106 return SUCCESS; -107} -108 -109/** -110 * Called when a process closes the device file -111 */ -112static int device_release(struct inode *inode, struct file *file) -113{ -114 /* We are now ready for our next caller. */ -115 atomic_set(&already_open, CDEV_NOT_USED); -116 -117 /** -118 * Decrement the usage count, or else once you opened the file, you will -119 * never get rid of the module. -120 */ -121 module_put(THIS_MODULE); -122 -123 return SUCCESS; -124} -125 -126/** -127 * Called when a process, which already opened the dev file, attempts to -128 * read from it. -129 */ -130static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ -131 char __user *buffer, /* buffer to fill with data */ -132 size_t length, /* length of the buffer */ -133 loff_t *offset) -134{ -135 /* Number of the bytes actually written to the buffer */ -136 int bytes_read = 0; -137 const char *msg_ptr = msg; -138 -139 if (!*(msg_ptr + *offset)) { /* We are at the end of the message */ -140 *offset = 0; /* reset the offset */ -141 return 0; /* signify end of file */ -142 } -143 -144 msg_ptr += *offset; -145 -146 /* Actually put the data into the buffer */ -147 while (length && *msg_ptr) { -148 /** -149 * The buffer is in the user data segment, not the kernel -150 * segment so "*" assignment won't work. We have to use -151 * put_user which copies data from the kernel data segment to -152 * the user data segment. -153 */ -154 put_user(*(msg_ptr++), buffer++); -155 length--; -156 bytes_read++; -157 } -158 -159 *offset += bytes_read; -160 -161 /* Most read functions return the number of bytes put into the buffer. */ -162 return bytes_read; -163} -164 -165/* Called when a process writes to dev file; echo "enable" > /dev/key_state */ -166static ssize_t device_write(struct file *filp, const char __user *buffer, -167 size_t length, loff_t *offset) -168{ -169 char command[10]; -170 -171 if (length > 10) { -172 pr_err("command exceeded 10 char\n"); -173 return -EINVAL; -174 } -175 -176 if (copy_from_user(command, buffer, length)) -177 return -EFAULT; -178 -179 if (strncmp(command, "enable", strlen("enable")) == 0) -180 static_branch_enable(&fkey); -181 else if (strncmp(command, "disable", strlen("disable")) == 0) -182 static_branch_disable(&fkey); -183 else { -184 pr_err("Invalid command: %s\n", command); -185 return -EINVAL; -186 } -187 -188 /* Again, return the number of input characters used. */ -189 return length; -190} -191 -192module_init(chardev_init); -193module_exit(chardev_exit); -194 -195MODULE_LICENSE("GPL");-
To check the state of the static key, we can use the /dev/key_state +
1/* +2 * static_key.c +3 */ +4 +5#include <linux/atomic.h> +6#include <linux/device.h> +7#include <linux/fs.h> +8#include <linux/kernel.h> /* for sprintf() */ +9#include <linux/module.h> +10#include <linux/printk.h> +11#include <linux/types.h> +12#include <linux/uaccess.h> /* for get_user and put_user */ +13#include <linux/jump_label.h> /* for static key macros */ +14#include <linux/version.h> +15 +16#include <asm/errno.h> +17 +18static int device_open(struct inode *inode, struct file *file); +19static int device_release(struct inode *inode, struct file *file); +20static ssize_t device_read(struct file *file, char __user *buf, size_t count, +21 loff_t *ppos); +22static ssize_t device_write(struct file *file, const char __user *buf, +23 size_t count, loff_t *ppos); +24 +25#define SUCCESS 0 +26#define DEVICE_NAME "key_state" +27#define BUF_LEN 10 +28 +29static int major; +30 +31enum { +32 CDEV_NOT_USED = 0, +33 CDEV_EXCLUSIVE_OPEN = 1, +34}; +35 +36static atomic_t already_open = ATOMIC_INIT(CDEV_NOT_USED); +37 +38static char msg[BUF_LEN + 1]; +39 +40static struct class *cls; +41 +42static DEFINE_STATIC_KEY_FALSE(fkey); +43 +44static struct file_operations chardev_fops = { +45#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) +46 .owner = THIS_MODULE, +47#endif +48 .open = device_open, +49 .release = device_release, +50 .read = device_read, +51 .write = device_write, +52}; +53 +54static int __init chardev_init(void) +55{ +56 major = register_chrdev(0, DEVICE_NAME, &chardev_fops); +57 if (major < 0) { +58 pr_alert("Registering char device failed with %d\n", major); +59 return major; +60 } +61 +62 pr_info("I was assigned major number %d\n", major); +63 +64#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) +65 cls = class_create(THIS_MODULE, DEVICE_NAME); +66#else +67 cls = class_create(DEVICE_NAME); +68#endif +69 +70 device_create(cls, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); +71 +72 pr_info("Device created on /dev/%s\n", DEVICE_NAME); +73 +74 return SUCCESS; +75} +76 +77static void __exit chardev_exit(void) +78{ +79 device_destroy(cls, MKDEV(major, 0)); +80 class_destroy(cls); +81 +82 /* Unregister the device */ +83 unregister_chrdev(major, DEVICE_NAME); +84} +85 +86/* Methods */ +87 +88/** +89 * Called when a process tried to open the device file, like +90 * cat /dev/key_state +91 */ +92static int device_open(struct inode *inode, struct file *file) +93{ +94 if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN)) +95 return -EBUSY; +96 +97 sprintf(msg, static_key_enabled(&fkey) ? "enabled\n" : "disabled\n"); +98 +99 pr_info("fastpath 1\n"); +100 if (static_branch_unlikely(&fkey)) +101 pr_alert("do unlikely thing\n"); +102 pr_info("fastpath 2\n"); +103 +104 try_module_get(THIS_MODULE); +105 +106 return SUCCESS; +107} +108 +109/** +110 * Called when a process closes the device file +111 */ +112static int device_release(struct inode *inode, struct file *file) +113{ +114 /* We are now ready for our next caller. */ +115 atomic_set(&already_open, CDEV_NOT_USED); +116 +117 /** +118 * Decrement the usage count, or else once you opened the file, you will +119 * never get rid of the module. +120 */ +121 module_put(THIS_MODULE); +122 +123 return SUCCESS; +124} +125 +126/** +127 * Called when a process, which already opened the dev file, attempts to +128 * read from it. +129 */ +130static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ +131 char __user *buffer, /* buffer to fill with data */ +132 size_t length, /* length of the buffer */ +133 loff_t *offset) +134{ +135 /* Number of the bytes actually written to the buffer */ +136 int bytes_read = 0; +137 const char *msg_ptr = msg; +138 +139 if (!*(msg_ptr + *offset)) { /* We are at the end of the message */ +140 *offset = 0; /* reset the offset */ +141 return 0; /* signify end of file */ +142 } +143 +144 msg_ptr += *offset; +145 +146 /* Actually put the data into the buffer */ +147 while (length && *msg_ptr) { +148 /** +149 * The buffer is in the user data segment, not the kernel +150 * segment so "*" assignment won't work. We have to use +151 * put_user which copies data from the kernel data segment to +152 * the user data segment. +153 */ +154 put_user(*(msg_ptr++), buffer++); +155 length--; +156 bytes_read++; +157 } +158 +159 *offset += bytes_read; +160 +161 /* Most read functions return the number of bytes put into the buffer. */ +162 return bytes_read; +163} +164 +165/* Called when a process writes to dev file; echo "enable" > /dev/key_state */ +166static ssize_t device_write(struct file *filp, const char __user *buffer, +167 size_t length, loff_t *offset) +168{ +169 char command[10]; +170 +171 if (length > 10) { +172 pr_err("command exceeded 10 char\n"); +173 return -EINVAL; +174 } +175 +176 if (copy_from_user(command, buffer, length)) +177 return -EFAULT; +178 +179 if (strncmp(command, "enable", strlen("enable")) == 0) +180 static_branch_enable(&fkey); +181 else if (strncmp(command, "disable", strlen("disable")) == 0) +182 static_branch_disable(&fkey); +183 else { +184 pr_err("Invalid command: %s\n", command); +185 return -EINVAL; +186 } +187 +188 /* Again, return the number of input characters used. */ +189 return length; +190} +191 +192module_init(chardev_init); +193module_exit(chardev_exit); +194 +195MODULE_LICENSE("GPL");+
To check the state of the static key, we can use the /dev/key_state interface.
-
1cat /dev/key_state-
This will display the current state of the key, which is disabled by default. -
To change the state of the static key, we can perform a write operation on the +
1cat /dev/key_state+
This will display the current state of the key, which is disabled by default. +
To change the state of the static key, we can perform a write operation on the file:
-
1echo enable > /dev/key_state-
This will enable the static key, causing the code path to switch from the fastpath +
1echo enable > /dev/key_state+
This will enable the static key, causing the code path to switch from the fastpath to the slowpath. -
In some cases, the key is enabled or disabled at initialization and never changed, +
In some cases, the key is enabled or disabled at initialization and never changed,
we can declare a static key as read-only, which means that it can only be toggled in
the module init function. To declare a read-only static key, we can use the
DEFINE_STATIC_KEY_FALSE_RO
or DEFINE_STATIC_KEY_TRUE_RO
macro instead. Attempts to change the key at runtime will result in a page fault. For
more information, see Static keys
-
+
-
+
-
You can not do that. In a kernel module, you can only use kernel functions which are -the functions you can see in /proc/kallsyms. -
+
You can not do that. In a kernel module, you can only use kernel functions which are +the functions you can see in /proc/kallsyms. +
-
You might need to do this for a short time and that is OK, but if you do not enable +
You might need to do this for a short time and that is OK, but if you do not enable them afterwards, your system will be stuck and you will have to power it off. -
+
-
For those deeply interested in kernel programming, kernelnewbies.org and the +
For those deeply interested in kernel programming, kernelnewbies.org and the Documentation subdirectory within the kernel source code are highly recommended. Although the latter may not always be straightforward, it serves as a valuable initial step for further exploration. Echoing Linus Torvalds’ perspective, the most effective method to understand the kernel is through personal examination of the source code. -
Contributions to this guide are welcome, especially if there are any significant +
Contributions to this guide are welcome, especially if there are any significant inaccuracies identified. To contribute or report an issue, please initiate an issue at https://github.com/sysprog21/lkmpg. Pull requests are greatly appreciated. -
Happy hacking! +
Happy hacking!
- 1The goal of threaded interrupts is to push more of the work to separate threads, so that the
+ 1The goal of threaded interrupts is to push more of the work to separate threads, so that the
minimum needed for acknowledging an interrupt is reduced, and therefore the time spent handling
the interrupt (where it can’t handle any other interrupts at the same time) is reduced. See
https://lwn.net/Articles/302043/.The Linux Kernel Module Programming Guide
-The Linux Kernel Module Programming Guide
15.1 Interrupt Handlers
15.2 Detecting button presses
15.3 Bottom Half
-
16 Virtual Input Device Driver
-
17 Standardizing the interfaces: The Device Model
-
18 Optimizations
-
18.1 Likely and Unlikely conditions
-
18.2 Static keys
-
19 Common Pitfalls
-
19.1 Using standard libraries
-
19.2 Disabling interrupts
-
20 Where To Go From Here?
+
15.4 Threaded IRQ
+
16 Virtual Input Device Driver
+
17 Standardizing the interfaces: The Device Model
+
18 Optimizations
+
18.1 Likely and Unlikely conditions
+
18.2 Static keys
+
19 Common Pitfalls
+
19.1 Using standard libraries
+
19.2 Disabling interrupts
+
20 Where To Go From Here?
The Linux Kernel Module Programming Guide is a free book; you may reproduce @@ -112,10 +113,10 @@
If you publish or distribute this book commercially, donations, royalties, and/or @@ -189,11 +190,11 @@
On Arch Linux:
1sudo pacman -S gcc kmod-
-
++
To discover what modules are already loaded within your current kernel use the command
+ It is possible that in future tasklets may be replaced by threaded irqs. However,
+discussion about that has been ongoing since 2007 (Eliminating tasklets), so do
+not hold your breath. See the section 15.1 if you wish to avoid the tasklet
+debate.
+
Here is an example tasklet module. The
+ Here is an example tasklet module. The
So with this example loaded So with this example loaded Although tasklet is easy to use, it comes with several drawbacks, and developers are
+ Although tasklet is easy to use, it comes with several drawbacks, and developers are
discussing about getting rid of tasklet in linux kernel. The tasklet callback
runs in atomic context, inside a software interrupt, meaning that it cannot
sleep or access user-space data, so not all work can be done in a tasklet
handler. Also, the kernel only allows one instance of any given tasklet to be
running at any given time; multiple different tasklet callbacks can run in
parallel.
- In recent kernels, tasklets can be replaced by workqueues, timers, or threaded
+ In recent kernels, tasklets can be replaced by workqueues, timers, or threaded
interrupts.1
While the removal of tasklets remains a longer-term goal, the current kernel contains more
than a hundred uses of tasklets. Now developers are proceeding with the API changes and
the macro
+
To add a task to the scheduler we can use a workqueue. The kernel then uses the
+ To add a task to the scheduler we can use a workqueue. The kernel then uses the
Completely Fair Scheduler (CFS) to execute work within the queue.
lsmod
@@ -4877,10 +4878,14 @@
14
14.1 Tasklets
- tasklet_fn
function runs for a few seconds. In the meantime, execution of the
example_tasklet_init
@@ -4932,7 +4937,7 @@
14.1
42
43MODULE_DESCRIPTION("Tasklet example");
44MODULE_LICENSE("GPL");
-
dmesg
+
dmesg
should show:
@@ -4944,23 +4949,23 @@ 14.1
Example tasklet init continues...
Example tasklet ends
-
DECLARE_TASKLET_OLD
exists for compatibility. For further information, see https://lwn.net/Articles/830964/.
-14.2 Work queues
-14.2
+
+
Except for the last chapter, everything we did in the kernel so far we have done as a +
Except for the last chapter, everything we did in the kernel so far we have done as a
response to a process asking for it, either by dealing with a special file, sending an
ioctl()
, or issuing a system call. But the job of the kernel is not just to respond to process
requests. Another job, which is every bit as important, is to speak to the hardware
connected to the machine.
-
There are two types of interaction between the CPU and the rest of the +
There are two types of interaction between the CPU and the rest of the computer’s hardware. The first type is when the CPU gives orders to the hardware, the other is when the hardware needs to tell the CPU something. The second, called interrupts, is much harder to implement because it has to be dealt with when convenient for the hardware, not the CPU. Hardware devices typically have a very small amount of RAM, and if you do not read their information when available, it is lost. -
Under Linux, hardware interrupts are called IRQ’s (Interrupt ReQuests). There +
Under Linux, hardware interrupts are called IRQ’s (Interrupt ReQuests). There are two types of IRQ’s, short and long. A short IRQ is one which is expected to take a very short period of time, during which the rest of the machine will be blocked and no other interrupts will be handled. A long IRQ is one which can take longer, and during which other interrupts may occur (but not interrupts from the same device). If at all possible, it is better to declare an interrupt handler to be long. -
When the CPU receives an interrupt, it stops whatever it is doing (unless it is +
When the CPU receives an interrupt, it stops whatever it is doing (unless it is processing a more important interrupt, in which case it will deal with this one only when the more important one is done), saves certain parameters on the stack and calls the interrupt handler. This means that certain things are not allowed in the @@ -5038,10 +5043,10 @@
The way to implement this is to call +
The way to implement this is to call
request_irq()
to get your interrupt handler called when the relevant IRQ is received.
-
In practice IRQ handling can be a bit more complex. Hardware is often designed +
In practice IRQ handling can be a bit more complex. Hardware is often designed in a way that chains two interrupt controllers, so that all the IRQs from interrupt controller B are cascaded to a certain IRQ from interrupt controller A. Of course, that requires that the kernel finds out which IRQ it really was @@ -5058,11 +5063,11 @@
This function receives the IRQ number, the name of the function, flags, a name +
This function receives the IRQ number, the name of the function, flags, a name for /proc/interrupts and a parameter to be passed to the interrupt handler. Usually there is a certain number of IRQs available. How many IRQs there are is hardware-dependent. -
The flags can be used for specify behaviors of the IRQ. For example, use +
The flags can be used for specify behaviors of the IRQ. For example, use
IRQF_SHARED
to indicate you are willing to share the IRQ with other interrupt handlers
(usually because a number of hardware devices sit on the same IRQ); use the
@@ -5076,16 +5081,16 @@
IRQF
flags are in use. This function will only succeed if there is not already a handler on
this IRQ, or if you are both willing to share.
-+
Many popular single board computers, such as Raspberry Pi or Beagleboards, have a +
Many popular single board computers, such as Raspberry Pi or Beagleboards, have a bunch of GPIO pins. Attaching buttons to those and then having a button press do something is a classic case in which you might need to use interrupts, so that instead of having the CPU waste time and battery power polling for a change in input state, it is better for the input to trigger the CPU to then run a particular handling function. -
Here is an example where buttons are connected to GPIO numbers 17 and 18 and +
Here is an example where buttons are connected to GPIO numbers 17 and 18 and an LED is connected to GPIO 4. You can change those numbers to whatever is appropriate for your board.
@@ -5235,17 +5240,17 @@
+
Suppose you want to do a bunch of stuff inside of an interrupt routine. A common +
Suppose you want to do a bunch of stuff inside of an interrupt routine. A common way to do that without rendering the interrupt unavailable for a significant duration is to combine it with a tasklet. This pushes the bulk of the work off into the scheduler. -
The example below modifies the previous example to also run an additional task +
The example below modifies the previous example to also run an additional task when an interrupt is triggered.
@@ -5417,10 +5422,206 @@
+
-
The input device driver is a module that provides a way to communicate +
Threaded IRQ is a mechanism to organize both top-half and bottom-half
+of an IRQ at once. A threaded IRQ splits the one handler in
+ request_irq()
+
into two: one for the top-half, the other for the bottom-half. The
+ request_threaded_irq()
+
is the function for using threaded IRQs. Two handlers are registered at once in the
+ request_threaded_irq()
+
.
+
Those two handlers run in different context. The top-half handler runs
+in interrupt context. It’s the equivalence of the handler passed to the
+ request_irq()
+
. The bottom-half handler on the other hand runs in its own thread. This
+thread is created on registration of a threaded IRQ. Its sole purpose is to run
+this bottom-half handler. This is where a threaded IRQ is “threaded”. If
+ IRQ_WAKE_THREAD
+
is returned by the top-half handler, that bottom-half serving thread will wake up.
+The thread then runs the bottom-half handler.
+
Here is an example of how to do the same thing as before, with top and bottom +halves, but using threads. +
+
+1/* +2 * bh_thread.c - Top and bottom half interrupt handling +3 * +4 * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de) +5 * from: +6 * https://github.com/wendlers/rpi-kmod-samples +7 * +8 * Press one button to turn on a LED and another to turn it off +9 */ +10 +11#include <linux/module.h> +12#include <linux/kernel.h> +13#include <linux/gpio.h> +14#include <linux/delay.h> +15#include <linux/interrupt.h> +16 +17static int button_irqs[] = { -1, -1 }; +18 +19/* Define GPIOs for LEDs. +20 * FIXME: Change the numbers for the GPIO on your board. +21 */ +22static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } }; +23 +24/* Define GPIOs for BUTTONS +25 * FIXME: Change the numbers for the GPIO on your board. +26 */ +27static struct gpio buttons[] = { +28 { 17, GPIOF_IN, "LED 1 ON BUTTON" }, +29 { 18, GPIOF_IN, "LED 1 OFF BUTTON" }, +30}; +31 +32/* This happens immediately, when the IRQ is triggered */ +33static irqreturn_t button_top_half(int irq, void *ident) +34{ +35 return IRQ_WAKE_THREAD; +36} +37 +38/* This can happen at leisure, freeing up IRQs for other high priority task */ +39static irqreturn_t button_bottom_half(int irq, void *ident) +40{ +41 pr_info("Bottom half task starts\n"); +42 mdelay(500); /* do something which takes a while */ +43 pr_info("Bottom half task ends\n"); +44 return IRQ_HANDLED; +45} +46 +47static int __init bottomhalf_init(void) +48{ +49 int ret = 0; +50 +51 pr_info("%s\n", __func__); +52 +53 /* register LED gpios */ +54 ret = gpio_request_array(leds, ARRAY_SIZE(leds)); +55 +56 if (ret) { +57 pr_err("Unable to request GPIOs for LEDs: %d\n", ret); +58 return ret; +59 } +60 +61 /* register BUTTON gpios */ +62 ret = gpio_request_array(buttons, ARRAY_SIZE(buttons)); +63 +64 if (ret) { +65 pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret); +66 goto fail1; +67 } +68 +69 pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio)); +70 +71 ret = gpio_to_irq(buttons[0].gpio); +72 +73 if (ret < 0) { +74 pr_err("Unable to request IRQ: %d\n", ret); +75 goto fail2; +76 } +77 +78 button_irqs[0] = ret; +79 +80 pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]); +81 +82 ret = request_threaded_irq( +83 button_irqs[0], button_top_half, button_bottom_half, +84 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button1", &buttons[0]); +85 +86 if (ret) { +87 pr_err("Unable to request IRQ: %d\n", ret); +88 goto fail2; +89 } +90 +91 ret = gpio_to_irq(buttons[1].gpio); +92 +93 if (ret < 0) { +94 pr_err("Unable to request IRQ: %d\n", ret); +95 goto fail2; +96 } +97 +98 button_irqs[1] = ret; +99 +100 pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]); +101 +102 ret = request_threaded_irq( +103 button_irqs[1], button_top_half, button_bottom_half, +104 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button2", &buttons[1]); +105 +106 if (ret) { +107 pr_err("Unable to request IRQ: %d\n", ret); +108 goto fail3; +109 } +110 +111 return 0; +112 +113/* cleanup what has been setup so far */ +114fail3: +115 free_irq(button_irqs[0], NULL); +116 +117fail2: +118 gpio_free_array(buttons, ARRAY_SIZE(leds)); +119 +120fail1: +121 gpio_free_array(leds, ARRAY_SIZE(leds)); +122 +123 return ret; +124} +125 +126static void __exit bottomhalf_exit(void) +127{ +128 int i; +129 +130 pr_info("%s\n", __func__); +131 +132 /* free irqs */ +133 free_irq(button_irqs[0], NULL); +134 free_irq(button_irqs[1], NULL); +135 +136 /* turn all LEDs off */ +137 for (i = 0; i < ARRAY_SIZE(leds); i++) +138 gpio_set_value(leds[i].gpio, 0); +139 +140 /* unregister */ +141 gpio_free_array(leds, ARRAY_SIZE(leds)); +142 gpio_free_array(buttons, ARRAY_SIZE(buttons)); +143} +144 +145module_init(bottomhalf_init); +146module_exit(bottomhalf_exit); +147 +148MODULE_LICENSE("GPL"); +149MODULE_DESCRIPTION("Interrupt with top and bottom half");+
A threaded IRQ is registered using request_threaded_irq()
+
. This function only takes one additional parameter than the
+ request_irq()
+
– the bottom-half handling function that runs in its own thread. In this example it is
+the button_bottom_half()
+
. Usage of other parameters are the same as
+ request_irq()
+
+
+
+
.
+
Presence of both handlers is not mandatory. If either of them is not needed, pass
+the NULL
+
instead. A NULL
+
top-half handler implies that no action is taken except to wake up the
+bottom-half serving thread, which runs the bottom-half handler. Similarly, a
+ NULL
+
bottom-half handler effectively acts as if
+ request_irq()
+
were used. In fact, this is how request_irq()
+
is implemented.
+
Note that passing NULL
+
to both handlers is considered an error and will make registration fail.
+
+
+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 @@ -5428,7 +5629,7 @@
input_register_device()
.
-Here is an example, vinput, It is an API to allow easy +
Here is an example, vinput, It is an API to allow easy
development of virtual input drivers. The drivers needs to export a
vinput_device()
that contains the virtual device name and
@@ -5440,546 +5641,546 @@
send()
-
+
read()
Then using Then using
This function is passed a This function is passed a
This function will receive a user string to interpret and inject the event using the
+ This function will receive a user string to interpret and inject the event using the
This function is used for debugging and should fill the buffer parameter with the
+ 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
+ vinput devices are created and destroyed using sysfs. And, event injection is done
through a /dev node. The device name will be used by the userland to export a new
virtual input device.
- The The
In vinput.c, the macro In vinput.c, the macro To create a vinputX sysfs entry and /dev node.
+ To create a vinputX sysfs entry and /dev node.
To unexport the device, just echo its id in unexport:
+ To unexport the device, just echo its id in unexport:
Here the virtual keyboard is one of example to use vinput. It supports all
+ Here the virtual keyboard is one of example to use vinput. It supports all
Simulate a key press on "g" ( Simulate a key press on "g" (
Simulate a key release on "g" ( Simulate a key release on "g" (
-
- Up to this point we have seen all kinds of modules doing all kinds of things, but there
+
+ 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. To impose some
consistency such that there is at minimum a standardized way to start, suspend and
resume a device model was added. An example is shown below, and you can
@@ -6124,111 +6325,111 @@
-
- Sometimes you might want your code to run as quickly as possible,
+
+
+ Sometimes you might want your code to run as quickly as possible,
especially if it is handling an interrupt or doing something which might
cause noticeable latency. If your code contains boolean conditions and if
you know that the conditions are almost always likely to evaluate as either
@@ -6240,40 +6441,40 @@ vinput_register_device()
+
vinput_register_device()
and vinput_unregister_device()
will add a new device to the list of support virtual input devices.
1int init(struct vinput *);
- struct vinput
-
already initialized with an allocated struct input_dev
+
1int init(struct vinput *);
+ struct vinput
+
already initialized with an allocated struct input_dev
. The init()
function is responsible for initializing the capabilities of the input device and register
it.
1int send(struct vinput *, char *, int);
-1int send(struct vinput *, char *, int);
+ input_report_XXXX
or input_event
call. The string is already copied from user.
1int read(struct vinput *, char *, int);
-1int read(struct vinput *, char *, int);
+ class_attribute
+
class_attribute
structure is similar to other attribute types we talked about in section 8:
1struct class_attribute {
-2 struct attribute attr;
-3 ssize_t (*show)(struct class *class, struct class_attribute *attr,
-4 char *buf);
-5 ssize_t (*store)(struct class *class, struct class_attribute *attr,
-6 const char *buf, size_t count);
-7};
- CLASS_ATTR_WO(export/unexport)
-
defined in include/linux/device.h (in this case, device.h is included in include/linux/input.h)
-will generate the class_attribute
-
structures which are named class_attr_export/unexport. Then, put them into
+ 1struct class_attribute {
+2 struct attribute attr;
+3 ssize_t (*show)(struct class *class, struct class_attribute *attr,
+4 char *buf);
+5 ssize_t (*store)(struct class *class, struct class_attribute *attr,
+6 const char *buf, size_t count);
+7};
+ CLASS_ATTR_WO(export/unexport)
+
defined in include/linux/device.h (in this case, device.h is included in include/linux/input.h)
+will generate the class_attribute
+
structures which are named class_attr_export/unexport. Then, put them into
vinput_class_attrs
array and the macro ATTRIBUTE_GROUPS(vinput_class)
-
will generate the struct attribute_group vinput_class_group
+
will generate the struct attribute_group vinput_class_group
that should be assigned in vinput_class
. Finally, call class_register(&vinput_class)
to create attributes in sysfs.
-1echo "vkbd" | sudo tee /sys/class/vinput/export
-1echo "vkbd" | sudo tee /sys/class/vinput/export
+1echo "0" | sudo tee /sys/class/vinput/unexport
+ 1echo "0" | sudo tee /sys/class/vinput/unexport
1/*
-2 * vinput.h
-3 */
-4
-5#ifndef VINPUT_H
-6#define VINPUT_H
-7
-8#include <linux/input.h>
-9#include <linux/spinlock.h>
-10
-11#define VINPUT_MAX_LEN 128
-12#define MAX_VINPUT 32
-13#define VINPUT_MINORS MAX_VINPUT
-14
-15#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
-16
-17struct vinput_device;
-18
-19struct vinput {
-20 long id;
-21 long devno;
-22 long last_entry;
-23 spinlock_t lock;
-24
-25 void *priv_data;
-26
-27 struct device dev;
-28 struct list_head list;
-29 struct input_dev *input;
-30 struct vinput_device *type;
-31};
-32
-33struct vinput_ops {
-34 int (*init)(struct vinput *);
-35 int (*kill)(struct vinput *);
-36 int (*send)(struct vinput *, char *, int);
-37 int (*read)(struct vinput *, char *, int);
-38};
-39
-40struct vinput_device {
-41 char name[16];
-42 struct list_head list;
-43 struct vinput_ops *ops;
-44};
-45
-46int vinput_register(struct vinput_device *dev);
-47void vinput_unregister(struct vinput_device *dev);
-48
-49#endif
+ 1/*
+2 * vinput.h
+3 */
+4
+5#ifndef VINPUT_H
+6#define VINPUT_H
+7
+8#include <linux/input.h>
+9#include <linux/spinlock.h>
+10
+11#define VINPUT_MAX_LEN 128
+12#define MAX_VINPUT 32
+13#define VINPUT_MINORS MAX_VINPUT
+14
+15#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
+16
+17struct vinput_device;
+18
+19struct vinput {
+20 long id;
+21 long devno;
+22 long last_entry;
+23 spinlock_t lock;
+24
+25 void *priv_data;
+26
+27 struct device dev;
+28 struct list_head list;
+29 struct input_dev *input;
+30 struct vinput_device *type;
+31};
+32
+33struct vinput_ops {
+34 int (*init)(struct vinput *);
+35 int (*kill)(struct vinput *);
+36 int (*send)(struct vinput *, char *, int);
+37 int (*read)(struct vinput *, char *, int);
+38};
+39
+40struct vinput_device {
+41 char name[16];
+42 struct list_head list;
+43 struct vinput_ops *ops;
+44};
+45
+46int vinput_register(struct vinput_device *dev);
+47void vinput_unregister(struct vinput_device *dev);
+48
+49#endif
1/*
-2 * vinput.c
-3 */
-4
-5#include <linux/cdev.h>
-6#include <linux/input.h>
-7#include <linux/module.h>
-8#include <linux/slab.h>
-9#include <linux/spinlock.h>
-10#include <linux/version.h>
-11
-12#include <asm/uaccess.h>
-13
-14#include "vinput.h"
-15
-16#define DRIVER_NAME "vinput"
-17
-18#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
-19
-20static DECLARE_BITMAP(vinput_ids, VINPUT_MINORS);
-21
-22static LIST_HEAD(vinput_devices);
-23static LIST_HEAD(vinput_vdevices);
-24
-25static int vinput_dev;
-26static struct spinlock vinput_lock;
-27static struct class vinput_class;
-28
-29/* Search the name of vinput device in the vinput_devices linked list,
-30 * which added at vinput_register().
-31 */
-32static struct vinput_device *vinput_get_device_by_type(const char *type)
-33{
-34 int found = 0;
-35 struct vinput_device *vinput;
-36 struct list_head *curr;
-37
-38 spin_lock(&vinput_lock);
-39 list_for_each (curr, &vinput_devices) {
-40 vinput = list_entry(curr, struct vinput_device, list);
-41 if (vinput && strncmp(type, vinput->name, strlen(vinput->name)) == 0) {
-42 found = 1;
-43 break;
-44 }
-45 }
-46 spin_unlock(&vinput_lock);
-47
-48 if (found)
-49 return vinput;
-50 return ERR_PTR(-ENODEV);
-51}
-52
-53/* Search the id of virtual device in the vinput_vdevices linked list,
-54 * which added at vinput_alloc_vdevice().
-55 */
-56static struct vinput *vinput_get_vdevice_by_id(long id)
-57{
-58 struct vinput *vinput = NULL;
-59 struct list_head *curr;
-60
-61 spin_lock(&vinput_lock);
-62 list_for_each (curr, &vinput_vdevices) {
-63 vinput = list_entry(curr, struct vinput, list);
-64 if (vinput && vinput->id == id)
-65 break;
-66 }
-67 spin_unlock(&vinput_lock);
-68
-69 if (vinput && vinput->id == id)
-70 return vinput;
-71 return ERR_PTR(-ENODEV);
-72}
-73
-74static int vinput_open(struct inode *inode, struct file *file)
-75{
-76 int err = 0;
-77 struct vinput *vinput = NULL;
-78
-79 vinput = vinput_get_vdevice_by_id(iminor(inode));
-80
-81 if (IS_ERR(vinput))
-82 err = PTR_ERR(vinput);
-83 else
-84 file->private_data = vinput;
-85
-86 return err;
-87}
-88
-89static int vinput_release(struct inode *inode, struct file *file)
-90{
-91 return 0;
-92}
-93
-94static ssize_t vinput_read(struct file *file, char __user *buffer, size_t count,
-95 loff_t *offset)
-96{
-97 int len;
-98 char buff[VINPUT_MAX_LEN + 1];
-99 struct vinput *vinput = file->private_data;
-100
-101 len = vinput->type->ops->read(vinput, buff, count);
-102
-103 if (*offset > len)
-104 count = 0;
-105 else if (count + *offset > VINPUT_MAX_LEN)
-106 count = len - *offset;
-107
-108 if (raw_copy_to_user(buffer, buff + *offset, count))
-109 count = -EFAULT;
-110
-111 *offset += count;
-112
-113 return count;
-114}
-115
-116static ssize_t vinput_write(struct file *file, const char __user *buffer,
-117 size_t count, loff_t *offset)
-118{
-119 char buff[VINPUT_MAX_LEN + 1];
-120 struct vinput *vinput = file->private_data;
-121
-122 memset(buff, 0, sizeof(char) * (VINPUT_MAX_LEN + 1));
-123
-124 if (count > VINPUT_MAX_LEN) {
-125 dev_warn(&vinput->dev, "Too long. %d bytes allowed\n", VINPUT_MAX_LEN);
-126 return -EINVAL;
-127 }
-128
-129 if (raw_copy_from_user(buff, buffer, count))
-130 return -EFAULT;
-131
-132 return vinput->type->ops->send(vinput, buff, count);
-133}
-134
-135static const struct file_operations vinput_fops = {
-136#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
-137 .owner = THIS_MODULE,
-138#endif
-139 .open = vinput_open,
-140 .release = vinput_release,
-141 .read = vinput_read,
-142 .write = vinput_write,
-143};
-144
-145static void vinput_unregister_vdevice(struct vinput *vinput)
-146{
-147 input_unregister_device(vinput->input);
-148 if (vinput->type->ops->kill)
-149 vinput->type->ops->kill(vinput);
-150}
-151
-152static void vinput_destroy_vdevice(struct vinput *vinput)
-153{
-154 /* Remove from the list first */
-155 spin_lock(&vinput_lock);
-156 list_del(&vinput->list);
-157 clear_bit(vinput->id, vinput_ids);
-158 spin_unlock(&vinput_lock);
-159
-160 module_put(THIS_MODULE);
-161
-162 kfree(vinput);
-163}
-164
-165static void vinput_release_dev(struct device *dev)
-166{
-167 struct vinput *vinput = dev_to_vinput(dev);
-168 int id = vinput->id;
-169
-170 vinput_destroy_vdevice(vinput);
-171
-172 pr_debug("released vinput%d.\n", id);
-173}
-174
-175static struct vinput *vinput_alloc_vdevice(void)
-176{
-177 int err;
-178 struct vinput *vinput = kzalloc(sizeof(struct vinput), GFP_KERNEL);
-179
-180 try_module_get(THIS_MODULE);
-181
-182 memset(vinput, 0, sizeof(struct vinput));
-183
-184 spin_lock_init(&vinput->lock);
-185
-186 spin_lock(&vinput_lock);
-187 vinput->id = find_first_zero_bit(vinput_ids, VINPUT_MINORS);
-188 if (vinput->id >= VINPUT_MINORS) {
-189 err = -ENOBUFS;
-190 goto fail_id;
-191 }
-192 set_bit(vinput->id, vinput_ids);
-193 list_add(&vinput->list, &vinput_vdevices);
-194 spin_unlock(&vinput_lock);
-195
-196 /* allocate the input device */
-197 vinput->input = input_allocate_device();
-198 if (vinput->input == NULL) {
-199 pr_err("vinput: Cannot allocate vinput input device\n");
-200 err = -ENOMEM;
-201 goto fail_input_dev;
-202 }
-203
-204 /* initialize device */
-205 vinput->dev.class = &vinput_class;
-206 vinput->dev.release = vinput_release_dev;
-207 vinput->dev.devt = MKDEV(vinput_dev, vinput->id);
-208 dev_set_name(&vinput->dev, DRIVER_NAME "%lu", vinput->id);
-209
-210 return vinput;
-211
-212fail_input_dev:
-213 spin_lock(&vinput_lock);
-214 list_del(&vinput->list);
-215fail_id:
-216 spin_unlock(&vinput_lock);
-217 module_put(THIS_MODULE);
-218 kfree(vinput);
-219
-220 return ERR_PTR(err);
-221}
-222
-223static int vinput_register_vdevice(struct vinput *vinput)
-224{
-225 int err = 0;
-226
-227 /* register the input device */
-228 vinput->input->name = vinput->type->name;
-229 vinput->input->phys = "vinput";
-230 vinput->input->dev.parent = &vinput->dev;
-231
-232 vinput->input->id.bustype = BUS_VIRTUAL;
-233 vinput->input->id.product = 0x0000;
-234 vinput->input->id.vendor = 0x0000;
-235 vinput->input->id.version = 0x0000;
-236
-237 err = vinput->type->ops->init(vinput);
-238
-239 if (err == 0)
-240 dev_info(&vinput->dev, "Registered virtual input %s %ld\n",
-241 vinput->type->name, vinput->id);
-242
-243 return err;
-244}
-245
-246#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
-247static ssize_t export_store(const struct class *class,
-248 const struct class_attribute *attr,
-249#else
-250static ssize_t export_store(struct class *class, struct class_attribute *attr,
-251#endif
-252 const char *buf, size_t len)
-253{
-254 int err;
-255 struct vinput *vinput;
-256 struct vinput_device *device;
-257
-258 device = vinput_get_device_by_type(buf);
-259 if (IS_ERR(device)) {
-260 pr_info("vinput: This virtual device isn't registered\n");
-261 err = PTR_ERR(device);
-262 goto fail;
-263 }
-264
-265 vinput = vinput_alloc_vdevice();
-266 if (IS_ERR(vinput)) {
-267 err = PTR_ERR(vinput);
-268 goto fail;
-269 }
-270
-271 vinput->type = device;
-272 err = device_register(&vinput->dev);
-273 if (err < 0)
-274 goto fail_register;
-275
-276 err = vinput_register_vdevice(vinput);
-277 if (err < 0)
-278 goto fail_register_vinput;
-279
-280 return len;
-281
-282fail_register_vinput:
-283 device_unregister(&vinput->dev);
-284fail_register:
-285 vinput_destroy_vdevice(vinput);
-286fail:
-287 return err;
-288}
-289/* This macro generates class_attr_export structure and export_store() */
-290static CLASS_ATTR_WO(export);
-291
-292#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
-293static ssize_t unexport_store(const struct class *class,
-294 const struct class_attribute *attr,
-295#else
-296static ssize_t unexport_store(struct class *class, struct class_attribute *attr,
-297#endif
-298 const char *buf, size_t len)
-299{
-300 int err;
-301 unsigned long id;
-302 struct vinput *vinput;
-303
-304 err = kstrtol(buf, 10, &id);
-305 if (err) {
-306 err = -EINVAL;
-307 goto failed;
-308 }
-309
-310 vinput = vinput_get_vdevice_by_id(id);
-311 if (IS_ERR(vinput)) {
-312 pr_err("vinput: No such vinput device %ld\n", id);
-313 err = PTR_ERR(vinput);
-314 goto failed;
-315 }
-316
-317 vinput_unregister_vdevice(vinput);
-318 device_unregister(&vinput->dev);
-319
-320 return len;
-321failed:
-322 return err;
-323}
-324/* This macro generates class_attr_unexport structure and unexport_store() */
-325static CLASS_ATTR_WO(unexport);
-326
-327static struct attribute *vinput_class_attrs[] = {
-328 &class_attr_export.attr,
-329 &class_attr_unexport.attr,
-330 NULL,
-331};
-332
-333/* This macro generates vinput_class_groups structure */
-334ATTRIBUTE_GROUPS(vinput_class);
-335
-336static struct class vinput_class = {
-337 .name = "vinput",
-338#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
-339 .owner = THIS_MODULE,
-340#endif
-341 .class_groups = vinput_class_groups,
-342};
-343
-344int vinput_register(struct vinput_device *dev)
-345{
-346 spin_lock(&vinput_lock);
-347 list_add(&dev->list, &vinput_devices);
-348 spin_unlock(&vinput_lock);
-349
-350 pr_info("vinput: registered new virtual input device '%s'\n", dev->name);
-351
-352 return 0;
-353}
-354EXPORT_SYMBOL(vinput_register);
-355
-356void vinput_unregister(struct vinput_device *dev)
-357{
-358 struct list_head *curr, *next;
-359
-360 /* Remove from the list first */
-361 spin_lock(&vinput_lock);
-362 list_del(&dev->list);
-363 spin_unlock(&vinput_lock);
-364
-365 /* unregister all devices of this type */
-366 list_for_each_safe (curr, next, &vinput_vdevices) {
-367 struct vinput *vinput = list_entry(curr, struct vinput, list);
-368 if (vinput && vinput->type == dev) {
-369 vinput_unregister_vdevice(vinput);
-370 device_unregister(&vinput->dev);
-371 }
-372 }
-373
-374 pr_info("vinput: unregistered virtual input device '%s'\n", dev->name);
-375}
-376EXPORT_SYMBOL(vinput_unregister);
-377
-378static int __init vinput_init(void)
-379{
-380 int err = 0;
-381
-382 pr_info("vinput: Loading virtual input driver\n");
-383
-384 vinput_dev = register_chrdev(0, DRIVER_NAME, &vinput_fops);
-385 if (vinput_dev < 0) {
-386 pr_err("vinput: Unable to allocate char dev region\n");
-387 err = vinput_dev;
-388 goto failed_alloc;
-389 }
-390
-391 spin_lock_init(&vinput_lock);
-392
-393 err = class_register(&vinput_class);
-394 if (err < 0) {
-395 pr_err("vinput: Unable to register vinput class\n");
-396 goto failed_class;
-397 }
-398
-399 return 0;
-400failed_class:
-401 class_unregister(&vinput_class);
-402failed_alloc:
-403 return err;
-404}
-405
-406static void __exit vinput_end(void)
-407{
-408 pr_info("vinput: Unloading virtual input driver\n");
-409
-410 unregister_chrdev(vinput_dev, DRIVER_NAME);
-411 class_unregister(&vinput_class);
-412}
-413
-414module_init(vinput_init);
-415module_exit(vinput_end);
-416
-417MODULE_LICENSE("GPL");
-418MODULE_DESCRIPTION("Emulate input events");
-1/*
+2 * vinput.c
+3 */
+4
+5#include <linux/cdev.h>
+6#include <linux/input.h>
+7#include <linux/module.h>
+8#include <linux/slab.h>
+9#include <linux/spinlock.h>
+10#include <linux/version.h>
+11
+12#include <asm/uaccess.h>
+13
+14#include "vinput.h"
+15
+16#define DRIVER_NAME "vinput"
+17
+18#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)
+19
+20static DECLARE_BITMAP(vinput_ids, VINPUT_MINORS);
+21
+22static LIST_HEAD(vinput_devices);
+23static LIST_HEAD(vinput_vdevices);
+24
+25static int vinput_dev;
+26static struct spinlock vinput_lock;
+27static struct class vinput_class;
+28
+29/* Search the name of vinput device in the vinput_devices linked list,
+30 * which added at vinput_register().
+31 */
+32static struct vinput_device *vinput_get_device_by_type(const char *type)
+33{
+34 int found = 0;
+35 struct vinput_device *vinput;
+36 struct list_head *curr;
+37
+38 spin_lock(&vinput_lock);
+39 list_for_each (curr, &vinput_devices) {
+40 vinput = list_entry(curr, struct vinput_device, list);
+41 if (vinput && strncmp(type, vinput->name, strlen(vinput->name)) == 0) {
+42 found = 1;
+43 break;
+44 }
+45 }
+46 spin_unlock(&vinput_lock);
+47
+48 if (found)
+49 return vinput;
+50 return ERR_PTR(-ENODEV);
+51}
+52
+53/* Search the id of virtual device in the vinput_vdevices linked list,
+54 * which added at vinput_alloc_vdevice().
+55 */
+56static struct vinput *vinput_get_vdevice_by_id(long id)
+57{
+58 struct vinput *vinput = NULL;
+59 struct list_head *curr;
+60
+61 spin_lock(&vinput_lock);
+62 list_for_each (curr, &vinput_vdevices) {
+63 vinput = list_entry(curr, struct vinput, list);
+64 if (vinput && vinput->id == id)
+65 break;
+66 }
+67 spin_unlock(&vinput_lock);
+68
+69 if (vinput && vinput->id == id)
+70 return vinput;
+71 return ERR_PTR(-ENODEV);
+72}
+73
+74static int vinput_open(struct inode *inode, struct file *file)
+75{
+76 int err = 0;
+77 struct vinput *vinput = NULL;
+78
+79 vinput = vinput_get_vdevice_by_id(iminor(inode));
+80
+81 if (IS_ERR(vinput))
+82 err = PTR_ERR(vinput);
+83 else
+84 file->private_data = vinput;
+85
+86 return err;
+87}
+88
+89static int vinput_release(struct inode *inode, struct file *file)
+90{
+91 return 0;
+92}
+93
+94static ssize_t vinput_read(struct file *file, char __user *buffer, size_t count,
+95 loff_t *offset)
+96{
+97 int len;
+98 char buff[VINPUT_MAX_LEN + 1];
+99 struct vinput *vinput = file->private_data;
+100
+101 len = vinput->type->ops->read(vinput, buff, count);
+102
+103 if (*offset > len)
+104 count = 0;
+105 else if (count + *offset > VINPUT_MAX_LEN)
+106 count = len - *offset;
+107
+108 if (raw_copy_to_user(buffer, buff + *offset, count))
+109 count = -EFAULT;
+110
+111 *offset += count;
+112
+113 return count;
+114}
+115
+116static ssize_t vinput_write(struct file *file, const char __user *buffer,
+117 size_t count, loff_t *offset)
+118{
+119 char buff[VINPUT_MAX_LEN + 1];
+120 struct vinput *vinput = file->private_data;
+121
+122 memset(buff, 0, sizeof(char) * (VINPUT_MAX_LEN + 1));
+123
+124 if (count > VINPUT_MAX_LEN) {
+125 dev_warn(&vinput->dev, "Too long. %d bytes allowed\n", VINPUT_MAX_LEN);
+126 return -EINVAL;
+127 }
+128
+129 if (raw_copy_from_user(buff, buffer, count))
+130 return -EFAULT;
+131
+132 return vinput->type->ops->send(vinput, buff, count);
+133}
+134
+135static const struct file_operations vinput_fops = {
+136#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
+137 .owner = THIS_MODULE,
+138#endif
+139 .open = vinput_open,
+140 .release = vinput_release,
+141 .read = vinput_read,
+142 .write = vinput_write,
+143};
+144
+145static void vinput_unregister_vdevice(struct vinput *vinput)
+146{
+147 input_unregister_device(vinput->input);
+148 if (vinput->type->ops->kill)
+149 vinput->type->ops->kill(vinput);
+150}
+151
+152static void vinput_destroy_vdevice(struct vinput *vinput)
+153{
+154 /* Remove from the list first */
+155 spin_lock(&vinput_lock);
+156 list_del(&vinput->list);
+157 clear_bit(vinput->id, vinput_ids);
+158 spin_unlock(&vinput_lock);
+159
+160 module_put(THIS_MODULE);
+161
+162 kfree(vinput);
+163}
+164
+165static void vinput_release_dev(struct device *dev)
+166{
+167 struct vinput *vinput = dev_to_vinput(dev);
+168 int id = vinput->id;
+169
+170 vinput_destroy_vdevice(vinput);
+171
+172 pr_debug("released vinput%d.\n", id);
+173}
+174
+175static struct vinput *vinput_alloc_vdevice(void)
+176{
+177 int err;
+178 struct vinput *vinput = kzalloc(sizeof(struct vinput), GFP_KERNEL);
+179
+180 try_module_get(THIS_MODULE);
+181
+182 memset(vinput, 0, sizeof(struct vinput));
+183
+184 spin_lock_init(&vinput->lock);
+185
+186 spin_lock(&vinput_lock);
+187 vinput->id = find_first_zero_bit(vinput_ids, VINPUT_MINORS);
+188 if (vinput->id >= VINPUT_MINORS) {
+189 err = -ENOBUFS;
+190 goto fail_id;
+191 }
+192 set_bit(vinput->id, vinput_ids);
+193 list_add(&vinput->list, &vinput_vdevices);
+194 spin_unlock(&vinput_lock);
+195
+196 /* allocate the input device */
+197 vinput->input = input_allocate_device();
+198 if (vinput->input == NULL) {
+199 pr_err("vinput: Cannot allocate vinput input device\n");
+200 err = -ENOMEM;
+201 goto fail_input_dev;
+202 }
+203
+204 /* initialize device */
+205 vinput->dev.class = &vinput_class;
+206 vinput->dev.release = vinput_release_dev;
+207 vinput->dev.devt = MKDEV(vinput_dev, vinput->id);
+208 dev_set_name(&vinput->dev, DRIVER_NAME "%lu", vinput->id);
+209
+210 return vinput;
+211
+212fail_input_dev:
+213 spin_lock(&vinput_lock);
+214 list_del(&vinput->list);
+215fail_id:
+216 spin_unlock(&vinput_lock);
+217 module_put(THIS_MODULE);
+218 kfree(vinput);
+219
+220 return ERR_PTR(err);
+221}
+222
+223static int vinput_register_vdevice(struct vinput *vinput)
+224{
+225 int err = 0;
+226
+227 /* register the input device */
+228 vinput->input->name = vinput->type->name;
+229 vinput->input->phys = "vinput";
+230 vinput->input->dev.parent = &vinput->dev;
+231
+232 vinput->input->id.bustype = BUS_VIRTUAL;
+233 vinput->input->id.product = 0x0000;
+234 vinput->input->id.vendor = 0x0000;
+235 vinput->input->id.version = 0x0000;
+236
+237 err = vinput->type->ops->init(vinput);
+238
+239 if (err == 0)
+240 dev_info(&vinput->dev, "Registered virtual input %s %ld\n",
+241 vinput->type->name, vinput->id);
+242
+243 return err;
+244}
+245
+246#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
+247static ssize_t export_store(const struct class *class,
+248 const struct class_attribute *attr,
+249#else
+250static ssize_t export_store(struct class *class, struct class_attribute *attr,
+251#endif
+252 const char *buf, size_t len)
+253{
+254 int err;
+255 struct vinput *vinput;
+256 struct vinput_device *device;
+257
+258 device = vinput_get_device_by_type(buf);
+259 if (IS_ERR(device)) {
+260 pr_info("vinput: This virtual device isn't registered\n");
+261 err = PTR_ERR(device);
+262 goto fail;
+263 }
+264
+265 vinput = vinput_alloc_vdevice();
+266 if (IS_ERR(vinput)) {
+267 err = PTR_ERR(vinput);
+268 goto fail;
+269 }
+270
+271 vinput->type = device;
+272 err = device_register(&vinput->dev);
+273 if (err < 0)
+274 goto fail_register;
+275
+276 err = vinput_register_vdevice(vinput);
+277 if (err < 0)
+278 goto fail_register_vinput;
+279
+280 return len;
+281
+282fail_register_vinput:
+283 device_unregister(&vinput->dev);
+284fail_register:
+285 vinput_destroy_vdevice(vinput);
+286fail:
+287 return err;
+288}
+289/* This macro generates class_attr_export structure and export_store() */
+290static CLASS_ATTR_WO(export);
+291
+292#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
+293static ssize_t unexport_store(const struct class *class,
+294 const struct class_attribute *attr,
+295#else
+296static ssize_t unexport_store(struct class *class, struct class_attribute *attr,
+297#endif
+298 const char *buf, size_t len)
+299{
+300 int err;
+301 unsigned long id;
+302 struct vinput *vinput;
+303
+304 err = kstrtol(buf, 10, &id);
+305 if (err) {
+306 err = -EINVAL;
+307 goto failed;
+308 }
+309
+310 vinput = vinput_get_vdevice_by_id(id);
+311 if (IS_ERR(vinput)) {
+312 pr_err("vinput: No such vinput device %ld\n", id);
+313 err = PTR_ERR(vinput);
+314 goto failed;
+315 }
+316
+317 vinput_unregister_vdevice(vinput);
+318 device_unregister(&vinput->dev);
+319
+320 return len;
+321failed:
+322 return err;
+323}
+324/* This macro generates class_attr_unexport structure and unexport_store() */
+325static CLASS_ATTR_WO(unexport);
+326
+327static struct attribute *vinput_class_attrs[] = {
+328 &class_attr_export.attr,
+329 &class_attr_unexport.attr,
+330 NULL,
+331};
+332
+333/* This macro generates vinput_class_groups structure */
+334ATTRIBUTE_GROUPS(vinput_class);
+335
+336static struct class vinput_class = {
+337 .name = "vinput",
+338#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
+339 .owner = THIS_MODULE,
+340#endif
+341 .class_groups = vinput_class_groups,
+342};
+343
+344int vinput_register(struct vinput_device *dev)
+345{
+346 spin_lock(&vinput_lock);
+347 list_add(&dev->list, &vinput_devices);
+348 spin_unlock(&vinput_lock);
+349
+350 pr_info("vinput: registered new virtual input device '%s'\n", dev->name);
+351
+352 return 0;
+353}
+354EXPORT_SYMBOL(vinput_register);
+355
+356void vinput_unregister(struct vinput_device *dev)
+357{
+358 struct list_head *curr, *next;
+359
+360 /* Remove from the list first */
+361 spin_lock(&vinput_lock);
+362 list_del(&dev->list);
+363 spin_unlock(&vinput_lock);
+364
+365 /* unregister all devices of this type */
+366 list_for_each_safe (curr, next, &vinput_vdevices) {
+367 struct vinput *vinput = list_entry(curr, struct vinput, list);
+368 if (vinput && vinput->type == dev) {
+369 vinput_unregister_vdevice(vinput);
+370 device_unregister(&vinput->dev);
+371 }
+372 }
+373
+374 pr_info("vinput: unregistered virtual input device '%s'\n", dev->name);
+375}
+376EXPORT_SYMBOL(vinput_unregister);
+377
+378static int __init vinput_init(void)
+379{
+380 int err = 0;
+381
+382 pr_info("vinput: Loading virtual input driver\n");
+383
+384 vinput_dev = register_chrdev(0, DRIVER_NAME, &vinput_fops);
+385 if (vinput_dev < 0) {
+386 pr_err("vinput: Unable to allocate char dev region\n");
+387 err = vinput_dev;
+388 goto failed_alloc;
+389 }
+390
+391 spin_lock_init(&vinput_lock);
+392
+393 err = class_register(&vinput_class);
+394 if (err < 0) {
+395 pr_err("vinput: Unable to register vinput class\n");
+396 goto failed_class;
+397 }
+398
+399 return 0;
+400failed_class:
+401 class_unregister(&vinput_class);
+402failed_alloc:
+403 return err;
+404}
+405
+406static void __exit vinput_end(void)
+407{
+408 pr_info("vinput: Unloading virtual input driver\n");
+409
+410 unregister_chrdev(vinput_dev, DRIVER_NAME);
+411 class_unregister(&vinput_class);
+412}
+413
+414module_init(vinput_init);
+415module_exit(vinput_end);
+416
+417MODULE_LICENSE("GPL");
+418MODULE_DESCRIPTION("Emulate input events");
+ KEY_MAX
keycodes. The injection format is the KEY_CODE
such as defined in include/linux/input.h. A positive value means
@@ -5987,136 +6188,136 @@ while a negative value is a
KEY_RELEASE
. The keyboard supports repetition when the key stays pressed for too long. The
following demonstrates how simulation work.
- KEY_G
+
KEY_G
= 34):
1echo "+34" | sudo tee /dev/vinput0
- KEY_G
+
1echo "+34" | sudo tee /dev/vinput0
+ KEY_G
= 34):
1echo "-34" | sudo tee /dev/vinput0
+1echo "-34" | sudo tee /dev/vinput0
1/*
-2 * vkbd.c
-3 */
-4
-5#include <linux/init.h>
-6#include <linux/input.h>
-7#include <linux/module.h>
-8#include <linux/spinlock.h>
-9
-10#include "vinput.h"
-11
-12#define VINPUT_KBD "vkbd"
-13#define VINPUT_RELEASE 0
-14#define VINPUT_PRESS 1
-15
-16static unsigned short vkeymap[KEY_MAX];
-17
-18static int vinput_vkbd_init(struct vinput *vinput)
-19{
-20 int i;
-21
-22 /* Set up the input bitfield */
-23 vinput->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
-24 vinput->input->keycodesize = sizeof(unsigned short);
-25 vinput->input->keycodemax = KEY_MAX;
-26 vinput->input->keycode = vkeymap;
-27
-28 for (i = 0; i < KEY_MAX; i++)
-29 set_bit(vkeymap[i], vinput->input->keybit);
-30
-31 /* vinput will help us allocate new input device structure via
-32 * input_allocate_device(). So, we can register it straightforwardly.
-33 */
-34 return input_register_device(vinput->input);
-35}
-36
-37static int vinput_vkbd_read(struct vinput *vinput, char *buff, int len)
-38{
-39 spin_lock(&vinput->lock);
-40 len = snprintf(buff, len, "%+ld\n", vinput->last_entry);
-41 spin_unlock(&vinput->lock);
-42
-43 return len;
-44}
-45
-46static int vinput_vkbd_send(struct vinput *vinput, char *buff, int len)
-47{
-48 int ret;
-49 long key = 0;
-50 short type = VINPUT_PRESS;
-51
-52 /* Determine which event was received (press or release)
-53 * and store the state.
-54 */
-55 if (buff[0] == '+')
-56 ret = kstrtol(buff + 1, 10, &key);
-57 else
-58 ret = kstrtol(buff, 10, &key);
-59 if (ret)
-60 dev_err(&vinput->dev, "error during kstrtol: -%d\n", ret);
-61 spin_lock(&vinput->lock);
-62 vinput->last_entry = key;
-63 spin_unlock(&vinput->lock);
-64
-65 if (key < 0) {
-66 type = VINPUT_RELEASE;
-67 key = -key;
-68 }
-69
-70 dev_info(&vinput->dev, "Event %s code %ld\n",
-71 (type == VINPUT_RELEASE) ? "VINPUT_RELEASE" : "VINPUT_PRESS", key);
-72
-73 /* Report the state received to input subsystem. */
-74 input_report_key(vinput->input, key, type);
-75 /* Tell input subsystem that it finished the report. */
-76 input_sync(vinput->input);
-77
-78 return len;
-79}
-80
-81static struct vinput_ops vkbd_ops = {
-82 .init = vinput_vkbd_init,
-83 .send = vinput_vkbd_send,
-84 .read = vinput_vkbd_read,
-85};
-86
-87static struct vinput_device vkbd_dev = {
-88 .name = VINPUT_KBD,
-89 .ops = &vkbd_ops,
-90};
-91
-92static int __init vkbd_init(void)
-93{
-94 int i;
-95
-96 for (i = 0; i < KEY_MAX; i++)
-97 vkeymap[i] = i;
-98 return vinput_register(&vkbd_dev);
-99}
-100
-101static void __exit vkbd_end(void)
-102{
-103 vinput_unregister(&vkbd_dev);
-104}
-105
-106module_init(vkbd_init);
-107module_exit(vkbd_end);
-108
-109MODULE_LICENSE("GPL");
-110MODULE_DESCRIPTION("Emulate keyboard input events through /dev/vinput");
-17 Standardizing the interfaces: The Device Model
-1/*
+2 * vkbd.c
+3 */
+4
+5#include <linux/init.h>
+6#include <linux/input.h>
+7#include <linux/module.h>
+8#include <linux/spinlock.h>
+9
+10#include "vinput.h"
+11
+12#define VINPUT_KBD "vkbd"
+13#define VINPUT_RELEASE 0
+14#define VINPUT_PRESS 1
+15
+16static unsigned short vkeymap[KEY_MAX];
+17
+18static int vinput_vkbd_init(struct vinput *vinput)
+19{
+20 int i;
+21
+22 /* Set up the input bitfield */
+23 vinput->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+24 vinput->input->keycodesize = sizeof(unsigned short);
+25 vinput->input->keycodemax = KEY_MAX;
+26 vinput->input->keycode = vkeymap;
+27
+28 for (i = 0; i < KEY_MAX; i++)
+29 set_bit(vkeymap[i], vinput->input->keybit);
+30
+31 /* vinput will help us allocate new input device structure via
+32 * input_allocate_device(). So, we can register it straightforwardly.
+33 */
+34 return input_register_device(vinput->input);
+35}
+36
+37static int vinput_vkbd_read(struct vinput *vinput, char *buff, int len)
+38{
+39 spin_lock(&vinput->lock);
+40 len = snprintf(buff, len, "%+ld\n", vinput->last_entry);
+41 spin_unlock(&vinput->lock);
+42
+43 return len;
+44}
+45
+46static int vinput_vkbd_send(struct vinput *vinput, char *buff, int len)
+47{
+48 int ret;
+49 long key = 0;
+50 short type = VINPUT_PRESS;
+51
+52 /* Determine which event was received (press or release)
+53 * and store the state.
+54 */
+55 if (buff[0] == '+')
+56 ret = kstrtol(buff + 1, 10, &key);
+57 else
+58 ret = kstrtol(buff, 10, &key);
+59 if (ret)
+60 dev_err(&vinput->dev, "error during kstrtol: -%d\n", ret);
+61 spin_lock(&vinput->lock);
+62 vinput->last_entry = key;
+63 spin_unlock(&vinput->lock);
+64
+65 if (key < 0) {
+66 type = VINPUT_RELEASE;
+67 key = -key;
+68 }
+69
+70 dev_info(&vinput->dev, "Event %s code %ld\n",
+71 (type == VINPUT_RELEASE) ? "VINPUT_RELEASE" : "VINPUT_PRESS", key);
+72
+73 /* Report the state received to input subsystem. */
+74 input_report_key(vinput->input, key, type);
+75 /* Tell input subsystem that it finished the report. */
+76 input_sync(vinput->input);
+77
+78 return len;
+79}
+80
+81static struct vinput_ops vkbd_ops = {
+82 .init = vinput_vkbd_init,
+83 .send = vinput_vkbd_send,
+84 .read = vinput_vkbd_read,
+85};
+86
+87static struct vinput_device vkbd_dev = {
+88 .name = VINPUT_KBD,
+89 .ops = &vkbd_ops,
+90};
+91
+92static int __init vkbd_init(void)
+93{
+94 int i;
+95
+96 for (i = 0; i < KEY_MAX; i++)
+97 vkeymap[i] = i;
+98 return vinput_register(&vkbd_dev);
+99}
+100
+101static void __exit vkbd_end(void)
+102{
+103 vinput_unregister(&vkbd_dev);
+104}
+105
+106module_init(vkbd_init);
+107module_exit(vkbd_end);
+108
+109MODULE_LICENSE("GPL");
+110MODULE_DESCRIPTION("Emulate keyboard input events through /dev/vinput");
+17 Standardizing the interfaces: The Device Model
+1/*
-2 * devicemodel.c
-3 */
-4#include <linux/kernel.h>
-5#include <linux/module.h>
-6#include <linux/platform_device.h>
-7
-8struct devicemodel_data {
-9 char *greeting;
-10 int number;
-11};
-12
-13static int devicemodel_probe(struct platform_device *dev)
-14{
-15 struct devicemodel_data *pd =
-16 (struct devicemodel_data *)(dev->dev.platform_data);
-17
-18 pr_info("devicemodel probe\n");
-19 pr_info("devicemodel greeting: %s; %d\n", pd->greeting, pd->number);
-20
-21 /* Your device initialization code */
-22
-23 return 0;
-24}
-25
-26static int devicemodel_remove(struct platform_device *dev)
-27{
-28 pr_info("devicemodel example removed\n");
-29
-30 /* Your device removal code */
-31
-32 return 0;
-33}
-34
-35static int devicemodel_suspend(struct device *dev)
-36{
-37 pr_info("devicemodel example suspend\n");
-38
-39 /* Your device suspend code */
-40
-41 return 0;
-42}
-43
-44static int devicemodel_resume(struct device *dev)
-45{
-46 pr_info("devicemodel example resume\n");
-47
-48 /* Your device resume code */
-49
-50 return 0;
-51}
-52
-53static const struct dev_pm_ops devicemodel_pm_ops = {
-54 .suspend = devicemodel_suspend,
-55 .resume = devicemodel_resume,
-56 .poweroff = devicemodel_suspend,
-57 .freeze = devicemodel_suspend,
-58 .thaw = devicemodel_resume,
-59 .restore = devicemodel_resume,
-60};
-61
-62static struct platform_driver devicemodel_driver = {
-63 .driver =
-64 {
-65 .name = "devicemodel_example",
-66 .pm = &devicemodel_pm_ops,
-67 },
-68 .probe = devicemodel_probe,
-69 .remove = devicemodel_remove,
-70};
-71
-72static int __init devicemodel_init(void)
-73{
-74 int ret;
-75
-76 pr_info("devicemodel init\n");
-77
-78 ret = platform_driver_register(&devicemodel_driver);
-79
-80 if (ret) {
-81 pr_err("Unable to register driver\n");
-82 return ret;
-83 }
-84
-85 return 0;
-86}
-87
-88static void __exit devicemodel_exit(void)
-89{
-90 pr_info("devicemodel exit\n");
-91 platform_driver_unregister(&devicemodel_driver);
-92}
-93
-94module_init(devicemodel_init);
-95module_exit(devicemodel_exit);
-96
-97MODULE_LICENSE("GPL");
-98MODULE_DESCRIPTION("Linux Device Model example");
-18 Optimizations
-18.1 Likely and Unlikely conditions
-1/*
+2 * devicemodel.c
+3 */
+4#include <linux/kernel.h>
+5#include <linux/module.h>
+6#include <linux/platform_device.h>
+7
+8struct devicemodel_data {
+9 char *greeting;
+10 int number;
+11};
+12
+13static int devicemodel_probe(struct platform_device *dev)
+14{
+15 struct devicemodel_data *pd =
+16 (struct devicemodel_data *)(dev->dev.platform_data);
+17
+18 pr_info("devicemodel probe\n");
+19 pr_info("devicemodel greeting: %s; %d\n", pd->greeting, pd->number);
+20
+21 /* Your device initialization code */
+22
+23 return 0;
+24}
+25
+26static int devicemodel_remove(struct platform_device *dev)
+27{
+28 pr_info("devicemodel example removed\n");
+29
+30 /* Your device removal code */
+31
+32 return 0;
+33}
+34
+35static int devicemodel_suspend(struct device *dev)
+36{
+37 pr_info("devicemodel example suspend\n");
+38
+39 /* Your device suspend code */
+40
+41 return 0;
+42}
+43
+44static int devicemodel_resume(struct device *dev)
+45{
+46 pr_info("devicemodel example resume\n");
+47
+48 /* Your device resume code */
+49
+50 return 0;
+51}
+52
+53static const struct dev_pm_ops devicemodel_pm_ops = {
+54 .suspend = devicemodel_suspend,
+55 .resume = devicemodel_resume,
+56 .poweroff = devicemodel_suspend,
+57 .freeze = devicemodel_suspend,
+58 .thaw = devicemodel_resume,
+59 .restore = devicemodel_resume,
+60};
+61
+62static struct platform_driver devicemodel_driver = {
+63 .driver =
+64 {
+65 .name = "devicemodel_example",
+66 .pm = &devicemodel_pm_ops,
+67 },
+68 .probe = devicemodel_probe,
+69 .remove = devicemodel_remove,
+70};
+71
+72static int __init devicemodel_init(void)
+73{
+74 int ret;
+75
+76 pr_info("devicemodel init\n");
+77
+78 ret = platform_driver_register(&devicemodel_driver);
+79
+80 if (ret) {
+81 pr_err("Unable to register driver\n");
+82 return ret;
+83 }
+84
+85 return 0;
+86}
+87
+88static void __exit devicemodel_exit(void)
+89{
+90 pr_info("devicemodel exit\n");
+91 platform_driver_unregister(&devicemodel_driver);
+92}
+93
+94module_init(devicemodel_init);
+95module_exit(devicemodel_exit);
+96
+97MODULE_LICENSE("GPL");
+98MODULE_DESCRIPTION("Linux Device Model example");
+18 Optimizations
+18.1 Likely and Unlikely conditions
+
-
1bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx); -2if (unlikely(!bvl)) { -3 mempool_free(bio, bio_pool); -4 bio = NULL; -5 goto out; -6}-
When the unlikely
+
1bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx); +2if (unlikely(!bvl)) { +3 mempool_free(bio, bio_pool); +4 bio = NULL; +5 goto out; +6}+
When the unlikely
macro is used, the compiler alters its machine instruction output, so that it
continues along the false branch and only jumps if the condition is true. That
avoids flushing the processor pipeline. The opposite happens if you use the
likely
macro.
-
+
-
Static keys allow us to enable or disable kernel code paths based on the runtime state +
Static keys allow us to enable or disable kernel code paths based on the runtime state
of key. Its APIs have been available since 2010 (most architectures are already
supported), use self-modifying code to eliminate the overhead of cache and branch
prediction. The most typical use case of static keys is for performance-sensitive kernel
code, such as tracepoints, context switching, networking, etc. These hot paths of the
kernel often contain branches and can be optimized easily using this technique.
Before we can use static keys in the kernel, we need to make sure that gcc supports
- asm goto
+
asm goto
inline assembly, and the following kernel configurations are set:
-
1CONFIG_JUMP_LABEL=y -2CONFIG_HAVE_ARCH_JUMP_LABEL=y -3CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y-
To declare a static key, we need to define a global variable using the +
1CONFIG_JUMP_LABEL=y +2CONFIG_HAVE_ARCH_JUMP_LABEL=y +3CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y+
To declare a static key, we need to define a global variable using the
DEFINE_STATIC_KEY_FALSE
or DEFINE_STATIC_KEY_TRUE
macro defined in include/linux/jump_label.h. This macro initializes the key with
@@ -6282,277 +6483,277 @@
-
1DEFINE_STATIC_KEY_FALSE(fkey);-
Once the static key has been declared, we need to add branching code to the +
1DEFINE_STATIC_KEY_FALSE(fkey);+
Once the static key has been declared, we need to add branching code to the module that uses the static key. For example, the code includes a fastpath, where a no-op instruction will be generated at compile time as the key is initialized to false and the branch is unlikely to be taken.
-
1pr_info("fastpath 1\n"); -2if (static_branch_unlikely(&fkey)) -3 pr_alert("do unlikely thing\n"); -4pr_info("fastpath 2\n");-
If the key is enabled at runtime by calling
- static_branch_enable(&fkey)
-
, the fastpath will be patched with an unconditional jump instruction to the slowpath
+
1pr_info("fastpath 1\n"); +2if (static_branch_unlikely(&fkey)) +3 pr_alert("do unlikely thing\n"); +4pr_info("fastpath 2\n");+
If the key is enabled at runtime by calling
+ static_branch_enable(&fkey)
+
, the fastpath will be patched with an unconditional jump instruction to the slowpath
code pr_alert
, so the branch will always be taken until the key is disabled again.
-
The following kernel module derived from chardev.c, demonstrates how the +
The following kernel module derived from chardev.c, demonstrates how the static key works.
-
1/* -2 * static_key.c -3 */ -4 -5#include <linux/atomic.h> -6#include <linux/device.h> -7#include <linux/fs.h> -8#include <linux/kernel.h> /* for sprintf() */ -9#include <linux/module.h> -10#include <linux/printk.h> -11#include <linux/types.h> -12#include <linux/uaccess.h> /* for get_user and put_user */ -13#include <linux/jump_label.h> /* for static key macros */ -14#include <linux/version.h> -15 -16#include <asm/errno.h> -17 -18static int device_open(struct inode *inode, struct file *file); -19static int device_release(struct inode *inode, struct file *file); -20static ssize_t device_read(struct file *file, char __user *buf, size_t count, -21 loff_t *ppos); -22static ssize_t device_write(struct file *file, const char __user *buf, -23 size_t count, loff_t *ppos); -24 -25#define SUCCESS 0 -26#define DEVICE_NAME "key_state" -27#define BUF_LEN 10 -28 -29static int major; -30 -31enum { -32 CDEV_NOT_USED = 0, -33 CDEV_EXCLUSIVE_OPEN = 1, -34}; -35 -36static atomic_t already_open = ATOMIC_INIT(CDEV_NOT_USED); -37 -38static char msg[BUF_LEN + 1]; -39 -40static struct class *cls; -41 -42static DEFINE_STATIC_KEY_FALSE(fkey); -43 -44static struct file_operations chardev_fops = { -45#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) -46 .owner = THIS_MODULE, -47#endif -48 .open = device_open, -49 .release = device_release, -50 .read = device_read, -51 .write = device_write, -52}; -53 -54static int __init chardev_init(void) -55{ -56 major = register_chrdev(0, DEVICE_NAME, &chardev_fops); -57 if (major < 0) { -58 pr_alert("Registering char device failed with %d\n", major); -59 return major; -60 } -61 -62 pr_info("I was assigned major number %d\n", major); -63 -64#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) -65 cls = class_create(THIS_MODULE, DEVICE_NAME); -66#else -67 cls = class_create(DEVICE_NAME); -68#endif -69 -70 device_create(cls, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); -71 -72 pr_info("Device created on /dev/%s\n", DEVICE_NAME); -73 -74 return SUCCESS; -75} -76 -77static void __exit chardev_exit(void) -78{ -79 device_destroy(cls, MKDEV(major, 0)); -80 class_destroy(cls); -81 -82 /* Unregister the device */ -83 unregister_chrdev(major, DEVICE_NAME); -84} -85 -86/* Methods */ -87 -88/** -89 * Called when a process tried to open the device file, like -90 * cat /dev/key_state -91 */ -92static int device_open(struct inode *inode, struct file *file) -93{ -94 if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN)) -95 return -EBUSY; -96 -97 sprintf(msg, static_key_enabled(&fkey) ? "enabled\n" : "disabled\n"); -98 -99 pr_info("fastpath 1\n"); -100 if (static_branch_unlikely(&fkey)) -101 pr_alert("do unlikely thing\n"); -102 pr_info("fastpath 2\n"); -103 -104 try_module_get(THIS_MODULE); -105 -106 return SUCCESS; -107} -108 -109/** -110 * Called when a process closes the device file -111 */ -112static int device_release(struct inode *inode, struct file *file) -113{ -114 /* We are now ready for our next caller. */ -115 atomic_set(&already_open, CDEV_NOT_USED); -116 -117 /** -118 * Decrement the usage count, or else once you opened the file, you will -119 * never get rid of the module. -120 */ -121 module_put(THIS_MODULE); -122 -123 return SUCCESS; -124} -125 -126/** -127 * Called when a process, which already opened the dev file, attempts to -128 * read from it. -129 */ -130static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ -131 char __user *buffer, /* buffer to fill with data */ -132 size_t length, /* length of the buffer */ -133 loff_t *offset) -134{ -135 /* Number of the bytes actually written to the buffer */ -136 int bytes_read = 0; -137 const char *msg_ptr = msg; -138 -139 if (!*(msg_ptr + *offset)) { /* We are at the end of the message */ -140 *offset = 0; /* reset the offset */ -141 return 0; /* signify end of file */ -142 } -143 -144 msg_ptr += *offset; -145 -146 /* Actually put the data into the buffer */ -147 while (length && *msg_ptr) { -148 /** -149 * The buffer is in the user data segment, not the kernel -150 * segment so "*" assignment won't work. We have to use -151 * put_user which copies data from the kernel data segment to -152 * the user data segment. -153 */ -154 put_user(*(msg_ptr++), buffer++); -155 length--; -156 bytes_read++; -157 } -158 -159 *offset += bytes_read; -160 -161 /* Most read functions return the number of bytes put into the buffer. */ -162 return bytes_read; -163} -164 -165/* Called when a process writes to dev file; echo "enable" > /dev/key_state */ -166static ssize_t device_write(struct file *filp, const char __user *buffer, -167 size_t length, loff_t *offset) -168{ -169 char command[10]; -170 -171 if (length > 10) { -172 pr_err("command exceeded 10 char\n"); -173 return -EINVAL; -174 } -175 -176 if (copy_from_user(command, buffer, length)) -177 return -EFAULT; -178 -179 if (strncmp(command, "enable", strlen("enable")) == 0) -180 static_branch_enable(&fkey); -181 else if (strncmp(command, "disable", strlen("disable")) == 0) -182 static_branch_disable(&fkey); -183 else { -184 pr_err("Invalid command: %s\n", command); -185 return -EINVAL; -186 } -187 -188 /* Again, return the number of input characters used. */ -189 return length; -190} -191 -192module_init(chardev_init); -193module_exit(chardev_exit); -194 -195MODULE_LICENSE("GPL");-
To check the state of the static key, we can use the /dev/key_state +
1/* +2 * static_key.c +3 */ +4 +5#include <linux/atomic.h> +6#include <linux/device.h> +7#include <linux/fs.h> +8#include <linux/kernel.h> /* for sprintf() */ +9#include <linux/module.h> +10#include <linux/printk.h> +11#include <linux/types.h> +12#include <linux/uaccess.h> /* for get_user and put_user */ +13#include <linux/jump_label.h> /* for static key macros */ +14#include <linux/version.h> +15 +16#include <asm/errno.h> +17 +18static int device_open(struct inode *inode, struct file *file); +19static int device_release(struct inode *inode, struct file *file); +20static ssize_t device_read(struct file *file, char __user *buf, size_t count, +21 loff_t *ppos); +22static ssize_t device_write(struct file *file, const char __user *buf, +23 size_t count, loff_t *ppos); +24 +25#define SUCCESS 0 +26#define DEVICE_NAME "key_state" +27#define BUF_LEN 10 +28 +29static int major; +30 +31enum { +32 CDEV_NOT_USED = 0, +33 CDEV_EXCLUSIVE_OPEN = 1, +34}; +35 +36static atomic_t already_open = ATOMIC_INIT(CDEV_NOT_USED); +37 +38static char msg[BUF_LEN + 1]; +39 +40static struct class *cls; +41 +42static DEFINE_STATIC_KEY_FALSE(fkey); +43 +44static struct file_operations chardev_fops = { +45#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) +46 .owner = THIS_MODULE, +47#endif +48 .open = device_open, +49 .release = device_release, +50 .read = device_read, +51 .write = device_write, +52}; +53 +54static int __init chardev_init(void) +55{ +56 major = register_chrdev(0, DEVICE_NAME, &chardev_fops); +57 if (major < 0) { +58 pr_alert("Registering char device failed with %d\n", major); +59 return major; +60 } +61 +62 pr_info("I was assigned major number %d\n", major); +63 +64#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) +65 cls = class_create(THIS_MODULE, DEVICE_NAME); +66#else +67 cls = class_create(DEVICE_NAME); +68#endif +69 +70 device_create(cls, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); +71 +72 pr_info("Device created on /dev/%s\n", DEVICE_NAME); +73 +74 return SUCCESS; +75} +76 +77static void __exit chardev_exit(void) +78{ +79 device_destroy(cls, MKDEV(major, 0)); +80 class_destroy(cls); +81 +82 /* Unregister the device */ +83 unregister_chrdev(major, DEVICE_NAME); +84} +85 +86/* Methods */ +87 +88/** +89 * Called when a process tried to open the device file, like +90 * cat /dev/key_state +91 */ +92static int device_open(struct inode *inode, struct file *file) +93{ +94 if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN)) +95 return -EBUSY; +96 +97 sprintf(msg, static_key_enabled(&fkey) ? "enabled\n" : "disabled\n"); +98 +99 pr_info("fastpath 1\n"); +100 if (static_branch_unlikely(&fkey)) +101 pr_alert("do unlikely thing\n"); +102 pr_info("fastpath 2\n"); +103 +104 try_module_get(THIS_MODULE); +105 +106 return SUCCESS; +107} +108 +109/** +110 * Called when a process closes the device file +111 */ +112static int device_release(struct inode *inode, struct file *file) +113{ +114 /* We are now ready for our next caller. */ +115 atomic_set(&already_open, CDEV_NOT_USED); +116 +117 /** +118 * Decrement the usage count, or else once you opened the file, you will +119 * never get rid of the module. +120 */ +121 module_put(THIS_MODULE); +122 +123 return SUCCESS; +124} +125 +126/** +127 * Called when a process, which already opened the dev file, attempts to +128 * read from it. +129 */ +130static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ +131 char __user *buffer, /* buffer to fill with data */ +132 size_t length, /* length of the buffer */ +133 loff_t *offset) +134{ +135 /* Number of the bytes actually written to the buffer */ +136 int bytes_read = 0; +137 const char *msg_ptr = msg; +138 +139 if (!*(msg_ptr + *offset)) { /* We are at the end of the message */ +140 *offset = 0; /* reset the offset */ +141 return 0; /* signify end of file */ +142 } +143 +144 msg_ptr += *offset; +145 +146 /* Actually put the data into the buffer */ +147 while (length && *msg_ptr) { +148 /** +149 * The buffer is in the user data segment, not the kernel +150 * segment so "*" assignment won't work. We have to use +151 * put_user which copies data from the kernel data segment to +152 * the user data segment. +153 */ +154 put_user(*(msg_ptr++), buffer++); +155 length--; +156 bytes_read++; +157 } +158 +159 *offset += bytes_read; +160 +161 /* Most read functions return the number of bytes put into the buffer. */ +162 return bytes_read; +163} +164 +165/* Called when a process writes to dev file; echo "enable" > /dev/key_state */ +166static ssize_t device_write(struct file *filp, const char __user *buffer, +167 size_t length, loff_t *offset) +168{ +169 char command[10]; +170 +171 if (length > 10) { +172 pr_err("command exceeded 10 char\n"); +173 return -EINVAL; +174 } +175 +176 if (copy_from_user(command, buffer, length)) +177 return -EFAULT; +178 +179 if (strncmp(command, "enable", strlen("enable")) == 0) +180 static_branch_enable(&fkey); +181 else if (strncmp(command, "disable", strlen("disable")) == 0) +182 static_branch_disable(&fkey); +183 else { +184 pr_err("Invalid command: %s\n", command); +185 return -EINVAL; +186 } +187 +188 /* Again, return the number of input characters used. */ +189 return length; +190} +191 +192module_init(chardev_init); +193module_exit(chardev_exit); +194 +195MODULE_LICENSE("GPL");+
To check the state of the static key, we can use the /dev/key_state interface.
-
1cat /dev/key_state-
This will display the current state of the key, which is disabled by default. -
To change the state of the static key, we can perform a write operation on the +
1cat /dev/key_state+
This will display the current state of the key, which is disabled by default. +
To change the state of the static key, we can perform a write operation on the file:
-
1echo enable > /dev/key_state-
This will enable the static key, causing the code path to switch from the fastpath +
1echo enable > /dev/key_state+
This will enable the static key, causing the code path to switch from the fastpath to the slowpath. -
In some cases, the key is enabled or disabled at initialization and never changed, +
In some cases, the key is enabled or disabled at initialization and never changed,
we can declare a static key as read-only, which means that it can only be toggled in
the module init function. To declare a read-only static key, we can use the
DEFINE_STATIC_KEY_FALSE_RO
or DEFINE_STATIC_KEY_TRUE_RO
macro instead. Attempts to change the key at runtime will result in a page fault. For
more information, see Static keys
-
+
-
+
-
You can not do that. In a kernel module, you can only use kernel functions which are -the functions you can see in /proc/kallsyms. -
+
You can not do that. In a kernel module, you can only use kernel functions which are +the functions you can see in /proc/kallsyms. +
-
You might need to do this for a short time and that is OK, but if you do not enable +
You might need to do this for a short time and that is OK, but if you do not enable them afterwards, your system will be stuck and you will have to power it off. -
+
-
For those deeply interested in kernel programming, kernelnewbies.org and the +
For those deeply interested in kernel programming, kernelnewbies.org and the Documentation subdirectory within the kernel source code are highly recommended. Although the latter may not always be straightforward, it serves as a valuable initial step for further exploration. Echoing Linus Torvalds’ perspective, the most effective method to understand the kernel is through personal examination of the source code. -
Contributions to this guide are welcome, especially if there are any significant +
Contributions to this guide are welcome, especially if there are any significant inaccuracies identified. To contribute or report an issue, please initiate an issue at https://github.com/sysprog21/lkmpg. Pull requests are greatly appreciated. -
Happy hacking! +
Happy hacking!
- 1The goal of threaded interrupts is to push more of the work to separate threads, so that the
+ 1The goal of threaded interrupts is to push more of the work to separate threads, so that the
minimum needed for acknowledging an interrupt is reduced, and therefore the time spent handling
the interrupt (where it can’t handle any other interrupts at the same time) is reduced. See
https://lwn.net/Articles/302043/.