fork of VodafoneUSBModem with updated USBHost library

Dependencies:   Socket USBHost lwip-sys lwip

Dependents:   VodafoneUSBModemSMSTest

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