Linux driver development notes (4): Introduction to device drivers, familiarity with miscellaneous device drivers and ubuntu development of miscellaneous device demos

If the article is an original article, please indicate the source of the original article when reprinting it
The blog address of this article:https://hpzwl.blog.csdn .net/article/details/134533533

The collection of red fat man’s network technology blog posts: a collection of development technologies (including Qt practical technology, Raspberry Pi, 3D, OpenCV, OpenGL, ffmpeg, OSG, microcontroller, software and hardware combination, etc.) is being continuously updated...

Linux system transplantation and driver development column

Previous article: "Linux driver development notes (3): helloworld driver source code writing, makefile writing and driver compilation and loading process testing based on ubuntu
Next article: "Linux driver development notes (5): Principle and Demo of file operation set for driver to connect user layer and kernel layer


Preface

  Driver development requires familiarity with basic concept types first. This article explains the basics of Linux miscellaneous devices. It is still based on the virtual machine Ubuntu to create drivers. You only need a virtual machine to try to write the basic process of registering miscellaneous devices.


Linux three major device drivers

  • Character device: The IO transmission process is in character units, without buffering, such as I2C (SDA, SCL), SPI (MISO, MOSI, SCLK, CS).
  • Block device: The IO transmission process is based on blocks, and all storage-related devices are block devices, such as TF cards and SD cards.
  • Network device: IO transmission is accessed through sockets.

Miscellaneous equipment

  • Miscellaneous devices are character devices and can automatically generate device nodes. The device nodes are located in the /dev/ directory and are device names, such as /dev/ttyS9 etc.
  • The major device numbers are the same and unified to 10. The minor device numbers are different and the major devices are the same to save kernel resources.
    You can view miscellaneous system equipment through the following commands
cat /proc/misc

  Test on a virtual machine and view miscellaneous items:
  Insert image description here

  • The device number is divided into a major device number and a minor device number. The major device number is unique, but the minor device number is not necessarily unique.
    You can view the system major device number through the following command:
cat /proc/devices

  Insert image description here

Miscellaneous device description structures

  For Ubuntu, the included /usr/src is the kernel header file.

cd /usr/src/linux-headers-4.18.0-15
vi include/linux/miscdevice.h

  Locate to the kernel header file that came with ubuntu before:
  Insert image description here
  Insert image description here

  View the structure of miscellaneous equipment:

struct miscdevice  {
    
    
        int minor;  // 次设备号
        const char *name;  // 设备节点名称(如/dev/ttyS8,则ttyS是名称)
        const struct file_operations *fops; // 文件操作集(非常重要)
        struct list_head list; 
        struct device *parent;
        struct device *this_device;
        const struct attribute_group **groups; 
        const char *nodename; 
        umode_t mode;
};

  (Note: If there is no annotation, generally ignore it)

Miscellaneous device file operations set

cd /usr/src/linux-headers-4.18.0-15
vi include/linux/fs.h

  Searched for (vi uses "/" directly):
  Insert image description here

struct file_operations {
    
    
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
        ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
        int (*iterate_shared) (struct file *, struct dir_context *);
        __poll_t (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        unsigned long mmap_supported_flags;
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, loff_t, loff_t, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*setfl)(struct file *, unsigned long);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
        int (*setlease)(struct file *, long, struct file_lock **, void **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                          loff_t len);
        void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
        unsigned (*mmap_capabilities)(struct file *);
#endif
        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
                        loff_t, size_t, unsigned int);
        int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
                        u64);
        ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
                        u64);
} __randomize_layout;

  For example, with the read function, the driver is opened and the system read is used. When the handle of the device driver is opened, the read function will be called. The rest can be deduced by analogy, which is easier to understand.
  Take our registerHelloWorld as an example to briefly explain.


Driver writing empty template preparation

  First copy the previous hello world driver and change its name to: registerMiscDev:

cd ~/work/drive
cp -arf hellowolrd registerMiscDev

  Insert image description here

cd registerMiscDev/
rm *.ko *.o *.order *.symvers

  It is troublesome to delete here. Modify the makefile and add clean:
  Insert image description here

  Then test it:
  Insert image description here

  Continue to modify the source code file name:

mv helloworld.c registerMiscDev.c

  The modification is as follows:
  Insert image description here

  Then modify the makefile (change the obj-m module name), and the template is ready
  Insert image description here

  The following is based on the registerMiscDev.c file to register miscellaneous devices, and then modify the .c file:
  Insert image description here

#include <linux/init.h>
#include <linux/module.h>

