Character Device Driver:
1. Generally, we use APIs that dynamically allocate device numbers:
Int alloc_chrdev_region (dev_t * Dev, unsigned int firstminor, unsigned int count, char * Name );
Dev is a function used for output. After successful call, the first number of the allocated range will be saved.
Void unregister_chrdev_region (dev_t first, unsigned int count );
The above function allocates device numbers for driver usage, but they didn't tell the kernel what to do with these numbers. Before the user space can access the above devices, the driver needs to associate the device number with the internal function.
To dynamically generate device numbers and establish device nodes, insmod calls can be replaced with a simple script. The disadvantage of dynamic allocation is that the device node cannot be automatically generated.
#! Bin/sh
Module = "scull"
Device = "scull"
MoD = "664"
/Sbin/insmod./$ module. Ko $ * | Exit 1 calls insmod and uses the path name to specify the module location.
Rm-F/dev/$ {Device} [0-3] delete a device Node
Major = $ (awk "/$2 =/" $ module/"{print/$1}"/proc/devices)
Mknod/dev/$ {Device} 0 C $ major 0
Mknod/dev/$ {Device} 1 C $ major 1
Mknod/dev/$ {Device} 2 C $ major 2
Mknod/dev/$ {Device} 3 C $ major 3
Group = "stuff"
Chgrp $ group/dev/$ {Device} [0-3]
Chmod $ MOD/dev/$ {Device} [0-3]
2. Code for obtaining the master device Number:
Scull adopts this practical method. He uses sucll_major to save the selected device number (also has a scull_minor for the next device number). The initial value of this variable is scul_major, this macro is defined in scull. in H, the default value is 0. You can use the default value or select a specific device number.
If (scull_major ){
Dev = mkdev (scull_major, scull_minor );
Result = register_chrdev_region (Dev, scull_nr_devs, "scull ");
} Else {
Result = alloc_chrdev_region (& Dev, scull_minor, scull_nr_devs, "scull ");
Scull_major = major (Dev );
}
If (result <0 ){
Printk (kern_info "can't get Major % d/N", scull_major );
Return result;
}
The front side said that Dev in alloc_chrdev_region is only used as the output parameter. Then get the master device number through major (Dev.
In this section, it is important to use the function to obtain the device number. The parameter name is the name of the device associated with the number range, it will appear in/proc/devices and sysfs.
Here, we can understand why mdev and udev can dynamically and automatically generate the device files required by the current system. Udev identifies hardware devices by reading sysfs information.
(See Understanding and understanding udev.
URL: http://blog.chinaunix.net/u/6541/showart_396425.html)
3. Several options in file_operations:
Unsigned int (* poll) (struct file *, struct poll_table_struct *)
The poll method is the backend Implementation of poll, epoll, and select system calls, these three system calls can be used to query whether reading or writing on one or more file descriptors (multiple file descriptors are implemented by constructing an fd_set) is blocked, the poll method should return a mask to indicate whether non-blocking reading or writing is possible, it also provides the kernel with information that puts the calling process in sleep state until I/O becomes possible.
4. The file struct contains the private_data struct. This variable is usually converted to the struct required by the device by force type conversion to save useful resources.
5. the kernel uses the inode structure to internally represent the file. Therefore, it is different from the file structure, and the latter indicates the opened file descriptor. For a single file, there may be two file structures that indicate opening the file descriptor, but they all point to a single inode structure.
There is a struct cdev * I _cdev FIELD IN THE inode struct. struct cdev indicates the internal kernel structure of the character device. When inode points to a character device, this field contains a pointer to the struct cdev structure.
6. The front side knows that the kernel uses the struct cdev structure to represent character devices. Before the kernel calls device operations, it must allocate and register one or more structures:
Struct cdev * my_cdev = cdev_alloc ();
My_cdev-> Ops = & my_fops;
Assign a cdev structure.
Void cdev_init (struct cdev * cdev, struct file_operations * FoPs );
Initialize the device.
Void cdev_add (struct cdev * Dev, dev_t num, int count );
Add the device to the kernel. Dev is a cdev device, num is the device number, and count is generally 1.
Struct cdev provides interfaces between the kernel and the device.
Static void scull_setup_cdev (struct scull_cdev * Dev, int index)
{
Int err, devno = mkdev (scull_major, scull_minor + index );
Cdev_init (& Dev-> cdev, & scull_fops );
Dev-> cdev. ower = this_module;
Dev-> cdev. Ops = & scull_fops;
Err = cdev_add (& Dev-> cdev, devno, 1 );
If (ERR ){
....
}
Return 0;
}
7. Open Functions
INT (* open) (struct inode * inode, struct file * filp)
The inode field in its I _cdev contains the information we need, that is, the cdev structure we set. The only problem is that we don't need the cdev structure, but need to know the scull_dev structure that contains the cdev structure. Here the container_of macro is used to complete the process. The macro can obtain the struct containing the member through the struct member:
Container_of (pointer, container_type, container_field );
This macro requires a pointer to the container_field field, which is included in the iner_type struct, and then returns the struct pointer containing this field, in scull_open, this macro is used to find the appropriate device structure.
Struct scull_dev * dev;
Dev = container_of (inode-> I _cdev, struct scull_dev, cdev );
Filp-> private_data = dev;
This code first defines a scull_dev device that contains the I _cdev field (including cdev), and then finds the scull_dev struct containing the cdev field through container_of, assign the struct value to the private member variable of filp.
One task to be completed by open is to allocate and fill in the data structure placed in filp-> private_data.
Int scull_open (struct inode * inode, struct file * filp)
{
Struct scull_dev * dev;
Dev = container_of (inode-> I _cdev, struct scull_dev, cdev );
Filp-> private_data = dev;
If (filp-> f_flags & o_accmode) = o_wronly ){
Scull_trim (Dev);/* release the entire memory area */
}
Return 0;
}
8. Functions of release/close
Release the data structure that is allocated by open and saved in filp-> private_data.
Disable the device during the last close operation.
The problem here is that the number of times the device is disabled may be greater than the number of open devices. For example, if the DUP/fork system calls open, a copy of the opened file will be created without calling open, however, each copy is closed before the program is terminated.
The reason is: not every call to the close system will cause a call to the release method. Only the close that truly releases the device data structure will call the call to the release method. The kernel maintains the number of times the counter is used for each file structure. System calls such as DUP/fork do not create a new data structure (only created by open, the cdev structure applied for and initialized in the open function, use container_of to obtain the scull_dev structure containing cdev and assign the value to filp-> private_data to allocate a new data structure.) they only increase the data structure count. When the count of the file structure is reduced to zero, close is called and release is executed.