Finishing--linux character device driven development base __linux

Source: Internet
Author: User
Tags function prototype reflection volatile
Knowledge Collation –linux character device Driven Development Foundation

Linux driver I understand: encapsulates the operation of the underlying hardware and provides an operational interface to the upper application

The article in some places did not post the corresponding function prototype, please check it yourself, or use souceinsight to search its own kernel source tree (I use this way to refer to the use of functions)

simple device-driven development basics, the driver framework is not considered. Article according to GFM typesetting Https://github.com/TongxinV

Development Environment Construction: Kernel source tree, NFS mount roofs, development and configuration of the corresponding Bootcmd and Bootargs

Drive the development of the steps : 1. Drive source code, makefile file compilation, compile, 2.insmod load module, test, rmmod unload module.

Bootcmd and Bootargs

1. The setup Bootcmd enables the Development board to download the Zimage
  set Bootcmd ' tftp 0x30008000 zimage;bootm 0x30008000 ' that was compiled by TFTP's own kernel source tree
( Note: bootcmd=movi read kernel 30008000; Movi read Rootfs 30b00000 300000; Bootm 30008000 30b00000 Such bootcmd are used when starting the kernel from Inand)

2. Set up Bootargs to mount the Development Board from NFS Rootfs (kernel configuration Remember to turn on Enable NFS form ROOTFS)
setenv Bootargs Root=/dev/nfs nfsroot=192.168.1.141:/root/x210_porting/rootfs/rootfs ip= 192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off  

compile makefile files that drive source code

#ubuntu的内核源码树, if you want to compile the modules installed in Ubuntu, open these 2
#KERN_VER = $ (Shell uname-r)
#KERN_DIR =/lib/modules/$ (kern_ver)/ 

Build # Development Board of the Linux kernel source tree directory, according to their own in the source tree storage Directory modified
kern_dir =/root/driver/kernel
obj-m   + = MODULE_TEST.O     //-m says we're going to compile module_test.c into a module
                           //-y means we're going to compile the module_test.c link into zimage all
: Make-c
    $ (kern_dir) m= ' PWD ' modules 
                           //-c means to go into a directory to compile
                           //' pwd ': means to execute the contents of the two ' number as a command
                           //m= ' pwd ' means to save the PWD printed content, The goal is to be able to return to the original directory after the
                           //modules is really used to compile the module command, in the other parts of the kernel has been defined
CP:                                 
    CP *.ko/root/porting_x210/rootfs/ Rootfs/driver_test

. Phony:clean   //treat clean as a pseudo target
:
    make-c $ (kern_dir) m= ' pwd ' modules

Summary: The makefile of the module is very simple, itself can not complete the compilation of the module, but through the make-c into the kernel source tree to borrow the kernel source of the system to complete the module compiled link.

Knowledge collation Linux character device driver development base character device base 1 from one of the simplest modules source code the principle of character device driver practice give empty module Add driver Shell application How to call the driver character device base 2 Add the data exchange between the application and driver of the read-write interface How to manipulate the hardware and the bare metal code in the driver different static mapping and dynamic mapping static mapping operation LED dynamic mapping operation led character Equipment base 3 character device driver register new interface CDEV structure Cdev and related operation function The reflection-style processing method using Cdev_alloc Character Device driver registration code Analysis Register_chrdev register_chrdev_regionalloc_chrdev_region Summary character device driver How to call 1 in the kernel automatically create and delete device files about SYS file system Summary of read-write register interface provided by kernel

Character Device Basics 1 from one of the simplest module source.

