Initial commit

This commit is contained in:
Nav
2021-04-04 21:04:12 +01:00
commit a29c5e1fec
549 changed files with 441216 additions and 0 deletions

View 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;
}

View 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);
};
}

View 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;
};