Wei Dongshan Linux driver entry experiment class (1) hello driver

foreword

(1) Learn Linux from Mr. Wei Dongshan, because his speech is so concise that many people can't understand it. Next, I will introduce the first Hello program of Wei Dongshan's driver experiment class.

(2) Attention, please finish the video before watching this tutorial! This article is for beginners only! For more depth, search other blogs!

(3) gitee warehouse ; GitHub warehouse ;

the code

First upload the code, and add comments to the code. If you can understand it just by looking at the comments, there is no need to spend time looking down. The specific code is in my warehouse.

driver code

/* 说明 : 
 	*1,本代码是学习韦东山老师的驱动入门视频所写,增加了注释。
 	*2,采用的是UTF-8编码格式,如果注释是乱码,需要改一下。
 	*3,这是驱动层代码
 * 作者 : CSDN风正豪
*/



#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/module.h>
#include <linux/uaccess.h>

static int major; //主设备号,用于最后的驱动卸载

/*
 *传入参数 :
	 *node :
	 *filp :
 *返回参数 : 如果成功返回0
*/
static int hello_open (struct inode *node, struct file *filp)
{
	/*__FILE__ :表示文件
	 *__FUNCTION__ :当前函数名
	 *__LINE__ :在文件的哪一行
	*/
    printk("%s %s %d\n",__FILE__,__FUNCTION__, __LINE__);
    return 0;
}

/*
 *传入参数 :
	 *filp :要读的文件
	 *buf :读的数据放在哪里
	 *size :读多大数据
	 *offset :偏移值(一般不用)
 *返回参数 :读到的数据长度	
*/
static	ssize_t hello_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
	/*__FILE__ :表示文件
	 *__FUNCTION__ :当前函数名
	 *__LINE__ :在文件的哪一行
	*/
    printk("%s %s %d\n",__FILE__,__FUNCTION__, __LINE__);
    return size;
}

/*
 *传入参数 :
	 *filp :要写的文件
	 *buf :写入的数据来自于buf
	 *size :写多大数据
	 *offset :偏移值(一般不用)
 *返回参数 :写的数据长度	
*/
static	ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
	/*__FILE__ :表示文件
	 *__FUNCTION__ :当前函数名
	 *__LINE__ :在文件的哪一行
	*/
    printk("%s %s %d\n",__FILE__,__FUNCTION__, __LINE__);
    return size;
}

/*作用 : 应用程序关闭的时候调用这个函数
 *传入参数 :
	 *node :
	 *filp :要关闭的文件
 *返回参数 :成功返回0	
*/
static	int hello_release (struct inode *node, struct file *filp)
{
	/*__FILE__ :表示文件
	 *__FUNCTION__ :当前函数名
	 *__LINE__ :在文件的哪一行
	*/
    printk("%s %s %d\n",__FILE__,__FUNCTION__, __LINE__);
    return 0;
}

//1,构造 file_operations 
static const struct file_operations  hello_drv = {
    .owner      = THIS_MODULE,
	.read		= hello_read,
	.write		= hello_write,
	.open		= hello_open,
    .release    = hello_release
};

//2,注册驱动(注意,我们在入口函数中注册)

//在命令行输入insmod命令,就是注册驱动程序。之后就会进入这个入口函数
//3,入口函数
static int  hello_init(void)
{
	/*将hello_drv这个驱动放在内核的第n项,中间传入的名字不重要,第三个是要告诉内核的驱动
	 *因为我们不知道第n项是否已经存放了其他驱动,就可以放在第0项,然后让系统自动往后遍历存放到空的地方
	 *major为最终存放的第n项,等下卸载程序需要使用。如果不卸载程序,可以不管这个
	*/
    major = register_chrdev(0,"100ask_hello",&hello_drv);
	//如果成功注册驱动,打印
	printk("insmod success!\n");
    return 0;
}

//在命令行输入rmmod命令,就是注册驱动程序。之后就会进入这个出口函数
//4,出口函数
static void  hello_exit(void)
{
	//卸载驱动程序
	//第一个参数是主设备号,第二个是名字
    unregister_chrdev(major,"100ask_hello");
	//如果成功卸载驱动,打印
	printk("rmmod success!\n");
}


