Home> System Tutorial> LINUX> body text

The order in which underlying hardware device drivers are inserted into the linked list (driver)

PHPz
Release: 2024-04-02 09:10:02
forward
1125 people have browsed it

What are the drivers:

The driver encapsulates the operations of the underlying hardware device and provides function sockets to the lower layer.

Equipment classification:

The Linux system divides devices into three categories: character devices, block devices, and network devices.

Character device: refers to a device that can only read and write byte by byte. Certain data in the device's video memory cannot be read randomly. Data must be read in order. Character devices are stream-oriented deviceslinux driver development. Common character devices include mice, keyboards, serial ports, consoles, and LED devices. Block device: refers to a device that can read a certain thickness of data from any location on the device. Block devices include hard drives, disks, USB flash drives, SD cards, etc. Network device: A network device can be a hardware device, such as a network card; but it can also be a purely software device, such as a loopback socket (lo). A network socket is responsible for sending and receiving data packets. Driven Cognition:

First look at a picture, which describes the process and helps to understand the driver.

驱动开发是干什么的_linux 驱动 开发_驱动开发和嵌入式开发的差别

User mode:

Kernel state:

驱动开发是干什么的_linux 驱动 开发_驱动开发和嵌入式开发的差别

linux 驱动 开发_驱动开发和嵌入式开发的差别_驱动开发是干什么的

Driver array: Manage the drivers of all devices, add or find them. The addition occurs after we compile the driver and load it into the kernel. The search is calling the driver, and the application layer user space uses the open function to search. The order in which the driver is inserted into the array is retrieved by the device number. That is to say, the major device number and the minor device number can not only distinguish different types of devices and different types of devices, but can also load the driver into a certain position in the array, which is introduced below. The development of driver code is nothing more than adding the driver (adding the device number, device name and device driver function) and calling the driver.

Replenish:

Each system call corresponds to a system call number, and the system call number corresponds to the corresponding processing function in the kernel. All system calls are triggered via interrupt 0x80. When using a system call, the system call number is passed to the kernel through the eax register. The input parameters of the system call are passed to the kernel in turn through ebx, ecx... Just like the function, the return value of the system call is stored in eax, and all the parameters must be obtained from eax. How to remove the character device driver

Character device driver working principle In the Linux world, everything is a file, and all hardware device operations will be embodied as file operations when operating at the application layer. We know that if the application layer wants to access a hardware device, it must call the driver corresponding to the hardware. There are so many drivers in the Linux kernel. How can an application accurately call the underlying driver?

Replenish:

(1) When the open function opens a device file, you can know the type of device to be operated (character device or block device) based on the information described by the structinode structure corresponding to the device file, and a structfile structure will be allocated .

(2) According to the device number recorded on the structinode structure, the corresponding driver can be found. Here we take character devices as an example. In the Linux operating system, each character device has a structcdev structure. This structure describes all the information of the character device, the most important of which is the operation function socket of the character device.

linux 驱动 开发_驱动开发是干什么的_驱动开发和嵌入式开发的差别

(3) After finding the structcdev structure, the Linux kernel will record the first address of the video memory space where the structcdev structure is located in the i_cdev member of the structinode structure, and record the function operation socket address recorded in the structcdev structure in the structfile structure in the body's f_ops member.

(4) When the task is completed, the VFS layer will return a file descriptor (fd) to the application. This fd corresponds to the structfile structure. Then the lower-layer application can find the structfile through fd, and then find the function socket file_operation in the structfile to operate the character device.

Among them, cdev_init and cdev_add have already been called in the driver's entry function, respectively completing the binding of the character device and the file_operation function operation socket, and registering the character driver to the kernel.

Related video recommendations

Free learning address: LinuxC/C development (front-end/audio and video/game/embedded/high-performance network/storage/infrastructure/security)

Need C/C Linux server architect learning materials and add qun579733396 to obtain them (materials include C/C, Linux, golang technology, Nginx, ZeroMQ, MySQL, Redis, fastdfs, MongoDB, ZK, streaming media, CDNlinux input method, P2P , K8S, Docker, TCP/IP, interpreter, DPDK, ffmpeg, etc.), free sharing

linux 驱动 开发_驱动开发是干什么的_驱动开发和嵌入式开发的差别

The relationship between character devices, character device drivers, and user space programs that access the device

驱动开发和嵌入式开发的差别_linux 驱动 开发_驱动开发是干什么的

As shown in the figure, the cdev structure is used in the Linux kernel to describe character devices, and its member dev_t is used to define the device number (divided into major and minor device numbers) to determine the uniqueness of the character device. Define the socket functions provided by the character device driver to VFS through its member file_operations, such as common open(), read(), write(), etc.

In the Linux character device driver, the module loading function obtains the device number statically or dynamically through register_chrdev_region() or alloc_chrdev_region(), builds the connection between cdev and file_operations through cdev_init(), and adds it to the system through cdev_add() A cdev to complete registration. The module uninstall function logs out cdev through cdev_del() and releases the device number through unregister_chrdev_region().

