emi_common.cpp
1 /* 2 EIBD eib bus access and management daemon 3 Copyright (C) 2005-2011 Martin Koegler <mkoegler@auto.tuwien.ac.at> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 #include "emi_common.h" 21 22 #include "emi.h" 23 24 /* add formatter for fmt >= 10.0.0 */ 25 int format_as(E_state t) { return t; } 26 27 EMIVer 28 cfgEMIVersion(IniSectionPtr& s) 29 { 30 int v = s->value("version",vERROR); 31 if (v > vRaw || v < vERROR) 32 return vERROR; 33 else if (v != vERROR) 34 return EMIVer(v); 35 36 std::string sv = s->value("version",""); 37 if (!sv.size()) 38 return vUnknown; 39 else if (sv == "EMI1") 40 return vEMI1; 41 else if (sv == "EMI2") 42 return vEMI2; 43 else if (sv == "CEMI") 44 return vCEMI; 45 else if (sv == "raw") 46 return vRaw; 47 else 48 return vERROR; 49 } 50 51 EMI_Common::EMI_Common (LowLevelIface* c, IniSectionPtr& s, LowLevelDriver *i) : LowLevelFilter(c,s,i) 52 { 53 timeout.set<EMI_Common, &EMI_Common::timeout_cb>(this); 54 t->setAuxName("EMI_common"); 55 iface = i; 56 } 57 58 bool 59 EMI_Common::setup() 60 { 61 if (iface == nullptr) 62 return false; 63 if(!LowLevelFilter::setup()) 64 return false; 65 send_timeout = cfg->value("send-timeout",2000) / 1000.; 66 max_retries = cfg->value("send-retries",3); 67 monitor = cfg->value("monitor",false); 68 69 return true; 70 } 71 72 void 73 EMI_Common::start() 74 { 75 state = E_idle; 76 LowLevelFilter::start(); 77 } 78 79 void 80 EMI_Common::started() 81 { 82 state = E_idle; 83 TRACEPRINTF (t, 2, "OpenL2"); 84 if (monitor) 85 cmdEnterMonitor(); 86 else 87 cmdOpen(); 88 } 89 90 void 91 EMI_Common::stop (bool err) 92 { 93 TRACEPRINTF (t, 2, "CloseL2"); 94 if (err) 95 LowLevelFilter::stop(err); 96 else if (monitor) 97 cmdLeaveMonitor(); 98 else 99 cmdClose(); 100 } 101 102 void 103 EMI_Common::send_L_Data (LDataPtr l) 104 { 105 if (state != E_idle) 106 { 107 ERRORPRINTF(t, E_ERROR | 59, "EMI_common: send while waiting (%d)", state); 108 return; 109 } 110 111 assert (l->lsdu.size() >= 1); 112 // discard long frames, they are not supported yet 113 // TODO add support for long frames! 114 if (l->lsdu.size() > maxPacketLen()) 115 { 116 TRACEPRINTF (t, 2, "Oversize (%d), discarded", l->lsdu.size()); 117 LowLevelFilter::do_send_Next(); 118 return; 119 } 120 CArray pdu = lData2EMI (0x11, l); 121 out = pdu; 122 retries = 0; 123 send_Data (pdu); 124 } 125 126 void 127 EMI_Common::do_send_Next() 128 { 129 if (state == E_wait) 130 state = E_wait_confirm; 131 else if (state == E_idle) 132 LowLevelFilter::do_send_Next(); 133 } 134 135 void 136 EMI_Common::send_Data(CArray &pdu) 137 { 138 state = E_wait; 139 timeout.start(send_timeout,0); 140 iface->send_Data(pdu); 141 } 142 143 void 144 EMI_Common::timeout_cb(ev::timer &, int) 145 { 146 if (state <= E_timed_out) 147 return; 148 if (state == E_wait) 149 { 150 state = E_timed_out; 151 stop(true); 152 return; 153 } 154 assert (state == E_wait_confirm); 155 if (++retries <= max_retries) 156 { 157 TRACEPRINTF (t, 1, "No confirm, repeating"); 158 send_Data(out); 159 return; 160 } 161 162 // TODO raise an error instead? 163 ERRORPRINTF(t, E_WARNING | 119, "EMI: No confirm, packet discarded"); 164 165 state = E_idle; 166 LowLevelFilter::do_send_Next(); 167 } 168 169 void 170 EMI_Common::recv_Data(CArray& c) 171 { 172 const uint8_t *ind = getIndTypes(); 173 if (c.size() > 0 && c[0] == 0xA0) 174 { 175 TRACEPRINTF (t, 2, "got reset ind"); 176 stop(true); 177 } 178 else if (c.size() > 0 && c[0] == ind[I_CONFIRM]) 179 { 180 if (state == E_wait_confirm) 181 { 182 TRACEPRINTF (t, 2, "Confirmed"); 183 state = E_idle; 184 timeout.stop(); 185 LowLevelFilter::do_send_Next(); 186 } 187 else 188 TRACEPRINTF (t, 2, "spurious Confirm %d",(int)state); 189 } 190 else if (c.size() > 0 && c[0] == ind[I_DATA] && !monitor) 191 { 192 LDataPtr p = EMI2lData (c); 193 if (p) 194 master->recv_L_Data (std::move(p)); 195 else 196 t->TracePacket (2, "unparseable EMI data", c); 197 } 198 else if (c.size() > 4 && c[0] == ind[I_BUSMON] && monitor) 199 { 200 LBusmonPtr p = LBusmonPtr(new L_Busmon_PDU ()); 201 p->l_status = c[1]; 202 p->time_stamp = (c[2] << 24) | (c[3] << 16); 203 p->lpdu.set (c.data() + 4, c.size() - 4); 204 master->recv_L_Busmonitor (std::move(p)); 205 } 206 else 207 { 208 TRACEPRINTF (t, 2, "unknown data"); 209 } 210 } 211