Excerpt from: http://blog.chinaunix.net/uid-20799298-id-99675.html
The original text is writing device driver in linux:a Brief tutorial.
This article focuses on three examples to explain the Linux driver, so that novice quickly, from the code layer to understand what is the Linux device driver.
This article is a note, in general translation of the first two parts of the article, namely the first two examples, these two examples can be successfully run correctly.
File: Writing device drivers in linux.pdf
Size: 216KB
Download: Download the required knowledge
-C language Programming
-microprocessor programming. Have a certain understanding of how the processor works, such as memory management, interrupts, etc.
user space and kernel space
When writing device drivers, it is important to understand the difference between "user space" and "kernel space."
-Kernel space. The Linux kernel simply and efficiently manages the hardware of the machine and provides users with a simple and
standard programming interface. Similarly, the kernel, especially the driver in the kernel, is the
bridge or interface between the user/programmer and the hard part. Any routines or functions of the kernel (such as modules, drivers) are part of the kernel
space.
-User space. User programs, such as Unix shells or other GUI applications, such as
KPresenter, belong to user space. Obviously, these applications have to deal with hardware. But
they do not and directly manipulate the hardware, but are implemented through the functions provided by the kernel.
interface functions between user space and kernel space
The kernel provides a series of routines or functions for user space that the user's application uses to interact with the hardware
。 Typically, in a UNIX or Linux system, this conversation is read or written to a file through a function or subroutine. Reason is from
From a user's point of view, UNIX devices are files.
On the other hand, Linux also provides a series of functions or subroutines in the kernel space to perform the underlying and hardware interactions
, and allows information to be passed from the kernel to user space.
Typically, each user-space (device or file-allowed) function can find a similar
Functions, (allowing information to be passed from the kernel to user space, and vice versa) interface functions between kernel space and hardware devices
There are many functions in kernel space to control hardware or interact information between the kernel and the hardware. First driver: loading and removing drivers in user space
Now we're going to show you how to do the first driver, in the kernel as a module
Create a new file nothing.c the following include
Module_license ("Dual BSD/GPL");
After the 2.6.x version of the kernel, the compiler module will be slightly more complex. First, there needs to be a complete, compiled, internal
Nuclear source tree. In the following text, you will assume that 2 is used. 6. 8 version of the kernel.
Second, a makefile file is needed, in this case the makefile file is named Makefile, which reads as follows:
Obj-m: = NOTHING.O
Unlike previous versions of the kernel, the kernel used to compile the module now needs to be the same as the kernel that the module will load.
To compile the above file, you can use the command:
Make-c/usr/src/kernel-source-2.6.8 m=pwd Modules
This extremely simple module belongs to the kernel space, and once it is loaded, it is part of the kernel space.
In user space, you can load it using the following command, which requires root permissions:
Insmod Nothing.ko
Insmod This command is used to load modules for the kernel. Although now that we have loaded the Nothing.ko module,
But this module, after all, is of no use.
You can check to see if the Nothing.ko has been successfully loaded by looking at the loaded modules in the system
Lsmod
Finally, you need to uninstall the module using the following command:
Rmmod Nothing
Using Lsmod again, you can see that the Nothing module is gone. "Hello World" drive: loading and removing drivers in kernel space
When a module device driver is loaded into the kernel, some initial work is performed, such as reserving the device,
RAM, reserving interrupts, reserving input/output ports, etc.
This work can be done in kernel space and must have two functions: Module_init and
Module_exit; they correspond to the insmod and Rmmod commands of user space. In short, the user commands Insmod and
Rmmod uses kernel-space functions module_init and Module_exit.
Look at a classic program HELLO world:
hello.c
#include
#include
#inlucde
module_license ("Dual BSD/GPL");
static int hello_init (void)
{
printk ("<1> Hello world!\n");
return 0;
}
static void Hello_exit (void)
{
printk ("<1> Bye, Cruel world!\n");
Module_init (hello_init);
Module_exit (Hello_exit);
Where the Hello_init and Hello_exit functions can take any name, but to load and remove features is easier to know
They are passed as arguments to function module_init and module_exit.
The PRINTK function is very similar to the printf function, but PRINTK works only in the kernel. <1> indicates printing information
is the highest priority (the lower the number, the higher the priority). In this way, not only can you see this in the kernel system log
Print the information, but also receive the print information at the system console.
You can compile this module with the previous command, just add the module name to the makefile file:
Obj-m: = NOTHING.O hello.o
The rest of this article will be makefile as an exercise for the reader. A complete makefile file can be compiled
All the sample modules in this tutorial.
When a module is loaded or unloaded, information printed through PRINTK will appear in the system console. If you print
Information does not appear in the terminal, you can use the DMESG command or view the System log file (cat
Var/log/syslog) See print information. a complete driver "memory": the initial part of this drive
Next, we'll describe how to build a complete device driver: memory.c. You can read one character from the device,
You can also write a character to it. This device doesn't really make sense, just because it's a complete
Driver and then describes it as an instance. It's easy to implement because it's not a real
The interface of the hardware device (except the computer itself).
In this drive, you want to add several #inclu#include include include include, which appear frequently in the device driver Include include, include, include, include
Module_license ("Dual BSD/GPL");
int Memory_open (struct inode *inode, struct file *filp);
int memory_release (struct inode *inode, struct file *filp);
ssize_t memory_read (struct file *filp, char *buf, size_t count, loff_t
*f_pos);
ssize_t memory_write (struct file *filp, char *buf, size_t count, loff_t
*f_pos);
void Memory_exit (void);
int memory_init (void);
struct File_operations memory_fops = {
Read:memory_read,
Write:memory_write,
Open:memory_open,
Release:memory_release
};
Module_init (Memory_init);
Module_exit (Memory_exit);
After the int memory_major = 60;r *memory_buffer, a few of the functions to be defined later are declared. Declared in the definition of the file_operations structure
Several functions that are commonly used to manipulate files. These will be described in detail later. Next, the kernel declares the initialization and
Exit Function-used when loading and unloading modules. Finally, declare the driven global variable: Memory_major represents the drive
The main drive number of the Memory_buffer, which points to an area of memory used to store the drive data. "Memory" driver: The connection of a device with its files
In Unix and Linux, accessing devices from user space is the same as accessing files. These device files are usually located in the/dev eye
Recorded.
Associating a normal file with a device file requires two digits: Major number and minor
Number The kernel uses major number to link a file to its driver. and minor number is for equipment
department to use.
To do this, the creation of a file (which will be used to access a device driver) must be typed using root as the following
Command:
Mknod/dev/memory C 60 0
In the above command, C means to create a character device that has a primary drive number of major 60, the secondary drive
Minor number is 0.
For this drive, in order to link the kernel space to the corresponding/dev file, you need to use the
Register_chrdev function. Call this function to use three parameters: Major number,
A string that represents the name of the module, a file_operations structure.
It is invoked in the following manner when the module is installed:
int memory_init (void)
{
int result;
result = Register_chrdev (Memory_major, "Memory", &memory_fops);
if (Result < 0) {
printk ("<1>memory:can ' t obtain major number%d\n",
memory_major);
return result;
}
Memory_buffer = Kmalloc (1, gfp_kernel);
if (!memroy_buffer) {result
=-enomem;
goto fail;
}
memset (Memory_buffer, 0, 1);
PRINTK ("<1> inserting Memory module\n");
return 0;
Fail
Memory_exit ();
return result;
}
Note the use of the Kmalloc function. This function allocates a piece of memory in the kernel space for the device-driven buffer.
It is used in the same way as the famous malloc function. Finally, if the registration master driver fails or the memory allocation fails,
This module will also fail. "Memory" Driver: Remove driver
In order to remove modules from the Memory_exit function, you need to use the Unregsiter_chrdev function. It will be the kernel
Release the corresponding Master drive
void Memory_exit (void)
{
Unregister_chrdev (Memory_major, "Memory");
if (memory_buffer) {
kfree (memory_buffer);
}
PRINTK ("<1> removing Memory module\n");
Restores a clean kernel when the drive is removed, freeing the drive buffer at the same time in this function. "Memory" drive: Open a device like a file
The kernel space corresponds to the open file (fopen) in user space, which is open: when calling Register_chrdev
A file_operations structure is used, and open is the member of this structure. The arguments for the open function are:
An inode structure that passes to the kernel the phase of the main drive number major number and the secondary drive numbers minor
A file structure that includes several different functions of the operation file. But this article does not treat these functions
For detailed introduction.
When a file is opened, it is usually necessary to initialize the drive variable or reset the device. But in this case, this
Some did not do these jobs.
The Memory_open function is as follows:
int Memory_open (struct inode *inode, struct file *filp)
{
return 0;
"Memory" driver: Shutting down devices like closing files
The corresponding to the closed file (fclose) in user space is release: When you call Register_chrdev, you use a
A file_operations structure in which release is a member of the structure. In this case, it corresponds to the
The Memory_release function, similar to the previous one, also has two parameters: the inode structure and the file structure.
When a file is closed, it is usually necessary to release the memory used and any variables that are off the chain when the file is opened. However, the same
Because this example is very simple, these jobs are not done here.
The Memory_release function is as follows:
int memory_release (struct inode *inode, struct file *filp)
{
return 0;
} "Memory" drive: Read devices
Similarly, fread, which corresponds to the user-space read file, is used here: It is also a file_operations structure.
Rapporteur Here it corresponds to the Memory_read function. Its parameters are: a file structure; a buffer buf, the user
Space reads data from the buffer; a counter count records the bytes transmitted; Finally, there are f_pos to
Indicates where to start reading from the file.
In this case, the Memory_read function uses the function copy_to_user to send a byte from the drive buffer to the user
ssize_t memory_read (struct *file filp, Char *buf,
size_t count, loff_t *f_pos)
{
Copy_to_user (BUF, Memory_buffer, 1);
if (*f_pos = = 0) {
*f_pos = 1;
return 1;
} else {return
0;
}
The F_pos will change as well. If you read from the beginning of the file, F_pos increments by 1 and returns the correct
The number of bytes read, that is, 1. If you do not read from the beginning of the file, the end flag 0 of the file will be returned because the file
No data. "Memory" drive: Write device
Like fwrite, kernel space has write: It is a member of the file_operations structure. In this case, the
Memory_write, with the following parameters: a file structure; buf buffer for user space write; count,
The number of bytes that the counter record writes to the data; F_pos, written ssize_t memory_write (struct file *filp, char *buf,
size_t count, loff_t *f_pos)
{
Char *tmp;
TMP = BUF + count-1;
Copy_from_user (Memory_buffer, TMP, 1);
return 1;
}
Copy_from_user transfers data from user space to kernel space. complete "Memory" drive
Once all the code is joined, a complete memory drive MEMORY.C is formed:
Before using this driver, it is of course necessary to compile it first, similar to the previous method. Load module:
Insmod Memory.ko
It is convenient to remove the protection of the device:
chmod 666/dev/memory
If all goes well, there will be a device/dev/memory that can write a string or character, and it will save
The last one in a stored string or multiple characters. You can do things like this:
Echo-n abcdef >/dev/memory
Use cat to check the contents of this device:
Cat/dev/memory
The saved character does not change unless the overwrite is rewritten or the module is unloaded.
Report:
Example 2 Memory drive experiment:
Code MEMORY.C
#include <linux/init.h>//#include #include <linux/module.h> #include <linux/kernel.h> #include < linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/proc_fs.h> #include <linux/fcntl.h>//#include #include <linux/uaccess.h> module_license ("
Dual BSD/GPL ");
int Memory_open (struct inode *inode, struct file *filp);
int memory_release (struct inode *inode, struct file *filp);
ssize_t memory_read (struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write (struct file *filp, char *buf, size_t count, loff_t *f_pos);
void Memory_exit (void);
int memory_init (void);
struct File_operations memory_fops = {read:memory_read, write:memory_write, Open:memory_open,
Release:memory_release};
Module_init (Memory_init);
Module_exit (Memory_exit);
int memory_major = 60;
Char *memory_buffer;
int Memory_init (void) {int result; result = Register_chrdev (Memory_major, "Memory", &memory_fops);
if (Result < 0) {PRINTK ("<1>memory:can ' t obtain major number%d\n", memory_major);
return result;
} Memory_buffer = Kmalloc (1, Gfp_kernel);
if (!memory_buffer) {result =-enomem;
Goto fail;
} memset (Memory_buffer, 0, 1);
PRINTK ("<1>inserting memory module\n");
return 0;
Fail:memory_exit ();
return result;
} void Memory_exit (void) {Unregister_chrdev (memory_major, "Memory");
if (Memory_buffer) Kfree (Memory_buffer);
PRINTK ("<1>removing memory module\n");
int Memory_open (struct inode *inode, struct file *filp) {return 0;}
int memory_release (struct inode *inode, struct file *filp) {return 0;}
ssize_t memory_read (struct file *filp, char *buf, size_t count, loff_t *f_pos) { Copy_to_user (BUF, Memory_buffer, 1);
if (*f_pos = = 0) {*f_pos = 1;
return 1;
else return 0;
} ssize_t memory_write (struct file *filp, char *buf, size_t count, loff_t *f_pos) {char *tmp;
TMP = BUF + count-1;
Copy_from_user (Memory_buffer, TMP, 1);
return 1; }
Makefile:
Obj-m: = memory.o
Kerneldir: =/lib/modules/(shelluname−r)/buildpwd:= (shell uname-r)/build PWD: = (shell PWD)
Modules
(make) −c (make)-C (Kerneldir) m=$ (PWD) modules
Compiling: Make
There are memory.ko in the makefile, which is the target module to use
Loading: sudo insmod./memory.ko
View dmesg information: DMESG | Tail-n 1
[10911.945739] inserting memory module
Change operation device file permissions: sudo chmod 666/dev/memory
Write data to drive: Echo-n ABCDEFG >/dev/memory
To view the data saved in the drive:
[linux@ ~] cat/dev/memoryg[linux@] cat/dev/memory g[linux@ ~]
This is the last data to be written.
Uninstall drive:
[linux@ ~] sudormmodmemory[linux@] sudo rmmod memory [linux@ ~] DMESG | Tail-n 2
[10911.945739] inserting memory module
[11155.809076] removing memory module
—————————-the experiment is complete.
Analysis
There are five main functions in the above code that focus on the following:
Register_chrdev
Unregister_chrdev
Copy_to_user
Copy_from_user
Kmalloc
/*
* Success: Return 0
* Failed:-einval indicates that the main device number for the request is illegal (probably the main device number is greater than the maximum device number)
*-ebusy indicates that the application's main equipment number has been used for other devices
* If the dynamic assignment succeeds, this function returns the main device number
*
*/
static inline int Register_chrdev (
unsigned int major,//device driver applies the main device number to the kernel, if 0 the system dynamically assigns a main device number
const char *name,//device name
const struct File_operations *fops//entry points for each call
);
static inline void Unregister_chrdev (
unsigned int major,
const CHAR *name
);
ARCH/X86/LIB/USERCOPY_32.C
/**
* copy_to_user:-Copy a block of data into user spaces.
* @to: Destination address, in user space.
* @from: Source address, kernel space.
* @n:number of bytes to copy.
*
* context:user context only. This function could sleep.
*
* Copy data from kernel spaces to user spaces.
*
* Returns number of bytes that could is copied.
* On success, this'll be zero.
*/
unsigned long copy_to_user (void __user *to, const void *from, unsigned long n);
/**
* Copy_from_user:-Copy a block of data from user spaces.
* @to: Destination address and kernel space.
* @from: Source address, into user space.
* @n:number of bytes to copy.
*
* Context:user context. This function could sleep.
*
* Copy data from user spaces to kernel spaces.
*
* Returns number of bytes that could is copied.
* On success, this is zero.
*
* If Some data could not is copied, this function would pad the copied
* Data to the requested size using zero bytes.
*/
unsigned long _copy_from_user (void *to, const void __user *from, unsigned long n);
Dynamically opening up memory in the kernel
void *kmalloc (size_t size, int flags);
Size: The size of the memory to allocate
Flags: Assigning flags to control the behavior of Kmalloc in several ways