2021-08-15 01:47:48 +01:00
|
|
|
#include "HidInterface.hpp"
|
|
|
|
|
|
2021-04-06 02:10:14 +01:00
|
|
|
#include "src/Logger/Logger.hpp"
|
2021-10-06 21:12:31 +01:00
|
|
|
|
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
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
namespace Bloom::Usb
|
|
|
|
|
{
|
|
|
|
|
using namespace Bloom::Exceptions;
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
HidInterface::HidInterface(std::uint8_t interfaceNumber, std::uint16_t vendorId, std::uint16_t productId)
|
|
|
|
|
: interfaceNumber(interfaceNumber)
|
|
|
|
|
, vendorId(vendorId)
|
|
|
|
|
, productId(productId)
|
|
|
|
|
{}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
void HidInterface::init() {
|
|
|
|
|
::hid_init();
|
|
|
|
|
::hid_device* hidDevice = nullptr;
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
const auto hidInterfacePath = this->getHidDevicePath();
|
2022-02-05 15:32:08 +00:00
|
|
|
Logger::debug("HID device path: " + hidInterfacePath);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
if ((hidDevice = ::hid_open_path(hidInterfacePath.c_str())) == nullptr) {
|
2022-02-05 15:32:08 +00:00
|
|
|
throw DeviceInitializationFailure("Failed to open HID device via hidapi.");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
this->hidDevice.reset(hidDevice);
|
|
|
|
|
|
|
|
|
|
if (this->hidDevice->input_ep_max_packet_size < 1) {
|
2022-02-05 15:32:08 +00:00
|
|
|
throw DeviceInitializationFailure(
|
|
|
|
|
"Invalid max packet size for USB endpoint, on interface "
|
2022-10-01 16:50:57 +01:00
|
|
|
+ std::to_string(this->interfaceNumber)
|
2022-02-05 15:32:08 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
this->inputReportSize = static_cast<std::size_t>(this->hidDevice->input_ep_max_packet_size);
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
void HidInterface::close() {
|
2022-10-01 16:50:57 +01:00
|
|
|
this->hidDevice.reset();
|
|
|
|
|
::hid_exit();
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
std::vector<unsigned char> HidInterface::read(std::optional<std::chrono::milliseconds> timeout) {
|
|
|
|
|
auto output = std::vector<unsigned char>();
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
const auto readSize = this->inputReportSize;
|
|
|
|
|
auto transferredByteCount = int(0);
|
|
|
|
|
auto totalByteCount = std::size_t(0);
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
output.resize(totalByteCount + readSize, 0x00);
|
|
|
|
|
|
|
|
|
|
transferredByteCount = ::hid_read_timeout(
|
|
|
|
|
this->hidDevice.get(),
|
|
|
|
|
output.data() + totalByteCount,
|
|
|
|
|
readSize,
|
|
|
|
|
timeout.has_value() ? static_cast<int>(timeout->count()) : -1
|
|
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
if (transferredByteCount == -1) {
|
|
|
|
|
throw DeviceCommunicationFailure("Failed to read from HID device.");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
if (totalByteCount == 0) {
|
|
|
|
|
// After the first read, set the timeout to 1 millisecond, as we don't want to wait for subsequent data
|
|
|
|
|
timeout = std::chrono::milliseconds(1);
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
totalByteCount += static_cast<std::size_t>(transferredByteCount);
|
2021-10-06 21:12:31 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
} while (transferredByteCount >= readSize);
|
2021-10-06 21:12:31 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
output.resize(totalByteCount, 0x00);
|
2022-02-05 15:32:08 +00:00
|
|
|
return output;
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-06-01 21:48:27 +01:00
|
|
|
void HidInterface::write(std::vector<unsigned char>&& buffer) {
|
2022-02-05 15:32:08 +00:00
|
|
|
if (buffer.size() > this->getInputReportSize()) {
|
|
|
|
|
throw DeviceCommunicationFailure(
|
|
|
|
|
"Cannot send data via HID interface - data exceeds maximum packet size."
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
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-04 21:04:12 +01:00
|
|
|
|
2022-02-05 15:32:08 +00:00
|
|
|
int transferred = 0;
|
2022-06-01 21:48:27 +01:00
|
|
|
const auto length = buffer.size();
|
2021-04-07 23:30:01 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
if ((transferred = ::hid_write(this->hidDevice.get(), buffer.data(), length)) != length) {
|
2022-02-05 15:32:08 +00:00
|
|
|
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-07 23:30:01 +01:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
std::string HidInterface::getHidDevicePath() {
|
|
|
|
|
const auto hidDeviceInfoList = std::unique_ptr<::hid_device_info, decltype(&::hid_free_enumeration)>(
|
|
|
|
|
::hid_enumerate(this->vendorId, this->productId),
|
|
|
|
|
::hid_free_enumeration
|
2022-02-05 15:32:08 +00:00
|
|
|
);
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
auto matchedDevice = std::optional<::hid_device_info*>();
|
|
|
|
|
|
|
|
|
|
auto* hidDeviceInfo = hidDeviceInfoList.get();
|
|
|
|
|
while (hidDeviceInfo != nullptr) {
|
|
|
|
|
if (hidDeviceInfo->interface_number == this->interfaceNumber) {
|
|
|
|
|
matchedDevice = hidDeviceInfo;
|
2022-02-05 15:32:08 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2021-10-06 21:12:31 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
hidDeviceInfo = hidDeviceInfoList->next;
|
2021-10-06 21:12:31 +01:00
|
|
|
}
|
|
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
if (!matchedDevice.has_value()) {
|
2022-02-05 15:32:08 +00:00
|
|
|
throw DeviceInitializationFailure("Failed to match interface number with HID interface.");
|
|
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
|
2022-10-01 16:50:57 +01:00
|
|
|
return std::string(matchedDevice.value()->path);
|
2021-10-06 21:12:31 +01:00
|
|
|
}
|
2021-04-04 21:04:12 +01:00
|
|
|
}
|