Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <stdio.h>
- #include "abi.h"
- #include <string.h>
- #define WIN32_LEAN_AND_MEAN
- #include <Windows.h>
- typedef struct {
- uint8_t* data;
- size_t size;
- size_t capacity;
- } arr_t;
- typedef struct {
- arr_t log;
- arr_t payload;
- int state;
- wchar_t portName[64];
- espmon_frame_callback_t frame_callback;
- void* frame_callback_state;
- espmon_disconnected_callback_t disconnected_callback;
- void* disconnected_callback_state;
- uint8_t* write_buf; // malloc'd full frame; thread frees after write
- size_t write_buf_length;
- HANDLE port;
- HANDLE exit_event;
- HANDLE exited_event;
- HANDLE write_pending_event; // auto-reset: WriteFrame signals, thread waits
- HANDLE write_slot_event; // manual-reset: starts signaled, WriteFrame waits for slot free
- HANDLE thread;
- } session_t;
- typedef struct {
- int state;
- uint8_t cmd;
- uint32_t len;
- uint32_t crc;
- } state_machine_t;
- static bool array_ensure(arr_t* arr,size_t slots) {
- if(arr->data==nullptr) {
- if(arr->capacity==0) {
- arr->capacity=slots<16?16:slots;
- arr->size = 0;
- }
- arr->data = (uint8_t*)malloc(arr->capacity);
- if(arr->data==nullptr) {arr->capacity=0; return false;}
- return true;
- }
- size_t cap = arr->capacity;
- while(arr->size+slots>cap) { cap*=2; }
- uint8_t* p = (uint8_t*)realloc(arr->data,cap);
- if(p==nullptr) {return false;}
- arr->data = p;
- arr->capacity=cap;
- return true;
- }
- static bool array_append(arr_t* arr, uint8_t data) {
- if(!array_ensure(arr,1)) { return false; }
- arr->data[arr->size++]=data;
- return true;
- }
- static void array_destroy(arr_t* arr) {
- if(arr->capacity>0 & arr->data!=nullptr) {
- free(arr->data);
- }
- arr->size = 0;
- arr->capacity = 0;
- arr->data = nullptr;
- }
- static uint32_t crc32(const uint8_t* data, size_t length, uint32_t seed = UINT32_MAX / 3) {
- uint32_t result = seed;
- while (length--) {
- result ^= *data++;
- }
- return result;
- }
- static bool state_step(session_t* session, state_machine_t* machine, uint8_t data) {
- if(machine->state==0) {
- if(data==0||data>31) {
- array_append(&session->log,data);
- return false;
- }
- machine->state = 1;
- machine->cmd = data;
- machine->len = 0;
- } else if(machine->state<8) {
- if(machine->cmd!=data) {
- if(array_ensure(&session->log,8-machine->state)) {
- for(size_t i = machine->state;i<7;++i) {
- array_append(&session->log,machine->cmd);
- }
- array_append(&session->log,data);
- }
- machine->state = 0;
- return false;
- }
- ++machine->state;
- } else if(machine->state==8) {
- machine->len = data;
- ++machine->state;
- } else if(machine->state<12) {
- machine->len <<= 8;
- machine->len |= data;
- ++machine->state;
- } else if(machine->state==12) {
- machine->crc = data;
- ++machine->state;
- } else if(machine->state<16) {
- machine->crc <<= 8;
- machine->crc |= data;
- ++machine->state;
- } else if(machine->state==16) {
- if(!array_ensure(&session->payload,machine->len)) {
- machine->state = 0;
- return false;
- }
- ++machine->state;
- } else {
- machine->state = 0;
- return false;
- }
- return true;
- }
- static bool do_write(HANDLE port, OVERLAPPED* ov, HANDLE exit_event, const uint8_t* data, size_t length) {
- if(length==0) return true;
- HANDLE handles[] { exit_event, ov->hEvent };
- DWORD written;
- if(WriteFile(port,data,(DWORD)length,&written,ov)) {
- ResetEvent(ov->hEvent);
- return true;
- }
- DWORD err = GetLastError();
- if(err!=ERROR_IO_PENDING) {
- puts("ERROR: write error");
- return false;
- }
- DWORD wait = WaitForMultipleObjects(2,handles,FALSE,INFINITE);
- ResetEvent(ov->hEvent);
- if(wait==WAIT_OBJECT_0) return false; // exit signalled
- if(wait!=WAIT_OBJECT_0+1) return false;
- return GetOverlappedResult(port,ov,&written,FALSE)!=0;
- }
- static void deinit_session(session_t* sess) {
- CloseHandle(sess->write_pending_event);
- CloseHandle(sess->write_slot_event);
- CloseHandle(sess->exited_event);
- CloseHandle(sess->exit_event);
- CloseHandle(sess->port);
- if(sess->log.data!=nullptr) {
- free(sess->log.data);
- }
- if(sess->payload.data!=nullptr) {
- free(sess->payload.data);
- }
- delete sess;
- }
- static ULONG STDMETHODCALLTYPE SessionThread(LPVOID threadArg) {
- session_t* sess = (session_t*)threadArg;
- state_machine_t mach = {0};
- OVERLAPPED o_reader = {0};
- o_reader.hEvent = CreateEvent(nullptr,TRUE,FALSE,nullptr);
- OVERLAPPED o_writer = {0};
- o_writer.hEvent = CreateEvent(nullptr,TRUE,FALSE,nullptr);
- OVERLAPPED o_status = {0};
- o_status.hEvent = CreateEvent(nullptr,TRUE,FALSE,nullptr);
- // handles[0]=exit, handles[1]=status handles[2]=write_pending, handles[3]=read completion
- // Index 2 is only meaningful when read_pending is true; we always wait on
- // all foue so writes are never delayed by a slow/absent read.
- HANDLE handles[] {
- sess->exit_event,
- o_status.hEvent,
- sess->write_pending_event,
- o_reader.hEvent
- };
- uint8_t peek;
- DWORD bytes_read;
- DWORD event_mask = 0;
- bool read_pending = false;
- bool status_pending = false;
- while(sess->state==0) {
- if(!status_pending) {
- if(WaitCommEvent(sess->port,&event_mask,&o_status)) {
- ResetEvent(o_status.hEvent);
- if(0!=(event_mask&EV_RLSD)) {
- sess->state=2;
- if(sess->disconnected_callback!=nullptr) {
- sess->disconnected_callback(sess->disconnected_callback_state);
- }
- continue;
- }
- } else {
- DWORD err = GetLastError();
- if(err==ERROR_IO_PENDING) {
- status_pending=true;
- } else {
- sess->state = 2;
- if(sess->disconnected_callback!=nullptr) {
- sess->disconnected_callback(sess->disconnected_callback_state);
- }
- continue;
- }
- }
- }
- // Issue a new read whenever one isn't already in flight
- if(!read_pending) {
- uint8_t* buf;
- DWORD bytes_to_read;
- if(mach.state<16) {
- buf = &peek;
- bytes_to_read = 1;
- } else if(mach.state==16) {
- if(mach.len==0) {
- // zero-length payload: verify crc and dispatch without issuing a read
- if(mach.crc==crc32(nullptr,0)) {
- if(sess->frame_callback) {
- sess->frame_callback(mach.cmd,nullptr,0,sess->frame_callback_state);
- }
- } else {
- puts("WARNING: CRC32 failure.");
- }
- mach.state = 0;
- continue;
- }
- if(!array_ensure(&sess->payload,mach.len)) {
- mach.state = 0;
- continue;
- }
- buf = sess->payload.data;
- bytes_to_read = mach.len;
- } else {
- // shouldn't happen; reset
- mach.state = 0;
- continue;
- }
- DWORD bytes_read_sync;
- if(ReadFile(sess->port,buf,bytes_to_read,&bytes_read_sync,&o_reader)) {
- // completed synchronously - process immediately, no wait needed
- ResetEvent(o_reader.hEvent);
- if(mach.state==16) {
- sess->payload.size = mach.len;
- if(mach.crc==crc32(sess->payload.data,sess->payload.size)) {
- if(sess->frame_callback) {
- sess->frame_callback(mach.cmd,sess->payload.data,sess->payload.size,sess->frame_callback_state);
- }
- } else {
- puts("WARNING: CRC32 failure.");
- }
- mach.state = 0;
- sess->payload.size = 0;
- } else {
- state_step(sess,&mach,peek);
- }
- continue;
- } else {
- DWORD err = GetLastError();
- if(err==ERROR_IO_PENDING) {
- read_pending = true;
- } else {
- puts("ERROR: read error");
- }
- }
- }
- // Wait: always watch for exit and write requests;
- // only include o_reader.hEvent when a read is actually in flight.
- DWORD nhandles = read_pending ? 4 : 3;
- DWORD wait = WaitForMultipleObjects(nhandles,handles,FALSE,INFINITE);
- if(wait==WAIT_OBJECT_0) {
- sess->state=1;
- } else if(wait==WAIT_OBJECT_0+1) {
- status_pending = false;
- if(GetOverlappedResult(sess->port,&o_reader,&bytes_read,FALSE)) {
- ResetEvent(o_status.hEvent);
- if(0!=(event_mask&EV_RLSD)) {
- sess->state=2;
- if(sess->disconnected_callback!=nullptr) {
- sess->disconnected_callback(sess->disconnected_callback_state);
- }
- continue;
- }
- }
- } else if(wait==WAIT_OBJECT_0+2) {
- // write request - do it while the read remains in flight in the kernel
- do_write(sess->port,&o_writer,sess->exit_event,sess->write_buf,sess->write_buf_length);
- free(sess->write_buf);
- sess->write_buf = nullptr;
- SetEvent(sess->write_slot_event);
- } else if(wait==WAIT_OBJECT_0+3) {
- read_pending = false;
- if(GetOverlappedResult(sess->port,&o_reader,&bytes_read,FALSE)) {
- ResetEvent(o_reader.hEvent);
- if(mach.state==16) {
- sess->payload.size = mach.len;
- if(mach.crc==crc32(sess->payload.data,sess->payload.size)) {
- if(sess->frame_callback) {
- sess->frame_callback(mach.cmd,sess->payload.data,sess->payload.size,sess->frame_callback_state);
- }
- } else {
- puts("WARNING: CRC32 failure.");
- }
- mach.state = 0;
- sess->payload.size = 0;
- } else {
- state_step(sess,&mach,peek);
- }
- }
- }
- }
- CloseHandle(o_reader.hEvent);
- CloseHandle(o_writer.hEvent);
- CloseHandle(o_status.hEvent);
- if(sess->state==2) {
- SetEvent(sess->exited_event);
- deinit_session(sess);
- } else {
- sess->state = 2;
- SetEvent(sess->exited_event);
- }
- return 0;
- }
- void ReadLogData(espmon_session_t handle, uint8_t* buffer,uint32_t* in_out_length) {
- session_t* sess = (session_t*)handle;
- size_t size = sess->log.size<*in_out_length?sess->log.size:*in_out_length;
- *in_out_length = size;
- if(!size) {
- return;
- }
- memcpy(buffer,sess->log.data,size);
- if(size<sess->log.size) {
- memmove(sess->log.data,sess->log.data+size,sess->log.size-size);
- }
- sess->log.size-=size;
- }
- void DestroySession(espmon_session_t handle) {
- session_t* sess = (session_t*)handle;
- SetEvent(sess->exit_event);
- WaitForSingleObject(sess->exited_event,INFINITE);
- deinit_session(sess);
- }
- espmon_session_t CreateSession(
- const wchar_t* portName,
- espmon_frame_callback_t frame_callback,
- void* frame_callback_state,
- espmon_disconnected_callback_t disconnected_callback,
- void* disconnected_callback_state
- ) {
- session_t* result = new session_t();
- result->payload.capacity = 0;
- result->payload.size = 0;
- result->payload.data = nullptr;
- result->log.capacity = 0;
- result->log.size = 0;
- result->log.data = nullptr;
- result->write_buf = nullptr;
- result->write_buf_length = 0;
- wcsncpy(result->portName,portName,63);
- result->frame_callback = frame_callback;
- result->frame_callback_state = frame_callback_state;
- result->disconnected_callback = disconnected_callback;
- result->disconnected_callback_state = disconnected_callback_state;
- wchar_t portFile[96];
- wcscpy(portFile,L"\\\\.\\");
- wcscat(portFile,portName);
- result->port = CreateFile(portFile,FILE_SHARE_READ|FILE_SHARE_WRITE,0,nullptr,OPEN_ALWAYS,FILE_FLAG_OVERLAPPED,nullptr);
- if(result->port==NULL) {
- DWORD err = GetLastError();
- delete result;
- SetLastError(err);
- return nullptr;
- }
- DCB dcb = {0};
- dcb.DCBlength = sizeof(DCB);
- //BuildCommDCB(L"115200,8,n,1",&dcb);
- if (!::GetCommState (result->port,&dcb))
- {
- DWORD err = GetLastError();
- CloseHandle(result->port);
- delete result;
- SetLastError(err);
- return nullptr;
- }
- dcb.BaudRate = 115200;
- dcb.ByteSize = 8;
- dcb.Parity = 0;
- dcb.StopBits = ONESTOPBIT;
- if (!::SetCommState (result->port,&dcb))
- {
- DWORD err = GetLastError();
- CloseHandle(result->port);
- delete result;
- SetLastError(err);
- return nullptr;
- }
- if(!SetCommMask(result->port,EV_RLSD)) {
- DWORD err = GetLastError();
- CloseHandle(result->port);
- delete result;
- SetLastError(err);
- return nullptr;
- }
- result->exit_event = CreateEvent(nullptr,TRUE,FALSE,nullptr);
- if(result->exit_event==nullptr) {
- DWORD err = GetLastError();
- CloseHandle(result->port);
- delete result;
- SetLastError(err);
- return nullptr;
- }
- result->exited_event = CreateEvent(nullptr,TRUE,FALSE,nullptr);
- if(result->exited_event==nullptr) {
- DWORD err = GetLastError();
- CloseHandle(result->exit_event);
- CloseHandle(result->port);
- delete result;
- SetLastError(err);
- return nullptr;
- }
- // auto-reset: WriteFrame signals this to kick the thread
- result->write_pending_event = CreateEvent(nullptr,FALSE,FALSE,nullptr);
- if(result->write_pending_event==nullptr) {
- DWORD err = GetLastError();
- CloseHandle(result->exit_event);
- CloseHandle(result->exited_event);
- CloseHandle(result->port);
- delete result;
- SetLastError(err);
- return nullptr;
- }
- // manual-reset, starts signaled: WriteFrame waits here for the slot to be free
- result->write_slot_event = CreateEvent(nullptr,TRUE,TRUE,nullptr);
- if(result->write_slot_event==nullptr) {
- DWORD err = GetLastError();
- CloseHandle(result->write_pending_event);
- CloseHandle(result->exit_event);
- CloseHandle(result->exited_event);
- CloseHandle(result->port);
- delete result;
- SetLastError(err);
- return nullptr;
- }
- DWORD threadId;
- result->thread = CreateThread(NULL,8192,SessionThread,result,0,&threadId);
- if(result->thread==nullptr) {
- DWORD err = GetLastError();
- CloseHandle(result->write_pending_event);
- CloseHandle(result->write_slot_event);
- CloseHandle(result->exit_event);
- CloseHandle(result->exited_event);
- CloseHandle(result->port);
- delete result;
- SetLastError(err);
- return nullptr;
- }
- return result;
- }
- void WriteFrame(espmon_session_t handle, uint8_t cmd, const uint8_t* payload, size_t length) {
- session_t* sess = (session_t*)handle;
- // Allocate one contiguous frame: 8 cmd bytes + 4 length + 4 crc + payload
- size_t frame_size = 16 + length;
- uint8_t* frame = (uint8_t*)malloc(frame_size);
- if(frame==nullptr) { puts("ERROR: out of memory in WriteFrame"); return; }
- for(int i = 0;i<8;++i) frame[i] = cmd;
- uint32_t len32 = (uint32_t)length;
- memcpy(&frame[8],&len32,4);
- uint32_t crc = crc32(payload,length);
- memcpy(&frame[12],&crc,4);
- if(length>0) memcpy(&frame[16],payload,length);
- // Block only if a previous write is still in flight, then hand off and return
- WaitForSingleObject(sess->write_slot_event,INFINITE);
- ResetEvent(sess->write_slot_event);
- sess->write_buf = frame;
- sess->write_buf_length = frame_size;
- SetEvent(sess->write_pending_event);
- }
- static void on_frame_callback(uint8_t cmd, const uint8_t* data, size_t length, void* state) {
- printf("Frame received. CMD = %d, length = %d\r\n",(int)cmd,(int)length);
- }
- static bool connected = false;
- static void on_disconnected_callback(void* state) {
- puts("Forcibly disconnected");
- connected = false;
- }
- int main(int argc, char**argv) {
- LARGE_INTEGER freq;
- QueryPerformanceFrequency(&freq);
- double tick_freq = double(freq.QuadPart)/1000.0;
- LARGE_INTEGER start;
- QueryPerformanceCounter(&start);
- char tmp[1025];
- espmon_session_t handle= CreateSession(L"COM14",on_frame_callback,nullptr,on_disconnected_callback,nullptr);
- connected=true;
- HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
- DWORD event_count = 0;
- if(handle!=nullptr && stdin_handle!=nullptr) {
- if(!GetNumberOfConsoleInputEvents(stdin_handle,&event_count)) {
- event_count = 0;
- }
- while(connected && event_count==0) {
- memset(tmp,0,sizeof(tmp));
- uint32_t len = sizeof(tmp)-1;
- ReadLogData(handle,(uint8_t*)tmp,&len);
- if(len>0) {
- fwrite(tmp,1,len,stdout);
- }
- if(!GetNumberOfConsoleInputEvents(stdin_handle,&event_count)) {
- event_count = 0;
- }
- LARGE_INTEGER current;
- QueryPerformanceCounter(¤t);
- if(((current.QuadPart - start.QuadPart)/tick_freq)>=1000) {
- QueryPerformanceCounter(&start);
- puts("Writing packet");
- uint8_t tmp[4] = {0};
- WriteFrame(handle,5,tmp,1);
- }
- };
- if(connected) {
- DestroySession(handle);
- }
- }
- return 0;
- }
Add Comment
Please, Sign In to add comment