keyboard/mdloader/mdloader_win32.c

561 lines
15 KiB
C

/*
* Copyright (C) 2018 Massdrop Inc.
*
* This file is part of Massdrop Loader.
*
* Massdrop Loader is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Massdrop Loader is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Massdrop Loader. If not, see <https://www.gnu.org/licenses/>.
*/
#include "mdloader_common.h"
#define EXAMPLE_PORT "COM23"
#define PORT_SEARCH_STRING "COM"
HANDLE gport = NULL; //Port of device
#define WRITE_SIZE 500 //Maximum bytes to write per call
#define READ_SIZE 500 //Maximum bytes to read per call
#define READ_RETRIES 10 //Maximum read retries before fail
void print_com_example(void)
{
printf("Example: -p " EXAMPLE_PORT "\n");
}
//Read data from device
//Must check read_error for a read error after return
//Return unsigned word from memory location
int read_data(int addr, int readsize)
{
read_error = 1; //Set read error flag as default
int readdata = 0;
char wbuf[] = "!XXXXXXXX,#"; //Largest buffer needed
DWORD ret;
if (readsize == 1)
sprintf(wbuf,"%c%02x,%c",CMD_READ_ADDR_8,addr,CMD_END);
else if (readsize == 2)
sprintf(wbuf,"%c%04x,%c",CMD_READ_ADDR_16,addr,CMD_END);
else if (readsize == 4)
sprintf(wbuf,"%c%08x,%c",CMD_READ_ADDR_32,addr,CMD_END);
else
{
if (verbose) printf("Error: Invalid read size given (%i)\n",readsize);
return 0;
}
if (verbose > 0) printf("Write: [%s]\n",wbuf);
PurgeComm(gport, PURGE_RXCLEAR | PURGE_TXCLEAR); //Flush any remaining data from a bad read
int writelen = strlen(wbuf);
if (!WriteFile(gport,wbuf,writelen,&ret,0))
{
if (verbose) printf("Error writing port [%s](%lu)\n",wbuf,GetLastError());
return 0;
}
if (ret != writelen)
{
if (verbose) printf("Error writing %i bytes [%lu]\n",writelen,ret);
return 0;
}
if (!ReadFile(gport, &readdata, readsize, &ret, NULL))
{
if (verbose) printf("Error reading port [%i][%lu](%lu)\n",readsize,ret,GetLastError());
return 0;
}
if (ret != readsize)
{
if (verbose) printf("Error reading %i bytes! [%lu]\n",readsize,ret);
return 0;
}
read_error = 0; //Clear read error flag
return readdata;
}
//Write data to device
//Return 1 on success, 0 on failure
int write_data(int addr, int writesize, int data)
{
if (check_bootloader_write_attempt(addr)) return 0; //Prevent writes to bootloader section
char wbuf[] = "!XXXXXXXX,XXXXXXXX#"; //Largest buffer needed
DWORD ret;
if (writesize == 1)
sprintf(wbuf,"%c%08x,%02x%c",CMD_WRITE_ADDR_8,addr,data,CMD_END);
else if (writesize == 2)
sprintf(wbuf,"%c%08x,%04x%c",CMD_WRITE_ADDR_16,addr,data,CMD_END);
else if (writesize == 4)
sprintf(wbuf,"%c%08x,%08x%c",CMD_WRITE_ADDR_32,addr,data,CMD_END);
else
{
if (verbose) printf("Error: Invalid write size given (%i)\n",writesize);
return 0;
}
if (verbose) printf("Write %i bytes: [%s]\n",writesize,wbuf);
int writelen = strlen(wbuf);
if (!WriteFile(gport,wbuf,writelen,&ret,0))
{
if (verbose) printf("Error writing port [%s](%lu)\n",wbuf,GetLastError());
return 0;
}
if (ret != writelen)
{
if (verbose) printf("Error writing %i bytes! [%lu]\n",writesize,ret);
return 0;
}
return 1;
}
//Jump to address and run
//Return 1 on success, 0 on failure
int goto_address(int addr)
{
char wbuf[] = "!XXXXXXXX#";
DWORD ret;
sprintf(wbuf,"%c%08x%c",CMD_GOTO_ADDR,addr,CMD_END);
if (verbose) printf("Write: [%s]\n",wbuf);
int writelen = strlen(wbuf);
if (!WriteFile(gport,wbuf,strlen(wbuf),&ret,0))
{
if (verbose) printf("Error writing port [%s](%lu)\n",wbuf,GetLastError());
return 0;
}
if (ret != writelen)
{
if (verbose) printf("Error writing goto address! [%lu]\n",ret);
return 0;
}
return 1;
}
//Read data from device RAM
//Return pointer to buffer on success, NULL on failure
char *recv_file(int addr, int bytes)
{
char wbuf[] = "!XXXXXXXX,XXXXXXXX#";
char *data = NULL;
data = (char *)calloc(bytes+1,sizeof(char));
if (data == NULL)
{
printf("Error allocating read buffer memory!\n");
return NULL;
}
char *pdata = data;
DWORD ret;
int retries = READ_RETRIES;
int readsize = READ_SIZE;
//Constrain read size to buffer size
if (initparams.argument.outputInit.bufferSize > 0 && initparams.argument.outputInit.bufferSize < readsize)
readsize = initparams.argument.outputInit.bufferSize;
while (bytes > 0)
{
if (readsize > bytes) readsize = bytes;
sprintf(wbuf,"%c%08x,%08x%c",CMD_RECV_FILE,addr,readsize,CMD_END);
if (verbose > 0) printf("Write: [%s]\n",wbuf);
PurgeComm(gport, PURGE_RXCLEAR | PURGE_TXCLEAR); //Flush any remaining data from a bad read
long writelen = strlen(wbuf);
if (!WriteFile(gport,wbuf,writelen,&ret,0))
{
if (verbose) printf("Error writing port [%s](%lu)\n",wbuf,GetLastError());
free(data);
return NULL;
}
if (!ReadFile(gport, pdata, bytes, &ret, NULL))
{
if (verbose) printf("Error reading port [%i][%lu](%lu)\n",readsize,ret,GetLastError());
free(data);
return NULL;
}
if (ret != readsize)
{
if (verbose) printf("Error reading %i bytes! [%lu](%lu)\n",readsize,ret,GetLastError());
if (retries <= 0)
{
if (verbose) printf("No retries remain!\n");
free(data);
return NULL;
}
retries--;
if (verbose) printf("Retrying read... (%i)\n",READ_RETRIES - retries);
continue; //Attempt read again
}
if (verbose > 0) printf("Recv: [%lu]\n",ret);
retries = READ_RETRIES; //Reset retry limit
bytes -= ret; //Decrement remaining bytes
addr += ret; //Increment to next address
pdata += ret; //Increment pointer in recv buffer
}
return data;
}
//Write data to device
//Return 1 on sucess, 0 on failure
int send_file(int addr, int bytes, char *data)
{
if (check_bootloader_write_attempt(addr)) return 0; //Prevent writes to bootloader section
if (bytes < 1)
{
printf("Error: No data to send!\n");
return 0;
}
char wbuf[] = "!XXXXXXXX,XXXXXXXX#";
DWORD ret;
char *pdata = data;
int writesize = WRITE_SIZE;
//Constrain write size to buffer size
if (initparams.argument.outputInit.bufferSize > 0 && initparams.argument.outputInit.bufferSize < writesize)
writesize = initparams.argument.outputInit.bufferSize;
while (bytes > 0)
{
if (writesize > bytes) writesize = bytes;
sprintf(wbuf,"%c%08x,%08x%c",CMD_SEND_FILE,addr,writesize,CMD_END);
if (verbose > 0) printf("Write: [%s]\n",wbuf);
if (!WriteFile(gport, wbuf, strlen(wbuf), &ret, 0))
{
if (verbose) printf("Error writing port [%s](%lu)\n",wbuf,GetLastError());
return 0;
}
if (ret != strlen(wbuf))
{
if (verbose) printf("Error writing port [%s](%lu)\n",wbuf,GetLastError());
return 0;
}
if (verbose > 0) printf("Write: %i bytes\n",writesize);
if (!WriteFile(gport, pdata, writesize, &ret, 0))
{
if (verbose) printf("Error writing port [%lu][%i](%lu)\n",ret,writesize,GetLastError());
return 0;
}
if (ret != writesize)
{
printf("Error writing port [%lu][%i](%lu)\n",ret,writesize,GetLastError());
return 0;
}
bytes -= ret; //Decrement remaining bytes
addr += ret; //Increment to next address
pdata += ret; //Increment pointer in send buffer
}
return 1;
}
//Print bootloader version
//Return 1 on sucess, 0 on failure
int print_bootloader_version(void)
{
char wbuf[] = "!#";
char readdata[128] = "";
DWORD ret;
int readsize = sizeof(readdata) - 1;
sprintf(wbuf,"%c%c",CMD_READ_VERSION,CMD_END);
if (verbose > 0) printf("Write: [%s]\n",wbuf);
int writelen = strlen(wbuf);
if (!WriteFile(gport,wbuf,writelen,&ret,0))
{
if (verbose) printf("Version: Error writing port [%s](%lu)\n",wbuf,GetLastError());
else printf("Version: Error retrieving!\n");
return 0;
}
if (!ReadFile(gport, &readdata, readsize, &ret, NULL))
{
if (verbose) printf("Version: Error reading port [%i][%lu](%lu)\n",readsize,ret,GetLastError());
else printf("Version: Error retrieving!\n");
return 0;
}
while (readdata[strlen(readdata)-1] == '\n' || readdata[strlen(readdata)-1] == '\r') readdata[strlen(readdata)-1] = 0;
printf("Bootloader version: %s\n",readdata);
return 1;
}
//Set normal command mode
//Return 1 on sucess, 0 on failure
int set_normal_mode(void)
{
if (verbose) printf("Setting normal mode... ");
int retbuf = 0;
char wbuf[] = "!#";
DWORD ret;
sprintf(wbuf,"%c%c",CMD_SET_NORMAL_MODE,CMD_END);
int writelen = strlen(wbuf);
if (!WriteFile(gport,wbuf,writelen,&ret,0))
{
if (verbose) printf("Failed! (%lu)\n",GetLastError());
return 0;
}
if (ret != writelen)
{
if (verbose) printf("Error writing %i bytes! [%lu](%lu)\n",writelen,ret,GetLastError());
return 0;
}
int readlen = 2;
if (!ReadFile(gport, &retbuf, readlen, &ret, NULL))
{
if (verbose) printf("Error reading port [%i][%lu](%lu)\n",readlen,ret,GetLastError());
return 0;
}
if (ret != readlen)
{
if (verbose) printf("Error reading %i bytes! [%lu][%04x](%lu)\n",readlen,ret,retbuf,GetLastError());
return 0;
}
if ((retbuf & 0xFFFF) != 0x0D0A)
{
if (verbose) printf("Error: Incorrect response! [%lu][%04x](%lu)\n",ret,retbuf,GetLastError());
return 0;
}
if (verbose) printf("Success!\n");
return 1;
}
//Jump to loaded application
//Return 1 on sucess, 0 on failure
int jump_application(void)
{
printf("Booting device... ");
if (testmode)
{
printf("(test mode disables restart)\n");
return 1;
}
char wbuf[] = "!#";
DWORD ret;
DWORD writelen = strlen(wbuf);
sprintf(wbuf,"%c%c",CMD_LOAD_APP,CMD_END);
if (!WriteFile(gport,wbuf,writelen,&ret,0))
{
printf("Failed! (%lu)\n",GetLastError());
return 0;
}
printf("Success!\n");
return 1;
}
//Open port
//Return 1 on sucess, 0 on failure
int open_port(char *portname, char silent)
{
char portnamebuf[64] = "";
if (!silent || verbose) printf("Opening port '%s'... ",portname);
sprintf(portnamebuf,"\\\\.\\%s",portname);
gport = CreateFile(portnamebuf,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
if (gport == INVALID_HANDLE_VALUE)
{
if (!silent || verbose)
{
printf("Failed!");
LPTSTR errstr = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|
FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&errstr,
0,
NULL);
if (errstr != NULL)
{
char *pstr = errstr + strlen(errstr);
while (pstr > errstr && *pstr <= 32) { *pstr = 0; pstr--; } //Clean up end of message
if (!silent) printf(" (%s)",errstr);
LocalFree(errstr);
}
printf("\n");
}
return 0;
}
if (!silent || verbose) printf("Success!\n");
return 1;
}
//Close port
//Return 1 on sucess, 0 on failure
int close_port(char silent)
{
if (!silent || verbose) printf("Closing port... ");
if (CloseHandle(gport) == 0)
{
if (!silent || verbose) printf("Failed! (%lu)\n",GetLastError());
return 0;
}
if (!silent || verbose) printf("Success!\n");
return 1;
}
//Configure port
//Return 1 on sucess, 0 on failure
int config_port(void)
{
DCB dcb = {};
if (verbose) printf("Configuring port... \n");
if (verbose) printf(" Get config... ");
if (GetCommState(gport,&dcb) == 0)
{
if (verbose) printf("Failed! (%lu)\n",GetLastError());
return 0;
}
if (verbose) printf("Success!\n");
dcb.BaudRate = CBR_115200;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
dcb.ByteSize = 8;
if (verbose) printf(" Set config... ");
if (!SetCommState(gport,&dcb))
{
if (verbose) printf("Failed! (%lu)\n",GetLastError());
return 0;
}
if (verbose) printf("Success!\n");
COMMTIMEOUTS comTimeOut;
comTimeOut.ReadIntervalTimeout = 1;
comTimeOut.ReadTotalTimeoutMultiplier = 1;
comTimeOut.ReadTotalTimeoutConstant = 1;
comTimeOut.WriteTotalTimeoutMultiplier = 1;
comTimeOut.WriteTotalTimeoutConstant = 1;
if (verbose) printf(" Set timeouts... ");
if (!SetCommTimeouts(gport,&comTimeOut))
{
if (verbose) printf("Failed! (%lu)\n",GetLastError());
return 0;
}
if (verbose) printf("Success!\n");
PurgeComm(gport, PURGE_RXCLEAR | PURGE_TXCLEAR); //Flush port
return 1;
}
//List devices which communicate properly
//If first_device is not null, store first found device and return
void list_devices(char *first_device)
{
int portnum = 1;
int portmax = 255; //Inclusive
int portcount = 0;
char portname[] = "COM255"; //Max buffer necessary
if (first_device == NULL)
{
printf("Bootloader port listing\n");
printf("-----------------------------\n");
}
while (portnum <= portmax)
{
sprintf(portname,PORT_SEARCH_STRING "%i",portnum);
if (test_port(portname,TRUE))
{
if (test_mcu(TRUE))
{
if (first_device) printf("\n");
printf("Device port: %s (%s)\n",portname,mcu->name);
if (first_device != NULL)
{
close_port(TRUE);
strcpy(first_device,portname);
return;
}
portcount++;
}
close_port(TRUE);
}
portnum++;
}
if (first_device == NULL)
{
if (portcount == 0)
printf("No devices found!\n");
}
}