By Roy G
Abstract: The development principle of Linux driver is introduced intuitively.
Preface
Linux
The idea is similar to other
Differences
Few functions are supported
It is not convenient to try
It is a variant of the UNIX operating system. It is used to compile the driver principle and UNIX System in Linux, but it has a large driver in the DoS or window environment. in the Linux environment, the driver is designed with simple ideas, convenient operations, and powerful functions. However, you can only rely on functions in the kernel, and some common operations must be compiled and tuned by yourself. I have developed a driver for a multimedia card developed by the lab over the past few weeks,
Gained some experience
Brennan's Guide to inline assembly, the Linux A-Z,
I would like to share it with Linux fans. If there are any mistakes, please correct me. The following text mainly comes from KHg, johnsonm's write Linux device driver, and Tsinghua BBS.
Device Driver
Corrected the test results.
Some of these materials are outdated and some are incorrect.
I
Linux Device Driver Concept
Interface between kernel and machine hardware
In the view of applications
Perform the same operations on hardware devices.
1.
2.
3.
4.
Block Device
Hardware
System calling is the interface between the operating system kernel and applications, and the device driver is the operating system. the device driver shields the application from hardware details, so that the hardware device is just a device file and the application can operate like a common file. the device driver is a part of the kernel and implements the following functions: Device initialization and release. transfers data from the kernel to the hardware and reads data from the hardware. read the data that the application sends to the device file and send back the data requested by the application. detects and processes device errors. in Linux, there are two main types of device files: character devices and character devices. the main difference between a character device and a block device is that when a read/write request is sent to a character device, the actual I/O usually happens immediately after it is sent, it uses a piece of system memory as a buffer,
When the user process requests the device to meet the user's requirements
Of
To wait
All have their file attributes.
Sn
Device Drivers for different hardware devices
They
Consistent
Preemptive Scheduling
Work
Is long
(
If not, the request function is called to perform actual I/O operations. block devices are designed for disks and other slow devices to avoid excessive CPU time consumption. as mentioned, a user process deals with actual hardware through device files. every device file is (C/B), indicating that the character device is still very strong? In addition, each file has two settings. The first is the master device number, which identifies the driver, and the second is the slave device number, which uses the same ID. For example, there are two floppy disks, it can be distinguished by the device number. the master device ID of the device file must be the same as the master device ID applied for by the device driver during registration. Otherwise, the user process will not be able to access the driver. finally, it must be mentioned that the system enters the core state when the user process calls the driver. that is to say, the system must perform other operations only after the sub-functions of your driver are returned. if your driver is in an endless loop, unfortunately you only have to restart the machine, and then fsck. // For more information about Hehe, see the next section)
II
. Instance profiling
You can understand
Get a real device driver
Let's write a simple character device driver. although it does not do anything, but through its Linux Device Driver working principle. input the following C code into the machine and you will. however, my kernel is 2.0.34. in earlier versions
Problems may occur.
# DEFINE _ no_version __
# Include <Linux/modules. h>
# Include <Linux/version. h>
Char kernel_version [] = uts_release;
, I have not tested. // Xixi
This section defines some version information
Some drivers must start
Although it is not very useful, it is also essential. johnsonm said <Linux/config. h>, but I don't think so.
Because user processes are dealing with hardware through Device Files
Some system calls
The Operation Method for device files is similar, such as open, read, write, close.... Note that it is not fopen, fread .,
But how can we associate system calls with drivers?
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 inode *, struct file *);
INT (* fsync) (struct inode *, struct file *);
INT (* fasync) (struct inode *, struct file *, INT );
INT (* check_media_change) (struct inode *, struct file *);
INT (* revalidate) (dev_t Dev );
}
Perform operations such
Find the corresponding Device Driver
Grant permission to this function
The main task of the device driver is to write sub-functions.
? This requires a very critical piece of data: The name of each member in this structure corresponds to a system call. when a user process uses the system to call the read/write operation, the system calls the master device number of the device file, reads the corresponding function pointer of the data structure, and then controls the data. this is the basic principle of Linux device drivers. in this case, write and fill in the fields of file_operations.
Relatively simple
Isn't it?
Next we will start to write a subroutine.
# Include <Linux/types. h>
# Include <Linux/fs. h>
# Include <Linux/mm. h>
# Include <Linux/errno. h>
# Include <ASM/segment. h>
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
Buffer write all
Buf
Read. when read is called, read_test () is called. is a parameter called by read. it is an address of the user's process space. but in read_test
Called
The system enters the core State. Therefore, the Buf address cannot be used, and _ put_user () must be used (),
This is
Function
A function provided by kernel to transmit data to users. there are also many similar functions. see <Linux/mm. h>. before copying data to a user space, you must verify whether the Buf is available.
This requires functions.
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;
}
Verify_area.
These functions are empty operations.
Provides function pointers.
. When the actual call occurs, nothing is done. They are only 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 we need to embed the driver into the kernel. Driver
You can compile it in two ways. One is to compile
Kernel, the other is compiled into modules ),
If it is compiled into the kernel, the size of the kernel will be increased and the source file of the kernel will be changed.
Dynamic uninstallation is not conducive to debugging. Therefore, we recommend that you use the module method.
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 ");
Return result;
}
If (test_major = 0) test_major = result;/* dynamic */
Return 0;
}
In use
Here,
Device.
If this parameter is set to zero, the system selects an unused device number and returns it. The second parameter is the device file name,
Parameter 3 is used to register the pointer of the function in which the driver actually performs the operation.
If the registration is successful, the system returns the device's master device number. If the registration fails, a negative value is returned.
When the insmod command transfers compiled modules to the memory, the init_module function is called. Only one thing in init_module is to register a register_chrdev character with the system's character device table. Three parameters are required. The first parameter is the device number to be obtained.
Void cleanup_module (void)
{
Unregister_chrdev (test_major, "test ");
}
In use
When rmmod uninstalls a module, the cleanup_module function is called, which releases the character device test.
The table items in the system character device table.
An extremely simple character device can be written, and the file name is called
Compile the following code
Test. C.
$ Gcc-O2-dmodule-d1_kernel _-C test. c
Get File
If the device driver has multiple files, compile each file according to the command line above, and then
Test. O is a device driver.
LD-r file1.o file2.o-O modulename.
The driver has been compiled. Now install it in the system.
$ Insmod-F test. o
If the installation is successful
The device test is displayed in the/proc/devices file,
And you can see its main device number.
Run
,.
$ Rmmod Test
Next, create a device file.
Mknod/dev/test C major minor
C
Use
It refers to the character device. Major is the master device number, which is seen in/proc/devices. Shell commands
$ CAT/proc/devices | awk "/$2 =" test "{print/$1 }"
You can get the master device number and add the above command line to your
In shell script.
Minor
We can now access our driver through the device file. Write a small test program.
From the device number, set it to 0.
# Include <stdio. h>
# Include <sys/types. h>
# Include <sys/STAT. h>
# Include <fcntl. h>
Main ()
{
Int testdev;
Int I;
Char Buf [10];
Testdev = open ("/dev/test", o_rdwr );
If (testdev =-1)
{
Printf ("cann' t open file ");
Exit (0 );
}
Read (testdev, Buf, 10 );
For (I = 0; I <10; I ++)
Printf ("% d", Buf [I]);
Close (testdev );
}
Compile and run the script to see if the script is fully printed.
The above is just a simple demonstration. A really practical driver needs to be much more complicated and needs to be handled, such as interruptions,
1?
DMA
And I/O port. These are the real difficulties. Please refer to the following section for more information.
How to compile a device driver in a Linux operating system
Roy G
3.
Specific issues in the device driver.
1. I/O port.
In
For any
Misuse the port.
I/O port is indispensable for dealing with hardware. The old ISA Device often occupies the actual I/O port. in Linux, the operating system does not block the I/O port. That is to say, any driver can operate on the I/O port, which can easily cause confusion. Every driver should be avoided by itself
There are two important kernel functions to ensure that the driver can do this.
1
) Check_region (INT io_port, int off_set) This function checks the system's I/O tables to see if other drivers occupy a certain segment of I/O Ports.
Parameter 1: The base address of the IO port,
Parameter 2: The range occupied by the I/O port.
Returned value: 0 is not in use. If it is not 0, it is already in use.
2
Before, you must register with the system to prevent other programs from occupying. After registration
) Request_region (INT io_port, int off_set, char * devname) if the I/O port is not occupied, you can use it in our driver. When using/proc/ioports
You can see
IO port.
Parameter 1: The base address of the IO port.
Parameter 2: The range occupied by the I/O port.
Parameter 3: The device name that uses this Io address.
After registering the I/O port, you can access it with letters such as INB () and outb () with confidence.
To access a piece of memory. Frequently, we need to obtain the physical address of a piece of memory. In
(Not to mention
It is too simple and insecure.) You only need to use the segment: offset. In
In some PCI devices, I/O ports are mapped to a memory segment. to access these ports, they are equivalent to dos, the DOS Operating System is because I think DOS is not an operating system at all. In window95, 95ddk
Provides
In
Vmm call _ maplineartophys to convert a linear address to a physical address. But what does Linux do?
2
Memory operations
Dynamically open the memory in the device driver, instead of using malloc, but kmalloc, or
Get_free_pages
Application page. Kfree or free_pages are used to release the memory. Note that,
Kmalloc
And other functions return a physical address! Malloc and other returned linear addresses! About
Kmalloc
The address is converted from
The driver cannot directly use a physical address but a linear address. But in fact
The returned physical address is a bit hard to understand: Since the linear address is completed by the physical worker CPU hardware, the operand of the Assembly command should be a linear address, kmalloc
The returned address is a physical address, and you can directly access the actual address.
In two ways, one is to disable paging in the core state, but this seems unrealistic; the other is
Ram, I think so
Linux
I do not know if it is correct. Please advise me.
The page Directory and page table items are designed to make the physical address equivalent to a linear address. My thoughts
The structure is occupied.
To put it bluntly, note that the maximum kmalloc value is-16, and the 16 bytes are the page descriptor kmalloc. For more information, see KHg. memory-mapped I/O Ports, registers, or RAM (such as memory) of hardware devices generally occupy f0000000
The above address space. Cannot be accessed directly in the driver.
Address after re ing.
Obtain the kernel function vremap
It resides in the memory and cannot be switched to files. However
This can be solved by sacrificing some system memory methods.
The specific method is as follows:
In addition, many hardware require a large continuous memory for DMA transmission. This memory requires that kmalloc can only open up to KB of memory. 32 MB memory, which is added to the startup parameters of Lilo. conf.
Mem = 30 m
In this way, Linux considers that your machine has only 30 mb of memory, and vremap exists in the remaining 2 MB.
Then you can
Remember to use
The DMA is used. Memory after vremap ing. unremap is released when not used; otherwise, page tables are wasted.
3
Interrupt handling
Like handling I/O ports, to use an interrupt, you must first register with the system.
Int request_irq (unsigned int IRQ,
Void (* handle) (INT, void *, struct pt_regs *),
Unsigned int long flags,
Const char * Device );
IRQ:
Is the interruption of the application.
Handle
: Interrupt processing function pointer.
Flags
: A sa_interrupt request is interrupted quickly, and 0 is interrupted normally.
Device
: Device name.
Interrupted.
If the registration is successful, 0 is returned. In this case, you can view your request in the/proc/interrupts file.
4
Some common problems.
Then,
Sometimes timing is important for hardware operations. However, if you use C language to write some low-level hardware operations, GCC will usually optimize your program, so that the timing is wrong. What if I write it in a sink,
Gcc
The solution is to disable optimization. Of course, this can only be a part of your own code. If all the code
Are not optimized, and you will find that the driver cannot be loaded at all. This is because
Used
.
The Assembly Code will also be optimized unless you modify it with the volatile keyword. Some of the most secure GCC extension features, which must be reflected only after the optimization options are added
I am very grateful. I have been
I have not found any suitable tool for kernel debugging. Anyone who knows this can tell me that printk can print debugging information, but it can also be used together.
I still don't quite understand.
There is still much content about the device driver, such as the wait/Wake-Up Mechanism and block device writing.
Thank you for your criticism.