module_init(hello_init); //确认入口函数
module_exit(hello_exit); //确认出口函数

/*最后我们需要在驱动中加入 LICENSE 信息和作者信息,其中 LICENSE 是必须添加的,否则的话编译的时候会报错,作者信息可以添加也可以不添加
 *这个协议要求我们代码必须免费开源,Linux遵循GPL协议,他的源代码可以开放使用,那么你写的内核驱动程序也要遵循GPL协议才能使用内核函数
 *因为指定了这个协议,你的代码也需要开放给别人免费使用,同时可以根据这个协议要求很多厂商提供源代码
 *但是很多厂商为了规避这个协议,驱动源代码很简单,复杂的东西放在应用层
*/
MODULE_LICENSE("GPL"); //指定模块为GPL协议
MODULE_AUTHOR("CSDN:qq_63922192");  //表明作者,可以不写



application layer code

/* 说明 : 
 	*1,本代码是学习韦东山老师的驱动入门视频所写,增加了注释。
 	*2,采用的是UTF-8编码格式,如果注释是乱码,需要改一下。
 	*3,这是应用层代码
 * 作者 : CSDN风正豪
*/



#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/* 作用:输入三个参数表示向/dev/xxx文件中写入数据,输入非三个数据,表示向/dev/xxx文件中读取数据。
 * 写 : ./hello_test /dev/xxx 100ask
 * 读 : ./hello_test /dev/xxx
 */
int main(int argc,char** argv)
{
    int fd,len;    //fd:存放文件描述符;len:存放写入字符个数
    char buf[100]; //存放文件中的字符
	//如果输入参数小于2个,打印本程序使用方法
    if(argc < 2)
    {
        printf("Usage:\n");
        printf("%s <dev> [string]\n",argv[0]);
        return -1;
    }

    //打开/dev/xxx文件(设备节点),返回一个文件描述符,我们之后可以直接根据这个文件描述符来操作设备节点,间接操作驱动
    fd = open(argv[1],O_RDWR);
	//如果打开失败
    if(fd < 0)
    {
        printf("can not open file %s\n",argv[1]);
        return -1;
    }

    //如果输入参数为3个,表示写入数据
    if(argc == 3)
    {
		//因为strlen计算字符串长度不会包括'\0',所以需要+1
        len = write(fd,argv[2],strlen(argv[2])+1);
		//打印出写入字符个数
        printf("write ret = %d\n",len);
    }
    //否则为读取数据
    else
    {
		//读入100个字符
        len =read(fd,buf,100);
		//无论传入多少个数据,最多都只会读100个字符
        buf[99] = '\0';
		//打印读取到的字符
        printf("read str : %s\n",buf);
    }

    //关闭文件
    close(fd);
    return 0;
}

Pre-knowledge

(1) Because I have just learned it, there may be deviations in what I said. Please correct me in time, thank you.

(2) First of all, we usually open an executable file on the Linux terminal, and then the executable file will execute the program. So what does this executable do?

(3) The executable file first reads the program at the application layer, and there will be many library functions in it, and the library functions belong to the kernel. And the kernel will call down the driver layer program. The final driver layer controls the specific hardware.

<1> In fact, it is relatively easy to understand from the application program to the library. For example, when we first learned C language, we used printf, scanf and other functions. And those functions are in the library.

<2> The library can be connected to the system kernel, but I don't know exactly how to achieve it.

<3> We wrote a driver, we need to tell the kernel, this process is called registration. After we register the driver, there will be information about the driver in the kernel, and then the upper layer application can call it.

(4) Does it sound ignorant? I am also ignorant. But don't be afraid, we just need to know that we need to write two programs, one for the driver layer and one for the application layer. Finally, the driver layer needs to be registered into the kernel before the application layer can use it . Leave everything else alone.

