Files
BloomPatched/src/DebugToolDrivers/USB/HID/HidInterface.cpp

130 lines
4.5 KiB
C++
Raw Normal View History

#include "HidInterface.hpp"
2021-04-06 02:10:14 +01:00
#include "src/Logger/Logger.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
2021-04-04 21:04:12 +01:00
namespace Bloom::Usb
{
using namespace Bloom::Exceptions;
2021-04-04 21:04:12 +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
void HidInterface::init() {
::hid_init();
::hid_device* hidDevice = nullptr;
2021-04-04 21:04:12 +01:00
const auto hidInterfacePath = this->getHidDevicePath();
Logger::debug("HID device path: " + hidInterfacePath);
2021-04-04 21:04:12 +01:00
if ((hidDevice = ::hid_open_path(hidInterfacePath.c_str())) == nullptr) {
throw DeviceInitializationFailure("Failed to open HID device via hidapi.");
}
2021-04-04 21:04:12 +01:00
this->hidDevice.reset(hidDevice);
if (this->hidDevice->input_ep_max_packet_size < 1) {
throw DeviceInitializationFailure(
"Invalid max packet size for USB endpoint, on interface "
+ std::to_string(this->interfaceNumber)
);
}
this->inputReportSize = static_cast<std::size_t>(this->hidDevice->input_ep_max_packet_size);
2021-04-04 21:04:12 +01:00
}
void HidInterface::close() {
this->hidDevice.reset();
::hid_exit();
}
2021-04-04 21:04:12 +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
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
if (transferredByteCount == -1) {
throw DeviceCommunicationFailure("Failed to read from HID device.");
}
2021-04-04 21:04:12 +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
totalByteCount += static_cast<std::size_t>(transferredByteCount);
} while (transferredByteCount >= readSize);
output.resize(totalByteCount, 0x00);
return output;
2021-04-04 21:04:12 +01:00
}
void HidInterface::write(std::vector<unsigned char>&& buffer) {
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
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
int transferred = 0;
const auto length = buffer.size();
if ((transferred = ::hid_write(this->hidDevice.get(), 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::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
);
2021-04-04 21:04:12 +01:00
auto matchedDevice = std::optional<::hid_device_info*>();
auto* hidDeviceInfo = hidDeviceInfoList.get();
while (hidDeviceInfo != nullptr) {
if (hidDeviceInfo->interface_number == this->interfaceNumber) {
matchedDevice = hidDeviceInfo;
break;
}
hidDeviceInfo = hidDeviceInfoList->next;
}
if (!matchedDevice.has_value()) {
throw DeviceInitializationFailure("Failed to match interface number with HID interface.");
}
2021-04-04 21:04:12 +01:00
return std::string(matchedDevice.value()->path);
}
2021-04-04 21:04:12 +01:00
}