Cover image for Linux driver framework

Linux driver framework

Words 5k
Views
Visitors

Timeline

Timeline

2025-11-09

init

This article introduces the basics of the Linux driver framework, including the three major driver categories (character devices, block devices, network devices), the main directory structure of the Linux kernel source code and its functions. It also analyzes the components of the simplest Linux driver module (header files, load/unload functions, license declarations, module parameters, etc.), and explains the correspondence between module loading success and return values, as well as the module information storage mechanism.

Linux Driver Notes

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

Linux kernel related resources:
https://github.com/0voice/linux_kernel_wiki/blob/main/README.md

Linux driver abstraction

Evolution of the Linux driver framework
Evolution of the Linux driver framework

Linux 2.4 driver architecture
Linux 2.4 driver architecture

Linux 2.6 introduced the platform bus architecture
Linux 2.6 introduced the platform bus architecture

Linux device tree
Linux device tree

Linux divides storage and peripherals into 3 basic categories:Character device driverBlock device driverNetwork device driver

Linux source code directory structure

Table of ContentsDescription
archArchitecture-related directory, containing adaptation code for multiple CPU architectures (such as ARM, x86, MIPS, etc.)
blockBlock device related code directory; in Linux, storage devices such as hard disks and SD cards are managed as block devices.
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, storing 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 startup initialization directory, storing initialization code for the Linux kernel boot stage.
ipcInter-process communication directory, containing implementation code for IPC mechanisms such as pipes, message queues, shared memory, etc.
kernelKernel core directory, containing the core functional code of the kernel itself.
libLibrary function directory, storing various library functions used by the kernel.
mmMemory management directory (mm is an abbreviation for memory management), responsible for kernel memory management functions.
netNetwork-related directory, storing implementation code for network functions such as the TCP/IP protocol stack.
scriptsScript directory, storing script files used in processes such as kernel compilation and testing.
securitySecurity-related directory, storing implementation code for kernel security mechanisms.
soundAudio-related directory, storing code for audio device drivers and audio processing.
toolsTools directory, storing utility programs used for Linux kernel development and debugging.
usrCode directory related to Linux kernel startup.
virtKernel virtual machine related directory, storing implementation code for kernel-level virtualization features.

Analysis of the simplest Linux driver structure.

  • Components
    1. Header file (required): The driver must include kernel-related header files, among which<linux/module.h>and<linux/init.h>is 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): Because the Linux kernel follows the GPL license, drivers must also comply with the relevant license when loaded; 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
1234567891011121314151617181920212223
#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 is loaded successfully if and only if the init function in module_init returns 0; a negative return value indicates loading failure.

Module information

123456789101112131415161718192021222324252627282930313233343536373839404142
$ objdump -h hello_world.kohello_world.ko:     file format elf64-littleSections: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_*All macros update the content of this section with the values passed as parameters. Some of these macros are
MODULE_DESCRIPTION()MODULE_AUTHOR()andMODULE_LICENSE(). The real low-level macro provided by the kernel to add entries to the module information section isMODULE_INFO(tag,info), and the general form of information it adds istag=info. This means driver authors are free to add any form of information they want, for example:

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

.modinfoThe content of the section:

123456789101112131415
$ readelf -x .modinfo hello_world.koHex 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 usemodinfoview

123456789
$ modinfo hello_world.kofilename:       /home/zhaohang/repository/linux/linux_driver_learning/01_hello_world/hello_world.kodescription:    hello world!author:         even629license:        GPLsrcversion:     A40003DF630179B6CF1D045depends:name:           hello_worldvermagic:       5.10.110-v8+ SMP preempt mod_unload modversions aarch64

Compiling Linux drivers

  • Put the driver inside the Linux kernel, then compile the Linux kernel. Compile the driver into the Linux kernel.
  • Compile the driverinto a kernel module, independent of the Linux kernel
    • Kernel modules are 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. Moreover, drivers can be inserted or unloaded while the system is running without needing to reboot. The suffix of kernel modules is .ko

obj-<X>kbuild variables

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>If set to m, use the variable obj-m, and build mymodule.o as a module.
  • if<X>If set to y, use the variable obj-y, and mymodule.o will be built as part of the kernel. It can also be said to be a built-in module.
  • if<X>If set to n, it means the module is not compiled.

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

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

$(CONFIG_MYMODULE)It 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 another case:

