diff --git a/src/DebugToolDrivers/CMakeLists.txt b/src/DebugToolDrivers/CMakeLists.txt index 0ea33ee1..aadfd65b 100755 --- a/src/DebugToolDrivers/CMakeLists.txt +++ b/src/DebugToolDrivers/CMakeLists.txt @@ -2,6 +2,7 @@ target_sources( Bloom PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/USB/UsbDevice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/USB/UsbInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/USB/HID/HidInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/CmsisDapInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/Command.cpp diff --git a/src/DebugToolDrivers/USB/UsbInterface.cpp b/src/DebugToolDrivers/USB/UsbInterface.cpp new file mode 100644 index 00000000..2278116d --- /dev/null +++ b/src/DebugToolDrivers/USB/UsbInterface.cpp @@ -0,0 +1,112 @@ +#include "UsbInterface.hpp" + +#include + +#include "src/Logger/Logger.hpp" + +#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" +#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp" + +namespace Usb +{ + using namespace Exceptions; + + UsbInterface::UsbInterface( + std::uint8_t interfaceNumber, + ::libusb_device_handle* deviceHandle + ) + : interfaceNumber(interfaceNumber) + , deviceHandle(deviceHandle) + {} + + void UsbInterface::init() { + Logger::debug("Claiming USB interface (number: " + std::to_string(this->interfaceNumber) + ")"); + + const auto statusCode = ::libusb_claim_interface(this->deviceHandle, this->interfaceNumber); + if (statusCode != 0) { + throw DeviceInitializationFailure( + "Failed to claim USB interface (number: " + std::to_string(this->interfaceNumber) + ") - error code: " + + std::to_string(statusCode) + ); + } + + this->claimed = true; + } + + void UsbInterface::close() { + if (this->claimed) { + const auto statusCode = ::libusb_release_interface(this->deviceHandle, this->interfaceNumber); + if (statusCode != 0) { + Logger::error( + "Failed to release USB interface (number: " + std::to_string(this->interfaceNumber) + + ") - error code: " + std::to_string(statusCode) + ); + } + } + } + + std::vector UsbInterface::readBulk( + std::uint8_t endpointAddress, + std::optional timeout + ) { + auto output = std::vector(); + + constexpr auto transferSize = 512; + auto bytesTransferred = int(0); + auto totalByteCount = std::size_t(0); + + do { + output.resize(totalByteCount + transferSize, 0x00); + + const auto statusCode = ::libusb_bulk_transfer( + this->deviceHandle, + endpointAddress, + output.data() + totalByteCount, + transferSize, + &bytesTransferred, + timeout.has_value() ? static_cast(timeout->count()) : 0 + ); + + if (statusCode != 0) { + throw DeviceCommunicationFailure("Failed to read from bulk endpoint"); + } + + 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); + } + + totalByteCount += static_cast(bytesTransferred); + + } while (bytesTransferred >= transferSize); + + output.resize(totalByteCount, 0x00); + return output; + } + + void UsbInterface::writeBulk(std::uint8_t endpointAddress, std::vector&& buffer) { + if (buffer.size() > std::numeric_limits::max()) { + throw DeviceCommunicationFailure("Attempted to send too much data to bulk endpoint"); + } + + const auto length = static_cast(buffer.size()); + auto bytesTransferred = int(0); + + const auto statusCode = ::libusb_bulk_transfer( + this->deviceHandle, + endpointAddress, + buffer.data(), + length, + &bytesTransferred, + 0 + ); + + if (statusCode != 0 || bytesTransferred != length) { + Logger::debug( + "Attempted to write " + std::to_string(length) + " bytes to USB bulk endpoint. Bytes written: " + + std::to_string(bytesTransferred) + ". Status code: " + std::to_string(statusCode) + ); + throw DeviceCommunicationFailure("Failed to write data to bulk endpoint"); + } + } +} diff --git a/src/DebugToolDrivers/USB/UsbInterface.hpp b/src/DebugToolDrivers/USB/UsbInterface.hpp new file mode 100644 index 00000000..b812c972 --- /dev/null +++ b/src/DebugToolDrivers/USB/UsbInterface.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace Usb +{ + /** + * The UsbInterface provides access to a particular USB interface. + */ + class UsbInterface + { + public: + std::uint8_t interfaceNumber = 0; + + UsbInterface( + std::uint8_t interfaceNumber, + ::libusb_device_handle* deviceHandle + ); + + UsbInterface(const UsbInterface& other) = delete; + UsbInterface& operator = (const UsbInterface& other) = delete; + + UsbInterface(UsbInterface&& other) = default; + UsbInterface& operator = (UsbInterface&& other) = default; + + /** + * Attempts to claim the interface + */ + void init(); + + /** + * Releases the claimed interface + */ + void close(); + + std::vector readBulk( + std::uint8_t endpointAddress, + std::optional timeout = std::nullopt + ); + + void writeBulk(std::uint8_t endpointAddress, std::vector&& buffer); + + private: + ::libusb_device_handle* deviceHandle; + bool claimed = false; + }; +}