How to write Linux device drivers

xiaoxiao2021-03-06  50

Linux is a variant of the UNIX operating system. The principles and ideas written under Linux are completely similar to other UNIX systems, but the drivers in the DOS or Window environment have great differences. Design drivers in the Linux environment, simple ideas, convenient operation, and powerful, but support function, can only rely on the function in kernel, some commonly used operations should be written, and debugging is not convenient. I have developed a driver for a multimedia card developed by the laboratory in these weeks, gaining some experience, is willing to share with Linux Fans, please correct. Some of the following words are primarily derived from KHG, Johnsonm Write Linux Device Driver, Brennan's Guide to Inline Assembly, The Linux AZ, and some information about Device Driver on Tsinghua BBS. These materials have been out of time, some have Some errors, I corrected according to my own test results. 1. Linux Device Driver's conceptual system call is an interface between the operating system kernel and the application, the device driver is the interface between the operating system kernel and machine hardware. The driver is the details of the hardware, so that the application is only a device file, the application can operate on the hardware device like a normal file. The device driver is part of the kernel, which completes the following Features: 1. Initialize and release the device. 2. Transfer the data from the kernel to the hardware and read data from the hardware. 3. Read the application to the data of the data and the data requested by the application. 4. Detection And the error of the processing device. There are two types of primary device file types under the Linux operating system, one is the character device, the other is the block device. The main difference between the character device and block device is: read the character device / Write request, the actual hardware I / O is usually taken immediately. If the block device, it uses a system memory as a buffer, and returns the request data when the user process requests the device to meet the user's requirements. If you cannot call the request function, the actual I / O operation is made. Block devices are mainly designed for slow devices such as disk, so as not to consume too much CPU time to wait. It has been mentioned that the user process is through the device file. Deal with actual hardware. Each device file has its file attribute (C / B), indicating that the character device is still strong? Different each file has two device numbers, the first is the main device number Identify the driver, the second is from the device number, identifies different hardware devices that use the same device driver, such as two floppy disk, can distinguish from the device number to distinguish them. The main device number of the device file must The user process will not be able to access the driver when the device driver applied, otherwise the user process will not be able to access the driver. Finally, the system enters the core state when the user process calls the driver. At this time, it is no longer a first Typical scheduling. That is to say, the system must return to other work after returning the child function of your driver. If your driver falls into a dead loop, unfortunately, you only restart the machine, then a long FSCK. // HEHE read / write, it first checks the contents of the buffer, if the data of the buffer, how to write the device driver under the Linux operating system Second, the instance analyzes us to write a simplest character device driver.

Although it doesn't do anything, it can understand the working principle of Linux device drivers. Enter the machine below, you will get a real device driver. But my kernel is 2.0.34, There may be problems on the low version of the kernel, I haven't tested it .//xixi #define __no_version__ # include # include

Char kernel_version [] = uts_release; this paragraph defines some version information, although it is not very large, but it is indispensable. Johnsonm said that all drivers must include

But I don't want to. Since the user process is dealing with hardware through the device file, the way to operate the device file is nothing more than the system call, such as Open, Read, Write, Close ...., notice, not fopen, Fread, but how do I associate system calls and drivers? This requires a very critical data structure: struct file_operations {int (* seek) (Struct Inode *, Struct File *, Off_t, int); int (* Read) (struct inode *, struct file *, char, int); int (* write) (struct inode *, struct file *, OFF_T, int); int (* readdir) (Struct Inode *, Struct File *, Struct Dirent *, Int); INT (* SELECT) (Struct Inode *, Struct File *, INT, SELECT_TABLE *); INT (* ioctl) (Struct Inode *, Struct File *, Unsined Int, Unsigned Long); int (* mmap (struct inode *, struct file *, struct vm_area_struct *); int (* open) (struct inode *, struct file *); int (* release *, struct file *); int (* fsync) (struct inode *, struct file *); int (* fas *, struct file *, int); int) (Struct Inode *, Struct File *); int (* revALIDATE) (DEV_T DEV );} The name of each member of this structure corresponds to a system call. User Process Using System Call When the device files such as READ / WRITE operations, the system calls the corresponding device driver through the main device number of the device file. , Then read the corresponding letter of this data structure Number pointers, then handed the control power to the function. This is the basic principle of Linux device driver work. Since this is the case, the main job of writing the device driver is to write the subunies and populate each domain of File_Operations. Pasually Simple , Isn't it? Let's start writing a subroutine. #Include

#include

#include

#include

#include

