Merge pull request #4119 from hasnainvirk/cellular_feature_br

Cellular feature br
pull/4422/head
Martin Kojtal 2017-06-01 14:01:17 +02:00 committed by GitHub
commit 8a870a66c0
68 changed files with 4685 additions and 396 deletions

View File

@ -15,8 +15,10 @@
*/
#include "drivers/RawSerial.h"
#include "platform/mbed_wait_api.h"
#include <stdio.h>
#include <cstdarg>
#if DEVICE_SERIAL
#define STRING_STACK_LIMIT 120

View File

@ -81,6 +81,23 @@ public:
*/
Serial(PinName tx, PinName rx, int baud);
/* Stream gives us a FileHandle with non-functional poll()/readable()/writable. Pass through
* the calls from the SerialBase instead for backwards compatibility. This problem is
* part of why Stream and Serial should be deprecated.
*/
bool readable()
{
return SerialBase::readable();
}
bool writable()
{
return SerialBase::writeable();
}
bool writeable()
{
return SerialBase::writeable();
}
protected:
virtual int _getc();
virtual int _putc(int c);

View File

@ -20,7 +20,6 @@
#if defined (DEVICE_SERIAL) || defined(DOXYGEN_ONLY)
#include "Stream.h"
#include "Callback.h"
#include "serial_api.h"
#include "mbed_toolchain.h"

265
drivers/UARTSerial.cpp Normal file
View File

@ -0,0 +1,265 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2017 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.
*/
#if DEVICE_SERIAL
#include <errno.h>
#include "UARTSerial.h"
#include "platform/mbed_poll.h"
#include "platform/mbed_wait_api.h"
namespace mbed {
UARTSerial::UARTSerial(PinName tx, PinName rx, int baud) :
SerialBase(tx, rx, baud),
_blocking(true),
_tx_irq_enabled(false),
_dcd_irq(NULL)
{
/* Attatch IRQ routines to the serial device. */
SerialBase::attach(callback(this, &UARTSerial::rx_irq), RxIrq);
}
UARTSerial::~UARTSerial()
{
delete _dcd_irq;
}
void UARTSerial::dcd_irq()
{
wake();
}
void UARTSerial::set_data_carrier_detect(PinName dcd_pin, bool active_high)
{
delete _dcd_irq;
_dcd_irq = NULL;
if (dcd_pin != NC) {
_dcd_irq = new InterruptIn(dcd_pin);
if (active_high) {
_dcd_irq->fall(callback(this, &UARTSerial::dcd_irq));
} else {
_dcd_irq->rise(callback(this, &UARTSerial::dcd_irq));
}
}
}
int UARTSerial::close()
{
/* Does not let us pass a file descriptor. So how to close ?
* Also, does it make sense to close a device type file descriptor*/
return 0;
}
int UARTSerial::isatty()
{
return 1;
}
off_t UARTSerial::seek(off_t offset, int whence)
{
/*XXX lseek can be done theoratically, but is it sane to mark positions on a dynamically growing/shrinking
* buffer system (from an interrupt context) */
return -ESPIPE;
}
int UARTSerial::sync()
{
lock();
while (!_txbuf.empty()) {
unlock();
// Doing better than wait would require TxIRQ to also do wake() when becoming empty. Worth it?
wait_ms(1);
lock();
}
unlock();
return 0;
}
void UARTSerial::sigio(Callback<void()> func) {
core_util_critical_section_enter();
_sigio_cb = func;
if (_sigio_cb) {
short current_events = poll(0x7FFF);
if (current_events) {
_sigio_cb();
}
}
core_util_critical_section_exit();
}
ssize_t UARTSerial::write(const void* buffer, size_t length)
{
size_t data_written = 0;
const char *buf_ptr = static_cast<const char *>(buffer);
lock();
while (_txbuf.full()) {
if (!_blocking) {
unlock();
return -EAGAIN;
}
unlock();
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
lock();
}
while (data_written < length && !_txbuf.full()) {
_txbuf.push(*buf_ptr++);
data_written++;
}
core_util_critical_section_enter();
if (!_tx_irq_enabled) {
UARTSerial::tx_irq(); // only write to hardware in one place
if (!_txbuf.empty()) {
SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq);
_tx_irq_enabled = true;
}
}
core_util_critical_section_exit();
unlock();
return data_written;
}
ssize_t UARTSerial::read(void* buffer, size_t length)
{
size_t data_read = 0;
char *ptr = static_cast<char *>(buffer);
lock();
while (_rxbuf.empty()) {
if (!_blocking) {
unlock();
return -EAGAIN;
}
unlock();
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
lock();
}
while (data_read < length && !_rxbuf.empty()) {
_rxbuf.pop(*ptr++);
data_read++;
}
unlock();
return data_read;
}
bool UARTSerial::hup() const
{
return _dcd_irq && _dcd_irq->read() != 0;
}
void UARTSerial::wake()
{
if (_sigio_cb) {
_sigio_cb();
}
}
short UARTSerial::poll(short events) const {
short revents = 0;
/* Check the Circular Buffer if space available for writing out */
if (!_rxbuf.empty()) {
revents |= POLLIN;
}
/* POLLHUP and POLLOUT are mutually exclusive */
if (hup()) {
revents |= POLLHUP;
} else if (!_txbuf.full()) {
revents |= POLLOUT;
}
/*TODO Handle other event types */
return revents;
}
void UARTSerial::lock(void)
{
_mutex.lock();
}
void UARTSerial::unlock(void)
{
_mutex.unlock();
}
void UARTSerial::rx_irq(void)
{
bool was_empty = _rxbuf.empty();
/* Fill in the receive buffer if the peripheral is readable
* and receive buffer is not full. */
while (SerialBase::readable()) {
char data = SerialBase::_base_getc();
if (!_rxbuf.full()) {
_rxbuf.push(data);
} else {
/* Drop - can we report in some way? */
}
}
/* Report the File handler that data is ready to be read from the buffer. */
if (was_empty && !_rxbuf.empty()) {
wake();
}
}
// Also called from write to start transfer
void UARTSerial::tx_irq(void)
{
bool was_full = _txbuf.full();
/* Write to the peripheral if there is something to write
* and if the peripheral is available to write. */
while (!_txbuf.empty() && SerialBase::writeable()) {
char data;
_txbuf.pop(data);
SerialBase::_base_putc(data);
}
if (_tx_irq_enabled && _txbuf.empty()) {
SerialBase::attach(NULL, TxIrq);
_tx_irq_enabled = false;
}
/* Report the File handler that data can be written to peripheral. */
if (was_full && !_txbuf.full() && !hup()) {
wake();
}
}
} //namespace mbed
#endif //DEVICE_SERIAL

198
drivers/UARTSerial.h Normal file
View File

@ -0,0 +1,198 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2017 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.
*/
#ifndef MBED_UARTSERIAL_H
#define MBED_UARTSERIAL_H
#include "platform/platform.h"
#if DEVICE_SERIAL
#include "FileHandle.h"
#include "SerialBase.h"
#include "InterruptIn.h"
#include "PlatformMutex.h"
#include "serial_api.h"
#include "CircularBuffer.h"
#ifndef MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE
#define MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE 256
#endif
#ifndef MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE
#define MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE 256
#endif
namespace mbed {
class UARTSerial : private SerialBase, public FileHandle {
public:
/** Create a UARTSerial port, connected to the specified transmit and receive pins, with a particular baud rate.
* @param tx Transmit pin
* @param rx Receive pin
* @param baud The baud rate of the serial port (optional, defaults to MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE)
*/
UARTSerial(PinName tx, PinName rx, int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE);
virtual ~UARTSerial();
/** Equivalent to POSIX poll(). Derived from FileHandle.
* Provides a mechanism to multiplex input/output over a set of file handles.
*/
virtual short poll(short events) const;
/** Write the contents of a buffer to a file
*
* @param buffer The buffer to write from
* @param size The number of bytes to write
* @return The number of bytes written, negative error on failure
*/
virtual ssize_t write(const void* buffer, size_t length);
/** Read the contents of a file into a buffer
*
* Follows POSIX semantics:
*
* * if no data is available, and non-blocking set return -EAGAIN
* * if no data is available, and blocking set, wait until data is available
* * If any data is available, call returns immediately
*
* @param buffer The buffer to read in to
* @param size The number of bytes to read
* @return The number of bytes read, 0 at end of file, negative error on failure
*/
virtual ssize_t read(void* buffer, size_t length);
/** Acquire mutex */
virtual void lock(void);
/** Release mutex */
virtual void unlock(void);
/** Close a file
*
* @return 0 on success, negative error code on failure
*/
virtual int close();
/** Check if the file in an interactive terminal device
*
* @return True if the file is a terminal
* @return False if the file is not a terminal
* @return Negative error code on failure
*/
virtual int isatty();
/** Move the file position to a given offset from from a given location
*
* Not valid for a device type FileHandle like UARTSerial.
* In case of UARTSerial, returns ESPIPE
*
* @param offset The offset from whence to move to
* @param whence The start of where to seek
* SEEK_SET to start from beginning of file,
* SEEK_CUR to start from current position in file,
* SEEK_END to start from end of file
* @return The new offset of the file, negative error code on failure
*/
virtual off_t seek(off_t offset, int whence);
/** Flush any buffers associated with the file
*
* @return 0 on success, negative error code on failure
*/
virtual int sync();
/** Set blocking or non-blocking mode
* The default is blocking.
*
* @param blocking true for blocking mode, false for non-blocking mode.
*/
virtual int set_blocking(bool blocking)
{
_blocking = blocking;
return 0;
}
/** Register a callback on state change of the file.
*
* The specified callback will be called on state changes such as when
* the file can be written to or read from.
*
* The callback may be called in an interrupt context and should not
* perform expensive operations.
*
* Note! This is not intended as an attach-like asynchronous api, but rather
* as a building block for constructing such functionality.
*
* The exact timing of when the registered function
* is called is not guaranteed and susceptible to change. It should be used
* as a cue to make read/write/poll calls to find the current state.
*
* @param func Function to call on state change
*/
virtual void sigio(Callback<void()> func);
/** Setup interrupt handler for DCD line
*
* If DCD line is connected, an IRQ handler will be setup.
* Does nothing if DCD is NC, i.e., not connected.
*
* @param dcd_pin Pin-name for DCD
* @param active_high a boolean set to true if DCD polarity is active low
*/
void set_data_carrier_detect(PinName dcd_pin, bool active_high = false);
private:
/** Software serial buffers
* By default buffer size is 256 for TX and 256 for RX. Configurable through mbed_app.json
*/
CircularBuffer<char, MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE> _rxbuf;
CircularBuffer<char, MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE> _txbuf;
PlatformMutex _mutex;
Callback<void()> _sigio_cb;
bool _blocking;
bool _tx_irq_enabled;
InterruptIn *_dcd_irq;
/** Device Hanged up
* Determines if the device hanged up on us.
*
* @return True, if hanged up
*/
bool hup() const;
/** ISRs for serial
* Routines to handle interrupts on serial pins.
* Copies data into Circular Buffer.
* Reports the state change to File handle.
*/
void tx_irq(void);
void rx_irq(void);
void wake(void);
void dcd_irq(void);
};
} //namespace mbed
#endif //DEVICE_SERIAL
#endif //MBED_UARTSERIAL_H

13
drivers/mbed_lib.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "drivers",
"config": {
"uart-serial-txbuf-size": {
"help": "Default TX buffer size for a UARTSerial instance (unit Bytes))",
"value": 256
},
"uart-serial-rxbuf-size": {
"help": "Default RX buffer size for a UARTSerial instance (unit Bytes))",
"value": 256
}
}
}

View File

@ -339,6 +339,11 @@ void mbed_vtracef(uint8_t dlevel, const char* grp, const char *fmt, va_list ap)
if (plain == true || dlevel == TRACE_LEVEL_CMD) {
//add trace data
retval = vsnprintf(ptr, bLeft, fmt, ap);
//convenience - trim off one trailing \n. Useful if trying to directly
//connect debug layers that do expect callers to pass \n to mbed_trace.
if (retval > 0 && retval < bLeft && ptr[retval - 1] == '\n') {
ptr[--retval] = '\0';
}
if (dlevel == TRACE_LEVEL_CMD && m_trace.cmd_printf) {
m_trace.cmd_printf(m_trace.line);
m_trace.cmd_printf("\n");
@ -441,6 +446,12 @@ void mbed_vtracef(uint8_t dlevel, const char* grp, const char *fmt, va_list ap)
if (retval > 0) {
ptr += retval;
bLeft -= retval;
//convenience - trim off one trailing \n. Useful if trying to directly
//connect debug layers that do expect callers to pass \n to mbed_trace.
if (ptr[-1] == '\n') {
*--ptr = '\0';
++bLeft;
}
}
}

View File

@ -1,7 +1,6 @@
lwip/doc/*
lwip/test/*
lwip/src/apps/*
lwip/src/netif/ppp/*
lwip/src/netif/lwip_slipif.c
lwip/src/include/lwip/apps/*
lwip/src/include/posix/*

View File

@ -46,7 +46,7 @@ nsapi_error_t EthernetInterface::set_dhcp(bool dhcp)
nsapi_error_t EthernetInterface::connect()
{
return mbed_lwip_bringup(_dhcp,
return mbed_lwip_bringup(_dhcp, false,
_ip_address[0] ? _ip_address : 0,
_netmask[0] ? _netmask : 0,
_gateway[0] ? _gateway : 0);
@ -54,7 +54,7 @@ nsapi_error_t EthernetInterface::connect()
nsapi_error_t EthernetInterface::disconnect()
{
return mbed_lwip_bringdown();
return mbed_lwip_bringdown(false);
}
const char *EthernetInterface::get_mac_address()

View File

@ -43,6 +43,8 @@
#include "mbed_interface.h"
#include <string.h>
#if LWIP_ARP || LWIP_ETHERNET
#ifndef LPC_EMAC_RMII
#error LPC_EMAC_RMII is not defined!
#endif
@ -1058,4 +1060,6 @@ void eth_arch_disable_interrupts(void) {
* @}
*/
#endif /* LWIP_ARP || LWIP_ETHERNET */
/* --------------------------------- End Of File ------------------------------ */

View File

@ -31,6 +31,8 @@
#include "lpc_phy.h"
#include "lpc17xx_emac.h"
#if LWIP_ARP || LWIP_ETHERNET
/** @defgroup dp83848_phy PHY status and control for the DP83848.
* @ingroup lwip_phy
*
@ -431,6 +433,8 @@ s32_t lpc_phy_sts_sm(struct netif *netif)
return changed;
}
#endif /* LWIP_ARP || LWIP_ETHERNET */
/**
* @}
*/

View File

@ -7,6 +7,12 @@
#include "cmsis_os.h"
#include "mbed_interface.h"
// Check for LWIP having Ethernet enabled
#if LWIP_ARP || LWIP_ETHERNET
// Check for Ethernet HAL being present
#ifdef ETH_SUCCESS
#define RECV_TASK_PRI (osPriorityHigh)
#define PHY_TASK_PRI (osPriorityLow)
#define PHY_TASK_WAIT (200)
@ -512,3 +518,7 @@ void mbed_default_mac_address(char *mac) {
return;
}
#endif //ETH_SUCCESS
#endif // LWIP_ARP || LWIP_ETHERNET

View File

@ -35,6 +35,11 @@
#include <stdint.h>
#include <stddef.h> /* for size_t */
#if LWIP_USE_EXTERNAL_MBEDTLS
#include "mbedtls/md5.h"
#endif
/* ARM/LPC17xx is little endian only */
#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
#ifdef BYTE_ORDER
@ -88,16 +93,28 @@
#ifdef LWIP_DEBUG
#include "stdio.h"
#if MBED_CONF_LWIP_USE_MBED_TRACE
void lwip_mbed_tracef_debug(const char *fmt, ...);
void lwip_mbed_tracef_error(const char *fmt, ...);
void lwip_mbed_tracef_warn(const char *fmt, ...);
void lwip_mbed_assert_fail(const char *msg, const char *func, const char *file, unsigned int line);
#define LWIP_PLATFORM_DIAG(vars) lwip_mbed_tracef_debug vars
#define LWIP_PLATFORM_DIAG_SEVERE(vars) lwip_mbed_tracef_error vars
#define LWIP_PLATFORM_DIAG_SERIOUS(vars) lwip_mbed_tracef_error vars
#define LWIP_PLATFORM_DIAG_WARNING(vars) lwip_mbed_tracef_warn vars
#define LWIP_PLATFORM_ASSERT(message) lwip_mbed_assert_fail(message, __func__, __FILE__, __LINE__)
#else // MBED_CONF_LWIP_USE_MBED_TRACE
#include <stdio.h>
void assert_printf(char *msg, int line, char *file);
/* Plaform specific diagnostic output */
#define LWIP_PLATFORM_DIAG(vars) printf vars
#define LWIP_PLATFORM_ASSERT(flag) { assert_printf((flag), __LINE__, __FILE__); }
#else
#define LWIP_PLATFORM_DIAG(msg) { ; }
#define LWIP_PLATFORM_ASSERT(flag) { ; }
#endif // MBED_CONF_LWIP_USE_MBED_TRACE
#endif
#include "cmsis.h"

View File

@ -489,6 +489,42 @@ sys_thread_t sys_thread_new(const char *pcName,
#ifdef LWIP_DEBUG
#include <stdbool.h>
#if MBED_CONF_LWIP_USE_MBED_TRACE
#include "mbed-trace/mbed_trace.h"
void lwip_mbed_tracef_debug(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
mbed_vtracef(TRACE_LEVEL_DEBUG, "lwIP", fmt, ap);
va_end(ap);
}
void lwip_mbed_tracef_warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
mbed_vtracef(TRACE_LEVEL_WARN, "lwIP", fmt, ap);
va_end(ap);
}
void lwip_mbed_tracef_error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
mbed_vtracef(TRACE_LEVEL_ERROR, "lwIP", fmt, ap);
va_end(ap);
}
void lwip_mbed_assert_fail(const char *msg, const char *func, const char *file, unsigned int line)
{
mbed_tracef(TRACE_LEVEL_ERROR, "lwIP", "Assertion failed: %s, function %s, file %s, line %u.", msg, func, file, line);
exit(EXIT_FAILURE); // XXX how about abort? mbed_assert uses exit, so follow suit
}
#else // MBED_CONF_LWIP_USE_MBED_TRACE
/** \brief Displays an error message on assertion
This function will display an error message on an assertion
@ -504,5 +540,6 @@ void assert_printf(char *msg, int line, char *file) {
else
error("LWIP ASSERT\n");
}
#endif // MBED_CONF_LWIP_USE_MBED_TRACE
#endif /* LWIP_DEBUG */

View File

@ -76,19 +76,21 @@
#include <string.h>
/* pull in md5 of ppp? */
#if !PPP_SUPPORT
#undef PPP_SUPPORT
#define PPP_SUPPORT 1
#define PPP_FAKED_ON 1
#endif
#include "netif/ppp/ppp_opts.h"
#include "netif/ppp/ppp.h"
#include "netif/ppp/pppcrypt.h"
#if !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS
#if PPP_FAKED_ON && !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS
#undef LWIP_INCLUDED_POLARSSL_MD5
#define LWIP_INCLUDED_POLARSSL_MD5 1
#include "netif/ppp/polarssl/lwip_md5.c"
#endif
#if LWIP_USE_EXTERNAL_MBEDTLS
#include "mbedtls/inc/mbedtls/md5.h"
#define md5_context mbedtls_md5_context
#endif
static u8_t input[64];
static u32_t base_time;
@ -124,7 +126,7 @@ u32_t
lwip_hook_tcp_isn(const ip_addr_t *local_ip, u16_t local_port,
const ip_addr_t *remote_ip, u16_t remote_port)
{
md5_context ctx;
lwip_md5_context ctx;
u8_t output[16];
u32_t isn;

View File

@ -148,6 +148,24 @@
#ifndef LWIP_PLATFORM_DIAG
#error "If you want to use LWIP_DEBUG, LWIP_PLATFORM_DIAG(message) needs to be defined in your arch/cc.h"
#endif
#ifdef LWIP_PLATFORM_DIAG_SERIOUS
#define LWIP_DEBUGF(debug, message) do { \
if ( \
((debug) & LWIP_DBG_ON) && \
((debug) & LWIP_DBG_TYPES_ON) && \
((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \
switch ((debug) & LWIP_DBG_MASK_LEVEL) { \
case LWIP_DBG_LEVEL_SERIOUS: LWIP_PLATFORM_DIAG_SERIOUS(message); break; \
case LWIP_DBG_LEVEL_SEVERE: LWIP_PLATFORM_DIAG_SEVERE(message); break; \
case LWIP_DBG_LEVEL_WARNING: LWIP_PLATFORM_DIAG_WARNING(message); break; \
default: LWIP_PLATFORM_DIAG(message); break; \
} \
if ((debug) & LWIP_DBG_HALT) { \
while(1); \
} \
} \
} while(0)
#else
#define LWIP_DEBUGF(debug, message) do { \
if ( \
((debug) & LWIP_DBG_ON) && \
@ -159,7 +177,7 @@
} \
} \
} while(0)
#endif
#else /* LWIP_DEBUG */
#define LWIP_DEBUGF(debug, message)
#endif /* LWIP_DEBUG */

