Vodafone K3770/K3772-Z modems driver & networking library

Dependencies:   Socket USBHostWANDongle lwip-sys lwip

Dependents:   VodafoneUSBModemHTTPClientTest VodafoneUSBModemNTPClientTest VodafoneUSBModemSMSTest VodafoneUSBModemUSSDTest ... more

Fork of VodafoneUSBModem_bleedingedge by Donatien Garnier

This is the driver for the Vodafone K3700 & K3772-Z Dongles:

K3770

More details and instructions can be found here.

ip/PPPIPInterface.cpp

Committer:
donatien
Date:
2012-07-11
Revision:
11:565b2ec40dea
Parent:
10:21a6f09d5631
Parent:
9:3f077dde13c9
Child:
12:66dc2c8eba2d

File content as of revision 11:565b2ec40dea:

/* PPPIPInterface.cpp */
/*
Copyright (C) 2012 ARM Limited.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#define __DEBUG__ 2 //Maximum verbosity
#ifndef __MODULE__
#define __MODULE__ "PPPIPInterface.cpp"
#endif

#include "core/fwk.h"
#include "rtos.h"

#include "PPPIPInterface.h"

extern "C" {
#include "lwip/ip_addr.h"
#include "lwip/inet.h"
#include "netif/ppp/ppp.h"
}

PPPIPInterface::PPPIPInterface(IOStream* pStream) : LwIPInterface(), m_linkStatusSphre(1), m_pppErrCode(0), m_pStream(pStream), m_streamAvail(true), m_pppd(-1)
{
  m_linkStatusSphre.wait();
}

/*virtual*/ PPPIPInterface::~PPPIPInterface()
{

}

/*virtual*/ int PPPIPInterface::init() //Init PPP-specific stuff, create the right bindings, etc
{
  DBG("Initializing LwIP");
  LwIPInterface::init(); //Init LwIP, NOT including PPP
  DBG("Initializing PPP");
  pppInit();
  DBG("Done");
  return OK;
}

int PPPIPInterface::setup(const char* user, const char* pw)
{
  DBG("Configuring PPP authentication method");
  pppSetAuth(PPPAUTHTYPE_ANY, user, pw);
  DBG("Done");
  return OK;
}

/*virtual*/ int PPPIPInterface::connect(const char* msisdn)
{
  int ret;
  char buf[32];
  size_t len;
  DBG("Trying to connect with PPP");
  
  do //Clear buf
  {
    ret = m_pStream->read(buf, &len, 32, 0) > 0
  } while( (ret == OK) && (len > 0) );
  
  sprintf(buf, "ATD %16s\x0D");
  
  ret = m_pStream->write(buf, strlen(buf), osWaitForever);
  if( ret != OK )
  {
    return NET_UNKNOWN;
  }
  
  const char expected = "\x0D\0x0ACONNECT\x0D\0x0A";
  
  ret = m_pStream->read(buf, strlen(expected), &len, 3000);
  if( ret != OK )
  {
    return NET_UNKNOWN;
  }
  
  if( (len < strlen(expected)) || (memcmp(expected, buf) != 0) )
  {
    //Discard buffer
    do //Clear buf
    {
      ret = m_pStream->read(buf, &len, 32, 0) > 0
    } while( (ret == OK) && (len > 0) );
    return NET_CONN;
  }
      
  DBG("Transport link open");
  m_linkStatusSphre.wait(0);
  if((m_pppd != -1) && (m_pppErrCode == 0)) //Already connected
  {
    return NET_INVALID;
  }
  int ret = pppOverSerialOpen(this, PPPIPInterface::linkStatusCb, this);
  if(ret < 0)
  {
    switch(ret)
    {
    case PPPERR_OPEN:
    default:
      return NET_FULL; //All available resources are already used
    }
  }
  m_pppd = ret; //PPP descriptor
  m_linkStatusSphre.wait(); //Block indefinitely; there should be a timeout there
  if(m_pppErrCode != PPPERR_NONE)
  {
    m_pppd = -1;
  }
  switch(m_pppErrCode)
  {
  case PPPERR_NONE: //Connected OK
    return OK;
  case PPPERR_CONNECT: //Connection lost
    return NET_INTERRUPTED;
  case PPPERR_AUTHFAIL: //Authentication failed
    return NET_AUTH;
  case PPPERR_PROTOCOL: //Protocol error
    return NET_PROTOCOL;
  default:
    return NET_UNKNOWN;
  }
}

