A good article on the Internet explaining the Linux Driver (applicable to kernels later than 2.6)

Source: Internet
Author: User

○ Description
Note is applicable to linux kernels later than 2.6.10.
Take notes in Linux
Device
The scull program provided by driver3 (main. C and scull. h In the scull directory) is the main record, and records the various system calls and function call processes in the driver.
Recording order. For example, module_init () and module_exit (
) Is a corresponding pair of system calls, which will be discussed together in general books, but this note does not, but will be involved only when called. Therefore, module_init (
) Will be placed at the beginning of the note, that is, when the module is loaded, and module_exit () will be placed before the end of the note, that is, it will be discussed again when the module is to be uninstalled.
The purpose of this note is to sort out all the knowledge points mentioned in Linux Device drvier3 and clarify the clues so that I can understand the Linux driver as a whole or globally.
Note: For personal understanding, mistakes are inevitable!
**************************************** ***
The workflow of the driver module is divided into four parts:
1. Use commands provided by Linux to load the driver module
2. initialize the driver module (after initialization, it enters the "Latent" State until there is a system call)
3. When a device is operated, that is, when a system call is made, various service functions provided by the driver module are called.
4. Uninstall the driver module

I. Driver Loading
The Linux driver can be directly compiled into the kernel, compiled into a module, and loaded manually when the driver module is required. The former is still to be learned.
Module driver. Linux provides two commands for loading: modprobe and insmod.
Its
Modprobe can solve the dependency of the driver module, that is, if the driver module being loaded references the kernel symbols or other resources provided by other modules
Modprobe will automatically load those modules. However, when using modprobe, the driver module to be loaded must be placed in the current module search path. Insmod
The command does not consider the dependency of the driver module, but it can load the driver module in any directory.
In general, in the driver development stage, it is easier to use/sbin/insmod, because you do not need to put the module in the current module search path.
Once the module is loaded using insmod, the Linux kernel calls the module_init (scull_init_module) special macro, where scull_init_module is the driver initialization function and can be customized.
When using insmod to load a module, you can also provide module parameters. However, you need to add several statements to the driver source code to make the module parameters visible to insmod and the driver, for example:
Static char * Whom = "world ";
Static int howmany = 10;
Module_param (howmany, Int, s_irugo );
Module_param (whom, CHARP, s_irugo );
In this way, when the driver is loaded using the/sbin/insmod scull. Ko whom = "string" how133 = 20 command, the values of whom and howmay will be passed into the scull driver module.
After the driver module is loaded, if the device is operated (such as open, read, write), the driver module calls the corresponding function to respond to the operation.
Then, when operating on the device, how does the driver module know that it should respond, rather than other driver modules? That is to say, how does the Linux kernel know which driver module should be called?
Contents
Previously, I only know that there are two ways to associate the device with the driver module (maybe it should be said that one way to provide access to the device is more appropriate ): the first is through the ID of some devices (such as PCI devices and
Device ID and product of the USB device
The Linux kernel calls the driver module based on these IDs. The second is to create the corresponding device node (that is, the device file) under the/dev directory based on the device's primary and secondary device numbers.
When the device file under the/dev directory is called, the corresponding driver module is called.
Ii. initialization of the driver module
When using insmod to load the driver module, you need to make the driver module
The main purpose of initialization is to let the Linux kernel know the device (or module ?), And when you operate the device (such as open, read, write, and so on) in the future,
Let the Linux kernel know which functions of this module can serve system calls.
Therefore, the scull_init_module function mainly performs the following tasks:
A) Allocate and register the Primary and Secondary device numbers.
B) initialize the struct representing the device: scull_dev
C) initialize the mutex init_mutex (this note is not organized)
D) initialize the cdev struct of the device in the kernel. It is mainly used to associate the device with the file_operations struct.
1. Allocate and register the Primary and Secondary device numbers
The device number is allocated and registered in the driver module. That is to say, the driver module has this device number (in my understanding ), the device files in the/dev directory are created based on the device number. Therefore, when accessing the device files in the/dev directory, the driver module knows that, you should launch the service yourself (kernel notification, of course ).
In the Linux kernel, the main device ID identifies the driver corresponding to the device and tells the Linux kernel which driver is used to serve the device (that is, the device file under/Dev; the device number is used to identify a specific and unique device.
In the kernel, the variables of the dev_t type (actually a 32-bit unsigned integer) are used to save the Primary and Secondary device numbers of the device. The 12-bit high represents the primary device number, the 20-digit device number indicates the next device number.
There are two ways for a device to obtain the Primary and Secondary device numbers: one is to manually specify a 32-digit number and associate it with the device (that is, register with a function ); the other is to call the system function to dynamically allocate a primary/secondary device number to the device.
To manually specify a primary/secondary device number, use the following function:
Int register_chrdev_region (dev_t first, unsigned int count, char * name)
First is the device number that we manually specify, count is the number of consecutive device numbers requested, and name is the name of the device associated with the range of the device numbers, it will appear in/proc/devices and sysfs.
Ratio
For example, if first is 0x3ffff0 and count is 0x5, this function registers device numbers for five devices, namely 0x3ffff0 and 0x3ffff1,
0x3ffff2, 0x3ffff3, 0x3ffff4, where 0x3 (12-bit high) is the master device number shared by the five devices (that is, the five devices use the same driver
). 0xffff0, 0xffff1, 0xffff2, 0xffff3, and 0xffff4 are the sub-device numbers of the five devices respectively.
Note that if
If the value of count is too large, the requested device number range may overlap with the next primary device number. For example, if first is 0x3ffff0 and count is 0x11
First + Count = 0x400001, that is, the number of the master device allocated to the last two devices is no longer 0x3, but 0x4!
One disadvantage of using this method to register a device number is that if the driver module is widely used by others, therefore, it cannot be ensured that the registered device number is a device number not assigned to others in Linux.
To dynamically allocate device numbers, use the following functions:
Int alloc_chrdev_region (dev_t * Dev, unsigned int firstminor, unsigned int count, char * name)
This function must be passed to the specified device number firstminor (usually 0), the number of devices to be allocated, and the device name, after this function is called, the device numbers automatically allocated are stored in Dev.
Dynamic
The allocation of device numbers can avoid the disadvantages of manually specifying device numbers, but it also has its own disadvantages, that is, it is impossible to create a device node under/dev in advance, because the Dynamic Allocation of device numbers cannot be ensured at each time
The driver module is always the same when being loaded. (In fact, if no other modules are loaded between two loaded drivers, the device IDs automatically allocated are the same, because the kernel
The allocation of device numbers is not random, but the book says some kernel developers will handle them randomly in the near future). However, this disadvantage can be avoided, because after loading the driver module
You can read the/proc/devices file to obtain the master device number assigned to the device by the Linux kernel.
Linux Device
Driver3 provides the scull_load and scull_unload scripts to create and delete device nodes for devices under dynamic allocation. In fact, it is also used
The awk tool obtains information from/proc/devices and then uses mknod to create a device node under/dev.
In fact, the scull_load and scull_unload scripts can also be applied to other drivers. You only need to redefine the variables and adjust the mknod statements.
Three macros related to the primary and secondary device numbers:
Major (dev_t Dev): obtains the master device number based on the device number Dev;
Minor (dev_t Dev): obtains the next device number based on the device number Dev;
Mkdev (INT major, int minor): Creates a device number based on the master device number major and sub-device number minor.
2. initialize the scull_dev struct of the device.
Scull Source Code defines a scull_dev struct, including qset, qutuam, semaphore SEM, cdev, and other fields. The initialization of qset and qutuam is irrelevant to the knowledge of the Linux driver, so we will not discuss it.
Me
As long as you know, some device-related variables can be initialized in the module initialization function called during module loading. However, according to the Linux Device
According to drvier3, it is best to initialize device-related variables or resources in the open function, such as interrupt numbers. Although it is allowed to be registered in the module initialization function
But it is best to re-allocate the device in the open function for the first time.
3. initialize the mutex init_mutex.
Mutex, a variant of the semaphore, is related to the completion, spin lock, and so on. It will be discussed later.
4. initialize the cdev struct that represents the device in the kernel.
In the Linux kernel, The cdev struct actually represents a device. Before the kernel calls open and read operations on devices, it is necessary to allocate and register one or more cdev structures.
Me
It can be understood that the primary and secondary device numbers are foreign-related and are mainly used to determine the identity when interacting with external devices (other than the driver module and the Linux kernel; the cdev struct is internal. When
It is required to pass some variables, pointers, buffers, and other things within the module or between the module and the Linux kernel, or to call a service function in the driver module.
Cdev struct.
In the scull function, a custom scull_setup_cdev function is used for the allocation, registration, and initialization of the cdev struct. In this function, the following four statements are used to initialize cdev:
Cdev_init (& Dev-> cdev, & scull_fops );
Dev-> cdev. Owner = this_module;
Dev-> cdev. Ops = & scull_fops;
Err = cdev_add (& Dev-> cdev, devno, 1 );
(The dev variable is a struct defined by the scull program that represents the device. It contains the cdev struct. For Dev, The cdev struct should be at its core)
The first statement is to initialize the cdev struct. For example, to allocate memory for the cdev struct
The structure specifies file_operations, and so on, and the role of the third statement seems to have already been the same as that of the first statement. But in the scull program, it must have its intention to write this code.
The Linux kernel source code can be understood, but I understand it as follows: Part of the first statement about file_operations is to tell the Linux kernel that
The file_operations related to the cdev struct are scull_fops, while the second statement specifies its file_operations for cdev.
The field is scull_fops.
Scull_fops is a variable of the file_operations type, and file_operations is also a struct and an important struct in the Linux driver. In the scull program, the definition is as follows:
Struct file_operations scull_fops = {
. Owner = this_module,
. Llseek = scull_llseek,
. Read = scull_read,
. Write = scull_write,
. IOCTL = scull_ioctl,
. Open = scull_open,
. Release = scull_release,
};
To
In the above definition, the first. Owner field indicates that the owner of the file_operations struct is the driver module, and the following fields tell the driver
Module. When a system call arrives at the module, which function should the module call to call the service for the system. For example, if an open system call arrives
Module, the module will know by querying the file_operations struct. The scull_open function is related to the open system call.
Module calls the scull_open function to call the service for the open system. The other fields are similar.
Of course, the file_operations struct defined in the Linux kernel also includes some other fields, such as Asynchronous read/write, but let's talk about it later.
The second statement for cdev Initialization is Dev-> cdev. Owner = this_module. This statement indicates that the owner of the cdev struct being initialized is the current module.
The last statement for cdev Initialization is err = cdev_add.
(& Dev-> cdev, devno,
1) The purpose of this statement is to tell the kernel that the cdev struct information. Because the cdev_add function may fail to be called, you need to check the return value of the function call. And once
If the cdev_add call is successful, our device will be "active! That is to say, operations performed by external applications on it will be allowed and called by the kernel. Therefore, the driver is not completely ready for processing.
You cannot call cdev_add when operating on a device.
Iii. device operations
After the driver module is initialized due to insmod loading, it enters the "Latent" state, that is, if there is no system call (such as open or read ), other functions defined in the module will never run!
The device operation here refers to the action that the module should call when a system call reaches the driver module.
For driver development, I only care about how to pass variable values in some external applications to the driver module.
In the scull program, functions related to device operations are mainly divided into three types: initialization functions, actual operation service functions and cleaning functions. There is only one initialization function, that is, the open function, and the operation service function includes the read, write, llseek and other functions. The cleaning function is the release function.
1. Open Functions
The open function provides the initialization capability for the driver to prepare for future operations.
Description
After the driver is loaded with insmod, there is also an initialization action, but that initialization is relative to the entire Linux kernel, or the global initialization of the entire module when it is foreign.
While the initialization of the open function is relative to the device operation, it is an internal initialization of the driver, for example, for a variable (such as a file structure) used for later read operations, perform the initial
For example, initialize the device and clear the buffer.
In most drivers, open should do the following:
A. Are you sure you want to enable the specified device?
B. Check device-specific errors (such as equipment not ready or similar hardware problems)
C. If the device is enabled for the first time, initialize it.
D. Update the f_op pointer if necessary.
E. Allocate and fill in the data structure placed in filp-> private_data
The following is a prototype of the open function (defined in the file_operations struct ):
INT (* open) (struct inode * inode, struct file * filp)
During driver development, you must implement the function. Of course, the name of the open function can be customized. If you enter the open field in the file_operations struct, enter the custom OPEN function name. In the scull program, the scull_open function name is used.
In the open function prototype, there are two parameters: inode and filp, both of which are operated by external applications.
The device is passed to the driver module by calling the system call. The driver module can use these two parameters to determine the specific device to be opened. Actually, the specific device mentioned here is not
The driver module needs to determine the device to be served from all the devices installed in the system. Instead, the module needs to determine the device to be served from a certain type of device with the same master device number.
Of
Therefore, the driver module corresponds to all the devices with a master device number. In other words, the Linux kernel only uses the device's primary device number, regardless of the device's secondary device number.
What, if two, three, or more devices have the same master device number, the Linux kernel will only call the same driver no matter which of these devices the external application wants to operate on.
Module. However, the driver module cannot ignore the device number, because it deals with a specific device, so it needs to be found in the parameters passed to it during the open system call.
Device number to determine the unique device (maybe the driver module can operate several devices at the same time, but it cannot be remembered at the moment ).
However, the above mentioned method is only one of the methods for finding a specific device through the next device number. The other method is to identify a specific device through the cdev struct.
Set
The cdev struct or secondary device Number of the slave node is stored in the inode parameter of the open function. We can use the container_of macro to pass
Cdev determines the specific device. You can also use the iminor macro to determine the device number from the I _rdev of inode (I _rdev is a dev_t type in the inode struct ).
And stores the real device primary and secondary numbers ).
For the file parameter in the open function, the scull program mainly uses it to do two things:
Save the scull_dev struct obtained by cdev to file-> private_data, which facilitates future access to this struct.
Instead of calling container_of macro or iminor macro every time to find the device struct. The second is determined based on the f_flags field in the file struct.
Open call is to open the device in write mode or read mode.
2. Read Functions
Many device operation service functions can be defined in the file_operations struct of the driver module, but I am concerned about how these functions interact with the system call, or external applications, no matter how specific device operations are implemented, only the READ function is recorded as the representative.
The READ function is prototype as follows:
Ssize_t read (struct file * filp, char _ User * Buf, size_t count, loff_t * f_pos)
The READ function prototype has four parameters: filp, Buf, count, and f_pos.
The file struct pointer parameter can be used to determine the device to operate, because in the open function, we save the struct representing the device to the private_data field of filp.
Buf
The parameter is a pointer to the buffer in the user space (_ User in front of the Buf indicates the user space). For read, the data to be transmitted to the external application can be placed in this
Buffer. Of course, we cannot simply copy data to this buffer, but use some functions provided by the Linux kernel, such as the copy_to_user function.
Number.
Count is the length of data transmitted by the request.
F_pos is a pointer to a long offset type object, indicating the location where external applications perform access operations in files.
The Return Value of the READ function is a signed integer, which is generally the actual number of accesses to the read operation.
3. Release Functions
The functions of the release function are opposite to those of the open function. Sometimes, the implementation of the release function is called device_close rather than device_release. However, regardless of the form, this device method should complete the following tasks:
A. Release all content allocated by open and stored in file-> private_data.
B. Disable the device during the last close operation.
The relese function is called by the close system, but not every call to the close system
Call the release function. Only the close system call that actually releases the device data structure will cause the call of the release function. Because the Linux Kernel
The file struct maintains the number of times it is referenced. Only when the counter of the file struct is set to 0 will the close system call reference the release function. This is only when the structure is deleted.
So each open driver will only see a corresponding release call.
4. Uninstall the driver module
An clearing function is required for each important module. This function logs out the interface before the module is removed and returns all resources to the system. If a module has no clear function defined, the kernel Cannot uninstall the module.
Linux
You can use/sbin/rmmod to uninstall the driver module.
Scull. Ko command, then the Linux kernel will call the clear letter defined in the special macro of module_exit (scull_cleanup_module) in the driver.
Number, that is, the module_exit statement is used to help the Linux kernel find the cleanup function of the module. In the scull program, the cleanup function is
Scull_cleanup_module function.
The clearing function of the module needs to cancel all the resources registered by the initialization function, and is used to (but not necessary) to note with the initialization Function
In reverse order. It should be noted that the initialization function here refers to the initialization function using module_init macro declaration, rather than the open function, which corresponds to the open function.
It should be the release function.
In the scull program, the cleanup function mainly does two things: first, free all the memory allocated for the scull device, and second, the device number registered by the initialization function.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.