(5) We call the read function in the application layer, which corresponds to the read function in the driver layer. The write function corresponds to the write function. The open function corresponds to the open function. The close function corresponds to the release function (I don't know why this is different).

(6) After we have a brief understanding of the process of calling the driver from the Linux application, I need to know how to do the entire program writing process. As for why the process is like this, we can just remember. Because these are stipulated by people, it will not be too late to study further after learning deeply. Now we are mainly getting started.

 

Driver code explanation

Process introduction

(1) Next, I will introduce a driver writing process. After reading the above, everyone is still confused. It doesn't matter, let's explain one by one according to the code, and everyone should have a simple understanding .

(2) Process: (Attention, you may see that the process described by some people may seem to be different from mine. You have to compare carefully, and you will find that they are actually the same, just different ways of speaking)

<1> We first need to write a structure of type file_operations, which is used to manage the driver. After we register the driver into the kernel, we call this driver at the application layer, then we can directly operate the open, write, read and other functions in the driver through this structure.

<2> Implement the corresponding functions such as drv_open/drv_read/drv_write, and fill in the file_operations structure. In this way, when we call open, write, read and other functions at the application layer, we call this driver.

At this time, someone may ask, there are so many drivers, how do I know which driver the open corresponds to? It's very simple. When we write the application layer program, do we need to pass in a device number as the first parameter? The system judges which driver is called according to the device number.

<3> Tell the file_operations structure to the kernel: register_chrdev. We wrote a driver, but the kernel doesn't know about it. So what to do? We will register him, and the kernel will understand that with this driver, he will be assigned a device number. Then the application layer can call the driver layer according to the device number.

<4> At this time, some people have doubts, who will register this structure? So we need an entry function to register, and this entry function will be called when the driver is installed.

<5> If there is an entry function, there should be an exit function: When unloading the driver, the exit function calls unregister_chrdev.

<6> Finally, you need to join the GPL agreement. Because Linux complies with the GPL agreement, if you need to use other Linux driver layer functions, you must comply with the GPL agreement and require open source code . According to this agreement, you can ask all manufacturers using Linux to provide the source code of the driver layer, and others can also ask you to disclose your driver layer code, which is mutual. However, in order to avoid this agreement, many manufacturers make the driver source code very simple, and put complicated things in the application layer. As for the addition of an author's name, write it or not.

Code by code analysis

(1) In the first step, we need to create a file_operations structure. The static const struct file_operations part of this structure must be written and stipulated in this way. Finally, the name of this structure may not be hello_drv, you can choose it at will, but register_chrdev will use this structure name to register later .

static const struct file_operations  hello_drv = {

};

(2)

<1> After creating the file_operations structure, we need to write parameters in this structure according to the requirements. For a file, open, write, read, and release are definitely needed. Then write data in these parameters, this data is a function pointer. (For novice friends, you may not know whatthe structure " .member " means, please see the assignment part of the detailed explanation of the C language structure ) As for .owner = THIS_MODULE, this is required to exist, and I am not very clear about why.

<2> When writing the driver, we need to pay attention that the printing function is printk, not printf . why? Because the kernel has no way to use the C language library .

<3> It should be noted that we need to enter the kernel printing information: echo "7 4 1 7" > /proc/sys/kernel/printk to open the kernel printing . Although most kernel printing is turned on by default, you still need to pay attention.

If you don't want to see the kernel print information, just enter: echo "7 4 1 7" > /proc/sys/kernel/printk . (It doesn't matter if you don't understand, it will be explained later in the demonstration)

/*
 *传入参数 :
	 *node :
	 *filp :
 *返回参数 : 如果成功返回0
*/
static int hello_open (struct inode *node, struct file *filp)
{
	/*__FILE__ :表示文件
	 *__FUNCTION__ :当前函数名
	 *__LINE__ :在文件的哪一行
	*/
    printk("%s %s %d\n",__FILE__,__FUNCTION__, __LINE__);
    return 0;
}

/*
 *传入参数 :
	 *filp :要读的文件
	 *buf :读的数据放在哪里
	 *size :读多大数据
	 *offset :偏移值(一般不用)
 *返回参数 :读到的数据长度	
*/
static	ssize_t hello_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
	/*__FILE__ :表示文件
	 *__FUNCTION__ :当前函数名
	 *__LINE__ :在文件的哪一行
	*/
    printk("%s %s %d\n",__FILE__,__FUNCTION__, __LINE__);
    return size;
}

