commit d5973cd0ee3a850b0fdd7085ef719d4e6c786f98 Author: Denis V. Dedkov Date: Tue May 28 15:07:59 2024 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0b2fe7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,75 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2fad804 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.14) + +project(noolite LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +add_library(noolite STATIC + interfaces/iusbdevice.h + src/noolite.h src/noolite.cpp + src/libusbdevice.h src/libusbdevice.cpp +) + +find_library(LIBUSB_LIBRARY NAMES usb-1.0 REQUIRED) + +target_link_libraries(noolite PUBLIC ${LIBUSB_LIBRARY}) + +target_compile_definitions(noolite PRIVATE NOOLITE_LIBRARY) + +add_subdirectory(tests) diff --git a/docs/PC11xx_HID_API.pdf b/docs/PC11xx_HID_API.pdf new file mode 100644 index 0000000..7c7d347 Binary files /dev/null and b/docs/PC11xx_HID_API.pdf differ diff --git a/interfaces/iusbdevice.h b/interfaces/iusbdevice.h new file mode 100644 index 0000000..9f9afb5 --- /dev/null +++ b/interfaces/iusbdevice.h @@ -0,0 +1,22 @@ +#ifndef IUSBDEVICE_H +#define IUSBDEVICE_H + +#include +#include + +namespace noolitelib +{ + +class IUsbDevice +{ +public: + virtual ~IUsbDevice() = default; + + virtual void openDevice(uint16_t vendorId, uint16_t productId) = 0; + virtual void close() = 0; + virtual bool sendDataToDevice(unsigned char *data, uint16_t length, std::chrono::milliseconds timeout) = 0; +}; + +} + +#endif // IUSBDEVICE_H diff --git a/src/libusbdevice.cpp b/src/libusbdevice.cpp new file mode 100644 index 0000000..8bf186f --- /dev/null +++ b/src/libusbdevice.cpp @@ -0,0 +1,55 @@ +#include "libusbdevice.h" + +#include +#include + +namespace noolitelib +{ + +LibUsbDevice::LibUsbDevice() +{ + auto status = libusb_init(&m_context); + if (status) { + std::cout << "Error initializing context: " << libusb_strerror(status) << std::endl; + } +} + +LibUsbDevice::~LibUsbDevice() +{ + if (m_context) { + libusb_exit(m_context); + } +} + +void LibUsbDevice::openDevice(uint16_t vendorId, uint16_t productId) +{ + m_device = libusb_open_device_with_vid_pid(m_context, vendorId, productId); +} + +void LibUsbDevice::close() +{ + if (m_device) { + libusb_close(m_device); + } +} + +bool LibUsbDevice::sendDataToDevice(unsigned char *data, uint16_t length, std::chrono::milliseconds timeout) +{ + if (!m_device) { + std::cout << "Device not opened" << std::endl; + } + + auto requestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE; + auto request = LIBUSB_REQUEST_SET_CONFIGURATION; + + auto status = libusb_control_transfer(m_device, requestType, request, 0, 0, data, length, timeout.count()); + + if (status) { + std::cout << "Sending data error: " << libusb_strerror(status) << std::endl; + return false; + } + + return true; +} + +} diff --git a/src/libusbdevice.h b/src/libusbdevice.h new file mode 100644 index 0000000..e66b170 --- /dev/null +++ b/src/libusbdevice.h @@ -0,0 +1,29 @@ +#ifndef LIBUSBDEVICE_H +#define LIBUSBDEVICE_H + +#include "interfaces/iusbdevice.h" + +struct libusb_context; +struct libusb_device_handle; + +namespace noolitelib +{ + +class LibUsbDevice : public IUsbDevice +{ +public: + LibUsbDevice(); + ~LibUsbDevice() override; + + void openDevice(uint16_t vendorId, uint16_t productId) override; + void close() override; + bool sendDataToDevice(unsigned char *data, uint16_t length, std::chrono::milliseconds timeout = std::chrono::seconds(1)) override; + +private: + libusb_context *m_context = nullptr; + libusb_device_handle *m_device = nullptr; +}; + +} + +#endif // LIBUSBDEVICE_H diff --git a/src/noolite.cpp b/src/noolite.cpp new file mode 100644 index 0000000..2586c44 --- /dev/null +++ b/src/noolite.cpp @@ -0,0 +1,29 @@ +#include "noolite.h" + +#include "src/libusbdevice.h" + +namespace noolitelib +{ + +namespace Device +{ + +constexpr auto VID = 0x16c0; +constexpr auto PID = 0x05df; + +} + +Noolite::Noolite(IUsbDevice *device) : + m_device(device ? device : new LibUsbDevice()) +{ + m_device->openDevice(Device::VID, Device::PID); +} + +Noolite::~Noolite() +{ + m_device->close(); + delete m_device; +} + +} + diff --git a/src/noolite.h b/src/noolite.h new file mode 100644 index 0000000..4593d00 --- /dev/null +++ b/src/noolite.h @@ -0,0 +1,42 @@ +#ifndef NOOLITE_H +#define NOOLITE_H + +#include "interfaces/iusbdevice.h" + +namespace noolitelib +{ + +enum Command +{ + Off = 0, + DecraseBrightnes, + On, + IncreaseBrightnes, + Switch, + InvertBrightnes, + Set, + CallScenario, + SaveScenario, + Unbind, + StopColorSelection, + Bind = 15, + // Commands for SD111-180 only + ColorSelection, + ColorSwitch, + ModeSwitch, + EffectSpeed +}; + +class Noolite +{ +public: + Noolite(IUsbDevice *device = nullptr); + ~Noolite(); + +private: + IUsbDevice *m_device; +}; + +} + +#endif // NOOLITE_H diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..4a0b530 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,74 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..085a2db --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.5) + +project(adapter LANGUAGES CXX) + +enable_testing() + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include(FetchContent) +FetchContent_Declare(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG main) + +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +enable_testing() + +include_directories(..) + +add_executable(adapter main.cpp tst_adapter.cpp + mocks/usbdevicemock.h) +add_test(NAME adapter COMMAND adapter) + +target_link_libraries(adapter PRIVATE gmock gtest noolite) diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..899ed9f --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/mocks/usbdevicemock.h b/tests/mocks/usbdevicemock.h new file mode 100644 index 0000000..d4d1896 --- /dev/null +++ b/tests/mocks/usbdevicemock.h @@ -0,0 +1,16 @@ +#ifndef USBDEVICEMOCK_H +#define USBDEVICEMOCK_H + +#include + +#include "interfaces/iusbdevice.h" + +class UsbDeviceMock : public noolitelib::IUsbDevice +{ +public: + MOCK_METHOD(void, openDevice, (uint16_t, uint16_t), (override)); + MOCK_METHOD(void, close, (), (override)); + MOCK_METHOD(bool, sendDataToDevice, (unsigned char *, uint16_t, std::chrono::milliseconds), (override)); +}; + +#endif // USBDEVICEMOCK_H diff --git a/tests/tst_adapter.cpp b/tests/tst_adapter.cpp new file mode 100644 index 0000000..1e52071 --- /dev/null +++ b/tests/tst_adapter.cpp @@ -0,0 +1,20 @@ +#include + +#include "mocks/usbdevicemock.h" + +#include "src/noolite.h" + +using namespace testing; + +TEST(noolite, createAndDeleteAdapter) +{ + UsbDeviceMock *usbDevice = new UsbDeviceMock(); + + EXPECT_CALL(*usbDevice, openDevice(0x16c0, 0x05df)). + Times(1); + EXPECT_CALL(*usbDevice, close()). + Times(1); + + noolitelib::Noolite *adapter = new noolitelib::Noolite(usbDevice); + delete adapter; +}