Linux Device Driver Learning (7)-kernel data type

Source: Internet
Author: User

As the previous study used the knowledge of the kernel data structure type in Chapter 11th, I checked it first. Key points are as follows:

When porting linux to a new architecture, developers encounter a number of problems related to incorrect data types. Sticking to strict data types and using-wall-wstrict-prototypes for compilation may avoid most of the bugs.

Kernel data is mainly divided into three types: Standard C language type, determined size type, and specific kernel object type.

Standard C language type

When a "2-byte padding character" or "representing something with a 4-byte string" is required, the standard C language type cannot be used, because in different architectures, the data types in C Language occupy different sizes. The datasize program experiment later demonstrates the size of the Data Types of various C Types in the user space on the current platform. In addition, in some architectures, the space occupied by kernel space and user space C data types may be different. The kdatasize module displays the space occupied by the C data type in the kernel space of the current module.

Although the address is a pointer, you can use an unsigned integer to better manage the memory. The kernel regards the physical memory as a giant array, and the memory address is the index of the array. We can easily value the pointer, but when we directly process the memory address, we almost never value it in this way. This value is avoided by using an integer type, thus avoiding bugs. Therefore, the memory address in the kernel is often unsigned long, taking into account the fact that pointers and long integers are always the same size on at least all platforms currently supported by Linux.

The c99 standard defines the intptr_t and uintptr_t types, which are integer variables that can save pointer values. But not in the 2.6 kernel.

Determine the type of the size

When you need to know the size of your defined data, you can use the following data types provided by the kernel (all data declarations are in <ASM/types. h>, included in <Linux/types. h> ):

u8; /* unsigned byte (8 bits) */u16; /* unsigned word (16 bits) */u32; /* unsigned 32-bit value */u64; /* unsigned 64-bit value */

/* Although there is little need for signed types, if needed, use s instead of u */

If a user space program needs to use these types, you can add a Double underline before the symbol: _ u8 and other types are independent of _ KERNEL.

These types are Linux-specific and they prevent porting software to other UNIX machines. The new compiler system supports c99-standard types, such as uint8_t and uint32_t. Considering portability, using these types is better than using specific Linux variants.

Interface-specific type (_ T Type )

The most common data types in the kernel are declared by their own typedef, blocking any portability issues. Interface-specific is a data type defined by a database to provide interfaces for a specific data structure. Many _ T types are defined in <Linux/types. h>.

Note: Recently, few new interface-specific types have been defined. Many kernel developers no longer like to use typedef statements. They prefer to see the real type information directly used in the code. Many old interface-specific types are retained in the kernel and they will not disappear soon.

Even if no specific interface type is defined, it should always be the same and appropriate data type as other parts of the kernel. As long as the driver uses this "Custom" type of function, but does not comply with the conventions, the compiler will issue a warning, then use the-wall compiler option and be careful to remove all warnings, you can be sure that the code is portable.

The main problem with the _ T type is that it is often difficult to select the correct printk or printf format when printing them. The best way to print interface-specific data is to forcibly convert it to the maximum possible type (often long or unsigned long) and print it in the corresponding format.

Other portability Problems

A typical porting rule is: Avoid using explicit constant values and use preprocessing macros to parameterize them.

Interval

During the processing interval, do not assume the number of jiffies per second, not every LINUX Platform runs at a fixed speed. when calculating the time interval, use Hz (the number of timer interruptions per second) to calibrate your time. By default, the Hz value of S3C2410 is 200.

Page size

When using memory, remember that a memory page is page_size byte, not 4 kb. The related macro definitions are page_size and page_shit (including the number of digits that shift an address to obtain its page number), which are defined in <ASM/page. h>. If the user space program needs this information, you can use the getpagesize library function.

If a driver needs to store data in 16 KB, get_order is a portable solution:

# Include <ASM/page. h> int order = get_order (16*1024); Buf = get_free_pages (gfp_kernel, order);/* The get_order parameter must be the power of 2 */
Byte order

Do not assume the byte order. The Code should be written in byte order independent of the operated data.

Header file <ASM/byteorder. h> definition:

#ifdef __ARMEB__#include <linux/byteorder/big_endian.h>#else#include <linux/byteorder/little_endian.h>#endif