/*
 *传入参数 :
	 *filp :要写的文件
	 *buf :写入的数据来自于buf
	 *size :写多大数据
	 *offset :偏移值(一般不用)
 *返回参数 :写的数据长度	
*/
static	ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
	/*__FILE__ :表示文件
	 *__FUNCTION__ :当前函数名
	 *__LINE__ :在文件的哪一行
	*/
    printk("%s %s %d\n",__FILE__,__FUNCTION__, __LINE__);
    return size;
}

/*作用 : 应用程序关闭的时候调用这个函数
 *传入参数 :
	 *node :
	 *filp :要关闭的文件
 *返回参数 :成功返回0	
*/
static	int hello_release (struct inode *node, struct file *filp)
{
	/*__FILE__ :表示文件
	 *__FUNCTION__ :当前函数名
	 *__LINE__ :在文件的哪一行
	*/
    printk("%s %s %d\n",__FILE__,__FUNCTION__, __LINE__);
    return 0;
}

static const struct file_operations  hello_drv = {
    .owner      = THIS_MODULE,
	.read		= hello_read,
	.write		= hello_write,
	.open		= hello_open,
    .release    = hello_release
};

(3)

<1> At this time, we need to tell the kernel the file_operations structure. Call the register_chrdev() function to let the kernel know the file_operations structure.

<2> If you have watched Wei Dongshan's video, you will know that according to his first step, you need to determine the master device number. However, if we enter 0 in the first parameter of register_chrdev(), the system will automatically determine the major device number for us and return the major device number.

<3> The second parameter of register_chrdev(), we can name it whatever we want. I just followed what Teacher Wei Dongshan said, and I was too lazy to change it.

<4> The third parameter of register_chrdev(), this is related to the file_operations structure we defined earlier. Because what we defined above is static const struct file_operations hello_drv; the structure name is hello_drv, so the third parameter is input hello_drv.

<5> We need to receive the returned main device number, because it needs to be used when uninstalling the program later. But if you don't plan to uninstall, it's okay not to receive this major device number .

(4)

<1> After telling the kernel about the file_operations structure, we still need to register the driver, which requires an entry function. This entry function needs the macro module_init (xxx) to tell the kernel and confirm the entry function . xxx is the name of our entry function.

<2> So the question is, when will we know that the driver is registered? Very simple, when we enter the insmod hello_drv.ko program in the Linux terminal, the system will call the hello_init() function.

//在命令行输入insmod命令,就是注册驱动程序。之后就会进入这个入口函数
//3,入口函数
static int  hello_init(void)
{
	/*将hello_drv这个驱动放在内核的第n项,中间传入的名字不重要,第三个是要告诉内核的驱动
	 *因为我们不知道第n项是否已经存放了其他驱动,就可以放在第0项,然后让系统自动往后遍历存放到空的地方
	 *major为最终存放的第n项,等下卸载程序需要使用。如果不卸载程序,可以不管这个
	*/
    major = register_chrdev(0,"100ask_hello",&hello_drv);
	//如果成功注册驱动,打印
	printk("insmod success!\n");
    return 0;
}

module_init(hello_init); //确认入口函数

(5) We have an entry program, and there must also be an exit program. At this time, module_exit (XXX) is needed to confirm the exit function.

//在命令行输入rmmod命令,就是注册驱动程序。之后就会进入这个出口函数
//4,出口函数
static void  hello_exit(void)
{
	//卸载驱动程序
	//第一个参数是主设备号,第二个是名字
    unregister_chrdev(major,"100ask_hello");
	//如果成功卸载驱动,打印
	printk("rmmod success!\n");
}

module_exit(hello_exit); //确认出口函数

(6) Finally, add the GPL agreement and author name. The GPL agreement is mandatory and must be added , and the author's name is random.

/*最后我们需要在驱动中加入 LICENSE 信息和作者信息,其中 LICENSE 是必须添加的,否则的话编译的时候会报错,作者信息可以添加也可以不添加
 *这个协议要求我们代码必须免费开源,Linux遵循GPL协议,他的源代码可以开放使用,那么你写的内核驱动程序也要遵循GPL协议才能使用内核函数
 *因为指定了这个协议,你的代码也需要开放给别人免费使用,同时可以根据这个协议要求很多厂商提供源代码
 *但是很多厂商为了规避这个协议,驱动源代码很简单,复杂的东西放在应用层
*/
MODULE_LICENSE("GPL"); //指定模块为GPL协议
MODULE_AUTHOR("CSDN:qq_63922192");  //表明作者,可以不写

