Extract HID report size for EDBG debug tools via HID endpoint descriptor.
Removed hidapi bodge (where we were mimicking the hid_device_ struct, to obtain the report size via `(hid_device_*)->input_ep_max_packet_size`).
This commit is contained in:
@@ -30,14 +30,19 @@ namespace Bloom::DebugToolDrivers
|
|||||||
void EdbgDevice::init() {
|
void EdbgDevice::init() {
|
||||||
UsbDevice::init();
|
UsbDevice::init();
|
||||||
|
|
||||||
auto cmsisHidInterface = Usb::HidInterface(this->cmsisHidInterfaceNumber, this->vendorId, this->productId);
|
this->detachKernelDriverFromInterface(this->cmsisHidInterfaceNumber);
|
||||||
|
|
||||||
this->detachKernelDriverFromInterface(cmsisHidInterface.interfaceNumber);
|
|
||||||
|
|
||||||
if (this->configurationIndex.has_value()) {
|
if (this->configurationIndex.has_value()) {
|
||||||
this->setConfiguration(this->configurationIndex.value());
|
this->setConfiguration(this->configurationIndex.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto cmsisHidInterface = Usb::HidInterface(
|
||||||
|
this->cmsisHidInterfaceNumber,
|
||||||
|
this->getCmsisHidReportSize(),
|
||||||
|
this->vendorId,
|
||||||
|
this->productId
|
||||||
|
);
|
||||||
|
|
||||||
cmsisHidInterface.init();
|
cmsisHidInterface.init();
|
||||||
|
|
||||||
this->edbgInterface = std::make_unique<EdbgInterface>(std::move(cmsisHidInterface));
|
this->edbgInterface = std::make_unique<EdbgInterface>(std::move(cmsisHidInterface));
|
||||||
@@ -125,4 +130,33 @@ namespace Bloom::DebugToolDrivers
|
|||||||
|
|
||||||
this->sessionStarted = false;
|
this->sessionStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint16_t EdbgDevice::getCmsisHidReportSize() {
|
||||||
|
const auto activeConfigDescriptor = this->getConfigDescriptor();
|
||||||
|
|
||||||
|
for (auto interfaceIndex = 0; interfaceIndex < activeConfigDescriptor->bNumInterfaces; ++interfaceIndex) {
|
||||||
|
const auto* interfaceDescriptor = (activeConfigDescriptor->interface + interfaceIndex)->altsetting;
|
||||||
|
|
||||||
|
if (interfaceDescriptor->bInterfaceNumber != this->cmsisHidInterfaceNumber) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto endpointIndex = 0; endpointIndex < activeConfigDescriptor->bNumInterfaces; ++endpointIndex) {
|
||||||
|
const auto* endpointDescriptor = (interfaceDescriptor->endpoint + endpointIndex);
|
||||||
|
|
||||||
|
if ((endpointDescriptor->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) != LIBUSB_ENDPOINT_IN) {
|
||||||
|
// Not an IN endpoint
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpointDescriptor->wMaxPacketSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw DeviceInitializationFailure(
|
||||||
|
"Failed to obtain CMSIS-DAP HID report size via endpoint descriptor - could not find IN endpoint for "
|
||||||
|
"selected configuration value (" + std::to_string(activeConfigDescriptor->bConfigurationValue)
|
||||||
|
+ ") and interface number (" + std::to_string(this->cmsisHidInterfaceNumber) + ")"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,5 +142,16 @@ namespace Bloom::DebugToolDrivers
|
|||||||
> targetPowerManagementInterface = nullptr;
|
> targetPowerManagementInterface = nullptr;
|
||||||
|
|
||||||
bool sessionStarted = false;
|
bool sessionStarted = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because EDBG devices require fixed-length reports to be transmitted to/from their HID interface, we must
|
||||||
|
* know the HID report size (as it differs across EDBG devices).
|
||||||
|
*
|
||||||
|
* This member function will obtain the report size from the endpoint descriptor of the IN endpoint, from the
|
||||||
|
* HID interface.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::uint16_t getCmsisHidReportSize();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,15 @@ namespace Bloom::Usb
|
|||||||
{
|
{
|
||||||
using namespace Bloom::Exceptions;
|
using namespace Bloom::Exceptions;
|
||||||
|
|
||||||
HidInterface::HidInterface(std::uint8_t interfaceNumber, std::uint16_t vendorId, std::uint16_t productId)
|
HidInterface::HidInterface(
|
||||||
|
std::uint8_t interfaceNumber,
|
||||||
|
std::uint16_t inputReportSize,
|
||||||
|
std::uint16_t vendorId,
|
||||||
|
std::uint16_t productId
|
||||||
|
)
|
||||||
: interfaceNumber(interfaceNumber)
|
: interfaceNumber(interfaceNumber)
|
||||||
, vendorId(vendorId)
|
, vendorId(vendorId)
|
||||||
|
, inputReportSize(inputReportSize)
|
||||||
, productId(productId)
|
, productId(productId)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -27,15 +33,6 @@ namespace Bloom::Usb
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->hidDevice.reset(hidDevice);
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HidInterface::close() {
|
void HidInterface::close() {
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#include "hidapi.hpp"
|
#include <hidapi/hidapi.h>
|
||||||
|
#include <hidapi/hidapi_libusb.h>
|
||||||
|
|
||||||
namespace Bloom::Usb
|
namespace Bloom::Usb
|
||||||
{
|
{
|
||||||
@@ -21,9 +22,11 @@ namespace Bloom::Usb
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::uint8_t interfaceNumber = 0;
|
std::uint8_t interfaceNumber = 0;
|
||||||
|
std::uint16_t inputReportSize = 64;
|
||||||
|
|
||||||
HidInterface(
|
HidInterface(
|
||||||
std::uint8_t interfaceNumber,
|
std::uint8_t interfaceNumber,
|
||||||
|
std::uint16_t inputReportSize,
|
||||||
std::uint16_t vendorId,
|
std::uint16_t vendorId,
|
||||||
std::uint16_t productId
|
std::uint16_t productId
|
||||||
);
|
);
|
||||||
@@ -72,12 +75,6 @@ namespace Bloom::Usb
|
|||||||
|
|
||||||
HidDevice hidDevice = HidDevice(nullptr, ::hid_close);
|
HidDevice hidDevice = HidDevice(nullptr, ::hid_close);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 (in size) to the report length.
|
|
||||||
*/
|
|
||||||
std::size_t inputReportSize = 64;
|
|
||||||
|
|
||||||
std::uint16_t vendorId = 0;
|
std::uint16_t vendorId = 0;
|
||||||
std::uint16_t productId = 0;
|
std::uint16_t productId = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libusb-1.0/libusb.h>
|
|
||||||
#include <hidapi/hidapi.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The code below was extracted from the HIDAPI library. Third-party license may apply here.
|
|
||||||
*
|
|
||||||
* https://github.com/signal11/hidapi
|
|
||||||
*/
|
|
||||||
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 start up sequence
|
|
||||||
int shutdown_thread;
|
|
||||||
int cancelled;
|
|
||||||
struct libusb_transfer* transfer;
|
|
||||||
|
|
||||||
// List of received input reports.
|
|
||||||
struct input_report* input_reports;
|
|
||||||
};
|
|
||||||
@@ -56,26 +56,9 @@ namespace Bloom::Usb
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UsbDevice::setConfiguration(std::uint8_t configurationIndex) {
|
void UsbDevice::setConfiguration(std::uint8_t configurationIndex) {
|
||||||
::libusb_config_descriptor* configDescriptorPtr = {};
|
const auto configDescriptor = this->getConfigDescriptor(configurationIndex);
|
||||||
auto libusbStatusCode = ::libusb_get_config_descriptor(
|
|
||||||
this->libusbDevice.get(),
|
|
||||||
configurationIndex,
|
|
||||||
&configDescriptorPtr
|
|
||||||
);
|
|
||||||
|
|
||||||
if (libusbStatusCode < 0) {
|
const auto libusbStatusCode = ::libusb_set_configuration(
|
||||||
throw DeviceInitializationFailure(
|
|
||||||
"Failed to obtain USB configuration descriptor - error code " + std::to_string(libusbStatusCode)
|
|
||||||
+ " returned."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto configDescriptor = std::unique_ptr<::libusb_config_descriptor, decltype(&::libusb_free_config_descriptor)>(
|
|
||||||
configDescriptorPtr,
|
|
||||||
::libusb_free_config_descriptor
|
|
||||||
);
|
|
||||||
|
|
||||||
libusbStatusCode = ::libusb_set_configuration(
|
|
||||||
this->libusbDeviceHandle.get(),
|
this->libusbDeviceHandle.get(),
|
||||||
configDescriptor->bConfigurationValue
|
configDescriptor->bConfigurationValue
|
||||||
);
|
);
|
||||||
@@ -121,6 +104,26 @@ namespace Bloom::Usb
|
|||||||
return matchedDevices;
|
return matchedDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LibusbConfigDescriptor UsbDevice::getConfigDescriptor(std::optional<std::uint8_t> configurationIndex) {
|
||||||
|
::libusb_config_descriptor* configDescriptor = {};
|
||||||
|
|
||||||
|
auto libusbStatusCode = configurationIndex.has_value()
|
||||||
|
? ::libusb_get_config_descriptor(this->libusbDevice.get(), *configurationIndex, &configDescriptor)
|
||||||
|
: ::libusb_get_active_config_descriptor(this->libusbDevice.get(), &configDescriptor);
|
||||||
|
|
||||||
|
if (libusbStatusCode < 0) {
|
||||||
|
throw DeviceInitializationFailure(
|
||||||
|
"Failed to obtain USB configuration descriptor - error code " + std::to_string(libusbStatusCode)
|
||||||
|
+ " returned."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LibusbConfigDescriptor(
|
||||||
|
configDescriptor,
|
||||||
|
::libusb_free_config_descriptor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void UsbDevice::detachKernelDriverFromInterface(std::uint8_t interfaceNumber) {
|
void UsbDevice::detachKernelDriverFromInterface(std::uint8_t interfaceNumber) {
|
||||||
const auto libusbStatusCode = ::libusb_kernel_driver_active(this->libusbDeviceHandle.get(), interfaceNumber);
|
const auto libusbStatusCode = ::libusb_kernel_driver_active(this->libusbDeviceHandle.get(), interfaceNumber);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace Bloom::Usb
|
|||||||
using LibusbContext = std::unique_ptr<::libusb_context, decltype(&::libusb_exit)>;
|
using LibusbContext = std::unique_ptr<::libusb_context, decltype(&::libusb_exit)>;
|
||||||
using LibusbDevice = std::unique_ptr<::libusb_device, decltype(&::libusb_unref_device)>;
|
using LibusbDevice = std::unique_ptr<::libusb_device, decltype(&::libusb_unref_device)>;
|
||||||
using LibusbDeviceHandle = std::unique_ptr<::libusb_device_handle, decltype(&::libusb_close)>;
|
using LibusbDeviceHandle = std::unique_ptr<::libusb_device_handle, decltype(&::libusb_close)>;
|
||||||
|
using LibusbConfigDescriptor = std::unique_ptr<::libusb_config_descriptor, decltype(&::libusb_free_config_descriptor)>;
|
||||||
|
|
||||||
class UsbDevice
|
class UsbDevice
|
||||||
{
|
{
|
||||||
@@ -47,6 +48,8 @@ namespace Bloom::Usb
|
|||||||
|
|
||||||
std::vector<LibusbDevice> findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId);
|
std::vector<LibusbDevice> findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId);
|
||||||
|
|
||||||
|
LibusbConfigDescriptor getConfigDescriptor(std::optional<std::uint8_t> configurationIndex = std::nullopt);
|
||||||
|
|
||||||
void detachKernelDriverFromInterface(std::uint8_t interfaceNumber);
|
void detachKernelDriverFromInterface(std::uint8_t interfaceNumber);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|||||||
Reference in New Issue
Block a user