Document directory
- 1. How to describe the device number in the kernel?
- 2. How to extract the master device number from dev_t
- 3. How to extract the device number from dev_t
- 4. How do I assign a master device number?
- 5. Cancel the device number
- 1. Manual Creation
- 1. struct File
- 2. struct inode
- 3. struct file_operations
- 1. character device registration can be divided into three steps:
- 2. device allocation method: cdev_alloc
- 3. device initialization method: cdev_init
- 4. How to add a device: cdev_add
- 1. INT (* open) (struct inode *, struct file *)
- 2. Void (* release) (struct inode *, struct file *)
- 3. ssize_t (* read) (struct file * filp, char _ User * buff, size_t count, loff_t * OFFP );
- 4. ssize_t (* write) (struct file * filp, char _ User * buff, size_t count, loff_t * OFFP );
- 5. Unsigned int (* poll) (struct file *, struct poll_table_struct *)
- 6. INT (* IOCTL) (struct inode *, struct file *, unsigned int, unsigned long)
- 7. INT (* MMAP) (struct file *, struct vm_area_struct *)
- 8. off_t (* llseek) (struct file *, loff_t, INT)
I. device number
The device number is divided into the primary device number and secondary device number.
The "main device number" is used to indicate the driver corresponding to the "Device File. The "device number" is used by the driver to identify which device is being operated.
Summary: The primary device number is used to identify the device type. The secondary device number is used to identify the specific device of the same type.
1. How to describe the device number in the kernel?
A: Use a dev_t variable to describe it;
The essence is an unsigned 32-bit integer. The 12-bit high is the primary device number, and the 20-bit low is the secondary device number.
2. How to extract the master device number from dev_t
A: Use macro major (dev_t Dev) for decomposition.
3. How to extract the device number from dev_t
A: Use macro minor (dev_t Dev)
4. How do I assign a master device number?
Two Methods: static application and Dynamic Allocation
4.1 static application
4.1.1 method:
(1) determine an unused master device number based on documentation/devices.txt.
(2) Use the register_chardev_region function to register the device number.
Advantage: simple
Disadvantage: it is prone to conflicts.
4.1.2 introduction to the register_chardev_region function:
(1) function prototype: int register_chardev_region (dev_t from, unsigned count, const char * name)
(2) function: apply for the Count device numbers starting from (the master device number remains the same, and the next device number increases)
(3) parameters:
From: The device number you want to apply
Count: number of devices you want to apply
Name: device name (in/proc/devices)
4.2 Dynamic Application
4.2.1 method:
Use the alloc_chardev_region function to assign a device number.
Advantages: simple and easy to drive promotion
Disadvantage: you cannot create a device file before installing the driver.
Solution: after the driver is installed, query the device number from/proc/devices.
4.2.2 description of the alloc_chardev_region Function
(1) function prototype: int alloc_chardev_region (dev_t * Dev, unsigned baseminor, unsigned count, const char * Name );
(2) function: Request the kernel to dynamically allocate count device numbers, and the number of devices starts from baseminor.
(3) parameters:
Dev: The assigned device number.
Baseminor: Device Number of the start time
Count: Number of device numbers to be allocated
Name: device name (in/proc/devices)
5. Cancel the device number
No matter which method is used to assign device numbers, these device numbers should be released when they are not used;
Function: void unregister_chardev_region (dev_t from, unsigned count );
Function: Release the Count device numbers starting with "from ".
Ii. Create a device file
Two methods: (1) Use the mknod command to manually create (2) automatically create
1. Manual Creation
(1) mknod method: mknod filename type major minor
(2) parameters:
Filename: Device File Name
Type: Device File Type
Major: master device number
Minor: Sub-device number
Iii. Common Data Structures 1. struct File
Represents an open file. Each opened file in the system has an associated struct file in the kernel space. It is created by the kernel when the file is opened and released when the file is closed.
Important members:
(1) loff_t f_pos/* file read/write location. loff_t is an integer variable */
(2) struct file_operations * f_op
2. struct inode
Used to record the physical information of a file. It has a different structure than the file that represents opening a file.
A file can correspond to multiple file structures, but only one inode structure exists.
Important members:
(1) dev_t I _rdev: Device number
3. struct file_operations
A collection of function pointers that define operations that can be performed on devices. The members in the structure point to the functions in the driver. These functions implement a special operation and retain NULL for unsupported operations.
Summary: it is actually a table used to convert function calls in an application to corresponding functions in the driver. For example:
struct file_operations mem_fops={ .owner = THIS_MODULE, .llseek = mem_seek, .read = mem_write, .ioctl = mem_ioctl, .open = mem_open, .release = mem_release,};
Iv. device registration
In the Linux 2.6 kernel, the character device uses struct cdev to describe
1. character device registration can be divided into three steps:
(1) Allocate cdev
(2) initialize cdev
(3) add cdev
2. device allocation method: cdev_alloc
The cdev_alloc function can be used to allocate struct cdev: struct cdev * cdev_alloc (void );
3. device initialization method: cdev_init
The initialization of struct cdev is completed using the cdev_init function.
Void cdev_init (struct cdev * cdev, const struct file_operations * FoPs)
Parameters:
Cdev: The cdev structure to be initialized.
FoPs: operation function set corresponding to the device
4. How to add a device: cdev_add
The registration of struct cdev is completed using the cdev_add function.
Int cdev_add (struct cdev * P, dev_t Dev, unsigned count)
Parameters:
P: character device structure to be added to the kernel
Dev: Device number
Count: number of devices added
After completing the three steps above, the registration of the driver is complete. The next step is to implement the operations supported by the device, that is, to implement the const struct file_operations * fops
5. device operation 1. INT (* open) (struct inode *, struct file *)
(1) The first operation on the device file can be directly assigned null without implementing this function. If this parameter is set to null, the device is always enabled successfully.
(2) The open method is used by the driver to complete initialization preparation for future operations. In most drivers, open completes the following tasks:
= Initialize the device, such as setting some registers
= Indicates the device number
2. Void (* release) (struct inode *, struct file *)
(1) it corresponds to the system call, which is called when the device file is closed. Like open, release does not exist.
(2) the opposite is to close the device.
3. ssize_t (* read) (struct file * filp, char _ User * buff, size_t count, loff_t * OFFP );
Read data from devices
4. ssize_t (* write) (struct file * filp, char _ User * buff, size_t count, loff_t * OFFP );
Send data to the device.
Read and Write summary:
(1) reading and writing methods are similar: Reading data from the device to the user space, and passing data to the driver.
(2) For the two methods, filp is the file pointer, and count is the data volume requested for transmission. The buff parameter points to the data cache. OFFP indicates the current access location of the file.
(3) The buff parameter of the read and write methods is a user space pointer. Therefore, it cannot be directly referenced by kernel code, because the user control pointer may be invalid in the kernel space and does not have the ing of that address.
In this regard, the kernel provides dedicated functions for accessing user control pointers, such:
Int copy_from_user (void * To, const void _ User * From, int N );
Int copy_to_user (void _ User * To, const void * From, int N );
5. Unsigned int (* poll) (struct file *, struct poll_table_struct *)
Corresponding to select system call
6. INT (* IOCTL) (struct inode *, struct file *, unsigned int, unsigned long)
Control Device
7. INT (* MMAP) (struct file *, struct vm_area_struct *)
Map the device to the virtual address space of the process.
8. off_t (* llseek) (struct file *, loff_t, INT)
Modify the current read/write location of the file and use the new location as the return value.