机身码写入到设备中,一般是写入EEPROM或是FLASH中,EEPROM更为可靠,但因为产品中没有EEPROM,所以就只能写在FLASH中了。
对于WINCE6.0系统来说,bootloader都会为至少TOC分配一块,如果机身码信息量比较小,可以考虑写在TOC所在的块中,但如果信息量大,可以专门指定一个block来保存机身码。
1. 保存机身码的变量定义
我们机身码目前只有8个字节,信息量比较小,可以保存在TOC所在的block中,TOC结构体定义如下:
typedef struct _TOC {
typedef struct _TOC {
DWORD dwSignature;
// How to boot the images in this TOC.
// This could be moved into the image descriptor if desired,
// but I prefer to conserve space.
BOOT_CFG BootCfg;
// Array of Image Descriptors.
IMAGE_DESCRIPTOR id[MAX_TOC_DESCRIPTORS];
CHAININFO chainInfo;
UINT8 CwDeviceId[8];//kandi add at 2013.09.17
} TOC, *PTOC; // 512 bytes
我们定义了UINT8 CwDeviceId[8]用于保存机身码。实现的思路是在FMD驱动中写入,在eboot读取出来并保存到BSP_ARGS结构体中,这样开机之后就可以取出机身码了,此结构体定义如下:
typedef struct {
OAL_ARGS_HEADER header;
UINT8 deviceId[16]; // Device identification
OAL_KITL_ARGS kitl;
UINT8 uuid[16];
BOOL bUpdateMode; // TRUE = Enter update mode on reboot.
BOOL bHiveCleanFlag; // TRUE = Clean hive at boot
BOOL bCleanBootFlag; // TRUE = Clear RAM, hive, user store at boot
BOOL bFormatPartFlag; // TRUE = Format partion when mounted at boot
DWORD nfsblk;
HANDLE g_SDCardDetectEvent; //kim
DWORD g_SDCardState ;
DWORD dwLcdType;
BOOL ChargingState;///<TRUE--->charge full
UINT8 CwDeviceId[8];//kandi add at 2013.09.17
} BSP_ARGS, *PBSP_ARGS;
定义了UINT8 CwDeviceId[8],用于保存从TOC block中读取的机身码。
2. 机身码的读写
我们把机身码的写入放在FMD驱动中FMD_OEMIoControl函数中实现,应用层通过DeviceIoControl的方式来调用,首先就涉及到控制码的定义,如下:
#define IOCTL_DISK_USER_START 0x7E0
#define IOCTL_DISK_BASE FILE_DEVICE_DISK
#define IOCTL_DISK_USER(Function) \
CTL_CODE( IOCTL_DISK_BASE, IOCTL_DISK_USER_START+Function, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_FMD_WRITE_DEVICE_ID IOCTL_DISK_USER(13)
#define IOCTL_FMD_READ_DEVICE_ID IOCTL_DISK_USER(14)
机身码的读写是通过BOOL control_device_id(PBYTE pInBuf, DWORD nInBufSize,BOOLaction,DWORD block)函数来实现的,pInBuf参数传递进来要写入的机身码,nInBufSize表示机身码长度,action表示读或是写的动作,block表示是从TOC还是专门指定的block中读写,此函数实现如下:
BOOL control_device_id(PBYTE pInBuf, DWORD nInBufSize,BOOL action,DWORD block)
{
BOOL result = TRUE;
BSP_ARGS *pBsp_Args = (BSP_ARGS *) IMAGE_SHARE_ARGS_UA_START;
SectorInfo si, si2;
DWORD i=0, SectorValue=0, BlockValue;
CHAR g_PageBuf[LB_PAGE_SIZE];
const PTOC g_pTOC = (PTOC)&g_PageBuf;
if(action)
{
RETAILMSG(1, (TEXT(" control_device_id() --->write device id\r\n")));
if ( !FMD_ReadSector(BLOCK_TO_SECTOR(block), (PUCHAR)g_PageBuf, &si, 1) )
{
RETAILMSG(1, (TEXT("control_device_id() : failed to read block (0x%x).\r\n"), block));
return FALSE;
}
if (!FMD_EraseBlock(block))
{
RETAILMSG(1, (TEXT("control_device_id(): failed to erase block (0x%x).\r\n"), block));
return FALSE;
}
// setup our metadata so filesys won't stomp us
si.dwReserved1 = 0;
si.bOEMReserved = OEM_BLOCK_RESERVED | OEM_BLOCK_READONLY;
si.bBadBlock = BADBLOCKMARK;
si.wReserved2 = 0;
if(nInBufSize > DEVICE_ID_LENGTH)
{
nInBufSize = DEVICE_ID_LENGTH;
}
for(i=0;i<nInBufSize;i++)
{
if(block == TOC_BLOCK)
{
g_pTOC->CwDeviceId[i] = pInBuf[i];
}
else
{
g_PageBuf[i] = pInBuf[i];
}
}
if ( !FMD_WriteSector(BLOCK_TO_SECTOR(block), (PUCHAR)&g_PageBuf, &si, 1) )
{
RETAILMSG(1, (TEXT("control_device_id(): failed to write block (0x%x).\r\n"), block));
return FALSE;
}
}
else
{
// read it back
RETAILMSG(1, (TEXT(" control_device_id()--->read device id\r\n")));
if ( !FMD_ReadSector(BLOCK_TO_SECTOR(block), (PUCHAR)&g_PageBuf, &si2, 1) )
{
RETAILMSG(1, (TEXT("control_device_id() : failed to read block (0x%x).\r\n"), block));
return FALSE;
}
else
{
memset(pBSPArgs->CwDeviceId,0x0,DEVICE_ID_LENGTH);
if(block == TOC_BLOCK)
{
memcpy(pBSPArgs->CwDeviceId, g_pTOC->CwDeviceId, nInBufSize);
}
else
{
memcpy(pBSPArgs->CwDeviceId, g_PageBuf, nInBufSize);
}
for(i=0;i<nInBufSize;i++)
{
RETAILMSG(TRUE,(TEXT(" write_device_id() --->after write device_id[%d] : 0x%x \r\n"),i, pBSPArgs->CwDeviceId[i]));
}
}
}
return result;
}
在实现此函数过程遇到一系列的问题,总结如下:
⑴编译问题,因为要在fmd.cpp中实现机身码的写入及读取动作,需要用到eboot中loader.h的定义,可通过下面两种方式较为方便引用loader.h函数:
其一:
在fmd.cpp中包含#include <..\\..\\..\\BOOTLOADER\\Eboot\\Eboot\\loader.h>
其二:
在fmd文件夹下Sources文件的INCLUDES=后面增加$(_TARGETPLATROOT)\SRC\BOOTLOADER\Eboot\Eboot
在fmd.cpp中包含#include "loader.h"
⑵写入时导致系统DataAbort问题,主要是指针操作的问题,一定要让指针指向分配好的内存区域。
⑶在向block写入数据之前一定要先擦除,否则无法成功写入。
设备启动时,在eboot阶段的OEMPlatformInit函数调用void read_device_id(DWORD block)函数来从FLASH获得机身码,此函数定义如下:
BOOL read_device_id(DWORD block)
{
UINT8 i=0;
static UCHAR SectorBuf[LB_PAGE_SIZE];
const PTOC pTOCBuf = (PTOC)&SectorBuf;
if ( !FMD_ReadSector(BLOCK_TO_SECTOR(block), (PUCHAR)&SectorBuf, NULL, 1) )
{
RETAILMSG(1, (TEXT(" Read id: Unable to read/verify TOC\r\n")));
return FALSE;
}
else
{
if(block ==TOC_BLOCK)
{
memcpy(pBSPArgs->CwDeviceId, pTOCBuf->CwDeviceId, DEVICE_ID_LENGTH);
RETAILMSG(1, (TEXT(" Read device id from Toc sector\r\n")));
}
else
{
memcpy(pBSPArgs->CwDeviceId, SectorBuf, DEVICE_ID_LENGTH);
RETAILMSG(1, (TEXT(" Read device id from sysm info sector\r\n")));
}
for(i=0;i<DEVICE_ID_LENGTH;i++)
{
RETAILMSG(TRUE,(TEXT(" read_device_id[%d] : 0x%x\r\n"),i,pBSPArgs->CwDeviceId[i]) ); //bad
}
}
return TRUE;
}
3. 应用程序读写机身码
在实现过程中遇到下面一些问题:
⑴ DeviceIoControl函数控制码不规范问题,刚开始随便定义#define IOCTL_FMD_WRITE_DEVICE_ID 1之前这么使用是没有问题的,但这里这么使用就有问题,通过调用GetLastError()函数获得返回值为87,在winerror.h中找到此值对应含义如下:
//
// MessageId: ERROR_INVALID_PARAMETER
//
// MessageText:
//
// The parameter is incorrect.
//
#define ERROR_INVALID_PARAMETER 87L // dderror
原来是控制参数无效,才意识到这个问题,重新定义如下:
#define IOCTL_DISK_USER_START 0x7E0
#define IOCTL_DISK_USER_END 0x7FF
#define IOCTL_DISK_BASE FILE_DEVICE_DISK
#define METHOD_BUFFERED 0
#define FILE_ANY_ACCESS 0
#define CTL_CODE( DeviceType, Function, Method, Access ) ( \
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)
#define IOCTL_DISK_USER(Function) \
CTL_CODE( IOCTL_DISK_BASE, IOCTL_DISK_USER_START+Function, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_FMD_WRITE_DEVICE_ID IOCTL_DISK_USER(13)
才解决了这个问题。
⑵Cstring和char转换问题
写函数的实现如下:
void CDeviceIDDlg::OnBnClickedWriteDeviceId()
{
// TODO: Add your control notification handler code here
BOOL status = TRUE;
DWORD result=0, i=0, nSize=0,nLength=0;
CString cstemp = L"",csDeviceId = L"";
GetDlgItem(IDC_EDIT_WRITE_DEVICE_ID)->GetWindowText(cstemp);
nLength=cstemp.GetLength();
if(nLength > DEVICE_ID_MAX)
{
nLength = DEVICE_ID_MAX;
}
nSize = WideCharToMultiByte(CP_ACP,NULL,cstemp.GetBuffer(nLength),-1,NULL,0,NULL,FALSE);
char *szDeviceId=new char[nSize];
WideCharToMultiByte(CP_ACP,NULL,cstemp.GetBuffer(nLength),-1,szDeviceId,nSize,NULL,FALSE);
g_DeviceIDhandle = CreateFile(L"XXX:", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING,0 ,0);
if(g_DeviceIDhandle == INVALID_HANDLE_VALUE)
{
AfxMessageBox(L"DSK3 OPEN FAILD!");
}
status = DeviceIoControl(g_DeviceIDhandle, IOCTL_FMD_WRITE_DEVICE_ID,(LPVOID)szDeviceId, nLength, NULL, 0, NULL, NULL);
if(status == 0)
{
result = GetLastError();
NKDbgPrintfW(TEXT("OnBnClickedWriteDeviceId()--->GetLastError()=0x%x\r\n"),status,result);
}
}
读函数实现如下:
void CDeviceIDDlg::OnBnClickedReadDeviceId()
{
// TODO: Add your control notification handler code here
CString csDeviceID=L"";
DWORD dwRead = 0,i=0, widecharlen=0;
char *szDeviceID=new char[DEVICE_ID_MAX];
SetDlgItemText(IDC_EDIT_READ_DEVICE_ID, L"");
if(KernelIoControl(IOCTL_HAL_GET_DEVICEID, NULL, 0, szDeviceID, DEVICE_ID_MAX, &dwRead) == FALSE)
{
NKDbgPrintfW(TEXT("GetDeviceID()--->error\r\n"));
}
else
{
widecharlen = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, szDeviceID, -1, 0, 0);
csDeviceID.GetBuffer(widecharlen);
MultiByteToWideChar(CP_ACP, MB_COMPOSITE, szDeviceID, -1, csDeviceID.GetBuffer(widecharlen), widecharlen);
}
SetDlgItemText(IDC_EDIT_READ_DEVICE_ID, csDeviceID);
csDeviceID.ReleaseBuffer();
}