/*virtual*/ int PPPIPInterface::disconnect()
{
  int ret = m_linkStatusSphre.wait(0);
  if(ret > 0) //Already disconnected?
  {
    m_pppd = -1; //Discard PPP descriptor
    switch(m_pppErrCode)
      {
      case PPPERR_CONNECT: //Connection terminated
      case PPPERR_AUTHFAIL: //Authentication failed
      case PPPERR_PROTOCOL: //Protocol error
      case PPPERR_USER:
        return OK;
      default:
        return NET_UNKNOWN;
      }
  }
  else
  {
    if(m_pppd == -1)
    {
      return NET_INVALID;
    }
    pppClose(m_pppd);
    do
    {
      m_linkStatusSphre.wait(); //Block indefinitely; there should be a timeout there
      DBG("Received PPP err code %d", m_pppErrCode);
    } while(m_pppErrCode != PPPERR_USER);
    m_pppd = -1; //Discard PPP descriptor
  }
  return OK;
}

/*static*/ void PPPIPInterface::linkStatusCb(void *ctx, int errCode, void *arg) //PPP link status
{
  PPPIPInterface* pIf = (PPPIPInterface*)ctx;
  struct ppp_addrs* addrs = (struct ppp_addrs*) arg;

  switch(errCode)
  {
  case PPPERR_NONE:
    WARN("Connected via PPP.");
    DBG("Local IP address: %s", inet_ntoa(addrs->our_ipaddr));
    DBG("Netmask: %s", inet_ntoa(addrs->netmask));
    DBG("Remote IP address: %s", inet_ntoa(addrs->his_ipaddr));
    DBG("Primary DNS: %s", inet_ntoa(addrs->dns1));
    DBG("Secondary DNS: %s", inet_ntoa(addrs->dns2));
    pIf->setConnected(true);
    pIf->setIPAddress(inet_ntoa(addrs->our_ipaddr));
    break;
  case PPPERR_CONNECT: //Connection lost
    WARN("Connection lost/terminated");
    pIf->setConnected(false);
    break;
  case PPPERR_AUTHFAIL: //Authentication failed
    WARN("Authentication failed");
    pIf->setConnected(false);
    break;
  case PPPERR_PROTOCOL: //Protocol error
    WARN("Protocol error");
    pIf->setConnected(false);
    break;
  case PPPERR_USER:
    WARN("Disconnected by user");
    pIf->setConnected(false);
    break;
  default:
    WARN("Unknown error (%d)", errCode);
    pIf->setConnected(false);
    break;
  }

  pIf->m_linkStatusSphre.wait(0); //If previous event has not been handled, "delete" it now
  pIf->m_pppErrCode = errCode;
  pIf->m_linkStatusSphre.release();
}

//LwIP PPP implementation
extern "C"
{

/**
 * Writes to the serial device.
 *
 * @param fd serial device handle
 * @param data pointer to data to send
 * @param len length (in bytes) of data to send
 * @return number of bytes actually sent
 *
 * @note This function will block until all data can be sent.
 */
u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len)
{
  DBG("sio_write");
  PPPIPInterface* pIf = (PPPIPInterface*)fd;
  int ret;
  if(!pIf->m_streamAvail) //If stream is not available (it is a shared resource) don't go further
  {
    return 0;
  }
  ret = pIf->m_pStream->write(data, len, osWaitForever); //Blocks until all data is sent or an error happens
  if(ret != OK)
  {
    return 0;
  }
  return len;
}

/**
 * Reads from the serial device.
 *
 * @param fd serial device handle
 * @param data pointer to data buffer for receiving
 * @param len maximum length (in bytes) of data to receive
 * @return number of bytes actually received - may be 0 if aborted by sio_read_abort
 *
 * @note This function will block until data can be received. The blocking
 * can be cancelled by calling sio_read_abort().
 */
u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)
{
  DBG("sio_read");
  PPPIPInterface* pIf = (PPPIPInterface*)fd;
  int ret;
  size_t readLen;
  if(!pIf->m_streamAvail) //If stream is not available (it is a shared resource) don't go further
  {
    WARN("EXIT NOT AVAIL");
    return 0;
  }
  ret = pIf->m_pStream->read(data, &readLen, len, osWaitForever); //Blocks until some data is received or an error happens
  if(ret != OK)
  {
    return 0;
  }
  DBG("ret");
  return readLen;
}

/**
 * Aborts a blocking sio_read() call.
 *
 * @param fd serial device handle
 */
void sio_read_abort(sio_fd_t fd)
{
  DBG("sio_read_abort");
  PPPIPInterface* pIf = (PPPIPInterface*)fd;
  if(!pIf->m_streamAvail) //If stream is not available (it is a shared resource) don't go further
  {
    return;
  }
  pIf->m_pStream->abortRead();
  DBG("ret");
}

}