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
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
Then modify the makefile (change the obj-m module name), and the template is ready.
gedit Makefile
The following is based on the testFileOpts.c file to register miscellaneous devices and modify the .c file:
gedit testFileOpts.c
#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;
}
Step 2: (Key) Assign to the file operation set pointer
Step 3: Compile and load the driver
Try compiling first:
Then load the driver:
sudo insmod tesFileOpts.ko
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;
}
Compilation:
gcc test.c
The default output is a.out, run it below:
It cannot be run because Ubuntu requires administrator rights for the device. To run with administrator rights:
Look at the kernel printout (there is no printout here, check "Enter the Pit 1"):
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
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
Pitfall 2: dmesg lacks close release printing
question
test
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
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