diff --git a/index.html b/index.html index 686b1a6..033921e 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 @@ electronic.
Derivative works and translations of this document must be placed under the Open Software License, and the original copyright notice must remain intact. If you have contributed new material to this book, you must make the material and source -code available for your revisions. Please make revisions and updates available directly +code available for your revisions. Please make revisions and updates available directly to the document maintainer, Jim Huang <jserv@ccns.ncku.edu.tw>. This will allow for the merging of updates and provide consistent revisions to the Linux community. @@ -142,18 +143,19 @@ revised the LaTeX document.
- 2011eric, 25077667, Arush Sharma, asas1asas200, Benno Bielmeier, Brad -Baker, ccs100203, Chih-Yu Chen, ChinYikMing, Cyril Brulebois, Daniele -Paolo Scarpazza, David Porter, demonsome, Dimo Velev, Ekang Monyet, -fennecJ, Francois Audeon, gagachang, Gilad Reti, Horst Schirmeier, -Hsin-Hsiang Peng, Ignacio Martin, JianXing Wu, linD026, Marconi Jiang, -RinHizakura, Roman Lakeev, Stacy Prowell, Tucker Polomik, VxTeemo, -Wei-Lun Tsai, xatier, Ylowy.
-
+ 2011eric, 25077667, Arush Sharma, asas1asas200, Benno Bielmeier, Bob Lee, +Brad Baker, ccs100203, Chih-Yu Chen, Ching-Hua (Vivian) Lin, +ChinYikMing, Cyril Brulebois, Daniele Paolo Scarpazza, David Porter, +demonsome, Dimo Velev, Ekang Monyet, fennecJ, Francois Audeon, +gagachang, Gilad Reti, Horst Schirmeier, Hsin-Hsiang Peng, Ignacio Martin, +JianXing Wu, linD026, lyctw, manbing, Marconi Jiang, mengxinayan, +RinHizakura, Roman Lakeev, Stacy Prowell, Steven Lung, Tristan Lelong, +Tucker Polomik, VxTeemo, Wei-Lun Tsai, xatier, Ylowy. ++
So, you want to write a kernel module. You know C, you have written a few normal programs to run as processes, and now you want to get to where the real action is, to @@ -187,11 +189,11 @@ new functionality.
To discover what modules are already loaded within your current kernel use the command
lsmod
.
-
1sudo lsmod+ +
1sudo lsmod
Modules are stored within the file /proc/modules, so you can also see them with:
@@ -221,13 +223,13 @@ thereafter. a different kernel unless you enable CONFIG_MODVERSIONS
in the kernel. We will not go into module versioning until later in this
guide. Until we cover modversions, the examples in the guide may not
+
+
+
work if you are running a kernel with modversioning turned on. However,
most stock Linux distribution kernels come with it turned on. If you are
having trouble loading the modules because of versioning errors, compile
a kernel with modversioning turned off.
-
-
-
Using X Window System. It is highly recommended that you extract, @@ -254,11 +256,11 @@ kernel.
1sudo pacman -S linux-headers
This will tell you what kernel header files are available. Then for example: -
-1sudo apt-get install kmod linux-headers-5.4.0-80-generic+ +
1sudo apt-get install kmod linux-headers-5.4.0-80-generic
In Makefile, $(CURDIR) can set to the absolute pathname of the current working directory(after all -C options are processed, if any). See more about CURDIR in GNU make manual.
And finally, just run make directly.
- - -
1make
If there is no PWD := $(CURDIR) statement in Makefile, then it may not compile @@ -5540,8 +5542,652 @@ and a password. 198MODULE_LICENSE("GPL");
-
Up to this point we have seen all kinds of modules doing all kinds of things, but there +
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
+ input_allocate_device()
+
and sets up input bitfields, device id, version, etc. After that, registers it by calling
+ input_register_device()
+
.
+
Here is an example, vinput, It is an API to allow easy
+development of virtual input drivers. The drivers needs to export a
+ vinput_device()
+
that contains the virtual device name and
+ vinput_ops
+
structure that describes:
+
init()
+
+ send()
+
+ read()
+
Then using vinput_register_device()
+
and vinput_unregister_device()
+
will add a new device to the list of support virtual input devices.
+
+ + + +
+1int init(struct vinput *);+
This function is passed a 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);+
This function will receive a user string to interpret and inject the event using the
+ input_report_XXXX
+
or input_event
+
call. The string is already copied from user.
+
+
+1int read(struct vinput *, char *, int);+
This function is used for debugging and should fill the buffer parameter with the +last event sent in the virtual input device format. The buffer will then be copied to +user. +
vinput devices are created and destroyed using sysfs. And, event injection is +done through a /dev node. The device name will be used by the userland to +export a new virtual input device. To create a vinputX sysfs entry and /dev +node. +
+
+1echo "vkbd" | sudo tee /sys/class/vinput/export+
To unexport the device, just echo its id in unexport: +
+
+1echo "0" | sudo tee /sys/class/vinput/unexport+ +
+
+1#ifndef VINPUT_H +2#define VINPUT_H +3 +4#include <linux/input.h> +5#include <linux/spinlock.h> +6 +7#define VINPUT_MAX_LEN 128 +8#define MAX_VINPUT 32 +9#define VINPUT_MINORS MAX_VINPUT +10 +11#define dev_to_vinput(dev) container_of(dev, struct vinput, dev) +12 +13struct vinput_device; +14 +15struct vinput { +16 long id; +17 long devno; +18 long last_entry; +19 spinlock_t lock; +20 +21 void *priv_data; +22 +23 struct device dev; +24 struct list_head list; +25 struct input_dev *input; +26 struct vinput_device *type; +27}; +28 +29struct vinput_ops { +30 int (*init)(struct vinput *); +31 int (*kill)(struct vinput *); +32 int (*send)(struct vinput *, char *, int); +33 int (*read)(struct vinput *, char *, int); +34}; +35 +36struct vinput_device { +37 char name[16]; +38 struct list_head list; +39 struct vinput_ops *ops; +40}; +41 +42int vinput_register(struct vinput_device *dev); +43void vinput_unregister(struct vinput_device *dev); +44 +45#endif+ +
+
+1#include <linux/cdev.h> +2#include <linux/input.h> +3#include <linux/module.h> +4#include <linux/slab.h> +5#include <linux/spinlock.h> +6 +7#include <asm/uaccess.h> +8 +9#include "vinput.h" +10 +11#define DRIVER_NAME "vinput" +12 +13#define dev_to_vinput(dev) container_of(dev, struct vinput, dev) +14 +15static DECLARE_BITMAP(vinput_ids, VINPUT_MINORS); +16 +17static LIST_HEAD(vinput_devices); +18static LIST_HEAD(vinput_vdevices); +19 +20static int vinput_dev; +21static struct spinlock vinput_lock; +22static struct class vinput_class; +23 +24/* Search the name of vinput device in the vinput_devices linked list, +25 * which added at vinput_register(). +26 */ +27static struct vinput_device *vinput_get_device_by_type(const char *type) +28{ +29 int found = 0; +30 struct vinput_device *vinput; +31 struct list_head *curr; +32 +33 spin_lock(&vinput_lock); +34 list_for_each (curr, &vinput_devices) { +35 vinput = list_entry(curr, struct vinput_device, list); +36 if (vinput && strncmp(type, vinput->name, strlen(vinput->name)) == 0) { +37 found = 1; +38 break; +39 } +40 } +41 spin_unlock(&vinput_lock); +42 +43 if (found) +44 return vinput; +45 return ERR_PTR(-ENODEV); +46} +47 +48/* Search the id of virtual device in the vinput_vdevices linked list, +49 * which added at vinput_alloc_vdevice(). +50 */ +51static struct vinput *vinput_get_vdevice_by_id(long id) +52{ +53 struct vinput *vinput = NULL; +54 struct list_head *curr; +55 +56 spin_lock(&vinput_lock); +57 list_for_each (curr, &vinput_vdevices) { +58 vinput = list_entry(curr, struct vinput, list); +59 if (vinput && vinput->id == id) +60 break; +61 } +62 spin_unlock(&vinput_lock); +63 +64 if (vinput && vinput->id == id) +65 return vinput; +66 return ERR_PTR(-ENODEV); +67} +68 +69static int vinput_open(struct inode *inode, struct file *file) +70{ +71 int err = 0; +72 struct vinput *vinput = NULL; +73 +74 vinput = vinput_get_vdevice_by_id(iminor(inode)); +75 +76 if (IS_ERR(vinput)) +77 err = PTR_ERR(vinput); +78 else +79 file->private_data = vinput; +80 +81 return err; +82} +83 +84static int vinput_release(struct inode *inode, struct file *file) +85{ +86 return 0; +87} +88 +89static ssize_t vinput_read(struct file *file, char __user *buffer, size_t count, +90 loff_t *offset) +91{ +92 int len; +93 char buff[VINPUT_MAX_LEN + 1]; +94 struct vinput *vinput = file->private_data; +95 +96 len = vinput->type->ops->read(vinput, buff, count); +97 +98 if (*offset > len) +99 count = 0; +100 else if (count + *offset > VINPUT_MAX_LEN) +101 count = len - *offset; +102 +103 if (raw_copy_to_user(buffer, buff + *offset, count)) +104 count = -EFAULT; +105 +106 *offset += count; +107 +108 return count; +109} +110 +111static ssize_t vinput_write(struct file *file, const char __user *buffer, +112 size_t count, loff_t *offset) +113{ +114 char buff[VINPUT_MAX_LEN + 1]; +115 struct vinput *vinput = file->private_data; +116 +117 memset(buff, 0, sizeof(char) * (VINPUT_MAX_LEN + 1)); +118 +119 if (count > VINPUT_MAX_LEN) { +120 dev_warn(&vinput->dev, "Too long. %d bytes allowed\n", VINPUT_MAX_LEN); +121 return -EINVAL; +122 } +123 +124 if (raw_copy_from_user(buff, buffer, count)) +125 return -EFAULT; +126 +127 return vinput->type->ops->send(vinput, buff, count); +128} +129 +130static const struct file_operations vinput_fops = { +131 .owner = THIS_MODULE, +132 .open = vinput_open, +133 .release = vinput_release, +134 .read = vinput_read, +135 .write = vinput_write, +136}; +137 +138static void vinput_unregister_vdevice(struct vinput *vinput) +139{ +140 input_unregister_device(vinput->input); +141 if (vinput->type->ops->kill) +142 vinput->type->ops->kill(vinput); +143} +144 +145static void vinput_destroy_vdevice(struct vinput *vinput) +146{ +147 /* Remove from the list first */ +148 spin_lock(&vinput_lock); +149 list_del(&vinput->list); +150 clear_bit(vinput->id, vinput_ids); +151 spin_unlock(&vinput_lock); +152 +153 module_put(THIS_MODULE); +154 +155 kfree(vinput); +156} +157 +158static void vinput_release_dev(struct device *dev) +159{ +160 struct vinput *vinput = dev_to_vinput(dev); +161 int id = vinput->id; +162 +163 vinput_destroy_vdevice(vinput); +164 +165 pr_debug("released vinput%d.\n", id); +166} +167 +168static struct vinput *vinput_alloc_vdevice(void) +169{ +170 int err; +171 struct vinput *vinput = kzalloc(sizeof(struct vinput), GFP_KERNEL); +172 +173 try_module_get(THIS_MODULE); +174 +175 memset(vinput, 0, sizeof(struct vinput)); +176 +177 spin_lock_init(&vinput->lock); +178 +179 spin_lock(&vinput_lock); +180 vinput->id = find_first_zero_bit(vinput_ids, VINPUT_MINORS); +181 if (vinput->id >= VINPUT_MINORS) { +182 err = -ENOBUFS; +183 goto fail_id; +184 } +185 set_bit(vinput->id, vinput_ids); +186 list_add(&vinput->list, &vinput_vdevices); +187 spin_unlock(&vinput_lock); +188 +189 /* allocate the input device */ +190 vinput->input = input_allocate_device(); +191 if (vinput->input == NULL) { +192 pr_err("vinput: Cannot allocate vinput input device\n"); +193 err = -ENOMEM; +194 goto fail_input_dev; +195 } +196 +197 /* initialize device */ +198 vinput->dev.class = &vinput_class; +199 vinput->dev.release = vinput_release_dev; +200 vinput->dev.devt = MKDEV(vinput_dev, vinput->id); +201 dev_set_name(&vinput->dev, DRIVER_NAME "%lu", vinput->id); +202 +203 return vinput; +204 +205fail_input_dev: +206 spin_lock(&vinput_lock); +207 list_del(&vinput->list); +208fail_id: +209 spin_unlock(&vinput_lock); +210 module_put(THIS_MODULE); +211 kfree(vinput); +212 +213 return ERR_PTR(err); +214} +215 +216static int vinput_register_vdevice(struct vinput *vinput) +217{ +218 int err = 0; +219 +220 /* register the input device */ +221 vinput->input->name = vinput->type->name; +222 vinput->input->phys = "vinput"; +223 vinput->input->dev.parent = &vinput->dev; +224 +225 vinput->input->id.bustype = BUS_VIRTUAL; +226 vinput->input->id.product = 0x0000; +227 vinput->input->id.vendor = 0x0000; +228 vinput->input->id.version = 0x0000; +229 +230 err = vinput->type->ops->init(vinput); +231 +232 if (err == 0) +233 dev_info(&vinput->dev, "Registered virtual input %s %ld\n", +234 vinput->type->name, vinput->id); +235 +236 return err; +237} +238 +239static ssize_t export_store(struct class *class, struct class_attribute *attr, +240 const char *buf, size_t len) +241{ +242 int err; +243 struct vinput *vinput; +244 struct vinput_device *device; +245 +246 device = vinput_get_device_by_type(buf); +247 if (IS_ERR(device)) { +248 pr_info("vinput: This virtual device isn't registered\n"); +249 err = PTR_ERR(device); +250 goto fail; +251 } +252 +253 vinput = vinput_alloc_vdevice(); +254 if (IS_ERR(vinput)) { +255 err = PTR_ERR(vinput); +256 goto fail; +257 } +258 +259 vinput->type = device; +260 err = device_register(&vinput->dev); +261 if (err < 0) +262 goto fail_register; +263 +264 err = vinput_register_vdevice(vinput); +265 if (err < 0) +266 goto fail_register_vinput; +267 +268 return len; +269 +270fail_register_vinput: +271 device_unregister(&vinput->dev); +272fail_register: +273 vinput_destroy_vdevice(vinput); +274fail: +275 return err; +276} +277/* This macro generates class_attr_export structure and export_store() */ +278static CLASS_ATTR_WO(export); +279 +280static ssize_t unexport_store(struct class *class, struct class_attribute *attr, +281 const char *buf, size_t len) +282{ +283 int err; +284 unsigned long id; +285 struct vinput *vinput; +286 +287 err = kstrtol(buf, 10, &id); +288 if (err) { +289 err = -EINVAL; +290 goto failed; +291 } +292 +293 vinput = vinput_get_vdevice_by_id(id); +294 if (IS_ERR(vinput)) { +295 pr_err("vinput: No such vinput device %ld\n", id); +296 err = PTR_ERR(vinput); +297 goto failed; +298 } +299 +300 vinput_unregister_vdevice(vinput); +301 device_unregister(&vinput->dev); +302 +303 return len; +304failed: +305 return err; +306} +307/* This macro generates class_attr_unexport structure and unexport_store() */ +308static CLASS_ATTR_WO(unexport); +309 +310static struct attribute *vinput_class_attrs[] = { +311 &class_attr_export.attr, +312 &class_attr_unexport.attr, +313 NULL, +314}; +315 +316/* This macro generates vinput_class_groups structure */ +317ATTRIBUTE_GROUPS(vinput_class); +318 +319static struct class vinput_class = { +320 .name = "vinput", +321 .owner = THIS_MODULE, +322 .class_groups = vinput_class_groups, +323}; +324 +325int vinput_register(struct vinput_device *dev) +326{ +327 spin_lock(&vinput_lock); +328 list_add(&dev->list, &vinput_devices); +329 spin_unlock(&vinput_lock); +330 +331 pr_info("vinput: registered new virtual input device '%s'\n", dev->name); +332 +333 return 0; +334} +335EXPORT_SYMBOL(vinput_register); +336 +337void vinput_unregister(struct vinput_device *dev) +338{ +339 struct list_head *curr, *next; +340 +341 /* Remove from the list first */ +342 spin_lock(&vinput_lock); +343 list_del(&dev->list); +344 spin_unlock(&vinput_lock); +345 +346 /* unregister all devices of this type */ +347 list_for_each_safe (curr, next, &vinput_vdevices) { +348 struct vinput *vinput = list_entry(curr, struct vinput, list); +349 if (vinput && vinput->type == dev) { +350 vinput_unregister_vdevice(vinput); +351 device_unregister(&vinput->dev); +352 } +353 } +354 +355 pr_info("vinput: unregistered virtual input device '%s'\n", dev->name); +356} +357EXPORT_SYMBOL(vinput_unregister); +358 +359static int __init vinput_init(void) +360{ +361 int err = 0; +362 +363 pr_info("vinput: Loading virtual input driver\n"); +364 +365 vinput_dev = register_chrdev(0, DRIVER_NAME, &vinput_fops); +366 if (vinput_dev < 0) { +367 pr_err("vinput: Unable to allocate char dev region\n"); +368 goto failed_alloc; +369 } +370 +371 spin_lock_init(&vinput_lock); +372 +373 err = class_register(&vinput_class); +374 if (err < 0) { +375 pr_err("vinput: Unable to register vinput class\n"); +376 goto failed_class; +377 } +378 +379 return 0; +380failed_class: +381 class_unregister(&vinput_class); +382failed_alloc: +383 return err; +384} +385 +386static void __exit vinput_end(void) +387{ +388 pr_info("vinput: Unloading virtual input driver\n"); +389 +390 unregister_chrdev(vinput_dev, DRIVER_NAME); +391 class_unregister(&vinput_class); +392} +393 +394module_init(vinput_init); +395module_exit(vinput_end); +396 +397MODULE_LICENSE("GPL"); +398MODULE_DESCRIPTION("Emulate input events");+ + + +
Here the virtual keyboard is one of example to use vinput. It supports all
+ KEY_MAX
+
keycodes. The injection format is the KEY_CODE
+
such as defined in include/linux/input.h. A positive value means
+ KEY_PRESS
+
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.
+
Simulate a key press on "g" ( KEY_G
+
= 34):
+
+
+1echo "+34" | sudo tee /dev/vinput0+
Simulate a key release on "g" ( KEY_G
+
= 34:)
+
+
+1echo "-34" | sudo tee /dev/vinput0+ +
+
+1#include <linux/init.h> +2#include <linux/input.h> +3#include <linux/module.h> +4#include <linux/spinlock.h> +5 +6#include "vinput.h" +7 +8#define VINPUT_KBD "vkbd" +9#define VINPUT_RELEASE 0 +10#define VINPUT_PRESS 1 +11 +12static unsigned short vkeymap[KEY_MAX]; +13 +14static int vinput_vkbd_init(struct vinput *vinput) +15{ +16 int i; +17 +18 /* Set up the input bitfield */ +19 vinput->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); +20 vinput->input->keycodesize = sizeof(unsigned short); +21 vinput->input->keycodemax = KEY_MAX; +22 vinput->input->keycode = vkeymap; +23 +24 for (i = 0; i < KEY_MAX; i++) +25 set_bit(vkeymap[i], vinput->input->keybit); +26 +27 /* vinput will help us allocate new input device structure via +28 * input_allocate_device(). So, we can register it straightforwardly. +29 */ +30 return input_register_device(vinput->input); +31} +32 +33static int vinput_vkbd_read(struct vinput *vinput, char *buff, int len) +34{ +35 spin_lock(&vinput->lock); +36 len = snprintf(buff, len, "%+ld\n", vinput->last_entry); +37 spin_unlock(&vinput->lock); +38 +39 return len; +40} +41 +42static int vinput_vkbd_send(struct vinput *vinput, char *buff, int len) +43{ +44 int ret; +45 long key = 0; +46 short type = VINPUT_PRESS; +47 +48 /* Determine which event was received (press or release) +49 * and store the state. +50 */ +51 if (buff[0] == '+') +52 ret = kstrtol(buff + 1, 10, &key); +53 else +54 ret = kstrtol(buff, 10, &key); +55 if (ret) +56 dev_err(&vinput->dev, "error during kstrtol: -%d\n", ret); +57 spin_lock(&vinput->lock); +58 vinput->last_entry = key; +59 spin_unlock(&vinput->lock); +60 +61 if (key < 0) { +62 type = VINPUT_RELEASE; +63 key = -key; +64 } +65 +66 dev_info(&vinput->dev, "Event %s code %ld\n", +67 (type == VINPUT_RELEASE) ? "VINPUT_RELEASE" : "VINPUT_PRESS", key); +68 +69 /* Report the state received to input subsystem. */ +70 input_report_key(vinput->input, key, type); +71 /* Tell input subsystem that it finished the report. */ +72 input_sync(vinput->input); +73 +74 return len; +75} +76 +77static struct vinput_ops vkbd_ops = { +78 .init = vinput_vkbd_init, +79 .send = vinput_vkbd_send, +80 .read = vinput_vkbd_read, +81}; +82 +83static struct vinput_device vkbd_dev = { +84 .name = VINPUT_KBD, +85 .ops = &vkbd_ops, +86}; +87 +88static int __init vkbd_init(void) +89{ +90 int i; +91 +92 for (i = 0; i < KEY_MAX; i++) +93 vkeymap[i] = i; +94 return vinput_register(&vkbd_dev); +95} +96 +97static void __exit vkbd_end(void) +98{ +99 vinput_unregister(&vkbd_dev); +100} +101 +102module_init(vkbd_init); +103module_exit(vkbd_end); +104 +105MODULE_LICENSE("GPL"); +106MODULE_DESCRIPTION("Emulate keyboard input events through /dev/vinput");+
+
+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 a device model was added. An example is shown below, and you can @@ -5549,112 +6195,115 @@ use this as a template to add your own suspend, resume or other interface functions.
-
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 .owner = THIS_MODULE, -67 .pm = &devicemodel_pm_ops, -68 }, -69 .probe = devicemodel_probe, -70 .remove = devicemodel_remove, -71}; -72 -73static int devicemodel_init(void) -74{ -75 int ret; -76 -77 pr_info("devicemodel init\n"); -78 -79 ret = platform_driver_register(&devicemodel_driver); -80 -81 if (ret) { -82 pr_err("Unable to register driver\n"); -83 return ret; -84 } -85 -86 return 0; -87} -88 -89static void devicemodel_exit(void) -90{ -91 pr_info("devicemodel exit\n"); -92 platform_driver_unregister(&devicemodel_driver); -93} -94 -95module_init(devicemodel_init); -96module_exit(devicemodel_exit); -97 -98MODULE_LICENSE("GPL"); -99MODULE_DESCRIPTION("Linux Device Model example");-
+
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 .owner = THIS_MODULE, +67 .pm = &devicemodel_pm_ops, +68 }, +69 .probe = devicemodel_probe, +70 .remove = devicemodel_remove, +71}; +72 +73static int devicemodel_init(void) +74{ +75 int ret; +76 +77 pr_info("devicemodel init\n"); +78 +79 ret = platform_driver_register(&devicemodel_driver); +80 +81 if (ret) { +82 pr_err("Unable to register driver\n"); +83 return ret; +84 } +85 +86 return 0; +87} +88 +89static void devicemodel_exit(void) +90{ +91 pr_info("devicemodel exit\n"); +92 platform_driver_unregister(&devicemodel_driver); +93} +94 +95module_init(devicemodel_init); +96module_exit(devicemodel_exit); +97 +98MODULE_LICENSE("GPL"); +99MODULE_DESCRIPTION("Linux Device Model example");+
-
+
-
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 @@ -5667,51 +6316,48 @@ you know that the conditions are almost always likely to evaluate as either to succeed.
-
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 When the
+
+
You can not do that. In a kernel module, you can only use kernel functions which are
+ 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 people seriously interested in kernel programming, I recommend kernelnewbies.org
+ For people seriously interested in kernel programming, I recommend kernelnewbies.org
and the Documentation subdirectory within the kernel source code which is not
always easy to understand but can be a starting point for further investigation. Also,
as Linus Torvalds said, the best way to learn the kernel is to read the source code
yourself.
- If you would like to contribute to this guide or notice anything glaringly wrong,
+ If you would like to contribute to this guide or notice anything glaringly wrong,
please create an issue at https://github.com/sysprog21/lkmpg. Your pull requests
will be appreciated.
- Happy hacking!
+ Happy hacking!
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
diff --git a/lkmpg-for-ht.css b/lkmpg-for-ht.css
index af4a55d..7bd2dea 100644
--- a/lkmpg-for-ht.css
+++ b/lkmpg-for-ht.css
@@ -3514,88 +3514,524 @@ pre#fancyvrb83{ border-top: solid 0.4pt; }
pre#fancyvrb83{ border-left: solid 0.4pt; }
pre#fancyvrb83{ border-bottom: solid 0.4pt; }
pre#fancyvrb83{ border-right: solid 0.4pt; }
-span#textcolor2889{color:rgb(0,127,0)}
-span#textcolor2890{color:rgb(0,127,0)}
-span#textcolor2891{color:rgb(0,127,0)}
+span#textcolor2889{color:rgb(43,145,175)}
+span#textcolor2890{color:rgb(0,0,255)}
+span#textcolor2891{color:rgb(0,0,255)}
span#textcolor2892{color:rgb(0,0,255)}
-span#textcolor2893{color:rgb(0,127,0)}
-span#textcolor2894{color:rgb(0,0,255)}
-span#textcolor2895{color:rgb(0,127,0)}
-span#textcolor2896{color:rgb(0,0,255)}
-span#textcolor2897{color:rgb(0,127,0)}
-span#textcolor2898{color:rgb(0,0,255)}
-span#textcolor2899{color:rgb(43,145,175)}
-span#textcolor2900{color:rgb(43,145,175)}
-span#textcolor2901{color:rgb(0,0,255)}
-span#textcolor2902{color:rgb(43,145,175)}
-span#textcolor2903{color:rgb(0,0,255)}
-span#textcolor2904{color:rgb(0,0,255)}
-span#textcolor2905{color:rgb(0,0,255)}
-span#textcolor2906{color:rgb(163,20,20)}
-span#textcolor2907{color:rgb(163,20,20)}
-span#textcolor2908{color:rgb(163,20,20)}
-span#textcolor2909{color:rgb(163,20,20)}
-span#textcolor2910{color:rgb(163,20,20)}
-span#textcolor2911{color:rgb(163,20,20)}
-span#textcolor2912{color:rgb(0,127,0)}
-span#textcolor2913{color:rgb(0,0,255)}
-span#textcolor2914{color:rgb(0,0,255)}
-span#textcolor2915{color:rgb(43,145,175)}
-span#textcolor2916{color:rgb(0,0,255)}
-span#textcolor2917{color:rgb(163,20,20)}
-span#textcolor2918{color:rgb(163,20,20)}
-span#textcolor2919{color:rgb(163,20,20)}
-span#textcolor2920{color:rgb(0,127,0)}
-span#textcolor2921{color:rgb(0,0,255)}
-span#textcolor2922{color:rgb(0,0,255)}
-span#textcolor2923{color:rgb(43,145,175)}
-span#textcolor2924{color:rgb(0,0,255)}
-span#textcolor2925{color:rgb(163,20,20)}
-span#textcolor2926{color:rgb(163,20,20)}
-span#textcolor2927{color:rgb(163,20,20)}
-span#textcolor2928{color:rgb(0,127,0)}
-span#textcolor2929{color:rgb(0,0,255)}
-span#textcolor2930{color:rgb(0,0,255)}
-span#textcolor2931{color:rgb(43,145,175)}
-span#textcolor2932{color:rgb(0,0,255)}
-span#textcolor2933{color:rgb(163,20,20)}
-span#textcolor2934{color:rgb(163,20,20)}
-span#textcolor2935{color:rgb(163,20,20)}
-span#textcolor2936{color:rgb(0,127,0)}
-span#textcolor2937{color:rgb(0,0,255)}
-span#textcolor2938{color:rgb(0,0,255)}
-span#textcolor2939{color:rgb(0,0,255)}
-span#textcolor2940{color:rgb(0,0,255)}
-span#textcolor2941{color:rgb(0,0,255)}
-span#textcolor2942{color:rgb(0,0,255)}
-span#textcolor2943{color:rgb(163,20,20)}
-span#textcolor2944{color:rgb(0,0,255)}
-span#textcolor2945{color:rgb(43,145,175)}
-span#textcolor2946{color:rgb(43,145,175)}
-span#textcolor2947{color:rgb(43,145,175)}
-span#textcolor2948{color:rgb(163,20,20)}
-span#textcolor2949{color:rgb(163,20,20)}
-span#textcolor2950{color:rgb(163,20,20)}
-span#textcolor2951{color:rgb(0,0,255)}
-span#textcolor2952{color:rgb(163,20,20)}
-span#textcolor2953{color:rgb(163,20,20)}
-span#textcolor2954{color:rgb(163,20,20)}
-span#textcolor2955{color:rgb(0,0,255)}
-span#textcolor2956{color:rgb(0,0,255)}
-span#textcolor2957{color:rgb(0,0,255)}
-span#textcolor2958{color:rgb(43,145,175)}
-span#textcolor2959{color:rgb(43,145,175)}
-span#textcolor2960{color:rgb(163,20,20)}
-span#textcolor2961{color:rgb(163,20,20)}
-span#textcolor2962{color:rgb(163,20,20)}
-span#textcolor2963{color:rgb(163,20,20)}
-span#textcolor2964{color:rgb(163,20,20)}
pre#fancyvrb84{padding:5.69054pt;}
pre#fancyvrb84{ border-top: solid 0.4pt; }
pre#fancyvrb84{ border-left: solid 0.4pt; }
pre#fancyvrb84{ border-bottom: solid 0.4pt; }
pre#fancyvrb84{ border-right: solid 0.4pt; }
-span#textcolor2965{color:rgb(0,0,255)}
+span#textcolor2893{color:rgb(43,145,175)}
+span#textcolor2894{color:rgb(0,0,255)}
+span#textcolor2895{color:rgb(43,145,175)}
+span#textcolor2896{color:rgb(43,145,175)}
+pre#fancyvrb85{padding:5.69054pt;}
+pre#fancyvrb85{ border-top: solid 0.4pt; }
+pre#fancyvrb85{ border-left: solid 0.4pt; }
+pre#fancyvrb85{ border-bottom: solid 0.4pt; }
+pre#fancyvrb85{ border-right: solid 0.4pt; }
+span#textcolor2897{color:rgb(43,145,175)}
+span#textcolor2898{color:rgb(0,0,255)}
+span#textcolor2899{color:rgb(43,145,175)}
+span#textcolor2900{color:rgb(43,145,175)}
+pre#fancyvrb86{padding:5.69054pt;}
+pre#fancyvrb86{ border-top: solid 0.4pt; }
+pre#fancyvrb86{ border-left: solid 0.4pt; }
+pre#fancyvrb86{ border-bottom: solid 0.4pt; }
+pre#fancyvrb86{ border-right: solid 0.4pt; }
+span#textcolor2901{color:rgb(163,20,20)}
+pre#fancyvrb87{padding:5.69054pt;}
+pre#fancyvrb87{ border-top: solid 0.4pt; }
+pre#fancyvrb87{ border-left: solid 0.4pt; }
+pre#fancyvrb87{ border-bottom: solid 0.4pt; }
+pre#fancyvrb87{ border-right: solid 0.4pt; }
+span#textcolor2902{color:rgb(163,20,20)}
+pre#fancyvrb88{padding:5.69054pt;}
+pre#fancyvrb88{ border-top: solid 0.4pt; }
+pre#fancyvrb88{ border-left: solid 0.4pt; }
+pre#fancyvrb88{ border-bottom: solid 0.4pt; }
+pre#fancyvrb88{ border-right: solid 0.4pt; }
+span#textcolor2903{color:rgb(0,0,255)}
+span#textcolor2904{color:rgb(0,0,255)}
+span#textcolor2905{color:rgb(0,0,255)}
+span#textcolor2906{color:rgb(0,127,0)}
+span#textcolor2907{color:rgb(0,0,255)}
+span#textcolor2908{color:rgb(0,127,0)}
+span#textcolor2909{color:rgb(0,0,255)}
+span#textcolor2910{color:rgb(0,0,255)}
+span#textcolor2911{color:rgb(0,0,255)}
+span#textcolor2912{color:rgb(0,0,255)}
+span#textcolor2913{color:rgb(0,0,255)}
+span#textcolor2914{color:rgb(0,0,255)}
+span#textcolor2915{color:rgb(43,145,175)}
+span#textcolor2916{color:rgb(43,145,175)}
+span#textcolor2917{color:rgb(43,145,175)}
+span#textcolor2918{color:rgb(43,145,175)}
+span#textcolor2919{color:rgb(0,0,255)}
+span#textcolor2920{color:rgb(0,0,255)}
+span#textcolor2921{color:rgb(0,0,255)}
+span#textcolor2922{color:rgb(0,0,255)}
+span#textcolor2923{color:rgb(0,0,255)}
+span#textcolor2924{color:rgb(43,145,175)}
+span#textcolor2925{color:rgb(0,0,255)}
+span#textcolor2926{color:rgb(43,145,175)}
+span#textcolor2927{color:rgb(0,0,255)}
+span#textcolor2928{color:rgb(43,145,175)}
+span#textcolor2929{color:rgb(0,0,255)}
+span#textcolor2930{color:rgb(43,145,175)}
+span#textcolor2931{color:rgb(43,145,175)}
+span#textcolor2932{color:rgb(43,145,175)}
+span#textcolor2933{color:rgb(0,0,255)}
+span#textcolor2934{color:rgb(43,145,175)}
+span#textcolor2935{color:rgb(43,145,175)}
+span#textcolor2936{color:rgb(0,0,255)}
+span#textcolor2937{color:rgb(43,145,175)}
+span#textcolor2938{color:rgb(0,0,255)}
+span#textcolor2939{color:rgb(0,0,255)}
+span#textcolor2940{color:rgb(43,145,175)}
+span#textcolor2941{color:rgb(0,0,255)}
+span#textcolor2942{color:rgb(43,145,175)}
+span#textcolor2943{color:rgb(0,0,255)}
+span#textcolor2944{color:rgb(0,0,255)}
+pre#fancyvrb89{padding:5.69054pt;}
+pre#fancyvrb89{ border-top: solid 0.4pt; }
+pre#fancyvrb89{ border-left: solid 0.4pt; }
+pre#fancyvrb89{ border-bottom: solid 0.4pt; }
+pre#fancyvrb89{ border-right: solid 0.4pt; }
+span#textcolor2945{color:rgb(0,0,255)}
+span#textcolor2946{color:rgb(0,127,0)}
+span#textcolor2947{color:rgb(0,0,255)}
+span#textcolor2948{color:rgb(0,127,0)}
+span#textcolor2949{color:rgb(0,0,255)}
+span#textcolor2950{color:rgb(0,127,0)}
+span#textcolor2951{color:rgb(0,0,255)}
+span#textcolor2952{color:rgb(0,127,0)}
+span#textcolor2953{color:rgb(0,0,255)}
+span#textcolor2954{color:rgb(0,127,0)}
+span#textcolor2955{color:rgb(0,0,255)}
+span#textcolor2956{color:rgb(0,127,0)}
+span#textcolor2957{color:rgb(0,0,255)}
+span#textcolor2958{color:rgb(0,127,0)}
+span#textcolor2959{color:rgb(0,0,255)}
+span#textcolor2960{color:rgb(0,0,255)}
+span#textcolor2961{color:rgb(0,0,255)}
+span#textcolor2962{color:rgb(0,0,255)}
+span#textcolor2963{color:rgb(0,0,255)}
+span#textcolor2964{color:rgb(0,0,255)}
+span#textcolor2965{color:rgb(43,145,175)}
span#textcolor2966{color:rgb(0,0,255)}
+span#textcolor2967{color:rgb(0,0,255)}
+span#textcolor2968{color:rgb(0,0,255)}
+span#textcolor2969{color:rgb(0,0,255)}
+span#textcolor2970{color:rgb(0,127,0)}
+span#textcolor2971{color:rgb(0,127,0)}
+span#textcolor2972{color:rgb(0,127,0)}
+span#textcolor2973{color:rgb(0,0,255)}
+span#textcolor2974{color:rgb(0,0,255)}
+span#textcolor2975{color:rgb(0,0,255)}
+span#textcolor2976{color:rgb(43,145,175)}
+span#textcolor2977{color:rgb(43,145,175)}
+span#textcolor2978{color:rgb(0,0,255)}
+span#textcolor2979{color:rgb(0,0,255)}
+span#textcolor2980{color:rgb(0,0,255)}
+span#textcolor2981{color:rgb(0,0,255)}
+span#textcolor2982{color:rgb(0,0,255)}
+span#textcolor2983{color:rgb(0,0,255)}
+span#textcolor2984{color:rgb(0,0,255)}
+span#textcolor2985{color:rgb(0,0,255)}
+span#textcolor2986{color:rgb(0,127,0)}
+span#textcolor2987{color:rgb(0,127,0)}
+span#textcolor2988{color:rgb(0,127,0)}
+span#textcolor2989{color:rgb(0,0,255)}
+span#textcolor2990{color:rgb(0,0,255)}
+span#textcolor2991{color:rgb(43,145,175)}
+span#textcolor2992{color:rgb(0,0,255)}
+span#textcolor2993{color:rgb(0,0,255)}
+span#textcolor2994{color:rgb(0,0,255)}
+span#textcolor2995{color:rgb(0,0,255)}
+span#textcolor2996{color:rgb(0,0,255)}
+span#textcolor2997{color:rgb(0,0,255)}
+span#textcolor2998{color:rgb(0,0,255)}
+span#textcolor2999{color:rgb(0,0,255)}
+span#textcolor3000{color:rgb(0,0,255)}
+span#textcolor3001{color:rgb(43,145,175)}
+span#textcolor3002{color:rgb(0,0,255)}
+span#textcolor3003{color:rgb(0,0,255)}
+span#textcolor3004{color:rgb(43,145,175)}
+span#textcolor3005{color:rgb(0,0,255)}
+span#textcolor3006{color:rgb(0,0,255)}
+span#textcolor3007{color:rgb(0,0,255)}
+span#textcolor3008{color:rgb(0,0,255)}
+span#textcolor3009{color:rgb(0,0,255)}
+span#textcolor3010{color:rgb(43,145,175)}
+span#textcolor3011{color:rgb(0,0,255)}
+span#textcolor3012{color:rgb(0,0,255)}
+span#textcolor3013{color:rgb(0,0,255)}
+span#textcolor3014{color:rgb(0,0,255)}
+span#textcolor3015{color:rgb(43,145,175)}
+span#textcolor3016{color:rgb(0,0,255)}
+span#textcolor3017{color:rgb(43,145,175)}
+span#textcolor3018{color:rgb(43,145,175)}
+span#textcolor3019{color:rgb(43,145,175)}
+span#textcolor3020{color:rgb(43,145,175)}
+span#textcolor3021{color:rgb(0,0,255)}
+span#textcolor3022{color:rgb(0,0,255)}
+span#textcolor3023{color:rgb(0,0,255)}
+span#textcolor3024{color:rgb(0,0,255)}
+span#textcolor3025{color:rgb(0,0,255)}
+span#textcolor3026{color:rgb(0,0,255)}
+span#textcolor3027{color:rgb(0,0,255)}
+span#textcolor3028{color:rgb(43,145,175)}
+span#textcolor3029{color:rgb(0,0,255)}
+span#textcolor3030{color:rgb(0,0,255)}
+span#textcolor3031{color:rgb(43,145,175)}
+span#textcolor3032{color:rgb(43,145,175)}
+span#textcolor3033{color:rgb(43,145,175)}
+span#textcolor3034{color:rgb(0,0,255)}
+span#textcolor3035{color:rgb(0,0,255)}
+span#textcolor3036{color:rgb(43,145,175)}
+span#textcolor3037{color:rgb(0,0,255)}
+span#textcolor3038{color:rgb(163,20,20)}
+span#textcolor3039{color:rgb(163,20,20)}
+span#textcolor3040{color:rgb(163,20,20)}
+span#textcolor3041{color:rgb(0,0,255)}
+span#textcolor3042{color:rgb(0,0,255)}
+span#textcolor3043{color:rgb(0,0,255)}
+span#textcolor3044{color:rgb(0,0,255)}
+span#textcolor3045{color:rgb(0,0,255)}
+span#textcolor3046{color:rgb(0,0,255)}
+span#textcolor3047{color:rgb(0,0,255)}
+span#textcolor3048{color:rgb(0,0,255)}
+span#textcolor3049{color:rgb(43,145,175)}
+span#textcolor3050{color:rgb(0,0,255)}
+span#textcolor3051{color:rgb(0,0,255)}
+span#textcolor3052{color:rgb(0,0,255)}
+span#textcolor3053{color:rgb(43,145,175)}
+span#textcolor3054{color:rgb(0,0,255)}
+span#textcolor3055{color:rgb(0,127,0)}
+span#textcolor3056{color:rgb(0,0,255)}
+span#textcolor3057{color:rgb(43,145,175)}
+span#textcolor3058{color:rgb(0,0,255)}
+span#textcolor3059{color:rgb(0,0,255)}
+span#textcolor3060{color:rgb(43,145,175)}
+span#textcolor3061{color:rgb(163,20,20)}
+span#textcolor3062{color:rgb(163,20,20)}
+span#textcolor3063{color:rgb(163,20,20)}
+span#textcolor3064{color:rgb(0,0,255)}
+span#textcolor3065{color:rgb(0,0,255)}
+span#textcolor3066{color:rgb(43,145,175)}
+span#textcolor3067{color:rgb(43,145,175)}
+span#textcolor3068{color:rgb(0,0,255)}
+span#textcolor3069{color:rgb(0,0,255)}
+span#textcolor3070{color:rgb(0,0,255)}
+span#textcolor3071{color:rgb(0,0,255)}
+span#textcolor3072{color:rgb(0,0,255)}
+span#textcolor3073{color:rgb(0,0,255)}
+span#textcolor3074{color:rgb(0,0,255)}
+span#textcolor3075{color:rgb(0,127,0)}
+span#textcolor3076{color:rgb(0,0,255)}
+span#textcolor3077{color:rgb(163,20,20)}
+span#textcolor3078{color:rgb(163,20,20)}
+span#textcolor3079{color:rgb(163,20,20)}
+span#textcolor3080{color:rgb(0,0,255)}
+span#textcolor3081{color:rgb(0,127,0)}
+span#textcolor3082{color:rgb(163,20,20)}
+span#textcolor3083{color:rgb(0,0,255)}
+span#textcolor3084{color:rgb(0,0,255)}
+span#textcolor3085{color:rgb(0,0,255)}
+span#textcolor3086{color:rgb(43,145,175)}
+span#textcolor3087{color:rgb(0,0,255)}
+span#textcolor3088{color:rgb(43,145,175)}
+span#textcolor3089{color:rgb(0,127,0)}
+span#textcolor3090{color:rgb(163,20,20)}
+span#textcolor3091{color:rgb(0,0,255)}
+span#textcolor3092{color:rgb(163,20,20)}
+span#textcolor3093{color:rgb(163,20,20)}
+span#textcolor3094{color:rgb(163,20,20)}
+span#textcolor3095{color:rgb(0,0,255)}
+span#textcolor3096{color:rgb(0,0,255)}
+span#textcolor3097{color:rgb(43,145,175)}
+span#textcolor3098{color:rgb(0,0,255)}
+span#textcolor3099{color:rgb(0,0,255)}
+span#textcolor3100{color:rgb(0,0,255)}
+span#textcolor3101{color:rgb(43,145,175)}
+span#textcolor3102{color:rgb(43,145,175)}
+span#textcolor3103{color:rgb(43,145,175)}
+span#textcolor3104{color:rgb(0,0,255)}
+span#textcolor3105{color:rgb(0,0,255)}
+span#textcolor3106{color:rgb(0,0,255)}
+span#textcolor3107{color:rgb(163,20,20)}
+span#textcolor3108{color:rgb(163,20,20)}
+span#textcolor3109{color:rgb(163,20,20)}
+span#textcolor3110{color:rgb(0,0,255)}
+span#textcolor3111{color:rgb(0,0,255)}
+span#textcolor3112{color:rgb(0,0,255)}
+span#textcolor3113{color:rgb(0,0,255)}
+span#textcolor3114{color:rgb(0,0,255)}
+span#textcolor3115{color:rgb(0,0,255)}
+span#textcolor3116{color:rgb(0,0,255)}
+span#textcolor3117{color:rgb(0,0,255)}
+span#textcolor3118{color:rgb(0,0,255)}
+span#textcolor3119{color:rgb(0,127,0)}
+span#textcolor3120{color:rgb(0,0,255)}
+span#textcolor3121{color:rgb(0,0,255)}
+span#textcolor3122{color:rgb(43,145,175)}
+span#textcolor3123{color:rgb(0,0,255)}
+span#textcolor3124{color:rgb(0,0,255)}
+span#textcolor3125{color:rgb(0,0,255)}
+span#textcolor3126{color:rgb(43,145,175)}
+span#textcolor3127{color:rgb(43,145,175)}
+span#textcolor3128{color:rgb(43,145,175)}
+span#textcolor3129{color:rgb(43,145,175)}
+span#textcolor3130{color:rgb(43,145,175)}
+span#textcolor3131{color:rgb(0,0,255)}
+span#textcolor3132{color:rgb(0,0,255)}
+span#textcolor3133{color:rgb(0,0,255)}
+span#textcolor3134{color:rgb(0,0,255)}
+span#textcolor3135{color:rgb(163,20,20)}
+span#textcolor3136{color:rgb(163,20,20)}
+span#textcolor3137{color:rgb(163,20,20)}
+span#textcolor3138{color:rgb(0,0,255)}
+span#textcolor3139{color:rgb(0,0,255)}
+span#textcolor3140{color:rgb(0,0,255)}
+span#textcolor3141{color:rgb(0,127,0)}
+span#textcolor3142{color:rgb(0,0,255)}
+span#textcolor3143{color:rgb(0,0,255)}
+span#textcolor3144{color:rgb(0,0,255)}
+span#textcolor3145{color:rgb(0,127,0)}
+span#textcolor3146{color:rgb(0,0,255)}
+span#textcolor3147{color:rgb(0,0,255)}
+span#textcolor3148{color:rgb(163,20,20)}
+span#textcolor3149{color:rgb(43,145,175)}
+span#textcolor3150{color:rgb(0,0,255)}
+span#textcolor3151{color:rgb(163,20,20)}
+span#textcolor3152{color:rgb(163,20,20)}
+span#textcolor3153{color:rgb(163,20,20)}
+span#textcolor3154{color:rgb(0,0,255)}
+span#textcolor3155{color:rgb(43,145,175)}
+span#textcolor3156{color:rgb(0,0,255)}
+span#textcolor3157{color:rgb(0,0,255)}
+span#textcolor3158{color:rgb(0,127,0)}
+span#textcolor3159{color:rgb(0,127,0)}
+span#textcolor3160{color:rgb(0,0,255)}
+span#textcolor3161{color:rgb(0,0,255)}
+span#textcolor3162{color:rgb(0,0,255)}
+span#textcolor3163{color:rgb(163,20,20)}
+span#textcolor3164{color:rgb(163,20,20)}
+span#textcolor3165{color:rgb(163,20,20)}
+span#textcolor3166{color:rgb(0,0,255)}
+span#textcolor3167{color:rgb(43,145,175)}
+span#textcolor3168{color:rgb(43,145,175)}
+span#textcolor3169{color:rgb(43,145,175)}
+span#textcolor3170{color:rgb(163,20,20)}
+span#textcolor3171{color:rgb(163,20,20)}
+span#textcolor3172{color:rgb(163,20,20)}
+span#textcolor3173{color:rgb(0,0,255)}
+span#textcolor3174{color:rgb(163,20,20)}
+span#textcolor3175{color:rgb(163,20,20)}
+span#textcolor3176{color:rgb(163,20,20)}
+span#textcolor3177{color:rgb(0,0,255)}
+span#textcolor3178{color:rgb(0,0,255)}
+span#textcolor3179{color:rgb(163,20,20)}
+span#textcolor3180{color:rgb(163,20,20)}
+span#textcolor3181{color:rgb(163,20,20)}
+span#textcolor3182{color:rgb(0,0,255)}
+span#textcolor3183{color:rgb(0,0,255)}
+span#textcolor3184{color:rgb(0,0,255)}
+span#textcolor3185{color:rgb(0,0,255)}
+span#textcolor3186{color:rgb(43,145,175)}
+span#textcolor3187{color:rgb(43,145,175)}
+span#textcolor3188{color:rgb(163,20,20)}
+span#textcolor3189{color:rgb(163,20,20)}
+span#textcolor3190{color:rgb(163,20,20)}
+span#textcolor3191{color:rgb(163,20,20)}
+span#textcolor3192{color:rgb(163,20,20)}
+pre#fancyvrb90{padding:5.69054pt;}
+pre#fancyvrb90{ border-top: solid 0.4pt; }
+pre#fancyvrb90{ border-left: solid 0.4pt; }
+pre#fancyvrb90{ border-bottom: solid 0.4pt; }
+pre#fancyvrb90{ border-right: solid 0.4pt; }
+span#textcolor3193{color:rgb(163,20,20)}
+pre#fancyvrb91{padding:5.69054pt;}
+pre#fancyvrb91{ border-top: solid 0.4pt; }
+pre#fancyvrb91{ border-left: solid 0.4pt; }
+pre#fancyvrb91{ border-bottom: solid 0.4pt; }
+pre#fancyvrb91{ border-right: solid 0.4pt; }
+span#textcolor3194{color:rgb(163,20,20)}
+pre#fancyvrb92{padding:5.69054pt;}
+pre#fancyvrb92{ border-top: solid 0.4pt; }
+pre#fancyvrb92{ border-left: solid 0.4pt; }
+pre#fancyvrb92{ border-bottom: solid 0.4pt; }
+pre#fancyvrb92{ border-right: solid 0.4pt; }
+span#textcolor3195{color:rgb(0,0,255)}
+span#textcolor3196{color:rgb(0,127,0)}
+span#textcolor3197{color:rgb(0,0,255)}
+span#textcolor3198{color:rgb(0,127,0)}
+span#textcolor3199{color:rgb(0,0,255)}
+span#textcolor3200{color:rgb(0,127,0)}
+span#textcolor3201{color:rgb(0,0,255)}
+span#textcolor3202{color:rgb(0,127,0)}
+span#textcolor3203{color:rgb(0,0,255)}
+span#textcolor3204{color:rgb(0,127,0)}
+span#textcolor3205{color:rgb(0,0,255)}
+span#textcolor3206{color:rgb(0,0,255)}
+span#textcolor3207{color:rgb(0,0,255)}
+span#textcolor3208{color:rgb(0,0,255)}
+span#textcolor3209{color:rgb(43,145,175)}
+span#textcolor3210{color:rgb(43,145,175)}
+span#textcolor3211{color:rgb(0,0,255)}
+span#textcolor3212{color:rgb(43,145,175)}
+span#textcolor3213{color:rgb(0,0,255)}
+span#textcolor3214{color:rgb(43,145,175)}
+span#textcolor3215{color:rgb(0,127,0)}
+span#textcolor3216{color:rgb(0,0,255)}
+span#textcolor3217{color:rgb(43,145,175)}
+span#textcolor3218{color:rgb(43,145,175)}
+span#textcolor3219{color:rgb(0,0,255)}
+span#textcolor3220{color:rgb(0,127,0)}
+span#textcolor3221{color:rgb(0,127,0)}
+span#textcolor3222{color:rgb(0,127,0)}
+span#textcolor3223{color:rgb(0,0,255)}
+span#textcolor3224{color:rgb(0,0,255)}
+span#textcolor3225{color:rgb(43,145,175)}
+span#textcolor3226{color:rgb(0,0,255)}
+span#textcolor3227{color:rgb(43,145,175)}
+span#textcolor3228{color:rgb(43,145,175)}
+span#textcolor3229{color:rgb(163,20,20)}
+span#textcolor3230{color:rgb(163,20,20)}
+span#textcolor3231{color:rgb(163,20,20)}
+span#textcolor3232{color:rgb(0,0,255)}
+span#textcolor3233{color:rgb(0,0,255)}
+span#textcolor3234{color:rgb(43,145,175)}
+span#textcolor3235{color:rgb(0,0,255)}
+span#textcolor3236{color:rgb(43,145,175)}
+span#textcolor3237{color:rgb(43,145,175)}
+span#textcolor3238{color:rgb(43,145,175)}
+span#textcolor3239{color:rgb(43,145,175)}
+span#textcolor3240{color:rgb(43,145,175)}
+span#textcolor3241{color:rgb(0,127,0)}
+span#textcolor3242{color:rgb(0,127,0)}
+span#textcolor3243{color:rgb(0,127,0)}
+span#textcolor3244{color:rgb(0,0,255)}
+span#textcolor3245{color:rgb(163,20,20)}
+span#textcolor3246{color:rgb(0,0,255)}
+span#textcolor3247{color:rgb(0,0,255)}
+span#textcolor3248{color:rgb(163,20,20)}
+span#textcolor3249{color:rgb(163,20,20)}
+span#textcolor3250{color:rgb(163,20,20)}
+span#textcolor3251{color:rgb(0,0,255)}
+span#textcolor3252{color:rgb(163,20,20)}
+span#textcolor3253{color:rgb(163,20,20)}
+span#textcolor3254{color:rgb(163,20,20)}
+span#textcolor3255{color:rgb(163,20,20)}
+span#textcolor3256{color:rgb(163,20,20)}
+span#textcolor3257{color:rgb(0,127,0)}
+span#textcolor3258{color:rgb(0,127,0)}
+span#textcolor3259{color:rgb(0,0,255)}
+span#textcolor3260{color:rgb(0,0,255)}
+span#textcolor3261{color:rgb(0,0,255)}
+span#textcolor3262{color:rgb(0,0,255)}
+span#textcolor3263{color:rgb(0,0,255)}
+span#textcolor3264{color:rgb(0,0,255)}
+span#textcolor3265{color:rgb(43,145,175)}
+span#textcolor3266{color:rgb(43,145,175)}
+span#textcolor3267{color:rgb(43,145,175)}
+span#textcolor3268{color:rgb(0,0,255)}
+span#textcolor3269{color:rgb(0,0,255)}
+span#textcolor3270{color:rgb(0,0,255)}
+span#textcolor3271{color:rgb(43,145,175)}
+span#textcolor3272{color:rgb(43,145,175)}
+span#textcolor3273{color:rgb(163,20,20)}
+span#textcolor3274{color:rgb(163,20,20)}
+pre#fancyvrb93{padding:5.69054pt;}
+pre#fancyvrb93{ border-top: solid 0.4pt; }
+pre#fancyvrb93{ border-left: solid 0.4pt; }
+pre#fancyvrb93{ border-bottom: solid 0.4pt; }
+pre#fancyvrb93{ border-right: solid 0.4pt; }
+span#textcolor3275{color:rgb(0,127,0)}
+span#textcolor3276{color:rgb(0,127,0)}
+span#textcolor3277{color:rgb(0,127,0)}
+span#textcolor3278{color:rgb(0,0,255)}
+span#textcolor3279{color:rgb(0,127,0)}
+span#textcolor3280{color:rgb(0,0,255)}
+span#textcolor3281{color:rgb(0,127,0)}
+span#textcolor3282{color:rgb(0,0,255)}
+span#textcolor3283{color:rgb(0,127,0)}
+span#textcolor3284{color:rgb(0,0,255)}
+span#textcolor3285{color:rgb(43,145,175)}
+span#textcolor3286{color:rgb(43,145,175)}
+span#textcolor3287{color:rgb(0,0,255)}
+span#textcolor3288{color:rgb(43,145,175)}
+span#textcolor3289{color:rgb(0,0,255)}
+span#textcolor3290{color:rgb(0,0,255)}
+span#textcolor3291{color:rgb(0,0,255)}
+span#textcolor3292{color:rgb(163,20,20)}
+span#textcolor3293{color:rgb(163,20,20)}
+span#textcolor3294{color:rgb(163,20,20)}
+span#textcolor3295{color:rgb(163,20,20)}
+span#textcolor3296{color:rgb(163,20,20)}
+span#textcolor3297{color:rgb(163,20,20)}
+span#textcolor3298{color:rgb(0,127,0)}
+span#textcolor3299{color:rgb(0,0,255)}
+span#textcolor3300{color:rgb(0,0,255)}
+span#textcolor3301{color:rgb(43,145,175)}
+span#textcolor3302{color:rgb(0,0,255)}
+span#textcolor3303{color:rgb(163,20,20)}
+span#textcolor3304{color:rgb(163,20,20)}
+span#textcolor3305{color:rgb(163,20,20)}
+span#textcolor3306{color:rgb(0,127,0)}
+span#textcolor3307{color:rgb(0,0,255)}
+span#textcolor3308{color:rgb(0,0,255)}
+span#textcolor3309{color:rgb(43,145,175)}
+span#textcolor3310{color:rgb(0,0,255)}
+span#textcolor3311{color:rgb(163,20,20)}
+span#textcolor3312{color:rgb(163,20,20)}
+span#textcolor3313{color:rgb(163,20,20)}
+span#textcolor3314{color:rgb(0,127,0)}
+span#textcolor3315{color:rgb(0,0,255)}
+span#textcolor3316{color:rgb(0,0,255)}
+span#textcolor3317{color:rgb(43,145,175)}
+span#textcolor3318{color:rgb(0,0,255)}
+span#textcolor3319{color:rgb(163,20,20)}
+span#textcolor3320{color:rgb(163,20,20)}
+span#textcolor3321{color:rgb(163,20,20)}
+span#textcolor3322{color:rgb(0,127,0)}
+span#textcolor3323{color:rgb(0,0,255)}
+span#textcolor3324{color:rgb(0,0,255)}
+span#textcolor3325{color:rgb(0,0,255)}
+span#textcolor3326{color:rgb(0,0,255)}
+span#textcolor3327{color:rgb(0,0,255)}
+span#textcolor3328{color:rgb(0,0,255)}
+span#textcolor3329{color:rgb(163,20,20)}
+span#textcolor3330{color:rgb(0,0,255)}
+span#textcolor3331{color:rgb(43,145,175)}
+span#textcolor3332{color:rgb(43,145,175)}
+span#textcolor3333{color:rgb(43,145,175)}
+span#textcolor3334{color:rgb(163,20,20)}
+span#textcolor3335{color:rgb(163,20,20)}
+span#textcolor3336{color:rgb(163,20,20)}
+span#textcolor3337{color:rgb(0,0,255)}
+span#textcolor3338{color:rgb(163,20,20)}
+span#textcolor3339{color:rgb(163,20,20)}
+span#textcolor3340{color:rgb(163,20,20)}
+span#textcolor3341{color:rgb(0,0,255)}
+span#textcolor3342{color:rgb(0,0,255)}
+span#textcolor3343{color:rgb(0,0,255)}
+span#textcolor3344{color:rgb(43,145,175)}
+span#textcolor3345{color:rgb(43,145,175)}
+span#textcolor3346{color:rgb(163,20,20)}
+span#textcolor3347{color:rgb(163,20,20)}
+span#textcolor3348{color:rgb(163,20,20)}
+span#textcolor3349{color:rgb(163,20,20)}
+span#textcolor3350{color:rgb(163,20,20)}
+pre#fancyvrb94{padding:5.69054pt;}
+pre#fancyvrb94{ border-top: solid 0.4pt; }
+pre#fancyvrb94{ border-left: solid 0.4pt; }
+pre#fancyvrb94{ border-bottom: solid 0.4pt; }
+pre#fancyvrb94{ border-right: solid 0.4pt; }
+span#textcolor3351{color:rgb(0,0,255)}
+span#textcolor3352{color:rgb(0,0,255)}
/* end css.sty */
diff --git a/lkmpg-for-ht.html b/lkmpg-for-ht.html
index 686b1a6..033921e 100644
--- a/lkmpg-for-ht.html
+++ b/lkmpg-for-ht.html
@@ -18,7 +18,7 @@
The Linux Kernel Module Programming Guide is a free book; you may reproduce
@@ -112,10 +113,10 @@ electronic.
Derivative works and translations of this document must be placed under the
Open Software License, and the original copyright notice must remain intact. If you
have contributed new material to this book, you must make the material and source
-code available for your revisions. Please make revisions and updates available directly
+code available for your revisions. Please make revisions and updates available directly
to the document maintainer, Jim Huang <jserv@ccns.ncku.edu.tw>. This will allow
for the merging of updates and provide consistent revisions to the Linux
community.
@@ -142,18 +143,19 @@ revised the LaTeX document.
- 2011eric, 25077667, Arush Sharma, asas1asas200, Benno Bielmeier, Brad
-Baker, ccs100203, Chih-Yu Chen, ChinYikMing, Cyril Brulebois, Daniele
-Paolo Scarpazza, David Porter, demonsome, Dimo Velev, Ekang Monyet,
-fennecJ, Francois Audeon, gagachang, Gilad Reti, Horst Schirmeier,
-Hsin-Hsiang Peng, Ignacio Martin, JianXing Wu, linD026, Marconi Jiang,
-RinHizakura, Roman Lakeev, Stacy Prowell, Tucker Polomik, VxTeemo,
-Wei-Lun Tsai, xatier, Ylowy.
- 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}
+ 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.
-19 Common Pitfalls
-20 Common Pitfalls
+19.1 Using standard libraries
-20.1 Using standard libraries
+19.2 Disabling interrupts
-20.2 Disabling interrupts
+20 Where To Go From Here?
-21 Where To Go From Here?
+The Linux Kernel Module Programming Guide
-
16 Crypto
16.1 Hash functions
16.2 Symmetric key encryption
-
17 Standardizing the interfaces: The Device Model
-
18 Optimizations
-
18.1 Likely and Unlikely conditions
-
19 Common Pitfalls
-
19.1 Using standard libraries
-
19.2 Disabling interrupts
-
20 Where To Go From Here?
+
17 Virtual Input Device Driver
+
18 Standardizing the interfaces: The Device Model
+
19 Optimizations
+
19.1 Likely and Unlikely conditions
+
20 Common Pitfalls
+
20.1 Using standard libraries
+
20.2 Disabling interrupts
+
21 Where To Go From Here?
1 Introduction
+
So, you want to write a kernel module. You know C, you have written a few normal programs to run as processes, and now you want to get to where the real action is, to @@ -187,11 +189,11 @@ new functionality.
To discover what modules are already loaded within your current kernel use the command
lsmod
.
-
1sudo lsmod+ +
1sudo lsmod
Modules are stored within the file /proc/modules, so you can also see them with:
@@ -221,13 +223,13 @@ thereafter. a different kernel unless you enable CONFIG_MODVERSIONS
in the kernel. We will not go into module versioning until later in this
guide. Until we cover modversions, the examples in the guide may not
+
+
+
work if you are running a kernel with modversioning turned on. However,
most stock Linux distribution kernels come with it turned on. If you are
having trouble loading the modules because of versioning errors, compile
a kernel with modversioning turned off.
-
-
-
Using X Window System. It is highly recommended that you extract, @@ -254,11 +256,11 @@ kernel.
1sudo pacman -S linux-headers
This will tell you what kernel header files are available. Then for example: -
-1sudo apt-get install kmod linux-headers-5.4.0-80-generic+ +
1sudo apt-get install kmod linux-headers-5.4.0-80-generic
In Makefile, $(CURDIR) can set to the absolute pathname of the current working directory(after all -C options are processed, if any). See more about CURDIR in GNU make manual.
And finally, just run make directly.
- - -
1make
If there is no PWD := $(CURDIR) statement in Makefile, then it may not compile @@ -5540,8 +5542,652 @@ and a password. 198MODULE_LICENSE("GPL");
-
Up to this point we have seen all kinds of modules doing all kinds of things, but there +
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
+ input_allocate_device()
+
and sets up input bitfields, device id, version, etc. After that, registers it by calling
+ input_register_device()
+
.
+
Here is an example, vinput, It is an API to allow easy
+development of virtual input drivers. The drivers needs to export a
+ vinput_device()
+
that contains the virtual device name and
+ vinput_ops
+
structure that describes:
+
init()
+
+ send()
+
+ read()
+
Then using vinput_register_device()
+
and vinput_unregister_device()
+
will add a new device to the list of support virtual input devices.
+
+ + + +
+1int init(struct vinput *);+
This function is passed a 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);+
This function will receive a user string to interpret and inject the event using the
+ input_report_XXXX
+
or input_event
+
call. The string is already copied from user.
+
+
+1int read(struct vinput *, char *, int);+
This function is used for debugging and should fill the buffer parameter with the +last event sent in the virtual input device format. The buffer will then be copied to +user. +
vinput devices are created and destroyed using sysfs. And, event injection is +done through a /dev node. The device name will be used by the userland to +export a new virtual input device. To create a vinputX sysfs entry and /dev +node. +
+
+1echo "vkbd" | sudo tee /sys/class/vinput/export+
To unexport the device, just echo its id in unexport: +
+
+1echo "0" | sudo tee /sys/class/vinput/unexport+ +
+
+1#ifndef VINPUT_H +2#define VINPUT_H +3 +4#include <linux/input.h> +5#include <linux/spinlock.h> +6 +7#define VINPUT_MAX_LEN 128 +8#define MAX_VINPUT 32 +9#define VINPUT_MINORS MAX_VINPUT +10 +11#define dev_to_vinput(dev) container_of(dev, struct vinput, dev) +12 +13struct vinput_device; +14 +15struct vinput { +16 long id; +17 long devno; +18 long last_entry; +19 spinlock_t lock; +20 +21 void *priv_data; +22 +23 struct device dev; +24 struct list_head list; +25 struct input_dev *input; +26 struct vinput_device *type; +27}; +28 +29struct vinput_ops { +30 int (*init)(struct vinput *); +31 int (*kill)(struct vinput *); +32 int (*send)(struct vinput *, char *, int); +33 int (*read)(struct vinput *, char *, int); +34}; +35 +36struct vinput_device { +37 char name[16]; +38 struct list_head list; +39 struct vinput_ops *ops; +40}; +41 +42int vinput_register(struct vinput_device *dev); +43void vinput_unregister(struct vinput_device *dev); +44 +45#endif+ +
+
+1#include <linux/cdev.h> +2#include <linux/input.h> +3#include <linux/module.h> +4#include <linux/slab.h> +5#include <linux/spinlock.h> +6 +7#include <asm/uaccess.h> +8 +9#include "vinput.h" +10 +11#define DRIVER_NAME "vinput" +12 +13#define dev_to_vinput(dev) container_of(dev, struct vinput, dev) +14 +15static DECLARE_BITMAP(vinput_ids, VINPUT_MINORS); +16 +17static LIST_HEAD(vinput_devices); +18static LIST_HEAD(vinput_vdevices); +19 +20static int vinput_dev; +21static struct spinlock vinput_lock; +22static struct class vinput_class; +23 +24/* Search the name of vinput device in the vinput_devices linked list, +25 * which added at vinput_register(). +26 */ +27static struct vinput_device *vinput_get_device_by_type(const char *type) +28{ +29 int found = 0; +30 struct vinput_device *vinput; +31 struct list_head *curr; +32 +33 spin_lock(&vinput_lock); +34 list_for_each (curr, &vinput_devices) { +35 vinput = list_entry(curr, struct vinput_device, list); +36 if (vinput && strncmp(type, vinput->name, strlen(vinput->name)) == 0) { +37 found = 1; +38 break; +39 } +40 } +41 spin_unlock(&vinput_lock); +42 +43 if (found) +44 return vinput; +45 return ERR_PTR(-ENODEV); +46} +47 +48/* Search the id of virtual device in the vinput_vdevices linked list, +49 * which added at vinput_alloc_vdevice(). +50 */ +51static struct vinput *vinput_get_vdevice_by_id(long id) +52{ +53 struct vinput *vinput = NULL; +54 struct list_head *curr; +55 +56 spin_lock(&vinput_lock); +57 list_for_each (curr, &vinput_vdevices) { +58 vinput = list_entry(curr, struct vinput, list); +59 if (vinput && vinput->id == id) +60 break; +61 } +62 spin_unlock(&vinput_lock); +63 +64 if (vinput && vinput->id == id) +65 return vinput; +66 return ERR_PTR(-ENODEV); +67} +68 +69static int vinput_open(struct inode *inode, struct file *file) +70{ +71 int err = 0; +72 struct vinput *vinput = NULL; +73 +74 vinput = vinput_get_vdevice_by_id(iminor(inode)); +75 +76 if (IS_ERR(vinput)) +77 err = PTR_ERR(vinput); +78 else +79 file->private_data = vinput; +80 +81 return err; +82} +83 +84static int vinput_release(struct inode *inode, struct file *file) +85{ +86 return 0; +87} +88 +89static ssize_t vinput_read(struct file *file, char __user *buffer, size_t count, +90 loff_t *offset) +91{ +92 int len; +93 char buff[VINPUT_MAX_LEN + 1]; +94 struct vinput *vinput = file->private_data; +95 +96 len = vinput->type->ops->read(vinput, buff, count); +97 +98 if (*offset > len) +99 count = 0; +100 else if (count + *offset > VINPUT_MAX_LEN) +101 count = len - *offset; +102 +103 if (raw_copy_to_user(buffer, buff + *offset, count)) +104 count = -EFAULT; +105 +106 *offset += count; +107 +108 return count; +109} +110 +111static ssize_t vinput_write(struct file *file, const char __user *buffer, +112 size_t count, loff_t *offset) +113{ +114 char buff[VINPUT_MAX_LEN + 1]; +115 struct vinput *vinput = file->private_data; +116 +117 memset(buff, 0, sizeof(char) * (VINPUT_MAX_LEN + 1)); +118 +119 if (count > VINPUT_MAX_LEN) { +120 dev_warn(&vinput->dev, "Too long. %d bytes allowed\n", VINPUT_MAX_LEN); +121 return -EINVAL; +122 } +123 +124 if (raw_copy_from_user(buff, buffer, count)) +125 return -EFAULT; +126 +127 return vinput->type->ops->send(vinput, buff, count); +128} +129 +130static const struct file_operations vinput_fops = { +131 .owner = THIS_MODULE, +132 .open = vinput_open, +133 .release = vinput_release, +134 .read = vinput_read, +135 .write = vinput_write, +136}; +137 +138static void vinput_unregister_vdevice(struct vinput *vinput) +139{ +140 input_unregister_device(vinput->input); +141 if (vinput->type->ops->kill) +142 vinput->type->ops->kill(vinput); +143} +144 +145static void vinput_destroy_vdevice(struct vinput *vinput) +146{ +147 /* Remove from the list first */ +148 spin_lock(&vinput_lock); +149 list_del(&vinput->list); +150 clear_bit(vinput->id, vinput_ids); +151 spin_unlock(&vinput_lock); +152 +153 module_put(THIS_MODULE); +154 +155 kfree(vinput); +156} +157 +158static void vinput_release_dev(struct device *dev) +159{ +160 struct vinput *vinput = dev_to_vinput(dev); +161 int id = vinput->id; +162 +163 vinput_destroy_vdevice(vinput); +164 +165 pr_debug("released vinput%d.\n", id); +166} +167 +168static struct vinput *vinput_alloc_vdevice(void) +169{ +170 int err; +171 struct vinput *vinput = kzalloc(sizeof(struct vinput), GFP_KERNEL); +172 +173 try_module_get(THIS_MODULE); +174 +175 memset(vinput, 0, sizeof(struct vinput)); +176 +177 spin_lock_init(&vinput->lock); +178 +179 spin_lock(&vinput_lock); +180 vinput->id = find_first_zero_bit(vinput_ids, VINPUT_MINORS); +181 if (vinput->id >= VINPUT_MINORS) { +182 err = -ENOBUFS; +183 goto fail_id; +184 } +185 set_bit(vinput->id, vinput_ids); +186 list_add(&vinput->list, &vinput_vdevices); +187 spin_unlock(&vinput_lock); +188 +189 /* allocate the input device */ +190 vinput->input = input_allocate_device(); +191 if (vinput->input == NULL) { +192 pr_err("vinput: Cannot allocate vinput input device\n"); +193 err = -ENOMEM; +194 goto fail_input_dev; +195 } +196 +197 /* initialize device */ +198 vinput->dev.class = &vinput_class; +199 vinput->dev.release = vinput_release_dev; +200 vinput->dev.devt = MKDEV(vinput_dev, vinput->id); +201 dev_set_name(&vinput->dev, DRIVER_NAME "%lu", vinput->id); +202 +203 return vinput; +204 +205fail_input_dev: +206 spin_lock(&vinput_lock); +207 list_del(&vinput->list); +208fail_id: +209 spin_unlock(&vinput_lock); +210 module_put(THIS_MODULE); +211 kfree(vinput); +212 +213 return ERR_PTR(err); +214} +215 +216static int vinput_register_vdevice(struct vinput *vinput) +217{ +218 int err = 0; +219 +220 /* register the input device */ +221 vinput->input->name = vinput->type->name; +222 vinput->input->phys = "vinput"; +223 vinput->input->dev.parent = &vinput->dev; +224 +225 vinput->input->id.bustype = BUS_VIRTUAL; +226 vinput->input->id.product = 0x0000; +227 vinput->input->id.vendor = 0x0000; +228 vinput->input->id.version = 0x0000; +229 +230 err = vinput->type->ops->init(vinput); +231 +232 if (err == 0) +233 dev_info(&vinput->dev, "Registered virtual input %s %ld\n", +234 vinput->type->name, vinput->id); +235 +236 return err; +237} +238 +239static ssize_t export_store(struct class *class, struct class_attribute *attr, +240 const char *buf, size_t len) +241{ +242 int err; +243 struct vinput *vinput; +244 struct vinput_device *device; +245 +246 device = vinput_get_device_by_type(buf); +247 if (IS_ERR(device)) { +248 pr_info("vinput: This virtual device isn't registered\n"); +249 err = PTR_ERR(device); +250 goto fail; +251 } +252 +253 vinput = vinput_alloc_vdevice(); +254 if (IS_ERR(vinput)) { +255 err = PTR_ERR(vinput); +256 goto fail; +257 } +258 +259 vinput->type = device; +260 err = device_register(&vinput->dev); +261 if (err < 0) +262 goto fail_register; +263 +264 err = vinput_register_vdevice(vinput); +265 if (err < 0) +266 goto fail_register_vinput; +267 +268 return len; +269 +270fail_register_vinput: +271 device_unregister(&vinput->dev); +272fail_register: +273 vinput_destroy_vdevice(vinput); +274fail: +275 return err; +276} +277/* This macro generates class_attr_export structure and export_store() */ +278static CLASS_ATTR_WO(export); +279 +280static ssize_t unexport_store(struct class *class, struct class_attribute *attr, +281 const char *buf, size_t len) +282{ +283 int err; +284 unsigned long id; +285 struct vinput *vinput; +286 +287 err = kstrtol(buf, 10, &id); +288 if (err) { +289 err = -EINVAL; +290 goto failed; +291 } +292 +293 vinput = vinput_get_vdevice_by_id(id); +294 if (IS_ERR(vinput)) { +295 pr_err("vinput: No such vinput device %ld\n", id); +296 err = PTR_ERR(vinput); +297 goto failed; +298 } +299 +300 vinput_unregister_vdevice(vinput); +301 device_unregister(&vinput->dev); +302 +303 return len; +304failed: +305 return err; +306} +307/* This macro generates class_attr_unexport structure and unexport_store() */ +308static CLASS_ATTR_WO(unexport); +309 +310static struct attribute *vinput_class_attrs[] = { +311 &class_attr_export.attr, +312 &class_attr_unexport.attr, +313 NULL, +314}; +315 +316/* This macro generates vinput_class_groups structure */ +317ATTRIBUTE_GROUPS(vinput_class); +318 +319static struct class vinput_class = { +320 .name = "vinput", +321 .owner = THIS_MODULE, +322 .class_groups = vinput_class_groups, +323}; +324 +325int vinput_register(struct vinput_device *dev) +326{ +327 spin_lock(&vinput_lock); +328 list_add(&dev->list, &vinput_devices); +329 spin_unlock(&vinput_lock); +330 +331 pr_info("vinput: registered new virtual input device '%s'\n", dev->name); +332 +333 return 0; +334} +335EXPORT_SYMBOL(vinput_register); +336 +337void vinput_unregister(struct vinput_device *dev) +338{ +339 struct list_head *curr, *next; +340 +341 /* Remove from the list first */ +342 spin_lock(&vinput_lock); +343 list_del(&dev->list); +344 spin_unlock(&vinput_lock); +345 +346 /* unregister all devices of this type */ +347 list_for_each_safe (curr, next, &vinput_vdevices) { +348 struct vinput *vinput = list_entry(curr, struct vinput, list); +349 if (vinput && vinput->type == dev) { +350 vinput_unregister_vdevice(vinput); +351 device_unregister(&vinput->dev); +352 } +353 } +354 +355 pr_info("vinput: unregistered virtual input device '%s'\n", dev->name); +356} +357EXPORT_SYMBOL(vinput_unregister); +358 +359static int __init vinput_init(void) +360{ +361 int err = 0; +362 +363 pr_info("vinput: Loading virtual input driver\n"); +364 +365 vinput_dev = register_chrdev(0, DRIVER_NAME, &vinput_fops); +366 if (vinput_dev < 0) { +367 pr_err("vinput: Unable to allocate char dev region\n"); +368 goto failed_alloc; +369 } +370 +371 spin_lock_init(&vinput_lock); +372 +373 err = class_register(&vinput_class); +374 if (err < 0) { +375 pr_err("vinput: Unable to register vinput class\n"); +376 goto failed_class; +377 } +378 +379 return 0; +380failed_class: +381 class_unregister(&vinput_class); +382failed_alloc: +383 return err; +384} +385 +386static void __exit vinput_end(void) +387{ +388 pr_info("vinput: Unloading virtual input driver\n"); +389 +390 unregister_chrdev(vinput_dev, DRIVER_NAME); +391 class_unregister(&vinput_class); +392} +393 +394module_init(vinput_init); +395module_exit(vinput_end); +396 +397MODULE_LICENSE("GPL"); +398MODULE_DESCRIPTION("Emulate input events");+ + + +
Here the virtual keyboard is one of example to use vinput. It supports all
+ KEY_MAX
+
keycodes. The injection format is the KEY_CODE
+
such as defined in include/linux/input.h. A positive value means
+ KEY_PRESS
+
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.
+
Simulate a key press on "g" ( KEY_G
+
= 34):
+
+
+1echo "+34" | sudo tee /dev/vinput0+
Simulate a key release on "g" ( KEY_G
+
= 34:)
+
+
+1echo "-34" | sudo tee /dev/vinput0+ +
+
+1#include <linux/init.h> +2#include <linux/input.h> +3#include <linux/module.h> +4#include <linux/spinlock.h> +5 +6#include "vinput.h" +7 +8#define VINPUT_KBD "vkbd" +9#define VINPUT_RELEASE 0 +10#define VINPUT_PRESS 1 +11 +12static unsigned short vkeymap[KEY_MAX]; +13 +14static int vinput_vkbd_init(struct vinput *vinput) +15{ +16 int i; +17 +18 /* Set up the input bitfield */ +19 vinput->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); +20 vinput->input->keycodesize = sizeof(unsigned short); +21 vinput->input->keycodemax = KEY_MAX; +22 vinput->input->keycode = vkeymap; +23 +24 for (i = 0; i < KEY_MAX; i++) +25 set_bit(vkeymap[i], vinput->input->keybit); +26 +27 /* vinput will help us allocate new input device structure via +28 * input_allocate_device(). So, we can register it straightforwardly. +29 */ +30 return input_register_device(vinput->input); +31} +32 +33static int vinput_vkbd_read(struct vinput *vinput, char *buff, int len) +34{ +35 spin_lock(&vinput->lock); +36 len = snprintf(buff, len, "%+ld\n", vinput->last_entry); +37 spin_unlock(&vinput->lock); +38 +39 return len; +40} +41 +42static int vinput_vkbd_send(struct vinput *vinput, char *buff, int len) +43{ +44 int ret; +45 long key = 0; +46 short type = VINPUT_PRESS; +47 +48 /* Determine which event was received (press or release) +49 * and store the state. +50 */ +51 if (buff[0] == '+') +52 ret = kstrtol(buff + 1, 10, &key); +53 else +54 ret = kstrtol(buff, 10, &key); +55 if (ret) +56 dev_err(&vinput->dev, "error during kstrtol: -%d\n", ret); +57 spin_lock(&vinput->lock); +58 vinput->last_entry = key; +59 spin_unlock(&vinput->lock); +60 +61 if (key < 0) { +62 type = VINPUT_RELEASE; +63 key = -key; +64 } +65 +66 dev_info(&vinput->dev, "Event %s code %ld\n", +67 (type == VINPUT_RELEASE) ? "VINPUT_RELEASE" : "VINPUT_PRESS", key); +68 +69 /* Report the state received to input subsystem. */ +70 input_report_key(vinput->input, key, type); +71 /* Tell input subsystem that it finished the report. */ +72 input_sync(vinput->input); +73 +74 return len; +75} +76 +77static struct vinput_ops vkbd_ops = { +78 .init = vinput_vkbd_init, +79 .send = vinput_vkbd_send, +80 .read = vinput_vkbd_read, +81}; +82 +83static struct vinput_device vkbd_dev = { +84 .name = VINPUT_KBD, +85 .ops = &vkbd_ops, +86}; +87 +88static int __init vkbd_init(void) +89{ +90 int i; +91 +92 for (i = 0; i < KEY_MAX; i++) +93 vkeymap[i] = i; +94 return vinput_register(&vkbd_dev); +95} +96 +97static void __exit vkbd_end(void) +98{ +99 vinput_unregister(&vkbd_dev); +100} +101 +102module_init(vkbd_init); +103module_exit(vkbd_end); +104 +105MODULE_LICENSE("GPL"); +106MODULE_DESCRIPTION("Emulate keyboard input events through /dev/vinput");+
+
+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 a device model was added. An example is shown below, and you can @@ -5549,112 +6195,115 @@ use this as a template to add your own suspend, resume or other interface functions.
-
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 .owner = THIS_MODULE, -67 .pm = &devicemodel_pm_ops, -68 }, -69 .probe = devicemodel_probe, -70 .remove = devicemodel_remove, -71}; -72 -73static int devicemodel_init(void) -74{ -75 int ret; -76 -77 pr_info("devicemodel init\n"); -78 -79 ret = platform_driver_register(&devicemodel_driver); -80 -81 if (ret) { -82 pr_err("Unable to register driver\n"); -83 return ret; -84 } -85 -86 return 0; -87} -88 -89static void devicemodel_exit(void) -90{ -91 pr_info("devicemodel exit\n"); -92 platform_driver_unregister(&devicemodel_driver); -93} -94 -95module_init(devicemodel_init); -96module_exit(devicemodel_exit); -97 -98MODULE_LICENSE("GPL"); -99MODULE_DESCRIPTION("Linux Device Model example");-
+
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 .owner = THIS_MODULE, +67 .pm = &devicemodel_pm_ops, +68 }, +69 .probe = devicemodel_probe, +70 .remove = devicemodel_remove, +71}; +72 +73static int devicemodel_init(void) +74{ +75 int ret; +76 +77 pr_info("devicemodel init\n"); +78 +79 ret = platform_driver_register(&devicemodel_driver); +80 +81 if (ret) { +82 pr_err("Unable to register driver\n"); +83 return ret; +84 } +85 +86 return 0; +87} +88 +89static void devicemodel_exit(void) +90{ +91 pr_info("devicemodel exit\n"); +92 platform_driver_unregister(&devicemodel_driver); +93} +94 +95module_init(devicemodel_init); +96module_exit(devicemodel_exit); +97 +98MODULE_LICENSE("GPL"); +99MODULE_DESCRIPTION("Linux Device Model example");+
-
+
-
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 @@ -5667,51 +6316,48 @@ you know that the conditions are almost always likely to evaluate as either to succeed.
-
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 When the
+
+
You can not do that. In a kernel module, you can only use kernel functions which are
+ 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 people seriously interested in kernel programming, I recommend kernelnewbies.org
+ For people seriously interested in kernel programming, I recommend kernelnewbies.org
and the Documentation subdirectory within the kernel source code which is not
always easy to understand but can be a starting point for further investigation. Also,
as Linus Torvalds said, the best way to learn the kernel is to read the source code
yourself.
- If you would like to contribute to this guide or notice anything glaringly wrong,
+ If you would like to contribute to this guide or notice anything glaringly wrong,
please create an issue at https://github.com/sysprog21/lkmpg. Your pull requests
will be appreciated.
- Happy hacking!
+ Happy hacking!
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
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}
+ 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.
-19 Common Pitfalls
-20 Common Pitfalls
+19.1 Using standard libraries
-20.1 Using standard libraries
+19.2 Disabling interrupts
-20.2 Disabling interrupts
+20 Where To Go From Here?
-21 Where To Go From Here?
+