How to compile a Linux Device Driver

Source: Internet
Author: User

 

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.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.