Compiling framework of Linux character Device Driver

Source: Internet
Author: User

Compiling framework of Linux character Device Driver

 

I. Concepts of Linux device driver

A system call is an interface between the operating system kernel and applications, and a device driver is an interface between the operating system kernel and machine hardware. The device driver shields the application from hardware details. In this way, the hardware device is only a device file, and the application can operate the hardware device like a common file. The device driver is part of the kernel and implements the following functions:

1. device initialization and release;

2. Transmit data from the kernel to the hardware and read data from the hardware;

3. Read the data transmitted by the application to the device file and send back the data requested by the application;

4. Detect and handle device errors.

In Linux, there are three main types of device files: character devices, Block devices, and network 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 hardware I/O usually happens immediately after it is sent, it uses a piece of system memory as a buffer. When a user's process can meet the user's requirements for device requests, it will return the requested data. If not, you can call the request function to perform actual I/O operations. Block devices are designed for disks and other slow devices to avoid excessive CPU time consumption.

It has been mentioned that user processes are dealing with actual hardware through device files. Each device file has its file attributes (c/B), indicating whether it is a character device or a block device? In addition, each file has two device numbers. The first is the master device number, which identifies the driver, and the second is the slave device number, which identifies different hardware devices that use the same device driver, for example, if there are two floppy disks, you can use the device number to distinguish them. The master device number of the device file must be the same as the master device number applied by the device driver during registration. Otherwise, the user process will not be able to access the driver.

It must be mentioned that when a user's process calls the driver, the system enters the core State and is no longer preemptive scheduling. That is to say, the system must perform other work 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 there is a long fsck.

Ii. instance analysis

Let's write a simple character device driver. Although it does not do anything, it can be used to understand how the Linux Device Driver works. Input the following C code into the machine and you will get a real device driver.

Because a user process deals with hardware through a device file, the operation on the device file is similar to some system calls, such as open, read, write, close ..., Note: It's not fopen or fread. But how can we associate system calls with drivers? This requires an understanding of 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 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 );
}

The name of each member in this structure corresponds to a system call. When a user process uses a system call to perform read/write operations on device files, the system calls the system to find the corresponding device driver through the master device number of the device file, then read the corresponding function pointer of the data structure, and then give the control to the function. This is the basic principle of Linux device drivers. In this case, the main task of writing a device driver is to write a sub-function and fill in the fields of file_operations.

Next we will start to write a subroutine.
# Include <Linux/types. h> Basic Type Definition
# Include <Linux/fs. h> file system use related header files
# Include <Linux/mm. h>
# Include <Linux/errno. h>
# Include <ASM/segment. h>
Unsigned int test_major = 0;
Static int read_test (struct inode * inode, struct file * file, char * Buf, int count)
{
Int left; user space and kernel space
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 prepared for the read call. When read is called, read_test () is called, which writes 1 to all user buffers. Buf is a parameter called by read. It is an address of the user's process space. However, when read_test is called, The system enters the core state. Therefore, you cannot use the buf address. You must use _ put_user (). This is a function provided by kernel to transmit data to users. There are also many similar functions. See. before copying data to a user space, you must verify whether the buf is available. The verify_area function is used. To verify whether the BUF can be used.

Static int write_test (struct inode * inode, struct file * file, const char * buf, int count)
{
Return count;
}
Static int open_test (struct inode * inode, struct file * file)
{
MOD_INC_USE_COUNT; the module counts to indicate that there is a device in the current kernel to load the kernel
Return 0;
}
Static void release_test (struct inode * inode, struct file * file)
{
MOD_DEC_USE_COUNT;
}

These functions are empty operations. The actual call does not do anything. They only provide function pointers for the following structure.
Struct file_operations test_fops = {?
Read_test,
Write_test,
Open_test,
Release_test,
};

The main body of the device driver can be said to be written. Now we need to embed the driver into the kernel. The driver can be compiled in two ways. One is to compile into the kernel, and the other is to compile into the module. If compiled into the kernel, it will increase the size of the kernel and change the source file of the kernel, it cannot be dynamically uninstalled, which 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); interface for device operations
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 compiled modules are transferred to the memory using the insmod command. Here, init_module only registers a character device to the system's character device table. Register_chrdev requires three parameters. The first parameter is the device number to be obtained. If it is zero, the system selects an unused device number and returns it. The second parameter is the device file name. The third parameter 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.
Void cleanup_module (void)
{
Unregister_chrdev (test_major, "test ");
}

When you use rmmod to uninstall a module, the cleanup_module function is called, which releases the table items that the character device test occupies in the system character device table.

A simple character device can be written. The file name is test. c.

Compile the following code:

$ Gcc-O2-DMODULE-d1_kernel _-c test. c-c indicates that the output name is specified and the. o file is automatically generated.

The file test. o is a device driver.

If the device driver has multiple files, compile each file according to the command line above, and then

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, you can see the device test in the/proc/devices file and its master device number. To uninstall the SDK, run:

$ Rmmod test

Next, create a device file.

Mknod/dev/test C major minor

C Indicates a character device. Major indicates the master device number, which is displayed in/proc/devices.

Use shell commands

$ CAT/proc/devices

You can get the master device number and add the above command line to your shell script.

Minor is set from the device number to 0.

We can now access our driver through the device file. Write a small test program.
# 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/N ");
Exit (0 );
}
Read (testdev, Buf, 10 );
For (I = 0; I <10; I ++)
Printf ("% d/N", Buf [I]);
Close (testdev );
}

Compile and run the program to see if all 1 is printed?

The above is just a simple demonstration. Real and practical drivers need to be complicated, such as interrupt, DMA, and I/O port. These are the real difficulties. The above provides a simple framework and principle for writing character device drivers. For more complex compilation, we need to carefully study the Linux Kernel Operating Mechanism and specific device operating mechanism. I hope you will have a good understanding of how to compile the driver program for LINUX devices.

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.