2021-08-15 01:47:48 +01:00
|
|
|
#include "HidInterface.hpp"
|
|
|
|
|
|
2021-04-04 21:04:12 +01:00
|
|
|
#include <cstdint>
|
|
|
|
|
#include <string>
|
|
|
|
|
|
2021-04-06 02:10:14 +01:00
|
|
|
#include "hidapi.hpp"
|
|
|
|
|
#include "src/Logger/Logger.hpp"
|
2021-08-15 01:47:48 +01:00
|
|
|
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
|
|
|
|
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
2021-04-04 21:04:12 +01:00
|
|
|
|
|
|
|
|
using namespace Bloom::Usb;
|
|
|
|
|
using namespace Bloom::Exceptions;
|
|
|
|
|
|
|
|
|
|
std::string HidInterface::getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber) {
|
2021-04-07 23:30:01 +01:00
|
|
|
hid_device_info* hidDeviceInfoList = hid_enumerate(this->getVendorId(), this->getProductId());
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2021-04-07 23:30:01 +01:00
|
|
|
while (hidDeviceInfoList != nullptr) {
|
|
|
|
|
if (hidDeviceInfoList->interface_number == interfaceNumber) {
|
2021-04-04 21:04:12 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-07 23:30:01 +01:00
|
|
|
hidDeviceInfoList = hidDeviceInfoList->next;
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-07 23:30:01 +01:00
|
|
|
if (hidDeviceInfoList == nullptr) {
|
2021-08-15 01:47:48 +01:00
|
|
|
throw DeviceInitializationFailure("Failed to match interface number with HID interface.");
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-07 23:30:01 +01:00
|
|
|
auto path = std::string(hidDeviceInfoList->path);
|
|
|
|
|
hid_free_enumeration(hidDeviceInfoList);
|
2021-04-04 21:04:12 +01:00
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HidInterface::init() {
|
2021-04-07 23:30:01 +01:00
|
|
|
if (this->libUsbDevice == nullptr) {
|
2021-08-15 01:47:48 +01:00
|
|
|
throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer.");
|
2021-04-07 23:30:01 +01:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
|
|
|
|
hid_init();
|
|
|
|
|
hid_device* hidDevice;
|
|
|
|
|
|
2021-04-07 23:30:01 +01:00
|
|
|
std::string hidInterfacePath = this->getDevicePathByInterfaceNumber(this->getNumber());
|
|
|
|
|
Logger::debug("HID device path: " + hidInterfacePath);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2021-04-07 23:30:01 +01:00
|
|
|
if ((hidDevice = hid_open_path(hidInterfacePath.c_str())) == nullptr) {
|
2021-08-15 01:47:48 +01:00
|
|
|
throw DeviceInitializationFailure("Failed to open HID device via hidapi.");
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hidDevice->input_ep_max_packet_size < 1) {
|
2021-08-15 01:47:48 +01:00
|
|
|
throw DeviceInitializationFailure(
|
|
|
|
|
"Invalid max packet size for USB endpoint, on interface " + std::to_string(this->getNumber())
|
|
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->setHidDevice(hidDevice);
|
|
|
|
|
this->setInputReportSize(static_cast<std::size_t>(hidDevice->input_ep_max_packet_size));
|
2021-04-07 23:30:01 +01:00
|
|
|
this->libUsbDeviceHandle = hidDevice->device_handle;
|
|
|
|
|
this->initialised = true;
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HidInterface::close() {
|
|
|
|
|
auto hidDevice = this->getHidDevice();
|
|
|
|
|
|
|
|
|
|
if (hidDevice != nullptr) {
|
2021-04-07 23:30:01 +01:00
|
|
|
this->libUsbDeviceHandle = nullptr;
|
2021-04-04 21:04:12 +01:00
|
|
|
hid_close(hidDevice);
|
|
|
|
|
// hid_close() releases the interface
|
2021-04-07 23:30:01 +01:00
|
|
|
this->claimed = false;
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hid_exit();
|
|
|
|
|
Interface::close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::size_t HidInterface::read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout) {
|
|
|
|
|
int transferred;
|
|
|
|
|
|
2021-04-07 23:30:01 +01:00
|
|
|
if ((transferred = hid_read_timeout(
|
|
|
|
|
this->hidDevice,
|
|
|
|
|
buffer,
|
|
|
|
|
maxLength,
|
|
|
|
|
timeout == 0 ? -1 : static_cast<int>(timeout))
|
|
|
|
|
) == -1
|
2021-04-04 21:04:12 +01:00
|
|
|
) {
|
|
|
|
|
throw DeviceCommunicationFailure("Failed to read from HID device.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return static_cast<std::size_t>(transferred);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HidInterface::write(std::vector<unsigned char> buffer) {
|
|
|
|
|
if (buffer.size() > this->getInputReportSize()) {
|
2021-08-15 01:47:48 +01:00
|
|
|
throw DeviceCommunicationFailure(
|
|
|
|
|
"Cannot send data via HID interface - data exceeds maximum packet size."
|
|
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
|
|
|
|
} 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);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-07 23:30:01 +01:00
|
|
|
int transferred;
|
|
|
|
|
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.");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|