Explanation of command line operation

Command line operation process

(1) Command line input process:

<1> According to the explanation of the driver code above, we know that after the driver code is written, the kernel does not know it and needs to be registered. There are two registration methods, the first is to compile the driver into the Linux kernel, so that the driver will automatically run when the Linux kernel starts. The second is to compile the driver into a module (the extension of the module under Linux is .ko), and use the "insmod" command to load the driver module after the Linux kernel is started . When debugging the driver, we generally choose to compile it into a module, so that after we modify the driver, we only need to compile the driver code, instead of compiling the entire Linux code. And when debugging, you only need to load or unload the driver module, without restarting the entire system. In short, the biggest advantage of compiling the driver as a module is that it is convenient for development . After the driver development is completed and no problems are confirmed, the driver can be compiled into the Linux kernel.
<2> We need to confirm whether the registration is successful, so we need to enter cat /proc/devices: to view the device numbers that are currently used. lsmod to view the drivers already loaded in the kernel .

<3> Create a device node. The successful loading of the driver needs to create a corresponding device node file in the /dev directory, and the application program completes the operation of the specific device by operating the device node file .

<4> After the device node is created, we cancall the driver layer from the application layer according to the format : application /dev/xxx (xxx is a device node with any name).

<5> After using it, we may want to uninstall it, so we can enter: rmmod driver to

(2) Now we know how the application calls the driver through the device node. So now let's talk about requirements. We think that if three parameters are entered on the command line, it means writing data. If two parameters are entered, it means to read data.

Makefile code

KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o hello_test hello_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f hello_test

obj-m	+= hello_drv.o

Application layer code explanation

(1) After we know the requirements, we will operate. However, in order to prevent some people from having a poor foundation, let me explain what the argc and argv of the main function are.

(2) When we enter ./hello_test /dev/xxx 100ask on the command line, these three will be stored in a secondary pointer of argv. This argv[0] stores ./hello_test, argv[1] stores /dev/xxx, and argv[2] stores 100ask . And argc stores several parameters passed in , for example, three parameters are passed in now. But someone will ask, how do I know that there are three parameters? This is very simple, separate parameters with spaces.

(3)

<1> If there are less than 2 incoming parameters, then we will print out the usage method of this application. At the same time end the program.

<2>Use open to open this device node, and then return a file descriptor, and we can operate this device node according to this file descriptor. At the same time, the kernel will call the hello_open() function of the driver layer, because the application layer uses the open function.

<3> If the file is not opened, the open function will return -1, we judge whether fd is -1, if it is -1, then end the program and remind that the device node is not opened.

<4> Now we need to judge that there are three parameters passed in. If there are three parameters, it means writing data, and the kernel calls the hello_write() function, because the application layer uses the write function. Otherwise, it means reading data, and the kernel calls the hello_read() function, because the application layer calls the read function.

<5> Finally, call the close function to close the file. The corresponding kernel calls the hello_release function.

/* 说明 : 
 	*1,本代码是学习韦东山老师的驱动入门视频所写,增加了注释。
 	*2,采用的是UTF-8编码格式,如果注释是乱码,需要改一下。
 	*3,这是应用层代码
 * 作者 : CSDN风正豪
*/



#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/* 作用:输入三个参数表示向/dev/xxx文件中写入数据,输入非三个数据,表示向/dev/xxx文件中读取数据。
 * 写 : ./hello_test /dev/xxx 100ask
 * 读 : ./hello_test /dev/xxx
 */