unsigned int test_major = 0; static int read_test (struct inode * node, struct file * file, char * buf, int count) {int left; if (verify_area (VERIFY_WRITE, buf, count) == -EFAULT) return -EFAULT; For (Left = count; left> 0; left--) {__put_user (1, buf, 1); buf ;} return count;} This function is ready for Read_Test () when calling Read_test () It writes all of the user's buffer all written 1.BUF is a parameter for the read call. It is an address of the user's process space. But when Read_Test is called, the system enters the core state. So you can't use the address, you must use _ _put_user (), this is a function provided by Kernel to transfer data to the user. There are also many functions of similar functions. Please refer to. You must verify that the BUF is available before copying the data to the user space. This function is used verify_area static int write_tibet (struct inode * inode, struct file * file, const char * buf, int count) {return count;}. Static int open_tibet (struct inode * inode, struct file * file) {MOD_INC_USE_COUNT Return 0;} static void release_tibet (Struct Inode * Inode, Struct File * file) {mod_dec_use_count;} These various functions are empty. Don't do anything when I actually call, they only provide function pointers for the following structure. . struct file_operations test_fops = {NULL, read_test, write_test, NULL, / * test_readdir * / NULL, NULL, / * test_ioctl * / NULL, / * test_mmap * / open_test, release_test, NULL, / * test_fsync * / NULL, / * test_fasync * / / * Nothing more, fill with nulls * /}; the main body of the device driver can be said to be written. Now embed the driver into the kernel. The driver can be compiled in two ways. One is to compile into kernel, and the other is to compile the module. If you compile into the kernel, increase the size of the kernel, and change the source file of the kernel, and cannot be dynamically uninstalled, it is not conducive to debugging, so recommended Use module mode.

INT init_module (void) {int result; result = register_chrdev (0, "test", & test_fops); if (Result <0) {printk (kern_info "test: can't get major number / n"); return result;} IF (Test_major == 0) Test_major = result; / * Dynamic * / return 0;} The init_module function is called when using the Insmod command to transfer the compiled module into the memory. Here, INIT_MODULE has only one thing, that is, register a character device to the character device table of the system. Register_chrdev requires three parameters. The parameter first is the device number you want to obtain. If it is zero, the system will select a device number that is not occupied. The parameter 2 is the device file name, the parameter three is used to register a pointer to the actual function of the actual execution operation. If the registration is successful, return to the main device number of the device, unsuccessful, return a negative value. Void cleanup_module (void) {unregister_chrdev (Test_major, "Test");} When using the RMMOD to uninstall the module, the cleanup_module function is called, which releases the character device TEST in the system character device table. An extremely simple character device can be said to be written, the file name is called Test.c. The following is compiled $ gcc -o2 -dmodule -d__kernel__ -c test.c gets the file Test.O is a device driver. If there are multiple files, press each file to compile each file, then ld -r file1.o file2.o -o modulename. Driver has been compiled, and now install it into the system. $ INSMOD -F TEST.O If the installation is successful, you can see the device TEST in the / proc / defices file, and you can see its main device number. To uninstall, run the RMMOD TEST Next to create a device file. MKNOD / DEV / TEST C MAJOR Minor C is a character device, and Major is a primary device number, which is seen in / proc / devices. With shell command $ cat / proc / devices | awk "}" to get the master number, you can join the above command line to your shell script. Minor is from the device number and setting it into 0. We can now access our drivers through device files. Write a small test program. # include # include

#include

#include

Main () {Int Testdev; Int i; char buf [10]; testdev = open ("/ dev / test", o_rdwr); if (TestDev == -1) {Printf ("Cann't open file / n" EXIT (0);} Read (TestDev, BUF, 10); for (i = 0; i <10; i ) Printf ("% d / n", buf [i]); close (testdev); Compile operation, see if you print all 1? The above is just a simple demonstration. Really practical drivers are more complicated, to handle problems such as interrupts, DMA, I / O Port. These are truly difficult. Please see the next step, the actual situation is processed. How to write device drivers under the Linux operating system 3. Some specific issues in the device driver 1. I / O port. And hardware dend the I / O Port, the old ISA device often occupies the actual I / O Port, under Linux, the operating system does not shield I / O port, that is, any driver can operate any I / O port, which is easy to cause confusion. Each driver should avoid misuse ports. There are two important kernel functions to ensure that the driver does this. 1) Check_region (int io_port, int off offt) This function looks at the system's I / O table to see if there is any other driver occupies a certain section I / O port. Parameter 1: The base address of the IO port, the range of parameters 2: IO ports. Return value: 0 is not occupied, non-0, has been occupied. 2) Request_region (int io_port, int off it, char * devname) If this I / O port is not occupied, you can use it in our driver. Before use, you must register with the system to prevent occupying by other programs. After registering, you can see the IO port you registered in the / proc / Ioports file. Parameter 1: The base address of the IO port. Parameter 2: The range of IO ports. Parameter 3: Using the device name of this IO address. After registering the I / O port, you can use INB (), OUTB (), and a letter. In some PCI devices, the I / O port is mapped to a period of memory, and to access these ports is equivalent to accessing a memory. Regular, we have to get a physical address of a memory. In the DOS environment, the DOS operating system is because I think DOS is not an operating system, it is too simple, too unsafe) Just use the segment: The shift is OK. In Window95, 95DDK offers a VMM call_maplineArtophys to convert linear addresses to physical addresses. But what do you do in Linux? 2. Memory operation Dynamically opens up memory in the device driver, not using Malloc, but kmalloc, or directly applied with GET_FREE_PAGES.

转载请注明原文地址:https://www.9cbs.com/read-60978.html

New Post(0)