1
obj-<X> += somedir/

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

into a kernel module

123456789101112
obj-m += hello_world.oKERNEL_SRC:=/home/zhaohang/repository/linux/linux-5.10.246PWD ?=$(shell pwd)ARCH = arm64CROSS_COMPILE = aarch64-linux-gnu-all:	$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_SRC) M=$(PWD) modulesclean:	$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_SRC) M=$(PWD) modules clean	rm -rf *.ko *.o *.mod.o *.mod.c *.symvers *.order

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

Compiling the driver into the kernel

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

12345
config helloworld	bool "helloworld support"	default y	help			helloworld

Modify drivers/char/Kconfig

123
emacs ../Kconfig# Addsource "drivers/char/helloworld/Kconfig"

Create a Makefile in the driver source code

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

Then add the following to the parent Makefile:

123
emacs ../Makefile# Addobj-y += helloworld/

Module loading command

  • insmod
    • Function: Load a Linux kernel module
    • Syntax: insmod module_name
    • Example: insmod hello_world.ko
  • modprobe
    • Function: Loads a kernel module, and also loads the modules that this module depends on.
    • Syntax: modprobe module_name
    • Example: modprobe hello_world

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

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

Module Unload Command

  • rmmod

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

    • It not only attempts to unloadmymodule, but also automatically checks and unloads those dependency modules that are onlymymoduledepended on, and are not currently used by other modules/processes.
    • Syntax:modprobe -r mymodule

The enabling or disabling of the kernel module unload feature is determined by the value of 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 unloading. 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 remove it. However, the following setting 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 the file/etc/modules-load.d/<filename>.conf, and add the names of the modules that should be loaded (one per line). People commonly use modules:/etc/modules-load.d/modules.confOf course, you can also create multiple .conf files as needed.

12345
emacs /etc/modules-load.d/mymodule.conf# Below is the content of mymodule.conf.uioiwlwifii2c-dev

Command to view module information

  • lsmodcommand
    • Function: List the kernel modules already loaded in Linux
    • You can also use the command cat /proc/modules to check whether the module was loaded successfully.
  • modinfocommand
    • Function: View kernel module information
    • Syntax: modinfo module_name
    • Example: modinfo hello_world.ko

Configuration file

menuconfigOperations for configuring driver option states:

Driver states:

  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 switch among these three different states.

The states of an option are:

  • []: Indicates there are two states, which can only be set to selected or not selected.
  • <>: Indicates there are three states, which can be set to selected, not selected, or compiled as a module.
  • () : Indicates it is used to store a string or a hexadecimal number.

Kconfig file

The Kconfig file is the source file of the graphical configuration interface. The options in the graphical configuration interface are determined by the Kconfig file. When we execute the commandmake menuconfigWhen the command is executed, the kernel configuration tool will read the files in the kernel source directory.arch/xxx/Kconfig.xxx is the value of ARCH, such as arm64, and then the corresponding configuration interface is generated 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 tree, and isthe default configuration file of the Linux system

  • The .config file is located in the top-level directory of the Linux kernel source tree. 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 you modify the settings in the graphical configuration interface and save, 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 generates 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

The menu/endmenu keywords can be used to create a menu. menu marks the beginning of a menu, and endmenu marks the end of a menu. They appear in pairs.

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

1234
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, intThe 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 a string type.

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

Example:

12345
config helloworld	bool "hello world support"	default y	help		hello world

Dependencies

Dependencies in Kconfig can be expressed using depends on and select

depends on indicates a direct dependency relationship

12
config A	depends on B

Indicates that option A depends on option B; option A can be selected only when option B is selected.

select indicates a reverse dependency:

12
config A	select B

When option A is selected, option B is automatically selected.

Selectable options

Use choice and endchoice to define selectable items.

12345678
choice	prompt "a"config b	bool b1config c	bool c1...endchoice

Comment

Display a comment in the graphical configuration interface.

123456
config TEST_CONFIG	bool "test"	default y	help		just testcomment "just for test"

source

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. By passing parameters to drivers, the driver can be made more flexible and more compatible.
  2. Security checks can be set through driver parameter passing to prevent the driver from being misappropriated.

Disadvantages

  1. Makes the driver code more complex.
  2. Increases the driver’s resource usage.

Parameter types that can be passed to drivers

The kernel supports most common C language data types 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

Ways to pass parameters to Linux drivers

  • Parameter types and corresponding functions

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