int main(int argc,char** argv)
{
    int fd,len;    //fd:存放文件描述符;len:存放写入字符个数
    char buf[100]; //存放文件中的字符
	//如果输入参数小于2个,打印本程序使用方法
    if(argc < 2)
    {
        printf("Usage:\n");
        printf("%s <dev> [string]\n",argv[0]);
        return -1;
    }

    //打开/dev/xxx文件(设备节点),返回一个文件描述符,我们之后可以直接根据这个文件描述符来操作设备节点,间接操作驱动
    fd = open(argv[1],O_RDWR);
	//如果打开失败
    if(fd < 0)
    {
        printf("can not open file %s\n",argv[1]);
        return -1;
    }

    //如果输入参数为3个,表示写入数据
    if(argc == 3)
    {
		//因为strlen计算字符串长度不会包括'\0',所以需要+1
        len = write(fd,argv[2],strlen(argv[2])+1);
		//打印出写入字符个数
        printf("write ret = %d\n",len);
    }
    //否则为读取数据
    else
    {
		//读入100个字符
        len =read(fd,buf,100);
		//无论传入多少个数据,都只会读100个字符
        buf[99] = '\0';
		//打印读取到的字符
        printf("read str : %s\n",buf);
    }

    //关闭文件
    close(fd);
    return 0;
}

Effect demonstration

In order to let everyone have a deeper understanding of the driver layer and application layer, I am divided into two demonstration methods here, one is to turn on the effect demonstration of kernel printing, and the other is to turn off the effect demonstration of kernel printing.

Close the kernel print effect demo

(1) make to compile the program, note that this needs to compile the program in Ubuntu !

(2)

<1> On the development board, share files with Ubuntu: mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt.

<2> After that, we close the kernel printing: echo 0 4 0 7 > /proc/sys/kernel/printk

(3)

<1> Confirm the existence of the hello_drv.ko file

<2>insmod hello_drv.ko loading driver

<3>lsmod confirms that the driver has been installed

 (4) We know that the driver has been installed, so we need to know the device number of the driver, enter: cat /proc/devices: to view the device number that is currently used. The driver name is related to the second parameter of the register_chrdev() function we use in the driver layer .

(5) Create a device node: mknod /dev/xyz c 240 0

<1> "/dev/xyz" is the node file to be created, xyz can be any name

<2> "c" indicates that this is a character device

<3> "240" is the main device number of the device. This value is 240 100ask_hello when viewing the driver according to cat /proc/devices, so the device number of the driver 100ask_hello is 240.

<4> "0" is the minor device number of the device, just write 0 by default.

 (6) Command line call

(7)

<1> Finally delete the driver: rmmod driver (our driver is called hello_drv)

<2> Delete the device node: rm /dev/xyz

 

 Open the kernel printing effect demo

(1) Turn on kernel printing: echo "7 4 1 7" > /proc/sys/kernel/printk

 (2)

<1>Because the kernel printing is turned on, so there will be a lot of strange things, don't worry about it. Just press shift+C.

<2> We input the insmod hello_drv.ko loader, which calls the entry function of the driver layer, so here the kernel will print "insmod success!"

//在命令行输入insmod命令,就是注册驱动程序。之后就会进入这个入口函数
//3,入口函数
static int  hello_init(void)
{
	/*将hello_drv这个驱动放在内核的第n项,中间传入的名字不重要,第三个是要告诉内核的驱动
	 *因为我们不知道第n项是否已经存放了其他驱动,就可以放在第0项,然后让系统自动往后遍历存放到空的地方
	 *major为最终存放的第n项,等下卸载程序需要使用。如果不卸载程序,可以不管这个
	*/
    major = register_chrdev(0,"100ask_hello",&hello_drv);
	//如果成功注册驱动,打印
	printk("insmod success!\n");
    return 0;
}

(3)

<1> Confirm the existence of the hello_drv.ko file

<2>insmod hello_drv.ko loading driver

<3>lsmod confirms that the driver has been installed

(4) Create a device node: mknod /dev/xyz c 240 0

 (5)

<1> We only input two parameters, and found that the kernel successfully printed the data, from which we can know that the hello_open, hello_read, hello_release functions were called.

<2> We input three parameters, we know from the kernel print, we call the hello_open, hello_read, hello_release functions. Corresponding to the application layer.

 

 (6)

<1>Delete the driver, and finally we found that the kernel information was successfully printed.

<2> Delete the device node: rm /dev/xyz

Guess you like

Origin blog.csdn.net/qq_63922192/article/details/130001370