Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
Generated on Tue Jul 12 2022 12:45:41 by
