diff --git a/drivers/RawSerial.cpp b/drivers/RawSerial.cpp index e7e6884c3a6..639586892ac 100644 --- a/drivers/RawSerial.cpp +++ b/drivers/RawSerial.cpp @@ -15,8 +15,10 @@ */ #include "drivers/RawSerial.h" #include "platform/mbed_wait_api.h" +#include #include + #if DEVICE_SERIAL #define STRING_STACK_LIMIT 120 diff --git a/drivers/Serial.h b/drivers/Serial.h index 7a9a2875fe1..5294c1c2f3b 100644 --- a/drivers/Serial.h +++ b/drivers/Serial.h @@ -81,6 +81,23 @@ class Serial : public SerialBase, public Stream { */ 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); diff --git a/drivers/SerialBase.h b/drivers/SerialBase.h index 83ef34c661c..47a81a7aba5 100644 --- a/drivers/SerialBase.h +++ b/drivers/SerialBase.h @@ -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" diff --git a/drivers/UARTSerial.cpp b/drivers/UARTSerial.cpp new file mode 100644 index 00000000000..19d8174dbcc --- /dev/null +++ b/drivers/UARTSerial.cpp @@ -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 +#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 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(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(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 diff --git a/drivers/UARTSerial.h b/drivers/UARTSerial.h new file mode 100644 index 00000000000..fda877d8960 --- /dev/null +++ b/drivers/UARTSerial.h @@ -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 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 _rxbuf; + CircularBuffer _txbuf; + + PlatformMutex _mutex; + + Callback _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 diff --git a/drivers/mbed_lib.json b/drivers/mbed_lib.json new file mode 100644 index 00000000000..c268b6381aa --- /dev/null +++ b/drivers/mbed_lib.json @@ -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 + } + } +} diff --git a/features/FEATURE_COMMON_PAL/mbed-trace/source/mbed_trace.c b/features/FEATURE_COMMON_PAL/mbed-trace/source/mbed_trace.c index 9f648415aa8..9c1c28fef70 100644 --- a/features/FEATURE_COMMON_PAL/mbed-trace/source/mbed_trace.c +++ b/features/FEATURE_COMMON_PAL/mbed-trace/source/mbed_trace.c @@ -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; + } } } diff --git a/features/FEATURE_LWIP/lwip-interface/.mbedignore b/features/FEATURE_LWIP/lwip-interface/.mbedignore index 13aba88ae4e..e12200526f3 100644 --- a/features/FEATURE_LWIP/lwip-interface/.mbedignore +++ b/features/FEATURE_LWIP/lwip-interface/.mbedignore @@ -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/* diff --git a/features/FEATURE_LWIP/lwip-interface/EthernetInterface.cpp b/features/FEATURE_LWIP/lwip-interface/EthernetInterface.cpp index 79fbfd11249..7fdfb425e20 100644 --- a/features/FEATURE_LWIP/lwip-interface/EthernetInterface.cpp +++ b/features/FEATURE_LWIP/lwip-interface/EthernetInterface.cpp @@ -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() diff --git a/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_NXP/lpc17_emac.c b/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_NXP/lpc17_emac.c index 9fa203df68f..38efe0a4283 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_NXP/lpc17_emac.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_NXP/lpc17_emac.c @@ -43,6 +43,8 @@ #include "mbed_interface.h" #include +#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 ------------------------------ */ diff --git a/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_NXP/lpc_phy_dp83848.c b/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_NXP/lpc_phy_dp83848.c index 14813a561b0..e44fa73c4c4 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_NXP/lpc_phy_dp83848.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_NXP/lpc_phy_dp83848.c @@ -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 */ + /** * @} */ diff --git a/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_STM/stm32xx_emac.c b/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_STM/stm32xx_emac.c index 4e230228aae..65605b69a1e 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_STM/stm32xx_emac.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_STM/stm32xx_emac.c @@ -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 diff --git a/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/cc.h b/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/cc.h index 624fc974a8d..d92f47f6394 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/cc.h +++ b/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/cc.h @@ -35,6 +35,11 @@ #include #include /* 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 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" diff --git a/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/lwip_sys_arch.c b/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/lwip_sys_arch.c index 4ec57613d3c..afca20824ff 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/lwip_sys_arch.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/lwip_sys_arch.c @@ -489,6 +489,42 @@ sys_thread_t sys_thread_new(const char *pcName, #ifdef LWIP_DEBUG +#include + +#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 */ diff --git a/features/FEATURE_LWIP/lwip-interface/lwip-sys/lwip_tcp_isn.c b/features/FEATURE_LWIP/lwip-interface/lwip-sys/lwip_tcp_isn.c index 5ddd47522bb..d89629c94b2 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip-sys/lwip_tcp_isn.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip-sys/lwip_tcp_isn.c @@ -76,19 +76,21 @@ #include /* 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; diff --git a/features/FEATURE_LWIP/lwip-interface/lwip/src/include/lwip/debug.h b/features/FEATURE_LWIP/lwip-interface/lwip/src/include/lwip/debug.h index a142f1cff3c..b74fd1a2b03 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip/src/include/lwip/debug.h +++ b/features/FEATURE_LWIP/lwip-interface/lwip/src/include/lwip/debug.h @@ -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 */ diff --git a/features/FEATURE_LWIP/lwip-interface/lwip/src/netif/ppp/lwip_utils.c b/features/FEATURE_LWIP/lwip-interface/lwip/src/netif/ppp/lwip_utils.c index 008c63375aa..d4feebb90bb 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip/src/netif/ppp/lwip_utils.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip/src/netif/ppp/lwip_utils.c @@ -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); diff --git a/features/FEATURE_LWIP/lwip-interface/lwip_stack.c b/features/FEATURE_LWIP/lwip-interface/lwip_stack.c index a1e7709d3e7..90935e700bc 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip_stack.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip_stack.c @@ -16,6 +16,7 @@ #include "nsapi.h" #include "mbed_interface.h" +#include "mbed_assert.h" #include #include #include @@ -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, + lwip_inited = true; + } +} + +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, + 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); + emac, MBED_NETIF_INIT_FN, tcpip_input)) { + return NSAPI_ERROR_DEVICE_ERROR; + } - netif_set_link_callback(&lwip_netif, mbed_lwip_netif_link_irq); - netif_set_status_callback(&lwip_netif, mbed_lwip_netif_status_irq); + // Note the MAC address actually in use + mbed_lwip_record_mac_address(&lwip_netif); #if !DEVICE_EMAC - eth_arch_enable_interrupts(); + 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, const char *ip, const char *netmask, const char *gw) +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); + } + } + + if (ret != NSAPI_ERROR_OK) { + return ret; + } + + netif_inited = true; + if (ppp) { + netif_is_ppp = ppp; } - // Zero out socket set - mbed_lwip_arena_init(); + 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; diff --git a/features/FEATURE_LWIP/lwip-interface/lwip_stack.h b/features/FEATURE_LWIP/lwip-interface/lwip_stack.h index afbb8d2f5b0..1cba75b98ab 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip_stack.h +++ b/features/FEATURE_LWIP/lwip-interface/lwip_stack.h @@ -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 diff --git a/features/FEATURE_LWIP/lwip-interface/lwipopts.h b/features/FEATURE_LWIP/lwip-interface/lwipopts.h index e28b008e33b..cc2d0b7790d 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwipopts.h +++ b/features/FEATURE_LWIP/lwip-interface/lwipopts.h @@ -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 #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 diff --git a/features/FEATURE_LWIP/lwip-interface/mbed_lib.json b/features/FEATURE_LWIP/lwip-interface/mbed_lib.json index 0ec67b79191..d330cb88865 100644 --- a/features/FEATURE_LWIP/lwip-interface/mbed_lib.json +++ b/features/FEATURE_LWIP/lwip-interface/mbed_lib.json @@ -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 } } } diff --git a/features/FEATURE_LWIP/lwip-interface/ppp_lwip.cpp b/features/FEATURE_LWIP/lwip-interface/ppp_lwip.cpp new file mode 100644 index 00000000000..9e779ea8fb7 --- /dev/null +++ b/features/FEATURE_LWIP/lwip-interface/ppp_lwip.cpp @@ -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 +#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 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()); + 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 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 diff --git a/features/FEATURE_LWIP/lwip-interface/ppp_lwip.h b/features/FEATURE_LWIP/lwip-interface/ppp_lwip.h new file mode 100644 index 00000000000..f958bbbd0a1 --- /dev/null +++ b/features/FEATURE_LWIP/lwip-interface/ppp_lwip.h @@ -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_ */ diff --git a/features/filesystem/File.cpp b/features/filesystem/File.cpp index af8ffc85ff4..e061aedd301 100644 --- a/features/filesystem/File.cpp +++ b/features/filesystem/File.cpp @@ -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); diff --git a/features/filesystem/File.h b/features/filesystem/File.h index 700c241634d..d08cf7c0d77 100644 --- a/features/filesystem/File.h +++ b/features/filesystem/File.h @@ -124,7 +124,7 @@ class File : public FileHandle { * * @return Size of the file in bytes */ - virtual size_t size(); + virtual off_t size(); private: FileSystem *_fs; diff --git a/features/filesystem/FileSystem.cpp b/features/filesystem/FileSystem.cpp index 0a3d2bc615a..c225761d637 100644 --- a/features/filesystem/FileSystem.cpp +++ b/features/filesystem/FileSystem.cpp @@ -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; } diff --git a/features/filesystem/FileSystem.h b/features/filesystem/FileSystem.h index ca7597282d1..ae66d16f397 100644 --- a/features/filesystem/FileSystem.h +++ b/features/filesystem/FileSystem.h @@ -175,7 +175,7 @@ class FileSystem : public FileBase { * @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 * diff --git a/features/filesystem/fat/FATFileSystem.cpp b/features/filesystem/fat/FATFileSystem.cpp index e9e369f845e..9706f196b81 100644 --- a/features/filesystem/fat/FATFileSystem.cpp +++ b/features/filesystem/fat/FATFileSystem.cpp @@ -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(file); lock(); - size_t res = fh->fsize; + off_t res = fh->fsize; unlock(); return res; diff --git a/features/filesystem/fat/FATFileSystem.h b/features/filesystem/fat/FATFileSystem.h index 8bf6022e0e7..11b4243a6ac 100644 --- a/features/filesystem/fat/FATFileSystem.h +++ b/features/filesystem/fat/FATFileSystem.h @@ -179,7 +179,7 @@ class FATFileSystem : public FileSystem { * @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 * diff --git a/features/netsocket/CellularBase.h b/features/netsocket/CellularBase.h new file mode 100644 index 00000000000..b1e970c62f3 --- /dev/null +++ b/features/netsocket/CellularBase.h @@ -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 + +/** @}*/ diff --git a/features/netsocket/CellularInterface.h b/features/netsocket/CellularInterface.h index 3e4e468d196..da337992c86 100644 --- a/features/netsocket/CellularInterface.h +++ b/features/netsocket/CellularInterface.h @@ -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 @@ class CellularInterface : public NetworkInterface * * @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 @@ class CellularInterface : public NetworkInterface * @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 @@ class CellularInterface : public NetworkInterface virtual nsapi_error_t disconnect() = 0; }; - + #endif /** @}*/ diff --git a/features/netsocket/cellular/generic_modem_driver/OnboardCellularInterface.cpp b/features/netsocket/cellular/generic_modem_driver/OnboardCellularInterface.cpp new file mode 100644 index 00000000000..6c823a383b0 --- /dev/null +++ b/features/netsocket/cellular/generic_modem_driver/OnboardCellularInterface.cpp @@ -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 diff --git a/features/netsocket/cellular/generic_modem_driver/OnboardCellularInterface.h b/features/netsocket/cellular/generic_modem_driver/OnboardCellularInterface.h new file mode 100644 index 00000000000..1adc085e74a --- /dev/null +++ b/features/netsocket/cellular/generic_modem_driver/OnboardCellularInterface.h @@ -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_ diff --git a/features/netsocket/cellular/generic_modem_driver/PPPCellularInterface.cpp b/features/netsocket/cellular/generic_modem_driver/PPPCellularInterface.cpp new file mode 100644 index 00000000000..c2c1dc9e7f8 --- /dev/null +++ b/features/netsocket/cellular/generic_modem_driver/PPPCellularInterface.cpp @@ -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 +#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: ,, + 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: ,[],[,, + //,,,,, + //] + // By default detailed SMS header CSDH=0 , so we are not expecting [,, + //,,,, + 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(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(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 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", ®_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 diff --git a/features/netsocket/cellular/generic_modem_driver/PPPCellularInterface.h b/features/netsocket/cellular/generic_modem_driver/PPPCellularInterface.h new file mode 100644 index 00000000000..7920cd8c3f8 --- /dev/null +++ b/features/netsocket/cellular/generic_modem_driver/PPPCellularInterface.h @@ -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 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 _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_ diff --git a/features/netsocket/cellular/generic_modem_driver/UARTCellularInterface.cpp b/features/netsocket/cellular/generic_modem_driver/UARTCellularInterface.cpp new file mode 100644 index 00000000000..e1ee6388767 --- /dev/null +++ b/features/netsocket/cellular/generic_modem_driver/UARTCellularInterface.cpp @@ -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 diff --git a/features/netsocket/cellular/generic_modem_driver/UARTCellularInterface.h b/features/netsocket/cellular/generic_modem_driver/UARTCellularInterface.h new file mode 100644 index 00000000000..e86d5862c69 --- /dev/null +++ b/features/netsocket/cellular/generic_modem_driver/UARTCellularInterface.h @@ -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_ diff --git a/features/netsocket/cellular/generic_modem_driver/mbed_lib.json b/features/netsocket/cellular/generic_modem_driver/mbed_lib.json new file mode 100644 index 00000000000..267aee66e29 --- /dev/null +++ b/features/netsocket/cellular/generic_modem_driver/mbed_lib.json @@ -0,0 +1,9 @@ +{ + "name": "ppp-cell-iface", + "config": { + "baud-rate": 115200, + "apn-lookup": false, + "at-parser-buffer-size": 256, + "at-parser-timeout": 8000 + } +} \ No newline at end of file diff --git a/features/netsocket/cellular/onboard_modem_api.h b/features/netsocket/cellular/onboard_modem_api.h new file mode 100644 index 00000000000..0e2a7df52ca --- /dev/null +++ b/features/netsocket/cellular/onboard_modem_api.h @@ -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_ */ diff --git a/features/netsocket/cellular/utils/APN_db.h b/features/netsocket/cellular/utils/APN_db.h new file mode 100644 index 00000000000..040cb2bf422 --- /dev/null +++ b/features/netsocket/cellular/utils/APN_db.h @@ -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; +} diff --git a/features/netsocket/nsapi_ppp.h b/features/netsocket/nsapi_ppp.h new file mode 100644 index 00000000000..e414651f698 --- /dev/null +++ b/features/netsocket/nsapi_ppp.h @@ -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 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_ */ diff --git a/features/netsocket/nsapi_types.h b/features/netsocket/nsapi_types.h index c1823adf52a..9075c997b93 100644 --- a/features/netsocket/nsapi_types.h +++ b/features/netsocket/nsapi_types.h @@ -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; diff --git a/platform/ATCmdParser.cpp b/platform/ATCmdParser.cpp new file mode 100644 index 00000000000..6200f1fa4b6 --- /dev/null +++ b/platform/ATCmdParser.cpp @@ -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 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; +} diff --git a/platform/ATCmdParser.h b/platform/ATCmdParser.h new file mode 100644 index 00000000000..afdb7fb94b1 --- /dev/null +++ b/platform/ATCmdParser.h @@ -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 +#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 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 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 diff --git a/platform/CircularBuffer.h b/platform/CircularBuffer.h index ef3fea7f658..bb7fd38c547 100644 --- a/platform/CircularBuffer.h +++ b/platform/CircularBuffer.h @@ -76,7 +76,7 @@ class CircularBuffer { * * @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 @@ class CircularBuffer { * * @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(); diff --git a/platform/FileHandle.cpp b/platform/FileHandle.cpp new file mode 100644 index 00000000000..026b11d9ce6 --- /dev/null +++ b/platform/FileHandle.cpp @@ -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 diff --git a/platform/FileHandle.h b/platform/FileHandle.h index cf6e3eb7e29..3a3afbc31f5 100644 --- a/platform/FileHandle.h +++ b/platform/FileHandle.h @@ -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 +#include +#include "Callback.h" +#include "platform/mbed_poll.h" #include "platform/platform.h" namespace mbed { @@ -40,6 +42,12 @@ class FileHandle { 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 @@ class FileHandle { * 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 @@ class FileHandle { /** 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 @@ class FileHandle { * * @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 @@ class FileHandle { * * @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 @@ class FileHandle { * -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 @@ class FileHandle { * -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 @@ class FileHandle { * 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 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 diff --git a/platform/LocalFileSystem.cpp b/platform/LocalFileSystem.cpp index 6215fc432b0..8b336fb5bed 100644 --- a/platform/LocalFileSystem.cpp +++ b/platform/LocalFileSystem.cpp @@ -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(); diff --git a/platform/LocalFileSystem.h b/platform/LocalFileSystem.h index 2aeb4f03c50..5a6a6a49786 100644 --- a/platform/LocalFileSystem.h +++ b/platform/LocalFileSystem.h @@ -51,7 +51,7 @@ class LocalFileHandle : public FileHandle { virtual int sync(); - virtual size_t size(); + virtual off_t size(); protected: virtual void lock(); diff --git a/platform/Stream.cpp b/platform/Stream.cpp index 299f9ae8ca0..61054d65bc3 100644 --- a/platform/Stream.cpp +++ b/platform/Stream.cpp @@ -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; } diff --git a/platform/Stream.h b/platform/Stream.h index 9fe100a418f..c715d64bfee 100644 --- a/platform/Stream.h +++ b/platform/Stream.h @@ -19,15 +19,16 @@ #include "platform/platform.h" #include "platform/FileLike.h" #include "platform/FileHandle.h" +#include #include 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 @@ class Stream : public FileLike { 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; diff --git a/platform/mbed_poll.cpp b/platform/mbed_poll.cpp new file mode 100644 index 00000000000..28492010d42 --- /dev/null +++ b/platform/mbed_poll.cpp @@ -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 diff --git a/platform/mbed_poll.h b/platform/mbed_poll.h new file mode 100644 index 00000000000..6df32529e1d --- /dev/null +++ b/platform/mbed_poll.h @@ -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 diff --git a/platform/mbed_retarget.cpp b/platform/mbed_retarget.cpp index 33e02cbd6d3..b2782f42ae1 100644 --- a/platform/mbed_retarget.cpp +++ b/platform/mbed_retarget.cpp @@ -30,6 +30,7 @@ #endif #include #include +#include #if DEVICE_STDIO_MESSAGES #include #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 @@ class ManagedDir : public Dir { * @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; + if (fhc == NULL) { + errno = EBADF; + return 0; + } - int err = fhc->isatty(); - if (err < 0) { - errno = -err; - return -1; - } else { + 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); diff --git a/platform/mbed_retarget.h b/platform/mbed_retarget.h index 87510590898..af0a12f6ee6 100644 --- a/platform/mbed_retarget.h +++ b/platform/mbed_retarget.h @@ -19,6 +19,9 @@ #ifndef RETARGET_H #define RETARGET_H +#if __cplusplus +#include +#endif //__cplusplus #include #include @@ -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 */ diff --git a/platform/mbed_toolchain.h b/platform/mbed_toolchain.h index 9b2238fe97d..a2dae816459 100644 --- a/platform/mbed_toolchain.h +++ b/platform/mbed_toolchain.h @@ -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 diff --git a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/C027_api.c b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/C027_api.c deleted file mode 100644 index 3f51dc0b8ae..00000000000 --- a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/C027_api.c +++ /dev/null @@ -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 - } -} diff --git a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/C027_api.h b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/C027_api.h deleted file mode 100644 index 46897945cb6..00000000000 --- a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/C027_api.h +++ /dev/null @@ -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 diff --git a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/PinNames.h b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/PinNames.h index e1c99e8a085..304d8343e27 100644 --- a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/PinNames.h +++ b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/PinNames.h @@ -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, diff --git a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/mbed_overrides.c b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/mbed_overrides.c index 9d9eac1c199..066786d981a 100644 --- a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/mbed_overrides.c +++ b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/mbed_overrides.c @@ -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(); } diff --git a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/onboard_modem_api.c b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/onboard_modem_api.c new file mode 100644 index 00000000000..7849f787374 --- /dev/null +++ b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/onboard_modem_api.c @@ -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 diff --git a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/ublox_low_level_api.c b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/ublox_low_level_api.c new file mode 100644 index 00000000000..68d31285351 --- /dev/null +++ b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/ublox_low_level_api.c @@ -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 +#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 + } +} diff --git a/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/ublox_low_level_api.h b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/ublox_low_level_api.h new file mode 100644 index 00000000000..6a39a5a3ee1 --- /dev/null +++ b/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/ublox_low_level_api.h @@ -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 diff --git a/targets/TARGET_NXP/TARGET_LPC176X/objects.h b/targets/TARGET_NXP/TARGET_LPC176X/objects.h index ecbd354934b..069a49bf88c 100644 --- a/targets/TARGET_NXP/TARGET_LPC176X/objects.h +++ b/targets/TARGET_NXP/TARGET_LPC176X/objects.h @@ -71,6 +71,10 @@ struct spi_s { LPC_SSP_TypeDef *spi; }; +struct modem_s { + uint32_t state; +}; + #ifdef __cplusplus } #endif diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/PinNames.h b/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/PinNames.h index 21c82161b1a..ca73457e6ef 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/PinNames.h +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/PinNames.h @@ -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 diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/device/TOOLCHAIN_IAR/stm32f411xe.icf b/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/device/TOOLCHAIN_IAR/stm32f411xe.icf index 8200cc69c46..87e12d26bb3 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/device/TOOLCHAIN_IAR/stm32f411xe.icf +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/device/TOOLCHAIN_IAR/stm32f411xe.icf @@ -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 }; diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/onboard_modem_api.c b/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/onboard_modem_api.c new file mode 100644 index 00000000000..ea532df442b --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_MTS_DRAGONFLY_F411RE/onboard_modem_api.c @@ -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 diff --git a/targets/targets.json b/targets/targets.json index 5b5fd0c7115..2f6d4e7c2f1 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -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",