diff --git a/cmake/CPackInfos.cmake b/cmake/CPackInfos.cmake index 0f156f1..f31c0d5 100644 --- a/cmake/CPackInfos.cmake +++ b/cmake/CPackInfos.cmake @@ -44,13 +44,17 @@ else() ) set(ENV{LANG} "en_US") - execute_process( - COMMAND ${GIT_EXECUTABLE} log -n 10 "--date=format:%a %b %d %Y" "--pretty=format:* %ad %aN <%aE> %h - %s" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - RESULT_VARIABLE CMD_RESULT - OUTPUT_VARIABLE CHANGELOG - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + if(GIT_VERSION_STRING VERSION_LESS 2.6) + set(CHANGELOG "") + else() + execute_process( + COMMAND ${GIT_EXECUTABLE} log -n 10 "--date=format:%a %b %d %Y" "--pretty=format:* %ad %aN <%aE> %h - %s" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE CMD_RESULT + OUTPUT_VARIABLE CHANGELOG + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif() file(WRITE "${CMAKE_BINARY_DIR}/changelog" "${CHANGELOG}") endif() diff --git a/openhantek/res/application.qrc b/openhantek/res/application.qrc index 89b85ce..2f53eff 100644 --- a/openhantek/res/application.qrc +++ b/openhantek/res/application.qrc @@ -1,6 +1,7 @@ images/openhantek.png + images/switch.png images/actions/open.png diff --git a/openhantek/res/images/switch.png b/openhantek/res/images/switch.png new file mode 100644 index 0000000..11e8a76 --- /dev/null +++ b/openhantek/res/images/switch.png diff --git a/openhantek/src/main.cpp b/openhantek/src/main.cpp index b91d047..68699fc 100644 --- a/openhantek/src/main.cpp +++ b/openhantek/src/main.cpp @@ -1,36 +1,25 @@ // SPDX-License-Identifier: GPL-2.0+ -#include #include -#include +#include #include -#include #include -#include -#include -#include #include -#include + #include #include - #include #include "analyse/dataanalyzer.h" #include "hantekdsocontrol.h" #include "mainwindow.h" #include "settings.h" -#include "usb/finddevices.h" -#include "usb/uploadFirmware.h" #include "usb/usbdevice.h" #include "dsomodel.h" +#include "selectdevice/selectdevice.h" using namespace Hantek; -void showMessage(const QString &message) { - QMessageBox::information(nullptr, QCoreApplication::translate("", "No connection established!"), message); -} - /// \brief Initialize resources and translations and show the main window. int main(int argc, char *argv[]) { //////// Set application information //////// @@ -53,97 +42,17 @@ int main(int argc, char *argv[]) { } //////// Find matching usb devices //////// - libusb_context *context; + libusb_context *context = nullptr; int error = libusb_init(&context); - if (error) { - showMessage(QCoreApplication::translate("", "Can't initalize USB: %1").arg(libUsbErrorString(error))); + SelectDevice().showLibUSBFailedDialogModel(error); return -1; } + std::unique_ptr device = SelectDevice().showSelectDeviceModal(context); - FindDevices findDevices; - std::list> devices = findDevices.findDevices(); - - if (devices.empty()) { - showMessage(QCoreApplication::translate("", "No Hantek oscilloscope found. Please check if your " - "device is supported by this software, is connected, " - "in the right mode (oscilloscope mode) and if the " - "driver is correctly installed. Refer to the website for help: %1") - .arg(findDevices.getErrorMessage())); - return -1; - } - - //////// Upload firmwares for all connected devices //////// - for (const auto &i : devices) { - QString modelName = QString::fromStdString(i->getModel()->name); - if (i->needsFirmware()) { - UploadFirmware uf; - uf.startUpload(i.get()); - } - } - devices.clear(); - -#define TR(str) QCoreApplication::translate("Firmware upload dialog", str) - - //////// Select device - Autoselect if only one device is ready //////// - std::unique_ptr dialog = std::unique_ptr(new QDialog); - QListWidget *w = new QListWidget(dialog.get()); - - devices = findDevices.findDevices(); - for (auto &i : devices) { - QString modelName = QString::fromStdString(i->getModel()->name); - - if (i->needsFirmware()) { - if (UploadFirmware().startUpload(&*i)) { - w->addItem(TR("%1: Upload failed").arg(modelName)); - } else { - w->addItem(TR("%1: Upload failed").arg(modelName)); - - } - continue; - } - QString errorMessage; - if (i->connectDevice(errorMessage)) { - w->addItem(TR("%1: Ready").arg(modelName)); - w->setCurrentRow(w->count() - 1); - } else { - w->addItem(TR("%1: %2").arg(modelName).arg(findDevices.getErrorMessage())); - } - } - - if (w->currentRow() == -1 || devices.size() > 1) { - QPushButton *btn = new QPushButton(QCoreApplication::translate("", "Connect to first device"), dialog.get()); - dialog->move(QApplication::desktop()->screen()->rect().center() - w->rect().center()); - dialog->setWindowTitle(QCoreApplication::translate("", "Firmware upload")); - dialog->setLayout(new QVBoxLayout()); - dialog->layout()->addWidget(w); - dialog->layout()->addWidget(btn); - btn->connect(btn, &QPushButton::clicked, QCoreApplication::instance(), &QCoreApplication::quit); - dialog->show(); - openHantekApplication.exec(); - dialog->close(); - } - int selectedDevice = w->currentRow(); - dialog.reset(nullptr); - - std::unique_ptr device; - int indexCounter = 0; - for (auto &i : devices) { - if (indexCounter == selectedDevice) { - device = std::move(i); - break; - } - } - devices.clear(); - - if (device == nullptr || device->needsFirmware() || !device->isConnected()) { - showMessage(QCoreApplication::translate("", "A device was found, but the " - "firmware upload seem to have " - "failed or the connection " - "could not be established: %1") - .arg(findDevices.getErrorMessage())); + QString errorMessage; + if (device == nullptr || !device->connectDevice(errorMessage)) { + libusb_exit(context); return -1; } diff --git a/openhantek/src/selectdevice/devicelistentry.h b/openhantek/src/selectdevice/devicelistentry.h new file mode 100644 index 0000000..262eeef --- /dev/null +++ b/openhantek/src/selectdevice/devicelistentry.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "usb/usbdevice.h" + +/** + * Represents an entry in the {@link DevicesListModel}. + */ +struct DeviceListEntry { + UniqueUSBid id; + QString name; + bool canConnect; + bool needFirmware; + QString errorMessage; + QString getStatus() const { + return errorMessage.size()? errorMessage : (canConnect?"Ready":(needFirmware?"Firmware upload":"Cannot connect")); + } +}; diff --git a/openhantek/src/selectdevice/deviceslistmodel.cpp b/openhantek/src/selectdevice/deviceslistmodel.cpp new file mode 100644 index 0000000..e93ba01 --- /dev/null +++ b/openhantek/src/selectdevice/deviceslistmodel.cpp @@ -0,0 +1,82 @@ +#include "deviceslistmodel.h" +#include "usb/finddevices.h" +#include "usb/uploadFirmware.h" +#include "dsomodel.h" +#include + +DevicesListModel::DevicesListModel(FindDevices *findDevices) :findDevices(findDevices) {} + +int DevicesListModel::rowCount(const QModelIndex &) const +{ + return entries.size(); +} + +int DevicesListModel::columnCount(const QModelIndex &parent) const +{ + return 2; +} + +QVariant DevicesListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical) + return QAbstractTableModel::headerData(section, orientation, role); + if (role == Qt::DisplayRole) { + switch(section) { + case 0: return tr("Devicename"); + case 1: return tr("Status"); + default: return QVariant(); + } + } + return QAbstractTableModel::headerData(section, orientation, role); +} + +QVariant DevicesListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) return QVariant(); + if (role==Qt::UserRole) return QVariant::fromValue(entries[index.row()].id); + else if (role==Qt::UserRole+1) return QVariant::fromValue(entries[index.row()].canConnect); + else if (role==Qt::UserRole+2) return QVariant::fromValue(entries[index.row()].needFirmware); + + if (role == Qt::DisplayRole) { + if (index.column() == 0) { + return entries[index.row()].name; + } else if (index.column() == 1) { + return entries[index.row()].getStatus(); + } + } + + if (role == Qt::BackgroundRole) { + if (entries[index.row()].canConnect) return QColor(Qt::darkGreen).lighter(); + else if (entries[index.row()].needFirmware) return QColor(Qt::yellow).lighter(); + } + + return QVariant(); +} + +void DevicesListModel::updateDeviceList() +{ + beginResetModel(); + entries.clear(); + endResetModel(); + const FindDevices::DeviceList* devices = findDevices->getDevices(); + beginInsertRows(QModelIndex(),0,devices->size()); + for (auto &i : *devices) { + DeviceListEntry entry; + entry.name= QString::fromStdString(i.second->getModel()->name); + entry.id = i.first; + if (i.second->needsFirmware()) { + UploadFirmware uf; + if (!uf.startUpload(i.second.get())) { + entry.errorMessage = uf.getErrorMessage(); + } + entry.needFirmware = true; + } else if (i.second->connectDevice(entry.errorMessage)) { + entry.canConnect = true; + i.second->disconnectFromDevice(); + } else { + entry.canConnect = false; + } + entries.push_back(entry); + } + endInsertRows(); +} diff --git a/openhantek/src/selectdevice/deviceslistmodel.h b/openhantek/src/selectdevice/deviceslistmodel.h new file mode 100644 index 0000000..73fbcfe --- /dev/null +++ b/openhantek/src/selectdevice/deviceslistmodel.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "devicelistentry.h" + +class FindDevices; + +/** + * Provides a Model for the Qt Model/View concept. The {@see FindDevices} is required + * to update the list of available devices. + */ +class DevicesListModel: public QAbstractTableModel { +public: + DevicesListModel(FindDevices* findDevices); + // QAbstractItemModel interface + virtual int rowCount(const QModelIndex &parent) const override; + virtual int columnCount(const QModelIndex &parent) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + virtual QVariant data(const QModelIndex &index, int role) const override; + void updateDeviceList(); +private: + std::vector entries; + FindDevices* findDevices; +}; diff --git a/openhantek/src/selectdevice/selectdevice.cpp b/openhantek/src/selectdevice/selectdevice.cpp new file mode 100644 index 0000000..7d0288f --- /dev/null +++ b/openhantek/src/selectdevice/selectdevice.cpp @@ -0,0 +1,125 @@ +#include + +#include "utils/printutils.h" +#include "selectdevice.h" +#include "usb/uploadFirmware.h" +#include "usb/finddevices.h" +#include "dsomodel.h" +#include +#include +#include +#include +#include + +#include "devicelistentry.h" +#include "deviceslistmodel.h" + +SelectDevice::SelectDevice() { + btn = new QPushButton(this); + w = new QTableView(this); + label = new QLabel(); + label->setWordWrap(true); + move(QApplication::desktop()->screen()->rect().center() - w->rect().center()); + setWindowTitle(tr("Select device")); + setLayout(new QVBoxLayout()); + layout()->addWidget(w); + layout()->addWidget(label); + layout()->addWidget(btn); + qRegisterMetaType(); + connect(btn, &QPushButton::clicked, [this]() { + if (w->currentIndex().isValid()) { + selectedDevice = w->currentIndex().data(Qt::UserRole).value(); + } + QCoreApplication::instance()->quit(); + }); +} + +std::unique_ptr SelectDevice::showSelectDeviceModal(libusb_context *&context) +{ + + std::unique_ptr findDevices = std::unique_ptr(new FindDevices(context)); + std::unique_ptr model = std::unique_ptr(new DevicesListModel(findDevices.get())); + w->setModel(model.get()); + w->verticalHeader()->hide(); + w->horizontalHeader()->hide(); + w->setSelectionBehavior(QAbstractItemView::SelectRows); + // w->setColumnWidth(1,60); + w->horizontalHeader()->setStretchLastSection(true); + connect(w->selectionModel(), &QItemSelectionModel::currentChanged, this, &SelectDevice::currentChanged); + + QTimer timer; + timer.setInterval(1000); + connect(&timer, &QTimer::timeout, [this, &model, &findDevices]() { + if (findDevices->updateDeviceList()) + model->updateDeviceList(); + if (model->rowCount(QModelIndex())) { + w->setVisible(true); + label->setVisible(false); + currentChanged(w->currentIndex(),QModelIndex()); + } else { + QString generalMessage = tr("

OpenHantek did not find any compatible devices.

" + "

" + "Don't forget to switch your device into oscilloscope mode if it has multiple modes.

" + ); +#if defined(Q_OS_WIN) + generalMessage = generalMessage.arg("Please make sure you have installed the windows usb driver correctly"); +#elif defined(Q_OS_LINUX) + QFile file("/lib/udev/rules.d/60-hantek.rules"); + if (!file.exists()) { + generalMessage += tr("

Please make sure you have copied the udev rules file to %1 for correct USB access permissions.

").arg(file.fileName()); + } +#else +#endif + generalMessage += tr("

Visit the build and run instruction " + "website for help.

"); + makeErrorDialog(generalMessage); + } + if (!w->currentIndex().isValid()) { + currentChanged(QModelIndex(),QModelIndex()); + } + }); + timer.start(); + QCoreApplication::sendEvent(&timer, new QTimerEvent(timer.timerId())); // immediate timer event + + show(); + QCoreApplication::instance()->exec(); + timer.stop(); + close(); + + return findDevices->takeDevice(selectedDevice); +} + +void SelectDevice::showLibUSBFailedDialogModel(int error) +{ + makeErrorDialog(tr("Can't initalize USB: %1").arg(libUsbErrorString(error))); + show(); + QCoreApplication::instance()->exec(); + close(); +} + +void SelectDevice::makeErrorDialog(const QString &message) +{ + w->setVisible(false); + label->setText(message); + label->setVisible(true); +} + +void SelectDevice::currentChanged(const QModelIndex ¤t, const QModelIndex &) +{ + if (!current.isValid()) { + btn->setText(tr("Quit application")); + btn->setEnabled(true); + return; + } + if (current.data(Qt::UserRole+1).toBool()) { + btn->setText(tr("Use device %1").arg(current.sibling(current.row(),0).data(Qt::DisplayRole).toString())); + btn->setEnabled(true); + } else { + btn->setEnabled(false); + if (current.data(Qt::UserRole+2).toBool()) { + btn->setText(tr("Upload in progress...")); + } else { + btn->setText(tr("Connection failed!")); + } + } +} diff --git a/openhantek/src/selectdevice/selectdevice.h b/openhantek/src/selectdevice/selectdevice.h new file mode 100644 index 0000000..84acb50 --- /dev/null +++ b/openhantek/src/selectdevice/selectdevice.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "usb/usbdevice.h" + +struct libusb_context; + +/** + * Offers the user a selection dialog. If you call any of the -Modal methods, + * the method will block and show a dialog for selection or for a usb error + * message. The method returns as soon as the user closes the dialog. + * + * An example to get a device: + * std::unique_ptr device = SelectDevice().showSelectDeviceModal(context); + */ +class SelectDevice: public QDialog { +public: + SelectDevice(); + std::unique_ptr showSelectDeviceModal(libusb_context *&context); + void showLibUSBFailedDialogModel(int error); +private: + void makeErrorDialog(const QString& message); + void updateDeviceList(); + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + QTableView *w; + QLabel *label; + QPushButton *btn; + UniqueUSBid selectedDevice = 0; +}; diff --git a/openhantek/src/usb/finddevices.cpp b/openhantek/src/usb/finddevices.cpp index 4fa9318..1dec832 100644 --- a/openhantek/src/usb/finddevices.cpp +++ b/openhantek/src/usb/finddevices.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "ezusb.h" #include "utils/printutils.h" #include @@ -16,50 +17,64 @@ FindDevices::FindDevices(libusb_context *context) : context(context) {} // Iterate through all usb devices -std::list> FindDevices::findDevices() { - std::list> devices; - +int FindDevices::updateDeviceList() { libusb_device **deviceList; ssize_t deviceCount = libusb_get_device_list(context, &deviceList); if (deviceCount < 0) { - errorMessage = QCoreApplication::translate("", "Failed to get device list"); - return devices; + return (int) deviceCount; } - noAccessDevices = false; - int noAccessDeviceCount = 0; + ++findIteration; + int changes = 0; for (ssize_t deviceIterator = 0; deviceIterator < deviceCount; ++deviceIterator) { libusb_device *device = deviceList[deviceIterator]; // Get device descriptor struct libusb_device_descriptor descriptor; - if (libusb_get_device_descriptor(device, &descriptor) < 0) continue; + libusb_get_device_descriptor(device, &descriptor); + + DeviceList::const_iterator inList = devices.find(USBDevice::computeUSBdeviceID(device)); + + if (inList != devices.end()) { + inList->second->setFindIteration(findIteration); + continue; + } for (DSOModel* model : supportedModels) { // Check VID and PID for firmware flashed devices - if (descriptor.idVendor == model->vendorID && descriptor.idProduct == model->productID) { - devices.push_back(std::unique_ptr(new USBDevice(model, device))); - break; - } + bool supported = descriptor.idVendor == model->vendorID && descriptor.idProduct == model->productID; // Devices without firmware have different VID/PIDs - if (descriptor.idVendor == model->vendorIDnoFirmware && descriptor.idProduct == model->productIDnoFirmware) { - devices.push_back(std::unique_ptr(new USBDevice(model, device))); - break; + supported |= descriptor.idVendor == model->vendorIDnoFirmware && descriptor.idProduct == model->productIDnoFirmware; + if (supported) { + ++changes; + devices[USBDevice::computeUSBdeviceID(device)] = std::unique_ptr(new USBDevice(model, device, findIteration)); } } } - if (noAccessDeviceCount == deviceCount) { - noAccessDevices = true; - errorMessage = - QCoreApplication::translate("", "Please make sure to have read/write access to your usb device. On " - "linux you need to install the correct udev file for example."); + // Remove non existing devices + for (DeviceList::iterator it=devices.begin();it!=devices.end();) { + if (it->second->getFindIteration() != findIteration) { + ++changes; + it = devices.erase(it); + } else { + ++it; + } } libusb_free_device_list(deviceList, true); - return devices; + + return changes; } -const QString &FindDevices::getErrorMessage() const { return errorMessage; } +const FindDevices::DeviceList* FindDevices::getDevices() +{ + return &devices; +} -bool FindDevices::allDevicesNoAccessError() const { return noAccessDevices; } +std::unique_ptr FindDevices::takeDevice(UniqueUSBid id) +{ + DeviceList::iterator i = devices.find(id); + if (i==devices.end()) return nullptr; + return std::move(i->second); +} diff --git a/openhantek/src/usb/finddevices.h b/openhantek/src/usb/finddevices.h index eed7a6a..75896df 100644 --- a/openhantek/src/usb/finddevices.h +++ b/openhantek/src/usb/finddevices.h @@ -4,6 +4,8 @@ #include #include +#include +#include #include "definitions.h" #include "usbdevice.h" @@ -11,19 +13,30 @@ struct libusb_context; /** - * @brief Search for Hantek devices and connect to the selected one. + * @brief Search for Hantek devices. + * Use usually want to call `updateDeviceList` and then retrieve the list via `getDevices`. + * You can call `updateDeviceList` as often as you want. + * If you have found your favorite device, you want to call `takeDevice`. The device will + * not be available in `getDevices` anymore and this will not change with calls to `updateDeviceList`. * - * At the moment this class connects to the first found devic automatically. + * Do not close the given usb context before this class object is destroyed. */ -class FindDevices { +class FindDevices{ public: - FindDevices(libusb_context *context = nullptr); - std::list> findDevices(); - const QString &getErrorMessage() const; - bool allDevicesNoAccessError() const; - + typedef std::map> DeviceList; + FindDevices(libusb_context *context); + /// Updates the device list. To clear the list, just dispose this object + /// \return If negative it represents a libusb error code otherwise the amount of updates + int updateDeviceList(); + const DeviceList *getDevices(); + /** + * @brief takeDevice + * @param id The unique usb id for the current bus layout + * @return A shared reference to the + */ + std::unique_ptr takeDevice(UniqueUSBid id); private: libusb_context *context; ///< The usb context used for this device - QString errorMessage; - bool noAccessDevices = false; + DeviceList devices; + unsigned findIteration = 0; }; diff --git a/openhantek/src/usb/uploadFirmware.cpp b/openhantek/src/usb/uploadFirmware.cpp index 6571d33..564f0d7 100644 --- a/openhantek/src/usb/uploadFirmware.cpp +++ b/openhantek/src/usb/uploadFirmware.cpp @@ -14,6 +14,8 @@ #include "dsomodel.h" +#define TR(str) QCoreApplication::translate("UploadFirmware", str) + bool UploadFirmware::startUpload(USBDevice *device) { if (device->isConnected() || !device->needsFirmware()) return false; @@ -22,7 +24,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { int errorCode = libusb_open(device->getRawDevice(), &handle); if (errorCode != LIBUSB_SUCCESS) { handle = nullptr; - errorMessage = QCoreApplication::translate("", "Couldn't open device: %1").arg(libUsbErrorString(errorCode)); + errorMessage = TR("Couldn't open device: %1").arg(libUsbErrorString(errorCode)); return false; } @@ -41,7 +43,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { libusb_set_auto_detach_kernel_driver(handle, 1); int status = libusb_claim_interface(handle, 0); if (status != LIBUSB_SUCCESS) { - errorMessage = QString("libusb_claim_interface() failed: %1").arg(libusb_error_name(status)); + errorMessage = TR("libusb_claim_interface() failed: %1").arg(libusb_error_name(status)); libusb_close(handle); return false; } @@ -50,7 +52,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { status = ezusb_load_ram(handle, temp_loader_path->fileName().toUtf8().constData(), FX_TYPE_FX2, IMG_TYPE_HEX, 0); if (status != LIBUSB_SUCCESS) { - errorMessage = QString("ezusb_load_ram(loader_path) failed: %1").arg(libusb_error_name(status)); + errorMessage = TR("Writing the loader firmware failed: %1").arg(libusb_error_name(status)); libusb_release_interface(handle, 0); libusb_close(handle); return false; @@ -60,7 +62,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { status = ezusb_load_ram(handle, temp_firmware_path->fileName().toUtf8().constData(), FX_TYPE_FX2, IMG_TYPE_HEX, 1); if (status != LIBUSB_SUCCESS) { - errorMessage = QString("ezusb_load_ram(firmware_path) failed: %1").arg(libusb_error_name(status)); + errorMessage = TR("Writing the main firmware failed: %1").arg(libusb_error_name(status)); libusb_release_interface(handle, 0); libusb_close(handle); return false; @@ -72,3 +74,4 @@ bool UploadFirmware::startUpload(USBDevice *device) { } const QString &UploadFirmware::getErrorMessage() const { return errorMessage; } + diff --git a/openhantek/src/usb/uploadFirmware.h b/openhantek/src/usb/uploadFirmware.h index d2d1bb8..1986f85 100644 --- a/openhantek/src/usb/uploadFirmware.h +++ b/openhantek/src/usb/uploadFirmware.h @@ -14,7 +14,6 @@ class UploadFirmware { public: bool startUpload(USBDevice *device); const QString &getErrorMessage() const; - private: QString errorMessage; }; diff --git a/openhantek/src/usb/usbdevice.cpp b/openhantek/src/usb/usbdevice.cpp index 86e0bdf..74c18c5 100644 --- a/openhantek/src/usb/usbdevice.cpp +++ b/openhantek/src/usb/usbdevice.cpp @@ -11,8 +11,14 @@ #include "models.h" #include "utils/printutils.h" +UniqueUSBid USBDevice::computeUSBdeviceID(libusb_device *device) { + UniqueUSBid v=0; + libusb_get_port_numbers(device, (uint8_t*)&v, sizeof(v)); + return v; +} -USBDevice::USBDevice(DSOModel *model, libusb_device *device) : model(model), device(device) { +USBDevice::USBDevice(DSOModel *model, libusb_device *device, unsigned findIteration) : + model(model), device(device), findIteration(findIteration), uniqueUSBdeviceID(computeUSBdeviceID(device)) { libusb_ref_device(device); libusb_get_device_descriptor(device, &descriptor); } @@ -59,7 +65,10 @@ bool USBDevice::connectDevice(QString &errorMessage) { return true; } -USBDevice::~USBDevice() { connectionLost(); } +USBDevice::~USBDevice() { + disconnectFromDevice(); + device = nullptr; +} int USBDevice::claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn) { int errorCode = libusb_claim_interface(this->handle, interfaceDescriptor->bInterfaceNumber); @@ -82,7 +91,8 @@ int USBDevice::claimInterface(const libusb_interface_descriptor *interfaceDescri return LIBUSB_SUCCESS; } -void USBDevice::connectionLost() { +void USBDevice::disconnectFromDevice() { + if (!device) return; libusb_unref_device(device); if (!this->handle) return; @@ -93,7 +103,7 @@ void USBDevice::connectionLost() { // Close device handle libusb_close(this->handle); - this->handle = 0; + this->handle = nullptr; emit deviceDisconnected(); } @@ -104,6 +114,16 @@ bool USBDevice::needsFirmware() { return this->descriptor.idProduct != model->productID || this->descriptor.idVendor != model->vendorID; } +void USBDevice::setFindIteration(unsigned iteration) +{ + findIteration = iteration; +} + +unsigned USBDevice::getFindIteration() const +{ + return findIteration; +} + int USBDevice::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigned int length, int attempts, unsigned int timeout) { if (!this->handle) return LIBUSB_ERROR_NO_DEVICE; @@ -113,7 +133,7 @@ int USBDevice::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigne for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) errorCode = libusb_bulk_transfer(this->handle, endpoint, data, length, &transferred, timeout); - if (errorCode == LIBUSB_ERROR_NO_DEVICE) connectionLost(); + if (errorCode == LIBUSB_ERROR_NO_DEVICE) disconnectFromDevice(); if (errorCode < 0) return errorCode; else @@ -211,7 +231,7 @@ int USBDevice::controlTransfer(unsigned char type, unsigned char request, unsign for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) errorCode = libusb_control_transfer(this->handle, type, request, value, index, data, length, HANTEK_TIMEOUT); - if (errorCode == LIBUSB_ERROR_NO_DEVICE) connectionLost(); + if (errorCode == LIBUSB_ERROR_NO_DEVICE) disconnectFromDevice(); return errorCode; } @@ -276,6 +296,11 @@ int USBDevice::getPacketSize() { libusb_device *USBDevice::getRawDevice() const { return device; } +unsigned long USBDevice::getUniqueUSBDeviceID() const +{ + return uniqueUSBdeviceID; +} + const DSOModel* USBDevice::getModel() const { return model; } void USBDevice::setEnableBulkTransfer(bool enable) { allowBulkTransfer = enable; } diff --git a/openhantek/src/usb/usbdevice.h b/openhantek/src/usb/usbdevice.h index a9517a6..f8b742d 100644 --- a/openhantek/src/usb/usbdevice.h +++ b/openhantek/src/usb/usbdevice.h @@ -14,21 +14,31 @@ class DSOModel; +typedef unsigned long UniqueUSBid; + /// \brief This class handles the USB communication with an usb device that has /// one in and one out endpoint. class USBDevice : public QObject { Q_OBJECT public: - USBDevice(DSOModel* model, libusb_device *device); + USBDevice(DSOModel* model, libusb_device *device, unsigned findIteration = 0); ~USBDevice(); bool connectDevice(QString &errorMessage); + void disconnectFromDevice(); /// \brief Check if the oscilloscope is connected. /// \return true, if a connection is up. bool isConnected(); bool needsFirmware(); + /** + * Keep track of the find iteration on which this device was found + * @param iteration The new iteration value + */ + void setFindIteration(unsigned iteration); + unsigned getFindIteration() const; + // Various methods to handle USB transfers /// \brief Bulk transfer to/from the oscilloscope. @@ -57,16 +67,31 @@ class USBDevice : public QObject { int getConnectionSpeed(); int getPacketSize(); + /** + * @return Returns the raw libusb device + */ libusb_device *getRawDevice() const; + + /** + * @return Return the unique usb device id {@link USBDevice::computeUSBdeviceID()}. + */ + unsigned long getUniqueUSBDeviceID() const; + /** + * The USB bus is organised in a tree hierarchy. A device is connected to a port on a bus device, + * which is connected to a port on another bus device etc up to the root usb device. + * + * The USB 3.0 standard allows up to 7 levels with 256 devices on each level (1 Byte). We generate + * a unique number for the connected device. + */ + static UniqueUSBid computeUSBdeviceID(libusb_device *device); + /// \brief Get the oscilloscope model. /// \return The ::Model of the connected Hantek DSO. const DSOModel *getModel() const; void setEnableBulkTransfer(bool enable); void overwriteInPacketLength(int len); - protected: int claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn); - void connectionLost(); // Command buffers ControlBeginCommand beginCommandControl; @@ -76,6 +101,8 @@ class USBDevice : public QObject { struct libusb_device_descriptor descriptor; libusb_device *device; ///< The USB handle for the oscilloscope libusb_device_handle *handle = nullptr; + unsigned findIteration; + const unsigned long uniqueUSBdeviceID; int interface; int outPacketLength; ///< Packet length for the OUT endpoint int inPacketLength; ///< Packet length for the IN endpoint