文件写入DNW源码解析
题记:写这篇博客要主是加深自己对文件写入的认识和总结实现算法时的一些验经和训教,如果有误错请指出,万分感谢。
一、 据数输传格式
USB Tx format:
addr(4)+size(4)+data(n)+cs(2)
addr:下载到开发板RAM的目标地址, 4节字
size:输传文件的巨细, 4节字
data:文件据数流, n节字
cs: checksum校验和, 2节字
二、 MenuUsbTransmit发送文件程流
1. 打开usb输出管道文件open_file( outPipe),如果失败,报错;
2. 选择并打开所要输传的文件;
3. 取获文件巨细;fileSize=GetFileSize(hFile,NULL);
4. 配分一个(fileSize+10)巨细的缓冲区;4+4+2=10
5. 将文件读入缓冲区txBuf+8(即保存前8个节字)开始处;
6. 将所设置的下载地址赋给txBuf[0:3]4个节字;downloadAddress;
7. 将fileSize+10赋给txBuf[4:7]4个节字
8. 将校验和赋给缓冲区的最后2节字(txBuf+8+fileSize))=cs;
9. 动启usb输传UsbTxFile()
DNW编译记载:
DNW的源码下载以后,直接编译和运行——当然这是你急切想去做的了。不过此时可能遇费事,是一系列“头文件没法识别”的误错。此时你要做的:
1.将WINDDK下的WXP目录下的头文件加添到vc下的include文件中。此时你只要须将E:\WINDDK\2600\inc\wxp中的 wxp文件,"ctrl+c"&&"ctrl+v"到C:\Program Files\Microsoft Visual Studio\VC98\Include中。当然,前提是你已安装了WINDDK和VC到响应的目录之下。这样你的编译器便不会对这些要重的“.h”习以为常了。
2.将WINDDK目录E:\WINDDK\2600\lib\wxp\i386下的usbd.lib,setupapi.lib加添到vc下的lib文件中
3.如果到碰:fatal error LNK1112: module machine type "IA64" conflicts with target machine type "IX86" (模块计算机类型“IA64”与目标计算机类型“IX86”冲突),首先,进入VC下的LINK,如图
并且要将Project Options下的内容手动改修成图中的内容——我别特指的是machine:IX86和最后一行的\lib\i386\... 然后将E:\WINDDK\2600\lib\wxp\i386下的库文件mfc42u.lib到C:\Program Files\Microsoft Visual Studio\VC98\Lib中之。注意,不是E:\WINDDK\2600\lib\wxp\ia64目录下的那个mfc42u.lib
Linux下编译记载:
编译PC端USB动驱和写入工具
dnw_linux.tgz压缩包文件结构如下
dnw_linux/
dnw_linux/secbulk/
dnw_linux/secbulk/Makefile
dnw_linux/secbulk/secbulk.c
dnw_linux/dnw/
dnw_linux/dnw/dnw.c
其中secbulk.c是PC端USB动驱, dnw.c是写入工具
编译动驱之前先改修secbulk.c
找到#define BULKOUT_BUFFER_SIZE
改修为
#define BULKOUT_BUFFER_SIZE 512
找到
static struct usb_device_id secbulk_table[]= {
{ USB_DEVICE(0x04e8, 0x1234)},
{ }
};
改修为下面的模样
接下来编译
$cd secbulk
$make -C /lib/modules/`uname -r`/build M=`pwd` modules
加载编译好的动驱
$sudo insmod ./secbulk.ko
注意,每次下载前都要须加载动驱,或者可以设置为开机自动加载
Ubuntu中,假设动驱文件在/opt/dnw_linux/secbulk/
则改修/etc/rc.d/rc.local文件,末端入加
sudo insmod /opt/dnw_linux/secbulk/secbulk.ko
即成完开机自动加载动驱模块
接下来编译dnw写入工具
先打开dnw.c改修
找到
printf("Writing data...\n");
size_t remain_size = file_stat.st_size+10;
size_t block_size = remain_size / 100;
size_t writed = 0;
在它后面加上2行码代,如下:
file_buffer [file_stat.st_size + 8] = sum & 0xff;
file_buffer [file_stat.st_size + 9] = sum >> 8;
printf("Writing data...\n");
size_t remain_size = file_stat.st_size+10;
size_t block_size = remain_size / 100;
size_t writed = 0;
编译dnw
$gcc -o dnw dnw.c
编译功成后生成可执行dnw
用使DNW下载
动启开发板,进入minicom,并将开发板和PC用USB电缆连接,此时用dmesg令命可以看到secbulk动驱加载:
[ 283.677772] usb 1-1: new full speed USB device using uhci_hcd and address 2
[ 284.084835] usb 1-1: configuration #1 chosen from 1 choice
[ 284.140430] secbulk:secbulk probing...
[ 284.140482] secbulk:bulk out endpoint found!
说明动驱可以用使
重起开发板,别进linux系统,按任意键进入uboot界面,
入输
dnw 50008000
当现出“USB host is connected. Waiting a download.”时,
在PC端Linux上用dnw工具写入要下载的文件,例如我要写入/tmp/zImage
$./dnw /tmp/zImage
写入成完后提示功成
100% 312349 bytes OK
至此,dnw在linux下用使一切正常
210通过DNW下载文件说明:
4. dnw0.5改修说明
4.1 winMain()->Register(HINSTANCE hInst)-> 回调函数WndProc()->设置一个时定器(用来时定探测usb否是连接)->息消测检循环
4.2 按下菜单栏中USB Port的Transmit->MenuUsbTransmit(HWND hwnd)(在此改修的码代),以下为要重改修或加添的码代
4.2.1查看动驱道知GUID要改修为 DEFINE_GUID(GUID_CLASS_I82930_BULK, 0xa5dcbf10, 0x6530, 0x11d2, 0x90, 0x1f, 0x00, 0xc0, 0x4f,0xb9, 0x51, 0xed);
4.2.2 UsbSendAckData()函数是3.1中用来发送“ATUD”知通210板子的, 当初这个函数是放在MenuUsbTransmit中,所以只有按下菜单栏中USB Port的Transmit,才会知通210板子下载,也可放入WndProc()中设置的时定器服务函数中,这样一打开dnw便行进连接。
4.3.3 FilebufToTxbuf()函数为3.8 和3.9 议协
DNW-LINUX源码分析:
源码代地址:http://code.google.com/p/dnw-linux/
参考文章:http://www.cnblogs.com/QuLory/archive/2012/11/16/2773389.html
http://blog.csdn.net/yming0221/article/details/7211396
1.道理
DNW道理就是通过PC端软件把要烧写的像镜(uboot,kernel,fs)通过usb口写进usb设备的RAM中,然后USB设备再把RAM里的据数写到rom(nandflash,emmc等)中实现固化程序。想比拟直接从SD端口直接固化程序费事了多许,但是对于很多没有sd卡口接的设备是却必须的.
2.用使
下载源码代,然后进入目录。入输令命sudo make install,注意这里要须root限权。
Makefile文件如下:
make指令编译出应用和动驱,没有行进安装,所以不要须root限权
make install则要须root限权。
在pc端用使dnw将要须下载的像镜文件写入usb设备ram
$sudo ./dnw [-a load_addr] /filepath/filename
3.源码代分析:
动驱文件secbulk.c这个文件没什么好说的,usb设备动驱的型模,填入对应码代,要注意的是
static struct usb_device_id secbulk_table[]= { { USB_DEVICE(0x5345, 0x1234) }, /* FS2410 */ { USB_DEVICE(0x04e8, 0x1234) }, /* EZ6410 */ { } };
这里设置的pid和vid要与设备对应,否则动驱没法识别。
应用程序dnw.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <malloc.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> #include <fcntl.h> #include <stdint.h> const char* dev = "/dev/secbulk0"; //dnw所创立的设备文件,要对其写入 #define BLOCK_SIZE (1*1024*1024) //设置的写入块巨细1MB struct download_buffer { uint32_t load_addr; /* load address */ uint32_t size; /* data size *///size=地址(4位)+巨细(4位)+据数+校验(2位)= uint8_t data[0];//0度长数组,向指据数 /* uint16_t checksum; */数组后紧接着的两位是校验位 }; static int _download_buffer(struct download_buffer *buf)//从缓存写入到usb设备文件 { int fd_dev = open(dev, O_WRONLY);//打开设备 if( -1 == fd_dev) { printf("Can not open %s: %s\n", dev, strerror(errno)); return -1; } printf("Writing data...\n"); size_t remain_size = buf->size;//写入文件的残余巨细 size_t block_size = BLOCK_SIZE;//每次写入的巨细 size_t writed = 0;//已写入的文件巨细 while(remain_size>0) { size_t to_write = remain_size > block_size ? block_size : remain_size;//每次写入的际实巨细 if( to_write != write(fd_dev, (unsigned char*)buf + writed, to_write)) { perror("write failed"); close(fd_dev); return -1; } remain_size -= to_write; writed += to_write; printf("\r%02zu%%\t0x%08zX bytes (%zu K)", (size_t)((uint64_t)writed*100/(buf->size)), writed, writed/1024);//打印写入的百分比 fflush(stdout);//将缓存写入文件 } printf("\n"); close(fd_dev); return 0; } static inline void cal_and_set_checksum(struct download_buffer *buf) { uint16_t sum = 0; int i; for(i = 0; i < buf->size; i++) { sum += buf->data[i]; } *((uint16_t*)(&((uint8_t*)buf)[buf->size - 2])) = sum;//校验码赋值给最后一个word } static struct download_buffer* alloc_buffer(size_t data_size)//配分空间的函数 { struct download_buffer *buffer = NULL; size_t total_size = data_size + sizeof(struct download_buffer) + 2;buffer=文件巨细+结构体前两项的巨细+2位的校验位 buffer = (typeof(buffer))malloc(total_size); if(NULL == buffer) return NULL; buffer->size = total_size; return buffer;//回返向指结构体的指针 } static void free_buffer(struct download_buffer *buf) { free(buf); } static struct download_buffer *load_file(const char *path, unsigned long load_addr)//载入文件到缓存 { struct stat file_stat; struct download_buffer *buffer = NULL; unsigned long total_size; int fd; fd = open(path, O_RDONLY);//通过路径打开文件,得获fd文件标识符 if(-1 == fd) { printf("Can not open file %s: %s\n", path, strerror(errno)); return NULL; } if( -1 == fstat(fd, &file_stat) ) {//取获文件的性属 perror("Get file size filed!\n"); goto error; } buffer = alloc_buffer(file_stat.st_size);//给buffer配分空间(文件占用空间+结构体空间+2位校验) if(NULL == buffer) { perror("malloc failed!\n"); goto error; } if( file_stat.st_size != read(fd, buffer->data, file_stat.st_size)) {//将文件写入buffer-》data perror("Read file failed!\n"); goto error; } buffer->load_addr = load_addr;//充填结构体 cal_and_set_checksum(buffer);//校验据数 return buffer; error: if(fd != -1) close(fd); if( NULL != buffer ) free(buffer); return NULL; } static int download_file(const char *path, unsigned long load_addr) { struct download_buffer *buffer; struct timeval __start, __end; long __time_val = 0; float speed = 0.0; buffer = load_file(path, load_addr);//将文件载入到buffer中 gettimeofday(&__start,NULL); if (buffer != NULL) { if (_download_buffer(buffer) == 0) {//将缓存中的据数写入usb口 gettimeofday(&__end,NULL); __time_val = (long)(__end.tv_usec - __start.tv_usec)/1000 + \ (long)(__end.tv_sec - __start.tv_sec) * 1000; speed = (float)buffer->size/__time_val/(1024*1024) * 1000; printf("speed: %fM/S\n",speed); free_buffer(buffer); } else { free_buffer(buffer); return -1; } } else return -1; } int main(int argc, char* argv[]) { unsigned load_addr = 0x57e00000; char* path = NULL; int c; while ((c = getopt (argc, argv, "a:h")) != EOF) switch (c) { case 'a': load_addr = strtol(optarg, NULL, 16); continue; case '?': case 'h': default: usage: printf("Usage: dwn [-a load_addr] <filename>\n"); printf("Default load address: 0x57e00000\n"); return 1; } if (optind < argc) path = argv[optind]; else goto usage; printf("load address: 0x%08X\n", load_addr); if (download_file(path, load_addr) != 0) { return -1; } return 0; }
要须注意的几点:
1. struct download_buffer结构体含有一个零度长数组,不占用结构体空间度长,可以灵巧配分空间。
2.cal_and_set_checksum检验函数通过指针偏移量写入到确正位置,位于配分空间的最后两位,据数段零度长数组后面2位。
3.load_addr数参要根据不同的设备,来设定,不指定具体地址,将会采取认默地址0x57e00000
文章结束给大家分享下程序员的一些笑话语录:
人工智能今天的发展水平:8乘8的国际象棋盘其实是一个体现思维与创意的强大媒介。象棋里蕴含了天文数字般的变化。卡斯帕罗夫指出,国际象棋的合法棋步共有1040。在棋局里每算度八步棋,里面蕴含的变化就已经超过银河系里的繁星总数。而地球上很少有任何数量达到这个级别。在金融危机之前,全世界的财富总和大约是1014人民币,而地球人口只有1010。棋盘上,所有可能的棋局总数达到10120,这超过了宇宙里所有原子的总数!经典语录网