第23章Linux设备驱动的移植之编写可移植的设备驱动

本章重点
        编写 Linux 设备驱动时,驱动程序所服务的硬件芯片可能会在多个不同处理器的系统中用到,因此,在编写驱动时,应该尽量考虑其可移植性。
        1、从数据类型、结构体对齐、大小端模式、内存页面大小等多个角度阐述编写可移植驱动程序的注意事项。
        2、为高效地推出新的设备驱动,借用 demo板、类似芯片和厂商范例程序是几种很有效的手段,讲解这些快速编写设备驱动的方法。
        3、 Linux 2.4 和 Linux 2.6 内核在设备驱动方面的差异,通过对两者差异的分析,可以得出移植 Linux 2.4 内核驱动到 Linux2.6 内核的方法。
        4、将其他操作系统内的驱动移植到 Linux 中的方法,主要分析实时操作系统 VxWorks 设备驱动和 Linux 设备驱动的异同点。
        5、如何将 Linux 移植到新的 SoC 和电路板。
23.1 编写可移植的设备驱动
23.1.1 可移植的数据类型

        C 语言中的标准数据类型 int、long 的长度直接与平台相关,在驱动中,关键部分代码直接使用这些类型时需要特别小心。表 23.1 给出了在几个不同的平台下 char、short、int、long、ptr、long long 的长度。


        因此,在 Linux 系统中,针对不同的体系结构重新 typedef 出了 u8、u16、u32、u64、s8、s16、s32、s64 等类型。例如,在 i386/arm 下这些类型的定义如代码清单 23.1 所示,而在 ppc64 下这些类型的定义则如代码清单 23.2 所示,可见影响 C 语言基本数据类型大小的主要因素是 CPU 字长

代码清单 23.1 i386/arm 平台下 u8、u16、u32、u64、s8、s16、s32、s64 的定义

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;

代码清单 23.2 ppc64 平台下 u8、u16、u32、u64、s8、s16、s32、s64 的定义

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 s64;

typedef unsigned long u64;

        由于 u8、u16、u32、u64、s8、s16、s32、s64 是被针对不同的体系结构单独定义的,因此,在任何平台下对其进行 sizeof 运算的结果都是不变的,是确定长度的数据类型。这些类型都应该只在内核空间使用。在 Linux 用户空间中,如果要使用确定长度的数据类型,应该使用上述定义加“__”的版本,如__u8、__u16、

__u32 等。设备驱动中如果要向用户空间导出头文件,在头文件中也应该定义__sxx、__uxx 等数据类型。

        上面给出的 _ _uxx、_ _sxx 类型定义都是 Linux 系统所特有的,为更好地向其他平台移植,驱动中最好使用 int8_t、int16_t、int32_t、uint8_t、uint16_t、uint32_t、int64_t、uint64_t这些 C99 标准确定长度类型。

        Linux 系统中定义许多以_t 为后缀的数据类型,这些类型的使用将使内核屏蔽实际数据类型间存在的差异。例如,file_operations 中的 read()、write()成员函数返回值为 ssize_t类型,llseek()返回的是 loff_t 类型。此外,ssize_t、pid_t 这些类型也被赋予一些含义,如 pid_t 是进程 ID 类型。

23.1.2 结构体对齐

        在 C 语言中使用结构体时有一个需要特别注意的事项,那就是结构体的对齐。struct 是一种复合数据类型,其构成元素既可以是基本数据类型的变量,也可以是一些复合数据类型(如结构体、共用体等)的数据单元。对于结构体,编译器很可能会自动进行成员变量的对齐,以提高存取效率。默认情况下,编译器为结构体的每个成员按其自然对齐(natural alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构体的地址相同

        自然对齐指按结构体的成员中 sizeof 最大的成员对齐(如果 sizeof 大于 CPU 的字长,仍然按照 CPU 字长对齐),例如对于 32 位系统:

struct naturalalign {
        char a;
        short b;
        char c;
};

        在上述结构体中,size 最大的是 short,其长度为两个字节,因而结构体中的 char 成员 a、c都以 2 为单位对齐,sizeof(naturalalign)的结果等于 6

如果改为:
struct naturalalign {
        char a;
        int b;
        char c;
};
其结果为 12。

        在 Linux 内核编程中,为了防止编译器自动在结构体的数据间插入空隙,使用__attribute__((packed))定义结构体,如:

struct naturalalign {
        char a;
        int b;
        char c;

}__attribute__((packed)); //不要在数据间插入空隙

sizeof(naturalalign)的结果等于 6

23.1.3 Little Endian 与 Big Endian

        采用 Little Endian 模式的 CPU 对操作数的存放方式是从低字节到高字节,而 Big Endian 模式的 CPU 对操作数的存放方式是从高字节到低字节。例如,16bit 宽的数 0x1234 在 Little Endian 模式 CPU内存中的存放方式(假设从地址 0x4000 开始存放)为:


低内存地址处存放低位数据

        而在 Big Endian 模式,CPU 内存中的存放方式则为:


低内存地址处存放高位数据

        32bit 宽的数 0x12345678 在 Little Endian 模式 CPU 内存中的存放方式(假设从地址 0x4000开始存放)为:


                                                        低内存地址处存放低位数据

        而在 Big Endian 模式 CPU 内存中的存放方式则为:


                                                        低内存地址处存放高位数据

        内核中定义如下多个宏来进行 Big Endian 模式与 Little Endian 模式的互换,包括 cpu_to_le64、

le64_to_cpu、cpu_to_le32、le32_to_cpu、cpu_to_le16、le16_to_cpu。

        内核中定义如下多个宏来进行 Big Endian 模式与 Big Endian 模式的互换:

cpu_to_be64、be64_to_cpu、cpu_to_be32、be32_to_cpu、cpu_to_be16、be16_to_cpu。

23.1.4 内存页面大小

        一般情况下,内存页面的大小是 4KB(即 PAGE_SIZE 定义为 4KB),但是这并非是一定的,实际上,页面大小在一个 4~64KB 的范围内是可变的,即使在相同的平台下也可以定义不同的PAGE_SIZE 和 PAGE_SHIFT。

        当在内核空间中通过 get_free_pages()函数申请内存时,如果它的第二个参数为 order,意味着申请 PAGE_SIZE * 2的 order次方 的内存,同样是申请 64KB 内存,如果 PAGE_SIZE 为 4KB,应该传入的 order 是 4,如果 PAGE_SIZE 是 16KB,应该传入的参数是 2。为了保证在申请 64KB 内存时,在任何 PAGE_SIZE 的情况下都成立,可以使用如代码清单 23.3 所示的方法。

代码清单 23.3 通过 get_order()获得要申请内存的 order

#include <asm/page.h>
int order = get_order(64*1024);
buf = get_free_pages(GFP_KERNEL, order);


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值