In <Linux/byteorder/big_endian.h>, _ big_endian is defined. in <Linux/byteorder/little_endian.h>, _ little_endian is defined, when dealing with the bytecode issue, these dependent processors need to encode a bunch of conditional statements similar to # ifdef _ litttle_endian.

But there is a better way: the Linux kernel has a set of macro definitions to process the conversion between the processor's byte order and the specific byte order. For example:

U32 cpu_to_le32 (u32); u32 le32_to_cpu (u32);/* these macros convert the values used by a CPU into unsigned 32-bit small header values, whether the CPU is large or small, whether it is a 32-bit processor or not. If no conversion is required, an unmodified value is returned. * // * Many similar functions are defined in <Linux/byteorder/big_endian.h> and <Linux/byteorder/little_endian.h> */
 
Data Alignment

The last question worth considering when writing portable code is how to access non-aligned data. The following macros should be used to access non-alignment data:

#include <asm/unaligned.h>get_unaligned(ptr);put_unaligned(val, ptr);

These macros are non-typed and valid for each total data item, either 1, 2, 4, or 8 bytes, and are defined in all kernel versions.

Another issue about alignment is the cross-platform portability of data structures. The same data structure may be compiled differently on different platforms. In order to write data structures that can be transplanted across systems, the natural alignment of data items should always be enforced. Natural alignment refers to the address that stores data items in an integer multiple of the data items. The padding character should be used to prevent the compiler from moving fields in the data structure when the natural alignment is forced, leaving holes in the data structure.

The dataalign program experiment shows how the compiler enforces alignment.

For the good performance of the target processor, the compiler may quietly Insert the padding into the structure to ensure that each member is aligned. If you define a structure that matches the structure required by the device, the auto-padding will destroy this intent. The solution to this problem is to tell the compiler that the structure must be "Compact" and there is no padding. For example, the following definition:

Struct

{

2017-11-id;

U64 Lun;

2010reserved1;

U32 reserved2;

}

_ Attribute _ (packed) SCSI;

/* If this structure is compiled on the 64-bit platform, if _ attribute _ (packed) is not found )), two or six padding characters may be added to the Lun member. Pointer and error value */

You can also find the practical application of this method in the spcaframe. h header file of the servfox source code that uses the video capture using the arm9-and USB cameras:

struct frame_t{

char header[5];

int nbframe;

double seqtimes;

int deltatimes;

int w;

int h;

int size;

int format;

unsigned short bright;

unsigned short contrast;

unsigned short colors;

unsigned short exposure;

unsigned char wakeup;

int acknowledge;

} __attribute__ ((packed));

struct client_t{

char message[4];

unsigned char x;

unsigned char y;

unsigned char fps;

unsigned char updobright;

unsigned char updocontrast;

unsigned char updocolors;

unsigned char updoexposure;

unsigned char updosize;

unsigned char sleepon;

} __attribute__ ((packed));

Pointer and error value

Many kernel interfaces return error messages by encoding error values to pointer values. Such information must be used with caution because their return values cannot be simply compared with null. To help you create and use such interfaces, <Linux/err. h> provides the following functions:

Void * err_ptr (long error);/* encode the error value to the pointer value. error is a common negative error code */long is_err (const void * PTR ); /* test whether the returned pointer is an error code */long ptr_err (const void * PTR);/* extract the actual error code, which is only used when is_err returns a true value, otherwise, a valid pointer */

Linked List

The operating system kernel usually needs to maintain a linked list of data structures. The Linux kernel has several linked list implementations at the same time. To reduce the number of code copies, the kernel has already created a standard annular two-way linked list, and encourages people who need to operate the linked list to use this facility.

When using the linked list interface, remember that the list function is not locked. If the driver may perform concurrent operations on the same list, a lock scheme must be implemented.

To use the Linked List mechanism, the driver must contain a file <Linux/list. h>, which defines a simple list_head type structure:

struct list_head { struct list_head *next, *prev; };

The linked list used in the actual code is almost always composed of a certain structure type. Each structure describes one of the linked lists. To use a Linux linked list, you only need to embed a list_head in the structure of the linked list. The linked list header is often an independent list_head structure. Shows how this simple struct list_head is used to maintain a list of data structures.

