Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ppp_lwip.cpp Source File

ppp_lwip.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2016 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include <errno.h>
00018 #include "platform/FileHandle.h"
00019 #include "platform/mbed_poll.h"
00020 #include "events/EventQueue.h"
00021 #if defined(FEATURE_COMMON_PAL)
00022 #include "mbed_trace.h"
00023 #define TRACE_GROUP "LPPP"
00024 #else
00025 #define tr_debug(...) (void(0)) //dummies if feature common pal is not added
00026 #define tr_info(...)  (void(0)) //dummies if feature common pal is not added
00027 #define tr_error(...) (void(0)) //dummies if feature common pal is not added
00028 #endif //defined(FEATURE_COMMON_PAL)
00029 #include "rtos/Thread.h"
00030 #include "lwip/tcpip.h"
00031 #include "lwip/tcp.h"
00032 #include "lwip/ip.h"
00033 #include "lwip/dns.h"
00034 #include "lwip/pbuf.h"
00035 extern "C" { // "pppos.h" is missing extern C
00036 #include "netif/ppp/pppapi.h"
00037 }
00038 
00039 #include "nsapi_ppp.h"
00040 #include "ppp_lwip.h"
00041 #include "lwip_stack.h"
00042 
00043 namespace mbed {
00044 
00045 using rtos::Thread;
00046 using events::EventQueue;
00047 
00048 #if LWIP_PPP_API
00049 
00050 static EventQueue *event_queue;
00051 static Thread *event_thread;
00052 static volatile bool event_queued;
00053 static nsapi_error_t connect_error_code;
00054 
00055 // Just one interface for now
00056 static FileHandle *my_stream;
00057 static ppp_pcb *my_ppp_pcb;
00058 static bool ppp_active = false;
00059 static const char *login;
00060 static const char *pwd;
00061 static sys_sem_t ppp_close_sem;
00062 static Callback<void(nsapi_event_t, intptr_t)> connection_status_cb;
00063 
00064 static EventQueue *prepare_event_queue()
00065 {
00066     if (event_queue) {
00067         return event_queue;
00068     }
00069 
00070     // Should be trying to get a global shared event queue here!
00071     // Shouldn't have to be making a private thread!
00072 
00073     // Only need to queue 2 events. new blows on failure.
00074     event_queue = new EventQueue(2 * EVENTS_EVENT_SIZE, NULL);
00075     event_thread = new Thread(osPriorityNormal, PPP_THREAD_STACK_SIZE);
00076 
00077     if (event_thread->start(callback(event_queue, &EventQueue::dispatch_forever)) != osOK) {
00078         delete event_thread;
00079         delete event_queue;
00080         return NULL;
00081     }
00082 
00083     return event_queue;
00084 }
00085 
00086 static u32_t ppp_output(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
00087 {
00088     FileHandle *stream = my_stream;
00089     if (!stream) {
00090         return 0;
00091     }
00092 
00093     pollfh fhs;
00094     fhs.fh = stream;
00095     fhs.events = POLLOUT;
00096 
00097     // LWIP expects us to block on write
00098     // File handle will be in non-blocking mode, because of read events.
00099     // Therefore must use poll to achieve the necessary block for writing.
00100 
00101     uint32_t written = 0;
00102     while (len != 0) {
00103         // Block forever until we're selected - don't care about reason we wake;
00104         // return from write should tell us what's up.
00105         poll(&fhs, 1, -1);
00106         // This write will be non-blocking, but blocking would be fine.
00107         ssize_t ret = stream->write(data, len);
00108         if (ret == -EAGAIN) {
00109             continue;
00110         } else if (ret < 0) {
00111             break;
00112         }
00113         written += ret;
00114         data += ret;
00115         len -= ret;
00116     }
00117 
00118 //    /tr_debug("> %ld bytes of data written\n", (long) written);
00119 
00120     return written;
00121 }
00122 
00123 static void ppp_link_status(ppp_pcb *pcb, int err_code, void *ctx)
00124 {
00125     nsapi_error_t mapped_err_code = NSAPI_ERROR_NO_CONNECTION ;
00126 
00127     switch(err_code) {
00128         case PPPERR_NONE:
00129             mapped_err_code = NSAPI_ERROR_OK ;
00130             tr_info("status_cb: Connected");
00131 #if PPP_IPV4_SUPPORT
00132             tr_debug("   our_ipaddr  = %s", ipaddr_ntoa(&ppp_netif(pcb)->ip_addr));
00133             tr_debug("   his_ipaddr  = %s", ipaddr_ntoa(&ppp_netif(pcb)->gw));
00134             tr_debug("   netmask     = %s", ipaddr_ntoa(&ppp_netif(pcb)->netmask));
00135 #if LWIP_DNS
00136             const ip_addr_t *ns;
00137             ns = dns_getserver(0);
00138             if (ns) {
00139                 tr_debug("   dns1        = %s", ipaddr_ntoa(ns));
00140             }
00141             ns = dns_getserver(1);
00142             if (ns) {
00143                 tr_debug("   dns2        = %s", ipaddr_ntoa(ns));
00144             }
00145 #endif /* LWIP_DNS */
00146 #endif /* PPP_IPV4_SUPPORT */
00147 #if PPP_IPV6_SUPPORT
00148             tr_debug("   our6_ipaddr = %s", ip6addr_ntoa(netif_ip6_addr(ppp_netif(pcb), 0)));
00149 #endif /* PPP_IPV6_SUPPORT */
00150             break;
00151 
00152         case PPPERR_PARAM:
00153             tr_info("status_cb: Invalid parameter");
00154             break;
00155 
00156         case PPPERR_OPEN:
00157             tr_info("status_cb: Unable to open PPP session");
00158             break;
00159 
00160         case PPPERR_DEVICE:
00161             tr_info("status_cb: Invalid I/O device for PPP");
00162             break;
00163 
00164         case PPPERR_ALLOC:
00165             tr_info("status_cb: Unable to allocate resources");
00166             break;
00167 
00168         case PPPERR_USER:
00169             tr_info("status_cb: User interrupt");
00170             break;
00171 
00172         case PPPERR_CONNECT:
00173             tr_info("status_cb: Connection lost");
00174             mapped_err_code = NSAPI_ERROR_CONNECTION_LOST ;
00175             break;
00176 
00177         case PPPERR_AUTHFAIL:
00178             tr_info("status_cb: Failed authentication challenge");
00179             mapped_err_code = NSAPI_ERROR_AUTH_FAILURE ;
00180             break;
00181 
00182         case PPPERR_PROTOCOL:
00183             tr_info("status_cb: Failed to meet protocol");
00184             break;
00185 
00186         case PPPERR_PEERDEAD:
00187             tr_info("status_cb: Connection timeout");
00188             mapped_err_code = NSAPI_ERROR_CONNECTION_TIMEOUT ;
00189             break;
00190 
00191         case PPPERR_IDLETIMEOUT:
00192             tr_info("status_cb: Idle Timeout");
00193             break;
00194 
00195         case PPPERR_CONNECTTIME:
00196             tr_info("status_cb: Max connect time reached");
00197             break;
00198 
00199         case PPPERR_LOOPBACK:
00200             tr_info("status_cb: Loopback detected");
00201             break;
00202 
00203         default:
00204             tr_info("status_cb: Unknown error code %d", err_code);
00205             break;
00206 
00207     }
00208 
00209     if (err_code == PPPERR_NONE) {
00210         /* status changes have to be reported */
00211         if (connection_status_cb) {
00212             connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_GLOBAL_UP );
00213         }
00214         return;
00215     }
00216 
00217     /* If some error happened, we need to properly shutdown the PPP interface  */
00218     if (ppp_active) {
00219         ppp_active = false;
00220         connect_error_code = mapped_err_code;
00221         sys_sem_signal(&ppp_close_sem);
00222     }
00223 
00224     /* Alright, PPP interface is down, we need to notify upper layer */
00225     if (connection_status_cb) {
00226         connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_DISCONNECTED );
00227     }
00228 }
00229 
00230 static void handle_modem_hangup()
00231 {
00232     if (my_ppp_pcb->phase != PPP_PHASE_DEAD) {
00233         ppp_close(my_ppp_pcb, 1);
00234     }
00235 }
00236 
00237 #if !PPP_INPROC_IRQ_SAFE
00238 #error "PPP_INPROC_IRQ_SAFE must be enabled"
00239 #endif
00240 static void ppp_input()
00241 {
00242     // Allow new events from now, avoiding potential races around the read
00243     event_queued = false;
00244 
00245     if (!my_stream) {
00246         return;
00247     }
00248 
00249     // Non-blocking error check handler
00250     pollfh fhs;
00251     fhs.fh = my_stream;
00252     fhs.events = POLLIN;
00253     poll(&fhs, 1, 0);
00254     if (fhs.revents & (POLLHUP|POLLERR|POLLNVAL)) {
00255         handle_modem_hangup();
00256         return;
00257     }
00258 
00259     // Infinite loop, but we assume that we can read faster than the
00260     // serial, so we will fairly rapidly hit -EAGAIN.
00261     for (;;) {
00262         u8_t buffer[16];
00263         ssize_t len = my_stream->read(buffer, sizeof buffer);
00264         if (len == -EAGAIN) {
00265             break;
00266         } else if (len <= 0) {
00267             handle_modem_hangup();
00268             return;
00269         }
00270         pppos_input(my_ppp_pcb, buffer, len);
00271     }
00272     return;
00273 }
00274 
00275 static void stream_cb() {
00276     if (my_stream && !event_queued) {
00277         event_queued = true;
00278         if (event_queue->call(callback(ppp_input)) == 0) {
00279             event_queued = false;
00280         }
00281     }
00282 }
00283 
00284 extern "C" err_t ppp_lwip_connect()
00285 {
00286 #if PPP_AUTH_SUPPORT
00287    ppp_set_auth(my_ppp_pcb, PPPAUTHTYPE_ANY, login, pwd);
00288 #endif //PPP_AUTH_SUPPORT
00289    ppp_active = true;
00290    err_t ret = ppp_connect(my_ppp_pcb, 0);
00291    // lwIP's ppp.txt says input must not be called until after connect
00292    if (ret == ERR_OK) {
00293        my_stream->sigio(callback(stream_cb));
00294    } else {
00295        ppp_active = false;
00296    }
00297    return ret;
00298 }
00299 
00300 extern "C" err_t ppp_lwip_disconnect()
00301 {
00302     err_t ret = ppp_close(my_ppp_pcb, 0);
00303     if (ret != ERR_OK) {
00304         return ret;
00305     }
00306 
00307     /* close call made, now let's catch the response in the status callback */
00308     sys_arch_sem_wait(&ppp_close_sem, 0);
00309 
00310     /* Detach callbacks, and put handle back to default blocking mode */
00311     my_stream->sigio(Callback<void()>());
00312     my_stream->set_blocking(true);
00313     my_stream = NULL;
00314 
00315     return ret;
00316 }
00317 
00318 extern "C" nsapi_error_t ppp_lwip_if_init(struct netif *netif, const nsapi_ip_stack_t stack)
00319 {
00320     if (!prepare_event_queue()) {
00321         return NSAPI_ERROR_NO_MEMORY ;
00322     }
00323 
00324     if (!my_ppp_pcb) {
00325         my_ppp_pcb = pppos_create(netif,
00326                                ppp_output, ppp_link_status, NULL);
00327         if (!my_ppp_pcb) {
00328             return NSAPI_ERROR_DEVICE_ERROR ;
00329         }
00330 
00331         sys_sem_new(&ppp_close_sem, 0);
00332     }
00333 
00334 #if LWIP_IPV4
00335     if (stack != IPV6_STACK) {
00336         ppp_set_usepeerdns(my_ppp_pcb, true);
00337     }
00338 #endif
00339 
00340 #if LWIP_IPV4 && LWIP_IPV6
00341     if (stack == IPV4_STACK) {
00342         my_ppp_pcb->ipv6cp_disabled = true;
00343     } else if (stack == IPV6_STACK) {
00344         my_ppp_pcb->ipcp_disabled = true;
00345     }
00346 #endif
00347 
00348     return NSAPI_ERROR_OK ;
00349 }
00350 
00351 nsapi_error_t nsapi_ppp_error_code()
00352 {
00353     return connect_error_code;
00354 }
00355 
00356 nsapi_error_t nsapi_ppp_set_blocking(bool blocking)
00357 {
00358     mbed_lwip_set_blocking(blocking);
00359     return NSAPI_ERROR_OK ;
00360 }
00361 
00362 nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_event_t, intptr_t)> cb, const char *uname, const char *password, const nsapi_ip_stack_t stack)
00363 {
00364     if (my_stream) {
00365         return NSAPI_ERROR_PARAMETER ;
00366     }
00367     my_stream = stream;
00368     stream->set_blocking(false);
00369     connection_status_cb = cb;
00370     login = uname;
00371     pwd = password;
00372 
00373     // mustn't start calling input until after connect -
00374     // attach deferred until ppp_lwip_connect, called from mbed_lwip_bringup
00375     nsapi_error_t retcode = mbed_lwip_bringup_2(false, true, NULL, NULL, NULL, stack);
00376 
00377     if (retcode != NSAPI_ERROR_OK  && connect_error_code != NSAPI_ERROR_OK ) {
00378         return connect_error_code;
00379     } else {
00380         return retcode;
00381     }
00382 }
00383 
00384 nsapi_error_t nsapi_ppp_disconnect(FileHandle *stream)
00385 {
00386     return mbed_lwip_bringdown_2(true);
00387 }
00388 
00389 NetworkStack *nsapi_ppp_get_stack()
00390 {
00391     return nsapi_create_stack(&lwip_stack);
00392 }
00393 
00394 const char *nsapi_ppp_get_ip_addr(FileHandle *stream)
00395 {
00396     static char ip_addr[IPADDR_STRLEN_MAX];
00397 
00398     if (stream == my_stream) {
00399 
00400         if (mbed_lwip_get_ip_address(ip_addr, IPADDR_STRLEN_MAX)) {
00401             return ip_addr;
00402         }
00403     }
00404 
00405     return NULL;
00406 }
00407 const char *nsapi_ppp_get_netmask(FileHandle *stream)
00408 {
00409 #if !LWIP_IPV4
00410     return NULL;
00411 #endif
00412 
00413     static char netmask[IPADDR_STRLEN_MAX];
00414     if (stream == my_stream) {
00415         if (mbed_lwip_get_netmask(netmask, IPADDR_STRLEN_MAX)) {
00416             return netmask;
00417         }
00418     }
00419 
00420     return NULL;
00421 }
00422 const char *nsapi_ppp_get_gw_addr(FileHandle *stream)
00423 {
00424 #if !LWIP_IPV4
00425     return NULL;
00426 #endif
00427 
00428     static char gwaddr[IPADDR_STRLEN_MAX];
00429     if (stream == my_stream) {
00430         if (mbed_lwip_get_gateway(gwaddr, IPADDR_STRLEN_MAX)) {
00431             return gwaddr;
00432         }
00433     }
00434 
00435     return NULL;
00436 }
00437 
00438 #endif /* LWIP_PPP_API */
00439 
00440 } // namespace mbed