Cover image for Linux Driver Framework

Linux Driver Framework


Timeline

Timeline

2025-11-09

init

This article introduces the basics of the Linux driver framework, outlines the three fundamental categories of Linux drivers and the kernel source directory structure, and provides a detailed analysis of the essential components, optional information, and related module loading mechanisms of the simplest Linux driver module.

Linux Driver Notes

Table of ContentsLinks
1. Linux Driver Framework
2. Linux Driver Loading Logic
3. Character Device Basics
4. Concurrency and Competition
5. Advanced Character Device Progression
6. Interrupts
7. Platform Bus
8. Device Tree
9. Device Model
10. Hot Plug
11. pinctrl Subsystem
12. GPIO Subsystem
13. Input Subsystem
14. Single Bus
15. I2C
16. SPI
17. UART
18. PWM
19. RTC
20. Watchdog
21. CAN
22. Network Device
23. ADC
24. IIO
25. USB
26. LCD

Linux Kernel Related Materials:
https://github.com/0voice/linux_kernel_wiki/blob/main/README.md

Linux Driver Abstraction

Evolution of Linux Driver Framework
Evolution of Linux Driver Framework

Linux 2.4 Driver Architecture
Linux 2.4 Driver Architecture

Linux 2.6 Introduced Platform Bus Architecture
Linux 2.6 Introduced Platform Bus Architecture

Linux Device Tree
Linux Device Tree

Linux divides memory and peripherals into 3 basic categories:Character Device DriverBlock Device DriverNetwork Device Driver

Linux Source Directory Structure

DirectoryDescription
archArchitecture-related directory, containing adaptation code for multiple CPU architectures (e.g., ARM, x86, MIPS, etc.)
blockBlock device-related code directory, managing storage devices such as hard drives and SD cards as block devices in Linux
cryptoCryptographic algorithm directory, containing implementation code for various encryption-related algorithms
DocumentationOfficial Linux kernel documentation directory, containing detailed descriptions of kernel functions, interfaces, etc.
driversDriver directory, containing driver code for various hardware devices supported by the Linux system
firmwareFirmware directory, containing firmware files required by hardware devices
fsFile system directory, containing implementation code for file systems such as ext2, ext3, FAT, etc.
includeCommon header file directory, providing header files shared by various kernel modules
initKernel boot initialization directory, containing initialization code for the Linux kernel boot phase
ipcInter-process communication directory, containing implementation code for IPC mechanisms such as pipes, message queues, and shared memory.
kernelKernel core directory, containing the core functional code of the kernel itself.
libLibrary function directory, containing various library functions used by the kernel.
mmMemory management directory (mm stands for memory management), responsible for kernel memory management functions.
netNetwork-related directory, containing implementation code for network functions such as the TCP/IP protocol stack.
scriptsScript directory, containing script files used in processes such as kernel compilation and testing.
securitySecurity-related directory, containing implementation code for kernel security mechanisms.
soundAudio-related directory, containing code for audio device drivers and audio processing.
toolsTools directory, containing tool programs used for Linux kernel development and debugging.
usrDirectory related to Linux kernel startup code.
virtKernel virtual machine-related directory, containing implementation code for kernel-level virtualization functions.

Analysis of the simplest Linux driver structure

  • Components
    1. Header files (mandatory): The driver must include kernel-related header files, among which<linux/module.h>and<linux/init.h>are essential.
    2. Driver loading function (required): When loading the driver, this function is automatically executed by the kernel.
    3. Driver unloading function (required): When unloading the driver, this function is automatically executed by the kernel.
    4. License declaration (required): Since the Linux kernel follows the GPL license, the driver must also comply with the relevant license when loading. Common license types include GPL v2 and others.
    5. Module parameters (optional): These are values passed to the kernel module when the module is loaded.
    6. Author and version information (optional): Used to declare the driver’s author and code version information.
  • Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>



static int __init hello_world_init(void)
{
printk(KERN_INFO "Hello World: Module loaded\n");
return 0;
}

static void __exit hello_world_exit(void)
{
printk(KERN_INFO "Hello World: Module unloaded\n");
}

