Base library for cellular modem implementations

Dependencies:   Socket lwip-sys lwip

Dependents:   CellularUSBModem CellularUSBModem

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers PPPIPInterface.cpp Source File

PPPIPInterface.cpp

00001 /* PPPIPInterface.cpp */
00002 /* Copyright (C) 2012 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019 
00020 #define __DEBUG__ 0
00021 #ifndef __MODULE__
00022 #define __MODULE__ "PPPIPInterface.cpp"
00023 #endif
00024 
00025 #include "core/fwk.h"
00026 #include "rtos.h"
00027 
00028 #include <cstdio>
00029 using std::sscanf;
00030 using std::sprintf;
00031 
00032 #include "PPPIPInterface.h"
00033 
00034 #define MSISDN "*99#"
00035 
00036 #define CONNECT_CMD_PREFIX "ATD "
00037 #define CONNECT_CMD_SUFFIX "\x0D"
00038 #define EXPECTED_RESP_SUFFIX "\x0D" "\x0A" "CONNECT" "\x0D" "\x0A"
00039 #define EXPECTED_RESP_DATARATE_SUFFIX "\x0D" "\x0A" "CONNECT %d" "\x0D" "\x0A"
00040 #define EXPECTED_RESP_MIN_LEN 20
00041 #define OK_RESP "\x0D" "\x0A" "OK" "\x0D" "\x0A"
00042 #define ESCAPE_SEQ "+++"
00043 #define HANGUP_CMD "ATH" "\x0D"
00044 #define NO_CARRIER_RESP "\x0D" "\x0A" "NO CARRIER" "\x0D" "\x0A"
00045 extern "C" {
00046 #include "lwip/ip_addr.h"
00047 #include "lwip/inet.h"
00048 #include "lwip/err.h"
00049 #include "lwip/dns.h"
00050 
00051 #include "netif/ppp/ppp.h"
00052 }
00053 
00054 PPPIPInterface::PPPIPInterface(IOStream* pStream) : LwIPInterface(), m_linkStatusSphre(1), m_pppErrCode(0), m_pStream(pStream), m_streamAvail(true), m_pppd(-1)
00055 {
00056   m_linkStatusSphre.wait();
00057 }
00058 
00059 
00060 
00061 /*virtual*/ PPPIPInterface::~PPPIPInterface()
00062 {
00063 }
00064 
00065 /*virtual*/ int PPPIPInterface::init() //Init PPP-specific stuff, create the right bindings, etc
00066 {
00067   DBG("Initializing LwIP");
00068   LwIPInterface::init(); //Init LwIP, NOT including PPP
00069   DBG("Initializing PPP");
00070   pppInit();
00071   DBG("Done");
00072   return OK;
00073 }
00074 
00075 int PPPIPInterface::setup(const char* user, const char* pw, const char* msisdn)
00076 {
00077   DBG("Configuring PPP authentication method");
00078   pppSetAuth(PPPAUTHTYPE_ANY, user, pw);
00079   m_msisdn = msisdn;
00080   DBG("Done");
00081   return OK;
00082 }
00083 
00084 /*virtual*/ int PPPIPInterface::connect()
00085 {
00086   int ret;
00087   char cmd[32];
00088   int cmdLen;
00089   char buf[32];
00090   size_t len;
00091   DBG("Trying to connect with PPP");
00092   
00093   cleanupLink();
00094   
00095   cmdLen = sprintf(cmd, "%s%s%s", CONNECT_CMD_PREFIX, m_msisdn, CONNECT_CMD_SUFFIX);
00096   DBG("Sending %s", cmd);
00097   ret = m_pStream->write((uint8_t*)cmd, cmdLen, osWaitForever);
00098   if( ret != OK )
00099   {
00100     return NET_UNKNOWN;
00101   }
00102   
00103   len = 0;
00104   size_t readLen;
00105   ret = m_pStream->read((uint8_t*)buf + len, &readLen, EXPECTED_RESP_MIN_LEN, 10000);
00106   if( ret != OK )
00107   {
00108     return NET_UNKNOWN;
00109   }
00110   len += readLen;
00111   while( (len < EXPECTED_RESP_MIN_LEN) || (buf[len-1] != LF) )
00112   {
00113     ret = m_pStream->read((uint8_t*)buf + len, &readLen, 1, 10000);
00114     if( ret != OK )
00115     {
00116       return NET_UNKNOWN;
00117     }
00118     len += readLen;
00119   }
00120   
00121   buf[len]=0;
00122   
00123   DBG("Got %s[len %d]", buf, len);
00124   
00125   int datarate = 0;
00126   strcpy(&cmd[cmdLen], EXPECTED_RESP_DATARATE_SUFFIX);
00127   if( (sscanf(buf, cmd, &datarate ) != 1)) 
00128   {
00129     strcpy(&cmd[cmdLen], EXPECTED_RESP_SUFFIX);
00130     if (strcmp(cmd, buf) != 0)
00131     {
00132       //Discard buffer
00133       do //Clear buf
00134       {
00135         ret = m_pStream->read((uint8_t*)buf, &len, 32, 0);
00136       } while( (ret == OK) && (len > 0) );
00137       return NET_CONN;
00138     }
00139   }    
00140   
00141   DBG("Transport link open");
00142   if(datarate != 0)
00143   {
00144     DBG("Datarate: %d bps", datarate);
00145   }
00146   m_linkStatusSphre.wait(0);
00147   if((m_pppd != -1) && (m_pppErrCode == 0)) //Already connected
00148   {
00149     return NET_INVALID;
00150   }
00151   
00152   ret = pppOverSerialOpen(this, PPPIPInterface::linkStatusCb, this);
00153   if(ret < 0)
00154   {
00155     switch(ret)
00156     {
00157     case PPPERR_OPEN:
00158     default:
00159       return NET_FULL; //All available resources are already used
00160     }
00161   }
00162   m_pppd = ret; //PPP descriptor
00163   m_linkStatusSphre.wait(); //Block indefinitely; there should be a timeout there
00164   if(m_pppErrCode != PPPERR_NONE)
00165   {
00166     m_pppd = -1;
00167   }
00168   switch(m_pppErrCode)
00169   {
00170   case PPPERR_NONE: //Connected OK
00171     return OK;
00172   case PPPERR_CONNECT: //Connection lost
00173     return NET_INTERRUPTED;
00174   case PPPERR_AUTHFAIL: //Authentication failed
00175     return NET_AUTH;
00176   case PPPERR_PROTOCOL: //Protocol error
00177     return NET_PROTOCOL;
00178   default:
00179     return NET_UNKNOWN;
00180   }
00181 }
00182 
00183 /*virtual*/ int PPPIPInterface::disconnect()
00184 {
00185   int ret = m_linkStatusSphre.wait(0);
00186   if(ret > 0) //Already disconnected?
00187   {
00188     m_pppd = -1; //Discard PPP descriptor
00189     switch(m_pppErrCode)
00190       {
00191       case PPPERR_CONNECT: //Connection terminated
00192       case PPPERR_AUTHFAIL: //Authentication failed
00193       case PPPERR_PROTOCOL: //Protocol error
00194       case PPPERR_USER:
00195         return OK;
00196       default:
00197         return NET_UNKNOWN;
00198       }
00199   }
00200   else
00201   {
00202     if(m_pppd == -1)
00203     {
00204       return NET_INVALID;
00205     }
00206     pppClose(m_pppd);
00207     do
00208     {
00209       m_linkStatusSphre.wait(); //Block indefinitely; there should be a timeout there
00210       DBG("Received PPP err code %d", m_pppErrCode);
00211     } while(m_pppErrCode != PPPERR_USER);
00212     m_pppd = -1; //Discard PPP descriptor
00213   }
00214   
00215   DBG("Sending %s", ESCAPE_SEQ);
00216   
00217   ret = m_pStream->write((uint8_t*)ESCAPE_SEQ, strlen(ESCAPE_SEQ), osWaitForever);
00218   if( ret != OK )
00219   {
00220     return NET_UNKNOWN;
00221   }
00222   
00223   cleanupLink();
00224   
00225   return OK;
00226 }
00227 
00228 
00229 int PPPIPInterface::cleanupLink()
00230 {
00231   int ret;
00232   char buf[32];
00233   size_t len;
00234   
00235   do //Clear buf
00236   {
00237     ret = m_pStream->read((uint8_t*)buf, &len, 32, 100);
00238     if(ret == OK)
00239     {
00240       buf[len] = '\0';
00241       DBG("Got %s", buf);
00242     }
00243   } while( (ret == OK) && (len > 0) );
00244   
00245   DBG("Sending %s", HANGUP_CMD);
00246   
00247   ret = m_pStream->write((uint8_t*)HANGUP_CMD, strlen(HANGUP_CMD), osWaitForever);
00248   if( ret != OK )
00249   {
00250     return NET_UNKNOWN;
00251   }
00252      
00253   size_t readLen;
00254   
00255   //Hangup
00256   DBG("Expect %s", HANGUP_CMD);
00257 
00258   len = 0;
00259   while( len < strlen(HANGUP_CMD) )
00260   {
00261     ret = m_pStream->read((uint8_t*)buf + len, &readLen, strlen(HANGUP_CMD) - len, 100);
00262     if( ret != OK )
00263     {
00264       break;
00265     }
00266     len += readLen;
00267     /////
00268     buf[len]=0;
00269     DBG("Got %s", buf);
00270   }
00271   
00272   buf[len]=0;
00273   
00274   DBG("Got %s[len %d]", buf, len);
00275   
00276   //OK response
00277   DBG("Expect %s", OK_RESP);
00278 
00279   len = 0;
00280   while( len < strlen(OK_RESP) )
00281   {
00282     ret = m_pStream->read((uint8_t*)buf + len, &readLen, strlen(OK_RESP) - len, 100);
00283     if( ret != OK )
00284     {
00285       break;
00286     }
00287     len += readLen;
00288     /////
00289     buf[len]=0;
00290     DBG("Got %s", buf);
00291   }
00292   
00293   buf[len]=0;
00294   
00295   DBG("Got %s[len %d]", buf, len);
00296   
00297   //NO CARRIER event
00298   DBG("Expect %s", NO_CARRIER_RESP);
00299 
00300   len = 0;
00301   while( len < strlen(NO_CARRIER_RESP) )
00302   {
00303     ret = m_pStream->read((uint8_t*)buf + len, &readLen, strlen(NO_CARRIER_RESP) - len, 100);
00304     if( ret != OK )
00305     {
00306       break;
00307     }
00308     len += readLen;
00309     /////
00310     buf[len]=0;
00311     DBG("Got %s", buf);
00312   }
00313   
00314   buf[len]=0;
00315   
00316   DBG("Got %s[len %d]", buf, len);
00317   
00318   do //Clear buf
00319   {
00320     ret = m_pStream->read((uint8_t*)buf, &len, 32, 100);
00321     if(ret == OK)
00322     {
00323       buf[len] = '\0';
00324       DBG("Got %s", buf);
00325     }
00326   } while( (ret == OK) && (len > 0) );
00327   
00328   
00329   return OK;
00330 }
00331 
00332 /*static*/ void PPPIPInterface::linkStatusCb(void *ctx, int errCode, void *arg) //PPP link status
00333 {
00334   PPPIPInterface* pIf = (PPPIPInterface*)ctx;
00335   struct ppp_addrs* addrs = (struct ppp_addrs*) arg;
00336 
00337   switch(errCode)
00338   {
00339   case PPPERR_NONE:
00340     WARN("Connected via PPP.");
00341     DBG("Local IP address: %s", inet_ntoa(addrs->our_ipaddr));
00342     DBG("Netmask: %s", inet_ntoa(addrs->netmask));
00343     DBG("Remote IP address: %s", inet_ntoa(addrs->his_ipaddr));
00344     DBG("Primary DNS: %s", inet_ntoa(addrs->dns1));
00345     DBG("Secondary DNS: %s", inet_ntoa(addrs->dns2));
00346     //Setup DNS
00347     if (addrs->dns1.addr != 0)
00348     {
00349       dns_setserver(0, (struct ip_addr*)&(addrs->dns1));
00350     }
00351     if (addrs->dns2.addr != 0)
00352     {
00353       dns_setserver(1, (struct ip_addr*)&(addrs->dns1));
00354     }
00355         
00356     pIf->setConnected(true);
00357     pIf->setIPAddress(inet_ntoa(addrs->our_ipaddr));
00358     break;
00359   case PPPERR_CONNECT: //Connection lost
00360     WARN("Connection lost/terminated");
00361     pIf->setConnected(false);
00362     break;
00363   case PPPERR_AUTHFAIL: //Authentication failed
00364     WARN("Authentication failed");
00365     pIf->setConnected(false);
00366     break;
00367   case PPPERR_PROTOCOL: //Protocol error
00368     WARN("Protocol error");
00369     pIf->setConnected(false);
00370     break;
00371   case PPPERR_USER:
00372     WARN("Disconnected by user");
00373     pIf->setConnected(false);
00374     break;
00375   default:
00376     WARN("Unknown error (%d)", errCode);
00377     pIf->setConnected(false);
00378     break;
00379   }
00380 
00381   pIf->m_linkStatusSphre.wait(0); //If previous event has not been handled, "delete" it now
00382   pIf->m_pppErrCode = errCode;
00383   pIf->m_linkStatusSphre.release();
00384 }
00385 
00386 //LwIP PPP implementation
00387 extern "C"
00388 {
00389 
00390 /**
00391  * Writes to the serial device.
00392  *
00393  * @param fd serial device handle
00394  * @param data pointer to data to send
00395  * @param len length (in bytes) of data to send
00396  * @return number of bytes actually sent
00397  *
00398  * @note This function will block until all data can be sent.
00399  */
00400 u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len)
00401 {
00402   DBG("sio_write");
00403   PPPIPInterface* pIf = (PPPIPInterface*)fd;
00404   int ret;
00405   if(!pIf->m_streamAvail) //If stream is not available (it is a shared resource) don't go further
00406   {
00407     return 0;
00408   }
00409   ret = pIf->m_pStream->write(data, len, osWaitForever); //Blocks until all data is sent or an error happens
00410   if(ret != OK)
00411   {
00412     return 0;
00413   }
00414   return len;
00415 }
00416 
00417 /**
00418  * Reads from the serial device.
00419  *
00420  * @param fd serial device handle
00421  * @param data pointer to data buffer for receiving
00422  * @param len maximum length (in bytes) of data to receive
00423  * @return number of bytes actually received - may be 0 if aborted by sio_read_abort
00424  *
00425  * @note This function will block until data can be received. The blocking
00426  * can be cancelled by calling sio_read_abort().
00427  */
00428 u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)
00429 {
00430   DBG("sio_read");
00431   PPPIPInterface* pIf = (PPPIPInterface*)fd;
00432   int ret;
00433   size_t readLen;
00434   if(!pIf->m_streamAvail) //If stream is not available (it is a shared resource) don't go further
00435   {
00436     WARN("EXIT NOT AVAIL");
00437     return 0;
00438   }
00439   ret = pIf->m_pStream->read(data, &readLen, len, osWaitForever); //Blocks until some data is received or an error happens
00440   if(ret != OK)
00441   {
00442     return 0;
00443   }
00444   DBG("ret");
00445   return readLen;
00446 }
00447 
00448 /**
00449  * Aborts a blocking sio_read() call.
00450  *
00451  * @param fd serial device handle
00452  */
00453 void sio_read_abort(sio_fd_t fd)
00454 {
00455   DBG("sio_read_abort");
00456   PPPIPInterface* pIf = (PPPIPInterface*)fd;
00457   if(!pIf->m_streamAvail) //If stream is not available (it is a shared resource) don't go further
00458   {
00459     return;
00460   }
00461   pIf->m_pStream->abortRead();
00462   DBG("ret");
00463 }
00464 
00465 }
00466