DNW原理和源代码分析

时间:2022-01-15 03:54:13

源代码地址: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文件如下:

   3 driver_src = `pwd`/src/driver
4 dnw_src = src/dnw
5
6 all: driver dnw
7
8 driver:
9 make -C /lib/modules/`uname -r`/build M=$(driver_src) modules
10
11 dnw:
12 make -C $(dnw_src)
13
14 install: all
15 make -C $(dnw_src) install
16 make -C /lib/modules/`uname -r`/build M=$(driver_src) modules_install
17 cp dnw.rules /etc/udev/rules.d/
18 depmod
19
20 clean:
21 make -C $(dnw_src) clean
22 make -C /lib/modules/`uname -r`/build M=$(driver_src) clean
make指令编译出应用和驱动,没有进行安装,所以不须要root权限

DNW原理和源代码分析

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_tload_addr; /* load address */
uint32_tsize; /* data size *///size=地址(4位)+大小(4位)+数据+校验(2位)=
uint8_tdata[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 statfile_stat;
struct download_buffer*buffer = NULL;
unsigned longtotal_size;
intfd;

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

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