#include <linux/module.h>       //module_init  module_exit
#include <linux/init.h>         //__init   __exit
//module installation function
static int __init chrdev_init (void)
{   
    PRINTK kern_info HelloWorld init\n ");
    return 0;
}
module unload function
static void __exit chrdev_exit (void)
{
    printk (kern_info "Chrdev_exit HelloWorld exit\n");
Module_init (chrdev_init);
Module_exit (chrdev_exit);

MODULE_XXX This macro action is used to add module description information
module_license ("GPL");              Describe a license for a module

(1) Use PRINTK to print debugging information, PRINTK can set the printing level. Common Kern_dbug-8\kern_info-7, the current system also has a level of printing information 0-7 (such as the current system printing information level of 4, then PRINTK print less than level 4).

To view the current system print information level: CAT/PROC/SYS/KERNEL/PRINTK: Echo 8 >/PROC/SYS/KERNEL/PRINTK

(2) The header file contained in the driver source code is not the same as the header file contained in the original application programming program. The header files included in application programming are the header files of the application tier and are brought by the compiler of the application (for example, GCC's header file path is/usr/include, which is unrelated to the operating system). Driver source code is part of the kernel source, driving source of the header file is in fact the kernel source directory under the Include directory of the header file.

(3) function modifier __init (underlined in front of this is a function for the kernel), is essentially a macro definition, in the kernel source code there is #define __INIT xxxx. The function of this __init is to put the functions that he modifies into the. Init.text segment (the function is, by default, placed in the. Text section).

#define __init  __section (. init.text) __cold notrace ├──
                      #define __section (S) __attribute__ ((__section__ (#S)) )              

All of these functions in the entire kernel will be placed by the linker according to the link script into the. Init.text segment, so all the kernel modules ' __init modified functions are actually unified together.
When the kernel starts, unification loads the module installation functions in the Init.text segment, which is released to save memory after loading. __exit the same. character device driver working principle

It can be understood that a module is a mechanism that drives the use of modules to implement

The whole system working principle : (1) Application layer->api-> device drive-> hardware, (2) Api:open, read, write, close, etc. (3) The driver source code provides real open, read, write, function entities such as close

file_operations Structural Body (another one for the attribute way to speak again): (1) The elements are mainly function pointers, used to hook up the entity function address, (2) each device driver needs a variable of the structure type; (3) A variable that provides the type of the struct when the device driver registers with the kernel.

registering character device driver Register_chrdev:

    static inline int Register_chrdev (unsigned int major, const char *name,const struct file_operations *fops)
    {
        Retu RN __register_chrdev (major, 0, 256, name, fops);
    }

(1) Role, drive to the kernel to register their own file_operations structure, the registration process is essentially the registration of the driving information stored in the kernel is specifically used to store registered character device-driven array in the corresponding location
(2) Parameters: Equipment Number Major–major 0 in to indicate that to allow the kernel to help us automatically allocate a suitable blank unused primary device, the kernel will return the assigned main device number if it is successfully allocated;
(3) inline and static
Inline: When the function is defined in the header file, if your header file is included with two and more than two functions, the link will be an error. The role of inline is to solve this problem, to expand in situ and to achieve static inspection. Another reason is that the function itself is relatively short.

how the kernel manages character device drivers

(1) The kernel uses an array to store the registered character device drivers; (2) Register_chrdev The driver information we want to register (fops structure address) is stored in the corresponding position in the array; (3) cat/proc/ Devices view of registered character device driver (and block device driven) character device driver code practices in the kernel-add a driver shell to an empty module

Core work: Defining File_operations type variables and their element padding, registering drivers

Simple Driver Example

MODULE_TEST.C
    ├── module installation function xxx
    │   └── registered character device driver Register_chrdev (mynmajor, MyName, &test_module_fops)
    ├── Module installation function yyy
    │   └── logoff character device driver Unregister_chrdev (mynmajor, myname)
    │   
    ├──module_init (module setup function xxx);
    ├──module_exit (module unload function yyy);
    │     
    └──module_license ("GPL");
#include <linux/module.h>//module_init module_exit #include <linux/init.h>//__init __exit #include

<linux/fs.h>//File_operations does not write an error: XXX has initializer but incomplete type #define MYNMAJOR #define MYNAME "Test_chrdev"//file_operations the entity of the function pointer filled in the struct variable, the format of the function follows the static int test_chrdev_o Pen (struct inode *inode, struct file *file) {///This function should actually place the hardware operation code section of the device open//But now we can not write so much, so we use a PRINTK print a message to do
      Representative PRINTK (kern_info "test_module_open\n");
return 0; 
    static int test_chrdev_release (struct inode *inode, struct file *file) {PRINTK (kern_info "test_chrdev_release\n");
return 0;          ///Customize a file_operations struct variable and populate the static const struct File_operations test_module_fops = {. Owner = This_module, Lease = test_chrdev_release,//corresponds to close, why not close. See later releaseand close to the difference between the explanation}; /*********************************************************************************///module installation function static int __init

    Chrdev_init (void) {PRINTK (kern_info "Chrdev_init HelloWorld init\n");     Register the character device driver int ret =-1 in the function that the Module_init macro calls;
    Register_chrdev return value is int type ret = Register_chrdev (Mynmajor, MyName, &test_module_fops); Parameters: Main device number major, device name, file_operations structure variable pointer, note is pointer, so add address character//after check return value if (ret) {PRINTK (kern_err  "Register_chrdev fial\n"); Note that this is no longer used kern_info return-einval;
    Many of the error number defined in the kernel are not return-1 in the previous way, and the minus sign should be added.
    } PRINTK (Kern_err "Register_chrdev success...\n");
return 0;
    }//module unload function static void __exit chrdev_exit (void) {PRINTK (kern_info "Chrdev_exit HelloWorld exit\n"); In the function of Module_exit macro call to unregister character device driver//experiment, when we do not write things here, rmmod after Lsmod view is really gone, but Cat/proc/device found that the device number is still occupied UNREGISTER_CHR  Dev (mynmajor, myname);
Parameter on two//check return value ... returns 0; }
/*******************/Module_init (Chrdev_init);        Call Module_exit (chrdev_exit) when insmod;            Rmmod Call//MODULE_XXX This macro action is used to add module description information module_license ("GPL"); Describe a license for a module
how the application invokes the driver

Drive Device file creation : (1) What is device file: used to index drive; (2) The key information of equipment file is: equipment number = Main equipment number + minor equipment number; (3) Create device files using Mknod: mknod/dev/xxx C Main equipment number of secondary equipment (c indicates the device file type to be created is a character device), (4) using LS xxx-l to view the device file, you can get the primary and secondary device number corresponding to this device file.

Note: It is not possible to always use Mknod to create device files and to automatically generate and delete device files. The Linux kernel has a mechanism –udev (embedded in the Mdev), followed by a fine talk

A simple example of an application app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h >        //man 2 Open View header file What
#define file    "/dev/test"//Mknod the device filename created just now double quotes don't leak
int main (void)
{
    int fd =-1;
    FD = open (FILE, O_RDWR);
    if (FD < 0) {
        printf ("Open%s error.\n", FILE);
        return-1;
    }
    printf ("Open%s success. \ n ", FILE);
    Read and write files 
    ...
    Closes file close
    (FD);  
    return 0;
}

Character Device Basics 2 Add read-write interface (data exchange between application and drive)

Suit

To add to the driver:

    ssize_t test_chrdev_read (struct files *file, char __user *buf, size_t size, loff_t *ppos)//struct file *file: Point to the document we want to manipulate; con St Char __user *buf: buf of user space
    {
        printk (kern_info "test_chrdev_read\n");
        ......
    Static ssize_t test_chrdev_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    {
       PRINTK (K Ern_info "test_chrdev_write\n");
       ......

To add to the application:

Read/write file
read (fd,buf,100);//The last parameter is the number of bytes to read
write (FD, "HelloWorld",);
......

Test: Before the test rmmod the module before the experiment to unload, Lsmod confirm Cat/proc/devices insmod, and equipment files to rm/dev/xxx delete equipment files, install the module and then mknod to re-establish equipment files. Then execute the application to view the print information (we'll talk about how to make it less troublesome later)

Data exchange between application and drive:

The essence of the writing function is to copy the data from the application layer into the kernel, and then write it in the correct way to the hardware, and to complete the operation, there are
two kinds of contact: Copy_from_user, Copy_to_user and Mmap.

Complete the Write and read functions:

The return value definition for the Copy_from_user function is somewhat different from the general rule. The return value returns 0 if it succeeds, and returns the number of bytes remaining that have not been successfully replicated if unsuccessful.

MODULE_TEST.C:

Char kbuf[100];//a buf of kernel space ... static ssize_t test_chrdev_write (struct file *file, const char __user *buf, size_t cou
    NT, loff_t *ppos) {int ret =-1;
    PRINTK (kern_info "test_chrdev_write\n");     Use this function to copy the contents of the UBUF from the application layer to a buf in the drive space (kernel space)//memcpy (KBUF,UBUF);
    No, because 2 are not in an address space Menset (kbuf, 0, sizeof (KBUF));
    ret = Copy_from_user (kbuf,ubuf,count);
        if (ret) {PRINTK (kern_err "Copy_from_user fail\n"); return-einval;//in the real driver does not replicate success there should be some error-correcting mechanism, here we simply point} PRINTK (Kern_err "copy_from_user success.
    \ n "); Here we have successfully transferred the user space data to the kernel space//real driver, after the data is copied from the application layer to the driver, we need to write the hardware to do the hardware according to this data./So the following should be the code to operate the hardware ... return
0;
    } ssize_t test_chrdev_read (struct file *file, char __user *buf, size_t size, loff_t *ppos) {int ret =-1;

    PRINTK (kern_info "test_chrdev_read\n");
    ret = Copy_to_user (ubuf,kbuf,size);
        if (ret) {PRINTK (kern_err "Copy_to_user fail\n");
 return-einval;//is not replicated in the real driver. There should be some error-correcting mechanisms here.   } PRINTK (Kern_err "copy_to_user success.

    \ n ");
return 0; }

App.c

......
Read and Write file
write (fd, "HelloWorld", ten);
Read (fd,buf,100);
printf ("read out:%s \ n", buf);
Print Results: ...
How to manipulate hardware in a drive (unlike bare-metal code)

In PowerPC, m68k and arm systems, peripheral I/O ports have the same physical address as memory, and the physical address of the peripheral I/O memory resources is known and determined by the hardware design. Linux drivers are not able to access the I/O memory resources directly through physical addresses, but must map physical addresses to the kernel virtual address space.

or the hardware:

(1) The physical principle of the hardware is unchanged, (2) The Hardware Operation Interface (register) is unchanged, and (3) the hardware operation code is unchanged.

Where different:

(1) The register address is different. The original is the physical address directly, now need to use the physical address in the kernel virtual address space corresponding to the virtual address. The physical address of the register is determined by the CPU design time and is found from the datasheet.
(2) different programming habits. In bare-metal habits directly using function pointers to operate register address, and kernel used to use encapsulated IO Read and write functions to operate registers, in order to achieve the greatest degree of portability.

The kernel's virtual address mapping method:

(1) Why the need for virtual address mapping: The kernel is running in its own virtual address space
(2) The kernel has 2 sets of virtual address mapping methods: Dynamic and Static
(3) Static Mapping method features:
    kernel porting in the form of code hard coding, If you want to change the source code after you have to recompile the kernel to
    create a static map when the kernel is started, to destroy when the kernel shuts down, the middle is always valid
    for the transplanted kernel, you don't need him there
(4) Characteristics of the dynamic mapping method:
    Dynamic mapping, use, and destruction mapping mappings
    are a short-term temporary requirement for drivers to be dynamically built.

How to select a virtual address mapping method:

(1) 2 mappings are not exclusive, can be used at the same time
(2) static mapping is similar to the C language global variables, dynamic way similar to the C language malloc heap memory
(3) The advantage of static mapping is high execution efficiency, the disadvantage is always occupy the virtual address space The advantage of dynamic mapping is the use of virtual address space on demand, the disadvantage is that before and after each use requires code to build maps & Destroy mappings (also learn to use those kernel functions)
No absolutely good absolutely bad
Static mappings and dynamic mappings

In arm storage systems, the Memory Management Unit (MMU) is used to map virtual addresses to actual physical addresses. The implementation process of MMU is actually a look-up table mapping process. Creating a page table is an indispensable step in implementing the MMU function. The page table is in the system's memory, and each item in the page table corresponds to a mapping of a virtual address to a physical address. The length of each item is the length of a word (in 32-bit arm, the length of a word is defined as 4B). In addition to completing the mapping of virtual addresses to physical addresses, page table entries also define access rights and buffering characteristics.

Due to limited space, here only to analyze how to use (take s5pv210 as an example), specifically how to achieve click here Linux kernel static mapping table establishment process Analysis static mapping operation led

What to say about static mappings:

(1) Different versions of the kernel static mapping table location, file name may be different
(2) different SOC static mapping table location, file name may be different
(3) The so-called mapping table is actually a header file in the macro definition

Static mapping tables in the Samsung version kernel:

(1) The main mapping table is located at: Arch/arm/plat-samsung/include/plat/map-base.h and Arch/arm/plat-s5p/include/plat/map-s5p.h
map-base.h

...
#define The   base address of the static mapping table as determined by the S3c_addr_base (0xfd000000)/Samsung transplant, all virtual addresses in the table are specified by this address + offset

#ifndef __assembly__
#define S3C_ADDR (x) ((void __iomem __force *) S3c_addr_base + (x))
#else
#define S3C_ADDR (x) (S3c_addr_base + (x))
#endif

#define S3C_VA_IRQ  s3c_addr (0x00000000)/    * IRQ Controller (s) */
#define S3c_va_sys  s3c_addr (0x00100000)/    * System control/
#define S3C_VA_MEM  s3c_addr (0x00200000)/    * Memory control * ...
#define S5P_VA_GPIO s3c_addr (0x00500000) ... 

Map-base.h and map-s5p.h are defined as the virtual addresses of each module's register base address. (The back one may be jiuding according to their own hardware to transplant)

CPU in the scheduling of register address is not randomly distributed, but according to the module to differentiate. The addresses of many registers within each module are contiguous. So the kernel in defining the register address is to find the base address, and then use the base site + offset to find a specific register. (Map-s5p.h is defined as the register base address of several modules to be used.) And not all, Samsung only wrote its own to use. The actual work in the future if you want to use this is not to add their own)

(2) Gpio each port-related primary mapping table is located in: arch/arm/mach-s5pv210/include/mach/regs-gpio.h table is the definition of the base address of each port of Gpio. Gpio also divided into GPA0, GPA1, GPB0, GPC, E, F, G, H, etc.
regs-gpio.h

/* Base addresses for each of the banks *
/#define S5pv210_gpa0_base       (S5p_va_gpio + 0x000)
#define S5pv210_gpa 1_base       (S5p_va_gpio + 0x020)
#define S5pv210_gpb_base        (S5p_va_gpio + 0x040)
#define S5pv210_gpc0_ BASE       (S5p_va_gpio + 0x060)
...

(3) The specific register definition of each Gpio is located in: arch/arm/mach-s5pv210/include/mach/gpio-bank.h
gpio-bank.h

...
#define S5pv210_gpa0con         (s5pv210_gpa0_base + 0x00)
#define S5pv210_gpa0dat         (s5pv210_gpa0_base + 0x04)
#define S5PV210_GPA0PUD         (S5pv210_gpa0_base + 0x08)
#define S5PV210_GPA0DRV         (s5pv210_gpa0_base + 0x0c)
#define S5PV210_GPA0CONPDN      (s5pv210_gpa0_base + 0x10)
#define S5PV210_GPA0PUDPDN      (s5pv210_gpa0_base + 0x14)
...

Q: Why give a virtual address to find the corresponding physical address?—-MMU, Linux kernel Static mapping table establishment process Analysis

Driver to add the appropriate code:

#include <mach/regs-gpio.h>//Virtual address mapping table #include <mach/gpio-bank.h> ... #define RGPJ0CON * (volatile uns igned int *) Gpj0con)//force type to be converted to pointer type, then dereference #define RGPJ0DAT * (volatile unsigned int *) gpj0dat) ...//write function is the essence of the application layer passed over  The data is first copied into the kernel and then written to the hardware static ssize_t test_chrdev_write (struct file *file, const char __user *ubuf, size_t count, loff_t
    *ppos) {int ret =-1;
    PRINTK (kern_info "test_chrdev_write\n");       Use this function to copy the contents of the UBUF from the application layer to a buf in the drive space//memcpy (KBUF, ubuf);
    No, because 2 are not in an address space memset (kbuf, 0, sizeof (KBUF));
    ret = Copy_from_user (Kbuf, Ubuf, Count);
        if (ret) {PRINTK (kern_err "Copy_from_user fail\n");
    Return-einval; } PRINTK (Kern_info "copy_from_user success.

    \ n "); if (kbuf[0] = = ' 1 ') {Rgpj0dat = (0<<3) | (0<<4) |
    (0<<5)); }else if (kbuf[0] = = ' 0 ') {rgpj0dat = (1<<3) | (1<<4) |
    (1<<5));
return 0; }
    ...

Complete source Code MODULE_TEST.C

Note: We drive this writing is both correct and incorrect, and rightly said that it can achieve functionality, it is not correct to say that it is not in line with the conventional, the conventional way is that we are in the driver only responsible for simple operation of the hardware, but should be a number of judgments with the user-related business logic written to the application and should not be written to the driver.
Dynamic mapping operation led

How to build dynamic mappings:

(1) request_mem_region, the kernel request (report) memory resources that need to be mapped. Parameters: Register Physical address, register occupies byte number, name
(2) Ioremap, really used to implement mapping, to pass to his physical address he maps you back to a virtual address. Parameters: Register Physical address, registers occupy bytes; return value: A pointer to the address of the virtual address.

How to destroy dynamic mappings

(1) Iounmap
(2) release_mem_region

Note: When mapping is established, it is necessary to apply for remapping first, then use it, and then release the mapping before using to unlock the application. (Reflection type structure)

Driver to add the appropriate code:

Application of resource and implementation mapping in module installation, unlocking and releasing resources in module uninstall

#define GPJ0CON_PA 0xe0200240 #define GPJ0DAT_PA 0xe0200244 unsigned int *pgpj0con;
unsigned int *pgpj0dat;
    ...//module installation function static int __init chrdev_init (void) {PRINTK (kern_info "Chrdev_init HelloWorld init\n"); Register the character device driver Mymajor = Register_chrdev (0, MyName, &test_fops) in the function called by the Module_init macro; the allocation will return the assigned primary device; If the allocation fails, the negative number i is returned
        F (Mymajor < 0) {PRINTK (kern_err "Register_chrdev fail\n");
    Return-einval;

    } PRINTK (kern_info "register_chrdev success ... mymajor =%d.\n", mymajor);
    Use dynamic mapping to manipulate registers if (!request_mem_region (GPJ0CON_PA, 4, "Gpj0con") Return-einval;

    if (!request_mem_region (GPJ0DAT_PA, 4, "Gpj0con")) Return-einval;
    Pgpj0con = Ioremap (GPJ0CON_PA, 4);

    Pgpj0dat = Ioremap (GPJ0DAT_PA, 4);
    /*** after the Pgpj0con, pgpj0dat to manipulate the corresponding registers to control the hardware ***/*pgpj0con = 0x11111111; *pgpj0dat = ((0<<3) | (0<<4) |     (0<<5));
Light return 0; }//module download function static void __exit Chrdev_exiT (void) {PRINTK (kern_info "Chrdev_exit HelloWorld exit\n"); *pgpj0dat = ((1<<3) | (1<<4) | 
    (1<<5));
    Unlock the Mapping Iounmap (Pgpj0con);
    Iounmap (Pgpj0dat);
    Release_mem_region (GPJ0CON_PA, 4);

    Release_mem_region (GPJ0DAT_PA, 4);
In the function of Module_exit macro call to unregister character device driver Unregister_chrdev (mymajor, myname);
 }

The

Implementation maps multiple registers at the same time:
because the address is next to each other, you can

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.