local fork

Dependencies:   Socket USBHostWANDongle_bleedingedge lwip-sys lwip

Dependents:   Encrypted

Fork of VodafoneUSBModem_bleedingedge by Donatien Garnier

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