The user space program that accesses the device uses Linux system calls, such as open(), read(), write(), to "call" file_operations to define the socket function provided by the character device driver to VFS.

Driver Development Steps

The Linux kernel is composed of various types of drivers. About 85% of the kernel source code is the code of various types of drivers. There are a complete range of drivers in the kernel, and they can be changed on the basis of similar drivers to match specific boards.

The difficulty in writing a driver is not the specific operation of the hardware, but figuring out the framework of the existing driver and adding the hardware to this framework.

Generally speaking, the general process of compiling a Linux device driver is as follows:

The following uses a simple character device driver framework code to develop and compile the driver.

Code development based on driver framework

Lower layer calling code

#include #include #include #include void main() { int fd,data; fd = open("/dev/pin4",O_RDWR); if(fd<0){ printf("open failn"); perror("reson:"); } else{ printf("open successn"); } fd=write(fd,'1',1); }
Copy after login

Driver framework code

#include //file_operations声明 #include //module_initmodule_exit声明 #include //__init__exit 宏定义声明 #include //classdevise声明 #include//copy_from_user 的头文件 #include//设备号dev_t 类型声明 #include //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno;//设备号 static int major = 231; //主设备号 static int minor = 0; //次设备号 static char *module_name = "pin4"; //模块名 //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_openn");//内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_writen"); return 0; } static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open= pin4_open, .write = pin4_write, }; int __init pin4_drv_init(void) //真实驱动入口 { int ret; devno = MKDEV(major, minor);//创建设备号 ret = register_chrdev(major, module_name, &pin4_fops);//注册驱动告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE, "myfirstdemo"); //用代码在dev自动生成设备 pin4_class_dev =device_create(pin4_class, NULL, devno, NULL, module_name);//创建设备文件 return 0; } void __exit pin4_drv_exit(void) { device_destroy(pin4_class, devno); class_destroy(pin4_class); unregister_chrdev(major, module_name);//卸载驱动 } module_init(pin4_drv_init);//入口,内核加载该驱动(insmod)的时候,这个宏被使用 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
Copy after login

It is said that the key difficulty in driver development is to understand the framework code and add and change devices on it. Let’s learn about the framework logic below.

Driven Framework Design Process

1. Determine the major device number

2. Define the structure type file_operations

3. Implement the corresponding drv_open/drv_read/drv_write and other functions and fill in the file_operations structure

4. Implement driver entry: When installing the driver, this entry function will be called to perform the work:

①Tell the kernel the file_operations structure: register the driver register_chrdev.

②Create class class_create.

③Create device device_create.

5. Implement export: This export function will be called only when the driver is uninstalled to perform the work:

①Unregister the file_operations structure from the kernel: unregister_chrdev.

②Destroy class class_create.

③Destroy the device node device_destroy.

6. Other establishments: GPL contract, entrance loading

1. Determine the main device and variable definition

static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno;//设备号 static int major = 231; //主设备号 static int minor = 0; //次设备号 static char *module_name = "pin4"; //模块名
Copy after login

2. Define the file_operations structure and load it into the kernel driver array

This is the file_operations structure in the Linux kernel

驱动开发是干什么的_linux 驱动 开发_驱动开发和嵌入式开发的差别

Define structure members according to the lower layer calling function

static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open= pin4_open, .write = pin4_write, .read= pin4_read, };
Copy after login

3. Implement functions such as structure member pin4_read

static int pin4_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { printk("pin4_readn"); return 0; } //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_openn");//内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_writen"); return 0; }
Copy after login

4. Driver entrance

int __init pin4_drv_init(void) //真实驱动入口 { int ret; devno = MKDEV(major, minor);//创建设备号 ret = register_chrdev(major, module_name, &pin4_fops);//注册驱动告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE, "myfirstdemo");//由代码在dev自动生成设备 pin4_class_dev =device_create(pin4_class, NULL, devno, NULL, module_name);//创建设备文件 return 0; }
Copy after login

其中pin4_class=class_create(THIS_MODULE,"myfirstdemo");//由代码在dev手动生成设备,除此之外还可以自动生成设备,在dev目录下sudomknod+设备名子+设备类型(c表示字符设备驱动)+主设备号+次设备号。

5、出口

