Linux character device-simple character device model
Linux character Devices
I. Use the character Device Driver
1. Compile/install the driver
In Linux, drivers generally use the program structure of the kernel module for encoding. Therefore, the essence of compiling/installing a driver is to compile/install a kernel module.
2. Character Device File
With the character device file, the application can use the corresponding character device driver to control character devices.
There are two methods to create a character device file:
1. Run the mknod command.
Mknod/dev/file name c main device number times device number
2. Create a function in the driver
Ii. Character-driven programming model
- Device description Structure
- Driver Model
In Linux, there are many types of devices, such as character devices, Block devices, network interface devices, USB devices, PCI devices, platform devices, and hybrid devices ......, Different device types also mean that the corresponding driver models are different, which leads to the need to master many driver models. So whether we can extract some common rules from these many driver models is the key to learning the Linux driver well.
1. device description Structure
In any driver model, devices are described using a structure in the kernel. Our character devices use structcdev to describe in the kernel.
Struct cdev {
Struct kobject kobj;
Struct module * owner;
Const struct file_operations * ops; // device operation set
Struct list_head list;
Dev_t dev; // device number
Unsigned int count; // number of devices
};
1.1 device No.
View the device number in the/dev directory
1.1 device No.
2.1 device No.-Operation
In the Linux kernel, The dev_t type is used to define the device number. The dev_t type is essentially a 32-bit unsigned int. The high 12-bit is the primary device number, and the low 20-bit is the secondary device number.
Question 1: How can I combine the master and secondary device numbers into dev_t types?
A: dev_t dev = MKDEV (primary device number, secondary device number)
Question 2: How do I obtain the master device number from dev_t?
Answer: master device number = MAJOR (dev_t dev)
Question 3: How do I obtain the device number from dev_t?
Answer: Sub-device number = MINOR (dev_t dev)
1.1 device number-allocation
How do I assign a master device number to a device?
Static Application
The developer selects a number as the master device number and then applies to the kernel through the register_chrdev_region function. Disadvantage: if the device number applied for is used by another driver in the kernel, the application fails.
Dynamic Allocation
Use alloc_chrdev_region to assign an available master device number to the kernel.
Advantage: Because the kernel knows which number has been used, it will not be assigned to the used number.
1.1 device No.-deregister
The unregister_chrdev_region must be used when the driver exits, regardless of the method used to assign the device number.
Function to release these device numbers.
1.2 operation function set
2.2 operation function set
Struct file_operations is a collection of function pointers, which can be defined in
The operation performed on the device. The function pointer in the structure points to the function in the driver,
These functions implement a device-specific operation.
Set the function pointer to NULL. For example:
Struct file_operations dev_fops = {
. Llseek = NULL,
. Read = dev_read,
. Write = dev_write,
. Ioctl = dev_ioctl,
. Open = dev_open,
. Release = dev_release,
};
2.1 character device Initialization
2.1 description structure-allocation
The cdev variable can be defined either static or dynamic.
• Static allocation
Struct cdev mdev;
• Dynamic Allocation
Struct cdev * pdev = cdev_alloc ();
2.1 description structure-initialization
The initialization of struct cdev is completed using the cdev_init function.
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
2.1 description structure-registration
The registration of character devices is completed using the cdev_add function.
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 of this type
2.1 hardware initialization
Complete the initialization according to the corresponding hardware chip manual.
2.2 perform device operations
2.2 hand-held for analysis
Analysis
File_operations
2.2 device operation prototype
Int (* open) (struct inode *, struct file *)
Open the device and respond to the open system
Int (* release) (struct inode *, struct file *);
Close the device and respond to the close system call.
Loff_t (* llseek) (struct file *, loff_t, int)
Relocate read/write pointers to respond to lseek system calls
Ssize_t (* read) (struct file *, char _ user *, size_t, loff_t *)
Reads data from devices and responds to read system calls.
Ssize_t (* write) (struct file *, const char _ user *, size_t, loff_t *)
Write Data to the device and respond to the write System Call
2.2 Struct file
In Linux, each opened file is associated with a struct file in the kernel, which is created by the kernel when the file is opened and released after the file is closed.
Important members:
Loff_t f_pos/* file read/write pointer */
Struct file_operations * f_op/* operations corresponding to the file */
2.2 Struct inode
Every file in the file system is associated with an inode structure, which is mainly used to record the physical information of the file. Therefore, it is different from the file structure that represents opening a file. When a file is not opened, the file structure is not associated, but an inode structure is associated.
Important members:
Dev_t I _rdev: Device number
3.2 device operation-open
The open device method is used by the driver to complete initialization preparation for future operations. In most drivers
, Open to complete the following work:
Device ID
Start the device
3.2 device operation-release
The release method works in the opposite way as open. This device method is also called close. It should:
Disable the device.
3.2 device operation-read
The read Device method usually completes two tasks:
Read data from devices (for hardware operations)
Return the read data to the application.
Ssize_t (* read) (struct file * filp, char _ user * buff, size_t count, loff_t * offp)
Parameter Analysis:
Filp: The file structure pointer associated with the character device file, which is created by the kernel.
Buff: The data read from the device, which must be saved to the location. This parameter is provided by the read system call.
Count: the amount of data to be transmitted. this parameter is provided by the read system.
Offp: The read/write location of the file, which is obtained by the kernel from the file structure and passed in.
The buff parameter is a pointer from the user space. Such pointers cannot be directly referenced by kernel code and must use special functions.
Int copy_from_user (void * to, const void _ user * from, int n)
Int copy_to_user (void _ user * to, const void * from, int n)
3.2 device operation-write
The write Device method usually completes two tasks:
Extract data from the address provided by the application to write data to the device (a hardware operation)
Ssize_t (* write) (struct file *, const char _ user *, size_t, loff_t *)
Its parameters are similar to read
2.3 drive logout
When we uninstall the driver from the kernel, we need to use the cdev_del function to cancel the character device.
First, write an example of a device node that automatically assigns character device numbers to manually assign characters and an APP
Manual installation steps:
Insmod char_dev.ko
View character device numbers
Cat/proc/devices
Install the device node again.
Mknod/dev/my_chardev c 248 0
Then test the app.
./My_char_dev_app 1
Kernel driver code char_dev.c
# Include <linux/module. h>
# Include <linux/init. h>
# Include <linux/io. h>
# Include <linux/fs. h>
# Include <asm/device. h> // The following three header files need to be added for dynamic creation.
# Include <linux/device. h>
# Include <linux/cdev. h>
# Include "my_cdev.h"
Struct cdev;
Dev_t devno; // The device number is dynamically allocated.
Int my_cdev_open (struct inode * node, struct file * filp)
{
Printk ("my_cdev_open sucess! \ N ");
Return 0;
}
Long my_cdev_ioctl (struct file * filp, unsigned int cmd, unsigned long arg)
{
Switch (cmd)
{
Case LED_ON:
Printk ("LED_ON is set! \ N ");
Return 0;
Case LED_OFF:
Printk ("LED_OFF is set! \ N ");
Return 0;
Default:
Return-EINVAL;
}
}
Struct file_operations my_cdev_fops =
{
. Open = my_cdev_open,
. Unlocked_ioctl = my_cdev_ioctl,
};
Static int my_cdev_init (void)
{
Int ret;
/** Dynamically allocate device numbers */
Ret = alloc_chrdev_region (& devno, 0, 1, "my_chardev ");
If (ret)
{
Printk ("alloc_chrdev_region fail! \ N ");
Unregister_chrdev_region (devno, 1 );
Return ret;
}
Else
{
Printk ("alloc_chrdev_region sucess! \ N ");
}
/** Description structure initialization */
Cdev_init (& cdev, & my_cdev_fops );
/** Description structure registration */
Ret = cdev_add (& cdev, devno, 1 );
If (ret)
{
Printk ("cdev add fail. \ n ");
Unregister_chrdev_region (devno, 1 );
Return ret;
}
Else
{
Printk ("cdev add sucess! \ N ");
}
Return 0;
}
Static void my_cdev_exit (void)
{
Cdev_del (& cdev );
Unregister_chrdev_region (devno, 1 );
Printk ("my_cdev_exit sucess! \ N ");
}
Module_init (my_cdev_init );
Module_exit (my_cdev_exit );
MODULE_LICENSE ("GPL ");
MODULE_AUTHOR ("YEFEI ");
MODULE_DESCRIPTION ("YEFEI Driver ");
APP header file my_cdev.h
# Ifndef _ MY_CDEV_H __
# Define _ MY_CDEV_H __
# Define LED_MAGIC 'l'
# Define LED_ON _ IO (LED_MAGIC, 0)
# Define LED_OFF _ IO (LED_MAGIC, 1)
# Endif
APP test file my_char_dev_app.c
# Include <sys/stat. h>
# Include <sys/types. h>
# Include <sys/ioctl. h>
# Include <fcntl. h>
# Include <stdio. h>
# Include "my_cdev.h"
Int main (int argc, char * argv [])
{
Int fd;
Int cmd;
If (argc <2)
{
Printf ("Please enter secend param! \ N ");
Return 0;
}
Cmd = atoi (argv [1]);
Fd = open ("/dev/my_chardev", O_RDWR );
If (fd <0)
{
Printf ("Open dev/my_chardev fail! \ N ");
Close (fd );
Return 0;
}
Switch (cmd)
{
Case 1:
Ioctl (fd, LED_ON );
Break;
Case 2:
Ioctl (fd, LED_OFF );
Break;
Default:
Break;
}
Close (fd );
Return 0;
}
January
21:54:48
This article permanently updates the link address: