u-blox / C027_Support_HTTP

Fork of C027_Support by u-blox

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MDM.cpp Source File

MDM.cpp

00001 #include "mbed.h"
00002 #include "MDM.h"
00003 #ifdef TARGET_UBLOX_C027
00004  #include "C027_api.h"
00005 #endif
00006 #include "MDMAPN.h"
00007         
00008 #define PROFILE         "0"   //!< this is the psd profile used
00009 #define MAX_SIZE        128   //!< max expected messages
00010 // num sockets
00011 #define NUMSOCKETS      (sizeof(_sockets)/sizeof(*_sockets))
00012 //! test if it is a socket is ok to use
00013 #define ISSOCKET(s)     (((s) >= 0) && ((s) < NUMSOCKETS) && (_sockets[s].handle != SOCKET_ERROR))
00014 //! check for timeout
00015 #define TIMEOUT(t, ms)  ((ms != TIMEOUT_BLOCKING) && (ms < t.read_ms())) 
00016 // num HTTP profiles
00017 #define NUMPROFILES      (sizeof(_httpProfiles)/sizeof(*_httpProfiles))
00018 //! test if it is an HTTP profile is ok to use
00019 #define ISPROFILE(p)     (((p) >= 0) && ((p) < NUMPROFILES) && (_httpProfiles[p].handle != HTTP_PROF_ERROR))
00020 //! registration ok check helper
00021 #define REG_OK(r)       ((r == REG_HOME) || (r == REG_ROAMING)) 
00022 //! registration done check helper (no need to poll further)
00023 #define REG_DONE(r)     ((r == REG_HOME) || (r == REG_ROAMING) || (r == REG_DENIED)) 
00024 //! helper to make sure that lock unlock pair is always balaced 
00025 #define LOCK()         { lock() 
00026 //! helper to make sure that lock unlock pair is always balaced 
00027 #define UNLOCK()       } unlock()
00028 
00029 #ifdef MDM_DEBUG
00030  #if 1 // colored terminal output using ANSI escape sequences
00031   #define COL(c) "\033[" c
00032  #else
00033   #define COL(c) 
00034  #endif
00035  #define DEF COL("39m")
00036  #define BLA COL("30m")
00037  #define RED COL("31m")
00038  #define GRE COL("32m")
00039  #define YEL COL("33m")
00040  #define BLU COL("34m")
00041  #define MAG COL("35m")
00042  #define CYA COL("36m")
00043  #define WHY COL("37m")
00044  
00045 void dumpAtCmd(const char* buf, int len)
00046 {
00047     ::printf(" %3d \"", len);
00048     while (len --) {
00049         char ch = *buf++;
00050         if ((ch > 0x1F) && (ch != 0x7F)) { // is printable
00051             if      (ch == '%')  ::printf("%%");
00052             else if (ch == '"')  ::printf("\\\"");
00053             else if (ch == '\\') ::printf("\\\\");
00054             else putchar(ch);
00055         } else {
00056             if      (ch == '\a') ::printf("\\a"); // BEL (0x07)
00057             else if (ch == '\b') ::printf("\\b"); // Backspace (0x08)
00058             else if (ch == '\t') ::printf("\\t"); // Horizontal Tab (0x09)
00059             else if (ch == '\n') ::printf("\\n"); // Linefeed (0x0A)
00060             else if (ch == '\v') ::printf("\\v"); // Vertical Tab (0x0B)
00061             else if (ch == '\f') ::printf("\\f"); // Formfeed (0x0C)
00062             else if (ch == '\r') ::printf("\\r"); // Carriage Return (0x0D)
00063             else                 ::printf("\\x%02x", (unsigned char)ch);
00064         }
00065     }
00066     ::printf("\"\r\n");
00067 }
00068  
00069 void MDMParser::_debugPrint(int level, const char* color, const char* format, ...)
00070 {
00071     if (_debugLevel >= level) 
00072     {
00073         va_list args;
00074         va_start (args, format);
00075         if (color) ::printf(color);
00076         ::vprintf(format, args);
00077         if (color) ::printf(DEF);
00078         va_end (args);
00079     }
00080 }
00081    
00082  #define ERROR(...)     _debugPrint(0, RED, __VA_ARGS__)
00083  #define INFO(...)      _debugPrint(1, GRE, __VA_ARGS__)
00084  #define TRACE(...)     _debugPrint(2, DEF, __VA_ARGS__)
00085  #define TEST(...)      _debugPrint(3, CYA, __VA_ARGS__)
00086  
00087 #else
00088  
00089  #define ERROR(...) (void)0 // no tracing
00090  #define TEST(...)  (void)0 // no tracing
00091  #define INFO(...)  (void)0 // no tracing
00092  #define TRACE(...) (void)0 // no tracing
00093 
00094 #endif
00095 
00096 MDMParser* MDMParser::inst;
00097 
00098 MDMParser::MDMParser(void)
00099 {
00100     inst = this;
00101     memset(&_dev, 0, sizeof(_dev));
00102     memset(&_net, 0, sizeof(_net));
00103     _net.lac = 0xFFFF;
00104     _net.ci = 0xFFFFFFFF;
00105     _ip        = NOIP;
00106     _init      = false;
00107     memset(_sockets, 0, sizeof(_sockets));
00108     for (int socket = 0; socket < NUMSOCKETS; socket ++)
00109         _sockets[socket].handle = SOCKET_ERROR;
00110     memset(_httpProfiles, 0, sizeof(_httpProfiles));
00111     for (int profile = 0; profile < NUMPROFILES; profile ++)
00112         _httpProfiles[profile].handle = HTTP_PROF_ERROR;
00113 #ifdef MDM_DEBUG
00114     _debugLevel = 1;
00115     _debugTime.start();
00116 #endif
00117 }
00118 
00119 int MDMParser::send(const char* buf, int len)
00120 {
00121 #ifdef MDM_DEBUG
00122     if (_debugLevel >= 3) {
00123         ::printf("%10.3f AT send    ", _debugTime.read_ms()*0.001);
00124         dumpAtCmd(buf,len);
00125     }
00126 #endif
00127     return _send(buf, len);
00128 }
00129 
00130 int MDMParser::sendFormated(const char* format, ...) {
00131     char buf[MAX_SIZE];
00132     va_list args;
00133     va_start(args, format);
00134     int len = vsnprintf(buf,sizeof(buf), format, args);
00135     va_end(args);
00136     return send(buf, len);
00137 }
00138 
00139 int MDMParser::waitFinalResp(_CALLBACKPTR cb /* = NULL*/, 
00140                              void* param /* = NULL*/, 
00141                              int timeout_ms /*= 5000*/)
00142 {
00143     char buf[MAX_SIZE + 64 /* add some more space for framing */]; 
00144     Timer timer;
00145     timer.start();
00146     do {
00147         int ret = getLine(buf, sizeof(buf));
00148 #ifdef MDM_DEBUG
00149         if ((_debugLevel >= 3) && (ret != WAIT) && (ret != NOT_FOUND))
00150         {
00151             int len = LENGTH(ret);
00152             int type = TYPE(ret);
00153             const char* s = (type == TYPE_UNKNOWN)? YEL "UNK" DEF : 
00154                             (type == TYPE_TEXT)   ? MAG "TXT" DEF : 
00155                             (type == TYPE_OK   )  ? GRE "OK " DEF : 
00156                             (type == TYPE_ERROR)  ? RED "ERR" DEF : 
00157                             (type == TYPE_PLUS)   ? CYA " + " DEF : 
00158                             (type == TYPE_PROMPT) ? BLU " > " DEF : 
00159                                                         "..." ;
00160             ::printf("%10.3f AT read %s", _debugTime.read_ms()*0.001, s);
00161             dumpAtCmd(buf, len);
00162         }
00163 #endif        
00164         if ((ret != WAIT) && (ret != NOT_FOUND))
00165         {
00166             int type = TYPE(ret);
00167             // handle unsolicited commands here
00168             if (type == TYPE_PLUS) {
00169                 const char* cmd = buf+3;
00170                 int a, b, c, d, r;
00171                 char s[32];
00172                                                 
00173                 // SMS Command ---------------------------------
00174                 // +CNMI: <mem>,<index>
00175                 if (sscanf(cmd, "CMTI: \"%*[^\"]\",%d", &a) == 1) { 
00176                     TRACE("New SMS at index %d\r\n", a);
00177                 // Socket Specific Command ---------------------------------
00178                 // +UUSORD: <socket>,<length>
00179                 } else if ((sscanf(cmd, "UUSORD: %d,%d", &a, &b) == 2)) {
00180                     int socket = _findSocket(a);
00181                     TRACE("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
00182                     if (socket != SOCKET_ERROR)
00183                         _sockets[socket].pending = b;
00184                 // +UUSORF: <socket>,<length>
00185                 } else if ((sscanf(cmd, "UUSORF: %d,%d", &a, &b) == 2)) {
00186                     int socket = _findSocket(a);
00187                     TRACE("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
00188                     if (socket != SOCKET_ERROR)
00189                         _sockets[socket].pending = b;
00190                 // +UUSOCL: <socket>
00191                 } else if ((sscanf(cmd, "UUSOCL: %d", &a) == 1)) {
00192                     int socket = _findSocket(a);
00193                     TRACE("Socket %d: handle %d closed by remote host\r\n", socket, a);
00194                     if ((socket != SOCKET_ERROR) && _sockets[socket].connected)
00195                         _sockets[socket].connected = false;                
00196                 // +UULOC: <date>,<time>,<lat>,<long>,<alt>,<uncertainty>,<speed>, <direction>,<vertical_acc>,<sensor_used>,<SV_used>,<antenna_status>, <jamming_status>
00197                 }else if (sscanf(cmd, "UULOC: %d/%d/%d,%d:%d:%d.%*d,%f,%f,%d,%d,%d,%d,%d,%d,%d,%*d,%*d",\
00198                         &_loc.time.tm_mday, &_loc.time.tm_mon, &_loc.time.tm_year, &_loc.time.tm_hour, &_loc.time.tm_min, &_loc.time.tm_sec,\
00199                         &_loc.latitue, &_loc.longitude, &_loc.altitutude, &_loc.uncertainty, &_loc.speed, &_loc.direction, &_loc.verticalAcc, \
00200                         &_loc.sensorUsed, &_loc.svUsed) == 15) {
00201                     _loc.time.tm_mon -= 1;
00202                     _loc.time.tm_wday=0;
00203                     _loc.time.tm_yday=0;
00204                     _loc.validData = true;
00205                     TRACE("Parsed UULOC position\r\n");
00206                 // +UHTTPCR: <profile_id>,<op_code>,<param_val>
00207                 } else if ((sscanf(cmd, "UUHTTPCR: %d,%d,%d", &a, &b, &c) == 3)) {
00208                     _httpProfiles[a].cmd = b;          //command
00209                     _httpProfiles[a].result = c;       //result
00210                     TRACE("%s for profile %d: result code is %d\r\n", getHTTPcmd(b), a, c);
00211                 }
00212                 if (_dev.dev == DEV_LISA_C2) {
00213                     // CDMA Specific -------------------------------------------
00214                     // +CREG: <n><SID>,<NID>,<stat>
00215                     if (sscanf(cmd, "CREG: %*d,%d,%d,%d",&a,&b,&c) == 3) {
00216                         // _net.sid = a;
00217                         // _net.nid = b;
00218                         if      (c == 0) _net.csd = REG_NONE;     // not registered, home network
00219                         else if (c == 1) _net.csd = REG_HOME;     // registered, home network
00220                         else if (c == 2) _net.csd = REG_NONE;     // not registered, but MT is currently searching a new operator to register to
00221                         else if (c == 3) _net.csd = REG_DENIED;   // registration denied
00222                         else if (c == 5) _net.csd = REG_ROAMING;  // registered, roaming
00223                         _net.psd = _net.csd; // fake PSD registration (CDMA is always registered)
00224                         _net.act = ACT_CDMA;
00225                         // +CSS: <mode>[,<format>,<oper>[,<AcT>]]
00226                     } else if (sscanf(cmd, "CSS %*c,%2s,%*d",s) == 1) {
00227                         //_net.reg = (strcmp("Z", s) == 0) ? REG_UNKNOWN : REG_HOME;
00228                     }
00229                 } else {
00230                     // GSM/UMTS Specific -------------------------------------------
00231                     // +UUPSDD: <profile_id> 
00232                     if (sscanf(cmd, "UUPSDD: %d",&a) == 1) {
00233                         if (*PROFILE == a) _ip = NOIP;
00234                     } else {
00235                         // +CREG|CGREG: <n>,<stat>[,<lac>,<ci>[,AcT[,<rac>]]] // reply to AT+CREG|AT+CGREG
00236                         // +CREG|CGREG: <stat>[,<lac>,<ci>[,AcT[,<rac>]]]     // URC
00237                         b = 0xFFFF; c = 0xFFFFFFFF; d = -1;
00238                         r = sscanf(cmd, "%s %*d,%d,\"%X\",\"%X\",%d",s,&a,&b,&c,&d);
00239                         if (r <= 1)
00240                             r = sscanf(cmd, "%s %d,\"%X\",\"%X\",%d",s,&a,&b,&c,&d);
00241                         if (r >= 2) {
00242                             Reg *reg = !strcmp(s, "CREG:")  ? &_net.csd : 
00243                                        !strcmp(s, "CGREG:") ? &_net.psd : 
00244                                        !strcmp(s, "CEREG:") ? &_net.eps : NULL;
00245                             if (reg) {
00246                                 // network status
00247                                 if      (a == 0) *reg = REG_NONE;     // 0: not registered, home network
00248                                 else if (a == 1) *reg = REG_HOME;     // 1: registered, home network
00249                                 else if (a == 2) *reg = REG_NONE;     // 2: not registered, but MT is currently searching a new operator to register to
00250                                 else if (a == 3) *reg = REG_DENIED;   // 3: registration denied
00251                                 else if (a == 4) *reg = REG_UNKNOWN;  // 4: unknown
00252                                 else if (a == 5) *reg = REG_ROAMING;  // 5: registered, roaming
00253                                 else if (a == 6) *reg = REG_HOME;     // 6: registered, sms only, home
00254                                 if ((r >= 3) && (b != 0xFFFF))      _net.lac = b; // location area code
00255                                 if ((r >= 4) && (c != 0xFFFFFFFF))  _net.ci  = c; // cell ID
00256                                 // access technology
00257                                 if (r >= 5) {
00258                                     if      (d == 0) _net.act = ACT_GSM;      // 0: GSM
00259                                     else if (d == 1) _net.act = ACT_GSM;      // 1: GSM COMPACT
00260                                     else if (d == 2) _net.act = ACT_UTRAN;    // 2: UTRAN
00261                                     else if (d == 3) _net.act = ACT_EDGE;     // 3: GSM with EDGE availability
00262                                     else if (d == 4) _net.act = ACT_UTRAN;    // 4: UTRAN with HSDPA availability
00263                                     else if (d == 5) _net.act = ACT_UTRAN;    // 5: UTRAN with HSUPA availability
00264                                     else if (d == 6) _net.act = ACT_UTRAN;    // 6: UTRAN with HSDPA and HSUPA availability
00265                                     else if (d == 7) _net.act = ACT_LTE;      // 7: LTE
00266                                 }
00267                             }
00268                         }
00269                     }
00270                 }
00271             }
00272             if (cb) {
00273                 int len = LENGTH(ret);
00274                 int ret = cb(type, buf, len, param);
00275                 if (WAIT != ret)
00276                     return ret; 
00277             }
00278             if (type == TYPE_OK)
00279                 return RESP_OK;
00280             if (type == TYPE_ERROR)
00281                 return RESP_ERROR;
00282             if (type == TYPE_ERROR_CME)
00283                 return RESP_ERROR_CME;                
00284             if (type == TYPE_PROMPT)    
00285                 return RESP_PROMPT;
00286         }
00287         // relax a bit
00288         wait_ms(10); 
00289     }
00290     while (!TIMEOUT(timer, timeout_ms));
00291     return WAIT;
00292 }
00293 
00294 int MDMParser::_cbString(int type, const char* buf, int len, char* str)
00295 {
00296     if (str && (type == TYPE_UNKNOWN)) {
00297         if (sscanf(buf, "\r\n%s\r\n", str) == 1)
00298             /*nothing*/;
00299     }
00300     return WAIT;
00301 }
00302 
00303 int MDMParser::_cbInt(int type, const char* buf, int len, int* val)
00304 {
00305     if (val && (type == TYPE_UNKNOWN)) {
00306         if (sscanf(buf, "\r\n%d\r\n", val) == 1)
00307             /*nothing*/;
00308     }
00309     return WAIT;
00310 }
00311 
00312 // ----------------------------------------------------------------
00313 
00314 bool MDMParser::connect(
00315             const char* simpin, 
00316             const char* apn, const char* username, 
00317             const char* password, Auth auth,
00318             PinName pn)
00319 {
00320     bool ok = init(simpin, NULL, pn);  
00321 #ifdef MDM_DEBUG
00322     if (_debugLevel >= 1) dumpDevStatus(&_dev);
00323 #endif
00324     if (!ok)
00325         return false;
00326     ok = registerNet();
00327 #ifdef MDM_DEBUG
00328     if (_debugLevel >= 1) dumpNetStatus(&_net);
00329 #endif
00330     if (!ok)
00331         return false;
00332     IP ip = join(apn,username,password,auth);
00333 #ifdef MDM_DEBUG
00334     if (_debugLevel >= 1) dumpIp(ip);
00335 #endif
00336     if (ip == NOIP)
00337         return false; 
00338     return true;
00339 }
00340 
00341 bool MDMParser::init(const char* simpin, DevStatus* status, PinName pn)
00342 {
00343     int i = 10;
00344     LOCK();
00345     memset(&_dev, 0, sizeof(_dev));
00346     if (pn != NC) {
00347         INFO("Modem::wakeup\r\n");
00348         DigitalOut pin(pn, 1);
00349         while (i--) {
00350             // SARA-U2/LISA-U2 50..80us
00351             pin = 0; ::wait_us(50);
00352             pin = 1; ::wait_ms(10); 
00353             
00354             // SARA-G35 >5ms, LISA-C2 > 150ms, LEON-G2 >5ms
00355             pin = 0; ::wait_ms(150);
00356             pin = 1; ::wait_ms(100);
00357             
00358             // purge any messages 
00359             purge();
00360             
00361             // check interface
00362             sendFormated("AT\r\n");
00363             int r = waitFinalResp(NULL,NULL,1000);
00364             if(RESP_OK == r) break;
00365         }
00366         if (i < 0) {
00367             ERROR("No Reply from Modem\r\n");
00368             goto failure;
00369         }
00370     }
00371     _init = true;
00372     
00373     INFO("Modem::init\r\n");
00374     // echo off
00375     sendFormated("AT E0\r\n");
00376     if(RESP_OK != waitFinalResp())
00377         goto failure; 
00378     // enable verbose error messages
00379     sendFormated("AT+CMEE=2\r\n");
00380     if(RESP_OK != waitFinalResp())
00381         goto failure;
00382     // set baud rate
00383     sendFormated("AT+IPR=115200\r\n");
00384     if (RESP_OK != waitFinalResp())
00385         goto failure;
00386     // wait some time until baudrate is applied
00387     wait_ms(200); // SARA-G > 40ms
00388     // identify the module 
00389     sendFormated("ATI\r\n");
00390     if (RESP_OK != waitFinalResp(_cbATI, &_dev.dev))
00391         goto failure;
00392     if (_dev.dev == DEV_UNKNOWN)
00393         goto failure;
00394     // device specific init
00395     if (_dev.dev == DEV_LISA_C2) {
00396         // get the manufacturer
00397         sendFormated("AT+GMI\r\n");
00398         if (RESP_OK != waitFinalResp(_cbString, _dev.manu))
00399             goto failure;
00400         // get the model identification
00401         sendFormated("AT+GMM\r\n");
00402         if (RESP_OK != waitFinalResp(_cbString, _dev.model))
00403             goto failure;
00404         // get the sw version
00405         sendFormated("AT+GMR\r\n");
00406         if (RESP_OK != waitFinalResp(_cbString, _dev.ver))
00407             goto failure;
00408         // get the pseudo ESN or MEID
00409         sendFormated("AT+GSN\r\n");
00410         if (RESP_OK != waitFinalResp(_cbString, _dev.meid))
00411             goto failure;
00412 #if 0
00413         // enable power saving
00414         if (_dev.lpm != LPM_DISABLED) {
00415              // enable power saving (requires flow control, cts at least)
00416             sendFormated("AT+UPSV=1,1280\r\n");
00417             if (RESP_OK != waitFinalResp())
00418                 goto failure;  
00419             _dev.lpm = LPM_ACTIVE;
00420         }
00421 #endif
00422     } else {
00423         if ((_dev.dev == DEV_LISA_U2) || (_dev.dev == DEV_LEON_G2) || 
00424             (_dev.dev == DEV_TOBY_L2)) {
00425             // enable the network identification feature 
00426             sendFormated("AT+UGPIOC=20,2\r\n");
00427             if (RESP_OK != waitFinalResp())
00428                 goto failure;
00429         } else if ((_dev.dev == DEV_SARA_U2) || (_dev.dev == DEV_SARA_G35)) {
00430             // enable the network identification feature 
00431             sendFormated("AT+UGPIOC=16,2\r\n");
00432             if (RESP_OK != waitFinalResp())
00433                 goto failure;
00434         }
00435         // check the sim card
00436         for (int i = 0; (i < 5) && (_dev.sim != SIM_READY); i++) {
00437             sendFormated("AT+CPIN?\r\n");
00438             int ret = waitFinalResp(_cbCPIN, &_dev.sim);
00439             // having an error here is ok (sim may still be initializing)
00440             if ((RESP_OK != ret) && (RESP_ERROR != ret))
00441                 goto failure;
00442             // Enter PIN if needed
00443             if (_dev.sim == SIM_PIN) {
00444                 if (!simpin) {
00445                     ERROR("SIM PIN not available\r\n");
00446                     goto failure;
00447                 }
00448                 sendFormated("AT+CPIN=\"%s\"\r\n", simpin);
00449                 if (RESP_OK != waitFinalResp(_cbCPIN, &_dev.sim))
00450                     goto failure;
00451             } else if (_dev.sim != SIM_READY) {
00452                 wait_ms(1000);
00453             }
00454         }
00455         if (_dev.sim != SIM_READY) {
00456             if (_dev.sim == SIM_MISSING)
00457                 ERROR("SIM not inserted\r\n");
00458             goto failure;
00459         }
00460         // get the manufacturer
00461         sendFormated("AT+CGMI\r\n");
00462         if (RESP_OK != waitFinalResp(_cbString, _dev.manu))
00463             goto failure;
00464         // get the model identification
00465         sendFormated("AT+CGMM\r\n");
00466         if (RESP_OK != waitFinalResp(_cbString, _dev.model))
00467             goto failure;
00468         // get the version
00469         sendFormated("ATI9\r\n");
00470         if (RESP_OK != waitFinalResp(_cbString, _dev.ver))
00471             goto failure;
00472         // Returns the ICCID (Integrated Circuit Card ID) of the SIM-card. 
00473         // ICCID is a serial number identifying the SIM.
00474         sendFormated("AT+CCID\r\n");
00475         if (RESP_OK != waitFinalResp(_cbCCID, _dev.ccid))
00476             goto failure;
00477         // Returns the product serial number, IMEI (International Mobile Equipment Identity)
00478         sendFormated("AT+CGSN\r\n");
00479         if (RESP_OK != waitFinalResp(_cbString, _dev.imei))
00480             goto failure;
00481         // enable power saving
00482         if (_dev.lpm != LPM_DISABLED) {
00483              // enable power saving (requires flow control, cts at least)
00484             sendFormated("AT+UPSV=1\r\n");
00485             if (RESP_OK != waitFinalResp())
00486                 goto failure;  
00487             _dev.lpm = LPM_ACTIVE;
00488         }
00489         // enable the psd registration unsolicited result code
00490         sendFormated("AT+CGREG=2\r\n");
00491         if (RESP_OK != waitFinalResp())
00492             goto failure;
00493     } 
00494     // enable the network registration unsolicited result code
00495     sendFormated("AT+CREG=%d\r\n", (_dev.dev == DEV_LISA_C2) ? 1 : 2);
00496     if (RESP_OK != waitFinalResp())
00497         goto failure;
00498     // Setup SMS in text mode 
00499     sendFormated("AT+CMGF=1\r\n");
00500     if (RESP_OK != waitFinalResp())
00501         goto failure;
00502     // setup new message indication
00503     sendFormated("AT+CNMI=2,1\r\n");
00504     if (RESP_OK != waitFinalResp())
00505         goto failure;
00506     // Request IMSI (International Mobile Subscriber Identification)
00507     sendFormated("AT+CIMI\r\n");
00508     if (RESP_OK != waitFinalResp(_cbString, _dev.imsi))
00509         goto failure;
00510     if (status)
00511         memcpy(status, &_dev, sizeof(DevStatus));
00512     UNLOCK();
00513     return true; 
00514 failure:
00515     unlock();
00516     return false; 
00517 }
00518 
00519 bool MDMParser::powerOff(void)
00520 {
00521     bool ok = false;
00522     if (_init) {
00523         LOCK();
00524         INFO("Modem::powerOff\r\n");
00525         sendFormated("AT+CPWROFF\r\n");
00526         if (RESP_OK == waitFinalResp(NULL,NULL,120*1000)) {
00527             _init = false;
00528             ok = true;
00529         }
00530         UNLOCK();
00531     }
00532     return ok;
00533 }
00534 
00535 int MDMParser::_cbATI(int type, const char* buf, int len, Dev* dev)
00536 {
00537     if ((type == TYPE_UNKNOWN) && dev) {
00538         if      (strstr(buf, "SARA-G35"))   *dev = DEV_SARA_G35;
00539         else if (strstr(buf, "LISA-U200-03S")) *dev = DEV_LISA_U2_03S;
00540         else if (strstr(buf, "LISA-U2"))    *dev = DEV_LISA_U2;        
00541         else if (strstr(buf, "LISA-C2"))    *dev = DEV_LISA_C2;
00542         else if (strstr(buf, "SARA-U2"))    *dev = DEV_SARA_U2;
00543         else if (strstr(buf, "LEON-G2"))    *dev = DEV_LEON_G2;
00544         else if (strstr(buf, "TOBY-L2"))    *dev = DEV_TOBY_L2;
00545         else if (strstr(buf, "MPCI-L2"))    *dev = DEV_MPCI_L2;
00546     }
00547     return WAIT;
00548 }
00549 
00550 int MDMParser::_cbCPIN(int type, const char* buf, int len, Sim* sim)
00551 {
00552     if (sim) {
00553         if (type == TYPE_PLUS){
00554             char s[16];
00555             if (sscanf(buf, "\r\n+CPIN: %[^\r]\r\n", s) >= 1)
00556                 *sim = (0 == strcmp("READY", s)) ? SIM_READY : SIM_PIN;
00557         } else if (type == TYPE_ERROR) {
00558             if (strstr(buf, "+CME ERROR: SIM not inserted"))
00559                 *sim = SIM_MISSING;
00560         }
00561     }
00562     return WAIT;
00563 }
00564 
00565 int MDMParser::_cbCCID(int type, const char* buf, int len, char* ccid)
00566 {
00567     if ((type == TYPE_PLUS) && ccid){
00568         if (sscanf(buf, "\r\n+CCID: %[^\r]\r\n", ccid) == 1)
00569             /*TRACE("Got CCID: %s\r\n", ccid)*/;
00570     }
00571     return WAIT;
00572 }
00573 
00574 bool MDMParser::registerNet(NetStatus* status /*= NULL*/, int timeout_ms /*= 180000*/) 
00575 {
00576     Timer timer;
00577     timer.start();
00578     INFO("Modem::register\r\n");
00579     while (!checkNetStatus(status) && !TIMEOUT(timer, timeout_ms))
00580         wait_ms(1000);
00581     if (_net.csd == REG_DENIED) ERROR("CSD Registration Denied\r\n");
00582     if (_net.psd == REG_DENIED) ERROR("PSD Registration Denied\r\n");
00583     if (_net.eps == REG_DENIED) ERROR("EPS Registration Denied\r\n");
00584     return REG_OK(_net.csd) || REG_OK(_net.psd) || REG_OK(_net.eps);
00585 }
00586 
00587 bool MDMParser::checkNetStatus(NetStatus* status /*= NULL*/)
00588 {
00589     bool ok = false;
00590     LOCK();
00591     memset(&_net, 0, sizeof(_net));
00592     _net.lac = 0xFFFF;
00593     _net.ci = 0xFFFFFFFF;
00594     // check registration
00595     sendFormated("AT+CREG?\r\n");
00596     waitFinalResp();     // don't fail as service could be not subscribed 
00597     if (_dev.dev != DEV_LISA_C2) {
00598         // check PSD registration
00599         sendFormated("AT+CGREG?\r\n");
00600         waitFinalResp(); // don't fail as service could be not subscribed 
00601         if ((_dev.dev == DEV_TOBY_L2) ||  (_dev.dev == DEV_MPCI_L2)) {
00602             // check EPS network registration
00603             sendFormated("AT+CEREG?\r\n");
00604             waitFinalResp(); // don't fail as service could be not subscribed
00605         }
00606     }
00607     if (REG_OK(_net.csd) || REG_OK(_net.psd) || REG_OK(_net.eps))
00608     {
00609         // check modem specific status messages 
00610         if (_dev.dev == DEV_LISA_C2) {
00611             sendFormated("AT+CSS?\r\n");
00612             if (RESP_OK != waitFinalResp())
00613                 goto failure;
00614             while (1) {
00615                 // get the Telephone number
00616                 sendFormated("AT$MDN?\r\n");
00617                 if (RESP_OK != waitFinalResp(_cbString, _net.num))
00618                     goto failure;
00619                 // check if we have a Mobile Directory Number
00620                 if (*_net.num && (memcmp(_net.num, "000000", 6) != 0))
00621                     break;
00622                     
00623                 INFO("Device not yet activated\r\n");
00624                 INFO("Make sure you have a valid contract with the network operator for this device.\r\n");
00625                 // Check if the the version contains a V for Verizon 
00626                 // Verizon: E0.V.xx.00.xxR, 
00627                 // Sprint E0.S.xx.00.xxR
00628                 if (_dev.ver[3] == 'V') { 
00629                     int i;
00630                     INFO("Start device over-the-air activation (this can take a few minutes)\r\n");
00631                     sendFormated("AT+CDV=*22899\r\n");
00632                     i = 1;
00633                     if ((RESP_OK != waitFinalResp(_cbUACTIND, &i, 120*1000)) || (i == 1)) {
00634                         ERROR("Device over-the-air activation failed\r\n");
00635                         goto failure;
00636                     }
00637                     INFO("Device over-the-air activation successful\r\n");
00638                     
00639                     INFO("Start PRL over-the-air update (this can take a few minutes)\r\n");
00640                     sendFormated("AT+CDV=*22891\r\n");
00641                     i = 1;
00642                     if ((RESP_OK != waitFinalResp(_cbUACTIND, &i, 120*1000)) || (i == 1)) {
00643                         ERROR("PRL over-the-air update failed\r\n");
00644                         goto failure;
00645                     }
00646                     INFO("PRL over-the-air update successful\r\n");
00647                     
00648                 } else { 
00649                     // Sprint or Aeris 
00650                     INFO("Wait for OMA-DM over-the-air activation (this can take a few minutes)\r\n");
00651                     wait_ms(120*1000);
00652                 }
00653             }
00654             // get the the Network access identifier string
00655             char nai[64];
00656             sendFormated("AT$QCMIPNAI?\r\n");
00657             if (RESP_OK != waitFinalResp(_cbString, nai))
00658                 goto failure;
00659         } else {
00660             sendFormated("AT+COPS?\r\n");
00661             if (RESP_OK != waitFinalResp(_cbCOPS, &_net))
00662                 goto failure;
00663             // get the MSISDNs related to this subscriber
00664             sendFormated("AT+CNUM\r\n");
00665             if (RESP_OK != waitFinalResp(_cbCNUM, _net.num))
00666                 goto failure;
00667         }  
00668         // get the signal strength indication
00669         sendFormated("AT+CSQ\r\n");
00670         if (RESP_OK != waitFinalResp(_cbCSQ, &_net))
00671             goto failure;
00672     }
00673     if (status) {
00674         memcpy(status, &_net, sizeof(NetStatus));
00675     }
00676     ok = REG_DONE(_net.csd) && 
00677         (REG_DONE(_net.psd) || REG_DONE(_net.eps));
00678     UNLOCK();
00679     return ok;
00680 failure:
00681     unlock();
00682     return false;
00683 }
00684 
00685 int MDMParser::_cbCOPS(int type, const char* buf, int len, NetStatus* status)
00686 {
00687     if ((type == TYPE_PLUS) && status){
00688         int act = 99;
00689         int mode = 99;
00690         // +COPS: <mode>[,<format>,<oper>[,<AcT>]]
00691        if (sscanf(buf, "\r\n+COPS: %d,%*d,\"%[^\"]\",%d",&mode,status->opr,&act) >= 1) {
00692             if      (act == 0) status->act = ACT_GSM;      // 0: GSM, 
00693             else if (act == 2) status->act = ACT_UTRAN;    // 2: UTRAN
00694             else if (act == 7) status->act = ACT_LTE;    // 2: UTRAN
00695             if (mode == 0)  status->regStatus = COPS_AUTOMATIC_REG;
00696             else if (mode == 1) status->regStatus = COPS_MANUAL_REG;
00697             else if (mode == 2) status->regStatus = COPS_DISABLED_REG;
00698         }
00699     }
00700     return WAIT;
00701 }
00702 
00703 int MDMParser::_cbCNUM(int type, const char* buf, int len, char* num)
00704 {
00705     if ((type == TYPE_PLUS) && num){
00706         int a;
00707         if ((sscanf(buf, "\r\n+CNUM: \"My Number\",\"%31[^\"]\",%d", num, &a) == 2) && 
00708             ((a == 129) || (a == 145))) {
00709         }
00710     }
00711     return WAIT;
00712 }
00713                     
00714 int MDMParser::_cbCSQ(int type, const char* buf, int len, NetStatus* status)
00715 {
00716     if ((type == TYPE_PLUS) && status){
00717         int a,b;
00718         char _ber[] = { 49, 43, 37, 25, 19, 13, 7, 0 }; // see 3GPP TS 45.008 [20] subclause 8.2.4
00719         // +CSQ: <rssi>,<qual>
00720         if (sscanf(buf, "\r\n+CSQ: %d,%d",&a,&b) == 2) {
00721             if (a != 99) status->rssi = -113 + 2*a;  // 0: -113 1: -111 ... 30: -53 dBm with 2 dBm steps, 31: >-51 dBm
00722             if ((b != 99) && (b < sizeof(_ber))) status->ber = _ber[b];  // 
00723         }
00724     }
00725     return WAIT;
00726 }
00727 
00728 
00729 int MDMParser::_cbUACTIND(int type, const char* buf, int len, int* i)
00730 {
00731     if ((type == TYPE_PLUS) && i){
00732         int a;
00733         if (sscanf(buf, "\r\n+UACTIND: %d", &a) == 1) {
00734             *i = a;
00735         }
00736     }
00737     return WAIT;
00738 }
00739 
00740 // ----------------------------------------------------------------
00741 // internet connection 
00742 
00743 bool MDMParser::_activateProfile(const char* apn, const char* username, const char* password, Auth auth)
00744 {
00745     // Set up the APN
00746     if (*apn) {
00747         sendFormated("AT+UPSD=" PROFILE ",1,\"%s\"\r\n", apn);
00748         if (RESP_OK != waitFinalResp())
00749             return false;
00750     }
00751     if (*username) {
00752         sendFormated("AT+UPSD=" PROFILE ",2,\"%s\"\r\n", username);
00753         if (RESP_OK != waitFinalResp())
00754             return false;
00755     }
00756     if (*password) {
00757         sendFormated("AT+UPSD=" PROFILE ",3,\"%s\"\r\n", password);
00758         if (RESP_OK != waitFinalResp())
00759             return false;
00760     }
00761     // Set up the dynamic IP address assignment.
00762     sendFormated("AT+UPSD=" PROFILE ",7,\"0.0.0.0\"\r\n");
00763     if (RESP_OK != waitFinalResp())
00764         return false;
00765     // try different Authentication Protocols
00766     // 0 = none 
00767     // 1 = PAP (Password Authentication Protocol)
00768     // 2 = CHAP (Challenge Handshake Authentication Protocol)
00769     for (int i = AUTH_NONE; i <= AUTH_CHAP; i ++) {
00770         if ((auth == AUTH_DETECT) || (auth == i)) {
00771             // Set up the Authentication Protocol
00772             sendFormated("AT+UPSD=" PROFILE ",6,%d\r\n",i);
00773             if (RESP_OK != waitFinalResp())
00774                 return false;
00775             // Activate the profile and make connection
00776             sendFormated("AT+UPSDA=" PROFILE ",3\r\n");
00777             if (RESP_OK == waitFinalResp(NULL,NULL,150*1000))
00778                 return true;
00779         }
00780     }
00781     return false;
00782 }
00783 
00784 bool MDMParser::_activateProfileReuseExternal(void)
00785 {
00786     int cid = -1;
00787     sendFormated("AT+CGDCONT?\r\n");
00788     if (RESP_OK != waitFinalResp(_cbCGDCONT, &cid))
00789         return false;
00790     if (cid == -1)
00791         return false;
00792     // we found a context that provides us a valid IP so lets reuse it for the internal IP stack
00793     sendFormated("AT+UPSD=" PROFILE ",100,%d\r\n", cid);
00794     if (RESP_OK != waitFinalResp())
00795         return false;
00796     // Activate the profile and make connection
00797     sendFormated("AT+UPSDA=" PROFILE ",3\r\n");
00798     return (RESP_OK == waitFinalResp(NULL,NULL,150*1000));
00799 }
00800 
00801 bool MDMParser::_activateProfileByCid(int cid, const char* apn, const char* username, const char* password, Auth auth)
00802 {
00803     sendFormated("AT+CGDCONT=%d,\"IP\",\"%s\"\r\n", cid, apn);
00804     if (RESP_OK != waitFinalResp())
00805         return false;
00806     sendFormated("AT+UAUTHREQ=%d,%d,\"%s\",\"%s\"\r\n", cid, auth, username, password);
00807     if (RESP_OK != waitFinalResp())
00808         return false;
00809     sendFormated("AT+UPSD=" PROFILE ",100,%d\r\n", cid);
00810     if (RESP_OK != waitFinalResp())
00811         return false;
00812     // Activate the profile and make connection
00813     sendFormated("AT+UPSDA=" PROFILE ",3\r\n");
00814     return (RESP_OK == waitFinalResp(NULL,NULL,150*1000));
00815 }
00816  
00817 int MDMParser::_cbCGDCONT(int type, const char* buf, int len, int* cid)
00818 {
00819     // accept with and without leading \r\n in +CGDCONT:
00820     if ((type == TYPE_PLUS) && (buf[0] == '\r') && (buf[1] == '\n') && (len >= 2))
00821         buf += 2, len -= 2, type = TYPE_UNKNOWN;
00822     if (type == TYPE_UNKNOWN) {
00823         int a,b,c,d,t;
00824         //+CGDCONT: <cid>,"IP","<apn name>","<ip adr>",0,0,0,0,0,0
00825         if (sscanf(buf, "+CGDCONT: %d,\"IP\",\"%*[^\"]\",\"" IPSTR "\",%*d,%*d,%*d,%*d,%*d,%*d", &t, &a,&b,&c,&d) == 5) {
00826             if (IPADR(a,b,c,d) != NOIP) 
00827                 *cid = t;
00828         }
00829     }
00830     return WAIT;
00831 }
00832 
00833 MDMParser::IP MDMParser::join(const char* apn /*= NULL*/, const char* username /*= NULL*/, 
00834                               const char* password /*= NULL*/, Auth auth /*= AUTH_DETECT*/)
00835 {
00836     LOCK();
00837     INFO("Modem::join\r\n");
00838     _ip = NOIP;
00839     if (_dev.dev == DEV_LISA_C2) {
00840         // make a dumy dns lookup (which will fail, so ignore the result) 
00841         sendFormated("AT+UDNSRN=0,\"u-blox.com\"\r\n");
00842         waitFinalResp(); 
00843         // This fake lookup will enable the IP connection and we 
00844         // should have an IP after this, so we check it
00845         
00846         //Get local IP address
00847         sendFormated("AT+CMIP?\r\n");
00848         if (RESP_OK != waitFinalResp(_cbCMIP, &_ip))
00849             goto failure;
00850     
00851     } else { 
00852         // check gprs attach status 
00853         sendFormated("AT+CGATT=1\r\n");
00854         if (RESP_OK != waitFinalResp(NULL,NULL,3*60*1000)) 
00855             goto failure;
00856         // Check the profile
00857         int a = 0;
00858         bool force = true;
00859         sendFormated("AT+UPSND=" PROFILE ",8\r\n");
00860         if (RESP_OK != waitFinalResp(_cbUPSND, &a))
00861             goto failure;
00862         if (a == 1 && force) {
00863             // disconnect the profile already if it is connected 
00864             sendFormated("AT+UPSDA=" PROFILE ",4\r\n");
00865             if (RESP_OK != waitFinalResp(NULL,NULL,40*1000))
00866                 goto failure;
00867             a = 0;
00868         }
00869         if (a == 0) {
00870             bool ok = false;
00871             // try to lookup the apn settings from our local database by mccmnc
00872             const char* config = NULL;
00873             if (!apn && !username && !password)
00874                 config = apnconfig(_dev.imsi);
00875             do {
00876                 if (config) {
00877                     apn      = _APN_GET(config);
00878                     username = _APN_GET(config);
00879                     password = _APN_GET(config);
00880                 }
00881                 // convert pointer to empty strings
00882                 apn      = apn      ? apn      : "";
00883                 username = username ? username : "";
00884                 password = password ? password : "";
00885                 auth = (*username && *password) ? auth : AUTH_NONE;
00886                 TRACE("Testing APN Settings(\"%s\",\"%s\",\"%s\",%d)\r\n", apn, username, password, auth);
00887                 if ((_dev.dev != DEV_TOBY_L2) && (_dev.dev != DEV_MPCI_L2))
00888                     ok = _activateProfile(apn, username, password, auth);
00889                 else {
00890                     ok = _activateProfileReuseExternal();
00891                     if (ok) 
00892                         TRACE("Reusing External Context\r\n");
00893                     else
00894                         ok = _activateProfileByCid(1, apn, username, password, auth);
00895                 }
00896             } while (!ok && config && *config); // maybe use next setting ? 
00897             if (!ok) {
00898                 ERROR("Your modem APN/password/username may be wrong\r\n");
00899                 goto failure;
00900             }
00901         }
00902         //Get local IP address
00903         sendFormated("AT+UPSND=" PROFILE ",0\r\n");
00904         if (RESP_OK != waitFinalResp(_cbUPSND, &_ip))
00905             goto failure;
00906     }
00907     UNLOCK();
00908     return _ip;
00909 failure: 
00910     unlock();
00911     return NOIP;
00912 }
00913 
00914 int MDMParser::_cbUDOPN(int type, const char* buf, int len, char* mccmnc)
00915 {
00916     if ((type == TYPE_PLUS) && mccmnc) {
00917         if (sscanf(buf, "\r\n+UDOPN: 0,\"%[^\"]\"", mccmnc) == 1)
00918             ;
00919     }
00920     return WAIT;
00921 }
00922 
00923 int MDMParser::_cbCMIP(int type, const char* buf, int len, IP* ip)
00924 {
00925     if ((type == TYPE_UNKNOWN) && ip) {
00926         int a,b,c,d;
00927         if (sscanf(buf, "\r\n" IPSTR, &a,&b,&c,&d) == 4)
00928             *ip = IPADR(a,b,c,d);
00929     }
00930     return WAIT;
00931 }
00932         
00933 int MDMParser::_cbUPSND(int type, const char* buf, int len, int* act)
00934 {
00935     if ((type == TYPE_PLUS) && act) {
00936         if (sscanf(buf, "\r\n+UPSND: %*d,%*d,%d", act) == 1)
00937             /*nothing*/;
00938     }
00939     return WAIT;
00940 }
00941 
00942 int MDMParser::_cbUPSND(int type, const char* buf, int len, IP* ip)
00943 {
00944     if ((type == TYPE_PLUS) && ip) {
00945         int a,b,c,d;
00946         // +UPSND=<profile_id>,<param_tag>[,<dynamic_param_val>]
00947         if (sscanf(buf, "\r\n+UPSND: " PROFILE ",0,\"" IPSTR "\"", &a,&b,&c,&d) == 4)
00948             *ip = IPADR(a,b,c,d);
00949     }
00950     return WAIT;
00951 }
00952 
00953 int MDMParser::_cbUDNSRN(int type, const char* buf, int len, IP* ip)
00954 {
00955     if ((type == TYPE_PLUS) && ip) {
00956         int a,b,c,d;
00957         if (sscanf(buf, "\r\n+UDNSRN: \"" IPSTR "\"", &a,&b,&c,&d) == 4)
00958             *ip = IPADR(a,b,c,d);
00959     }
00960     return WAIT;
00961 }
00962 
00963 bool MDMParser::disconnect(void)
00964 {
00965     bool ok = false;
00966     LOCK();
00967     INFO("Modem::disconnect\r\n");
00968     if (_ip != NOIP) {
00969         if (_dev.dev == DEV_LISA_C2) {
00970             // There something to do here
00971             _ip = NOIP;
00972             ok = true;
00973         } else { 
00974             sendFormated("AT+UPSDA=" PROFILE ",4\r\n");
00975             if (RESP_OK != waitFinalResp()) {
00976                 _ip = NOIP;
00977                 ok = true;
00978             }
00979         }
00980     }
00981     UNLOCK();
00982     return ok;
00983 }
00984 
00985 MDMParser::IP MDMParser::gethostbyname(const char* host)
00986 {
00987     IP ip = NOIP; 
00988     int a,b,c,d;
00989     if (sscanf(host, IPSTR, &a,&b,&c,&d) == 4)
00990         ip = IPADR(a,b,c,d);
00991     else {
00992         LOCK();
00993         sendFormated("AT+UDNSRN=0,\"%s\"\r\n", host);
00994         if (RESP_OK != waitFinalResp(_cbUDNSRN, &ip))
00995             ip = NOIP;
00996         UNLOCK();
00997     }
00998     return ip;
00999 }
01000 
01001 // ----------------------------------------------------------------
01002 // sockets
01003 
01004 int MDMParser::_cbUSOCR(int type, const char* buf, int len, int* handle)
01005 {
01006     if ((type == TYPE_PLUS) && handle) {
01007         // +USOCR: socket
01008         if (sscanf(buf, "\r\n+USOCR: %d", handle) == 1)
01009             /*nothing*/;  
01010     }
01011     return WAIT;
01012 }
01013 
01014 int MDMParser::socketSocket(IpProtocol ipproto, int port)
01015 {
01016     int socket;
01017     LOCK();
01018     // find an free socket
01019     socket = _findSocket();
01020     TRACE("socketSocket(%d)\r\n", ipproto);
01021     if (socket != SOCKET_ERROR) {
01022         if (ipproto == IPPROTO_UDP) {
01023             // sending port can only be set on 2G/3G modules
01024             if ((port != -1) && (_dev.dev != DEV_LISA_C2)) {
01025                 sendFormated("AT+USOCR=17,%d\r\n", port);
01026             } else {
01027                 sendFormated("AT+USOCR=17\r\n");
01028             }
01029         } else /*(ipproto == IPPROTO_TCP)*/ {
01030             sendFormated("AT+USOCR=6\r\n");
01031         } 
01032         int handle = SOCKET_ERROR;
01033         if ((RESP_OK == waitFinalResp(_cbUSOCR, &handle)) && 
01034             (handle != SOCKET_ERROR)) {
01035             TRACE("Socket %d: handle %d was created\r\n", socket, handle);
01036             _sockets[socket].handle     = handle;
01037             _sockets[socket].timeout_ms = TIMEOUT_BLOCKING;
01038             _sockets[socket].connected  = false;
01039             _sockets[socket].pending    = 0;
01040         }
01041         else
01042             socket = SOCKET_ERROR;
01043     }
01044     UNLOCK();
01045     return socket;
01046 }
01047 
01048 bool MDMParser::socketConnect(int socket, const char * host, int port)
01049 {
01050     IP ip = gethostbyname(host);
01051     if (ip == NOIP)
01052         return false;
01053     // connect to socket
01054     bool ok = false; 
01055     LOCK();
01056     if (ISSOCKET(socket) && (!_sockets[socket].connected)) {
01057         TRACE("socketConnect(%d,%s,%d)\r\n", socket,host,port);
01058         sendFormated("AT+USOCO=%d,\"" IPSTR "\",%d\r\n", _sockets[socket].handle, IPNUM(ip), port);
01059         if (RESP_OK == waitFinalResp())
01060             ok = _sockets[socket].connected = true;
01061     }
01062     UNLOCK();
01063     return ok;
01064 }
01065 
01066 bool MDMParser::socketIsConnected(int socket)
01067 {
01068     bool ok = false;
01069     LOCK();
01070     ok = ISSOCKET(socket) && _sockets[socket].connected;
01071     TRACE("socketIsConnected(%d) %s\r\n", socket, ok?"yes":"no");
01072     UNLOCK();
01073     return ok;
01074 }
01075 
01076 bool MDMParser::socketSetBlocking(int socket, int timeout_ms)
01077 {
01078     bool ok = false;
01079     LOCK();
01080     TRACE("socketSetBlocking(%d,%d)\r\n", socket,timeout_ms);
01081     if (ISSOCKET(socket)) {
01082         _sockets[socket].timeout_ms = timeout_ms;
01083         ok = true;
01084     }
01085     UNLOCK();
01086     return ok;
01087 }
01088 
01089 bool  MDMParser::socketClose(int socket)
01090 {
01091     bool ok = false;
01092     LOCK();
01093     if (ISSOCKET(socket) && _sockets[socket].connected) {
01094         TRACE("socketClose(%d)\r\n", socket);
01095         sendFormated("AT+USOCL=%d\r\n", _sockets[socket].handle);
01096         if (RESP_OK == waitFinalResp()) {
01097             _sockets[socket].connected = false;
01098             ok = true;
01099         }
01100     }
01101     UNLOCK();
01102     return ok;
01103 }
01104 
01105 bool  MDMParser::socketFree(int socket)
01106 {
01107     // make sure it is closed
01108     socketClose(socket);
01109     bool ok = true;
01110     LOCK();
01111     if (ISSOCKET(socket)) {
01112         TRACE("socketFree(%d)\r\n",  socket);
01113         _sockets[socket].handle     = SOCKET_ERROR;
01114         _sockets[socket].timeout_ms = TIMEOUT_BLOCKING;
01115         _sockets[socket].connected  = false;
01116         _sockets[socket].pending    = 0;
01117         ok = true;
01118     }
01119     UNLOCK();
01120     return ok;
01121 }
01122 
01123 #define USO_MAX_WRITE 1024 //!< maximum number of bytes to write to socket
01124 
01125 int MDMParser::socketSend(int socket, const char * buf, int len)
01126 {
01127     TRACE("socketSend(%d,,%d)\r\n", socket,len);
01128     int cnt = len;
01129     while (cnt > 0) {
01130         int blk = USO_MAX_WRITE;
01131         if (cnt < blk) 
01132             blk = cnt;
01133         bool ok = false;
01134         LOCK();
01135         if (ISSOCKET(socket)) {
01136             sendFormated("AT+USOWR=%d,%d\r\n",_sockets[socket].handle,blk);
01137             if (RESP_PROMPT == waitFinalResp()) {
01138                 wait_ms(50);
01139                 send(buf, blk);
01140                 if (RESP_OK == waitFinalResp()) 
01141                     ok = true;
01142             }
01143         }
01144         UNLOCK();
01145         if (!ok) 
01146             return SOCKET_ERROR;
01147         buf += blk;
01148         cnt -= blk;
01149     }
01150     return (len - cnt);
01151 }
01152 
01153 int MDMParser::socketSendTo(int socket, IP ip, int port, const char * buf, int len)
01154 {
01155     TRACE("socketSendTo(%d," IPSTR ",%d,,%d)\r\n", socket,IPNUM(ip),port,len);
01156     int cnt = len;
01157     while (cnt > 0) {
01158         int blk = USO_MAX_WRITE;
01159         if (cnt < blk) 
01160             blk = cnt;
01161         bool ok = false;
01162         LOCK();
01163         if (ISSOCKET(socket)) {
01164             sendFormated("AT+USOST=%d,\"" IPSTR "\",%d,%d\r\n",_sockets[socket].handle,IPNUM(ip),port,blk);
01165             if (RESP_PROMPT == waitFinalResp()) {
01166                 wait_ms(50);
01167                 send(buf, blk);
01168                 if (RESP_OK == waitFinalResp())
01169                     ok = true;
01170             }
01171         }
01172         UNLOCK();
01173         if (!ok)
01174             return SOCKET_ERROR;
01175         buf += blk;
01176         cnt -= blk;
01177     }
01178     return (len - cnt);
01179 }
01180 
01181 int MDMParser::socketReadable(int socket)
01182 {
01183     int pending = SOCKET_ERROR;
01184     LOCK();
01185     if (ISSOCKET(socket) && _sockets[socket].connected) {
01186         TRACE("socketReadable(%d)\r\n", socket);
01187         // allow to receive unsolicited commands 
01188         waitFinalResp(NULL, NULL, 0);
01189         if (_sockets[socket].connected)
01190            pending = _sockets[socket].pending; 
01191     }
01192     UNLOCK();
01193     return pending;
01194 }
01195 
01196 int MDMParser::_cbUSORD(int type, const char* buf, int len, char* out)
01197 {
01198     if ((type == TYPE_PLUS) && out) {
01199         int sz, sk;
01200         if ((sscanf(buf, "\r\n+USORD: %d,%d,", &sk, &sz) == 2) && 
01201             (buf[len-sz-2] == '\"') && (buf[len-1] == '\"')) {
01202             memcpy(out, &buf[len-1-sz], sz);
01203         }
01204     }
01205     return WAIT;
01206 }
01207 
01208 int MDMParser::socketRecv(int socket, char* buf, int len)
01209 {
01210     int cnt = 0;
01211     TRACE("socketRecv(%d,,%d)\r\n", socket, len);
01212 #ifdef MDM_DEBUG
01213     memset(buf, '\0', len);
01214 #endif
01215     Timer timer;
01216     timer.start();
01217     while (len) {
01218         int blk = MAX_SIZE; // still need space for headers and unsolicited  commands 
01219         if (len < blk) blk = len;
01220         bool ok = false;        
01221         LOCK();
01222         if (ISSOCKET(socket)) {
01223             if (_sockets[socket].connected) {
01224                 if (_sockets[socket].pending < blk)
01225                     blk = _sockets[socket].pending;
01226                 if (blk > 0) {
01227                     sendFormated("AT+USORD=%d,%d\r\n",_sockets[socket].handle, blk);
01228                     if (RESP_OK == waitFinalResp(_cbUSORD, buf)) {
01229                         _sockets[socket].pending -= blk;
01230                         len -= blk;
01231                         cnt += blk;
01232                         buf += blk;
01233                         ok = true;
01234                     }
01235                 } else if (!TIMEOUT(timer, _sockets[socket].timeout_ms)) {
01236                     ok = (WAIT == waitFinalResp(NULL,NULL,0)); // wait for URCs
01237                 } else {
01238                     len = 0;
01239                     ok = true;
01240                 }
01241             } else {
01242                 len = 0;
01243                 ok = true;
01244             }
01245         }
01246         UNLOCK();
01247         if (!ok) {
01248             TRACE("socketRecv: ERROR\r\n");
01249             return SOCKET_ERROR;
01250         }
01251     }
01252     TRACE("socketRecv: %d \"%*s\"\r\n", cnt, cnt, buf-cnt);
01253     return cnt;
01254 }
01255 
01256 int MDMParser::_cbUSORF(int type, const char* buf, int len, USORFparam* param)
01257 {
01258     if ((type == TYPE_PLUS) && param) {
01259         int sz, sk, p, a,b,c,d;
01260         int r = sscanf(buf, "\r\n+USORF: %d,\"" IPSTR "\",%d,%d,", 
01261             &sk,&a,&b,&c,&d,&p,&sz);
01262         if ((r == 7) && (buf[len-sz-2] == '\"') && (buf[len-1] == '\"')) {
01263             memcpy(param->buf, &buf[len-1-sz], sz);
01264             param->ip = IPADR(a,b,c,d);
01265             param->port = p;
01266         }
01267     }
01268     return WAIT;
01269 }
01270 
01271 int MDMParser::socketRecvFrom(int socket, IP* ip, int* port, char* buf, int len)
01272 {
01273     int cnt = 0;
01274     TRACE("socketRecvFrom(%d,,%d)\r\n", socket, len);
01275 #ifdef MDM_DEBUG
01276     memset(buf, '\0', len);
01277 #endif
01278     Timer timer;
01279     timer.start();
01280     while (len) {
01281         int blk = MAX_SIZE; // still need space for headers and unsolicited commands 
01282         if (len < blk) blk = len;
01283         bool ok = false;        
01284         LOCK();
01285         if (ISSOCKET(socket)) {
01286             if (_sockets[socket].pending < blk)
01287                 blk = _sockets[socket].pending;
01288             if (blk > 0) {
01289                 sendFormated("AT+USORF=%d,%d\r\n",_sockets[socket].handle, blk);
01290                 USORFparam param;
01291                 param.buf = buf;
01292                 if (RESP_OK == waitFinalResp(_cbUSORF, &param)) {
01293                     _sockets[socket].pending -= blk;
01294                     *ip = param.ip;
01295                     *port = param.port;
01296                     len -= blk;
01297                     cnt += blk;
01298                     buf += blk;
01299                     len = 0; // done 
01300                     ok = true;
01301                 }
01302             } else if (!TIMEOUT(timer, _sockets[socket].timeout_ms)) {
01303                 ok = (WAIT == waitFinalResp(NULL,NULL,0)); // wait for URCs
01304             } else {
01305                 len = 0; // no more data and socket closed or timed-out
01306                 ok = true;
01307             }
01308         }
01309         UNLOCK();
01310         if (!ok) {
01311             TRACE("socketRecv: ERROR\r\n");
01312             return SOCKET_ERROR;
01313         }
01314     }
01315     timer.stop();
01316     timer.reset();
01317     TRACE("socketRecv: %d \"%*s\"\r\n", cnt, cnt, buf-cnt);
01318     return cnt;
01319 }
01320 
01321 int MDMParser::_findSocket(int handle) {
01322     for (int socket = 0; socket < NUMSOCKETS; socket ++) {
01323         if (_sockets[socket].handle == handle)
01324             return socket;
01325     }
01326     return SOCKET_ERROR;
01327 }
01328 
01329 // ----------------------------------------------------------------
01330 // HTTP
01331 
01332 int MDMParser::httpFindProfile()
01333 {
01334     int profile = HTTP_PROF_ERROR;  //default value
01335     LOCK();
01336     // find a free HTTP profile 
01337     profile = _findProfile();
01338     TRACE("httpFindProfile: profile is %d\r\n", profile);
01339     if (profile != HTTP_PROF_ERROR) {
01340         _httpProfiles[profile].handle     = 1;
01341         _httpProfiles[profile].timeout_ms = TIMEOUT_BLOCKING;
01342         _httpProfiles[profile].pending    = false;
01343         _httpProfiles[profile].cmd        = -1;
01344         _httpProfiles[profile].result     = -1;
01345     }
01346     UNLOCK();
01347     return profile;
01348 }
01349 
01350 int MDMParser::_findProfile(int handle) {
01351     for (int profile = 0; profile < NUMPROFILES; profile++) {
01352         if (_httpProfiles[profile].handle == handle)
01353             return profile;
01354     }
01355     return HTTP_PROF_ERROR;
01356 }
01357 
01358 bool MDMParser::httpSetBlocking(int profile, int timeout_ms)
01359 {
01360     bool ok = false;
01361     LOCK();
01362     TRACE("httpSetBlocking(%d,%d)\r\n", profile, timeout_ms);
01363     if (ISPROFILE(profile)) {
01364         _httpProfiles[profile].timeout_ms = timeout_ms;
01365         ok = true;
01366     }
01367     UNLOCK();
01368     return ok;
01369 }
01370 
01371 bool MDMParser::httpSetProfileForCmdMng(int profile)
01372 {
01373     bool ok = false;
01374     LOCK();
01375     TRACE("httpSetProfileForCmdMng(%d)\r\n", profile);
01376     if (ISPROFILE(profile)) {
01377         _httpProfiles[profile].pending = true;
01378         _httpProfiles[profile].result = -1;
01379         ok = true;
01380     }
01381     UNLOCK();
01382     return ok;
01383 }
01384 
01385 bool MDMParser::httpFreeProfile(int profile)
01386 {
01387     bool ok = true;
01388     LOCK();
01389     if (ISPROFILE(profile)) {
01390         TRACE("httpFreeProfile(%d)\r\n", profile);
01391         _httpProfiles[profile].handle     = HTTP_PROF_ERROR;
01392         _httpProfiles[profile].timeout_ms = TIMEOUT_BLOCKING;
01393         _httpProfiles[profile].pending    = false;
01394         _httpProfiles[profile].cmd        = -1;
01395         _httpProfiles[profile].result     = -1;
01396         ok = true;
01397     }
01398     UNLOCK();
01399     return ok;
01400 }
01401 
01402 bool MDMParser::httpResetProfile(int httpProfile)
01403 {
01404     bool ok = false;
01405     
01406     LOCK();
01407     TRACE("httpResetProfile(%d)\r\n", httpProfile);
01408     sendFormated("AT+UHTTP=%d\r\n", httpProfile);
01409     if (RESP_OK == waitFinalResp())
01410         ok = true;
01411     UNLOCK();
01412     
01413     return ok;
01414 }
01415 
01416 bool MDMParser::httpSetPar(int httpProfile, HttpOpCode httpOpCode, const char * httpInPar)
01417 {
01418     bool ok = false;
01419     IP ip = NOIP;
01420     int httpInParNum = 0;
01421     
01422     LOCK();
01423     TRACE("httpSetPar(%d,%d,\"%s\")\r\n", httpProfile, httpOpCode, httpInPar);
01424     switch(httpOpCode){
01425         case HTTP_IP_ADDRESS:   //0
01426             ip = gethostbyname(httpInPar);
01427             if (ip == NOIP)
01428                 return false;
01429             
01430             sendFormated("AT+UHTTP=%d,%d,\"" IPSTR "\"\r\n", httpProfile, httpOpCode, IPNUM(ip));
01431             if (RESP_OK == waitFinalResp())
01432                 ok = true;
01433             break;
01434             
01435         case HTTP_SERVER_NAME:  //1
01436         case HTTP_USER_NAME:    //2
01437         case HTTP_PASSWORD:     //3
01438             sendFormated("AT+UHTTP=%d,%d,\"%s\"\r\n", httpProfile, httpOpCode, httpInPar);
01439             if (RESP_OK == waitFinalResp())
01440                 ok = true;
01441             break;
01442         
01443         case HTTP_AUTH_TYPE:    //4    
01444         case HTTP_PORT:         //5
01445             httpInParNum = atoi(httpInPar);
01446             sendFormated("AT+UHTTP=%d,%d,%d\r\n", httpProfile, httpOpCode, httpInParNum); 
01447             if (RESP_OK == waitFinalResp())
01448                 ok = true;
01449             break;
01450             
01451         case HTTP_SECURE:       //6
01452             if(_dev.dev != DEV_LISA_C2)
01453             {
01454                 httpInParNum = atoi(httpInPar);
01455                 sendFormated("AT+UHTTP=%d,%d,%d\r\n", httpProfile, httpOpCode, httpInParNum); 
01456                 if (RESP_OK == waitFinalResp())
01457                     ok = true;
01458             } else {
01459                 TRACE("httpSetPar: HTTP secure option not supported by module\r\n");
01460                 ok = false;
01461             }
01462             break;
01463             
01464         default:
01465             TRACE("httpSetPar: unknown httpOpCode %s\r\n", httpOpCode);
01466             ok = false; 
01467             break;   
01468     }
01469     UNLOCK();
01470     return ok;
01471 }
01472 
01473 bool MDMParser::httpCommand(int httpProfile, HttpCmd httpCmdCode, const char* httpPath, const char* httpOut, \
01474                             const char* httpIn, int httpContentType, const char* httpCustomPar, char* buf, int len)
01475 {   
01476     bool ok = false;
01477 #ifdef MDM_DEBUG
01478     memset(buf, '\0', len);
01479 #endif
01480     LOCK();
01481     TRACE("%s\r\n", getHTTPcmd(httpCmdCode));
01482     switch (httpCmdCode) 
01483     {   
01484         case HTTP_HEAD:
01485             sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\"\r\n", httpProfile, HTTP_HEAD, httpPath, httpOut);
01486             break;
01487             
01488         case HTTP_GET:
01489             sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\"\r\n", httpProfile, HTTP_GET, httpPath, httpOut);
01490             break;
01491             
01492         case HTTP_DELETE:
01493             sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\"\r\n", httpProfile, HTTP_DELETE, httpPath, httpOut);
01494             break;
01495             
01496         case HTTP_PUT:
01497             //in this case the parameter httpIn is a filename
01498             sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\"\r\n", httpProfile, HTTP_PUT, httpPath, httpOut, httpIn);
01499             break;
01500             
01501         case HTTP_POST_FILE:
01502             //in this case the parameter httpIn is a filename
01503             if(_dev.dev != DEV_LISA_C2)
01504             {
01505                 if(httpContentType != 6)
01506                     sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d\r\n", \
01507                                   httpProfile, HTTP_POST_FILE, httpPath, httpOut, httpIn, httpContentType);
01508                 else
01509                     sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d,%d\r\n", \
01510                                   httpProfile, HTTP_POST_FILE, httpPath, httpOut, httpIn, httpContentType, httpCustomPar);
01511             }
01512             else{
01513                 if((httpContentType != 5) && (httpContentType != 6) && (httpCustomPar == NULL))
01514                 {
01515                     //parameters values consistent with the AT commands specs of LISA-C200
01516                     //(in particular httpCustomPar has to be not defined)
01517                     sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d\r\n", \
01518                                   httpProfile, HTTP_POST_FILE, httpPath, httpOut, httpIn, httpContentType);
01519                 } else {
01520                     TRACE("httpCommand: command not supported by module");
01521                     return ok;  //error
01522                 }
01523             }
01524             break;
01525             
01526         case HTTP_POST_DATA:
01527             //in this case the parameter httpIn is a string containing data
01528             if(_dev.dev != DEV_LISA_C2)
01529             {
01530                 if(httpContentType != 6)
01531                     sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d\r\n", \
01532                                   httpProfile, HTTP_POST_DATA, httpPath, httpOut, httpIn, httpContentType);
01533                 else
01534                     sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d,%d\r\n", \
01535                                   httpProfile, HTTP_POST_DATA, httpPath, httpOut, httpIn, httpContentType, httpCustomPar);
01536             } else {
01537                 if((httpContentType != 5) && (httpContentType != 6) && (httpCustomPar == NULL))
01538                 {
01539                     //parameters values consistent with the AT commands specs of LISA-C200
01540                     //(in particular httpCustomPar has to be not defined)
01541                     sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d\r\n", \
01542                                   httpProfile, HTTP_POST_DATA, httpPath, httpOut, httpIn, httpContentType);
01543                 } else {
01544                     TRACE("httpCommand: command not supported by module");
01545                     return ok;  //error
01546                 }
01547             }    
01548             break;
01549             
01550         default:
01551             TRACE("HTTP command not recognized\r\n");
01552             return ok;  //error
01553     }
01554     
01555     if (RESP_OK == waitFinalResp())
01556     {
01557         Timer timer;
01558         timer.start();
01559         httpSetProfileForCmdMng(httpProfile);
01560         while (_httpProfiles[httpProfile].pending)  //waiting for unsolicited
01561         {     
01562             ok = false;  //reset variable  
01563             if(_httpProfiles[httpProfile].result != -1)
01564             {    
01565                 //received unsolicited: starting its analysis 
01566                 _httpProfiles[httpProfile].pending = false;   
01567                 if(_httpProfiles[httpProfile].result == 1)
01568                 {
01569                     //HTTP command successfully executed
01570                     if(_dev.dev != DEV_LISA_C2)
01571                     {
01572                         TRACE("httpCommand: reading files with a dimension " \
01573                               "also greater than MAX_SIZE bytes\r\n");
01574                         if(readFileNew(httpOut,buf,len) >=0 )
01575                             ok = true;
01576                     } else {
01577                         TRACE("httpCommand: reading files with a dimension " \
01578                               "less than MAX_SIZE bytes, otherwise error\r\n");
01579                         if(readFile(httpOut,buf,len) >=0 )
01580                             ok = true;
01581                     }
01582                 } else {
01583                     //HTTP command not successfully executed
01584                     ok = false;
01585                 }
01586             } else if (!TIMEOUT(timer, _httpProfiles[httpProfile].timeout_ms)) {
01587                 ok = (WAIT == waitFinalResp(NULL,NULL,0)); // wait for URCs
01588             } else  {
01589                 //not received unsolicited and expired timer
01590                 TRACE("httpCommand: not received unsolicited and expired timer\r\n");
01591                 ok = false;
01592             }
01593             if (!ok) {
01594                 TRACE("%s: ERROR\r\n", getHTTPcmd(httpCmdCode));
01595                 _httpProfiles[httpProfile].pending = false;  //no more while loops
01596             }
01597         }
01598     }
01599     UNLOCK();
01600     return ok;
01601 }
01602 
01603 const char* MDMParser::getHTTPcmd(int httpCmdCode) 
01604 {
01605     switch (httpCmdCode) 
01606     {
01607         case HTTP_HEAD:
01608             return "HTTP HEAD command";
01609         case HTTP_GET:
01610             return "HTTP GET command";
01611         case HTTP_DELETE:
01612             return "HTTP DELETE command";
01613         case HTTP_PUT:
01614             return "HTTP PUT command";
01615         case HTTP_POST_FILE:
01616             return "HTTP POST file command";
01617         case HTTP_POST_DATA:
01618             return "HTTP POST data command";
01619         default:
01620             return "HTTP command not recognized";
01621    }
01622 }
01623 
01624 // ----------------------------------------------------------------
01625 
01626 int MDMParser::_cbCMGL(int type, const char* buf, int len, CMGLparam* param)
01627 { 
01628     if ((type == TYPE_PLUS) && param && param->num) {
01629         // +CMGL: <ix>,...
01630         int ix;
01631         if (sscanf(buf, "\r\n+CMGL: %d,", &ix) == 1)
01632         {
01633             *param->ix++ = ix;
01634             param->num--;
01635         }
01636     }
01637     return WAIT;
01638 }
01639 
01640 int MDMParser::smsList(const char* stat /*= "ALL"*/, int* ix /*=NULL*/, int num /*= 0*/) {
01641     int ret = -1;
01642     LOCK();
01643     sendFormated("AT+CMGL=\"%s\"\r\n", stat);
01644     CMGLparam param;
01645     param.ix = ix;
01646     param.num = num;
01647     if (RESP_OK == waitFinalResp(_cbCMGL, &param))
01648         ret = num - param.num;
01649     UNLOCK();
01650     return ret;
01651 }
01652 
01653 bool MDMParser::smsSend(const char* num, const char* buf)
01654 {
01655     bool ok = false;
01656     LOCK();
01657     sendFormated("AT+CMGS=\"%s\"\r\n",num);
01658     if (RESP_PROMPT == waitFinalResp(NULL,NULL,150*1000)) {
01659         send(buf, strlen(buf));
01660         const char ctrlZ = 0x1A;
01661         send(&ctrlZ, sizeof(ctrlZ));
01662         ok = (RESP_OK == waitFinalResp());
01663     }
01664     UNLOCK();
01665     return ok;
01666 }
01667 
01668 bool MDMParser::smsDelete(int ix)
01669 {
01670     bool ok = false;
01671     LOCK();
01672     sendFormated("AT+CMGD=%d\r\n",ix);
01673     ok = (RESP_OK == waitFinalResp());
01674     UNLOCK();
01675     return ok;
01676 }
01677 
01678 int MDMParser::_cbCMGR(int type, const char* buf, int len, CMGRparam* param)
01679 {
01680     if (param) {
01681         if (type == TYPE_PLUS) {
01682             if (sscanf(buf, "\r\n+CMGR: \"%*[^\"]\",\"%[^\"]", param->num) == 1) {
01683             }
01684         } else if ((type == TYPE_UNKNOWN) && (buf[len-2] == '\r') && (buf[len-1] == '\n')) {
01685             memcpy(param->buf, buf, len-2);
01686             param->buf[len-2] = '\0';
01687         }
01688     }
01689     return WAIT;
01690 }
01691 
01692 bool MDMParser::smsRead(int ix, char* num, char* buf, int len)
01693 {
01694     bool ok = false;
01695     LOCK();
01696     CMGRparam param;
01697     param.num = num;
01698     param.buf = buf;
01699     sendFormated("AT+CMGR=%d\r\n",ix);
01700     ok = (RESP_OK == waitFinalResp(_cbCMGR, &param));
01701     UNLOCK();
01702     return ok;
01703 }
01704    
01705 // ----------------------------------------------------------------
01706   
01707 int MDMParser::_cbCUSD(int type, const char* buf, int len, char* resp)
01708 {
01709     if ((type == TYPE_PLUS) && resp) {
01710         // +USD: \"%*[^\"]\",\"%[^\"]\",,\"%*[^\"]\",%d,%d,%d,%d,\"*[^\"]\",%d,%d"..);
01711         if (sscanf(buf, "\r\n+CUSD: %*d,\"%[^\"]\",%*d", resp) == 1) {
01712             /*nothing*/            
01713         }
01714     }
01715     return WAIT;
01716 }  
01717 
01718 bool MDMParser::ussdCommand(const char* cmd, char* buf)
01719 {
01720     bool ok = false;
01721     LOCK();
01722     *buf = '\0';
01723     if (_dev.dev != DEV_LISA_C2) {
01724         sendFormated("AT+CUSD=1,\"%s\"\r\n",cmd);
01725         ok = (RESP_OK == waitFinalResp(_cbCUSD, buf));
01726     }
01727     UNLOCK();
01728     return ok;
01729 }
01730 
01731 // ----------------------------------------------------------------
01732    
01733 int MDMParser::_cbUDELFILE(int type, const char* buf, int len, void*)
01734 {
01735     if ((type == TYPE_ERROR) && strstr(buf, "+CME ERROR: FILE NOT FOUND")) 
01736         return RESP_OK; // file does not exist, so all ok...
01737     return WAIT;
01738 }  
01739 
01740 bool MDMParser::delFile(const char* filename)
01741 {
01742     bool ok = false;
01743     LOCK();
01744     sendFormated("AT+UDELFILE=\"%s\"\r\n", filename);
01745     ok = (RESP_OK == waitFinalResp(_cbUDELFILE));
01746     UNLOCK();
01747     return ok;
01748 }
01749 
01750 int MDMParser::writeFile(const char* filename, const char* buf, int len)
01751 {
01752     bool ok = false;
01753     LOCK();
01754     sendFormated("AT+UDWNFILE=\"%s\",%d\r\n", filename, len);
01755     if (RESP_PROMPT == waitFinalResp()) {
01756         send(buf, len);
01757         ok = (RESP_OK == waitFinalResp());
01758     }
01759     UNLOCK();
01760     return ok ? len : -1;
01761 }
01762 
01763 int MDMParser::readFile(const char* filename, char* buf, int len)
01764 {
01765     URDFILEparam param;
01766     param.filename = filename;
01767     param.buf = buf; 
01768     param.sz = len; 
01769     param.len = 0;
01770     LOCK();
01771     sendFormated("AT+URDFILE=\"%s\"\r\n", filename, len);
01772     if (RESP_OK != waitFinalResp(_cbURDFILE, &param))
01773         param.len = -1;
01774     UNLOCK();
01775     return param.len;
01776 }
01777 
01778 int MDMParser::_cbURDFILE(int type, const char* buf, int len, URDFILEparam* param)
01779 {
01780     if ((type == TYPE_PLUS) && param && param->filename && param->buf) {
01781         char filename[48];
01782         int sz;
01783         if ((sscanf(buf, "\r\n+URDFILE: \"%[^\"]\",%d,", filename, &sz) == 2) && 
01784             (0 == strcmp(param->filename, filename)) &&
01785             (buf[len-sz-2] == '\"') && (buf[len-1] == '\"')) {
01786             param->len = (sz < param->sz) ? sz : param->sz;
01787             memcpy(param->buf, &buf[len-1-sz], param->len);
01788         }
01789     }
01790     return WAIT;
01791 }
01792 
01793 //The following function is useful for reading files with a dimension greater than MAX_SIZE bytes
01794 int MDMParser::readFileNew(const char* filename, char* buf, int len)
01795 {   
01796     int countBytes = -1;  //counter for file reading (default value)
01797     
01798     if(_dev.dev != DEV_LISA_C2)
01799     {
01800         //retrieve information about the file, in particular its size
01801         int filesize = infoFile(filename);
01802         TRACE("readFileNew: filename is %s; filesize is %d\r\n", filename, filesize);
01803         
01804         if (len < filesize)
01805             TRACE("readFileNew: WARNING. Buffer dimension is %d bytes," \
01806                   "while file size is %d bytes\r\n", len, filesize);
01807         
01808         if (filesize > 0)
01809         {
01810 #ifdef MDM_DEBUG
01811             memset(buf, '\0', len);
01812 #endif
01813             int offset = 0;              //start reading from 0
01814             int blockSize = MAX_SIZE;    //still need space for headers and unsolicited commands
01815             int bytesToRead = filesize;  //bytes to read 
01816             
01817             while (bytesToRead)
01818             {    
01819                 bool ok = false;
01820                 
01821                 if (bytesToRead < blockSize)
01822                     blockSize = bytesToRead;
01823                 
01824                 LOCK();
01825                 if (blockSize > 0) {
01826                             
01827                     sendFormated("AT+URDBLOCK=\"%s\",%d,%d\r\n", filename, offset, blockSize);
01828                     
01829                     if (RESP_OK == waitFinalResp(_cbURDBLOCK, buf)) {
01830                         bytesToRead -= blockSize;
01831                         offset += blockSize;
01832                         buf += blockSize;
01833                         ok = true;
01834                     } else {
01835                         //error condition
01836                         countBytes = -1;
01837                         ok = false;
01838                     }
01839                 }
01840                 UNLOCK();
01841                 
01842                 if (!ok) {
01843                     TRACE("readFileNew: ERROR\r\n");
01844                     return countBytes;  //in this case countBytes is -1
01845                 }
01846             }
01847             
01848             countBytes = offset;  //total read bytes
01849             return countBytes;
01850         }
01851     } else {
01852         TRACE("httpCommand: command not supported by module"); 
01853     }
01854     return countBytes;  //it could be 0 or -1 (possible error)    
01855 }
01856 
01857 int MDMParser::_cbURDBLOCK(int type, const char* buf, int len, char* out)
01858 {   
01859     char fileNameRes[48]; 
01860     int sizeRes;
01861     
01862     if ((type == TYPE_PLUS) && out) {
01863         if ((sscanf(buf, "\r\n+URDBLOCK: \"%[^\"]\",%d,", fileNameRes, &sizeRes) == 2) &&
01864             (buf[len-sizeRes-2] == '\"') && (buf[len-1] == '\"')) {
01865             memcpy(out, &buf[len-1-sizeRes], sizeRes);
01866         }
01867     }
01868     
01869     return WAIT;
01870 }
01871 
01872 int MDMParser::infoFile(const char* filename)
01873 {
01874     int infoFile = 0;  //default value
01875     
01876     LOCK();
01877     sendFormated("AT+ULSTFILE=2,\"%s\"\r\n", filename);
01878     if (RESP_OK != waitFinalResp(_cbULSTFILE, &infoFile))
01879         infoFile = -1;  //error condition    
01880     UNLOCK();
01881     
01882     return infoFile;
01883 }
01884 
01885 int MDMParser::_cbULSTFILE(int type, const char* buf, int len, int* infoFile)
01886 { 
01887     if (infoFile) {
01888         if (type == TYPE_PLUS) {
01889             if (sscanf(buf, "\r\n+ULSTFILE: %d\r\n", infoFile) == 1) {
01890             }
01891         }
01892     }
01893     return WAIT;
01894 }
01895 
01896 // ----------------------------------------------------------------
01897 
01898 int MDMParser::cellLocSrvHttp (const char* token, const char* server_1, const char* server_2, int days/* = 14*/, \
01899         int period/* = 4*/, int resolution/* = 1*/)
01900 {
01901     bool ok = false;
01902     LOCK();
01903     if (_dev.dev == DEV_LISA_U2_03S || _dev.dev == DEV_SARA_U2 ){
01904         sendFormated("AT+UGSRV=\"%s\",\"%s\",\"%s\"\r\n", server_1, server_2, token, days, period, resolution);
01905         ok = (RESP_OK == waitFinalResp());
01906     } else
01907         ok = false; //command not supported by module
01908     UNLOCK();
01909     return ok;
01910 }
01911 
01912 int MDMParser::cellLocSrvUdp(const char* server_1 /*= "cell-live1.services.u-blox.com"*/, int port /*= 46434*/, \
01913         int latency/* = 1000*/, int mode/* = 0*/)
01914 {
01915     bool ok = false;
01916     LOCK();
01917     if (_dev.dev != DEV_TOBY_L2){
01918         sendFormated("AT+UGAOP=\"%s\",%d,%d,%d\r\n", server_1, port, latency, mode);
01919         ok = (RESP_OK == waitFinalResp());
01920     } else
01921         ok = false; //command not supported by module
01922     UNLOCK();
01923     return ok;
01924 }
01925 
01926 int MDMParser::cellLocUnsolIndication(int mode)
01927 {
01928     bool ok = false;
01929     LOCK();
01930     if (_dev.dev == DEV_LISA_U2_03S){
01931         sendFormated("AT+ULOCIND=%d\r\n", mode);
01932         ok = (RESP_OK == waitFinalResp());
01933     } else
01934         ok = false; //command not supported by module
01935     UNLOCK();
01936     return ok;
01937 }
01938 
01939 int MDMParser::cellLocConfigSensor(int scanMode)
01940 {
01941     bool ok = false;
01942     LOCK();
01943     if (_dev.dev != DEV_TOBY_L2){
01944         sendFormated("AT+ULOCCELL=%d\r\n", scanMode);
01945         ok = (RESP_OK == waitFinalResp());    
01946     }else
01947         ok = false; //command not supported by module
01948     UNLOCK();
01949     return ok;
01950 }
01951 
01952 int MDMParser::cellLocRequest(int sensor, int timeout, int accuracy, int numHypotesis /* =1*/)
01953 {
01954     bool ok = false;
01955     
01956     LOCK();
01957      _loc.validData = false;
01958     if (_dev.dev == DEV_LISA_U2_03S){
01959         sendFormated("AT+ULOC=2,%d,1,%d,%d,%d\r\n", sensor, timeout, accuracy, numHypotesis);
01960         ok = (RESP_OK == waitFinalResp());
01961     } else if (_dev.dev != DEV_TOBY_L2){
01962         sendFormated("AT+ULOC=2,%d,1,%d,%d\r\n",  sensor, timeout, accuracy);
01963         ok = (RESP_OK == waitFinalResp());
01964     }
01965     else  ok = false; //command not supported by module
01966 
01967 
01968     UNLOCK();
01969     return ok;
01970 }  
01971 
01972 int MDMParser::cellLocGet(CellLocData *data){
01973     waitFinalResp(NULL,NULL,0);
01974     if (_loc.validData){
01975         memcpy(data, &_loc, sizeof(_loc));
01976         _loc.validData = false;
01977         return true;
01978     }
01979     return false;
01980 }
01981 
01982 // ----------------------------------------------------------------
01983 bool MDMParser::setDebug (int level) 
01984 {
01985 #ifdef MDM_DEBUG
01986     _debugLevel = (level < -1) ? -1 : 
01987                   (level >  3) ?  3 : 
01988                                  level;
01989 #endif
01990     return _debugLevel == level;
01991 }
01992 
01993 void MDMParser::dumpDevStatus(MDMParser::DevStatus* status, 
01994             _DPRINT dprint, void* param) 
01995 {
01996     dprint(param, "Modem::devStatus\r\n");
01997     const char* txtDev[] = { "Unknown",  "SARA-G35", "LISA-U2", "LISA-U2-03S", "LISA-C2", 
01998                              "SARA-U2",  "LEON-G2",  "TOBY-L2", "MPCI-L2" };
01999     if (status->dev < sizeof(txtDev)/sizeof(*txtDev) && (status->dev != DEV_UNKNOWN))
02000         dprint(param, "  Device:       %s\r\n", txtDev[status->dev]);
02001     const char* txtLpm[] = { "Disabled", "Enabled", "Active" };
02002     if (status->lpm < sizeof(txtLpm)/sizeof(*txtLpm))
02003         dprint(param, "  Power Save:   %s\r\n", txtLpm[status->lpm]);
02004     const char* txtSim[] = { "Unknown", "Missing", "Pin", "Ready" };
02005     if (status->sim < sizeof(txtSim)/sizeof(*txtSim) && (status->sim != SIM_UNKNOWN))
02006         dprint(param, "  SIM:          %s\r\n", txtSim[status->sim]);
02007     if (*status->ccid)  
02008         dprint(param, "  CCID:         %s\r\n", status->ccid);
02009     if (*status->imei) 
02010         dprint(param, "  IMEI:         %s\r\n", status->imei);
02011     if (*status->imsi)  
02012         dprint(param, "  IMSI:         %s\r\n", status->imsi);
02013     if (*status->meid) 
02014         dprint(param, "  MEID:         %s\r\n", status->meid); // LISA-C
02015     if (*status->manu) 
02016         dprint(param, "  Manufacturer: %s\r\n", status->manu);
02017     if (*status->model)  
02018         dprint(param, "  Model:        %s\r\n", status->model);
02019     if (*status->ver)  
02020         dprint(param, "  Version:      %s\r\n", status->ver);
02021 }
02022 
02023 void MDMParser::dumpNetStatus(MDMParser::NetStatus *status,
02024             _DPRINT dprint, void* param)
02025 {
02026     dprint(param, "Modem::netStatus\r\n");
02027     const char* txtReg[] = { "Unknown", "Denied", "None", "Home", "Roaming" };
02028     if (status->csd < sizeof(txtReg)/sizeof(*txtReg) && (status->csd != REG_UNKNOWN))
02029         dprint(param, "  CSD Registration:   %s\r\n", txtReg[status->csd]);
02030     if (status->psd < sizeof(txtReg)/sizeof(*txtReg) && (status->psd != REG_UNKNOWN))
02031         dprint(param, "  PSD Registration:   %s\r\n", txtReg[status->psd]);
02032     if (status->eps < sizeof(txtReg)/sizeof(*txtReg) && (status->eps != REG_UNKNOWN))
02033         dprint(param, "  EPS Registration:   %s\r\n", txtReg[status->eps]);
02034     const char* txtAct[] = { "Unknown", "GSM", "Edge", "3G", "CDMA", "LTE" };
02035     if (status->act < sizeof(txtAct)/sizeof(*txtAct) && (status->act != ACT_UNKNOWN))
02036         dprint(param, "  Access Technology:  %s\r\n", txtAct[status->act]);
02037     if (status->rssi) 
02038         dprint(param, "  Signal Strength:    %d dBm\r\n", status->rssi);
02039     if (status->ber) 
02040         dprint(param, "  Bit Error Rate:     %d\r\n", status->ber);
02041     if (*status->opr)  
02042         dprint(param, "  Operator:           %s\r\n", status->opr);
02043     if (status->lac != 0xFFFF)  
02044         dprint(param, "  Location Area Code: %04X\r\n", status->lac);
02045     if (status->ci != 0xFFFFFFFF)  
02046         dprint(param, "  Cell ID:            %08X\r\n", status->ci);
02047     if (*status->num)  
02048         dprint(param, "  Phone Number:       %s\r\n", status->num);
02049 }
02050 
02051 void MDMParser::dumpIp(MDMParser::IP ip,
02052             _DPRINT dprint, void* param) 
02053 {
02054     if (ip != NOIP)
02055         dprint(param, "Modem:IP " IPSTR "\r\n", IPNUM(ip));
02056 }
02057     
02058 // ----------------------------------------------------------------
02059 int MDMParser::_parseMatch(Pipe<char> * pipe, int len, const char* sta, const char* end)
02060 {
02061     int o = 0;
02062     if (sta) {
02063         while (*sta) {
02064             if (++o > len)                  return WAIT;
02065             char ch = pipe->next();
02066             if (*sta++ != ch)               return NOT_FOUND;
02067         }
02068     }
02069     if (!end)                               return o; // no termination
02070     // at least any char
02071     if (++o > len)                      return WAIT;
02072     pipe->next();
02073     // check the end     
02074     int x = 0;
02075     while (end[x]) {
02076         if (++o > len)                      return WAIT;
02077         char ch = pipe->next();
02078         x = (end[x] == ch) ? x + 1 : 
02079             (end[0] == ch) ? 1 : 
02080                             0;
02081     }
02082     return o;
02083 }
02084 
02085 int MDMParser::_parseFormated(Pipe<char> * pipe, int len, const char* fmt)
02086 {
02087     int o = 0;
02088     int num = 0;
02089     if (fmt) {
02090         while (*fmt) {
02091             if (++o > len)                  return WAIT;
02092             char ch = pipe->next();
02093             if (*fmt == '%') {
02094                 fmt++;
02095                 if (*fmt == 'd') { // numeric
02096                     fmt ++;
02097                     num = 0;
02098                     while (ch >= '0' && ch <= '9') {
02099                         num = num * 10 + (ch - '0'); 
02100                         if (++o > len)      return WAIT;
02101                         ch = pipe->next();
02102                     }
02103                 }   
02104                 else if (*fmt == 'c') { // char buffer (takes last numeric as length)
02105                     fmt ++;
02106                     while (num --) {
02107                         if (++o > len)      return WAIT;
02108                         ch = pipe->next();
02109                     }   
02110                 }
02111                 else if (*fmt == 's') {
02112                     fmt ++;
02113                     if (ch != '\"')         return NOT_FOUND;
02114                     do {
02115                         if (++o > len)      return WAIT;
02116                         ch = pipe->next();
02117                     } while (ch != '\"');
02118                     if (++o > len)          return WAIT;
02119                     ch = pipe->next();
02120                 }
02121             }
02122             if (*fmt++ != ch)               return NOT_FOUND;
02123         }
02124     }
02125     return o; 
02126 }
02127 
02128 int MDMParser::_getLine(Pipe<char> * pipe, char* buf, int len)
02129 {
02130     int unkn = 0;
02131     int sz = pipe->size();
02132     int fr = pipe->free();
02133     if (len > sz)
02134         len = sz;
02135     while (len > 0)
02136     {
02137         static struct { 
02138               const char* fmt;                              int type; 
02139         } lutF[] = {
02140             { "\r\n+USORD: %d,%d,\"%c\"",                   TYPE_PLUS       },
02141             { "\r\n+USORF: %d,\"" IPSTR "\",%d,%d,\"%c\"",  TYPE_PLUS       },
02142             { "\r\n+URDFILE: %s,%d,\"%c\"",                 TYPE_PLUS       },
02143             { "\r\n+URDBLOCK: %s,%d,\"%c\"",                TYPE_PLUS       },
02144         };
02145         static struct { 
02146               const char* sta;          const char* end;    int type; 
02147         } lut[] = {
02148             { "\r\nOK\r\n",             NULL,               TYPE_OK         },
02149             { "\r\nERROR\r\n",          NULL,               TYPE_ERROR      },
02150             { "\r\n+CME ERROR:",        "\r\n",             TYPE_ERROR_CME      }, 
02151             { "\r\n+CMS ERROR:",        "\r\n",             TYPE_ERROR      },
02152             { "\r\nRING\r\n",           NULL,               TYPE_RING       },
02153             { "\r\nCONNECT\r\n",        NULL,               TYPE_CONNECT    },
02154             { "\r\nNO CARRIER\r\n",     NULL,               TYPE_NOCARRIER  },
02155             { "\r\nNO DIALTONE\r\n",    NULL,               TYPE_NODIALTONE },
02156             { "\r\nBUSY\r\n",           NULL,               TYPE_BUSY       },
02157             { "\r\nNO ANSWER\r\n",      NULL,               TYPE_NOANSWER   },
02158             { "\r\n+",                  "\r\n",             TYPE_PLUS       },
02159             { "\r\n@",                  NULL,               TYPE_PROMPT     }, // Sockets
02160             { "\r\n>",                  NULL,               TYPE_PROMPT     }, // SMS
02161             { "\n>",                    NULL,               TYPE_PROMPT     }, // File
02162         };
02163         for (int i = 0; i < sizeof(lutF)/sizeof(*lutF); i ++) {
02164             pipe->set(unkn);
02165             int ln = _parseFormated(pipe, len, lutF[i].fmt);
02166             if (ln == WAIT && fr)                       
02167                 return WAIT;
02168             if ((ln != NOT_FOUND) && (unkn > 0))  
02169                 return TYPE_UNKNOWN | pipe->get (buf, unkn);
02170             if (ln > 0)
02171                 return lutF[i].type  | pipe->get (buf, ln);
02172         }
02173         for (int i = 0; i < sizeof(lut)/sizeof(*lut); i ++) {
02174             pipe->set(unkn);
02175             int ln = _parseMatch(pipe, len, lut[i].sta, lut[i].end);
02176             if (ln == WAIT && fr)                       
02177                 return WAIT;
02178             if ((ln != NOT_FOUND) && (unkn > 0))  
02179                 return TYPE_UNKNOWN | pipe->get (buf, unkn);
02180             if (ln > 0)
02181                 return lut[i].type | pipe->get (buf, ln);
02182         }
02183         // UNKNOWN
02184         unkn ++;
02185         len--;
02186     }
02187     return WAIT;
02188 }
02189 
02190 // ----------------------------------------------------------------
02191 // Serial Implementation 
02192 // ----------------------------------------------------------------
02193 
02194 /*! Helper Dev Null Device 
02195     Small helper class used to shut off stderr/stdout. Sometimes stdin/stdout
02196     is shared with the serial port of the modem. Having printfs inbetween the 
02197     AT commands you cause a failure of the modem.
02198 */
02199 class DevNull : public Stream {
02200 public: 
02201     DevNull() : Stream(_name+1) { }             //!< Constructor
02202     void claim(const char* mode, FILE* file) 
02203         { freopen(_name, mode, file); }         //!< claim a stream
02204 protected:
02205     virtual int _getc()         { return EOF; } //!< Nothing
02206     virtual int _putc(int c)    { return c; }   //!< Discard
02207     static const char* _name;                   //!< File name
02208 };
02209 const char* DevNull::_name = "/null";  //!< the null device name
02210 static      DevNull null;              //!< the null device
02211 
02212 MDMSerial::MDMSerial(PinName tx /*= MDMTXD*/, PinName rx /*= MDMRXD*/, 
02213             int baudrate /*= MDMBAUD*/,
02214 #if DEVICE_SERIAL_FC
02215             PinName rts /*= MDMRTS*/, PinName cts /*= MDMCTS*/, 
02216 #endif
02217             int rxSize /*= 256*/, int txSize /*= 128*/) : 
02218             SerialPipe(tx, rx, rxSize, txSize) 
02219 {
02220     if (rx == USBRX) 
02221         null.claim("r", stdin);
02222     if (tx == USBTX) {
02223         null.claim("w", stdout);
02224         null.claim("w", stderr);
02225 #ifdef MDM_DEBUG
02226         _debugLevel = -1;
02227 #endif
02228     }
02229 #ifdef TARGET_UBLOX_C027
02230     _onboard = (tx == MDMTXD) && (rx == MDMRXD);
02231     if (_onboard)
02232        c027_mdm_powerOn(false);
02233 #endif
02234     baud(baudrate);
02235 #if DEVICE_SERIAL_FC
02236     if ((rts != NC) || (cts != NC))
02237     {
02238         Flow flow = (cts == NC) ? RTS :
02239                     (rts == NC) ? CTS : RTSCTS ;
02240         set_flow_control(flow, rts, cts);
02241         if (cts != NC) _dev.lpm = LPM_ENABLED;
02242     }
02243 #endif
02244 }
02245 
02246 MDMSerial::~MDMSerial(void)
02247 {
02248     powerOff();
02249 #ifdef TARGET_UBLOX_C027
02250     if (_onboard)
02251         c027_mdm_powerOff();
02252 #endif
02253 }
02254 
02255 int MDMSerial::_send(const void* buf, int len)   
02256 { 
02257     return put((const char*)buf, len, true/*=blocking*/);
02258 }
02259 
02260 int MDMSerial::getLine(char* buffer, int length)
02261 {
02262     return _getLine(&_pipeRx, buffer, length);
02263 }
02264 
02265 // ----------------------------------------------------------------
02266 // USB Implementation 
02267 // ----------------------------------------------------------------
02268 
02269 #ifdef HAVE_MDMUSB
02270 MDMUsb::MDMUsb(void)                             
02271 { 
02272 #ifdef MDM_DEBUG
02273     _debugLevel = 1;
02274 #endif    
02275 #ifdef TARGET_UBLOX_C027
02276     _onboard = true;
02277     c027_mdm_powerOn(true);
02278 #endif
02279 }
02280 
02281 MDMUsb::~MDMUsb(void)
02282 {
02283     powerOff();
02284 #ifdef TARGET_UBLOX_C027
02285     if (_onboard)
02286         c027_mdm_powerOff();
02287 #endif
02288 }
02289 
02290 int MDMUsb::_send(const void* buf, int len)      { return 0; }
02291 
02292 int MDMUsb::getLine(char* buffer, int length)    { return NOT_FOUND; }
02293 
02294 #endif