2020-08-31 04:03:41 +08:00
# include "device.h"
# include <signal.h>
# include <QDebug>
# include <QString>
# include <QMessageBox>
# include <mutex>
using namespace std ;
Device : : Device ( QString serial )
{
qDebug ( ) < < " Starting device connection... " ;
m_handle = nullptr ;
libusb_init ( & m_context ) ;
SearchDevices ( [ = ] ( libusb_device_handle * handle , QString found_serial ) - > bool {
if ( serial . isEmpty ( ) | | serial = = found_serial ) {
// accept connection to this device
m_serial = found_serial ;
m_handle = handle ;
// abort device search
return false ;
} else {
// not the requested device, continue search
return true ;
}
} , m_context ) ;
if ( ! m_handle ) {
QString message = " No device found " ;
auto msg = new QMessageBox ( QMessageBox : : Icon : : Warning , " Error opening device " , message ) ;
msg - > exec ( ) ;
libusb_exit ( m_context ) ;
throw std : : runtime_error ( message . toStdString ( ) ) ;
return ;
}
// Found the correct device, now connect
/* claim the interfaces */
for ( int if_num = 0 ; if_num < 1 ; if_num + + ) {
int ret = libusb_claim_interface ( m_handle , if_num ) ;
if ( ret < 0 ) {
libusb_close ( m_handle ) ;
/* Failed to open */
QString message = " Failed to claim interface: \" " ;
message . append ( libusb_strerror ( ( libusb_error ) ret ) ) ;
message . append ( " \" Maybe you are already connected to this device? " ) ;
qWarning ( ) < < message ;
auto msg = new QMessageBox ( QMessageBox : : Icon : : Warning , " Error opening device " , message ) ;
msg - > exec ( ) ;
libusb_exit ( m_context ) ;
throw std : : runtime_error ( message . toStdString ( ) ) ;
}
}
qInfo ( ) < < " USB connection established " < < flush ;
m_connected = true ;
m_receiveThread = new std : : thread ( & Device : : USBHandleThread , this ) ;
dataBuffer = new USBInBuffer ( m_handle , EP_Data_In_Addr , 2048 ) ;
logBuffer = new USBInBuffer ( m_handle , EP_Log_In_Addr , 2048 ) ;
connect ( dataBuffer , & USBInBuffer : : DataReceived , this , & Device : : ReceivedData , Qt : : DirectConnection ) ;
connect ( dataBuffer , & USBInBuffer : : TransferError , this , & Device : : ConnectionLost ) ;
connect ( logBuffer , & USBInBuffer : : DataReceived , this , & Device : : ReceivedLog , Qt : : DirectConnection ) ;
}
Device : : ~ Device ( )
{
if ( m_connected ) {
delete dataBuffer ;
delete logBuffer ;
m_connected = false ;
for ( int if_num = 0 ; if_num < 1 ; if_num + + ) {
int ret = libusb_release_interface ( m_handle , if_num ) ;
if ( ret < 0 ) {
qCritical ( ) < < " Error releasing interface " < < libusb_error_name ( ret ) ;
}
}
libusb_close ( m_handle ) ;
m_receiveThread - > join ( ) ;
libusb_exit ( m_context ) ;
}
}
bool Device : : Configure ( Protocol : : SweepSettings settings )
{
if ( m_connected ) {
unsigned char buffer [ 128 ] ;
Protocol : : PacketInfo p ;
p . type = Protocol : : PacketType : : SweepSettings ;
p . settings = settings ;
unsigned int length = Protocol : : EncodePacket ( p , buffer , sizeof ( buffer ) ) ;
if ( ! length ) {
qCritical ( ) < < " Failed to encode packet " ;
return false ;
}
int actual_length ;
auto ret = libusb_bulk_transfer ( m_handle , EP_Data_Out_Addr , buffer , length , & actual_length , 0 ) ;
if ( ret < 0 ) {
qCritical ( ) < < " Error sending data: "
< < libusb_strerror ( ( libusb_error ) ret ) ;
return false ;
}
return true ;
} else {
return false ;
}
}
bool Device : : SetManual ( Protocol : : ManualControl manual )
{
if ( m_connected ) {
unsigned char buffer [ 128 ] ;
Protocol : : PacketInfo p ;
p . type = Protocol : : PacketType : : ManualControl ;
p . manual = manual ;
unsigned int length = Protocol : : EncodePacket ( p , buffer , sizeof ( buffer ) ) ;
if ( ! length ) {
qCritical ( ) < < " Failed to encode packet " ;
return false ;
}
int actual_length ;
auto ret = libusb_bulk_transfer ( m_handle , EP_Data_Out_Addr , buffer , length , & actual_length , 0 ) ;
if ( ret < 0 ) {
qCritical ( ) < < " Error sending data: "
< < libusb_strerror ( ( libusb_error ) ret ) ;
return false ;
}
return true ;
} else {
return false ;
}
}
bool Device : : SendFirmwareChunk ( Protocol : : FirmwarePacket & fw )
{
if ( m_connected ) {
unsigned char buffer [ Protocol : : FirmwareChunkSize + 4 + 8 ] ;
Protocol : : PacketInfo p ;
p . type = Protocol : : PacketType : : FirmwarePacket ;
p . firmware = fw ;
unsigned int length = Protocol : : EncodePacket ( p , buffer , sizeof ( buffer ) ) ;
if ( ! length ) {
qCritical ( ) < < " Failed to encode packet " ;
return false ;
}
int actual_length ;
auto ret = libusb_bulk_transfer ( m_handle , EP_Data_Out_Addr , buffer , length , & actual_length , 0 ) ;
2020-08-31 23:57:24 +08:00
if ( ret < 0 ) {
qCritical ( ) < < " Error sending data: "
< < libusb_strerror ( ( libusb_error ) ret ) ;
return false ;
}
return true ;
} else {
return false ;
}
}
bool Device : : SendCommandWithoutPayload ( Protocol : : PacketType type )
{
if ( m_connected ) {
unsigned char buffer [ 32 ] ;
Protocol : : PacketInfo p ;
p . type = type ;
unsigned int length = Protocol : : EncodePacket ( p , buffer , sizeof ( buffer ) ) ;
if ( ! length ) {
qCritical ( ) < < " Failed to encode packet " ;
return false ;
}
int actual_length ;
auto ret = libusb_bulk_transfer ( m_handle , EP_Data_Out_Addr , buffer , length , & actual_length , 0 ) ;
2020-08-31 04:03:41 +08:00
if ( ret < 0 ) {
qCritical ( ) < < " Error sending data: "
< < libusb_strerror ( ( libusb_error ) ret ) ;
return false ;
}
return true ;
} else {
return false ;
}
}
2020-09-02 03:33:10 +08:00
std : : set < QString > Device : : GetDevices ( )
2020-08-31 04:03:41 +08:00
{
2020-09-02 03:33:10 +08:00
std : : set < QString > serials ;
2020-08-31 04:03:41 +08:00
libusb_context * ctx ;
libusb_init ( & ctx ) ;
SearchDevices ( [ & serials ] ( libusb_device_handle * , QString serial ) - > bool {
2020-09-02 03:33:10 +08:00
serials . insert ( serial ) ;
2020-08-31 04:03:41 +08:00
return true ;
} , ctx ) ;
libusb_exit ( ctx ) ;
return serials ;
}
void Device : : USBHandleThread ( )
{
qInfo ( ) < < " Receive thread started " < < flush ;
while ( m_connected ) {
libusb_handle_events ( m_context ) ;
}
qDebug ( ) < < " Disconnected, receive thread exiting " ;
}
void Device : : SearchDevices ( std : : function < bool ( libusb_device_handle * , QString ) > foundCallback , libusb_context * context )
{
libusb_device * * devList ;
auto ndevices = libusb_get_device_list ( context , & devList ) ;
for ( ssize_t idx = 0 ; idx < ndevices ; idx + + ) {
int ret ;
libusb_device * device = devList [ idx ] ;
libusb_device_descriptor desc = { } ;
ret = libusb_get_device_descriptor ( device , & desc ) ;
if ( ret ) {
/* some error occured */
qCritical ( ) < < " Failed to get device descriptor: "
< < libusb_strerror ( ( libusb_error ) ret ) ;
continue ;
}
if ( desc . idVendor ! = VID | | desc . idProduct ! = PID ) {
/* Not the correct IDs */
continue ;
}
/* Try to open the device */
libusb_device_handle * handle = nullptr ;
ret = libusb_open ( device , & handle ) ;
if ( ret ) {
/* Failed to open */
QString message = " Found potential device but failed to open usb connection: \" " ;
message . append ( libusb_strerror ( ( libusb_error ) ret ) ) ;
message . append ( " \" On Linux this is most likely caused by a missing udev rule. On Windows it could be a missing driver. Try installing the WinUSB driver using Zadig (https://zadig.akeo.ie/) " ) ;
qWarning ( ) < < message ;
auto msg = new QMessageBox ( QMessageBox : : Icon : : Warning , " Error opening device " , message ) ;
msg - > exec ( ) ;
continue ;
}
char c_product [ 256 ] ;
char c_serial [ 256 ] ;
libusb_get_string_descriptor_ascii ( handle , desc . iSerialNumber ,
( unsigned char * ) c_serial , sizeof ( c_serial ) ) ;
ret = libusb_get_string_descriptor_ascii ( handle , desc . iProduct ,
( unsigned char * ) c_product , sizeof ( c_product ) ) ;
if ( ret > 0 ) {
/* managed to read the product string */
QString product ( c_product ) ;
qDebug ( ) < < " Opened device: " < < product ;
if ( product = = " VNA " ) {
// this is a match
if ( ! foundCallback ( handle , QString ( c_serial ) ) ) {
// abort search
break ;
}
}
} else {
qWarning ( ) < < " Failed to get product descriptor: "
< < libusb_strerror ( ( libusb_error ) ret ) ;
}
libusb_close ( handle ) ;
}
libusb_free_device_list ( devList , 1 ) ;
}
Protocol : : DeviceInfo Device : : getLastInfo ( ) const
{
return lastInfo ;
}
QString Device : : getLastDeviceInfoString ( )
{
QString ret ;
if ( ! lastInfoValid ) {
ret . append ( " No device information available yet " ) ;
} else {
ret . append ( " HW Rev. " ) ;
ret . append ( lastInfo . HW_Revision ) ;
ret . append ( " FW " + QString : : number ( lastInfo . FW_major ) + " . " + QString : : number ( lastInfo . FW_minor ) . rightJustified ( 2 , ' 0 ' ) ) ;
ret . append ( " Temps: " + QString : : number ( lastInfo . temperatures . source ) + " °C/ " + QString : : number ( lastInfo . temperatures . LO1 ) + " °C/ " + QString : : number ( lastInfo . temperatures . MCU ) + " °C " ) ;
ret . append ( " Reference: " ) ;
if ( lastInfo . extRefInUse ) {
ret . append ( " External " ) ;
} else {
ret . append ( " Internal " ) ;
if ( lastInfo . extRefAvailable ) {
ret . append ( " (External available) " ) ;
}
}
}
return ret ;
}
void Device : : ReceivedData ( )
{
Protocol : : PacketInfo packet ;
uint16_t handled_len ;
do {
handled_len = Protocol : : DecodeBuffer ( dataBuffer - > getBuffer ( ) , dataBuffer - > getReceived ( ) , & packet ) ;
dataBuffer - > removeBytes ( handled_len ) ;
if ( packet . type = = Protocol : : PacketType : : Datapoint ) {
emit DatapointReceived ( packet . datapoint ) ;
} else if ( packet . type = = Protocol : : PacketType : : Status ) {
qDebug ( ) < < " Got status " ;
emit ManualStatusReceived ( packet . status ) ;
} else if ( packet . type = = Protocol : : PacketType : : DeviceInfo ) {
lastInfo = packet . info ;
lastInfoValid = true ;
emit DeviceInfoUpdated ( ) ;
} else if ( packet . type = = Protocol : : PacketType : : Ack ) {
emit AckReceived ( ) ;
}
} while ( handled_len > 0 ) ;
}
void Device : : ReceivedLog ( )
{
uint16_t handled_len ;
do {
handled_len = 0 ;
auto firstLinebreak = ( uint8_t * ) memchr ( logBuffer - > getBuffer ( ) , ' \n ' , logBuffer - > getReceived ( ) ) ;
if ( firstLinebreak ) {
handled_len = firstLinebreak - logBuffer - > getBuffer ( ) ;
auto line = QString : : fromLatin1 ( ( const char * ) logBuffer - > getBuffer ( ) , handled_len - 1 ) ;
emit LogLineReceived ( line ) ;
logBuffer - > removeBytes ( handled_len + 1 ) ;
}
} while ( handled_len > 0 ) ;
}
QString Device : : serial ( ) const
{
return m_serial ;
}
USBInBuffer : : USBInBuffer ( libusb_device_handle * handle , unsigned char endpoint , int buffer_size ) :
buffer_size ( buffer_size ) ,
received_size ( 0 ) ,
inCallback ( false )
{
buffer = new unsigned char [ buffer_size ] ;
transfer = libusb_alloc_transfer ( 0 ) ;
libusb_fill_bulk_transfer ( transfer , handle , endpoint , buffer , 64 , CallbackTrampoline , this , 100 ) ;
libusb_submit_transfer ( transfer ) ;
}
USBInBuffer : : ~ USBInBuffer ( )
{
if ( transfer ) {
qDebug ( ) < < " Start cancellation " ;
libusb_cancel_transfer ( transfer ) ;
// wait for cancellation to complete
mutex mtx ;
unique_lock < mutex > lck ( mtx ) ;
cv . wait ( lck ) ;
qDebug ( ) < < " Cancellation complete " ;
}
delete buffer ;
}
void USBInBuffer : : removeBytes ( int handled_bytes )
{
if ( ! inCallback ) {
throw runtime_error ( " Removing of bytes is only allowed from within receive callback " ) ;
}
if ( handled_bytes > = received_size ) {
received_size = 0 ;
} else {
// not removing all bytes, have to move remaining data to the beginning of the buffer
memmove ( buffer , & buffer [ handled_bytes ] , received_size - handled_bytes ) ;
received_size - = handled_bytes ;
}
}
int USBInBuffer : : getReceived ( ) const
{
return received_size ;
}
void USBInBuffer : : Callback ( libusb_transfer * transfer )
{
switch ( transfer - > status ) {
case LIBUSB_TRANSFER_COMPLETED :
received_size + = transfer - > actual_length ;
inCallback = true ;
emit DataReceived ( ) ;
inCallback = false ;
break ;
case LIBUSB_TRANSFER_ERROR :
case LIBUSB_TRANSFER_NO_DEVICE :
case LIBUSB_TRANSFER_OVERFLOW :
case LIBUSB_TRANSFER_STALL :
qCritical ( ) < < " LIBUSB_TRANSFER_ERROR " ;
libusb_free_transfer ( transfer ) ;
this - > transfer = nullptr ;
emit TransferError ( ) ;
return ;
break ;
case LIBUSB_TRANSFER_TIMED_OUT :
// nothing to do
break ;
case LIBUSB_TRANSFER_CANCELLED :
// destructor called, do not resubmit
libusb_free_transfer ( transfer ) ;
this - > transfer = nullptr ;
cv . notify_all ( ) ;
return ;
break ;
}
// Resubmit the transfer
transfer - > buffer = & buffer [ received_size ] ;
libusb_submit_transfer ( transfer ) ;
}
void USBInBuffer : : CallbackTrampoline ( libusb_transfer * transfer )
{
auto usb = ( USBInBuffer * ) transfer - > user_data ;
usb - > Callback ( transfer ) ;
}
uint8_t * USBInBuffer : : getBuffer ( ) const
{
return buffer ;
}