WizziLab's serial protocol library
Dependents: modem_ref_helper_for_v5_3_217 modem_ref_helper
WizziCom.cpp
- Committer:
- Jeej
- Date:
- 2022-03-10
- Revision:
- 17:8ce53c6e0350
- Parent:
- 16:32c006e829df
File content as of revision 17:8ce53c6e0350:
#include "WizziCom.h" #if 0 #define COM_DPRINT(...) PRINT(__VA_ARGS__) #define COM_DPRINT_DATA(...) PRINT_DATA(__VA_ARGS__) #define COM_FPRINT(...) FPRINT(__VA_ARGS__) #else #define COM_DPRINT(...); #define COM_DPRINT_DATA(...); #define COM_FPRINT(...); #endif #define XON_SIGNAL (0x0001 << 0) #define START_SIGNAL (0x0001 << 1) // +--------------+--------+--------+--------+---- - - - - - - - - - - --------+ // | SYNC | LEN | SEQ | ID | PAYLOAD | // +--------------+--------+--------+--------+---- - - - - - - - - - - --------+ // // 2 bytes 1 byte 1 byte 1 byte LEN bytes // |<------------>|<------>|<------>|<------>|<--- - - - - - - - - - - ------->| // first byte of the sync word // (ASCII start of heading) #define KAL_COM_SYNC_BYTE_0 0x01 // second byte of the sync word // (ASCII group separator) #define KAL_COM_SYNC_BYTE_1 0x1F //====================================================================== // wizzi_com_fid_t //---------------------------------------------------------------------- // Enumerator of serial Flow-ids //====================================================================== typedef enum { // Trace channel KAL_COM_FLOWID_TRC = 0, // General purpose Command channel KAL_COM_FLOWID_CMD, // ALP channel KAL_COM_FLOWID_ALP, // System notifications KAL_COM_FLOWID_SYS, // File System channel KAL_COM_FLOWID_FS, KAL_COM_FLOWID_QTY } wizzi_com_fid_t; #define KAL_COM_FLOW(fid,type) ((((fid)&0x7)<<4) | ((type)&0xF)) #define KAL_COM_FLOWID(id) (((id)>>4)&0x7) #define KAL_COM_FLOWID_REDIRECT 0x80 //====================================================================== // wizzi_com_flow_t //---------------------------------------------------------------------- // Enumerator of serial flows //====================================================================== typedef enum { // Default printf type KAL_COM_FLOW_PRINTF = KAL_COM_FLOW(KAL_COM_FLOWID_TRC,0), // Substitute the string by a codeword // interpreted by the PC com tool KAL_COM_FLOW_PRINTF_COMPRESSED = KAL_COM_FLOW(KAL_COM_FLOWID_TRC,1), // Display the payload as hex data KAL_COM_FLOW_PRINT_HEX = KAL_COM_FLOW(KAL_COM_FLOWID_TRC,2), // AT command KAL_COM_FLOW_AT_CMD = KAL_COM_FLOW(KAL_COM_FLOWID_ALP,0), // AT command response KAL_COM_FLOW_AT_RESP = KAL_COM_FLOW(KAL_COM_FLOWID_ALP,1), // AT unsolicited message KAL_COM_FLOW_AT_UNS = KAL_COM_FLOW(KAL_COM_FLOWID_ALP,2), // AT unsolicited error KAL_COM_FLOW_AT_ERR = KAL_COM_FLOW(KAL_COM_FLOWID_ALP,3), // Remote Commands KAL_COM_FLOW_CMD = KAL_COM_FLOW(KAL_COM_FLOWID_CMD,0), // Remote System reset KAL_COM_FLOW_SYS_RST = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,0), // Button Emulator KAL_COM_FLOW_SYS_BUTTON = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,1), // Dump Debug parameters KAL_COM_FLOW_SYS_INFO = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,2), // CUP signalisation KAL_COM_FLOW_SYS_CUP = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,3), // Ping distant COM KAL_COM_FLOW_SYS_PING = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,4), // Pong from distant COM KAL_COM_FLOW_SYS_PONG = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,5), // Enable system config from distant COM KAL_COM_FLOW_SYS_CFG = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,6), // Configure Output Trace level from distant COM KAL_COM_FLOW_SYS_TLEV = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,7), // Configure COM port redirection KAL_COM_FLOW_SYS_REDIR = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,8), // Flow control signalling KAL_COM_FLOW_SYS_XON = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,9), KAL_COM_FLOW_SYS_XOFF = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,10), KAL_COM_FLOW_SYS_XACK = KAL_COM_FLOW(KAL_COM_FLOWID_SYS,11), // File System Command/Response KAL_COM_FLOW_FS_CMD = KAL_COM_FLOW(KAL_COM_FLOWID_FS,0), KAL_COM_FLOW_FS_RESP = KAL_COM_FLOW(KAL_COM_FLOWID_FS,1), } wizzi_com_flow_t; enum { SEARCH_HEADER, SEARCH_BODY, }; void wizzi_com_rx_thread(); void wizzi_com_tx_thread(); const uint8_t g_type_to_flow[WIZZICOM_PKT_QTY] = { // Default printf type KAL_COM_FLOW_PRINTF, // Substitute the string by a codeword // interpreted by the PC com tool KAL_COM_FLOW_PRINTF_COMPRESSED, // Display the payload as hex data KAL_COM_FLOW_PRINT_HEX, // AT command KAL_COM_FLOW_AT_CMD, // AT command response KAL_COM_FLOW_AT_RESP, // AT unsolicited message KAL_COM_FLOW_AT_UNS, // AT unsolicited error KAL_COM_FLOW_AT_ERR, // Remote Commands KAL_COM_FLOW_CMD, // Remote System reset KAL_COM_FLOW_SYS_RST, // Button Emulator KAL_COM_FLOW_SYS_BUTTON, // Dump Debug parameters KAL_COM_FLOW_SYS_INFO, // CUP signalisation KAL_COM_FLOW_SYS_CUP, // Ping distant COM KAL_COM_FLOW_SYS_PING, // Pong from distant COM KAL_COM_FLOW_SYS_PONG, // Enable system config from distant COM KAL_COM_FLOW_SYS_CFG, // Configure Output Trace level from distant COM KAL_COM_FLOW_SYS_TLEV, // Configure COM port redirection KAL_COM_FLOW_SYS_REDIR, // Flow control signalling KAL_COM_FLOW_SYS_XON, KAL_COM_FLOW_SYS_XOFF, KAL_COM_FLOW_SYS_XACK, // File System Command/Response KAL_COM_FLOW_FS_CMD, KAL_COM_FLOW_FS_RESP }; uint8_t wizzicom_type_to_flow(uint8_t packet_type) { return g_type_to_flow[packet_type]; } uint8_t wizzicom_flow_to_type(uint8_t flow_id) { // Get packet type from flow_id uint8_t packet_type = 0; while ((g_type_to_flow[packet_type] != flow_id) && (packet_type < WIZZICOM_PKT_QTY)) { packet_type++; } return packet_type; } WizziCom::WizziCom(PinName tx, PinName rx, PinName irq_out, PinName irq_in) : _tx_done(0), _rx_done(0), _irq_in_int(0), _rx_thread(osPriorityHigh, 512, NULL, "wzc_rx"), _tx_thread(osPriorityHigh, 512, NULL, "wzc_tx"), _cb_thread(osPriorityHigh, 1024, NULL, "wzc_cb") { _tx_seq = 0; _rx_seq = 0; memset(_callback, 0, sizeof(_callback)); _cbuf = &_cbuf_h; kal_buf_circ_create_static(&_cbuf_h, _cbuf_b, RX_BUF_SIZE, sizeof(uint8_t)); _serial = new RawSerial(tx, rx, 115200); _serial->format(8, SerialBase::None, 1); //_serial->attach(callback(this, &WizziCom::_rx_isr), Serial::RxIrq); _serial->read(&_rx_byte, sizeof(uint8_t), callback(this, &WizziCom::_rx_done_isr)); _serial->set_flow_control(SerialBase::Disabled); _irq_out = (irq_out != NC)? new DigitalOut(irq_out) : NULL; if (irq_in != NC) { _irq_in = new InterruptIn(irq_in); _irq_in->rise(callback(this, &WizziCom::_irq_in_isr)); } else { _irq_in = NULL; } osStatus err = _rx_thread.start(callback(this, &WizziCom::_thread_rx)); ASSERT(err == osOK, "Failed to start WizziCom _thread_rx (err: %d)\r\n", err); err = _tx_thread.start(callback(this, &WizziCom::_thread_tx)); ASSERT(err == osOK, "Failed to start WizziCom _thread_tx (err: %d)\r\n", err); err = _cb_thread.start(callback(this, &WizziCom::_thread_callback)); ASSERT(err == osOK, "Failed to start WizziCom _thread_callback (err: %d)\r\n", err); } WizziCom::~WizziCom() { _rx_thread.terminate(); _tx_thread.terminate(); _cb_thread.terminate(); delete _serial; delete _irq_out; delete _irq_in; } void WizziCom::attach(WizziComCallback function, WizziComPacketType packet_type) { _callback[packet_type] = callback(function); } void WizziCom::reset(void) { COM_FPRINT("\r\n"); _tx_seq = 0; _rx_seq = 0; } void WizziCom::send(WizziComPacketType type, uint8_t length, uint8_t* data) { wizzi_com_tx_msg_t msg; msg.id = g_type_to_flow[type]; msg.pbuf = data; msg.plen = length; msg.alen = 0; _post_msg(&msg); } void WizziCom::send(WizziComPacket_t* packet) { wizzi_com_tx_msg_t msg; msg.id = g_type_to_flow[packet->type]; msg.pbuf = packet->data; msg.plen = packet->length; msg.alen = 0; _post_msg(&msg); } void WizziCom::print_raw(char* str) { _send_raw((uint8_t*)str, (uint32_t)strlen(str)); } void WizziCom::send_raw(uint8_t* data, uint32_t len) { _send_raw(data, len); } /** Serial Rx Interrupt Service Routine. Add recevied bytes to the RX buffer. @param void @return void */ void WizziCom::_tx_done_isr(int event) { _tx_done.release(); } void WizziCom::_rx_isr(void) { uint8_t byte; while (_serial->readable()) { byte = _serial->getc(); kal_buf_circ_push(_cbuf, &byte); } _rx_done.release(); } void WizziCom::_rx_done_isr(int event) { uint8_t byte = _rx_byte; _serial->read(&_rx_byte, sizeof(uint8_t), callback(this, &WizziCom::_rx_done_isr)); kal_buf_circ_push(_cbuf, &byte); _rx_done.release(); } /** CTS pin Interrupt Service Routine. For flow control (not yet inplemented) @param void @return void */ void WizziCom::_irq_in_isr() { //PRINT("IRQ_IN_ISR\r\n"); //_irq_in_int->release(); } /** Wakes-up modem and send data throught Serial. @param const uint8_t* Pointer to data buffer @param uint32_t Data length @return void */ void WizziCom::_send_raw(uint8_t* data, uint32_t len) { if (_irq_out) { *(_irq_out) = 1; ThisThread::sleep_for(10); } _serial->write(data, len, callback(this, &WizziCom::_tx_done_isr)); _tx_done.acquire(); if (_irq_out) { // Important to not release the ressource too soon ThisThread::sleep_for(2); *(_irq_out) = 0; } } void WizziCom::_sys_xack(void) { send(WizziComPacketSysXack, 0, NULL); } // Formats to send packet throught Serial. wizzi_com_tx_buf_t* WizziCom::_new_msg(wizzi_com_tx_msg_t* msg) { uint8_t len = KAL_COM_HEADER_LEN + msg->alen + msg->plen; COM_FPRINT("(len:%d)\r\n", len); wizzi_com_tx_buf_t* tx_buf = (wizzi_com_tx_buf_t*)MALLOC(sizeof(wizzi_com_tx_buf_t) - 1 + len); // construct serial header // concatenate and update tx_seq ID uint8_t* p = tx_buf->buf; uint8_t* t = p; *p++ = (uint8_t)KAL_COM_SYNC_BYTE_0; *p++ = (uint8_t)KAL_COM_SYNC_BYTE_1; *p++ = (uint8_t)msg->alen + msg->plen; *p++ = (uint8_t)_tx_seq++; *p++ = (uint8_t)msg->id; // copy payload and parameters memcpy(p, msg->pbuf, msg->plen); p += msg->plen; memcpy(p, msg->abuf, msg->alen); p += msg->alen; tx_buf->len = (uint32_t)(p - t); ASSERT(tx_buf->len == len, "New msg wrong length %d expected %d\r\n", tx_buf->len, len); return tx_buf; } void WizziCom::_post_msg(wizzi_com_tx_msg_t* msg) { COM_FPRINT("\r\n"); wizzi_com_tx_buf_t* new_m = _new_msg(msg); tx_queue_element_t* new_e = _tx_mpool.alloc(); memcpy(new_e, &new_m, sizeof(tx_queue_element_t)); ASSERT(_tx_queue.put(new_e) == osOK, "WizziCom TX queue full!\r\n"); } void WizziCom::_new_pkt(WizziComPacket_t* packet) { //COM_COM_FPRINT("\r\n"); COM_DPRINT("--> (%02d) %d\r\n", packet->type, packet->length); // Distribute packet types to callbacks switch (packet->type) { case WizziComPacketSysXon: FREE(packet); COM_DPRINT("XON\r\n"); _tx_thread.flags_set(XON_SIGNAL); break; case WizziComPacketSysXoff: FREE(packet); COM_DPRINT("XOFF\r\n"); _sys_xack(); break; default: rx_queue_element_t* new_e = _rx_mpool.alloc(); memcpy(new_e, &packet, sizeof(rx_queue_element_t)); ASSERT(_rx_queue.put(new_e) == osOK, "WizziCom RX queue full!\r\n"); break; } } // Thread for calling callbacks // Like arg, arg thread is stalled by callbacks but not the parsing thread. void WizziCom::_thread_callback(void) { osEvent evt; rx_queue_element_t* e; WizziComPacket_t* packet; COM_FPRINT("(id:0x%08x)\r\n", osThreadGetId()); while (true) { // wait for available packet evt = _rx_queue.get(); e = (evt.status == osEventMessage)? (rx_queue_element_t*)evt.value.p : NULL; if (e != NULL) { packet = *e; if (_callback[packet->type]) { _callback[packet->type].call(this, packet); } else if (_callback[WizziComPacketUntreated]) { _callback[WizziComPacketUntreated].call(this, packet); } else { EPRINT("Untreated pkt type %d in queue!\r\n", packet->type); } FREE(packet); _rx_mpool.free(e); } } } // Thread for parsing packets from RX buffer. void WizziCom::_thread_rx(void) { uint8_t seqnum; uint8_t header[KAL_COM_HEADER_LEN]; COM_FPRINT("(id:0x%08x)\r\n", osThreadGetId()); while (true) { // Wait for header data while (kal_buf_circ_size(_cbuf) < KAL_COM_HEADER_LEN) { _rx_done.acquire(); } // Copy header from buffer (data stays in buffer) kal_buf_circ_fetch(_cbuf, header, KAL_COM_HEADER_LEN); // Check sync bytes if(KAL_COM_SYNC_BYTE_0 == header[0] && KAL_COM_SYNC_BYTE_1 == header[1]) { // Packet valid, drop header from buffer kal_buf_circ_get(_cbuf, NULL, KAL_COM_HEADER_LEN); // Fill temp header _msg.blen = header[2]; seqnum = header[3]; _msg.id = wizzicom_flow_to_type(header[4]); // Wait for body data while (kal_buf_circ_size(_cbuf) < _msg.blen) { _rx_done.acquire(); } // Update seqnum WARNING(_rx_seq == seqnum, "COM Bad seqnum expected:%d got:%d\r\n", _rx_seq, seqnum); _rx_seq = seqnum + 1; COM_DPRINT("COM packet (id: %02X seq: %d body: %d bytes)\r\n", _msg.id, seqnum, _msg.blen); if (_callback[_msg.id] || _callback[WizziComPacketUntreated]) { WizziComPacket_t* pkt = (WizziComPacket_t*)MALLOC(sizeof(WizziComPacket_t) - 1 + _msg.blen); // copy data to buffer pkt->length = _msg.blen; pkt->type = _msg.id; if (_msg.blen) { // Get payload from buffer kal_buf_circ_get(_cbuf, pkt->data, _msg.blen); } // add packet to queue _new_pkt(pkt); } else { // Ignore packet COM_DPRINT("Ignore pkt id %02X\r\n", _msg.id); // Drop payload from buffer kal_buf_circ_get(_cbuf, NULL, _msg.blen); } } else { // Resync PRINT("COM: Resync\n"); // Drop a byte kal_buf_circ_pop(_cbuf, NULL); } } } void WizziCom::_thread_tx(void) { COM_FPRINT("(id:0x%08x)\r\n", osThreadGetId()); tx_queue_element_t* e; wizzi_com_tx_buf_t* msg; osEvent evt; uint8_t flow_id; while (true) { // wait for data to send evt = _tx_queue.get(); e = (evt.status == osEventMessage)? (tx_queue_element_t*)evt.value.p : NULL; // send message if (e != NULL) { msg = *e; flow_id = msg->buf[4]; COM_DPRINT("<-- (%02d) %d\r\n", wizzicom_flow_to_type(flow_id), (msg->len - KAL_COM_HEADER_LEN)); _send_raw(msg->buf, msg->len); FREE(msg); _tx_mpool.free(e); if (KAL_COM_FLOW_SYS_XACK == flow_id) { COM_DPRINT("XACK\r\n"); ThisThread::flags_wait_all(XON_SIGNAL); } } } }