微電腦 - Zipit Z1 - Upload程式(C/C++)



#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#define verify assert
#include <windows.h>
#include <conio.h>
 
HANDLE OpenSerial(int iPort)
{
    char szPort[32] = {0};
     
    sprintf(szPort, "COM%d", iPort);
    return CreateFile(szPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
 
void CloseSerial(HANDLE hSerial)
{
    CloseHandle(hSerial);
}
 
bool SetCommDefaultsPart1(HANDLE hSerial, int baud)
{
    DCB dcb;
 
    memset(&dcb, 0, sizeof(DCB));
    dcb.DCBlength = sizeof(DCB);
    dcb.fBinary = TRUE;
    dcb.fParity = TRUE;
    dcb.BaudRate = baud;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;
    dcb.fOutxDsrFlow = FALSE;
    dcb.fDtrControl = DTR_CONTROL_ENABLE;
    dcb.fOutxCtsFlow = FALSE;
    dcb.fRtsControl = RTS_CONTROL_ENABLE;
    dcb.fInX = dcb.fOutX = FALSE;
 
    if (!SetCommState(hSerial, &dcb)) {
        return false;
    }
    return true;
}
 
bool SetCommDefaults(HANDLE hSerial)
{
    if (!SetCommDefaultsPart1(hSerial, CBR_9600)) {
        return false;
    }
 
    COMMTIMEOUTS to;
    memset(&to, 0, sizeof(to));
    to.ReadIntervalTimeout = MAXDWORD;
    if (!SetCommTimeouts(hSerial, &to)) {
        return false;
    }
    PurgeComm(hSerial, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
    return true;
}
 
bool SetBaud(HANDLE hSerial, int baud)
{
    if (!SetCommDefaultsPart1(hSerial, baud)) {
        return false;
    }
    PurgeComm(hSerial, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
    return true;
}
 
void PurgeSerial(HANDLE hSerial)
{
    while (1) {
        BYTE b;
        DWORD cbRead;

        if (!ReadFile(hSerial, &b, 1, &cbRead, NULL)) {
            fprintf(stderr, "FATAL: Purge failed for serial port\n");
            exit(-1);
        }
        if (cbRead == 0) {
            break;
        }
    }
}
 
bool PeekSerialByte(HANDLE hSerial, BYTE& bRet)
{
    DWORD cbRead;
     
    if (!ReadFile(hSerial, &bRet, 1, &cbRead, NULL)) {
        fprintf(stderr, "FATAL: ReadFile failed for serial port\n");
        exit(-1);
    }
    if (cbRead == 1) {
        return true;
    }
    return false;
}
 
void SendSerialBytes(HANDLE hSerial, const BYTE* pb, int cb)
{
    DWORD cbWrite;

    if (!WriteFile(hSerial, pb, cb, &cbWrite, NULL) || (cbWrite != cb)) {
        fprintf(stderr, "FATAL: WriteFile failed for serial port\n");
        exit(-1);
    }
}
 
void PrUsage(void)
{
    printf("usage: upl -# file.bin\n");
    printf("    -# = port number [COM1 default]\n");
    exit(-1);
}
 
bool LoadFile(const char* szInFile, byte* pbImage, int cbImage)
{
    FILE* pfIn = fopen(szInFile, "rb");

    if (pfIn == NULL) {
        fprintf(stderr, "FATAL: can't open input file '%s'\n", szInFile);
        return false;
    }
    fseek(pfIn, 0, SEEK_END);
    int cbFile = ftell(pfIn);
    fseek(pfIn, 0, SEEK_SET);
 
    if (cbFile < cbImage) {
        fclose(pfIn);
        fprintf(stderr, "FATAL: input file '%s' wrong size\n", szInFile);
        fprintf(stderr, "\t %d bytes (%d bytes expected)\n", cbFile, cbImage);
        return false;
    }
     
    if (cbFile > cbImage) {
        printf("uploading %d bytes of %d byte file\n", cbImage, cbFile);
    }
 
    if (fread(pbImage, cbImage, 1, pfIn) != 1) {
        fclose(pfIn);
        fprintf(stderr, "FATAL: read error on input file '%s'\n", szInFile);
        return false;
    }
 
    if (cbFile > cbImage) {
        int cbExtra = cbFile - cbImage;
        byte* pbExtra = new byte[cbExtra];
        assert(fread(pbExtra, cbExtra, 1, pfIn) == 1);

        int nErr = 0;
        for (int i = 0; i < cbExtra; i++) {
            if (pbExtra[i] != 0) {
                nErr++;
            }
        }
 
        delete [] pbExtra;
        if (nErr > 0) {
            fclose(pfIn);
            fprintf(stderr, "FATAL: non-zero data at end of input file '%s'\n", szInFile);
            return false;
        }
    }
    fclose(pfIn);
    return true;
}
     
FILE *g_pfLog = NULL;
bool EchoSerial(HANDLE hSerial)
{
    BYTE b;

    if (!PeekSerialByte(hSerial, b)) {
        return false;
    }
    if ((b >= 0x20) && (b < 0x7f)) {
        printf("%c", b);
    }
    else if ((b == 0xD) || (b == 0xA)) {
        printf("%c", b);
    }
    else {
        printf("$%02x$", b);
    }
     
    if (g_pfLog != NULL) {
        fputc((char)b, g_pfLog);
    }
    return true;
}
 
void EchoSerialTilEmpty(HANDLE hSerial, int nDelay)
{
    for (int iTry = 0; iTry < 10; iTry++) {
        while (EchoSerial(hSerial));
        Sleep(nDelay);
        if (!EchoSerial(hSerial)) {
            break;
        }
    }
}
 
BYTE* LoadFile(const char *szFile, long &cbRet)
{
    FILE *pf = fopen(szFile, "rb");
     
    if (pf == NULL) {
        return NULL;
    }
    fseek(pf, 0, SEEK_END);
    cbRet = ftell(pf);
    fseek(pf, 0, SEEK_SET);
    byte *pbFile = new byte[cbRet];
    assert(fread(pbFile, cbRet, 1, pf) == 1);
    fclose(pf);
    return pbFile;
}
 
BYTE CalcChecksum(const BYTE *pb, long cb)
{
    BYTE csum = 0;
     
    while (cb--) {
        csum += *pb++;
    }
    return csum;
}
 
bool UploadFile(HANDLE hSerial, const char *szFile, BYTE chCmd)
{
    long cbFile;
    byte *pbFile = LoadFile(szFile, cbFile);
     
    if (pbFile == NULL) {
        printf("No %s to upload!\n", szFile);
        return false;
    }
    assert(cbFile > 0);
    printf("//Uploading %ld bytes from %s\n", cbFile, szFile);
    SendSerialBytes(hSerial, &chCmd, 1);
    SendSerialBytes(hSerial, (BYTE*)&cbFile, 4);
    Sleep(250);
 
    //BLOCK: ZPM should reply with "BEG:xxxxxxxx" where xxxxxxxx = hex length
    {
        char reply[4 + 8 + 1];
        DWORD cbRead;
        long cbAck;
 
        reply[4 + 8] = '\0';
        if (!ReadFile(hSerial, reply, sizeof(reply)-1, &cbRead, NULL) ||
            (cbRead != sizeof(reply)-1) ||
            (memcmp(reply, "BEG:", 4) != 0) ||
            (sscanf(reply+4, "%08lX", &cbAck) != 1) ||
            (cbAck != cbFile))
        {
            printf("Zipit is not responding - not ready to upload\n");
            printf("\t'%s'\n", reply);
            delete [] pbFile;
            return false;
        }
    }
    SendSerialBytes(hSerial, pbFile, cbFile);
 
    BYTE csum = CalcChecksum(pbFile, cbFile);
    delete [] pbFile;
    Sleep(500);
 
    //BLOCK: ZPM should reply with "END:xx" where xx = hex checksum
    {
        char reply[4 + 2 + 1];
        reply[4 + 2] = '\0';
        DWORD cbRead;
        BYTE csumAck;
         
        if (!ReadFile(hSerial, reply, sizeof(reply)-1, &cbRead, NULL) ||
            (cbRead != sizeof(reply)-1) ||
            (memcmp(reply, "END:", 4) != 0) ||
            (sscanf(reply+4, "%02X", &csumAck) != 1))
        {
            printf("Zipit did not upload properly\n");
            return false;
        }
         
        if (csumAck != csum) {
            printf("Zipit uploaded incorrectly - checksum mismatch\n");
            return false;
        }
    }
         
    printf("// done upload\n");
    return true;
}
 
bool GrabFile(HANDLE hSerial, const char *szFile, long cbGrab, BYTE chCmd)
{
    byte *pbGrab = new byte[cbGrab];
     
    assert(cbGrab > 0);
    printf("//Grabbing %ld bytes from ROM - store in %s\n", cbGrab, szFile);
 
    // send command byte, 'R'
    SendSerialBytes(hSerial, &chCmd, 1);
    int cbSoFar = 0;
    while (cbSoFar < cbGrab) {
        DWORD cbRead = 0;
        if (!ReadFile(hSerial, &pbGrab[cbSoFar], cbGrab-cbSoFar, &cbRead, NULL) || cbRead <= 0) {
            printf("-");
            Sleep(50);
            continue;
        }
        printf("+");
        assert(cbRead > 0);
        cbSoFar += cbRead;
    }
 
    FILE *pf = fopen(szFile, "wb");
    verify(pf != NULL);
    verify(fwrite(pbGrab, cbGrab, 1, pf) == 1);
    verify(fclose(pf) == 0);
 
    delete [] pbGrab;
    return true;
}
 
BYTE g_rgbImage[2 * 1024];
int main(int argc, char *argv[])
{
    int iPort = 1; // default COM1
    const char *szInputSource;
     
    if (argc == 3 && argv[1][0] == '-') {
        iPort = atoi(&argv[1][1]);
        szInputSource = argv[2];
    }
    else if (argc == 2) {
        szInputSource = argv[1];
    }
    else {
        PrUsage();
    }
    assert(iPort > 0);
 
    int cbImage = 2 * 1024;
    if (!LoadFile(szInputSource, g_rgbImage, cbImage)) {
        return -1;
    }
 
    HANDLE hSerial = OpenSerial(iPort);
    if (hSerial == (HANDLE)-1) {
        fprintf(stderr, "Failed to open serial port COM%d\n", iPort);
        exit(-1);
    }
 
    if (!SetCommDefaults(hSerial)) {
        fprintf(stderr, "Failed to set COM%d to 9600 baud\n", iPort);
        CloseSerial(hSerial);
        return -1;
    }
    printf("Connected\n");
    printf("Turn on the Zipit (press button, wait for green, release)\n");
    printf("    Wait 10 seconds, then press any key to start transfer\n");
    printf("    (DBG pin must be grounded)\n");
 
    int nSkip = 0;
    BYTE bIgnore;
    while (PeekSerialByte(hSerial, bIgnore)) {
        nSkip++;
    }
     
    if (nSkip > 0) {
        printf("Skipping %d left over data bytes\n");
    }
 
    while (_kbhit() == 0) {
        Sleep(100);
    }
    _getch(); // eat the character
 
    printf("Transfering bootstrap program\n");
    SendSerialBytes(hSerial, g_rgbImage, cbImage);
    printf("Transfer complete\n");
 
    Sleep(100);
    printf("Switch to 57.6kbps\n");
    if (!SetBaud(hSerial, 57600)) {
        printf("SET BAUD RATE ERROR!\n");
        return -1;
    }
 
#if 0
    g_pfLog = fopen("log.txt", "at"); // save session
#endif
 
    printf("Echo + upload commands\n");
    printf("\tDecide what to upload\n");
    printf("\t'A' - allrom.bin upload (2MB)\n");
    printf("\t'a' - loader.bin upload (8K max)\n");
    printf("\t'k' - zimage.dat upload (581K max)\n");
    printf("\t'u' - ramdisk.gz upload (1.5MB max)\n");
    printf("\tFollow changes with 'WYes' to write\n");
    printf("\tOther commands\n");
    printf("\t'R' - grab ROM\n");
    while (1) {
        while (EchoSerial(hSerial));
        if (_kbhit()) {
            BYTE b = _getch();
            if (b == 'u') {
                UploadFile(hSerial, "ramdisk.gz", b); // -> ramdisk.gz
            }
            else if (b == 'k') {
                UploadFile(hSerial, "zimage.dat", b); // -> kernel.gz
            }
            else if (b == 'a') {
                UploadFile(hSerial, "loader.bin", b); // -> start of ROM image
            }
            else if (b == 'A') {
                UploadFile(hSerial, "allrom.bin", b); // -> start of ROM image
            }
            else if (b == 'R') {
                GrabFile(hSerial, "grabrom.bin", 2*1024*1024, b);
                printf("// grab done -- I hope...\n");
            }
            else if (b == 'Q') {
                printf("// done echo mode\n");
                break;
            }
            else {
                SendSerialBytes(hSerial, &b, 1);
            }
        }
        Sleep(50);    // don't be a total CPU pig
    }
    if (g_pfLog != NULL) {
        fclose(g_pfLog);
    }
    g_pfLog = NULL;
    return 0;
}