This commit is contained in:
2025-05-13 02:03:18 +03:00
parent 72860c7968
commit 90ef6120fd
463 changed files with 380758 additions and 2 deletions
+26
View File
@@ -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)
}
+70
View File
@@ -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_ */
+55
View File
@@ -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);
}
+37
View File
@@ -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
)
+30
View File
@@ -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>(); \
}
}
+602
View File
@@ -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
View File
@@ -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__
+121
View File
@@ -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
View File
@@ -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;
}
};
}
+36
View File
@@ -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
+10
View File
@@ -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)
+39
View File
@@ -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");
}
}
+178
View File
@@ -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
@@ -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
)
+150
View File
@@ -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
+24
View File
@@ -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
+27
View File
@@ -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__
+80
View File
@@ -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
+32
View File
@@ -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
)
+32
View File
@@ -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_ */
+42
View File
@@ -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__
@@ -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
)
+76
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
}
}
+28
View File
@@ -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__
+87
View File
@@ -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
)
+34
View File
@@ -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
View File
@@ -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