Write Driver (VFS) under Linux

Source: Internet
Author: User

Ext.: http://hi.baidu.com/firstm25/item/8fe022155e1fa78988a9568f

Summary: The device driver is the interface between the operating system kernel and the machine hardware. The device driver masks the hardware details for the application. Then how the driver writes to implement this interface feature is the focus of this article and introduces the writing details with a simple driver.

When the user process calls the driver, the system enters the kernel mentality, which is no longer preemptive dispatch. (The application usually takes place in the user state) that is, the system must return the driver's child function to do other work, that is, the driver cannot enter the dead loop.

The character device driver is written with information:

#define _no_version_

#include <linux/modules.h>

#include <linux/version.h>

Char Kernel_version[]=uts_release

This paragraph defines some version information, which is not very useful, but is essential. <linux/config.h> best to include. Because user processes are related to hardware through device files, the operation of the device files is simply a system call, such as open,read,write,close ..., (note, not fopen,fread,) but how do you relate the system call to the driver? This requires an understanding of a very critical data structure:

struct file_opertions{

Int (*seek) (struct inode*, struct file*, off_t, int);/* File Locator */
Int (*read) (struct inode*, struct file*, char, int);/* Read data */
Int (*write) (struct inode*, struct file*, off_t, int);/* Write Data */
Int (*readdir) (struct inode*, struct file*, struct dirent*, int);/* Read related directory */
Int (*select) (struct inlde*, struct file*, int, select_table*);/* Non-blocking device access */
Int (*ioctl) (struct inlde*, struct file*, unsigned int, unsigned long);
Int (*mmap) (struct inlde*, struct file*, struct vm_area_struct*);
Int (*open) (struct inlde*, struct file*);
Int (*release) (struct inlde*, struct file*);
Int (*fsync) (struct inlde*, struct file*);/* Forced synchronization */
Int (*fasync) (struct inlde*, struct file*);
Int (*check_media_change) (struct inlde*, struct file*);
Int (*revalidata) (dev_t dev);/* make device re-active */

}

where Read,write,open,close (release), the IOCTL is the most core, must be achieved.

The name of each member of the struct corresponds to a system call. The user process takes advantage of the system call when making a device file such as a read/write operation, the system calls through the device file's main device number to find the appropriate device registration program, then reads the corresponding function pointer of the data structure, and then gives control to the function. This is the basic principle of the Linux device driver work. Since this is the case, the main task of writing device drivers is to write sub-functions and populate the various fields of file_operatons.

The following is a simple character device driver writing method, the example program is not involved in the specific device, but a writing framework.

#include <linux/types.h>//linux Basic type definitions

#include <linux/fs.h>//File system related header files

#include <linux/mm.h>//memmory Management memory Management

#include <linux/errno.h>//error codes

#include <asm/segment.h>//Assembly documents

unsigned int test_major = 0; /* Define a Master device number (main device number, from device number in Linux device management) */

static int read_test (struct inode *inode, struct file *file, char *buf, int count)

{

/* This function corresponds to the implementation of read in File_opertions, and the function name is defined by itself. The inode is the device node, file is the device descriptor (open () automatically or after opening), BUF is the data buffer, and count is the number of data transfers. The "static" modifier function name here indicates that the function is valid only in this file. This function only implements the simple data copy function. */

int left;

if (Verify_area (Verify_write, buf, count) = =-efault)//Verify that the data in the cache is valid

Return-efault; Error code, included in <linux/errno.h>

for (Left=count; left>0; left--)

{

__put_user (1, buf, 1);

/* "__" means the kernel call function, which means to put data from the kernel space into the user space, the parameters are: padding number, user space, data volume. */

buf++;

}

return count;

}

This function is prepared for the read call. When read is called, Read_test () is called, which writes the user's buffer to all 1. BUF is a parameter to the read call. It is an address of the user process space. However, when Read_test is called, the system enters the kernel State (core space) and must be __put_user (), which is a function provided by kernel to transmit data to the user. There are also a number of functions similar to functions, referring to the kernel call interface functions. Before you can copy data to a user space, you must verify that the BUF space is available. This is used to Verify_area ().

static int write_test (struct inode *inode *inode, struct file *file, const char *buf, int count)

{

return count;

}

Write data function, specifically not implemented, directly return the count value.

static int open_test (struct inode *inode, struct file *file)

{

Mod_inc_use_count; Macros: number of registered modules plus 1

return 0; A return of 0 indicates success, as defined by the function itself.

}

This function is relatively simple, it does not involve the device files, only the number of modules plus 1.

