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>
- #define FRAME_ESCAPE (0xDB)
- #define FRAME_DELIMITER (0xC0)
- typedef struct {
- uint8_t* data;
- size_t size;
- size_t capacity;
- } arr_t;
- typedef struct {
- int32_t timeout_ts;
- arr_t read_frame;
- bool found_start;
- } read_frame_state_t;
- typedef struct {
- arr_t write_frame;
- int state;
- wchar_t portName[64];
- esplink_disconnected_callback_t disconnected_callback;
- void* disconnected_callback_state;
- esplink_progress_callback_t progress_callback;
- void* progress_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 build_frame(session_t* sess, const uint8_t* payload, size_t payload_length) {
- int count = 0;
- for(int i = 0;i<payload_length;++i)
- {
- uint8_t b = payload[i];
- switch(b)
- {
- case FRAME_ESCAPE:
- case FRAME_DELIMITER:
- count += 2;
- break;
- default:
- ++count;
- break;
- }
- }
- if(!array_ensure(&sess->write_frame,count)) {
- return false;
- }
- uint8_t* p = sess->write_frame.data;
- *p=FRAME_DELIMITER;
- int j = 1;
- for(int i = 0;i<payload_length;++i)
- {
- uint8_t src = payload[i];
- switch(src)
- {
- case FRAME_ESCAPE:
- p[j++] = FRAME_ESCAPE;
- p[j++] = 0xDD;
- break;
- case FRAME_DELIMITER:
- p[j++] = FRAME_ESCAPE;
- p[j++] = 0xDC;
- break;
- default:
- p[j++] = src;
- break;
- }
- }
- p[j] = FRAME_DELIMITER;
- }
- static double qpc_freq = 0;
- static LARGE_INTEGER qpc_start;
- static void millis_init() {
- LARGE_INTEGER li;
- if(!QueryPerformanceFrequency(&li))
- qpc_freq = double(li.QuadPart)/1000.0;
- QueryPerformanceCounter(&qpc_start);
- }
- static uint32_t millis_get() {
- if(qpc_freq==0) {
- millis_init();
- }
- LARGE_INTEGER li;
- QueryPerformanceCounter(&li);
- return double(li.QuadPart-qpc_start.QuadPart)/qpc_freq;
- }
- static void read_frame_start(read_frame_state_t* state, int32_t timeout) {
- state->read_frame.size = 0;
- uint32_t start_ms = millis_get();
- state->timeout_ts = timeout>-1?start_ms + timeout:-1;
- state->found_start = false;
- }
- static char read_frame_next(read_frame_state_t* state,uint8_t data) {
- if(state->timeout_ts>0 && millis_get()>=state->timeout_ts) {
- return -1;
- }
- if(!state->found_start)
- {
- if(data == FRAME_DELIMITER)
- {
- state->found_start = true;
- }
- return 1;
- } else
- {
- if(data==FRAME_DELIMITER) {
- return 0;
- }
- if (state->read_frame.size>0 && state->read_frame.data[state->read_frame.size-1]==FRAME_ESCAPE)
- {
- switch (data)
- {
- case 0xDD:
- state->read_frame.data[state->read_frame.size-1]=FRAME_ESCAPE;
- break;
- case 0xDC:
- state->read_frame.data[state->read_frame.size-1]=FRAME_DELIMITER;
- break;
- default:
- // Invalid escape content in frame
- return -1;
- }
- return 1;
- }
- if(!array_append(&state->read_frame,data)) {
- return -1;
- }
- }
- }
- 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);
- 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
- };
- read_frame_state_t read_mach = {0};
- bool in_read_frame = false;
- uint8_t peek;
- DWORD bytes_read;
- DWORD event_mask = 0;
- bool need_frame = false;
- 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;
- }
- }
- }
- if(need_frame) {
- // Issue a new read if one isn't already in flight
- if(!read_pending) {
- uint8_t* buf;
- DWORD bytes_to_read;
- buf = &peek;
- bytes_to_read = 1;
- 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(!in_read_frame) {
- read_frame_start(&read_mach,1000);
- in_read_frame = true;
- }
- if(0==read_frame_next(&read_mach,peek)) {
- // TODO: call frame handler
- in_read_frame = false;
- need_frame = false;
- }
- 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(!in_read_frame) {
- read_frame_start(&read_mach,1000);
- in_read_frame = true;
- }
- if(0==read_frame_next(&read_mach,peek)) {
- // TODO: call frame handler
- in_read_frame = false;
- need_frame = false;
- }
- }
- }
- }
- 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