Files

1022 lines
38 KiB
C++

/************************************************************************
* Copyright (c) 2010-2011, Microchip Technology Inc.
*
* Microchip licenses this software to you solely for use with Microchip
* products. The software is owned by Microchip and its licensors, and
* is protected under applicable copyright laws. All rights reserved.
*
* SOFTWARE IS PROVIDED "AS IS." MICROCHIP EXPRESSLY DISCLAIMS ANY
* WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL
* MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR
* EQUIPMENT, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY
* OR SERVICES, ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED
* TO ANY DEFENSE THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION,
* OR OTHER SIMILAR COSTS.
*
* To the fullest extent allowed by law, Microchip and its licensors
* liability shall not exceed the amount of fees, if any, that you
* have paid directly to Microchip to use this software.
*
* MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE
* OF THESE TERMS.
*
************************************************************************/
#include "Comm.h"
#include <QByteArray>
#include <QCoreApplication>
#include <QTime>
#include <stdint.h>
const int Comm::SyncWaitTime = 40000;
/**
*
*/
Comm::Comm()
{
connected = false;
boot_device = NULL;
}
/**
*
*/
Comm::~Comm()
{
boot_device = NULL;
}
/**
*
*/
void Comm::PollUSB(uint16_t deviceVIDtoPoll, uint16_t devicePIDtoPoll)
{
hid_device_info *dev;
dev = hid_enumerate(deviceVIDtoPoll, devicePIDtoPoll);
connected = (dev != NULL);
hid_free_enumeration(dev);
}
/**
*
*/
bool Comm::isConnected(void)
{
return connected;
}
/**
*
*/
Comm::ErrorCode Comm::open(uint16_t deviceVIDtoOpen, uint16_t devicePIDtoOpen)
{
boot_device = hid_open(deviceVIDtoOpen, devicePIDtoOpen, NULL);
if(boot_device)
{
connected = true;
hid_set_nonblocking(boot_device, true);
qWarning("Device successfully connected to.");
return Success;
}
qWarning("Unable to open device.");
return NotConnected;
}
/**
*
*/
void Comm::close(void)
{
hid_close(boot_device);
boot_device = NULL;
connected = false;
}
/**
*
*/
void Comm::Reset(void)
{
unsigned char sendPacket[65];
QTime elapsed;
ErrorCode status;
if(connected) {
memset((void*)&sendPacket, 0x00, sizeof(sendPacket));
sendPacket[1] = RESET_DEVICE;
qDebug("Sending Reset Command...");
elapsed.start();
status = SendPacket(sendPacket, sizeof(sendPacket));
if(status == Comm::Success)
qDebug("Successfully sent reset command (%fs)", (double)elapsed.elapsed() / 1000);
else
qWarning("Sending reset command failed.");
}
}
/**
*
*/
Comm::ErrorCode Comm::Program(uint32_t address, unsigned char bytesPerPacket,
unsigned char bytesPerAddress, unsigned char bytesPerWord, unsigned char deviceFamily,
uint32_t endAddress, unsigned char *pData)
{
WritePacket writePacket;
ErrorCode result = Success;
uint32_t i;
bool allPayloadBytesFF;
bool firstAllFFPacketFound = false;
uint32_t bytesToSend;
unsigned char lastCommandSent = PROGRAM_DEVICE;
uint32_t percentCompletion;
uint32_t addressesToProgram;
uint32_t startOfDataPayloadIndex;
//Error check input parameters before using them
if((pData == NULL) || (bytesPerAddress == 0) || (address > endAddress) || (bytesPerWord == 0))
{
qWarning("Bad parameters specified when calling Program() function.");
return Fail;
}
//Error check to make sure the requested maximum data payload size is an exact multiple of the bytesPerAddress.
//If not, shrink the number of bytes we actually send, so that it is always an exact multiple of the
//programmable media bytesPerAddress. This ensures that we don't "half" program any memory address (ex: if each
//flash address is a 16-bit word address, we don't want to only program one byte of the address, we want to program
//both bytes.
while((bytesPerPacket % bytesPerWord) != 0)
{
bytesPerPacket--;
}
//Setup variable, used for progress bar updating computations.
addressesToProgram = endAddress - address;
if(addressesToProgram == 0) //Check to avoid potential divide by zero later.
addressesToProgram++;
//Make sure the device is still connected before we start trying to communicate with it.
if(connected)
{
//Loop through the entire data set/region, but break it into individual packets before sending it
//to the device.
while(address < endAddress)
{
//Update the progress bar so the user knows things are happening.
percentCompletion = 100*((float)1 - (float)((float)(endAddress - address)/(float)addressesToProgram));
if(percentCompletion > 100)
{
percentCompletion = 100;
}
//Reformat the percent completion so it "fits" in the 33% to 66% region (since erase
//"completes" 0%-32% of the total erase/program/verify cycle, and verify completes 67%-100%).
percentCompletion /= 3;
percentCompletion += 33;
emit SetProgressBar(percentCompletion);
//Prepare the packet to send to the device.
memset((void*)&writePacket, 0x00, sizeof(writePacket)); //initialize all bytes clear, so unused pad bytes are = 0x00.
writePacket.command = PROGRAM_DEVICE;
writePacket.address = address;
//Check if we are near the end of the programmable region, and need to send a "short packet" (with less than the maximum
//allowed program data payload bytes). In this case we need to notify the device by using the PROGRAM_COMPLETE command instead
//of the normal PROGRAM_DEVICE command. This lets the bootloader firmware in the device know it should flush any internal
//buffers it may be using, by programming all of the bufferred data to NVM memory.
if(((endAddress - address) * bytesPerAddress) < bytesPerPacket)
{
writePacket.bytesPerPacket = (endAddress - address) * bytesPerAddress;
//Copy the packet data to the actual outgoing buffer and then send it over USB to the device.
memcpy((unsigned char*)&writePacket.data[0] + 58 - writePacket.bytesPerPacket, pData, writePacket.bytesPerPacket);
//Check to make sure we are completely programming all bytes of the destination address. If not,
//increase the data size and set the extra byte(s) to 0xFF (the default/blank value).
while((writePacket.bytesPerPacket % bytesPerWord) != 0)
{
if(writePacket.bytesPerPacket >= bytesPerPacket)
{
break; //should never hit this break, due to while((bytesPerPacket % bytesPerWord) != 0) check at start of function
}
//Shift all the data payload bytes in the packet to the left one (lower address),
//so we can insert a new 0xFF byte.
for(i = 0; i < (unsigned char)(bytesPerPacket - 1); i++)
{
writePacket.data[i] = writePacket.data[i+1];
}
writePacket.data[writePacket.bytesPerPacket] = 0xFF;
writePacket.bytesPerPacket++;
}
bytesToSend = writePacket.bytesPerPacket;
qDebug("Preparing short packet of final program data with payload: 0x%x", (uint32_t)writePacket.bytesPerPacket);
}
else
{
//Else we are planning on sending a full length packet with the full size payload.
writePacket.bytesPerPacket = bytesPerPacket;
bytesToSend = bytesPerPacket;
//Copy the packet data to the actual outgoing buffer and then prepare to send it.
memcpy((unsigned char*)&writePacket.data[0] + 58 - writePacket.bytesPerPacket, pData, writePacket.bytesPerPacket);
}
//Check if all bytes of the data payload section of the packet are == 0xFF. If so, we can save programming
//time by skipping the packet by not sending it to the device. The default/erased value is already = 0xFF, so
//the contents of the flash memory will be correct (although if we want to be certain we should make sure
//the 0xFF regions are still getting checked during the verify step, in case the erase procedure failed to set
//all bytes = 0xFF).
allPayloadBytesFF = true; //assume true until we do the actual check below
//Loop for all of the bytes in the data payload portion of the writePacket. The data payload is little endian but is stored
//"right justified" in the packet. Therefore, writePacket.data[0] isn't necessarily the LSB data byte in the packet.
startOfDataPayloadIndex = 58 - writePacket.bytesPerPacket;
for(i = startOfDataPayloadIndex; i < (startOfDataPayloadIndex + writePacket.bytesPerPacket); i++)
{
if(writePacket.data[i] != 0xFF)
{
//Special check for PIC24, where every 4th byte from the .hex file is == 0x00,
//which is the "phantom byte" (the upper byte of each odd address 16-bit word
//is unimplemented, and is probably 0x00 in the .hex file).
if((((i - startOfDataPayloadIndex) % bytesPerWord) == 3) && (deviceFamily == Device::PIC24))
{
//We can ignore this "phantom byte", since it is unimplemented and effectively a "don't care" byte.
}
else
{
//We found a non 0xFF (or blank value) byte. We need to send and program the
//packet of useful data into the device.
allPayloadBytesFF = false;
break;
}
}
}
//Check if we need to send a normal packet of data to the device, if the packet was all 0xFF and
//we need to send a PROGRAM_COMPLETE packet, or if it was all 0xFF and we can simply skip it without
//doing anything.
if(allPayloadBytesFF == false)
{
qDebug("Sending program data packet with address: 0x%x", (uint32_t)writePacket.address);
//We need to send a normal PROGRAM_DEVICE packet worth of data to program.
result = SendPacket((unsigned char*)&writePacket, sizeof(writePacket));
//Verify the data was successfully received by the USB device.
if(result != Success)
{
qWarning("Error during program sending packet with address: 0x%x", (uint32_t)writePacket.address);
return result;
}
firstAllFFPacketFound = true; //reset flag so it will be true the next time a pure 0xFF packet is found
lastCommandSent = PROGRAM_DEVICE;
}
else if((allPayloadBytesFF == true) && (firstAllFFPacketFound == true))
{
//In this case we need to send a PROGRAM_COMPLETE command to let the firmware know it should flush
//its buffer by programming all of it to flash, since we are about to skip to a new address range.
writePacket.command = PROGRAM_COMPLETE;
writePacket.bytesPerPacket = 0;
firstAllFFPacketFound = false;
qDebug("Sending program complete data packet to skip a packet with address: 0x%x", (uint32_t)writePacket.address);
result = SendPacket((unsigned char*)&writePacket, sizeof(writePacket));
//Verify the data was successfully received by the USB device.
if(result != Success)
{
qWarning("Error during program sending packet with address: 0x%x", (uint32_t)writePacket.address);
return result;
}
lastCommandSent = PROGRAM_COMPLETE;
}
else
{
//If we get to here, this means that (allPayloadBytesFF == true) && (firstAllFFPacketFound == false).
//In this case, the last packet that we processed was all 0xFF, and all bytes of this packet are
//also all 0xFF. In this case, we don't need to send any packet data to the device. All we need
//to do is advance our pointers and keep checking for a new non-0xFF section.
qDebug("Skipping data packet with all 0xFF with address: 0x%x", (uint32_t)writePacket.address);
}
//Increment pointers now that we successfully programmed (or deliberately skipped) a packet worth of data
address += bytesPerPacket / bytesPerAddress;
pData += bytesToSend;
//Check if we just now exactly finished programming the memory region (in which case address will be exactly == endAddress)
//region. (ex: we sent a PROGRAM_DEVICE instead of PROGRAM_COMPLETE for the last packet sent).
//In this case, we still need to send the PROGRAM_COMPLETE command to let the firmware know that it is done,
//and will not be receiving any subsequent program packets for this memory region.
if((uint32_t)address >= (uint32_t)endAddress)
{
//Check if we still need to send a PROGRAM_COMPLETE command (we don't need to send one if
//the last command we sent was a PRORAM_COMPLETE already).
if(lastCommandSent == PROGRAM_COMPLETE)
{
break;
}
memset((void*)&writePacket, 0x00, sizeof(writePacket));
writePacket.command = PROGRAM_COMPLETE;
writePacket.bytesPerPacket = 0;
qDebug("Sending final program complete command for this region.");
result = SendPacket((unsigned char*)&writePacket, sizeof(writePacket));
break;
}
}//while(address < endAddress)
return result;
}//if(connected)
else
{
return NotConnected;
}
}
#if defined(Q_OS_WIN)
//Must be running on Windows.
#include <Windows.h>
#include "../HIDAPI/hidapi.h"
//Note: Below structure definition is from HIDAPI Windows hid.cpp file (and not in the header).
//In order to speed/bandwdith optimize the GetData() method, it is desireable to use asynchronous
//queueing of packet requests with the host, particularly on the HID interrupt OUT endpoint.
//Queueing up multiple OUT requests simultaneously ensures that the host won't waste potential bandwidth,
//due to the maximum 1 packet / 1 ms polling interval for full speed interrupt endpoints, whereby a single
//missed write opportunity will waste half of the theoretical bandwidth (ex: 1 packet / 2 ms).
//In order to do this queueing however, it is necessary to have direct access to the device_handle, as
//the currently implemented hid_write() HIDAPI doesn't implement multi-packet queuing and tracking.
struct hid_device_
{
HANDLE device_handle;
BOOL blocking;
size_t input_report_length;
void *last_error_str;
DWORD last_error_num;
BOOL ioPending;
};
Comm::ErrorCode Comm::GetData(uint32_t address, unsigned char bytesPerPacket,
unsigned char bytesPerAddress, unsigned char bytesPerWord,
uint32_t endAddress, unsigned char *pData)
{
ReadPacket readPacket;
WritePacket writePacket1;
WritePacket writePacket2;
ErrorCode result;
uint32_t percentCompletion;
uint32_t addressesToFetch = endAddress - address;
uint32_t readAddress = address;
OVERLAPPED write1;
OVERLAPPED write2;
DWORD bytesWritten1 = 0;
DWORD bytesWritten2 = 0;
DWORD actualBytesWritten1 = 0;
DWORD actualBytesWritten2 = 0;
bool useWrite1StructNext = true;
bool checkWrite1ForCompletionNext = true;
uint32_t writePacketsPending = 0;
//Check to avoid possible division by zero when computing the percentage completion status.
if(addressesToFetch == 0)
addressesToFetch++;
//Initialize a couple of overlapped write structures for doing async write operations.
write1.Internal = 0;
write1.InternalHigh = 0;
write1.Offset = 0;
write1.OffsetHigh = 0;
write1.Pointer = NULL;
write1.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
write2.Internal = 0;
write2.InternalHigh = 0;
write2.Offset = 0;
write2.OffsetHigh = 0;
write2.Pointer = NULL;
write2.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(connected)
{
//First error check the input parameters before using them
if((pData == NULL) || (endAddress < address) || (bytesPerPacket == 0))
{
qWarning("Error, bad parameters provided to call of GetData()");
return Fail;
}
//Set up an initial write to request the first packet of data we want from the device.
// Set up the buffer packet with the appropriate address and with the get data command
memset((void*)&writePacket1, 0x00, sizeof(writePacket1));
writePacket1.command = GET_DATA;
writePacket1.address = address;
//Debug output info.
qWarning("Fetching packet with address: 0x%X", (uint32_t)writePacket1.address);
// Calculate to see if the entire buffer can be filled with data, or just partially
if(((endAddress - address) * bytesPerAddress) < bytesPerPacket)
{
// If the amount of bytes left over between current address and end address is less than
// the max amount of bytes per packet, then make sure the bytesPerPacket info is updated
writePacket1.bytesPerPacket = (endAddress - address) * bytesPerAddress;
}
else
{
// Otherwise keep it at its maximum
writePacket1.bytesPerPacket = bytesPerPacket;
}
WriteFile(boot_device->device_handle, &writePacket1, sizeof(writePacket1), &bytesWritten1, &write1);
useWrite1StructNext = false;
// Increment address by however many bytes were requested divided by how many bytes per address
address += writePacket1.bytesPerPacket / bytesPerAddress;
checkWrite1ForCompletionNext = true;
writePacketsPending++;
// Continue reading from device until the entire programmable region has been read
while(readAddress < endAddress)
{
//Update the progress bar so the user knows things are happening.
percentCompletion = 100*((float)1 - (float)((float)(endAddress - address)/(float)addressesToFetch));
if(percentCompletion > 100)
{
percentCompletion = 100;
}
//Reformat the percent completion so it "fits" in the 33% to 66% region (since erase
//"completes" 0%-32% of the total erase/program/verify cycle, and verify completes 67%-100%).
percentCompletion /= 3;
percentCompletion += 67;
emit SetProgressBar(percentCompletion);
//Queue up the next HID OUT write request, to begin requesting the next packet worth of data.
//Note: At this point, there is already one outstanding write request already pending in the background.
//This queuing of two HID OUT write requests simultaneosly is done so as to maximize bandwidth/avoid
//any wasted time in between packets.
if(useWrite1StructNext == true)
{
//Check if there is at least one additional packet worth of data that needs requesting.
if(address < endAddress)
{
//Prepare an OUT packet to request the data.
write1.Internal = 0;
write1.InternalHigh = 0;
write1.Offset = 0;
write1.OffsetHigh = 0;
write1.Pointer = NULL;
ResetEvent(write1.hEvent);
// Set up the buffer packet with the appropriate address and with the get data command
memset((void*)&writePacket1, 0x00, sizeof(writePacket1));
writePacket1.command = GET_DATA;
writePacket1.address = address;
//Debug output info.
qWarning("Fetching packet with address: 0x%X", (uint32_t)writePacket1.address);
// Calculate to see if the entire buffer can be filled with data, or just partially
if(((endAddress - address) * bytesPerAddress) < bytesPerPacket)
{
// If the amount of bytes left over between current address and end address is less than
// the max amount of bytes per packet, then make sure the bytesPerPacket info is updated
writePacket1.bytesPerPacket = (endAddress - address) * bytesPerAddress;
}
else
{
// Otherwise keep it at its maximum
writePacket1.bytesPerPacket = bytesPerPacket;
}
WriteFile(boot_device->device_handle, &writePacket1, sizeof(writePacket1), &bytesWritten1, &write1);
useWrite1StructNext = false;
// Increment address by however many bytes were requested divided by how many bytes per address
address += writePacket1.bytesPerPacket / bytesPerAddress;
writePacketsPending++;
}
}
else
{
//Check if there is at least one additional packet worth of data that needs requesting.
if(address < endAddress)
{
//Prepare an OUT packet to request the data.
write2.Internal = 0;
write2.InternalHigh = 0;
write2.Offset = 0;
write2.OffsetHigh = 0;
write2.Pointer = NULL;
ResetEvent(write2.hEvent);
// Set up the buffer packet with the appropriate address and with the get data command
memset((void*)&writePacket2, 0x00, sizeof(writePacket2));
writePacket2.command = GET_DATA;
writePacket2.address = address;
//Debug output info.
qWarning("Fetching packet with address: 0x%X", (uint32_t)writePacket2.address);
// Calculate to see if the entire buffer can be filled with data, or just partially
if(((endAddress - address) * bytesPerAddress) < bytesPerPacket)
{
// If the amount of bytes left over between current address and end address is less than
// the max amount of bytes per packet, then make sure the bytesPerPacket info is updated
writePacket2.bytesPerPacket = (endAddress - address) * bytesPerAddress;
}
else
{
// Otherwise keep it at its maximum
writePacket2.bytesPerPacket = bytesPerPacket;
}
WriteFile(boot_device->device_handle, &writePacket2, sizeof(writePacket2), &bytesWritten2, &write2);
useWrite1StructNext = true;
// Increment address by however many bytes were requested divided by how many bytes per address
address += writePacket2.bytesPerPacket / bytesPerAddress;
writePacketsPending++;
}
}
//Now read back a HID IN packet from the device, which should contains the data of interest (which was previously
//requested via a HID OUT packet).
memset((void*)&readPacket, 0x00, sizeof(readPacket));
result = ReceivePacket((unsigned char*)&readPacket, sizeof(readPacket));
// If it wasn't successful, then return with error
if(result != Success)
{
qWarning("Error reading packet with address: 0x%x", (uint32_t)readPacket.address);
return result;
}
//Now block until the older of the two HID OUT write requests completes. This is necessary, to flow control the code,
//to make sure it never has more than two total (max) HID interrupt OUT write requests pending at any given time.
if(checkWrite1ForCompletionNext == true)
{
if(writePacketsPending != 0)
{
GetOverlappedResult(boot_device->device_handle, &write1, &actualBytesWritten1, TRUE); //blocks until finished
checkWrite1ForCompletionNext = false;
writePacketsPending--; //Just finished sending an OUT packet. This effectively dequeues the request since it is finished.
}
}
else
{
if(writePacketsPending != 0)
{
GetOverlappedResult(boot_device->device_handle, &write2, &actualBytesWritten2, TRUE); //blocks until finished
checkWrite1ForCompletionNext = true;
writePacketsPending--; //Just finished sending an OUT packet. This effectively dequeues the request since it is finished.
}
}
// Copy contents from packet to data pointer
memcpy(pData, readPacket.data + 58 - readPacket.bytesPerPacket, readPacket.bytesPerPacket);
// Increment data pointer
pData += readPacket.bytesPerPacket;
// Increment address by however many bytes were received divided by how many bytes per address
readAddress += readPacket.bytesPerPacket / bytesPerAddress;
}
// if successfully received entire region, return success
return Success;
}//if(connected)
// If not connected, return not connected
return NotConnected;
}
#else
//Must be running on Mac OS X or Linux
Comm::ErrorCode Comm::GetData(uint32_t address, unsigned char bytesPerPacket,
unsigned char bytesPerAddress, unsigned char bytesPerWord,
uint32_t endAddress, unsigned char *pData)
{
ReadPacket readPacket;
WritePacket writePacket;
ErrorCode result;
uint32_t percentCompletion;
uint32_t addressesToFetch = endAddress - address;
//Check to avoid possible division by zero when computing the percentage completion status.
if(addressesToFetch == 0)
addressesToFetch++;
if(connected)
{
//First error check the input parameters before using them
if((pData == NULL) || (endAddress < address) || (bytesPerPacket == 0))
{
qWarning("Error, bad parameters provided to call of GetData()");
return Fail;
}
// Continue reading from device until the entire programmable region has been read
while(address < endAddress)
{
//Update the progress bar so the user knows things are happening.
percentCompletion = 100*((float)1 - (float)((float)(endAddress - address)/(float)addressesToFetch));
if(percentCompletion > 100)
{
percentCompletion = 100;
}
//Reformat the percent completion so it "fits" in the 33% to 66% region (since erase
//"completes" 0%-32% of the total erase/program/verify cycle, and verify completes 67%-100%).
percentCompletion /= 3;
percentCompletion += 67;
emit SetProgressBar(percentCompletion);
// Set up the buffer packet with the appropriate address and with the get data command
memset((void*)&writePacket, 0x00, sizeof(writePacket));
writePacket.command = GET_DATA;
writePacket.address = address;
//Debug output info.
qWarning("Fetching packet with address: 0x%x", (uint32_t)writePacket.address);
// Calculate to see if the entire buffer can be filled with data, or just partially
if(((endAddress - address) * bytesPerAddress) < bytesPerPacket)
// If the amount of bytes left over between current address and end address is less than
// the max amount of bytes per packet, then make sure the bytesPerPacket info is updated
writePacket.bytesPerPacket = (endAddress - address) * bytesPerAddress;
else
// Otherwise keep it at its maximum
writePacket.bytesPerPacket = bytesPerPacket;
// Send the packet
result = SendPacket((unsigned char*)&writePacket, sizeof(writePacket));
// If it wasn't successful, then return with error
if(result != Success)
{
qWarning("Error during verify sending packet with address: 0x%x", (uint32_t)writePacket.address);
return result;
}
// Otherwise, read back the packet from the device
memset((void*)&readPacket, 0x00, sizeof(readPacket));
result = ReceivePacket((unsigned char*)&readPacket, sizeof(readPacket));
// If it wasn't successful, then return with error
if(result != Success)
{
qWarning("Error reading packet with address: 0x%x", (uint32_t)readPacket.address);
return result;
}
// Copy contents from packet to data pointer
memcpy(pData, readPacket.data + 58 - readPacket.bytesPerPacket, readPacket.bytesPerPacket);
// Increment data pointer
pData += readPacket.bytesPerPacket;
// Increment address by however many bytes were received divided by how many bytes per address
address += readPacket.bytesPerPacket / bytesPerAddress;
}
// if successfully received entire region, return success
return Success;
}//if(connected)
// If not connected, return not connected
return NotConnected;
}
#endif
/**
*
*/
Comm::ErrorCode Comm::Erase(void) {
WritePacket sendPacket;
QTime elapsed;
ErrorCode status;
if(connected) {
memset((void*)&sendPacket, 0x00, sizeof(sendPacket));
sendPacket.command = ERASE_DEVICE;
qDebug("Sending Erase Command...");
elapsed.start();
status = SendPacket((unsigned char*)&sendPacket, sizeof(sendPacket));
if(status == Comm::Success)
qDebug("Successfully sent erase command (%fs)", (double)elapsed.elapsed() / 1000);
else
qDebug("Erasing Device Failed");
return status;
}
qDebug("Device not connected");
return NotConnected;
}
//Sends command to USB device to lock or unlock the config bit region. If the config bits overlapped an
//erase page with standard program memory, this will also affect the status of the erase page. (for example
//locking the config bits on a PIC18FxxJ or PIC24FJ device, which stores config bits at the end of the last page
//of flash memory, will also prevent reprogramming of the last page of flash memory).
//The bool lock value input parameter is either true (lock/protect the config bits), or false (unlock/allow
//reprogramming of config bits and any overlapping flash erase page, if relevant).
Comm::ErrorCode Comm::LockUnlockConfig(bool lock)
{
WritePacket sendPacket;
QTime elapsed;
ErrorCode status;
if(connected) {
memset((void*)&sendPacket, 0x00, sizeof(sendPacket));
sendPacket.command = UNLOCK_CONFIG;
if(lock == false)
{
sendPacket.LockedValue = 0x00; //0x00 is used in the bootloader firmware to mean unlock the config bits ("UNLOCKCONFIG")
}
else
{
//All other values should mean we are locking the config bits
sendPacket.LockedValue = 0x01; //lock the config bits
}
if(lock)
qDebug("Locking Config Bits...");
else
qDebug("Unlocking Config Bits...");
elapsed.start();
status = SendPacket((unsigned char*)&sendPacket, sizeof(sendPacket));
if(status == Comm::Success)
qDebug("Successfully sent un/lock command (%fs)", (double)elapsed.elapsed() / 1000);
else
qDebug("Unsuccessfully sent un/lock command");
return status;
}
qDebug("Device not connected");
return NotConnected;
}
/**
*
*/
Comm::ErrorCode Comm::ReadBootloaderInfo(BootInfo* bootInfo)
{
QTime elapsed;
WritePacket sendPacket;
ErrorCode status;
qDebug("Getting Query packet...");
if(connected)
{
memset((void*)&sendPacket, 0x00, sizeof(sendPacket));
sendPacket.command = QUERY_DEVICE;
elapsed.start();
status = SendPacket((unsigned char*)&sendPacket, sizeof(sendPacket));
switch(status)
{
case Fail:
close();
case Timeout:
return status;
default:
break;
}
qDebug("Successfully sent querying command (%fs)", (double)elapsed.elapsed() / 1000);
memset((void*)bootInfo, 0x00, sizeof(BootInfo));
elapsed.start();
status = ReceivePacket((unsigned char*)bootInfo, sizeof(BootInfo));
if(bootInfo->command != 0x02) {
qWarning("Received incorrect command.");
return IncorrectCommand;
}
switch(status)
{
case Fail:
close();
case Timeout:
return status;
default:
break;
}
qDebug("Successfully received query packet (%fs)", (double)elapsed.elapsed() / 1000);
return Success;
}
return NotConnected;
}
Comm::ErrorCode Comm::ReadExtendedQueryInfo(ExtendedQueryInfo* extendedBootInfo)
{
QTime elapsed;
WritePacket sendPacket;
ErrorCode status;
qDebug("Getting Extended Query Info packet...");
if(connected)
{
memset((void*)&sendPacket, 0x00, sizeof(sendPacket));
sendPacket.command = QUERY_EXTENDED_INFO;
elapsed.start();
status = SendPacket((unsigned char*)&sendPacket, sizeof(sendPacket));
switch(status)
{
case Fail:
close();
case Timeout:
return status;
default:
break;
}
qDebug("Successfully sent QUERY_EXTENDED_INFO command (%fs)", (double)elapsed.elapsed() / 1000);
memset((void*)extendedBootInfo, 0x00, sizeof(ExtendedQueryInfo));
elapsed.start();
status = ReceivePacket((unsigned char*)extendedBootInfo, sizeof(ExtendedQueryInfo));
if(extendedBootInfo->command != QUERY_EXTENDED_INFO)
{
qWarning("Received incorrect command.");
return IncorrectCommand;
}
switch(status)
{
case Fail:
close();
case Timeout:
return status;
default:
break;
}
qDebug("Successfully received QUERY_EXTENDED_INFO response packet (%fs)", (double)elapsed.elapsed() / 1000);
return Success;
}
return NotConnected;
}
Comm::ErrorCode Comm::SignFlash(void)
{
QTime elapsed;
WritePacket sendPacket;
ErrorCode status;
BootInfo QueryInfoBuffer;
qDebug("Sending SIGN_FLASH command...");
if(connected)
{
memset((void*)&sendPacket, 0x00, sizeof(sendPacket));
sendPacket.command = SIGN_FLASH;
elapsed.start();
status = SendPacket((unsigned char*)&sendPacket, sizeof(sendPacket));
switch(status)
{
case Fail:
close();
case Timeout:
return status;
default:
break;
}
//Now issue a query command, so as to "poll" for the completion of
//the prior request (which doesn't by itself generate a respone packet).
status = ReadBootloaderInfo(&QueryInfoBuffer);
switch(status)
{
case Fail:
close();
case Timeout:
return status;
default:
break;
}
qDebug("Successfully sent SIGN_FLASH command (%fs)", (double)elapsed.elapsed() / 1000);
return Success;
}//if(connected)
return NotConnected;
}
Comm::ErrorCode Comm::SendPacket(unsigned char *pData, int size)
{
QTime timeoutTimer;
int res = 0, timeout = 5;
timeoutTimer.start();
while(res < 1)
{
res = hid_write(boot_device, pData, size);
if(timeoutTimer.elapsed() > SyncWaitTime)
{
timeoutTimer.start();
timeout--;
}
// If timed out several times, or return error then close device and return failure
if(timeout == 0)
{
qWarning("Timed out waiting for query command acknowledgement.");
return Timeout;
}
if(res == -1)
{
qWarning("Write failed.");
close();
return Fail;
}
}
return Success;
}
Comm::ErrorCode Comm::ReceivePacket(unsigned char *data, int size)
{
QTime timeoutTimer;
int res = 0, timeout = 3;
timeoutTimer.start();
while(res < 1)
{
res = hid_read(boot_device, data, size);
if(timeoutTimer.elapsed() > SyncWaitTime)
{
timeoutTimer.start();
timeout--;
}
// If timed out twice, or return error then close device and return failure
if(timeout == 0)
{
qWarning("Timeout.");
return Timeout;
}
if(res == -1)
{
qWarning("Read failed.");
close();
return Fail;
}
}
return Success;
}