Let's start with a well-written kernel driver module to experience the following character device drivers
You can temporarily ignore the following code implementation!
Memdev.c
#include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/cdev.h > #include <asm/uaccess.h>int dev1_registers[5];int dev2_registers[5];struct cdev Cdev; dev_t devno;/* File Open function */int mem_open (struct inode *inode, struct file *filp) {/* Get secondary device number */int num = MINOR (inode-> ; i_rdev); if (num==0) filp->private_data = dev1_registers; else if (num = = 1) filp->private_data = dev2_registers; else Return-enodev; Invalid secondary device number return 0; }/* file Release function */int mem_release (struct inode *inode, struct file *filp) {return 0;} /* Read function */static ssize_t mem_read (struct file *filp, char __user *buf, size_t size, loff_t *ppos) {unsigned long p = *ppos; unsigned int count = size; int ret = 0; int *register_addr = filp->private_data; /* Obtain the device's register base address */* To determine if the read position is valid */if (P >= 5*sizeof (int)) return 0; if (Count > 5*sizeof (int)-P) Count = 5*sizeof (int)-p; /* Read data to User space */if (Copy_to_user (buf, RegiSter_addr+p, Count)) {ret =-efault; } else {*ppos + = count; ret = count; } return ret;} /* Write function */static ssize_t mem_write (struct file *filp, const char __user *buf, size_t size, loff_t *ppos) {unsigned long p = *ppos; unsigned int count = size; int ret = 0; int *register_addr = filp->private_data; /* Get the device's register address */* parse and get valid write length */if (P >= 5*sizeof (int)) return 0; if (Count > 5*sizeof (int)-P) Count = 5*sizeof (int)-p; /* Write data from user space */if (Copy_from_user (Register_addr + p, buf, count)) ret =-efault; else {*ppos + = count; ret = count; } return ret;} /* Seek file Locator function */static loff_t mem_llseek (struct file *filp, loff_t offset, int whence) {loff_t newpos; Switch (whence) {case seek_set:newpos = offset; Break Case Seek_cur:newpos = Filp->f_pos + offset; Break Case seek_end:newpos = 5*sizeof (int)-1 + offset; Break Default:return-einval; } if ((newpos<0) | | (newpos>5*sizeof (int))) Return-einval; Filp->f_pos = Newpos; return newpos;} /* File operation struct */static const struct file_operations mem_fops ={. Llseek = Mem_llseek,. Read = Mem_read,. Write = Mem_write, . open = Mem_open,. Release = mem_release,};/* device driver module load function */static int memdev_init (void) {/* Initialize CDEV structure */Cdev_init (&CD EV, &MEM_FOPS); /* Register character device */alloc_chrdev_region (&DEVNO, 0, 2, "Memdev"); Cdev_add (&cdev, Devno, 2);} /* Module unload function */static void memdev_exit (void) {Cdev_del (&cdev); /* Unregister device */Unregister_chrdev_region (DEVNO, 2); /* Release device number */}module_license ("GPL"); Module_init (Memdev_init); Module_exit (Memdev_exit);
1. Compile/install drivers: in Linux systems, drivers are usually encoded using the kernel module's program structure, so compiling and installing a driver is essentially compiling/installing a kernel module.
2. Create a Device file
How the application accesses the device driver interface through the character device files, that is, the character device driver access to the big secret , the following is a small example of the application: ( How the application is to find the device driver portal through the system call and then let the device driver work )
Read-mem.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main () { int FD = 0;int DST = 0;/* to open the device file */FD = open ("/dev/memdev0", O_RDWR);/* Write Data */read (FD, &DST, sizeof (int));p rintf ("DST is% D\n ", DST);/* close Device */close (FD); return 0;}
The above files are statically compiled under Linux (taking into account that some libraries previously ported on the board have not yet been added) to generate the Read-mem target file, then disassemble and import the disassembled files into the dump in the current directory.
Vim Open dump file, search/main can see this section of assembly code
You can see that the read () function in the application actually calls the __libc_read function and then continues to search for the __libc_read function in dump
Here the red arrow pointing to the two lines is the more important two lines, the 3 passed to R7, and then use the SVC system call instruction, the PC pointer from the user space into the kernel space (through a fixed entrance), the second step will take the R7 register value 3, Then check a table based on this value to determine which system call to invoke (that is, the kernel code for 3 system calls). This opens the kernel source project and opens a file:
Entry-comon. S (under/arch/arm/kernel directory)
The kernel code section above VECTOR_SWI is the fixed entry, the second arrow part is the second step above, the third step in this part of the code here is not cut so much (that is, according to the number table)
Enable_irqget_thread_info tskadrtbl, <span style= "color: #ff0000;" >sys_call_table</span>@ Load syscall Table pointer
Search sys_call_table, see what this watch is?
View calls. S file
System is to enter the kernel space through a fixed entry, then take out the system call number, use the number to find the above table, and then remove the kernel in the above user space of the Read implementation function! ( This is an analysis of how user-space read is the process of finding the sys_read of the kernel space .)
Here's a look at the kernel code implementation of Sys_read: In the read_write.c file in the/fs directory
Syscall_define3 (read, unsigned int, FD, char __user *, buf, size_t, count) {struct file *file;ssize_t ret =-ebadf;int fput _needed;file = Fget_light (fd, &fput_needed); if (file) {loff_t pos = file_pos_read (file); ret = vfs_read (file, buf, Coun T, &pos); File_pos_write (file, POS); fput_light (file, fput_needed);} return ret;}
Each open file will have a struct file corresponding to it! From the function above, we can see that the corresponding struct file can be found by the incoming FD parameter.
The Vfs_read () function is then called through file. Let's look at the internal implementation of the function.
Red Arrow Part! F_op part is the driver inside the custom structure, through the F_OP structure to find the device reading Method!
That's it:
/* File operation struct */static const struct file_operations mem_fops ={ . Llseek = Mem_llseek, . Read = Mem_read, . Write = M Em_write, . Open = Mem_open, . Release = Mem_release,};
Linux application access character device driver detailed process parsing