#include "HidInterface.hpp" #include "src/Logger/Logger.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" #include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp" namespace Bloom::Usb { using namespace Bloom::Exceptions; void HidInterface::init() { if (this->libUsbDevice == nullptr) { throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer."); } hid_init(); hid_device* hidDevice = nullptr; std::string hidInterfacePath = this->getDevicePathByInterfaceNumber(this->getNumber()); Logger::debug("HID device path: " + hidInterfacePath); if ((hidDevice = hid_open_path(hidInterfacePath.c_str())) == nullptr) { throw DeviceInitializationFailure("Failed to open HID device via hidapi."); } if (hidDevice->input_ep_max_packet_size < 1) { throw DeviceInitializationFailure( "Invalid max packet size for USB endpoint, on interface " + std::to_string(this->getNumber()) ); } this->setHidDevice(hidDevice); this->setInputReportSize(static_cast(hidDevice->input_ep_max_packet_size)); this->libUsbDeviceHandle = hidDevice->device_handle; this->initialised = true; } void HidInterface::close() { auto* hidDevice = this->getHidDevice(); if (hidDevice != nullptr) { this->libUsbDeviceHandle = nullptr; hid_close(hidDevice); // hid_close() releases the interface this->claimed = false; } hid_exit(); Interface::close(); } std::vector HidInterface::read(unsigned int timeout) { std::vector 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; } void HidInterface::write(std::vector buffer) { if (buffer.size() > this->getInputReportSize()) { throw DeviceCommunicationFailure( "Cannot send data via HID interface - data exceeds maximum packet size." ); } 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); } int transferred = 0; auto length = buffer.size(); if ((transferred = hid_write(this->getHidDevice(), buffer.data(), 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."); } } std::size_t HidInterface::read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout) { int transferred; if ((transferred = hid_read_timeout( this->hidDevice, buffer, maxLength, timeout == 0 ? -1 : static_cast(timeout)) ) == -1 ) { throw DeviceCommunicationFailure("Failed to read from HID device."); } return static_cast(transferred); } 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 DeviceInitializationFailure("Failed to match interface number with HID interface."); } auto path = std::string(hidDeviceInfoList->path); hid_free_enumeration(hidDeviceInfoList); return path; } }