Initial commit
This commit is contained in:
135
src/DebugToolDrivers/USB/HID/HidInterface.cpp
Normal file
135
src/DebugToolDrivers/USB/HID/HidInterface.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <src/Logger/Logger.hpp>
|
||||
|
||||
#include "hidapi.hpp"
|
||||
#include "HidInterface.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/Exceptions/DeviceCommunicationFailure.hpp"
|
||||
|
||||
using namespace Bloom::Usb;
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
std::string HidInterface::getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber) {
|
||||
hid_device_info* HIDDeviceInfoList = hid_enumerate(this->getVendorId(), this->getProductId());
|
||||
|
||||
while (HIDDeviceInfoList != nullptr) {
|
||||
if (HIDDeviceInfoList->interface_number == interfaceNumber) {
|
||||
break;
|
||||
}
|
||||
|
||||
HIDDeviceInfoList = HIDDeviceInfoList->next;
|
||||
}
|
||||
|
||||
if (HIDDeviceInfoList == nullptr) {
|
||||
throw std::runtime_error("Failed to match interface number with HID interface.");
|
||||
}
|
||||
|
||||
auto path = std::string(HIDDeviceInfoList->path);
|
||||
hid_free_enumeration(HIDDeviceInfoList);
|
||||
return path;
|
||||
}
|
||||
|
||||
void HidInterface::init() {
|
||||
/*
|
||||
* Because we use the HIDAPI with libusb for our HID interfaces, we must allow the HIDAPI to own the device
|
||||
* resources (device handle and the interface). However, the HIDAPI does not provide any means to ensure that a
|
||||
* specific configuration is set against the device. This is why we first open the device via libusb (by calling
|
||||
* the generic init() method), so that we can set the correct configuration. We then close the device to allow
|
||||
* the HIDAPI to take ownership.
|
||||
*/
|
||||
Interface::init();
|
||||
Interface::detachKernelDriver();
|
||||
Interface::setConfiguration(0);
|
||||
Interface::close();
|
||||
|
||||
hid_init();
|
||||
hid_device* hidDevice;
|
||||
|
||||
std::string HIDInterfacePath = this->getDevicePathByInterfaceNumber(this->getNumber());
|
||||
Logger::debug("HID device path: " + HIDInterfacePath);
|
||||
|
||||
if ((hidDevice = hid_open_path(HIDInterfacePath.c_str())) == nullptr) {
|
||||
throw Exception("Failed to open HID device via hidapi.");
|
||||
}
|
||||
|
||||
if (hidDevice->input_ep_max_packet_size < 1) {
|
||||
throw Exception("Invalid max packet size for USB endpoint, on interface " + std::to_string(this->getNumber()));
|
||||
}
|
||||
|
||||
this->setHidDevice(hidDevice);
|
||||
this->setInputReportSize(static_cast<std::size_t>(hidDevice->input_ep_max_packet_size));
|
||||
this->setLibUsbDeviceHandle(hidDevice->device_handle);
|
||||
}
|
||||
|
||||
void HidInterface::close() {
|
||||
auto hidDevice = this->getHidDevice();
|
||||
|
||||
if (hidDevice != nullptr) {
|
||||
this->setLibUsbDeviceHandle(nullptr);
|
||||
hid_close(hidDevice);
|
||||
// hid_close() releases the interface
|
||||
Interface::setClaimed(false);
|
||||
}
|
||||
|
||||
hid_exit();
|
||||
Interface::close();
|
||||
}
|
||||
|
||||
std::size_t HidInterface::read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout) {
|
||||
int transferred;
|
||||
|
||||
if ((transferred = hid_read_timeout(this->getHidDevice(), buffer, maxLength,
|
||||
timeout == 0 ? -1 : static_cast<int>(timeout))) == -1
|
||||
) {
|
||||
throw DeviceCommunicationFailure("Failed to read from HID device.");
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>(transferred);
|
||||
}
|
||||
|
||||
void HidInterface::write(unsigned char* buffer, std::size_t length) {
|
||||
int transferred;
|
||||
|
||||
if ((transferred = hid_write(this->getHidDevice(), buffer, length)) != length) {
|
||||
Logger::debug("Attempted to write " + std::to_string(length)
|
||||
+ " bytes to HID interface. Bytes written: " + std::to_string(transferred));
|
||||
throw DeviceCommunicationFailure("Failed to write data to HID interface.");
|
||||
}
|
||||
}
|
||||
|
||||
void HidInterface::write(std::vector<unsigned char> buffer) {
|
||||
if (buffer.size() > this->getInputReportSize()) {
|
||||
throw Exception("Cannot send data via HID interface - data exceeds maximum packet size.");
|
||||
|
||||
} else if (buffer.size() < this->getInputReportSize()) {
|
||||
/*
|
||||
* Every report we send via the USB HID interface should be of a fixed size.
|
||||
* In the event of a report being too small, we just fill the buffer vector with 0.
|
||||
*/
|
||||
buffer.resize(this->getInputReportSize(), 0);
|
||||
}
|
||||
|
||||
this->write(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
std::vector<unsigned char> HidInterface::read(unsigned int timeout) {
|
||||
std::vector<unsigned char> output;
|
||||
auto readSize = this->getInputReportSize();
|
||||
|
||||
// Attempt to read the first HID report packet, and whatever is left after that.
|
||||
output.resize(readSize);
|
||||
auto transferredByteCount = this->read(output.data(), readSize, timeout);
|
||||
auto totalByteCount = transferredByteCount;
|
||||
|
||||
while (transferredByteCount >= readSize) {
|
||||
output.resize(totalByteCount + readSize);
|
||||
|
||||
transferredByteCount = this->read(output.data() + totalByteCount, readSize, 1);
|
||||
totalByteCount += transferredByteCount;
|
||||
}
|
||||
|
||||
output.resize(totalByteCount);
|
||||
return output;
|
||||
}
|
||||
117
src/DebugToolDrivers/USB/HID/HidInterface.hpp
Normal file
117
src/DebugToolDrivers/USB/HID/HidInterface.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "hidapi.hpp"
|
||||
#include "src/DebugToolDrivers/USB/Interface.hpp"
|
||||
|
||||
namespace Bloom::Usb
|
||||
{
|
||||
/**
|
||||
* The HIDInterface uses the HIDAPI library to implement communication with HID endpoints.
|
||||
*
|
||||
* Currently, this interface only supports single-report HID implementations. HID interfaces with
|
||||
* multiple reports will be supported as-and-when we need it.
|
||||
*/
|
||||
class HidInterface: public Interface
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* The HIDAPI library provides a hid_device data structure to represent a USB HID interface.
|
||||
*
|
||||
* @see hidapi.hpp or the HIDAPI documentation for more on this.
|
||||
*/
|
||||
hid_device* hidDevice = nullptr;
|
||||
|
||||
/**
|
||||
* All HID reports have a fixed report length. This means that every packet
|
||||
* we send or receive to/from an HID endpoint must be equal to the report length in size.
|
||||
*
|
||||
* The default input report size is 64 bytes, but this is overridden at interface initialisation.
|
||||
* @see definition of init() for more on this.
|
||||
*/
|
||||
std::size_t inputReportSize = 64;
|
||||
|
||||
void setHidDevice(hid_device* hidDevice) {
|
||||
this->hidDevice = hidDevice;
|
||||
}
|
||||
|
||||
void setInputReportSize(const std::size_t& inputReportSize) {
|
||||
this->inputReportSize = inputReportSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a maximum of `maxLength` bytes into `buffer`, from the HID input endpoint.
|
||||
*
|
||||
* Keeping this in the private scope to enforce use of vector<unsigned char>. See read()
|
||||
* method in public scope.
|
||||
*
|
||||
* @param buffer
|
||||
* @param maxLength
|
||||
* @param timeout
|
||||
*
|
||||
* @return
|
||||
* Number of bytes read.
|
||||
*/
|
||||
std::size_t read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout);
|
||||
|
||||
/**
|
||||
* Writes `length` bytes from `buffer` to HID output endpoint.
|
||||
*
|
||||
* Keeping this in the private scope to enforce use of vector<unsigned char>.
|
||||
* @see write(std::vector<unsigned char> buffer);
|
||||
*
|
||||
* @param buffer
|
||||
* @param length
|
||||
*/
|
||||
void write(unsigned char* buffer, std::size_t length);
|
||||
|
||||
protected:
|
||||
hid_device* getHidDevice() const {
|
||||
return this->hidDevice;
|
||||
}
|
||||
|
||||
public:
|
||||
std::size_t getInputReportSize() {
|
||||
return 512;
|
||||
// return this->inputReportSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Claims the USB HID interface, obtains a hid_device instance and configures
|
||||
*/
|
||||
void init() override;
|
||||
|
||||
void close() override;
|
||||
|
||||
/**
|
||||
* Wrapper for write(unsigned char *buffer, int length)
|
||||
*
|
||||
* @param buffer
|
||||
*/
|
||||
void write(std::vector<unsigned char> buffer);
|
||||
|
||||
/**
|
||||
* Reads as much data as the device has to offer, into a vector.
|
||||
*
|
||||
* If `timeout` is set to 0, this method will block until at least one HID report
|
||||
* packet is received.
|
||||
*
|
||||
* @param timeout
|
||||
*
|
||||
* @return
|
||||
* A vector of the data received from the device.
|
||||
*/
|
||||
std::vector<unsigned char> read(unsigned int timeout = 0);
|
||||
|
||||
/**
|
||||
* Resolves a device path from a USB interface number.
|
||||
*
|
||||
* @param interfaceNumber
|
||||
* @return
|
||||
*/
|
||||
std::string getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber);
|
||||
};
|
||||
}
|
||||
37
src/DebugToolDrivers/USB/HID/hidapi.hpp
Normal file
37
src/DebugToolDrivers/USB/HID/hidapi.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#include <hidapi/hidapi.h>
|
||||
|
||||
struct hid_device_ {
|
||||
/* Handle to the actual device. */
|
||||
libusb_device_handle* device_handle;
|
||||
|
||||
/* Endpoint information */
|
||||
int input_endpoint;
|
||||
int output_endpoint;
|
||||
int input_ep_max_packet_size;
|
||||
|
||||
/* The interface number of the HID */
|
||||
int interface;
|
||||
|
||||
/* Indexes of Strings */
|
||||
int manufacturer_index;
|
||||
int product_index;
|
||||
int serial_index;
|
||||
|
||||
/* Whether blocking reads are used */
|
||||
int blocking; /* boolean */
|
||||
|
||||
/* Read thread objects */
|
||||
pthread_t thread;
|
||||
pthread_mutex_t mutex; /* Protects input_reports */
|
||||
pthread_cond_t condition;
|
||||
pthread_barrier_t barrier; /* Ensures correct startup sequence */
|
||||
int shutdown_thread;
|
||||
int cancelled;
|
||||
struct libusb_transfer* transfer;
|
||||
|
||||
/* List of received input reports. */
|
||||
struct input_report* input_reports;
|
||||
};
|
||||
150
src/DebugToolDrivers/USB/Interface.cpp
Normal file
150
src/DebugToolDrivers/USB/Interface.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "Interface.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
using namespace Bloom::Usb;
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
|
||||
void Interface::init() {
|
||||
libusb_device_descriptor deviceDescriptor = {};
|
||||
auto deviceHandle = this->getLibUsbDeviceHandle();
|
||||
auto libUsbDevice = this->getUSBDevice();
|
||||
int libUsbStatusCode;
|
||||
|
||||
if (libUsbDevice == nullptr) {
|
||||
throw Exception("Cannot open USB device without libusb device pointer.");
|
||||
}
|
||||
|
||||
if (deviceHandle == nullptr) {
|
||||
// Obtain a device handle from libusb
|
||||
if ((libUsbStatusCode = libusb_open(libUsbDevice, &deviceHandle)) < 0) {
|
||||
throw Exception("Failed to open USB device - error code "
|
||||
+ std::to_string(libUsbStatusCode) + " returned.");
|
||||
}
|
||||
}
|
||||
|
||||
if ((libUsbStatusCode = libusb_get_device_descriptor(libUsbDevice, &deviceDescriptor))) {
|
||||
throw Exception("Failed to obtain USB device descriptor - error code "
|
||||
+ std::to_string(libUsbStatusCode) + " returned.");
|
||||
}
|
||||
|
||||
this->setLibUsbDeviceHandle(deviceHandle);
|
||||
this->setInitialised(true);
|
||||
}
|
||||
|
||||
void Interface::setConfiguration(int configIndex) {
|
||||
libusb_config_descriptor* configDescriptor = {};
|
||||
int libUsbStatusCode;
|
||||
|
||||
if ((libUsbStatusCode = libusb_get_config_descriptor(this->getUSBDevice(), 0, &configDescriptor))) {
|
||||
throw Exception("Failed to obtain USB configuration descriptor - error code "
|
||||
+ std::to_string(libUsbStatusCode) + " returned.");
|
||||
}
|
||||
|
||||
if ((libUsbStatusCode = libusb_set_configuration(this->getLibUsbDeviceHandle(), configDescriptor->bConfigurationValue))) {
|
||||
throw Exception("Failed to set USB configuration - error code "
|
||||
+ std::to_string(libUsbStatusCode) + " returned.");
|
||||
}
|
||||
|
||||
libusb_free_config_descriptor(configDescriptor);
|
||||
}
|
||||
|
||||
void Interface::close() {
|
||||
auto deviceHandle = this->getLibUsbDeviceHandle();
|
||||
this->release();
|
||||
|
||||
if (deviceHandle != nullptr) {
|
||||
libusb_close(deviceHandle);
|
||||
this->setLibUsbDeviceHandle(nullptr);
|
||||
}
|
||||
|
||||
this->setInitialised(false);
|
||||
}
|
||||
|
||||
void Interface::claim() {
|
||||
int interfaceNumber = this->getNumber();
|
||||
int libUsbStatusCode = 0;
|
||||
|
||||
this->detachKernelDriver();
|
||||
|
||||
if (libusb_claim_interface(this->getLibUsbDeviceHandle(), interfaceNumber) != 0) {
|
||||
throw Exception("Failed to claim interface {" + std::to_string(interfaceNumber) + "} on USB device\n");
|
||||
}
|
||||
|
||||
this->setClaimed(true);
|
||||
}
|
||||
|
||||
void Interface::detachKernelDriver() {
|
||||
int interfaceNumber = this->getNumber();
|
||||
int libUsbStatusCode;
|
||||
|
||||
if ((libUsbStatusCode = libusb_kernel_driver_active(this->getLibUsbDeviceHandle(), interfaceNumber)) != 0) {
|
||||
if (libUsbStatusCode == 1) {
|
||||
// A kernel driver is active on this interface. Attempt to detach it
|
||||
if (libusb_detach_kernel_driver(this->getLibUsbDeviceHandle(), interfaceNumber) != 0) {
|
||||
throw Exception("Failed to detach kernel driver from interface " +
|
||||
std::to_string(interfaceNumber) + "\n");
|
||||
}
|
||||
} else {
|
||||
throw Exception("Failed to check for active kernel driver on USB interface.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::release() {
|
||||
if (this->isClaimed()) {
|
||||
if (libusb_release_interface(this->getLibUsbDeviceHandle(), this->getNumber()) != 0) {
|
||||
throw Exception("Failed to release interface {" + std::to_string(this->getNumber()) + "} on USB device\n");
|
||||
}
|
||||
|
||||
this->setClaimed(false);
|
||||
}
|
||||
}
|
||||
|
||||
int Interface::read(unsigned char* buffer, unsigned char endPoint, size_t length, size_t timeout) {
|
||||
int totalTransferred = 0;
|
||||
int transferred = 0;
|
||||
int libUsbStatusCode = 0;
|
||||
|
||||
while (length > totalTransferred) {
|
||||
libUsbStatusCode = libusb_interrupt_transfer(
|
||||
this->getLibUsbDeviceHandle(),
|
||||
endPoint,
|
||||
buffer,
|
||||
static_cast<int>(length),
|
||||
&transferred,
|
||||
static_cast<unsigned int>(timeout)
|
||||
);
|
||||
|
||||
if (libUsbStatusCode != 0 && libUsbStatusCode != -7) {
|
||||
throw Exception("Failed to read from USB device. Error code returned: " + std::to_string(libUsbStatusCode));
|
||||
}
|
||||
|
||||
totalTransferred += transferred;
|
||||
}
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
void Interface::write(unsigned char* buffer, unsigned char endPoint, int length) {
|
||||
int transferred = 0;
|
||||
int libUsbStatusCode = 0;
|
||||
|
||||
libUsbStatusCode = libusb_interrupt_transfer(
|
||||
this->getLibUsbDeviceHandle(),
|
||||
endPoint,
|
||||
buffer,
|
||||
length,
|
||||
&transferred,
|
||||
0
|
||||
);
|
||||
|
||||
if (libUsbStatusCode != 0) {
|
||||
throw Exception("Failed to read from USB device. Error code returned: " + std::to_string(libUsbStatusCode));
|
||||
}
|
||||
}
|
||||
130
src/DebugToolDrivers/USB/Interface.hpp
Normal file
130
src/DebugToolDrivers/USB/Interface.hpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#include <string>
|
||||
|
||||
#include "UsbDevice.hpp"
|
||||
|
||||
namespace Bloom::Usb
|
||||
{
|
||||
class Interface
|
||||
{
|
||||
private:
|
||||
libusb_device* USBDevice = nullptr;
|
||||
std::uint16_t vendorId = 0;
|
||||
std::uint16_t productId = 0;
|
||||
|
||||
/**
|
||||
* With libusb, we can only claim a single USB interface per device handle. For this reason,
|
||||
* device handles are stored against the interface. Each interface must obtain a new handle before
|
||||
* claiming.
|
||||
*/
|
||||
libusb_device_handle* libUsbDeviceHandle = nullptr;
|
||||
std::uint8_t number = 0;
|
||||
std::string name = "";
|
||||
bool initialised = false;
|
||||
bool claimed = false;
|
||||
|
||||
void setInitialised(bool initialised) {
|
||||
this->initialised = initialised;
|
||||
}
|
||||
|
||||
protected:
|
||||
void setClaimed(bool claimed) {
|
||||
this->claimed = claimed;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit Interface(const std::uint8_t& interfaceNumber = 0) {
|
||||
this->setNumber(interfaceNumber);
|
||||
}
|
||||
|
||||
libusb_device* getUSBDevice() const {
|
||||
return this->USBDevice;
|
||||
}
|
||||
|
||||
void setUSBDevice(libusb_device* USBDevice) {
|
||||
this->USBDevice = USBDevice;
|
||||
}
|
||||
|
||||
libusb_device_handle* getLibUsbDeviceHandle() const {
|
||||
return this->libUsbDeviceHandle;
|
||||
}
|
||||
|
||||
void setLibUsbDeviceHandle(libusb_device_handle* libUsbDeviceHandle) {
|
||||
this->libUsbDeviceHandle = libUsbDeviceHandle;
|
||||
}
|
||||
|
||||
std::uint8_t getNumber() const {
|
||||
return this->number;
|
||||
}
|
||||
|
||||
void setNumber(std::uint8_t number) {
|
||||
this->number = number;
|
||||
}
|
||||
|
||||
const std::string& getName() const {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
void setName(const std::string& name) {
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
bool isClaimed() {
|
||||
return this->claimed;
|
||||
}
|
||||
|
||||
bool isInitialised() {
|
||||
return this->initialised;
|
||||
}
|
||||
|
||||
std::uint16_t getVendorId() const {
|
||||
return this->vendorId;
|
||||
}
|
||||
|
||||
void setVendorId(std::uint16_t vendorId) {
|
||||
this->vendorId = vendorId;
|
||||
}
|
||||
|
||||
std::uint16_t getProductId() const {
|
||||
return this->productId;
|
||||
}
|
||||
|
||||
void setProductId(std::uint16_t productId) {
|
||||
this->productId = productId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to obtain a device descriptor and device handle via libusb.
|
||||
*/
|
||||
virtual void init();
|
||||
|
||||
virtual void setConfiguration(int configIndex);
|
||||
|
||||
/**
|
||||
* Releases the interface and closes the device descriptor.
|
||||
*/
|
||||
virtual void close();
|
||||
|
||||
/**
|
||||
* Attempts to claim the interface
|
||||
*/
|
||||
void claim();
|
||||
void detachKernelDriver();
|
||||
void release();
|
||||
|
||||
/**
|
||||
* @TODO Remove or refactor these (read() and write()) - they're not currently used
|
||||
*
|
||||
* @param buffer
|
||||
* @param endPoint
|
||||
* @param length
|
||||
* @param timeout
|
||||
* @return
|
||||
*/
|
||||
virtual int read(unsigned char* buffer, unsigned char endPoint, std::size_t length, std::size_t timeout);
|
||||
virtual void write(unsigned char* buffer, unsigned char endPoint, int length);
|
||||
};
|
||||
}
|
||||
77
src/DebugToolDrivers/USB/UsbDevice.cpp
Normal file
77
src/DebugToolDrivers/USB/UsbDevice.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include "UsbDevice.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
using Bloom::Usb::UsbDevice;
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
std::vector<libusb_device*> UsbDevice::findMatchingDevices(
|
||||
std::optional<std::uint16_t> vendorId, std::optional<std::uint16_t> productId
|
||||
) {
|
||||
auto libUsbContext = this->libUsbContext;
|
||||
libusb_device** devices = nullptr;
|
||||
libusb_device* device;
|
||||
std::vector<libusb_device*> matchedDevices;
|
||||
ssize_t i = 0, libUsbStatusCode;
|
||||
|
||||
auto vendorIdToMatch = vendorId.value_or(this->vendorId);
|
||||
auto productIdToMatch = productId.value_or(this->productId);
|
||||
|
||||
if ((libUsbStatusCode = libusb_get_device_list(libUsbContext, &devices)) < 0) {
|
||||
throw Exception("Failed to retrieve USB devices - return code: '" + std::to_string(libUsbStatusCode)
|
||||
+ "'");
|
||||
}
|
||||
|
||||
while ((device = devices[i++]) != nullptr) {
|
||||
struct libusb_device_descriptor desc = {};
|
||||
|
||||
if ((libUsbStatusCode = libusb_get_device_descriptor(device, &desc)) < 0) {
|
||||
Logger::warning("Failed to retrieve USB device descriptor - return code: '"
|
||||
+ std::to_string(libUsbStatusCode) + "'");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (desc.idVendor == vendorIdToMatch && desc.idProduct == productIdToMatch) {
|
||||
matchedDevices.push_back(device);
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_device_list(devices, 1);
|
||||
return matchedDevices;
|
||||
}
|
||||
|
||||
void UsbDevice::init()
|
||||
{
|
||||
libusb_init(&this->libUsbContext);
|
||||
// libusb_set_option(this->libUsbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE);
|
||||
auto devices = this->findMatchingDevices();
|
||||
|
||||
if (devices.empty()) {
|
||||
throw Exception("Failed to find USB device with matching vendor & product ID.");
|
||||
|
||||
} else if (devices.size() > 1) {
|
||||
// TODO: implement support for multiple devices (maybe via serial number?)
|
||||
throw Exception("Multiple devices of matching vendor & product ID found.\n"
|
||||
"Yes, as a program I really am too stupid to figure out what to do "
|
||||
"here, so I'm just going to quit.\n Please ensure that only one debug tool "
|
||||
"is connected and then try again.");
|
||||
}
|
||||
|
||||
// For now, just use the first device found.
|
||||
this->setLibUsbDevice(devices.front());
|
||||
}
|
||||
|
||||
void UsbDevice::close()
|
||||
{
|
||||
if (this->libUsbContext != nullptr) {
|
||||
libusb_exit(this->libUsbContext);
|
||||
}
|
||||
}
|
||||
54
src/DebugToolDrivers/USB/UsbDevice.hpp
Normal file
54
src/DebugToolDrivers/USB/UsbDevice.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include "src/DebugToolDrivers/DebugTool.hpp"
|
||||
|
||||
namespace Bloom::Usb
|
||||
{
|
||||
class UsbDevice
|
||||
{
|
||||
private:
|
||||
libusb_context* libUsbContext = nullptr;
|
||||
libusb_device* libUsbDevice = nullptr;
|
||||
std::uint16_t vendorId;
|
||||
std::uint16_t productId;
|
||||
|
||||
std::vector<libusb_device*> findMatchingDevices(
|
||||
std::optional<std::uint16_t> vendorId = std::nullopt, std::optional<std::uint16_t> productId = std::nullopt
|
||||
);
|
||||
|
||||
protected:
|
||||
void close();
|
||||
|
||||
public:
|
||||
void init();
|
||||
|
||||
UsbDevice(std::uint16_t vendorId, std::uint16_t productId) {
|
||||
this->vendorId = vendorId;
|
||||
this->productId = productId;
|
||||
};
|
||||
|
||||
~UsbDevice() = default;
|
||||
|
||||
[[nodiscard]] libusb_device* getLibUsbDevice() const {
|
||||
return this->libUsbDevice;
|
||||
}
|
||||
|
||||
void setLibUsbDevice(libusb_device* libUsbDevice) {
|
||||
this->libUsbDevice = libUsbDevice;
|
||||
}
|
||||
|
||||
std::uint16_t getVendorId() const {
|
||||
return this->vendorId;
|
||||
}
|
||||
|
||||
std::uint16_t getProductId() const {
|
||||
return this->productId;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user