Add device sharing support

This commit is contained in:
Kyle Schwarz
2022-12-13 11:46:32 -05:00
parent 78465e0f20
commit a9157c82e5
46 changed files with 1946 additions and 102 deletions
+68 -3
View File
@@ -4,15 +4,49 @@
#include <stdint.h>
#include <time.h>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4201) // nameless struct/union
#endif
typedef struct {
const char* description;
time_t timestamp;
uint32_t eventNumber;
uint8_t severity;
char serial[7];
} neoeventcontext_t;
typedef struct {
time_t timestamp;
union {
struct {
uint32_t eventNumber;
int8_t severity;
char serial[7];
};
neoeventcontext_t eventContext;
};
} neosocketevent_t;
typedef neoeventcontext_t neosocketeventfilter_t;
typedef struct {
const char* description;
union {
struct {
time_t timestamp;
uint32_t eventNumber;
uint8_t severity;
char serial[7];
};
neosocketevent_t socketEvent;
};
uint8_t reserved[16];
} neoevent_t;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#ifdef __cplusplus
#include <vector>
@@ -49,6 +83,7 @@ public:
ValueNotYetPresent = 0x1013,
Timeout = 0x1014,
WiVINotSupported = 0x1015,
TestEvent = 0x1016,
// Device Events
PollingMessageOverflow = 0x2000,
@@ -103,7 +138,31 @@ public:
PCAPCouldNotStart = 0x3102,
PCAPCouldNotFindDevices = 0x3103,
PacketDecodingError = 0x3104,
// Device Sharing Server Events
SharedMemoryDataIsNull = 0x4001,
SharedMemoryFailedToClose = 0x4002,
SharedMemoryFailedToOpen = 0x4003,
SharedMemoryFailedToUnlink = 0x4004,
SharedMemoryFileTruncateError = 0x4005,
SharedMemoryMappingError = 0x4006,
SharedMemoryUnmapError = 0x4007,
SharedSemaphoreFailedToClose = 0x4008,
SharedSemaphoreFailedToOpen = 0x4009,
SharedSemaphoreFailedToPost = 0x4010,
SharedSemaphoreFailedToUnlink = 0x4011,
SharedSemaphoreFailedToWait = 0x4012,
SharedSemaphoreNotOpenForPost = 0x4013,
SharedSemaphoreNotOpenForWait = 0x4014,
SocketFailedToOpen = 0x4015,
SocketFailedToClose = 0x4016,
SocketFailedToConnect = 0x4017,
SocketFailedToRead = 0x4018,
SocketFailedToWrite = 0x4019,
SocketAcceptorFailedToBind = 0x4020,
SocketAcceptorFailedToListen = 0x4021,
// Other Errors
NoErrorFound = 0xFFFFFFFD,
TooManyEvents = 0xFFFFFFFE,
Unknown = 0xFFFFFFFF
@@ -117,8 +176,10 @@ public:
APIEvent() : eventStruct({}), serial(), timepoint(), device(nullptr) {}
APIEvent(APIEvent::Type event, APIEvent::Severity severity, const Device* device = nullptr);
APIEvent(neosocketevent_t evStruct, const Device* device = nullptr);
const neoevent_t* getNeoEvent() const noexcept { return &eventStruct; }
neosocketevent_t getNeoSocketEvent() const noexcept;
Type getType() const noexcept { return Type(eventStruct.eventNumber); }
Severity getSeverity() const noexcept { return Severity(eventStruct.severity); }
std::string getDescription() const noexcept { return std::string(eventStruct.description); }
@@ -157,8 +218,12 @@ public:
EventFilter(const Device* device, APIEvent::Severity severity) : severity(severity), matchOnDevicePtr(true), device(device) {}
EventFilter(std::string serial, APIEvent::Type type = APIEvent::Type::Any, APIEvent::Severity severity = APIEvent::Severity::Any) : type(type), severity(severity), serial(serial) {}
EventFilter(std::string serial, APIEvent::Severity severity) : severity(severity), serial(serial) {}
EventFilter(neosocketeventfilter_t evFilterSt) : type(static_cast<APIEvent::Type>(evFilterSt.eventNumber)),
severity(static_cast<APIEvent::Severity>(evFilterSt.severity)),
serial(std::string(evFilterSt.serial)) {}
bool match(const APIEvent& event) const noexcept;
neosocketeventfilter_t getNeoSocketEventFilter() const noexcept;
APIEvent::Type type = APIEvent::Type::Any;
APIEvent::Severity severity = APIEvent::Severity::Any;
+7
View File
@@ -10,6 +10,7 @@
#include <map>
#include <thread>
#include <algorithm>
#include <optional>
#include "icsneo/api/event.h"
#include "icsneo/api/eventcallback.h"
@@ -34,6 +35,8 @@ public:
// If this thread exists in the map, turn off downgrading
void cancelErrorDowngradingOnCurrentThread();
void removeEventMirror(const std::thread::id& id);
bool isDowngradingErrorsOnCurrentThread() const;
int addEventCallback(const EventCallback &cb);
@@ -108,6 +111,10 @@ private:
bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow
void discardOldest(size_t count = 1);
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
std::optional<std::vector<neosocketevent_t>> getServerEvents(const size_t& max);
#endif
};
}
@@ -21,11 +21,14 @@
#include <thread>
#include <queue>
#include <map>
#include <list>
namespace icsneo {
class Communication {
public:
typedef std::function<void(std::vector<uint8_t>&)> RawCallback;
// Note that the Packetizer is not created by the constructor,
// and should be done once the Communication module is in place.
Communication(
@@ -45,6 +48,7 @@ public:
void modeChangeIncoming() { driver->modeChangeIncoming(); }
void awaitModeChangeComplete() { driver->awaitModeChangeComplete(); }
bool rawWrite(const std::vector<uint8_t>& bytes) { return driver->write(bytes); }
void modifyRawCallbacks(std::function<void(std::list<Communication::RawCallback>&)>&& cb);
virtual bool sendPacket(std::vector<uint8_t>& bytes);
bool redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo);
void clearRedirectRead();
@@ -88,6 +92,8 @@ protected:
std::atomic<bool> redirectingRead{false};
std::function<void(std::vector<uint8_t>&&)> redirectionFn;
std::mutex redirectingReadMutex; // Don't allow read to be disabled while in the redirectionFn
std::mutex rawCallbacksMutex;
std::list<std::function<void(std::vector<uint8_t>&)>> rawCallbacks;
std::mutex syncMessageMutex;
void dispatchMessage(const std::shared_ptr<Message>& msg);
+4 -1
View File
@@ -9,6 +9,7 @@
#include <thread>
#include <mutex>
#include <condition_variable>
#include "icsneo/device/neodevice.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
@@ -16,7 +17,7 @@ namespace icsneo {
class Driver {
public:
Driver(const device_eventhandler_t& handler) : report(handler) {}
Driver(const device_eventhandler_t& handler, neodevice_t& forDevice) : report(handler), device(forDevice) {}
virtual ~Driver() {}
virtual bool open() = 0;
virtual bool isOpen() = 0;
@@ -24,12 +25,14 @@ public:
virtual void awaitModeChangeComplete() {}
virtual bool isDisconnected() { return disconnected; };
virtual bool close() = 0;
virtual bool enableHeartbeat() const { return false; }
bool read(std::vector<uint8_t>& bytes, size_t limit = 0);
bool readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout = std::chrono::milliseconds(100), size_t limit = 0);
bool write(const std::vector<uint8_t>& bytes);
virtual bool isEthernet() const { return false; }
device_eventhandler_t report;
neodevice_t& device;
size_t writeQueueSize = 50;
bool writeBlocks = true; // Otherwise it just fails when the queue is full
@@ -0,0 +1,42 @@
#ifndef __INTERPROCESSMAILBOX_H_
#define __INTERPROCESSMAILBOX_H_
#ifdef __cplusplus
#include <cstdint>
#include "icsneo/platform/sharedmemory.h"
#include "icsneo/platform/sharedsemaphore.h"
static constexpr uint16_t MESSAGE_COUNT = 1024;
static constexpr uint16_t BLOCK_SIZE = 2048;
using LengthFieldType = uint16_t;
static constexpr uint8_t LENGTH_FIELD_SIZE = sizeof(LengthFieldType);
static constexpr uint16_t MAX_DATA_SIZE = BLOCK_SIZE - LENGTH_FIELD_SIZE;
namespace icsneo {
class InterprocessMailbox {
public:
bool open(const std::string& name, bool create = false /* create the shared resources or not */);
bool close();
operator bool() const;
// data must be large enough to hold at least MAX_DATA_SIZE
// messageLength can be larger than MAX_DATA_SIZE if the message spans multiple blocks, only MAX_DATA_SIZE will be read
bool read(void* data, LengthFieldType& messageLength, const std::chrono::milliseconds& timeout);
// if messageLength is larger than MAX_DATA_SIZE it's expected that future write() calls will send the remaining data
bool write(const void* data, LengthFieldType messageLength, const std::chrono::milliseconds& timeout);
private:
icsneo::SharedSemaphore queuedSem;
icsneo::SharedSemaphore emptySem;
icsneo::SharedMemory sharedMem;
unsigned index = 0; // index into messages;
bool valid = false;
};
}
#endif // __cplusplus
#endif
+34
View File
@@ -0,0 +1,34 @@
#ifndef __SDIO_H_
#define __SDIO_H_
#ifdef __cplusplus
#include "icsneo/communication/driver.h"
#include "icsneo/communication/interprocessmailbox.h"
namespace icsneo {
class SDIO : public Driver {
public:
static void Find(std::vector<FoundDevice>& found);
SDIO(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err, forDevice) {}
~SDIO() { if(isOpen()) close(); }
bool open() override;
bool close() override;
bool isOpen() override;
bool enableHeartbeat() const override { return true; }
private:
void readTask() override;
void writeTask() override;
bool deviceOpen = false;
InterprocessMailbox outboundIO;
InterprocessMailbox inboundIO;
};
}
#endif // __cplusplus
#endif
+124
View File
@@ -0,0 +1,124 @@
#ifndef __SOCKET_H_
#define __SOCKET_H_
#ifdef __cplusplus
#include <vector>
#include <optional>
#include <string>
#include <memory>
#include <functional>
#include <mutex>
#include <atomic>
#ifdef _WIN32
#define NOMINMAX
#include <winsock2.h>
#include <ws2tcpip.h>
typedef SOCKET SocketFileDescriptor;
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string.h>
typedef int SocketFileDescriptor;
#endif
namespace icsneo {
enum class RPC {
DEVICE_FINDER_FIND_ALL,
DEVICE_FINDER_GET_SUPORTED_DEVICES,
DEVICE_OPEN,
DEVICE_GO_ONLINE,
DEVICE_GO_OFFLINE,
DEVICE_CLOSE,
DEVICE_LOCK,
DEVICE_UNLOCK,
SDIO_OPEN,
SDIO_CLOSE,
GET_EVENTS,
GET_LAST_ERROR,
GET_EVENT_COUNT,
DISCARD_EVENTS,
SET_EVENT_LIMIT,
GET_EVENT_LIMIT
};
static constexpr uint16_t RPC_PORT = 54949;
class SocketBase {
public:
enum class Protocol {
TCP = SOCK_STREAM,
};
bool open();
bool close();
bool connect();
bool isOpen();
bool isConnected();
bool read(void* output, std::size_t length);
bool write(const void* input, std::size_t length);
bool writeString(const std::string& str);
bool readString(std::string& str);
template<typename... Ts>
bool writeTyped(Ts... input) {
return (... && write(&input, sizeof(input)));
}
template<typename... Ts>
bool readTyped(Ts&... output) {
return (... && read(&output, sizeof(output)));
}
protected:
Protocol protocol;
uint16_t port;
SocketFileDescriptor sockFileDescriptor;
bool sockIsOpen = false;
bool sockIsConnected = false;
void setIgnoreSIGPIPE();
};
// RAII Socket
class ActiveSocket : public SocketBase {
public:
ActiveSocket(SocketFileDescriptor sockFD);
ActiveSocket(Protocol protocol, uint16_t port);
~ActiveSocket();
};
// RAII Socket IO
class LockedSocket : public SocketBase {
public:
LockedSocket(SocketBase& socket, std::unique_lock<std::mutex>&& lock);
private:
std::unique_lock<std::mutex> lock;
};
class Acceptor : public ActiveSocket {
public:
Acceptor(Protocol protocol, uint16_t port);
bool initialize();
std::shared_ptr<ActiveSocket> accept();
private:
bool isValid = false;
bool bind();
bool listen();
};
LockedSocket lockSocket();
}
#endif // __cplusplus
#endif
+1 -1
View File
@@ -3,7 +3,7 @@
// Hold the length of the longest name, so that C applications can allocate memory accordingly
// Currently the longest is "Intrepid Ethernet Evaluation Board"
#define ICSNEO_DEVICETYPE_LONGEST_NAME (35 + 1) // Add 1 so that if someone forgets, they still have space for null terminator
#define ICSNEO_DEVICETYPE_LONGEST_NAME (145 + 1) // Add 1 so that if someone forgets, they still have space for null terminator
#define ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION (ICSNEO_DEVICETYPE_LONGEST_NAME + 7) // 6 character serial, plus space
#ifndef __cplusplus
+1 -1
View File
@@ -11,7 +11,7 @@ typedef std::function< std::unique_ptr<Driver>(device_eventhandler_t err, neodev
class FoundDevice {
public:
neodevice_handle_t handle = 0;
char serial[7] = {};
deviceserial_t serial = {};
uint16_t productId = 0;
driver_factory_t makeDriver;
};
+2 -1
View File
@@ -18,6 +18,7 @@ typedef void* devicehandle_t;
#endif
typedef int32_t neodevice_handle_t;
typedef char deviceserial_t[7];
#pragma pack(push, 1)
@@ -31,7 +32,7 @@ typedef struct {
devicehandle_t device; // Pointer back to the C++ device object
neodevice_handle_t handle; // Handle for use by the underlying driver
devicetype_t type;
char serial[7];
deviceserial_t serial;
} neodevice_t;
#pragma pack(pop)
+1 -2
View File
@@ -25,7 +25,7 @@ public:
* in cdcacmlinux.cpp and cdcacmdarwin.cpp respectively
* Other POSIX systems (BSDs, QNX, etc) will need bespoke code written in the future
*/
CDCACM(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {}
CDCACM(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err, forDevice) {}
~CDCACM();
static void Find(std::vector<FoundDevice>& found);
@@ -37,7 +37,6 @@ public:
void awaitModeChangeComplete() override;
private:
neodevice_t& device;
int fd = -1;
std::optional<ino_t> disallowedInode;
std::atomic<bool> modeChanging{false};
-2
View File
@@ -60,8 +60,6 @@ private:
void readTask();
void writeTask();
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
neodevice_t& device;
};
}
+1 -1
View File
@@ -24,9 +24,9 @@ public:
bool isOpen() override;
bool close() override;
bool isEthernet() const override { return true; }
bool enableHeartbeat() const override { return true; }
private:
char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
neodevice_t& device;
uint8_t deviceMAC[6];
bool openable = true;
EthernetPacketizer ethPacketizer;
@@ -0,0 +1,36 @@
#ifndef __SHAREDMEMORY_POSIX_H_
#define __SHAREDMEMORY_POSIX_H_
#ifdef __cplusplus
#include <string>
#include <optional>
#include "icsneo/api/eventmanager.h"
namespace icsneo {
class SharedMemory {
public:
SharedMemory() : report(makeEventHandler()) {};
~SharedMemory();
bool open(const std::string& name, uint32_t size, bool create = false);
bool close();
uint8_t* data();
private:
virtual device_eventhandler_t makeEventHandler() {
return [](APIEvent::Type type, APIEvent::Severity severity)
{ EventManager::GetInstance().add(type, severity); };
}
device_eventhandler_t report;
std::optional<std::string> mName;
std::optional<std::pair<uint8_t*, uint32_t>> mData;
std::optional<bool> mCreated;
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,40 @@
#ifndef __SHAREDSEMAPHORE_POSIX_H_
#define __SHAREDSEMAPHORE_POSIX_H_
#ifdef __cplusplus
#include <string>
#include <optional>
#include <atomic>
#include <semaphore.h>
#include "icsneo/api/eventmanager.h"
namespace icsneo {
class SharedSemaphore {
public:
~SharedSemaphore();
bool open(const std::string& name, bool create = false, unsigned initialCount = 0);
bool close();
bool wait(const std::chrono::milliseconds& timeout);
bool post();
private:
virtual device_eventhandler_t makeEventHandler() {
return [](APIEvent::Type type, APIEvent::Severity severity)
{ EventManager::GetInstance().add(type, severity); };
}
std::atomic<bool> closing = false;
std::atomic<bool> waiting = false;
device_eventhandler_t report = makeEventHandler();
std::optional<const std::string> mName;
std::optional<sem_t*> semaphore;
std::optional<const bool> created;
};
}
#endif // __cplusplus
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef __SHAREDMEMORY_H_
#define __SHAREDMEMORY_H_
#ifdef _WIN32
#include "icsneo/platform/windows/sharedmemory.h"
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include "icsneo/platform/posix/sharedmemory.h"
#else
#warning "Shared memory are not supported on this platform"
#endif
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef __SHAREDSEMAPHORE_H_
#define __SHAREDSEMAPHORE_H_
#ifdef _WIN32
#include "icsneo/platform/windows/sharedsemaphore.h"
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include "icsneo/platform/posix/sharedsemaphore.h"
#else
#warning "Shared semaphores are not supported on this platform"
#endif
#endif
+1 -1
View File
@@ -24,10 +24,10 @@ public:
bool isOpen() override;
bool close() override;
bool isEthernet() const override { return true; }
bool enableHeartbeat() const override { return true; }
private:
const PCAPDLL& pcap;
char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
neodevice_t& device;
uint8_t deviceMAC[6];
bool openable = true;
EthernetPacketizer ethPacketizer;
@@ -0,0 +1,37 @@
#ifndef __SHAREDMEMORY_WINDOWS_H_
#define __SHAREDMEMORY_WINDOWS_H_
#ifdef __cplusplus
#include <string>
#include <optional>
#include "icsneo/platform/windows.h"
#include "icsneo/api/eventmanager.h"
namespace icsneo {
class SharedMemory {
public:
SharedMemory() : report(makeEventHandler()) {};
~SharedMemory();
bool open(const std::string& name, uint32_t size, bool create = false);
bool close();
uint8_t* data();
private:
virtual device_eventhandler_t makeEventHandler() {
return [](APIEvent::Type type, APIEvent::Severity severity)
{ EventManager::GetInstance().add(type, severity); };
}
device_eventhandler_t report;
std::optional<HANDLE> mHandle;
std::optional<std::pair<uint8_t*, uint32_t>> mData;
std::optional<bool> mCreated;
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,38 @@
#ifndef __SHAREDSEMAPHORE_WINDOWS_H_
#define __SHAREDSEMAPHORE_WINDOWS_H_
#ifdef __cplusplus
#include <string>
#include <chrono>
#include <optional>
#include "icsneo/platform/windows.h"
#include "icsneo/api/eventmanager.h"
namespace icsneo {
class SharedSemaphore {
public:
~SharedSemaphore();
bool open(const std::string& name, bool create = false, unsigned initialCount = 0);
bool close();
bool wait(const std::chrono::milliseconds& timeout);
bool post();
private:
virtual device_eventhandler_t makeEventHandler() {
return [](APIEvent::Type type, APIEvent::Severity severity)
{ EventManager::GetInstance().add(type, severity); };
}
bool closing = false;
device_eventhandler_t report = makeEventHandler();
std::optional<HANDLE> semaphore;
std::optional<bool> created;
};
}
#endif // __cplusplus
#endif
-1
View File
@@ -31,7 +31,6 @@ public:
private:
bool open(bool fromAsync);
bool opening = false;
neodevice_t& device;
struct Detail;
std::shared_ptr<Detail> detail;