#include "librevnausbdriver.h" #include "CustomWidgets/informationbox.h" #include "deviceusblog.h" #include using namespace std; using USBID = struct { int VID; int PID; }; static constexpr USBID IDs[] = { {0x0483, 0x564e}, {0x0483, 0x4121}, }; USBInBuffer::USBInBuffer(libusb_device_handle *handle, unsigned char endpoint, int buffer_size) : buffer_size(buffer_size), received_size(0), inCallback(false), cancelling(false) { buffer = new unsigned char[buffer_size]; memset(buffer, 0, buffer_size); transfer = libusb_alloc_transfer(0); libusb_fill_bulk_transfer(transfer, handle, endpoint, buffer, buffer_size, CallbackTrampoline, this, 0); libusb_submit_transfer(transfer); } USBInBuffer::~USBInBuffer() { if(transfer) { cancelling = true; libusb_cancel_transfer(transfer); // wait for cancellation to complete mutex mtx; unique_lock lck(mtx); using namespace std::chrono_literals; if(cv.wait_for(lck, 100ms) == cv_status::timeout) { qWarning() << "Timed out waiting for mutex acquisition during disconnect"; } } 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) { if(cancelling || (transfer->status == LIBUSB_TRANSFER_CANCELLED)) { // destructor called, do not resubmit libusb_free_transfer(transfer); this->transfer = nullptr; cv.notify_all(); return; } switch(transfer->status) { case LIBUSB_TRANSFER_COMPLETED: received_size += transfer->actual_length; inCallback = true; emit DataReceived(); inCallback = false; break; case LIBUSB_TRANSFER_NO_DEVICE: qCritical() << "LIBUSB_TRANSFER_NO_DEVICE"; libusb_free_transfer(transfer); return; case LIBUSB_TRANSFER_ERROR: case LIBUSB_TRANSFER_OVERFLOW: case LIBUSB_TRANSFER_STALL: qCritical() << "LIBUSB_ERROR" << transfer->status; libusb_free_transfer(transfer); this->transfer = nullptr; emit TransferError(); return; break; case LIBUSB_TRANSFER_TIMED_OUT: // nothing to do break; case LIBUSB_TRANSFER_CANCELLED: // already handled before switch-case break; } // Resubmit the transfer transfer->buffer = &buffer[received_size]; transfer->length = buffer_size - 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; } LibreVNAUSBDriver::LibreVNAUSBDriver() : LibreVNADriver() { connected = false; m_handle = nullptr; m_context = nullptr; dataBuffer = nullptr; logBuffer = nullptr; m_receiveThread = nullptr; } QString LibreVNAUSBDriver::getDriverName() { return "LibreVNA/USB"; } std::set LibreVNAUSBDriver::GetAvailableDevices() { std::set serials; libusb_context *ctx; libusb_init(&ctx); #if LIBUSB_API_VERSION >= 0x01000106 libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO); #endif SearchDevices([&serials](libusb_device_handle *, QString serial) -> bool { serials.insert(serial); return true; }, ctx, true); libusb_exit(ctx); return serials; } bool LibreVNAUSBDriver::connectTo(QString serial) { if(connected) { disconnect(); } // info = defaultInfo; // status = {}; m_handle = nullptr; // infoValid = false; libusb_init(&m_context); #if LIBUSB_API_VERSION >= 0x01000106 libusb_set_option(m_context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO); #endif SearchDevices([=](libusb_device_handle *handle, QString found_serial) -> bool { if(serial.isEmpty() || serial == found_serial) { // accept connection to this device this->serial = found_serial; m_handle = handle; // abort device search return false; } else { // not the requested device, continue search return true; } }, m_context, false); if(!m_handle) { QString message = "No device found"; InformationBox::ShowError("Error opening device", message); libusb_exit(m_context); throw std::runtime_error(message.toStdString()); } // Found the correct device, now connect /* claim the interface */ int ret = libusb_claim_interface(m_handle, 0); 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; InformationBox::ShowError("Error opening device", message); libusb_exit(m_context); throw std::runtime_error(message.toStdString()); } qInfo() << "USB connection established" << flush; connected = true; m_receiveThread = new std::thread(&LibreVNAUSBDriver::USBHandleThread, this); dataBuffer = new USBInBuffer(m_handle, EP_Data_In_Addr, 65536); logBuffer = new USBInBuffer(m_handle, EP_Log_In_Addr, 65536); connect(dataBuffer, &USBInBuffer::DataReceived, this, &LibreVNAUSBDriver::ReceivedData, Qt::DirectConnection); connect(dataBuffer, &USBInBuffer::TransferError, this, &LibreVNAUSBDriver::ConnectionLost); connect(logBuffer, &USBInBuffer::DataReceived, this, &LibreVNAUSBDriver::ReceivedLog, Qt::DirectConnection); connect(&transmissionTimer, &QTimer::timeout, this, &LibreVNAUSBDriver::transmissionTimeout); connect(this, &LibreVNAUSBDriver::receivedAnswer, this, &LibreVNAUSBDriver::transmissionFinished, Qt::QueuedConnection); connect(this, &LibreVNAUSBDriver::receivedPacket, this, &LibreVNAUSBDriver::handleReceivedPacket, Qt::QueuedConnection); transmissionTimer.setSingleShot(true); transmissionActive = false; sendWithoutPayload(Protocol::PacketType::RequestDeviceInfo); sendWithoutPayload(Protocol::PacketType::RequestDeviceStatus); return true; } void LibreVNAUSBDriver::disconnect() { if(connected) { setIdle(); delete dataBuffer; delete logBuffer; connected = false; serial = ""; 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_release_interface(m_handle, 0); libusb_close(m_handle); m_receiveThread->join(); libusb_exit(m_context); delete m_receiveThread; m_handle = nullptr; m_context = nullptr; m_receiveThread = nullptr; dataBuffer = nullptr; logBuffer = nullptr; } } void LibreVNAUSBDriver::registerTypes() { qRegisterMetaType("LibreVNAUSBPacket"); qRegisterMetaType("LibreVNAUSBResult"); } void LibreVNAUSBDriver::ReceivedData() { Protocol::PacketInfo packet; uint16_t handled_len; // qDebug() << "Received data"; do { // qDebug() << "Decoding" << dataBuffer->getReceived() << "Bytes"; handled_len = Protocol::DecodeBuffer(dataBuffer->getBuffer(), dataBuffer->getReceived(), &packet); // qDebug() << "Handled" << handled_len << "Bytes, type:" << (int) packet.type; if(handled_len > 0) { auto &log = DeviceUSBLog::getInstance(); if(packet.type != Protocol::PacketType::None) { log.addPacket(packet, serial); } else { log.addInvalidBytes(dataBuffer->getBuffer(), handled_len, serial); } } dataBuffer->removeBytes(handled_len); switch(packet.type) { case Protocol::PacketType::Ack: emit receivedAnswer(TransmissionResult::Ack); break; case Protocol::PacketType::Nack: emit receivedAnswer(TransmissionResult::Nack); break; default: // pass on to LibreVNADriver class emit receivedPacket(packet); break; } } while (handled_len > 0); } void LibreVNAUSBDriver::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); } void LibreVNAUSBDriver::transmissionFinished(LibreVNADriver::TransmissionResult result) { lock_guard lock(transmissionMutex); // remove transmitted packet // qDebug() << "Transmission finsished (" << result << "), queue at " << transmissionQueue.size() << " Outstanding ACKs:"< cb, unsigned int timeout) { Transmission t; t.packet = packet; t.timeout = timeout; t.callback = cb; lock_guard lock(transmissionMutex); transmissionQueue.enqueue(t); // qDebug() << "Enqueued packet, queue at " << transmissionQueue.size(); if(!transmissionActive) { startNextTransmission(); } return true; } void LibreVNAUSBDriver::USBHandleThread() { qDebug() << "Receive thread started"; while (connected) { libusb_handle_events(m_context); } qDebug() << "Disconnected, receive thread exiting"; } void LibreVNAUSBDriver::SearchDevices(std::function foundCallback, libusb_context *context, bool ignoreOpenError) { 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; } bool correctID = false; int numIDs = sizeof(IDs)/sizeof(IDs[0]); for(int i=0;i 0) { /* managed to read the product string */ QString product(c_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); } bool LibreVNAUSBDriver::startNextTransmission() { if(transmissionQueue.isEmpty() || !connected) { // nothing more to transmit transmissionActive = false; return false; } transmissionActive = true; auto t = transmissionQueue.head(); unsigned char buffer[1024]; unsigned int length = Protocol::EncodePacket(t.packet, buffer, sizeof(buffer)); if(!length) { qCritical() << "Failed to encode packet"; return false; } int actual_length; auto &log = DeviceUSBLog::getInstance(); log.addPacket(t.packet); 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; } transmissionTimer.start(t.timeout); // qDebug() << "Transmission started, queue at " << transmissionQueue.size(); return true; }