Character device is one of the three major Linux devices (the other two are block devices, network equipment), character device is the byte stream form of communication I/O devices, most devices are character devices, common character devices include mouse, keyboard, monitor, serial, etc., when we execute ls-l/dev , you can see a large number of device files,C is the character device,B is a block device, network equipment does not have the corresponding device files. Writing a character device driver for an external module, in addition to implementing the code required to write a module, requires code that is written as a character device.
Drive model
Linux all files, then as a device file, its operation method interface encapsulated in struct file_operations
, when we write a driver, we must implement the corresponding interface, so as to make this driver available, the Linux kernel use a lot of "registration + callback" mechanism for driver programming, The so-called registration callback, simple understanding, is when we open a device file, in fact, through the VFS to find the corresponding inode, and execute the previous creation of the device file when the Open function registered in the Inode, other functions are also so, In order for us to write the driver can be properly operated by the application, the first thing to do is to implement the appropriate method, and then create the corresponding device files.
#Include<linux/cdev.h>//for struct CDEV#Include<linux/fs.h>//for struct file#Include<asm-generic/uaccess.h>//for Copy_to_user#Include<linux/errno.h>//for Error numberStaticint MA =0;Staticint mi =0;Constint count =3;/* Prepare action Method Set *//* struct File_operations {struct module *owner;//this_module//Read device ssize_t (*read) (struct file *, char __user *, Size_ T, loff_t *); Write device ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); Map kernel space to user space int (*mmap) (struct file *, struct vm_area_struct *); Read/write device parameters, read device status, control device Long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); Open device Int (*open) (struct inode *, struct file *); Turn off device int (*release) (struct inode *, struct file *); Refresh device int (*flush) (struct file *, fl_owner_t ID); File location loff_t (*llseek) (struct file *, loff_t, int); asynchronous notification int (*fasync) (int, struct file *, int); Poll mechanism unsigned int (*poll) (struct file *, struct poll_table_struct *); };*/ssize_t Myread (struct file *filep,Char __user * user_buf,size_t size,loff_t* offset) {Return0;}struct file FoPs = {. Owner = This_module,. Read = Myread, ...};/* character device object type struct Cdev {struct kobject kobj; struct module *owner;//module owner (this_module) for module Count Const struct FILE_OPERATI ONS *ops; Operation Method Set (Division of labor: Open, close, read/write 、... ) struct List_head list; dev_t Dev; Device number (first) unsigned int count; Number of Devices};*/Staticint __InitChrdev_init(void) {.../* Construct Cdev device Object */struct Cdev *Cdev_alloc(void);/* Initialize the Cdev device Object */voidCdev_init(struct cdev*,Conststruct file_opeartions*);/* Apply for device number, static or dynamic *//* Request the first device number for the character device static */IntRegister_chrdev_region(dev_t from,unsigned count,Constchar* name);/* Request the first device number dynamically for the character device */IntAlloc_chrdev_region(Dev_t* Dev,Unsigned baseminor,unsigned count,Constchar* name); Ma = MAJOR (dev)Get the main device number from dev_t data mi = MINOR (dev)Get the secondary device number MKDEV (MA, from the dev_t data)1)Combine the main device number and the secondary device number into a device number, which is used to batch create/delete device files/* Register character device object Cdev to Kernel */IntCdev_add(struct cdev*, dev_t,unsigned); ...}Staticvoid __exit chrdev_exit (void) {... /* Cdev_del (), Cdev_put () two Select one *//* unregister from kernel Cdev device Object */void cdev_del ( Span class= "Hljs-keyword" >struct cdev*); /* logoff from kernel Cdev device Object */void cdev_put (stuct Cdev *); /* Recycling Equipment Number */void unregister_chrdev_regionunsigned count); ...}
Wordy, if the use of static application device number, then the biggest problem is not with the known device number conflict, the kernel in the document "Documentation/devices.txt" has already indicated which main device number is used, which can be seen in 2^ Of the 12 main device numbers, the range we can use is 240-255 and 261-2^12-1 , which can also explain why the device number is often 250 when we apply for a dynamic application. In addition, through this file, we can also see that "the main device number characterization of a class of devices," but the character/block device itself can be divided into many classes, so the kernel to each of them assigned the main device number.
Implement Read,write
Linux under each process has its own independent process space, even if the kernel data mapped to the user process, the PID of the data will be automatically converted to the PID of the user process, due to the existence of this mechanism, we can not directly copy the data from the kernel space and user space, but requires specialized copy data function/ Macro:
long copy_from_user(void *to, const void __user * from, unsigned long n)long copy_to_user(void __user *to, const void *from, unsigned long n)
These two functions can copy the data of the kernel space to the user process space of the user process that callback the function, and with these two functions, the read,write in the kernel can realize the data copy of kernel space and user space.
file *filep, char __user * user_buf, size_t size, loff_t* offset){ long ret = 0; size = size > MAX_KBUF?MAX_KBUF:size; if(copy_to_user(user_buf, kbuf,size) return -EAGAIN; } return 0;}
Implement IOCTL
The IOCTL is a system invocation interface that Linux specifically designs for the user-level control device, which has great flexibility, which is what our device intends to do with what commands the user implements, which can be implemented by the IOCTL, which corresponds to the function pointer in the operation method set long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
, The commands and parameters are specified entirely by the driver, and the command is typically written in a header file for the application layer and the drive layer to comply with the same communication protocol, and the Linux recommended way to define the IOCTL () command
设备类型 序列号 方向 数据尺寸8bit 8bit 2bit 13/14bit
The device Type field is a magic number, which can be a number between 0~0xff, and the "ioctl-number.txt" in the kernel gives a recommended and already used magic number (but has not been maintained for a long time), New device drivers to avoid conflicts when defining magic numbers.
The serial number field indicates that the current command is the first of the entire IOCTL command, counting from 1 onwards.
The direction field is 2bit, which indicates the direction of data transmission, and the possible values are:_ioc_none,_ioc_read,_ioc_write , and _ioc_read|_ Ioc_write.
The data Size field represents the size of the user data involved, and the width of the member depends on the architecture, usually 13 or 14 bits.
The kernel also defines the 4 macros _io (),_ior (),_iow (),_iowr ( ) to assist in generating commands in this format. The purpose of these macros is to generate a command code based on the type of input (device Type field), NR (Serial Number field) and size (data length field) and direction field shift combination.
Some I/O control commands are also pre-defined in the kernel, and if a device driver contains the same command code as a predefined command, the commands are processed as predefined commands by the kernel instead of being driven by the device, as in the following 4 types:
- Fioclex: That is, file ioctl close on exec sets a special flag for the files, notifies the kernel when the exec () system takes oh to automatically close open files when they occur
- Fionclex: File ioctl not close on Exec, clear flag set by Fioclex
- fioqsize: Gets the size of a file or directory that returns a Enotty error when used with a device file
- Fionbio: The file ioctl non-blocking I/o This call modifies the O_NONBLOCK flag in Flip->f_flags
Instance
//mycmd.h ... #include <asm/ioctl.h >#define cmdt ' A ' #define karg_size 36struct karg{int kval; char kbuf[karg_size];}; #define cmd_off _io (cmdt,0) #< Span class= "Hljs-meta-keyword" >define cmd_on _io (cmdt,1) # Define Cmd_r _ior (cmdt,2,struct karg) #define CMD_W _IOW ( Cmdt,3,struct karg) ...
Chrdev.cStaticLongDemo_ioctl(struct file *filp,Unsignedint cmd,UnsignedLong Arg) {Staticstruct Karg Karg = {. Kval =0,. kbuf = {0},};struct Karg *usr_arg;Switch (CMD) {Case CMD_ON:/* Turn on the light */BreakCase Cmd_off:/* Turn off the light */BreakCase Cmd_r:if (_ioc_size (cmd)! =sizeof (Karg)) {return-einval;} usr_arg = (struct Karg *) arg; if (Copy_to_user (Usr_arg, &karg, sizeof (Karg))) { return-eagain;} break; case cmd_w: if (_ioc_size (CMD)! = sizeof (Karg)) {return-einval;} usr_arg = (struct Karg *) arg; Span class= "Hljs-keyword" >if (Copy_from_user (&karg, Usr_arg, sizeof (Karg))) {return-eagain; } break; default:;}; return 0;}
Create a device file
Insert the device module, we can use the cat/proc/devices command to view the current system registered devices, but we have not created the appropriate device files, users will not be able to access the device through the file. The inode of the device file should be the device number that contains the device, the operation method set pointer, and so on, so that we can find the appropriate inode through the device file and then access the device. There are two ways to create a device file, either manually created or automatically created , and manually creating a device file is created with a command that uses the mknod/dev/xxx device type Master device number, so you first need to use the cat/proc/devices See the device's main device number and through the source to find the device's secondary device number, it should be noted that the theoretical device files can be placed in any file folder, but put to "/dev" in accordance with the Linux device management mechanism, The DEVTMPFS is a file system specifically designed to manage device files. When the device file is created, it is bound to the device specified at the time of creation, even if the device has been uninstalled, and if you want to delete the device file, you just need to delete the normal file as rm . Theoretically the module name (lsmod), the device name (/proc/devices), the device file name (/dev) does not have anything to do with it, but in principle it is recommended that the three be unified and manageable.
In addition to the use of crappy manual creation of device nodes, we can also use the device source code in the corresponding measures to automatically create the device once the device is loaded, automatically create the device files need us to compile the kernel or the root file system when the corresponding configuration:
---> Generic Driver Options ---> [*]Maintain a devtmpfs filesystem to mount at /dev [*] Automount devtmpfs at /dev,after the kernel mounted the rootfs
OR
Making a startup script write to the root file system
none sysfs /sysmdev -s //udev也行
With these preparations, you only need to export the appropriate device information to "/sys" to automatically create the device files according to our requirements. The kernel provides us with the relevant API
Class_create (Owner,name);struct device *device_create_ Vargs (struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, va_list Vargs); void class_destroy (struct class *cls); void device_destroy (struct class *cls, dev_t devt);
With these functions, we can automatically create and delete device files by filling in the following code in Xxx_init () and xxx_exit () .
/* 在/sys中导出设备类信息 */ cls = class_create(THIS_MODULE,DEV_NAME); /* 在cls指向的类中创建一组(个)设备文件 */ for(i= minor;i<(minor+cnt);i++){ devp = device_create(cls,NULL,MKDEV(major,i),NULL,"%s%d",DEV_NAME,i); }
/* 在cls指向的类中删除一组(个)设备文件 */ for(i= minor;i<(minor+cnt);i++){ device_destroy(cls,MKDEV(major,i)); } /* 在/sys中删除设备类信息 */ class_destroy(cls); //一定要先卸载device再卸载class
With this done, a simple character device driver is built and now it's time to write a user program to test ^-^
Linux character device driver framework