Developing Linux drivers is significantly different from developing application programs. These differences lead to the essential distinction between writing Linux device drivers and writing application programs.
The Linux operating system is divided into user mode and kernel mode. The kernel mode handles interactions with hardware, such as reading and writing memory, and loading data from the hard disk into memory. Drivers interact with the hardware at a low level, thus operating in kernel mode. User mode can be understood as upper-level applications, which can be Java applications, Qt applications, Python applications, etc. The reason for dividing the Linux operating system into two states is that even if an application in user mode encounters an exception, it will not cause the operating system to crash, all thanks to the strong protective capabilities of the kernel mode over the operating system.
On the other hand, the main reason for dividing the Linux operating system into two states is to provide a unified hardware abstraction for application programs. Applications running in user mode do not have to consider low-level hardware operations; these operations are handled by kernel mode programs, most of which are device drivers. Applications can operate hardware devices effectively without understanding the working principles of the hardware, while also preventing the hardware devices from entering an illegal state.
It is worth noting that user mode and kernel mode can be converted to each other. Whenever an application executes a system call or is suspended by a hardware interrupt, the Linux operating system switches from user mode to kernel mode; when the system call is completed or the interrupt handling is finished, the operating system returns to user mode to continue executing the application.
A module is code that can be added to the kernel at runtime, which is a great feature of Linux. This feature allows the kernel to easily expand or shrink; expanding the kernel can add functionality, while shrinking the kernel can reduce its size. The Linux kernel supports various modules, with device drivers being one of the most important. Each module consists of compiled object code, which can be added to the running kernel using the insmod command and removed using the rmmod command.
Modules loaded during kernel startup are called static loading, while those loaded while the kernel is already running are called dynamic loading. Modules can expand any functionality that the kernel expects but are usually used to implement device drivers.
The most basic framework code for a module is as follows:
To master Linux driver development, one must deeply understand the Linux bus device driver framework. This framework is formed mainly for code reusability since the relationship between drivers and devices is one-to-many. Just as there is a distinction between the major device number and the minor device number, the major device number represents the driver, while the minor device number represents the specific device.
Additionally, to enhance the portability of drivers, Linux separates the resources needed by drivers (such as GPIO and interrupts) for the device to manage. That is, the device contains its own attributes and the resources it uses to connect to the SoC. The driver focuses on the operational flow and methods.
The bus’s role is to manage devices and drivers at the software level. For a device to let the system recognize its existence, it needs to register itself with the bus; similarly, a driver also needs to register itself with the bus for the system to recognize its existence. During initialization, both the device and the bus must clearly identify which bus they belong to. Therefore, to achieve operational consistency, Linux invented a virtual bus called the Platform bus.
When multiple devices and multiple drivers are registered on the same bus, how does a device find the most suitable driver for itself, or how does a driver find the devices it supports? This is also the responsibility of the bus, which acts like a matchmaker between devices and drivers. Devices will specify their conditions for drivers to the bus (the simplest and most precise condition is to specify the name), while drivers will inform the bus of the conditions they can support (usually model IDs, or simply the device name).
When devices register, the bus will traverse the drivers registered on it to find the most suitable driver for the device, then fill in the device’s structure members; when drivers register, the bus will also traverse the devices registered on it to find the devices they support (which can be multiple, as the relationship between drivers and devices is 1:N) and fill the devices into the driver’s support list. We refer to this matchmaking behavior of the bus as match. Once the connection is made, the bus no longer concerns the interactions between the device and the driver.
1. A heartfelt message from an embedded veteran: mere enthusiasm is far from enough!
2.Linux Foundation opens ACRN hypervisor for IoT and embedded device design!
3. Detailed explanation of the growth experience of embedded Linux engineers
4. What is the portability of C language?
5. The ultimate value of blockchain technology: the Internet of Things
6. After eight years in analog electronics, why do I feel like I haven’t even started? What’s going on?
Disclaimer: This article is a network reproduction, and the copyright belongs to the original author. If there are any copyright issues, please contact us, and we will confirm the copyright based on the copyright certificate you provide and either pay the remuneration or delete the content.