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