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