static int registerMiscDev_init(void)
{
    
     
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    return 0;
}
static void registerMiscDev_exit(void)
{
    
    
    printk("bye-bye!!!\n");
}
MODULE_LICENSE("GPL");
module_init(registerMiscDev_init);

module_exit(registerMiscDev_exit); 

Miscellaneous device registration process demo

Step 1: Fill in the miscdevice structure

  When writing the driver, the information structure is filled in the code.
  Add the header file miscdevice.h

#include <linux/miscdevice.h>
#include <linux/fs.h>

  Insert image description here

  Then populate the miscellaneous device structure:
  Insert image description here

  (Note: The beginning is "." and the end is ",". It is customary to add "," to the last line, so that everything can be copied uniformly, and what is saved is added)

struct miscdevice misc_dev {
    
    
    .minor = MISC_DYNAMIC_MINRO, // 这个宏是动态分配次设备号,避免冲突
    .name = "register_hongPangZi_misc,  // 设备节点名称
    .fops = misc_fops, // 这个变量记住,自己起的,步骤二使用
}

  Insert image description here

Step 2: Fill in the file_operations structure

  When writing the driver, the file operation structure is filled in the code.
  Insert image description here

struct file_operations misc_fops {
    
    
  .owner = THIS_MODULE
}

  Insert image description here

Step 3: Register miscellaneous devices and generate device nodes

  Register to the kernel:

static int registerMiscDev_init(void)
{
    
     
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	

    int ret = 0;
    ret = misc_register(misc_dev);
    if(ret < 0)
    {
    
    
        printk("Failed to misc_register(misc_dev)\n");	
        return -1;
    } 
    return 0;
}

  Insert image description here

  Once registered, there will be deregistration:

static int registerMiscDev_init(void)
{
    
     
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	

    int ret = 0;
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
    
    
        printk("Failed to misc_register(misc_dev)\n");	
        return -1;
    } 
    return 0;
}

  Insert image description here

  Complete file source code:

#include <linux/init.h>
#include <linux/module.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>

struct file_operations misc_fops = {
    
    
  .owner = THIS_MODULE,
};

struct miscdevice misc_dev = {
    
    
    .minor = MISC_DYNAMIC_MINOR, // 这个宏是动态分配次设备号,避免冲突
    .name = "register_hongPangZi_misc", // 设备节点名称
    .fops = &misc_fops,  // 这个变量记住,自己起的,步骤二使用
};

static int registerMiscDev_init(void)
{
    
     
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	

    int ret = 0;
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
    
    
        printk("Failed to misc_register(&misc_dev)\n");	
        return -1;
    } 
    return 0;
}

static void registerMiscDev_exit(void)
{
    
    
    misc_deregister(&misc_dev);
    printk("bye-bye!!!\n");
}

MODULE_LICENSE("GPL");
module_init(registerMiscDev_init);
module_exit(registerMiscDev_exit);

Step 4: Compile make

make

  Compile directly in the driver project directory:
  Insert image description here

  The following warning is actually defined before any use of the function:
  Insert image description here

  Modify:
  Insert image description here

  Insert image description here

  Compilation successful
  Insert image description here

Step 5: Load and unload driver test

  Copy the driver to the development board or target system, and then use the load command:

sudo insmod registerMiscDev.ko

  Will print the printk output loaded by the entry.
  Insert image description here

  Possible reasons for the problem are that the compiler used for kernel compilation and the compiler version used by the module are inconsistent. The printk terminal in Ubuntu enters the kernel log message. You can use dmesg to view it:

dmesg

  Insert image description here

  Then check whether miscellaneous equipment nodes have been added:
  Insert image description here

  Then log out:

sudo rmmod registerMiscDev.ko

  Insert image description here

  Following this, the node disappears:
  Insert image description here


Fall into the trap

Pitfall 1: Compilation error, no semicolon after the structure

question

  Compilation error, semicolon is added after the structure

solve

  Add a semicolon, my head is a little confused
  Insert image description here

Pitfall 2: Compilation error, file operation pointer problem

question

  Insert image description here

solve

  This is a typo, it is a pointer, and the address needs to be added &.


Previous article: "Linux driver development notes (3): helloworld driver source code writing, makefile writing and driver compilation and loading process testing based on ubuntu
Next article: "Linux driver development notes (5): Principle and Demo of file operation set for driver to connect user layer and kernel layer


If the article is an original article, please indicate the source of the original article when reprinting it
The blog address of this article:https://hpzwl.blog.csdn .net/article/details/134533533

Guess you like

Origin blog.csdn.net/qq21497936/article/details/134533533