lwIP Wiki

PPP from an application perspective[]

There are two ways to use lwIP with PPP support. Either PPPoE (PPP over Ethernet) or PPP-over-serial. lwIP supports being run in a threaded environment, where ppp is a separate task that runs alongside the main lwIP thread. lwIP also supports being run from a main loop, with lwIP functions being called from the main loop.

PPP over serial[]

To setup a PPP connection over a serial link you will need to provide the Serial IO functions.

Both the thread environment and a main loop require implemented sio_write() to be implemented.

 * 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);

With Task Support[]

In addition to sio_write(), sio_read(), sio_read_abort() and the linkStatusCB are required to be implemented by you. The first 2 functions are statically linked, while the last function is defined through a function pointer (so the name can be different, and could be dynamically created as well).

The function sio_read() is called from the pppInputThread, repeatedly. It blocks until it fills the requested buffer size or times-out, reading data from the serial device. It must abort itself if there is a call to sio_read_abort(). The soft link between sio_read and sio_read_abort must be implemented by you, either via a global variable, RTOS event, etc. The timeout should be relatively small, just large enough so that data is actually buffered in more than 1 byte chunks but small enough to make the ppp stack responsive. A timeout of about 2 ms is reasonable.

 * 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
u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len);

The sio_read_abort() function must cause the sio_read command to exit immediately. This command is called by the tcpip_thread, mostly when ending a PPP session (terminated, link down or explicit close).

 * Aborts a blocking sio_read() call.
 * @param fd serial device handle
void  sio_read_abort(sio_fd_t fd);

Required callback function[]

The callback

 void (*linkStatusCB)(void *ctx, int errCode, void *arg)

is called under the following events:

  • Link Terminated. errCode is nonzero, arg is null.
  • sifup (Interface Up).errCode is PPPERR_NONE, and arg is pointer to ppp_addrs structure, which contains IP address.
  • sifdown (Interace Down). errCode is PPPERR_CONNECT, and arg is null.

The errCode can be one of the following

#define PPPERR_NONE      0 /* No error. */
#define PPPERR_PARAM    -1 /* Invalid parameter. */
#define PPPERR_OPEN     -2 /* Unable to open PPP session. */
#define PPPERR_DEVICE   -3 /* Invalid I/O device for PPP. */
#define PPPERR_ALLOC    -4 /* Unable to allocate resources. */
#define PPPERR_USER     -5 /* User interrupt. */
#define PPPERR_CONNECT  -6 /* Connection lost. */
#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */
#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */

The ctx pointer is an optional user defined pointer that is defined as an argument in the call to pppOverSerialOpen, which can point to user defined data.

With no Task Support[]

You need to receive the serial data in your main thread, then from your main thread call

pppos_input(int pd, u_char* data, int len);

Example Task Application code[]

This example shows the initialization of the TCP and PPP threads. It assumes that you will do your socket handling in the context of the main() function.

#include "ppp/ppp.h"


static void linkStatusCB(void* ctx, int errCode, void* arg);
static void tcpip_init_done(void *arg);

int main(void) {
    int connected = 0;
    volatile int setup = 0;
    int pd;
    const char *username = "myuser";
    const char *password = "mypassword";
    /* initialise lwIP. This creates a new thread, tcpip_thread, that 
     * communicates with the pppInputThread (see below) */
    tcpip_init(tcpip_init_done, &setup);
    while (!setup) {

    /* initialise PPP. This needs to be done only once after boot up, to 
     * initialize global variables, etc. */

    /* set the method of authentication. Use PPPAUTHTYPE_PAP, or
     * PPPAUTHTYPE_CHAP for more security .
     * If this is not called, the default is PPPAUTHTYPE_NONE. 
    pppSetAuth(PPPAUTHTYPE_ANY, username, password);

    /* call the board specific function that will open the specific serial
     * port hardware, for example, configuring pins, I/Os, UART, USART, 
     * clocks, etc. This function is not part of lwip or ppp.  You need to
     * supply this or find an example for your hardware.
     OpenMySerialPortOrUART(PPP_SERIAL_PORT, ...);

    /* call the board specific function that will connect to the modem,
     * ie dialing phone numbers, sending configuration AT commands, etc. 
     * This function is not part of lwip or ppp. You need to supply this
    if (DialOutMyModem(PPP_SERIAL_PORT, ...) != 0) {
       printf("can't dial out");
    } else {
    /** Open a new PPP connection using the given I/O device.
     * This initializes the PPP control block but does not
     * attempt to negotiate the LCP session.  
     * Return a new PPP connection descriptor on success or
     * an error code (negative) on failure. 
     * This call creates one new thread per call, to service the particular
     * serial port, serviced by the pppInputThread function.
       pd = pppOverSerialOpen(PPP_SERIAL_PORT, linkStatusCB, &connected);
       if (pd >= 0) {
        // the thread was successfully started.
        while (!connected && timeout(60 seconds)) {
          sleep(100 ms);
        if (!timeout) {
            // We are connected on lwIP over PPP!
            while (working) {
            /* create some socket connections, 
             * do writes and reads on the sockets, close them, etc */
        /* calling pppClose will end the pppInputThread associated with pd*/

   // shut down the rest of your hardware.

static void tcpip_init_done(void *arg)
    if (arg) {
        *((bool *)arg) = 1;

static void linkStatusCB(void *ctx, int errCode, void *arg) {
   //DTRACE("ctx = 0x%04X, errCode = %d arg = 0x%04X", ctx, errCode, arg);
   int *connected = (int *) ctx;

   struct ppp_addrs *addrs = arg;

   if (errCode == PPPERR_NONE) {
       /* We are connected */
       *connected = 1;
       syslog(LOG_DEBUG, "ip_addr = %s", inet_ntoa(addrs->our_ipaddr));
       syslog(LOG_DEBUG, "netmask = %s", inet_ntoa(addrs->netmask));
       syslog(LOG_DEBUG, "dns1    = %s", inet_ntoa(addrs->dns1));
       syslog(LOG_DEBUG, "dns2    = %s", inet_ntoa(addrs->dns2));
   } else {
       /* We have lost connection */

Debug Support[]

Add this to lwipopts.h

 #define PPP_THREAD_NAME             "ppp"
 #define PPP_THREAD_STACKSIZE    200
 #define PPP_THREAD_PRIO               2

 #define LWIP_DEBUG              1
 #define PPP_DEBUG               LWIP_DBG_ON

PPP support history in lwIP[]

HEAD Unspecified.
1.2 Unspecified.

External references[]

  • RFC 1661 The Point-to-Point Protocol (PPP)

See also:[]

This information is not adequate. We would appreciate if someone can add more information regarding implementation of PPP with RTOS.