/* ; Project: Open Vehicle Monitor System ; Date: 14th March 2017 ; ; Changes: ; 1.0 Initial release ; ; (C) 2011 Michael Stegen / Stegen Electronics ; (C) 2011-2017 Mark Webb-Johnson ; (C) 2011 Sonny Chen @ EPRO/DX ; ; Permission is hereby granted, free of charge, to any person obtaining a copy ; of this software and associated documentation files (the "Software"), to deal ; in the Software without restriction, including without limitation the rights ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ; copies of the Software, and to permit persons to whom the Software is ; furnished to do so, subject to the following conditions: ; ; The above copyright notice and this permission notice shall be included in ; all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ; THE SOFTWARE. */ #include "ovms_log.h" static const char *TAG = "gsm-mux"; #include #include "gsmmux.h" #include "ovms_cellular.h" #define GSM_CR 0x02 #define GSM_EA 0x01 #define GSM_PF 0x10 #define GSM_RR 0x01 #define GSM_UI 0x03 #define GSM_RNR 0x05 #define GSM_REJ 0x09 #define GSM_DM 0x0F #define GSM_SABM 0x2F #define GSM_DISC 0x43 #define GSM_UA 0x63 #define GSM_UIH 0xEF #define GSM_CMD_NSC 0x09 #define GSM_CMD_TEST 0x11 #define GSM_CMD_PSC 0x21 #define GSM_CMD_RLS 0x29 #define GSM_CMD_FCOFF 0x31 #define GSM_CMD_PN 0x41 #define GSM_CMD_RPN 0x49 #define GSM_CMD_FCON 0x51 #define GSM_CMD_CLD 0x61 #define GSM_CMD_SNC 0x69 #define GSM_CMD_MSC 0x71 #define GSM_MDM_FC 0x01 #define GSM_MDM_RTC 0x02 #define GSM_MDM_RTR 0x04 #define GSM_MDM_IC 0x20 #define GSM_MDM_DV 0x40 #define GSM0_SOF 0xF9 #define XON 0x11 #define XOFF 0x13 static const uint8_t gsm_fcs8[256] = { 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF }; #define FCS_INIT 0xFF #define FCS_GOOD 0xCF static inline uint8_t gsm_fcs_add(uint8_t fcs, uint8_t c) { return gsm_fcs8[fcs ^ c]; } static inline uint8_t gsm_fcs_add_block(uint8_t fcs, uint8_t *c, size_t len) { while (len--) fcs = gsm_fcs8[fcs ^ *c++]; return fcs; } GsmMuxChannel::GsmMuxChannel(GsmMux* mux, int channel, size_t buffersize) : m_buffer(buffersize) { m_state = ChanClosed; m_mux = mux; m_channel = channel; } GsmMuxChannel::~GsmMuxChannel() { } void GsmMuxChannel::ProcessFrame(uint8_t* frame, size_t length, size_t iframepos) { // Note the provided excludes the start byte, stop byte, and checksum // The pointer itself points to the byte after the start byte //ESP_LOGV(TAG, "ChanProcessFrame(CHAN=%d, ADDR=%02x, CTRL=%02x, LEN=%d, IFP=%d)", m_channel, frame[0], frame[1], length, iframepos); switch (m_state) { case ChanClosed: break; case ChanOpening: if (frame[1] == (GSM_UA + GSM_PF)) { ESP_LOGI(TAG, "Channel #%d is open",m_channel); m_state = ChanOpen; // SABM established if (m_channel != 0) m_mux->m_openchannels++; if (m_channel==0) m_mux->m_state = GsmMux::DlciOpen; if (m_channel < m_mux->m_channelcount) m_mux->StartChannel(m_channel+1); } #if __GNUC__ >= 7 [[fallthrough]]; #endif case ChanOpen: if (frame[1] == (GSM_UIH + GSM_PF)) { for (size_t k=iframepos;km_modem->IncomingMuxData(this); } break; case ChanClosing: break; default: break; } } GsmMux::GsmMux(modem* m, int channelcount, size_t maxframesize) { m_channelcount = channelcount; m_state = DlciClosed; m_modem = m; m_frame = new uint8_t[maxframesize]; m_framesize = maxframesize; m_framepos = 0; m_frameipos = 0; m_framelen = 0; m_framemorelen = false; m_openchannels = 0; m_framingerrors = 0; m_lastgoodrxframe = 0; m_rxframecount = 0; m_txframecount = 0; } GsmMux::~GsmMux() { Shutdown(); delete [] m_frame; } void GsmMux::Startup() { ESP_LOGI(TAG, "Start MUX"); m_framingerrors = 0; m_lastgoodrxframe = 0; m_rxframecount = 0; m_txframecount = 0; m_channels.insert(m_channels.end(),new GsmMuxChannel(this,0,8)); for (int k=1; k<=m_channelcount; k++) { m_channels.insert(m_channels.end(), new GsmMuxChannel(this,k,CONFIG_OVMS_HW_CELLULAR_MODEM_MUXCHANNEL_SIZE)); } StartChannel(0); m_state = DlciOpening; } void GsmMux::Shutdown() { if (m_channels.size() > 0) { ESP_LOGI(TAG, "Stop MUX"); for (int k=0; km_state = GsmMuxChannel::ChanOpening; } void GsmMux::StopChannel(int channel) { ESP_LOGI(TAG, "StopChannel(%d)",channel); } bool GsmMux::IsChannelOpen(int channel) { GsmMuxChannel* chan = m_channels[channel]; return (chan && chan->m_state == GsmMuxChannel::ChanOpen); } bool GsmMux::IsMuxUp() { return (m_openchannels == m_channelcount); } uint32_t GsmMux::GoodFrameAge() { if (m_openchannels != m_channelcount) return 0; return (m_lastgoodrxframe > 0) ? (monotonictime-m_lastgoodrxframe) : 0; } void GsmMux::Process(OvmsBuffer* buf) { while (buf->UsedSpace()>0) { if (m_framepos == m_framesize) { // Overflow frame ESP_LOGW(TAG, "Frame overflow (%d bytes)",m_framesize); MyCommandApp.HexDump(TAG, "Frame head", (const char*)m_frame, 8*16); m_framepos = 0; m_framelen = 0; m_framemorelen = false; m_framingerrors++; continue; } uint8_t b = buf->Pop(); if ((m_framepos == 0)&&(b != GSM0_SOF)) { continue; // Skip to start of frame } if ((m_framepos == 1)&&(b == GSM0_SOF)) continue; // We found end of previous frame, so just skip it // ESP_LOGI(TAG, "Got %02x at %d (length sofar = %d)",b,m_framepos,m_framelen); m_frame[m_framepos++] = b; if (m_framepos == 4) { // First byte of length field m_framemorelen = !(b & GSM_EA); m_framelen = (b>>1); if (!m_framemorelen) { m_framelen += (m_framepos+2); m_frameipos = m_framepos; } else { m_framelen += (m_framepos+3); m_frameipos = m_framepos+1; } // ESP_LOGI(TAG, "Frame length (first byte) = %d",m_framelen); } if ((m_framepos == 5)&&(m_framemorelen)) { // Second byte of length field m_framelen += (b<<7); m_framemorelen = false; // ESP_LOGI(TAG, "Frame length (second byte) = %d",m_framelen); } if (m_framepos == m_framelen) { if (b == GSM0_SOF) { // We have a complete frame... ProcessFrame(); } else { // Frame error: int channel = m_frame[1] >> 2; ESP_LOGW(TAG, "Frame error: EOF mismatch (CHAN=%d, ADDR=%02x, CTRL=%02x, FCS=%02x, LEN=%d)", channel, m_frame[1], m_frame[2], m_frame[m_framelen-2], m_framelen); MyCommandApp.HexDump(TAG, "Frame dump", (const char*)m_frame, m_framelen); // find next frame: m_framepos = 0; m_framelen = 0; m_framemorelen = false; m_framingerrors++; } } } } void GsmMux::ProcessFrame() { int channel = m_frame[1] >>2; ESP_LOGV(TAG, "ProcessFrame(CHAN=%d, ADDR=%02x, CTRL=%02x, FCS=%02x, LEN=%d)", channel, m_frame[1], m_frame[2], m_frame[m_framelen-2], m_framelen); uint8_t fcs = 0xFF - gsm_fcs_add_block(FCS_INIT, m_frame+1, m_frameipos-1); if (fcs != m_frame[m_framelen-2]) { ESP_LOGW(TAG, "FCS mismatch (%02x != %02x)",fcs,m_frame[m_framelen-2]); m_framepos = 0; m_frameipos = 0; m_framelen = 0; m_framingerrors++; m_framemorelen = false; return; } GsmMuxChannel* chan = m_channels[channel]; if (chan) { m_lastgoodrxframe = monotonictime; m_rxframecount++; chan->ProcessFrame(m_frame+1,m_framelen-3,m_frameipos-1); } else { ESP_LOGW(TAG, "Incoming message for unrecognised channel #%d",channel); } m_framepos = 0; m_frameipos = 0; m_framelen = 0; m_framemorelen = false; } void GsmMux::txfcs(uint8_t* data, size_t size, size_t ipos) { data[size-2] = 0xFF - gsm_fcs_add_block(FCS_INIT, data+1, ipos-1); m_modem->tx(data,size); m_txframecount++; } size_t GsmMux::tx(int channel, uint8_t* data, ssize_t size) { uint8_t* buf = new uint8_t[size+7]; size_t ipos; int len; int cn = (channel<<2)+GSM_EA; buf[0] = GSM0_SOF; buf[1] = (uint8_t)cn; // Address: EA=1, DLCI=channel buf[2] = GSM_UIH+GSM_PF; // Control: UIH + Poll if (size < 128) { len = (size<<1) + GSM_EA; buf[3] = (uint8_t)len; // Length: EA=1, Length=size ipos = 4; } else { len = ((size%128)<<1); buf[3] = (uint8_t)len; // Length: lower 7 bit, shifted once len = (size/128); buf[4] = (uint8_t)len; // Length: upper 7 bits ipos = 5; } for (size_t k=0; k= 0) return tx(channel, (uint8_t*)data,size); else return tx(channel, (uint8_t*)data,strlen(data)); }