void __exit pin4_drv_exit(void) { device_destroy(pin4_class, devno); class_destroy(pin4_class); unregister_chrdev(major, module_name);//卸载驱动 }
Copy after login

6、GPI合同,入口加载,出口加载

驱动开发和嵌入式开发的差别_linux 驱动 开发_驱动开发是干什么的

module_init(pin4_drv_init);//入口,内核加载该驱动(insmod)的时候,这个宏被使用 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
Copy after login

驱动模块代码编译和测试

编译阶段

驱动模块代码编译(模块的编译须要配置过的内核源码,编译、连接后生成的内核模块后缀为.ko,编译过程首先会到内核源码目录下,读取顶楼的Makefile文件,之后再返回模块源码所在目录。)

#include //file_operations声明 #include //module_initmodule_exit声明 #include //__init__exit 宏定义声明 #include //classdevise声明 #include//copy_from_user 的头文件 #include//设备号dev_t 类型声明 #include //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno;//设备号 static int major = 231;//主设备号 static int minor = 0;//次设备号 static char *module_name = "pin4"; //模块名 static int pin4_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { printk("pin4_readn"); return 0; } //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_openn");//内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_writen"); return 0; } static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open= pin4_open, .write = pin4_write, .read= pin4_read, }; int __init pin4_drv_init(void) //真实驱动入口 { int ret; devno = MKDEV(major, minor);//创建设备号 ret = register_chrdev(major, module_name, &pin4_fops);//注册驱动告诉内 核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE, "myfirstdemo");//用代码 在dev自动生成设备 return 0; } void __exit pin4_drv_exit(void) { device_destroy(pin4_class, devno); class_destroy(pin4_class); unregister_chrdev(major, module_name);//卸载驱动 } module_init(pin4_drv_init);//入口,内核加载该驱动(insmod)的时候,这个宏被使>用 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
Copy after login

将该驱动代码拷贝到linux-rpi-4.14.y/drivers/char目录下文件中(也可选择设备目录下其它文件)

驱动开发是干什么的_linux 驱动 开发_驱动开发和嵌入式开发的差别

更改该文件夹下Makefile(驱动代码放在那个目录,就更改该目录下的Makefile),将前面的代码编译生成模块,文件内容如右图所示:(-y表示编译进内核,-m表示生成驱动模块,CONFIG_表示是按照config生成的),所以只须要将obj-m+=pin4drive.o添加到Makefile中即可。

驱动开发和嵌入式开发的差别_驱动开发是干什么的_linux 驱动 开发

回到linux-rpi-4.14.y/编译驱动文件

使用指令:ARCH=armCROSS_COMPILE=arm-linux-gnueabihf-KERNEL=kernel7makemodules进行编译生成驱动模块。

linux 驱动 开发_驱动开发和嵌入式开发的差别_驱动开发是干什么的

编译生成驱动模块会生成以下几个文件:

驱动开发是干什么的_linux 驱动 开发_驱动开发和嵌入式开发的差别

.o的文件是object文件,.ko是kernelobject,与.o的区别在于其多了一些sections,例如.modinfo。.modinfosection是由kernelsource里的modpost工具生成的,包括MODULE_AUTHOR,MODULE_DESCRIPTION,MODULE_LICENSE,deviceIDtable以及模块依赖关系等等。depmod工具按照.modinfosection生成modules.dep,modules.*map等文件,便于modprobe更便捷的加载模块。

编译过程中,经历了这样的步骤:先步入Linux内核所在的目录,并编译出pin4drive.o文件,运行MODPOST会生成临时的pin4drive.mod.c文件linux 驱动 开发,而后依据此文件编译出pin4drive.mod.o,然后联接pin4drive.o和pin4drive.mod.o文件得到模块目标文件pin4drive.ko,最后离开Linux内核所在的目录。

将生成的.ko文件发送给猕猴桃派:[email protected]:/home/pi

驱动开发是干什么的_驱动开发和嵌入式开发的差别_linux 驱动 开发

将pin4test.c(下层调用代码)进行交叉编译后发送给猕猴桃派,就可以看见pi目录下存在发送过来的.ko文件和pin4test这两个文件,

linux 驱动 开发_驱动开发和嵌入式开发的差别_驱动开发是干什么的

linux 驱动 开发_驱动开发是干什么的_驱动开发和嵌入式开发的差别

加载内核驱动

sudo insmod pin4drive.ko
Copy after login

加载内核驱动(相当于通过insmod调用了module_init这个宏,之后将整个结构体加载到驱动数组中)加载完成后就可以在dev下边见到名子为pin4的设备驱动(这个和驱动代码上面staticchar*module_name="pin4";//模块名这行代码有关),设备号也和代码上面相关。

驱动开发是干什么的_linux 驱动 开发_驱动开发和嵌入式开发的差别

lsmod查看系统的驱动模块,执行下层代码,赋于权限

驱动开发是干什么的_驱动开发和嵌入式开发的差别_linux 驱动 开发

查看内核复印的信息,

dmesg |grep pin4
Copy after login

如右图所示:表示驱动调用成功

在装完驱动后可以使用指令:sudormmod+驱动名(不须要写ko)将驱动卸载。

调用流程:

我们下层空间的open去查找dev下的驱动(文件名),文件名背后包含了驱动的主设备号和次设备号,此时用户open触发一个系统调用linux是什么系统,系统调用经过vfs(虚拟文件系统),vfs按照文件名背后的设备号去调用sys_open去判定,找到内核中驱动数组的驱动位置,再去调用驱动上面自己的dev_open函数

为何生成驱动模块须要在虚拟机上生成?猕猴桃派不行吗?

生成驱动模块须要编译环境(linux源码而且编译,须要下载和系统版本相同的Linux内核源代码),也可以在猕猴桃派里面编译,但在猕猴桃派里编译,效率会很低,要特别久。

The above is the detailed content of The order in which underlying hardware device drivers are inserted into the linked list (driver). For more information, please follow other related articles on the PHP Chinese website!

source:itcool.net
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!