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

Peter Jay Salzman, Michael Burian, Ori Pomerantz, Bob Mottram, Jim Huang

-
March 18, 2022
+
April 8, 2022
@@ -88,13 +88,14 @@
 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

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.

+

+

1.3 What Is A Kernel Module?

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

    3 Examples

    @@ -318,14 +320,14 @@ to use tabs, not spaces. 7 8clean: 9    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + + +

    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");

    -

    17 Standardizing the interfaces: The Device Model

    -

    Up to this point we have seen all kinds of modules doing all kinds of things, but there +

    17 Virtual Input Device Driver

    +

    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: +

    + +

    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");
    +

    +

    +

    18 Standardizing the interfaces: The Device Model

    +

    Up to this point we have seen all kinds of modules doing all kinds of things, but there was no consistency in their interfaces with the rest of the kernel. 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");
    +

    -

    18 Optimizations

    -

    +

    19 Optimizations

    + + + +

    -

    18.1 Likely and Unlikely conditions

    -

    Sometimes you might want your code to run as quickly as possible, +

    19.1 Likely and Unlikely conditions

    +

    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 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. -

    +

    -

    19 Common Pitfalls

    -

    +

    20 Common Pitfalls

    +

    -

    19.1 Using standard libraries

    -

    You can not do that. In a kernel module, you can only use kernel functions which are +

    20.1 Using standard libraries

    +

    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. -

    +

    -

    19.2 Disabling interrupts

    -

    You might need to do this for a short time and that is OK, but if you do not enable +

    20.2 Disabling interrupts

    +

    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. -

    + + + +

    -

    20 Where To Go From Here?

    -

    For people seriously interested in kernel programming, I recommend kernelnewbies.org +

    21 Where To Go From Here?

    +

    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

    Peter Jay Salzman, Michael Burian, Ori Pomerantz, Bob Mottram, Jim Huang

    -
    March 18, 2022
    +
    April 8, 2022
    @@ -88,13 +88,14 @@
     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

    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.

    +

    +

    1.3 What Is A Kernel Module?

    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

    3 Examples

    @@ -318,14 +320,14 @@ to use tabs, not spaces. 7 8clean: 9    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + + +

    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");

    -

    17 Standardizing the interfaces: The Device Model

    -

    Up to this point we have seen all kinds of modules doing all kinds of things, but there +

    17 Virtual Input Device Driver

    +

    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: +

    + +

    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");
    +

    +

    +

    18 Standardizing the interfaces: The Device Model

    +

    Up to this point we have seen all kinds of modules doing all kinds of things, but there was no consistency in their interfaces with the rest of the kernel. 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");
    +

    -

    18 Optimizations

    -

    +

    19 Optimizations

    + + + +

    -

    18.1 Likely and Unlikely conditions

    -

    Sometimes you might want your code to run as quickly as possible, +

    19.1 Likely and Unlikely conditions

    +

    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 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. -

    +

    -

    19 Common Pitfalls

    -

    +

    20 Common Pitfalls

    +

    -

    19.1 Using standard libraries

    -

    You can not do that. In a kernel module, you can only use kernel functions which are +

    20.1 Using standard libraries

    +

    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. -

    +

    -

    19.2 Disabling interrupts

    -

    You might need to do this for a short time and that is OK, but if you do not enable +

    20.2 Disabling interrupts

    +

    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. -

    + + + +

    -

    20 Where To Go From Here?

    -

    For people seriously interested in kernel programming, I recommend kernelnewbies.org +

    21 Where To Go From Here?

    +

    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