module_init(hello_world_init);
module_exit(hello_world_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhao Hang");
MODULE_DESCRIPTION("Hello World Kernel Module");

The module will be loaded successfully if and only if the init function in module_init returns a value greater than or equal to 0.

Module information

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
$ objdump -h hello_world.ko

hello_world.ko: file format elf64-little

Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000000 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .init.text 00000034 0000000000000000 0000000000000000 00000040 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 .exit.text 00000024 0000000000000000 0000000000000000 00000074 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
3 .note.gnu.property 00000020 0000000000000000 0000000000000000 00000098 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .note.gnu.build-id 00000024 0000000000000000 0000000000000000 000000b8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .note.Linux 00000018 0000000000000000 0000000000000000 000000dc 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .rodata.str1.8 0000002b 0000000000000000 0000000000000000 000000f8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .modinfo 000000b2 0000000000000000 0000000000000000 00000123 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 __versions 00000080 0000000000000000 0000000000000000 000001d8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 __patchable_function_entries 00000008 0000000000000080 0000000000000080 00000258 2**3
CONTENTS, ALLOC, LOAD, RELOC, DATA
10 .data 00000000 0000000000000000 0000000000000000 00000260 2**0
CONTENTS, ALLOC, LOAD, DATA
11 .gnu.linkonce.this_module 000003c0 0000000000000000 0000000000000000 00000260 2**6
CONTENTS, ALLOC, LOAD, RELOC, DATA, LINK_ONCE_DISCARD
12 .plt 00000001 0000000000000000 0000000000000000 00000620 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .init.plt 00000001 0000000000000000 0000000000000000 00000621 2**0
ALLOC, READONLY
14 .text.ftrace_trampoline 00000001 0000000000000000 0000000000000000 00000621 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .bss 00000000 0000000000000000 0000000000000000 00000622 2**0
ALLOC
16 .comment 00000026 0000000000000000 0000000000000000 00000622 2**0
CONTENTS, READONLY
17 .note.GNU-stack 00000000 0000000000000000 0000000000000000 00000648 2**0
CONTENTS, READONLY

The kernel module uses its.modinfosection to store information about the module, and allMODULE_*macros update this section’s content with the values passed as parameters. Some of these macros are
MODULE_DESCRIPTION()MODULE_AUTHOR()andMODULE_LICENSE(). The truly low-level macro provided by the kernel for adding entries to the module information section isMODULE_INFO(tag,info), which adds general information in the form oftag=info. This means driver authors can freely add any form of information they want, for example:

1
MODULE_INFO(my_field_name, "What eeasy value");

.modeinfoThe content of the section:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ readelf -x .modinfo hello_world.ko

Hex dump of section '.modinfo':
0x00000000 64657363 72697074 696f6e3d 68656c6c description=hell
0x00000010 6f20776f 726c6421 00617574 686f723d o world!.author=
0x00000020 6576656e 36323900 6c696365 6e73653d even629.license=
0x00000030 47504c00 73726376 65727369 6f6e3d41 GPL.srcversion=A
0x00000040 34303030 33444636 33303137 39423643 40003DF630179B6C
0x00000050 46314430 34350064 6570656e 64733d00 F1D045.depends=.
0x00000060 6e616d65 3d68656c 6c6f5f77 6f726c64 name=hello_world
0x00000070 00766572 6d616769 633d352e 31302e31 .vermagic=5.10.1
0x00000080 31302d76 382b2053 4d502070 7265656d 10-v8+ SMP preem
0x00000090 7074206d 6f645f75 6e6c6f61 64206d6f pt mod_unload mo
0x000000a0 64766572 73696f6e 73206161 72636836 dversions aarch6
0x000000b0 3400 4.

You can also usemodinfoto view

1
2
3
4
5
6
7
8
9
$ modinfo hello_world.ko
filename: /home/zhaohang/repository/linux/linux_driver_learning/01_hello_world/hello_world.ko
description: hello world!
author: even629
license: GPL
srcversion: A40003DF630179B6CF1D045
depends:
name: hello_world
vermagic: 5.10.110-v8+ SMP preempt mod_unload modversions aarch64

Compiling a Linux driver

  • Placing the driver inside the Linux kernel, then compiling the Linux kernel. Compiling the driver into the Linux kernel.
  • Compiling the driveras a kernel module, independent of the Linux kernel
    • A kernel module is a special mechanism in the Linux system that cancompile some rarely used or temporarily unused functions into kernel modules, and dynamically load them into the kernel when needed.
    • Using kernel modules can reduce the kernel size and speed up boot time. Drivers can also be inserted or removed while the system is running without rebooting. The suffix of kernel modules is .ko

obj-<X>kbuild variable

This actually corresponds toobj- <X>mode, where<X>should be y, m, blank, or n. In general, the makefile at the top of the kernel build system uses it.

1
obj-y += mymodule.o

This tells kbuild that there is an object named mymodule.o in the current directory. mymodule.o will be built from mymodule.c or mymodule.S.

How and whether to build or link mymodule.o depends on<X>the value of

  • If<X>is set to m, then the variable obj-m is used, and mymodule.o is built as a module.
  • If<X>If set to y, the variable obj-y is used, and mymodule.o will be built as part of the kernel. It can also be said to be a built-in module.
  • If<X>is set to n, mymodule.o will not be built at all.

Therefore, it is often usedobj-$(CONFIG_XXX)pattern (whereCONFIG_XXXis a kernel configuration option), which can be set or unset during the kernel configuration process. Here is an example:

1
obj-$(CONFIG_MYMODULE) += mymodule.o

$(CONFIG_MYMODULE)evaluates to y or m based on the value during kernel configuration (make menuconfig). IfCONFIG_MYMODULEis neither y nor m, the file will not be compiled or linked.

There is also a case:

1
obj-<X> += somedir/

This means kbuild should enter the somedir directory, find all makefiles in it, and process them to decide which objects should be built.

Compiled as a kernel module

1
2
3
4
5
6
7
8
9
10
11
12
obj-m += hello_world.o
KERNEL_SRC:=/home/zhaohang/repository/linux/linux-5.10.246
PWD ?=$(shell pwd)
ARCH = arm64
CROSS_COMPILE = aarch64-linux-gnu-

all:
$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_SRC) M=$(PWD) modules

