[IMX6ULL driver development learning] 01. Write the first hello driver + automatically create device nodes (no hardware operations involved)

Table of contents

1. Driver writing process

2. Code writing

2.1 Driver hello_drv.c

2.2 Test procedure

2.3 Write the Makefile of the driver

3. Computer experiment

3.1 NFS mount

3.2 Test example


1. Driver writing process

  • Construct the file_operations structure

  • Fill in the open/read/write/ioctl members inside

  • Register file_operations structure int major = register_chrdev(0, "name", &fops);

  • Entry function: call register_chrdev

  • Exit function: call unregiister_chrdev

  • Auxiliary information: class_create/class_destroy device_create/device_destroy

Summary: When the application calls open/read/write/ioctl, the driver will provide you with the corresponding open/read/write/ioctl. However, in order to facilitate management, these functions of the driver are placed in the file_operations structure. Tell the kernel the structure through the register_chrdev function and register the character device driver. There is an entry function in the driver, which is equivalent to the main function. It is a function called when loading the driver. Register in the entry function and put the structure into the chrdevs array. Unregister it in the exit function, which means removing the structure. , a function called when uninstalling the driver.  

2. Code writing

2.1 Driver hello_drv.c

Refer to the program in driver/char, including header files, writing frameworks, and data transmission:

  • Open, read, write, and release are implemented in the driver. When the APP calls these functions, kernel information is printed.
  • When the APP calls the write function, the incoming data is saved in the driver.   
  • When the APP calls the read function, the data saved in the driver is returned to the APP.   
  • It should be noted that copy_from_user(hello_buf, buf, len) and copy_to_user(buf, hello_buf, len) are used to transfer data between the driver and the application.
  • The two functions class_create and device_create create auxiliary information such as device nodes, primary and secondary device numbers for us, so there is no need to manually create device nodes. mknod /dev/xyz c 245 0

  • class_create(THIS_MODULE, "hello_class"), create class: Create a class for this module, the class name is hello_class

  • device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"), create device information under the class: create the device under hello_class, no parent NULL, primary and secondary device numbers, no private data NULL, format hello according to With this information, the system will create a device node for us. The name of the device node is /dev/hello, which has nothing to do with the above class name.

  • device_destroy(hello_class, MKDEV(major, 0)) destroys the device under the hello_class class (determined by the primary and secondary device numbers)

  • class_destroy(hello_class) destroys the hello_class class

#include "asm/cacheflush.h"
#include <linux/mm.h>
#include <linux/module.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/uaccess.h>

static struct class *hello_class;
static int major;
static unsigned char hello_buf[100];

static int hello_open (struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    unsigned long len = size > 100 ? 100 : size;
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    copy_to_user(buf, hello_buf, len);
    return len;
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    unsigned long len = size > 100 ? 100 : size;
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    copy_from_user(hello_buf, buf, len);
    return len;
}

static int hello_release (struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

/* 1. create 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. register_chrdev */

/* 3. entry function */
static int hello_init(void)
{
    major = register_chrdev(0, "100ask_hello", &hello_drv);
    
	hello_class = class_create(THIS_MODULE, "hello_class");
	if (IS_ERR(hello_class)) {
		printk("failed to allocate class\n");
		return PTR_ERR(hello_class);
	}

    device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello");  /* /dev/hello */
       
    return 0;
}


/* 4. exit function */
static void hello_exit(void)
{    
    
    device_destroy(hello_class, MKDEV(major, 0));
    class_destroy(hello_class);
    
    unregister_chrdev(major, "100ask_hello");
}


module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

2.2 Test procedure

The test program must implement reading and writing functions:

./hello_test /dev/xxx abcdef     // 把字符串“abcdeft”发给驱动程序
./hello_test /dev/xxx            // 把驱动中保存的字符串读回来

The source code of hello_drv_test.c is as follows

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

/* 写: ./hello_test /dev/xxx 100ask
 * 读: ./hello_test /dev/xxx
 */
int main(int argc, char **argv)
{
    int fd;
    int len;
    char buf[100];

    if (argc < 2)
    {
        printf("Usage: \n");
        printf("%s <dev> [string]\n", argv[0]);
        return -1;
    }

    // open
    fd = open(argv[1], O_RDWR);
    if (fd < 0)
    {
        printf("can not open file %s\n", argv[1]);
        return -1;
    }

    if (argc == 3)
    {
        // write
        len = write(fd, argv[2], strlen(argv[2])+1);
        printf("write ret = %d\n", len);
    }
    else
    {
        // read
        len = read(fd, buf, 100);
        buf[99] = '\0';
        printf("read str : %s\n", buf);
    }

    // close
    close(fd);
    return 0;
}

2.3 Write the Makefile of the driver

The driver contains many header files, which come from the kernel. Some of the header files may be different for different ARM boards. Therefore, when compiling the driver, you need to specify the source code path of the kernel used by the board. Which file should be compiled? This also needs to be specified. Just set the obj-m variable. How to compile the .c file into driver .ko? This requires the help of the kernel's top-level Makefile.

The Makefile content of this driver is as follows:

KERN_DIR = /home/me/Linux-4.9.88: Specify the kernel directory

First set up the cross-compilation tool chain, compile the kernel used by your board, then modify the Makefile to specify the kernel source code path, and finally execute the make command to compile the driver and test program. 

3. Computer experiment

3.1 NFS mount

We compile the program in Ubuntu, but we need to test it on the ARM board. So the program needs to be put on the ARM board. After starting the board, you can mount a directory in Ubuntu through NFS and access the programs in the directory.

ifconfig eth0 192.168.5.9                    //静态配置开发板ip地址 
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt //挂载到开发板上的mnt目录下
echo "7 4 1 7" > /proc/sys/kernel/printk     //打开内核打印信息

 

3.2 Test example

First, check whether the file is mounted successfully in the mnt directory of the development board. In the current directory, there are drivers and test files compiled by Ubuntu.

insmod hello_drv.ko   // 安装驱动程序
ls /dev/hello -l      // 驱动程序会生成设备节点
./hello_drv_test      // 查看测试程序的用法

 Show modules loaded into the system

Check the usage of the test program, write the string "abcdef" and read it out. The test results are as follows: 

Guess you like

Origin blog.csdn.net/qq_43460230/article/details/132162277