/* The linked list header must be initialized before use. There are two forms: * // * First, runtime initialization: */struct list_head todo_list; init_list_head (& todo_list ); /* Second, initialization during compilation: */list_head (todo_list); list_add (struct list_head * New, struct list_head * head);/* Add a new item next to the head of the linked list. Note: The head does not need to be a nominal head of the linked list; if you pass a list_head structure, it is in the middle of a chain table, and the new item is closely behind it. Because the Linux linked list is circular, the linked list header is usually no different from any other items */list_add_tail (struct list_head * New, struct list_head * head ); /* Add a new item before the given linked list header, that is, add a new item at the end of the linked list. */List_del (struct list_head * entry); list_del_init (struct list_head * entry);/* remove the specified item from the queue. If the entry item may be registered in another linked list, you should use list_del_init to reinitialize the linked list pointer. */list_move (struct list_head * entry, struct list_head * head); list_move_tail (struct list_head * entry, struct list_head * head ); /* The given entry is removed from its current linked list and added to the beginning of the head. To place the entry at the end of the new linked list, use list_move_tail instead of */list_empty (struct list_head * head);/* If the given linked list is empty, a non-zero value is returned. */list_splice (struct list_head * List, struct list_head * head);/* connects two linked lists after the list is placed in the head. * // * list_entry is a pointer to a struct containing the list_head structure. After reading the source code, you will find that you are familiar with it. Yes. In fact, container_of has been used in the module's open method. There are many variants of list_entry. You can see the source code. */# define list_entry (PTR, type, member) \ container_of (PTR, type, member)

ARM9.Lab board Experiment

DatasizeLab

The uname function is used in the experiment. It is introduced as follows (based on "Advanced Programming of Unix environment", Chapter 1 system data file and information 6th system identification ):

Posix.1 defines the uname function, which returns information related to the host and operating system.

# Include <sys/utsname. h>

Int uname (struct utsname * n a m e );

Return Value: If successful, the value is not negative. If an error occurs, the value is-1.

Pass the utsname structure address to the parameter of the function, and then fill in the structure of the function. Posix.1 only defines at least the fields to be provided in the structure (they are character arrays), and the length of each array is determined by the implementation. Some implementations provide other fields in this structure. Historically, System V has allocated nine bytes to each array, one of which is used for the string terminator (null character ).

Struct utsname {

Char sysname [9];/* Name of the Operating System */

Char nodename [9];/* Name of this node */

Char release [9];/* current release of operating system */

Char version [9];/* Current version of this release */

Char Machine [9];/* Name of hardware type */

};

The information in the utsname structure can be printed by the uname (1) command.

Link to the experiment program source code:Datasize

KdatasizeModule Experiment

The utsname function is used in the experiment. The source code is as follows:

//<linux/utsname.h>struct new_utsname {char sysname[65];char nodename[65];char release[65];char version[65];char machine[65];char domainname[65];};static inline struct new_utsname *utsname(void){return &current->nsproxy->uts_ns->name;}

Source code link of the experiment module:Kdatasize

KdataalignModule Experiment

For more information about the experiment principle, see the source code.

Source code link of the experiment module:Kdataalign

Lab symptom:

[Tekkaman2440@SBC2440V4]#cd /tmp/[Tekkaman2440@SBC2440V4]#./datasizearch Size: char short int long ptr long-long u8 u16 u32 u64armv4tl 1 2 4 4 4 8 1 2 4 8[Tekkaman2440@SBC2440V4]#cd /lib/modules/[Tekkaman2440@SBC2440V4]#insmod kdatasize.koarch Size: char short int long ptr long-long u8 u16 u32 u64armv4tl 1 2 4 4 4 8 1 2 4 8insmod: cannot insert 'kdatasize.ko': No such device (-1): No such device[Tekkaman2440@SBC2440V4]#insmod kdataalign.koarch Align: char short int long ptr long-long u8 u16 u32 u64armv4tl 1 2 4 4 4 4 1 2 4 4insmod: cannot insert 'kdataalign.ko': No such device (-1): No such device

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.