clean:
$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_SRC) M=$(PWD) modules clean
rm -rf *.ko *.o *.mod.o *.mod.c *.symvers *.order

Local Linux code is under /lib/modules/$(uname -r)/kernel

Compile the driver into the kernel

Indrivers/char(taking a character driver as an example) create a folder helloworld, then put the driver source code into it, and then create a Kconfig file

1
2
3
4
5
config helloworld
bool "helloworld support"
default y
help
helloworld

Change drivers

1
2
3
emacs ../Kconfig
# Add
source "drivers/char/helloworld/Kconfig"

Create a Makefile in the driver source code

1
obj-$(CONFIG_helloworld) += helloworld.o

Then add the following in the parent directory’s Makefile:

1
2
3
emacs ../Makefile
# Add
obj-y += helloworld/

Module loading commands

  • insmod
    • Function: Load a Linux kernel module
    • Syntax: insmod module_name
    • Example: insmod hello_world.ko
  • modprobe
    • Function: Load a kernel module, and also load the modules it depends on
    • Syntax: modprobe module_name
    • Example: modprobe hello_world.ko

System administrators or those in production systems often use modprobe. modprobe is smarter; it parses the modules.dep file before loading the specified module to load dependencies first. It automatically handles module dependencies, much like a package manager does.

But**modprobecannot directly load modules from arbitrary paths.kofiles** — it only looks for modules in standard kernel module directories (such as/lib/modules/$(uname -r)/) and relies onmodules.depindex.

Module Unload Command

  • rmmod

    • Function: Remove a kernel module that has been loaded into Linux
    • Syntax: rmmod module_name
    • Example: rmmod hello_world.ko
  • modeprobe -r

    • It not only attempts to unloadmymodule, but also automatically checks and unloads dependency modules that are only used bymymoduleand are not currently in use by other modules or processes.
    • Syntax:modeprobe -r mymodule

The enabling or disabling of the kernel module unload feature is determined by the CONFIG_MODULE_UNLOAD configuration option. Without this option, no modules can be unloaded.

At runtime, if unloading a module would cause other adverse effects, the kernel will prevent it even if someone requests the unload. This is becauseThe kernel records the usage count of modules through reference counting, so it knows whether a module is in use. If the kernel considers it unsafe to remove a module, it will not delete it. However, the following settings can change this behavior: MODULE_FORCE_UNLOAD=y

Setting modules to load at boot

If you want to load some modules at boot, simply create a file/etc/modules-load.d/<filename>.conf, and add the names of the modules to be loaded (one per line). People commonly use modules:/etc/modules-load.d/modules.conf. Of course, you can also create multiple .conf files as needed.

