From 7f9ca71ed825f4bae1462b6dc6c2d75bb879ac27 Mon Sep 17 00:00:00 2001 From: Marius Greuel Date: Sat, 18 Jan 2020 19:07:02 +0200 Subject: [PATCH] Add support for WinUSB devices --- external/libusb/libusb.vcxproj | 3 +- external/libusb/libusb.vcxproj.filters | 3 +- external/libusb/src/libwinusb.h | 1299 ++++++++++++++++++++++++ external/libusb/src/windows.c | 1281 ----------------------- external/libusb/src/windows.cpp | 470 +++++++++ 5 files changed, 1773 insertions(+), 1283 deletions(-) create mode 100644 external/libusb/src/libwinusb.h delete mode 100644 external/libusb/src/windows.c create mode 100644 external/libusb/src/windows.cpp diff --git a/external/libusb/libusb.vcxproj b/external/libusb/libusb.vcxproj index ab0f529c..ddd33f5c 100644 --- a/external/libusb/libusb.vcxproj +++ b/external/libusb/libusb.vcxproj @@ -23,13 +23,14 @@ - + + diff --git a/external/libusb/libusb.vcxproj.filters b/external/libusb/libusb.vcxproj.filters index 0fd41508..8eaf05b1 100644 --- a/external/libusb/libusb.vcxproj.filters +++ b/external/libusb/libusb.vcxproj.filters @@ -5,7 +5,7 @@ - + @@ -14,5 +14,6 @@ + \ No newline at end of file diff --git a/external/libusb/src/libwinusb.h b/external/libusb/src/libwinusb.h new file mode 100644 index 00000000..c19c2735 --- /dev/null +++ b/external/libusb/src/libwinusb.h @@ -0,0 +1,1299 @@ +// +// libwinusb.h +// Copyright (C) 2019 Marius Greuel. All rights reserved. +// + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "winusb") +#pragma comment(lib, "setupapi") + +namespace LibWinUsb +{ + class Handle + { + public: + Handle() = default; + + ~Handle() + { + Close(); + } + + Handle(const Handle&) = delete; + Handle& operator=(const Handle& other) = delete; + Handle(Handle&&) = default; + Handle& operator=(Handle&& other) = default; + + operator HANDLE() const noexcept + { + return m_handle; + } + + void Close() noexcept + { + if (m_handle != nullptr) + { + CloseHandle(m_handle); + m_handle = nullptr; + } + } + + void Attach(HANDLE handle) noexcept + { + assert(m_handle == nullptr); + m_handle = handle; + } + + HANDLE Detach() noexcept + { + HANDLE handle = m_handle; + m_handle = nullptr; + return handle; + } + + protected: + HANDLE m_handle = nullptr; + }; + + class Event : public Handle + { + public: + Event() = default; + Event(const Event&) = delete; + Event& operator=(const Event& other) = delete; + Event(Event&&) = default; + Event& operator=(Event&& other) = default; + + HRESULT Create(BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName = nullptr, LPSECURITY_ATTRIBUTES lpAttributes = nullptr) noexcept + { + HANDLE hEvent = CreateEventW(lpAttributes, bManualReset, bInitialState, lpName); + if (hEvent == nullptr) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + Close(); + Attach(hEvent); + return S_OK; + } + + HRESULT Open(LPCWSTR lpName, DWORD dwDesiredAccess = EVENT_ALL_ACCESS, BOOL bInheritHandle = FALSE) noexcept + { + HANDLE hEvent = OpenEventW(dwDesiredAccess, bInheritHandle, lpName); + if (hEvent == nullptr) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + Close(); + Attach(hEvent); + return S_OK; + } + + HRESULT Set() noexcept + { + if (!SetEvent(m_handle)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + return S_OK; + } + + HRESULT Reset() noexcept + { + if (!ResetEvent(m_handle)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + return S_OK; + } + }; + + class AutoResetEvent : public Event + { + public: + HRESULT Create(BOOL bInitialState = FALSE, LPCWSTR lpName = nullptr, LPSECURITY_ATTRIBUTES lpAttributes = nullptr) + { + return Event::Create(FALSE, bInitialState, lpName, lpAttributes); + } + }; + + class ManualResetEvent : public Event + { + public: + HRESULT Create(BOOL bInitialState = FALSE, LPCWSTR lpName = nullptr, LPSECURITY_ATTRIBUTES lpAttributes = nullptr) + { + return Event::Create(TRUE, bInitialState, lpName, lpAttributes); + } + }; + + class Encoding + { + public: + static std::string ToAnsi(const std::wstring& text) + { + std::string str; + + if (!text.empty()) + { + int chars = ::WideCharToMultiByte(CP_ACP, 0, text.c_str(), static_cast(text.size()), nullptr, 0, nullptr, nullptr); + if (chars == 0) + { + return std::string(); + } + + str.resize(chars); + chars = ::WideCharToMultiByte(CP_ACP, 0, text.c_str(), static_cast(text.size()), &str[0], static_cast(str.size()), nullptr, nullptr); + if (chars == 0) + { + return std::string(); + } + } + + return str; + } + + static std::wstring ToUnicode(const std::string& text) + { + std::wstring str; + + if (!text.empty()) + { + int chars = ::MultiByteToWideChar(CP_ACP, 0, text.c_str(), static_cast(text.size()), nullptr, 0); + if (chars == 0) + { + return std::wstring(); + } + + str.resize(chars); + chars = ::MultiByteToWideChar(CP_ACP, 0, text.c_str(), static_cast(text.size()), &str[0], static_cast(str.size())); + if (chars == 0) + { + return std::wstring(); + } + } + + return str; + } + }; + + class DeviceInfo : public SP_DEVINFO_DATA + { + public: + DeviceInfo() + { + static_cast(*this) = { sizeof(SP_DEVINFO_DATA) }; + } + + DeviceInfo(const DeviceInfo&) = delete; + DeviceInfo& operator=(const DeviceInfo& other) = delete; + DeviceInfo(DeviceInfo&&) = delete; + DeviceInfo& operator=(DeviceInfo&& other) = delete; + + public: + HRESULT Open(HDEVINFO hDeviceInfoSet, LPCWSTR pszDeviceInstanceId, HWND hwndParent = nullptr, DWORD dwOpenFlags = 0) + { + if (!SetupDiOpenDeviceInfoW(hDeviceInfoSet, pszDeviceInstanceId, hwndParent, dwOpenFlags, this)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + return S_OK; + } + + HRESULT GetProperty(HDEVINFO hDeviceInfoSet, const DEVPROPKEY* key, bool& value) + { + DEVPROP_BOOLEAN buffer{}; + DEVPROPTYPE type = DEVPROP_TYPE_EMPTY; + DWORD dwRequiredSize = 0; + if (!SetupDiGetDevicePropertyW(hDeviceInfoSet, this, key, &type, reinterpret_cast(&buffer), sizeof(buffer), &dwRequiredSize, 0)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (type != DEVPROP_TYPE_BOOLEAN) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + } + + value = buffer != DEVPROP_FALSE; + return S_OK; + } + + HRESULT GetProperty(HDEVINFO hDeviceInfoSet, const DEVPROPKEY* key, uint32_t& value) + { + uint32_t buffer{}; + DEVPROPTYPE type = DEVPROP_TYPE_EMPTY; + DWORD dwRequiredSize = 0; + if (!SetupDiGetDevicePropertyW(hDeviceInfoSet, this, key, &type, reinterpret_cast(&buffer), sizeof(buffer), &dwRequiredSize, 0)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (type != DEVPROP_TYPE_UINT32) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + } + + value = buffer; + return S_OK; + } + + HRESULT GetProperty(HDEVINFO hDeviceInfoSet, const DEVPROPKEY* key, GUID& value) + { + GUID buffer{}; + DEVPROPTYPE type = DEVPROP_TYPE_EMPTY; + DWORD dwRequiredSize = 0; + if (!SetupDiGetDevicePropertyW(hDeviceInfoSet, this, key, &type, reinterpret_cast(&buffer), sizeof(buffer), &dwRequiredSize, 0)) + { + DWORD dwError = GetLastError(); + if (dwError != ERROR_INSUFFICIENT_BUFFER) + { + return HRESULT_FROM_WIN32(dwError); + } + } + + if (type != DEVPROP_TYPE_GUID) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + } + + value = buffer; + return S_OK; + } + + HRESULT GetProperty(HDEVINFO hDeviceInfoSet, const DEVPROPKEY* key, std::wstring& value) + { + DEVPROPTYPE type = DEVPROP_TYPE_EMPTY; + DWORD dwRequiredSize = 0; + if (!SetupDiGetDevicePropertyW(hDeviceInfoSet, this, key, &type, nullptr, 0, &dwRequiredSize, 0)) + { + DWORD dwError = GetLastError(); + if (dwError != ERROR_INSUFFICIENT_BUFFER) + { + return HRESULT_FROM_WIN32(dwError); + } + } + + if (type != DEVPROP_TYPE_STRING) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + } + + std::vector buffer(dwRequiredSize); + if (!SetupDiGetDevicePropertyW(hDeviceInfoSet, this, key, &type, buffer.data(), dwRequiredSize, nullptr, 0)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + LPCWSTR pszValue = reinterpret_cast(buffer.data()); + DWORD dwChars = dwRequiredSize / sizeof(WCHAR); + if (dwChars > 0 && pszValue[dwChars - 1] == 0) + { + dwChars--; + } + + value = std::wstring(pszValue, dwChars); + return S_OK; + } + + HRESULT GetProperty(HDEVINFO hDeviceInfoSet, const DEVPROPKEY* key, std::vector& values) + { + DEVPROPTYPE type = DEVPROP_TYPE_EMPTY; + DWORD dwRequiredSize = 0; + if (!SetupDiGetDevicePropertyW(hDeviceInfoSet, this, key, &type, nullptr, 0, &dwRequiredSize, 0)) + { + DWORD dwError = GetLastError(); + if (dwError != ERROR_INSUFFICIENT_BUFFER) + { + return HRESULT_FROM_WIN32(dwError); + } + } + + if (type != DEVPROP_TYPE_STRING_LIST) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + } + + std::vector buffer(dwRequiredSize); + if (!SetupDiGetDevicePropertyW(hDeviceInfoSet, this, key, &type, buffer.data(), dwRequiredSize, nullptr, 0)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + auto data = reinterpret_cast(buffer.data()); + while (*data != 0) + { + values.push_back(data); + data += wcslen(data) + 1; + } + + return S_OK; + } + }; + + class DeviceInfoSet + { + public: + DeviceInfoSet() = default; + DeviceInfoSet(const DeviceInfoSet&) = delete; + DeviceInfoSet& operator=(const DeviceInfoSet& other) = delete; + DeviceInfoSet(DeviceInfoSet&&) = delete; + DeviceInfoSet& operator=(DeviceInfoSet&& other) = delete; + + ~DeviceInfoSet() + { + Destroy(); + } + + public: + operator HDEVINFO() const noexcept + { + return m_hDeviceInfoSet; + } + + public: + HRESULT Create(const GUID* classGuid, HWND hwndParent) + { + Destroy(); + + HDEVINFO hDeviceInfoSet = SetupDiCreateDeviceInfoList(classGuid, hwndParent); + if (hDeviceInfoSet == INVALID_HANDLE_VALUE) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + m_hDeviceInfoSet = hDeviceInfoSet; + return S_OK; + } + + HRESULT GetClassDevs(const GUID* classGuid, PCWSTR pszEnumerator, HWND hWndParent, DWORD dwFlags) + { + Destroy(); + + HDEVINFO hDeviceInfoSet = SetupDiGetClassDevsW(classGuid, pszEnumerator, hWndParent, dwFlags); + if (hDeviceInfoSet == INVALID_HANDLE_VALUE) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + m_hDeviceInfoSet = hDeviceInfoSet; + return S_OK; + } + + void Destroy() + { + if (m_hDeviceInfoSet != INVALID_HANDLE_VALUE) + { + SetupDiDestroyDeviceInfoList(m_hDeviceInfoSet); + m_hDeviceInfoSet = INVALID_HANDLE_VALUE; + } + } + + private: + HDEVINFO m_hDeviceInfoSet = INVALID_HANDLE_VALUE; + }; + + class UsbDriver + { + public: + virtual ~UsbDriver() + { + Close(); + } + + UsbDriver() = default; + UsbDriver(const UsbDriver&) = delete; + UsbDriver& operator=(const UsbDriver& other) = delete; + UsbDriver(UsbDriver&&) = delete; + UsbDriver& operator=(UsbDriver&& other) = delete; + + public: + virtual HRESULT Open(LPCWSTR pszPath) + { + Close(); + + HANDLE hDevice = CreateFileW( + pszPath, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + nullptr); + if (hDevice == INVALID_HANDLE_VALUE) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + m_hDevice.Attach(hDevice); + return S_OK; + } + + virtual void Close() + { + m_hDevice.Close(); + } + + virtual HRESULT GetDescriptor(uint8_t type, uint8_t index, uint16_t languageID, void* buffer, uint16_t bufferSize, uint32_t* bytesReturned, uint32_t timeout) = 0; + virtual HRESULT ControlTransfer(uint8_t requesttype, uint8_t request, uint16_t value, uint16_t index, void* buffer, uint16_t bufferSize, uint32_t* bytesReturned, uint32_t timeout) = 0; + virtual HRESULT SetConfiguration(uint8_t configuration) = 0; + virtual HRESULT ClaimInterface(uint8_t interfaceIndex) = 0; + virtual HRESULT ReleaseInterface(uint8_t interfaceIndex) = 0; + virtual HRESULT SetAlternateSetting(uint8_t interfaceIndex, uint8_t alternate) = 0; + virtual HRESULT ReadPipe(uint8_t pipeId, void* buffer, uint32_t bufferSize, uint32_t* bytesRead, uint32_t timeout) = 0; + virtual HRESULT WritePipe(uint8_t pipeId, void* buffer, uint32_t bufferSize, uint32_t* bytesWritten, uint32_t timeout) = 0; + + protected: + Handle m_hDevice; + }; + + class WinUsbDriver : public UsbDriver + { + public: + HRESULT Open(LPCWSTR pszPath) override + { + HRESULT hr = UsbDriver::Open(pszPath); + if (FAILED(hr)) + { + return hr; + } + + WINUSB_INTERFACE_HANDLE hWinUsbInterface = nullptr; + if (!WinUsb_Initialize(m_hDevice, &hWinUsbInterface)) + { + DWORD dwError = GetLastError(); + Close(); + return HRESULT_FROM_WIN32(dwError); + } + + m_hWinUsbInterface = hWinUsbInterface; + return S_OK; + } + + void Close() override + { + if (m_hWinUsbInterface != nullptr) + { + WinUsb_Free(m_hWinUsbInterface); + m_hWinUsbInterface = nullptr; + } + + UsbDriver::Close(); + } + + HRESULT GetDescriptor(uint8_t type, uint8_t index, uint16_t languageID, void* buffer, uint16_t bufferSize, uint32_t* bytesReturned, uint32_t timeout) override + { + UNREFERENCED_PARAMETER(timeout); + + if (bytesReturned != nullptr) + { + *bytesReturned = 0; + } + + ULONG bytesTransferred = 0; + if (!WinUsb_GetDescriptor(m_hWinUsbInterface, type, index, languageID, static_cast(buffer), bufferSize, &bytesTransferred)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesReturned != nullptr) + { + *bytesReturned = bytesTransferred; + } + + return S_OK; + } + + HRESULT ControlTransfer(uint8_t requesttype, uint8_t request, uint16_t value, uint16_t index, void* buffer, uint16_t bufferSize, uint32_t* bytesReturned, uint32_t timeout) override + { + UNREFERENCED_PARAMETER(timeout); + + if (bytesReturned != nullptr) + { + *bytesReturned = 0; + } + + WINUSB_SETUP_PACKET packet{}; + packet.RequestType = requesttype; + packet.Request = request; + packet.Value = value; + packet.Index = index; + packet.Length = bufferSize; + + ULONG bytesTransferred = 0; + if (!WinUsb_ControlTransfer(m_hWinUsbInterface, packet, static_cast(buffer), bufferSize, &bytesTransferred, nullptr)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesReturned != nullptr) + { + *bytesReturned = bytesTransferred; + } + + return S_OK; + } + + HRESULT SetConfiguration(uint8_t configuration) override + { + // Not supported, fail silently. + return S_FALSE; + } + + HRESULT ClaimInterface(uint8_t interfaceIndex) override + { + m_associatedInterface.index = interfaceIndex; + + if (interfaceIndex > 0) + { + WINUSB_INTERFACE_HANDLE handle = nullptr; + if (!WinUsb_GetAssociatedInterface(m_hWinUsbInterface, interfaceIndex, &handle)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + m_associatedInterface.first = false; + m_associatedInterface.handle = handle; + } + else + { + m_associatedInterface.first = true; + m_associatedInterface.handle = m_hWinUsbInterface; + } + + return S_OK; + } + + HRESULT ReleaseInterface(uint8_t interfaceIndex) override + { + if (interfaceIndex != m_associatedInterface.index) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE); + } + + if (m_associatedInterface.handle != nullptr && !m_associatedInterface.first) + { + WinUsb_Free(m_associatedInterface.handle); + m_associatedInterface.handle = nullptr; + } + + return S_OK; + } + + HRESULT SetAlternateSetting(uint8_t interfaceIndex, uint8_t alternate) override + { + if (m_associatedInterface.handle == nullptr) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE); + } + + return E_FAIL; + } + + HRESULT ReadPipe(uint8_t pipeId, void* buffer, uint32_t bufferSize, uint32_t* bytesRead, uint32_t timeout) override + { + UNREFERENCED_PARAMETER(timeout); + + if (bytesRead != nullptr) + { + *bytesRead = 0; + } + + if (m_associatedInterface.handle == nullptr) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE); + } + + if (!WinUsb_SetPipePolicy(m_associatedInterface.handle, pipeId, PIPE_TRANSFER_TIMEOUT, sizeof(timeout), &timeout)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + ULONG lengthTransferred = 0; + if (!WinUsb_ReadPipe(m_associatedInterface.handle, pipeId, static_cast(buffer), bufferSize, &lengthTransferred, nullptr)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesRead != nullptr) + { + *bytesRead = lengthTransferred; + } + + return S_OK; + } + + HRESULT WritePipe(uint8_t pipeId, void* buffer, uint32_t bufferSize, uint32_t* bytesWritten, uint32_t timeout) override + { + UNREFERENCED_PARAMETER(timeout); + + if (bytesWritten != nullptr) + { + *bytesWritten = 0; + } + + if (m_associatedInterface.handle == nullptr) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE); + } + + if (!WinUsb_SetPipePolicy(m_associatedInterface.handle, pipeId, PIPE_TRANSFER_TIMEOUT, sizeof(timeout), &timeout)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + ULONG lengthTransferred = 0; + if (!WinUsb_WritePipe(m_associatedInterface.handle, pipeId, static_cast(buffer), bufferSize, &lengthTransferred, nullptr)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesWritten != nullptr) + { + *bytesWritten = lengthTransferred; + } + + return S_OK; + } + + private: + struct Interface + { + WINUSB_INTERFACE_HANDLE handle = nullptr; + uint8_t index = 0; + bool first = false; + }; + + WINUSB_INTERFACE_HANDLE m_hWinUsbInterface = nullptr; + Interface m_associatedInterface; + }; + + class LibUsbDriver : public UsbDriver + { + public: + HRESULT Open(LPCWSTR pszPath) override + { + HRESULT hr = UsbDriver::Open(pszPath); + if (FAILED(hr)) + { + return hr; + } + + hr = m_event.Create(); + if (FAILED(hr)) + { + Close(); + return hr; + } + + return S_OK; + } + + void Close() override + { + m_event.Close(); + UsbDriver::Close(); + } + + HRESULT GetDescriptor(uint8_t type, uint8_t index, uint16_t languageID, void* buffer, uint16_t bufferSize, uint32_t* bytesReturned, uint32_t timeout) override + { + libusb_request data{}; + data.timeout = timeout; + data.descriptor.type = type; + data.descriptor.index = index; + data.descriptor.language_id = languageID; + data.descriptor.recipient = USB_RECIP_DEVICE; + return DeviceIoControl(LIBUSB_IOCTL_GET_DESCRIPTOR, &data, sizeof(data), buffer, bufferSize, bytesReturned); + } + + HRESULT ControlTransfer(uint8_t requesttype, uint8_t request, uint16_t value, uint16_t index, void* buffer, uint16_t bufferSize, uint32_t* bytesTransmitted, uint32_t timeout) override + { + libusb_request data{}; + data.timeout = timeout; + + uint32_t ioControlCode = 0; + switch (requesttype & (0x03 << 5)) + { + case USB_TYPE_STANDARD: + switch (request) + { + case USB_REQ_GET_STATUS: + ioControlCode = LIBUSB_IOCTL_GET_STATUS; + data.status.recipient = requesttype & 0x1F; + data.status.index = index; + break; + case USB_REQ_CLEAR_FEATURE: + ioControlCode = LIBUSB_IOCTL_CLEAR_FEATURE; + data.feature.recipient = requesttype & 0x1F; + data.feature.feature = value; + data.feature.index = index; + break; + case USB_REQ_SET_FEATURE: + ioControlCode = LIBUSB_IOCTL_SET_FEATURE; + data.feature.recipient = requesttype & 0x1F; + data.feature.feature = value; + data.feature.index = index; + break; + case USB_REQ_GET_DESCRIPTOR: + ioControlCode = LIBUSB_IOCTL_GET_DESCRIPTOR; + data.descriptor.recipient = requesttype & 0x1F; + data.descriptor.type = (value >> 8) & 0xFF; + data.descriptor.index = value & 0xFF; + data.descriptor.language_id = index; + break; + case USB_REQ_SET_DESCRIPTOR: + ioControlCode = LIBUSB_IOCTL_SET_DESCRIPTOR; + data.descriptor.recipient = requesttype & 0x1F; + data.descriptor.type = (value >> 8) & 0xFF; + data.descriptor.index = value & 0xFF; + data.descriptor.language_id = index; + break; + case USB_REQ_GET_CONFIGURATION: + ioControlCode = LIBUSB_IOCTL_GET_CONFIGURATION; + break; + case USB_REQ_SET_CONFIGURATION: + data.configuration.configuration = value; + ioControlCode = LIBUSB_IOCTL_SET_CONFIGURATION; + break; + case USB_REQ_GET_INTERFACE: + ioControlCode = LIBUSB_IOCTL_GET_INTERFACE; + data.intf.interface_number = index; + break; + case USB_REQ_SET_INTERFACE: + ioControlCode = LIBUSB_IOCTL_SET_INTERFACE; + data.intf.interface_number = index; + data.intf.altsetting_number = value; + break; + default: + return E_INVALIDARG; + } + break; + case USB_TYPE_CLASS: + case USB_TYPE_VENDOR: + ioControlCode = (requesttype & 0x80) != 0 ? LIBUSB_IOCTL_VENDOR_READ : LIBUSB_IOCTL_VENDOR_WRITE; + data.vendor.type = (requesttype >> 5) & 0x03; + data.vendor.recipient = requesttype & 0x1F; + data.vendor.request = request; + data.vendor.value = value; + data.vendor.index = index; + break; + default: + return E_INVALIDARG; + } + + if ((requesttype & USB_ENDPOINT_IN) == 0) + { + if (bytesTransmitted != nullptr) + { + *bytesTransmitted = 0; + } + + if (bufferSize > 0) + { + std::vector out(sizeof(data) + bufferSize); + std::memcpy(out.data(), &data, sizeof(data)); + std::memcpy(out.data() + sizeof(data), buffer, bufferSize); + HRESULT hr = DeviceIoControl(ioControlCode, out.data(), sizeof(data) + bufferSize, nullptr, 0); + if (FAILED(hr)) + return hr; + + if (bytesTransmitted != nullptr) + { + *bytesTransmitted = bufferSize; + } + + return hr; + } + else + { + return DeviceIoControl(ioControlCode, &data, sizeof(data), nullptr, 0); + } + } + else + { + return DeviceIoControl(ioControlCode, &data, sizeof(data), buffer, bufferSize, bytesTransmitted); + } + } + + HRESULT SetConfiguration(uint8_t configuration) override + { + libusb_request data{}; + data.configuration.configuration = configuration; + data.timeout = LIBUSB_DEFAULT_TIMEOUT; + return DeviceIoControl(LIBUSB_IOCTL_SET_CONFIGURATION, &data, sizeof(data), nullptr, 0); + } + + HRESULT ClaimInterface(uint8_t interfaceIndex) override + { + libusb_request data{}; + data.intf.interface_number = interfaceIndex; + data.timeout = LIBUSB_DEFAULT_TIMEOUT; + return DeviceIoControl(LIBUSB_IOCTL_CLAIM_INTERFACE, &data, sizeof(data), nullptr, 0); + } + + HRESULT ReleaseInterface(uint8_t interfaceIndex) override + { + libusb_request data{}; + data.intf.interface_number = interfaceIndex; + data.timeout = LIBUSB_DEFAULT_TIMEOUT; + return DeviceIoControl(LIBUSB_IOCTL_RELEASE_INTERFACE, &data, sizeof(data), nullptr, 0); + } + + HRESULT SetAlternateSetting(uint8_t interfaceIndex, uint8_t alternate) override + { + libusb_request data{}; + data.intf.interface_number = interfaceIndex; + data.intf.altsetting_number = alternate; + data.timeout = LIBUSB_DEFAULT_TIMEOUT; + return DeviceIoControl(LIBUSB_IOCTL_SET_INTERFACE, &data, sizeof(data), nullptr, 0); + } + + HRESULT ReadPipe(uint8_t pipeId, void* buffer, uint32_t bufferSize, uint32_t* bytesRead, uint32_t timeout) override + { + return DeviceIoTransfer(LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ, pipeId, buffer, bufferSize, bytesRead, timeout); + } + + HRESULT WritePipe(uint8_t pipeId, void* buffer, uint32_t bufferSize, uint32_t* bytesWritten, uint32_t timeout) override + { + return DeviceIoTransfer(LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE, pipeId, buffer, bufferSize, bytesWritten, timeout); + } + + private: + static const uint32_t LIBUSB_DEFAULT_TIMEOUT = 5000; + + static const uint32_t LIBUSB_IOCTL_SET_CONFIGURATION = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_GET_CONFIGURATION = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_SET_INTERFACE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_GET_INTERFACE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_SET_FEATURE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_CLEAR_FEATURE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_GET_STATUS = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_SET_DESCRIPTOR = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_GET_DESCRIPTOR = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_IN_DIRECT, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80B, METHOD_OUT_DIRECT, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_VENDOR_WRITE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80C, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_VENDOR_READ = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80D, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_RESET_ENDPOINT = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80E, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_ABORT_ENDPOINT = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80F, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_RESET_DEVICE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x810, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_SET_DEBUG_LEVEL = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x811, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_GET_VERSION = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x812, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_ISOCHRONOUS_WRITE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x813, METHOD_IN_DIRECT, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_ISOCHRONOUS_READ = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x814, METHOD_OUT_DIRECT, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_CLAIM_INTERFACE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x815, METHOD_BUFFERED, FILE_ANY_ACCESS); + static const uint32_t LIBUSB_IOCTL_RELEASE_INTERFACE = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x816, METHOD_BUFFERED, FILE_ANY_ACCESS); + +#include + + struct interface_request + { + uint32_t interface_number; + uint32_t altsetting_number; + uint8_t intf_use_index : 1; + uint8_t altf_use_index : 1; + uint8_t : 6; + int16_t interface_index; + int16_t altsetting_index; + }; + + struct libusb_request + { + uint32_t timeout; + union + { + struct + { + uint32_t configuration; + } configuration; + interface_request intf; + struct + { + uint32_t endpoint; + uint32_t packet_size; + uint32_t max_transfer_size; + uint32_t transfer_flags; + uint32_t iso_start_frame_latency; + } endpoint; + struct + { + uint32_t type; + uint32_t recipient; + uint32_t request; + uint32_t value; + uint32_t index; + } vendor; + struct + { + uint32_t recipient; + uint32_t feature; + uint32_t index; + } feature; + struct + { + uint32_t recipient; + uint32_t index; + uint32_t status; + } status; + struct + { + uint32_t type; + uint32_t index; + uint32_t language_id; + uint32_t recipient; + } descriptor; + struct + { + uint32_t level; + } debug; + struct + { + uint32_t major; + uint32_t minor; + uint32_t micro; + uint32_t nano; + uint32_t mod_value; + } version; + struct + { + uint32_t property; + } device_property; + struct + { + uint32_t key_type; + uint32_t name_offset; + uint32_t value_offset; + uint32_t value_length; + } device_registry_key; + struct + { + uint32_t objname_index; + } objname; + struct + { + uint32_t information_type; + } query_device; + struct + { + uint32_t interface_index; + uint32_t pipe_id; + uint32_t policy_type; + } pipe_policy; + struct + { + uint32_t policy_type; + } power_policy; + struct + { + uint32_t reset_type; + } reset_ex; + struct + { + uint8_t request_type; + uint8_t request; + uint16_t value; + uint16_t index; + uint16_t length; + } control; + }; + }; + +#include + + private: + static const uint32_t maxReadWriteSize = 0x10000; + + HRESULT DeviceIoTransfer(uint32_t ioControlCode, uint8_t pipeId, void* buffer, uint32_t bufferSize, uint32_t* bytesTransferred, uint32_t timeout) + { + if (bytesTransferred != nullptr) + { + *bytesTransferred = 0; + } + + if (timeout == 0) + { + timeout = INFINITE; + } + + ManualResetEvent event; + HRESULT hr = event.Create(); + if (FAILED(hr)) + { + return hr; + } + + uint32_t transferred = 0; + while (bufferSize > 0) + { + uint32_t chunkSize = bufferSize > maxReadWriteSize ? maxReadWriteSize : bufferSize; + + libusb_request transfer{}; + transfer.endpoint.endpoint = pipeId; + transfer.endpoint.packet_size = 0; + + event.Reset(); + + OVERLAPPED overlapped{}; + overlapped.hEvent = event; + hr = DeviceIoControl(ioControlCode, &transfer, sizeof(transfer), buffer, chunkSize, nullptr, &overlapped); + if (FAILED(hr)) + { + return hr; + } + + if (WaitForSingleObject(overlapped.hEvent, timeout) != WAIT_OBJECT_0) + { + libusb_request cancel{}; + cancel.endpoint.endpoint = pipeId; + cancel.timeout = LIBUSB_DEFAULT_TIMEOUT; + DeviceIoControl(LIBUSB_IOCTL_ABORT_ENDPOINT, &cancel, sizeof(cancel), nullptr, 0); + return HRESULT_FROM_WIN32(ERROR_TIMEOUT); + } + + DWORD dwNumberOfBytesTransferred = 0; + if (!GetOverlappedResult(m_hDevice, &overlapped, &dwNumberOfBytesTransferred, TRUE)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + transferred += dwNumberOfBytesTransferred; + if (dwNumberOfBytesTransferred != chunkSize) + { + break; + } + + buffer = static_cast(buffer) + dwNumberOfBytesTransferred; + bufferSize -= dwNumberOfBytesTransferred; + } + + if (bytesTransferred != nullptr) + { + *bytesTransferred = transferred; + } + + return S_OK; + } + + HRESULT DeviceIoControl( + uint32_t ioControlCode, + void* lpInBuffer, + uint32_t inBufferSize, + void* pOutBuffer, + uint32_t outBufferSize, + uint32_t* bytesReturned = nullptr, + LPOVERLAPPED pOverlapped = nullptr) + { + if (bytesReturned != nullptr) + { + *bytesReturned = 0; + } + + if (pOverlapped == nullptr) + { + m_event.Reset(); + + OVERLAPPED overlapped{}; + overlapped.hEvent = m_event; + if (!::DeviceIoControl(m_hDevice, ioControlCode, lpInBuffer, inBufferSize, pOutBuffer, outBufferSize, nullptr, &overlapped)) + { + DWORD dwError = GetLastError(); + if (dwError != ERROR_IO_PENDING) + { + return HRESULT_FROM_WIN32(dwError); + } + } + + DWORD dwNumberOfBytesTransferred = 0; + if (!::GetOverlappedResult(m_hDevice, &overlapped, &dwNumberOfBytesTransferred, TRUE)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (bytesReturned != nullptr) + { + *bytesReturned = dwNumberOfBytesTransferred; + } + } + else + { + DWORD dwBytesReturned = 0; + if (!::DeviceIoControl(m_hDevice, ioControlCode, lpInBuffer, inBufferSize, pOutBuffer, outBufferSize, &dwBytesReturned, pOverlapped)) + { + DWORD dwError = GetLastError(); + if (dwError != ERROR_IO_PENDING) + { + return HRESULT_FROM_WIN32(dwError); + } + } + + if (bytesReturned != nullptr) + { + *bytesReturned = dwBytesReturned; + } + } + + return S_OK; + } + + private: + ManualResetEvent m_event; + }; + + class UsbDevice + { + friend class UsbDeviceEnumerator; + + public: + UsbDevice() = default; + UsbDevice(const UsbDevice&) = delete; + UsbDevice& operator=(const UsbDevice& other) = delete; + UsbDevice(UsbDevice&&) = delete; + UsbDevice& operator=(UsbDevice&& other) = delete; + + public: + std::wstring GetInstanceId() const { return m_instanceId; } + std::wstring GetInterfacePath() const { return m_interfacePath; } + std::wstring GetService() const { return m_service; } + + public: + HRESULT Open(std::unique_ptr& driver) + { + return Open(m_service, m_interfacePath, driver); + } + + static HRESULT Open(const std::wstring& service, const std::wstring& interfacePath, std::unique_ptr& driver) + { + if (_wcsicmp(service.c_str(), L"WINUSB") == 0) + { + driver = std::make_unique(); + } + else if (_wcsicmp(service.c_str(), L"libusb0") == 0 || _wcsicmp(service.c_str(), L"libusbK") == 0) + { + driver = std::make_unique(); + } + else + { + return E_NOTIMPL; + } + + return driver->Open(interfacePath.c_str()); + } + + private: + std::wstring m_instanceId; + std::wstring m_interfacePath; + std::wstring m_service; + }; + + class UsbDeviceEnumerator + { + public: + UsbDeviceEnumerator() = default; + UsbDeviceEnumerator(const UsbDeviceEnumerator&) = delete; + UsbDeviceEnumerator& operator=(const UsbDeviceEnumerator& other) = delete; + UsbDeviceEnumerator(UsbDeviceEnumerator&&) = delete; + UsbDeviceEnumerator& operator=(UsbDeviceEnumerator&& other) = delete; + + public: + std::vector>& GetDevices() { return m_devices; } + + public: + HRESULT EnumerateDevices() + { + DeviceInfoSet deviceInfoSet; + HRESULT hr = deviceInfoSet.GetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (FAILED(hr)) + return hr; + + for (DWORD index = 0; ; index++) + { + DeviceInfo deviceInfo; + if (!SetupDiEnumDeviceInfo(deviceInfoSet, index, &deviceInfo)) + { + DWORD dwError = GetLastError(); + if (dwError != ERROR_NO_MORE_ITEMS) + { + return HRESULT_FROM_WIN32(dwError); + } + + break; + } + + SP_DEVICE_INTERFACE_DATA deviceInterface = { sizeof(SP_DEVICE_INTERFACE_DATA) }; + if (!SetupDiEnumDeviceInterfaces(deviceInfoSet, &deviceInfo, &GUID_DEVINTERFACE_USB_DEVICE, 0, &deviceInterface)) + { + DWORD dwError = GetLastError(); + if (dwError != ERROR_NO_MORE_ITEMS) + { + return HRESULT_FROM_WIN32(dwError); + } + } + else + { + auto device = std::make_unique(); + + uint32_t installState = 0; + if (SUCCEEDED(deviceInfo.GetProperty(deviceInfoSet, &DEVPKEY_Device_InstanceId, device->m_instanceId)) && + SUCCEEDED(deviceInfo.GetProperty(deviceInfoSet, &DEVPKEY_Device_InstallState, installState)) && + SUCCEEDED(deviceInfo.GetProperty(deviceInfoSet, &DEVPKEY_Device_Service, device->m_service)) && + SUCCEEDED(GetDeviceInterfacePath(deviceInfoSet, &deviceInterface, device->m_interfacePath)) && + installState == 0) + { + m_devices.push_back(std::move(device)); + } + } + } + + return S_OK; + } + + HRESULT GetDeviceInterfacePath(HDEVINFO hDeviceInfoSet, PSP_DEVICE_INTERFACE_DATA pDeviceInterfaceData, std::wstring& deviceInterfacePath) + { + DWORD dwRequiredSize = 0; + if (!SetupDiGetDeviceInterfaceDetailW(hDeviceInfoSet, pDeviceInterfaceData, nullptr, 0, &dwRequiredSize, nullptr)) + { + DWORD dwError = GetLastError(); + if (dwError != ERROR_INSUFFICIENT_BUFFER) + { + return HRESULT_FROM_WIN32(dwError); + } + } + + std::vector buffer(dwRequiredSize); + SP_DEVICE_INTERFACE_DETAIL_DATA_W* pDeviceInterfaceDetailData = reinterpret_cast(buffer.data()); + pDeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); + + if (!SetupDiGetDeviceInterfaceDetailW(hDeviceInfoSet, pDeviceInterfaceData, pDeviceInterfaceDetailData, dwRequiredSize, &dwRequiredSize, nullptr)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + deviceInterfacePath = pDeviceInterfaceDetailData->DevicePath; + return S_OK; + } + + private: + std::vector> m_devices; + }; +} diff --git a/external/libusb/src/windows.c b/external/libusb/src/windows.c deleted file mode 100644 index b9a79a07..00000000 --- a/external/libusb/src/windows.c +++ /dev/null @@ -1,1281 +0,0 @@ -/* libusb-win32, Generic Windows USB Library - * Copyright (c) 2002-2005 Stephan Meyer - * Copyright (c) 2000-2005 Johannes Erdfelt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lusb0_usb.h" -#include "error.h" -#include "usbi.h" -#include "driver_api.h" -#include "registry.h" -#include "libusb-win32_version.h" - -#define LIBUSB_WIN32_DLL_LARGE_TRANSFER_SUPPORT - -#define LIBUSB_DEFAULT_TIMEOUT 5000 -#define LIBUSB_DEVICE_NAME "\\\\.\\libusb0-" -#define LIBUSB_BUS_NAME "bus-0" -#define LIBUSB_MAX_DEVICES 256 - -typedef struct -{ - usb_dev_handle *dev; - libusb_request req; - char *bytes; - int size; - DWORD control_code; - OVERLAPPED ol; -} usb_context_t; - - -static struct usb_version _usb_version = -{ - { VERSION_MAJOR, - VERSION_MINOR, - VERSION_MICRO, - VERSION_NANO }, - { -1, -1, -1, -1 } -}; - - -static int _usb_setup_async(usb_dev_handle *dev, void **context, - DWORD control_code, - unsigned char ep, int pktsize); -static int _usb_transfer_sync(usb_dev_handle *dev, int control_code, - int ep, int pktsize, char *bytes, int size, - int timeout); - -static int usb_get_configuration(usb_dev_handle *dev, bool_t cached); -static int _usb_cancel_io(usb_context_t *context); -static int _usb_abort_ep(usb_dev_handle *dev, unsigned int ep); - -static int _usb_io_sync(HANDLE dev, unsigned int code, void *in, int in_size, - void *out, int out_size, int *ret); -static int _usb_reap_async(void *context, int timeout, int cancel); -static int _usb_add_virtual_hub(struct usb_bus *bus); - -static void _usb_free_bus_list(struct usb_bus *bus); -static void _usb_free_dev_list(struct usb_device *dev); -static void _usb_deinit(void); - -/* DLL main entry point */ -BOOL WINAPI DllMain(HANDLE module, DWORD reason, LPVOID reserved) -{ - switch (reason) - { - case DLL_PROCESS_ATTACH: - break; - case DLL_PROCESS_DETACH: - _usb_deinit(); - break; - case DLL_THREAD_ATTACH: - break; - case DLL_THREAD_DETACH: - break; - default: - break; - } - return TRUE; -} - - -static int usb_get_configuration(usb_dev_handle *dev, bool_t cached) -{ - int ret; - char config; - libusb_request request; - - if (cached) - { - memset(&request, 0, sizeof(request)); - request.timeout = LIBUSB_DEFAULT_TIMEOUT; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_GET_CACHED_CONFIGURATION, - &request, sizeof(request), &request, sizeof(request), &ret)) - { - USBERR("sending get cached configuration ioctl failed, win error: %s\n", usb_win_error_to_string()); - ret = -usb_win_error_to_errno(); - } - - if (ret < 1) - ret = -EINVAL; - else - config = *((char*)&request); - } - else - { - ret = usb_control_msg(dev, USB_RECIP_DEVICE | USB_ENDPOINT_IN, - USB_REQ_GET_CONFIGURATION, 0, 0, &config, 1, - LIBUSB_DEFAULT_TIMEOUT); - } - - if(ret < 0) - return ret; - - return config; -} - -int usb_os_open(usb_dev_handle *dev) -{ - char dev_name[LIBUSB_PATH_MAX]; - char *p; - int config; - if (!dev) - { - USBERR("invalid device handle %p", dev); - return -EINVAL; - } - - dev->impl_info = INVALID_HANDLE_VALUE; - dev->config = 0; - dev->interface = -1; - dev->altsetting = -1; - - if (!dev->device->filename) - { - USBERR0("invalid file name\n"); - return -ENOENT; - } - - /* build the Windows file name from the unique device name */ - strcpy(dev_name, dev->device->filename); - - p = strstr(dev_name, "--"); - - if (!p) - { - USBERR("invalid file name %s\n", dev->device->filename); - return -ENOENT; - } - - *p = 0; - - dev->impl_info = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR("failed to open %s: win error: %s", - dev->device->filename, usb_win_error_to_string()); - return -ENOENT; - } - - // get the cached configuration (no device i/o) - config = usb_get_configuration(dev, TRUE); - if (config > 0) - { - dev->config = config; - dev->interface = -1; - dev->altsetting = -1; - } - - return 0; -} - -int usb_os_close(usb_dev_handle *dev) -{ - if (dev->impl_info != INVALID_HANDLE_VALUE) - { - if (dev->interface >= 0) - { - usb_release_interface(dev, dev->interface); - } - - CloseHandle(dev->impl_info); - dev->impl_info = INVALID_HANDLE_VALUE; - dev->interface = -1; - dev->altsetting = -1; - } - - return 0; -} - -int usb_set_configuration(usb_dev_handle *dev, int configuration) -{ - libusb_request req; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("error: device not open\n"); - return -EINVAL; - } - - if (dev->config == configuration) - { - return 0; - } - - if (dev->interface >= 0) - { - USBERR0("can't change configuration, an interface is still in use (claimed)\n"); - return -EINVAL; - } - - req.configuration.configuration = configuration; - req.timeout = LIBUSB_DEFAULT_TIMEOUT; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_SET_CONFIGURATION, - &req, sizeof(libusb_request), NULL, 0, NULL)) - { - USBERR("could not set config %d: " - "win error: %s", configuration, usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - dev->config = configuration; - dev->interface = -1; - dev->altsetting = -1; - - return 0; -} - -int usb_claim_interface(usb_dev_handle *dev, int interface) -{ - libusb_request req; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - if (!dev->config) - { - USBERR("could not claim interface %d, invalid configuration %d\n", interface, dev->config); - return -EINVAL; - } - - if (dev->interface == interface) - { - return 0; - } - - req.intf.interface_number = interface; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_CLAIM_INTERFACE, - &req, sizeof(libusb_request), NULL, 0, NULL)) - { - USBERR("could not claim interface %d, " - "win error: %s", interface, usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - else - { - dev->interface = interface; - dev->altsetting = 0; - return 0; - } -} - -int usb_release_interface(usb_dev_handle *dev, int interface) -{ - libusb_request req; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - if (!dev->config) - { - USBERR("could not release interface %d, invalid configuration %d\n", interface, dev->config); - return -EINVAL; - } - - req.intf.interface_number = interface; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RELEASE_INTERFACE, - &req, sizeof(libusb_request), NULL, 0, NULL)) - { - USBERR("could not release interface %d, " - "win error: %s", interface, usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - else - { - dev->interface = -1; - dev->altsetting = -1; - - return 0; - } -} - -int usb_set_altinterface(usb_dev_handle *dev, int alternate) -{ - libusb_request req; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - if (dev->config <= 0) - { - USBERR("could not set alt interface %d: invalid configuration %d\n", alternate, dev->config); - return -EINVAL; - } - - if (dev->interface < 0) - { - USBERR("could not set alt interface %d: no interface claimed\n", alternate); - return -EINVAL; - } - - req.intf.interface_number = dev->interface; - req.intf.altsetting_number = alternate; - req.timeout = LIBUSB_DEFAULT_TIMEOUT; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_SET_INTERFACE, - &req, sizeof(libusb_request), - NULL, 0, NULL)) - { - USBERR("could not set alt interface " - "%d/%d: win error: %s", - dev->interface, alternate, usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - dev->altsetting = alternate; - - return 0; -} - -static int _usb_setup_async(usb_dev_handle *dev, void **context, - DWORD control_code, - unsigned char ep, int pktsize) -{ - usb_context_t **c = (usb_context_t **)context; - - if (((control_code == LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE) - || (control_code == LIBUSB_IOCTL_ISOCHRONOUS_WRITE)) - && (ep & USB_ENDPOINT_IN)) - { - USBERR("invalid endpoint 0x%02x", ep); - return -EINVAL; - } - - if (((control_code == LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ) - || (control_code == LIBUSB_IOCTL_ISOCHRONOUS_READ)) - && !(ep & USB_ENDPOINT_IN)) - { - USBERR("invalid endpoint 0x%02x\n", ep); - return -EINVAL; - } - - *c = malloc(sizeof(usb_context_t)); - - if (!*c) - { - USBERR0("memory allocation error\n"); - return -ENOMEM; - } - - memset(*c, 0, sizeof(usb_context_t)); - - (*c)->dev = dev; - (*c)->req.endpoint.endpoint = ep; - (*c)->req.endpoint.packet_size = pktsize; - (*c)->control_code = control_code; - - (*c)->ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - if (!(*c)->ol.hEvent) - { - free(*c); - *c = NULL; - USBERR("creating event failed: win error: %s", - usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - return 0; -} - -int usb_submit_async(void *context, char *bytes, int size) -{ - usb_context_t *c = (usb_context_t *)context; - - if (!c) - { - USBERR0("invalid context"); - return -EINVAL; - } - - if (c->dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - if (c->dev->config <= 0) - { - USBERR("invalid configuration %d\n", c->dev->config); - return -EINVAL; - } - - if (c->dev->interface < 0) - { - USBERR("invalid interface %d\n", c->dev->interface); - return -EINVAL; - } - - - c->ol.Offset = 0; - c->ol.OffsetHigh = 0; - c->bytes = bytes; - c->size = size; - - ResetEvent(c->ol.hEvent); - - if (!DeviceIoControl(c->dev->impl_info, - c->control_code, - &c->req, sizeof(libusb_request), - c->bytes, - c->size, NULL, &c->ol)) - { - if (GetLastError() != ERROR_IO_PENDING) - { - USBERR("submitting request failed, " - "win error: %s", usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - } - - return 0; -} - -static int _usb_reap_async(void *context, int timeout, int cancel) -{ - usb_context_t *c = (usb_context_t *)context; - ULONG ret = 0; - - if (!c) - { - USBERR0("invalid context\n"); - return -EINVAL; - } - - if (WaitForSingleObject(c->ol.hEvent, timeout) == WAIT_TIMEOUT) - { - /* request timed out */ - if (cancel) - { - _usb_cancel_io(c); - } - - USBERR0("timeout error\n"); - return -ETRANSFER_TIMEDOUT; - } - - if (!GetOverlappedResult(c->dev->impl_info, &c->ol, &ret, TRUE)) - { - USBERR("reaping request failed, win error: %s\n",usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - return ret; -} - -int usb_reap_async(void *context, int timeout) -{ - return _usb_reap_async(context, timeout, TRUE); -} - -int usb_reap_async_nocancel(void *context, int timeout) -{ - return _usb_reap_async(context, timeout, FALSE); -} - - -int usb_cancel_async(void *context) -{ - /* NOTE that this function will cancel all pending URBs */ - /* on the same endpoint as this particular context, or even */ - /* all pending URBs for this particular device. */ - - usb_context_t *c = (usb_context_t *)context; - - if (!c) - { - USBERR0("invalid context\n"); - return -EINVAL; - } - - if (c->dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - _usb_cancel_io(c); - - return 0; -} - -int usb_free_async(void **context) -{ - usb_context_t **c = (usb_context_t **)context; - - if (!*c) - { - USBERR0("invalid context\n"); - return -EINVAL; - } - - CloseHandle((*c)->ol.hEvent); - - free(*c); - *c = NULL; - - return 0; -} - -static int _usb_transfer_sync(usb_dev_handle *dev, int control_code, - int ep, int pktsize, char *bytes, int size, - int timeout) -{ - void *context = NULL; - int transmitted = 0; - int ret; - int requested; - - if (!timeout) timeout=INFINITE; - ret = _usb_setup_async(dev, &context, control_code, (unsigned char )ep, - pktsize); - - if (ret < 0) - { - return ret; - } - - do - { -#ifdef LIBUSB_WIN32_DLL_LARGE_TRANSFER_SUPPORT - requested = size > LIBUSB_MAX_READ_WRITE ? LIBUSB_MAX_READ_WRITE : size; -#else - requested = size; -#endif - ret = usb_submit_async(context, bytes, requested); - - if (ret < 0) - { - transmitted = ret; - break; - } - - ret = usb_reap_async(context, timeout); - - if (ret < 0) - { - transmitted = ret; - break; - } - - transmitted += ret; - bytes += ret; - size -= ret; - } - while (size > 0 && ret == requested); - - usb_free_async(&context); - - return transmitted; -} - -int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE, - ep, 0, bytes, size, timeout); -} - -int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ, - ep, 0, bytes, size, timeout); -} - -int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE, - ep, 0, bytes, size, timeout); -} - -int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ, - ep, 0, bytes, size, timeout); -} - -int usb_isochronous_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep, int pktsize) -{ - if (ep & 0x80) - return _usb_setup_async(dev, context, LIBUSB_IOCTL_ISOCHRONOUS_READ, - ep, pktsize); - else - return _usb_setup_async(dev, context, LIBUSB_IOCTL_ISOCHRONOUS_WRITE, - ep, pktsize); -} - -int usb_bulk_setup_async(usb_dev_handle *dev, void **context, unsigned char ep) -{ - if (ep & 0x80) - return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ, - ep, 0); - else - return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE, - ep, 0); -} - -int usb_interrupt_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep) -{ - if (ep & 0x80) - return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ, - ep, 0); - else - return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE, - ep, 0); -} - -int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, - int value, int index, char *bytes, int size, int timeout) -{ - int read = 0; - libusb_request req; - void *out = &req; - int out_size = sizeof(libusb_request); - void *in = bytes; - int in_size = size; - int code; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - req.timeout = timeout; - - /* windows doesn't support generic control messages, so it needs to be */ - /* split up */ - switch (requesttype & (0x03 << 5)) - { - case USB_TYPE_STANDARD: - switch (request) - { - case USB_REQ_GET_STATUS: - req.status.recipient = requesttype & 0x1F; - req.status.index = index; - code = LIBUSB_IOCTL_GET_STATUS; - break; - - case USB_REQ_CLEAR_FEATURE: - req.feature.recipient = requesttype & 0x1F; - req.feature.feature = value; - req.feature.index = index; - code = LIBUSB_IOCTL_CLEAR_FEATURE; - break; - - case USB_REQ_SET_FEATURE: - req.feature.recipient = requesttype & 0x1F; - req.feature.feature = value; - req.feature.index = index; - code = LIBUSB_IOCTL_SET_FEATURE; - break; - - case USB_REQ_GET_DESCRIPTOR: - req.descriptor.recipient = requesttype & 0x1F; - req.descriptor.type = (value >> 8) & 0xFF; - req.descriptor.index = value & 0xFF; - req.descriptor.language_id = index; - code = LIBUSB_IOCTL_GET_DESCRIPTOR; - break; - - case USB_REQ_SET_DESCRIPTOR: - req.descriptor.recipient = requesttype & 0x1F; - req.descriptor.type = (value >> 8) & 0xFF; - req.descriptor.index = value & 0xFF; - req.descriptor.language_id = index; - code = LIBUSB_IOCTL_SET_DESCRIPTOR; - break; - - case USB_REQ_GET_CONFIGURATION: - code = LIBUSB_IOCTL_GET_CONFIGURATION; - break; - - case USB_REQ_SET_CONFIGURATION: - req.configuration.configuration = value; - code = LIBUSB_IOCTL_SET_CONFIGURATION; - break; - - case USB_REQ_GET_INTERFACE: - req.intf.interface_number = index; - code = LIBUSB_IOCTL_GET_INTERFACE; - break; - - case USB_REQ_SET_INTERFACE: - req.intf.interface_number = index; - req.intf.altsetting_number = value; - code = LIBUSB_IOCTL_SET_INTERFACE; - break; - - default: - USBERR("invalid request 0x%x", request); - return -EINVAL; - } - break; - - case USB_TYPE_VENDOR: - case USB_TYPE_CLASS: - - req.vendor.type = (requesttype >> 5) & 0x03; - req.vendor.recipient = requesttype & 0x1F; - req.vendor.request = request; - req.vendor.value = value; - req.vendor.index = index; - - if (requesttype & 0x80) - code = LIBUSB_IOCTL_VENDOR_READ; - else - code = LIBUSB_IOCTL_VENDOR_WRITE; - break; - - case USB_TYPE_RESERVED: - default: - USBERR("invalid or unsupported request type: %x", - requesttype); - return -EINVAL; - } - - /* out request? */ - if (!(requesttype & USB_ENDPOINT_IN)) - { - if (!(out = malloc(sizeof(libusb_request) + size))) - { - USBERR0("memory allocation failed\n"); - return -ENOMEM; - } - - memcpy(out, &req, sizeof(libusb_request)); - memcpy((char *)out + sizeof(libusb_request), bytes, size); - out_size = sizeof(libusb_request) + size; - in = NULL; - in_size = 0; - } - - if (!_usb_io_sync(dev->impl_info, code, out, out_size, in, in_size, &read)) - { - USBERR("sending control message failed, win error: %s\n", usb_win_error_to_string()); - if (!(requesttype & USB_ENDPOINT_IN)) - { - free(out); - } - return -usb_win_error_to_errno(); - } - - /* out request? */ - if (!(requesttype & USB_ENDPOINT_IN)) - { - free(out); - return size; - } - else - return read; -} - - -int usb_os_find_busses(struct usb_bus **busses) -{ - struct usb_bus *bus = NULL; - - /* create one 'virtual' bus */ - - bus = malloc(sizeof(struct usb_bus)); - - if (!bus) - { - USBERR0("memory allocation failed\n"); - return -ENOMEM; - } - - memset(bus, 0, sizeof(*bus)); - strcpy(bus->dirname, LIBUSB_BUS_NAME); - - USBMSG("found %s\n", bus->dirname); - - *busses = bus; - - return 0; -} - -int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices) -{ - int i; - struct usb_device *dev, *fdev = NULL; - char dev_name[LIBUSB_PATH_MAX]; - int ret; - HANDLE handle; - libusb_request req; - - for (i = 1; i < LIBUSB_MAX_DEVICES; i++) - { - ret = 0; - - _snprintf(dev_name, sizeof(dev_name) - 1,"%s%04d", - LIBUSB_DEVICE_NAME, i); - - if (!(dev = malloc(sizeof(*dev)))) - { - USBERR0("memory allocation failed\n"); - return -ENOMEM; - } - - memset(dev, 0, sizeof(*dev)); - dev->bus = bus; - dev->devnum = (unsigned char)i; - - handle = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - - if (handle == INVALID_HANDLE_VALUE) - { - free(dev); - continue; - } - - /* retrieve device descriptor */ - req.descriptor.type = USB_DT_DEVICE; - req.descriptor.recipient = USB_RECIP_DEVICE; - req.descriptor.index = 0; - req.descriptor.language_id = 0; - req.timeout = LIBUSB_DEFAULT_TIMEOUT; - - _usb_io_sync(handle, LIBUSB_IOCTL_GET_DESCRIPTOR, - &req, sizeof(libusb_request), - &dev->descriptor, USB_DT_DEVICE_SIZE, &ret); - - if (ret < USB_DT_DEVICE_SIZE) - { - USBERR0("couldn't read device descriptor\n"); - free(dev); - CloseHandle(handle); - continue; - } - - _snprintf(dev->filename, LIBUSB_PATH_MAX - 1, "%s--0x%04x-0x%04x", - dev_name, dev->descriptor.idVendor, dev->descriptor.idProduct); - - CloseHandle(handle); - - LIST_ADD(fdev, dev); - - USBMSG("found %s on %s\n", dev->filename, bus->dirname); - } - - *devices = fdev; - - return 0; -} - - -void usb_os_init(void) -{ - HANDLE dev; - libusb_request req; - int i; - int ret; - char dev_name[LIBUSB_PATH_MAX]; - - USBMSG("dll version: %d.%d.%d.%d\n", - VERSION_MAJOR, VERSION_MINOR, - VERSION_MICRO, VERSION_NANO); - - - for (i = 1; i < LIBUSB_MAX_DEVICES; i++) - { - /* build the Windows file name */ - _snprintf(dev_name, sizeof(dev_name) - 1,"%s%04d", - LIBUSB_DEVICE_NAME, i); - - dev = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - - if (dev == INVALID_HANDLE_VALUE) - { - continue; - } - - if (!_usb_io_sync(dev, LIBUSB_IOCTL_GET_VERSION, - &req, sizeof(libusb_request), - &req, sizeof(libusb_request), &ret) - || (ret < sizeof(libusb_request))) - { - USBERR0("getting driver version failed\n"); - CloseHandle(dev); - continue; - } - else - { - _usb_version.driver.major = req.version.major; - _usb_version.driver.minor = req.version.minor; - _usb_version.driver.micro = req.version.micro; - _usb_version.driver.nano = req.version.nano; - - USBMSG("driver version: %d.%d.%d.%d\n", - req.version.major, req.version.minor, - req.version.micro, req.version.nano); - - /* set debug level */ - req.timeout = 0; - req.debug.level = usb_log_get_level(); - - if (!_usb_io_sync(dev, LIBUSB_IOCTL_SET_DEBUG_LEVEL, - &req, sizeof(libusb_request), - NULL, 0, NULL)) - { - USBERR0("setting debug level failed"); - } - - CloseHandle(dev); - break; - } - } -} - - -int usb_resetep(usb_dev_handle *dev, unsigned int ep) -{ - libusb_request req; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - req.endpoint.endpoint = (int)ep; - req.timeout = LIBUSB_DEFAULT_TIMEOUT; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_ABORT_ENDPOINT, &req, - sizeof(libusb_request), NULL, 0, NULL)) - { - USBERR("could not abort ep 0x%02x, win error: %s\n", ep, usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_ENDPOINT, &req, - sizeof(libusb_request), NULL, 0, NULL)) - { - USBERR("could not reset ep 0x%02x, win error: %s\n", ep, usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - return 0; -} - -int usb_clear_halt(usb_dev_handle *dev, unsigned int ep) -{ - libusb_request req; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - req.endpoint.endpoint = (int)ep; - req.timeout = LIBUSB_DEFAULT_TIMEOUT; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_ENDPOINT, &req, - sizeof(libusb_request), NULL, 0, NULL)) - { - USBERR("could not clear halt, ep 0x%02x, " - "win error: %s", ep, usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - return 0; -} - -int usb_reset(usb_dev_handle *dev) -{ - libusb_request req; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - req.timeout = LIBUSB_DEFAULT_TIMEOUT; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_DEVICE, - &req, sizeof(libusb_request), NULL, 0, NULL)) - { - USBERR("could not reset device, win error: %s\n", usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - return 0; -} - -int usb_reset_ex(usb_dev_handle *dev, unsigned int reset_type) -{ - libusb_request req; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open\n"); - return -EINVAL; - } - - req.timeout = LIBUSB_DEFAULT_TIMEOUT; - req.reset_ex.reset_type = reset_type; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_DEVICE_EX, - &req, sizeof(libusb_request), NULL, 0, NULL)) - { - USBERR("could not reset device, win error: %s\n", usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - return 0; -} - -const struct usb_version *usb_get_version(void) -{ - return &_usb_version; -} - -void usb_set_debug(int level) -{ - HANDLE dev; - libusb_request req; - int i; - char dev_name[LIBUSB_PATH_MAX]; - - if (usb_log_get_level() || level) - { - USBMSG("setting debugging level to %d (%s)\n", - level, level ? "on" : "off"); - } - - usb_log_set_level(level); - - /* find a valid device */ - for (i = 1; i < LIBUSB_MAX_DEVICES; i++) - { - /* build the Windows file name */ - _snprintf(dev_name, sizeof(dev_name) - 1,"%s%04d", - LIBUSB_DEVICE_NAME, i); - - dev = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - - if (dev == INVALID_HANDLE_VALUE) - { - continue; - } - - /* set debug level */ - req.timeout = 0; - req.debug.level = usb_log_get_level(); - - if (!_usb_io_sync(dev, LIBUSB_IOCTL_SET_DEBUG_LEVEL, - &req, sizeof(libusb_request), - NULL, 0, NULL)) - { - USBERR0("setting debug level failed\n"); - } - - CloseHandle(dev); - - break; - } -} - -int usb_os_determine_children(struct usb_bus *bus) -{ - struct usb_device *dev; - int i = 0; - - /* add a virtual hub to the bus to emulate this feature */ - if (_usb_add_virtual_hub(bus)) - { - if (bus->root_dev->children) - { - free(bus->root_dev->children); - } - - bus->root_dev->num_children = 0; - for (dev = bus->devices; dev; dev = dev->next) - bus->root_dev->num_children++; - - bus->root_dev->children - = malloc(sizeof(struct usb_device *) * bus->root_dev->num_children); - - for (dev = bus->devices; dev; dev = dev->next) - bus->root_dev->children[i++] = dev; - } - - return 0; -} - -static int _usb_cancel_io(usb_context_t *context) -{ - int ret; - ret = _usb_abort_ep(context->dev, context->req.endpoint.endpoint); - WaitForSingleObject(context->ol.hEvent, 0); - return ret; -} - -static int _usb_abort_ep(usb_dev_handle *dev, unsigned int ep) -{ - libusb_request req; - - if (dev->impl_info == INVALID_HANDLE_VALUE) - { - USBERR0("device not open"); - return -EINVAL; - } - - req.endpoint.endpoint = (int)ep; - req.timeout = LIBUSB_DEFAULT_TIMEOUT; - - if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_ABORT_ENDPOINT, &req, - sizeof(libusb_request), NULL, 0, NULL)) - { - USBERR("could not abort ep 0x%02x, win error: %s\n", ep, usb_win_error_to_string()); - return -usb_win_error_to_errno(); - } - - return 0; -} - -static int _usb_io_sync(HANDLE dev, unsigned int code, void *out, int out_size, - void *in, int in_size, int *ret) -{ - OVERLAPPED ol; - DWORD _ret; - - memset(&ol, 0, sizeof(ol)); - - if (ret) - *ret = 0; - - ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - if (!ol.hEvent) - return FALSE; - - if (!DeviceIoControl(dev, code, out, out_size, in, in_size, NULL, &ol)) - { - if (GetLastError() != ERROR_IO_PENDING) - { - CloseHandle(ol.hEvent); - return FALSE; - } - } - - if (GetOverlappedResult(dev, &ol, &_ret, TRUE)) - { - if (ret) - *ret = (int)_ret; - CloseHandle(ol.hEvent); - return TRUE; - } - - CloseHandle(ol.hEvent); - return FALSE; -} - -static int _usb_add_virtual_hub(struct usb_bus *bus) -{ - struct usb_device *dev; - - if (!bus->root_dev) - { - if (!(dev = malloc(sizeof(*dev)))) - return FALSE; - - memset(dev, 0, sizeof(*dev)); - strcpy(dev->filename, "virtual-hub"); - dev->bus = bus; - - dev->descriptor.bLength = USB_DT_DEVICE_SIZE; - dev->descriptor.bDescriptorType = USB_DT_DEVICE; - dev->descriptor.bcdUSB = 0x0200; - dev->descriptor.bDeviceClass = USB_CLASS_HUB; - dev->descriptor.bDeviceSubClass = 0; - dev->descriptor.bDeviceProtocol = 0; - dev->descriptor.bMaxPacketSize0 = 64; - dev->descriptor.idVendor = 0; - dev->descriptor.idProduct = 0; - dev->descriptor.bcdDevice = 0x100; - dev->descriptor.iManufacturer = 0; - dev->descriptor.iProduct = 0; - dev->descriptor.iSerialNumber = 0; - dev->descriptor.bNumConfigurations = 0; - - bus->root_dev = dev; - } - - return TRUE; -} - -static void _usb_free_bus_list(struct usb_bus *bus) -{ - if (bus) - { - _usb_free_bus_list(bus->next); - if (bus->root_dev) - usb_free_dev(bus->root_dev); - _usb_free_dev_list(bus->devices); - usb_free_bus(bus); - } -} - -static void _usb_free_dev_list(struct usb_device *dev) -{ - if (dev) - { - _usb_free_dev_list(dev->next); - usb_free_dev(dev); - } -} - -static void _usb_deinit(void) -{ - _usb_free_bus_list(usb_get_busses()); -} diff --git a/external/libusb/src/windows.cpp b/external/libusb/src/windows.cpp new file mode 100644 index 00000000..2ac8b705 --- /dev/null +++ b/external/libusb/src/windows.cpp @@ -0,0 +1,470 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2019 Marius Greuel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "libwinusb.h" + +extern "C" +{ +#include "error.h" +#include "usbi.h" +} + +static std::string CreateMessageFromError(HRESULT hr) +{ + LPSTR pszMessage = nullptr; + DWORD dwChars = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, hr, 0, (LPSTR)&pszMessage, 0, nullptr); + if (dwChars > 0 && pszMessage != nullptr) + { + if (dwChars > 0 && pszMessage[dwChars - 1] == '\n') + dwChars--; + + if (dwChars > 0 && pszMessage[dwChars - 1] == '\r') + dwChars--; + + std::string message = std::string(pszMessage, dwChars); + LocalFree(pszMessage); + return message; + } + else + { + return "Unknown error"; + } +} + +static int usb_hresult_to_errno(HRESULT hr) +{ + if (SUCCEEDED(hr)) + return 0; + + switch (hr) + { + case HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER): + return EINVAL; + case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY): + return ENOMEM; + case HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED): + return EPERM; + case HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE): + case HRESULT_FROM_WIN32(ERROR_BAD_DEVICE): + case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): + return ENODEV; + case HRESULT_FROM_WIN32(ERROR_SEM_TIMEOUT): + case HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED): + return ETIMEDOUT; + default: + return EIO; + } +} + +void usb_os_init(void) +{ +} + +void usb_set_debug(int level) +{ + if (usb_log_get_level() || level) + { + USBMSG("setting debugging level to %d (%s)\n", level, level ? "on" : "off"); + } + + usb_log_set_level(static_cast(level)); +} + +int usb_os_find_busses(struct usb_bus** busses) +{ + auto bus = static_cast(std::malloc(sizeof(struct usb_bus))); + if (bus == nullptr) + { + USBERR("memory allocation failed\n"); + return -ENOMEM; + } + + std::memset(bus, 0, sizeof(*bus)); + std::strcpy(bus->dirname, "bus-0"); + + USBMSG("found %s\n", bus->dirname); + + *busses = bus; + return 0; +} + +int usb_os_find_devices(struct usb_bus* bus, struct usb_device** devices) +{ + if (bus == nullptr) + { + return -EINVAL; + } + + if (devices == nullptr) + { + return -EINVAL; + } + + LibWinUsb::UsbDeviceEnumerator enumerator; + HRESULT hr = enumerator.EnumerateDevices(); + if (FAILED(hr)) + { + return -1; + } + + struct usb_device* fdev = nullptr; + + const auto& list = enumerator.GetDevices(); + for (const auto& item : list) + { + struct usb_device* dev = static_cast(std::malloc(sizeof(struct usb_device))); + if (dev == nullptr) + { + USBERR0("memory allocation failed\n"); + return -ENOMEM; + } + + std::memset(dev, 0, sizeof(*dev)); + + std::unique_ptr driver; + if (FAILED(item->Open(driver)) || + FAILED(driver->GetDescriptor(USB_DT_DEVICE, 0, 0, &dev->descriptor, sizeof(dev->descriptor), nullptr, 5000))) + { + USBERR0("couldn't read device descriptor\n"); + free(dev); + continue; + } + + _snprintf(dev->filename, LIBUSB_PATH_MAX - 1, "%s--%s", + LibWinUsb::Encoding::ToAnsi(item->GetInterfacePath()).c_str(), + LibWinUsb::Encoding::ToAnsi(item->GetService()).c_str()); + + LIST_ADD(fdev, dev); + + USBMSG("found %s on %s\n", dev->filename, bus->dirname); + } + + *devices = fdev; + return 0; +} + +int usb_os_determine_children(struct usb_bus* bus) +{ + return 0; +} + +int usb_os_open(usb_dev_handle* dev) +{ + if (dev == nullptr) + { + USBERR("invalid device handle %p", dev); + return -EINVAL; + } + + dev->impl_info = nullptr; + dev->config = 0; + dev->interface = -1; + dev->altsetting = -1; + + std::string dev_name = dev->device->filename; + auto pos = dev_name.find("--"); + if (pos == std::string::npos) + { + USBERR("invalid file name %s\n", dev->device->filename); + return -ENOENT; + } + + std::unique_ptr driver; + HRESULT hr = LibWinUsb::UsbDevice::Open(LibWinUsb::Encoding::ToUnicode(dev_name.substr(pos + 2)), LibWinUsb::Encoding::ToUnicode(dev_name.substr(0, pos)), driver); + if (FAILED(hr)) + { + USBERR("failed to open %s: win error: %s\n", dev->device->filename, CreateMessageFromError(hr).c_str()); + return -ENOENT; + } + + dev->impl_info = driver.release(); + + return 0; +} + +int usb_os_close(usb_dev_handle* dev) +{ + delete static_cast(dev->impl_info); + + dev->impl_info = nullptr; + dev->interface = -1; + dev->altsetting = -1; + + return 0; +} + +int usb_control_msg(usb_dev_handle* dev, int requestType, int request, int value, int index, char* bytes, int size, int timeout) +{ + if (dev->impl_info == nullptr) + { + USBERR0("error: device not open\n"); + return -EINVAL; + } + + if (requestType == (USB_ENDPOINT_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) && request == USB_REQ_GET_DESCRIPTOR) + { + uint32_t bytesReturned = 0; + HRESULT hr = static_cast(dev->impl_info)->GetDescriptor( + static_cast(value >> 8), + static_cast(value), + static_cast(index), + bytes, + static_cast(size), + &bytesReturned, + timeout); + if (FAILED(hr)) + { + return -usb_hresult_to_errno(hr); + } + + return static_cast(bytesReturned); + } + else + { + uint32_t bytesReturned = 0; + HRESULT hr = static_cast(dev->impl_info)->ControlTransfer( + static_cast(requestType), + static_cast(request), + static_cast(value), + static_cast(index), + bytes, + static_cast(size), + &bytesReturned, + timeout); + if (FAILED(hr)) + { + return -usb_hresult_to_errno(hr); + } + + return static_cast(bytesReturned); + } +} + +int usb_set_configuration(usb_dev_handle* dev, int configuration) +{ + if (dev->impl_info == nullptr) + { + USBERR0("error: device not open\n"); + return -EINVAL; + } + + if (dev->config == configuration) + { + return 0; + } + + if (dev->interface >= 0) + { + USBERR0("can't change configuration, an interface is still in use (claimed)\n"); + return -EINVAL; + } + + HRESULT hr = static_cast(dev->impl_info)->SetConfiguration(static_cast(configuration)); + if (FAILED(hr)) + { + return -usb_hresult_to_errno(hr); + } + + dev->config = configuration; + dev->interface = -1; + dev->altsetting = -1; + + return 0; +} + +int usb_claim_interface(usb_dev_handle* dev, int interface) +{ + if (dev->impl_info == nullptr) + { + USBERR0("device not open\n"); + return -EINVAL; + } + + if (dev->config == 0) + { + USBERR("could not claim interface %d, invalid configuration %d\n", interface, dev->config); + return -EINVAL; + } + + if (dev->interface == interface) + { + return 0; + } + + HRESULT hr = static_cast(dev->impl_info)->ClaimInterface(static_cast(interface)); + if (FAILED(hr)) + { + return -usb_hresult_to_errno(hr); + } + + dev->interface = interface; + dev->altsetting = 0; + + return 0; +} + +int usb_release_interface(usb_dev_handle* dev, int interface) +{ + if (dev->impl_info == nullptr) + { + USBERR0("device not open\n"); + return -EINVAL; + } + + if (dev->config == 0) + { + USBERR("could not release interface %d, invalid configuration %d\n", interface, dev->config); + return -EINVAL; + } + + HRESULT hr = static_cast(dev->impl_info)->ReleaseInterface(static_cast(interface)); + if (FAILED(hr)) + { + return -usb_hresult_to_errno(hr); + } + + dev->interface = -1; + dev->altsetting = -1; + + return 0; +} + +int usb_set_altinterface(usb_dev_handle* dev, int alternate) +{ + if (dev->impl_info == nullptr) + { + USBERR0("device not open\n"); + return -EINVAL; + } + + if (dev->config == 0) + { + USBERR("could not set alt interface %d: invalid configuration %d\n", alternate, dev->config); + return -EINVAL; + } + + if (dev->interface < 0) + { + USBERR("could not set alt interface %d: no interface claimed\n", alternate); + return -EINVAL; + } + + HRESULT hr = static_cast(dev->impl_info)->SetAlternateSetting( + static_cast(dev->interface), + static_cast(alternate)); + if (FAILED(hr)) + { + return -usb_hresult_to_errno(hr); + } + + dev->altsetting = alternate; + + return 0; +} + +int usb_bulk_read(usb_dev_handle* dev, int ep, char* bytes, int size, int timeout) +{ + if (dev->impl_info == nullptr) + { + USBERR0("device not open\n"); + return -EINVAL; + } + + if (dev->config == 0) + { + USBERR("invalid configuration %d\n", dev->config); + return -EINVAL; + } + + if (dev->interface < 0) + { + USBERR("invalid interface %d\n", dev->interface); + return -EINVAL; + } + + if ((ep & USB_ENDPOINT_IN) == 0) + { + USBERR("invalid endpoint 0x%02x", ep); + return -EINVAL; + } + + uint32_t bytesRead = 0; + HRESULT hr = static_cast(dev->impl_info)->ReadPipe( + static_cast(ep), + bytes, + size, + &bytesRead, + timeout); + if (FAILED(hr)) + { + return -usb_hresult_to_errno(hr); + } + + return static_cast(bytesRead); +} + +int usb_bulk_write(usb_dev_handle* dev, int ep, char* bytes, int size, int timeout) +{ + if (dev->impl_info == nullptr) + { + USBERR0("device not open\n"); + return -EINVAL; + } + + if (dev->config == 0) + { + USBERR("invalid configuration %d\n", dev->config); + return -EINVAL; + } + + if (dev->interface < 0) + { + USBERR("invalid interface %d\n", dev->interface); + return -EINVAL; + } + + if ((ep & USB_ENDPOINT_IN) != 0) + { + USBERR("invalid endpoint 0x%02x", ep); + return -EINVAL; + } + + uint32_t bytesWritten = 0; + HRESULT hr = static_cast(dev->impl_info)->WritePipe( + static_cast(ep), + bytes, + size, + &bytesWritten, + timeout); + if (FAILED(hr)) + { + return -usb_hresult_to_errno(hr); + } + + return static_cast(bytesWritten); +} + +int usb_interrupt_read(usb_dev_handle* dev, int ep, char* bytes, int size, int timeout) +{ + return usb_bulk_read(dev, ep, bytes, size, timeout); +} + +int usb_interrupt_write(usb_dev_handle* dev, int ep, char* bytes, int size, int timeout) +{ + return usb_bulk_write(dev, ep, bytes, size, timeout); +}