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, andarg
is pointer toppp_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"
#define PPP_SERIAL_PORT 0
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) {
sleep(1);
}
/* initialise PPP. This needs to be done only once after boot up, to
* initialize global variables, etc. */
pppInit();
/* 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*/
pppClose(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 packets all start and end with 0x7E. If you don't see these, something is wrong with your low level driver
- Further packet debugging hints are here: http://lists.nongnu.org/archive/html/lwip-users/2011-06/msg00036.html
- If you have trouble with the RTOS crashing on pppClose, look here : http://lists.gnu.org/archive/html/lwip-users/2012-02/msg00124.html
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.