1
2
3
4
5
emacs /etc/modules-load.d/mymodule.conf
# Below is the content of mymodule.conf
uio
iwlwifi
i2c-dev

Command to view module information

  • lsmodCommand
    • Function: List kernel modules already loaded in Linux
    • You can also use the command cat /proc/modules to check if a module has been loaded successfully
  • modinfoCommand
    • Function: View kernel module information
    • Syntax: modinfo module_name
    • Example: modinfo hello_world.ko

Configuration file

menuconfigConfigure driver option state operations:

Driver state:

  1. Compile the driver as a kernel module, represented by M
  2. **Compile the driver into the kernel, represented by ***
  3. Do not compile

Use the spacebar to toggle among these three states.

The states of options are:

  • []: Indicates two states, can only be set to selected or not selected
  • <>: Indicates three states, can be set to selected, not selected, or compiled as a module
  • () : Indicates used to store strings or hexadecimal numbers

Kconfig file

The Kconfig file is the source file for the graphical configuration interface; the options in the graphical configuration interface are determined by the Kconfig file. When we execute the commandmake menuconfigcommand, the kernel configuration tool reads thearch/xxx/Kconfig. xxx is the value of ARCH, such as arm64, and then generates the corresponding configuration interface for developers to use.

config file and .config file

Both the config file and the .config file are configuration files for the Linux kernel.

  • The config file is located in the arch/$(ARCH)/configs directory of the Linux kernel source code, and isthe default configuration file for the Linux system。.

  • The .config file is located in the top-level directory of the Linux kernel source code. When compiling the Linux kernel, the configuration in the .config file is used to compile the kernel image.

    • If .config exists, the default configuration in the make menuconfig interface is the configuration of the current .config file. If settings in the graphical configuration interface are modified and saved, the .config file will be updated.

    • If the .config file does not exist, the default configuration in the make menuconfig interface is the default configuration in the Kconfig file.

Using the command make xxx_defconfig will generate a .config file based on the default file in the arch/$(ARCH)/configs directory.

Kconfig syntax

Reference:

Main menu

mainmenuUsed to set the title of the main menu

Example:mainmenu "Linux/\$(ARCH) $(KERNELVERSION) Kernel Configuration"

The menu name set by the above name isLinux/\$(ARCH) $(KERNELVERSION) Kernel Configuration

Menu structure

You can use menu/endmenu to generate a menu. menu is the marker for the start of a menu, and endmenu is the marker for the end of a menu. These two appear in pairs.

The following describes a menu named: “Network device support”

1
2
3
4
menu "Network device support"
config NETDEVICE
...
endmenu

Configuration options

Use the keywordconfigto define a new option. Each option must specify a type, and the types includebool, tristate, string, hex, int. The most common ones are bool, tristate, and string.

  • The bool type has two values: y and n;

  • The tristate type has three values: y, m, and n;

  • string is the string type.

help indicates help information. When we press the h key in the graphical interface, the content of help pops up.

Example:

1
2
3
4
5
config helloworld
bool "hello world support"
default y
help
hello world

Dependencies

Dependencies in Kconfig can use depends on and select

depends on indicates a direct dependency

1
2
config A
depends on B

It means option A depends on option B; only when option B is selected can option A be selected

select indicates a reverse dependency:

1
2
config A
select B

When option A is selected, option B is automatically selected

Optional options

Use choice and endchoice to define selectable items

1
2
3
4
5
6
7
8
choice
bool "a"
config b
boot b1
config c
boot c1
...
endchoice

Comment

Display a comment in the graphical configuration interface

1
2
3
4
5
6
config TEST_CONFIG
bool "test"
default y
help
just test
comment "just for test"

souce

source is used to read another Kconfig file, for example, source “init/Kconfig” reads the Kconfig file in the init directory into the current Kconfig file

Driver module parameter passing

Significance of driver parameter passing:

Advantages:

  1. Driver parameter passing makes the driver more flexible and compatible.
  2. Security checks can be set via driver parameter passing to prevent driver theft

Disadvantages

  1. Complicates the driver code
  2. Increases driver resource usage

Parameter types that can be passed to a driver

Most data types commonly used in C language are supported by the kernel for driver parameter passing. Here, the parameter types supported by the kernel for driver passing are divided into three categories:

  • Basic types:char, bool, int, long, short, byte, ushort, uint
  • Array:array
  • String:string

