Connection Manager library for u-blox cellular modules. It manages the modem for keeping data connection always active.
CNManager.cpp@1:29ad1d1ac1f9, 2016-01-21 (annotated)
- Committer:
- msinig
- Date:
- Thu Jan 21 14:00:25 2016 +0000
- Revision:
- 1:29ad1d1ac1f9
- Parent:
- 0:86284a262735
fix bugs
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
msinig | 0:86284a262735 | 1 | #include "CNManager.h" |
msinig | 0:86284a262735 | 2 | |
msinig | 0:86284a262735 | 3 | #include "CNData.h" |
msinig | 0:86284a262735 | 4 | #include "CNUtil.h" |
msinig | 0:86284a262735 | 5 | #include "CNLib.h" |
msinig | 0:86284a262735 | 6 | #include "CNReg.h" |
msinig | 0:86284a262735 | 7 | |
msinig | 1:29ad1d1ac1f9 | 8 | //! Manager internal state |
msinig | 0:86284a262735 | 9 | typedef enum { |
msinig | 0:86284a262735 | 10 | MNG_NOT_INIT = 0, //!< not initiated |
msinig | 0:86284a262735 | 11 | MNG_RADIO_OFF, //!< radio is off |
msinig | 0:86284a262735 | 12 | MNG_RADIO_ON, //!< radio is on |
msinig | 0:86284a262735 | 13 | MNG_IDLE, //!< idle |
msinig | 0:86284a262735 | 14 | MNG_DATA_UP, //!< data connection is active |
msinig | 0:86284a262735 | 15 | MNG_ERROR_RESET, //!< reset |
msinig | 0:86284a262735 | 16 | MNG_ERROR_STUCK //!< unrecoverable error |
msinig | 0:86284a262735 | 17 | } MngState; |
msinig | 0:86284a262735 | 18 | |
msinig | 0:86284a262735 | 19 | static MngState state = MNG_NOT_INIT; //!< Manager state |
msinig | 0:86284a262735 | 20 | static bool moduleOn; //!< Module power requested power status |
msinig | 0:86284a262735 | 21 | static bool dataOn; |
msinig | 0:86284a262735 | 22 | static CNLib* cnLib; //!< Pointer to CN library |
msinig | 0:86284a262735 | 23 | static char simPin[10]; //!< Sim Pin |
msinig | 0:86284a262735 | 24 | static int errorCounter; //!< Error counter |
msinig | 1:29ad1d1ac1f9 | 25 | static evMngHandler mngHandler; //!< Function point handler |
msinig | 1:29ad1d1ac1f9 | 26 | static void* paramDataHandler; //!< Pointer to pass to the handler |
msinig | 1:29ad1d1ac1f9 | 27 | static int respError; |
msinig | 0:86284a262735 | 28 | |
msinig | 0:86284a262735 | 29 | static CNResp radioSwitchOn(CNLib*& lib); |
msinig | 0:86284a262735 | 30 | static CNResp radioSwitchOff(CNLib*& lib); |
msinig | 0:86284a262735 | 31 | static CNResp simInit(CNLib* lib, char* simPin); |
msinig | 0:86284a262735 | 32 | |
msinig | 1:29ad1d1ac1f9 | 33 | bool cnInit(bool powerOn /*=true*/, bool dataEnabled /*=true*/, bool roomingEnabled /*=true*/){ |
msinig | 1:29ad1d1ac1f9 | 34 | mngHandler = NULL; |
msinig | 1:29ad1d1ac1f9 | 35 | paramDataHandler = NULL; |
msinig | 0:86284a262735 | 36 | state = MNG_RADIO_OFF; |
msinig | 0:86284a262735 | 37 | moduleOn = powerOn; |
msinig | 0:86284a262735 | 38 | errorCounter = 0; |
msinig | 1:29ad1d1ac1f9 | 39 | respError = 0; |
msinig | 0:86284a262735 | 40 | cnRegInit(); |
msinig | 0:86284a262735 | 41 | cnDataInit(); |
msinig | 0:86284a262735 | 42 | dataOn = dataEnabled; |
msinig | 0:86284a262735 | 43 | cnRegSetRoaming(roomingEnabled); |
msinig | 0:86284a262735 | 44 | return true; |
msinig | 0:86284a262735 | 45 | } |
msinig | 0:86284a262735 | 46 | |
msinig | 1:29ad1d1ac1f9 | 47 | void cnRegHandler(evMngHandler handler, void* param/*=NULL*/) |
msinig | 1:29ad1d1ac1f9 | 48 | { |
msinig | 1:29ad1d1ac1f9 | 49 | mngHandler = handler; |
msinig | 1:29ad1d1ac1f9 | 50 | paramDataHandler = param; |
msinig | 1:29ad1d1ac1f9 | 51 | } |
msinig | 1:29ad1d1ac1f9 | 52 | |
msinig | 1:29ad1d1ac1f9 | 53 | MDMSerial* cnGetMDM(){ |
msinig | 1:29ad1d1ac1f9 | 54 | return (MDMSerial*) cnLib; |
msinig | 0:86284a262735 | 55 | } |
msinig | 0:86284a262735 | 56 | |
msinig | 0:86284a262735 | 57 | void cnSetPower(bool on){ |
msinig | 0:86284a262735 | 58 | moduleOn = on; |
msinig | 0:86284a262735 | 59 | } |
msinig | 0:86284a262735 | 60 | |
msinig | 0:86284a262735 | 61 | void cnSetDataEnabled(bool hasDataConnToBeEnabled){ |
msinig | 1:29ad1d1ac1f9 | 62 | TRACE("%s enter \r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 63 | dataOn = hasDataConnToBeEnabled; |
msinig | 0:86284a262735 | 64 | cnDataEnable(dataOn); |
msinig | 0:86284a262735 | 65 | } |
msinig | 0:86284a262735 | 66 | |
msinig | 0:86284a262735 | 67 | void cnSetRoamingOn(bool isRoomingEnabled){ |
msinig | 0:86284a262735 | 68 | cnRegSetRoaming(isRoomingEnabled); |
msinig | 0:86284a262735 | 69 | } |
msinig | 0:86284a262735 | 70 | |
msinig | 0:86284a262735 | 71 | void cnSetApn(const char *apn,const char* username/*= NULL*/,const char* password/*= NULL*/){ |
msinig | 0:86284a262735 | 72 | cnDataSetupApn(apn, username, password); |
msinig | 0:86284a262735 | 73 | } |
msinig | 0:86284a262735 | 74 | |
msinig | 0:86284a262735 | 75 | void cnSetSimPin(const char* pin){ |
msinig | 1:29ad1d1ac1f9 | 76 | if (pin) |
msinig | 0:86284a262735 | 77 | strncpy(simPin,pin, sizeof(simPin)); |
msinig | 0:86284a262735 | 78 | } |
msinig | 0:86284a262735 | 79 | |
msinig | 1:29ad1d1ac1f9 | 80 | bool cnIsDataUp(){ |
msinig | 1:29ad1d1ac1f9 | 81 | return (state == MNG_DATA_UP)? true : false; |
msinig | 0:86284a262735 | 82 | } |
msinig | 0:86284a262735 | 83 | |
msinig | 0:86284a262735 | 84 | bool cnSetDebug(int level){ |
msinig | 0:86284a262735 | 85 | if (!setUtilDebugLevel(level)) |
msinig | 0:86284a262735 | 86 | return false; |
msinig | 0:86284a262735 | 87 | return true; |
msinig | 0:86284a262735 | 88 | } |
msinig | 0:86284a262735 | 89 | |
msinig | 0:86284a262735 | 90 | int cnLoop(){ |
msinig | 0:86284a262735 | 91 | int ret; |
msinig | 0:86284a262735 | 92 | MngState oldState = state; |
msinig | 0:86284a262735 | 93 | |
msinig | 0:86284a262735 | 94 | TRACE("%s enter \r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 95 | //check if init has been called |
msinig | 0:86284a262735 | 96 | if (state == MNG_NOT_INIT){ |
msinig | 0:86284a262735 | 97 | ERROR("%s Module has not been initiated\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 98 | return false; |
msinig | 0:86284a262735 | 99 | } |
msinig | 0:86284a262735 | 100 | //switch on |
msinig | 0:86284a262735 | 101 | if (state == MNG_RADIO_OFF && moduleOn ){ |
msinig | 0:86284a262735 | 102 | INFO("%s: Switching ON radio \r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 103 | cnDataEnable(dataOn); |
msinig | 0:86284a262735 | 104 | ret = radioSwitchOn(cnLib); |
msinig | 0:86284a262735 | 105 | state = (ret == RES_OK) ? MNG_RADIO_ON : MNG_ERROR_RESET; |
msinig | 1:29ad1d1ac1f9 | 106 | |
msinig | 0:86284a262735 | 107 | } |
msinig | 0:86284a262735 | 108 | //switch off |
msinig | 0:86284a262735 | 109 | if (state != MNG_RADIO_OFF && !moduleOn){ |
msinig | 0:86284a262735 | 110 | INFO("%s: Switching OFF radio \r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 111 | if (state == MNG_DATA_UP) |
msinig | 0:86284a262735 | 112 | //disable data |
msinig | 0:86284a262735 | 113 | cnDataEnable(false); |
msinig | 0:86284a262735 | 114 | else if (state == MNG_IDLE){ |
msinig | 0:86284a262735 | 115 | //switching off |
msinig | 0:86284a262735 | 116 | cnDataReset(); |
msinig | 0:86284a262735 | 117 | cnRegReset(); |
msinig | 0:86284a262735 | 118 | radioSwitchOff(cnLib); |
msinig | 0:86284a262735 | 119 | state = MNG_RADIO_OFF; |
msinig | 0:86284a262735 | 120 | } |
msinig | 0:86284a262735 | 121 | } |
msinig | 0:86284a262735 | 122 | //sim initialization |
msinig | 0:86284a262735 | 123 | if (state == MNG_RADIO_ON ){ |
msinig | 0:86284a262735 | 124 | ret = simInit(cnLib, simPin); |
msinig | 0:86284a262735 | 125 | state = (ret == RES_OK) ? MNG_IDLE : MNG_ERROR_RESET; |
msinig | 1:29ad1d1ac1f9 | 126 | if (state == MNG_IDLE && mngHandler) |
msinig | 1:29ad1d1ac1f9 | 127 | mngHandler(MNG_EV_IDLE, paramDataHandler); |
msinig | 0:86284a262735 | 128 | } |
msinig | 0:86284a262735 | 129 | //cycle the registration and data service |
msinig | 0:86284a262735 | 130 | if (state == MNG_IDLE || state == MNG_DATA_UP){ |
msinig | 0:86284a262735 | 131 | RegStatus regStatus; |
msinig | 1:29ad1d1ac1f9 | 132 | int res; |
msinig | 0:86284a262735 | 133 | DataConnStatus dataStatus; |
msinig | 0:86284a262735 | 134 | //Tick the registration service |
msinig | 1:29ad1d1ac1f9 | 135 | res = cnRegLoop(cnLib, ®Status); |
msinig | 1:29ad1d1ac1f9 | 136 | if (res != RES_OK && ++respError > MEX_RESP_ERROR) |
msinig | 1:29ad1d1ac1f9 | 137 | state = MNG_ERROR_RESET; |
msinig | 0:86284a262735 | 138 | //Tick Data service |
msinig | 0:86284a262735 | 139 | cnDataLoop(cnLib, regStatus, &dataStatus); |
msinig | 0:86284a262735 | 140 | //manage data status |
msinig | 0:86284a262735 | 141 | if (dataStatus == DATA_IS_CONNECTED){ |
msinig | 1:29ad1d1ac1f9 | 142 | state = MNG_DATA_UP; |
msinig | 1:29ad1d1ac1f9 | 143 | if (mngHandler) mngHandler(MNG_EV_DATA_UP, paramDataHandler); |
msinig | 0:86284a262735 | 144 | } else if (dataStatus == DATA_IS_DISCONNECTED){ |
msinig | 0:86284a262735 | 145 | state = MNG_IDLE; |
msinig | 1:29ad1d1ac1f9 | 146 | if (mngHandler) mngHandler(MNG_EV_DATA_DOWN, paramDataHandler); |
msinig | 0:86284a262735 | 147 | } |
msinig | 0:86284a262735 | 148 | } |
msinig | 0:86284a262735 | 149 | //error management |
msinig | 0:86284a262735 | 150 | if (state == MNG_ERROR_RESET){ |
msinig | 0:86284a262735 | 151 | //block state machine when errors reach a max value counter |
msinig | 0:86284a262735 | 152 | if (++errorCounter > MAX_ERROR_RETRIES) |
msinig | 0:86284a262735 | 153 | state = MNG_ERROR_STUCK; |
msinig | 0:86284a262735 | 154 | else{ |
msinig | 0:86284a262735 | 155 | //otherwise start reset procedure |
msinig | 1:29ad1d1ac1f9 | 156 | ERROR("%s: State machine is in Error state, doing Reset.\r\n", __FUNCTION__); |
msinig | 1:29ad1d1ac1f9 | 157 | respError=0; |
msinig | 0:86284a262735 | 158 | cnDataReset(); |
msinig | 0:86284a262735 | 159 | cnRegReset(); |
msinig | 0:86284a262735 | 160 | radioSwitchOff(cnLib); |
msinig | 0:86284a262735 | 161 | state = MNG_RADIO_OFF; |
msinig | 0:86284a262735 | 162 | } |
msinig | 0:86284a262735 | 163 | } |
msinig | 0:86284a262735 | 164 | //when the state is stuck stay here forever |
msinig | 0:86284a262735 | 165 | if (state == MNG_ERROR_STUCK){ |
msinig | 1:29ad1d1ac1f9 | 166 | ERROR("%s: State machine is in Blocked state, please change setting.\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 167 | return false; |
msinig | 0:86284a262735 | 168 | } |
msinig | 0:86284a262735 | 169 | //dump info |
msinig | 1:29ad1d1ac1f9 | 170 | if (getUtilDebugLevel() > 1 && oldState != state){ |
msinig | 1:29ad1d1ac1f9 | 171 | INFO("CNManager Dump\r\n"); |
msinig | 1:29ad1d1ac1f9 | 172 | const char* txtState[] = { "Not Initiated", "Radio is Off", "Radio is On", \ |
msinig | 1:29ad1d1ac1f9 | 173 | "Idle", "Connected", "Reset Error", "Blocked Error"}; |
msinig | 1:29ad1d1ac1f9 | 174 | if (state < sizeof(txtState)/sizeof(*txtState)) |
msinig | 1:29ad1d1ac1f9 | 175 | INFO(" Internal State: %s\r\n", txtState[state]); |
msinig | 1:29ad1d1ac1f9 | 176 | } |
msinig | 0:86284a262735 | 177 | return true; |
msinig | 0:86284a262735 | 178 | } |
msinig | 0:86284a262735 | 179 | |
msinig | 0:86284a262735 | 180 | /** Switch On Radio |
msinig | 0:86284a262735 | 181 | \param lib pointer to library |
msinig | 0:86284a262735 | 182 | */ |
msinig | 0:86284a262735 | 183 | CNResp radioSwitchOn(CNLib*& lib) |
msinig | 0:86284a262735 | 184 | { |
msinig | 0:86284a262735 | 185 | TRACE("%s enter \r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 186 | lib = new CNLib(); |
msinig | 0:86284a262735 | 187 | if (lib == NULL){ |
msinig | 1:29ad1d1ac1f9 | 188 | ERROR("%s: Error on allocating lib\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 189 | goto error; |
msinig | 0:86284a262735 | 190 | } |
msinig | 0:86284a262735 | 191 | lib->setDebug(getUtilDebugLevel()); |
msinig | 0:86284a262735 | 192 | //power on the module and detect if alive |
msinig | 0:86284a262735 | 193 | if (!RESPOK(lib->powerOnModem())){ |
msinig | 1:29ad1d1ac1f9 | 194 | ERROR("%s: Error on detecting the modem\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 195 | goto error; |
msinig | 0:86284a262735 | 196 | } |
msinig | 0:86284a262735 | 197 | //init generic modem |
msinig | 0:86284a262735 | 198 | if (!RESPOK(lib->initModem())){ |
msinig | 1:29ad1d1ac1f9 | 199 | ERROR("%s: Error on init device\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 200 | goto error; |
msinig | 0:86284a262735 | 201 | } |
msinig | 0:86284a262735 | 202 | //handle unknown device |
msinig | 0:86284a262735 | 203 | if (lib->getDev()->dev == MDMParser::DEV_UNKNOWN){ |
msinig | 1:29ad1d1ac1f9 | 204 | ERROR("%s: Device is unknown\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 205 | goto error; |
msinig | 0:86284a262735 | 206 | } |
msinig | 0:86284a262735 | 207 | return RES_OK; |
msinig | 0:86284a262735 | 208 | |
msinig | 0:86284a262735 | 209 | error: |
msinig | 0:86284a262735 | 210 | return RES_ERROR; |
msinig | 0:86284a262735 | 211 | |
msinig | 0:86284a262735 | 212 | } |
msinig | 0:86284a262735 | 213 | |
msinig | 0:86284a262735 | 214 | /** Switch off the radio |
msinig | 0:86284a262735 | 215 | \param lib pointer to CN library |
msinig | 0:86284a262735 | 216 | */ |
msinig | 0:86284a262735 | 217 | CNResp radioSwitchOff(CNLib*& lib){ |
msinig | 0:86284a262735 | 218 | TRACE("%s enter \r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 219 | if (lib != NULL){ |
msinig | 0:86284a262735 | 220 | lib->powerOff(); |
msinig | 0:86284a262735 | 221 | delete lib; |
msinig | 0:86284a262735 | 222 | } |
msinig | 0:86284a262735 | 223 | return RES_OK; |
msinig | 0:86284a262735 | 224 | } |
msinig | 0:86284a262735 | 225 | |
msinig | 0:86284a262735 | 226 | /** Sim Pin initialization |
msinig | 0:86284a262735 | 227 | \param lib pointer to CN library |
msinig | 0:86284a262735 | 228 | \param simPin sim pin |
msinig | 0:86284a262735 | 229 | */ |
msinig | 0:86284a262735 | 230 | CNResp simEnterPin(CNLib* lib, char* simPin){ |
msinig | 0:86284a262735 | 231 | int res; |
msinig | 0:86284a262735 | 232 | |
msinig | 0:86284a262735 | 233 | if (simPin == NULL){ |
msinig | 1:29ad1d1ac1f9 | 234 | ERROR("%s: Sim Pin is required but not provided\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 235 | return RES_ERROR_STUCK; |
msinig | 0:86284a262735 | 236 | } |
msinig | 0:86284a262735 | 237 | res = lib->simInit(simPin); |
msinig | 0:86284a262735 | 238 | if (RESPOK(res) ) |
msinig | 0:86284a262735 | 239 | return RES_OK; |
msinig | 0:86284a262735 | 240 | if (lib->getDev()->sim == MDMParser::WRONG_PIN){ |
msinig | 1:29ad1d1ac1f9 | 241 | ERROR("%s: Sim pin, %s ,provided is wrong\r\n", simPin); |
msinig | 0:86284a262735 | 242 | return RES_ERROR_STUCK; |
msinig | 0:86284a262735 | 243 | } |
msinig | 0:86284a262735 | 244 | return RES_ERROR; |
msinig | 0:86284a262735 | 245 | } |
msinig | 0:86284a262735 | 246 | |
msinig | 0:86284a262735 | 247 | /** Sim initialization |
msinig | 0:86284a262735 | 248 | \param lib pointer to CN library |
msinig | 0:86284a262735 | 249 | \param simPin sim pin |
msinig | 0:86284a262735 | 250 | */ |
msinig | 0:86284a262735 | 251 | CNResp simInit(CNLib* lib, char* simPin) |
msinig | 0:86284a262735 | 252 | { |
msinig | 0:86284a262735 | 253 | CNResp res = RES_ERROR; |
msinig | 0:86284a262735 | 254 | int _simWaitCounter=0; |
msinig | 0:86284a262735 | 255 | TRACE("CNLib::%s enter\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 256 | |
msinig | 0:86284a262735 | 257 | do{ |
msinig | 0:86284a262735 | 258 | if (RESPOK(lib->simInit(NULL))) |
msinig | 0:86284a262735 | 259 | break; |
msinig | 0:86284a262735 | 260 | wait_ms(10); |
msinig | 0:86284a262735 | 261 | } while(_simWaitCounter++ < SIM_WAIT_MAX_CYCLE); |
msinig | 0:86284a262735 | 262 | INFO("Sim status is %d \r\n", lib->getDev()->sim); |
msinig | 0:86284a262735 | 263 | //handle sim status |
msinig | 0:86284a262735 | 264 | switch (lib->getDev()->sim){ |
msinig | 0:86284a262735 | 265 | case MDMParser::SIM_READY: |
msinig | 1:29ad1d1ac1f9 | 266 | INFO("%s: Sim has been initiated correctly\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 267 | res = RES_OK; |
msinig | 0:86284a262735 | 268 | break; |
msinig | 0:86284a262735 | 269 | case MDMParser::SIM_PIN: |
msinig | 1:29ad1d1ac1f9 | 270 | INFO("%s: Sim Pin is requested\r\n"); |
msinig | 0:86284a262735 | 271 | res = simEnterPin(lib, simPin); |
msinig | 0:86284a262735 | 272 | break; |
msinig | 0:86284a262735 | 273 | case MDMParser::SIM_MISSING: |
msinig | 1:29ad1d1ac1f9 | 274 | ERROR("%s: Sim has not been inserted, HALT\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 275 | res = RES_ERROR; |
msinig | 0:86284a262735 | 276 | break; |
msinig | 0:86284a262735 | 277 | case MDMParser::SIM_PUK: |
msinig | 1:29ad1d1ac1f9 | 278 | ERROR("%s: Sim Puk is required, HALT\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 279 | res = RES_ERROR_STUCK; |
msinig | 0:86284a262735 | 280 | break; |
msinig | 0:86284a262735 | 281 | case MDMParser::SIM_UNKNOWN: |
msinig | 1:29ad1d1ac1f9 | 282 | ERROR("%s: Sim Unknown state\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 283 | res = RES_ERROR_STUCK; |
msinig | 0:86284a262735 | 284 | break; |
msinig | 0:86284a262735 | 285 | case MDMParser::WRONG_PIN: |
msinig | 1:29ad1d1ac1f9 | 286 | ERROR("%s: Wrong sim pin provided, HALT\r\n", __FUNCTION__); |
msinig | 0:86284a262735 | 287 | res = RES_ERROR_STUCK; |
msinig | 0:86284a262735 | 288 | break; |
msinig | 0:86284a262735 | 289 | } |
msinig | 0:86284a262735 | 290 | //sim has been successfully initialized |
msinig | 0:86284a262735 | 291 | if (res == RES_OK){ |
msinig | 0:86284a262735 | 292 | lib->getSimInfo(); |
msinig | 0:86284a262735 | 293 | } |
msinig | 0:86284a262735 | 294 | return res; |
msinig | 1:29ad1d1ac1f9 | 295 | } |
msinig | 1:29ad1d1ac1f9 | 296 | |
msinig | 1:29ad1d1ac1f9 | 297 | void getNetStatus(MDMParser::NetStatus* net){ |
msinig | 1:29ad1d1ac1f9 | 298 | if (net && cnLib) |
msinig | 1:29ad1d1ac1f9 | 299 | memcpy(net, cnLib->getNet(), sizeof(MDMParser::NetStatus)); |
msinig | 1:29ad1d1ac1f9 | 300 | } |
msinig | 1:29ad1d1ac1f9 | 301 | void getDevStatus(MDMParser::DevStatus* dev){ |
msinig | 1:29ad1d1ac1f9 | 302 | if (dev && cnLib) |
msinig | 1:29ad1d1ac1f9 | 303 | memcpy(dev, cnLib->getDev(), sizeof(MDMParser::DevStatus)); |
msinig | 0:86284a262735 | 304 | } |