Linux驱动移植(一)—— 驱动移植考虑的出发点
在编写设备驱动时,应充分考虑驱动的可移植性,主要从以下几个角度:数据的类型、结构体对界、大小端、内存页面的大小等;
1. 数据类型
不同体系的系统,其数据类型的长度有可能也存在不同;其长度由CPU的字长决定。
一般在平台体系目录,如i386/arm下会有对各类型的定义,如:
typedef signed char s8;
typedef unsigned char u8;
typedef signed short s16;
typedef unsigned short u16;
typedef signed int s32;
typedef unsigned int u32;
typedef signed long long s64;
typedef unsigned long long u64;
引申:
A. s8 u8 s16 u16 s32 u32 s64 u64是针对不同的体系结构设计的,因此在任何平台下,对其进行sizeof()结果都是保持不变的;
B. 该类型只针对内核类型,在用户空间下为_ _u8 _ _8等;
C. 驱动程序中如果有向用户空间导出,则应该向其头文件中加入_ _u8 _ _s8 _ _u16 _ _s16等;
D. 上述为LInux系统所特有的,为了兼容其他平台,可以使用C99标准int8_t、int16_t、int32_t、uint8_t、uint16_t、uint32_t等。
2. 结构体对界
在C语言编程中,struct结构体中除了可能包含一些基本的类型如int 、chat等,还可能包含其它的结构体、复合体等。对于结构体,编译器为了提高存取效率,可能会自动进行成员变量的对齐,如下:
struct mystruct
{
char a;
char b;
short c;
};
上述结构体如果sizeof(mystruct)结果将为2 * 3 =6;
在Linux内核编写过程中,为了避免编译器在编译过程中自动填空,可以使用"_ _attribute__((packet))"定义结构体来避免自动填充。如下:
struct mystruct
{
char a;
char b;
short c;
}_ _attribute_ _((packet));
3.Little Endian 与Big Endian
采用Little Endian模式,CPU对操作数的存储顺序是从小到大,才用Big Endian模式,CPU对操作数的存储顺序是从大到小;如一个16位数据0x1234
LE与BE存储模式对比
另外,内核中定义了多个宏来提供LE同BE的互换,如cpu_to_le64、le64_to_cpu、cpu_to_le32、le32_to_32、cpu_to_le16、le16_to_cpu、cpu_to_be64、be64_tocpu、cpu_to_be32、be32_to_cpu、cpu_to_be16、be16_to_cpu等。
存储地址 | 0x4000 | 0x4001 |
LE | 0x34 | 0x12 |
BE | 0x12 | 0x34 |
4. 内存页面的大小
一般情况情况下,内存页面的大小PAGE_SIZE为4KB,其范围在4KB~64KB之间,即使相同的平台下也可以定义不同的PAGE_SIZE 和PAGE_SHIFT。
在内核空间中,通过get_free_pages()函数来申请内存,其第二个参数order决定了申请内存的大小,其大小为PAGE_SIZE*2^order。因此,申请的内存大小是与
PAGE_SIZE有关。因而,如果要申请64KB大小的内存可以利用如下方法:
#include <ams/page.h>
int order = get_order(64 * 1024);
buf = get_free_pages(GFP_KENEL,order);