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
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:
- 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
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:
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):
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
cd registerMiscDev/
rm *.ko *.o *.order *.symvers
It is troublesome to delete here. Modify the makefile and add clean:
Then test it:
Continue to modify the source code file name:
mv helloworld.c registerMiscDev.c
The modification is as follows:
Then modify the makefile (change the obj-m module name), and the template is ready
The following is based on the registerMiscDev.c file to register miscellaneous devices, and then modify the .c file:
#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>
Then populate the miscellaneous device structure:
(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, // 这个变量记住,自己起的,步骤二使用
}
Step 2: Fill in the file_operations structure
When writing the driver, the file operation structure is filled in the code.
struct file_operations misc_fops {
.owner = THIS_MODULE
}
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;
}
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;
}
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:
The following warning is actually defined before any use of the function:
Modify:
Compilation successful
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.
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
Then check whether miscellaneous equipment nodes have been added:
Then log out:
sudo rmmod registerMiscDev.ko
Following this, the node disappears:
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
Pitfall 2: Compilation error, file operation pointer problem
question
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