Linux driver development notes (5): Principle and Demo of the file operation set of the driver connecting the user layer and the 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/134561660

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 (4): Introduction to device drivers, familiarity with miscellaneous device drivers and ubuntu development of miscellaneous device Demo
Next article: Stay tuned...


Preface

  After the driver is written, the user layer uses system function calls to operate related drivers to achieve association with the system kernel. This article mainly aims to understand how the driver allows user programming to interact with the kernel.


Miscellaneous device file operations set

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

  Searched for (vi uses "/" directly):
  

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);
        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
                        loff_t, 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

        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, you open the driver and use the system read. If you open the handle of the device driver, the read function will be called. The rest can be deduced by analogy, which is relatively easy to understand.


The meaning of Linux file operation set

Overview

  Everything in Linux is a file, and there are corresponding operations such as opening, closing, reading and writing, and these operations are represented by the handle after opening the file. Then the function will be based on the type of the handle, such as the opened If it is a miscellaneous device driver, it will call the corresponding function in the character set of the miscellaneous device operation file to perform the operation.
  When programming, you will use open to open a device node (it can be a file or a device). At this time, the device node handle identifier fd is returned (failure is -1), and then fd is used. Removing various operations such as read and write will be equivalent to calling read and write of the file operation set in this device driver.
  The following are commonly used file operations.

open function (implementation test)

int (*open) (struct inode *, struct file *);

read function (implementation test)

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *)

write function (implementation test)

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

poll/select function (not written in this article)

__poll_t (*poll) (struct file *, struct poll_table_struct *);

ioctl function (not written in this article)

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

close function (implementation test)

int (*release) (struct inode *, struct file *);


Driver template preparation

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

cd ~/work/drive
cp -arf registerMiscDev testFileOpts
cd testFileOpts
make clean
mv registerMiscDev testFileOpts.c

  Insert image description here

   Then modify the makefile (change the obj-m module name), and the template is ready.

gedit Makefile  

  Insert image description here

  The following is based on the testFileOpts.c file to register miscellaneous devices and modify the .c file:

gedit testFileOpts.c

  Insert image description here

#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_testFileOpt", // 设备节点名称
    .fops = &misc_fops,  // 这个变量记住,自己起的,步骤二使用
};

static int registerMiscDev_init(void)
{
    
     
    int ret;
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    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);

Miscellaneous equipment adds common operation set open function Demo

  Note that if the called function is not written, no error will be reported and no other operations will occur, so not all functions must be written.

Step 1: Implement the open function

// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    
    
    printk("int misc_open(struct inode * pInode, struct file * pFile)");
    return 0;
}

  Insert image description here

Step 2: (Key) Assign to the file operation set pointer

  Insert image description here

Step 3: Compile and load the driver

  Try compiling first:
  Insert image description here

  Then load the driver:

sudo insmod tesFileOpts.ko

  Insert image description here

  Insert image description here

  Insert image description here

  At this time, the device node registration is successful.

Step 4: Call open device node open in the program

  This step is C language programming. Use the Linux system function to open the device node:
  Create a new file:

vi test.c

  Enter the code:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    
    
  int fd;
  const char devPath[] = "/dev/register_hongPangZi_testFileOpt";
  fd = open(devPath, O_RDWR);
  if(fd < 0)
  {
    
    
    printf("fialed to open %s\n", devPath);
    return -1;
  } else{
    
    
    printf("Succeed to open %s\n", devPath);
  }
  return 0;
}

  Insert image description here

  Compilation:

gcc test.c

  Insert image description here

  The default output is a.out, run it below:
  Insert image description here

  It cannot be run because Ubuntu requires administrator rights for the device. To run with administrator rights:
  Insert image description here

  Look at the kernel printout (there is no printout here, check "Enter the Pit 1"):
  Insert image description here

  At this point, it is basically clear from the user programming layer how to access device nodes and then call kernel layer functions.


Supplement other function Demo

Supplement read and write

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

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

// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    
    
    printk("int misc_open(struct inode * pInode, struct file * pFile\n)");
    return 0;
}

// int (*release) (struct inode *, struct file *);
int misc_release(struct inode * pInde, struct file * pFile)
{
    
    
    printk("int misc_release(struct inode * pInde, struct file * pFile\n)");
    return 0;
}

// ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)
{
    
    
    printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)
{
    
    
    printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

struct file_operations misc_fops = {
    
    
  .owner = THIS_MODULE,
  .open = misc_open,
  .release = misc_release,
  .read = misc_read,
  .write = misc_write,
};

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

static int registerMiscDev_init(void)
{
    
     
    int ret;
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    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);

Modify test.c test driver source code

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
  int fd;
  char buf[32] = {0};

  const char devPath[] = "/dev/register_hongPangZi_testFileOpt";
  fd = open(devPath, O_RDWR);
  if(fd < 0)
  {
    printf("Failed to open %s\n", devPath);
    return -1;
  }else{
    printf("Succeed to open %s\n", devPath);
  }

  read(fd, buf, sizeof(buf));
  write(fd, buf, sizeof(buf));

  close(fd);
  printf("exit\n");
  fd = -1;
  return 0;
}

View output

  Insert image description here


Fall into the trap

Pitfall 1: The kernel does not print the open function

question

  The program opens a separate node and the open function is not printed.

reason

  The open function is not assigned to the file operation set.

solve

  Insert image description here

Pitfall 2: dmesg lacks close release printing

question

  Insert image description here

test

  Insert image description here

  Insert image description here

  I studied dmesg, but it didn’t come out. This was not clear. Later, I asked the driver boss and he reminded me that it might be a line break problem. I added it later and it worked.

Solution

  Insert image description here
  Insert image description here


Previous article: "Linux driver development notes (4): Introduction to device drivers, familiarity with miscellaneous device drivers and ubuntu development of miscellaneous device Demo
Next article: Stay tuned...


The blog address of this article:https://hpzwl.blog.csdn.net/article/details/134561660

Guess you like

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