Methods for passing parameters to Linux drivers

  • Parameter types and corresponding functions

    Parameter typeCorresponding functionFunctionality
    Basic typemodule_paramPass basic type parameters
    Array typemodule_param_arrayPass array type parameters
    String typemodule_param_stringPassing a string type parameter
  • Function definition location: These three functions are in the Linux kernel source code’sinclude/linux/moduleparam.hdefined in.

module_param

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* module_param - typesafe helper for a module/cmdline parameter
* @name: the variable to alter, and exposed parameter name.
* @type: the type of the parameter
* @perm: visibility in sysfs.
*
* @name becomes the module parameter, or (prefixed by KBUILD_MODNAME and a
* ".") the kernel commandline parameter. Note that - is changed to _, so
* the user can use "foo-bar=1" even for variable "foo_bar".
*
* @perm is 0 if the variable is not to appear in sysfs, or 0444
* for world-readable, 0644 for root-writable, etc. Note that if it
* is writable, you may need to use kernel_param_lock() around
* accesses (esp. charp, which can be kfreed when it changes).
*
* The @type is simply pasted to refer to a param_ops_##type and a
* param_check_##type: for convenience many standard types are provided but
* you can create your own by defining those variables.
*
* Standard types are:
* byte, hexint, short, ushort, int, uint, long, ulong
* charp: a character pointer
* bool: a bool, values 0/1, y/n, Y/N.
* invbool: the above, only sense-reversed (N = true).
*/
#define module_param(name, type, perm) \
module_param_named(name, name, type, perm)

module_param_array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

/**
* module_param_array - a parameter which is an array of some type
* @name: the name of the array variable
* @type: the type, as per module_param()
* @nump: optional pointer filled in with the number written
* @perm: visibility in sysfs
*
* Input and output are as comma-separated values. Commas inside values
* don't work properly (eg. an array of charp).
*
* ARRAY_SIZE(@name) is used to determine the number of elements in the
* array, so the definition must be visible.
*/
#define module_param_array(name, type, nump, perm) \
module_param_array_named(name, name, type, nump, perm)

