1022 lines
38 KiB
C++
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;
|
|
}
|