1. The Three Major Types of Linux Device Drivers
1. Character Devices: The IO transfer process is done in characters without buffering, such as I2C and SPI, which are character devices.
2. Block Devices: The IO transfer process is done in blocks, related to storage, such as TF cards.
3. Network Devices: Unlike the previous two, these are accessed via socket.
Miscellaneous devices are a type of character device that can automatically generate nodes.
2. Miscellaneous Devices
1. Our system has many miscellaneous devices, which we can view by entering the command cat /proc/misc.

The major device number for miscellaneous devices is the same, which is 10, while the minor device numbers are different. The benefit of having the same major device number is that it saves kernel resources.
2. Major and Minor Device Numbers
-
The device number consists of a major device number and a minor device number. The major device number is unique in the Linux system, while the minor device number is not necessarily unique.
-
The device number is a way for the computer to identify devices; devices with the same device number are considered the same type of device.
-
The major device number can be compared to the area code of a phone number, such as 010 for Beijing, while the minor device number is analogous to the phone number itself.
-
The major device number can be viewed using the command
cat /proc/devices.
// Path: kernel-3.18/include/linux/miscdevice.h
struct miscdevice {
int minor; // Minor device number
const char *name; // Name of the device node
const struct file_operations *fops; // File operation set
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
umode_t mode;
};
3. Description of the File Operation Set file_operations
// Path: kernel-3.18/include/linux/fs.h
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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, 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 *);
unsigned int (*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 *);
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 (*aio_fsync) (struct kiocb *, 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 (*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);
int (*show_fdinfo)(struct seq_file *m, struct file *f);
};
Each member of file_operations corresponds to a call. It looks complex, but we only need to focus on four commonly used members.
read, write, open, release The four members in <code>file_operationscorrespond to the upper-level application functionsread(),write(),open(), andrelease().
4. Registration and Deregistration of Miscellaneous Devices
extern int misc_register(struct miscdevice *misc); // Register miscellaneous device
extern int misc_deregister(struct miscdevice *misc); // Deregister miscellaneous device
Process of Registering a Miscellaneous Device
(1) Fill in the miscdevice structure.
(2) Fill in the file_operations structure.
(3) Register the miscellaneous device with the kernel and generate the device node.
// Template for registering a miscellaneous device struct file_operations xxfops = { // Fill in the <code>file_operationsstructure .owner = THIS_MODULE ....... }; struct miscdevice xx_dev = { // Fill in themiscdevicestructure .minor = MISC_DYNAMIC_MINOR, .name = "xxx", .fops = &xxfops }; static int xxx_init() { int ret; ret = misc_register(&xx_dev); // Register miscellaneous device if (ret < 0) { printk("misc register error !!!\n"); return -1; } printk("misc register succeed!!!\n"); return 0; } void xxx_exit() { misc_deregister(&xx_dev); // Deregister miscellaneous device }
5. Experimental Practice
Requirement: Register a miscellaneous device according to the registration process and generate a device node.
#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 msic_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "hello_misc",
.fops = &misc_fops
};
static int misc_init(void) {
int ret = 0;
ret = misc_register(&msic_dev);
if (ret < 0) {
printk("misc register is error\n");
return -1;
}
printk("misc register is succeed\n");
return 0;
}
static void misc_eixt(void) {
printk("misc bye bye\n");
}
module_init(misc_init);
module_exit(misc_eixt);
MODULE_LICENSE("GPL");
6. Experimental Results