这个主题主要说的就是固件文件的读写,它为我们提供了一个把用户文件读入内核的机制。
前面的实例代码在实际的一些驱动中可能很少见到,但这一节所说的东西就比较实用了。
首先说一下固件文件。
Ldd3:
作为一个驱动作者, 你可能发现你面对一个设备必须在它能支持工作前下载固
件到它里面. 硬件市场的许多地方的竞争是如此得强烈, 以至于甚至一点用作
设备控制固件的 EEPROM 的成本制造商都不愿意花费. 因此固件发布在随硬件
一起的一张 CD 上,并且操作系统负责传送固件到设备自身.
这里的意思是固件文件是为了节省成本,当然我觉得固件文件也将底了升级难度。
固件文件的格式:
有很多种——trx\usr\bin等
固件文件存储会因为CPU的不同,数据存储的方式分为Big—endian和 Little—endian
我们看bin
bin是C1~20子公司Linksys公司无线路由器固件文件所采用的格式。文件有60字节头部,前32字节是Linksys公司无线路由器固件文件bin格式特有的头部,后28字节是t r x格式文件头部。
Bin有两个头,
先看前32位
struct bin_header
{
uint32_t product_abbreviation; //存放固件的产品型号缩写
uint32_t reserved1[1]; //代表预留的4个字节
uint8_t release_date[3]; //“年一月一日”代表固件发布日期
uint8_t firmware_version [3];//代表固件版本号
uint32_t magic; //字段用于存放魔数
uint32_t reserved2[3];//是代表预留的 1 2个字节,包含一些辅助信息
};
后28位
trx 是CISCO公司无线路由器 wrt54g系列固件文件所采用的格式。文件格式开头是 2 8字节的头部,下面是数据部分。文件头部数据结构:
struct trx_header
{
uint32_t magic; //魔数, F L A S H写入程序在使用固件文件时,通过判断这个魔数是否正确就可以知道固件是否是正确的固件文件类型
uint32_t len; //用于存放包含本头部结构信息的固件文件的长度
uint32_t crc32; //存放 C R C 3 2校验码。生成校验码所使用的数据是从 f l a g_v e r s i o n字段开始到文件末尾所有数据,可以通过此校验码来验证传送到目标机中的固件文件数据是否正确。
uint32_t flag_version;//存放固件标志和版本( 0~1 5代表标志, 1 6~3 1 代表版本)
uint32_t offsets[3];// 存放各个部分在固件文件中的3 2位偏移值。
};
这一段我真是在班门弄斧。我根据这个头说明做一个test.bin。我不贴出来,这个文件只有头,可能这个头都不符合标准,但我们的重点是读这个文件。如果你也想测一下可以到这里下载源码和test.bin(test.bin是大端,csc32是瞎写的)
我的bin:
如果是小端cpu的linux下用hexdump看,数据是反的。
你可以在linux的firmware下看到很多固件文件,我不知道为什么加.ihex。
还要说一下mdev
固件文件的读写要用uevent,具体我就不分析了,mdev的话要在/lib下
#cd /lib
#mkdir firmware
把test.bin放入/lib/firmware下。不然找不到文件。用udev的兄弟自己解决吧!
对之前my_dvr.c中的修改
…
+#include
#include "my_bus.h"
#include "common.h"
+#define TEST_BIN "test1.bin"
MODULE_AUTHOR("wwxxxxll");
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(TEST_BIN);
…
//把special_init改为下面的
//特殊器件初始化
static int special_init(struct device *dev)
{
conststruct firmware *binp;
u8 bin_data[60];
intretval = 0;
size_t size = 0;
MY_DEBUG("special_init\n");
retval =request_firmware(&binp, TEST_BIN, dev);
//等待时间由/sys/class/firmware/timeout中的值决定,默认为.
if(retval)
{
printk(KERN_ERR "test: Failed to load firmware\"%s\"\n", TEST_BIN);
gotoout_init;
}
size = (binp->size > 60) ?size = 60 : binp->size;
memcpy(bin_data, binp->data,size);
//我只是打印,*如果要把数据放入到寄存器要考虑大小端的问题
MY_DEBUG("bin_header:\nproduct: %c%c%c%c\ndate: %d-%d-%d\nversion:%d.%d.%d\nmagic: %c%c%c%c\n",\
bin_data[0],bin_data[1],bin_data[2],bin_data[3],\
bin_data[8],bin_data[9],bin_data[10],\
bin_data[11],bin_data[12],bin_data[13],\
bin_data[14],bin_data[15],bin_data[16],bin_data[17]\
);
MY_DEBUG("trx_header:\nmagic: %c%c%c%c\nlen: %ld\nCSC32: %lx\nversion:%lx\noffsets1: %ld\noffsets2: %ld\noffsets3: %ld\n", \
bin_data[32],bin_data[33],bin_data[34],bin_data[35], \
(unsignedlong)(bin_data[36] << 24 | bin_data[37]<< 16 | bin_data[38] << 8 | bin_data[39]),\
(unsigned long)(bin_data[40]<< 24 | bin_data[41] << 16 | bin_data[42] << 8 | bin_data[43]),\
(unsigned long)(bin_data[44]<< 24 | bin_data[45] << 16 | bin_data[46] << 8 |bin_data[47]),\
(unsigned long)(bin_data[48]<< 24 | bin_data[49] << 16 | bin_data[50] << 8 |bin_data[51]),\
(unsigned long)(bin_data[52]<< 24 | bin_data[53] << 16 | bin_data[54] << 8 |bin_data[55]),\
(unsigned long)(bin_data[56]<< 24 | bin_data[57] << 16 | bin_data[58] << 8 |bin_data[59]));
release_firmware(binp);
out_init:
returnretval;
}
…
调试
#insmod my_bus.ko
#insmod my_dvr.ko
#insmod my_dvc.ko
#dmesg
看到最后有这些信息
下期是设备模型的最后一篇——电源管理
写完之后,我就开始写一些实际的驱动。