Rtos API example

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_error_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         /* suppress generating a callback event for connection up
00211          * Because connect() call is blocking, why wait for a callback */
00212         return;
00213     }
00214 
00215     /* If some error happened, we need to properly shutdown the PPP interface  */
00216     if (ppp_active) {
00217         ppp_active = false;
00218         connect_error_code = mapped_err_code;
00219         sys_sem_signal(&ppp_close_sem);
00220     }
00221 
00222     /* Alright, PPP interface is down, we need to notify upper layer */
00223     if (connection_status_cb) {
00224         connection_status_cb(mapped_err_code);
00225     }
00226 }
00227 
00228 static void handle_modem_hangup()
00229 {
00230     if (my_ppp_pcb->phase != PPP_PHASE_DEAD) {
00231         ppp_close(my_ppp_pcb, 1);
00232     }
00233 }
00234 
00235 #if !PPP_INPROC_IRQ_SAFE
00236 #error "PPP_INPROC_IRQ_SAFE must be enabled"
00237 #endif
00238 static void ppp_input()
00239 {
00240     // Allow new events from now, avoiding potential races around the read
00241     event_queued = false;
00242 
00243     if (!my_stream) {
00244         return;
00245     }
00246 
00247     // Non-blocking error check handler
00248     pollfh fhs;
00249     fhs.fh = my_stream;
00250     fhs.events = POLLIN;
00251     poll(&fhs, 1, 0);
00252     if (fhs.revents & (POLLHUP|POLLERR|POLLNVAL)) {
00253         handle_modem_hangup();
00254         return;
00255     }
00256 
00257     // Infinite loop, but we assume that we can read faster than the
00258     // serial, so we will fairly rapidly hit -EAGAIN.
00259     for (;;) {
00260         u8_t buffer[16];
00261         ssize_t len = my_stream->read(buffer, sizeof buffer);
00262         if (len == -EAGAIN) {
00263             break;
00264         } else if (len <= 0) {
00265             handle_modem_hangup();
00266             return;
00267         }
00268         pppos_input(my_ppp_pcb, buffer, len);
00269     }
00270     return;
00271 }
00272 
00273 static void stream_cb() {
00274     if (my_stream && !event_queued) {
00275         event_queued = true;
00276         if (event_queue->call(callback(ppp_input)) == 0) {
00277             event_queued = false;
00278         }
00279     }
00280 }
00281 
00282 extern "C" err_t ppp_lwip_connect()
00283 {
00284 #if PPP_AUTH_SUPPORT
00285    ppp_set_auth(my_ppp_pcb, PPPAUTHTYPE_ANY, login, pwd);
00286 #endif //PPP_AUTH_SUPPORT
00287    ppp_active = true;
00288    err_t ret = ppp_connect(my_ppp_pcb, 0);
00289    // lwIP's ppp.txt says input must not be called until after connect
00290    if (ret == ERR_OK) {
00291        my_stream->sigio(callback(stream_cb));
00292    } else {
00293        ppp_active = false;
00294    }
00295    return ret;
00296 }
00297 
00298 extern "C" err_t ppp_lwip_disconnect()
00299 {
00300     err_t ret = ppp_close(my_ppp_pcb, 0);
00301     if (ret != ERR_OK) {
00302         return ret;
00303     }
00304 
00305     /* close call made, now let's catch the response in the status callback */
00306     sys_arch_sem_wait(&ppp_close_sem, 0);
00307 
00308     /* Detach callbacks, and put handle back to default blocking mode */
00309     my_stream->sigio(Callback<void()>());
00310     my_stream->set_blocking(true);
00311     my_stream = NULL;
00312 
00313     return ret;
00314 }
00315 
00316 extern "C" nsapi_error_t ppp_lwip_if_init(struct netif *netif, const nsapi_ip_stack_t stack)
00317 {
00318     if (!prepare_event_queue()) {
00319         return NSAPI_ERROR_NO_MEMORY ;
00320     }
00321 
00322     if (!my_ppp_pcb) {
00323         my_ppp_pcb = pppos_create(netif,
00324                                ppp_output, ppp_link_status, NULL);
00325         if (!my_ppp_pcb) {
00326             return NSAPI_ERROR_DEVICE_ERROR ;
00327         }
00328 
00329         sys_sem_new(&ppp_close_sem, 0);
00330     }
00331 
00332 #if LWIP_IPV4
00333     if (stack != IPV6_STACK) {
00334         ppp_set_usepeerdns(my_ppp_pcb, true);
00335     }
00336 #endif
00337 
00338 #if LWIP_IPV4 && LWIP_IPV6
00339     if (stack == IPV4_STACK) {
00340         my_ppp_pcb->ipv6cp_disabled = true;
00341     } else if (stack == IPV6_STACK) {
00342         my_ppp_pcb->ipcp_disabled = true;
00343     }
00344 #endif
00345 
00346     return NSAPI_ERROR_OK ;
00347 }
00348 
00349 nsapi_error_t nsapi_ppp_error_code()
00350 {
00351     return connect_error_code;
00352 }
00353 
00354 nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_error_t)> cb, const char *uname, const char *password, const nsapi_ip_stack_t stack)
00355 {
00356     if (my_stream) {
00357         return NSAPI_ERROR_PARAMETER ;
00358     }
00359     my_stream = stream;
00360     stream->set_blocking(false);
00361     connection_status_cb = cb;
00362     login = uname;
00363     pwd = password;
00364 
00365     // mustn't start calling input until after connect -
00366     // attach deferred until ppp_lwip_connect, called from mbed_lwip_bringup
00367     nsapi_error_t retcode = mbed_lwip_bringup_2(false, true, NULL, NULL, NULL, stack);
00368 
00369     if (retcode != NSAPI_ERROR_OK  && connect_error_code != NSAPI_ERROR_OK ) {
00370         return connect_error_code;
00371     } else {
00372         return retcode;
00373     }
00374 }
00375 
00376 nsapi_error_t nsapi_ppp_disconnect(FileHandle *stream)
00377 {
00378     return mbed_lwip_bringdown_2(true);
00379 }
00380 
00381 NetworkStack *nsapi_ppp_get_stack()
00382 {
00383     return nsapi_create_stack(&lwip_stack);
00384 }
00385 
00386 const char *nsapi_ppp_get_ip_addr(FileHandle *stream)
00387 {
00388     static char ip_addr[IPADDR_STRLEN_MAX];
00389 
00390     if (stream == my_stream) {
00391 
00392         if (mbed_lwip_get_ip_address(ip_addr, IPADDR_STRLEN_MAX)) {
00393             return ip_addr;
00394         }
00395     }
00396 
00397     return NULL;
00398 }
00399 const char *nsapi_ppp_get_netmask(FileHandle *stream)
00400 {
00401 #if !LWIP_IPV4
00402     return NULL;
00403 #endif
00404 
00405     static char netmask[IPADDR_STRLEN_MAX];
00406     if (stream == my_stream) {
00407         if (mbed_lwip_get_netmask(netmask, IPADDR_STRLEN_MAX)) {
00408             return netmask;
00409         }
00410     }
00411 
00412     return NULL;
00413 }
00414 const char *nsapi_ppp_get_gw_addr(FileHandle *stream)
00415 {
00416 #if !LWIP_IPV4
00417     return NULL;
00418 #endif
00419 
00420     static char gwaddr[IPADDR_STRLEN_MAX];
00421     if (stream == my_stream) {
00422         if (mbed_lwip_get_gateway(gwaddr, IPADDR_STRLEN_MAX)) {
00423             return gwaddr;
00424         }
00425     }
00426 
00427     return NULL;
00428 }
00429 
00430 #endif /* LWIP_PPP_API */
00431 
00432 } // namespace mbed