module_param

123456789101112131415161718192021222324252627
/** * 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

12345678910111213141516
/** * 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

123456789101112131415161718
/** * 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: 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 on the command line)
typeParameter type (e.g.,intboolcharp
permBefore/sys/module/<modname>/parameters/permissions in, such as0644
nump(array only) Address of the variable that stores the number of array elements
len(string only) Buffer length
stringPointer to the string buffer

Permission definition

perm refers to parameter file in sysfsof file permission bits

read/write permissions are ininclude/linux/stat.handinclude/uapi/linux/stat.hdefined in.

  • include/linux/stat.h
12345678910111213141516
/* 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
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
/* 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 related 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_IRWXO00007Other users (O) read, write, execute permissions (RWX)
S_IROTH00004Other users (OTH) read permission ®
S_IWOTH00002Other users (OTH) write permission (W)
S_IXOTH00001Other users (OTH) execute permission (X)

andCombined permissions

S_IRWXUGO

  • Definition(S_IRWXU | S_IRWXG | S_IRWXO)
  • Meaning (expanded)00700 | 00070 | 00007 = 00777
  • Actual meaning: Allow Owner, group, other users (UGO) have read, write, execute Permissions
  • 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(such as/tmp

S_IRUGO

  • Definition(S_IRUSR | S_IRGRP | S_IROTH)
  • Meaning (expanded)00400 | 00040 | 00004 = 00444
  • Actual meaning: All users Read Permissions
  • Example usage: Often used for read-only files

S_IWUGO

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

S_IXUGO

  • Definition(S_IXUSR | S_IXGRP | S_IXOTH)
  • Meaning (expanded)00100 | 00010 | 00001 = 00111
  • Actual meaning: All users Execute Permissions
  • Example usage: Make a script or program executable by all users

example

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
#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

123
cd /sys/module/param_test/parameters/cat myintecho 99 > myint

Kernel symbol table import/export

Drivers can be compiled as 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, e.g., module B wants to use a function in module A. (Module B depends on module A)

Symbol table

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

Module dependency relationships

Modules in the Linux kernel can provide functions or variables, usingEXPORT_SYMBOLthe macro to export them makes them available to other modules; these are called symbols.

Module B’s dependency on module A means that module B uses symbols exported from module A.

depmodis a user-space tool (usually provided bykmodpackage), run after the kernel is installed:

1
depmod -a <kernel_version>
  • It scans/lib/modules/<kernel_release>/all under.komodule files:
    • usemodinfo -F dependsor directly parses the ELF symbol table;
    • determines for each module which symbols it needs (imports) and which symbols it provides (exports)
    • builds a dependency graph between modules.

The generated dependency files

filesFunction
modules.deptext format, each line format:module.ko: dependent_module1.ko dependent_module2.ko ...
modules.dep.binbinary format, formodprobefast loading (avoid parsing text each time)
modules.symbols/modules.symbols.binrecords all exported symbols and their owning modules (for reverse lookup)

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

modprobe parses the modules.alias file.

Kernel symbol table export

Export macros

Macro nameApplicable scenarios
EXPORT_SYMBOLExport symbols to the kernel symbol table
EXPORT_SYMBOL_GPLOnly applies to GPL-licensed modules

Exported symbols can be used by other modules; they only need to be declared before use.

Example

123456789101112131415161718192021222324
#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

1234567891011121314151617181920212223242526272829303132
#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\n");	pr_info("a=%d, b=%d\n", a, b);	pr_info("a+b=%d\n", add(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 automatically handle these dependencies without explicit declaration.

Using macros defined in the makefile

Core idea: to make it visible to C code, you must pass macro definitions via the compiler command line

1
cc -D宏名=值 source.c

Example:

123456789
obj-m += mydriver.o# Define a macroMY_DRIVER_VER := 0x10# Pass it to the compilerKBUILD_CFLAGS_MODULE += -DMY_DRIVER_VER=$(MY_DRIVER_VER)# or useccflags-y += -DMY_DRIVER_VER=$(MY_DRIVER_VER)

Other variable names:

Variable nameScopeTypical Uses
ccflags-yAll of the current module’s.cfilesordinary 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 modules
EXTRA_CFLAGSDeprecated (legacy usage)Temporary additional options

Then in the driver, you can use:

123456789101112131415161718
#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");
Loading comments…