static void Release_test (struct inode *inode, struct file *file)

{

Mod_dec_use_count; Number of modules minus 1

}

The above implementation of four functions, the latter three functions are empty operations, the actual call occurs when nothing is done, they only provide a function pointer to the file_operations struct.

Start registering the function you just wrote

struct File_operations test_fops =

/*file_operations struct name, Test_fops structure Object */

{

NILL,/*seek*/

Read_test,

Write_test,

NULL,/*test_readdir*/

NULL,/*test_mmap*/

Open_test,

Release_test,

NULL,/*test_fsvnc*/

NULL,/*test_fasync*/

/* Other locations are filled with empty nill*/

};

The body of the device driver can be said to be written, and now the driver is embedded in the kernel. The driver can be compiled in two ways. One is compiled into the kernel (kernel), the other is compiled into modules (modules) such as *.O files, if compiled into the kernel, will increase the size of the kernel, but also to change the kernel source files, and can not be dynamically unloaded, not conducive to debugging, so the recommended use of modular approach.

1. Registration Equipment:

Method one: Compile into kernel, use function init_module ()

int init_module (void)

{

int result;

result = Register_chrdev (0, "test", &test_fops);

/* Kernel function: Register a character-type device into the kernel. Parameters: Specify the device number (0: Indicates that the kernel was obtained from the main device number and returned to result), the device name, the device structure name */

if (Result < 0)

{

PRINTK (kern_info "test:: Can ' t get major number\n");

/* Print data in kernel-space driver, parameters: Print priority, print information */

return result;

}

if (Test_major = = 0) Test_major = result;

return 0;

}

Mode two: Compile the Load module mode, using the Insmod command, when the compiled module with the INSMOD command to call memory, the Init_module () function is called. Here, the Init_module () function only does one thing, registering a character device with the system's character device table.

Register_chrdev () requires three parameters, parameter one is the desired device number, if it is 0, the system will select an not occupied device number returned. Parameter two is the device file name, parameter three is used to register the driver to actually perform the function of the pointer. If the registration succeeds, the device's main device number is returned, which is unsuccessful and returns a negative.

2, unloading equipment:

void Cleanup_module (void)

{

Unregister_chrdev (test_major, "test");

}

When uninstalling a module with Rmmod, the Cleanup_module () function is called, which releases the table entries that the character device test occupies in the System character device table.

At this point, a simple character device can be said to write well, for the following narrative convenient, named file for test.c.

As mentioned above, the driver has been basically written and named TEST.c file, which is compiled below:

$ gcc-o2-dmodule-d__kernel__-C test.c

Note:-O 2 indicates an optimization level,-dmodule is compiled into a module,-d__kernel__ represents a module that is loaded into the kernel, and-C represents a build TEST.O file (version 2.4)

The resulting file TEST.O is a device driver. If the device driver has multiple files, compile each file on the command line above and then link

$ ld-r file1.o File2.o-o < module name >

The driver has been compiled and now installs it into the system.

$ insmod-f TEST.O

Note:-F means forced loading, TEST.O for module name

If the installation succeeds, you can see the device test in the/proc/devices file, and you can see its device number. To uninstall, run

$ rmmod Test

Next step to create a device node

$ mknod/dev/test C main device number from device number

Note: c denotes a character device, and the main device number is seen in the/proc/devices. You can get the main device number by printing all the devices with the shell command.

$ cat/proc/device

We can now access our drivers through the device files. Write a 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);

The/*open () function first assigns a file handle to an open file, FD, and then assigns an open file structure entry in the file table to the opened file, and then lets the file structure pointer of the file handle that the newly allocated files handle to the searched file structure, and then calls Namei () to get the corresponding file I node, Then let the file structure be associated with the I-node structure. The I node contains the main device number and child device number that the file represents, and what type of file it belongs to (such as normal files, directory files, character device files, block device files, pipeline files, and so on). */

if (Testdev = =-1)

{

printf ("Cann ' t open file \ n"); User space using printf (), kernel space using PRINTK ()

Exit (0);//Exit system

}

Read (Testdev, buf, 10);

/*read ()/write () returns the file handle FD from open () and obtains the I node of the file. Depending on the I node's attribute fields (I_pipe and I_mode), the corresponding read-write operation function is called. */

for (i=0; i<10; i++)

printf ("%d\n", Buf[i]);

Close (Testdev);

}

Compile run, print results should be output all 1

The above is just a simple demo. Really use the driver to be more complex, to deal with interruptions, dma,i/oport and other issues. This is the real difficulty.

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.