View File

@ -605,7 +605,8 @@ void ppp_print_string(const u_char *p, int len, void (*printer) (void *, const c
* ppp_logit - does the hard work for fatal et al.
*/
static void ppp_logit(int level, const char *fmt, va_list args) {
char buf[1024];
char buf[256];
ppp_vslprintf(buf, sizeof(buf), fmt, args);
ppp_log_write(level, buf);

View File

@ -16,6 +16,7 @@
#include "nsapi.h"
#include "mbed_interface.h"
#include "mbed_assert.h"
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
@ -32,10 +33,13 @@
#include "lwip/mld6.h"
#include "lwip/dns.h"
#include "lwip/udp.h"
#include "netif/lwip_ethernet.h"
#include "emac_api.h"
#include "ppp_lwip.h"
#include "lwip_tcp_isn.h"
static nsapi_error_t mbed_lwip_err_remap(err_t err);
#if DEVICE_EMAC
#define MBED_NETIF_INIT_FN emac_lwip_if_init
#else
@ -56,12 +60,10 @@ static struct lwip_socket {
void *data;
} lwip_arena[MEMP_NUM_NETCONN];
static bool lwip_inited = false;
static bool lwip_connected = false;
static void mbed_lwip_arena_init(void)
{
memset(lwip_arena, 0, sizeof lwip_arena);
}
static bool netif_inited = false;
static bool netif_is_ppp = false;
static struct lwip_socket *mbed_lwip_arena_alloc(void)
{
@ -109,7 +111,7 @@ static void mbed_lwip_socket_callback(struct netconn *nc, enum netconn_evt eh, u
/* TCP/IP and Network Interface Initialisation */
static struct netif lwip_netif;
#if LWIP_IPV4
#if LWIP_DHCP
static bool lwip_dhcp = false;
#endif
static char lwip_mac_address[NSAPI_MAC_SIZE];
@ -297,10 +299,13 @@ static void mbed_lwip_tcpip_init_irq(void *eh)
}
static sys_sem_t lwip_netif_linked;
static sys_sem_t lwip_netif_unlinked;
static void mbed_lwip_netif_link_irq(struct netif *lwip_netif)
{
if (netif_is_link_up(lwip_netif)) {
sys_sem_signal(&lwip_netif_linked);
} else {
sys_sem_signal(&lwip_netif_unlinked);
}
}
@ -326,32 +331,43 @@ static void mbed_lwip_netif_status_irq(struct netif *lwip_netif)
}
}
static void mbed_lwip_set_mac_address(void)
#if LWIP_ETHERNET
static void mbed_lwip_set_mac_address(struct netif *netif)
{
#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE)
(void) snprintf(lwip_mac_address, NSAPI_MAC_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
MBED_MAC_ADDR_0, MBED_MAC_ADDR_1, MBED_MAC_ADDR_2,
MBED_MAC_ADDR_3, MBED_MAC_ADDR_4, MBED_MAC_ADDR_5);
netif->hwaddr[0] = MBED_MAC_ADDR_0;
netif->hwaddr[1] = MBED_MAC_ADDR_1;
netif->hwaddr[2] = MBED_MAC_ADDR_2;
netif->hwaddr[3] = MBED_MAC_ADDR_3;
netif->hwaddr[4] = MBED_MAC_ADDR_4;
netif->hwaddr[5] = MBED_MAC_ADDR_5;
#else
char mac[6];
mbed_mac_address(mac);
(void) snprintf(lwip_mac_address, NSAPI_MAC_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
mbed_mac_address((char *)netif->hwaddr);
#endif
netif->hwaddr_len = ETH_HWADDR_LEN;
/* Use mac address as additional seed to random number generator */
uint64_t seed = mac[0];
uint64_t seed = netif->hwaddr[0];
for (uint8_t i = 1; i < 8; i++) {
seed <<= 8;
seed |= mac[i % 6];
seed |= netif->hwaddr[i % 6];
}
lwip_add_random_seed(seed);
}
static void mbed_lwip_record_mac_address(const struct netif *netif)
{
const u8_t *mac = netif->hwaddr;
snprintf(lwip_mac_address, NSAPI_MAC_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
#endif // LWIP_ETHERNET
/* LWIP interface implementation */
const char *mbed_lwip_get_mac_address(void)
{
return lwip_mac_address[0] ? lwip_mac_address : 0;
return lwip_mac_address[0] ? lwip_mac_address : NULL;
}
char *mbed_lwip_get_ip_address(char *buf, nsapi_size_t buflen)
@ -403,15 +419,14 @@ char *mbed_lwip_get_gateway(char *buf, nsapi_size_t buflen)
#endif
}
nsapi_error_t mbed_lwip_init(emac_interface_t *emac)
static void mbed_lwip_core_init(void)
{
// Check if we've already brought up lwip
if (!mbed_lwip_get_mac_address()) {
// Seed lwip random
if (!lwip_inited) {
// Seed lwip random
lwip_seed_random();
// Set up network
// Initialise TCP sequence number
uint32_t tcp_isn_secret[4];
for (int i = 0; i < 4; i++) {
@ -421,50 +436,94 @@ nsapi_error_t mbed_lwip_init(emac_interface_t *emac)
sys_sem_new(&lwip_tcpip_inited, 0);
sys_sem_new(&lwip_netif_linked, 0);
sys_sem_new(&lwip_netif_unlinked, 0);
sys_sem_new(&lwip_netif_has_addr, 0);
tcpip_init(mbed_lwip_tcpip_init_irq, NULL);
sys_arch_sem_wait(&lwip_tcpip_inited, 0);
memset(&lwip_netif, 0, sizeof lwip_netif);
if (!netif_add(&lwip_netif,
#if LWIP_IPV4
0, 0, 0,
#endif
emac, MBED_NETIF_INIT_FN, tcpip_input)) {
return NSAPI_ERROR_DEVICE_ERROR;
}
mbed_lwip_set_mac_address();
netif_set_default(&lwip_netif);
netif_set_link_callback(&lwip_netif, mbed_lwip_netif_link_irq);
netif_set_status_callback(&lwip_netif, mbed_lwip_netif_status_irq);
#if !DEVICE_EMAC
eth_arch_enable_interrupts();
#endif
lwip_inited = true;
}
return NSAPI_ERROR_OK;
}
nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask, const char *gw)
nsapi_error_t mbed_lwip_emac_init(emac_interface_t *emac)
{
#if LWIP_ETHERNET
// Choose a MAC address - driver can override
mbed_lwip_set_mac_address(&lwip_netif);
// Set up network
if (!netif_add(&lwip_netif,
#if LWIP_IPV4
0, 0, 0,
#endif
emac, MBED_NETIF_INIT_FN, tcpip_input)) {
return NSAPI_ERROR_DEVICE_ERROR;
}
// Note the MAC address actually in use
mbed_lwip_record_mac_address(&lwip_netif);
#if !DEVICE_EMAC
eth_arch_enable_interrupts();
#endif
return NSAPI_ERROR_OK;
#else
return NSAPI_ERROR_UNSUPPORTED;
#endif //LWIP_ETHERNET
}
// Backwards compatibility with people using DEVICE_EMAC
nsapi_error_t mbed_lwip_init(emac_interface_t *emac)
{
mbed_lwip_core_init();
return mbed_lwip_emac_init(emac);
}
nsapi_error_t mbed_lwip_bringup(bool dhcp, bool ppp, const char *ip, const char *netmask, const char *gw)
{
// Check if we've already connected
if (lwip_connected) {
return NSAPI_ERROR_PARAMETER;
}
if(mbed_lwip_init(NULL) != NSAPI_ERROR_OK) {
return NSAPI_ERROR_DEVICE_ERROR;
mbed_lwip_core_init();
nsapi_error_t ret;
if (netif_inited) {
/* Can't cope with changing mode */
if (netif_is_ppp == ppp) {
ret = NSAPI_ERROR_OK;
} else {
ret = NSAPI_ERROR_PARAMETER;
}
} else {
if (ppp) {
ret = ppp_lwip_if_init(&lwip_netif);
} else {
ret = mbed_lwip_emac_init(NULL);
}
}
// Zero out socket set
mbed_lwip_arena_init();
if (ret != NSAPI_ERROR_OK) {
return ret;
}
netif_inited = true;
if (ppp) {
netif_is_ppp = ppp;
}
netif_set_default(&lwip_netif);
netif_set_link_callback(&lwip_netif, mbed_lwip_netif_link_irq);
netif_set_status_callback(&lwip_netif, mbed_lwip_netif_status_irq);
#if LWIP_IPV6
netif_create_ip6_linklocal_address(&lwip_netif, 1/*from MAC*/);
if (lwip_netif.hwaddr_len == ETH_HWADDR_LEN) {
netif_create_ip6_linklocal_address(&lwip_netif, 1/*from MAC*/);
}
#if LWIP_IPV6_MLD
/*
* For hardware/netifs that implement MAC filtering.
@ -480,23 +539,12 @@ nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask,
#if LWIP_IPV6_AUTOCONFIG
/* IPv6 address autoconfiguration not enabled by default */
lwip_netif.ip6_autoconfig_enabled = 1;
lwip_netif.ip6_autoconfig_enabled = 1;
#endif /* LWIP_IPV6_AUTOCONFIG */
#endif
u32_t ret;
if (!netif_is_link_up(&lwip_netif)) {
ret = sys_arch_sem_wait(&lwip_netif_linked, 15000);
if (ret == SYS_ARCH_TIMEOUT) {
return NSAPI_ERROR_NO_CONNECTION;
}
}
#endif // LWIP_IPV6
#if LWIP_IPV4
if (!dhcp) {
if (!dhcp && !ppp) {
ip4_addr_t ip_addr;
ip4_addr_t netmask_addr;
ip4_addr_t gw_addr;
@ -511,9 +559,27 @@ nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask,
}
#endif
netif_set_up(&lwip_netif);
if (ppp) {
err_t err = ppp_lwip_connect();
if (err) {
return mbed_lwip_err_remap(err);
}
}
#if LWIP_IPV4
if (!netif_is_link_up(&lwip_netif)) {
if (sys_arch_sem_wait(&lwip_netif_linked, 15000) == SYS_ARCH_TIMEOUT) {
if (ppp) {
ppp_lwip_disconnect();
}
return NSAPI_ERROR_NO_CONNECTION;
}
}
if (!ppp) {
netif_set_up(&lwip_netif);
}
#if LWIP_DHCP
// Connect to the network
lwip_dhcp = dhcp;
@ -527,8 +593,10 @@ nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask,
// If doesn't have address
if (!mbed_lwip_get_ip_addr(true, &lwip_netif)) {
ret = sys_arch_sem_wait(&lwip_netif_has_addr, 15000);
if (ret == SYS_ARCH_TIMEOUT) {
if (sys_arch_sem_wait(&lwip_netif_has_addr, 15000) == SYS_ARCH_TIMEOUT) {
if (ppp) {
ppp_lwip_disconnect();
}
return NSAPI_ERROR_DHCP_FAILURE;
}
}
@ -537,7 +605,7 @@ nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask,
// If address is not for preferred stack waits a while to see
// if preferred stack address is acquired
if (!mbed_lwip_get_ip_addr(false, &lwip_netif)) {
ret = sys_arch_sem_wait(&lwip_netif_has_addr, ADDR_TIMEOUT * 1000);
sys_arch_sem_wait(&lwip_netif_has_addr, ADDR_TIMEOUT * 1000);
}
#endif
@ -556,14 +624,14 @@ void mbed_lwip_clear_ipv6_addresses(struct netif *lwip_netif)
}
#endif
nsapi_error_t mbed_lwip_bringdown(void)
nsapi_error_t mbed_lwip_bringdown(bool ppp)
{
// Check if we've connected
if (!lwip_connected) {
return NSAPI_ERROR_PARAMETER;
}
#if LWIP_IPV4
#if LWIP_DHCP
// Disconnect from the network
if (lwip_dhcp) {
dhcp_release(&lwip_netif);
@ -572,14 +640,30 @@ nsapi_error_t mbed_lwip_bringdown(void)
}
#endif
netif_set_down(&lwip_netif);
if (ppp) {
/* this is a blocking call, returns when PPP is properly closed */
err_t err = ppp_lwip_disconnect();
if (err) {
return mbed_lwip_err_remap(err);
}
MBED_ASSERT(!netif_is_link_up(&lwip_netif));
/*if (netif_is_link_up(&lwip_netif)) {
if (sys_arch_sem_wait(&lwip_netif_unlinked, 15000) == SYS_ARCH_TIMEOUT) {
return NSAPI_ERROR_DEVICE_ERROR;
}
}*/
} else {
netif_set_down(&lwip_netif);
}
#if LWIP_IPV6
mbed_lwip_clear_ipv6_addresses(&lwip_netif);
#endif
sys_sem_free(&lwip_netif_has_addr);
sys_sem_new(&lwip_netif_has_addr, 0);
lwip_connected = false;
return 0;
}
@ -598,14 +682,18 @@ static nsapi_error_t mbed_lwip_err_remap(err_t err) {
return NSAPI_ERROR_NO_CONNECTION;
case ERR_TIMEOUT:
case ERR_RTE:
case ERR_INPROGRESS:
case ERR_WOULDBLOCK:
return NSAPI_ERROR_WOULD_BLOCK;
case ERR_VAL:
case ERR_USE:
case ERR_ISCONN:
case ERR_ARG:
return NSAPI_ERROR_PARAMETER;
case ERR_INPROGRESS:
return NSAPI_ERROR_IN_PROGRESS;
case ERR_ALREADY:
return NSAPI_ERROR_ALREADY;
case ERR_ISCONN:
return NSAPI_ERROR_IS_CONNECTED;
default:
return NSAPI_ERROR_DEVICE_ERROR;
}
@ -727,7 +815,10 @@ static nsapi_error_t mbed_lwip_socket_bind(nsapi_stack_t *stack, nsapi_socket_t
struct lwip_socket *s = (struct lwip_socket *)handle;
ip_addr_t ip_addr;
if ((s->conn->type == NETCONN_TCP && s->conn->pcb.tcp->local_port != 0) ||
if (
#if LWIP_TCP
(s->conn->type == NETCONN_TCP && s->conn->pcb.tcp->local_port != 0) ||
#endif
(s->conn->type == NETCONN_UDP && s->conn->pcb.udp->local_port != 0)) {
return NSAPI_ERROR_PARAMETER;
}
@ -876,6 +967,7 @@ static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t h
struct lwip_socket *s = (struct lwip_socket *)handle;
switch (optname) {
#if LWIP_TCP
case NSAPI_KEEPALIVE:
if (optlen != sizeof(int) || s->conn->type != NETCONN_TCP) {
return NSAPI_ERROR_UNSUPPORTED;
@ -899,6 +991,7 @@ static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t h
s->conn->pcb.tcp->keep_intvl = *(int*)optval;
return 0;
#endif
case NSAPI_REUSEADDR:
if (optlen != sizeof(int)) {
@ -906,9 +999,9 @@ static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t h
}
if (*(int *)optval) {
s->conn->pcb.tcp->so_options |= SOF_REUSEADDR;
ip_set_option(s->conn->pcb.ip, SOF_REUSEADDR);
} else {
s->conn->pcb.tcp->so_options &= ~SOF_REUSEADDR;
ip_reset_option(s->conn->pcb.ip, SOF_REUSEADDR);
}
return 0;

View File

@ -19,15 +19,16 @@
#include "nsapi.h"
#include "emac_api.h"
#include "lwip/opt.h"
#ifdef __cplusplus
extern "C" {
#endif
// Access to lwip through the nsapi
nsapi_error_t mbed_lwip_init(emac_interface_t *emac);
nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask, const char *gw);
nsapi_error_t mbed_lwip_bringdown(void);
nsapi_error_t mbed_lwip_emac_init(emac_interface_t *emac);
nsapi_error_t mbed_lwip_bringup(bool dhcp, bool ppp, const char *ip, const char *netmask, const char *gw);
nsapi_error_t mbed_lwip_bringdown(bool ppp);
const char *mbed_lwip_get_mac_address(void);
char *mbed_lwip_get_ip_address(char *buf, int buflen);
@ -36,7 +37,6 @@ char *mbed_lwip_get_gateway(char *buf, int buflen);
extern nsapi_stack_t lwip_stack;
#ifdef __cplusplus
}
#endif

View File

@ -65,7 +65,11 @@
#error "Either IPv4 or IPv6 must be preferred."
#endif
//#define LWIP_DEBUG
#if defined(MBED_CONF_LWIP_DEBUG_ENABLED)
#define LWIP_DEBUG MBED_CONF_LWIP_DEBUG_ENABLED
#else
#define LWIP_DEBUG 0
#endif
#if NO_SYS == 0
#include "cmsis_os.h"
@ -85,7 +89,7 @@
#define MBED_CONF_LWIP_TCPIP_THREAD_STACKSIZE 1200
#endif
#ifdef LWIP_DEBUG
#if LWIP_DEBUG
#define TCPIP_THREAD_STACKSIZE MBED_CONF_LWIP_TCPIP_THREAD_STACKSIZE*2
#else
#define TCPIP_THREAD_STACKSIZE MBED_CONF_LWIP_TCPIP_THREAD_STACKSIZE
@ -98,10 +102,17 @@
#define MBED_CONF_LWIP_DEFAULT_THREAD_STACKSIZE 512
#endif
#ifdef LWIP_DEBUG
// Thread stack size for private PPP thread
#ifndef MBED_CONF_LWIP_PPP_THREAD_STACKSIZE
#define MBED_CONF_LWIP_PPP_THREAD_STACKSIZE 512
#endif
#if LWIP_DEBUG
#define DEFAULT_THREAD_STACKSIZE MBED_CONF_LWIP_DEFAULT_THREAD_STACKSIZE*2
#define PPP_THREAD_STACK_SIZE MBED_CONF_LWIP_PPP_THREAD_STACKSIZE*2
#else
#define DEFAULT_THREAD_STACKSIZE MBED_CONF_LWIP_DEFAULT_THREAD_STACKSIZE
#define PPP_THREAD_STACK_SIZE MBED_CONF_LWIP_PPP_THREAD_STACKSIZE
#endif
#define MEMP_NUM_SYS_TIMEOUT 16
@ -164,10 +175,15 @@
#define MEMP_NUM_NETCONN 4
#endif
#if MBED_CONF_LWIP_TCP_ENABLED
#define LWIP_TCP 1
#define TCP_QUEUE_OOSEQ 0
#define TCP_OVERSIZE 0
#define LWIP_TCP_KEEPALIVE 1
#else
#define LWIP_TCP 0
#endif
#define LWIP_DHCP LWIP_IPV4
#define LWIP_DNS 1
#define LWIP_SOCKET 0
@ -181,7 +197,8 @@
#define LWIP_COMPAT_SOCKETS 0
#define LWIP_POSIX_SOCKETS_IO_NAMES 0
#define LWIP_SO_RCVTIMEO 1
#define LWIP_TCP_KEEPALIVE 1
#define LWIP_BROADCAST_PING 1
// Fragmentation on, as per IPv4 default
#define LWIP_IPV6_FRAG LWIP_IPV6
@ -221,34 +238,63 @@
#define AUTOIP_DEBUG LWIP_DBG_OFF
#define DNS_DEBUG LWIP_DBG_OFF
#define IP6_DEBUG LWIP_DBG_OFF
#if MBED_CONF_LWIP_ENABLE_PPP_TRACE
#define PPP_DEBUG LWIP_DBG_ON
#else
#define PPP_DEBUG LWIP_DBG_OFF
#endif //MBED_CONF_LWIP_ENABLE_PPP_TRACE
#define ETHARP_DEBUG LWIP_DBG_OFF
#define UDP_LPC_EMAC LWIP_DBG_OFF
#ifdef LWIP_DEBUG
#if LWIP_DEBUG
#define MEMP_OVERFLOW_CHECK 1
#define MEMP_SANITY_CHECK 1
#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
#else
#define LWIP_NOASSERT 1
#define LWIP_STATS 0
#endif
#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
#define LWIP_PLATFORM_BYTESWAP 1
#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 1
#if LWIP_TRANSPORT_ETHERNET
// Interface type configuration
#if MBED_CONF_LWIP_ETHERNET_ENABLED
#define LWIP_ARP 1
#define LWIP_ETHERNET 1
#define LWIP_DHCP LWIP_IPV4
#else
#define LWIP_ARP 0
#define LWIP_ETHERNET 0
#endif // MBED_CONF_LWIP_ETHERNET_ENABLED
// Note generic macro name used rather than MBED_CONF_LWIP_PPP_ENABLED
// to allow users like PPPCellularInterface to detect that nsapi_ppp.h is available.
#if NSAPI_PPP_AVAILABLE
#define PPP_SUPPORT 1
#define CHAP_SUPPORT 1
#define PPP_INPROC_IRQ_SAFE 1
// Save RAM
#define PAP_SUPPORT 0
#define VJ_SUPPORT 0
#define PRINTPKT_SUPPORT 0
// Broadcast
#define IP_SOF_BROADCAST 0
#define IP_SOF_BROADCAST_RECV 0
#define LWIP_BROADCAST_PING 1
#define MAXNAMELEN 64 /* max length of hostname or name for auth */
#define MAXSECRETLEN 64
#endif // NSAPI_PPP_AVAILABLE
// Make sure we default these to off, so
// LWIP doesn't default to on
#ifndef LWIP_ARP
#define LWIP_ARP 0
#endif
// Checksum-on-copy disabled due to https://savannah.nongnu.org/bugs/?50914
#define LWIP_CHECKSUM_ON_COPY 0
@ -256,31 +302,15 @@
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK 1
#elif LWIP_TRANSPORT_PPP
#define TCP_SND_BUF (3 * 536)
#define TCP_WND (2 * 536)
#define LWIP_ARP 0
#define PPP_SUPPORT 1
#define CHAP_SUPPORT 1
#define PAP_SUPPORT 1
#define PPP_THREAD_STACKSIZE 4*192
#define PPP_THREAD_PRIO 0
#define MAXNAMELEN 64 /* max length of hostname or name for auth */
#define MAXSECRETLEN 64
#else
#error A transport mechanism (Ethernet or PPP) must be defined
#endif
#define DNS_TABLE_SIZE 2
#define DNS_MAX_NAME_LENGTH 128
#include <lwip/arch.h>
#include "lwip_random.h"
#include "lwip_tcp_isn.h"
#define LWIP_HOOK_TCP_ISN lwip_hook_tcp_isn
#if MBEDTLS_MD5_C
#ifdef MBEDTLS_MD5_C
#include "mbedtls/inc/mbedtls/md5.h"
#define LWIP_USE_EXTERNAL_MBEDTLS 1
#endif

View File

@ -10,17 +10,42 @@
"value": false
},
"ip-ver-pref": {
"help": "On dual stack system the preferred stack: 4 for IPv4 and 6 for IPv6",
"help": "On dual-stack system the preferred stack: 4 for IPv4 and 6 for IPv6",
"value": 4
},
"addr-timeout": {
"help": "On dual stack system how long to wait preferred stack's address in seconds",
"help": "On dual-stack system how long to wait preferred stack's address in seconds",
"value": 5
},
"ethernet-enabled": {
"help": "Enable support for Ethernet interfaces",
"value": true
},
"debug-enabled": {
"help": "Enable debug trace support",
"value": false
},
"ppp-enabled": {
"help": "Enable support for PPP interfaces",
"value": false,
"macro_name": "NSAPI_PPP_AVAILABLE"
},
"use-mbed-trace": {
"help": "Use mbed trace for debug, rather than printf",
"value": false
},
"enable-ppp-trace": {
"help": "Enable trace support for PPP interfaces",
"value": false
},
"socket-max": {
"help": "Maximum number of open TCPServer, TCPSocket and UDPSocket instances allowed, including one used internally for DNS. Each requires 236 bytes of pre-allocated RAM",
"value": 4
},
"tcp-enabled": {
"help": "Enable TCP",
"value": true
},
"tcp-server-max": {
"help": "Maximum number of open TCPServer instances allowed. Each requires 72 bytes of pre-allocated RAM",
"value": 4
@ -40,6 +65,10 @@
"default-thread-stacksize": {
"help": "Stack size for lwip system threads",
"value": 512
},
"ppp-thread-stacksize": {
"help": "Thread stack size for PPP",
"value": 512
}
}
}

View File

@ -0,0 +1,422 @@
/* 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"
#if defined(FEATURE_COMMON_PAL)
#include "mbed_trace.h"
#define TRACE_GROUP "LPPP"
#else
#define tr_debug(...) (void(0)) //dummies if feature common pal is not added
#define tr_info(...) (void(0)) //dummies if feature common pal is not added
#define tr_error(...) (void(0)) //dummies if feature common pal is not added
#endif //defined(FEATURE_COMMON_PAL)
#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 "lwip_stack.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 ppp_pcb *my_ppp_pcb;
static bool ppp_active = false;
static const char *login;
static const char *pwd;
static sys_sem_t ppp_close_sem;
static Callback<void(nsapi_error_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) {
/* suppress generating a callback event for connection up
* Because connect() call is blocking, why wait for a callback */
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(mapped_err_code);
}
}
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()
{
#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()
{
err_t ret = ppp_close(my_ppp_pcb, 0);
if (ret != ERR_OK) {
return ret;
}
/* close call made, now let's catch the response in the status callback */
sys_arch_sem_wait(&ppp_close_sem, 0);
/* Detach callbacks, and put handle back to default blocking mode */
my_stream->sigio(Callback<void()>());
my_stream->set_blocking(true);
my_stream = NULL;
return ret;
}
extern "C" nsapi_error_t ppp_lwip_if_init(struct netif *netif)
{
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
ppp_set_usepeerdns(my_ppp_pcb, true);
#endif
return NSAPI_ERROR_OK;
}
nsapi_error_t nsapi_ppp_error_code()
{
return connect_error_code;
}
nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_error_t)> cb, const char *uname, const char *password)
{
if (my_stream) {
return NSAPI_ERROR_PARAMETER;
}
my_stream = stream;
stream->set_blocking(false);
connection_status_cb = cb;
login = uname;
pwd = password;
// mustn't start calling input until after connect -
// attach deferred until ppp_lwip_connect, called from mbed_lwip_bringup
nsapi_error_t retcode = mbed_lwip_bringup(false, true, NULL, NULL, 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)
{
return mbed_lwip_bringdown(true);
}
NetworkStack *nsapi_ppp_get_stack()
{
return nsapi_create_stack(&lwip_stack);
}
const char *nsapi_ppp_get_ip_addr(FileHandle *stream)
{
static char ip_addr[IPADDR_STRLEN_MAX];
if (stream == my_stream) {
if (mbed_lwip_get_ip_address(ip_addr, IPADDR_STRLEN_MAX)) {
return ip_addr;
}
}
return NULL;
}
const char *nsapi_ppp_get_netmask(FileHandle *stream)
{
#if LWIP_IPV6
return NULL;
#endif
static char netmask[IPADDR_STRLEN_MAX];
if (stream == my_stream) {
if (mbed_lwip_get_netmask(netmask, IPADDR_STRLEN_MAX)) {
return netmask;
}
}
return NULL;
}
const char *nsapi_ppp_get_gw_addr(FileHandle *stream)
{
#if LWIP_IPV6
return NULL;
#endif
static char gwaddr[IPADDR_STRLEN_MAX];
if (stream == my_stream) {
if (mbed_lwip_get_gateway(gwaddr, IPADDR_STRLEN_MAX)) {
return gwaddr;
}
}
return NULL;
}
#endif /* LWIP_PPP_API */
} // namespace mbed

View File

@ -0,0 +1,64 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 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.
*/
#ifndef PPP_LWIP_H_
#define PPP_LWIP_H_
#include "netif/ppp/pppapi.h"
#ifdef __cplusplus
extern "C" {
#endif
#if LWIP_PPP_API
/** Initializes LWIP PPP interface
*
* Starts a private thread for LWIP PPP and turns the PPP interface
* up.
*
* @param netif LWIP netif struct
*
* @return 0 for success and negative error codes for failure
*/
nsapi_error_t ppp_lwip_if_init(struct netif *netif);
/** Connects to a PPP pipe
*
* Called during LWIP interface bringup
*
* @return 0 for success and negative error codes for failure
*/
err_t ppp_lwip_connect(void);
/** Disconnects from a PPP pipe
*
* Can be called from multiple places. If LWIP bringup fails after initializing
* PPP interface, this API kicks in. Formal disconnection occurs when LWIP
* interface is brought down.
*
* @return 0 for success and negative error codes for failure
*/
err_t ppp_lwip_disconnect(void);
#else
/**
* Stubs in case LWIP PPP is not enabled
*/
#define ppp_lwip_if_init(netif) NSAPI_ERROR_UNSUPPORTED
#define ppp_lwip_connect() ERR_IF
#define ppp_lwip_disconnect() ERR_IF
#endif //LWIP_PPP_API
#ifdef __cplusplus
}
#endif
#endif /* PPP_LWIP_H_ */

View File

@ -104,7 +104,7 @@ void File::rewind()
return _fs->file_rewind(_file);
}
size_t File::size()
off_t File::size()
{
MBED_ASSERT(_fs);
return _fs->file_size(_file);

View File

@ -124,7 +124,7 @@ public:
*
* @return Size of the file in bytes
*/
virtual size_t size();
virtual off_t size();
private:
FileSystem *_fs;

View File

@ -45,10 +45,10 @@ void FileSystem::file_rewind(fs_file_t file)
file_seek(file, 0, SEEK_SET);
}
size_t FileSystem::file_size(fs_file_t file)
off_t FileSystem::file_size(fs_file_t file)
{
off_t off = file_tell(file);
size_t size = file_seek(file, 0, SEEK_END);
off_t size = file_seek(file, 0, SEEK_END);
file_seek(file, off, SEEK_SET);
return size;
}

View File

@ -175,7 +175,7 @@ protected:
* @param file File handle
* @return Size of the file in bytes
*/
virtual size_t file_size(fs_file_t file);
virtual off_t file_size(fs_file_t file);
/** Open a directory on the filesystem
*

View File

@ -508,11 +508,11 @@ off_t FATFileSystem::file_tell(fs_file_t file) {
return res;
}
size_t FATFileSystem::file_size(fs_file_t file) {
off_t FATFileSystem::file_size(fs_file_t file) {
FIL *fh = static_cast<FIL*>(file);
lock();
size_t res = fh->fsize;
off_t res = fh->fsize;
unlock();
return res;

View File

@ -179,7 +179,7 @@ protected:
* @param file File handle
* @return Size of the file in bytes
*/
virtual size_t file_size(fs_file_t file);
virtual off_t file_size(fs_file_t file);
/** Open a directory on the filesystem
*

View File

@ -0,0 +1,108 @@
/* Copyright (c) 2017 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.
*/
#ifndef CELLULAR_BASE_H
#define CELLULAR_BASE_H
#include "netsocket/NetworkInterface.h"
/** CellularBase class
*
* Common interface that is shared between Cellular interfaces
*/
class CellularBase: public NetworkInterface {
public:
/** Set the Cellular network credentials
*
* Please check documentation of connect() for default behaviour of APN settings.
*
* @param apn Access point name
* @param uname optionally, Username
* @param pwd optionally, password
*/
virtual void set_credentials(const char *apn, const char *uname = 0,
const char *pwd = 0) = 0;
/** Set the pin code for SIM card
*
* @param sim_pin PIN for the SIM card
*/
virtual void set_sim_pin(const char *sim_pin) = 0;
/** Start the interface
*
* Attempts to connect to a Cellular network.
*
* @param sim_pin PIN for the SIM card
* @param apn optionally, access point name
* @param uname optionally, Username
* @param pwd optionally, password
* @return NSAPI_ERROR_OK on success, or negative error code on failure
*/
virtual nsapi_error_t connect(const char *sim_pin, const char *apn = 0,
const char *uname = 0,
const char *pwd = 0) = 0;
/** Start the interface
*
* Attempts to connect to a Cellular network.
* If the SIM requires a PIN, and it is not set/invalid, NSAPI_ERROR_AUTH_ERROR is returned.
*
* @return NSAPI_ERROR_OK on success, or negative error code on failure
*/
virtual nsapi_error_t connect() = 0;
/** Stop the interface
*
* @return 0 on success, or error code on failure
*/
virtual nsapi_error_t disconnect() = 0;
/** Check if the connection is currently established or not
*
* @return true/false If the cellular module have successfully acquired a carrier and is
* connected to an external packet data network using PPP, isConnected()
* API returns true and false otherwise.
*/
virtual bool is_connected() = 0;
/** Get the local IP address
*
* @return Null-terminated representation of the local IP address
* or null if no IP address has been received
*/
virtual const char *get_ip_address() = 0;
/** Get the local network mask
*
* @return Null-terminated representation of the local network mask
* or null if no network mask has been received
*/
virtual const char *get_netmask() = 0;
/** Get the local gateways
*
* @return Null-terminated representation of the local gateway
* or null if no network mask has been received
*/
virtual const char *get_gateway() = 0;
};
#endif //CELLULAR_BASE_H
/** @}*/

View File

@ -1,4 +1,3 @@
/** \addtogroup netsocket */
/** @{*/
/* CellularInterface
@ -16,12 +15,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CELLULAR_INTERFACE_H
#define CELLULAR_INTERFACE_H
#include "netsocket/NetworkInterface.h"
/** CellularInterface class
*
@ -48,7 +47,7 @@ public:
*
* @param apn Optional name of the network to connect to
* @param username Optional username for your APN
* @param password Optional password for your APN
* @param password Optional password for your APN
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t connect(const char *apn,
@ -61,7 +60,7 @@ public:
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t connect() = 0;
/** Stop the interface
*
* @return 0 on success, negative error code on failure
@ -69,7 +68,7 @@ public:
virtual nsapi_error_t disconnect() = 0;
};
#endif
/** @}*/

View File

@ -0,0 +1,54 @@
/* Copyright (c) 2017 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 "OnboardCellularInterface.h"
#if MODEM_ON_BOARD && MODEM_ON_BOARD_UART && NSAPI_PPP_AVAILABLE
#include "onboard_modem_api.h"
/**
* OnboardCellularInterface is an on-board specific implementation.
*/
OnboardCellularInterface::OnboardCellularInterface(bool debug) :
UARTCellularInterface(MDMTXD, MDMRXD, MDMDCD, MDMRTS,
MDMCTS, MDMRI, MDMDTR, MDMDSR,
MBED_CONF_PPP_CELL_IFACE_BAUD_RATE, MDM_PIN_POLARITY, debug)
{
}
OnboardCellularInterface::~OnboardCellularInterface()
{
}
void OnboardCellularInterface::modem_init()
{
::onboard_modem_init();
}
void OnboardCellularInterface::modem_deinit()
{
::onboard_modem_deinit();
}
void OnboardCellularInterface::modem_power_up()
{
::onboard_modem_power_up();
}
void OnboardCellularInterface::modem_power_down()
{
::onboard_modem_power_down();
}
#endif

View File

@ -0,0 +1,73 @@
/* Copyright (c) 2017 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.
*/
#ifndef ONBOARD_CELLULAR_INTERFACE_
#define ONBOARD_CELLULAR_INTERFACE_
#if MODEM_ON_BOARD && MODEM_ON_BOARD_UART && NSAPI_PPP_AVAILABLE
#include "UARTCellularInterface.h"
/** OnboardCellularInterface class
*
* This interface serves as the controller/driver for an
* onboard modem implementing onboard_modem_api.h.
*
* Depending on the type of on-board modem, OnboardCellularInterface
* could be derived from different implementation classes.
* Portable applications should only rely on it being a CellularBase.
*/
class OnboardCellularInterface : public UARTCellularInterface {
public:
OnboardCellularInterface(bool debug = false);
virtual ~OnboardCellularInterface();
protected:
/** Sets the modem up for powering on
*
* modem_init() is equivalent to plugging in the device, e.g., attaching power and serial port.
* Uses onboard_modem_api.h where the implementation of onboard_modem_api is provided by the target.
*/
virtual void modem_init();
/** Sets the modem in unplugged state
*
* modem_deinit() will be equivalent to pulling the plug off of the device, i.e., detaching power
* and serial port. This puts the modem in lowest power state.
* Uses onboard_modem_api.h where the implementation of onboard_modem_api is provided by the target.
*/
virtual void modem_deinit();
/** Powers up the modem
*
* modem_power_up() is equivalent to pressing the soft power button.
* The driver may repeat this if the modem is not responsive to AT commands.
* Uses onboard_modem_api.h where the implementation of onboard_modem_api is provided by the target.
*/
virtual void modem_power_up();
/** Powers down the modem
*
* modem_power_down() is equivalent to turning off the modem by button press.
* Uses onboard_modem_api.h where the implementation of onboard_modem_api is provided by the target.
*/
virtual void modem_power_down();
};
#endif //MODEM_ON_BOARD && MODEM_ON_BOARD_UART && NSAPI_PPP_AVAILABLE
#endif //ONBOARD_CELLULAR_INTERFACE_

View File

@ -0,0 +1,781 @@
/* Copyright (c) 2017 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 "PPPCellularInterface.h"
#if NSAPI_PPP_AVAILABLE
#include <string.h>
#include "nsapi_ppp.h"
#if MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP
#include "utils/APN_db.h"
#endif //MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP
#if defined(FEATURE_COMMON_PAL)
#include "mbed_trace.h"
#define TRACE_GROUP "UCID"
#else
#define tr_debug(...) (void(0)) //dummies if feature common pal is not added
#define tr_info(...) (void(0)) //dummies if feature common pal is not added
#define tr_error(...) (void(0)) //dummies if feature common pal is not added
#endif //defined(FEATURE_COMMON_PAL)
/**
* PDP (packet data profile) Context
*/
#define CTX "1"
/**
* Output Enter sequence for the modem , default CR
*/
#define OUTPUT_ENTER_KEY "\r"
#if MBED_CONF_PPP_CELL_IFACE_AT_PARSER_BUFFER_SIZE
#define AT_PARSER_BUFFER_SIZE MBED_CONF_PPP_CELL_IFACE_AT_PARSER_BUFFER_SIZE //bytes
#else
#define AT_PARSER_BUFFER_SIZE 256 //bytes
#endif //MBED_CONF_PPP_CELL_IFACE_AT_PARSER_BUFFER_SIZE
#if MBED_CONF_PPP_CELL_IFACE_AT_PARSER_TIMEOUT
#define AT_PARSER_TIMEOUT MBED_CONF_PPP_CELL_IFACE_AT_PARSER_TIMEOUT
#else
#define AT_PARSER_TIMEOUT 8*1000 //miliseconds
#endif //MBED_CONF_PPP_CELL_IFACE_AT_PARSER_TIMEOUT
static bool initialized;
static bool set_credentials_api_used;
static bool set_sim_pin_check_request;
static bool change_pin;
static device_info dev_info;
static void parser_abort(ATCmdParser *at)
{
at->abort();
}
static bool get_CCID(ATCmdParser *at)
{
// Returns the ICCID (Integrated Circuit Card ID) of the SIM-card.
// ICCID is a serial number identifying the SIM.
bool success = at->send("AT+CCID") && at->recv("+CCID: %20[^\n]\nOK\n", dev_info.ccid);
tr_debug("DevInfo: CCID=%s", dev_info.ccid);
return success;
}
static bool get_IMSI(ATCmdParser *at)
{
// International mobile subscriber identification
bool success = at->send("AT+CIMI") && at->recv("%15[^\n]\nOK\n", dev_info.imsi);
tr_debug("DevInfo: IMSI=%s", dev_info.imsi);
return success;
}
static bool get_IMEI(ATCmdParser *at)
{
// International mobile equipment identifier
bool success = at->send("AT+CGSN") && at->recv("%15[^\n]\nOK\n", dev_info.imei);
tr_debug("DevInfo: IMEI=%s", dev_info.imei);
return success;
}
static bool get_MEID(ATCmdParser *at)
{
// Mobile equipment identifier
bool success = at->send("AT+GSN")
&& at->recv("%18[^\n]\nOK\n", dev_info.meid);
tr_debug("DevInfo: MEID=%s", dev_info.meid);
return success;
}
static bool set_CMGF(ATCmdParser *at)
{
// Preferred message format
// set AT+CMGF=[mode] , 0 PDU mode, 1 text mode
bool success = at->send("AT+CMGF=1") && at->recv("OK");
return success;
}
static bool set_CNMI(ATCmdParser *at)
{
// New SMS indication configuration
// set AT+CMTI=[mode, index] , 0 PDU mode, 1 text mode
// Multiple URCs for SMS, i.e., CMT, CMTI, UCMT, CBMI, CDSI as DTE could be following any of these SMS formats
bool success = at->send("AT+CNMI=2,"CTX) && at->recv("OK");
return success;
}
static void CMTI_URC(ATCmdParser *at)
{
// our CMGF = 1, i.e., text mode. So we expect response in this format:
//+CMTI: <mem>,<index>,
at->recv(": %*u,%*u");
tr_info("New SMS received");
}
static void CMT_URC(ATCmdParser *at)
{
// our CMGF = 1, i.e., text mode. So we expect response in this format:
//+CMT: <oa>,[<alpha>],<scts>[,<tooa>,
//<fo>,<pid>,<dcs>,<sca>,<tosca>,
//<length>]<CR><LF><data>
// By default detailed SMS header CSDH=0 , so we are not expecting [,<tooa>,
//<fo>,<pid>,<dcs>,<sca>,<tosca>
char sms[50];
char service_timestamp[15];
at->recv(": %49[^\"]\",,%14[^\"]\"\n", sms, service_timestamp);
tr_info("SMS:%s, %s", service_timestamp, sms);
}
static bool set_atd(ATCmdParser *at)
{
bool success = at->send("ATD*99***"CTX"#") && at->recv("CONNECT");
return success;
}
/**
* Enables or disables SIM pin check lock
*/
static nsapi_error_t do_sim_pin_check(ATCmdParser *at, const char *pin)
{
bool success;
if (set_sim_pin_check_request) {
/* use the SIM locked */
success = at->send("AT+CLCK=\"SC\",1,\"%s\"", pin) && at->recv("OK");
} else {
/* use the SIM unlocked */
success = at->send("AT+CLCK=\"SC\",0,\"%s\"",pin) && at->recv("OK");
}
if (success) return NSAPI_ERROR_OK;
return NSAPI_ERROR_AUTH_FAILURE;
}
/**
* Change the pin code for the SIM card
*/
static nsapi_error_t do_change_sim_pin(ATCmdParser *at, const char *old_pin, const char *new_pin)
{
/* changes the SIM pin */
bool success = at->send("AT+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin) && at->recv("OK");
if (success) {
return NSAPI_ERROR_OK;
}
return NSAPI_ERROR_AUTH_FAILURE;
}
static void set_nwk_reg_status_csd(unsigned int status)
{
switch (status) {
case CSD_NOT_REGISTERED_NOT_SEARCHING:
case CSD_NOT_REGISTERED_SEARCHING:
break;
case CSD_REGISTERED:
case CSD_REGISTERED_ROAMING:
tr_debug("Registered for circuit switched service");
break;
case CSD_REGISTRATION_DENIED:
tr_debug("Circuit switched service denied");
break;
case CSD_UNKNOWN_COVERAGE:
tr_debug("Out of circuit switched service coverage");
break;
case CSD_SMS_ONLY:
tr_debug("SMS service only");
break;
case CSD_SMS_ONLY_ROAMING:
tr_debug("SMS service only");
break;
case CSD_CSFB_NOT_PREFERRED:
tr_debug("Registered for circuit switched service with CSFB not preferred");
break;
default:
tr_debug("Unknown circuit switched service registration status. %u", status);
break;
}
dev_info.reg_status_csd = static_cast<nwk_registration_status_csd>(status);
}
static void set_nwk_reg_status_psd(unsigned int status)
{
switch (status) {
case PSD_NOT_REGISTERED_NOT_SEARCHING:
case PSD_NOT_REGISTERED_SEARCHING:
break;
case PSD_REGISTERED:
case PSD_REGISTERED_ROAMING:
tr_debug("Registered for packet switched service");
break;
case PSD_REGISTRATION_DENIED:
tr_debug("Packet switched service denied");
break;
case PSD_UNKNOWN_COVERAGE:
tr_debug("Out of packet switched service coverage");
break;
case PSD_EMERGENCY_SERVICES_ONLY:
tr_debug("Limited access for packet switched service. Emergency use only.");
break;
default:
tr_debug("Unknown packet switched service registration status. %u", status);
break;
}
dev_info.reg_status_psd = static_cast<nwk_registration_status_psd>(status);
}
static bool is_registered_csd()
{
return (dev_info.reg_status_csd == CSD_REGISTERED) ||
(dev_info.reg_status_csd == CSD_REGISTERED_ROAMING) ||
(dev_info.reg_status_csd == CSD_CSFB_NOT_PREFERRED);
}
static bool is_registered_psd()
{
return (dev_info.reg_status_psd == PSD_REGISTERED) ||
(dev_info.reg_status_psd == PSD_REGISTERED_ROAMING);
}
PPPCellularInterface::PPPCellularInterface(FileHandle *fh, bool debug)
{
_new_pin = NULL;
_pin = NULL;
_at = NULL;
_apn = "internet";
_uname = NULL;
_pwd = NULL;
_fh = fh;
_debug_trace_on = debug;
dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
dev_info.ppp_connection_up = false;
}
PPPCellularInterface::~PPPCellularInterface()
{
delete _at;
}
void PPPCellularInterface::enable_hup(bool)
{
//meant to be overridden
}
void PPPCellularInterface::modem_init()
{
//meant to be overridden
}
void PPPCellularInterface::modem_deinit()
{
//meant to be overridden
}
void PPPCellularInterface::modem_power_up()
{
//meant to be overridden
}
void PPPCellularInterface::modem_power_down()
{
//meant to be overridden
}
void PPPCellularInterface::modem_debug_on(bool on)
{
_debug_trace_on = on;
}
void PPPCellularInterface::connection_status_cb(Callback<void(nsapi_error_t)> cb)
{
_connection_status_cb = cb;
}
/**
* Public API. Sets up the flag for the driver to enable or disable SIM pin check
* at the next boot.
*/
void PPPCellularInterface::set_sim_pin_check(bool check)
{
set_sim_pin_check_request = check;
}
/**
* Public API. Sets up the flag for the driver to change pin code for SIM card
*/
void PPPCellularInterface::set_new_sim_pin(const char *new_pin)
{
change_pin = true;
_new_pin = new_pin;
}
bool PPPCellularInterface::nwk_registration(uint8_t nwk_type)
{
bool success = false;
bool registered = false;
char str[35];
int retcode;
int retry_counter = 0;
unsigned int reg_status;
success = nwk_type == PACKET_SWITCHED ?
_at->send("AT+CGREG=0") :
_at->send("AT+CREG=0") && _at->recv("OK\n");
success = _at->send("AT+COPS=0") //initiate auto-registration
&& _at->recv("OK");
if (!success) {
tr_error("Modem not responding.");
return false;
}
//Network search
//If not registered after 60 attempts, i.e., 30 seconds wait, give up
tr_debug("Searching Network ...");
while (!registered) {
if (retry_counter > 60) {
success = false;
goto give_up;
}
success = nwk_type == PACKET_SWITCHED ?
_at->send("AT+CGREG?")
&& _at->recv("+CGREG: %34[^\n]\n", str)
&& _at->recv("OK\n") :
_at->send("AT+CREG?")
&& _at->recv("+CREG: %34[^\n]\n", str)
&& _at->recv("OK\n");
retcode = sscanf(str, "%*u,%u", &reg_status);
if (retcode >= 1) {
if (nwk_type == PACKET_SWITCHED) {
set_nwk_reg_status_psd(reg_status);
if (is_registered_psd()) {
registered = true;
}
} else if (nwk_type == CIRCUIT_SWITCHED) {
set_nwk_reg_status_csd(reg_status);
if (is_registered_csd()) {
registered = true;
}
}
}
if (registered) {
break;
} else {
wait_ms(500);
}
retry_counter++;
}
give_up:
return registered;
}
bool PPPCellularInterface::is_connected()
{
return dev_info.ppp_connection_up;
}
// Get the SIM card going.
nsapi_error_t PPPCellularInterface::initialize_sim_card()
{
nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
int retry_count = 0;
bool done = false;
/* SIM initialization may take a significant amount, so an error is
* kind of expected. We should retry 10 times until we succeed or timeout. */
for (retry_count = 0; !done && (retry_count < 10); retry_count++) {
char pinstr[16];
if (_at->send("AT+CPIN?") && _at->recv("+CPIN: %15[^\n]\nOK\n", pinstr)) {
if (strcmp(pinstr, "SIM PIN") == 0) {
if (!_at->send("AT+CPIN=\"%s\"", _pin) || !_at->recv("OK")) {
tr_debug("PIN correct");
nsapi_error = NSAPI_ERROR_OK;
}
} else if (strcmp(pinstr, "READY") == 0) {
tr_debug("SIM Ready");
nsapi_error = NSAPI_ERROR_OK;
done = true;
} else {
tr_debug("Unexpected response from SIM: \"%s\"", pinstr);
}
}
/* wait for a second before retry */
wait_ms(1000);
}
if (!done) {
tr_error("SIM not ready.");
}
return nsapi_error;
}
void PPPCellularInterface::set_sim_pin(const char *pin) {
/* overwrite the default pin by user provided pin */
_pin = pin;
}
nsapi_error_t PPPCellularInterface::setup_context_and_credentials()
{
bool success;
if (!_apn) {
return NSAPI_ERROR_PARAMETER;
}
bool try_ipv6 = false;
const char *auth = _uname && _pwd ? "CHAP:" : "";
retry_without_ipv6:
success = _at->send("AT"
"+FCLASS=0;" // set to connection (ATD) to data mode
"+CGDCONT="CTX",\"%s\",\"%s%s\"",
try_ipv6 ? "IPV4V6" : "IP", auth, _apn
)
&& _at->recv("OK");
if (!success && try_ipv6) {
try_ipv6 = false;
goto retry_without_ipv6;
}
if (!success) {
_at->recv("OK");
}
return success ? NSAPI_ERROR_OK : NSAPI_ERROR_PARAMETER;
}
void PPPCellularInterface::set_credentials(const char *apn, const char *uname,
const char *pwd)
{
_apn = apn;
_uname = uname;
_pwd = pwd;
set_credentials_api_used = true;
}
void PPPCellularInterface::setup_at_parser()
{
if (_at) {
return;
}
_at = new ATCmdParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE, AT_PARSER_TIMEOUT,
_debug_trace_on ? true : false);
/* Error cases, out of band handling */
_at->oob("ERROR", callback(parser_abort, _at));
_at->oob("+CME ERROR", callback(parser_abort, _at));
_at->oob("+CMS ERROR", callback(parser_abort, _at));
_at->oob("NO CARRIER", callback(parser_abort, _at));
/* URCs, handled out of band */
_at->oob("+CMT", callback(CMT_URC, _at));
_at->oob("+CMTI", callback(CMTI_URC, _at));
}
void PPPCellularInterface::shutdown_at_parser()
{
delete _at;
_at = NULL;
}
nsapi_error_t PPPCellularInterface::connect(const char *sim_pin, const char *apn, const char *uname, const char *pwd)
{
if (!sim_pin) {
return NSAPI_ERROR_PARAMETER;
}
if (apn) {
_apn = apn;
}
if (uname && pwd) {
_uname = uname;
_pwd = pwd;
} else {
_uname = NULL;
_pwd = NULL;
}
_pin = sim_pin;
return connect();
}
nsapi_error_t PPPCellularInterface::connect()
{
nsapi_error_t retcode;
bool success;
bool did_init = false;
if (dev_info.ppp_connection_up) {
return NSAPI_ERROR_IS_CONNECTED;
}
const char *apn_config = NULL;
#if MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP
if (!set_credentials_api_used) {
apn_config = apnconfig(dev_info.imsi);
}
#endif
do {
retry_init:
/* setup AT parser */
setup_at_parser();
if (!initialized) {
/* If we have hangup (eg DCD) detection, we don't want it active
* as long as we are using ATCmdParser.
* As soon as we get into data mode, we will turn it back on. */
enable_hup(false);
if (!power_up()) {
return NSAPI_ERROR_DEVICE_ERROR;
}
retcode = initialize_sim_card();
if (retcode != NSAPI_ERROR_OK) {
return retcode;
}
success = nwk_registration(PACKET_SWITCHED) //perform network registration
&& get_CCID(_at)//get integrated circuit ID of the SIM
&& get_IMSI(_at)//get international mobile subscriber information
&& get_IMEI(_at)//get international mobile equipment identifier
&& get_MEID(_at)//its same as IMEI
&& set_CMGF(_at)//set message format for SMS
&& set_CNMI(_at);//set new SMS indication
if (!success) {
return NSAPI_ERROR_NO_CONNECTION;
}
/* Check if user want skip SIM pin checking on boot up */
if (set_sim_pin_check_request) {
retcode = do_sim_pin_check(_at, _pin);
if (retcode != NSAPI_ERROR_OK) {
return retcode;
}
/* set this request to false, as it is unnecessary to repeat in case of retry */
set_sim_pin_check_request = false;
}
/* check if the user requested a sim pin change */
if (change_pin) {
retcode = do_change_sim_pin(_at, _pin, _new_pin);
if (retcode != NSAPI_ERROR_OK) {
return retcode;
}
/* set this request to false, as it is unnecessary to repeat in case of retry */
change_pin = false;
}
#if MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP
if (apn_config) {
_apn = _APN_GET(apn_config);
_uname = _APN_GET(apn_config);
_pwd = _APN_GET(apn_config);
}
#endif
//sets up APN and IP protocol for external PDP context
retcode = setup_context_and_credentials();
if (retcode != NSAPI_ERROR_OK) {
return retcode;
}
if (!success) {
shutdown_at_parser();
return NSAPI_ERROR_NO_CONNECTION;
}
initialized = true;
did_init = true;
} else {
/* If we were already initialized, we expect to receive NO_CARRIER response
* from the modem as we were kicked out of Data mode */
_at->recv("NO CARRIER");
success = _at->send("AT") && _at->recv("OK");
}
/* Attempt to enter data mode */
success = set_atd(_at); //enter into Data mode with the modem
if (!success) {
power_down();
initialized = false;
/* if we were previously initialized , i.e., not in this particular attempt,
* we want to re-initialize */
if (!did_init) {
goto retry_init;
}
/* shutdown AT parser before notifying application of the failure */
shutdown_at_parser();
return NSAPI_ERROR_NO_CONNECTION;
}
/* This is the success case.
* Save RAM, discard AT Parser as we have entered Data mode. */
shutdown_at_parser();
/* We now want hangup (e.g., DCD) detection if available */
enable_hup(true);
/* Initialize PPP
* mbed_ppp_init() is a blocking call, it will block until
* connected, or timeout after 30 seconds*/
retcode = nsapi_ppp_connect(_fh, _connection_status_cb, _uname, _pwd);
if (retcode == NSAPI_ERROR_OK) {
dev_info.ppp_connection_up = true;
}
}while(!dev_info.ppp_connection_up && apn_config && *apn_config);
return retcode;
}
/**
* User initiated disconnect
*
* Disconnects from PPP connection only and brings down the underlying network
* interface
*/
nsapi_error_t PPPCellularInterface::disconnect()
{
nsapi_error_t ret = nsapi_ppp_disconnect(_fh);
if (ret == NSAPI_ERROR_OK) {
dev_info.ppp_connection_up = false;
return NSAPI_ERROR_OK;
}
return ret;
}
const char *PPPCellularInterface::get_ip_address()
{
return nsapi_ppp_get_ip_addr(_fh);
}
const char *PPPCellularInterface::get_netmask()
{
return nsapi_ppp_get_netmask(_fh);
}
const char *PPPCellularInterface::get_gateway()
{
return nsapi_ppp_get_ip_addr(_fh);
}
/** Power down modem
* Uses AT command to do it */
void PPPCellularInterface::power_down()
{
modem_power_down();
modem_deinit();
}
/**
* Powers up the modem
*
* Enables the GPIO lines to the modem and then wriggles the power line in short pulses.
*/
bool PPPCellularInterface::power_up()
{
/* Initialize GPIO lines */
modem_init();
/* Give modem a little time to settle down */
wait(0.25);
bool success = false;
int retry_count = 0;
while (true) {
modem_power_up();
/* Modem tends to spit out noise during power up - don't confuse the parser */
_at->flush();
/* It is mandatory to avoid sending data to the serial port during the first 200 ms
* of the module startup. Telit_xE910 Global form factor App note.
* Not necessary for all types of modems however. Let's wait just to be on the safe side */
wait_ms(200);
_at->set_timeout(1000);
if (_at->send("AT") && _at->recv("OK")) {
tr_info("Modem Ready.");
break;
}
if (++retry_count > 10) {
goto failure;
}
}
_at->set_timeout(8000);
/*For more details regarding DCD and DTR circuitry, please refer to Modem AT manual */
success = _at->send("AT"
"E0;" //turn off modem echoing
"+CMEE=2;"//turn on verbose responses
"&K0"//turn off RTC/CTS handshaking
"+IPR=115200;"//setup baud rate
"&C1;"//set DCD circuit(109), changes in accordance with the carrier detect status
"&D0")//set DTR circuit, we ignore the state change of DTR
&& _at->recv("OK");
if (!success) {
goto failure;
}
/* If everything alright, return from here with success*/
return success;
failure:
tr_error("Preliminary modem setup failed.");
return false;
}
/**
* Get a pointer to the underlying network stack
*/
NetworkStack *PPPCellularInterface::get_stack()
{
return nsapi_ppp_get_stack();
}
#endif // NSAPI_PPP_AVAILABLE

View File

@ -0,0 +1,344 @@
/* Copyright (c) 2017 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.
*/
#ifndef PPP_CELLULAR_INTERFACE_
#define PPP_CELLULAR_INTERFACE_
#include "CellularBase.h"
#include "platform/ATCmdParser.h"
#include "mbed.h"
#if NSAPI_PPP_AVAILABLE
// Forward declaration
class NetworkStack;
/**
* Network registration status
* UBX-13001820 - AT Commands Example Application Note (Section 4.1.4.5)
*/
typedef enum {
GSM=0,
COMPACT_GSM=1,
UTRAN=2,
EDGE=3,
HSDPA=4,
HSUPA=5,
HSDPA_HSUPA=6,
LTE=7
} radio_access_nwk_type;
/**
* Used in registration process to tell which type of network
* to connect.
*/
typedef enum {
CIRCUIT_SWITCHED=0,
PACKET_SWITCHED
} nwk_type;
/**
* Circuit Switched network registration status (CREG Usage)
* UBX-13001820 - AT Commands Example Application Note (Section 7.10.3)
*/
typedef enum {
CSD_NOT_REGISTERED_NOT_SEARCHING=0,
CSD_REGISTERED=1,
CSD_NOT_REGISTERED_SEARCHING=2,
CSD_REGISTRATION_DENIED=3,
CSD_UNKNOWN_COVERAGE=4,
CSD_REGISTERED_ROAMING=5,
CSD_SMS_ONLY=6,
CSD_SMS_ONLY_ROAMING=7,
CSD_CSFB_NOT_PREFERRED=9
} nwk_registration_status_csd;
/**
* Packet Switched network registration status (CGREG Usage)
* UBX-13001820 - AT Commands Example Application Note (Section 18.27.3)
*/
typedef enum {
PSD_NOT_REGISTERED_NOT_SEARCHING=0,
PSD_REGISTERED=1,
PSD_NOT_REGISTERED_SEARCHING=2,
PSD_REGISTRATION_DENIED=3,
PSD_UNKNOWN_COVERAGE=4,
PSD_REGISTERED_ROAMING=5,
PSD_EMERGENCY_SERVICES_ONLY=8
} nwk_registration_status_psd;
typedef struct {
char ccid[20+1]; //!< Integrated Circuit Card ID
char imsi[15+1]; //!< International Mobile Station Identity
char imei[15+1]; //!< International Mobile Equipment Identity
char meid[18+1]; //!< Mobile Equipment IDentifier
int flags;
bool ppp_connection_up;
radio_access_nwk_type rat;
nwk_registration_status_csd reg_status_csd;
nwk_registration_status_psd reg_status_psd;
} device_info;
/** PPPCellularInterface class
*
* This interface serves as the controller/driver for the Cellular
* modems (tested with UBLOX_C027 and MTS_DRAGONFLY_F411RE).
*
* The driver will work with any generic FileHandle, and can be
* derived from in order to provide forms for specific interfaces, as well as
* adding extra power and reset controls alongside the FileHandle.
*/
class PPPCellularInterface : public CellularBase {
public:
/** Constructor for a generic modem, using a single FileHandle for commands and PPP data.
*
* The file handle pointer is not accessed within the constructor, only recorded for later
* use - this permits a derived class to pass a pointer to a not-yet-constructed member object.
*/
PPPCellularInterface(FileHandle *fh, bool debug = false);
virtual ~PPPCellularInterface();
/** Set the Cellular network credentials
*
* Please check documentation of connect() for default behaviour of APN settings.
*
* @param apn Access point name
* @param uname optionally, Username
* @param pwd optionally, password
*/
virtual void set_credentials(const char *apn, const char *uname = 0,
const char *pwd = 0);
/** Set the pin code for SIM card
*
* @param sim_pin PIN for the SIM card
*/
virtual void set_sim_pin(const char *sim_pin);
/** Start the interface
*
* Attempts to connect to a Cellular network.
* This driver is written mainly for data network connections as CellularInterface
* is NetworkInterface. That's why connect() call internally calls nwk_registration()
* method with parameter PACKET_SWITCHED network. Circuit switched hook and registration
* process is implemented and left in the driver for future extension/subclass support,e.g.,
* an SMS or GPS extension.
*
* @param sim_pin PIN for the SIM card
* @param apn optionally, access point name
* @param uname optionally, Username
* @param pwd optionally, password
* @return NSAPI_ERROR_OK on success, or negative error code on failure
*/
virtual nsapi_error_t connect(const char *sim_pin, const char *apn = 0,
const char *uname = 0, const char *pwd = 0);
/** Attempt to connect to the Cellular network
*
* Brings up the network interface. Connects to the Cellular Radio
* network and then brings up the underlying network stack to be used
* by the cellular modem over PPP interface.
*
* If the SIM requires a PIN, and it is not set/invalid, NSAPI_ERROR_AUTH_ERROR is returned.
* For APN setup, default behaviour is to use 'internet' as APN string and assuming no authentication
* is required, i.e., username and password are not set. Optionally, a database lookup can be requested
* by turning on the APN database lookup feature. In order to do so, add 'MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP'
* in your mbed_app.json. APN database is by no means exhaustive. It contains a short list of some public
* APNs with publicly available usernames and passwords (if required) in some particular countries only.
* Lookup is done using IMSI (International mobile subscriber identifier).
* Please note that even if 'MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP' config option is set, 'set_credentials()' api still
* gets the precedence. If the aforementioned API is not used and the config option is set but no match is found in
* the lookup table then the driver tries to resort to default APN settings.
*
* Preferred method is to setup APN using 'set_credentials()' API.
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t connect();
/** Attempt to disconnect from the network
*
* Brings down the network interface. Shuts down the PPP interface
* of the underlying network stack. Does not bring down the Radio network
*
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t disconnect();
/** Adds or removes a SIM facility lock
*
* Can be used to enable or disable SIM pin check at device startup.
* This API sets up flags for the driver which would either enable or disable
* SIM pin checking depending upon the user provided argument while establishing
* connection. It doesn't do anything immediately other than setting up flags.
*
* @param set can be set to true if the SIM pin check is supposed to be enabled
* and vice versa.
*/
void set_sim_pin_check(bool set);
/** Change the pin for the SIM card
*
* Provide the new pin for your SIM card with this API. Old pin code will be assumed to
* be set using set_SIM_pin() API. This API have no immediate effect. While establishing
* connection, driver will change the SIM pin for the next boot.
*
* @param new_pin new pin to be used in string format
*/
void set_new_sim_pin(const char *new_pin);
/** Check if the connection is currently established or not
*
* @return true/false If the cellular module have successfully acquired a carrier and is
* connected to an external packet data network using PPP, isConnected()
* API returns true and false otherwise.
*/
virtual bool is_connected();
/** Get the local IP address
*
* @return Null-terminated representation of the local IP address
* or null if no IP address has been recieved
*/
virtual const char *get_ip_address();
/** Get the local network mask
*
* @return Null-terminated representation of the local network mask
* or null if no network mask has been recieved
*/
virtual const char *get_netmask();
/** Get the local gateways
*
* @return Null-terminated representation of the local gateway
* or null if no network mask has been recieved
*/
virtual const char *get_gateway();
/** Get notified if the connection gets lost
*
* @param cb user defined callback
*/
void connection_status_cb(Callback<void(nsapi_error_t)> cb);
/** Turn modem debug traces on
*
* @param on set true to enable debug traces
*/
void modem_debug_on(bool on);
private:
FileHandle *_fh;
ATCmdParser *_at;
const char *_new_pin;
const char *_pin;
const char *_apn;
const char *_uname;
const char *_pwd;
bool _debug_trace_on;
Callback<void(nsapi_error_t)> _connection_status_cb;
void base_initialization();
void setup_at_parser();
void shutdown_at_parser();
nsapi_error_t initialize_sim_card();
nsapi_error_t setup_context_and_credentials();
bool power_up();
void power_down();
protected:
/** Enable or disable hang-up detection
*
* When in PPP data pump mode, it is helpful if the FileHandle will signal hang-up via
* POLLHUP, e.g., if the DCD line is deasserted on a UART. During command mode, this
* signaling is not desired. enable_hup() controls whether this function should be
* active.
*
* Meant to be overridden.
*/
virtual void enable_hup(bool enable);
/** Sets the modem up for powering on
*
* modem_init() is equivalent to plugging in the device, e.g., attaching power and serial port.
* It is meant to be overridden.
* If the modem is on-board, an implementation of onboard_modem_api.h
* will override this.
* If the the modem is a plugged-in device (i.e., a shield type component), the driver deriving from this
* class will override.
*/
virtual void modem_init();
/** Sets the modem in unplugged state
*
* modem_deinit() will be equivalent to pulling the plug off of the device, i.e., detaching power
* and serial port. This puts the modem in lowest power state.
* It is meant to be overridden.
* If the modem is on-board, an implementation of onboard_modem_api.h
* will override this.
* If the the modem is a plugged-in device (i.e., a shield type component), the driver deriving from this
* class will override.
*/
virtual void modem_deinit();
/** Powers up the modem
*
* modem_power_up() is equivalent to pressing the soft power button.
* The driver may repeat this if the modem is not responsive to AT commands.
* It is meant to be overridden.
* If the modem is on-board, an implementation of onboard_modem_api.h
* will override this.
* If the the modem is a plugged-in device (i.e., a shield type component), the driver deriving from this
* class will override.
*/
virtual void modem_power_up();
/** Powers down the modem
*
* modem_power_down() is equivalent to turning off the modem by button press.
* It is meant to be overridden.
* If the modem is on-board, an implementation of onboard_modem_api.h
* will override this.
* If the the modem is a plugged-in device (i.e., a shield type component), the driver deriving from this
* class will override.
*/
virtual void modem_power_down();
/** Provide access to the underlying stack
*
* @return The underlying network stack
*/
virtual NetworkStack *get_stack();
/** Starts network registration process.
*
* Potential users could be subclasses who are not network interface
* but would like to use CellularInterface infrastructure to register
* with a cellular network, e.g., an SMS extension to CellularInterface.
*
* @param nwk_type type of network to connect, defaults to packet switched network
*
* @return true if registration is successful
*/
bool nwk_registration(uint8_t nwk_type=PACKET_SWITCHED);
};
#endif //NSAPI_PPP_AVAILABLE
#endif //PPP_CELLULAR_INTERFACE_

View File

@ -0,0 +1,38 @@
/* Copyright (c) 2017 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 "UARTCellularInterface.h"
#if NSAPI_PPP_AVAILABLE
UARTCellularInterface::UARTCellularInterface(PinName txd, PinName rxd, PinName dcd, PinName rts, PinName cts, PinName ri,
PinName dtr, PinName dsr, int baud, bool active_high, bool debug) :
PPPCellularInterface(&_serial, debug),
_serial(txd, rxd, baud)
{
_dcd_pin = dcd;
_active_high = active_high;
}
UARTCellularInterface::~UARTCellularInterface()
{
//do nothing
}
void UARTCellularInterface::enable_hup(bool enable)
{
_serial.set_data_carrier_detect(enable ? _dcd_pin : NC, _active_high);
}
#endif // NSAPI_PPP_AVAILABLE

View File

@ -0,0 +1,61 @@
/* Copyright (c) 2017 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.
*/
#ifndef UART_CELLULAR_INTERFACE_
#define UART_CELLULAR_INTERFACE_
#include "PPPCellularInterface.h"
#include "UARTSerial.h"
#if NSAPI_PPP_AVAILABLE
/** UARTCellularInterface class
*
* This interface serves as the controller/driver for Cellular
* modems attached via a UART (tested with UBLOX_C027 and MTS_DRAGONFLY_F411RE).
*
* It constructs a FileHandle and passes it back to its base class as well as overrides
* enable_hup() in the base class.
*/
class UARTCellularInterface : public PPPCellularInterface {
public:
UARTCellularInterface(PinName tx, PinName rx, PinName dcd = NC, PinName rts = NC, PinName cts = NC, PinName ri = NC,
PinName dtr = NC, PinName dsr = NC, int baud = MBED_CONF_PPP_CELL_IFACE_BAUD_RATE,
bool active_high = false,
bool debug = false);
virtual ~UARTCellularInterface();
private:
UARTSerial _serial;
PinName _dcd_pin;
bool _active_high;
protected:
/** Enable or disable hang-up detection
*
* When in PPP data pump mode, it is helpful if the FileHandle will signal hang-up via
* POLLHUP, e.g., if the DCD line is deasserted on a UART. During command mode, this
* signaling is not desired. enable_hup() controls whether this function should be
* active.
*/
virtual void enable_hup(bool enable);
};
#endif //NSAPI_PPP_AVAILABLE
#endif //UART_CELLULAR_INTERFACE_

View File

@ -0,0 +1,9 @@
{
"name": "ppp-cell-iface",
"config": {
"baud-rate": 115200,
"apn-lookup": false,
"at-parser-buffer-size": 256,
"at-parser-timeout": 8000
}
}

View File

@ -0,0 +1,79 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 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.
*/
#ifndef ONBOARD_MODEM_API_H_
#define ONBOARD_MODEM_API_H_
/** onboard_modem_api is a standardizing API for Modem type devices under mbed-os.
* It provides a simple hardware abstraction layer on top of the modem drivers
* written for mbed-os.
*
* It is required from the engineers porting any modem type device (e.g., Cellular)
* to provide an implementation of this API in their respective target folder as well as
* usage of standard PinNames (in PinNames.h) is required. For example,
*
* MDMTXD = P0_15, // Transmit Data
* MDMRXD = P0_16, // Receive Data
* MDMCTS = P0_17, // Clear to Send
* MDMDCD = P0_18, // Data Carrier Detect
* MDMDSR = P0_19, // Data Set Ready
* MDMDTR = P0_20, // Data Terminal Ready (set high or use handshake)
* MDMRI = P0_21, // Ring Indicator
* MDMRTS = P0_22, // Request to Send (set high or use handshake)
*
* MDM_PIN_POLARITY must also be defined as 0 (active low) or 1 (active high).
*
* NOTE: This API should only be used when the modem exists on-board, i.e., the modem is
* NOT a plugged-in component.
*/
#if MODEM_ON_BOARD
#ifdef __cplusplus
extern "C" {
#endif
/** Sets the modem up for powering on
* modem_init() will be equivalent to plugging in the device, i.e.,
* attaching power and serial port.
*/
void onboard_modem_init(void);
/** Sets the modem in unplugged state
* modem_deinit() will be equivalent to pulling the plug off of the device, i.e.,
* detaching power and serial port.
* This puts the modem in lowest power state.
*/
void onboard_modem_deinit(void);
/** Powers up the modem
* modem_power_up() will be equivalent to pressing the soft power button.
* The driver may repeat this if the modem is not responsive to AT commands.
*/
void onboard_modem_power_up(void);
/** Powers down the modem
* modem_power_down() will be equivalent to turning off the modem by button press.
*/
void onboard_modem_power_down(void);
#ifdef __cplusplus
}
#endif
#endif /* MODEM_ON_BOARD*/
#endif /* ONBOARD_MODEM_API_H_ */

View File

@ -0,0 +1,176 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ublox, 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.
*/
/* ----------------------------------------------------------------
APN stands for Access Point Name, a setting on your modem or phone
that identifies an external network your phone can access for data
(e.g. 3G or 4G Internet service on your phone).
The APN settings can be forced when calling the join function.
Below is a list of known APNs that us used if no apn config
is forced. This list could be extended by other settings.
For further reading:
wiki apn: http://en.wikipedia.org/wiki/Access_Point_Name
wiki mcc/mnc: http://en.wikipedia.org/wiki/Mobile_country_code
google: https://www.google.de/search?q=APN+list
---------------------------------------------------------------- */
/**
* Helper to generate the APN string
*/
#define _APN(apn,username,password) apn "\0" username "\0" password "\0"
/**
* Helper to extract a field from the cfg string
*/
#define _APN_GET(cfg) \
*cfg ? cfg : NULL; \
cfg += strlen(cfg) + 1
/**
* APN lookup struct
*/
typedef struct {
const char* mccmnc; /**< mobile country code (MCC) and mobile network code MNC */
const char* cfg; /**< APN configuartion string, use _APN macro to generate */
} APN_t;
/**
* Default APN settings used by many networks
*/
static const char* apndef = _APN("internet",,);
/**
* List of special APNs for different network operators.
*
* No need to add default, "internet" will be used as a default if no entry matches.
* The APN without username/password have to be listed first.
*/
static const APN_t apnlut[] = {
// MCC Country
// { /* Operator */ "MCC-MNC[,MNC]" _APN(APN,USERNAME,PASSWORD) },
// MCC must be 3 digits
// MNC must be either 2 or 3 digits
// MCC must be separated by '-' from MNC, multiple MNC can be separated by ','
// 232 Austria - AUT
{ /* T-Mobile */ "232-03", _APN("m2m.business",,) },
// 460 China - CN
{ /* CN Mobile */"460-00", _APN("cmnet",,)
_APN("cmwap",,) },
{ /* Unicom */ "460-01", _APN("3gnet",,)
_APN("uninet","uninet","uninet") },
// 262 Germany - DE
{ /* T-Mobile */ "262-01", _APN("internet.t-mobile","t-mobile","tm") },
{ /* T-Mobile */ "262-02,06",
_APN("m2m.business",,) },
// 222 Italy - IT
{ /* TIM */ "222-01", _APN("ibox.tim.it",,) },
{ /* Vodafone */ "222-10", _APN("web.omnitel.it",,) },
{ /* Wind */ "222-88", _APN("internet.wind.biz",,) },
// 440 Japan - JP
{ /* Softbank */ "440-04,06,20,40,41,42,43,44,45,46,47,48,90,91,92,93,94,95"
",96,97,98"
_APN("open.softbank.ne.jp","opensoftbank","ebMNuX1FIHg9d3DA")
_APN("smile.world","dna1trop","so2t3k3m2a") },
{ /* NTTDoCoMo */"440-09,10,11,12,13,14,15,16,17,18,19,21,22,23,24,25,26,27,"
"28,29,30,31,32,33,34,35,36,37,38,39,58,59,60,61,62,63,"
"64,65,66,67,68,69,87,99",
_APN("bmobilewap",,) /*BMobile*/
_APN("mpr2.bizho.net","Mopera U",) /* DoCoMo */
_APN("bmobile.ne.jp","bmobile@wifi2","bmobile") /*BMobile*/ },
// 204 Netherlands - NL
{ /* Vodafone */ "204-04", _APN("public4.m2minternet.com",,) },
// 293 Slovenia - SI
{ /* Si.mobil */ "293-40", _APN("internet.simobil.si",,) },
{ /* Tusmobil */ "293-70", _APN("internet.tusmobil.si",,) },
// 240 Sweden SE
{ /* Telia */ "240-01", _APN("online.telia.se",,) },
{ /* Telenor */ "240-06,08",
_APN("services.telenor.se",,) },
{ /* Tele2 */ "240-07", _APN("mobileinternet.tele2.se",,) },
// 228 Switzerland - CH
{ /* Swisscom */ "228-01", _APN("gprs.swisscom.ch",,) },
{ /* Orange */ "228-03", _APN("internet",,) /* contract */
_APN("click",,) /* pre-pay */ },
// 234 United Kingdom - GB
{ /* O2 */ "234-02,10,11",
_APN("mobile.o2.co.uk","faster","web") /* contract */
_APN("mobile.o2.co.uk","bypass","web") /* pre-pay */
_APN("payandgo.o2.co.uk","payandgo","payandgo") },
{ /* Vodafone */ "234-15", _APN("internet","web","web") /* contract */
_APN("pp.vodafone.co.uk","wap","wap") /* pre-pay */ },
{ /* Three */ "234-20", _APN("three.co.uk",,) },
{ /* Jersey */ "234-50", _APN("jtm2m",,) /* as used on u-blox C030 U201 boards */ },
// 310 United States of America - US
{ /* T-Mobile */ "310-026,260,490",
_APN("epc.tmobile.com",,)
_APN("fast.tmobile.com",,) /* LTE */ },
{ /* AT&T */ "310-030,150,170,260,410,560,680",
_APN("phone",,)
_APN("wap.cingular","WAP@CINGULARGPRS.COM","CINGULAR1")
_APN("isp.cingular","ISP@CINGULARGPRS.COM","CINGULAR1") },
// 901 International - INT
{ /* Transatel */ "901-37", _APN("netgprs.com","tsl","tsl") },
};
/**
* Configuring APN by extraction from IMSI and matching the table.
*
* @param imsi strinf containing IMSI
*/
inline const char* apnconfig(const char* imsi)
{
const char* config = NULL;
if (imsi && *imsi) {
// many carriers use internet without username and password, os use this as default
// now try to lookup the setting for our table
for (size_t i = 0; i < sizeof(apnlut)/sizeof(*apnlut) && !config; i ++) {
const char* p = apnlut[i].mccmnc;
// check the MCC
if ((0 == memcmp(imsi, p, 3))) {
p += 3;
// check all the MNC, MNC length can be 2 or 3 digits
while (((p[0] == '-') || (p[0] == ',')) &&
(p[1] >= '0') && (p[1] <= '9') &&
(p[2] >= '0') && (p[2] <= '9') && !config) {
int l = ((p[3] >= '0') && (p[3] <= '9')) ? 3 : 2;
if (0 == memcmp(imsi+3,p+1,l))
config = apnlut[i].cfg;
p += 1 + l;
}
}
}
}
// use default if not found
if (!config) {
config = apndef;
}
return config;
}

View File

@ -0,0 +1,86 @@
/** \addtogroup netsocket */
/** @{*/
/* nsapi_ppp.h
* Modified work Copyright (c) 2017 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.
*/
#ifndef NSAPI_PPP_H_
#define NSAPI_PPP_H_
#include "FileHandle.h"
#include "NetworkStack.h"
namespace mbed {
/** Provide access to the NetworkStack object
*
* @return The underlying NetworkStack object
*/
NetworkStack *nsapi_ppp_get_stack();
/** Connect to a PPP pipe
*
* @param stream Pointer to a device type file handle (descriptor)
* @param status_cb Optional, user provided callback for connection status
* @param uname Optional, username for the connection
* @param pwd Optional, password for the connection
*
* @return 0 on success, negative error code on failure
*/
nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_error_t)> status_cb=0, const char *uname=0, const char *pwd=0);
/** Close a PPP connection
*
* @param stream Pointer to a device type file handle (descriptor)
*
* @return 0 on success, negative error code on failure
*/
nsapi_error_t nsapi_ppp_disconnect(FileHandle *stream);
/** Get IP address
*
* After a successful connection, this API can be used to retrieve assigned IP address.
*
* @param stream Pointer to a device type file handle (descriptor)
*
* @return A string containing IP address or NULL
*/
const char *nsapi_ppp_get_ip_addr(FileHandle *stream);
/** Get network mask
*
* After a successful connection, this API can be used to retrieve network mask
* in case of an IPv4 network.
*
* @param stream Pointer to a device type file handle (descriptor)
*
* @return A string containing network mask or NULL
*/
const char *nsapi_ppp_get_netmask(FileHandle *stream);
/** Get gateway address
*
* After a successful connection, this API can be used to retrieve IP address
* of the default gateway in case of an IPv4 network.
*
* @param stream Pointer to a device type file handle (descriptor)
*
* @return A string containing gateway IP address or NULL
*/
const char *nsapi_ppp_get_gw_addr(FileHandle *stream);
} //namespace mbed
#endif /* NSAPI_PPP_H_ */

View File

@ -35,22 +35,24 @@ extern "C" {
* @enum nsapi_error_t
*/
enum nsapi_error {
NSAPI_ERROR_OK = 0, /*!< no error */
NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */
NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported functionality */
NSAPI_ERROR_PARAMETER = -3003, /*!< invalid configuration */
NSAPI_ERROR_NO_CONNECTION = -3004, /*!< not connected to a network */
NSAPI_ERROR_NO_SOCKET = -3005, /*!< socket not available for use */
NSAPI_ERROR_NO_ADDRESS = -3006, /*!< IP address is not known */
NSAPI_ERROR_NO_MEMORY = -3007, /*!< memory resource not available */
NSAPI_ERROR_NO_SSID = -3008, /*!< ssid not found */
NSAPI_ERROR_DNS_FAILURE = -3009, /*!< DNS failed to complete successfully */
NSAPI_ERROR_DHCP_FAILURE = -3010, /*!< DHCP failed to complete successfully */
NSAPI_ERROR_AUTH_FAILURE = -3011, /*!< connection to access point failed */
NSAPI_ERROR_DEVICE_ERROR = -3012, /*!< failure interfacing with the network processor */
NSAPI_ERROR_IN_PROGRESS = -3013, /*!< operation (eg connect) in progress */
NSAPI_ERROR_ALREADY = -3014, /*!< operation (eg connect) already in progress */
NSAPI_ERROR_IS_CONNECTED = -3015, /*!< socket is already connected */
NSAPI_ERROR_OK = 0, /*!< no error */
NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */
NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported functionality */
NSAPI_ERROR_PARAMETER = -3003, /*!< invalid configuration */
NSAPI_ERROR_NO_CONNECTION = -3004, /*!< not connected to a network */
NSAPI_ERROR_NO_SOCKET = -3005, /*!< socket not available for use */
NSAPI_ERROR_NO_ADDRESS = -3006, /*!< IP address is not known */
NSAPI_ERROR_NO_MEMORY = -3007, /*!< memory resource not available */
NSAPI_ERROR_NO_SSID = -3008, /*!< ssid not found */
NSAPI_ERROR_DNS_FAILURE = -3009, /*!< DNS failed to complete successfully */
NSAPI_ERROR_DHCP_FAILURE = -3010, /*!< DHCP failed to complete successfully */
NSAPI_ERROR_AUTH_FAILURE = -3011, /*!< connection to access point failed */
NSAPI_ERROR_DEVICE_ERROR = -3012, /*!< failure interfacing with the network processor */
NSAPI_ERROR_IN_PROGRESS = -3013, /*!< operation (eg connect) in progress */
NSAPI_ERROR_ALREADY = -3014, /*!< operation (eg connect) already in progress */
NSAPI_ERROR_IS_CONNECTED = -3015, /*!< socket is already connected */
NSAPI_ERROR_CONNECTION_LOST = -3016, /*!< connection lost */
NSAPI_ERROR_CONNECTION_TIMEOUT = -3017, /*!< connection timed out */
};
/** Type used to represent error codes
@ -82,6 +84,8 @@ typedef enum nsapi_security {
NSAPI_SECURITY_WPA = 0x2, /*!< phrase conforms to WPA */
NSAPI_SECURITY_WPA2 = 0x3, /*!< phrase conforms to WPA2 */
NSAPI_SECURITY_WPA_WPA2 = 0x4, /*!< phrase conforms to WPA/WPA2 */
NSAPI_SECURITY_PAP = 0x5, /*!< phrase conforms to PPP authentication context */
NSAPI_SECURITY_CHAP = 0x6, /*!< phrase conforms to PPP authentication context */
NSAPI_SECURITY_UNKNOWN = 0xFF, /*!< unknown/unsupported security in scan results */
} nsapi_security_t;

382
platform/ATCmdParser.cpp Normal file
View File

@ -0,0 +1,382 @@
/* Copyright (c) 2017 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.
*
* @section DESCRIPTION
*
* Parser for the AT command syntax
*
*/
#include "ATCmdParser.h"
#include "mbed_poll.h"
#include "mbed_debug.h"
#ifdef LF
#undef LF
#define LF 10
#else
#define LF 10
#endif
#ifdef CR
#undef CR
#define CR 13
#else
#define CR 13
#endif
// getc/putc handling with timeouts
int ATCmdParser::putc(char c)
{
pollfh fhs;
fhs.fh = _fh;
fhs.events = POLLOUT;
int count = poll(&fhs, 1, _timeout);
if (count > 0 && (fhs.revents & POLLOUT)) {
return _fh->write(&c, 1) == 1 ? 0 : -1;
} else {
return -1;
}
}
int ATCmdParser::getc()
{
pollfh fhs;
fhs.fh = _fh;
fhs.events = POLLIN;
int count = poll(&fhs, 1, _timeout);
if (count > 0 && (fhs.revents & POLLIN)) {
unsigned char ch;
return _fh->read(&ch, 1) == 1 ? ch : -1;
} else {
return -1;
}
}
void ATCmdParser::flush()
{
while (_fh->readable()) {
unsigned char ch;
_fh->read(&ch, 1);
}
}
// read/write handling with timeouts
int ATCmdParser::write(const char *data, int size)
{
int i = 0;
for ( ; i < size; i++) {
if (putc(data[i]) < 0) {
return -1;
}
}
return i;
}
int ATCmdParser::read(char *data, int size)
{
int i = 0;
for ( ; i < size; i++) {
int c = getc();
if (c < 0) {
return -1;
}
data[i] = c;
}
return i;
}
// printf/scanf handling
int ATCmdParser::vprintf(const char *format, va_list args)
{
if (vsprintf(_buffer, format, args) < 0) {
return false;
}
int i = 0;
for ( ; _buffer[i]; i++) {
if (putc(_buffer[i]) < 0) {
return -1;
}
}
return i;
}
int ATCmdParser::vscanf(const char *format, va_list args)
{
// Since format is const, we need to copy it into our buffer to
// add the line's null terminator and clobber value-matches with asterisks.
//
// We just use the beginning of the buffer to avoid unnecessary allocations.
int i = 0;
int offset = 0;
while (format[i]) {
if (format[i] == '%' && format[i+1] != '%' && format[i+1] != '*') {
_buffer[offset++] = '%';
_buffer[offset++] = '*';
i++;
} else {
_buffer[offset++] = format[i++];
}
}
// Scanf has very poor support for catching errors
// fortunately, we can abuse the %n specifier to determine
// if the entire string was matched.
_buffer[offset++] = '%';
_buffer[offset++] = 'n';
_buffer[offset++] = 0;
// To workaround scanf's lack of error reporting, we actually
// make two passes. One checks the validity with the modified
// format string that only stores the matched characters (%n).
// The other reads in the actual matched values.
//
// We keep trying the match until we succeed or some other error
// derails us.
int j = 0;
while (true) {
// Ran out of space
if (j+1 >= _buffer_size - offset) {
return false;
}
// Recieve next character
int c = getc();
if (c < 0) {
return -1;
}
_buffer[offset + j++] = c;
_buffer[offset + j] = 0;
// Check for match
int count = -1;
sscanf(_buffer+offset, _buffer, &count);
// We only succeed if all characters in the response are matched
if (count == j) {
// Store the found results
vsscanf(_buffer+offset, format, args);
return j;
}
}
}
// Command parsing with line handling
bool ATCmdParser::vsend(const char *command, va_list args)
{
// Create and send command
if (vsprintf(_buffer, command, args) < 0) {
return false;
}
for (int i = 0; _buffer[i]; i++) {
if (putc(_buffer[i]) < 0) {
return false;
}
}
// Finish with newline
for (size_t i = 0; _output_delimiter[i]; i++) {
if (putc(_output_delimiter[i]) < 0) {
return false;
}
}
debug_if(_dbg_on, "AT> %s\n", _buffer);
return true;
}
bool ATCmdParser::vrecv(const char *response, va_list args)
{
restart:
_aborted = false;
// Iterate through each line in the expected response
while (response[0]) {
// Since response is const, we need to copy it into our buffer to
// add the line's null terminator and clobber value-matches with asterisks.
//
// We just use the beginning of the buffer to avoid unnecessary allocations.
int i = 0;
int offset = 0;
bool whole_line_wanted = false;
while (response[i]) {
if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') {
_buffer[offset++] = '%';
_buffer[offset++] = '*';
i++;
} else {
_buffer[offset++] = response[i++];
// Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification
if (response[i - 1] == '\n' && !(i >= 3 && response[i-3] == '[' && response[i-2] == '^')) {
whole_line_wanted = true;
break;
}
}
}
// Scanf has very poor support for catching errors
// fortunately, we can abuse the %n specifier to determine
// if the entire string was matched.
_buffer[offset++] = '%';
_buffer[offset++] = 'n';
_buffer[offset++] = 0;
debug_if(_dbg_on, "AT? %s\n", _buffer);
// To workaround scanf's lack of error reporting, we actually
// make two passes. One checks the validity with the modified
// format string that only stores the matched characters (%n).
// The other reads in the actual matched values.
//
// We keep trying the match until we succeed or some other error
// derails us.
int j = 0;
while (true) {
// Receive next character
int c = getc();
if (c < 0) {
debug_if(_dbg_on, "AT(Timeout)\n");
return false;
}
// Simplify newlines (borrowed from retarget.cpp)
if ((c == CR && _in_prev != LF) ||
(c == LF && _in_prev != CR)) {
_in_prev = c;
c = '\n';
} else if ((c == CR && _in_prev == LF) ||
(c == LF && _in_prev == CR)) {
_in_prev = c;
// onto next character
continue;
} else {
_in_prev = c;
}
_buffer[offset + j++] = c;
_buffer[offset + j] = 0;
// Check for oob data
for (struct oob *oob = _oobs; oob; oob = oob->next) {
if ((unsigned)j == oob->len && memcmp(
oob->prefix, _buffer+offset, oob->len) == 0) {
debug_if(_dbg_on, "AT! %s\n", oob->prefix);
oob->cb();
if (_aborted) {
debug_if(_dbg_on, "AT(Aborted)\n");
return false;
}
// oob may have corrupted non-reentrant buffer,
// so we need to set it up again
goto restart;
}
}
// Check for match
int count = -1;
if (whole_line_wanted && c != '\n') {
// Don't attempt scanning until we get delimiter if they included it in format
// This allows recv("Foo: %s\n") to work, and not match with just the first character of a string
// (scanf does not itself match whitespace in its format string, so \n is not significant to it)
} else {
sscanf(_buffer+offset, _buffer, &count);
}
// We only succeed if all characters in the response are matched
if (count == j) {
debug_if(_dbg_on, "AT= %s\n", _buffer+offset);
// Reuse the front end of the buffer
memcpy(_buffer, response, i);
_buffer[i] = 0;
// Store the found results
vsscanf(_buffer+offset, _buffer, args);
// Jump to next line and continue parsing
response += i;
break;
}
// Clear the buffer when we hit a newline or ran out of space
// running out of space usually means we ran into binary data
if (c == '\n' || j+1 >= _buffer_size - offset) {
debug_if(_dbg_on, "AT< %s", _buffer+offset);
j = 0;
}
}
}
return true;
}
// Mapping to vararg functions
int ATCmdParser::printf(const char *format, ...)
{
va_list args;
va_start(args, format);
int res = vprintf(format, args);
va_end(args);
return res;
}
int ATCmdParser::scanf(const char *format, ...)
{
va_list args;
va_start(args, format);
int res = vscanf(format, args);
va_end(args);
return res;
}
bool ATCmdParser::send(const char *command, ...)
{
va_list args;
va_start(args, command);
bool res = vsend(command, args);
va_end(args);
return res;
}
bool ATCmdParser::recv(const char *response, ...)
{
va_list args;
va_start(args, response);
bool res = vrecv(response, args);
va_end(args);
return res;
}
// oob registration
void ATCmdParser::oob(const char *prefix, Callback<void()> cb)
{
struct oob *oob = new struct oob;
oob->len = strlen(prefix);
oob->prefix = prefix;
oob->cb = cb;
oob->next = _oobs;
_oobs = oob;
}
void ATCmdParser::abort()
{
_aborted = true;
}

297
platform/ATCmdParser.h Normal file
View File

@ -0,0 +1,297 @@
/* Copyright (c) 2017 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.
*
* @section DESCRIPTION
*
* Parser for the AT command syntax
*
*/
#ifndef MBED_ATCMDPARSER_H
#define MBED_ATCMDPARSER_H
#include "mbed.h"
#include <cstdarg>
#include "Callback.h"
/**
* Parser class for parsing AT commands
*
* Here are some examples:
* @code
* ATCmdParser at = ATCmdParser(serial, "\r\n");
* int value;
* char buffer[100];
*
* at.send("AT") && at.recv("OK");
* at.send("AT+CWMODE=%d", 3) && at.recv("OK");
* at.send("AT+CWMODE?") && at.recv("+CWMODE:%d\r\nOK", &value);
* at.recv("+IPD,%d:", &value);
* at.read(buffer, value);
* at.recv("OK");
* @endcode
*/
namespace mbed {
class ATCmdParser
{
private:
// File handle
// Not owned by ATCmdParser
FileHandle *_fh;
int _buffer_size;
char *_buffer;
int _timeout;
// Parsing information
const char *_output_delimiter;
int _output_delim_size;
char _in_prev;
bool _dbg_on;
bool _aborted;
struct oob {
unsigned len;
const char *prefix;
mbed::Callback<void()> cb;
oob *next;
};
oob *_oobs;
// Prohibiting use of of copy constructor
ATCmdParser(const ATCmdParser &);
// Prohibiting copy assignment Operator
ATCmdParser &operator=(const ATCmdParser &);
public:
/**
* Constructor
*
* @param serial serial interface to use for AT commands
* @param buffer_size size of internal buffer for transaction
* @param timeout timeout of the connection
* @param debug turns on/off debug output for AT commands
*/
ATCmdParser(FileHandle *fh, const char *output_delimiter = "\r",
int buffer_size = 256, int timeout = 8000, bool debug = false)
: _fh(fh), _buffer_size(buffer_size), _in_prev(0), _oobs(NULL)
{
_buffer = new char[buffer_size];
set_timeout(timeout);
set_delimiter(output_delimiter);
debug_on(debug);
}
/**
* Destructor
*/
~ATCmdParser()
{
while (_oobs) {
struct oob *oob = _oobs;
_oobs = oob->next;
delete oob;
}
delete[] _buffer;
}
/**
* Allows timeout to be changed between commands
*
* @param timeout timeout of the connection
*/
void set_timeout(int timeout)
{
_timeout = timeout;
}
/**
* For backwards compatibility.
*
* Please use set_timeout(int) API only from now on.
* Allows timeout to be changed between commands
*
* @param timeout timeout of the connection
*/
MBED_DEPRECATED_SINCE("mbed-os-5.5.0", "Replaced with set_timeout for consistency")
void setTimeout(int timeout)
{
set_timeout(timeout);
}
/**
* Sets string of characters to use as line delimiters
*
* @param delimiter string of characters to use as line delimiters
*/
void set_delimiter(const char *output_delimiter)
{
_output_delimiter = output_delimiter;
_output_delim_size = strlen(output_delimiter);
}
/**
* For backwards compatibility.
*
* Please use set_delimiter(const char *) API only from now on.
* Sets string of characters to use as line delimiters
*
* @param delimiter string of characters to use as line delimiters
*/
MBED_DEPRECATED_SINCE("mbed-os-5.5.0", "Replaced with set_delimiter for consistency")
void setDelimiter(const char *output_delimiter)
{
set_delimiter(output_delimiter);
}
/**
* Allows traces from modem to be turned on or off
*
* @param on set as 1 to turn on traces and vice versa.
*/
void debug_on(uint8_t on)
{
_dbg_on = (on) ? 1 : 0;
}
/**
* For backwards compatibility.
*
* Allows traces from modem to be turned on or off
*
* @param on set as 1 to turn on traces and vice versa.
*/
MBED_DEPRECATED_SINCE("mbed-os-5.5.0", "Replaced with debug_on for consistency")
void debugOn(uint8_t on)
{
debug_on(on);
}
/**
* Sends an AT command
*
* Sends a formatted command using printf style formatting
* @see ::printf
*
* @param command printf-like format string of command to send which
* is appended with a newline
* @param ... all printf-like arguments to insert into command
* @return true only if command is successfully sent
*/
bool send(const char *command, ...) MBED_PRINTF_METHOD(1,2);
bool vsend(const char *command, va_list args);
/**
* Receive an AT response
*
* Receives a formatted response using scanf style formatting
* @see ::scanf
*
* Responses are parsed line at a time.
* Any received data that does not match the response is ignored until
* a timeout occurs.
*
* @param response scanf-like format string of response to expect
* @param ... all scanf-like arguments to extract from response
* @return true only if response is successfully matched
*/
bool recv(const char *response, ...) MBED_SCANF_METHOD(1,2);
bool vrecv(const char *response, va_list args);
/**
* Write a single byte to the underlying stream
*
* @param c The byte to write
* @return The byte that was written or -1 during a timeout
*/
int putc(char c);
/**
* Get a single byte from the underlying stream
*
* @return The byte that was read or -1 during a timeout
*/
int getc();
/**
* Write an array of bytes to the underlying stream
*
* @param data the array of bytes to write
* @param size number of bytes to write
* @return number of bytes written or -1 on failure
*/
int write(const char *data, int size);
/**
* Read an array of bytes from the underlying stream
*
* @param data the destination for the read bytes
* @param size number of bytes to read
* @return number of bytes read or -1 on failure
*/
int read(char *data, int size);
/**
* Direct printf to underlying stream
* @see ::printf
*
* @param format format string to pass to printf
* @param ... arguments to printf
* @return number of bytes written or -1 on failure
*/
int printf(const char *format, ...) MBED_PRINTF_METHOD(1,2);
int vprintf(const char *format, va_list args);
/**
* Direct scanf on underlying stream
* @see ::scanf
*
* @param format format string to pass to scanf
* @param ... arguments to scanf
* @return number of bytes read or -1 on failure
*/
int scanf(const char *format, ...) MBED_SCANF_METHOD(1,2);
int vscanf(const char *format, va_list args);
/**
* Attach a callback for out-of-band data
*
* @param prefix string on when to initiate callback
* @param func callback to call when string is read
* @note out-of-band data is only processed during a scanf call
*/
void oob(const char *prefix, mbed::Callback<void()> func);
/**
* Flushes the underlying stream
*/
void flush();
/**
* Abort current recv
*
* Can be called from oob handler to interrupt the current
* recv operation.
*/
void abort();
};
} //namespace mbed
#endif //MBED_ATCMDPARSER_H

View File

@ -76,7 +76,7 @@ public:
*
* @return True if the buffer is empty, false if not
*/
bool empty() {
bool empty() const {
core_util_critical_section_enter();
bool is_empty = (_head == _tail) && !_full;
core_util_critical_section_exit();
@ -87,7 +87,7 @@ public:
*
* @return True if the buffer is full, false if not
*/
bool full() {
bool full() const {
core_util_critical_section_enter();
bool full = _full;
core_util_critical_section_exit();

41
platform/FileHandle.cpp Normal file
View File

@ -0,0 +1,41 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-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 "FileHandle.h"
#include "platform/mbed_retarget.h"
#include "platform/mbed_critical.h"
namespace mbed {
off_t FileHandle::size()
{
/* remember our current position */
off_t off = seek(0, SEEK_CUR);
if (off < 0) {
return off;
}
/* seek to the end to get the file length */
off_t size = seek(0, SEEK_END);
/* return to our old position */
seek(off, SEEK_SET);
return size;
}
std::FILE *fdopen(FileHandle *fh, const char *mode)
{
return mbed_fdopen(fh, mode);
}
} // namespace mbed

View File

@ -1,5 +1,5 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,7 +18,9 @@
typedef int FILEHANDLE;
#include <stdio.h>
#include <cstdio>
#include "Callback.h"
#include "platform/mbed_poll.h"
#include "platform/platform.h"
namespace mbed {
@ -40,6 +42,12 @@ public:
virtual ~FileHandle() {}
/** Read the contents of a file into a buffer
*
* Devices acting as FileHandles should follow POSIX semantics:
*
* * if no data is available, and non-blocking set return -EAGAIN
* * if no data is available, and blocking set, wait until data is available
* * If any data is available, call returns immediately
*
* @param buffer The buffer to read in to
* @param size The number of bytes to read
@ -62,7 +70,7 @@ public:
* SEEK_SET to start from beginning of file,
* SEEK_CUR to start from current position in file,
* SEEK_END to start from end of file
* @return The new offset of the file
* @return The new offset of the file, negative error code on failure
*/
virtual off_t seek(off_t offset, int whence = SEEK_SET) = 0;
@ -84,6 +92,8 @@ public:
/** Check if the file in an interactive terminal device
*
* @return True if the file is a terminal
* @return False if the file is not a terminal
* @return Negative error code on failure
*/
virtual int isatty()
{
@ -94,7 +104,7 @@ public:
*
* @note This is equivalent to seek(0, SEEK_CUR)
*
* @return The current offset in the file
* @return The current offset in the file, negative error code on failure
*/
virtual off_t tell()
{
@ -114,13 +124,7 @@ public:
*
* @return Size of the file in bytes
*/
virtual size_t size()
{
off_t off = tell();
size_t size = seek(0, SEEK_END);
seek(off, SEEK_SET);
return size;
}
virtual off_t size();
/** Move the file position to a given offset from a given location.
*
@ -133,7 +137,10 @@ public:
* -1 on failure or unsupported
*/
MBED_DEPRECATED_SINCE("mbed-os-5.4", "Replaced by FileHandle::seek")
virtual off_t lseek(off_t offset, int whence) { return seek(offset, whence); }
virtual off_t lseek(off_t offset, int whence)
{
return seek(offset, whence);
}
/** Flush any buffers associated with the FileHandle, ensuring it
* is up to date on disk
@ -143,7 +150,10 @@ public:
* -1 on error
*/
MBED_DEPRECATED_SINCE("mbed-os-5.4", "Replaced by FileHandle::sync")
virtual int fsync() { return sync(); }
virtual int fsync()
{
return sync();
}
/** Find the length of the file
*
@ -151,9 +161,91 @@ public:
* Length of the file
*/
MBED_DEPRECATED_SINCE("mbed-os-5.4", "Replaced by FileHandle::size")
virtual off_t flen() { return size(); }
virtual off_t flen()
{
return size();
}
/** Set blocking or non-blocking mode of the file operation like read/write.
* Definition depends upon the subclass implementing FileHandle.
* The default is blocking.
*
* @param blocking true for blocking mode, false for non-blocking mode.
*/
virtual int set_blocking(bool blocking)
{
return -1;
}
/** Check for poll event flags
* The input parameter can be used or ignored - the could always return all events,
* or could check just the events listed in events.
* Call is non-blocking - returns instantaneous state of events.
* Whenever an event occurs, the derived class should call the sigio() callback).
*
* @param events bitmask of poll events we're interested in - POLLIN/POLLOUT etc.
*
* @returns
* bitmask of poll events that have occurred.
*/
virtual short poll(short events) const
{
// Possible default for real files
return POLLIN | POLLOUT;
}
/** Returns true if the FileHandle is writable.
* Definition depends upon the subclass implementing FileHandle.
* For example, if the FileHandle is of type Stream, writable() could return
* true when there is ample buffer space available for write() calls.
*/
bool writable() const
{
return poll(POLLOUT) & POLLOUT;
}
/** Returns true if the FileHandle is readable.
* Definition depends upon the subclass implementing FileHandle.
* For example, if the FileHandle is of type Stream, readable() could return
* true when there is something available to read.
*/
bool readable() const
{
return poll(POLLIN) & POLLIN;
}
/** Register a callback on state change of the file.
*
* The specified callback will be called on state changes such as when
* the file can be written to or read from.
*
* The callback may be called in an interrupt context and should not
* perform expensive operations.
*
* Note! This is not intended as an attach-like asynchronous api, but rather
* as a building block for constructing such functionality.
*
* The exact timing of when the registered function
* is called is not guaranteed and susceptible to change. It should be used
* as a cue to make read/write/poll calls to find the current state.
*
* @param func Function to call on state change
*/
virtual void sigio(Callback<void()> func)
{
//Default for real files. Do nothing for real files.
}
};
/** Not a member function
* This call is equivalent to posix fdopen().
* Returns a pointer to std::FILE.
* It associates a Stream to an already opened file descriptor (FileHandle)
*
* @param fh, a pointer to an opened file descriptor
* @param mode, operation upon the file descriptor, e.g., 'wb+'*/
std::FILE *fdopen(FileHandle *fh, const char *mode);
} // namespace mbed

View File

@ -164,7 +164,7 @@ int LocalFileHandle::sync() {
return ret;
}
size_t LocalFileHandle::size() {
off_t LocalFileHandle::size() {
lock();
off_t off = semihost_flen(_fh);
unlock();

View File

@ -51,7 +51,7 @@ public:
virtual int sync();
virtual size_t size();
virtual off_t size();
protected:
virtual void lock();

View File

@ -22,9 +22,10 @@ namespace mbed {
Stream::Stream(const char *name) : FileLike(name), _file(NULL) {
// No lock needed in constructor
/* open ourselves */
char buf[12]; /* :0x12345678 + null byte */
std::sprintf(buf, ":%p", this);
_file = std::fopen(buf, "w+");
_file = fdopen(this, "w+");
// fdopen() will make us buffered because Stream::isatty()
// wrongly returns zero which is not being changed for
// backward compatibility
if (_file) {
mbed_set_unbuffered_stream(_file);
} else {
@ -119,7 +120,7 @@ int Stream::sync() {
return 0;
}
size_t Stream::size() {
off_t Stream::size() {
return 0;
}

View File

@ -19,15 +19,16 @@
#include "platform/platform.h"
#include "platform/FileLike.h"
#include "platform/FileHandle.h"
#include <cstdio>
#include <cstdarg>
namespace mbed {
/** \addtogroup platform */
/** @{*/
extern void mbed_set_unbuffered_stream(FILE *_file);
extern int mbed_getc(FILE *_file);
extern char* mbed_gets(char *s, int size, FILE *_file);
extern void mbed_set_unbuffered_stream(std::FILE *_file);
extern int mbed_getc(std::FILE *_file);
extern char* mbed_gets(char *s, int size, std::FILE *_file);
/** @}*/
/** File stream
@ -61,7 +62,7 @@ protected:
virtual void rewind();
virtual int isatty();
virtual int sync();
virtual size_t size();
virtual off_t size();
virtual int _putc(int c) = 0;
virtual int _getc() = 0;

75
platform/mbed_poll.cpp Normal file
View File

@ -0,0 +1,75 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 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 "mbed_poll.h"
#include "FileHandle.h"
#include "Timer.h"
#ifdef MBED_CONF_RTOS_PRESENT
#include "rtos/Thread.h"
#endif
namespace mbed {
// timeout -1 forever, or milliseconds
int poll(pollfh fhs[], unsigned nfhs, int timeout)
{
/**
* TODO Proper wake-up mechanism.
* In order to correctly detect availability of read/write a FileHandle, we needed
* a select or poll mechanisms. We opted for poll as POSIX defines in
* http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html Currently,
* mbed::poll() just spins and scans filehandles looking for any events we are
* interested in. In future, his spinning behaviour will be replaced with
* condition variables.
*/
Timer timer;
if (timeout > 0) {
timer.start();
}
int count = 0;
for (;;) {
/* Scan the file handles */
for (unsigned n = 0; n < nfhs; n++) {
FileHandle *fh = fhs[n].fh;
short mask = fhs[n].events | POLLERR | POLLHUP | POLLNVAL;
if (fh) {
fhs[n].revents = fh->poll(mask) & mask;
} else {
fhs[n].revents = POLLNVAL;
}
if (fhs[n].revents) {
count++;
}
}
if (count) {
break;
}
/* Nothing selected - this is where timeout handling would be needed */
if (timeout == 0 || (timeout > 0 && timer.read_ms() > timeout)) {
break;
}
#ifdef MBED_CONF_RTOS_PRESENT
// TODO - proper blocking
// wait for condition variable, wait queue whatever here
rtos::Thread::yield();
#endif
}
return count;
}
} // namespace mbed

54
platform/mbed_poll.h Normal file
View File

@ -0,0 +1,54 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 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.
*/
#ifndef MBED_POLL_H
#define MBED_POLL_H
#define POLLIN 0x0001 ///< Data may be read without blocking
#define POLLOUT 0x0010 ///< Data may be written without blocking
#define POLLERR 0x1000 ///< An error has occurred on the device or stream
#define POLLHUP 0x2000 ///< The device has been disconnected
#define POLLNVAL 0x4000 ///< The specified file handle value is invalid
namespace mbed {
class FileHandle;
/** \addtogroup platform */
struct pollfh {
FileHandle *fh;
short events;
short revents;
};
/** A mechanism to multiplex input/output over a set of file handles(file descriptors).
* For every file handle provided, poll() examines it for any events registered for that particular
* file handle.
*
* @param fhs, an array of PollFh struct carrying a FileHandle and bitmasks of events
* @param nhfs, number of file handles
* @param timeout, timer value to timeout or -1 for loop forever
*
* @return number of file handles selected (for which revents is non-zero).
* @return 0 if timed out with nothing selected.
* @return -1 for error.
*/
int poll(pollfh fhs[], unsigned nfhs, int timeout);
} // namespace mbed
#endif //MBED_POLL_H

View File

@ -30,6 +30,7 @@
#endif
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#if DEVICE_STDIO_MESSAGES
#include <stdio.h>
#endif
@ -120,6 +121,20 @@ static void init_serial() {
#endif
}
/**
* Sets errno when file opening fails.
* Wipes out the filehandle too.
*
* @param error is a negative error code returned from an mbed function and
* will be negated to store a positive error code in errno
*/
static int handle_open_errors(int error, unsigned filehandle_idx) {
errno = -error;
// Free file handle
filehandles[filehandle_idx] = NULL;
return -1;
}
#if MBED_CONF_FILESYSTEM_PRESENT
static inline int openmode_to_posix(int openmode) {
int posix = openmode;
@ -189,7 +204,7 @@ public:
* @return
* On success, a valid FILEHANDLE is returned.
* On failure, -1 is returned and errno is set to an appropriate value e.g.
* EBADF a bad file descriptor was found (default errno setting)
* ENOENT file not found (default errno setting)
* EMFILE the maximum number of open files was exceeded.
*
* */
@ -219,9 +234,6 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
}
#endif
/* if something goes wrong and errno is not explicly set, errno will be set to EBADF */
errno = EBADF;
// find the first empty slot in filehandles
filehandle_mutex->lock();
unsigned int fh_i;
@ -243,7 +255,7 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
/* FILENAME: ":0x12345678" describes a FileHandle* */
if (name[0] == ':') {
void *p;
sscanf(name, ":%p", &p);
std::sscanf(name, ":%p", &p);
res = (FileHandle*)p;
/* FILENAME: "/file_system/file_name" */
@ -253,41 +265,30 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
if (!path.exists()) {
/* The first part of the filename (between first 2 '/') is not a
* registered mount point in the namespace.
* Free file handle.
*/
filehandles[fh_i] = NULL;
errno = ENOENT;
return -1;
} else if (path.isFile()) {
return handle_open_errors(-ENOENT, fh_i);
}
if (path.isFile()) {
res = path.file();
#if MBED_CONF_FILESYSTEM_PRESENT
} else {
FileSystem *fs = path.fileSystem();
if (fs == NULL) {
/* The filesystem instance managing the namespace under the mount point
* has not been found. Free file handle */
errno = ENOENT;
filehandles[fh_i] = NULL;
return -1;
return handle_open_errors(-ENOENT, fh_i);
}
int posix_mode = openmode_to_posix(openmode);
File *file = new ManagedFile;
int err = file->open(fs, path.fileName(), posix_mode);
if (err < 0) {
errno = -err;
delete file;
} else {
res = file;
return handle_open_errors(err, fh_i);
}
res = file;
#endif
}
}
if (res == NULL) {
// Free file handle
filehandles[fh_i] = NULL;
return -1;
}
filehandles[fh_i] = res;
return fh_i + 3; // +3 as filehandles 0-2 are stdin/out/err
@ -296,10 +297,12 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
extern "C" int PREFIX(_close)(FILEHANDLE fh) {
if (fh < 3) return 0;
errno = EBADF;
FileHandle* fhc = filehandles[fh-3];
filehandles[fh-3] = NULL;
if (fhc == NULL) return -1;
if (fhc == NULL) {
errno = EBADF;
return -1;
}
int err = fhc->close();
if (err < 0) {
@ -317,7 +320,6 @@ extern "C" int PREFIX(_write)(FILEHANDLE fh, const unsigned char *buffer, unsign
#endif
int n; // n is the number of bytes written
errno = EBADF;
if (fh < 3) {
#if DEVICE_SERIAL
if (!stdio_uart_inited) init_serial();
@ -338,7 +340,10 @@ extern "C" int PREFIX(_write)(FILEHANDLE fh, const unsigned char *buffer, unsign
n = length;
} else {
FileHandle* fhc = filehandles[fh-3];
if (fhc == NULL) return -1;
if (fhc == NULL) {
errno = EBADF;
return -1;
}
n = fhc->write(buffer, length);
if (n < 0) {
@ -359,7 +364,6 @@ extern "C" int PREFIX(_read)(FILEHANDLE fh, unsigned char *buffer, unsigned int
#endif
int n; // n is the number of bytes read
errno = EBADF;
if (fh < 3) {
// only read a character at a time from stdin
#if DEVICE_SERIAL
@ -390,7 +394,10 @@ extern "C" int PREFIX(_read)(FILEHANDLE fh, unsigned char *buffer, unsigned int
n = 1;
} else {
FileHandle* fhc = filehandles[fh-3];
if (fhc == NULL) return -1;
if (fhc == NULL) {
errno = EBADF;
return -1;
}
n = fhc->read(buffer, length);
if (n < 0) {
@ -404,57 +411,76 @@ extern "C" int PREFIX(_read)(FILEHANDLE fh, unsigned char *buffer, unsigned int
#endif
}
#ifdef __ARMCC_VERSION
extern "C" int PREFIX(_istty)(FILEHANDLE fh)
#else
extern "C" int _isatty(FILEHANDLE fh)
#endif
{
errno = EBADF;
/* stdin, stdout and stderr should be tty */
if (fh < 3) return 1;
FileHandle* fhc = filehandles[fh-3];
if (fhc == NULL) return -1;
int err = fhc->isatty();
if (err < 0) {
errno = -err;
return -1;
} else {
if (fhc == NULL) {
errno = EBADF;
return 0;
}
int tty = fhc->isatty();
if (tty < 0) {
errno = -tty;
return 0;
} else {
return tty;
}
}
extern "C"
#if defined(__ARMCC_VERSION)
int _sys_seek(FILEHANDLE fh, long position)
int _sys_seek(FILEHANDLE fh, long offset)
#elif defined(__ICCARM__)
long __lseek(int fh, long offset, int whence)
#else
int _lseek(FILEHANDLE fh, int offset, int whence)
#endif
{
errno = EBADF;
if (fh < 3) return 0;
#if defined(__ARMCC_VERSION)
int whence = SEEK_SET;
#endif
if (fh < 3) {
errno = ESPIPE;
return -1;
}
FileHandle* fhc = filehandles[fh-3];
if (fhc == NULL) return -1;
if (fhc == NULL) {
errno = EBADF;
return -1;
}
#if defined(__ARMCC_VERSION)
return fhc->seek(position, SEEK_SET);
#else
return fhc->seek(offset, whence);
#endif
off_t off = fhc->seek(offset, whence);
if (off < 0) {
errno = -off;
return -1;
}
// Assuming INT_MAX = LONG_MAX, so we don't care about prototype difference
if (off > INT_MAX) {
errno = EOVERFLOW;
return -1;
}
return off;
}
#ifdef __ARMCC_VERSION
extern "C" int PREFIX(_ensure)(FILEHANDLE fh) {
errno = EBADF;
if (fh < 3) return 0;
FileHandle* fhc = filehandles[fh-3];
if (fhc == NULL) return -1;
if (fhc == NULL) {
errno = EBADF;
return -1;
}
int err = fhc->sync();
if (err < 0) {
@ -466,13 +492,27 @@ extern "C" int PREFIX(_ensure)(FILEHANDLE fh) {
}
extern "C" long PREFIX(_flen)(FILEHANDLE fh) {
errno = EBADF;
if (fh < 3) return 0;
if (fh < 3) {
errno = EINVAL;
return -1;
}
FileHandle* fhc = filehandles[fh-3];
if (fhc == NULL) return -1;
if (fhc == NULL) {
errno = EBADF;
return -1;
}
return fhc->size();
off_t size = fhc->size();
if (size < 0) {
errno = -size;
return -1;
}
if (size > LONG_MAX) {
errno = EOVERFLOW;
return -1;
}
return size;
}
#endif
@ -491,10 +531,12 @@ extern "C" int _fstat(int fd, struct stat *st) {
namespace std {
extern "C" int remove(const char *path) {
#if MBED_CONF_FILESYSTEM_PRESENT
errno = EBADF;
FilePath fp(path);
FileSystem *fs = fp.fileSystem();
if (fs == NULL) return -1;
if (fs == NULL) {
errno = ENOENT;
return -1;
}
int err = fs->remove(fp.fileName());
if (err < 0) {
@ -511,14 +553,21 @@ extern "C" int remove(const char *path) {
extern "C" int rename(const char *oldname, const char *newname) {
#if MBED_CONF_FILESYSTEM_PRESENT
errno = EBADF;
FilePath fpOld(oldname);
FilePath fpNew(newname);
FileSystem *fsOld = fpOld.fileSystem();
FileSystem *fsNew = fpNew.fileSystem();
if (fsOld == NULL) {
errno = ENOENT;
return -1;
}
/* rename only if both files are on the same FS */
if (fsOld != fsNew || fsOld == NULL) return -1;
if (fsOld != fsNew) {
errno = EXDEV;
return -1;
}
int err = fsOld->rename(fpOld.fileName(), fpNew.fileName());
if (err < 0) {
@ -552,11 +601,12 @@ extern "C" char *_sys_command_string(char *cmd, int len) {
extern "C" DIR *opendir(const char *path) {
#if MBED_CONF_FILESYSTEM_PRESENT
errno = EBADF;
FilePath fp(path);
FileSystem* fs = fp.fileSystem();
if (fs == NULL) return NULL;
if (fs == NULL) {
errno = ENOENT;
return NULL;
}
Dir *dir = new ManagedDir;
int err = dir->open(fs, fp.fileName());
@ -894,16 +944,34 @@ int __wrap_atexit(void (*func)()) {
namespace mbed {
void mbed_set_unbuffered_stream(FILE *_file) {
void mbed_set_unbuffered_stream(std::FILE *_file) {
#if defined (__ICCARM__)
char buf[2];
std::setvbuf(_file,buf,_IONBF,NULL);
std::setvbuf(_file,buf,_IONBF,NULL);
#else
setbuf(_file, NULL);
#endif
}
int mbed_getc(FILE *_file){
/* Applications are expected to use fdopen()
* not this function directly. This code had to live here because FILE and FileHandle
* processes are all linked together here.
*/
std::FILE *mbed_fdopen(FileHandle *fh, const char *mode)
{
char buf[12]; /* :0x12345678 + null byte */
std::sprintf(buf, ":%p", fh);
std::FILE *stream = std::fopen(buf, mode);
/* newlib-nano doesn't appear to ever call _isatty itself, so
* happily fully buffers an interactive stream. Deal with that here.
*/
if (stream && fh->isatty()) {
mbed_set_unbuffered_stream(stream);
}
return stream;
}
int mbed_getc(std::FILE *_file){
#if defined (__ICCARM__)
/*This is only valid for unbuffered streams*/
int res = std::fgetc(_file);
@ -918,7 +986,7 @@ int mbed_getc(FILE *_file){
#endif
}
char* mbed_gets(char*s, int size, FILE *_file){
char* mbed_gets(char*s, int size, std::FILE *_file){
#if defined (__ICCARM__)
/*This is only valid for unbuffered streams*/
char *str = fgets(s,size,_file);

View File

@ -19,6 +19,9 @@
#ifndef RETARGET_H
#define RETARGET_H
#if __cplusplus
#include <cstdio>
#endif //__cplusplus
#include <stdint.h>
#include <stddef.h>
@ -48,7 +51,11 @@ typedef int mode_t; ///< Mode for opening files
/* DIR declarations must also be here */
#if __cplusplus
namespace mbed { class Dir; }
namespace mbed {
class Dir;
class FileHandle;
std::FILE *mbed_fdopen(FileHandle *fh, const char *mode);
}
typedef mbed::Dir DIR;
#else
typedef struct Dir DIR;
@ -77,71 +84,60 @@ extern "C" {
* Note also that ARMCC errno.h defines some symbol values differently from
* the GCC_ARM/IAR/standard POSIX definitions. The definitions guard against
* this and future changes by changing the symbol definition as shown below. */
#ifdef ENOENT
#undef ENOENT
#endif
#define ENOENT 2 /* No such file or directory. */
#ifdef EIO
#undef EIO
#endif
#define EIO 5 /* I/O error */
#ifdef ENXIO
#undef ENXIO
#endif
#define ENXIO 6 /* No such device or address */
#ifdef ENOEXEC
#undef ENOEXEC
#endif
#define ENOEXEC 8 /* Exec format error */
#ifdef EBADF
#undef EBADF
#endif
#define EBADF 9 /* Bad file number */
#ifdef ENOMEM
#undef EAGAIN
#define EAGAIN 11 /* Resource unavailable, try again */
#undef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN /* Operation would block */
#undef ENOMEM
#endif
#define ENOMEM 12 /* Not enough space */
#ifdef EACCES
#undef EACCES
#endif
#define EACCES 13 /* Permission denied */
#ifdef EFAULT
#undef EFAULT
#endif
#define EFAULT 14 /* Bad address */
#ifdef EEXIST
#undef EEXIST
#endif
#define EEXIST 17 /* File exists */
#ifdef EINVAL
#undef EXDEV
#define EXDEV 18 /* Cross-device link */
#undef EINVAL
#endif
#define EINVAL 22 /* Invalid argument */
#ifdef ENFILE
#undef ENFILE
#endif
#define ENFILE 23 /* Too many open files in system */
#ifdef EMFILE
#undef EMFILE
#endif
#define EMFILE 24 /* File descriptor value too large */
#ifdef ENOSYS
#undef ESPIPE
#define ESPIPE 29 /* Invalid seek */
#undef ENOSYS
#endif
#define ENOSYS 38 /* Function not implemented */
#undef EOVERFLOW
#define EOVERFLOW 75 /* Value too large to be stored in data type */
/* Missing stat.h defines.
* The following are sys/stat.h definitions not currently present in the ARMCC
* errno.h. Note, ARMCC errno.h defines some symbol values differing from
@ -187,5 +183,4 @@ enum {
DT_SOCK, // This is a UNIX domain socket.
};
#endif /* RETARGET_H */

View File

@ -278,6 +278,38 @@
#endif
#endif
#ifndef MBED_PRINTF
#if defined(__GNUC__) || defined(__CC_ARM)
#define MBED_PRINTF(format_idx, first_param_idx) __attribute__ ((__format__(__printf__, format_idx, first_param_idx)))
#else
#define MBED_PRINTF(format_idx, first_param_idx)
#endif
#endif
#ifndef MBED_PRINTF_METHOD
#if defined(__GNUC__) || defined(__CC_ARM)
#define MBED_PRINTF_METHOD(format_idx, first_param_idx) __attribute__ ((__format__(__printf__, format_idx+1, first_param_idx+1)))
#else
#define MBED_PRINTF_METHOD(format_idx, first_param_idx)
#endif
#endif
#ifndef MBED_SCANF
#if defined(__GNUC__) || defined(__CC_ARM)
#define MBED_SCANF(format_idx, first_param_idx) __attribute__ ((__format__(__scanf__, format_idx, first_param_idx)))
#else
#define MBED_SCANF(format_idx, first_param_idx)
#endif
#endif
#ifndef MBED_SCANF_METHOD
#if defined(__GNUC__) || defined(__CC_ARM)
#define MBED_SCANF_METHOD(format_idx, first_param_idx) __attribute__ ((__format__(__scanf__, format_idx+1, first_param_idx+1)))
#else
#define MBED_SCANF_METHOD(format_idx, first_param_idx)
#endif
#endif
// FILEHANDLE declaration
#if defined(TOOLCHAIN_ARM)
#include <rt_sys.h>

View File

@ -1,85 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 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 "gpio_api.h"
#include "mbed_wait_api.h"
#include "C027_api.h"
#include "us_ticker_api.h"
static gpio_t mdmEn, mdmLvlOe, mdmILvlOe, mdmUsbDet;
static gpio_t gpsEn;
void c027_init(void) {
gpio_t led, mdmRts, mdmRst, gpsRst, mdmPwrOn;
// start with modem disabled
gpio_init_out_ex(&mdmEn, MDMEN, 0);
gpio_init_out_ex(&mdmRst, MDMRST, 1);
gpio_init_out_ex(&mdmPwrOn, MDMPWRON, 1);
gpio_init_out_ex(&mdmLvlOe, MDMLVLOE, 1); // LVLEN: 1=disabled
gpio_init_out_ex(&mdmILvlOe, MDMILVLOE, 0); // ILVLEN: 0=disabled
gpio_init_out_ex(&mdmUsbDet, MDMUSBDET, 0);
gpio_init_out_ex(&mdmRts, MDMRTS, 0);
// start with gps disabled
gpio_init_out_ex(&gpsEn, GPSEN, 0);
gpio_init_out_ex(&gpsRst, GPSRST, 1);
// led should be off
gpio_init_out_ex(&led, LED, 0);
// Can't use wait_ms() as RTOS isn't initialised yet
// so this is the correct way to wait for 50 ms
uint32_t start = us_ticker_read();
while ((us_ticker_read() - start) < 50000);
}
void c027_mdm_powerOn(int usb) {
// turn on the mode by enabling power with power on pin low and correct USB detect level
gpio_write(&mdmUsbDet, usb ? 1 : 0); // USBDET: 0=disabled, 1=enabled
if (!gpio_read(&mdmEn)) { // enable modem
gpio_write(&mdmEn, 1); // LDOEN: 1=on
wait_ms(1); // wait until supply switched off
// now we can safely enable the level shifters
gpio_write(&mdmLvlOe, 0); // LVLEN: 0=enabled (uart/gpio)
if (gpio_read(&gpsEn))
gpio_write(&mdmILvlOe, 1); // ILVLEN: 1=enabled (i2c)
}
}
void c027_mdm_powerOff(void) {
if (gpio_read(&mdmEn)) {
// diable all level shifters
gpio_write(&mdmILvlOe, 0); // ILVLEN: 0=disabled (i2c)
gpio_write(&mdmLvlOe, 1); // LVLEN: 1=disabled (uart/gpio)
gpio_write(&mdmUsbDet, 0); // USBDET: 0=disabled
// now we can savely switch off the ldo
gpio_write(&mdmEn, 0); // LDOEN: 0=off
}
}
void c027_gps_powerOn(void) {
if (!gpio_read(&gpsEn)) {
// switch on power supply
gpio_write(&gpsEn, 1); // LDOEN: 1=on
wait_ms(1); // wait until supply switched off
if (gpio_read(&mdmEn))
gpio_write(&mdmILvlOe, 1); // ILVLEN: 1=enabled (i2c)
}
}
void c027_gps_powerOff(void) {
if (gpio_read(&gpsEn)) {
gpio_write(&mdmILvlOe, 0); // ILVLEN: 0=disabled (i2c)
gpio_write(&gpsEn, 0); // LDOEN: 0=off
}
}

View File

@ -1,22 +0,0 @@
#ifndef C027_H
#define C027_H
#ifdef __cplusplus
extern "C" {
#endif
void c027_init(void);
void c027_mdm_powerOn(int usb);
void c027_mdm_powerOff(void);
void c027_gps_powerOn(void);
void c027_gps_powerOff(void);
#ifdef __cplusplus
}
#endif
#endif // C027_H

View File

@ -162,6 +162,11 @@ typedef enum {
NC = (int)0xFFFFFFFF
} PinName;
#define ACTIVE_HIGH_POLARITY 1
#define ACTIVE_LOW_POLARITY 0
#define MDM_PIN_POLARITY ACTIVE_LOW_POLARITY
typedef enum {
PullUp = 0,
PullDown = 3,

View File

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "C027_api.h"
#include "ublox_low_level_api.h"
// called before main
void mbed_sdk_init() {
c027_init();
ublox_mdm_init();
}

View File

@ -0,0 +1,62 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 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.
*/
#if MBED_CONF_NSAPI_PRESENT
#include "cellular/onboard_modem_api.h"
#include "ublox_low_level_api.h"
#include "gpio_api.h"
#include "platform/mbed_wait_api.h"
#include "PinNames.h"
#if MODEM_ON_BOARD
static void press_power_button(int time_ms)
{
gpio_t gpio;
gpio_init_out_ex(&gpio, MDMPWRON, 0);
wait_ms(time_ms);
gpio_write(&gpio, 1);
}
void onboard_modem_init()
{
//currently USB is not supported, so pass 0 to disable USB
//This call does everything except actually pressing the power button
ublox_mdm_powerOn(0);
}
void onboard_modem_deinit()
{
ublox_mdm_powerOff();
}
void onboard_modem_power_up()
{
/* keep the power line low for 150 milisecond */
press_power_button(150);
/* give modem a little time to respond */
wait_ms(100);
}
void onboard_modem_power_down()
{
/* keep the power line low for 1 second */
press_power_button(1000);
}
#endif //MODEM_ON_BOARD
#endif //MBED_CONF_NSAPI_PRESENT

View File

@ -0,0 +1,97 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 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 <stdbool.h>
#include "hal/us_ticker_api.h"
#include "platform/mbed_wait_api.h"
#include "gpio_api.h"
#include "ublox_low_level_api.h"
static bool modemOn;
static bool gpsOn;
void ublox_mdm_init(void)
{
gpio_t gpio;
// start with modem disabled
gpio_init_out_ex(&gpio, MDMEN, 0);
gpio_init_out_ex(&gpio, MDMRST, 1);
gpio_init_out_ex(&gpio, MDMPWRON, 1);
gpio_init_out_ex(&gpio, MDMLVLOE, 1); // LVLEN: 1=disabled
gpio_init_out_ex(&gpio, MDMILVLOE, 0); // ILVLEN: 0=disabled
gpio_init_out_ex(&gpio, MDMUSBDET, 0);
gpio_init_out_ex(&gpio, MDMRTS, 0);
// start with gps disabled
gpio_init_out_ex(&gpio, GPSEN, 0);
gpio_init_out_ex(&gpio, GPSRST, 1);
// led should be off
gpio_init_out_ex(&gpio, LED, 0);
// Can't use wait_ms() as RTOS isn't initialised yet
//wait_ms(50); // when USB cable is inserted the interface chip issues
// Here's the code from the non-RTOS version
uint32_t start = us_ticker_read();
while ((us_ticker_read() - start) < 50000);
}
void ublox_mdm_powerOn(int usb)
{
gpio_t gpio;
// turn on the mode by enabling power with power on pin low and correct USB detect level
gpio_init_out_ex(&gpio, MDMUSBDET, usb ? 1 : 0); // USBDET: 0=disabled, 1=enabled
if (!modemOn) { // enable modem
gpio_init_out_ex(&gpio, MDMEN, 1); // LDOEN: 1=on
wait_ms(1); // wait until supply switched off
// now we can safely enable the level shifters
gpio_init_out_ex(&gpio, MDMLVLOE, 0); // LVLEN: 0=enabled (uart/gpio)
if (gpsOn)
gpio_init_out_ex(&gpio, MDMILVLOE, 1); // ILVLEN: 1=enabled (i2c)
}
}
void ublox_mdm_powerOff(void)
{
gpio_t gpio;
if (modemOn) {
// diable all level shifters
gpio_init_out_ex(&gpio, MDMILVLOE, 0); // ILVLEN: 0=disabled (i2c)
gpio_init_out_ex(&gpio, MDMLVLOE, 1); // LVLEN: 1=disabled (uart/gpio)
gpio_init_out_ex(&gpio,MDMUSBDET, 0); // USBDET: 0=disabled
// now we can savely switch off the ldo
gpio_init_out_ex(&gpio, MDMEN, 0); // LDOEN: 0=off
modemOn = false;
}
}
void ublox_gps_powerOn(void)
{
gpio_t gpio;
if (!gpsOn) {
// switch on power supply
gpio_init_out_ex(&gpio, GPSEN, 1); // LDOEN: 1=on
wait_ms(1); // wait until supply switched off
if (modemOn)
gpio_init_out_ex(&gpio, MDMILVLOE, 1); // ILVLEN: 1=enabled (i2c)
}
}
void ublox_gps_powerOff(void)
{
gpio_t gpio;
if (gpsOn) {
gpio_init_out_ex(&gpio, MDMILVLOE, 0); // ILVLEN: 0=disabled (i2c)
gpio_init_out_ex(&gpio, GPSEN, 0); // LDOEN: 0=off
}
}

View File

@ -0,0 +1,37 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 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.
*/
#ifndef UBLOX_LOW_LEVEL_H
#define UBLOX_LOW_LEVEL_H
#ifdef __cplusplus
extern "C" {
#endif
void ublox_mdm_init(void);
void ublox_mdm_powerOn(int usb);
void ublox_mdm_powerOff(void);
void ublox_gps_powerOn(void);
void ublox_gps_powerOff(void);
#ifdef __cplusplus
}
#endif
#endif // UBLOX_LOW_LEVEL_H

View File

@ -71,6 +71,10 @@ struct spi_s {
LPC_SSP_TypeDef *spi;
};
struct modem_s {
uint32_t state;
};
#ifdef __cplusplus
}
#endif

View File

@ -38,6 +38,9 @@ extern "C" {
#endif
typedef enum {
// Not connected
NC = -1,
PA_0 = 0x00,
PA_1 = 0x01,
PA_2 = 0x02,
@ -141,10 +144,20 @@ typedef enum {
RADIO_RX = PC_6,
RADIO_RTS = PB_10,
RADIO_CTS = PB_12,
RADIO_DCD = D5,
RADIO_DSR = D8,
RADIO_DTR = D4,
RADIO_RI = D9,
RADIO_DCD = NC,
RADIO_DSR = NC,
RADIO_DTR = NC,
RADIO_RI = NC,
MDMPWRON = PC_13, // 3G_ONOFF DragonFly Design Guide, Page No. 16
MDMTXD = RADIO_TX, // Transmit Data
MDMRXD = RADIO_RX, // Receive Data
MDMRTS = RADIO_RTS, // Request to Send
MDMCTS = RADIO_CTS, // Clear to Send
MDMDCD = RADIO_DCD, // Data Carrier Detect
MDMDSR = RADIO_DSR, // Data Set Ready
MDMDTR = RADIO_DTR, // Data Terminal Ready
MDMRI = RADIO_RI, // Ring Indicator
WAKEUP = D3,
// I2C1 and I2C3 are available on Arduino pins
@ -175,12 +188,16 @@ typedef enum {
SPI_MISO = SPI3_MISO,
SPI_SCK = SPI3_SCK,
SPI_CS1 = PA_4,
SPI_CS2 = PB_14,
SPI_CS2 = PB_14
// Not connected
NC = (int)0xFFFFFFFF
} PinName;
#define ACTIVE_HIGH_POLARITY 1
#define ACTIVE_LOW_POLARITY 0
#define MDM_PIN_POLARITY ACTIVE_HIGH_POLARITY
#ifdef __cplusplus
}
#endif

View File

@ -16,9 +16,9 @@ define region RAM_region = mem:[from __region_RAM_start__ to __region_RAM_end__]
/* Stack and Heap */
/* Stack: 4kB - 408B for vector table */
/* Heap: 96kB */
/* Heap: 64kB */
define symbol __size_cstack__ = 0xe68;
define symbol __size_heap__ = 0x18000;
define symbol __size_heap__ = 0x10000;
define block CSTACK with alignment = 8, size = __size_cstack__ { };
define block HEAP with alignment = 8, size = __size_heap__ { };
define block STACKHEAP with fixed order { block HEAP, block CSTACK };

View File

@ -0,0 +1,65 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 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.
*/
#if MBED_CONF_NSAPI_PRESENT
#include "cellular/onboard_modem_api.h"
#include "gpio_api.h"
#include "platform/mbed_wait_api.h"
#include "PinNames.h"
#if MODEM_ON_BOARD
static void press_power_button(int time_ms)
{
gpio_t gpio;
gpio_init_out_ex(&gpio, MDMPWRON, 1);
gpio_write(&gpio, 0);
wait_ms(time_ms);
gpio_write(&gpio, 1);
}
void onboard_modem_init()
{
//does nothing at the moment, TODO: MultiTech to add hardware initialization stuff if needed
}
void onboard_modem_deinit()
{
//does nothing at the moment, TODO: MultiTech to add hardware de-initialization stuff if needed
}
void onboard_modem_power_up()
{
/* keep the power line low for 200 milisecond */
press_power_button(200);
/* give modem a little time to respond */
wait_ms(100);
}
void onboard_modem_power_down()
{
gpio_t gpio;
gpio_init_out_ex(&gpio, MDMPWRON, 0);
/* keep the power line low for more than 10 seconds.
* If 3G_ON_OFF pin is kept low for more than a second, a controlled disconnect and shutdown takes
* place, Due to the network disconnect, shut-off can take up to 30 seconds. However, we wait for 10
* seconds only */
wait_ms(10*1000);
}
#endif //MODEM_ON_BOARD
#endif //MBED_CONF_NSAPI_PRESENT

View File

@ -248,7 +248,19 @@
"supported_form_factors": ["ARDUINO"],
"core": "Cortex-M3",
"supported_toolchains": ["ARM", "uARM", "GCC_ARM", "GCC_CR", "IAR"],
"extra_labels": ["NXP", "LPC176X", "FLASH_CMSIS_ALGO"],
"extra_labels": ["NXP", "LPC176X", "FLASH_CMSIS_ALGO", "UBLOX_MODEM_GENERIC"],
"config": {
"modem_is_on_board": {
"help": "Value: Tells the build system that the modem is on-board as oppose to a plug-in shield/module.",
"value": 1,
"macro_name": "MODEM_ON_BOARD"
},
"modem_data_connection_type": {
"help": "Value: Defines how the modem is wired up to the MCU, e.g., data connection can be a UART or USB and so forth.",
"value": 1,
"macro_name": "MODEM_ON_BOARD_UART"
}
},
"macros": ["TARGET_LPC1768"],
"inherits": ["LPCTarget"],
"device_has": ["ANALOGIN", "ANALOGOUT", "CAN", "DEBUG_AWARENESS", "ERROR_RED", "ETHERNET", "I2C", "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_FC", "SLEEP", "SPI", "SPISLAVE", "STDIO_MESSAGES", "FLASH"],
@ -1338,6 +1350,18 @@
"core": "Cortex-M4F",
"supported_toolchains": ["ARM", "uARM", "GCC_ARM", "IAR"],
"extra_labels": ["STM", "STM32F4", "STM32F411RE"],
"config": {
"modem_is_on_board": {
"help": "Value: Tells the build system that the modem is on-board as oppose to a plug-in shield/module.",
"value": 1,
"macro_name": "MODEM_ON_BOARD"
},
"modem_data_connection_type": {
"help": "Value: Defines how an on-board modem is wired up to the MCU, e.g., data connection can be a UART or USB and so forth.",
"value": 1,
"macro_name": "MODEM_ON_BOARD_UART"
}
},
"macros": ["HSE_VALUE=26000000", "VECT_TAB_OFFSET=0x08010000","TRANSACTION_QUEUE_SIZE_SPI=2", "RTC_LSI=1"],
"post_binary_hook": {
"function": "MTSCode.combine_bins_mts_dragonfly",