*
This commit is contained in:
+26
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Debug.h"
|
||||
#include "IO/Terminal/Terminal.h"
|
||||
|
||||
namespace LFramework::Debug {
|
||||
class Assert : public LogWriter {
|
||||
public:
|
||||
Assert():LogWriter(LogLevel::Fatal){}
|
||||
~Assert(){
|
||||
for(;;);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define lfAssert(expression) \
|
||||
do { \
|
||||
if(!(expression)) { \
|
||||
LFramework::Debug::Assert() \
|
||||
<< "Assert: " << #expression << LFramework::Terminal::NewLine \
|
||||
<< "File: " << __FILE__ << LFramework::Terminal::NewLine; \
|
||||
} \
|
||||
}while(false)
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* BitField.h
|
||||
*
|
||||
* Created on: 14 sept. 2017 ã.
|
||||
* Author: l-pro
|
||||
*/
|
||||
|
||||
#ifndef LFRAMEWORK_BITFIELD_H_
|
||||
#define LFRAMEWORK_BITFIELD_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <limits>
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
template<typename TBase, typename T>
|
||||
struct BitFieldValueConverter {
|
||||
static TBase toBase(T value) {
|
||||
return static_cast<TBase>(value);
|
||||
}
|
||||
static T fromBase(TBase value) {
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TBase, size_t BitOffset, size_t BitCount = 1, typename T = TBase, typename Converter = BitFieldValueConverter<TBase, T>>
|
||||
struct BitFieldMember {
|
||||
static constexpr size_t FieldBitsCount = BitCount;
|
||||
static constexpr size_t TBitsCount = std::numeric_limits<unsigned char>::digits * sizeof(T);
|
||||
static_assert(std::numeric_limits<unsigned char>::digits == 8, "Only systems with 8 bit chars are supported for now");
|
||||
static_assert(std::is_standard_layout_v<TBase>, "Only standard layout types supported in unions (and so in BitFieldMember) without UB");
|
||||
static_assert(BitOffset + BitCount <= TBitsCount, "Base type bits overflow");
|
||||
|
||||
TBase baseValue;
|
||||
|
||||
static constexpr TBase LowMask = (static_cast<TBase>((~(TBase{ 0 }))) >> (TBitsCount - BitCount));
|
||||
static constexpr TBase HiMask = TBase(LowMask << BitOffset);
|
||||
|
||||
template<typename TR = T, typename = std::enable_if_t<!std::is_same_v<TR, TBase>>>
|
||||
volatile BitFieldMember& operator =(TR value) volatile {
|
||||
baseValue = assign(baseValue, Converter::toBase(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename TR = TBase>
|
||||
constexpr BitFieldMember& operator =(TR value) {
|
||||
if constexpr(std::is_same_v<TR, TBase>){
|
||||
baseValue = assign(baseValue, value);
|
||||
}else{
|
||||
baseValue = assign(baseValue, Converter::toBase(value));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr operator T() const {
|
||||
return Converter::fromBase((baseValue >> BitOffset) & LowMask);
|
||||
}
|
||||
static constexpr inline TBase assign(TBase left, TBase right) {
|
||||
return (left & ~(HiMask)) | ((right & LowMask) << BitOffset);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static_assert(std::is_standard_layout_v<BitFieldMember<uint32_t, 0, 1>>, "Only standard layout types supported in unions (and so in BitFieldMember) without UB");
|
||||
|
||||
}
|
||||
|
||||
#endif /* LFRAMEWORK_BITFIELD_H_ */
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* BitFieldTest.cpp
|
||||
*
|
||||
* Created on: 14 ñåíò. 2017 ã.
|
||||
* Author: l-pro
|
||||
*/
|
||||
|
||||
#include "UnitTest/UnitTest.h"
|
||||
#include "BitField.h"
|
||||
|
||||
using namespace LFramework;
|
||||
|
||||
union Test8BitField {
|
||||
constexpr Test8BitField():value(0){}
|
||||
uint8_t value;
|
||||
BitFieldMember<uint8_t, 0, 1> bit_0_1;
|
||||
BitFieldMember<uint8_t, 1, 3> bit_1_3;
|
||||
BitFieldMember<uint8_t, 4, 4> bit_4_4;
|
||||
};
|
||||
|
||||
TEST(BitField_8bit){
|
||||
Test8BitField bitfield;
|
||||
TEST_TRUE(bitfield.value == 0);
|
||||
|
||||
bitfield.bit_0_1 = 1;
|
||||
TEST_TRUE(bitfield.value == 1);
|
||||
TEST_TRUE(bitfield.bit_0_1 == 1);
|
||||
|
||||
bitfield.bit_1_3 = 4;
|
||||
TEST_TRUE(bitfield.bit_1_3 == 4);
|
||||
TEST_TRUE(bitfield.value == 9);
|
||||
bitfield.bit_1_3 = 0;
|
||||
TEST_TRUE(bitfield.value == 1);
|
||||
|
||||
bitfield.bit_4_4 = 1;
|
||||
TEST_TRUE(bitfield.value == 17);
|
||||
|
||||
bitfield.bit_4_4 = 15;
|
||||
TEST_TRUE(bitfield.value == 241);
|
||||
|
||||
bitfield.bit_1_3 = 7;
|
||||
TEST_TRUE(bitfield.value == 0xff);
|
||||
|
||||
bitfield.bit_0_1 = 0;
|
||||
TEST_TRUE(bitfield.value == 0xfe);
|
||||
|
||||
bitfield.bit_4_4 = 0;
|
||||
TEST_TRUE(bitfield.value == 0x0e);
|
||||
|
||||
bitfield.bit_1_3 = 0;
|
||||
TEST_TRUE(bitfield.value == 0x00);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
target_sources(LFramework
|
||||
INTERFACE
|
||||
Assert.h
|
||||
BitField.h
|
||||
CMakeLists.txt
|
||||
Debug.h
|
||||
Guid.h
|
||||
GuidUtils.h
|
||||
GuidUtils.cpp
|
||||
System.h
|
||||
SystemArmCortexM.h
|
||||
SystemWindows.h
|
||||
TupleUtils.h
|
||||
#BitFieldTest.cpp
|
||||
Syscalls.cpp
|
||||
Text/Encoding.h
|
||||
|
||||
TypeTraits/FunctionTraits.h
|
||||
TypeTraits/Integral.h
|
||||
|
||||
Reflection/Reflection.h
|
||||
)
|
||||
|
||||
add_subdirectory(USB)
|
||||
add_subdirectory(UnitTest)
|
||||
add_subdirectory(Threading)
|
||||
add_subdirectory(IO)
|
||||
add_subdirectory(Input)
|
||||
#add_subdirectory(Time)
|
||||
|
||||
#add_subdirectory(MCU)
|
||||
#add_subdirectory(Macro)
|
||||
#add_subdirectory(IO)
|
||||
#add_subdirectory(Input)
|
||||
#add_subdirectory(Detect)
|
||||
#add_subdirectory(Containers)
|
||||
#add_subdirectory(COM)
|
||||
@@ -0,0 +1,4 @@
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
ComObject.h
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "ComObject.h"
|
||||
|
||||
#if defined(__CYGWIN32__)
|
||||
#define LFRAMEWORK_COM_LIBRARY_CALL __stdcall
|
||||
#define LFRAMEWORK_COM_LIBRARY_EXPORT __declspec(dllexport)
|
||||
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(WINAPI_FAMILY)
|
||||
#define LFRAMEWORK_COM_LIBRARY_CALL __stdcall
|
||||
#define LFRAMEWORK_COM_LIBRARY_EXPORT __declspec(dllexport)
|
||||
#elif defined(__MACH__)
|
||||
#define LFRAMEWORK_COM_LIBRARY_CALL
|
||||
#define LFRAMEWORK_COM_LIBRARY_EXPORT
|
||||
#elif defined(__linux__) || defined(__ANDROID__)
|
||||
#define LFRAMEWORK_COM_LIBRARY_CALL
|
||||
#define LFRAMEWORK_COM_LIBRARY_EXPORT __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define LFRAMEWORK_COM_LIBRARY_CALL
|
||||
#define LFRAMEWORK_COM_LIBRARY_EXPORT
|
||||
#endif
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
#define LFRAMEWORK_COM_LIBRARY_EXPORT_ENTRY_FUNCTION() \
|
||||
extern "C" LFRAMEWORK_COM_LIBRARY_EXPORT void LFRAMEWORK_COM_LIBRARY_CALL getInterface(LFramework::ComPtr<LFramework::IUnknown>& result){ \
|
||||
result = LFramework::ComPtr<ILibrary>::create<Library>(); \
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
#pragma once
|
||||
|
||||
#include <LFramework/Detect/DetectOS.h>
|
||||
#include <LFramework/Guid.h>
|
||||
#include <LFramework/TypeTraits/FunctionTraits.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
#if (LF_TARGET_OS == LF_OS_WINDOWS) || (LF_TARGET_OS == LF_OS_CYGWIN)
|
||||
#define LFRAMEWORK_COM_CALL __stdcall
|
||||
#else
|
||||
#define LFRAMEWORK_COM_CALL
|
||||
#endif
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
using InterfaceID = Guid;
|
||||
|
||||
//IUnknown
|
||||
class IUnknown;
|
||||
|
||||
template<class TInterface, class TImplementer>
|
||||
struct InterfaceRemap {};
|
||||
|
||||
template<typename Remap>
|
||||
struct RemapTraits {
|
||||
|
||||
};
|
||||
|
||||
template<class TInterface, class TImplementer>
|
||||
struct RemapTraits<InterfaceRemap<TInterface, TImplementer>> {
|
||||
using InterfaceType = TInterface;
|
||||
using ImplementerType = TImplementer;
|
||||
};
|
||||
|
||||
template<class Interface>
|
||||
struct InterfaceAbi {};
|
||||
|
||||
|
||||
|
||||
|
||||
template<class InterfaceAbi>
|
||||
bool InterfaceAbiInterfaceSupported(const InterfaceID& id) {
|
||||
if (InterfaceAbi::ID() == id) {
|
||||
return true;
|
||||
}
|
||||
if constexpr (!std::is_same_v<typename InterfaceAbi::Base, void>) {
|
||||
return InterfaceAbiInterfaceSupported<typename InterfaceAbi::Base>(id);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
struct RemapChainItemBase {
|
||||
using InterfaceAbiInterfaceSupportedPtr = bool(*)(const InterfaceID&);
|
||||
RemapChainItemBase* next = nullptr;
|
||||
InterfaceAbiInterfaceSupportedPtr isSupported = nullptr;
|
||||
void* getRemapPtr() {
|
||||
return reinterpret_cast<std::uint8_t*>(&isSupported) + sizeof(isSupported);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TRemap>
|
||||
struct RemapChainItem : RemapChainItemBase {
|
||||
RemapChainItem(typename RemapTraits<TRemap>::ImplementerType* implementer) {
|
||||
remap._implementer = implementer;
|
||||
this->isSupported = &InterfaceAbiInterfaceSupported<InterfaceAbi<typename RemapTraits<TRemap>::InterfaceType>>;
|
||||
implementer->registerRemap(this);
|
||||
}
|
||||
TRemap remap;
|
||||
};
|
||||
|
||||
template<class Implementer, class FirstInterface, class ... Interfaces>
|
||||
struct ComRemapList : ComRemapList<Implementer, Interfaces...> {
|
||||
using Base = ComRemapList<Implementer, Interfaces...>;
|
||||
ComRemapList(Implementer* _this) : Base(_this), remapContainer{ _this } {}
|
||||
RemapChainItem<InterfaceRemap<FirstInterface, Implementer>> remapContainer;
|
||||
};
|
||||
|
||||
template<class Implementer, class FirstInterface>
|
||||
struct ComRemapList<Implementer, FirstInterface> {
|
||||
ComRemapList(Implementer* _this) : remapContainer{ _this } {}
|
||||
RemapChainItem<InterfaceRemap<FirstInterface, Implementer>> remapContainer;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct IsInterface : std::conditional_t<std::is_base_of_v<InterfaceAbi<IUnknown>, InterfaceAbi<T>>, std::true_type, std::false_type> {};
|
||||
|
||||
template<class...T>
|
||||
struct IsAllInterfaces : std::conjunction<IsInterface<T>...> {};
|
||||
|
||||
template<class TImplementer, class TBase, class ... TInterfaceList>
|
||||
struct ComImplement : public TBase {
|
||||
using ComImplement_BaseType = TBase;
|
||||
using ComImplement_SelfType = ComImplement;
|
||||
using Implementer = TImplementer;
|
||||
ComRemapList<TImplementer, TInterfaceList...> _remaps = { reinterpret_cast<TImplementer*>(this) };
|
||||
};
|
||||
|
||||
|
||||
template<class T, class = void>
|
||||
struct IsComImplement : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct IsComImplement<T, std::void_t<typename T::ComImplement_SelfType>> : std::true_type {};
|
||||
|
||||
template<class...T>
|
||||
struct IsAllComImplement : std::conjunction<IsComImplement<T>...> {};
|
||||
|
||||
template<class TInterface, class ... TInterfaceList>
|
||||
struct HasInterface : std::disjunction<std::is_base_of<InterfaceAbi<TInterface>, InterfaceAbi<TInterfaceList>>...> {};
|
||||
|
||||
template<class TImplementer, class TInterface>
|
||||
class ComDelegate;
|
||||
|
||||
|
||||
template<class T>
|
||||
struct IsComDelegate : std::false_type {};
|
||||
|
||||
template<class TImplementer, class TInterface>
|
||||
struct IsComDelegate<ComDelegate<TImplementer, TInterface>> : std::true_type {};
|
||||
|
||||
|
||||
enum class Result : uint32_t {
|
||||
Ok = 0,
|
||||
NotImplemented = 0x80004001L,
|
||||
NoInterface = 0x80004002L,
|
||||
ErrorPointer = 0x80004003L,
|
||||
UnknownFailure = 0x80004005L,
|
||||
OutOfMemory = 0x8007000EL,
|
||||
InvalidArg = 0x80070057L,
|
||||
Pending = 0x8000000AL,
|
||||
AsyncOperationNotStarted = 0x80000019L,
|
||||
RpcTimeout = 0x8001011FL,
|
||||
RpcDisconnected = 0x80010108L
|
||||
};
|
||||
|
||||
struct ComException : std::exception {
|
||||
ComException(Result code) : _errorCode(code) {
|
||||
|
||||
}
|
||||
ComException() : ComException(Result::UnknownFailure) {
|
||||
|
||||
}
|
||||
ComException(Result code, std::string message) :_errorCode(code), _message(std::move(message)) {
|
||||
|
||||
}
|
||||
ComException(std::string message) : ComException(Result::UnknownFailure, std::move(message)) {
|
||||
|
||||
}
|
||||
Result code() const {
|
||||
return _errorCode;
|
||||
}
|
||||
char const* what() const noexcept override {
|
||||
return _message.c_str();
|
||||
}
|
||||
const std::string& message() const {
|
||||
return _message;
|
||||
}
|
||||
private:
|
||||
Result _errorCode;
|
||||
std::string _message;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct InterfaceAbi<IUnknown> {
|
||||
public:
|
||||
using Base = void;
|
||||
static constexpr InterfaceID ID() { return { 0x00000000, 0x0000000, 0x000000C0, 0x46000000 }; }
|
||||
template<class TInterface>
|
||||
friend class ComPtr;
|
||||
friend class ComObject;
|
||||
virtual Result LFRAMEWORK_COM_CALL queryInterface(const InterfaceID& riid, void** ppvObject) = 0;
|
||||
virtual std::uint32_t LFRAMEWORK_COM_CALL addRef() = 0;
|
||||
virtual std::uint32_t LFRAMEWORK_COM_CALL release() = 0;
|
||||
~InterfaceAbi() = delete;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename TInterface>
|
||||
static constexpr InterfaceID getInterfaceId() {
|
||||
return InterfaceAbi<TInterface>::ID();
|
||||
}
|
||||
|
||||
|
||||
template<class TImplementer>
|
||||
struct InterfaceRemap<IUnknown, TImplementer> {
|
||||
virtual Result LFRAMEWORK_COM_CALL queryInterface(const InterfaceID& riid, void** ppvObject) { return _implementer->queryInterface(riid, ppvObject); }
|
||||
virtual std::uint32_t LFRAMEWORK_COM_CALL addRef() { return _implementer->addRef(); }
|
||||
virtual std::uint32_t LFRAMEWORK_COM_CALL release() { return _implementer->release(); }
|
||||
inline auto implementer() {
|
||||
if constexpr (IsComDelegate<TImplementer>::value) {
|
||||
return _implementer->getImplementer();
|
||||
} else {
|
||||
return _implementer;
|
||||
}
|
||||
}
|
||||
TImplementer* _implementer;
|
||||
~InterfaceRemap() = default;
|
||||
};
|
||||
|
||||
template <class TInterface, class TComImplement>
|
||||
struct IsComImplementSupportsInterface : std::false_type {};
|
||||
|
||||
template <class TInterface, class TImplementer, class TBase, class ... TInterfaceList>
|
||||
struct IsComImplementSupportsInterface<TInterface, ComImplement<TImplementer, TBase, TInterfaceList...>> : HasInterface<TInterface, TInterfaceList...> {};
|
||||
|
||||
class RefCountedObject {
|
||||
public:
|
||||
virtual ~RefCountedObject() = default;
|
||||
std::uint32_t addRef() {
|
||||
return _refCount.fetch_add(1) + 1;
|
||||
}
|
||||
std::uint32_t release() {
|
||||
auto result = _refCount.fetch_sub(1);
|
||||
if (result == 1) {
|
||||
delete this;
|
||||
}
|
||||
return result - 1;
|
||||
}
|
||||
private:
|
||||
std::atomic<unsigned long> _refCount{};
|
||||
};
|
||||
|
||||
template<class TInterface>
|
||||
class ComPtr;
|
||||
|
||||
|
||||
class ComObject : public RefCountedObject {
|
||||
public:
|
||||
template<typename TRemap>
|
||||
friend struct RemapChainItem;
|
||||
|
||||
ComObject() {
|
||||
_headRemap.next = &_headRemap;
|
||||
}
|
||||
|
||||
template<typename TInterface>
|
||||
ComPtr<TInterface> queryInterface() {
|
||||
void* result = nullptr;
|
||||
if(queryInterface(InterfaceAbi<TInterface>::ID(), &result) == Result::Ok){
|
||||
auto abiPtr = reinterpret_cast<InterfaceAbi<TInterface>*>(result);
|
||||
ComPtr<TInterface> result;
|
||||
result.attach(abiPtr);
|
||||
return result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Result queryInterface(const InterfaceID& riid, void** ppvObject) {
|
||||
if (ppvObject == nullptr) {
|
||||
return Result::ErrorPointer;
|
||||
}
|
||||
|
||||
findInterface(riid, ppvObject);
|
||||
|
||||
if (*ppvObject == nullptr) {
|
||||
return Result::NoInterface;
|
||||
} else {
|
||||
auto obj = (reinterpret_cast<InterfaceAbi<IUnknown>*>(*ppvObject));
|
||||
obj->addRef();
|
||||
return Result::Ok;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void findInterface(const InterfaceID& id, void** result) {
|
||||
RemapChainItemBase* current = &_headRemap;
|
||||
do {
|
||||
if (current->isSupported(id)) {
|
||||
*result = current->getRemapPtr();
|
||||
return;
|
||||
}
|
||||
current = current->next;
|
||||
} while (current != &_headRemap);
|
||||
|
||||
*result = nullptr;
|
||||
}
|
||||
void registerRemap(RemapChainItemBase* remap) {
|
||||
remap->next = _headRemap.next;
|
||||
_headRemap.next = remap;
|
||||
}
|
||||
|
||||
RemapChainItem<InterfaceRemap<IUnknown, ComObject>> _headRemap{ this };
|
||||
};
|
||||
|
||||
|
||||
template<class TImplementer, class TInterface>
|
||||
constexpr bool IsInterfaceSupported() {
|
||||
if constexpr(!LFramework::IsInterface<TInterface>::value || !IsComImplement<TImplementer>::value){
|
||||
return false;
|
||||
}else{
|
||||
if constexpr(std::is_same_v<TImplementer, ComObject> && std::is_same_v<TInterface, LFramework::IUnknown>){
|
||||
return true;
|
||||
}else{
|
||||
if constexpr (LFramework::IsComImplementSupportsInterface<TInterface, typename TImplementer::ComImplement_SelfType>::value){
|
||||
return true;
|
||||
}else{
|
||||
return IsInterfaceSupported<typename TImplementer::ComImplement_BaseType, TInterface>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class TInterface>
|
||||
class InterfaceWrapper {
|
||||
public:
|
||||
using NotSpecialized = bool;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<class TInterface, class = void>
|
||||
class HasInterfaceWrapper : public std::true_type {};
|
||||
|
||||
template<class TInterface>
|
||||
class HasInterfaceWrapper<TInterface, std::void_t<typename LFramework::InterfaceWrapper<TInterface>::NotSpecialized>> : public std::false_type {};
|
||||
|
||||
template<class TInterface>
|
||||
class ComPtr {
|
||||
public:
|
||||
using InterfacePtr = InterfaceAbi<TInterface>*;
|
||||
|
||||
using PublicInterfacePtr = std::conditional_t<HasInterfaceWrapper<TInterface>::value, InterfaceWrapper<TInterface>*, InterfacePtr>;
|
||||
|
||||
ComPtr() = default;
|
||||
|
||||
ComPtr(InterfacePtr ptr) : _interface(ptr){
|
||||
if(_interface != nullptr){
|
||||
_interface->addRef();
|
||||
}
|
||||
}
|
||||
|
||||
template<class U, class = std::enable_if_t<std::is_base_of_v<InterfaceAbi<TInterface>, InterfaceAbi<U>>>>
|
||||
ComPtr(ComPtr<U> ptr) : _interface(ptr.get()) {
|
||||
_interface->addRef();
|
||||
}
|
||||
|
||||
ComPtr(const ComPtr& ptr) : _interface(ptr.get()) {
|
||||
if(_interface != nullptr){
|
||||
_interface->addRef();
|
||||
}
|
||||
}
|
||||
|
||||
template<class U>
|
||||
ComPtr<U> queryInterface(){
|
||||
ComPtr<U> result;
|
||||
if(_interface->queryInterface(InterfaceAbi<U>::ID(), result.put()) == Result::Ok){
|
||||
return result;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
~ComPtr() {
|
||||
reset();
|
||||
}
|
||||
|
||||
PublicInterfacePtr operator ->() {
|
||||
if constexpr(HasInterfaceWrapper<TInterface>::value){
|
||||
return reinterpret_cast<PublicInterfacePtr>(&_interface);
|
||||
}else{
|
||||
return reinterpret_cast<InterfacePtr>(_interface);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<class U, class = std::enable_if_t<std::is_base_of_v<InterfaceAbi<TInterface>, InterfaceAbi<U>>>>
|
||||
operator ComPtr<U>(){
|
||||
return ComPtr<U>(_interface);
|
||||
}
|
||||
|
||||
ComPtr operator = (std::nullptr_t) {
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComPtr operator = (const ComPtr& other) {
|
||||
reset();
|
||||
auto ptr = other._interface;
|
||||
if (ptr != nullptr) {
|
||||
ptr->addRef();
|
||||
}
|
||||
_interface = ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool operator == (const ComPtr& other) const {
|
||||
return _interface == other._interface;
|
||||
}
|
||||
|
||||
bool operator != (const ComPtr& other) const {
|
||||
return _interface != other._interface;
|
||||
}
|
||||
bool operator == (std::nullptr_t) const {
|
||||
return _interface == nullptr;
|
||||
}
|
||||
|
||||
bool operator != (std::nullptr_t) const {
|
||||
return _interface != nullptr;
|
||||
}
|
||||
|
||||
InterfacePtr operator*() {
|
||||
return _interface;
|
||||
}
|
||||
|
||||
const InterfacePtr operator*() const {
|
||||
return _interface;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return (_interface != nullptr);
|
||||
}
|
||||
|
||||
InterfacePtr get() const {
|
||||
return _interface;
|
||||
}
|
||||
|
||||
void** put() {
|
||||
return reinterpret_cast<void**>(&_interface);
|
||||
}
|
||||
|
||||
InterfacePtr detach() {
|
||||
auto result = _interface;
|
||||
_interface = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
void attach(InterfacePtr ptr) {
|
||||
reset();
|
||||
_interface = ptr;
|
||||
}
|
||||
void reset() {
|
||||
auto ptr = _interface;
|
||||
if (ptr != nullptr) {
|
||||
_interface = nullptr;
|
||||
ptr->release();
|
||||
}
|
||||
}
|
||||
|
||||
template<class TImplementer, class ... TArgs>
|
||||
static ComPtr create(TArgs&& ... args) {
|
||||
if constexpr(IsInterfaceSupported<TImplementer, TInterface>()){
|
||||
auto obj = new TImplementer(std::forward<TArgs>(args)...);
|
||||
return obj->template queryInterface<TInterface>();
|
||||
}else{
|
||||
static_assert(IsInterfaceSupported<TImplementer, TInterface>(), "Interface not supported");
|
||||
}
|
||||
}
|
||||
private:
|
||||
InterfacePtr _interface = nullptr;
|
||||
};
|
||||
|
||||
|
||||
template<class TImplementer, class TInterface>
|
||||
class ComDelegate : public ComImplement<ComDelegate<TImplementer, TInterface>, ComObject, TInterface> {
|
||||
public:
|
||||
using DelegatedImplementer = TImplementer;
|
||||
typedef void (TImplementer::*DelegateDestroyCallback)();
|
||||
|
||||
ComDelegate(TImplementer* implementer, DelegateDestroyCallback destroyCallback)
|
||||
: _implementer(implementer), _destroyCallback(destroyCallback) {
|
||||
if constexpr (std::is_base_of_v<RefCountedObject, TImplementer>) {
|
||||
_implementer->addRef();
|
||||
}
|
||||
}
|
||||
~ComDelegate() {
|
||||
if (_destroyCallback != nullptr) {
|
||||
((_implementer)->*(_destroyCallback))();
|
||||
}
|
||||
if constexpr (std::is_base_of_v<RefCountedObject, TImplementer>) {
|
||||
_implementer->release();
|
||||
}
|
||||
}
|
||||
TImplementer* getImplementer() {
|
||||
return _implementer;
|
||||
}
|
||||
private:
|
||||
DelegateDestroyCallback _destroyCallback;
|
||||
TImplementer* _implementer;
|
||||
};
|
||||
|
||||
|
||||
template<class TInterface, class TImplementer>
|
||||
ComPtr<TInterface> makeComDelegate(TImplementer* implementer, typename ComDelegate<TImplementer, TInterface>::DelegateDestroyCallback delegateDestroyCallback = nullptr) {
|
||||
return ComPtr<TInterface>::template create<ComDelegate<TImplementer, TInterface>>(implementer, delegateDestroyCallback);
|
||||
}
|
||||
|
||||
|
||||
class ArrayOutMarshaler {
|
||||
public:
|
||||
using SizeType = std::uint32_t;
|
||||
typedef void* ContextPtr;
|
||||
typedef void* (LFRAMEWORK_COM_CALL *ContainerResizeCallback)(ContextPtr context, SizeType size);
|
||||
|
||||
ContextPtr context;
|
||||
ContainerResizeCallback callback;
|
||||
|
||||
template<typename T>
|
||||
ArrayOutMarshaler(std::vector<T>& buffer) {
|
||||
context = &buffer;
|
||||
callback = &ArrayOutMarshaler::resizeCallback<T>;
|
||||
}
|
||||
|
||||
ArrayOutMarshaler(std::string& buffer) {
|
||||
context = &buffer;
|
||||
callback = &ArrayOutMarshaler::resizeCallback;
|
||||
}
|
||||
template<typename T>
|
||||
void operator = (const std::vector<T>& source) {
|
||||
void* data = callback(context, static_cast<SizeType>(source.size()));
|
||||
if (data != nullptr && !source.empty()) {
|
||||
memcpy(data, source.data(), sizeof(T) * source.size());
|
||||
}
|
||||
}
|
||||
void operator = (const std::string& source) {
|
||||
void* data = callback(context, static_cast<SizeType>(source.size()));
|
||||
if (data != nullptr && !source.empty()) {
|
||||
memcpy(data, source.data(), source.size());
|
||||
}
|
||||
}
|
||||
private:
|
||||
template<typename T>
|
||||
static void* LFRAMEWORK_COM_CALL resizeCallback(ContextPtr context, SizeType size) {
|
||||
auto container = reinterpret_cast<std::vector<T>*>(context);
|
||||
container->resize(size);
|
||||
return size == 0 ? nullptr : container->data();
|
||||
}
|
||||
static void* LFRAMEWORK_COM_CALL resizeCallback(ContextPtr context, SizeType size) {
|
||||
auto container = reinterpret_cast<std::string*>(context);
|
||||
container->resize(size);
|
||||
return size == 0 ? nullptr : container->data();
|
||||
}
|
||||
ArrayOutMarshaler() = delete;
|
||||
};
|
||||
|
||||
class ArrayInMarshaler {
|
||||
public:
|
||||
using SizeType = std::uint32_t;
|
||||
const void* data;
|
||||
SizeType itemsCount;
|
||||
|
||||
ArrayInMarshaler(const std::string& source) {
|
||||
data = source.data();
|
||||
itemsCount = static_cast<SizeType>(source.size());
|
||||
}
|
||||
template<typename T>
|
||||
ArrayInMarshaler(const std::vector<T>& source) {
|
||||
data = source.data();
|
||||
itemsCount = static_cast<SizeType>(source.size());
|
||||
}
|
||||
operator std::string() const {
|
||||
return std::string(reinterpret_cast<const char*>(data), itemsCount);
|
||||
}
|
||||
template<typename T>
|
||||
operator std::vector<T>() const {
|
||||
std::vector<T> result;
|
||||
if (itemsCount != 0) {
|
||||
result.resize(itemsCount);
|
||||
memcpy(result.data(), data, sizeof(T) * itemsCount);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private:
|
||||
ArrayInMarshaler() = delete;
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
class InterfaceWrapper<IUnknown> {
|
||||
public:
|
||||
template<typename TInterface, typename = typename std::enable_if<std::is_base_of<InterfaceAbi<IUnknown>, InterfaceAbi<TInterface>>::value>::type>
|
||||
ComPtr<TInterface> queryInterface() {
|
||||
if (_abi == nullptr) {
|
||||
return {};
|
||||
}
|
||||
ComPtr<TInterface> result{};
|
||||
reinterpret_cast<InterfaceAbi<IUnknown>*>(_abi)->queryInterface(InterfaceAbi<TInterface>::ID(), reinterpret_cast<void**>(&result));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t addRef() {
|
||||
return reinterpret_cast<InterfaceAbi<IUnknown>*>(_abi)->addRef();
|
||||
}
|
||||
uint32_t release() {
|
||||
return reinterpret_cast<InterfaceAbi<IUnknown>*>(_abi)->release();
|
||||
}
|
||||
protected:
|
||||
void* _abi = nullptr;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
template<std::size_t _Capacity>
|
||||
class ByteFifo {
|
||||
public:
|
||||
static constexpr std::size_t Capacity = _Capacity;
|
||||
|
||||
bool write(const void* data, std::size_t size){
|
||||
std::size_t availableSize = getAvailableSize();
|
||||
if(availableSize >= size){
|
||||
auto writeOffset = (_begin + _size) % Capacity;
|
||||
auto distanceToEnd = Capacity - writeOffset;
|
||||
if(distanceToEnd >= size){
|
||||
memcpy(_buffer.data() + writeOffset, data, size);
|
||||
}else{
|
||||
memcpy(_buffer.data() + writeOffset, data, distanceToEnd);
|
||||
memcpy(_buffer.data(), reinterpret_cast<const std::uint8_t*>(data) + distanceToEnd, size - distanceToEnd);
|
||||
}
|
||||
_size += size;
|
||||
if(_size >= 100000){
|
||||
for(;;);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write(std::uint8_t value){
|
||||
return write(&value, 1);
|
||||
}
|
||||
|
||||
bool read(void* data, std::uint8_t size) {
|
||||
if(_size >= 100000){
|
||||
for(;;);
|
||||
}
|
||||
if(_size < size){
|
||||
return false;
|
||||
}
|
||||
|
||||
auto distanceToEnd = Capacity - _begin;
|
||||
|
||||
if(distanceToEnd >= size){
|
||||
memcpy(data, _buffer.data() + _begin, size);
|
||||
_begin = (_begin + size) % Capacity;
|
||||
}else{
|
||||
memcpy(data, _buffer.data() + _begin, distanceToEnd);
|
||||
memcpy(reinterpret_cast<std::uint8_t*>(data) + distanceToEnd, _buffer.data(), size - distanceToEnd);
|
||||
_begin = size - distanceToEnd;
|
||||
}
|
||||
if(_size >= 100000){
|
||||
for(;;);
|
||||
}
|
||||
_size -= size;
|
||||
if(_size >= 100000){
|
||||
for(;;);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t discard(std::size_t count){
|
||||
if(count > _size){
|
||||
count = _size;
|
||||
}
|
||||
|
||||
_begin = (_begin + count) % Capacity;
|
||||
_size -= count;
|
||||
if(_size >= 100000){
|
||||
for(;;);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool peek(void* data, std::size_t size) {
|
||||
if(_size < size){
|
||||
return false;
|
||||
}
|
||||
|
||||
auto distanceToEnd = Capacity - _begin;
|
||||
|
||||
if(distanceToEnd >= size){
|
||||
memcpy(data, _buffer.data() + _begin, size);
|
||||
}else{
|
||||
memcpy(data, _buffer.data() + _begin, distanceToEnd);
|
||||
memcpy(reinterpret_cast<std::uint8_t*>(data) + distanceToEnd, _buffer.data(), size - distanceToEnd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool read(std::uint8_t* value) {
|
||||
return read(value, 1);
|
||||
}
|
||||
|
||||
std::size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
std::size_t sizeAvailable() const {
|
||||
return Capacity - _size;
|
||||
}
|
||||
bool empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
void clear() {
|
||||
_begin = 0;
|
||||
_size = 0;
|
||||
}
|
||||
private:
|
||||
std::size_t getAvailableSize() const {
|
||||
return Capacity - _size;
|
||||
}
|
||||
std::size_t _size = 0;
|
||||
std::size_t _begin = 0;
|
||||
std::array<std::uint8_t, Capacity> _buffer;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
ByteFifo.h
|
||||
LinkedList.h
|
||||
StaticVector.h
|
||||
)
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
class LinkedList;
|
||||
|
||||
class LinkedListItem {
|
||||
public:
|
||||
LinkedListItem* next = nullptr;
|
||||
LinkedListItem* previous = nullptr;
|
||||
LinkedList* owner = nullptr;
|
||||
};
|
||||
|
||||
|
||||
class LinkedList {
|
||||
public:
|
||||
|
||||
LinkedList():_size(0){
|
||||
_head.next = &_head;
|
||||
_head.previous = &_head;
|
||||
_head.owner = this;
|
||||
}
|
||||
|
||||
bool pushFront(LinkedListItem* newItem){
|
||||
if (newItem->owner == nullptr) {
|
||||
newItem->owner = this;
|
||||
newItem->next = _head.next;
|
||||
newItem->previous = &_head;
|
||||
_head.next->previous = newItem;
|
||||
_head.next = newItem;
|
||||
++_size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pushBack(LinkedListItem* newItem) {
|
||||
if (newItem->owner == nullptr) {
|
||||
newItem->owner = _head.owner;
|
||||
newItem->previous = _head.previous;
|
||||
newItem->next = &_head;
|
||||
_head.previous->next = newItem;
|
||||
_head.previous = newItem;
|
||||
++_size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
void remove(LinkedListItem* item) {
|
||||
if (item->owner == this) {
|
||||
item->next->previous = item->previous;
|
||||
item->previous->next = item->next;
|
||||
--_size;
|
||||
item->next = nullptr;
|
||||
item->previous = nullptr;
|
||||
item->owner = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void foreach(T functor) {
|
||||
auto current = _head.next;
|
||||
while (current != &_head) {
|
||||
functor(current);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
private:
|
||||
size_t _size;
|
||||
LinkedListItem _head;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <new>
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
template<class ValueType, size_t Capacity>
|
||||
class StaticVector {
|
||||
public:
|
||||
typedef ValueType& ValueReference;
|
||||
typedef const ValueReference& ConstValueReference;
|
||||
|
||||
StaticVector() = default;
|
||||
|
||||
~StaticVector() {
|
||||
clear();
|
||||
}
|
||||
|
||||
constexpr std::size_t capacity() const {
|
||||
return Capacity;
|
||||
}
|
||||
|
||||
std::size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
bool full() const {
|
||||
return _size == Capacity;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for(auto it = begin(); it != end(); ++it) {
|
||||
it->~value_type();
|
||||
}
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
ValueType* begin() {
|
||||
return reinterpret_cast<ValueType*>(&_buffer[0]);
|
||||
}
|
||||
|
||||
const ValueType* begin() const {
|
||||
return reinterpret_cast<const ValueType*>(&_buffer[0]);
|
||||
}
|
||||
|
||||
ValueType* end() {
|
||||
return reinterpret_cast<ValueType*>(&_buffer[_size]);
|
||||
}
|
||||
|
||||
const ValueType* end() const {
|
||||
return reinterpret_cast<const ValueType*>(&_buffer[_size]);
|
||||
}
|
||||
|
||||
ValueReference back() {
|
||||
return *reinterpret_cast<ValueType*>(&_buffer[_size - 1]);
|
||||
}
|
||||
|
||||
ConstValueReference back() const {
|
||||
return *reinterpret_cast<const ValueType*>(&_buffer[_size - 1]);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
bool emplace_back(Args&&... args) {
|
||||
if(!full()) {
|
||||
::new(&_buffer[_size++]) ValueType(std::forward<Args>(args)...);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool push_back(ConstValueReference value) {
|
||||
if (!full()) {
|
||||
::new((void*)&_buffer[_size++]) ValueType(value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void erase(const std::size_t first, const std::size_t count) {
|
||||
std::size_t last = first + count;
|
||||
if ((last <= first) || (last > _size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//move items
|
||||
auto writePtr = reinterpret_cast<ValueType*>(&_buffer[first]);
|
||||
for(std::size_t i = last; i < _size; ++i){
|
||||
*writePtr = std::move(*reinterpret_cast<ValueType*>(&_buffer[i]));
|
||||
++writePtr;
|
||||
}
|
||||
|
||||
//destroy tail
|
||||
for(std::size_t i = first; i < last; ++i){
|
||||
writePtr->~value_type();
|
||||
++writePtr;
|
||||
}
|
||||
|
||||
_size -= (last - first);
|
||||
}
|
||||
|
||||
void erase(const ValueType& value) {
|
||||
for (std::size_t i = 0; i < _size; ++i) {
|
||||
if (_buffer[i] == value) {
|
||||
erase(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void erase(const ValueType* itemPtr) {
|
||||
if(itemPtr < begin()){
|
||||
return;
|
||||
}
|
||||
std::size_t id = itemPtr - begin();
|
||||
erase(id, 1);
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
if (!empty()) {
|
||||
reinterpret_cast<ValueType*>(&_buffer[--_size])->~value_type();
|
||||
}
|
||||
}
|
||||
|
||||
ValueReference operator[](std::size_t i) {
|
||||
return *reinterpret_cast<ValueType*>(&_buffer[i]);
|
||||
}
|
||||
|
||||
ConstValueReference operator[](std::size_t i) const {
|
||||
return *reinterpret_cast<const ValueType*>(&_buffer[i]);
|
||||
}
|
||||
|
||||
bool contains(ConstValueReference value) {
|
||||
for (std::size_t i = 0; i < _size; ++i) {
|
||||
if (_buffer[i] == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t resize(std::size_t newSize) {
|
||||
if (newSize > Capacity) {
|
||||
newSize = Capacity;
|
||||
}
|
||||
|
||||
for(std::size_t i = _size; i < newSize; ++i){
|
||||
::new(reinterpret_cast<ValueType*>(&_buffer[i])) ValueType;
|
||||
}
|
||||
|
||||
for(std::size_t i = newSize; i < _size; ++i){
|
||||
reinterpret_cast<ValueType*>(&_buffer[i])->~value_type();
|
||||
}
|
||||
|
||||
_size = newSize;
|
||||
return newSize;
|
||||
}
|
||||
|
||||
private:
|
||||
typename std::aligned_storage<sizeof(ValueType), alignof(ValueType)>::type _buffer[Capacity];
|
||||
std::size_t _size = 0;
|
||||
};
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "IO/Terminal/Terminal.h"
|
||||
|
||||
namespace LFramework::Debug {
|
||||
|
||||
enum class LogLevel {
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Critical,
|
||||
Fatal
|
||||
};
|
||||
|
||||
inline const char* toString(LogLevel category) {
|
||||
switch (category) {
|
||||
case LogLevel::Debug:
|
||||
return "Debug";
|
||||
case LogLevel::Info:
|
||||
return "Info";
|
||||
case LogLevel::Warning:
|
||||
return "Warning";
|
||||
case LogLevel::Critical:
|
||||
return "Critical";
|
||||
case LogLevel::Fatal:
|
||||
return "Fatal";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
class LogWriter {
|
||||
public:
|
||||
LogWriter(LogLevel level){
|
||||
|
||||
}
|
||||
template<typename T>
|
||||
LogWriter& operator << (const T& value) {
|
||||
Terminal::out << value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~LogWriter() {
|
||||
Terminal::out << Terminal::NewLine;
|
||||
}
|
||||
};
|
||||
|
||||
inline LogWriter Log() { return LogWriter(LogLevel::Debug); }
|
||||
inline LogWriter Info() { return LogWriter(LogLevel::Info); }
|
||||
inline LogWriter Warning() { return LogWriter(LogLevel::Warning); }
|
||||
inline LogWriter Critical() { return LogWriter(LogLevel::Critical); }
|
||||
inline LogWriter Fatal() { return LogWriter(LogLevel::Fatal); }
|
||||
|
||||
|
||||
#define lfDebug() LFramework::Debug::Log()
|
||||
#define lfInfo() LFramework::Debug::Info()
|
||||
#define lfWarning() LFramework::Debug::Warning()
|
||||
#define lfCritical() LFramework::Debug::Critical()
|
||||
#define lfFatal() LFramework::Debug::Fatal()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
DetectCPU.h
|
||||
DetectEndianness.h
|
||||
DetectOS.h
|
||||
)
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef DetectCPU_h__
|
||||
#define DetectCPU_h__
|
||||
|
||||
#define LF_CPU_ARCH_ALPHA 0
|
||||
#define LF_CPU_ARCH_AMD64 1
|
||||
#define LF_CPU_ARCH_ARM 2
|
||||
#define LF_CPU_ARCH_ARM64 3
|
||||
#define LF_CPU_ARCH_BLACKFIN 4
|
||||
#define LF_CPU_ARCH_CONVEX 5
|
||||
#define LF_CPU_ARCH_IX86 6
|
||||
#define LF_CPU_ARCH_IA64 7
|
||||
#define LF_CPU_ARCH_MOTOROLA68K 8
|
||||
#define LF_CPU_ARCH_MIPS 9
|
||||
#define LF_CPU_ARCH_POWERPC 10
|
||||
#define LF_CPU_ARCH_SPARC 11
|
||||
|
||||
|
||||
#ifndef LF_CPU_ARCH
|
||||
|
||||
#if defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_ALPHA
|
||||
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_AMD64
|
||||
#elif defined(__arm__) || defined(__thumb__) || defined(__TARGET_ARCH_ARM) || defined(__TARGET_ARCH_THUMB) || defined(_ARM) || defined(_M_ARM)|| defined(_M_ARMT) || defined(__arm)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_ARM
|
||||
#elif defined(__aarch64__)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_ARM64
|
||||
#elif defined(__bfin) || defined(__BFIN__)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_BLACKFIN
|
||||
#elif defined(__convex__)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_CONVEX
|
||||
|
||||
#elif defined(i386) || defined(__i386) || defined(__i386__) || defined(__IA32__) \
|
||||
|| defined(_M_I86) || defined(_M_IX86) || defined(__X86__) || defined(_X86_) \
|
||||
|| defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || defined(__386)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_IX86
|
||||
|
||||
#elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || defined(__ia64) \
|
||||
|| defined(_M_IA64) || defined(_M_IA64) || defined(__itanium__)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_IA64
|
||||
|
||||
#elif defined(__m68k__) || defined(M68000) || defined(__MC68K__)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_MOTOROLA68K
|
||||
|
||||
#elif defined(__mips__) || defined(mips) || defined(__mips) || defined(__MIPS__)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_MIPS
|
||||
|
||||
#elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__POWERPC__) \
|
||||
|| defined(__ppc__) || defined(__ppc64__) || defined(__PPC__) || defined(__PPC64__) \
|
||||
|| defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_M_PPC) || defined(__ppc)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_POWERPC
|
||||
|
||||
#elif defined(__sparc__) || defined(__sparc)
|
||||
#define LF_CPU_ARCH LF_CPU_ARCH_MIPS
|
||||
|
||||
#else
|
||||
#error "Unknown CPU arch"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
enum class CpuArchitecture {
|
||||
Alpha = LF_CPU_ARCH_ALPHA,
|
||||
AMD64 = LF_CPU_ARCH_AMD64,
|
||||
ARM = LF_CPU_ARCH_ARM,
|
||||
ARM64 = LF_CPU_ARCH_ARM64,
|
||||
Blackfin = LF_CPU_ARCH_BLACKFIN,
|
||||
Convex = LF_CPU_ARCH_CONVEX,
|
||||
IX86 = LF_CPU_ARCH_IX86,
|
||||
IA64 = LF_CPU_ARCH_IA64,
|
||||
Motorola68K = LF_CPU_ARCH_MOTOROLA68K,
|
||||
MIPS = LF_CPU_ARCH_MIPS,
|
||||
PowerPC = LF_CPU_ARCH_POWERPC,
|
||||
SPARC = LF_CPU_ARCH_SPARC
|
||||
};
|
||||
|
||||
namespace Detect {
|
||||
static constexpr CpuArchitecture cpuArchitecture() {
|
||||
return static_cast<CpuArchitecture>(LF_CPU_ARCH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DetectCPU_h__
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef DetectEndianness_h__
|
||||
#define DetectEndianness_h__
|
||||
|
||||
#define LF_ENDIANNESS_UNKNOWN 0
|
||||
#define LF_ENDIANNESS_LITTLE 1
|
||||
#define LF_ENDIANNESS_BIG 2
|
||||
|
||||
#ifndef LF_ENDIANNESS
|
||||
|
||||
#if defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) \
|
||||
|| defined(__AARCH64EB__) || defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) \
|
||||
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) \
|
||||
|| (defined(__FLOAT_WORD_ORDER__) && (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
#define LF_ENDIANNESS LF_ENDIANNESS_BIG
|
||||
|
||||
#elif defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__THUMBEL__) \
|
||||
|| defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) \
|
||||
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \
|
||||
|| (defined(__FLOAT_WORD_ORDER__) && (__FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__))
|
||||
#define LF_ENDIANNESS LF_ENDIANNESS_LITTLE
|
||||
|
||||
#else
|
||||
#define LF_ENDIANNESS LF_ENDIANNESS_UNKNOWN
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
enum class Endianness {
|
||||
Unknown = LF_ENDIANNESS_UNKNOWN,
|
||||
Little = LF_ENDIANNESS_LITTLE,
|
||||
Big = LF_ENDIANNESS_BIG
|
||||
};
|
||||
|
||||
namespace Detect {
|
||||
|
||||
static constexpr Endianness endianness() {
|
||||
return static_cast<Endianness>(LF_ENDIANNESS);
|
||||
}
|
||||
|
||||
static Endianness endiannessRuntime() {
|
||||
union {
|
||||
uint32_t value;
|
||||
uint8_t data[sizeof(uint32_t)];
|
||||
} number;
|
||||
|
||||
number.data[0] = 0x00;
|
||||
number.data[1] = 0x01;
|
||||
number.data[2] = 0x02;
|
||||
number.data[3] = 0x03;
|
||||
|
||||
switch (number.value){
|
||||
case 0x00010203U: return Endianness::Big;
|
||||
case 0x03020100U: return Endianness::Little;
|
||||
default: return Endianness::Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // DetectEndianness_h__
|
||||
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
//OS Types
|
||||
#define LF_OS_NONE 0
|
||||
#define LF_OS_WINDOWS 1
|
||||
#define LF_OS_LINUX 2
|
||||
#define LF_OS_ANDROID 3
|
||||
#define LF_OS_FREERTOS 4
|
||||
|
||||
//BSD family
|
||||
#define LF_OS_BSD_DRAGONFLY 5
|
||||
#define LF_OS_BSD_FREE 6
|
||||
#define LF_OS_BSD_NET 7
|
||||
#define LF_OS_BSD_OPEN 8
|
||||
#define LF_OS_BSD_BSDI 9
|
||||
|
||||
//Apple
|
||||
#define LF_OS_IPHONE 10
|
||||
#define LF_OS_MAC 11
|
||||
|
||||
#define LF_OS_CYGWIN 12
|
||||
#define LF_OS_LYNX 13
|
||||
#define LF_OS_MSDOS 14
|
||||
#define LF_OS_NUCLEUS_RTOS 15
|
||||
#define LF_OS_OS2 16
|
||||
#define LF_OS_PALMOS 17
|
||||
#define LF_OS_SOLARIS 18
|
||||
|
||||
|
||||
//OS Detect
|
||||
#if !defined(LF_TARGET_OS)
|
||||
#if defined(_WIN32) || defined(_WIN16) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
|
||||
#define LF_TARGET_OS LF_OS_WINDOWS
|
||||
#elif defined(__linux__)
|
||||
#define LF_TARGET_OS LF_OS_LINUX
|
||||
#elif defined(__ANDROID__)
|
||||
#define LF_TARGET_OS LF_OS_ANDROID
|
||||
|
||||
//Detect BDS
|
||||
#elif defined(__DragonFly__)
|
||||
#define LF_TARGET_OS LF_OS_BSD_DRAGONFLY
|
||||
#elif defined(__FreeBSD__)
|
||||
#define LF_TARGET_OS LF_OS_BSD_FREE
|
||||
#elif defined(__NetBSD__)
|
||||
#define LF_TARGET_OS LF_OS_BSD_NET
|
||||
#elif defined(__OpenBSD__)
|
||||
#define LF_TARGET_OS LF_OS_BSD_OPEN
|
||||
#elif defined(__bsdi__)
|
||||
#define LF_TARGET_OS LF_OS_BSD_BSDI
|
||||
|
||||
//Detect Apple
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
#include <TargetConditionals.h>
|
||||
#if (TARGET_IPHONE_SIMULATOR == 1) || (TARGET_OS_IPHONE == 1)
|
||||
#define LF_TARGET_OS LF_OS_IPHONE
|
||||
#elif TARGET_OS_MAC == 1
|
||||
#define LF_TARGET_OS LF_OS_MAC
|
||||
#else
|
||||
#error "Unknown Apple OS"
|
||||
#endif
|
||||
|
||||
#elif defined(__CYGWIN__)
|
||||
#define LF_TARGET_OS LF_OS_CYGWIN
|
||||
#elif defined(__Lynx__)
|
||||
#define LF_TARGET_OS LF_OS_LYNX
|
||||
|
||||
|
||||
//detect MS-DOS
|
||||
#elif defined(MSDOS) || defined(__MSDOS__) || defined(_MSDOS) || defined(__DOS__)
|
||||
#define LF_TARGET_OS LF_OS_MSDOS
|
||||
|
||||
#elif defined(__nucleus__)
|
||||
#define LF_TARGET_OS LF_OS_NUCLEUS_RTOS
|
||||
|
||||
#elif defined(OS2) || defined(_OS2) || defined(__OS2__) || defined(__TOS_OS2__)
|
||||
#define LF_TARGET_OS LF_OS_OS2
|
||||
|
||||
#elif defined(__palmos__)
|
||||
#define LF_TARGET_OS LF_OS_PALMOS
|
||||
|
||||
#elif defined(sun) || defined(__sun)
|
||||
#define LF_TARGET_OS LF_OS_SOLARIS
|
||||
|
||||
#else
|
||||
#error "OS auto detect fail!"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
enum class OSType {
|
||||
None = LF_OS_NONE,
|
||||
Windows = LF_OS_WINDOWS,
|
||||
Linux = LF_OS_LINUX,
|
||||
Android = LF_OS_ANDROID,
|
||||
FreeRTOS = LF_OS_FREERTOS,
|
||||
|
||||
BSDDragonfly = LF_OS_BSD_DRAGONFLY,
|
||||
BSDFree = LF_OS_BSD_FREE,
|
||||
BSDNet = LF_OS_BSD_NET,
|
||||
BSDOpen = LF_OS_BSD_OPEN,
|
||||
BSDI = LF_OS_BSD_BSDI,
|
||||
|
||||
AppleIPhone = LF_OS_IPHONE,
|
||||
AppleMacOSX = LF_OS_MAC,
|
||||
|
||||
Cygwin = LF_OS_CYGWIN,
|
||||
Lynx = LF_OS_LYNX,
|
||||
MSDOS = LF_OS_MSDOS,
|
||||
NucleusRTOS = LF_OS_NUCLEUS_RTOS,
|
||||
OS2 = LF_OS_OS2,
|
||||
PlamOS = LF_OS_PALMOS,
|
||||
Solaris = LF_OS_SOLARIS
|
||||
};
|
||||
|
||||
namespace Detect {
|
||||
static constexpr inline OSType operatingSystem() {
|
||||
return static_cast<OSType>(LF_TARGET_OS);
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
struct Guid {
|
||||
std::uint32_t data1;
|
||||
std::uint32_t data2_3;
|
||||
std::uint32_t data4_0;
|
||||
std::uint32_t data4_1;
|
||||
|
||||
//std::uint16_t data2;
|
||||
//std::uint16_t data3;
|
||||
//std::uint8_t data4[8];
|
||||
|
||||
Guid() = default;
|
||||
|
||||
constexpr Guid(std::uint32_t data1, std::uint32_t data2_3, std::uint32_t data4_0, std::uint32_t data4_1) : data1(data1), data2_3(data2_3), data4_0(data4_0), data4_1(data4_1) {}
|
||||
|
||||
constexpr bool operator ==(const Guid& rhs) const {
|
||||
return
|
||||
(data1 == rhs.data1) &&
|
||||
(data2_3 == rhs.data2_3) &&
|
||||
(data4_0 == rhs.data4_0) &&
|
||||
(data4_1 == rhs.data4_1);
|
||||
}
|
||||
|
||||
constexpr bool operator !=(const Guid& rhs) const {
|
||||
return !(this->operator ==(rhs));
|
||||
}
|
||||
|
||||
bool isZero() const {
|
||||
return (data1 == 0) && (data2_3 == 0) && (data4_0 == 0) && (data4_1 == 0);
|
||||
}
|
||||
|
||||
Guid& operator =(const Guid& right) {
|
||||
data1 = right.data1;
|
||||
data2_3 = right.data2_3;
|
||||
data4_0 = right.data4_0;
|
||||
data4_1 = right.data4_1;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifdef WIN32
|
||||
#include <LFramework/GuidUtils.h>
|
||||
#include <LFramework/Text/Encoding.h>
|
||||
#include <Windows.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace LFramework::GuidUtils {
|
||||
LFramework::Guid newGuid() {
|
||||
LFramework::Guid result;
|
||||
if (CoCreateGuid((GUID*)&result) != S_OK) {
|
||||
throw std::runtime_error("Failed to create GUID");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LFramework::Guid fromString(const char* str) {
|
||||
LFramework::Guid iid;
|
||||
if (IIDFromString(LFramework::Text::Encoding::u8StringToWString(str).c_str(), (GUID*)&iid) != S_OK) {
|
||||
throw std::runtime_error("Failed to parse GUID");
|
||||
}
|
||||
return iid;
|
||||
}
|
||||
|
||||
std::string toString(const LFramework::Guid& guid) {
|
||||
LPOLESTR guid_str = nullptr;
|
||||
if (StringFromIID(*((const GUID*)&guid), &guid_str) != S_OK) {
|
||||
throw std::runtime_error("Failed to convert GUID to string");
|
||||
}
|
||||
std::wstring result(guid_str);
|
||||
CoTaskMemFree(guid_str);
|
||||
return LFramework::Text::Encoding::wStringToU8String(result);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <LFramework/Guid.h>
|
||||
|
||||
namespace LFramework::GuidUtils {
|
||||
LFramework::Guid newGuid();
|
||||
LFramework::Guid fromString(const char* str);
|
||||
std::string toString(const LFramework::Guid& guid);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
add_library(LFrameworkIo INTERFACE )
|
||||
add_library(LFramework::IO ALIAS LFrameworkIo )
|
||||
|
||||
|
||||
target_sources(LFrameworkIo
|
||||
INTERFACE
|
||||
Stream.h
|
||||
StreamWriter.h
|
||||
|
||||
StreamWriter.cpp
|
||||
)
|
||||
add_subdirectory(Terminal)
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
|
||||
template<typename T>
|
||||
class OutputStream {
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
enum class AdjustField {
|
||||
Left,
|
||||
Right,
|
||||
Internal
|
||||
};
|
||||
|
||||
template<typename TChar>
|
||||
class OutputStreamFormat {
|
||||
public:
|
||||
size_t fillWidth = 0;
|
||||
TChar fillChar{};
|
||||
AdjustField adjust = AdjustField::Right;
|
||||
};
|
||||
|
||||
template<>
|
||||
class OutputStream<char> {
|
||||
public:
|
||||
OutputStreamFormat<char> format;
|
||||
|
||||
virtual ~OutputStream() = default;
|
||||
virtual int write(const char* data, int length) = 0;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "StreamWriter.h"
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
OutputStream<char>& operator <<(OutputStream<char>& stream, const char* value){
|
||||
auto len = strlen(value);
|
||||
OutputStreamFormatter::fieldBegin(stream, len);
|
||||
stream.write(value, static_cast<int>(len));
|
||||
OutputStreamFormatter::fieldEnd(stream, len);
|
||||
return stream;
|
||||
}
|
||||
|
||||
OutputStream<char>& operator <<(OutputStream<char>& stream, char value){
|
||||
OutputStreamFormatter::fieldBegin(stream, 1);
|
||||
stream.write(&value, 1);
|
||||
OutputStreamFormatter::fieldEnd(stream, 1);
|
||||
return stream;
|
||||
}
|
||||
|
||||
OutputStream<char>& operator <<(OutputStream<char>& stream, bool value){
|
||||
return stream << (value ? "true" : "false");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
#include "../TypeTraits/Integral.h"
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
|
||||
template<typename T, typename = ::std::enable_if_t<::std::is_integral_v<T>>>
|
||||
struct DecimalFormat {
|
||||
DecimalFormat(T value) :value(value) {}
|
||||
T value;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename T, bool UpperCase = false, size_t MinWidth = 0, typename = ::std::enable_if_t<::std::is_integral_v<T>>>
|
||||
struct HexFormat {
|
||||
HexFormat(T value) :value(value) {}
|
||||
T value;
|
||||
};
|
||||
|
||||
template<bool UpperCase = false, size_t MinWidth = 0, typename T>
|
||||
HexFormat<T, UpperCase, MinWidth> hex(T value) {
|
||||
return HexFormat<T, UpperCase, MinWidth>(value);
|
||||
}
|
||||
|
||||
|
||||
class OutputStreamFormatter {
|
||||
public:
|
||||
|
||||
static void writeFiller(OutputStream<char>& stream, size_t itemSize){
|
||||
auto fillSize = stream.format.fillWidth - itemSize;
|
||||
for(size_t i = 0; i < fillSize; ++i){
|
||||
stream.write(&stream.format.fillChar, 1);
|
||||
}
|
||||
stream.format.fillWidth = 0;
|
||||
}
|
||||
|
||||
static void fieldBegin(OutputStream<char>& stream, size_t itemSize){
|
||||
|
||||
if(stream.format.adjust == AdjustField::Right || stream.format.adjust == AdjustField::Internal){
|
||||
if(itemSize < stream.format.fillWidth){
|
||||
writeFiller(stream, itemSize);
|
||||
}
|
||||
stream.format.fillWidth = 0;
|
||||
}
|
||||
}
|
||||
static void fieldEnd(OutputStream<char>& stream, size_t itemSize){
|
||||
if(stream.format.adjust == AdjustField::Left){
|
||||
if(itemSize < stream.format.fillWidth){
|
||||
writeFiller(stream, itemSize);
|
||||
}
|
||||
stream.format.fillWidth = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
size_t outputStreamWriteInteger (OutputStream<char>& stream, DecimalFormat<T> formatter){
|
||||
auto v = formatter.value;
|
||||
|
||||
static constexpr size_t BufferSize = TypeTraits::Integral::maxDigitsCount<T>() + (::std::is_signed_v<T> ? 1 : 0); //Add space for sign
|
||||
char result[BufferSize];
|
||||
|
||||
size_t writePos = BufferSize - 1;
|
||||
|
||||
if constexpr(::std::is_signed_v<T>) {
|
||||
//print negative numbers
|
||||
if (v < 0) {
|
||||
do {
|
||||
auto next = v / 10;
|
||||
result[writePos--] = static_cast<char>('0' - (v - (next * 10)));
|
||||
v = next;
|
||||
} while (v != 0);
|
||||
result[writePos] = '-';
|
||||
|
||||
auto fieldSize = BufferSize - writePos;
|
||||
|
||||
if(fieldSize < stream.format.fillWidth){
|
||||
|
||||
if(stream.format.adjust == AdjustField::Internal){
|
||||
stream.write(&result[writePos], 1);
|
||||
OutputStreamFormatter::writeFiller(stream, fieldSize);
|
||||
stream.write(&result[writePos + 1], fieldSize - 1);
|
||||
}else {
|
||||
OutputStreamFormatter::fieldBegin(stream, fieldSize);
|
||||
stream.write(&result[writePos], fieldSize);
|
||||
OutputStreamFormatter::fieldEnd(stream, fieldSize);
|
||||
}
|
||||
|
||||
}else{
|
||||
stream.write(&result[writePos], fieldSize);
|
||||
}
|
||||
|
||||
return fieldSize;
|
||||
}
|
||||
}
|
||||
|
||||
//print positive numbers
|
||||
do {
|
||||
result[writePos--] = static_cast<char>('0' + (v % 10));
|
||||
v /= 10;
|
||||
} while (v != 0);
|
||||
|
||||
auto fieldSize = BufferSize - writePos - 1;
|
||||
|
||||
OutputStreamFormatter::fieldBegin(stream, fieldSize);
|
||||
stream.write(&result[writePos + 1], fieldSize);
|
||||
OutputStreamFormatter::fieldEnd(stream, fieldSize);
|
||||
|
||||
return fieldSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename T, bool UpperCase, size_t MinWidth>
|
||||
OutputStream<char>& operator <<(OutputStream<char>& stream, const HexFormat<T, UpperCase, MinWidth>& formatter){
|
||||
auto v = static_cast<typename std::make_unsigned<T>::type>(formatter.value);
|
||||
|
||||
const auto bufferSize = sizeof(T) * 2;
|
||||
char buffer[bufferSize];
|
||||
size_t end = bufferSize;
|
||||
|
||||
char a = 'a';
|
||||
if constexpr(UpperCase) {
|
||||
a = 'A';
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < bufferSize; ++i) {
|
||||
auto digit = v & 0xf;
|
||||
if (digit < 10) {
|
||||
buffer[end - 1] = static_cast<char>('0' + digit);
|
||||
}
|
||||
else {
|
||||
buffer[end - 1] = static_cast<char>(a + (digit - 10));
|
||||
}
|
||||
v >>= 4;
|
||||
--end;
|
||||
if(v == 0 && (i + 1) >= MinWidth) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto dataSize = bufferSize - end;
|
||||
|
||||
OutputStreamFormatter::fieldBegin(stream, dataSize);
|
||||
stream.write(&buffer[end], dataSize);
|
||||
OutputStreamFormatter::fieldEnd(stream, dataSize);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
OutputStream<char>& operator <<(OutputStream<char>& stream, const char* value);
|
||||
OutputStream<char>& operator <<(OutputStream<char>& stream, char value);
|
||||
OutputStream<char>& operator <<(OutputStream<char>& stream, bool value);
|
||||
|
||||
template<typename T>
|
||||
OutputStream<char>& operator <<(OutputStream<char>& stream, DecimalFormat<T> formatter){
|
||||
outputStreamWriteInteger(stream, formatter);
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename = std::enable_if_t<
|
||||
std::is_integral_v<T> &&
|
||||
std::is_arithmetic_v<T> &&
|
||||
!std::is_same_v<T, bool> &&
|
||||
!std::is_same_v<T, char>
|
||||
>>
|
||||
OutputStream<char>& operator <<(OutputStream<char>& stream, T value){
|
||||
return stream << DecimalFormat<T>(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
add_library(LFrameworkIoTerminal INTERFACE )
|
||||
add_library(LFramework::IO::Terminal ALIAS LFrameworkIoTerminal )
|
||||
|
||||
target_sources(LFrameworkIoTerminal
|
||||
INTERFACE
|
||||
Terminal.h
|
||||
TerminalAnsi.h
|
||||
TerminalStream.h
|
||||
|
||||
Terminal.cpp
|
||||
TerminalStreamCout.cpp
|
||||
TerminalStreamUartStm32.cpp
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "Terminal.h"
|
||||
|
||||
namespace LFramework::Terminal {
|
||||
Stream out;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "TerminalStream.h"
|
||||
|
||||
namespace LFramework::Terminal {
|
||||
static const char* NewLine = "\r\n";
|
||||
extern Stream out;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#define LF_TERMINAL_ANSI_ESC "\033"
|
||||
|
||||
#define LF_TERMINAL_ANSI_SS2 LF_TERMINAL_ANSI_ESC"N"
|
||||
#define LF_TERMINAL_ANSI_SS3 LF_TERMINAL_ANSI_ESC"O"
|
||||
#define LF_TERMINAL_ANSI_DCS LF_TERMINAL_ANSI_ESC"P"
|
||||
#define LF_TERMINAL_ANSI_CSI LF_TERMINAL_ANSI_ESC"["
|
||||
#define LF_TERMINAL_ANSI_ST LF_TERMINAL_ANSI_ESC"\\"
|
||||
#define LF_TERMINAL_ANSI_OSC LF_TERMINAL_ANSI_ESC"]"
|
||||
#define LF_TERMINAL_ANSI_SOS LF_TERMINAL_ANSI_ESC"X"
|
||||
#define LF_TERMINAL_ANSI_PM LF_TERMINAL_ANSI_ESC"^"
|
||||
#define LF_TERMINAL_ANSI_APC LF_TERMINAL_ANSI_ESC"_"
|
||||
#define LF_TERMINAL_ANSI_RIS LF_TERMINAL_ANSI_ESC"c"
|
||||
|
||||
#if defined(LF__TERMINAL_ANSI_CODES_ENABLE)
|
||||
#define LF_TERMINAL_ANSI_CODE(__id, __suffix) LF_TERMINAL_ANSI_CSI #__id #__suffix
|
||||
#else
|
||||
#define LF_TERMINAL_ANSI_CODE(__id, __suffix) ""
|
||||
#endif
|
||||
|
||||
#define LF_TERMINAL_ANSI_GRAPHICS_CODE(__id) LF_TERMINAL_ANSI_CODE(__id, m)
|
||||
#define LF_TERMINAL_ANSI_CODE_DEFINE(__codeName, __id) inline const char (& __codeName ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_GRAPHICS_CODE(__id))>>::value] { return LF_TERMINAL_ANSI_GRAPHICS_CODE(__id); }
|
||||
|
||||
|
||||
namespace LFramework::Terminal::Ansi {
|
||||
namespace Color {
|
||||
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Black, 30)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Red, 31)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Green, 32)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Yellow, 33)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Blue, 34)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Magenta, 35)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Cyan, 36)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightGray, 37)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(DarkGray, 90)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightRed, 91)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightGreen, 92)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightYellow, 93)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightBlue, 94)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightMagenta, 95)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightCyan, 96)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(White, 97)
|
||||
|
||||
//Background
|
||||
namespace BG {
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Black, 40)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Red, 41)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Green, 42)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Yellow, 43)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Blue, 44)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Magenta, 45)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Cyan, 46)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightGray, 47)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(DarkGray, 100)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightRed, 101)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightGreen, 102)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightYellow, 103)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightBlue, 104)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightMagenta, 105)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(LightCyan, 106)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(White, 107)
|
||||
}
|
||||
}
|
||||
|
||||
namespace Format {
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(BoldSet, 1)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(BoldReset, 21)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(UnderlinedSet, 4)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(UnderlinedReset, 24)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(BlinkSet, 5)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(BlinkReset, 25)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(ReverseSet, 7)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(ReverseReset, 27)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(ResetAll, 0)
|
||||
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Framed, 51)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Encircled, 52)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(Overlined, 53)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(ClearFramedEncircled, 54)
|
||||
LF_TERMINAL_ANSI_CODE_DEFINE(ClearOverlined, 55)
|
||||
}
|
||||
|
||||
|
||||
namespace Cursor {
|
||||
inline const char (& EnableBlinking ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"?12h")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"?12h";
|
||||
}
|
||||
inline const char (& DisableBlinking ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"?12l")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"?12l";
|
||||
}
|
||||
inline const char (& Show ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"?25h")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"?25h";
|
||||
}
|
||||
inline const char (& Hide ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"?25l")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"?25l";
|
||||
}
|
||||
|
||||
|
||||
inline const char (& MoveHome ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"H")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"H";
|
||||
}
|
||||
|
||||
inline const char (& SavePosition ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"s")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"s";
|
||||
}
|
||||
|
||||
inline const char (& RestorePosition ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"u")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"u";
|
||||
}
|
||||
}
|
||||
|
||||
namespace TextEdit {
|
||||
inline const char (& EraseLineCurToBeg ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"K")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"K";
|
||||
}
|
||||
inline const char (& EraseLineBegToCur ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"1K")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"1K";
|
||||
}
|
||||
inline const char (& EraseLine ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"2K")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"2K";
|
||||
}
|
||||
}
|
||||
|
||||
namespace Viewport {
|
||||
inline const char (& ClearScreenAfterCursor ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"0J")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"0J";
|
||||
}
|
||||
inline const char (& ClearScreenBeforeCursor ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"1J")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"1J";
|
||||
}
|
||||
inline const char (& ClearScreen ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"2J")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"2J";
|
||||
}
|
||||
inline const char (& ClearScreenAndScrollback ())[std::extent<std::remove_reference_t<decltype(LF_TERMINAL_ANSI_CSI"3J")>>::value] {
|
||||
return LF_TERMINAL_ANSI_CSI"3J";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "../StreamWriter.h"
|
||||
|
||||
namespace LFramework::Terminal {
|
||||
class Stream : public OutputStream<char>{
|
||||
public:
|
||||
int write(const char* data, int length) override final;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "TerminalStream.h"
|
||||
|
||||
|
||||
#ifdef LF_TERMINAL_STREAM_COUT
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace LFramework::Terminal {
|
||||
int LFramework::Terminal::Stream::write(const char* data, int length) {
|
||||
std::cout.write(data, length);
|
||||
std::cout.flush();
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#include "TerminalStream.h"
|
||||
|
||||
#ifdef LF_TERMINAL_STREAM_USART_STM32
|
||||
|
||||
|
||||
#ifdef STM32F7
|
||||
#include <stm32f7xx_hal.h>
|
||||
#include <stm32f7xx_ll_usart.h>
|
||||
#elif STM32H7
|
||||
#include <stm32h7xx_hal.h>
|
||||
#include <stm32h7xx_ll_usart.h>
|
||||
#endif
|
||||
|
||||
namespace LFramework::Terminal {
|
||||
|
||||
|
||||
int LFramework::Terminal::Stream::write(const char* data, int length) {
|
||||
for(int i = 0; i < length; ++i){
|
||||
while (!LL_USART_IsActiveFlag_TXE(LF_TERMINAL_STREAM_USART)) {}
|
||||
LL_USART_TransmitData8(LF_TERMINAL_STREAM_USART, *data);
|
||||
++data;
|
||||
}
|
||||
|
||||
//if(HAL_OK == HAL_UART_Transmit(&huart1, reinterpret_cast<uint8_t*>(const_cast<char*>(data)), length, HAL_MAX_DELAY)){
|
||||
// return length;
|
||||
//}
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
add_library(LFrameworkInput INTERFACE )
|
||||
add_library(LFramework::Input ALIAS LFrameworkInput )
|
||||
|
||||
target_sources(LFrameworkInput
|
||||
INTERFACE
|
||||
Input.h
|
||||
Keyboard.h
|
||||
KeysStateManager.h
|
||||
Mouse.h
|
||||
|
||||
Input.cpp
|
||||
Keyboard.cpp
|
||||
Mouse.cpp
|
||||
)
|
||||
@@ -0,0 +1,150 @@
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Input.h"
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
Input::Input() {
|
||||
auto hInst = GetModuleHandle(NULL);
|
||||
register_input_window_class(hInst);
|
||||
|
||||
_input_window = CreateWindow(_class_name, TEXT("RawInputWindow"), WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL);
|
||||
|
||||
if (_input_window == NULL) {
|
||||
std::cout << "Failed to create input window!" << std::endl;
|
||||
}
|
||||
SetWindowLongPtr(_input_window, GWLP_USERDATA, (LONG_PTR)this);
|
||||
ShowWindow(_input_window, SW_HIDE);
|
||||
UpdateWindow(_input_window);
|
||||
|
||||
enumerate_devices();
|
||||
|
||||
RAWINPUTDEVICE Rid[2];
|
||||
|
||||
Rid[0].usUsagePage = 0x01; //HID mouse
|
||||
Rid[0].usUsage = 0x02;
|
||||
Rid[0].dwFlags = RIDEV_INPUTSINK;
|
||||
Rid[0].hwndTarget = _input_window;
|
||||
|
||||
Rid[1].usUsagePage = 0x01; //HID keyboard
|
||||
Rid[1].usUsage = 0x06;
|
||||
Rid[1].dwFlags = RIDEV_INPUTSINK;
|
||||
Rid[1].hwndTarget = _input_window;
|
||||
|
||||
if (RegisterRawInputDevices(Rid, 2, sizeof(Rid[0])) == FALSE) {
|
||||
std::cout << "Failed to register raw input devices!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Input> Input::Instance() {
|
||||
static std::shared_ptr<Input> instance(new Input());
|
||||
return instance;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Mouse>>& Input::mice() const{
|
||||
return _mice;
|
||||
}
|
||||
|
||||
void Input::strobe(){
|
||||
|
||||
MSG msg = { 0 };
|
||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
for(auto mouse : _mice){
|
||||
mouse->Strobe();
|
||||
}
|
||||
for(auto kb : _keyboards){
|
||||
kb->Strobe();
|
||||
}
|
||||
}
|
||||
|
||||
void Input::enumerate_devices() {
|
||||
_mice.clear();
|
||||
|
||||
UINT numDevices;
|
||||
GetRawInputDeviceList(NULL, &numDevices, sizeof(RAWINPUTDEVICELIST));
|
||||
if (numDevices == 0) return;
|
||||
|
||||
std::vector<RAWINPUTDEVICELIST> deviceList(numDevices);
|
||||
GetRawInputDeviceList(&deviceList[0], &numDevices, sizeof(RAWINPUTDEVICELIST));
|
||||
|
||||
std::vector<wchar_t> deviceNameData;
|
||||
std::wstring deviceName;
|
||||
for (UINT i = 0; i < numDevices; ++i) {
|
||||
const RAWINPUTDEVICELIST& device = deviceList[i];
|
||||
if (device.dwType == RIM_TYPEMOUSE) {
|
||||
_mice.push_back(std::make_shared<Mouse>(device.hDevice));
|
||||
} else if (device.dwType == RIM_TYPEKEYBOARD) {
|
||||
_keyboards.push_back(std::make_shared<Keyboard>(device.hDevice));
|
||||
} else {
|
||||
std::cout << "HID device" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ATOM Input::register_input_window_class(HINSTANCE hinstance) {
|
||||
|
||||
WNDCLASSEX wcex;
|
||||
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcex.lpfnWndProc = &Input::wnd_proc;
|
||||
wcex.cbClsExtra = 0;
|
||||
wcex.cbWndExtra = 0;
|
||||
wcex.hInstance = hinstance;
|
||||
wcex.hIcon = LoadIcon(wcex.hInstance, IDI_APPLICATION);
|
||||
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||
wcex.lpszMenuName = NULL;
|
||||
wcex.lpszClassName = _class_name;
|
||||
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
|
||||
return RegisterClassEx(&wcex);
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Keyboard>>& Input::keyboards() const
|
||||
{
|
||||
return _keyboards;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK Input::wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
auto input = (Input*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
||||
switch (message) {
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
case WM_INPUT: {
|
||||
UINT dwSize;
|
||||
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
|
||||
LPBYTE lpb = new BYTE[dwSize];
|
||||
|
||||
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
|
||||
OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
|
||||
}
|
||||
|
||||
RAWINPUT* raw = (RAWINPUT*)lpb;
|
||||
|
||||
if (raw->header.dwType == RIM_TYPEKEYBOARD) {
|
||||
auto device = std::find_if(input->_keyboards.begin(), input->_keyboards.end(), [raw](std::shared_ptr<Keyboard> m) {return m->GetHandle() == raw->header.hDevice; });
|
||||
if (device != input->_keyboards.end()) {
|
||||
(*device)->ApplyInput(*raw);
|
||||
}
|
||||
} else if (raw->header.dwType == RIM_TYPEMOUSE) {
|
||||
auto device = std::find_if(input->_mice.begin(), input->_mice.end(), [raw](std::shared_ptr<Mouse> m) {return m->GetHandle() == raw->header.hDevice; });
|
||||
if(device != input->_mice.end()){
|
||||
(*device)->ApplyInput(*raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <Windows.h>
|
||||
#include <vector>
|
||||
#include "Mouse.h"
|
||||
#include "Keyboard.h"
|
||||
|
||||
class Input {
|
||||
public:
|
||||
Input();
|
||||
static std::shared_ptr<Input> Instance();
|
||||
const std::vector<std::shared_ptr<Mouse>>& mice() const;
|
||||
const std::vector<std::shared_ptr<Keyboard>>& keyboards() const;
|
||||
void strobe();
|
||||
private:
|
||||
void enumerate_devices();
|
||||
ATOM register_input_window_class(HINSTANCE hinstance);
|
||||
static LRESULT CALLBACK wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
HWND _input_window;
|
||||
const TCHAR* _class_name = TEXT("RawInputWindow");
|
||||
std::vector<std::shared_ptr<Mouse>> _mice;
|
||||
std::vector<std::shared_ptr<Keyboard>> _keyboards;
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Keyboard.h"
|
||||
#include <iostream>
|
||||
|
||||
Keyboard::Keyboard(HANDLE hdevice) :_hdevice(hdevice) {
|
||||
UINT dataSize;
|
||||
GetRawInputDeviceInfo(hdevice, RIDI_DEVICENAME, nullptr, &dataSize);
|
||||
if (dataSize) {
|
||||
std::wstring name_data;
|
||||
name_data.resize(dataSize);
|
||||
UINT result = GetRawInputDeviceInfo(hdevice, RIDI_DEVICENAME, &name_data[0], &dataSize);
|
||||
if (result != UINT_MAX) {
|
||||
_name = name_data;
|
||||
}
|
||||
}
|
||||
|
||||
RID_DEVICE_INFO deviceInfo;
|
||||
deviceInfo.cbSize = sizeof deviceInfo;
|
||||
dataSize = sizeof deviceInfo;
|
||||
UINT result = GetRawInputDeviceInfo(hdevice, RIDI_DEVICEINFO, &deviceInfo, &dataSize);
|
||||
_function_keys_count = deviceInfo.keyboard.dwNumberOfFunctionKeys;
|
||||
_keys_count = deviceInfo.keyboard.dwNumberOfKeysTotal;
|
||||
_indicators_count = deviceInfo.keyboard.dwNumberOfIndicators;
|
||||
}
|
||||
|
||||
void Keyboard::Strobe() {
|
||||
_keys_manager.strobe();
|
||||
}
|
||||
|
||||
HANDLE Keyboard::GetHandle() const {
|
||||
return _hdevice;
|
||||
}
|
||||
|
||||
bool Keyboard::GetKeyState(uint32_t key_index) const {
|
||||
return _keys_manager._key_states[key_index];
|
||||
}
|
||||
|
||||
bool Keyboard::GetKeyPressed(uint32_t key_index) const {
|
||||
return _keys_manager._key_pressed[key_index];
|
||||
}
|
||||
|
||||
bool Keyboard::GetKeyReleased(uint32_t key_index) const {
|
||||
return _keys_manager._key_released[key_index];
|
||||
}
|
||||
|
||||
void Keyboard::ApplyInput(RAWINPUT input) {
|
||||
if (input.data.keyboard.Flags == RI_KEY_BREAK)
|
||||
_keys_manager._key_states[input.data.keyboard.VKey] = false;
|
||||
else
|
||||
_keys_manager._key_states[input.data.keyboard.VKey] = true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include "KeysStateManager.h"
|
||||
|
||||
class Keyboard {
|
||||
public:
|
||||
friend class Input;
|
||||
Keyboard(HANDLE hdevice);
|
||||
|
||||
HANDLE GetHandle() const;
|
||||
bool GetKeyState(uint32_t key_index) const;
|
||||
bool GetKeyPressed(uint32_t key_index) const;
|
||||
bool GetKeyReleased(uint32_t key_index) const;
|
||||
private:
|
||||
void Strobe();
|
||||
void ApplyInput(RAWINPUT input);
|
||||
HANDLE _hdevice;
|
||||
std::wstring _name;
|
||||
uint32_t _function_keys_count;
|
||||
uint32_t _keys_count;
|
||||
uint32_t _indicators_count;
|
||||
|
||||
KeysStateManager<0xff, Keyboard> _keys_manager;
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef KeysStateManager_h__
|
||||
#define KeysStateManager_h__
|
||||
|
||||
template<uint32_t Count, typename Owner>
|
||||
class KeysStateManager {
|
||||
public:
|
||||
friend Owner;
|
||||
KeysStateManager()
|
||||
{
|
||||
for (uint32_t i = 0; i < Count; ++i) {
|
||||
_key_states[i] = _key_pressed[i] = _key_released[i] = _key_states_previous[i] = false;
|
||||
}
|
||||
}
|
||||
void strobe();
|
||||
private:
|
||||
bool _key_states[Count];
|
||||
bool _key_states_previous[Count];
|
||||
bool _key_pressed[Count];
|
||||
bool _key_released[Count];
|
||||
};
|
||||
|
||||
template <uint32_t Count, typename Owner>
|
||||
void KeysStateManager<Count, Owner>::strobe(){
|
||||
|
||||
|
||||
for (uint32_t i = 0; i < Count; ++i) {
|
||||
if (_key_states[i]) { //if now pressed
|
||||
if (_key_states_previous[i]) {//if was pressed
|
||||
_key_pressed[i] = false;
|
||||
} else {
|
||||
_key_pressed[i] = true;
|
||||
}
|
||||
} else {
|
||||
_key_pressed[i] = false;
|
||||
}
|
||||
if (!_key_states[i]) { //not pressed
|
||||
if (_key_states_previous[i]) {//if was pressed
|
||||
_key_released[i] = true;
|
||||
} else {
|
||||
_key_released[i] = false;
|
||||
}
|
||||
} else {
|
||||
_key_released[i] = false;
|
||||
}
|
||||
_key_states_previous[i] = _key_states[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // KeysStateManager_h__
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Mouse.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
Mouse::Mouse(HANDLE handle) :_handle(handle){
|
||||
_deltax = _deltay = _deltax_integral = _deltay_integral = 0;
|
||||
|
||||
UINT dataSize;
|
||||
GetRawInputDeviceInfo(_handle, RIDI_DEVICENAME, nullptr, &dataSize);
|
||||
if (dataSize) {
|
||||
_name.resize(dataSize);
|
||||
UINT result = GetRawInputDeviceInfo(_handle, RIDI_DEVICENAME, &_name[0], &dataSize);
|
||||
if (result != UINT_MAX) {
|
||||
_name.assign(_name.begin(), _name.end());
|
||||
}
|
||||
}
|
||||
|
||||
RID_DEVICE_INFO deviceInfo;
|
||||
deviceInfo.cbSize = sizeof deviceInfo;
|
||||
dataSize = sizeof deviceInfo;
|
||||
UINT result = GetRawInputDeviceInfo(_handle, RIDI_DEVICEINFO, &deviceInfo, &dataSize);
|
||||
if (result != UINT_MAX) {
|
||||
_buttons_count = deviceInfo.mouse.dwNumberOfButtons;
|
||||
_has_horizontal_wheel = deviceInfo.mouse.fHasHorizontalWheel == TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE Mouse::GetHandle() const{
|
||||
return _handle;
|
||||
}
|
||||
|
||||
bool Mouse::GetKeyState(uint32_t key_index) const{
|
||||
return _keys_manager._key_states[key_index];
|
||||
}
|
||||
|
||||
bool Mouse::GetKeyPressed(uint32_t key_index) const{
|
||||
return _keys_manager._key_pressed[key_index];
|
||||
}
|
||||
|
||||
bool Mouse::GetKeyReleased(uint32_t key_index) const{
|
||||
return _keys_manager._key_released[key_index];
|
||||
}
|
||||
|
||||
void Mouse::Strobe() {
|
||||
_deltax = _deltax_integral;
|
||||
_deltax_integral = 0;
|
||||
_deltay = _deltay_integral;
|
||||
_deltay_integral = 0;
|
||||
_keys_manager.strobe();
|
||||
}
|
||||
|
||||
int32_t Mouse::GetDx() const{
|
||||
return _deltax;
|
||||
}
|
||||
|
||||
int32_t Mouse::GetDy() const{
|
||||
return _deltay;
|
||||
}
|
||||
|
||||
void Mouse::ApplyInput(RAWINPUT input) {
|
||||
//input.data.mouse.lLastX
|
||||
_deltax_integral += input.data.mouse.lLastX;
|
||||
_deltay_integral += input.data.mouse.lLastY;
|
||||
|
||||
RAWMOUSE data = input.data.mouse;
|
||||
for (uint32_t i = 0; i < maxButtonsCount; ++i){
|
||||
if((data.ulButtons & (1 << (i << 1))) != 0){
|
||||
_keys_manager._key_states[i] = true;
|
||||
}
|
||||
if ((data.ulButtons & (2 << (i << 1))) != 0) {
|
||||
_keys_manager._key_states[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <cstdint>
|
||||
#include "KeysStateManager.h"
|
||||
#include <string>
|
||||
|
||||
class Mouse {
|
||||
public:
|
||||
static constexpr uint32_t maxButtonsCount = 5;
|
||||
friend class Input;
|
||||
Mouse(HANDLE handle);
|
||||
HANDLE GetHandle() const;
|
||||
bool GetKeyState(uint32_t key_index) const;
|
||||
bool GetKeyPressed(uint32_t key_index) const;
|
||||
bool GetKeyReleased(uint32_t key_index) const;
|
||||
int32_t GetDx() const;
|
||||
int32_t GetDy() const;
|
||||
private:
|
||||
void Strobe();
|
||||
void ApplyInput(RAWINPUT input);
|
||||
LONG _deltax_integral;
|
||||
LONG _deltay_integral;
|
||||
LONG _deltax;
|
||||
LONG _deltay;
|
||||
KeysStateManager<maxButtonsCount, Mouse> _keys_manager;
|
||||
HANDLE _handle;
|
||||
std::wstring _name;
|
||||
uint32_t _buttons_count;
|
||||
bool _has_horizontal_wheel;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
Mcu.h
|
||||
)
|
||||
add_subdirectory(DMA)
|
||||
add_subdirectory(stm32)
|
||||
add_subdirectory(USBDevice)
|
||||
@@ -0,0 +1,4 @@
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
DMA.h
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* DMA.h
|
||||
*
|
||||
* Created on: 25 ñåíò. 2017 ã.
|
||||
* Author: l-pro
|
||||
*/
|
||||
|
||||
#ifndef LFRAMEWORK_MCU_DMA_DMA_H_
|
||||
#define LFRAMEWORK_MCU_DMA_DMA_H_
|
||||
|
||||
|
||||
namespace EmbeddedFramework {
|
||||
|
||||
class DMA{
|
||||
|
||||
};
|
||||
|
||||
template<int Number, int Channel>
|
||||
class DmaIrqHandler{
|
||||
|
||||
};
|
||||
|
||||
template<int Number, int Channel>
|
||||
class DmaCallback {
|
||||
public:
|
||||
/*static void error();
|
||||
static void complete();
|
||||
static void halfComplete();*/
|
||||
};
|
||||
|
||||
|
||||
#endif /* LFRAMEWORK_MCU_DMA_DMA_H_ */
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Mcu.h
|
||||
*
|
||||
* Created on: 19 July. 2017 ã.
|
||||
* Author: l-pro
|
||||
*/
|
||||
|
||||
#ifndef MCUFRAMEWORK_MCU_H_
|
||||
#define MCUFRAMEWORK_MCU_H_
|
||||
|
||||
#include "LFrameworkConfig.h"
|
||||
#include <cstddef>
|
||||
|
||||
#ifndef CMSIS_DEVICE_FILE
|
||||
#error "CMSIS_DEVICE_FILE path not defined in McuConfig.h"
|
||||
#else
|
||||
#include CMSIS_DEVICE_FILE
|
||||
#endif
|
||||
|
||||
namespace LFramework {
|
||||
namespace Mcu {
|
||||
inline size_t getCurrentInterruptHandlerIndex(){
|
||||
return __get_IPSR();
|
||||
}
|
||||
|
||||
inline bool isHandlingInterrupt() {
|
||||
return getCurrentInterruptHandlerIndex() != 0;
|
||||
}
|
||||
|
||||
inline bool isDebuggerAttached() {
|
||||
return (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) != 0;
|
||||
}
|
||||
|
||||
inline void setBreakpoint() {
|
||||
if(isDebuggerAttached){
|
||||
__BKPT(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MCUFRAMEWORK_MCU_H_ */
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(PHY)
|
||||
@@ -0,0 +1,9 @@
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
Stm32FsPhy.h
|
||||
UsbDeviceSoftPhy.h
|
||||
UsbInternalFsDeviceDriver.h
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef Stm32FsPhy_h__
|
||||
#define Stm32FsPhy_h__
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
template<typename Child>
|
||||
class Stm32FsPhy {
|
||||
public:
|
||||
/*void UsbDevice::start() {
|
||||
HAL_PCD_Start(_pcd);
|
||||
}*/
|
||||
|
||||
//UsbDevice(PCD_HandleTypeDef* pcd);
|
||||
|
||||
/*void initPHY() {
|
||||
|
||||
_pcd->Instance = USB_OTG_FS;
|
||||
_pcd->Init.dev_endpoints = 1;
|
||||
_pcd->Init.speed = PCD_SPEED_FULL;
|
||||
_pcd->Init.dma_enable = DISABLE;
|
||||
_pcd->Init.Sof_enable = DISABLE;
|
||||
_pcd->Init.ep0_mps = 64;
|
||||
_pcd->Init.phy_itface = PCD_PHY_EMBEDDED;
|
||||
_pcd->Init.low_power_enable = DISABLE;
|
||||
_pcd->Init.lpm_enable = DISABLE;
|
||||
_pcd->Init.vbus_sensing_enable = DISABLE;
|
||||
_pcd->Init.use_dedicated_ep1 = DISABLE;
|
||||
//_pcd->Init.battery_charging_enable = DISABLE;
|
||||
HAL_PCD_Init(_pcd);
|
||||
|
||||
//HAL_PCD_SetRxFiFo(&USBD_PCD, 0x80);
|
||||
//HAL_PCD_SetTxFiFo(&USBD_PCD, 0, 0x20);
|
||||
//HAL_PCD_SetTxFiFo(&USBD_PCD, 1, 0x40);
|
||||
|
||||
HAL_PCD_SetRxFiFo(_pcd, 0x80);
|
||||
HAL_PCD_SetTxFiFo(_pcd, 0, 0x40);
|
||||
HAL_PCD_SetTxFiFo(_pcd, 1, 0x80);
|
||||
}*/
|
||||
|
||||
void connect() {
|
||||
//HAL_PCD_DevConnect(_pcd);
|
||||
}
|
||||
|
||||
void disconnect() {
|
||||
//HAL_PCD_DevDisconnect(_pcd);
|
||||
}
|
||||
|
||||
//PCD_HandleTypeDef* _pcd;
|
||||
};
|
||||
}
|
||||
#endif // Stm32FsPhy_h__
|
||||
@@ -0,0 +1,211 @@
|
||||
#ifndef UsbDeviceSoftPhy_h__
|
||||
#define UsbDeviceSoftPhy_h__
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
namespace LFramework {
|
||||
namespace USB {
|
||||
|
||||
static const char* StageStrings[] = { "WaitForConnection", "Reset1", "GetDeviceDescriptor", "Reset2" };
|
||||
|
||||
class SoftEndpoint {
|
||||
public:
|
||||
bool isIn = false;
|
||||
bool isOpen = false;
|
||||
uint8_t maxPacketSize = 0;
|
||||
bool receiving = false;
|
||||
std::queue<std::vector<uint8_t>> packets;
|
||||
};
|
||||
|
||||
template<typename Device>
|
||||
class UsbDeviceSoftPhy {
|
||||
public:
|
||||
static constexpr uint8_t MaxEndpointsCount = 16;
|
||||
static constexpr uint8_t MaxPacketSize = 64;
|
||||
|
||||
|
||||
void connect() {
|
||||
_connected = true;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
_running = true;
|
||||
setStage(Stage::WaitForConnection);
|
||||
_usbThread = std::thread(std::bind(&UsbDeviceSoftPhy::threadHandler, this));
|
||||
}
|
||||
|
||||
void stop() {
|
||||
|
||||
if (_running) {
|
||||
_running = false;
|
||||
_usbThread.join;
|
||||
}
|
||||
}
|
||||
|
||||
enum class Stage {
|
||||
WaitForConnection,
|
||||
Reset1,
|
||||
GetDeviceDescriptor,
|
||||
Reset2,
|
||||
StageCount
|
||||
};
|
||||
|
||||
|
||||
|
||||
UsbDeviceSoftPhy() {
|
||||
_stageHandlers[static_cast<int>(Stage::WaitForConnection)] = &UsbDeviceSoftPhy::stageWaitForConnection;
|
||||
_stageHandlers[static_cast<int>(Stage::Reset1)] = &UsbDeviceSoftPhy::stageReset1;
|
||||
_stageHandlers[static_cast<int>(Stage::GetDeviceDescriptor)] = &UsbDeviceSoftPhy::stageGetDeviceDescriptor;
|
||||
_stageHandlers[static_cast<int>(Stage::Reset2)] = &UsbDeviceSoftPhy::stageReset2;
|
||||
}
|
||||
|
||||
Device* asDevice() {
|
||||
return static_cast<Device*>(this);
|
||||
}
|
||||
|
||||
static bool isValidPacketSize(uint8_t maxPacketSize, EndpointAddress endpointAddress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool openEndpoint(EndpointAddress address, uint8_t maxPacketSize, EndpointType type) {
|
||||
if(!isValidPacketSize(maxPacketSize, address)) {
|
||||
std::cout << "Failed to open endpoint with address " << address.value << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
SoftEndpoint& endpoint = address.isIn() ? _inEndpoints[address.index()] : _outEndpoints[address.index()];
|
||||
endpoint.maxPacketSize = maxPacketSize;
|
||||
endpoint.isOpen = true;
|
||||
std::cout << "Open endpoint with address " << (int)address.value << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void closeEndpoint(EndpointAddress address) {
|
||||
SoftEndpoint& endpoint = address.isIn() ? _inEndpoints[address.index()] : _outEndpoints[address.index()];
|
||||
endpoint.isOpen = false;
|
||||
endpoint.packets.clear();
|
||||
}
|
||||
|
||||
void beginReceive(EndpointAddress address, void* buffer, uint8_t size) {
|
||||
auto& ep = _outEndpoints[address.index()];
|
||||
ep.receiving = true;
|
||||
}
|
||||
|
||||
void beginTransmit(EndpointAddress address, void* buffer, uint8_t size) {
|
||||
auto& ep = _inEndpoints[address.index()];
|
||||
size = (std::min)(size, ep.maxPacketSize);
|
||||
std::vector<uint8_t> packet;
|
||||
if(size != 0) {
|
||||
packet.insert(packet.end(), (uint8_t*)buffer, (uint8_t*)buffer + size);
|
||||
}
|
||||
ep.packets.push(packet);
|
||||
}
|
||||
private:
|
||||
|
||||
void setStage(Stage stage) {
|
||||
std::cout << "Change stage from " << StageStrings[(int)_currentStage] << " to " << StageStrings[(int)stage] << std::endl;
|
||||
_currentStage = stage;
|
||||
|
||||
if(_currentStage == Stage::WaitForConnection) {
|
||||
for(auto& ep : _inEndpoints) {
|
||||
ep.isOpen = false;
|
||||
ep.receiving = false;
|
||||
ep.packets = {};
|
||||
}
|
||||
for (auto& ep : _outEndpoints) {
|
||||
ep.isOpen = false;
|
||||
ep.receiving = false;
|
||||
ep.packets = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SoftEndpoint _inEndpoints[MaxEndpointsCount];
|
||||
SoftEndpoint _outEndpoints[MaxEndpointsCount];
|
||||
|
||||
Stage _currentStage = Stage::WaitForConnection;
|
||||
|
||||
void stageWaitForConnection() {
|
||||
if (_connected) {
|
||||
std::cout << "Device connected" << std::endl;
|
||||
setStage(Stage::Reset1);
|
||||
|
||||
}
|
||||
}
|
||||
void stageReset1() {
|
||||
std::cout << "Device reset1 begin" << std::endl;
|
||||
asDevice()->onReset();
|
||||
std::cout << "Device reset1 end" << std::endl;
|
||||
setStage(Stage::GetDeviceDescriptor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<uint8_t> controlIn(SetupPacket setup) {
|
||||
/*SetupPacket setup;
|
||||
setup.bmRequestType.Direction = RequestDirection::DeviceToHost;
|
||||
setup.bmRequestType.Recipient = RequestRecipient::Device;
|
||||
setup.bmRequestType.Type = RequestType::Standard;*/
|
||||
|
||||
asDevice()->onSetup(&setup);
|
||||
|
||||
|
||||
std::vector<uint8_t> rxBuffer;
|
||||
auto& ep = _inEndpoints[0];
|
||||
for (;;) {
|
||||
if(ep.isOpen) {
|
||||
if(!ep.packets.empty()) {
|
||||
auto packet = ep.packets.front();
|
||||
ep.packets.pop();
|
||||
if(packet.empty()) {
|
||||
return rxBuffer;
|
||||
}else {
|
||||
rxBuffer.insert(rxBuffer.end(), packet.begin(), packet.end());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Sleep(1);
|
||||
}
|
||||
}
|
||||
void stageGetDeviceDescriptor() {
|
||||
SetupPacket setup;
|
||||
setup.bmRequestType.Direction = RequestDirection::DeviceToHost;
|
||||
setup.bmRequestType.Recipient = RequestRecipient::Device;
|
||||
setup.bmRequestType.Type = RequestType::Standard;
|
||||
setup.bRequest = SetupRequest::GetDescriptor;
|
||||
setup.wIndex = 0;
|
||||
setup.wValue = ((uint8_t)DescriptorType::DeviceDescriptor << 8) | 0;
|
||||
setup.wLength = sizeof(DeviceDescriptor);
|
||||
|
||||
auto descriptorData = controlIn(setup);
|
||||
|
||||
for (;;);
|
||||
}
|
||||
void stageReset2() {
|
||||
|
||||
}
|
||||
void threadHandler() {
|
||||
while (_running) {
|
||||
(this->*_stageHandlers[static_cast<int>(_currentStage)])();
|
||||
Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<void(UsbDeviceSoftPhy::*)(), static_cast<int>(Stage::StageCount)> _stageHandlers;
|
||||
|
||||
std::thread _usbThread;
|
||||
bool _connected = false;
|
||||
bool _running = false;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UsbDeviceSoftPhy_h__
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* UsbInternalFsDeviceDriver.h
|
||||
*
|
||||
* Created on: 1 îêò. 2017 ã.
|
||||
* Author: l-pro
|
||||
*/
|
||||
|
||||
#ifndef LFRAMEWORK_MCU_USBDEVICE_USBINTERNALFSDEVICEDRIVER_H_
|
||||
#define LFRAMEWORK_MCU_USBDEVICE_USBINTERNALFSDEVICEDRIVER_H_
|
||||
|
||||
namespace LFramework {
|
||||
namespace USB {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* LFRAMEWORK_MCU_USBDEVICE_USBINTERNALFSDEVICEDRIVER_H_ */
|
||||
@@ -0,0 +1,8 @@
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
HalLibrary.h
|
||||
HardFaultHandler.h
|
||||
|
||||
HardFaultHandler.cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* HalLibrary.h
|
||||
*
|
||||
* Created on: 17 ñåíò. 2017 ã.
|
||||
* Author: l-pro
|
||||
*/
|
||||
|
||||
#ifndef LFRAMEWORK_MCU_STM32_HALLIBRARY_H_
|
||||
#define LFRAMEWORK_MCU_STM32_HALLIBRARY_H_
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* LFRAMEWORK_MCU_STM32_HALLIBRARY_H_ */
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef _WIN32
|
||||
|
||||
|
||||
#include <LFramework/Debug.h>
|
||||
#include <LFramework/IO/StreamWriter.h>
|
||||
|
||||
extern "C" {
|
||||
void HardFaultHandlerExtended(unsigned int * hardfault_args){
|
||||
unsigned int stacked_r0;
|
||||
unsigned int stacked_r1;
|
||||
unsigned int stacked_r2;
|
||||
unsigned int stacked_r3;
|
||||
unsigned int stacked_r12;
|
||||
unsigned int stacked_lr;
|
||||
unsigned int stacked_pc;
|
||||
unsigned int stacked_psr;
|
||||
|
||||
stacked_r0 = ((unsigned int)hardfault_args[0]);
|
||||
stacked_r1 = ((unsigned int)hardfault_args[1]);
|
||||
stacked_r2 = ((unsigned int)hardfault_args[2]);
|
||||
stacked_r3 = ((unsigned int)hardfault_args[3]);
|
||||
|
||||
stacked_r12 = ((unsigned int)hardfault_args[4]);
|
||||
stacked_lr = ((unsigned int)hardfault_args[5]);
|
||||
stacked_pc = ((unsigned int)hardfault_args[6]);
|
||||
stacked_psr = ((unsigned int)hardfault_args[7]);
|
||||
|
||||
lfFatal() << "----------";
|
||||
lfFatal() << "Hard fault";
|
||||
lfFatal() << "----------";
|
||||
|
||||
lfFatal() << "stacked_lr: 0x" << LFramework::HexFormat(stacked_lr) ;
|
||||
lfFatal() << "stacked_pc: 0x" << LFramework::HexFormat(stacked_pc) ;
|
||||
|
||||
while(true){
|
||||
asm("nop");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#define HARD_FAULT_HANDLER \
|
||||
__asm( ".syntax unified\n" \
|
||||
"MOVS R0, #4 \n" \
|
||||
"MOV R1, LR \n" \
|
||||
"TST R0, R1 \n" \
|
||||
"BEQ _MSP \n" \
|
||||
"MRS R0, PSP \n" \
|
||||
"B HardFaultHandlerExtended \n" \
|
||||
"_MSP: \n" \
|
||||
"MRS R0, MSP \n" \
|
||||
"B HardFaultHandlerExtended \n" \
|
||||
".syntax divided\n") ;
|
||||
@@ -0,0 +1,4 @@
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
ForEach.h
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef ForEach_h__
|
||||
#define ForEach_h__
|
||||
|
||||
#define UNPACK(...) __VA_ARGS__
|
||||
|
||||
//Count of commas == max args count (32)
|
||||
#define CNT_PREFIX__CNT_POSTFIX ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0
|
||||
|
||||
#define COUNTER_SEQUENCE 32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0
|
||||
|
||||
#define ARGS_CNT2(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31, N, ...) N
|
||||
#define ARGS_CNT1(__ARGS) ARGS_CNT2 __ARGS
|
||||
#define ARGS_CNT(...) UNPACK(ARGS_CNT1((CNT_PREFIX_ ## __VA_ARGS__ ## _CNT_POSTFIX, COUNTER_SEQUENCE)))
|
||||
|
||||
#define CONCAT(arg1, arg2) CONCAT1(arg1, arg2)
|
||||
#define CONCAT1(arg1, arg2) CONCAT2(arg1, arg2)
|
||||
#define CONCAT2(arg1, arg2) arg1##arg2
|
||||
|
||||
#define FOR_EACH_0(macro, runner, ...)
|
||||
#define FOR_EACH_1(macro, runner, x) runner(macro, 0, LAST, x)
|
||||
#define FOR_EACH_2(macro, runner, x, ...) runner(macro, 1, NOT_LAST, x) UNPACK(FOR_EACH_1(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_3(macro, runner, x, ...) runner(macro, 2, NOT_LAST, x) UNPACK(FOR_EACH_2(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_4(macro, runner, x, ...) runner(macro, 3, NOT_LAST, x) UNPACK(FOR_EACH_3(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_5(macro, runner, x, ...) runner(macro, 4, NOT_LAST, x) UNPACK(FOR_EACH_4(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_6(macro, runner, x, ...) runner(macro, 5, NOT_LAST, x) UNPACK(FOR_EACH_5(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_7(macro, runner, x, ...) runner(macro, 6, NOT_LAST, x) UNPACK(FOR_EACH_6(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_8(macro, runner, x, ...) runner(macro, 7, NOT_LAST, x) UNPACK(FOR_EACH_7(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_9(macro, runner, x, ...) runner(macro, 8, NOT_LAST, x) UNPACK(FOR_EACH_8(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_10(macro, runner, x, ...) runner(macro, 9, NOT_LAST, x) UNPACK(FOR_EACH_9(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_11(macro, runner, x, ...) runner(macro, 10, NOT_LAST, x) UNPACK(FOR_EACH_10(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_12(macro, runner, x, ...) runner(macro, 11, NOT_LAST, x) UNPACK(FOR_EACH_11(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_13(macro, runner, x, ...) runner(macro, 12, NOT_LAST, x) UNPACK(FOR_EACH_12(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_14(macro, runner, x, ...) runner(macro, 13, NOT_LAST, x) UNPACK(FOR_EACH_13(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_15(macro, runner, x, ...) runner(macro, 14, NOT_LAST, x) UNPACK(FOR_EACH_14(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_16(macro, runner, x, ...) runner(macro, 15, NOT_LAST, x) UNPACK(FOR_EACH_15(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_17(macro, runner, x, ...) runner(macro, 16, NOT_LAST, x) UNPACK(FOR_EACH_16(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_18(macro, runner, x, ...) runner(macro, 17, NOT_LAST, x) UNPACK(FOR_EACH_17(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_19(macro, runner, x, ...) runner(macro, 18, NOT_LAST, x) UNPACK(FOR_EACH_18(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_20(macro, runner, x, ...) runner(macro, 19, NOT_LAST, x) UNPACK(FOR_EACH_19(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_21(macro, runner, x, ...) runner(macro, 20, NOT_LAST, x) UNPACK(FOR_EACH_20(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_22(macro, runner, x, ...) runner(macro, 21, NOT_LAST, x) UNPACK(FOR_EACH_21(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_23(macro, runner, x, ...) runner(macro, 22, NOT_LAST, x) UNPACK(FOR_EACH_22(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_24(macro, runner, x, ...) runner(macro, 23, NOT_LAST, x) UNPACK(FOR_EACH_23(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_25(macro, runner, x, ...) runner(macro, 24, NOT_LAST, x) UNPACK(FOR_EACH_24(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_26(macro, runner, x, ...) runner(macro, 25, NOT_LAST, x) UNPACK(FOR_EACH_25(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_27(macro, runner, x, ...) runner(macro, 26, NOT_LAST, x) UNPACK(FOR_EACH_26(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_28(macro, runner, x, ...) runner(macro, 27, NOT_LAST, x) UNPACK(FOR_EACH_27(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_29(macro, runner, x, ...) runner(macro, 28, NOT_LAST, x) UNPACK(FOR_EACH_28(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_30(macro, runner, x, ...) runner(macro, 29, NOT_LAST, x) UNPACK(FOR_EACH_29(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_31(macro, runner, x, ...) runner(macro, 30, NOT_LAST, x) UNPACK(FOR_EACH_30(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_32(macro, runner, x, ...) runner(macro, 31, NOT_LAST, x) UNPACK(FOR_EACH_31(macro, runner, __VA_ARGS__))
|
||||
|
||||
|
||||
#define FOREACH_SIMPLE_RUNNER(macro, id, isLast, x) macro(x)
|
||||
#define FOREACH_INDEXED_RUNNER(macro, id, isLast, x) macro(id, x)
|
||||
|
||||
#define FOREACH_SIMPLE_RUNNER_ISLAST(macro, id, isLast, x) macro(isLast, x)
|
||||
|
||||
#define FOR_EACH1_R(macro, runner, N, ...) UNPACK(CONCAT(FOR_EACH_, N)(macro, runner, __VA_ARGS__))
|
||||
#define FOR_EACH_R(macro, runner, ...) UNPACK(FOR_EACH1_R(macro, runner, ARGS_CNT(__VA_ARGS__), __VA_ARGS__))
|
||||
|
||||
#define FOR_EACH(macro, ...) FOR_EACH_R(macro, FOREACH_SIMPLE_RUNNER, __VA_ARGS__)
|
||||
|
||||
#define FOR_EACH_N(macro, ...) FOR_EACH_R(macro, FOREACH_INDEXED_RUNNER, __VA_ARGS__)
|
||||
#define FOR_EACH_L(macro, ...) FOR_EACH_R(macro, FOREACH_SIMPLE_RUNNER_ISLAST, __VA_ARGS__)
|
||||
|
||||
static_assert(ARGS_CNT() == 0, "ARGS_CNT test fail");
|
||||
static_assert(ARGS_CNT(a) == 1, "ARGS_CNT test fail");
|
||||
static_assert(ARGS_CNT(a, a) == 2, "ARGS_CNT test fail");
|
||||
static_assert(ARGS_CNT(a, a, a, a) == 4, "ARGS_CNT test fail");
|
||||
static_assert(ARGS_CNT(a, a, a, a, a, a, a, a) == 8, "ARGS_CNT test fail");
|
||||
static_assert(ARGS_CNT(a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a) == 16, "ARGS_CNT test fail");
|
||||
static_assert(ARGS_CNT(a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a) == 32, "ARGS_CNT test fail");
|
||||
|
||||
|
||||
#endif // ForEach_h__
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "../Macro/ForEach.h"
|
||||
|
||||
#define COMMA_LAST(x) x
|
||||
#define COMMA_NOT_LAST(x) x ,
|
||||
|
||||
#define JOIN_IMPL(last, x) COMMA_##last(x)
|
||||
|
||||
#define JOIN2(...) UNPACK(FOR_EACH_L(JOIN_IMPL, __VA_ARGS__))
|
||||
#define JOIN(...) UNPACK( JOIN2 (__VA_ARGS__) )
|
||||
|
||||
template<typename T>
|
||||
struct Reflect {
|
||||
|
||||
};
|
||||
|
||||
|
||||
#define GEN_FIELD2(type_, name_) type_ name_;
|
||||
#define GEN_FIELD(field_) GEN_FIELD2 field_
|
||||
|
||||
#define GEN_FIELD_GETTER2(id_, type_, name_) if constexpr(ID == FieldsCount - 1 - id_) return object. name_;
|
||||
#define GEN_FIELD_GETTER1(id_, ...) UNPACK(GEN_FIELD_GETTER2(id_, __VA_ARGS__))
|
||||
#define GEN_FIELD_GETTER(id_, ...) GEN_FIELD_GETTER1(id_, UNPACK __VA_ARGS__ )
|
||||
|
||||
|
||||
#define DEFINE_STRUCT_RTTI(structName_, publicFields_) \
|
||||
template<> struct Reflect<structName_> { \
|
||||
static const char* Name() {return #structName_; } \
|
||||
static constexpr size_t FieldsCount = ARGS_CNT(UNPACK publicFields_) ; \
|
||||
template<size_t ID> static auto& getField(structName_& object){ \
|
||||
FOR_EACH_N(GEN_FIELD_GETTER, UNPACK publicFields_) ; \
|
||||
} \
|
||||
};
|
||||
#define DEFINE_STRUCT_TYPE(structName_, publicFields_) struct structName_ { FOR_EACH(GEN_FIELD, UNPACK publicFields_) };
|
||||
|
||||
|
||||
#define DEFINE_STRUCT(structName_, publicFields_) \
|
||||
DEFINE_STRUCT_TYPE(structName_, publicFields_) \
|
||||
DEFINE_STRUCT_RTTI(structName_, publicFields_)
|
||||
|
||||
|
||||
|
||||
#define UNPACK_TEMPLATE_PARAMS1(last_, type_, name_) COMMA_##last_(type_ name_)
|
||||
#define UNPACK_TEMPLATE_PARAM1(last, ...) UNPACK( UNPACK_TEMPLATE_PARAMS1(last, __VA_ARGS__ ))
|
||||
#define UNPACK_TEMPLATE_PARAM(last, x) UNPACK_TEMPLATE_PARAM1(last, UNPACK x)
|
||||
|
||||
|
||||
#define UNPACK_TEMPLATE_ARGS1(last_, type_, name_) COMMA_##last_(name_)
|
||||
#define UNPACK_TEMPLATE_ARG1(last, ...) UNPACK( UNPACK_TEMPLATE_ARGS1(last, __VA_ARGS__ ))
|
||||
#define UNPACK_TEMPLATE_ARG(last, x) UNPACK_TEMPLATE_ARG1(last, UNPACK x)
|
||||
|
||||
#define UNPACK_TEMPLATE_PARAMS(...) UNPACK (FOR_EACH_L(UNPACK_TEMPLATE_PARAM, __VA_ARGS__) )
|
||||
|
||||
|
||||
|
||||
#define UNPACK_TEMPLATE_ARGS(...) UNPACK (FOR_EACH_L(UNPACK_TEMPLATE_ARG, __VA_ARGS__) )
|
||||
|
||||
|
||||
#define DEFINE_TEMPLATE_STRUCT_RTTI(templateParams_, structName_, publicFields_) \
|
||||
template< UNPACK_TEMPLATE_PARAMS templateParams_ > struct Reflect<structName_< UNPACK_TEMPLATE_ARGS templateParams_ > > { \
|
||||
static const char* Name() {return #structName_; } \
|
||||
static constexpr size_t FieldsCount = ARGS_CNT(UNPACK publicFields_) ; \
|
||||
template<size_t ID> static auto& getField(structName_ < UNPACK_TEMPLATE_ARGS templateParams_ > & object){ \
|
||||
FOR_EACH_N(GEN_FIELD_GETTER, UNPACK publicFields_) ; \
|
||||
} \
|
||||
};
|
||||
|
||||
#define DEFINE_TEMPLATE_STRUCT_TYPE(templateParams_, structName_, publicFields_) template< UNPACK_TEMPLATE_PARAMS templateParams_ > struct structName_ { FOR_EACH(GEN_FIELD, UNPACK publicFields_) };
|
||||
|
||||
#define DEFINE_TEMPLATE_STRUCT(templateParams_, structName_, publicFields_) \
|
||||
DEFINE_TEMPLATE_STRUCT_TYPE(templateParams_, structName_, publicFields_) \
|
||||
DEFINE_TEMPLATE_STRUCT_RTTI(templateParams_, structName_, publicFields_)
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
#include "Detect/DetectOS.h"
|
||||
#include "Assert.h"
|
||||
|
||||
#if defined(LF_NEWLIB_TO_RTOS_MALLOC)
|
||||
#include "FreeRTOS.h"
|
||||
#endif
|
||||
|
||||
#if defined(LF_SYSCALLS_STUBS) || defined(LF_NEWLIB_TO_RTOS_MALLOC)
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include "Debug.h"
|
||||
#include "System.h"
|
||||
#include <malloc.h>
|
||||
#endif//Todo check right files for different configuration
|
||||
|
||||
extern "C" {
|
||||
|
||||
#if defined(LF_SYSCALLS_STUBS)
|
||||
|
||||
//extern int errno;
|
||||
//register char * stack_ptr asm("sp");
|
||||
|
||||
#ifndef __errno_r
|
||||
#include <sys/reent.h>
|
||||
#define __errno_r(reent) reent->_errno
|
||||
#endif
|
||||
|
||||
caddr_t _sbrk(int incr) {
|
||||
static_cast<void>(incr);
|
||||
//std::terminate(); //Do not use newlib memory management
|
||||
for(;;);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int _write_r(struct _reent* r, int file, char* ptr, int len){
|
||||
static_cast<void>(r);
|
||||
static_cast<void>(ptr);
|
||||
|
||||
if(stdout->_file != file && stderr->_file != file){
|
||||
for(;;);//Assert: unknown file descriptor
|
||||
}
|
||||
|
||||
#if defined(AF_TERMINAL_STDIO)
|
||||
Terminal::write(ptr, len);
|
||||
#endif
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int _read_r(struct _reent *r, int file, char * ptr, int len)
|
||||
{
|
||||
(void)r;
|
||||
(void)file;
|
||||
(void)ptr;
|
||||
(void)len;
|
||||
__errno_r(r) = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _lseek_r(struct _reent *r, int file, int ptr, int dir){
|
||||
(void)r;
|
||||
(void)file;
|
||||
(void)ptr;
|
||||
(void)dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _close_r(struct _reent *r, int file){
|
||||
(void)r;
|
||||
(void)file;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _open_r ( struct _reent *ptr, const char *file, int flags, int mode ){
|
||||
static_cast<void>(ptr);
|
||||
static_cast<void>(file);
|
||||
static_cast<void>(flags);
|
||||
static_cast<void>(mode);
|
||||
int fd = -1;
|
||||
return fd;
|
||||
}
|
||||
|
||||
int _fstat_r(struct _reent *r, int file, struct stat * st){
|
||||
(void)r;
|
||||
(void)file;
|
||||
memset(st, 0, sizeof(*st));
|
||||
st->st_mode = S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _isatty_r(struct _reent *r, int fd){
|
||||
(void)r;
|
||||
(void)fd;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _kill (int a, int b){
|
||||
(void)a;
|
||||
(void)b;
|
||||
return 0;
|
||||
}
|
||||
int _getpid(int a){
|
||||
(void)a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
//memory management
|
||||
|
||||
#if LF_TARGET_OS == LF_OS_FREERTOS || defined(LF_SYSCALLS_STUBS)
|
||||
|
||||
void __assert_func(const char * file, int line, const char * func, const char * error){
|
||||
lfDebug() << "libc assert";
|
||||
lfDebug() << "File: " << file;
|
||||
//lfDebug() << "Line: " << line;
|
||||
lfDebug() << "Function: " << func;
|
||||
lfDebug() << "Expression: " << error;
|
||||
for(;;);
|
||||
}
|
||||
void _exit(int status){
|
||||
lfDebug() << "_exit called";
|
||||
for(;;);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LF_NEWLIB_TO_RTOS_MALLOC)
|
||||
|
||||
|
||||
void* _realloc_r(struct _reent *re, void* oldAddr, size_t newSize) {
|
||||
static_cast<void>(re);
|
||||
static_cast<void>(oldAddr);
|
||||
static_cast<void>(newSize);
|
||||
lfAssert(false); //No mechanism is currently available to correctly reallocate memory, so just don't allow it for now
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* _calloc_r(struct _reent *re, size_t num, size_t size) {
|
||||
static_cast<void>(re);
|
||||
void* result = pvPortMalloc(num*size);
|
||||
if(result){
|
||||
//calloc should zero-initialize allocated memory
|
||||
uint8_t* bytes = (uint8_t*)result;
|
||||
for(size_t i = 0; i < (num * size); ++i){
|
||||
bytes[i] = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void* _malloc_r(struct _reent *re, size_t size) {
|
||||
static_cast<void>(re);
|
||||
return pvPortMalloc(size);
|
||||
}
|
||||
|
||||
void _free_r(struct _reent *re, void* ptr) {
|
||||
static_cast<void>(re);
|
||||
vPortFree(ptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
} //extern "C"
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#ifndef System_h__
|
||||
#define System_h__
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "Detect/DetectOS.h"
|
||||
|
||||
|
||||
#if LF_TARGET_OS == LF_OS_FREERTOS
|
||||
#include "SystemArmCortexM.h"
|
||||
#elif LF_TARGET_OS == LF_OS_WINDOWS
|
||||
#include "SystemWindows.h"
|
||||
#endif
|
||||
|
||||
|
||||
#endif // System_h__
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#ifndef CMSIS_DEVICE_FILE
|
||||
#error "CMSIS_DEVICE_FILE path not defined in McuConfig.h"
|
||||
#else
|
||||
#include CMSIS_DEVICE_FILE
|
||||
#endif
|
||||
|
||||
namespace LFramework {
|
||||
namespace System {
|
||||
inline size_t getCurrentInterruptHandlerIndex() {
|
||||
return __get_IPSR();
|
||||
}
|
||||
|
||||
inline bool isHandlingInterrupt() {
|
||||
return getCurrentInterruptHandlerIndex() != 0;
|
||||
}
|
||||
|
||||
inline bool isDebuggerAttached() {
|
||||
return (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) != 0;
|
||||
}
|
||||
|
||||
inline void setBreakpoint() {
|
||||
if (isDebuggerAttached) {
|
||||
__BKPT(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef SystemWindows_h__
|
||||
#define SystemWindows_h__
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
namespace LFramework {
|
||||
namespace System {
|
||||
inline size_t getCurrentInterruptHandlerIndex() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool isHandlingInterrupt() {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isDebuggerAttached() {
|
||||
return IsDebuggerPresent() == TRUE;
|
||||
}
|
||||
|
||||
inline void setBreakpoint() {
|
||||
if (isDebuggerAttached()) {
|
||||
DebugBreak();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SystemWindows_h__
|
||||
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <codecvt>
|
||||
#include <string>
|
||||
|
||||
namespace LFramework::Text {
|
||||
class Encoding {
|
||||
public:
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
// https://social.msdn.microsoft.com/Forums/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error?forum=vcgeneral
|
||||
#define _MSVC_CONVERT_WORKAROUND
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_MSVC_CONVERT_WORKAROUND)
|
||||
static inline std::wstring_convert<std::codecvt_utf8<int32_t>, int32_t> s_u32Converter;
|
||||
static inline std::wstring_convert<std::codecvt_utf8_utf16<int16_t>, int16_t> s_u16Converter;
|
||||
#else
|
||||
static inline std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> s_u32Converter;
|
||||
static inline std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> s_u16Converter;
|
||||
#endif
|
||||
|
||||
static std::wstring u8StringToWString(const std::string& src) {
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
|
||||
return converter.from_bytes(src);
|
||||
}
|
||||
|
||||
static std::string wStringToU8String(const std::wstring& src) {
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
|
||||
return convert.to_bytes(src);
|
||||
}
|
||||
|
||||
static std::u32string u16StringToU32String(const std::u16string& u16String)
|
||||
{
|
||||
std::string temp{ u16StringToU8String(u16String) };
|
||||
|
||||
return u8StringToU32String(temp);
|
||||
}
|
||||
|
||||
static std::string u16StringToU8String(const std::u16string& u16String)
|
||||
{
|
||||
#if defined(_MSVC_CONVERT_WORKAROUND)
|
||||
using workaround_str = std::basic_string<int16_t, std::char_traits<int16_t>, std::allocator<int16_t> >;
|
||||
workaround_str workaround(reinterpret_cast<const int16_t*>(u16String.c_str()));
|
||||
return s_u16Converter.to_bytes(workaround);
|
||||
#else
|
||||
return s_u16Converter.to_bytes(u16String);
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::string u32StringToU8String(const std::u32string& u32String)
|
||||
{
|
||||
#if defined(_MSVC_CONVERT_WORKAROUND)
|
||||
using workaround_str = std::basic_string<int32_t, std::char_traits<int32_t>, std::allocator<int32_t> >;
|
||||
workaround_str workaround(reinterpret_cast<const int32_t*>(u32String.c_str()));
|
||||
return s_u32Converter.to_bytes(workaround);
|
||||
#else
|
||||
return s_u32Converter.to_bytes(u32String);
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::u16string u8StringToU16String(const std::string& u8String)
|
||||
{
|
||||
#if defined(_MSVC_CONVERT_WORKAROUND)
|
||||
auto&& workaround = s_u16Converter.from_bytes(u8String);
|
||||
return std::u16string(reinterpret_cast<const char16_t*>(workaround.c_str()));
|
||||
#else
|
||||
return s_u16Converter.from_bytes(u8String);
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::u32string u8StringToU32String(const std::string& u8String)
|
||||
{
|
||||
#if defined(_MSVC_CONVERT_WORKAROUND)
|
||||
auto&& workaround = s_u32Converter.from_bytes(u8String);
|
||||
return std::u32string(reinterpret_cast<const char32_t*>(workaround.c_str()));
|
||||
#else
|
||||
return s_u32Converter.from_bytes(u8String);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#ifdef _MSVC_CONVERT_WORKAROUND
|
||||
#undef _MSVC_CONVERT_WORKAROUND
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
add_library(LFrameworkThreading INTERFACE )
|
||||
add_library(LFramework::Threading ALIAS LFrameworkThreading )
|
||||
|
||||
target_sources(LFrameworkThreading
|
||||
INTERFACE
|
||||
CriticalSection.h
|
||||
CriticalSectionFreeRTOS.h
|
||||
CriticalSectionStd.h
|
||||
Semaphore.h
|
||||
SemaphoreFreeRTOS.h
|
||||
SemaphoreStd.h
|
||||
Thread.h
|
||||
ThreadFreeRTOS.h
|
||||
ThreadPlatform.h
|
||||
ThreadStd.h
|
||||
|
||||
CriticalSectionStd.cpp
|
||||
SemaphoreStdTest.cpp
|
||||
Thread.cpp
|
||||
ThreadTest.cpp
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <LFramework/Detect/DetectOS.h>
|
||||
|
||||
#if LF_TARGET_OS == LF_OS_FREERTOS
|
||||
#include "CriticalSectionFreeRTOS.h"
|
||||
#elif (LF_TARGET_OS == LF_OS_WINDOWS) || (LF_TARGET_OS == LF_OS_LINUX)
|
||||
#include "CriticalSectionStd.h"
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "stm32f746xx.h"
|
||||
|
||||
namespace LFramework::Threading {
|
||||
|
||||
class CriticalSection {
|
||||
public:
|
||||
CriticalSection() {
|
||||
_status = lock();
|
||||
}
|
||||
|
||||
~CriticalSection() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
unlock(_status);
|
||||
}
|
||||
|
||||
static size_t getCurrentInterruptNumber() {
|
||||
return __get_IPSR();
|
||||
}
|
||||
|
||||
static bool isInterruptHandling() {
|
||||
return getCurrentInterruptNumber() != 0;
|
||||
}
|
||||
|
||||
static uint32_t lock() {
|
||||
if (isInterruptHandling()) {
|
||||
return taskENTER_CRITICAL_FROM_ISR();
|
||||
}
|
||||
else {
|
||||
taskENTER_CRITICAL();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unlock(uint32_t irqStatus) {
|
||||
if (isInterruptHandling()) {
|
||||
taskEXIT_CRITICAL_FROM_ISR(irqStatus);
|
||||
}
|
||||
else {
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t _status = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <LFramework/Detect/DetectOS.h>
|
||||
|
||||
#if (LF_TARGET_OS == LF_OS_WINDOWS) || (LF_TARGET_OS == LF_OS_LINUX)
|
||||
#include <LFramework/Threading/CriticalSectionStd.h>
|
||||
|
||||
std::recursive_mutex LFramework::Threading::CriticalSection::_lock;
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
namespace LFramework::Threading {
|
||||
|
||||
class CriticalSection {
|
||||
public:
|
||||
CriticalSection() {
|
||||
lock();
|
||||
}
|
||||
|
||||
~CriticalSection() {
|
||||
unlock(0);
|
||||
}
|
||||
|
||||
static uint32_t lock() {
|
||||
_lock.lock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unlock(int ) {
|
||||
_lock.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
static std::recursive_mutex _lock;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <LFramework/Detect/DetectOS.h>
|
||||
|
||||
#if LF_TARGET_OS == LF_OS_FREERTOS
|
||||
#include "SemaphoreFreeRTOS.h"
|
||||
#elif (LF_TARGET_OS == LF_OS_WINDOWS) || (LF_TARGET_OS == LF_OS_LINUX)
|
||||
#include "SemaphoreStd.h"
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../MCU/Mcu.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "portable.h"
|
||||
#include "queue.h"
|
||||
#include "semphr.h"
|
||||
|
||||
|
||||
namespace LFramework::Threading {
|
||||
|
||||
class Semaphore {
|
||||
public:
|
||||
~Semaphore(){
|
||||
vSemaphoreDelete(_handle);
|
||||
}
|
||||
|
||||
size_t messagesWaiting() const {
|
||||
return (size_t)uxQueueMessagesWaiting(_handle);
|
||||
}
|
||||
|
||||
bool take(TickType_t wait = portMAX_DELAY) {
|
||||
if (LFramework::Mcu::isHandlingInterrupt()) {
|
||||
portBASE_TYPE taskWoken = pdFALSE;
|
||||
if (xSemaphoreTakeFromISR(_handle, &taskWoken) != pdTRUE) {
|
||||
return false;
|
||||
}
|
||||
portEND_SWITCHING_ISR(taskWoken);
|
||||
return true;
|
||||
} else {
|
||||
return xSemaphoreTake(_handle, wait) == pdTRUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool give() {
|
||||
if (LFramework::Mcu::isHandlingInterrupt()) {
|
||||
portBASE_TYPE taskWoken = pdFALSE;
|
||||
if (xSemaphoreGiveFromISR(_handle, &taskWoken) != pdTRUE) {
|
||||
return false;
|
||||
}
|
||||
portEND_SWITCHING_ISR(taskWoken);
|
||||
return true;
|
||||
} else {
|
||||
return xSemaphoreGive(_handle) == pdTRUE;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
SemaphoreHandle_t _handle;
|
||||
};
|
||||
|
||||
class BinarySemaphore : public Semaphore{
|
||||
public:
|
||||
BinarySemaphore(){
|
||||
_handle = xSemaphoreCreateBinary();
|
||||
assert(_handle != nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
class CountingSemaphore : public Semaphore{
|
||||
public:
|
||||
CountingSemaphore(UBaseType_t maxCount, UBaseType_t initialCount){
|
||||
_handle = xSemaphoreCreateCounting(maxCount, initialCount);
|
||||
assert(_handle != nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
|
||||
namespace LFramework::Threading {
|
||||
|
||||
class CountingSemaphore {
|
||||
public:
|
||||
static constexpr std::size_t InfiniteTimeout = -1;
|
||||
CountingSemaphore(std::size_t maxCount, std::size_t initialCount) :_maxCount(maxCount), _count(initialCount) {
|
||||
|
||||
}
|
||||
bool take() {
|
||||
std::unique_lock<std::mutex> lk(_mutex);
|
||||
_conditional.wait(lk, [this] { return _count > 0; });
|
||||
--_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool take(std::size_t milliseconds) {
|
||||
std::unique_lock<std::mutex> lk(_mutex);
|
||||
if (_conditional.wait_for(lk, ::std::chrono::milliseconds(milliseconds), [this] { return _count > 0; })) {
|
||||
--_count;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tryTake() {
|
||||
std::lock_guard<::std::mutex> lk(_mutex);
|
||||
if (_count > 0) {
|
||||
--_count;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool give(std::size_t count) {
|
||||
if(count == 0){
|
||||
return true;
|
||||
}
|
||||
std::lock_guard<::std::mutex> lk(_mutex);
|
||||
if ((_count + count) <= _maxCount) {
|
||||
_count += count;
|
||||
if(count > 1){
|
||||
_conditional.notify_all();
|
||||
}else{
|
||||
_conditional.notify_one();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool give() {
|
||||
std::lock_guard<::std::mutex> lk(_mutex);
|
||||
if (_count < _maxCount) {
|
||||
++_count;
|
||||
_conditional.notify_one();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t messagesWaiting() const {
|
||||
return _count.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t _maxCount = -1;
|
||||
std::atomic<std::size_t> _count = 0;
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _conditional;
|
||||
};
|
||||
|
||||
class BinarySemaphore : public CountingSemaphore {
|
||||
public:
|
||||
BinarySemaphore(bool startWithValue = false) : CountingSemaphore(1, startWithValue ? 1 : 0){ }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "../UnitTest/UnitTest.h"
|
||||
|
||||
#ifdef LF_THREADING
|
||||
|
||||
#include "SemaphoreStd.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace LFramework;
|
||||
|
||||
|
||||
TEST(SemaphoreStd_SyncUsage) {
|
||||
Semaphore s0;
|
||||
|
||||
TEST_TRUE(s0.available() == 0);
|
||||
s0.give();
|
||||
TEST_TRUE(s0.available() == 1);
|
||||
s0.give(9);
|
||||
TEST_TRUE(s0.available() == 10);
|
||||
|
||||
s0.take(0);
|
||||
TEST_TRUE(s0.available() == 10);
|
||||
s0.take();
|
||||
TEST_TRUE(s0.available() == 9);
|
||||
s0.take(9);
|
||||
TEST_TRUE(s0.available() == 0);
|
||||
}
|
||||
|
||||
TEST(SemaphoreStd_AsyncUsage) {
|
||||
static constexpr size_t initialCount = 25;
|
||||
|
||||
Semaphore resources(initialCount);
|
||||
|
||||
Thread consumerThread0 = Thread("", 0, ThreadPriority::Normal, [&resources]() {
|
||||
for(size_t i = 0; i < 100; ++i) {
|
||||
resources.take();
|
||||
ThisThread::sleepForMs(1);
|
||||
}
|
||||
});
|
||||
|
||||
Thread consumerThread1 = Thread("", 0, ThreadPriority::Normal, [&resources]() {
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
resources.take(10);
|
||||
ThisThread::sleepForMs(2);
|
||||
}
|
||||
});
|
||||
|
||||
for(size_t i = 0; i < (200 - initialCount); i += 5) {
|
||||
resources.give(5);
|
||||
}
|
||||
|
||||
consumerThread0.join();
|
||||
consumerThread1.join();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <LFramework/Threading/Thread.h>
|
||||
#include <LFramework/Threading/ThreadFreeRTOS.h>
|
||||
#include <LFramework/Threading/ThreadStd.h>
|
||||
|
||||
|
||||
namespace LFramework::Threading {
|
||||
|
||||
Thread::Thread(const char* name, size_t stackSize, ThreadPriority priority, std::function<void()>&& func) {
|
||||
_impl = new Thread::Impl(name, stackSize, priority, std::move(func));
|
||||
}
|
||||
|
||||
|
||||
Thread::Thread() noexcept = default;
|
||||
|
||||
Thread::Thread(Thread&& other) noexcept{
|
||||
moveTask(other);
|
||||
}
|
||||
|
||||
Thread::~Thread() {
|
||||
assert(!joinable());
|
||||
//_impl.reset();
|
||||
}
|
||||
bool Thread::joinable() const noexcept {
|
||||
return _impl != nullptr;
|
||||
}
|
||||
void Thread::join() {
|
||||
assert(joinable());
|
||||
assert(ThisThread::getId() != getId()); //Can't join self
|
||||
_impl->join();
|
||||
delete _impl;
|
||||
_impl = nullptr;
|
||||
}
|
||||
void Thread::swap(Thread& other) {
|
||||
std::swap(_impl, other._impl);
|
||||
}
|
||||
/*Thread::NativeHandleType Thread::nativeHandle() const {
|
||||
assert(joinable());
|
||||
return _impl->getNativeHandle();
|
||||
}*/
|
||||
Thread::ID Thread::getId() const noexcept {
|
||||
if (_impl != nullptr) {
|
||||
return _impl->getId();
|
||||
}
|
||||
return Thread::ID{};
|
||||
}
|
||||
ThreadPriority Thread::getPriority() const {
|
||||
assert(joinable());
|
||||
return _impl->getPriority();
|
||||
}
|
||||
void Thread::setPriority(ThreadPriority priority) const {
|
||||
assert(joinable());
|
||||
_impl->setPriority(priority);
|
||||
}
|
||||
void Thread::suspend() {
|
||||
assert(joinable());
|
||||
_impl->suspend();
|
||||
}
|
||||
void Thread::resume() {
|
||||
assert(joinable());
|
||||
_impl->resume();
|
||||
}
|
||||
Thread& Thread::operator = (Thread&& other) noexcept {
|
||||
return moveTask(other);
|
||||
}
|
||||
const char* Thread::getName() const {
|
||||
assert(joinable());
|
||||
return _impl->getName();
|
||||
}
|
||||
|
||||
Thread& Thread::moveTask(Thread& other) {
|
||||
assert(!joinable());
|
||||
_impl = std::move(other._impl);
|
||||
other._impl = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} //LFramework
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <LFramework/Threading/ThreadPlatform.h>
|
||||
|
||||
namespace LFramework::Threading {
|
||||
|
||||
enum class ThreadPriority{
|
||||
Idle,
|
||||
Low,
|
||||
BelowNormal,
|
||||
Normal,
|
||||
AboveNormal,
|
||||
High,
|
||||
Realtime,
|
||||
Error
|
||||
};
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
using ID = ThreadID;
|
||||
|
||||
explicit Thread(const char* name, size_t stackSize, ThreadPriority priority, std::function<void()>&& func);
|
||||
|
||||
~Thread();
|
||||
Thread() noexcept;
|
||||
Thread(const Thread&) = delete;
|
||||
Thread(Thread&& other) noexcept;
|
||||
|
||||
Thread& operator = (const Thread&) = delete;
|
||||
Thread& operator = (Thread&& other) noexcept;
|
||||
|
||||
void swap(Thread& other);
|
||||
|
||||
bool joinable() const noexcept;
|
||||
void join();
|
||||
//TODO: detach
|
||||
|
||||
ID getId() const noexcept;
|
||||
//NativeHandleType nativeHandle() const noexcept;
|
||||
|
||||
const char* getName() const;
|
||||
|
||||
ThreadPriority getPriority() const;
|
||||
void setPriority(ThreadPriority priority) const;
|
||||
|
||||
void suspend();
|
||||
void resume();
|
||||
private:
|
||||
class Impl;
|
||||
Thread& moveTask(Thread& other);
|
||||
Impl* _impl = nullptr;
|
||||
};
|
||||
|
||||
namespace ThisThread {
|
||||
Thread::ID getId();
|
||||
ThreadPriority getPriority();
|
||||
void setPriority(ThreadPriority priority);
|
||||
void sleepForMs(size_t milliseconds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include <LFramework/Threading/Thread.h>
|
||||
#include "../Detect/DetectOS.h"
|
||||
|
||||
|
||||
#if LF_TARGET_OS == LF_OS_FREERTOS
|
||||
#include <LFramework/Threading/SemaphoreFreeRTOS.h>
|
||||
#include "task.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace LFramework::Threading {
|
||||
|
||||
#if LF_TARGET_OS == LF_OS_FREERTOS
|
||||
static Thread::ID makeThreadId(TaskHandle_t handle) {
|
||||
return reinterpret_cast<Thread::ID>(handle);
|
||||
}
|
||||
|
||||
static ThreadPriority makeThreadPriority(portBASE_TYPE osPriority) {
|
||||
return static_cast<ThreadPriority>(osPriority);
|
||||
}
|
||||
|
||||
static portBASE_TYPE makeOsPriority(ThreadPriority priority) {
|
||||
return static_cast<portBASE_TYPE>(priority);
|
||||
}
|
||||
|
||||
|
||||
class Thread::Impl {
|
||||
public:
|
||||
explicit Impl(const char* name, size_t stackSize, ThreadPriority priority, std::function<void()>&& body) :_body(std::move(body)) {
|
||||
xTaskCreate(&Impl::staticTaskHandler, name, stackSize, this, (UBaseType_t)priority, &_rtosTaskHandle);
|
||||
}
|
||||
|
||||
void join(){
|
||||
_joinSemaphore.take();
|
||||
}
|
||||
|
||||
Thread::ID getId() const {
|
||||
return makeThreadId(_rtosTaskHandle);
|
||||
}
|
||||
|
||||
TaskHandle_t getNativeHandle() const {
|
||||
return _rtosTaskHandle;
|
||||
}
|
||||
|
||||
const char* getName() const {
|
||||
return pcTaskGetTaskName(_rtosTaskHandle);
|
||||
}
|
||||
void suspend() {
|
||||
vTaskSuspend(_rtosTaskHandle);
|
||||
}
|
||||
void resume() {
|
||||
vTaskResume(_rtosTaskHandle);
|
||||
}
|
||||
ThreadPriority getPriority() const {
|
||||
return makeThreadPriority(uxTaskPriorityGet(_rtosTaskHandle));
|
||||
}
|
||||
void setPriority(ThreadPriority priority) const {
|
||||
vTaskPrioritySet(_rtosTaskHandle, makeOsPriority(priority));
|
||||
}
|
||||
private:
|
||||
static void staticTaskHandler(void* param) {
|
||||
Impl* _this = (Impl*)param;
|
||||
_this->taskHandler();
|
||||
}
|
||||
void taskHandler() {
|
||||
_body();
|
||||
assert(_joinSemaphore.give());
|
||||
vTaskDelete(xTaskGetCurrentTaskHandle());
|
||||
}
|
||||
TaskHandle_t _rtosTaskHandle = nullptr;
|
||||
std::function<void()> _body;
|
||||
Threading::BinarySemaphore _joinSemaphore;
|
||||
};
|
||||
|
||||
|
||||
namespace ThisThread {
|
||||
Thread::ID getId() {
|
||||
return makeThreadId(xTaskGetCurrentTaskHandle());
|
||||
}
|
||||
ThreadPriority getPriority() {
|
||||
return makeThreadPriority(uxTaskPriorityGet(xTaskGetCurrentTaskHandle()));
|
||||
}
|
||||
void setPriority(ThreadPriority priority) {
|
||||
vTaskPrioritySet(xTaskGetCurrentTaskHandle(), makeOsPriority(priority));
|
||||
}
|
||||
void sleepForMs(size_t milliseconds){
|
||||
vTaskDelay(milliseconds);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Detect/DetectOS.h"
|
||||
|
||||
#if LF_TARGET_OS == LF_OS_FREERTOS
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
namespace LFramework::Threading {
|
||||
using ThreadID = TaskHandle_t;
|
||||
}
|
||||
#else
|
||||
#include <thread>
|
||||
namespace LFramework::Threading {
|
||||
using ThreadID = std::thread::id;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(LF_THREAD_STD)
|
||||
|
||||
#include <LFramework/Threading/Thread.h>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
namespace LFramework::Threading {
|
||||
class Thread::Impl {
|
||||
public:
|
||||
explicit Impl(const char* name, size_t stackSize, ThreadPriority priority, std::function<void()>&& body):_name(name){
|
||||
_thread = std::thread(body);
|
||||
}
|
||||
void join() {
|
||||
_thread.join();
|
||||
}
|
||||
Thread::ID getId() const {
|
||||
return _thread.get_id();
|
||||
}
|
||||
const char* getName() const {
|
||||
return _name.c_str();
|
||||
}
|
||||
void suspend() {
|
||||
|
||||
}
|
||||
void resume() {
|
||||
|
||||
}
|
||||
ThreadPriority getPriority() const {
|
||||
return ThreadPriority::Normal;
|
||||
}
|
||||
void setPriority(ThreadPriority priority) const {
|
||||
|
||||
}
|
||||
private:
|
||||
std::string _name;
|
||||
std::thread _thread;
|
||||
};
|
||||
|
||||
namespace ThisThread {
|
||||
inline Thread::ID getId() {
|
||||
return std::this_thread::get_id();
|
||||
}
|
||||
void sleepForMs(size_t milliseconds) {
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||
}
|
||||
/*ThreadPriority getPriority() {
|
||||
return Private::makeThreadPriority(uxTaskPriorityGet(xTaskGetCurrentTaskHandle()));
|
||||
}
|
||||
void setPriority(ThreadPriority priority) {
|
||||
vTaskPrioritySet(xTaskGetCurrentTaskHandle(), Private::makeOsPriority(priority));
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <LFramework/Threading/Thread.h>
|
||||
#include "../UnitTest/UnitTest.h"
|
||||
|
||||
#ifdef LF_THREADING
|
||||
|
||||
using namespace LFramework::Threading;
|
||||
|
||||
void test(int& counter) {
|
||||
counter = 100500;
|
||||
}
|
||||
|
||||
TEST(Thread_Create) {
|
||||
int counter = 0;
|
||||
Thread t;
|
||||
TEST_TRUE(t.joinable() == false);
|
||||
|
||||
t = Thread("123", 123, ThreadPriority::Normal, [&](){
|
||||
test(counter);
|
||||
});
|
||||
t.join();
|
||||
|
||||
TEST_TRUE(counter == 100500);
|
||||
TEST_TRUE(!t.joinable());
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,6 @@
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
Stopwatch.h
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef Stopwatch_h__
|
||||
#define Stopwatch_h__
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LFramework {
|
||||
class Stopwatch {
|
||||
public:
|
||||
Stopwatch() {
|
||||
Reset();
|
||||
}
|
||||
typedef std::chrono::high_resolution_clock clock_t;
|
||||
|
||||
void Reset() {
|
||||
_startTime = clock_t::now();
|
||||
}
|
||||
clock_t::duration Check() {
|
||||
_lastDelta = clock_t::now() - _startTime;
|
||||
return _lastDelta;
|
||||
}
|
||||
std::chrono::milliseconds CheckMs() {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(Check());
|
||||
}
|
||||
clock_t::duration GetLastDelta() {
|
||||
return _lastDelta;
|
||||
}
|
||||
private:
|
||||
clock_t::time_point _startTime;
|
||||
clock_t::duration _lastDelta;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // Stopwatch_h__
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
#ifndef TUPLEUTILS_H
|
||||
#define TUPLEUTILS_H
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
|
||||
template <class... Ts>
|
||||
struct Tuple;
|
||||
|
||||
template <>
|
||||
struct Tuple<> {};
|
||||
|
||||
template <class T, class... Ts>
|
||||
struct Tuple<T, Ts...>{
|
||||
T first;
|
||||
Tuple<Ts...> rest;
|
||||
constexpr Tuple() = default;
|
||||
template <class U, class...Us, class = typename ::std::enable_if<!::std::is_base_of<Tuple,typename ::std::decay<U>::type>::value>::type>
|
||||
constexpr Tuple(U&& u, Us&&...tail) : first(::std::forward<U>(u)), rest(::std::forward<Us>(tail)...) {}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Tuple<T>{
|
||||
T first;
|
||||
constexpr Tuple() = default;
|
||||
template <class U, class = typename ::std::enable_if<!::std::is_base_of<Tuple, typename ::std::decay<U>::type>::value>::type>
|
||||
constexpr Tuple(U&& u) : first(::std::forward<U>(u)) {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IsTuple : std::false_type {};
|
||||
|
||||
template<typename ...T>
|
||||
struct IsTuple<Tuple<T...>> : std::true_type {};
|
||||
|
||||
|
||||
#pragma pack(push, 1)
|
||||
template <class... Ts>
|
||||
struct PackedTuple;
|
||||
|
||||
template <>
|
||||
struct PackedTuple<> {};
|
||||
|
||||
template <class T, class... Ts>
|
||||
struct PackedTuple<T, Ts...>{
|
||||
T first;
|
||||
Tuple<Ts...> rest;
|
||||
PackedTuple() = default;
|
||||
template <class U, class...Us, class = typename ::std::enable_if<!::std::is_base_of<PackedTuple,typename ::std::decay<U>::type>::value>::type>
|
||||
PackedTuple(U&& u, Us&&...tail) : first(::std::forward<U>(u)), rest(::std::forward<Us>(tail)...) {}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct PackedTuple<T>{
|
||||
T first;
|
||||
PackedTuple() = default;
|
||||
template <class U, class = typename ::std::enable_if<!::std::is_base_of<PackedTuple, typename ::std::decay<U>::type>::value>::type>
|
||||
PackedTuple(U&& u) : first(::std::forward<U>(u)) {}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
template<typename T>
|
||||
struct IsPackedTuple : std::false_type {};
|
||||
|
||||
template<typename ...T>
|
||||
struct IsPackedTuple<PackedTuple<T...>> : std::true_type {};
|
||||
|
||||
|
||||
namespace details {
|
||||
template<size_t Index, size_t CurrentIndex, typename...Types>
|
||||
auto& Get(Tuple<Types...>& t) {
|
||||
if constexpr (Index == CurrentIndex) {
|
||||
return t.first;
|
||||
}
|
||||
else if constexpr (sizeof...(Types) > 1) {
|
||||
return details::Get<Index, CurrentIndex + 1>(t.rest);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(t) == 0, "Invalid tuple index");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct tupleSizeHelper {
|
||||
static constexpr size_t size = 0;
|
||||
};
|
||||
|
||||
template<typename... T>
|
||||
struct tupleSizeHelper<Tuple<T...>> {
|
||||
static constexpr size_t size = sizeof...(T);
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr size_t tupleSize = tupleSizeHelper<T>::size;
|
||||
|
||||
|
||||
template<size_t Index, typename...Types>
|
||||
auto& Get(Tuple<Types...>& t) {
|
||||
return details::Get<Index, 0>(t);
|
||||
}
|
||||
|
||||
|
||||
namespace details {
|
||||
template<size_t Index, size_t CurrentIndex, typename...Types>
|
||||
auto& Get(PackedTuple<Types...>& t) {
|
||||
if constexpr (Index == CurrentIndex) {
|
||||
return t.first;
|
||||
}
|
||||
else if constexpr (sizeof...(Types) > 1) {
|
||||
return details::Get<Index, CurrentIndex + 1>(t.rest);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(t) == 0, "Invalid tuple index");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Index, typename...Types>
|
||||
auto& Get(PackedTuple<Types...>& t) {
|
||||
return details::Get<Index, 0>(t);
|
||||
}
|
||||
|
||||
struct TupleSkipType {};
|
||||
|
||||
struct TupleStepInType {};
|
||||
|
||||
namespace details {
|
||||
template<typename...A, typename...B>
|
||||
auto tupleCatImpl(Tuple<A...> done, Tuple<B...> t) {
|
||||
return Tuple<A..., B...>{};
|
||||
};
|
||||
|
||||
|
||||
template<typename A, typename B>
|
||||
using tupleCat = decltype(tupleCatImpl(std::declval<A>(), std::declval<B>()));
|
||||
|
||||
template<typename F, typename...T, typename...U>
|
||||
auto tupleSelectImpl(F f, const Tuple<T...>& tuple, const Tuple<U...>& intermediate) {
|
||||
if constexpr(sizeof...(T) > 1) {
|
||||
using SelectedType = decltype(f(tuple.first));
|
||||
|
||||
if constexpr (std::is_same_v<SelectedType, TupleStepInType>) {
|
||||
return tupleCat<decltype(tupleSelectImpl(f, tuple.first, Tuple<>())), decltype(tupleSelectImpl(f, tuple.rest, Tuple<U...>()))>{};
|
||||
}else if constexpr (std::is_same_v<SelectedType, TupleSkipType>) {
|
||||
return tupleSelectImpl(f, tuple.rest, Tuple<U...>());
|
||||
}
|
||||
else {
|
||||
return tupleSelectImpl(f, tuple.rest, Tuple<U..., SelectedType>());
|
||||
}
|
||||
}
|
||||
else {
|
||||
using SelectedType = decltype(f(tuple.first));
|
||||
if constexpr (std::is_same_v<SelectedType, TupleStepInType>) {
|
||||
return tupleSelectImpl(f, tuple.first, Tuple<>());
|
||||
}
|
||||
else if constexpr (std::is_same_v<SelectedType, TupleSkipType>) {
|
||||
return Tuple<U...>();
|
||||
}
|
||||
else {
|
||||
return Tuple<U..., SelectedType>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename...T>
|
||||
auto tupleSelectType_(F f, const Tuple<T...>& tuple) {
|
||||
return tupleSelectImpl(f, tuple, Tuple<>{});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename A, typename B>
|
||||
using tupleAppendFront = tupleCat<Tuple<A>, B>;
|
||||
|
||||
template<typename A, typename B>
|
||||
using tupleAppendBack = tupleCat<B, Tuple<A>>;
|
||||
|
||||
|
||||
template<typename ... T>
|
||||
struct tupleFlattenImpl {
|
||||
using type = Tuple<>;
|
||||
};
|
||||
|
||||
template<typename D, typename...T>
|
||||
struct tupleFlattenImpl<D, T...> {
|
||||
using type = tupleAppendFront<D, typename tupleFlattenImpl<T...>::type>;
|
||||
};
|
||||
|
||||
template<typename D>
|
||||
struct tupleFlattenImpl<D> {
|
||||
using type = Tuple<D>;
|
||||
};
|
||||
|
||||
template<typename...D, typename...T>
|
||||
struct tupleFlattenImpl<Tuple<D...>, T...> {
|
||||
using type = tupleCat<typename tupleFlattenImpl<D...>::type, typename tupleFlattenImpl<T...>::type>;
|
||||
};
|
||||
|
||||
template<typename...T>
|
||||
auto tupleFlatten_(const Tuple<T...>& tuple) {
|
||||
return typename tupleFlattenImpl<T...>::type{};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename F, typename T>
|
||||
struct tupleSelectTypeHelp {
|
||||
typedef decltype(details::tupleSelectType_(std::declval<F>(), std::declval<T>())) type;
|
||||
};
|
||||
|
||||
template<typename F, typename T>
|
||||
using tupleSelectType = typename tupleSelectTypeHelp<F, T>::type;
|
||||
|
||||
|
||||
template<typename T>
|
||||
using tupleFlatten = decltype(details::tupleFlatten_(std::declval<T>()));
|
||||
|
||||
template<typename Target, bool Recurse = false>
|
||||
struct TupleTypeSelector {
|
||||
template<typename T>
|
||||
auto operator () (const T& t) {
|
||||
if constexpr (IsTuple<T>::value) {
|
||||
if constexpr (Recurse) {
|
||||
return TupleStepInType{};
|
||||
}
|
||||
else {
|
||||
return TupleSkipType{};
|
||||
}
|
||||
}
|
||||
else {
|
||||
if constexpr (std::is_base_of_v<Target, T>) {
|
||||
return T{};
|
||||
}
|
||||
else {
|
||||
return TupleSkipType{};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
namespace details {
|
||||
template<typename F, typename...T, typename...U>
|
||||
auto tupleSelectImpl(F f, PackedTuple<T...> tuple, PackedTuple<U...> intermediate) {
|
||||
if constexpr(sizeof...(T) > 1) {
|
||||
return tupleSelectImpl(f, tuple.rest, PackedTuple<U..., decltype(f(tuple.first))>());
|
||||
}
|
||||
else {
|
||||
return PackedTuple<U..., decltype(f(tuple.first))>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename...T>
|
||||
auto tupleSelectType_(F f, PackedTuple<T...> tuple) {
|
||||
return tupleSelectImpl(f, tuple, PackedTuple<>{});
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename T>
|
||||
using packedTupleSelectType = decltype(details::tupleSelectType_(std::declval<F>(), std::declval<T>()));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename T, typename F, int... Is>
|
||||
void forEach(T&& t, F f, std::index_sequence<Is...>) {
|
||||
auto l = { (f(std::get<Is>(t)), 0)... };
|
||||
}
|
||||
|
||||
template<typename T, typename F, int... Is>
|
||||
void forEachIndexed(T&& t, F f, std::index_sequence<Is...>) {
|
||||
auto l = { (f(std::get<Is>(t), Is), 0)... };
|
||||
}
|
||||
|
||||
template<typename... Ts, typename F>
|
||||
void forEachInTuple(std::tuple<Ts...> const& t, F f) {
|
||||
forEach(t, f, std::index_sequence_for<Ts...>());
|
||||
}
|
||||
|
||||
template<typename... Ts, typename F>
|
||||
void forEachInTupleIndexed(std::tuple<Ts...> const& t, F f) {
|
||||
forEachIndexed(t, f, std::index_sequence_for<Ts...>());
|
||||
}
|
||||
|
||||
template<typename Functor, int ID = 0, typename ... Types>
|
||||
bool executeOnIndex(std::tuple<Types...>& t, int id, Functor f) {
|
||||
if constexpr (ID < sizeof...(Types)) {
|
||||
if (id == ID) {
|
||||
f(std::get<ID>(t));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return executeOnIndex<Functor, ID + 1, Types...>(t, id, f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // TUPLEUTILS_H
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
|
||||
namespace LFramework {
|
||||
|
||||
template<class F>
|
||||
struct FunctionTraits;
|
||||
|
||||
template<class R, class... Args>
|
||||
struct FunctionTraits<R(Args...)> {
|
||||
using ReturnType = R;
|
||||
|
||||
static constexpr std::size_t Arity = sizeof...(Args);
|
||||
|
||||
template <int N>
|
||||
struct ArgumentHelper {
|
||||
static_assert(N < Arity, "error: invalid parameter index.");
|
||||
using Type = typename std::tuple_element<N, std::tuple<Args...>>::type;
|
||||
};
|
||||
|
||||
template <int N>
|
||||
using Argument = typename ArgumentHelper<N>::Type;
|
||||
};
|
||||
|
||||
// function pointer
|
||||
template<class R, class... Args>
|
||||
struct FunctionTraits<R(*)(Args...)> : public FunctionTraits<R(Args...)> {
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <limits>
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
|
||||
namespace LFramework::TypeTraits::Integral {
|
||||
|
||||
template<typename T, typename = ::std::enable_if_t<::std::is_integral_v<T>>>
|
||||
static constexpr std::size_t digitsCount(T value) {
|
||||
return value == 0 ? 0 : 1 + digitsCount(value / 10);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr std::size_t digitsCountBase10(T value) {
|
||||
std::size_t result = 0;
|
||||
while (value != 0) {
|
||||
++result;
|
||||
value /= 10;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, typename = ::std::enable_if_t<::std::is_integral_v<T>>>
|
||||
static constexpr std::size_t maxDigitsCount() {
|
||||
return (::std::max)(digitsCount((::std::numeric_limits<T>::min)()), digitsCount((::std::numeric_limits<T>::max)()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
add_library(LFrameworkUsb INTERFACE )
|
||||
add_library( LFramework::Usb ALIAS LFrameworkUsb )
|
||||
|
||||
target_sources(LFrameworkUsb
|
||||
INTERFACE
|
||||
USBDevice_Deprecated.h
|
||||
usbd_conf.h
|
||||
UsbEndpoint.h
|
||||
UsbException.h
|
||||
UsbInterface.h
|
||||
UsbMicrosoftTypes.h
|
||||
UsbPollingTransfer.h
|
||||
UsbTransfer.h
|
||||
USBTypes.h
|
||||
USBTypesTest.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(Device)
|
||||
add_subdirectory(Host)
|
||||
|
||||
|
||||
target_include_directories(LFrameworkUsb
|
||||
INTERFACE
|
||||
.
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
add_library(LFrameworkUsbDevice INTERFACE )
|
||||
add_library(LFramework::Usb::Device ALIAS LFrameworkUsbDevice )
|
||||
|
||||
target_sources(LFrameworkUsbDevice
|
||||
INTERFACE
|
||||
UsbDBulkInterface.h
|
||||
UsbDDeviceContext.h
|
||||
UsbDEndpoint.h
|
||||
UsbDDevice.h
|
||||
UsbDInterface.h
|
||||
|
||||
UsbDEndpoint.cpp
|
||||
USBDDevice.cpp
|
||||
UsbDLowLevel.cpp
|
||||
UsbDDeviceCallbacks.cpp
|
||||
)
|
||||
|
||||
|
||||
target_compile_definitions(LFrameworkUsbDevice
|
||||
INTERFACE
|
||||
USBD_SUPPORT_USER_STRING_DESC=1
|
||||
)
|
||||
|
||||
target_include_directories(LFrameworkUsbDevice
|
||||
INTERFACE
|
||||
.
|
||||
)
|
||||
@@ -0,0 +1,344 @@
|
||||
#include <UsbDDevice.h>
|
||||
#include <usbd_conf.h>
|
||||
#include <usbd_def.h>
|
||||
#include <usbd_ioreq.h>
|
||||
#include <usbd_ctlreq.h>
|
||||
|
||||
#include <usbd_core.h>
|
||||
#include <stm32f7xx_hal.h>
|
||||
#include <LFramework/USB/USBTypes.h>
|
||||
#include <LFramework/USB/UsbMicrosoftTypes.h>
|
||||
#include <type_traits>
|
||||
#include <new>
|
||||
|
||||
using namespace LFramework;
|
||||
|
||||
extern "C" {
|
||||
extern PCD_HandleTypeDef hpcd_USB_OTG_FS;
|
||||
}
|
||||
|
||||
namespace LFramework::USB {
|
||||
__attribute__((used)) USBD_HandleTypeDef UsbDevice::hUsbDevice{};
|
||||
__attribute__((used)) UsbDDeviceContext* _context = nullptr;
|
||||
|
||||
|
||||
|
||||
|
||||
//For other-speed description
|
||||
__ALIGN_BEGIN volatile USB::DeviceQualifierDescriptor USBD_NDC_DeviceQualifierDesc __ALIGN_END {
|
||||
USB::UsbVersion(2),
|
||||
USB::UsbClass::Device::UseInterfaceClass(),
|
||||
64,
|
||||
1,
|
||||
0
|
||||
};
|
||||
|
||||
#define USB_VENDOR_CODE_WINUSB 'P'
|
||||
|
||||
__ALIGN_BEGIN volatile USB::Microsoft::MicrosoftStringDescriptor NDC_StringDescriptor __ALIGN_END = {
|
||||
(uint8_t)USB_VENDOR_CODE_WINUSB
|
||||
};
|
||||
|
||||
|
||||
extern volatile USB::DeviceQualifierDescriptor USBD_NDC_DeviceQualifierDesc;
|
||||
extern volatile USB::Microsoft::MicrosoftStringDescriptor NDC_StringDescriptor;
|
||||
|
||||
__attribute__((used)) std::aligned_storage_t<USBD_MAX_STR_DESC_SIZ, 4> _descriptorsBuffer;
|
||||
|
||||
|
||||
UsbDDeviceContext* UsbDevice::getContext(){
|
||||
return _context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void hang(){
|
||||
while(true){
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
__attribute__((used)) const USBD_DescriptorsTypeDef UsbDevice::_descriptorsTable = {
|
||||
&UsbDevice::getDeviceDescriptor,
|
||||
&UsbDevice::getLangidStrDescriptor,
|
||||
&UsbDevice::getManufacturerStringDescriptor,
|
||||
&UsbDevice::getProductStringDescriptor,
|
||||
&UsbDevice::getSerialStringDescriptor,
|
||||
&UsbDevice::getConfigStringDescriptor,
|
||||
&UsbDevice::getInterfaceStringDescriptor
|
||||
};
|
||||
|
||||
__attribute__((used)) const USBD_ClassTypeDef UsbDevice::_usbClassBinding = {
|
||||
&UsbDevice::coreInit,
|
||||
&UsbDevice::coreDeinit,
|
||||
&UsbDevice::coreSetup,
|
||||
0, //USBD_NDC_EP0_TxReady,
|
||||
&UsbDevice::coreEp0RxReady,
|
||||
&UsbDevice::coreDataIn,
|
||||
&UsbDevice::coreDataOut,
|
||||
&UsbDevice::coreSof,
|
||||
&UsbDevice::coreIsoInIncomplete,
|
||||
&UsbDevice::coreIsoOutIncomplete,
|
||||
&UsbDevice::coreGetCfgDesc,
|
||||
&UsbDevice::coreGetCfgDesc,
|
||||
&UsbDevice::coreGetCfgDesc,
|
||||
&UsbDevice::coreGetDeviceQualifierDesc,
|
||||
&UsbDevice::coreGetUserStringDesc
|
||||
};
|
||||
|
||||
|
||||
uint8_t UsbDevice::coreInit(USBD_HandleTypeDef* pdev, uint8_t cfgidx){
|
||||
//TODO: use configuration id
|
||||
for(size_t i = 0; i < _context->getInterfaceCount(); ++i){
|
||||
auto interface = _context->getInterface(i);
|
||||
if(interface != nullptr){
|
||||
if(!interface->init(pdev)){
|
||||
return USBD_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
|
||||
uint8_t UsbDevice::coreDeinit(USBD_HandleTypeDef* pdev, uint8_t cfgidx){
|
||||
//TODO: use configuration id
|
||||
|
||||
for(size_t i = 0; i < _context->getInterfaceCount(); ++i){
|
||||
auto interface = _context->getInterface(i);
|
||||
if(interface != nullptr){
|
||||
if(!interface->deinit(pdev)){
|
||||
//return USBD_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
uint8_t UsbDevice::coreImplSetup(USBD_SetupReqTypedef request, void* data){
|
||||
switch ( request.bmRequest & USB_REQ_RECIPIENT_MASK ){
|
||||
case USB_REQ_RECIPIENT_INTERFACE:{
|
||||
if(_context != nullptr){
|
||||
auto* interface = _context->getInterface(request.wValue);
|
||||
if(interface != nullptr){
|
||||
hang();
|
||||
/*if(interface->control(&hUsbDevice, request.bRequest, (uint8_t*)data, request.wLength)){
|
||||
return USBD_OK;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_REQ_RECIPIENT_ENDPOINT:
|
||||
hang();
|
||||
/*if(_usb_device_context != nullptr){
|
||||
if(request.bRequest == USB_REQ_CLEAR_FEATURE){ //reset pipe is called at host side
|
||||
//do reset pipe
|
||||
if(_clearFeatureCallback != nullptr){
|
||||
_clearFeatureCallback(request.wIndex);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
break;
|
||||
case USB_REQ_RECIPIENT_DEVICE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
|
||||
uint8_t UsbDevice::coreSetup(USBD_HandleTypeDef* pdev, USBD_SetupReqTypedef *req){
|
||||
hang();
|
||||
/*if (req->wLength){
|
||||
//Request with data stage{
|
||||
if((req->bmRequest & USB_REQ_DATA_PHASE_MASK) == USB_REQ_DATA_PHASE_DEVICE_TO_HOST){
|
||||
//device to host data stage => handler should send data
|
||||
return coreImplSetup(*req, 0);
|
||||
}else{ //host to device data stage! Can't execute now, read data first & execute later in Ep0Receive callback
|
||||
last_request = *req;
|
||||
USBD_CtlPrepareRx (pdev, (uint8_t*)&ep0Buffer[0], req->wLength);
|
||||
}
|
||||
} else {//No data stage => simple request => execute now
|
||||
return coreImplSetup(*req, 0);
|
||||
}*/
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t UsbDevice::coreEp0RxReady(USBD_HandleTypeDef* pdev){
|
||||
hang();
|
||||
//coreImplSetup(last_request, &ep0Buffer[0]); //data in stage complete => execute request
|
||||
//last_request.bRequest = 0xff;
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
UsbDInterface* UsbDevice::findInterfaceByEndpointAddress(uint8_t address){
|
||||
if(_context == nullptr){
|
||||
return nullptr;
|
||||
}
|
||||
auto if_cnt = _context->getInterfaceCount();
|
||||
for(uint32_t i = 0; i < if_cnt; ++i){
|
||||
auto interface = _context->getInterface(i);
|
||||
if(interface != nullptr){
|
||||
auto endpoint = interface->getEndpoint(address);
|
||||
if(endpoint != nullptr){
|
||||
return interface;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t UsbDevice::coreDataIn(USBD_HandleTypeDef* pdev, uint8_t epnum){
|
||||
auto interface = findInterfaceByEndpointAddress(USB::EndpointAddress::makeIn(epnum));//TODO: cleanup
|
||||
if(interface != nullptr){
|
||||
interface->txComplete(epnum | 0x80);
|
||||
}
|
||||
return USBD_OK;
|
||||
}
|
||||
uint8_t UsbDevice::coreDataOut(USBD_HandleTypeDef* pdev, uint8_t epnum){
|
||||
|
||||
uint32_t rxLen = USBD_LL_GetRxDataSize (pdev, epnum);
|
||||
auto interface = findInterfaceByEndpointAddress(USB::EndpointAddress::makeOut(epnum));
|
||||
if(interface != nullptr){
|
||||
interface->rxComplete(rxLen, epnum);
|
||||
}
|
||||
return USBD_OK;
|
||||
}
|
||||
uint8_t UsbDevice::coreSof(USBD_HandleTypeDef* pdev){
|
||||
hang();
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
uint8_t UsbDevice::coreIsoInIncomplete(USBD_HandleTypeDef* pdev, uint8_t epnum){
|
||||
hang();
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
uint8_t UsbDevice::coreIsoOutIncomplete(USBD_HandleTypeDef* pdev, uint8_t epnum){
|
||||
hang();
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
uint8_t* UsbDevice::coreGetCfgDesc(uint16_t* length){
|
||||
auto* mem = reinterpret_cast<uint8_t*>(&_descriptorsBuffer);
|
||||
auto* buffer = mem;
|
||||
USB::ConfigurationDescriptor* cd = new(buffer) USB::ConfigurationDescriptor();
|
||||
cd->wTotalLength = 0;
|
||||
cd->bNumInterfaces = (uint8_t)_context->getInterfaceCount();
|
||||
cd->bConfigurationValue = 0x01;
|
||||
cd->iConfiguration = USBD_IDX_CONFIG_STR;
|
||||
cd->bmAttributes = USB::UsbAttributes().value;
|
||||
cd->bMaxPower = 500;
|
||||
buffer += sizeof(USB::ConfigurationDescriptor);
|
||||
|
||||
for(uint32_t i = 0; i < _context->getInterfaceCount(); ++i){
|
||||
auto emptySize = (mem + sizeof(_descriptorsBuffer)) - buffer;
|
||||
auto size = _context->getInterface(i)->getDescriptor(buffer, emptySize, i);
|
||||
buffer += size;
|
||||
cd->wTotalLength += size;
|
||||
}
|
||||
cd->wTotalLength += sizeof(USB::ConfigurationDescriptor);
|
||||
|
||||
*length = cd->wTotalLength;
|
||||
return reinterpret_cast<uint8_t*>(&_descriptorsBuffer);
|
||||
}
|
||||
|
||||
|
||||
uint8_t* UsbDevice::getDeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t* length){
|
||||
auto mem = reinterpret_cast<uint8_t*>(&_descriptorsBuffer);
|
||||
auto& desc = *new(mem) USB::DeviceDescriptor();
|
||||
*length = sizeof(USB::DeviceDescriptor);
|
||||
|
||||
desc.bcdUSB = 0x0200;
|
||||
desc.classDescription = USB::UsbClassDescriptor(0,0,0);
|
||||
desc.bMaxPacketSize0 = 64;
|
||||
desc.idVendor = 0x0301;
|
||||
desc.idProduct = 0x1111;
|
||||
desc.bcdDevice = USB::UsbVersion(2);
|
||||
desc.iManufacturer = USBD_IDX_MFC_STR;
|
||||
desc.iProduct = USBD_IDX_PRODUCT_STR;
|
||||
desc.iSerialNumber = USBD_IDX_SERIAL_STR;
|
||||
desc.bNumConfigurations = 1;
|
||||
return reinterpret_cast<uint8_t*>(&desc);
|
||||
}
|
||||
|
||||
struct USBDDummyClassData{
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
|
||||
|
||||
__attribute__((used)) static USBDDummyClassData _classData = {};
|
||||
|
||||
void UsbDevice::start(UsbDDeviceContext* context){
|
||||
_context = context;
|
||||
|
||||
hUsbDevice.pClassData = &_classData; //Otherwise USBD_Reset handler would not disable interfaces ((
|
||||
//hUsbDevice.pClassData = nullptr; //Init?
|
||||
hUsbDevice.dev_speed = USBD_SPEED_FULL;
|
||||
|
||||
USBD_Init(&hUsbDevice, const_cast<USBD_DescriptorsTypeDef*>(&_descriptorsTable), 0);
|
||||
|
||||
USBD_RegisterClass(&hUsbDevice, const_cast<USBD_ClassTypeDef*>(&_usbClassBinding));
|
||||
USBD_Start(&hUsbDevice);
|
||||
|
||||
}
|
||||
uint8_t* UsbDevice::coreGetDeviceQualifierDesc (uint16_t *length){
|
||||
*length = sizeof (USBD_NDC_DeviceQualifierDesc);
|
||||
return (uint8_t*)&USBD_NDC_DeviceQualifierDesc;
|
||||
}
|
||||
|
||||
uint8_t* UsbDevice::coreGetUserStringDesc(USBD_HandleTypeDef* pdev, uint8_t index, uint16_t* length){
|
||||
*length = 0;
|
||||
if ( 0xEE == index ){
|
||||
*length = sizeof (NDC_StringDescriptor);
|
||||
return (uint8_t*)&NDC_StringDescriptor;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t* UsbDevice::getLangidStrDescriptor(USBD_SpeedTypeDef speed, uint16_t* length){
|
||||
auto mem = static_cast<void*>(&_descriptorsBuffer);
|
||||
auto& desc = *new(mem) USB::LanguageIDStringDescriptor<1>();
|
||||
*length = sizeof(USB::LanguageIDStringDescriptor<1>);
|
||||
desc.languages[0] = USB::LanguageID::EnglishUnitedStates;
|
||||
return reinterpret_cast<uint8_t*>(&desc);
|
||||
}
|
||||
uint8_t* UsbDevice::getManufacturerStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length){
|
||||
return USB::MakeStringDescriptor(_context->manufacturerStringDescriptor, &_descriptorsBuffer, sizeof(_descriptorsBuffer), length);
|
||||
}
|
||||
uint8_t* UsbDevice::getProductStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length){
|
||||
return USB::MakeStringDescriptor(_context->productStringDescriptor, &_descriptorsBuffer, sizeof(_descriptorsBuffer), length);
|
||||
}
|
||||
|
||||
|
||||
uint8_t* UsbDevice::getSerialStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length){
|
||||
return USB::MakeStringDescriptor(_context->serialStringDescriptor, &_descriptorsBuffer, sizeof(_descriptorsBuffer), length);
|
||||
}
|
||||
uint8_t* UsbDevice::getConfigStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length){
|
||||
return USB::MakeStringDescriptor(_context->configurationStringDescriptor, &_descriptorsBuffer, sizeof(_descriptorsBuffer), length);
|
||||
}
|
||||
uint8_t* UsbDevice::getInterfaceStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length){
|
||||
return USB::MakeStringDescriptor(_context->interfaceStringDescriptor, &_descriptorsBuffer, sizeof(_descriptorsBuffer), length);
|
||||
}
|
||||
|
||||
|
||||
USBD_StatusTypeDef UsbDevice::interfaceRequest(USBD_HandleTypeDef* pdev, USBD_SetupReqTypedef* req){
|
||||
uint8_t interface_id = (uint8_t)req->wValue; //TODO: bug! wIndex == interface id
|
||||
auto interface = _context->getInterface(interface_id);
|
||||
if(interface == nullptr){
|
||||
return USBD_FAIL;
|
||||
}
|
||||
return interface->interfaceRequest(pdev, req);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "UsbDInterface.h"
|
||||
#include <optional>
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class UsbDBulkInterface : public UsbDInterface {
|
||||
public:
|
||||
UsbDBulkInterface(){
|
||||
_descriptor.bNumEndpoints = 2;
|
||||
_descriptor.classDescription = USB::UsbClass::Interface::VendorSpecific<0,0>();
|
||||
_descriptor.iInterface = USBD_IDX_INTERFACE_STR;
|
||||
|
||||
USB::EndpointDescriptor desc;
|
||||
desc.bEndpointAddress = USB::EndpointAddress::makeIn(1);
|
||||
desc.bmAttributes.setType(EndpointType::Bulk);
|
||||
desc.wMaxPacketSize = 64;
|
||||
_inEp.emplace(desc);
|
||||
|
||||
desc.bEndpointAddress = USB::EndpointAddress::makeOut(1);
|
||||
_outEp.emplace(desc);
|
||||
}
|
||||
bool init(USBD_HandleTypeDef* pdev) override final {
|
||||
auto status = UsbDInterface::init(pdev);
|
||||
|
||||
/*if(status &) {
|
||||
_initListener->onBulkInterfaceInitComplete();
|
||||
}*/
|
||||
|
||||
return status;
|
||||
}
|
||||
bool deinit(USBD_HandleTypeDef* pdev) override final {
|
||||
return UsbDInterface::deinit(pdev);
|
||||
}
|
||||
UsbEndpoint* getEndpoint(bool isInEndpoint, uint8_t id)override final {
|
||||
if(id != 0){
|
||||
return nullptr;
|
||||
}
|
||||
return isInEndpoint ? &_inEp.value() : &_outEp.value();
|
||||
}
|
||||
const UsbEndpoint* getEndpoint(bool isInEndpoint, uint8_t id) const override final {
|
||||
if(id != 0){
|
||||
return nullptr;
|
||||
}
|
||||
return isInEndpoint ? &_inEp.value() : &_outEp.value();
|
||||
}
|
||||
|
||||
const USB::InterfaceDescriptor& getInterfaceDescriptor() const override final {
|
||||
return _descriptor;
|
||||
}
|
||||
|
||||
private:
|
||||
USB::InterfaceDescriptor _descriptor;
|
||||
std::optional<UsbDEndpoint> _inEp;
|
||||
std::optional<UsbDEndpoint> _outEp;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <stm32f7xx_hal.h>
|
||||
#include <usbd_def.h>
|
||||
#include "UsbDDeviceContext.h"
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class UsbDevice {
|
||||
public:
|
||||
friend class UsbDEndpoint;
|
||||
|
||||
static void start(UsbDDeviceContext* context);
|
||||
|
||||
static UsbDDeviceContext* getContext();
|
||||
|
||||
static USBD_StatusTypeDef interfaceRequest(USBD_HandleTypeDef* pdev, USBD_SetupReqTypedef* req);
|
||||
|
||||
//Descriptors
|
||||
static uint8_t* getDeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getLangidStrDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getManufacturerStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getProductStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getSerialStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getConfigStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getInterfaceStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
|
||||
//USB device core interface
|
||||
static uint8_t* coreGetCfgDesc(uint16_t* length);
|
||||
static uint8_t coreSof(USBD_HandleTypeDef* pdev);
|
||||
static uint8_t coreIsoInIncomplete(USBD_HandleTypeDef* pdev, uint8_t epnum);
|
||||
static uint8_t coreIsoOutIncomplete(USBD_HandleTypeDef* pdev, uint8_t epnum);
|
||||
static uint8_t coreSetup(USBD_HandleTypeDef* pdev, USBD_SetupReqTypedef *req);
|
||||
static uint8_t coreEp0RxReady(USBD_HandleTypeDef* pdev);
|
||||
static uint8_t coreDataIn(USBD_HandleTypeDef* pdev, uint8_t epnum);
|
||||
static uint8_t coreDataOut(USBD_HandleTypeDef* pdev, uint8_t epnum);
|
||||
static uint8_t* coreGetDeviceQualifierDesc (uint16_t *length);
|
||||
static uint8_t coreInit(USBD_HandleTypeDef* pdev, uint8_t cfgidx);
|
||||
static uint8_t coreDeinit(USBD_HandleTypeDef* pdev, uint8_t cfgidx);
|
||||
static uint8_t* coreGetUserStringDesc(USBD_HandleTypeDef* pdev, uint8_t index, uint16_t* length);
|
||||
static uint8_t coreImplSetup(USBD_SetupReqTypedef request, void* data);
|
||||
private:
|
||||
static UsbDInterface* findInterfaceByEndpointAddress(uint8_t address);
|
||||
static const USBD_DescriptorsTypeDef _descriptorsTable;
|
||||
static const USBD_ClassTypeDef _usbClassBinding;
|
||||
static USBD_HandleTypeDef hUsbDevice;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
#include <usbd_def.h>
|
||||
#include <LFramework/USB/Device/UsbDDevice.h>
|
||||
|
||||
#include <LFramework/USB/Device/UsbDDevice.h>
|
||||
#include <LFramework/USB/USBTypes.h>
|
||||
#include <LFramework/USB/UsbMicrosoftTypes.h>
|
||||
|
||||
using namespace LFramework;
|
||||
|
||||
volatile USB::Microsoft::CompatId::WinUSBFunction _winusbFunctionDescriptor(0);
|
||||
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_hook_device_request(USBD_HandleTypeDef * pdev, USBD_SetupReqTypedef * req) {
|
||||
if ((req->bmRequest & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_VENDOR) {
|
||||
if (req->bRequest == (uint8_t)USB_VENDOR_CODE_WINUSB) {
|
||||
if (req->wIndex == 0x04) {
|
||||
USBD_CtlSendData(pdev, (uint8_t*)&_winusbFunctionDescriptor, req->wLength);
|
||||
return USBD_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return USBD_FAIL;
|
||||
}
|
||||
extern "C" USBD_StatusTypeDef USBD_hook_interface_request(USBD_HandleTypeDef * pdev, USBD_SetupReqTypedef * req) {
|
||||
return USB::UsbDevice::interfaceRequest(pdev, req);
|
||||
}
|
||||
|
||||
|
||||
extern "C" void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef * hpcd) {
|
||||
USBD_HandleTypeDef* pdev = (USBD_HandleTypeDef*)hpcd->pData;
|
||||
|
||||
//Hack to support WinUSB requests :(
|
||||
USBD_ParseSetupRequest(&pdev->request, (uint8_t*)hpcd->Setup);
|
||||
pdev->ep0_state = USBD_EP0_SETUP;
|
||||
pdev->ep0_data_len = pdev->request.wLength;
|
||||
|
||||
switch (pdev->request.bmRequest & 0x1F) {
|
||||
case USB_REQ_RECIPIENT_DEVICE:
|
||||
if (USBD_hook_device_request(pdev, &pdev->request) == USBD_OK) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_RECIPIENT_INTERFACE:
|
||||
if (USBD_hook_interface_request(pdev, &pdev->request) == USBD_OK) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
USBD_LL_SetupStage((USBD_HandleTypeDef*)hpcd->pData, (uint8_t*)hpcd->Setup);
|
||||
}
|
||||
|
||||
extern "C" void HAL_PCD_SOFCallback2(PCD_HandleTypeDef * hpcd) {
|
||||
while (true) {
|
||||
//asm("nop");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" void HAL_PCD_ResetCallback(PCD_HandleTypeDef * hpcd) {
|
||||
USBD_SpeedTypeDef speed = USBD_SPEED_FULL;
|
||||
switch (hpcd->Init.speed) {
|
||||
case PCD_SPEED_FULL:
|
||||
speed = USBD_SPEED_FULL;
|
||||
break;
|
||||
default:
|
||||
speed = USBD_SPEED_FULL;
|
||||
break;
|
||||
}
|
||||
USBD_LL_SetSpeed((USBD_HandleTypeDef*)hpcd->pData, speed);
|
||||
USBD_LL_Reset((USBD_HandleTypeDef*)hpcd->pData);
|
||||
}
|
||||
|
||||
extern "C" void HAL_PCD_SuspendCallback(PCD_HandleTypeDef * hpcd) {
|
||||
//Inform USB library that core enters in suspend Mode
|
||||
if (hpcd->pData != 0) {
|
||||
USBD_LL_Suspend((USBD_HandleTypeDef*)hpcd->pData);
|
||||
}
|
||||
|
||||
//Enter in STOP mode
|
||||
if (hpcd->Init.low_power_enable) {
|
||||
//Set SLEEPDEEP bit and SleepOnExit of Cortex System Control Register
|
||||
SCB->SCR |= (uint32_t)((uint32_t)(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void HAL_PCD_ResumeCallback(PCD_HandleTypeDef * hpcd) {
|
||||
while (true) {
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef * hpcd, uint8_t epnum) {
|
||||
while (true) {
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef * hpcd, uint8_t epnum) {
|
||||
while (true) {
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void HAL_PCD_ConnectCallback(PCD_HandleTypeDef * hpcd) {
|
||||
while (true) {
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef * hpcd) {
|
||||
while (true) {
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "UsbInterface.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "UsbDInterface.h"
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class UsbDDeviceContext {
|
||||
public:
|
||||
|
||||
UsbDDeviceContext(UsbDInterface* usbInterface) : _interface(usbInterface){}
|
||||
|
||||
size_t getInterfaceCount() {
|
||||
return 1;
|
||||
}
|
||||
UsbDInterface* getInterface(size_t id) {
|
||||
return _interface;
|
||||
}
|
||||
|
||||
const char* manufacturerStringDescriptor = nullptr;
|
||||
const char* productStringDescriptor = nullptr;
|
||||
const char* serialStringDescriptor = nullptr;
|
||||
const char* configurationStringDescriptor = nullptr;
|
||||
const char* interfaceStringDescriptor = nullptr;
|
||||
|
||||
private:
|
||||
UsbDInterface* _interface = nullptr;
|
||||
|
||||
/*static uint8_t* getDeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getLangidStrDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getManufacturerStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getProductStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getSerialStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getConfigStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* length);
|
||||
static uint8_t* getInterfaceStringDescriptor(USBD_SpeedTypeDef speed, uint16_t* lengt*/
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "UsbDEndpoint.h"
|
||||
#include "UsbDDevice.h"
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
bool UsbDEndpoint::transferAsync(UsbTransfer* transfer){
|
||||
//CriticalSectionLock lock;
|
||||
|
||||
if(transfer == nullptr || _activeTransfer != nullptr || !isOpen()){
|
||||
return false;
|
||||
}
|
||||
|
||||
_activeTransfer = transfer;
|
||||
|
||||
if(getDescriptor().bEndpointAddress.isIn()){
|
||||
if(USBD_OK != USBD_LL_Transmit(&UsbDevice::hUsbDevice, getDescriptor().bEndpointAddress.value, (uint8_t*)transfer->buffer, transfer->size)){
|
||||
_activeTransfer = nullptr;
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
if(USBD_OK != USBD_LL_PrepareReceive(&UsbDevice::hUsbDevice, getDescriptor().bEndpointAddress.value, (uint8_t*)transfer->buffer, transfer->size)){
|
||||
_activeTransfer = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void UsbDEndpoint::onTxCompleteIsr(){
|
||||
finalizeTransfer(true, 0);
|
||||
}
|
||||
|
||||
void UsbDEndpoint::onRxCompleteIsr(uint32_t rxLength){
|
||||
finalizeTransfer(true, rxLength);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <usbd_core.h>
|
||||
#include <LFramework/USB/USBTypes.h>
|
||||
#include "../UsbEndpoint.h"
|
||||
#include "../UsbTransfer.h"
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class UsbDEndpoint : public UsbEndpoint {
|
||||
public:
|
||||
UsbDEndpoint(LFramework::USB::EndpointDescriptor descriptor):UsbEndpoint(descriptor){
|
||||
|
||||
}
|
||||
|
||||
bool open(USBD_HandleTypeDef *pdev){
|
||||
_isOpen = USBD_OK == USBD_LL_OpenEP(pdev, getDescriptor().bEndpointAddress.value, (uint8_t)getDescriptor().bmAttributes.getType(), getDescriptor().wMaxPacketSize);
|
||||
|
||||
//FIX для L0 ядра и подобных. Зачем-то в них эндпоинты запускаются не с NAK статусом
|
||||
#if defined(STM32F0)
|
||||
PCD_HandleTypeDef* hpcd = (PCD_HandleTypeDef*)pdev->pData;
|
||||
if(isInDirection()){ //TX EP
|
||||
PCD_SET_EP_TX_STATUS(hpcd->Instance, id, USB_EP_TX_NAK);
|
||||
}else{
|
||||
PCD_SET_EP_RX_STATUS(hpcd->Instance, id, USB_EP_RX_NAK);
|
||||
}
|
||||
#endif
|
||||
return _isOpen;
|
||||
}
|
||||
|
||||
void close(USBD_HandleTypeDef *pdev){
|
||||
USBD_LL_CloseEP(pdev, getDescriptor().bEndpointAddress.value);
|
||||
_isOpen = false;
|
||||
//finalizeTransfer(false, 0);
|
||||
}
|
||||
bool transferAsync(UsbTransfer* transfer);
|
||||
|
||||
bool isOpen() const {
|
||||
return _isOpen;
|
||||
}
|
||||
void onTxCompleteIsr();
|
||||
void onRxCompleteIsr(uint32_t rxLength);
|
||||
protected:
|
||||
void finalizeTransfer(bool success, uint32_t size){
|
||||
if(_activeTransfer != nullptr){
|
||||
auto transfer = _activeTransfer;
|
||||
_activeTransfer = nullptr;
|
||||
transfer->actualSize = size;
|
||||
if(transfer->callback != nullptr){
|
||||
transfer->callback(transfer, success);
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
UsbTransfer* _activeTransfer = nullptr;
|
||||
|
||||
bool _isOpen = false;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include "../UsbInterface.h"
|
||||
|
||||
#include "usbd_def.h"
|
||||
#include <LFramework/USB/USBTypes.h>
|
||||
#include <LFramework/USB/UsbMicrosoftTypes.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "UsbDEndpoint.h"
|
||||
#define USB_VENDOR_CODE_WINUSB 'P'
|
||||
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class UsbDInterface : public UsbInterface {
|
||||
public:
|
||||
virtual bool init(USBD_HandleTypeDef* pdev) {
|
||||
|
||||
|
||||
for(uint32_t i = 0;; ++i){
|
||||
auto ep = getEndpoint(false, i);
|
||||
if(ep == nullptr){
|
||||
break;
|
||||
}
|
||||
static_cast<UsbDEndpoint*>(ep)->open(pdev);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0;; ++i){
|
||||
auto ep = getEndpoint(true, i);
|
||||
if(ep == nullptr){
|
||||
break;
|
||||
}
|
||||
static_cast<UsbDEndpoint*>(ep)->open(pdev);
|
||||
}
|
||||
|
||||
_isOpen = true;
|
||||
return true;
|
||||
}
|
||||
virtual bool deinit(USBD_HandleTypeDef* pdev) {
|
||||
for(uint32_t i = 0;; ++i){
|
||||
auto ep = getEndpoint(false, i);
|
||||
if(ep == nullptr){
|
||||
break;
|
||||
}
|
||||
static_cast<UsbDEndpoint*>(ep)->close(pdev);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0;; ++i){
|
||||
auto ep = getEndpoint(true, i);
|
||||
if(ep == nullptr){
|
||||
break;
|
||||
}
|
||||
static_cast<UsbDEndpoint*>(ep)->close(pdev);
|
||||
}
|
||||
_isOpen = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t getDescriptor(uint8_t* buffer, uint32_t bufferSize, uint32_t interface_id) const {
|
||||
*reinterpret_cast<LFramework::USB::InterfaceDescriptor*>(buffer) = getInterfaceDescriptor();
|
||||
buffer += sizeof(LFramework::USB::InterfaceDescriptor);
|
||||
|
||||
for(uint8_t i = 0;; ++i){
|
||||
auto ep = getEndpoint(false, i);
|
||||
if(ep == nullptr){
|
||||
break;
|
||||
}
|
||||
makeEndpointDescriptor(buffer, ep);
|
||||
buffer += sizeof(LFramework::USB::EndpointDescriptor);
|
||||
}
|
||||
|
||||
for(uint8_t i = 0;; ++i){
|
||||
auto ep = getEndpoint(true, i);
|
||||
if(ep == nullptr){
|
||||
break;
|
||||
}
|
||||
makeEndpointDescriptor(buffer, ep);
|
||||
buffer += sizeof(LFramework::USB::EndpointDescriptor);
|
||||
}
|
||||
|
||||
return sizeof(LFramework::USB::InterfaceDescriptor) + getInterfaceDescriptor().bNumEndpoints * sizeof(LFramework::USB::EndpointDescriptor);
|
||||
}
|
||||
|
||||
Microsoft::Property::InterfaceGuid _interfaceGuidDescriptor;
|
||||
|
||||
USBD_StatusTypeDef interfaceRequest(USBD_HandleTypeDef* pdev, USBD_SetupReqTypedef* req) {
|
||||
if((req->bmRequest & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_VENDOR){
|
||||
if(req->bRequest == (uint8_t)USB_VENDOR_CODE_WINUSB){
|
||||
if (req->wIndex == 0x05){
|
||||
if(req->wLength != 10){
|
||||
USBD_CtlSendData (pdev, (uint8_t*)&_interfaceGuidDescriptor, req->wLength);
|
||||
}else {
|
||||
USBD_CtlSendData (pdev, (uint8_t*)&_interfaceGuidDescriptor, req->wLength);
|
||||
}
|
||||
return USBD_OK;
|
||||
}else{
|
||||
USBD_CtlError(pdev, req);
|
||||
return USBD_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return USBD_FAIL;
|
||||
}
|
||||
|
||||
static void makeEndpointDescriptor(uint8_t* buffer, const UsbEndpoint* ep){
|
||||
*reinterpret_cast<LFramework::USB::EndpointDescriptor*>(buffer) = ep->getDescriptor();
|
||||
}
|
||||
|
||||
virtual void txComplete(uint8_t epnum) {
|
||||
auto ep = getEndpoint(USB::EndpointAddress::makeIn(epnum));
|
||||
if(ep != nullptr){
|
||||
static_cast<UsbDEndpoint*>(ep)->onTxCompleteIsr();
|
||||
}
|
||||
}
|
||||
virtual void rxComplete(uint32_t rx_length, uint8_t epnum) {
|
||||
auto ep = getEndpoint(USB::EndpointAddress::makeOut(epnum));
|
||||
if(ep != nullptr){
|
||||
static_cast<UsbDEndpoint*>(ep)->onRxCompleteIsr(rx_length);
|
||||
}
|
||||
}
|
||||
bool isOpen() const {
|
||||
return _isOpen;
|
||||
}
|
||||
private:
|
||||
bool _isOpen = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
#include <stm32f7xx_hal.h>
|
||||
#include <usbd_def.h>
|
||||
#include <usbd_core.h>
|
||||
#include "UsbDDevice.h"
|
||||
|
||||
using namespace LFramework::USB;
|
||||
|
||||
extern "C" {
|
||||
extern PCD_HandleTypeDef hpcd_USB_OTG_FS;
|
||||
}
|
||||
|
||||
extern "C" USBD_StatusTypeDef halToUsbdStatus(HAL_StatusTypeDef status){
|
||||
if(status == HAL_OK){
|
||||
return USBD_OK;
|
||||
}else if(status == HAL_ERROR){
|
||||
return USBD_FAIL;
|
||||
}else if((status == HAL_BUSY) || (status == HAL_TIMEOUT)){
|
||||
return USBD_BUSY;
|
||||
}
|
||||
return USBD_FAIL;
|
||||
}
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_mps){
|
||||
USBD_StatusTypeDef status = USBD_LL_FlushEP(pdev, ep_addr);
|
||||
if(status != USBD_OK){
|
||||
return status;
|
||||
}
|
||||
return HAL_PCD_EP_Open((PCD_HandleTypeDef*)pdev->pData,ep_addr, ep_mps, ep_type) == HAL_OK ? USBD_OK : USBD_FAIL;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_FlushEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr){
|
||||
#if defined(STM32L4)//Fix for set start NACK status
|
||||
PCD_HandleTypeDef *hpcd = (PCD_HandleTypeDef *)pdev->pData;
|
||||
uint32_t USBx_BASE = (uint32_t)hpcd->Instance;
|
||||
if((ep_addr & 0x80) != 0){
|
||||
USBx_INEP(ep_addr & 0x7f)->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
|
||||
}else{
|
||||
USBx_OUTEP(ep_addr & 0x7f)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
|
||||
}
|
||||
|
||||
#endif
|
||||
return HAL_PCD_EP_Flush((PCD_HandleTypeDef*)pdev->pData, ep_addr) == HAL_OK ? USBD_OK : USBD_FAIL;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_CloseEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr){
|
||||
#if defined(STM32F4) || defined(STM32L4)
|
||||
PCD_HandleTypeDef *hpcd = (PCD_HandleTypeDef *)pdev->pData;
|
||||
USB_OTG_GlobalTypeDef *USBx = (USB_OTG_GlobalTypeDef *)hpcd->Instance;
|
||||
uint32_t USBx_BASE = (uint32_t)USBx;
|
||||
if((ep_addr & 0x80) != 0){
|
||||
USBx_INEP(ep_addr & 0x7f)->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
|
||||
}else{
|
||||
USBx_OUTEP(ep_addr & 0x7f)->DOEPCTL |= USB_OTG_DOEPCTL_EPDIS;
|
||||
}
|
||||
#endif
|
||||
return HAL_PCD_EP_Close((PCD_HandleTypeDef*)pdev->pData, ep_addr) == HAL_OK ? USBD_OK : USBD_FAIL;
|
||||
}
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev){
|
||||
return HAL_PCD_Start((PCD_HandleTypeDef*)pdev->pData) == HAL_OK ? USBD_OK : USBD_FAIL;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_Stop (USBD_HandleTypeDef *pdev){
|
||||
return HAL_PCD_Stop((PCD_HandleTypeDef*)pdev->pData) == HAL_OK ? USBD_OK : USBD_FAIL;
|
||||
}
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_LL_StallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr){
|
||||
return HAL_PCD_EP_SetStall((PCD_HandleTypeDef*)pdev->pData, ep_addr) == HAL_OK ? USBD_OK : USBD_FAIL;
|
||||
}
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size){
|
||||
return halToUsbdStatus(HAL_PCD_EP_Receive((PCD_HandleTypeDef*)pdev->pData, ep_addr, pbuf, size));
|
||||
}
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_LL_ClearStallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr){
|
||||
/*#if defined(STM32F0)
|
||||
PCD_HandleTypeDef *hpcd = (PCD_HandleTypeDef *)pdev->pData;
|
||||
PCD_EPTypeDef *ep;
|
||||
|
||||
if ((0x80 & ep_addr) == 0x80){
|
||||
ep = &hpcd->IN_ep[ep_addr & 0x7F];
|
||||
}
|
||||
else{
|
||||
ep = &hpcd->OUT_ep[ep_addr];
|
||||
}
|
||||
|
||||
ep->is_stall = 0;
|
||||
ep->num = ep_addr & 0x7F;
|
||||
ep->is_in = ((ep_addr & 0x80) == 0x80);
|
||||
|
||||
__HAL_LOCK(hpcd);
|
||||
|
||||
if (ep->is_in){
|
||||
PCD_CLEAR_TX_DTOG(hpcd->Instance, ep->num);
|
||||
//PCD_SET_EP_TX_STATUS(hpcd->Instance, ep->num, USB_EP_TX_NAK);
|
||||
} else{
|
||||
PCD_CLEAR_RX_DTOG(hpcd->Instance, ep->num);
|
||||
//PCD_SET_EP_RX_STATUS(hpcd->Instance, ep->num, USB_EP_RX_NAK);
|
||||
}
|
||||
__HAL_UNLOCK(hpcd);
|
||||
#elif defined(STM32F4) || defined(STM32L4)*/
|
||||
HAL_PCD_EP_ClrStall((PCD_HandleTypeDef*)pdev->pData, ep_addr);
|
||||
//HAL_PCD_EP_ClrStall_Ex((PCD_HandleTypeDef*)pdev->pData, ep_addr);
|
||||
/*#else
|
||||
|
||||
#error "Not supported controller"
|
||||
#endif*/
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
extern "C" uint8_t USBD_LL_IsStallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr){
|
||||
PCD_HandleTypeDef *hpcd = (PCD_HandleTypeDef*)pdev->pData;
|
||||
if((ep_addr & 0x80) == 0x80){
|
||||
return hpcd->IN_ep[ep_addr & 0x7F].is_stall;
|
||||
} else{
|
||||
return hpcd->OUT_ep[ep_addr & 0x7F].is_stall;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_LL_SetUSBAddress (USBD_HandleTypeDef *pdev, uint8_t dev_addr){
|
||||
return halToUsbdStatus(HAL_PCD_SetAddress((PCD_HandleTypeDef*)pdev->pData, dev_addr));
|
||||
}
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size){
|
||||
return halToUsbdStatus(HAL_PCD_EP_Transmit((PCD_HandleTypeDef*)pdev->pData, ep_addr, pbuf, size));
|
||||
}
|
||||
|
||||
extern "C" uint32_t USBD_LL_GetRxDataSize (USBD_HandleTypeDef *pdev, uint8_t ep_addr){
|
||||
return HAL_PCD_EP_GetRxCount((PCD_HandleTypeDef*)pdev->pData, ep_addr);
|
||||
}
|
||||
|
||||
extern "C" void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum){
|
||||
USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff);
|
||||
}
|
||||
|
||||
extern "C" void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum){
|
||||
USBD_LL_DataInStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->IN_ep[epnum].xfer_buff);
|
||||
}
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_LL_Init (USBD_HandleTypeDef* pdev){
|
||||
auto* device = UsbDevice::getContext();
|
||||
|
||||
/* Link the driver to the stack. */
|
||||
hpcd_USB_OTG_FS.pData = pdev;
|
||||
pdev->pData = &hpcd_USB_OTG_FS;
|
||||
|
||||
hpcd_USB_OTG_FS.Instance = USB_OTG_FS;
|
||||
hpcd_USB_OTG_FS.Init.dev_endpoints = 6;
|
||||
hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL;
|
||||
hpcd_USB_OTG_FS.Init.dma_enable = DISABLE;
|
||||
hpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
|
||||
hpcd_USB_OTG_FS.Init.Sof_enable = DISABLE;
|
||||
hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE;
|
||||
hpcd_USB_OTG_FS.Init.lpm_enable = DISABLE;
|
||||
hpcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE;
|
||||
hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = DISABLE;
|
||||
if (HAL_PCD_Init(&hpcd_USB_OTG_FS) != HAL_OK)
|
||||
{
|
||||
for(;;){
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
|
||||
HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80);
|
||||
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);
|
||||
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x80);
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
extern "C" USBD_StatusTypeDef USBD_LL_DeInit (USBD_HandleTypeDef *pdev){
|
||||
return HAL_PCD_DeInit((PCD_HandleTypeDef*)pdev->pData) == HAL_OK ? USBD_OK : USBD_FAIL;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
add_library(LFrameworkUsbHost INTERFACE )
|
||||
add_library(LFramework::Usb::Host ALIAS LFrameworkUsbHost )
|
||||
|
||||
|
||||
target_sources(LFrameworkUsbHost
|
||||
INTERFACE
|
||||
IUsbDevice.h
|
||||
IUsbHostEndpoint.h
|
||||
IUsbInterface.h
|
||||
IUsbService.h
|
||||
IUsbTransfer.h
|
||||
UsbException.h
|
||||
)
|
||||
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
|
||||
target_sources(LFrameworkUsbHost
|
||||
INTERFACE
|
||||
Linux/NetlinkClient.h
|
||||
Linux/NetlinkClient.cpp
|
||||
Linux/NetlinkReader.h
|
||||
Linux/UsbService.h
|
||||
Linux/UsbService.cpp
|
||||
Linux/UsbDevice.h
|
||||
Linux/UsbDevice.cpp
|
||||
Linux/UsbInterface.h
|
||||
Linux/UsbHostEndpoint.h
|
||||
Linux/UsbHostEndpoint.cpp
|
||||
Linux/UsbIoctl.h
|
||||
Linux/UsbBulkTransfer.h
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
|
||||
target_link_libraries(LFrameworkUsbHost
|
||||
INTERFACE
|
||||
Winusb
|
||||
#User32
|
||||
#Advapi32
|
||||
#Ole32
|
||||
SetupAPI
|
||||
)
|
||||
|
||||
|
||||
target_sources(LFrameworkUsbHost
|
||||
INTERFACE
|
||||
Windows/USBHDevice.cpp
|
||||
Windows/UsbHDevice.h
|
||||
Windows/UsbHostEndpoint.h
|
||||
Windows/UsbHException.h
|
||||
Windows/UsbHInterface.h
|
||||
Windows/UsbHostManager.cpp
|
||||
Windows/UsbHostManager.h
|
||||
Windows/UsbNotifyWindow.h
|
||||
Windows/UsbService.cpp
|
||||
Windows/UsbService.h
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "IUsbInterface.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class IUsbDevice {
|
||||
public:
|
||||
virtual std::shared_ptr<IUsbInterface> getInterface(std::size_t id) = 0;
|
||||
virtual std::size_t getInterfaceCount() const = 0;
|
||||
};
|
||||
|
||||
IUsbDevice* openUsbDevice(const std::string& path);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "../UsbEndpoint.h"
|
||||
#include "IUsbTransfer.h"
|
||||
#include <memory>
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class IUsbHostEndpoint : public UsbEndpoint {
|
||||
public:
|
||||
IUsbHostEndpoint(const USB::EndpointDescriptor& descriptor):UsbEndpoint(descriptor){}
|
||||
virtual ~IUsbHostEndpoint() = default;
|
||||
virtual std::shared_ptr<IUsbTransfer> transferAsync(void* buffer, size_t size) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "IUsbHostEndpoint.h"
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class IUsbInterface {
|
||||
public:
|
||||
virtual IUsbHostEndpoint* getEndpoint(bool isInEndpoint, uint8_t id) = 0;
|
||||
virtual const IUsbHostEndpoint* getEndpoint(bool isInEndpoint, uint8_t id) const = 0;
|
||||
virtual const USB::InterfaceDescriptor& getInterfaceDescriptor() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
struct UsbDeviceInfo {
|
||||
std::uint16_t vid;
|
||||
std::uint16_t pid;
|
||||
std::string serialNumber;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
class IUsbService {
|
||||
public:
|
||||
virtual ~IUsbService() = default;
|
||||
virtual UsbDeviceInfo getUsbDeviceInfo(const std::string& path) = 0;
|
||||
virtual std::vector<UsbDeviceInfo> enumerateDevices() = 0;
|
||||
virtual bool startEventsListening(std::function<void()> deviceChangeCallback) = 0;
|
||||
virtual void stopEventsListening() = 0;
|
||||
};
|
||||
|
||||
IUsbService* createUsbService();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class IUsbTransfer {
|
||||
public:
|
||||
virtual ~IUsbTransfer() = default;
|
||||
virtual std::size_t wait() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "NetlinkClient.h"
|
||||
#include "../UsbException.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#define NL_GROUP_KERNEL 1
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
NetlinkClient::NetlinkClient() {
|
||||
sockaddr_nl socketAddressClient = {};
|
||||
socketAddressClient.nl_family = AF_NETLINK;
|
||||
socketAddressClient.nl_groups = NL_GROUP_KERNEL;
|
||||
auto socketType = SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC;
|
||||
_socket = socket(PF_NETLINK, socketType, NETLINK_KOBJECT_UEVENT);
|
||||
if ((_socket < 0) && errno == EINVAL) {
|
||||
socketType = SOCK_RAW;
|
||||
_socket = socket(PF_NETLINK, socketType, NETLINK_KOBJECT_UEVENT);
|
||||
}
|
||||
|
||||
if (_socket < 0){
|
||||
throw UsbException(std::string("Failed to create Netlink socket: ") + strerror(errno));
|
||||
}
|
||||
|
||||
//set non blocking mode
|
||||
int flags;
|
||||
if (!static_cast<bool>(socketType & SOCK_NONBLOCK)) {
|
||||
flags = fcntl(_socket, F_GETFL);
|
||||
fcntl(_socket, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
|
||||
if(bind(_socket, reinterpret_cast<sockaddr*>(&socketAddressClient), sizeof(socketAddressClient)) < 0) {
|
||||
close(_socket);
|
||||
throw UsbException(std::string("Failed to bing Netlink socket: ") + strerror(errno));
|
||||
}
|
||||
|
||||
int optValue = 1;
|
||||
if (setsockopt(_socket, SOL_SOCKET, SO_PASSCRED, &optValue, sizeof(optValue)) < 0) {
|
||||
close(_socket);
|
||||
throw UsbException(std::string("Failed to set Netlink socket option: ") + strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
NetlinkClient::~NetlinkClient(){
|
||||
if(_socket >= 0){
|
||||
close(_socket);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
class NetlinkClient {
|
||||
public:
|
||||
NetlinkClient();
|
||||
~NetlinkClient();
|
||||
int getSocketFileDescriptor() const {
|
||||
return _socket;
|
||||
}
|
||||
private:
|
||||
int _socket = -1;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
#pragma once
|
||||
|
||||
#include "NetlinkClient.h"
|
||||
#include <memory>
|
||||
#include <sys/epoll.h>
|
||||
#include <stdexcept>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <thread>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
namespace LFramework::USB {
|
||||
|
||||
#define NL_GROUP_KERNEL 1
|
||||
|
||||
class NetlinkReader {
|
||||
public:
|
||||
struct DeviceEvent {
|
||||
bool removed;
|
||||
std::uint8_t busNumber;
|
||||
std::uint8_t deviceNumber;
|
||||
std::string sysfsFileName;
|
||||
};
|
||||
|
||||
using EventCallback = std::function<void(const DeviceEvent&)>;
|
||||
|
||||
NetlinkReader(std::shared_ptr<NetlinkClient> client, EventCallback callback) : _client(client), _callback(callback){
|
||||
_epoll = epoll_create(1);
|
||||
|
||||
epoll_event epollEvent = {};
|
||||
epollEvent.events = EPOLLIN | EPOLLERR;
|
||||
epollEvent.data.fd = client->getSocketFileDescriptor();
|
||||
|
||||
if(epoll_ctl(_epoll, EPOLL_CTL_ADD, epollEvent.data.fd, &epollEvent) < 0){
|
||||
close(_epoll);
|
||||
throw std::runtime_error("epoll_ctl failed on Netlink socket");
|
||||
}
|
||||
|
||||
_isRunning = true;
|
||||
_pollingThread = std::thread([this](){
|
||||
readNetlinkThreadHandler();
|
||||
});
|
||||
}
|
||||
|
||||
~NetlinkReader(){
|
||||
_isRunning = false;
|
||||
epoll_ctl(_epoll, EPOLL_CTL_DEL, _client->getSocketFileDescriptor(), nullptr);
|
||||
close(_epoll);
|
||||
if(_pollingThread.joinable()){
|
||||
_pollingThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<NetlinkClient> _client;
|
||||
EventCallback _callback;
|
||||
int _epoll = -1;
|
||||
std::thread _pollingThread;
|
||||
bool _isRunning = false;
|
||||
|
||||
|
||||
void readNetlinkThreadHandler() {
|
||||
epoll_event event{};
|
||||
while(_isRunning) {
|
||||
auto epollFileDescriptors = epoll_wait(_epoll, &event, 1, 100);
|
||||
if(epollFileDescriptors <= 0){
|
||||
continue;
|
||||
}
|
||||
if (static_cast<bool>(event.events & EPOLLIN)){
|
||||
std::vector<std::string> message;
|
||||
if(readNetlinkMessage(_client->getSocketFileDescriptor(), message)){
|
||||
|
||||
auto subsystem = getNetlinkVariableValue("SUBSYSTEM", message);
|
||||
if(!subsystem.has_value() || subsystem.value() != "usb"){
|
||||
continue; //Not USB subsystem message
|
||||
}
|
||||
|
||||
auto devtype = getNetlinkVariableValue("DEVTYPE", message);
|
||||
if(!devtype.has_value() || (devtype != "usb_device")){
|
||||
continue; //Not USB device ?
|
||||
}
|
||||
|
||||
auto action = getNetlinkVariableValue("ACTION", message);
|
||||
bool removed = false;
|
||||
if(!action.has_value()){
|
||||
continue;
|
||||
}else if(action.value() == "remove"){
|
||||
removed = true;
|
||||
}else if(action.value() != "add"){
|
||||
continue; //Not interesting message
|
||||
}
|
||||
|
||||
DeviceEvent event;
|
||||
event.removed = removed;
|
||||
|
||||
|
||||
auto busnum = getNetlinkVariableValue("BUSNUM", message);
|
||||
if(busnum.has_value()){
|
||||
event.busNumber = std::atoi(busnum.value().c_str());
|
||||
auto devnum = getNetlinkVariableValue("DEVNUM", message);
|
||||
if(!devnum.has_value()){
|
||||
//Well, this is not expected. Maybe report error here!?
|
||||
continue;
|
||||
}
|
||||
event.deviceNumber = std::atoi(devnum.value().c_str());
|
||||
}else{
|
||||
//Some old format device path?
|
||||
|
||||
auto devicePath = getNetlinkVariableValue("DEVICE", message);
|
||||
if(!devicePath.has_value()){
|
||||
continue;
|
||||
}
|
||||
|
||||
auto slashPos = devicePath.value().find_last_of('/');
|
||||
auto devNumStr = devicePath.value().substr(slashPos + 1);
|
||||
auto busNumStr = devicePath.value().substr(slashPos - 3);
|
||||
|
||||
event.busNumber = std::atoi(busNumStr.c_str());
|
||||
event.deviceNumber = std::atoi(devNumStr.c_str());
|
||||
|
||||
}
|
||||
|
||||
auto devicePath = getNetlinkVariableValue("DEVPATH", message);
|
||||
if(devicePath.has_value()){
|
||||
auto slashPos = devicePath.value().find_last_of('/');
|
||||
event.sysfsFileName = devicePath.value().substr(slashPos + 1);
|
||||
}else{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(_callback != nullptr){
|
||||
_callback(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> getNetlinkVariableValue(const char* key, const std::vector<std::string>& data){
|
||||
auto keyLength = strlen(key);
|
||||
for(const auto& s : data){
|
||||
if((strncmp(key, s.c_str(), keyLength) == 0) && (s[keyLength] == '=')){
|
||||
return s.substr(keyLength + 1);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static std::vector<std::string> parseNetlinkMessageBuffer(const char* buffer, int bufferLength){
|
||||
std::vector<std::string> result;
|
||||
const char *end = buffer + bufferLength;
|
||||
|
||||
while (buffer < end && *buffer) {
|
||||
result.push_back(buffer);
|
||||
buffer += strlen(buffer) + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool readNetlinkMessage(int netlinkFd, std::vector<std::string>& messageData){
|
||||
auto result = true;
|
||||
char buffer[CMSG_SPACE(sizeof(struct ucred))];
|
||||
constexpr size_t messageBufferLength = 2048;
|
||||
char messageBuffer[messageBufferLength];
|
||||
sockaddr_nl socketAddress{};
|
||||
iovec iov {};
|
||||
iov.iov_base = messageBuffer;
|
||||
iov.iov_len = sizeof(messageBuffer);
|
||||
msghdr msg{};
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = buffer;
|
||||
msg.msg_controllen = sizeof(buffer);
|
||||
msg.msg_name = &socketAddress;
|
||||
msg.msg_namelen = sizeof(socketAddress);
|
||||
ssize_t length = recvmsg(netlinkFd, &msg, 0);
|
||||
if (length == -1 && errno != EAGAIN && errno != EINTR)
|
||||
result = false;
|
||||
if ((msg.msg_flags & MSG_TRUNC) != 0)
|
||||
result = false;
|
||||
if (socketAddress.nl_groups != NL_GROUP_KERNEL || socketAddress.nl_pid != 0)
|
||||
result = false;
|
||||
auto cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS)
|
||||
result = false;
|
||||
auto cred = (ucred *)CMSG_DATA(cmsg);
|
||||
if (cred->uid != 0)
|
||||
result = false;
|
||||
if(result)
|
||||
messageData = parseNetlinkMessageBuffer(messageBuffer, length);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
namespace LFramework::USB::SysfsUtils {
|
||||
|
||||
inline bool isUsbDeviceName(const std::string& name){
|
||||
return !(
|
||||
std::any_of(name.begin(), name.end(), [](char c){ return std::isalpha(c); })
|
||||
|| name == "." || name == ".."
|
||||
|| name.find(':') != std::string::npos
|
||||
|| name.find("usb") != std::string::npos);
|
||||
}
|
||||
|
||||
inline std::string readDeviceAttribute(const std::string &devicePath, const char *attributeName) {
|
||||
std::string attributeValue = "";
|
||||
auto attributeFilePath = devicePath + attributeName;
|
||||
std::ifstream attributeFile;
|
||||
attributeFile.open(attributeFilePath);
|
||||
if(attributeFile.is_open()) {
|
||||
attributeFile >> attributeValue;
|
||||
}
|
||||
attributeFile.close();
|
||||
return attributeValue;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T readIntDeviceAttribute(const std::string &devicePath, const char *attributeName, uint base){
|
||||
auto strValue = readDeviceAttribute(devicePath, attributeName);
|
||||
return static_cast<T>(std::strtol(strValue.c_str(), nullptr, base));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user