RTC auf true
Diff: features/lwipstack/ppp_lwip.cpp
- Revision:
- 0:38ceb79fef03
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/lwipstack/ppp_lwip.cpp Wed Nov 28 15:10:15 2018 +0000 @@ -0,0 +1,465 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include "platform/FileHandle.h" +#include "platform/mbed_poll.h" +#include "events/EventQueue.h" +#include "mbed_trace.h" +#define TRACE_GROUP "LPPP" +#include "rtos/Thread.h" +#include "lwip/tcpip.h" +#include "lwip/tcp.h" +#include "lwip/ip.h" +#include "lwip/dns.h" +#include "lwip/pbuf.h" +extern "C" { // "pppos.h" is missing extern C +#include "netif/ppp/pppapi.h" +} + +#include "nsapi_ppp.h" +#include "ppp_lwip.h" +#include "LWIPStack.h" + +namespace mbed { + +using rtos::Thread; +using events::EventQueue; + +#if LWIP_PPP_API + +static EventQueue *event_queue; +static Thread *event_thread; +static volatile bool event_queued; +static nsapi_error_t connect_error_code; + +// Just one interface for now +static FileHandle *my_stream; +static LWIP::Interface *my_interface; +static ppp_pcb *my_ppp_pcb; +static bool ppp_active = false; +static bool blocking_connect = true; +static const char *login; +static const char *pwd; +static sys_sem_t ppp_close_sem; +static Callback<void(nsapi_event_t, intptr_t)> connection_status_cb; + +static EventQueue *prepare_event_queue() +{ + if (event_queue) { + return event_queue; + } + + // Should be trying to get a global shared event queue here! + // Shouldn't have to be making a private thread! + + // Only need to queue 2 events. new blows on failure. + event_queue = new EventQueue(2 * EVENTS_EVENT_SIZE, NULL); + event_thread = new Thread(osPriorityNormal, PPP_THREAD_STACK_SIZE); + + if (event_thread->start(callback(event_queue, &EventQueue::dispatch_forever)) != osOK) { + delete event_thread; + delete event_queue; + return NULL; + } + + return event_queue; +} + +static u32_t ppp_output(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) +{ + FileHandle *stream = my_stream; + if (!stream) { + return 0; + } + + pollfh fhs; + fhs.fh = stream; + fhs.events = POLLOUT; + + // LWIP expects us to block on write + // File handle will be in non-blocking mode, because of read events. + // Therefore must use poll to achieve the necessary block for writing. + + uint32_t written = 0; + while (len != 0) { + // Block forever until we're selected - don't care about reason we wake; + // return from write should tell us what's up. + poll(&fhs, 1, -1); + // This write will be non-blocking, but blocking would be fine. + ssize_t ret = stream->write(data, len); + if (ret == -EAGAIN) { + continue; + } else if (ret < 0) { + break; + } + written += ret; + data += ret; + len -= ret; + } + +// /tr_debug("> %ld bytes of data written\n", (long) written); + + return written; +} + +static void ppp_link_status(ppp_pcb *pcb, int err_code, void *ctx) +{ + nsapi_error_t mapped_err_code = NSAPI_ERROR_NO_CONNECTION; + + switch(err_code) { + case PPPERR_NONE: + mapped_err_code = NSAPI_ERROR_OK; + tr_info("status_cb: Connected"); +#if PPP_IPV4_SUPPORT + tr_debug(" our_ipaddr = %s", ipaddr_ntoa(&ppp_netif(pcb)->ip_addr)); + tr_debug(" his_ipaddr = %s", ipaddr_ntoa(&ppp_netif(pcb)->gw)); + tr_debug(" netmask = %s", ipaddr_ntoa(&ppp_netif(pcb)->netmask)); +#if LWIP_DNS + const ip_addr_t *ns; + ns = dns_getserver(0); + if (ns) { + tr_debug(" dns1 = %s", ipaddr_ntoa(ns)); + } + ns = dns_getserver(1); + if (ns) { + tr_debug(" dns2 = %s", ipaddr_ntoa(ns)); + } +#endif /* LWIP_DNS */ +#endif /* PPP_IPV4_SUPPORT */ +#if PPP_IPV6_SUPPORT + tr_debug(" our6_ipaddr = %s", ip6addr_ntoa(netif_ip6_addr(ppp_netif(pcb), 0))); +#endif /* PPP_IPV6_SUPPORT */ + break; + + case PPPERR_PARAM: + tr_info("status_cb: Invalid parameter"); + break; + + case PPPERR_OPEN: + tr_info("status_cb: Unable to open PPP session"); + break; + + case PPPERR_DEVICE: + tr_info("status_cb: Invalid I/O device for PPP"); + break; + + case PPPERR_ALLOC: + tr_info("status_cb: Unable to allocate resources"); + break; + + case PPPERR_USER: + tr_info("status_cb: User interrupt"); + break; + + case PPPERR_CONNECT: + tr_info("status_cb: Connection lost"); + mapped_err_code = NSAPI_ERROR_CONNECTION_LOST; + break; + + case PPPERR_AUTHFAIL: + tr_info("status_cb: Failed authentication challenge"); + mapped_err_code = NSAPI_ERROR_AUTH_FAILURE; + break; + + case PPPERR_PROTOCOL: + tr_info("status_cb: Failed to meet protocol"); + break; + + case PPPERR_PEERDEAD: + tr_info("status_cb: Connection timeout"); + mapped_err_code = NSAPI_ERROR_CONNECTION_TIMEOUT; + break; + + case PPPERR_IDLETIMEOUT: + tr_info("status_cb: Idle Timeout"); + break; + + case PPPERR_CONNECTTIME: + tr_info("status_cb: Max connect time reached"); + break; + + case PPPERR_LOOPBACK: + tr_info("status_cb: Loopback detected"); + break; + + default: + tr_info("status_cb: Unknown error code %d", err_code); + break; + + } + + if (err_code == PPPERR_NONE) { + /* status changes have to be reported */ + if (connection_status_cb) { + connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_GLOBAL_UP); + } + return; + } + + /* If some error happened, we need to properly shutdown the PPP interface */ + if (ppp_active) { + ppp_active = false; + connect_error_code = mapped_err_code; + sys_sem_signal(&ppp_close_sem); + } + + /* Alright, PPP interface is down, we need to notify upper layer */ + if (connection_status_cb) { + connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED); + } +} + +static void handle_modem_hangup() +{ + if (my_ppp_pcb->phase != PPP_PHASE_DEAD) { + ppp_close(my_ppp_pcb, 1); + } +} + +#if !PPP_INPROC_IRQ_SAFE +#error "PPP_INPROC_IRQ_SAFE must be enabled" +#endif +static void ppp_input() +{ + // Allow new events from now, avoiding potential races around the read + event_queued = false; + + if (!my_stream) { + return; + } + + // Non-blocking error check handler + pollfh fhs; + fhs.fh = my_stream; + fhs.events = POLLIN; + poll(&fhs, 1, 0); + if (fhs.revents & (POLLHUP|POLLERR|POLLNVAL)) { + handle_modem_hangup(); + return; + } + + // Infinite loop, but we assume that we can read faster than the + // serial, so we will fairly rapidly hit -EAGAIN. + for (;;) { + u8_t buffer[16]; + ssize_t len = my_stream->read(buffer, sizeof buffer); + if (len == -EAGAIN) { + break; + } else if (len <= 0) { + handle_modem_hangup(); + return; + } + pppos_input(my_ppp_pcb, buffer, len); + } + return; +} + +static void stream_cb() { + if (my_stream && !event_queued) { + event_queued = true; + if (event_queue->call(callback(ppp_input)) == 0) { + event_queued = false; + } + } +} + +extern "C" err_t ppp_lwip_connect(void *pcb) +{ +#if PPP_AUTH_SUPPORT + ppp_set_auth(my_ppp_pcb, PPPAUTHTYPE_ANY, login, pwd); +#endif //PPP_AUTH_SUPPORT + ppp_active = true; + err_t ret = ppp_connect(my_ppp_pcb, 0); + // lwIP's ppp.txt says input must not be called until after connect + if (ret == ERR_OK) { + my_stream->sigio(callback(stream_cb)); + } else { + ppp_active = false; + } + return ret; +} + +extern "C" err_t ppp_lwip_disconnect(void *pcb) +{ + err_t ret = ERR_OK; + if (ppp_active) { + ret = ppp_close(my_ppp_pcb, 0); + if (ret == ERR_OK) { + /* close call made, now let's catch the response in the status callback */ + sys_arch_sem_wait(&ppp_close_sem, 0); + } + ppp_active = false; + } + return ret; +} + +extern "C" nsapi_error_t ppp_lwip_if_init(void *pcb, struct netif *netif, const nsapi_ip_stack_t stack) +{ + if (!prepare_event_queue()) { + return NSAPI_ERROR_NO_MEMORY; + } + + if (!my_ppp_pcb) { + my_ppp_pcb = pppos_create(netif, + ppp_output, ppp_link_status, NULL); + if (!my_ppp_pcb) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + sys_sem_new(&ppp_close_sem, 0); + } + +#if LWIP_IPV4 + if (stack != IPV6_STACK) { + ppp_set_usepeerdns(my_ppp_pcb, true); + } +#endif + +#if LWIP_IPV4 && LWIP_IPV6 + if (stack == IPV4_STACK) { + my_ppp_pcb->ipv6cp_disabled = true; + } else if (stack == IPV6_STACK) { + my_ppp_pcb->ipcp_disabled = true; + } +#endif + + return NSAPI_ERROR_OK; +} + +nsapi_error_t nsapi_ppp_error_code() +{ + return connect_error_code; +} + +nsapi_error_t nsapi_ppp_set_blocking(bool blocking) +{ + blocking_connect = blocking; + return NSAPI_ERROR_OK; +} + +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) +{ + if (my_stream) { + return NSAPI_ERROR_PARAMETER; + } + my_stream = stream; + stream->set_blocking(false); + connection_status_cb = cb; + login = uname; + pwd = password; + + nsapi_error_t retcode; + + if (!my_interface) { + LWIP &lwip = LWIP::get_instance(); + retcode = lwip._add_ppp_interface(stream, true, stack, &my_interface); + if (retcode != NSAPI_ERROR_OK) { + my_interface = NULL; + my_stream->set_blocking(true); + my_stream = NULL; + connection_status_cb = NULL; + return retcode; + } + } + + // mustn't start calling input until after connect - + // attach deferred until ppp_lwip_connect, called from mbed_lwip_bringup + retcode = my_interface->bringup(false, NULL, NULL, NULL, stack, blocking_connect); + + if (retcode != NSAPI_ERROR_OK) { + connection_status_cb = NULL; + my_stream->sigio(NULL); + my_stream->set_blocking(true); + my_stream = NULL; + } + + if (retcode != NSAPI_ERROR_OK && connect_error_code != NSAPI_ERROR_OK) { + return connect_error_code; + } else { + return retcode; + } +} + +nsapi_error_t nsapi_ppp_disconnect(FileHandle *stream) +{ + if (my_stream != stream) { + return NSAPI_ERROR_NO_CONNECTION; + } + + nsapi_error_t retcode = my_interface->bringdown(); + + connection_status_cb = NULL; + /* Detach callbacks, and put handle back to default blocking mode */ + my_stream->sigio(NULL); + my_stream->set_blocking(true); + my_stream = NULL; + + return retcode; +} + +NetworkStack *nsapi_ppp_get_stack() +{ + return &LWIP::get_instance(); +} + +const char *nsapi_ppp_get_ip_addr(FileHandle *stream) +{ + static char ip_addr[IPADDR_STRLEN_MAX]; + + if (stream == my_stream) { + + if (my_interface->get_ip_address(ip_addr, IPADDR_STRLEN_MAX)) { + return ip_addr; + } + } + + return NULL; +} +const char *nsapi_ppp_get_netmask(FileHandle *stream) +{ +#if !LWIP_IPV4 + return NULL; +#endif + + static char netmask[IPADDR_STRLEN_MAX]; + if (stream == my_stream) { + if (my_interface->get_netmask(netmask, IPADDR_STRLEN_MAX)) { + return netmask; + } + } + + return NULL; +} +const char *nsapi_ppp_get_gw_addr(FileHandle *stream) +{ +#if !LWIP_IPV4 + return NULL; +#endif + + static char gwaddr[IPADDR_STRLEN_MAX]; + if (stream == my_stream) { + if (my_interface->get_gateway(gwaddr, IPADDR_STRLEN_MAX)) { + return gwaddr; + } + } + + return NULL; +} + +#endif /* LWIP_PPP_API */ + +} // namespace mbed