Labrador/Desktop_Interface/unixusbdriver.cpp

376 lines
12 KiB
C++

#include "unixusbdriver.h"
#include "platformspecific.h"
unixUsbDriver::unixUsbDriver(QWidget *parent) : genericUsbDriver(parent)
{
qDebug() << "unixUsbDriver created!";
tv.tv_sec = 0;
tv.tv_usec = 100000;
for (unsigned char k=0; k<NUM_ISO_ENDPOINTS; k++){
midBuffer_current[k] = (unsigned char*)calloc(ISO_PACKETS_PER_CTX * ISO_PACKET_SIZE, 1);
midBuffer_prev[k] = (unsigned char*)calloc(ISO_PACKETS_PER_CTX * ISO_PACKET_SIZE, 1);
}
}
unixUsbDriver::~unixUsbDriver(void){
qDebug() << "\n\nunixUsbDriver destructor ran!";
//unixDriverDeleteMutex.lock();
if(connected){
workerThread->terminate();
delete(isoHandler);
delete(workerThread);
qDebug() << "THREAD Gone!";
for (int i=0; i<NUM_FUTURE_CTX; i++){
for (int k=0; k<NUM_ISO_ENDPOINTS; k++){
libusb_free_transfer(isoCtx[k][i]);
}
}
qDebug() << "Transfers freed.";
}
if(handle != NULL){
libusb_release_interface(handle, 0);
qDebug() << "Interface released";
libusb_close(handle);
qDebug() << "Device Closed";
}
if(ctx != NULL){
libusb_exit(ctx);
qDebug() << "Libusb exited";
}
//unixDriverDeleteMutex.unlock();
qDebug() << "unixUsbDriver destructor completed!\n\n";
}
unsigned char unixUsbDriver::usbInit(unsigned long VIDin, unsigned long PIDin){
qDebug() << "Entering unixUsbDriver::usbInit";
int error;
//Should only run once.
if(ctx == NULL){
error = libusb_init(&ctx);
if(error){
qDebug() << "libusb_init FAILED";
return 1;
} else qDebug() << "Libusb context initialised";
libusb_set_debug(ctx, 3);
}
if(handle == NULL){
handle = libusb_open_device_with_vid_pid(ctx, VIDin, PIDin);
if(handle==NULL){
qDebug() << "DEVICE NOT FOUND";
return 1;
}
qDebug() << "Device found!!";
}
qDebug() << (libusb_kernel_driver_active(handle, 0) ? "KERNEL DRIVER ACTIVE" : "KERNEL DRIVER INACTIVE");
if(libusb_kernel_driver_active(handle, 0)){
libusb_detach_kernel_driver(handle, 0);
}
error = libusb_claim_interface(handle, 0);
if(error){
qDebug() << "libusb_claim_interface FAILED";
qDebug() << "ERROR" << error << libusb_error_name(error);
handle = NULL;
return 1;
} else qDebug() << "Interface claimed!";
return 0;
}
void unixUsbDriver::usbSendControl(uint8_t RequestType, uint8_t Request, uint16_t Value, uint16_t Index, uint16_t Length, unsigned char *LDATA){
//qDebug("Sending Control packet! 0x%x,\t0x%x,\t%u,\t%u,\t%d,\t%u", RequestType, Request, Value, Index, LDATA, Length);
unsigned char *controlBuffer;
if(!connected){
qDebug() << "Control packet requested before device has connected!";
return;
}
if (LDATA==NULL){
controlBuffer = inBuffer;
}
else controlBuffer = LDATA;
int error = libusb_control_transfer(handle, RequestType, Request, Value, Index, controlBuffer, Length, 4000);
if(error){
qDebug("unixUsbDriver::usbSendControl FAILED with error %s", libusb_error_name(error));
} //else qDebug() << "unixUsbDriver::usbSendControl SUCCESS";
if((error == LIBUSB_ERROR_NO_DEVICE) && (Request!=0xa7)){ //Bootloader Jump won't return; this is expected behaviour.
qDebug() << "Device not found. Becoming an hero.";
connectedStatus(false);
killMe();
}
return;
}
unsigned char unixUsbDriver::usbIsoInit(void){
int error;
for(int n=0;n<NUM_FUTURE_CTX;n++){
for (unsigned char k=0;k<NUM_ISO_ENDPOINTS;k++){
isoCtx[k][n] = libusb_alloc_transfer(ISO_PACKETS_PER_CTX);
transferCompleted[k][n].number = (k * ISO_PACKETS_PER_CTX) + n;
transferCompleted[k][n].completed = false;
libusb_fill_iso_transfer(isoCtx[k][n], handle, pipeID[k], dataBuffer[k][n], ISO_PACKET_SIZE*ISO_PACKETS_PER_CTX, ISO_PACKETS_PER_CTX, isoCallback, (void*)&transferCompleted[k][n], 4000);
libusb_set_iso_packet_lengths(isoCtx[k][n], ISO_PACKET_SIZE);
}
}
for(int n=0;n<NUM_FUTURE_CTX;n++){
for (unsigned char k=0;k<NUM_ISO_ENDPOINTS;k++){
error = libusb_submit_transfer(isoCtx[k][n]);
if(error){
qDebug() << "libusb_submit_transfer FAILED";
qDebug() << "ERROR" << libusb_error_name(error);
} else {
if(n == 0){
qint64 t0;
qint64 t = QDateTime::currentMSecsSinceEpoch();
if(k==0){
t0 = t;
}
midBufferOffsets[k] = t0 - t;
qDebug() << "isoCtx submitted successfully!";
qDebug() << "[n, k] = " << n << k;
qDebug() << "t = " << t;
qDebug() << "Delay = " << 0;//midBufferOffsets[k];
}
}
}
}
isoTimer = new QTimer();
isoTimer->setTimerType(Qt::PreciseTimer);
isoTimer->start(ISO_TIMER_PERIOD);
connect(isoTimer, SIGNAL(timeout()), this, SLOT(isoTimerTick()));
qDebug() << "Setup successful!";
isoHandler = new worker();
workerThread = new QThread();
isoHandler->ctx = ctx;
isoHandler->moveToThread(workerThread);
connect(workerThread, SIGNAL(started()), isoHandler, SLOT(handle()));
workerThread->start();
qDebug() << "MAIN THREAD ID" << QThread::currentThreadId();
//QThread::sleep(1);
qDebug() << "Iso Stack initialised!";
return 1;
}
void unixUsbDriver::isoTimerTick(void){
timerCount++;
char subString[3] = "th";
if(timerCount%10 == 1) strcpy(subString, "st");
if(timerCount%10 == 2) strcpy(subString, "nd");
if(timerCount%10 == 3) strcpy(subString, "rd");
if((timerCount<20) && (timerCount > 10)) strcpy(subString, "th");
//qDebug("\n\nThis is the %d%s Tick!", timerCount, subString);
int n, error, earliest = MAX_OVERLAP;
qint64 minFrame = 9223372036854775807; //max value for 64 bit signed
unsigned int i, packetLength = 0;
unsigned char* packetPointer;
tcBlockMutex.lock();
for (n=0; n<NUM_FUTURE_CTX; n++){
if(allEndpointsComplete(n)){
//qDebug("Transfer %d is complete!!", n);
if(transferCompleted[0][n].timeReceived < minFrame){
minFrame = transferCompleted[0][n].timeReceived;
earliest = n;
}
}
}
if (earliest == MAX_OVERLAP){
tcBlockMutex.unlock();
//qDebug() << "Returning early...";
return;
}
//qDebug() << "Processing Ctx" << earliest;
//Swap the buffers so that we override the oldest one.
for (unsigned char k = 0; k< NUM_ISO_ENDPOINTS; k++){
unsigned char* temp = midBuffer_prev[k];
midBuffer_prev[k] = midBuffer_current[k];
midBuffer_current[k] = temp;
}
//Copy iso data into mid-buffer
for(i=0;i<isoCtx[0][earliest]->num_iso_packets;i++){
for(unsigned char k=0; k<NUM_ISO_ENDPOINTS;k++){
packetPointer = libusb_get_iso_packet_buffer_simple(isoCtx[k][earliest], i);
//qDebug() << packetLength;
memcpy(&(midBuffer_current[k][packetLength]), packetPointer, isoCtx[k][earliest]->iso_packet_desc[i].actual_length);
}
packetLength += ISO_PACKET_SIZE;
}
packetLength = 0; //I don't really know why I use the same variable twice but hey
//Read out from mid-buffer to out-buffer
unsigned char *srcPtr;
qint64 current_offset;
for(i=0;i<isoCtx[0][earliest]->num_iso_packets;i++){
for(unsigned char k=0; k<NUM_ISO_ENDPOINTS;k++){
current_offset = midBufferOffsets[k] + i;
if(current_offset >= 0){
srcPtr = &midBuffer_current[k][ISO_PACKET_SIZE * current_offset];
}else{
srcPtr = &midBuffer_prev[k][ISO_PACKET_SIZE * (ISO_PACKETS_PER_CTX + current_offset)];
}
memcpy(&(outBuffers[currentWriteBuffer][packetLength]), srcPtr, ISO_PACKET_SIZE);
packetLength += ISO_PACKET_SIZE;
}
}
//qDebug() << "Data copy complete!";
//Control data for isoDriver
bufferLengths[currentWriteBuffer] = packetLength;
currentWriteBuffer = !currentWriteBuffer;
//Setup next transfer
for(unsigned char k=0; k<NUM_ISO_ENDPOINTS;k++){
transferCompleted[k][earliest].completed = false;
if(shutdownMode){
error = libusb_cancel_transfer(isoCtx[k][earliest]);
numCancelled++;
qDebug() << "Cancelled" << earliest << k;
qDebug() << "Total Cancelled =" << numCancelled;
if(numCancelled == (NUM_FUTURE_CTX * NUM_ISO_ENDPOINTS)){
isoHandler->stopTime = true;
}
}else{
error = libusb_submit_transfer(isoCtx[k][earliest]);
}
if(error){
qDebug() << (shutdownMode ? "libusb_cancel_transfer FAILED" : "libusb_submit_transfer FAILED");
qDebug() << "ERROR" << libusb_error_name(error);
} //else qDebug() << "isoCtx submitted successfully!";
}
tcBlockMutex.unlock();
//qDebug() << "Calling upTick()";
upTick();
return;
}
char *unixUsbDriver::isoRead(unsigned int *newLength){
//*(newLength) = 0;
//return (char*) NULL;
//qDebug() << "unixUsbDriver::isoRead";
*(newLength) = bufferLengths[!currentWriteBuffer];
return (char*) outBuffers[(unsigned char) !currentWriteBuffer];
}
void unixUsbDriver::recoveryTick(void){
//This should not be called in shutdown mode since it cause double deletion!
if(!shutdownMode){
avrDebug();
}
}
bool unixUsbDriver::allEndpointsComplete(int n){
//Just tells you if transfers have completed on _all_ iso endpoints for a given value of n.
for (unsigned char k=0;k<NUM_ISO_ENDPOINTS;k++){
if(!transferCompleted[k][n].completed){
return false;
}
}
return true;
}
void unixUsbDriver::shutdownProcedure(){
shutdownMode = true;
QTimer::singleShot(1500, this, SLOT(backupCleanup()));
}
//On physical disconnect, isoTimerTick will not assert stopTime. Hence this duct-tape function.
void unixUsbDriver::backupCleanup(){
isoHandler->stopTime = true;
}
int unixUsbDriver::flashFirmware(void){
#ifndef PLATFORM_ANDROID
char fname[128];
qDebug() << "\n\n\n\n\n\n\n\nFIRMWARE MISMATCH!!!! FLASHING....\n\n\n\n\n\n\n";
sprintf(fname, "/firmware/labrafirm_%04x_%02x.hex", EXPECTED_FIRMWARE_VERSION, DEFINED_EXPECTED_VARIANT);
qDebug() << "FLASHING " << fname;
bootloaderJump();
qDebug() << "BA94 closed";
QThread::msleep(2000);
QString dirString = QCoreApplication::applicationDirPath();
dirString.append(fname);
QByteArray array = dirString.toLocal8Bit();
char* buffer = array.data();
//qDebug() << buffer;
//Set up interface to dfuprog
int exit_code = 1;
char command1[256];
sprintf(command1, "dfu-programmer atxmega32a4u erase --force");
char command2[256];
sprintf(command2, "dfu-programmer atxmega32a4u flash %s", buffer);
char command3[256];
sprintf(command3, "dfu-programmer atxmega32a4u launch");
char command4[256];
sprintf(command4, "dfu-programmer atxmega32a4u launch");
//Run stage 1
while(exit_code){
QThread::msleep(250);
exit_code = dfuprog_virtual_cmd(command1);
}
//Run stage 2
exit_code = dfuprog_virtual_cmd(command2);
if(exit_code){
return exit_code+200;
}
//Run stage 3
exit_code = dfuprog_virtual_cmd(command3);
if(exit_code){
return exit_code+300;
}
QThread::msleep(2000);
//Run stage 4 - double launch to clear the eeprom flag from bootloaderJump.
exit_code = 1;
while(exit_code){
QThread::msleep(250);
exit_code = dfuprog_virtual_cmd(command4);
}
libusb_release_interface(handle, 0);
qDebug() << "Interface released";
libusb_close(handle);
qDebug() << "Device Closed";
libusb_exit(ctx);
qDebug() << "Libusb exited";
connected = false;
handle = NULL;
ctx = NULL;
return 0;
#endif
}