module_param_string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* module_param_string - a char array parameter
* @name: the name of the parameter
* @string: the string variable
* @len: the maximum length of the string, incl. terminator
* @perm: visibility in sysfs.
*
* This actually copies the string when it's set (unlike type charp).
* @len is usually just sizeof(string).
*/
#define module_param_string(name, string, len, perm) \
static const struct kparam_string __param_string_##name \
= { len, string }; \
__module_param_call(MODULE_PARAM_PREFIX, name, \
&param_ops_string, \
.str = &__param_string_##name, perm, -1, 0);\
__MODULE_PARM_TYPE(name, "string")

MODULE_PARM_DESC

Function purpose: Describes module parameter information. Defined in include/linux/moduleparam.h

Function prototype:MODULE_PARM_DESC(_parm, desc)

Function parameters:_parm: The parameter name of the parameter to be described. desc: Description information

Parameter nameMeaning
nameParameter name (can be passed via command line)
typeParameter type (e.g.,intboolcharp
permin/sys/module/<modname>/parameters/permissions in, such as0644
nump(array only) Variable address storing the number of array elements
len(string only) buffer length
stringpointer to string buffer

permission definition

permrefers toparameter file in sysfs’sfile permission bits

read/write permissions are defined ininclude/linux/stat.handinclude/uapi/linux/stat.h.

  • include/linux/stat.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_STAT_H
#define _LINUX_STAT_H


#include <asm/stat.h>
#include <uapi/linux/stat.h>

#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)


#endif
  • include/uapi/linux/stat.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _UAPI_LINUX_STAT_H
#define _UAPI_LINUX_STAT_H

#include <linux/types.h>

#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)

#define S_IFMT 00170000
#define S_IFSOCK 0140000
#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFBLK 0060000
#define S_IFDIR 0040000
#define S_IFCHR 0020000
#define S_IFIFO 0010000
#define S_ISUID 0004000
#define S_ISGID 0002000
#define S_ISVTX 0001000

#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)

#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100

#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010

#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001

#endif

#endif /* _UAPI_LINUX_STAT_H */

the relevant permissions are mainlyfile access permission macros (File Permission Bits)

macro nameoctal valuemeaning
S_IRWXU00700Owner (U) read, write, execute permissions (RWX)
S_IRUSR00400Owner (USR) read permission ®
S_IWUSR00200Owner (USR) write permission (W)
S_IXUSR00100Owner (USR) execute permission (X)
S_IRWXG00070Group (G) read, write, execute permissions (RWX)
S_IRGRP00040Group (GRP) read permission ®
S_IWGRP00020Group (GRP) write permission (W)
S_IXGRP00010Group (GRP) execute permission (X)
S_IRWXO00007Others (O) read, write, execute permissions (RWX)
S_IROTH00004Others (OTH) read permission ®
S_IWOTH00002Others (OTH) write permission (W)
S_IXOTH00001Others (OTH) execute permission (X)

AlsoCombined permissions

S_IRWXUGO

  • Definition(S_IRWXU | S_IRWXG | S_IRWXO)
  • Meaning (expanded)00700 | 00070 | 00007 = 00777
  • Actual meaning: AllowOwner, group, other users(UGO) haveread, write, executepermissions
  • Example usage: Set all users to read, write, execute, e.g., temporary directory/tmp

S_IALLUGO

  • Definition(S_ISUID | S_ISGID | S_ISVTX | S_IRWXUGO)
  • Meaning (expanded)0004000 | 0002000 | 0001000 | 00777 = 01777
  • Actual meaning: Includes special bits (SUID, SGID, Sticky) and all users’ read, write, execute permissions
  • Example usage: Common permissions:drwxrwxrwt(e.g.,/tmp

S_IRUGO

  • Definition(S_IRUSR | S_IRGRP | S_IROTH)
  • Meaning (expanded)00400 | 00040 | 00004 = 00444
  • Practical meaning: All usersreadpermission
  • Example usage: Commonly used for read-only files

S_IWUGO

  • Definition(S_IWUSR | S_IWGRP | S_IWOTH)
  • Meaning (expanded)00200 | 00020 | 00002 = 00222
  • Practical meaning: All userswritepermission
  • Example usage: Rarely used, generally limited to specific directories

S_IXUGO

  • Definition(S_IXUSR | S_IXGRP | S_IXOTH)
  • Meaning (expanded)00100 | 00010 | 00001 = 00111
  • Practical significance: EveryoneExecutepermission
  • Example usage: Make a script or program executable by everyone

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <linux/init.h>
#include <linux/module.h>


static int myint = 0;
module_param(myint,int, 0644);
MODULE_PARM_DESC(myint, "A sample int parameter");

static char* mycharp = "hello";
module_param(mycharp, charp, 0644);
MODULE_PARM_DESC(mycharp, "A sample charp parameter");

static int myarr[3] = {1, 2, 3};
static int myarr_argc = ARRAY_SIZE(myarr);
module_param_array(myarr, int, &myarr_argc, 0644);
MODULE_PARM_DESC(myarr, "A sample array parameter");

static char mystring[] = "default_value";
module_param_string(mystr, mystring, ARRAY_SIZE(mystring), 0644);
MODULE_PARM_DESC(mystr, "A sample string parameter");


static void print_param(void){
int i;
pr_info("[myint]: %d\n", myint);
pr_info("[mycharp]: %s\n", mycharp);
pr_info("[myarr]: ");
for(i =0;i<myarr_argc;i++){
pr_info("%d ", myarr[i]);
}
pr_info("[mystr]: %s\n", mystring);
}

static int __init param_test_init(void){
printk("param test init\n");
print_param();
return 0;
}

static void __exit param_test_exit(void){
print_param();
printk("param test exit\n");
}

module_init(param_test_init);
module_exit(param_test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("even629<asqwgo@163.com>");
MODULE_DESCRIPTION("linux driver parameter test");

Parameters can be passed when loading the module

1
insmod parameter.ko myint=42 mycharp="hello world" myarr=9,8,7 mystr="hello"

View parameters at runtime

1
2
3
cd /sys/module/parameter/parameters/
cat myint
echo 99 > myint

Kernel symbol table import and export

Drivers can be compiled into kernel modules, i.e., KO files. Each KO file is independent, meaning modules cannot access each other. However, in some usage scenarios, they need to access each other, such as when module B needs to use a function from module A. (Module B depends on module A)

Symbol table

**“Symbols” refer to function names, global variable names, etc., in the kernel.**A symbol table is a file used to record these “symbols”.

Module dependency relationship

Modules in the Linux kernel can provide functions or variables, which areEXPORT_SYMBOLexported via macros for use by other modules; these are called symbols.

The dependency of module B on module A means that module B uses symbols exported from module A.

depmodis a user-space tool (usually provided by thekmodpackage), run after kernel installation:

1
depmod -a <kernel_version>
  • It scans all/lib/modules/<kernel_release>/module files under.ko:
    • Usingmodinfo -F dependsor directly parsing the ELF symbol table;
    • Determine each moduleWhich symbols are needed (imports)andWhich symbols are provided (exports)
    • Build a dependency graph between modules.

Generated dependency files

FilePurpose
modules.depText format, each line format:module.ko: dependent_module1.ko dependent_module2.ko ...
modules.dep.binBinary format, formodprobeFast loading (avoids parsing text each time)
modules.symbols/modules.symbols.binRecords all exported symbols and their modules (for reverse lookup)

depmod also processes module files to extract and collect this information, and in/lib/modules/<kernel_release>/modules.aliasgenerates the modules.alias file, which maps devices to their corresponding drivers.

modprobe parses the modules.alias file.

Kernel symbol table export

Export Macro

Macro NameApplicable Scenario
EXPORT_SYMBOLExport Symbols to Kernel Symbol Table
EXPORT_SYMBOL_GPLOnly Applicable to Modules with GPL License

Exported symbols can be used by other modules; simply declare them before use.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <linux/module.h>
#include <linux/init.h>

int add(int a, int b){
return a+b;
}
EXPORT_SYMBOL(add);

static int __init module_export_init(void){
pr_info("add init\n");
return 0;
}

static void __exit module_export_exit(void){
pr_info("add exit\n");
}

module_init(module_export_init);
module_exit(module_export_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("even629<asqwgo@163.com>");
MODULE_DESCRIPTION("A sample for module export");

Import

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <linux/module.h>
#include <linux/init.h>

static int a = 0;
module_param(a,int, 0644);
MODULE_PARM_DESC(a, "add test left num a, default 0");

static int b = 0;
module_param(b, int, 0644);
MODULE_PARM_DESC(b, "add test left num b, default 0");



extern int add(int a, int b);

static int __init module_export_init(void){
pr_info("hello init");
pr_info("a=%d, b=%d\n", a, b);
pr_info("a+b=%d\n", a+b);
return 0;
}

static void __exit module_export_exit(void){
pr_info("hello exit");
}

module_init(module_export_init);
module_exit(module_export_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("even629<731005515@qq.com>");
MODULE_DESCRIPTION("A sample for module export");

Note: When loading, load the exporting module first; when unloading, unload the importing module first.Because the two modules now have a dependency relationship, modprobe will handle these dependencies automatically without explicit declaration.

Using Macros Defined in Makefile

Core idea: To make itvisible to C code, it must bepassed as a macro definition via the compiler command line

1
cc -D宏名=值 source.c

Example:

1
2
3
4
5
6
7
8
9
obj-m += mydriver.o

# Define a macro
MY_DRIVER_VER := 0x10

# Pass it to the compiler
KBUILD_CFLAGS_MODULE += -DMY_DRIVER_VER=$(MY_DRIVER_VER)
# Or use
ccflags-y += -DMY_DRIVER_VER=$(MY_DRIVER_VER)

Other variable names:

Variable NameScopeTypical Use
ccflags-yAll of the current module.cFilesCommon C compilation options
asflags-yAssembly filesAssembly parameters
subdir-ccflags-yCurrent directory and subdirectoriesGlobal scope
KBUILD_CFLAGSGlobal (set by kernel top-level Makefile)Platform-level CFLAGS
KBUILD_CFLAGS_MODULEKernel module
EXTRA_CFLAGSDeprecated (legacy usage)Temporary additional options

Then in the driver, you can use:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <linux/module.h>
#include <linux/kernel.h>

static int __init mydriver_init(void)
{
pr_info("mydriver version: 0x%x\n", MY_DRIVER_VER);
return 0;
}

static void __exit mydriver_exit(void)
{
pr_info("mydriver exit\n");
}

module_init(mydriver_init);
module_exit(mydriver_exit);

MODULE_LICENSE("GPL");