#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <image.h>
#include <asm/types.h>
#include <asm/byteorder.h>
#include <system_default.h>
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <asm/types.h>
#include "mtd/mtd-user.h"
#define SUCCESS
0
#define FAIL
-1
#define ENVALID_PRM
-2
#define UNKOWN
-3
#define FILE_NAME_SIZE
80
#define KERNELBLOCK
"/dev/mtd2"
#define FILESYSBLOCK
"/dev/mtd3"
#define TEMP_PATH
"/tmp"
#define TEMP_FILE
"tmpfile.txt"
#define FILESYS_PRE
"cramfs"
#ifdef U_DEBUG
#define DBG(fmt, args...)
write(STDOUT_FILENO, out_buf, sprintf(out_buf, fmt, ##args) + 1)
#else
#define DBG(fmt, args...)
#endif
#define OUTPUT(fmt, args...)
write(STDOUT_FILENO, out_buf, sprintf(out_buf, fmt, ##args) + 1)
typedef struct _FileInfo{
long nStartOffset;
long nEndOffset;
int fd;
long nLength;
char strFileName[FILE_NAME_SIZE + 1];
}FileInfo;
struct mtd_info_user {
uint8_t type;
uint32_t flags;
uint32_t size;
// Total size of the MTD
uint32_t erasesize;
uint32_t oobblock; // Size of OOB blocks (e.g. 512) oob块大小
uint32_t oobsize; // Amount of OOB data per block (e.g. 16) 每块的oob数据数量大小
uint32_t ecctype;
uint32_t eccsize;
};
const char update_html_head[] = //更新html的头部,相当于一个一个html网页
"HTTP/1.1 200 OK\r\nContent-type: text/html\r\nPragma: no-cache\r\n" //网址
"Cache-Control: no-store\r\n\r\n"
"<HTML><HEAD><TITLE>Firmware Update</TITLE></HEAD>\n" //html head ,body
"<style>\n BODY { PADDING-RIGHT: 0px; MARGIN-TOP: 10px; PADDING-LEFT: 10px; MARGIN-LEFT: auto; WIDTH: auto; MARGIN-RIGHT: auto; background-color:#182439; FONT-FAMILY: Tahoma, Geneva, Arial, \"Arial Narrow\", Verdana, sans-serif; font-size:14px; color:#FFFFFF;overflow-x: hidden;overflow-y: hidden} </style>";
const char update_html_end[] = "";
static int gLastI = -1, gLastRead = 0;
static char out_buf[80];
/******************************************************************************
Start of nandwrite global
******************************************************************************/
#define MAX_OOB_SIZE
64
#define MAX_PAGE_SIZE
2048
unsigned char oobbuf[MAX_OOB_SIZE];
unsigned char oobreadbuf[MAX_OOB_SIZE];
unsigned char writebuf[MAX_PAGE_SIZE];
int
blockalign = 1; /*默认使用16k的块大小 default to using 16K block size */
int
forcejffs2 = 1;
int
forceyaffs = 0;
int
autoplace = 0;
int
forcelegacy = 1;
int
noecc = 0;
int
writeoob = 0;
int
pad = 1;
struct nand_oobinfo jffs2_oobinfo = {
.useecc = MTD_NANDECC_PLACE,
.eccbytes = 6,
.eccpos = { 0, 1, 2, 3, 6, 7 }
};
struct nand_oobinfo yaffs_oobinfo = {
.useecc = MTD_NANDECC_PLACE,
.eccbytes = 6,
.eccpos = { 8, 9, 10, 13, 14, 15}
};
/******************************************************************************
End of nandwrite global
******************************************************************************/
extern unsigned long crc32 (unsigned long, const unsigned char *, unsigned int);
/**
*@brief Reverse a string
*@param[in,out] pStr String to reverse.
*/
void Reverse(char *pStr)
{
int i,j;
char temp;
for(i = 0,j = strlen(pStr) - 1;i < j; i++, j--){
temp = pStr[i];
pStr[i] = pStr[j];
pStr[j] = temp;
}
}
/**
*@brief Take the file name from string 复制pbuf字符串到pFileName
*@param[in] pbuf String include file name in it.
*@param[in] nBufSize String size.
*@param[out] pFileName File name get from string.
*/
void TakeFileName(char *pbuf, int nBufSize, char *pFileName)
{
int i,count = 0;
for(i = 0;i < nBufSize;i++){ //总共nBuffer字节
if(pbuf[i] != '"') //遇到“”就跳过,直到末尾
continue;
gLastI = i;
while(--i >= 0 )
{
if(pbuf[i] == '/' || pbuf[i] == '\\')
break;
pFileName[count++] = pbuf[i];
}
break;
}
pFileName[count] = '\0';
Reverse(pFileName);
return ;
}
/**
*@brief Get file name from stdin 从标准输入获取文件名字?*@param[in] fd File fd.
*@param[in] pBuf Buffer to store temp date.
*@param[in] nBufSize Buffer size.
*@return File name.
*/
char* GetFileName(int fd, char *pBuf, int nBufSize)
{
static char strFileName[FILE_NAME_SIZE + 1]; //文件名strFileName[81]
const char target[]="filename=\"; //目标字符串,需要删除的字符串,过滤
int nReadBytes = 0, count = 0, i,tempI = gLastI;
while((nReadBytes = read(fd, pBuf, nBufSize)) > 0) //读取输入,存到pBuf中,每次读取nBuffer字节,每次512字节
{
for(i = 0;i < nBufSize;i++) //对读到的buffer进行操作,512字节
{
if(count == strlen(target))
{
gLastI = i;
TakeFileName(&pBuf[i], nReadBytes - i, strFileName); //当前i下下标开始为真正的文件名? //长为nReadBytes - i字节,从pBuf值
if(strFileName[0] == '\0')
{
count = 0;
gLastI = tempI;
}else{
gLastRead = nReadBytes;
return strFileName;
}
}
else if(pBuf[i] == target[count]) //过滤掉"filename=\"字符串
count++;
else
count = 0;
}
}
return NULL;
}
/**
*@brief Skip package header 跳过包头
*@param[in] fd File fd.
*@param[in] pBuf Buffer to store temp date.
*@param[in] nBufSize Buffer size.
*@return Value of buffer index
*/
int SkipHeader(int fd, char *pBuf, int nBufSize) //跳过包头
{
int i,count = 0;
const char target[]="\r\n\r\n";
if(gLastRead > 0) //最后一次读 >0
{
i = (gLastI > 0) ? gLastI : 0;
do
{
for(; i < gLastRead;i++){ //对读到的nBufsize数据进行操作
if(count == strlen(target))
{
return i;
} else if(pBuf[i] == target[count])
count++;
else
count = 0;
}
i = 0;
}while((gLastRead = read(fd, pBuf, nBufSize)) > 0); //从标准输入中、读取nBufsize数据,其实也就是跳到target的末尾
}
else //最后一次读 == 0
{
while((gLastRead = read(fd, pBuf, nBufSize)) > 0) //从标准输入中、读取nBufsize数据,其实也就是跳到target的末尾
{
for(i = 0; i < gLastRead;i++){
if(count == strlen(target)){
return i;
} else if(pBuf[i] == target[count])
count++;
else
count = 0;
}
}
}
return -1;
}
/**
*@brief Create a file
*@param[in] pFileName File name. 文件名
*@param[in] fd File fd. 文件句柄
*@param[in] pBuf Buffer to store temp date. 缓冲区 存放临时数据
*@param[in] nBufSize Buffer size. 总的大小
*@param[out] pFile File information to new file. 新文件的文件信息
*@retval ENVALID_PRM gLastRead is not set correctly.
*@retval FAIL File create fail.
*@retval SUCCESS File create success.
*/
int CreateFile(char *pFileName, int fd, char *pBuf, int nBufSize, FileInfo* pFile) //创建文件,读取输入,填充pFile
{
char path[100];
const char target[]="\r\n-----------------------------";
int i,count = 0;
FILE *db;
db = fopen("/tmp/createfile.txt", "wt"); //打开文件db
sprintf(path, "%s/%s", TEMP_PATH, pFileName); //根据文件名字构建临时文件名字
fprintf(db,"path=<%s>\n", path); //临时目录
fprintf(db,"gLastRead=%d\n", gLastRead); //最后一次读
fclose(db); //文件文件
if(gLastRead <= 0)
return ENVALID_PRM;
i = (gLastI > 0) ? gLastI : 0; //最后一次读
DBG("i = %d\n", i);
strcpy(pFile->strFileName, pFileName); //填充fileinfo
pFile->fd = fd;
pFile->nStartOffset = lseek(fd, 0, SEEK_CUR) - gLastRead + i; //文件当前位置 - 最后一次读的大小 - gLastI
do
{
DBG("gLastRead : %d\n", gLastRead);
for(; i < gLastRead;i++) //循环处理读到的数据,去掉前面的 "\r\n-----------------------------"字符
{
if(count == strlen(target)){
gLastI = i; //到第i下标
pFile->nEndOffset = lseek(fd, 0, SEEK_CUR) - gLastRead + i - count;
pFile->nLength = pFile->nEndOffset - pFile->nStartOffset; //实际读到的大小
DBG("File length = %ld\n", pFile->nLength);
return SUCCESS;
} else if(pBuf[i] == target[count]){
count++;
} else if(count > 0){
count = 0;
i--;
} else {
//
putc(pBuf[i], fp);
}
}
i = 0;
}while((gLastRead = read(fd, pBuf, nBufSize)) > 0); //继续从标准输入中读取数据
gLastI = -1;
return FAIL;
}
/**
*@brief Print image type
*@param[in] hdr uImage header.
*/
static void print_type (image_header_t *hdr)
{
char *os, *arch, *type, *comp;
switch (hdr->ih_os) {
case IH_OS_INVALID:
os = "Invalid OS";
break;
case IH_OS_NETBSD:
os = "NetBSD";
break;
case IH_OS_LINUX:
os = "Linux";
break;
case IH_OS_VXWORKS:
os = "VxWorks";
break;
case IH_OS_QNX:
os = "QNX";
break;
case IH_OS_U_BOOT:
os = "U-Boot";
break;
case IH_OS_RTEMS:
os = "RTEMS";
break;
default:
os = "Unknown OS";
break;
}
switch (hdr->ih_arch) {
case IH_CPU_INVALID:
arch = "Invalid CPU";
break;
case IH_CPU_ALPHA:
arch = "Alpha";
break;
case IH_CPU_ARM:
arch = "ARM";
break;
case IH_CPU_AVR32:
arch = "AVR32";
break;
case IH_CPU_I386:
arch = "Intel x86";
break;
case IH_CPU_IA64:
arch = "IA64";
break;
case IH_CPU_MIPS:
arch = "MIPS";
break;
case IH_CPU_MIPS64:
arch = "MIPS 64 Bit";
break;
case IH_CPU_PPC:
arch = "PowerPC";
break;
case IH_CPU_S390:
arch = "IBM S390";
break;
case IH_CPU_SH:
arch = "SuperH";
break;
case IH_CPU_SPARC:
arch = "SPARC";
break;
case IH_CPU_SPARC64:
arch = "SPARC 64 Bit";
break;
case IH_CPU_M68K:
arch = "M68K";
break;
case IH_CPU_MICROBLAZE:
arch = "Microblaze";
break;
case IH_CPU_NIOS:
arch = "Nios";
break;
case IH_CPU_NIOS2:
arch = "Nios-II";
break;
default:
arch = "Unknown Architecture";
break;
}
switch (hdr->ih_type) {
case IH_TYPE_INVALID:
type = "Invalid Image";
break;
case IH_TYPE_STANDALONE:type = "Standalone Program";
break;
case IH_TYPE_KERNEL:
type = "Kernel Image";
break;
case IH_TYPE_RAMDISK:
type = "RAMDisk Image";
break;
case IH_TYPE_MULTI:
type = "Multi-File Image";
break;
case IH_TYPE_FIRMWARE:
type = "Firmware";
break;
case IH_TYPE_SCRIPT:
type = "Script";
break;
case IH_TYPE_FLATDT:
type = "Flat Device Tree";
break;
default:
type = "Unknown Image";
break;
}
switch (hdr->ih_comp) {
case IH_COMP_NONE:
comp = "uncompressed";
break;
case IH_COMP_GZIP:
comp = "gzip compressed";
break;
case IH_COMP_BZIP2:
comp = "bzip2 compressed";
break;
default:
comp = "unknown compression";
break;
}
OUTPUT("%s %s %s (%s)", arch, os, type, comp);
}
/**
*@brief Print size
* print sizes as "xxx kB", "xxx.y kB", "xxx MB" or "xxx.y MB" as needed;
* allow for optional trailing string (like "\n")
*@param[in] size Size to print.
*@param[in] s Tailing string.
*/
void print_size (unsigned long size, const char *s)
{
unsigned long m, n;
unsigned long d = 1 << 20;
/* 1 MB */
char c = 'M';
if (size < d) {
/* print in kB */
c = 'k';
d = 1 << 10;
}
n = size / d;
m = (10 * (size - (n * d)) + (d / 2) ) / d;
if (m >= 10) {
m -= 10;
n += 1;
}
OUTPUT ("%2ld", n);
if (m) {
OUTPUT (".%ld", m);
}
OUTPUT (" %cB%s", c, s);
}
/**
*@brief Print image header
*@param[in] hdr uImage header.
*/
void print_image_hdr (image_header_t *hdr)
{
OUTPUT("<H6> Image Name: %.*s\n", IH_NMLEN, hdr->ih_name);
OUTPUT("<H6> Image Type: ");print_type(hdr);
OUTPUT ("<H6>\n Data Size: %d Bytes = ", ntohl(hdr->ih_size));
print_size (ntohl(hdr->ih_size), "\n");
OUTPUT ("<H6> Load Address: %08x\n"
"<H6> Entry Point: %08x\n",
ntohl(hdr->ih_load), ntohl(hdr->ih_ep));
if (hdr->ih_type == IH_TYPE_MULTI) {
int i;
unsigned long len;
unsigned long *len_ptr;
len_ptr = (unsigned long *)
((unsigned long)hdr + sizeof(image_header_t));
OUTPUT("<H6> Contents:\n");
for (i=0; (len = ntohl(*len_ptr)); ++i, ++len_ptr) {
OUTPUT ("<H6> Image %d: %8ld Bytes = ", i, len);
print_size (len, "\n");
}
}
}
/**
*@brief Write file to nand
*@param[in] pFile File to write.
*@param[in] pBlockName Block device name.
*@param[in] offset Offset to block start.
*@retval SUCCESS Nand write complete.
*@retval FAIL Error occures.
*/
//FlashNand(pFile, FILESYSBLOCK, 0);
int FlashNand(FileInfo* pFile, char *pBlockName, unsigned long offset) //写文件内容到nand flash
{
char cmd[100]; //命令
int cnt, fd, imglen = 0, pagelen, baderaseblock, blockstart = -1;
struct mtd_info_user meminfo; //mtd的分区信息
struct mtd_oob_buf oob; //mtd的带外数据
loff_t offs; //偏移值
int ret, readlen;
int oobinfochanged = 0; //带外数据信息改变标志
struct nand_oobinfo old_oobinfo; //nand的带外数据信息
ret = SUCCESS;
sprintf(cmd, "/usr/sbin/flash_eraseall -j %s > %s/%s\n", pBlockName, // 构建执行擦除命令 : /usr/sbin/flash_eraseall -j "/dev/mtd2" /tmp "tmpfile.txt"
TEMP_PATH, TEMP_FILE);
if(system(cmd)){ //执行命令
OUTPUT("<H6>Fail on erase block\r\n");
ret = FAIL;
goto EXIT_FLASHNAND;
}
#if 0 //不会编译到这里
sprintf(cmd, "/usr/sbin/nandwrite -s %ld -f -p -j %s %s/%s > %s/%s\n", offset, pBlockName,
TEMP_PATH, pFile->strFileName, TEMP_PATH, TEMP_FILE); //构建写命令 : nandwrite 块设备写入命令 偏移 块名 临时路径 临时文件
DBG(cmd);
if(system(cmd)){ //执行nandwrite 写命令
OUTPUT("<H6>Fail on update new firmware\r\n");
sprintf(cmd, "rm -f %s/%s\n", TEMP_PATH, TEMP_FILE);
system(cmd);
return FAIL;
}
#else
/* mtdoffset = offset, forcelegacy = 1, pad = 1, forcejffs2 = 1,
mtd_device = pBlockName, img = pFile */
//mtd的偏移,pad, jffs2, 设备名,映像
memset(oobbuf, 0xff, sizeof(oobbuf)); //清空带外数据 //unsigned char oobbuf[64];
if ((fd = open(pBlockName, O_RDWR)) == -1) { //打开/dev/mtd2块设备
OUTPUT("<H6>open flash error\r\n");
ret = FAIL;
goto EXIT_FLASHNAND;
}
/* Fill in MTD device capability structure */ //获得mtd块设备的内容,
if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { //struct mtd_info_user meminfo; /dev/mtd2的分区信息
OUTPUT("<H6>MEMGETINFO error\r\n");
close(fd);
ret = FAIL;
goto EXIT_FLASHNAND;
}
//设置指定mtd设备块数目的擦除大小,匹配jifs2虚拟系统的块大小
meminfo.erasesize *= blockalign; //默认使用16k的块大小
/* Make sure device page sizes are valid */ //确保设备页大小是有效的,
if (!(meminfo.oobsize == 16 && meminfo.oobblock == 512) //mtd_info_user分区中的oob数据大小和块
&& !(meminfo.oobsize == 8 && meminfo.oobblock == 256) && //oob块大小和每块oob的数据数量大小
!(meminfo.oobsize == 64 && meminfo.oobblock == 2048)) {
OUTPUT("<H6>Unknown flash (not normal NAND)\r\n");
close(fd);
ret = FAIL;
goto EXIT_FLASHNAND;
}
//获取/dev/mtd2设备当前oob带外数据
if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
OUTPUT("<H6>MEMGETOOBSEL error\r\n");
close (fd);
ret = FAIL;
goto EXIT_FLASHNAND;
}
//强制带外数据安排给jifs2或者yaffs,
//force oob layout for jffs2 or yaffs ?
if (forcejffs2 || forceyaffs)
{ //根据jifs2或者yaffs选择不同的oob数据,ecc校验不同
struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;
if (autoplace) { // 对于预留的-j/y选项,不允许自动布局,autoplace为0
OUTPUT("<H6>Autoplacement is not possible for legacy -j/-y options\n");
ret = FAIL;
goto restoreoob;
} //当前不强制预留,而且为MTD设备的nand ecc自动布局,失败
if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) {
OUTPUT("<H6>Use -f option to enforce legacy placement on autoplacement enabled mtd device\n");
ret = FAIL;
goto restoreoob;
}
if (meminfo.oobsize == 8) { //如果/dev/mtd2的每块oob的数据大小为8字节,不允许使用yaffs
if (forceyaffs)
{
OUTPUT("<H6>YAFSS cannot operate on 256 Byte page size");
ret = FAIL;
goto restoreoob;
}
//则将ecc的字节改为3字节,适应
jffs2_oobinfo.eccbytes = 3;
}
if (ioctl (fd, MEMSETOOBSEL, oobsel) != 0) { //向mtd设备设置带外数据
OUTPUT("<H6>MEMSETOOBSEL error\r\n");
ret = FAIL;
goto restoreoob;
}
}
oob.length = meminfo.oobsize; //dev/mtd2的每块oob的数据大小,设置mtd_oob_buf和内存
oob.ptr = noecc ? oobreadbuf : oobbuf;
//设置文件开始读,设置映像文件偏移为开始位置
lseek(pFile->fd, pFile->nStartOffset, SEEK_SET);
//获得映像文件的长度
imglen = pFile->nLength;
//一页的大小=/dev/mtd2中oob块大小 + 0
pagelen = meminfo.oobblock + ((writeoob == 1) ? meminfo.oobsize : 0);
if ((!pad) && ((imglen % pagelen) != 0)) { //检查是否页对齐,映像文件的长度/一页的大小
OUTPUT("<H6>Input file is not page aligned\n");
ret = FAIL;
goto restoreoob;
}
//页数 = 映像文件长度 / 1页的大小
//检查长度和设备适应 Check, if length fits into device
//页数*mtd2中oob块大小 > (/dev/mtd的总大小 - 偏移0)
//表示不够内存能够装下映像文件
if ( ((imglen / pagelen) * meminfo.oobblock) > (meminfo.size - offset)) {
OUTPUT("<H6>Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n",
imglen, pagelen, meminfo.oobblock, meminfo.size);
OUTPUT("<H6>Input file does not fit into device error\r\n");
ret = FAIL;
goto restoreoob;
}
//从输入输出到nand设备中获取数据 Get data from input and write to the device
while (imglen && (offset < meminfo.size)) {
//新的eraseblock,检查坏块,一直循环去确保如果mtd偏移因为
//一个坏块改变,然后下一个块将被写入也会被检查.防止了跳过块后出现错误
while (blockstart != (offset & (~meminfo.erasesize + 1))) {
blockstart = offset & (~meminfo.erasesize + 1);
offs = blockstart; //快起始,快偏移
baderaseblock = 0; //擦除坏块数
DBG("Writing data to block %x\n", blockstart); //写数据到起始块
//对于坏块,检查一个擦除块中的所有块 /* Check all the blocks in an erase block for bad blocks */
do {
if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
OUTPUT("<H6>ioctl(MEMGETBADBLOCK) error\r\n");
ret = FAIL;
goto restoreoob;
}
if (ret == 1) { //发现坏块,打印坏块数,地址,进行擦除坏块操作
baderaseblock = 1;
DBG("Bad block at %x, %u block(s) from %x will be skipped\n", (int) offs, blockalign, blockstart);
}
if (baderaseblock) {
offset = blockstart + meminfo.erasesize;
}
offs += meminfo.erasesize / blockalign ; //擦除位置后移
} while ( offs < blockstart + meminfo.erasesize );
}
readlen = meminfo.oobblock; //mtd的块大小
if (pad && (imglen < readlen)) //如果映像文件的大小小于/dev/mtd2的块大小,
//将多余的内存置为0xff
{
readlen = imglen;
memset(writebuf + readlen, 0xff, meminfo.oobblock - readlen);
}
//从映像文件中读页数据 Read Page Data from input file
if ((cnt = read(pFile->fd, writebuf, readlen)) != readlen) {
if (cnt == 0)
// EOF
break;
OUTPUT("<H6>File I/O error on input file\r\n");
ret =FAIL;
goto restoreoob;
}
if (writeoob) {
//从映像文件中读带外数据 Read OOB data from input file, exit on failure
//读一块oob数据的大小的内容
if ((cnt = read(pFile->fd, oobreadbuf, meminfo.oobsize)) != meminfo.oobsize) {
OUTPUT("<H6>File I/O error on input file");
ret = FAIL;
goto restoreoob;
}
if (!noecc) {
int i, start, len;
/*
* We use autoplacement and have the oobinfo with the autoplacement
* information from the kernel available
*
* Modified to support out of order oobfree segments,
* such as the layout used by diskonchip.c
*/
if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) {
for (i = 0;old_oobinfo.oobfree[i][1]; i++) {
/* Set the reserved bytes to 0xff */
start = old_oobinfo.oobfree[i][0];
len = old_oobinfo.oobfree[i][1];
memcpy(oobbuf + start,
oobreadbuf + start,
len);
}
} else {
/* Set at least the ecc byte positions to 0xff */
start = old_oobinfo.eccbytes;
len = meminfo.oobsize - start;
memcpy(oobbuf + start,
oobreadbuf + start,
len);
}
}
//写入oob数据首先,eec也将被写入这里
oob.start = offset;
if (ioctl(fd, MEMWRITEOOB, &oob) != 0) {
OUTPUT("<H6>ioctl(MEMWRITEOOB) error\r\n");
ret = FAIL;
goto restoreoob;
}
imglen -= meminfo.oobsize; //映像文件长度减去mtd减去1块oob的数据长度
}
//将页数据全部写入/dev/mtd
if (pwrite(fd, writebuf, meminfo.oobblock, offset) != meminfo.oobblock) {
OUTPUT("<H6>pwrite error\r\n");
ret = FAIL;
goto restoreoob;
}
imglen -= readlen;
offset += meminfo.oobblock;
}
restoreoob:
if (oobinfochanged) {
if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
OUTPUT ("<H6>MEMSETOOBSEL error\r\n");
close (fd);
ret = FAIL;
goto EXIT_FLASHNAND;
}
}
close(fd);
if (imglen > 0) {
OUTPUT ("<H6>Data did not fit into device, due to bad blocks\n");
ret = FAIL;
}
#endif
EXIT_FLASHNAND:
return ret;
}
/**
*@brief Write kernel to flash
*@param[in] pFileName Kernel file name.
*@retval SUCCESS Kernel write success.
*@retval FAIL Error occures.
*/
int FlashKernel(char *pFileName)
{
char cmd[80], path[100];
//
FILE* fp;
sprintf(path, "%s/update.sh", TEMP_PATH);
#if 0
fp = fopen(path, "wt");
if(fp){
fprintf(fp, "/usr/sbin/flash_eraseall -j %s\n", KERNELBLOCK);
fprintf(fp ,"/usr/sbin/nandwrite -p -j %s %s/%s\n", KERNELBLOCK, TEMP_PATH,
pFileName);
fclose(fp);
} else
return FAIL;
sprintf(cmd, "chmod 700 %s/update.sh", TEMP_PATH);
if(system(cmd)){
OUTPUT("<H6>Can't change mode\r\n");
return FAIL;
}
sprintf(cmd, "%s/update.sh\n", TEMP_PATH);
return system(cmd);
#else
sprintf(cmd, "/usr/sbin/flash_eraseall -j %s > %s/%s\n", KERNELBLOCK,
TEMP_PATH, TEMP_FILE);
if(system(cmd)){
OUTPUT("<H6>Fail on erase kernel block\r\n");
sprintf(cmd, "rm -f %s/%s\n", TEMP_PATH, TEMP_FILE);
system(cmd);
return FAIL;
}
sprintf(cmd, "/usr/sbin/nandwrite -p -j %s %s/%s > %s/%s\n", KERNELBLOCK,
TEMP_PATH, pFileName, TEMP_PATH, TEMP_FILE);
if(system(cmd)){
OUTPUT("<H6>Fail on update kernel\r\n");
sprintf(cmd, "rm -f %s/%s\n", TEMP_PATH, TEMP_FILE);
system(cmd);
return FAIL;
}
sprintf(cmd, "rm -f %s/%s\n", TEMP_PATH, TEMP_FILE);
system(cmd);
return SUCCESS;
#endif
}
/**
*@brief Do kernel update
*@param[in] pFile Kernel file information.
*@retval SUCCESS Update success.
*@retval FAIL Fail to update.
*@retval UNKOWN Unkown file format.
*/
int DoKernelUpdate(FileInfo* pFile) //内核更新文件
{
image_header_t hdr;
unsigned long checksum,len, data; //校验和,长度,数据
long nFileSize; //文件大小
void *pImage = NULL; //映像
int ret = SUCCESS;
lseek(pFile->fd, pFile->nStartOffset, SEEK_SET); //设置当前文件的开始位置
if(read(pFile->fd, &hdr, sizeof(hdr)) != sizeof(hdr)){ //读取映像文件的hdr
ret = UNKOWN;
goto EXIT_UPDATE_K;
}
if(ntohl(hdr.ih_magic) != IH_MAGIC){ //将一个无符号长整形数从网络字节顺序转换为主机字节顺序,确保正确
ret = UNKOWN;
goto EXIT_UPDATE_K;
}
DBG("Valid magic number\r\n");
data = (unsigned long)&hdr; //取得4字节数据
len = sizeof(hdr); //hdr的大小
checksum = ntohl(hdr.ih_hcrc); //校验crc
hdr.ih_hcrc = 0;
if (crc32 (0, (unsigned char *)data, len) != checksum) {
DBG ("Bad Header Checksum\n");
ret = UNKOWN;
goto EXIT_UPDATE_K;
}
print_image_hdr(&hdr); //打印映像内核信息
len = ntohl(hdr.ih_size); //分配 映像指针
pImage = malloc(len);
if(pImage == NULL){
OUTPUT("<H6>No enough memory to cache uImage\r\n");
ret = FAIL;
goto EXIT_UPDATE_K;
}
data = (unsigned long)pImage; //取得映像4字节数据
if(read(pFile->fd, pImage, len) != len){ //从文件中读取信息放入data
DBG("<H6>File size error\r\n");
ret = UNKOWN;
goto EXIT_UPDATE_K;
}
DBG(" Verifying Checksum ... \r\n");
if (crc32 (0, (unsigned char *)data, len) != ntohl(hdr.ih_dcrc)) { //crc校验
DBG("Bad Data CRC\n");
ret = UNKOWN;
goto EXIT_UPDATE_K;
}
DBG("OK\n");
if((nFileSize = pFile->nLength) < 0){
OUTPUT("<H6>Error on get file size\r\n");
ret = FAIL;
goto EXIT_UPDATE_K;
}
data = nFileSize; //文件大小
if(data != (len + sizeof(hdr))){
OUTPUT("<H6>Unkown data found at tail\r\n");
ret = FAIL;
goto EXIT_UPDATE_K;
}
if(FlashNand(pFile, KERNELBLOCK, 0) != SUCCESS){
ret = FAIL;
goto EXIT_UPDATE_K;
}
EXIT_UPDATE_K:
if(pImage)
free(pImage);
return ret;
}
/**
*@brief Root file system update
*@param[in] pFile File information.
*@retval SUCCESS Update success.
*@retval FAIL Update fail.
*@retval UNKOWN Unkown file format.
*/
int DoRootFileSysUpdate(FileInfo* pFile)
{
//
char cmd[80];
/* We only check filename */
DBG("Enter %s\r\n", __func__);
if(strncmp(FILESYS_PRE, pFile->strFileName, strlen(FILESYS_PRE))){
DBG("Not root file\r\n");
return UNKOWN;
}
DBG("File name ok\r\n");
#if 0
/* CRC check */
sprintf(cmd, "gzip -t %s/%s\n", TEMP_PATH, pFileName);
if(system(cmd)){
DBG("Can't check\r\n");
return FAIL;
}
#endif
return FlashNand(pFile, FILESYSBLOCK, 0);
}
/**
*@brief Firmware update
*@param[in] pFile File information.
*@retval SUCCESS Firmware update successfully.
*@retval FAIL Firmware update failed.
*@retval UNKOWN Unkown file format.
*/
int DoFirmwareUpdate(FileInfo* pFile)
{
int ret;
char cmd[80];
ret = DoKernelUpdate(pFile); //内核更新 文件
if(ret == SUCCESS){
OUTPUT("<H4>\nKernel update success,\r\n");
} else if(ret == UNKOWN){
if((ret = DoRootFileSysUpdate(pFile)) == SUCCESS){
OUTPUT("<H4>File system update success,\r\n");
sprintf(cmd, "rm -f %s\n", SYS_FILE);
system(cmd);
sprintf(cmd, "rm -f %s\n", LOG_FILE);
system(cmd);
}
}
return ret;
}
/**
*
*/
int main(int argc, char **argv)
{
char buffer[512],*pFileName;
struct stat st; //文件状态
FileInfo tFile;
#ifdef U_DEBUG
FILE *fp;
#endif
fstat(STDIN_FILENO, &st); //由文件描述词取得文件状态,STDIN_FILENO就是标准输入设备(一般是键盘)
write(STDOUT_FILENO, update_html_head, sizeof(update_html_head)); //将网页写到终端,显示
if((pFileName = GetFileName(STDIN_FILENO, buffer, sizeof(buffer))) != NULL) //从标准输入中获取文件名字
{
#ifdef U_DEBUG
fp = fopen("/tmp/main.dbg", "wt"); //以写的方式打开文件 /tmp/main.dbg
fprintf(fp, "pFileName=<%s>\n",pFileName); //向文件中写入文件名
#endif
gLastI = SkipHeader(STDIN_FILENO, buffer, sizeof(buffer)); //跳过包头
#ifdef U_DEBUG
fprintf(fp, "gLastI=%d\n",gLastI);
#endif
if(CreateFile(pFileName, STDIN_FILENO, buffer, sizeof(buffer), &tFile)==SUCCESS){ //创建文件tFile
OUTPUT("<H4>File create success\r\n");
OUTPUT("<H4>\r\n");
switch(DoFirmwareUpdate(&tFile)){ //更新文件tFile
case SUCCESS:
OUTPUT("Firmware update success\r\n");
OUTPUT("<H4> Please Restart IPNC by Clicking on \"Restart Camera\" Button\r\n");
break;
case UNKOWN:
OUTPUT("<H4>Unknown file format\r\n");
break;
default:
OUTPUT("<H4>Unknown error\r\n");
break;
}
}else
OUTPUT("<H4>Fail to create file\r\n");
#ifdef U_DEBUG
fclose(fp);
#endif
} else {
OUTPUT("<H3>Unknown file.</H3>\r\n");
}
write(STDOUT_FILENO, update_html_end, sizeof(update_html_end));
return SUCCESS;
}