mirror of https://github.com/ARMmbed/mbed-os.git
commit
8a870a66c0
|
@ -15,8 +15,10 @@
|
|||
*/
|
||||
#include "drivers/RawSerial.h"
|
||||
#include "platform/mbed_wait_api.h"
|
||||
#include <stdio.h>
|
||||
#include <cstdarg>
|
||||
|
||||
|
||||
#if DEVICE_SERIAL
|
||||
|
||||
#define STRING_STACK_LIMIT 120
|
||||
|
|
|
@ -81,6 +81,23 @@ public:
|
|||
*/
|
||||
Serial(PinName tx, PinName rx, int baud);
|
||||
|
||||
/* Stream gives us a FileHandle with non-functional poll()/readable()/writable. Pass through
|
||||
* the calls from the SerialBase instead for backwards compatibility. This problem is
|
||||
* part of why Stream and Serial should be deprecated.
|
||||
*/
|
||||
bool readable()
|
||||
{
|
||||
return SerialBase::readable();
|
||||
}
|
||||
bool writable()
|
||||
{
|
||||
return SerialBase::writeable();
|
||||
}
|
||||
bool writeable()
|
||||
{
|
||||
return SerialBase::writeable();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int _getc();
|
||||
virtual int _putc(int c);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if DEVICE_SERIAL
|
||||
|
||||
#include <errno.h>
|
||||
#include "UARTSerial.h"
|
||||
#include "platform/mbed_poll.h"
|
||||
#include "platform/mbed_wait_api.h"
|
||||
|
||||
namespace mbed {
|
||||
|
||||
UARTSerial::UARTSerial(PinName tx, PinName rx, int baud) :
|
||||
SerialBase(tx, rx, baud),
|
||||
_blocking(true),
|
||||
_tx_irq_enabled(false),
|
||||
_dcd_irq(NULL)
|
||||
{
|
||||
/* Attatch IRQ routines to the serial device. */
|
||||
SerialBase::attach(callback(this, &UARTSerial::rx_irq), RxIrq);
|
||||
}
|
||||
|
||||
UARTSerial::~UARTSerial()
|
||||
{
|
||||
delete _dcd_irq;
|
||||
}
|
||||
|
||||
void UARTSerial::dcd_irq()
|
||||
{
|
||||
wake();
|
||||
}
|
||||
|
||||
void UARTSerial::set_data_carrier_detect(PinName dcd_pin, bool active_high)
|
||||
{
|
||||
delete _dcd_irq;
|
||||
_dcd_irq = NULL;
|
||||
|
||||
if (dcd_pin != NC) {
|
||||
_dcd_irq = new InterruptIn(dcd_pin);
|
||||
if (active_high) {
|
||||
_dcd_irq->fall(callback(this, &UARTSerial::dcd_irq));
|
||||
} else {
|
||||
_dcd_irq->rise(callback(this, &UARTSerial::dcd_irq));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int UARTSerial::close()
|
||||
{
|
||||
/* Does not let us pass a file descriptor. So how to close ?
|
||||
* Also, does it make sense to close a device type file descriptor*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UARTSerial::isatty()
|
||||
{
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
off_t UARTSerial::seek(off_t offset, int whence)
|
||||
{
|
||||
/*XXX lseek can be done theoratically, but is it sane to mark positions on a dynamically growing/shrinking
|
||||
* buffer system (from an interrupt context) */
|
||||
return -ESPIPE;
|
||||
}
|
||||
|
||||
int UARTSerial::sync()
|
||||
{
|
||||
lock();
|
||||
|
||||
while (!_txbuf.empty()) {
|
||||
unlock();
|
||||
// Doing better than wait would require TxIRQ to also do wake() when becoming empty. Worth it?
|
||||
wait_ms(1);
|
||||
lock();
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UARTSerial::sigio(Callback<void()> func) {
|
||||
core_util_critical_section_enter();
|
||||
_sigio_cb = func;
|
||||
if (_sigio_cb) {
|
||||
short current_events = poll(0x7FFF);
|
||||
if (current_events) {
|
||||
_sigio_cb();
|
||||
}
|
||||
}
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
||||
ssize_t UARTSerial::write(const void* buffer, size_t length)
|
||||
{
|
||||
size_t data_written = 0;
|
||||
const char *buf_ptr = static_cast<const char *>(buffer);
|
||||
|
||||
lock();
|
||||
|
||||
while (_txbuf.full()) {
|
||||
if (!_blocking) {
|
||||
unlock();
|
||||
return -EAGAIN;
|
||||
}
|
||||
unlock();
|
||||
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
|
||||
lock();
|
||||
}
|
||||
|
||||
while (data_written < length && !_txbuf.full()) {
|
||||
_txbuf.push(*buf_ptr++);
|
||||
data_written++;
|
||||
}
|
||||
|
||||
core_util_critical_section_enter();
|
||||
if (!_tx_irq_enabled) {
|
||||
UARTSerial::tx_irq(); // only write to hardware in one place
|
||||
if (!_txbuf.empty()) {
|
||||
SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq);
|
||||
_tx_irq_enabled = true;
|
||||
}
|
||||
}
|
||||
core_util_critical_section_exit();
|
||||
|
||||
unlock();
|
||||
|
||||
return data_written;
|
||||
}
|
||||
|
||||
ssize_t UARTSerial::read(void* buffer, size_t length)
|
||||
{
|
||||
size_t data_read = 0;
|
||||
|
||||
char *ptr = static_cast<char *>(buffer);
|
||||
|
||||
lock();
|
||||
|
||||
while (_rxbuf.empty()) {
|
||||
if (!_blocking) {
|
||||
unlock();
|
||||
return -EAGAIN;
|
||||
}
|
||||
unlock();
|
||||
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
|
||||
lock();
|
||||
}
|
||||
|
||||
while (data_read < length && !_rxbuf.empty()) {
|
||||
_rxbuf.pop(*ptr++);
|
||||
data_read++;
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
return data_read;
|
||||
}
|
||||
|
||||
bool UARTSerial::hup() const
|
||||
{
|
||||
return _dcd_irq && _dcd_irq->read() != 0;
|
||||
}
|
||||
|
||||
void UARTSerial::wake()
|
||||
{
|
||||
if (_sigio_cb) {
|
||||
_sigio_cb();
|
||||
}
|
||||
}
|
||||
|
||||
short UARTSerial::poll(short events) const {
|
||||
|
||||
short revents = 0;
|
||||
/* Check the Circular Buffer if space available for writing out */
|
||||
|
||||
|
||||
if (!_rxbuf.empty()) {
|
||||
revents |= POLLIN;
|
||||
}
|
||||
|
||||
/* POLLHUP and POLLOUT are mutually exclusive */
|
||||
if (hup()) {
|
||||
revents |= POLLHUP;
|
||||
} else if (!_txbuf.full()) {
|
||||
revents |= POLLOUT;
|
||||
}
|
||||
|
||||
/*TODO Handle other event types */
|
||||
|
||||
return revents;
|
||||
}
|
||||
|
||||
void UARTSerial::lock(void)
|
||||
{
|
||||
_mutex.lock();
|
||||
}
|
||||
|
||||
void UARTSerial::unlock(void)
|
||||
{
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
void UARTSerial::rx_irq(void)
|
||||
{
|
||||
bool was_empty = _rxbuf.empty();
|
||||
|
||||
/* Fill in the receive buffer if the peripheral is readable
|
||||
* and receive buffer is not full. */
|
||||
while (SerialBase::readable()) {
|
||||
char data = SerialBase::_base_getc();
|
||||
if (!_rxbuf.full()) {
|
||||
_rxbuf.push(data);
|
||||
} else {
|
||||
/* Drop - can we report in some way? */
|
||||
}
|
||||
}
|
||||
|
||||
/* Report the File handler that data is ready to be read from the buffer. */
|
||||
if (was_empty && !_rxbuf.empty()) {
|
||||
wake();
|
||||
}
|
||||
}
|
||||
|
||||
// Also called from write to start transfer
|
||||
void UARTSerial::tx_irq(void)
|
||||
{
|
||||
bool was_full = _txbuf.full();
|
||||
|
||||
/* Write to the peripheral if there is something to write
|
||||
* and if the peripheral is available to write. */
|
||||
while (!_txbuf.empty() && SerialBase::writeable()) {
|
||||
char data;
|
||||
_txbuf.pop(data);
|
||||
SerialBase::_base_putc(data);
|
||||
}
|
||||
|
||||
if (_tx_irq_enabled && _txbuf.empty()) {
|
||||
SerialBase::attach(NULL, TxIrq);
|
||||
_tx_irq_enabled = false;
|
||||
}
|
||||
|
||||
/* Report the File handler that data can be written to peripheral. */
|
||||
if (was_full && !_txbuf.full() && !hup()) {
|
||||
wake();
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace mbed
|
||||
|
||||
#endif //DEVICE_SERIAL
|
|
@ -0,0 +1,198 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef MBED_UARTSERIAL_H
|
||||
#define MBED_UARTSERIAL_H
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#if DEVICE_SERIAL
|
||||
|
||||
#include "FileHandle.h"
|
||||
#include "SerialBase.h"
|
||||
#include "InterruptIn.h"
|
||||
#include "PlatformMutex.h"
|
||||
#include "serial_api.h"
|
||||
#include "CircularBuffer.h"
|
||||
|
||||
#ifndef MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE
|
||||
#define MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE 256
|
||||
#endif
|
||||
|
||||
#ifndef MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE
|
||||
#define MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE 256
|
||||
#endif
|
||||
|
||||
namespace mbed {
|
||||
|
||||
class UARTSerial : private SerialBase, public FileHandle {
|
||||
|
||||
public:
|
||||
|
||||
/** Create a UARTSerial port, connected to the specified transmit and receive pins, with a particular baud rate.
|
||||
* @param tx Transmit pin
|
||||
* @param rx Receive pin
|
||||
* @param baud The baud rate of the serial port (optional, defaults to MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE)
|
||||
*/
|
||||
UARTSerial(PinName tx, PinName rx, int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE);
|
||||
virtual ~UARTSerial();
|
||||
|
||||
/** Equivalent to POSIX poll(). Derived from FileHandle.
|
||||
* Provides a mechanism to multiplex input/output over a set of file handles.
|
||||
*/
|
||||
virtual short poll(short events) const;
|
||||
|
||||
/** Write the contents of a buffer to a file
|
||||
*
|
||||
* @param buffer The buffer to write from
|
||||
* @param size The number of bytes to write
|
||||
* @return The number of bytes written, negative error on failure
|
||||
*/
|
||||
virtual ssize_t write(const void* buffer, size_t length);
|
||||
|
||||
/** Read the contents of a file into a buffer
|
||||
*
|
||||
* Follows POSIX semantics:
|
||||
*
|
||||
* * if no data is available, and non-blocking set return -EAGAIN
|
||||
* * if no data is available, and blocking set, wait until data is available
|
||||
* * If any data is available, call returns immediately
|
||||
*
|
||||
* @param buffer The buffer to read in to
|
||||
* @param size The number of bytes to read
|
||||
* @return The number of bytes read, 0 at end of file, negative error on failure
|
||||
*/
|
||||
virtual ssize_t read(void* buffer, size_t length);
|
||||
|
||||
/** Acquire mutex */
|
||||
virtual void lock(void);
|
||||
|
||||
/** Release mutex */
|
||||
virtual void unlock(void);
|
||||
|
||||
/** Close a file
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int close();
|
||||
|
||||
/** Check if the file in an interactive terminal device
|
||||
*
|
||||
* @return True if the file is a terminal
|
||||
* @return False if the file is not a terminal
|
||||
* @return Negative error code on failure
|
||||
*/
|
||||
virtual int isatty();
|
||||
|
||||
/** Move the file position to a given offset from from a given location
|
||||
*
|
||||
* Not valid for a device type FileHandle like UARTSerial.
|
||||
* In case of UARTSerial, returns ESPIPE
|
||||
*
|
||||
* @param offset The offset from whence to move to
|
||||
* @param whence The start of where to seek
|
||||
* SEEK_SET to start from beginning of file,
|
||||
* SEEK_CUR to start from current position in file,
|
||||
* SEEK_END to start from end of file
|
||||
* @return The new offset of the file, negative error code on failure
|
||||
*/
|
||||
virtual off_t seek(off_t offset, int whence);
|
||||
|
||||
/** Flush any buffers associated with the file
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int sync();
|
||||
|
||||
/** Set blocking or non-blocking mode
|
||||
* The default is blocking.
|
||||
*
|
||||
* @param blocking true for blocking mode, false for non-blocking mode.
|
||||
*/
|
||||
virtual int set_blocking(bool blocking)
|
||||
{
|
||||
_blocking = blocking;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Register a callback on state change of the file.
|
||||
*
|
||||
* The specified callback will be called on state changes such as when
|
||||
* the file can be written to or read from.
|
||||
*
|
||||
* The callback may be called in an interrupt context and should not
|
||||
* perform expensive operations.
|
||||
*
|
||||
* Note! This is not intended as an attach-like asynchronous api, but rather
|
||||
* as a building block for constructing such functionality.
|
||||
*
|
||||
* The exact timing of when the registered function
|
||||
* is called is not guaranteed and susceptible to change. It should be used
|
||||
* as a cue to make read/write/poll calls to find the current state.
|
||||
*
|
||||
* @param func Function to call on state change
|
||||
*/
|
||||
virtual void sigio(Callback<void()> func);
|
||||
|
||||
/** Setup interrupt handler for DCD line
|
||||
*
|
||||
* If DCD line is connected, an IRQ handler will be setup.
|
||||
* Does nothing if DCD is NC, i.e., not connected.
|
||||
*
|
||||
* @param dcd_pin Pin-name for DCD
|
||||
* @param active_high a boolean set to true if DCD polarity is active low
|
||||
*/
|
||||
void set_data_carrier_detect(PinName dcd_pin, bool active_high = false);
|
||||
|
||||
private:
|
||||
|
||||
/** Software serial buffers
|
||||
* By default buffer size is 256 for TX and 256 for RX. Configurable through mbed_app.json
|
||||
*/
|
||||
CircularBuffer<char, MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE> _rxbuf;
|
||||
CircularBuffer<char, MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE> _txbuf;
|
||||
|
||||
PlatformMutex _mutex;
|
||||
|
||||
Callback<void()> _sigio_cb;
|
||||
|
||||
bool _blocking;
|
||||
bool _tx_irq_enabled;
|
||||
InterruptIn *_dcd_irq;
|
||||
|
||||
/** Device Hanged up
|
||||
* Determines if the device hanged up on us.
|
||||
*
|
||||
* @return True, if hanged up
|
||||
*/
|
||||
bool hup() const;
|
||||
|
||||
/** ISRs for serial
|
||||
* Routines to handle interrupts on serial pins.
|
||||
* Copies data into Circular Buffer.
|
||||
* Reports the state change to File handle.
|
||||
*/
|
||||
void tx_irq(void);
|
||||
void rx_irq(void);
|
||||
|
||||
void wake(void);
|
||||
|
||||
void dcd_irq(void);
|
||||
};
|
||||
} //namespace mbed
|
||||
|
||||
#endif //DEVICE_SERIAL
|
||||
#endif //MBED_UARTSERIAL_H
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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/*
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
#include "mbed_interface.h"
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_ARP || LWIP_ETHERNET
|
||||
|
||||
#ifndef LPC_EMAC_RMII
|
||||
#error LPC_EMAC_RMII is not defined!
|
||||
#endif
|
||||
|
@ -1058,4 +1060,6 @@ void eth_arch_disable_interrupts(void) {
|
|||
* @}
|
||||
*/
|
||||
|
||||
#endif /* LWIP_ARP || LWIP_ETHERNET */
|
||||
|
||||
/* --------------------------------- End Of File ------------------------------ */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h> /* for size_t */
|
||||
|
||||
#if LWIP_USE_EXTERNAL_MBEDTLS
|
||||
#include "mbedtls/md5.h"
|
||||
#endif
|
||||
|
||||
|
||||
/* ARM/LPC17xx is little endian only */
|
||||
#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
|
||||
#ifdef BYTE_ORDER
|
||||
|
@ -88,16 +93,28 @@
|
|||
|
||||
#ifdef LWIP_DEBUG
|
||||
|
||||
#include "stdio.h"
|
||||
#if MBED_CONF_LWIP_USE_MBED_TRACE
|
||||
void lwip_mbed_tracef_debug(const char *fmt, ...);
|
||||
void lwip_mbed_tracef_error(const char *fmt, ...);
|
||||
void lwip_mbed_tracef_warn(const char *fmt, ...);
|
||||
void lwip_mbed_assert_fail(const char *msg, const char *func, const char *file, unsigned int line);
|
||||
|
||||
#define LWIP_PLATFORM_DIAG(vars) lwip_mbed_tracef_debug vars
|
||||
#define LWIP_PLATFORM_DIAG_SEVERE(vars) lwip_mbed_tracef_error vars
|
||||
#define LWIP_PLATFORM_DIAG_SERIOUS(vars) lwip_mbed_tracef_error vars
|
||||
#define LWIP_PLATFORM_DIAG_WARNING(vars) lwip_mbed_tracef_warn vars
|
||||
|
||||
#define LWIP_PLATFORM_ASSERT(message) lwip_mbed_assert_fail(message, __func__, __FILE__, __LINE__)
|
||||
|
||||
#else // MBED_CONF_LWIP_USE_MBED_TRACE
|
||||
#include <stdio.h>
|
||||
|
||||
void assert_printf(char *msg, int line, char *file);
|
||||
|
||||
/* Plaform specific diagnostic output */
|
||||
#define LWIP_PLATFORM_DIAG(vars) printf vars
|
||||
#define LWIP_PLATFORM_ASSERT(flag) { assert_printf((flag), __LINE__, __FILE__); }
|
||||
#else
|
||||
#define LWIP_PLATFORM_DIAG(msg) { ; }
|
||||
#define LWIP_PLATFORM_ASSERT(flag) { ; }
|
||||
#endif // MBED_CONF_LWIP_USE_MBED_TRACE
|
||||
#endif
|
||||
|
||||
#include "cmsis.h"
|
||||
|
|
|
@ -489,6 +489,42 @@ sys_thread_t sys_thread_new(const char *pcName,
|
|||
|
||||
#ifdef LWIP_DEBUG
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#if MBED_CONF_LWIP_USE_MBED_TRACE
|
||||
#include "mbed-trace/mbed_trace.h"
|
||||
|
||||
void lwip_mbed_tracef_debug(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
mbed_vtracef(TRACE_LEVEL_DEBUG, "lwIP", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void lwip_mbed_tracef_warn(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
mbed_vtracef(TRACE_LEVEL_WARN, "lwIP", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void lwip_mbed_tracef_error(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
mbed_vtracef(TRACE_LEVEL_ERROR, "lwIP", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void lwip_mbed_assert_fail(const char *msg, const char *func, const char *file, unsigned int line)
|
||||
{
|
||||
mbed_tracef(TRACE_LEVEL_ERROR, "lwIP", "Assertion failed: %s, function %s, file %s, line %u.", msg, func, file, line);
|
||||
exit(EXIT_FAILURE); // XXX how about abort? mbed_assert uses exit, so follow suit
|
||||
}
|
||||
#else // MBED_CONF_LWIP_USE_MBED_TRACE
|
||||
|
||||
/** \brief Displays an error message on assertion
|
||||
|
||||
This function will display an error message on an assertion
|
||||
|
@ -504,5 +540,6 @@ void assert_printf(char *msg, int line, char *file) {
|
|||
else
|
||||
error("LWIP ASSERT\n");
|
||||
}
|
||||
#endif // MBED_CONF_LWIP_USE_MBED_TRACE
|
||||
|
||||
#endif /* LWIP_DEBUG */
|
||||
|
|
|
@ -76,19 +76,21 @@
|
|||
#include <string.h>
|
||||
|
||||
/* pull in md5 of ppp? */
|
||||
#if !PPP_SUPPORT
|
||||
#undef PPP_SUPPORT
|
||||
#define PPP_SUPPORT 1
|
||||
#define PPP_FAKED_ON 1
|
||||
#endif
|
||||
|
||||
#include "netif/ppp/ppp_opts.h"
|
||||
#include "netif/ppp/ppp.h"
|
||||
#include "netif/ppp/pppcrypt.h"
|
||||
#if !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS
|
||||
|
||||
#if PPP_FAKED_ON && !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS
|
||||
#undef LWIP_INCLUDED_POLARSSL_MD5
|
||||
#define LWIP_INCLUDED_POLARSSL_MD5 1
|
||||
#include "netif/ppp/polarssl/lwip_md5.c"
|
||||
#endif
|
||||
#if LWIP_USE_EXTERNAL_MBEDTLS
|
||||
#include "mbedtls/inc/mbedtls/md5.h"
|
||||
#define md5_context mbedtls_md5_context
|
||||
#endif
|
||||
|
||||
static u8_t input[64];
|
||||
static u32_t base_time;
|
||||
|
@ -124,7 +126,7 @@ u32_t
|
|||
lwip_hook_tcp_isn(const ip_addr_t *local_ip, u16_t local_port,
|
||||
const ip_addr_t *remote_ip, u16_t remote_port)
|
||||
{
|
||||
md5_context ctx;
|
||||
lwip_md5_context ctx;
|
||||
u8_t output[16];
|
||||
u32_t isn;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "nsapi.h"
|
||||
#include "mbed_interface.h"
|
||||
#include "mbed_assert.h"
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
@ -32,10 +33,13 @@
|
|||
#include "lwip/mld6.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/udp.h"
|
||||
|
||||
#include "netif/lwip_ethernet.h"
|
||||
#include "emac_api.h"
|
||||
#include "ppp_lwip.h"
|
||||
#include "lwip_tcp_isn.h"
|
||||
|
||||
static nsapi_error_t mbed_lwip_err_remap(err_t err);
|
||||
|
||||
#if DEVICE_EMAC
|
||||
#define MBED_NETIF_INIT_FN emac_lwip_if_init
|
||||
#else
|
||||
|
@ -56,12 +60,10 @@ static struct lwip_socket {
|
|||
void *data;
|
||||
} lwip_arena[MEMP_NUM_NETCONN];
|
||||
|
||||
static bool lwip_inited = false;
|
||||
static bool lwip_connected = false;
|
||||
|
||||
static void mbed_lwip_arena_init(void)
|
||||
{
|
||||
memset(lwip_arena, 0, sizeof lwip_arena);
|
||||
}
|
||||
static bool netif_inited = false;
|
||||
static bool netif_is_ppp = false;
|
||||
|
||||
static struct lwip_socket *mbed_lwip_arena_alloc(void)
|
||||
{
|
||||
|
@ -109,7 +111,7 @@ static void mbed_lwip_socket_callback(struct netconn *nc, enum netconn_evt eh, u
|
|||
|
||||
/* TCP/IP and Network Interface Initialisation */
|
||||
static struct netif lwip_netif;
|
||||
#if LWIP_IPV4
|
||||
#if LWIP_DHCP
|
||||
static bool lwip_dhcp = false;
|
||||
#endif
|
||||
static char lwip_mac_address[NSAPI_MAC_SIZE];
|
||||
|
@ -297,10 +299,13 @@ static void mbed_lwip_tcpip_init_irq(void *eh)
|
|||
}
|
||||
|
||||
static sys_sem_t lwip_netif_linked;
|
||||
static sys_sem_t lwip_netif_unlinked;
|
||||
static void mbed_lwip_netif_link_irq(struct netif *lwip_netif)
|
||||
{
|
||||
if (netif_is_link_up(lwip_netif)) {
|
||||
sys_sem_signal(&lwip_netif_linked);
|
||||
} else {
|
||||
sys_sem_signal(&lwip_netif_unlinked);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,32 +331,43 @@ static void mbed_lwip_netif_status_irq(struct netif *lwip_netif)
|
|||
}
|
||||
}
|
||||
|
||||
static void mbed_lwip_set_mac_address(void)
|
||||
#if LWIP_ETHERNET
|
||||
static void mbed_lwip_set_mac_address(struct netif *netif)
|
||||
{
|
||||
#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE)
|
||||
(void) snprintf(lwip_mac_address, NSAPI_MAC_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
MBED_MAC_ADDR_0, MBED_MAC_ADDR_1, MBED_MAC_ADDR_2,
|
||||
MBED_MAC_ADDR_3, MBED_MAC_ADDR_4, MBED_MAC_ADDR_5);
|
||||
netif->hwaddr[0] = MBED_MAC_ADDR_0;
|
||||
netif->hwaddr[1] = MBED_MAC_ADDR_1;
|
||||
netif->hwaddr[2] = MBED_MAC_ADDR_2;
|
||||
netif->hwaddr[3] = MBED_MAC_ADDR_3;
|
||||
netif->hwaddr[4] = MBED_MAC_ADDR_4;
|
||||
netif->hwaddr[5] = MBED_MAC_ADDR_5;
|
||||
#else
|
||||
char mac[6];
|
||||
mbed_mac_address(mac);
|
||||
(void) snprintf(lwip_mac_address, NSAPI_MAC_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
mbed_mac_address((char *)netif->hwaddr);
|
||||
#endif
|
||||
|
||||
netif->hwaddr_len = ETH_HWADDR_LEN;
|
||||
|
||||
/* Use mac address as additional seed to random number generator */
|
||||
uint64_t seed = mac[0];
|
||||
uint64_t seed = netif->hwaddr[0];
|
||||
for (uint8_t i = 1; i < 8; i++) {
|
||||
seed <<= 8;
|
||||
seed |= mac[i % 6];
|
||||
seed |= netif->hwaddr[i % 6];
|
||||
}
|
||||
lwip_add_random_seed(seed);
|
||||
}
|
||||
|
||||
static void mbed_lwip_record_mac_address(const struct netif *netif)
|
||||
{
|
||||
const u8_t *mac = netif->hwaddr;
|
||||
snprintf(lwip_mac_address, NSAPI_MAC_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
#endif // LWIP_ETHERNET
|
||||
|
||||
/* LWIP interface implementation */
|
||||
const char *mbed_lwip_get_mac_address(void)
|
||||
{
|
||||
return lwip_mac_address[0] ? lwip_mac_address : 0;
|
||||
return lwip_mac_address[0] ? lwip_mac_address : NULL;
|
||||
}
|
||||
|
||||
char *mbed_lwip_get_ip_address(char *buf, nsapi_size_t buflen)
|
||||
|
@ -403,15 +419,14 @@ char *mbed_lwip_get_gateway(char *buf, nsapi_size_t buflen)
|
|||
#endif
|
||||
}
|
||||
|
||||
nsapi_error_t mbed_lwip_init(emac_interface_t *emac)
|
||||
static void mbed_lwip_core_init(void)
|
||||
{
|
||||
|
||||
// Check if we've already brought up lwip
|
||||
if (!mbed_lwip_get_mac_address()) {
|
||||
// Seed lwip random
|
||||
if (!lwip_inited) {
|
||||
// Seed lwip random
|
||||
lwip_seed_random();
|
||||
|
||||
// Set up network
|
||||
// Initialise TCP sequence number
|
||||
uint32_t tcp_isn_secret[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
@ -421,50 +436,94 @@ nsapi_error_t mbed_lwip_init(emac_interface_t *emac)
|
|||
|
||||
sys_sem_new(&lwip_tcpip_inited, 0);
|
||||
sys_sem_new(&lwip_netif_linked, 0);
|
||||
sys_sem_new(&lwip_netif_unlinked, 0);
|
||||
sys_sem_new(&lwip_netif_has_addr, 0);
|
||||
|
||||
tcpip_init(mbed_lwip_tcpip_init_irq, NULL);
|
||||
sys_arch_sem_wait(&lwip_tcpip_inited, 0);
|
||||
|
||||
memset(&lwip_netif, 0, sizeof lwip_netif);
|
||||
if (!netif_add(&lwip_netif,
|
||||
#if LWIP_IPV4
|
||||
0, 0, 0,
|
||||
#endif
|
||||
emac, MBED_NETIF_INIT_FN, tcpip_input)) {
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
mbed_lwip_set_mac_address();
|
||||
netif_set_default(&lwip_netif);
|
||||
|
||||
netif_set_link_callback(&lwip_netif, mbed_lwip_netif_link_irq);
|
||||
netif_set_status_callback(&lwip_netif, mbed_lwip_netif_status_irq);
|
||||
|
||||
#if !DEVICE_EMAC
|
||||
eth_arch_enable_interrupts();
|
||||
#endif
|
||||
lwip_inited = true;
|
||||
}
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask, const char *gw)
|
||||
nsapi_error_t mbed_lwip_emac_init(emac_interface_t *emac)
|
||||
{
|
||||
#if LWIP_ETHERNET
|
||||
// Choose a MAC address - driver can override
|
||||
mbed_lwip_set_mac_address(&lwip_netif);
|
||||
|
||||
// Set up network
|
||||
if (!netif_add(&lwip_netif,
|
||||
#if LWIP_IPV4
|
||||
0, 0, 0,
|
||||
#endif
|
||||
emac, MBED_NETIF_INIT_FN, tcpip_input)) {
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Note the MAC address actually in use
|
||||
mbed_lwip_record_mac_address(&lwip_netif);
|
||||
|
||||
#if !DEVICE_EMAC
|
||||
eth_arch_enable_interrupts();
|
||||
#endif
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
#else
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
#endif //LWIP_ETHERNET
|
||||
}
|
||||
|
||||
// Backwards compatibility with people using DEVICE_EMAC
|
||||
nsapi_error_t mbed_lwip_init(emac_interface_t *emac)
|
||||
{
|
||||
mbed_lwip_core_init();
|
||||
return mbed_lwip_emac_init(emac);
|
||||
}
|
||||
|
||||
nsapi_error_t mbed_lwip_bringup(bool dhcp, bool ppp, const char *ip, const char *netmask, const char *gw)
|
||||
{
|
||||
// Check if we've already connected
|
||||
if (lwip_connected) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
if(mbed_lwip_init(NULL) != NSAPI_ERROR_OK) {
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
mbed_lwip_core_init();
|
||||
|
||||
nsapi_error_t ret;
|
||||
if (netif_inited) {
|
||||
/* Can't cope with changing mode */
|
||||
if (netif_is_ppp == ppp) {
|
||||
ret = NSAPI_ERROR_OK;
|
||||
} else {
|
||||
ret = NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
if (ppp) {
|
||||
ret = ppp_lwip_if_init(&lwip_netif);
|
||||
} else {
|
||||
ret = mbed_lwip_emac_init(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Zero out socket set
|
||||
mbed_lwip_arena_init();
|
||||
if (ret != NSAPI_ERROR_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
netif_inited = true;
|
||||
if (ppp) {
|
||||
netif_is_ppp = ppp;
|
||||
}
|
||||
|
||||
netif_set_default(&lwip_netif);
|
||||
netif_set_link_callback(&lwip_netif, mbed_lwip_netif_link_irq);
|
||||
netif_set_status_callback(&lwip_netif, mbed_lwip_netif_status_irq);
|
||||
|
||||
#if LWIP_IPV6
|
||||
netif_create_ip6_linklocal_address(&lwip_netif, 1/*from MAC*/);
|
||||
if (lwip_netif.hwaddr_len == ETH_HWADDR_LEN) {
|
||||
netif_create_ip6_linklocal_address(&lwip_netif, 1/*from MAC*/);
|
||||
}
|
||||
|
||||
#if LWIP_IPV6_MLD
|
||||
/*
|
||||
* For hardware/netifs that implement MAC filtering.
|
||||
|
@ -480,23 +539,12 @@ nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask,
|
|||
|
||||
#if LWIP_IPV6_AUTOCONFIG
|
||||
/* IPv6 address autoconfiguration not enabled by default */
|
||||
lwip_netif.ip6_autoconfig_enabled = 1;
|
||||
lwip_netif.ip6_autoconfig_enabled = 1;
|
||||
#endif /* LWIP_IPV6_AUTOCONFIG */
|
||||
|
||||
#endif
|
||||
|
||||
u32_t ret;
|
||||
|
||||
if (!netif_is_link_up(&lwip_netif)) {
|
||||
ret = sys_arch_sem_wait(&lwip_netif_linked, 15000);
|
||||
|
||||
if (ret == SYS_ARCH_TIMEOUT) {
|
||||
return NSAPI_ERROR_NO_CONNECTION;
|
||||
}
|
||||
}
|
||||
#endif // LWIP_IPV6
|
||||
|
||||
#if LWIP_IPV4
|
||||
if (!dhcp) {
|
||||
if (!dhcp && !ppp) {
|
||||
ip4_addr_t ip_addr;
|
||||
ip4_addr_t netmask_addr;
|
||||
ip4_addr_t gw_addr;
|
||||
|
@ -511,9 +559,27 @@ nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask,
|
|||
}
|
||||
#endif
|
||||
|
||||
netif_set_up(&lwip_netif);
|
||||
if (ppp) {
|
||||
err_t err = ppp_lwip_connect();
|
||||
if (err) {
|
||||
return mbed_lwip_err_remap(err);
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
if (!netif_is_link_up(&lwip_netif)) {
|
||||
if (sys_arch_sem_wait(&lwip_netif_linked, 15000) == SYS_ARCH_TIMEOUT) {
|
||||
if (ppp) {
|
||||
ppp_lwip_disconnect();
|
||||
}
|
||||
return NSAPI_ERROR_NO_CONNECTION;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ppp) {
|
||||
netif_set_up(&lwip_netif);
|
||||
}
|
||||
|
||||
#if LWIP_DHCP
|
||||
// Connect to the network
|
||||
lwip_dhcp = dhcp;
|
||||
|
||||
|
@ -527,8 +593,10 @@ nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask,
|
|||
|
||||
// If doesn't have address
|
||||
if (!mbed_lwip_get_ip_addr(true, &lwip_netif)) {
|
||||
ret = sys_arch_sem_wait(&lwip_netif_has_addr, 15000);
|
||||
if (ret == SYS_ARCH_TIMEOUT) {
|
||||
if (sys_arch_sem_wait(&lwip_netif_has_addr, 15000) == SYS_ARCH_TIMEOUT) {
|
||||
if (ppp) {
|
||||
ppp_lwip_disconnect();
|
||||
}
|
||||
return NSAPI_ERROR_DHCP_FAILURE;
|
||||
}
|
||||
}
|
||||
|
@ -537,7 +605,7 @@ nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask,
|
|||
// If address is not for preferred stack waits a while to see
|
||||
// if preferred stack address is acquired
|
||||
if (!mbed_lwip_get_ip_addr(false, &lwip_netif)) {
|
||||
ret = sys_arch_sem_wait(&lwip_netif_has_addr, ADDR_TIMEOUT * 1000);
|
||||
sys_arch_sem_wait(&lwip_netif_has_addr, ADDR_TIMEOUT * 1000);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -556,14 +624,14 @@ void mbed_lwip_clear_ipv6_addresses(struct netif *lwip_netif)
|
|||
}
|
||||
#endif
|
||||
|
||||
nsapi_error_t mbed_lwip_bringdown(void)
|
||||
nsapi_error_t mbed_lwip_bringdown(bool ppp)
|
||||
{
|
||||
// Check if we've connected
|
||||
if (!lwip_connected) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
#if LWIP_DHCP
|
||||
// Disconnect from the network
|
||||
if (lwip_dhcp) {
|
||||
dhcp_release(&lwip_netif);
|
||||
|
@ -572,14 +640,30 @@ nsapi_error_t mbed_lwip_bringdown(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
netif_set_down(&lwip_netif);
|
||||
if (ppp) {
|
||||
/* this is a blocking call, returns when PPP is properly closed */
|
||||
err_t err = ppp_lwip_disconnect();
|
||||
if (err) {
|
||||
return mbed_lwip_err_remap(err);
|
||||
}
|
||||
MBED_ASSERT(!netif_is_link_up(&lwip_netif));
|
||||
/*if (netif_is_link_up(&lwip_netif)) {
|
||||
if (sys_arch_sem_wait(&lwip_netif_unlinked, 15000) == SYS_ARCH_TIMEOUT) {
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
}*/
|
||||
} else {
|
||||
netif_set_down(&lwip_netif);
|
||||
}
|
||||
|
||||
#if LWIP_IPV6
|
||||
mbed_lwip_clear_ipv6_addresses(&lwip_netif);
|
||||
#endif
|
||||
|
||||
|
||||
sys_sem_free(&lwip_netif_has_addr);
|
||||
sys_sem_new(&lwip_netif_has_addr, 0);
|
||||
|
||||
lwip_connected = false;
|
||||
return 0;
|
||||
}
|
||||
|
@ -598,14 +682,18 @@ static nsapi_error_t mbed_lwip_err_remap(err_t err) {
|
|||
return NSAPI_ERROR_NO_CONNECTION;
|
||||
case ERR_TIMEOUT:
|
||||
case ERR_RTE:
|
||||
case ERR_INPROGRESS:
|
||||
case ERR_WOULDBLOCK:
|
||||
return NSAPI_ERROR_WOULD_BLOCK;
|
||||
case ERR_VAL:
|
||||
case ERR_USE:
|
||||
case ERR_ISCONN:
|
||||
case ERR_ARG:
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
case ERR_INPROGRESS:
|
||||
return NSAPI_ERROR_IN_PROGRESS;
|
||||
case ERR_ALREADY:
|
||||
return NSAPI_ERROR_ALREADY;
|
||||
case ERR_ISCONN:
|
||||
return NSAPI_ERROR_IS_CONNECTED;
|
||||
default:
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
@ -727,7 +815,10 @@ static nsapi_error_t mbed_lwip_socket_bind(nsapi_stack_t *stack, nsapi_socket_t
|
|||
struct lwip_socket *s = (struct lwip_socket *)handle;
|
||||
ip_addr_t ip_addr;
|
||||
|
||||
if ((s->conn->type == NETCONN_TCP && s->conn->pcb.tcp->local_port != 0) ||
|
||||
if (
|
||||
#if LWIP_TCP
|
||||
(s->conn->type == NETCONN_TCP && s->conn->pcb.tcp->local_port != 0) ||
|
||||
#endif
|
||||
(s->conn->type == NETCONN_UDP && s->conn->pcb.udp->local_port != 0)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
@ -876,6 +967,7 @@ static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t h
|
|||
struct lwip_socket *s = (struct lwip_socket *)handle;
|
||||
|
||||
switch (optname) {
|
||||
#if LWIP_TCP
|
||||
case NSAPI_KEEPALIVE:
|
||||
if (optlen != sizeof(int) || s->conn->type != NETCONN_TCP) {
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
|
@ -899,6 +991,7 @@ static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t h
|
|||
|
||||
s->conn->pcb.tcp->keep_intvl = *(int*)optval;
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
case NSAPI_REUSEADDR:
|
||||
if (optlen != sizeof(int)) {
|
||||
|
@ -906,9 +999,9 @@ static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t h
|
|||
}
|
||||
|
||||
if (*(int *)optval) {
|
||||
s->conn->pcb.tcp->so_options |= SOF_REUSEADDR;
|
||||
ip_set_option(s->conn->pcb.ip, SOF_REUSEADDR);
|
||||
} else {
|
||||
s->conn->pcb.tcp->so_options &= ~SOF_REUSEADDR;
|
||||
ip_reset_option(s->conn->pcb.ip, SOF_REUSEADDR);
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -65,7 +65,11 @@
|
|||
#error "Either IPv4 or IPv6 must be preferred."
|
||||
#endif
|
||||
|
||||
//#define LWIP_DEBUG
|
||||
#if defined(MBED_CONF_LWIP_DEBUG_ENABLED)
|
||||
#define LWIP_DEBUG MBED_CONF_LWIP_DEBUG_ENABLED
|
||||
#else
|
||||
#define LWIP_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if NO_SYS == 0
|
||||
#include "cmsis_os.h"
|
||||
|
@ -85,7 +89,7 @@
|
|||
#define MBED_CONF_LWIP_TCPIP_THREAD_STACKSIZE 1200
|
||||
#endif
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
#if LWIP_DEBUG
|
||||
#define TCPIP_THREAD_STACKSIZE MBED_CONF_LWIP_TCPIP_THREAD_STACKSIZE*2
|
||||
#else
|
||||
#define TCPIP_THREAD_STACKSIZE MBED_CONF_LWIP_TCPIP_THREAD_STACKSIZE
|
||||
|
@ -98,10 +102,17 @@
|
|||
#define MBED_CONF_LWIP_DEFAULT_THREAD_STACKSIZE 512
|
||||
#endif
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
// Thread stack size for private PPP thread
|
||||
#ifndef MBED_CONF_LWIP_PPP_THREAD_STACKSIZE
|
||||
#define MBED_CONF_LWIP_PPP_THREAD_STACKSIZE 512
|
||||
#endif
|
||||
|
||||
#if LWIP_DEBUG
|
||||
#define DEFAULT_THREAD_STACKSIZE MBED_CONF_LWIP_DEFAULT_THREAD_STACKSIZE*2
|
||||
#define PPP_THREAD_STACK_SIZE MBED_CONF_LWIP_PPP_THREAD_STACKSIZE*2
|
||||
#else
|
||||
#define DEFAULT_THREAD_STACKSIZE MBED_CONF_LWIP_DEFAULT_THREAD_STACKSIZE
|
||||
#define PPP_THREAD_STACK_SIZE MBED_CONF_LWIP_PPP_THREAD_STACKSIZE
|
||||
#endif
|
||||
|
||||
#define MEMP_NUM_SYS_TIMEOUT 16
|
||||
|
@ -164,10 +175,15 @@
|
|||
#define MEMP_NUM_NETCONN 4
|
||||
#endif
|
||||
|
||||
#if MBED_CONF_LWIP_TCP_ENABLED
|
||||
#define LWIP_TCP 1
|
||||
#define TCP_QUEUE_OOSEQ 0
|
||||
#define TCP_OVERSIZE 0
|
||||
#define LWIP_TCP_KEEPALIVE 1
|
||||
#else
|
||||
#define LWIP_TCP 0
|
||||
#endif
|
||||
|
||||
#define LWIP_DHCP LWIP_IPV4
|
||||
#define LWIP_DNS 1
|
||||
#define LWIP_SOCKET 0
|
||||
|
||||
|
@ -181,7 +197,8 @@
|
|||
#define LWIP_COMPAT_SOCKETS 0
|
||||
#define LWIP_POSIX_SOCKETS_IO_NAMES 0
|
||||
#define LWIP_SO_RCVTIMEO 1
|
||||
#define LWIP_TCP_KEEPALIVE 1
|
||||
|
||||
#define LWIP_BROADCAST_PING 1
|
||||
|
||||
// Fragmentation on, as per IPv4 default
|
||||
#define LWIP_IPV6_FRAG LWIP_IPV6
|
||||
|
@ -221,34 +238,63 @@
|
|||
#define AUTOIP_DEBUG LWIP_DBG_OFF
|
||||
#define DNS_DEBUG LWIP_DBG_OFF
|
||||
#define IP6_DEBUG LWIP_DBG_OFF
|
||||
|
||||
#if MBED_CONF_LWIP_ENABLE_PPP_TRACE
|
||||
#define PPP_DEBUG LWIP_DBG_ON
|
||||
#else
|
||||
#define PPP_DEBUG LWIP_DBG_OFF
|
||||
#endif //MBED_CONF_LWIP_ENABLE_PPP_TRACE
|
||||
#define ETHARP_DEBUG LWIP_DBG_OFF
|
||||
#define UDP_LPC_EMAC LWIP_DBG_OFF
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
#if LWIP_DEBUG
|
||||
#define MEMP_OVERFLOW_CHECK 1
|
||||
#define MEMP_SANITY_CHECK 1
|
||||
#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
|
||||
#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
|
||||
#else
|
||||
#define LWIP_NOASSERT 1
|
||||
#define LWIP_STATS 0
|
||||
#endif
|
||||
|
||||
#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
|
||||
#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
|
||||
|
||||
#define LWIP_PLATFORM_BYTESWAP 1
|
||||
|
||||
#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 1
|
||||
|
||||
#if LWIP_TRANSPORT_ETHERNET
|
||||
// Interface type configuration
|
||||
|
||||
#if MBED_CONF_LWIP_ETHERNET_ENABLED
|
||||
#define LWIP_ARP 1
|
||||
#define LWIP_ETHERNET 1
|
||||
#define LWIP_DHCP LWIP_IPV4
|
||||
#else
|
||||
#define LWIP_ARP 0
|
||||
#define LWIP_ETHERNET 0
|
||||
#endif // MBED_CONF_LWIP_ETHERNET_ENABLED
|
||||
|
||||
// Note generic macro name used rather than MBED_CONF_LWIP_PPP_ENABLED
|
||||
// to allow users like PPPCellularInterface to detect that nsapi_ppp.h is available.
|
||||
#if NSAPI_PPP_AVAILABLE
|
||||
#define PPP_SUPPORT 1
|
||||
#define CHAP_SUPPORT 1
|
||||
#define PPP_INPROC_IRQ_SAFE 1
|
||||
// Save RAM
|
||||
#define PAP_SUPPORT 0
|
||||
#define VJ_SUPPORT 0
|
||||
#define PRINTPKT_SUPPORT 0
|
||||
|
||||
// Broadcast
|
||||
#define IP_SOF_BROADCAST 0
|
||||
#define IP_SOF_BROADCAST_RECV 0
|
||||
|
||||
#define LWIP_BROADCAST_PING 1
|
||||
#define MAXNAMELEN 64 /* max length of hostname or name for auth */
|
||||
#define MAXSECRETLEN 64
|
||||
#endif // NSAPI_PPP_AVAILABLE
|
||||
|
||||
// Make sure we default these to off, so
|
||||
// LWIP doesn't default to on
|
||||
#ifndef LWIP_ARP
|
||||
#define LWIP_ARP 0
|
||||
#endif
|
||||
// Checksum-on-copy disabled due to https://savannah.nongnu.org/bugs/?50914
|
||||
#define LWIP_CHECKSUM_ON_COPY 0
|
||||
|
||||
|
@ -256,31 +302,15 @@
|
|||
#define LWIP_NETIF_STATUS_CALLBACK 1
|
||||
#define LWIP_NETIF_LINK_CALLBACK 1
|
||||
|
||||
#elif LWIP_TRANSPORT_PPP
|
||||
|
||||
#define TCP_SND_BUF (3 * 536)
|
||||
#define TCP_WND (2 * 536)
|
||||
|
||||
#define LWIP_ARP 0
|
||||
|
||||
#define PPP_SUPPORT 1
|
||||
#define CHAP_SUPPORT 1
|
||||
#define PAP_SUPPORT 1
|
||||
#define PPP_THREAD_STACKSIZE 4*192
|
||||
#define PPP_THREAD_PRIO 0
|
||||
|
||||
#define MAXNAMELEN 64 /* max length of hostname or name for auth */
|
||||
#define MAXSECRETLEN 64
|
||||
|
||||
#else
|
||||
#error A transport mechanism (Ethernet or PPP) must be defined
|
||||
#endif
|
||||
#define DNS_TABLE_SIZE 2
|
||||
#define DNS_MAX_NAME_LENGTH 128
|
||||
|
||||
#include <lwip/arch.h>
|
||||
#include "lwip_random.h"
|
||||
#include "lwip_tcp_isn.h"
|
||||
#define LWIP_HOOK_TCP_ISN lwip_hook_tcp_isn
|
||||
#if MBEDTLS_MD5_C
|
||||
#ifdef MBEDTLS_MD5_C
|
||||
#include "mbedtls/inc/mbedtls/md5.h"
|
||||
#define LWIP_USE_EXTERNAL_MBEDTLS 1
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,422 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include "platform/FileHandle.h"
|
||||
#include "platform/mbed_poll.h"
|
||||
#include "events/EventQueue.h"
|
||||
#if defined(FEATURE_COMMON_PAL)
|
||||
#include "mbed_trace.h"
|
||||
#define TRACE_GROUP "LPPP"
|
||||
#else
|
||||
#define tr_debug(...) (void(0)) //dummies if feature common pal is not added
|
||||
#define tr_info(...) (void(0)) //dummies if feature common pal is not added
|
||||
#define tr_error(...) (void(0)) //dummies if feature common pal is not added
|
||||
#endif //defined(FEATURE_COMMON_PAL)
|
||||
#include "rtos/Thread.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/pbuf.h"
|
||||
extern "C" { // "pppos.h" is missing extern C
|
||||
#include "netif/ppp/pppapi.h"
|
||||
}
|
||||
|
||||
#include "nsapi_ppp.h"
|
||||
#include "ppp_lwip.h"
|
||||
#include "lwip_stack.h"
|
||||
|
||||
namespace mbed {
|
||||
|
||||
using rtos::Thread;
|
||||
using events::EventQueue;
|
||||
|
||||
#if LWIP_PPP_API
|
||||
|
||||
static EventQueue *event_queue;
|
||||
static Thread *event_thread;
|
||||
static volatile bool event_queued;
|
||||
static nsapi_error_t connect_error_code;
|
||||
|
||||
// Just one interface for now
|
||||
static FileHandle *my_stream;
|
||||
static ppp_pcb *my_ppp_pcb;
|
||||
static bool ppp_active = false;
|
||||
static const char *login;
|
||||
static const char *pwd;
|
||||
static sys_sem_t ppp_close_sem;
|
||||
static Callback<void(nsapi_error_t)> connection_status_cb;
|
||||
|
||||
static EventQueue *prepare_event_queue()
|
||||
{
|
||||
if (event_queue) {
|
||||
return event_queue;
|
||||
}
|
||||
|
||||
// Should be trying to get a global shared event queue here!
|
||||
// Shouldn't have to be making a private thread!
|
||||
|
||||
// Only need to queue 2 events. new blows on failure.
|
||||
event_queue = new EventQueue(2 * EVENTS_EVENT_SIZE, NULL);
|
||||
event_thread = new Thread(osPriorityNormal, PPP_THREAD_STACK_SIZE);
|
||||
|
||||
if (event_thread->start(callback(event_queue, &EventQueue::dispatch_forever)) != osOK) {
|
||||
delete event_thread;
|
||||
delete event_queue;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return event_queue;
|
||||
}
|
||||
|
||||
static u32_t ppp_output(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
|
||||
{
|
||||
FileHandle *stream = my_stream;
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pollfh fhs;
|
||||
fhs.fh = stream;
|
||||
fhs.events = POLLOUT;
|
||||
|
||||
// LWIP expects us to block on write
|
||||
// File handle will be in non-blocking mode, because of read events.
|
||||
// Therefore must use poll to achieve the necessary block for writing.
|
||||
|
||||
uint32_t written = 0;
|
||||
while (len != 0) {
|
||||
// Block forever until we're selected - don't care about reason we wake;
|
||||
// return from write should tell us what's up.
|
||||
poll(&fhs, 1, -1);
|
||||
// This write will be non-blocking, but blocking would be fine.
|
||||
ssize_t ret = stream->write(data, len);
|
||||
if (ret == -EAGAIN) {
|
||||
continue;
|
||||
} else if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
written += ret;
|
||||
data += ret;
|
||||
len -= ret;
|
||||
}
|
||||
|
||||
// /tr_debug("> %ld bytes of data written\n", (long) written);
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
static void ppp_link_status(ppp_pcb *pcb, int err_code, void *ctx)
|
||||
{
|
||||
nsapi_error_t mapped_err_code = NSAPI_ERROR_NO_CONNECTION;
|
||||
|
||||
switch(err_code) {
|
||||
case PPPERR_NONE:
|
||||
mapped_err_code = NSAPI_ERROR_OK;
|
||||
tr_info("status_cb: Connected");
|
||||
#if PPP_IPV4_SUPPORT
|
||||
tr_debug(" our_ipaddr = %s", ipaddr_ntoa(&ppp_netif(pcb)->ip_addr));
|
||||
tr_debug(" his_ipaddr = %s", ipaddr_ntoa(&ppp_netif(pcb)->gw));
|
||||
tr_debug(" netmask = %s", ipaddr_ntoa(&ppp_netif(pcb)->netmask));
|
||||
#if LWIP_DNS
|
||||
const ip_addr_t *ns;
|
||||
ns = dns_getserver(0);
|
||||
if (ns) {
|
||||
tr_debug(" dns1 = %s", ipaddr_ntoa(ns));
|
||||
}
|
||||
ns = dns_getserver(1);
|
||||
if (ns) {
|
||||
tr_debug(" dns2 = %s", ipaddr_ntoa(ns));
|
||||
}
|
||||
#endif /* LWIP_DNS */
|
||||
#endif /* PPP_IPV4_SUPPORT */
|
||||
#if PPP_IPV6_SUPPORT
|
||||
tr_debug(" our6_ipaddr = %s", ip6addr_ntoa(netif_ip6_addr(ppp_netif(pcb), 0)));
|
||||
#endif /* PPP_IPV6_SUPPORT */
|
||||
break;
|
||||
|
||||
case PPPERR_PARAM:
|
||||
tr_info("status_cb: Invalid parameter");
|
||||
break;
|
||||
|
||||
case PPPERR_OPEN:
|
||||
tr_info("status_cb: Unable to open PPP session");
|
||||
break;
|
||||
|
||||
case PPPERR_DEVICE:
|
||||
tr_info("status_cb: Invalid I/O device for PPP");
|
||||
break;
|
||||
|
||||
case PPPERR_ALLOC:
|
||||
tr_info("status_cb: Unable to allocate resources");
|
||||
break;
|
||||
|
||||
case PPPERR_USER:
|
||||
tr_info("status_cb: User interrupt");
|
||||
break;
|
||||
|
||||
case PPPERR_CONNECT:
|
||||
tr_info("status_cb: Connection lost");
|
||||
mapped_err_code = NSAPI_ERROR_CONNECTION_LOST;
|
||||
break;
|
||||
|
||||
case PPPERR_AUTHFAIL:
|
||||
tr_info("status_cb: Failed authentication challenge");
|
||||
mapped_err_code = NSAPI_ERROR_AUTH_FAILURE;
|
||||
break;
|
||||
|
||||
case PPPERR_PROTOCOL:
|
||||
tr_info("status_cb: Failed to meet protocol");
|
||||
break;
|
||||
|
||||
case PPPERR_PEERDEAD:
|
||||
tr_info("status_cb: Connection timeout");
|
||||
mapped_err_code = NSAPI_ERROR_CONNECTION_TIMEOUT;
|
||||
break;
|
||||
|
||||
case PPPERR_IDLETIMEOUT:
|
||||
tr_info("status_cb: Idle Timeout");
|
||||
break;
|
||||
|
||||
case PPPERR_CONNECTTIME:
|
||||
tr_info("status_cb: Max connect time reached");
|
||||
break;
|
||||
|
||||
case PPPERR_LOOPBACK:
|
||||
tr_info("status_cb: Loopback detected");
|
||||
break;
|
||||
|
||||
default:
|
||||
tr_info("status_cb: Unknown error code %d", err_code);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (err_code == PPPERR_NONE) {
|
||||
/* suppress generating a callback event for connection up
|
||||
* Because connect() call is blocking, why wait for a callback */
|
||||
return;
|
||||
}
|
||||
|
||||
/* If some error happened, we need to properly shutdown the PPP interface */
|
||||
if (ppp_active) {
|
||||
ppp_active = false;
|
||||
connect_error_code = mapped_err_code;
|
||||
sys_sem_signal(&ppp_close_sem);
|
||||
}
|
||||
|
||||
/* Alright, PPP interface is down, we need to notify upper layer */
|
||||
if (connection_status_cb) {
|
||||
connection_status_cb(mapped_err_code);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_modem_hangup()
|
||||
{
|
||||
if (my_ppp_pcb->phase != PPP_PHASE_DEAD) {
|
||||
ppp_close(my_ppp_pcb, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#if !PPP_INPROC_IRQ_SAFE
|
||||
#error "PPP_INPROC_IRQ_SAFE must be enabled"
|
||||
#endif
|
||||
static void ppp_input()
|
||||
{
|
||||
// Allow new events from now, avoiding potential races around the read
|
||||
event_queued = false;
|
||||
|
||||
if (!my_stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-blocking error check handler
|
||||
pollfh fhs;
|
||||
fhs.fh = my_stream;
|
||||
fhs.events = POLLIN;
|
||||
poll(&fhs, 1, 0);
|
||||
if (fhs.revents & (POLLHUP|POLLERR|POLLNVAL)) {
|
||||
handle_modem_hangup();
|
||||
return;
|
||||
}
|
||||
|
||||
// Infinite loop, but we assume that we can read faster than the
|
||||
// serial, so we will fairly rapidly hit -EAGAIN.
|
||||
for (;;) {
|
||||
u8_t buffer[16];
|
||||
ssize_t len = my_stream->read(buffer, sizeof buffer);
|
||||
if (len == -EAGAIN) {
|
||||
break;
|
||||
} else if (len <= 0) {
|
||||
handle_modem_hangup();
|
||||
return;
|
||||
}
|
||||
pppos_input(my_ppp_pcb, buffer, len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void stream_cb() {
|
||||
if (my_stream && !event_queued) {
|
||||
event_queued = true;
|
||||
if (event_queue->call(callback(ppp_input)) == 0) {
|
||||
event_queued = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" err_t ppp_lwip_connect()
|
||||
{
|
||||
#if PPP_AUTH_SUPPORT
|
||||
ppp_set_auth(my_ppp_pcb, PPPAUTHTYPE_ANY, login, pwd);
|
||||
#endif //PPP_AUTH_SUPPORT
|
||||
ppp_active = true;
|
||||
err_t ret = ppp_connect(my_ppp_pcb, 0);
|
||||
// lwIP's ppp.txt says input must not be called until after connect
|
||||
if (ret == ERR_OK) {
|
||||
my_stream->sigio(callback(stream_cb));
|
||||
} else {
|
||||
ppp_active = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" err_t ppp_lwip_disconnect()
|
||||
{
|
||||
err_t ret = ppp_close(my_ppp_pcb, 0);
|
||||
if (ret != ERR_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* close call made, now let's catch the response in the status callback */
|
||||
sys_arch_sem_wait(&ppp_close_sem, 0);
|
||||
|
||||
/* Detach callbacks, and put handle back to default blocking mode */
|
||||
my_stream->sigio(Callback<void()>());
|
||||
my_stream->set_blocking(true);
|
||||
my_stream = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" nsapi_error_t ppp_lwip_if_init(struct netif *netif)
|
||||
{
|
||||
if (!prepare_event_queue()) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (!my_ppp_pcb) {
|
||||
my_ppp_pcb = pppos_create(netif,
|
||||
ppp_output, ppp_link_status, NULL);
|
||||
if (!my_ppp_pcb) {
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
sys_sem_new(&ppp_close_sem, 0);
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
ppp_set_usepeerdns(my_ppp_pcb, true);
|
||||
#endif
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
nsapi_error_t nsapi_ppp_error_code()
|
||||
{
|
||||
return connect_error_code;
|
||||
}
|
||||
|
||||
nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_error_t)> cb, const char *uname, const char *password)
|
||||
{
|
||||
if (my_stream) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
my_stream = stream;
|
||||
stream->set_blocking(false);
|
||||
connection_status_cb = cb;
|
||||
login = uname;
|
||||
pwd = password;
|
||||
|
||||
// mustn't start calling input until after connect -
|
||||
// attach deferred until ppp_lwip_connect, called from mbed_lwip_bringup
|
||||
nsapi_error_t retcode = mbed_lwip_bringup(false, true, NULL, NULL, NULL);
|
||||
|
||||
if (retcode != NSAPI_ERROR_OK && connect_error_code != NSAPI_ERROR_OK) {
|
||||
return connect_error_code;
|
||||
} else {
|
||||
return retcode;
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_error_t nsapi_ppp_disconnect(FileHandle *stream)
|
||||
{
|
||||
return mbed_lwip_bringdown(true);
|
||||
}
|
||||
|
||||
NetworkStack *nsapi_ppp_get_stack()
|
||||
{
|
||||
return nsapi_create_stack(&lwip_stack);
|
||||
}
|
||||
|
||||
const char *nsapi_ppp_get_ip_addr(FileHandle *stream)
|
||||
{
|
||||
static char ip_addr[IPADDR_STRLEN_MAX];
|
||||
|
||||
if (stream == my_stream) {
|
||||
|
||||
if (mbed_lwip_get_ip_address(ip_addr, IPADDR_STRLEN_MAX)) {
|
||||
return ip_addr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
const char *nsapi_ppp_get_netmask(FileHandle *stream)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
static char netmask[IPADDR_STRLEN_MAX];
|
||||
if (stream == my_stream) {
|
||||
if (mbed_lwip_get_netmask(netmask, IPADDR_STRLEN_MAX)) {
|
||||
return netmask;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
const char *nsapi_ppp_get_gw_addr(FileHandle *stream)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
static char gwaddr[IPADDR_STRLEN_MAX];
|
||||
if (stream == my_stream) {
|
||||
if (mbed_lwip_get_gateway(gwaddr, IPADDR_STRLEN_MAX)) {
|
||||
return gwaddr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* LWIP_PPP_API */
|
||||
|
||||
} // namespace mbed
|
|
@ -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_ */
|
|
@ -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);
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
*
|
||||
* @return Size of the file in bytes
|
||||
*/
|
||||
virtual size_t size();
|
||||
virtual off_t size();
|
||||
|
||||
private:
|
||||
FileSystem *_fs;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ protected:
|
|||
* @param file File handle
|
||||
* @return Size of the file in bytes
|
||||
*/
|
||||
virtual size_t file_size(fs_file_t file);
|
||||
virtual off_t file_size(fs_file_t file);
|
||||
|
||||
/** Open a directory on the filesystem
|
||||
*
|
||||
|
|
|
@ -508,11 +508,11 @@ off_t FATFileSystem::file_tell(fs_file_t file) {
|
|||
return res;
|
||||
}
|
||||
|
||||
size_t FATFileSystem::file_size(fs_file_t file) {
|
||||
off_t FATFileSystem::file_size(fs_file_t file) {
|
||||
FIL *fh = static_cast<FIL*>(file);
|
||||
|
||||
lock();
|
||||
size_t res = fh->fsize;
|
||||
off_t res = fh->fsize;
|
||||
unlock();
|
||||
|
||||
return res;
|
||||
|
|
|
@ -179,7 +179,7 @@ protected:
|
|||
* @param file File handle
|
||||
* @return Size of the file in bytes
|
||||
*/
|
||||
virtual size_t file_size(fs_file_t file);
|
||||
virtual off_t file_size(fs_file_t file);
|
||||
|
||||
/** Open a directory on the filesystem
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
||||
/** @}*/
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/** \addtogroup netsocket */
|
||||
/** @{*/
|
||||
/* CellularInterface
|
||||
|
@ -16,12 +15,12 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CELLULAR_INTERFACE_H
|
||||
#define CELLULAR_INTERFACE_H
|
||||
|
||||
|
||||
#include "netsocket/NetworkInterface.h"
|
||||
|
||||
|
||||
|
||||
/** CellularInterface class
|
||||
*
|
||||
|
@ -48,7 +47,7 @@ public:
|
|||
*
|
||||
* @param apn Optional name of the network to connect to
|
||||
* @param username Optional username for your APN
|
||||
* @param password Optional password for your APN
|
||||
* @param password Optional password for your APN
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t connect(const char *apn,
|
||||
|
@ -61,7 +60,7 @@ public:
|
|||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t connect() = 0;
|
||||
|
||||
|
||||
/** Stop the interface
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
|
@ -69,7 +68,7 @@ public:
|
|||
virtual nsapi_error_t disconnect() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/** @}*/
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -0,0 +1,781 @@
|
|||
/* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "PPPCellularInterface.h"
|
||||
|
||||
#if NSAPI_PPP_AVAILABLE
|
||||
|
||||
#include <string.h>
|
||||
#include "nsapi_ppp.h"
|
||||
#if MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP
|
||||
#include "utils/APN_db.h"
|
||||
#endif //MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP
|
||||
#if defined(FEATURE_COMMON_PAL)
|
||||
#include "mbed_trace.h"
|
||||
#define TRACE_GROUP "UCID"
|
||||
#else
|
||||
#define tr_debug(...) (void(0)) //dummies if feature common pal is not added
|
||||
#define tr_info(...) (void(0)) //dummies if feature common pal is not added
|
||||
#define tr_error(...) (void(0)) //dummies if feature common pal is not added
|
||||
#endif //defined(FEATURE_COMMON_PAL)
|
||||
|
||||
/**
|
||||
* PDP (packet data profile) Context
|
||||
*/
|
||||
#define CTX "1"
|
||||
|
||||
/**
|
||||
* Output Enter sequence for the modem , default CR
|
||||
*/
|
||||
#define OUTPUT_ENTER_KEY "\r"
|
||||
|
||||
#if MBED_CONF_PPP_CELL_IFACE_AT_PARSER_BUFFER_SIZE
|
||||
#define AT_PARSER_BUFFER_SIZE MBED_CONF_PPP_CELL_IFACE_AT_PARSER_BUFFER_SIZE //bytes
|
||||
#else
|
||||
#define AT_PARSER_BUFFER_SIZE 256 //bytes
|
||||
#endif //MBED_CONF_PPP_CELL_IFACE_AT_PARSER_BUFFER_SIZE
|
||||
|
||||
#if MBED_CONF_PPP_CELL_IFACE_AT_PARSER_TIMEOUT
|
||||
#define AT_PARSER_TIMEOUT MBED_CONF_PPP_CELL_IFACE_AT_PARSER_TIMEOUT
|
||||
#else
|
||||
#define AT_PARSER_TIMEOUT 8*1000 //miliseconds
|
||||
#endif //MBED_CONF_PPP_CELL_IFACE_AT_PARSER_TIMEOUT
|
||||
|
||||
static bool initialized;
|
||||
static bool set_credentials_api_used;
|
||||
static bool set_sim_pin_check_request;
|
||||
static bool change_pin;
|
||||
static device_info dev_info;
|
||||
|
||||
static void parser_abort(ATCmdParser *at)
|
||||
{
|
||||
at->abort();
|
||||
}
|
||||
|
||||
static bool get_CCID(ATCmdParser *at)
|
||||
{
|
||||
// Returns the ICCID (Integrated Circuit Card ID) of the SIM-card.
|
||||
// ICCID is a serial number identifying the SIM.
|
||||
bool success = at->send("AT+CCID") && at->recv("+CCID: %20[^\n]\nOK\n", dev_info.ccid);
|
||||
tr_debug("DevInfo: CCID=%s", dev_info.ccid);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool get_IMSI(ATCmdParser *at)
|
||||
{
|
||||
// International mobile subscriber identification
|
||||
bool success = at->send("AT+CIMI") && at->recv("%15[^\n]\nOK\n", dev_info.imsi);
|
||||
tr_debug("DevInfo: IMSI=%s", dev_info.imsi);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool get_IMEI(ATCmdParser *at)
|
||||
{
|
||||
// International mobile equipment identifier
|
||||
bool success = at->send("AT+CGSN") && at->recv("%15[^\n]\nOK\n", dev_info.imei);
|
||||
tr_debug("DevInfo: IMEI=%s", dev_info.imei);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool get_MEID(ATCmdParser *at)
|
||||
{
|
||||
// Mobile equipment identifier
|
||||
bool success = at->send("AT+GSN")
|
||||
&& at->recv("%18[^\n]\nOK\n", dev_info.meid);
|
||||
tr_debug("DevInfo: MEID=%s", dev_info.meid);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool set_CMGF(ATCmdParser *at)
|
||||
{
|
||||
// Preferred message format
|
||||
// set AT+CMGF=[mode] , 0 PDU mode, 1 text mode
|
||||
bool success = at->send("AT+CMGF=1") && at->recv("OK");
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool set_CNMI(ATCmdParser *at)
|
||||
{
|
||||
// New SMS indication configuration
|
||||
// set AT+CMTI=[mode, index] , 0 PDU mode, 1 text mode
|
||||
// Multiple URCs for SMS, i.e., CMT, CMTI, UCMT, CBMI, CDSI as DTE could be following any of these SMS formats
|
||||
bool success = at->send("AT+CNMI=2,"CTX) && at->recv("OK");
|
||||
return success;
|
||||
}
|
||||
|
||||
static void CMTI_URC(ATCmdParser *at)
|
||||
{
|
||||
// our CMGF = 1, i.e., text mode. So we expect response in this format:
|
||||
//+CMTI: <mem>,<index>,
|
||||
at->recv(": %*u,%*u");
|
||||
tr_info("New SMS received");
|
||||
|
||||
}
|
||||
|
||||
static void CMT_URC(ATCmdParser *at)
|
||||
{
|
||||
// our CMGF = 1, i.e., text mode. So we expect response in this format:
|
||||
//+CMT: <oa>,[<alpha>],<scts>[,<tooa>,
|
||||
//<fo>,<pid>,<dcs>,<sca>,<tosca>,
|
||||
//<length>]<CR><LF><data>
|
||||
// By default detailed SMS header CSDH=0 , so we are not expecting [,<tooa>,
|
||||
//<fo>,<pid>,<dcs>,<sca>,<tosca>
|
||||
char sms[50];
|
||||
char service_timestamp[15];
|
||||
at->recv(": %49[^\"]\",,%14[^\"]\"\n", sms, service_timestamp);
|
||||
|
||||
tr_info("SMS:%s, %s", service_timestamp, sms);
|
||||
|
||||
}
|
||||
|
||||
static bool set_atd(ATCmdParser *at)
|
||||
{
|
||||
bool success = at->send("ATD*99***"CTX"#") && at->recv("CONNECT");
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables SIM pin check lock
|
||||
*/
|
||||
static nsapi_error_t do_sim_pin_check(ATCmdParser *at, const char *pin)
|
||||
{
|
||||
bool success;
|
||||
if (set_sim_pin_check_request) {
|
||||
/* use the SIM locked */
|
||||
success = at->send("AT+CLCK=\"SC\",1,\"%s\"", pin) && at->recv("OK");
|
||||
} else {
|
||||
/* use the SIM unlocked */
|
||||
success = at->send("AT+CLCK=\"SC\",0,\"%s\"",pin) && at->recv("OK");
|
||||
}
|
||||
|
||||
if (success) return NSAPI_ERROR_OK;
|
||||
|
||||
return NSAPI_ERROR_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the pin code for the SIM card
|
||||
*/
|
||||
static nsapi_error_t do_change_sim_pin(ATCmdParser *at, const char *old_pin, const char *new_pin)
|
||||
{
|
||||
/* changes the SIM pin */
|
||||
bool success = at->send("AT+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin) && at->recv("OK");
|
||||
if (success) {
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
return NSAPI_ERROR_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
static void set_nwk_reg_status_csd(unsigned int status)
|
||||
{
|
||||
switch (status) {
|
||||
case CSD_NOT_REGISTERED_NOT_SEARCHING:
|
||||
case CSD_NOT_REGISTERED_SEARCHING:
|
||||
break;
|
||||
case CSD_REGISTERED:
|
||||
case CSD_REGISTERED_ROAMING:
|
||||
tr_debug("Registered for circuit switched service");
|
||||
break;
|
||||
case CSD_REGISTRATION_DENIED:
|
||||
tr_debug("Circuit switched service denied");
|
||||
break;
|
||||
case CSD_UNKNOWN_COVERAGE:
|
||||
tr_debug("Out of circuit switched service coverage");
|
||||
break;
|
||||
case CSD_SMS_ONLY:
|
||||
tr_debug("SMS service only");
|
||||
break;
|
||||
case CSD_SMS_ONLY_ROAMING:
|
||||
tr_debug("SMS service only");
|
||||
break;
|
||||
case CSD_CSFB_NOT_PREFERRED:
|
||||
tr_debug("Registered for circuit switched service with CSFB not preferred");
|
||||
break;
|
||||
default:
|
||||
tr_debug("Unknown circuit switched service registration status. %u", status);
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info.reg_status_csd = static_cast<nwk_registration_status_csd>(status);
|
||||
}
|
||||
|
||||
static void set_nwk_reg_status_psd(unsigned int status)
|
||||
{
|
||||
switch (status) {
|
||||
case PSD_NOT_REGISTERED_NOT_SEARCHING:
|
||||
case PSD_NOT_REGISTERED_SEARCHING:
|
||||
break;
|
||||
case PSD_REGISTERED:
|
||||
case PSD_REGISTERED_ROAMING:
|
||||
tr_debug("Registered for packet switched service");
|
||||
break;
|
||||
case PSD_REGISTRATION_DENIED:
|
||||
tr_debug("Packet switched service denied");
|
||||
break;
|
||||
case PSD_UNKNOWN_COVERAGE:
|
||||
tr_debug("Out of packet switched service coverage");
|
||||
break;
|
||||
case PSD_EMERGENCY_SERVICES_ONLY:
|
||||
tr_debug("Limited access for packet switched service. Emergency use only.");
|
||||
break;
|
||||
default:
|
||||
tr_debug("Unknown packet switched service registration status. %u", status);
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info.reg_status_psd = static_cast<nwk_registration_status_psd>(status);
|
||||
}
|
||||
|
||||
static bool is_registered_csd()
|
||||
{
|
||||
return (dev_info.reg_status_csd == CSD_REGISTERED) ||
|
||||
(dev_info.reg_status_csd == CSD_REGISTERED_ROAMING) ||
|
||||
(dev_info.reg_status_csd == CSD_CSFB_NOT_PREFERRED);
|
||||
}
|
||||
|
||||
static bool is_registered_psd()
|
||||
{
|
||||
return (dev_info.reg_status_psd == PSD_REGISTERED) ||
|
||||
(dev_info.reg_status_psd == PSD_REGISTERED_ROAMING);
|
||||
}
|
||||
|
||||
PPPCellularInterface::PPPCellularInterface(FileHandle *fh, bool debug)
|
||||
{
|
||||
_new_pin = NULL;
|
||||
_pin = NULL;
|
||||
_at = NULL;
|
||||
_apn = "internet";
|
||||
_uname = NULL;
|
||||
_pwd = NULL;
|
||||
_fh = fh;
|
||||
_debug_trace_on = debug;
|
||||
dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
|
||||
dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
|
||||
dev_info.ppp_connection_up = false;
|
||||
}
|
||||
|
||||
|
||||
PPPCellularInterface::~PPPCellularInterface()
|
||||
{
|
||||
delete _at;
|
||||
}
|
||||
|
||||
void PPPCellularInterface::enable_hup(bool)
|
||||
{
|
||||
//meant to be overridden
|
||||
}
|
||||
|
||||
void PPPCellularInterface::modem_init()
|
||||
{
|
||||
//meant to be overridden
|
||||
}
|
||||
|
||||
void PPPCellularInterface::modem_deinit()
|
||||
{
|
||||
//meant to be overridden
|
||||
}
|
||||
|
||||
void PPPCellularInterface::modem_power_up()
|
||||
{
|
||||
//meant to be overridden
|
||||
}
|
||||
|
||||
void PPPCellularInterface::modem_power_down()
|
||||
{
|
||||
//meant to be overridden
|
||||
}
|
||||
|
||||
void PPPCellularInterface::modem_debug_on(bool on)
|
||||
{
|
||||
_debug_trace_on = on;
|
||||
}
|
||||
|
||||
void PPPCellularInterface::connection_status_cb(Callback<void(nsapi_error_t)> cb)
|
||||
{
|
||||
_connection_status_cb = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API. Sets up the flag for the driver to enable or disable SIM pin check
|
||||
* at the next boot.
|
||||
*/
|
||||
void PPPCellularInterface::set_sim_pin_check(bool check)
|
||||
{
|
||||
set_sim_pin_check_request = check;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API. Sets up the flag for the driver to change pin code for SIM card
|
||||
*/
|
||||
void PPPCellularInterface::set_new_sim_pin(const char *new_pin)
|
||||
{
|
||||
change_pin = true;
|
||||
_new_pin = new_pin;
|
||||
}
|
||||
|
||||
bool PPPCellularInterface::nwk_registration(uint8_t nwk_type)
|
||||
{
|
||||
bool success = false;
|
||||
bool registered = false;
|
||||
|
||||
char str[35];
|
||||
int retcode;
|
||||
int retry_counter = 0;
|
||||
unsigned int reg_status;
|
||||
|
||||
success = nwk_type == PACKET_SWITCHED ?
|
||||
_at->send("AT+CGREG=0") :
|
||||
_at->send("AT+CREG=0") && _at->recv("OK\n");
|
||||
|
||||
success = _at->send("AT+COPS=0") //initiate auto-registration
|
||||
&& _at->recv("OK");
|
||||
if (!success) {
|
||||
tr_error("Modem not responding.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Network search
|
||||
//If not registered after 60 attempts, i.e., 30 seconds wait, give up
|
||||
tr_debug("Searching Network ...");
|
||||
|
||||
while (!registered) {
|
||||
|
||||
if (retry_counter > 60) {
|
||||
success = false;
|
||||
goto give_up;
|
||||
}
|
||||
|
||||
success = nwk_type == PACKET_SWITCHED ?
|
||||
_at->send("AT+CGREG?")
|
||||
&& _at->recv("+CGREG: %34[^\n]\n", str)
|
||||
&& _at->recv("OK\n") :
|
||||
_at->send("AT+CREG?")
|
||||
&& _at->recv("+CREG: %34[^\n]\n", str)
|
||||
&& _at->recv("OK\n");
|
||||
|
||||
retcode = sscanf(str, "%*u,%u", ®_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
|
|
@ -0,0 +1,344 @@
|
|||
/* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PPP_CELLULAR_INTERFACE_
|
||||
#define PPP_CELLULAR_INTERFACE_
|
||||
|
||||
#include "CellularBase.h"
|
||||
#include "platform/ATCmdParser.h"
|
||||
#include "mbed.h"
|
||||
|
||||
#if NSAPI_PPP_AVAILABLE
|
||||
|
||||
// Forward declaration
|
||||
class NetworkStack;
|
||||
|
||||
/**
|
||||
* Network registration status
|
||||
* UBX-13001820 - AT Commands Example Application Note (Section 4.1.4.5)
|
||||
*/
|
||||
typedef enum {
|
||||
GSM=0,
|
||||
COMPACT_GSM=1,
|
||||
UTRAN=2,
|
||||
EDGE=3,
|
||||
HSDPA=4,
|
||||
HSUPA=5,
|
||||
HSDPA_HSUPA=6,
|
||||
LTE=7
|
||||
} radio_access_nwk_type;
|
||||
|
||||
/**
|
||||
* Used in registration process to tell which type of network
|
||||
* to connect.
|
||||
*/
|
||||
typedef enum {
|
||||
CIRCUIT_SWITCHED=0,
|
||||
PACKET_SWITCHED
|
||||
} nwk_type;
|
||||
|
||||
/**
|
||||
* Circuit Switched network registration status (CREG Usage)
|
||||
* UBX-13001820 - AT Commands Example Application Note (Section 7.10.3)
|
||||
*/
|
||||
typedef enum {
|
||||
CSD_NOT_REGISTERED_NOT_SEARCHING=0,
|
||||
CSD_REGISTERED=1,
|
||||
CSD_NOT_REGISTERED_SEARCHING=2,
|
||||
CSD_REGISTRATION_DENIED=3,
|
||||
CSD_UNKNOWN_COVERAGE=4,
|
||||
CSD_REGISTERED_ROAMING=5,
|
||||
CSD_SMS_ONLY=6,
|
||||
CSD_SMS_ONLY_ROAMING=7,
|
||||
CSD_CSFB_NOT_PREFERRED=9
|
||||
} nwk_registration_status_csd;
|
||||
|
||||
/**
|
||||
* Packet Switched network registration status (CGREG Usage)
|
||||
* UBX-13001820 - AT Commands Example Application Note (Section 18.27.3)
|
||||
*/
|
||||
typedef enum {
|
||||
PSD_NOT_REGISTERED_NOT_SEARCHING=0,
|
||||
PSD_REGISTERED=1,
|
||||
PSD_NOT_REGISTERED_SEARCHING=2,
|
||||
PSD_REGISTRATION_DENIED=3,
|
||||
PSD_UNKNOWN_COVERAGE=4,
|
||||
PSD_REGISTERED_ROAMING=5,
|
||||
PSD_EMERGENCY_SERVICES_ONLY=8
|
||||
} nwk_registration_status_psd;
|
||||
|
||||
typedef struct {
|
||||
char ccid[20+1]; //!< Integrated Circuit Card ID
|
||||
char imsi[15+1]; //!< International Mobile Station Identity
|
||||
char imei[15+1]; //!< International Mobile Equipment Identity
|
||||
char meid[18+1]; //!< Mobile Equipment IDentifier
|
||||
int flags;
|
||||
bool ppp_connection_up;
|
||||
radio_access_nwk_type rat;
|
||||
nwk_registration_status_csd reg_status_csd;
|
||||
nwk_registration_status_psd reg_status_psd;
|
||||
} device_info;
|
||||
|
||||
/** PPPCellularInterface class
|
||||
*
|
||||
* This interface serves as the controller/driver for the Cellular
|
||||
* modems (tested with UBLOX_C027 and MTS_DRAGONFLY_F411RE).
|
||||
*
|
||||
* The driver will work with any generic FileHandle, and can be
|
||||
* derived from in order to provide forms for specific interfaces, as well as
|
||||
* adding extra power and reset controls alongside the FileHandle.
|
||||
*/
|
||||
class PPPCellularInterface : public CellularBase {
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor for a generic modem, using a single FileHandle for commands and PPP data.
|
||||
*
|
||||
* The file handle pointer is not accessed within the constructor, only recorded for later
|
||||
* use - this permits a derived class to pass a pointer to a not-yet-constructed member object.
|
||||
*/
|
||||
PPPCellularInterface(FileHandle *fh, bool debug = false);
|
||||
|
||||
virtual ~PPPCellularInterface();
|
||||
|
||||
/** Set the Cellular network credentials
|
||||
*
|
||||
* Please check documentation of connect() for default behaviour of APN settings.
|
||||
*
|
||||
* @param apn Access point name
|
||||
* @param uname optionally, Username
|
||||
* @param pwd optionally, password
|
||||
*/
|
||||
virtual void set_credentials(const char *apn, const char *uname = 0,
|
||||
const char *pwd = 0);
|
||||
|
||||
/** Set the pin code for SIM card
|
||||
*
|
||||
* @param sim_pin PIN for the SIM card
|
||||
*/
|
||||
virtual void set_sim_pin(const char *sim_pin);
|
||||
|
||||
/** Start the interface
|
||||
*
|
||||
* Attempts to connect to a Cellular network.
|
||||
* This driver is written mainly for data network connections as CellularInterface
|
||||
* is NetworkInterface. That's why connect() call internally calls nwk_registration()
|
||||
* method with parameter PACKET_SWITCHED network. Circuit switched hook and registration
|
||||
* process is implemented and left in the driver for future extension/subclass support,e.g.,
|
||||
* an SMS or GPS extension.
|
||||
*
|
||||
* @param sim_pin PIN for the SIM card
|
||||
* @param apn optionally, access point name
|
||||
* @param uname optionally, Username
|
||||
* @param pwd optionally, password
|
||||
* @return NSAPI_ERROR_OK on success, or negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t connect(const char *sim_pin, const char *apn = 0,
|
||||
const char *uname = 0, const char *pwd = 0);
|
||||
|
||||
/** Attempt to connect to the Cellular network
|
||||
*
|
||||
* Brings up the network interface. Connects to the Cellular Radio
|
||||
* network and then brings up the underlying network stack to be used
|
||||
* by the cellular modem over PPP interface.
|
||||
*
|
||||
* If the SIM requires a PIN, and it is not set/invalid, NSAPI_ERROR_AUTH_ERROR is returned.
|
||||
* For APN setup, default behaviour is to use 'internet' as APN string and assuming no authentication
|
||||
* is required, i.e., username and password are not set. Optionally, a database lookup can be requested
|
||||
* by turning on the APN database lookup feature. In order to do so, add 'MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP'
|
||||
* in your mbed_app.json. APN database is by no means exhaustive. It contains a short list of some public
|
||||
* APNs with publicly available usernames and passwords (if required) in some particular countries only.
|
||||
* Lookup is done using IMSI (International mobile subscriber identifier).
|
||||
* Please note that even if 'MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP' config option is set, 'set_credentials()' api still
|
||||
* gets the precedence. If the aforementioned API is not used and the config option is set but no match is found in
|
||||
* the lookup table then the driver tries to resort to default APN settings.
|
||||
*
|
||||
* Preferred method is to setup APN using 'set_credentials()' API.
|
||||
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t connect();
|
||||
|
||||
/** Attempt to disconnect from the network
|
||||
*
|
||||
* Brings down the network interface. Shuts down the PPP interface
|
||||
* of the underlying network stack. Does not bring down the Radio network
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t disconnect();
|
||||
|
||||
/** Adds or removes a SIM facility lock
|
||||
*
|
||||
* Can be used to enable or disable SIM pin check at device startup.
|
||||
* This API sets up flags for the driver which would either enable or disable
|
||||
* SIM pin checking depending upon the user provided argument while establishing
|
||||
* connection. It doesn't do anything immediately other than setting up flags.
|
||||
*
|
||||
* @param set can be set to true if the SIM pin check is supposed to be enabled
|
||||
* and vice versa.
|
||||
*/
|
||||
void set_sim_pin_check(bool set);
|
||||
|
||||
/** Change the pin for the SIM card
|
||||
*
|
||||
* Provide the new pin for your SIM card with this API. Old pin code will be assumed to
|
||||
* be set using set_SIM_pin() API. This API have no immediate effect. While establishing
|
||||
* connection, driver will change the SIM pin for the next boot.
|
||||
*
|
||||
* @param new_pin new pin to be used in string format
|
||||
*/
|
||||
void set_new_sim_pin(const char *new_pin);
|
||||
|
||||
/** Check if the connection is currently established or not
|
||||
*
|
||||
* @return true/false If the cellular module have successfully acquired a carrier and is
|
||||
* connected to an external packet data network using PPP, isConnected()
|
||||
* API returns true and false otherwise.
|
||||
*/
|
||||
virtual bool is_connected();
|
||||
|
||||
/** Get the local IP address
|
||||
*
|
||||
* @return Null-terminated representation of the local IP address
|
||||
* or null if no IP address has been recieved
|
||||
*/
|
||||
virtual const char *get_ip_address();
|
||||
|
||||
/** Get the local network mask
|
||||
*
|
||||
* @return Null-terminated representation of the local network mask
|
||||
* or null if no network mask has been recieved
|
||||
*/
|
||||
virtual const char *get_netmask();
|
||||
|
||||
/** Get the local gateways
|
||||
*
|
||||
* @return Null-terminated representation of the local gateway
|
||||
* or null if no network mask has been recieved
|
||||
*/
|
||||
virtual const char *get_gateway();
|
||||
|
||||
/** Get notified if the connection gets lost
|
||||
*
|
||||
* @param cb user defined callback
|
||||
*/
|
||||
void connection_status_cb(Callback<void(nsapi_error_t)> cb);
|
||||
|
||||
/** Turn modem debug traces on
|
||||
*
|
||||
* @param on set true to enable debug traces
|
||||
*/
|
||||
void modem_debug_on(bool on);
|
||||
|
||||
private:
|
||||
FileHandle *_fh;
|
||||
ATCmdParser *_at;
|
||||
const char *_new_pin;
|
||||
const char *_pin;
|
||||
const char *_apn;
|
||||
const char *_uname;
|
||||
const char *_pwd;
|
||||
bool _debug_trace_on;
|
||||
Callback<void(nsapi_error_t)> _connection_status_cb;
|
||||
void base_initialization();
|
||||
void setup_at_parser();
|
||||
void shutdown_at_parser();
|
||||
nsapi_error_t initialize_sim_card();
|
||||
nsapi_error_t setup_context_and_credentials();
|
||||
bool power_up();
|
||||
void power_down();
|
||||
|
||||
protected:
|
||||
/** Enable or disable hang-up detection
|
||||
*
|
||||
* When in PPP data pump mode, it is helpful if the FileHandle will signal hang-up via
|
||||
* POLLHUP, e.g., if the DCD line is deasserted on a UART. During command mode, this
|
||||
* signaling is not desired. enable_hup() controls whether this function should be
|
||||
* active.
|
||||
*
|
||||
* Meant to be overridden.
|
||||
*/
|
||||
virtual void enable_hup(bool enable);
|
||||
|
||||
/** Sets the modem up for powering on
|
||||
*
|
||||
* modem_init() is equivalent to plugging in the device, e.g., attaching power and serial port.
|
||||
* It is meant to be overridden.
|
||||
* If the modem is on-board, an implementation of onboard_modem_api.h
|
||||
* will override this.
|
||||
* If the the modem is a plugged-in device (i.e., a shield type component), the driver deriving from this
|
||||
* class will override.
|
||||
*/
|
||||
virtual void modem_init();
|
||||
|
||||
/** Sets the modem in unplugged state
|
||||
*
|
||||
* modem_deinit() will be equivalent to pulling the plug off of the device, i.e., detaching power
|
||||
* and serial port. This puts the modem in lowest power state.
|
||||
* It is meant to be overridden.
|
||||
* If the modem is on-board, an implementation of onboard_modem_api.h
|
||||
* will override this.
|
||||
* If the the modem is a plugged-in device (i.e., a shield type component), the driver deriving from this
|
||||
* class will override.
|
||||
*/
|
||||
virtual void modem_deinit();
|
||||
|
||||
/** Powers up the modem
|
||||
*
|
||||
* modem_power_up() is equivalent to pressing the soft power button.
|
||||
* The driver may repeat this if the modem is not responsive to AT commands.
|
||||
* It is meant to be overridden.
|
||||
* If the modem is on-board, an implementation of onboard_modem_api.h
|
||||
* will override this.
|
||||
* If the the modem is a plugged-in device (i.e., a shield type component), the driver deriving from this
|
||||
* class will override.
|
||||
*/
|
||||
virtual void modem_power_up();
|
||||
|
||||
/** Powers down the modem
|
||||
*
|
||||
* modem_power_down() is equivalent to turning off the modem by button press.
|
||||
* It is meant to be overridden.
|
||||
* If the modem is on-board, an implementation of onboard_modem_api.h
|
||||
* will override this.
|
||||
* If the the modem is a plugged-in device (i.e., a shield type component), the driver deriving from this
|
||||
* class will override.
|
||||
*/
|
||||
virtual void modem_power_down();
|
||||
|
||||
/** Provide access to the underlying stack
|
||||
*
|
||||
* @return The underlying network stack
|
||||
*/
|
||||
virtual NetworkStack *get_stack();
|
||||
|
||||
/** Starts network registration process.
|
||||
*
|
||||
* Potential users could be subclasses who are not network interface
|
||||
* but would like to use CellularInterface infrastructure to register
|
||||
* with a cellular network, e.g., an SMS extension to CellularInterface.
|
||||
*
|
||||
* @param nwk_type type of network to connect, defaults to packet switched network
|
||||
*
|
||||
* @return true if registration is successful
|
||||
*/
|
||||
bool nwk_registration(uint8_t nwk_type=PACKET_SWITCHED);
|
||||
|
||||
};
|
||||
|
||||
#endif //NSAPI_PPP_AVAILABLE
|
||||
|
||||
#endif //PPP_CELLULAR_INTERFACE_
|
|
@ -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
|
|
@ -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_
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "ppp-cell-iface",
|
||||
"config": {
|
||||
"baud-rate": 115200,
|
||||
"apn-lookup": false,
|
||||
"at-parser-buffer-size": 256,
|
||||
"at-parser-timeout": 8000
|
||||
}
|
||||
}
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/** \addtogroup netsocket */
|
||||
/** @{*/
|
||||
/* nsapi_ppp.h
|
||||
* Modified work Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef NSAPI_PPP_H_
|
||||
#define NSAPI_PPP_H_
|
||||
|
||||
#include "FileHandle.h"
|
||||
#include "NetworkStack.h"
|
||||
|
||||
namespace mbed {
|
||||
|
||||
/** Provide access to the NetworkStack object
|
||||
*
|
||||
* @return The underlying NetworkStack object
|
||||
*/
|
||||
NetworkStack *nsapi_ppp_get_stack();
|
||||
|
||||
|
||||
/** Connect to a PPP pipe
|
||||
*
|
||||
* @param stream Pointer to a device type file handle (descriptor)
|
||||
* @param status_cb Optional, user provided callback for connection status
|
||||
* @param uname Optional, username for the connection
|
||||
* @param pwd Optional, password for the connection
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_error_t)> status_cb=0, const char *uname=0, const char *pwd=0);
|
||||
|
||||
/** Close a PPP connection
|
||||
*
|
||||
* @param stream Pointer to a device type file handle (descriptor)
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
nsapi_error_t nsapi_ppp_disconnect(FileHandle *stream);
|
||||
|
||||
/** Get IP address
|
||||
*
|
||||
* After a successful connection, this API can be used to retrieve assigned IP address.
|
||||
*
|
||||
* @param stream Pointer to a device type file handle (descriptor)
|
||||
*
|
||||
* @return A string containing IP address or NULL
|
||||
*/
|
||||
const char *nsapi_ppp_get_ip_addr(FileHandle *stream);
|
||||
|
||||
/** Get network mask
|
||||
*
|
||||
* After a successful connection, this API can be used to retrieve network mask
|
||||
* in case of an IPv4 network.
|
||||
*
|
||||
* @param stream Pointer to a device type file handle (descriptor)
|
||||
*
|
||||
* @return A string containing network mask or NULL
|
||||
*/
|
||||
const char *nsapi_ppp_get_netmask(FileHandle *stream);
|
||||
|
||||
/** Get gateway address
|
||||
*
|
||||
* After a successful connection, this API can be used to retrieve IP address
|
||||
* of the default gateway in case of an IPv4 network.
|
||||
*
|
||||
* @param stream Pointer to a device type file handle (descriptor)
|
||||
*
|
||||
* @return A string containing gateway IP address or NULL
|
||||
*/
|
||||
const char *nsapi_ppp_get_gw_addr(FileHandle *stream);
|
||||
|
||||
} //namespace mbed
|
||||
|
||||
#endif /* NSAPI_PPP_H_ */
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -0,0 +1,382 @@
|
|||
/* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* Parser for the AT command syntax
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ATCmdParser.h"
|
||||
#include "mbed_poll.h"
|
||||
#include "mbed_debug.h"
|
||||
|
||||
#ifdef LF
|
||||
#undef LF
|
||||
#define LF 10
|
||||
#else
|
||||
#define LF 10
|
||||
#endif
|
||||
|
||||
#ifdef CR
|
||||
#undef CR
|
||||
#define CR 13
|
||||
#else
|
||||
#define CR 13
|
||||
#endif
|
||||
|
||||
// getc/putc handling with timeouts
|
||||
int ATCmdParser::putc(char c)
|
||||
{
|
||||
pollfh fhs;
|
||||
fhs.fh = _fh;
|
||||
fhs.events = POLLOUT;
|
||||
|
||||
int count = poll(&fhs, 1, _timeout);
|
||||
if (count > 0 && (fhs.revents & POLLOUT)) {
|
||||
return _fh->write(&c, 1) == 1 ? 0 : -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int ATCmdParser::getc()
|
||||
{
|
||||
pollfh fhs;
|
||||
fhs.fh = _fh;
|
||||
fhs.events = POLLIN;
|
||||
|
||||
int count = poll(&fhs, 1, _timeout);
|
||||
if (count > 0 && (fhs.revents & POLLIN)) {
|
||||
unsigned char ch;
|
||||
return _fh->read(&ch, 1) == 1 ? ch : -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void ATCmdParser::flush()
|
||||
{
|
||||
while (_fh->readable()) {
|
||||
unsigned char ch;
|
||||
_fh->read(&ch, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// read/write handling with timeouts
|
||||
int ATCmdParser::write(const char *data, int size)
|
||||
{
|
||||
int i = 0;
|
||||
for ( ; i < size; i++) {
|
||||
if (putc(data[i]) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int ATCmdParser::read(char *data, int size)
|
||||
{
|
||||
int i = 0;
|
||||
for ( ; i < size; i++) {
|
||||
int c = getc();
|
||||
if (c < 0) {
|
||||
return -1;
|
||||
}
|
||||
data[i] = c;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// printf/scanf handling
|
||||
int ATCmdParser::vprintf(const char *format, va_list args)
|
||||
{
|
||||
|
||||
if (vsprintf(_buffer, format, args) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for ( ; _buffer[i]; i++) {
|
||||
if (putc(_buffer[i]) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int ATCmdParser::vscanf(const char *format, va_list args)
|
||||
{
|
||||
// Since format is const, we need to copy it into our buffer to
|
||||
// add the line's null terminator and clobber value-matches with asterisks.
|
||||
//
|
||||
// We just use the beginning of the buffer to avoid unnecessary allocations.
|
||||
int i = 0;
|
||||
int offset = 0;
|
||||
|
||||
while (format[i]) {
|
||||
if (format[i] == '%' && format[i+1] != '%' && format[i+1] != '*') {
|
||||
_buffer[offset++] = '%';
|
||||
_buffer[offset++] = '*';
|
||||
i++;
|
||||
} else {
|
||||
_buffer[offset++] = format[i++];
|
||||
}
|
||||
}
|
||||
|
||||
// Scanf has very poor support for catching errors
|
||||
// fortunately, we can abuse the %n specifier to determine
|
||||
// if the entire string was matched.
|
||||
_buffer[offset++] = '%';
|
||||
_buffer[offset++] = 'n';
|
||||
_buffer[offset++] = 0;
|
||||
|
||||
// To workaround scanf's lack of error reporting, we actually
|
||||
// make two passes. One checks the validity with the modified
|
||||
// format string that only stores the matched characters (%n).
|
||||
// The other reads in the actual matched values.
|
||||
//
|
||||
// We keep trying the match until we succeed or some other error
|
||||
// derails us.
|
||||
int j = 0;
|
||||
|
||||
while (true) {
|
||||
// Ran out of space
|
||||
if (j+1 >= _buffer_size - offset) {
|
||||
return false;
|
||||
}
|
||||
// Recieve next character
|
||||
int c = getc();
|
||||
if (c < 0) {
|
||||
return -1;
|
||||
}
|
||||
_buffer[offset + j++] = c;
|
||||
_buffer[offset + j] = 0;
|
||||
|
||||
// Check for match
|
||||
int count = -1;
|
||||
sscanf(_buffer+offset, _buffer, &count);
|
||||
|
||||
// We only succeed if all characters in the response are matched
|
||||
if (count == j) {
|
||||
// Store the found results
|
||||
vsscanf(_buffer+offset, format, args);
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Command parsing with line handling
|
||||
bool ATCmdParser::vsend(const char *command, va_list args)
|
||||
{
|
||||
// Create and send command
|
||||
if (vsprintf(_buffer, command, args) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; _buffer[i]; i++) {
|
||||
if (putc(_buffer[i]) < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Finish with newline
|
||||
for (size_t i = 0; _output_delimiter[i]; i++) {
|
||||
if (putc(_output_delimiter[i]) < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
debug_if(_dbg_on, "AT> %s\n", _buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ATCmdParser::vrecv(const char *response, va_list args)
|
||||
{
|
||||
restart:
|
||||
_aborted = false;
|
||||
// Iterate through each line in the expected response
|
||||
while (response[0]) {
|
||||
// Since response is const, we need to copy it into our buffer to
|
||||
// add the line's null terminator and clobber value-matches with asterisks.
|
||||
//
|
||||
// We just use the beginning of the buffer to avoid unnecessary allocations.
|
||||
int i = 0;
|
||||
int offset = 0;
|
||||
bool whole_line_wanted = false;
|
||||
|
||||
while (response[i]) {
|
||||
if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') {
|
||||
_buffer[offset++] = '%';
|
||||
_buffer[offset++] = '*';
|
||||
i++;
|
||||
} else {
|
||||
_buffer[offset++] = response[i++];
|
||||
// Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification
|
||||
if (response[i - 1] == '\n' && !(i >= 3 && response[i-3] == '[' && response[i-2] == '^')) {
|
||||
whole_line_wanted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scanf has very poor support for catching errors
|
||||
// fortunately, we can abuse the %n specifier to determine
|
||||
// if the entire string was matched.
|
||||
_buffer[offset++] = '%';
|
||||
_buffer[offset++] = 'n';
|
||||
_buffer[offset++] = 0;
|
||||
|
||||
debug_if(_dbg_on, "AT? %s\n", _buffer);
|
||||
// To workaround scanf's lack of error reporting, we actually
|
||||
// make two passes. One checks the validity with the modified
|
||||
// format string that only stores the matched characters (%n).
|
||||
// The other reads in the actual matched values.
|
||||
//
|
||||
// We keep trying the match until we succeed or some other error
|
||||
// derails us.
|
||||
int j = 0;
|
||||
|
||||
while (true) {
|
||||
// Receive next character
|
||||
int c = getc();
|
||||
if (c < 0) {
|
||||
debug_if(_dbg_on, "AT(Timeout)\n");
|
||||
return false;
|
||||
}
|
||||
// Simplify newlines (borrowed from retarget.cpp)
|
||||
if ((c == CR && _in_prev != LF) ||
|
||||
(c == LF && _in_prev != CR)) {
|
||||
_in_prev = c;
|
||||
c = '\n';
|
||||
} else if ((c == CR && _in_prev == LF) ||
|
||||
(c == LF && _in_prev == CR)) {
|
||||
_in_prev = c;
|
||||
// onto next character
|
||||
continue;
|
||||
} else {
|
||||
_in_prev = c;
|
||||
}
|
||||
_buffer[offset + j++] = c;
|
||||
_buffer[offset + j] = 0;
|
||||
|
||||
// Check for oob data
|
||||
for (struct oob *oob = _oobs; oob; oob = oob->next) {
|
||||
if ((unsigned)j == oob->len && memcmp(
|
||||
oob->prefix, _buffer+offset, oob->len) == 0) {
|
||||
debug_if(_dbg_on, "AT! %s\n", oob->prefix);
|
||||
oob->cb();
|
||||
|
||||
if (_aborted) {
|
||||
debug_if(_dbg_on, "AT(Aborted)\n");
|
||||
return false;
|
||||
}
|
||||
// oob may have corrupted non-reentrant buffer,
|
||||
// so we need to set it up again
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for match
|
||||
int count = -1;
|
||||
if (whole_line_wanted && c != '\n') {
|
||||
// Don't attempt scanning until we get delimiter if they included it in format
|
||||
// This allows recv("Foo: %s\n") to work, and not match with just the first character of a string
|
||||
// (scanf does not itself match whitespace in its format string, so \n is not significant to it)
|
||||
} else {
|
||||
sscanf(_buffer+offset, _buffer, &count);
|
||||
}
|
||||
|
||||
// We only succeed if all characters in the response are matched
|
||||
if (count == j) {
|
||||
debug_if(_dbg_on, "AT= %s\n", _buffer+offset);
|
||||
// Reuse the front end of the buffer
|
||||
memcpy(_buffer, response, i);
|
||||
_buffer[i] = 0;
|
||||
|
||||
// Store the found results
|
||||
vsscanf(_buffer+offset, _buffer, args);
|
||||
|
||||
// Jump to next line and continue parsing
|
||||
response += i;
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear the buffer when we hit a newline or ran out of space
|
||||
// running out of space usually means we ran into binary data
|
||||
if (c == '\n' || j+1 >= _buffer_size - offset) {
|
||||
debug_if(_dbg_on, "AT< %s", _buffer+offset);
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mapping to vararg functions
|
||||
int ATCmdParser::printf(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int res = vprintf(format, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
int ATCmdParser::scanf(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int res = vscanf(format, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ATCmdParser::send(const char *command, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
bool res = vsend(command, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ATCmdParser::recv(const char *response, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, response);
|
||||
bool res = vrecv(response, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
// oob registration
|
||||
void ATCmdParser::oob(const char *prefix, Callback<void()> cb)
|
||||
{
|
||||
struct oob *oob = new struct oob;
|
||||
oob->len = strlen(prefix);
|
||||
oob->prefix = prefix;
|
||||
oob->cb = cb;
|
||||
oob->next = _oobs;
|
||||
_oobs = oob;
|
||||
}
|
||||
|
||||
void ATCmdParser::abort()
|
||||
{
|
||||
_aborted = true;
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
/* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* Parser for the AT command syntax
|
||||
*
|
||||
*/
|
||||
#ifndef MBED_ATCMDPARSER_H
|
||||
#define MBED_ATCMDPARSER_H
|
||||
|
||||
#include "mbed.h"
|
||||
#include <cstdarg>
|
||||
#include "Callback.h"
|
||||
|
||||
/**
|
||||
* Parser class for parsing AT commands
|
||||
*
|
||||
* Here are some examples:
|
||||
* @code
|
||||
* ATCmdParser at = ATCmdParser(serial, "\r\n");
|
||||
* int value;
|
||||
* char buffer[100];
|
||||
*
|
||||
* at.send("AT") && at.recv("OK");
|
||||
* at.send("AT+CWMODE=%d", 3) && at.recv("OK");
|
||||
* at.send("AT+CWMODE?") && at.recv("+CWMODE:%d\r\nOK", &value);
|
||||
* at.recv("+IPD,%d:", &value);
|
||||
* at.read(buffer, value);
|
||||
* at.recv("OK");
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
namespace mbed {
|
||||
|
||||
class ATCmdParser
|
||||
{
|
||||
private:
|
||||
// File handle
|
||||
// Not owned by ATCmdParser
|
||||
FileHandle *_fh;
|
||||
|
||||
int _buffer_size;
|
||||
char *_buffer;
|
||||
int _timeout;
|
||||
|
||||
// Parsing information
|
||||
const char *_output_delimiter;
|
||||
int _output_delim_size;
|
||||
char _in_prev;
|
||||
bool _dbg_on;
|
||||
bool _aborted;
|
||||
|
||||
struct oob {
|
||||
unsigned len;
|
||||
const char *prefix;
|
||||
mbed::Callback<void()> cb;
|
||||
oob *next;
|
||||
};
|
||||
oob *_oobs;
|
||||
|
||||
// Prohibiting use of of copy constructor
|
||||
ATCmdParser(const ATCmdParser &);
|
||||
// Prohibiting copy assignment Operator
|
||||
ATCmdParser &operator=(const ATCmdParser &);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param serial serial interface to use for AT commands
|
||||
* @param buffer_size size of internal buffer for transaction
|
||||
* @param timeout timeout of the connection
|
||||
* @param debug turns on/off debug output for AT commands
|
||||
*/
|
||||
ATCmdParser(FileHandle *fh, const char *output_delimiter = "\r",
|
||||
int buffer_size = 256, int timeout = 8000, bool debug = false)
|
||||
: _fh(fh), _buffer_size(buffer_size), _in_prev(0), _oobs(NULL)
|
||||
{
|
||||
_buffer = new char[buffer_size];
|
||||
set_timeout(timeout);
|
||||
set_delimiter(output_delimiter);
|
||||
debug_on(debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~ATCmdParser()
|
||||
{
|
||||
while (_oobs) {
|
||||
struct oob *oob = _oobs;
|
||||
_oobs = oob->next;
|
||||
delete oob;
|
||||
}
|
||||
delete[] _buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows timeout to be changed between commands
|
||||
*
|
||||
* @param timeout timeout of the connection
|
||||
*/
|
||||
void set_timeout(int timeout)
|
||||
{
|
||||
_timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* Please use set_timeout(int) API only from now on.
|
||||
* Allows timeout to be changed between commands
|
||||
*
|
||||
* @param timeout timeout of the connection
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.5.0", "Replaced with set_timeout for consistency")
|
||||
void setTimeout(int timeout)
|
||||
{
|
||||
set_timeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets string of characters to use as line delimiters
|
||||
*
|
||||
* @param delimiter string of characters to use as line delimiters
|
||||
*/
|
||||
void set_delimiter(const char *output_delimiter)
|
||||
{
|
||||
_output_delimiter = output_delimiter;
|
||||
_output_delim_size = strlen(output_delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* Please use set_delimiter(const char *) API only from now on.
|
||||
* Sets string of characters to use as line delimiters
|
||||
*
|
||||
* @param delimiter string of characters to use as line delimiters
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.5.0", "Replaced with set_delimiter for consistency")
|
||||
void setDelimiter(const char *output_delimiter)
|
||||
{
|
||||
set_delimiter(output_delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows traces from modem to be turned on or off
|
||||
*
|
||||
* @param on set as 1 to turn on traces and vice versa.
|
||||
*/
|
||||
void debug_on(uint8_t on)
|
||||
{
|
||||
_dbg_on = (on) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* Allows traces from modem to be turned on or off
|
||||
*
|
||||
* @param on set as 1 to turn on traces and vice versa.
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.5.0", "Replaced with debug_on for consistency")
|
||||
void debugOn(uint8_t on)
|
||||
{
|
||||
debug_on(on);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an AT command
|
||||
*
|
||||
* Sends a formatted command using printf style formatting
|
||||
* @see ::printf
|
||||
*
|
||||
* @param command printf-like format string of command to send which
|
||||
* is appended with a newline
|
||||
* @param ... all printf-like arguments to insert into command
|
||||
* @return true only if command is successfully sent
|
||||
*/
|
||||
bool send(const char *command, ...) MBED_PRINTF_METHOD(1,2);
|
||||
|
||||
bool vsend(const char *command, va_list args);
|
||||
|
||||
/**
|
||||
* Receive an AT response
|
||||
*
|
||||
* Receives a formatted response using scanf style formatting
|
||||
* @see ::scanf
|
||||
*
|
||||
* Responses are parsed line at a time.
|
||||
* Any received data that does not match the response is ignored until
|
||||
* a timeout occurs.
|
||||
*
|
||||
* @param response scanf-like format string of response to expect
|
||||
* @param ... all scanf-like arguments to extract from response
|
||||
* @return true only if response is successfully matched
|
||||
*/
|
||||
bool recv(const char *response, ...) MBED_SCANF_METHOD(1,2);
|
||||
|
||||
bool vrecv(const char *response, va_list args);
|
||||
|
||||
/**
|
||||
* Write a single byte to the underlying stream
|
||||
*
|
||||
* @param c The byte to write
|
||||
* @return The byte that was written or -1 during a timeout
|
||||
*/
|
||||
int putc(char c);
|
||||
|
||||
/**
|
||||
* Get a single byte from the underlying stream
|
||||
*
|
||||
* @return The byte that was read or -1 during a timeout
|
||||
*/
|
||||
int getc();
|
||||
|
||||
/**
|
||||
* Write an array of bytes to the underlying stream
|
||||
*
|
||||
* @param data the array of bytes to write
|
||||
* @param size number of bytes to write
|
||||
* @return number of bytes written or -1 on failure
|
||||
*/
|
||||
int write(const char *data, int size);
|
||||
|
||||
/**
|
||||
* Read an array of bytes from the underlying stream
|
||||
*
|
||||
* @param data the destination for the read bytes
|
||||
* @param size number of bytes to read
|
||||
* @return number of bytes read or -1 on failure
|
||||
*/
|
||||
int read(char *data, int size);
|
||||
|
||||
/**
|
||||
* Direct printf to underlying stream
|
||||
* @see ::printf
|
||||
*
|
||||
* @param format format string to pass to printf
|
||||
* @param ... arguments to printf
|
||||
* @return number of bytes written or -1 on failure
|
||||
*/
|
||||
int printf(const char *format, ...) MBED_PRINTF_METHOD(1,2);
|
||||
|
||||
int vprintf(const char *format, va_list args);
|
||||
|
||||
/**
|
||||
* Direct scanf on underlying stream
|
||||
* @see ::scanf
|
||||
*
|
||||
* @param format format string to pass to scanf
|
||||
* @param ... arguments to scanf
|
||||
* @return number of bytes read or -1 on failure
|
||||
*/
|
||||
int scanf(const char *format, ...) MBED_SCANF_METHOD(1,2);
|
||||
|
||||
int vscanf(const char *format, va_list args);
|
||||
|
||||
/**
|
||||
* Attach a callback for out-of-band data
|
||||
*
|
||||
* @param prefix string on when to initiate callback
|
||||
* @param func callback to call when string is read
|
||||
* @note out-of-band data is only processed during a scanf call
|
||||
*/
|
||||
void oob(const char *prefix, mbed::Callback<void()> func);
|
||||
|
||||
/**
|
||||
* Flushes the underlying stream
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* Abort current recv
|
||||
*
|
||||
* Can be called from oob handler to interrupt the current
|
||||
* recv operation.
|
||||
*/
|
||||
void abort();
|
||||
};
|
||||
} //namespace mbed
|
||||
|
||||
#endif //MBED_ATCMDPARSER_H
|
|
@ -76,7 +76,7 @@ public:
|
|||
*
|
||||
* @return True if the buffer is empty, false if not
|
||||
*/
|
||||
bool empty() {
|
||||
bool empty() const {
|
||||
core_util_critical_section_enter();
|
||||
bool is_empty = (_head == _tail) && !_full;
|
||||
core_util_critical_section_exit();
|
||||
|
@ -87,7 +87,7 @@ public:
|
|||
*
|
||||
* @return True if the buffer is full, false if not
|
||||
*/
|
||||
bool full() {
|
||||
bool full() const {
|
||||
core_util_critical_section_enter();
|
||||
bool full = _full;
|
||||
core_util_critical_section_exit();
|
||||
|
|
|
@ -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
|
|
@ -1,5 +1,5 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2013 ARM Limited
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,7 +18,9 @@
|
|||
|
||||
typedef int FILEHANDLE;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include "Callback.h"
|
||||
#include "platform/mbed_poll.h"
|
||||
#include "platform/platform.h"
|
||||
|
||||
namespace mbed {
|
||||
|
@ -40,6 +42,12 @@ public:
|
|||
virtual ~FileHandle() {}
|
||||
|
||||
/** Read the contents of a file into a buffer
|
||||
*
|
||||
* Devices acting as FileHandles should follow POSIX semantics:
|
||||
*
|
||||
* * if no data is available, and non-blocking set return -EAGAIN
|
||||
* * if no data is available, and blocking set, wait until data is available
|
||||
* * If any data is available, call returns immediately
|
||||
*
|
||||
* @param buffer The buffer to read in to
|
||||
* @param size The number of bytes to read
|
||||
|
@ -62,7 +70,7 @@ public:
|
|||
* SEEK_SET to start from beginning of file,
|
||||
* SEEK_CUR to start from current position in file,
|
||||
* SEEK_END to start from end of file
|
||||
* @return The new offset of the file
|
||||
* @return The new offset of the file, negative error code on failure
|
||||
*/
|
||||
virtual off_t seek(off_t offset, int whence = SEEK_SET) = 0;
|
||||
|
||||
|
@ -84,6 +92,8 @@ public:
|
|||
/** Check if the file in an interactive terminal device
|
||||
*
|
||||
* @return True if the file is a terminal
|
||||
* @return False if the file is not a terminal
|
||||
* @return Negative error code on failure
|
||||
*/
|
||||
virtual int isatty()
|
||||
{
|
||||
|
@ -94,7 +104,7 @@ public:
|
|||
*
|
||||
* @note This is equivalent to seek(0, SEEK_CUR)
|
||||
*
|
||||
* @return The current offset in the file
|
||||
* @return The current offset in the file, negative error code on failure
|
||||
*/
|
||||
virtual off_t tell()
|
||||
{
|
||||
|
@ -114,13 +124,7 @@ public:
|
|||
*
|
||||
* @return Size of the file in bytes
|
||||
*/
|
||||
virtual size_t size()
|
||||
{
|
||||
off_t off = tell();
|
||||
size_t size = seek(0, SEEK_END);
|
||||
seek(off, SEEK_SET);
|
||||
return size;
|
||||
}
|
||||
virtual off_t size();
|
||||
|
||||
/** Move the file position to a given offset from a given location.
|
||||
*
|
||||
|
@ -133,7 +137,10 @@ public:
|
|||
* -1 on failure or unsupported
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.4", "Replaced by FileHandle::seek")
|
||||
virtual off_t lseek(off_t offset, int whence) { return seek(offset, whence); }
|
||||
virtual off_t lseek(off_t offset, int whence)
|
||||
{
|
||||
return seek(offset, whence);
|
||||
}
|
||||
|
||||
/** Flush any buffers associated with the FileHandle, ensuring it
|
||||
* is up to date on disk
|
||||
|
@ -143,7 +150,10 @@ public:
|
|||
* -1 on error
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.4", "Replaced by FileHandle::sync")
|
||||
virtual int fsync() { return sync(); }
|
||||
virtual int fsync()
|
||||
{
|
||||
return sync();
|
||||
}
|
||||
|
||||
/** Find the length of the file
|
||||
*
|
||||
|
@ -151,9 +161,91 @@ public:
|
|||
* Length of the file
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.4", "Replaced by FileHandle::size")
|
||||
virtual off_t flen() { return size(); }
|
||||
virtual off_t flen()
|
||||
{
|
||||
return size();
|
||||
}
|
||||
|
||||
/** Set blocking or non-blocking mode of the file operation like read/write.
|
||||
* Definition depends upon the subclass implementing FileHandle.
|
||||
* The default is blocking.
|
||||
*
|
||||
* @param blocking true for blocking mode, false for non-blocking mode.
|
||||
*/
|
||||
virtual int set_blocking(bool blocking)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Check for poll event flags
|
||||
* The input parameter can be used or ignored - the could always return all events,
|
||||
* or could check just the events listed in events.
|
||||
* Call is non-blocking - returns instantaneous state of events.
|
||||
* Whenever an event occurs, the derived class should call the sigio() callback).
|
||||
*
|
||||
* @param events bitmask of poll events we're interested in - POLLIN/POLLOUT etc.
|
||||
*
|
||||
* @returns
|
||||
* bitmask of poll events that have occurred.
|
||||
*/
|
||||
virtual short poll(short events) const
|
||||
{
|
||||
// Possible default for real files
|
||||
return POLLIN | POLLOUT;
|
||||
}
|
||||
|
||||
/** Returns true if the FileHandle is writable.
|
||||
* Definition depends upon the subclass implementing FileHandle.
|
||||
* For example, if the FileHandle is of type Stream, writable() could return
|
||||
* true when there is ample buffer space available for write() calls.
|
||||
*/
|
||||
bool writable() const
|
||||
{
|
||||
return poll(POLLOUT) & POLLOUT;
|
||||
}
|
||||
|
||||
/** Returns true if the FileHandle is readable.
|
||||
* Definition depends upon the subclass implementing FileHandle.
|
||||
* For example, if the FileHandle is of type Stream, readable() could return
|
||||
* true when there is something available to read.
|
||||
*/
|
||||
bool readable() const
|
||||
{
|
||||
return poll(POLLIN) & POLLIN;
|
||||
}
|
||||
|
||||
/** Register a callback on state change of the file.
|
||||
*
|
||||
* The specified callback will be called on state changes such as when
|
||||
* the file can be written to or read from.
|
||||
*
|
||||
* The callback may be called in an interrupt context and should not
|
||||
* perform expensive operations.
|
||||
*
|
||||
* Note! This is not intended as an attach-like asynchronous api, but rather
|
||||
* as a building block for constructing such functionality.
|
||||
*
|
||||
* The exact timing of when the registered function
|
||||
* is called is not guaranteed and susceptible to change. It should be used
|
||||
* as a cue to make read/write/poll calls to find the current state.
|
||||
*
|
||||
* @param func Function to call on state change
|
||||
*/
|
||||
virtual void sigio(Callback<void()> func)
|
||||
{
|
||||
//Default for real files. Do nothing for real files.
|
||||
}
|
||||
};
|
||||
|
||||
/** Not a member function
|
||||
* This call is equivalent to posix fdopen().
|
||||
* Returns a pointer to std::FILE.
|
||||
* It associates a Stream to an already opened file descriptor (FileHandle)
|
||||
*
|
||||
* @param fh, a pointer to an opened file descriptor
|
||||
* @param mode, operation upon the file descriptor, e.g., 'wb+'*/
|
||||
|
||||
std::FILE *fdopen(FileHandle *fh, const char *mode);
|
||||
|
||||
} // namespace mbed
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
|
||||
virtual int sync();
|
||||
|
||||
virtual size_t size();
|
||||
virtual off_t size();
|
||||
|
||||
protected:
|
||||
virtual void lock();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,15 +19,16 @@
|
|||
#include "platform/platform.h"
|
||||
#include "platform/FileLike.h"
|
||||
#include "platform/FileHandle.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
|
||||
namespace mbed {
|
||||
/** \addtogroup platform */
|
||||
/** @{*/
|
||||
|
||||
extern void mbed_set_unbuffered_stream(FILE *_file);
|
||||
extern int mbed_getc(FILE *_file);
|
||||
extern char* mbed_gets(char *s, int size, FILE *_file);
|
||||
extern void mbed_set_unbuffered_stream(std::FILE *_file);
|
||||
extern int mbed_getc(std::FILE *_file);
|
||||
extern char* mbed_gets(char *s, int size, std::FILE *_file);
|
||||
/** @}*/
|
||||
|
||||
/** File stream
|
||||
|
@ -61,7 +62,7 @@ protected:
|
|||
virtual void rewind();
|
||||
virtual int isatty();
|
||||
virtual int sync();
|
||||
virtual size_t size();
|
||||
virtual off_t size();
|
||||
|
||||
virtual int _putc(int c) = 0;
|
||||
virtual int _getc() = 0;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -30,6 +30,7 @@
|
|||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#if DEVICE_STDIO_MESSAGES
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
@ -120,6 +121,20 @@ static void init_serial() {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets errno when file opening fails.
|
||||
* Wipes out the filehandle too.
|
||||
*
|
||||
* @param error is a negative error code returned from an mbed function and
|
||||
* will be negated to store a positive error code in errno
|
||||
*/
|
||||
static int handle_open_errors(int error, unsigned filehandle_idx) {
|
||||
errno = -error;
|
||||
// Free file handle
|
||||
filehandles[filehandle_idx] = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if MBED_CONF_FILESYSTEM_PRESENT
|
||||
static inline int openmode_to_posix(int openmode) {
|
||||
int posix = openmode;
|
||||
|
@ -189,7 +204,7 @@ public:
|
|||
* @return
|
||||
* On success, a valid FILEHANDLE is returned.
|
||||
* On failure, -1 is returned and errno is set to an appropriate value e.g.
|
||||
* EBADF a bad file descriptor was found (default errno setting)
|
||||
* ENOENT file not found (default errno setting)
|
||||
* EMFILE the maximum number of open files was exceeded.
|
||||
*
|
||||
* */
|
||||
|
@ -219,9 +234,6 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
|
|||
}
|
||||
#endif
|
||||
|
||||
/* if something goes wrong and errno is not explicly set, errno will be set to EBADF */
|
||||
errno = EBADF;
|
||||
|
||||
// find the first empty slot in filehandles
|
||||
filehandle_mutex->lock();
|
||||
unsigned int fh_i;
|
||||
|
@ -243,7 +255,7 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
|
|||
/* FILENAME: ":0x12345678" describes a FileHandle* */
|
||||
if (name[0] == ':') {
|
||||
void *p;
|
||||
sscanf(name, ":%p", &p);
|
||||
std::sscanf(name, ":%p", &p);
|
||||
res = (FileHandle*)p;
|
||||
|
||||
/* FILENAME: "/file_system/file_name" */
|
||||
|
@ -253,41 +265,30 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
|
|||
if (!path.exists()) {
|
||||
/* The first part of the filename (between first 2 '/') is not a
|
||||
* registered mount point in the namespace.
|
||||
* Free file handle.
|
||||
*/
|
||||
filehandles[fh_i] = NULL;
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
} else if (path.isFile()) {
|
||||
return handle_open_errors(-ENOENT, fh_i);
|
||||
}
|
||||
|
||||
if (path.isFile()) {
|
||||
res = path.file();
|
||||
#if MBED_CONF_FILESYSTEM_PRESENT
|
||||
} else {
|
||||
FileSystem *fs = path.fileSystem();
|
||||
if (fs == NULL) {
|
||||
/* The filesystem instance managing the namespace under the mount point
|
||||
* has not been found. Free file handle */
|
||||
errno = ENOENT;
|
||||
filehandles[fh_i] = NULL;
|
||||
return -1;
|
||||
return handle_open_errors(-ENOENT, fh_i);
|
||||
}
|
||||
int posix_mode = openmode_to_posix(openmode);
|
||||
File *file = new ManagedFile;
|
||||
int err = file->open(fs, path.fileName(), posix_mode);
|
||||
if (err < 0) {
|
||||
errno = -err;
|
||||
delete file;
|
||||
} else {
|
||||
res = file;
|
||||
return handle_open_errors(err, fh_i);
|
||||
}
|
||||
res = file;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (res == NULL) {
|
||||
// Free file handle
|
||||
filehandles[fh_i] = NULL;
|
||||
return -1;
|
||||
}
|
||||
filehandles[fh_i] = res;
|
||||
|
||||
return fh_i + 3; // +3 as filehandles 0-2 are stdin/out/err
|
||||
|
@ -296,10 +297,12 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
|
|||
extern "C" int PREFIX(_close)(FILEHANDLE fh) {
|
||||
if (fh < 3) return 0;
|
||||
|
||||
errno = EBADF;
|
||||
FileHandle* fhc = filehandles[fh-3];
|
||||
filehandles[fh-3] = NULL;
|
||||
if (fhc == NULL) return -1;
|
||||
if (fhc == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = fhc->close();
|
||||
if (err < 0) {
|
||||
|
@ -317,7 +320,6 @@ extern "C" int PREFIX(_write)(FILEHANDLE fh, const unsigned char *buffer, unsign
|
|||
#endif
|
||||
int n; // n is the number of bytes written
|
||||
|
||||
errno = EBADF;
|
||||
if (fh < 3) {
|
||||
#if DEVICE_SERIAL
|
||||
if (!stdio_uart_inited) init_serial();
|
||||
|
@ -338,7 +340,10 @@ extern "C" int PREFIX(_write)(FILEHANDLE fh, const unsigned char *buffer, unsign
|
|||
n = length;
|
||||
} else {
|
||||
FileHandle* fhc = filehandles[fh-3];
|
||||
if (fhc == NULL) return -1;
|
||||
if (fhc == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = fhc->write(buffer, length);
|
||||
if (n < 0) {
|
||||
|
@ -359,7 +364,6 @@ extern "C" int PREFIX(_read)(FILEHANDLE fh, unsigned char *buffer, unsigned int
|
|||
#endif
|
||||
int n; // n is the number of bytes read
|
||||
|
||||
errno = EBADF;
|
||||
if (fh < 3) {
|
||||
// only read a character at a time from stdin
|
||||
#if DEVICE_SERIAL
|
||||
|
@ -390,7 +394,10 @@ extern "C" int PREFIX(_read)(FILEHANDLE fh, unsigned char *buffer, unsigned int
|
|||
n = 1;
|
||||
} else {
|
||||
FileHandle* fhc = filehandles[fh-3];
|
||||
if (fhc == NULL) return -1;
|
||||
if (fhc == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = fhc->read(buffer, length);
|
||||
if (n < 0) {
|
||||
|
@ -404,57 +411,76 @@ extern "C" int PREFIX(_read)(FILEHANDLE fh, unsigned char *buffer, unsigned int
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef __ARMCC_VERSION
|
||||
extern "C" int PREFIX(_istty)(FILEHANDLE fh)
|
||||
#else
|
||||
extern "C" int _isatty(FILEHANDLE fh)
|
||||
#endif
|
||||
{
|
||||
errno = EBADF;
|
||||
/* stdin, stdout and stderr should be tty */
|
||||
if (fh < 3) return 1;
|
||||
|
||||
FileHandle* fhc = filehandles[fh-3];
|
||||
if (fhc == NULL) return -1;
|
||||
|
||||
int err = fhc->isatty();
|
||||
if (err < 0) {
|
||||
errno = -err;
|
||||
return -1;
|
||||
} else {
|
||||
if (fhc == NULL) {
|
||||
errno = EBADF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tty = fhc->isatty();
|
||||
if (tty < 0) {
|
||||
errno = -tty;
|
||||
return 0;
|
||||
} else {
|
||||
return tty;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
#if defined(__ARMCC_VERSION)
|
||||
int _sys_seek(FILEHANDLE fh, long position)
|
||||
int _sys_seek(FILEHANDLE fh, long offset)
|
||||
#elif defined(__ICCARM__)
|
||||
long __lseek(int fh, long offset, int whence)
|
||||
#else
|
||||
int _lseek(FILEHANDLE fh, int offset, int whence)
|
||||
#endif
|
||||
{
|
||||
errno = EBADF;
|
||||
if (fh < 3) return 0;
|
||||
#if defined(__ARMCC_VERSION)
|
||||
int whence = SEEK_SET;
|
||||
#endif
|
||||
if (fh < 3) {
|
||||
errno = ESPIPE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FileHandle* fhc = filehandles[fh-3];
|
||||
if (fhc == NULL) return -1;
|
||||
if (fhc == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(__ARMCC_VERSION)
|
||||
return fhc->seek(position, SEEK_SET);
|
||||
#else
|
||||
return fhc->seek(offset, whence);
|
||||
#endif
|
||||
off_t off = fhc->seek(offset, whence);
|
||||
if (off < 0) {
|
||||
errno = -off;
|
||||
return -1;
|
||||
}
|
||||
// Assuming INT_MAX = LONG_MAX, so we don't care about prototype difference
|
||||
if (off > INT_MAX) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
#ifdef __ARMCC_VERSION
|
||||
extern "C" int PREFIX(_ensure)(FILEHANDLE fh) {
|
||||
errno = EBADF;
|
||||
if (fh < 3) return 0;
|
||||
|
||||
FileHandle* fhc = filehandles[fh-3];
|
||||
if (fhc == NULL) return -1;
|
||||
if (fhc == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = fhc->sync();
|
||||
if (err < 0) {
|
||||
|
@ -466,13 +492,27 @@ extern "C" int PREFIX(_ensure)(FILEHANDLE fh) {
|
|||
}
|
||||
|
||||
extern "C" long PREFIX(_flen)(FILEHANDLE fh) {
|
||||
errno = EBADF;
|
||||
if (fh < 3) return 0;
|
||||
if (fh < 3) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FileHandle* fhc = filehandles[fh-3];
|
||||
if (fhc == NULL) return -1;
|
||||
if (fhc == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fhc->size();
|
||||
off_t size = fhc->size();
|
||||
if (size < 0) {
|
||||
errno = -size;
|
||||
return -1;
|
||||
}
|
||||
if (size > LONG_MAX) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -491,10 +531,12 @@ extern "C" int _fstat(int fd, struct stat *st) {
|
|||
namespace std {
|
||||
extern "C" int remove(const char *path) {
|
||||
#if MBED_CONF_FILESYSTEM_PRESENT
|
||||
errno = EBADF;
|
||||
FilePath fp(path);
|
||||
FileSystem *fs = fp.fileSystem();
|
||||
if (fs == NULL) return -1;
|
||||
if (fs == NULL) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = fs->remove(fp.fileName());
|
||||
if (err < 0) {
|
||||
|
@ -511,14 +553,21 @@ extern "C" int remove(const char *path) {
|
|||
|
||||
extern "C" int rename(const char *oldname, const char *newname) {
|
||||
#if MBED_CONF_FILESYSTEM_PRESENT
|
||||
errno = EBADF;
|
||||
FilePath fpOld(oldname);
|
||||
FilePath fpNew(newname);
|
||||
FileSystem *fsOld = fpOld.fileSystem();
|
||||
FileSystem *fsNew = fpNew.fileSystem();
|
||||
|
||||
if (fsOld == NULL) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* rename only if both files are on the same FS */
|
||||
if (fsOld != fsNew || fsOld == NULL) return -1;
|
||||
if (fsOld != fsNew) {
|
||||
errno = EXDEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = fsOld->rename(fpOld.fileName(), fpNew.fileName());
|
||||
if (err < 0) {
|
||||
|
@ -552,11 +601,12 @@ extern "C" char *_sys_command_string(char *cmd, int len) {
|
|||
|
||||
extern "C" DIR *opendir(const char *path) {
|
||||
#if MBED_CONF_FILESYSTEM_PRESENT
|
||||
errno = EBADF;
|
||||
|
||||
FilePath fp(path);
|
||||
FileSystem* fs = fp.fileSystem();
|
||||
if (fs == NULL) return NULL;
|
||||
if (fs == NULL) {
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Dir *dir = new ManagedDir;
|
||||
int err = dir->open(fs, fp.fileName());
|
||||
|
@ -894,16 +944,34 @@ int __wrap_atexit(void (*func)()) {
|
|||
|
||||
namespace mbed {
|
||||
|
||||
void mbed_set_unbuffered_stream(FILE *_file) {
|
||||
void mbed_set_unbuffered_stream(std::FILE *_file) {
|
||||
#if defined (__ICCARM__)
|
||||
char buf[2];
|
||||
std::setvbuf(_file,buf,_IONBF,NULL);
|
||||
std::setvbuf(_file,buf,_IONBF,NULL);
|
||||
#else
|
||||
setbuf(_file, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
int mbed_getc(FILE *_file){
|
||||
/* Applications are expected to use fdopen()
|
||||
* not this function directly. This code had to live here because FILE and FileHandle
|
||||
* processes are all linked together here.
|
||||
*/
|
||||
std::FILE *mbed_fdopen(FileHandle *fh, const char *mode)
|
||||
{
|
||||
char buf[12]; /* :0x12345678 + null byte */
|
||||
std::sprintf(buf, ":%p", fh);
|
||||
std::FILE *stream = std::fopen(buf, mode);
|
||||
/* newlib-nano doesn't appear to ever call _isatty itself, so
|
||||
* happily fully buffers an interactive stream. Deal with that here.
|
||||
*/
|
||||
if (stream && fh->isatty()) {
|
||||
mbed_set_unbuffered_stream(stream);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
int mbed_getc(std::FILE *_file){
|
||||
#if defined (__ICCARM__)
|
||||
/*This is only valid for unbuffered streams*/
|
||||
int res = std::fgetc(_file);
|
||||
|
@ -918,7 +986,7 @@ int mbed_getc(FILE *_file){
|
|||
#endif
|
||||
}
|
||||
|
||||
char* mbed_gets(char*s, int size, FILE *_file){
|
||||
char* mbed_gets(char*s, int size, std::FILE *_file){
|
||||
#if defined (__ICCARM__)
|
||||
/*This is only valid for unbuffered streams*/
|
||||
char *str = fgets(s,size,_file);
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
#ifndef RETARGET_H
|
||||
#define RETARGET_H
|
||||
|
||||
#if __cplusplus
|
||||
#include <cstdio>
|
||||
#endif //__cplusplus
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
@ -48,7 +51,11 @@ typedef int mode_t; ///< Mode for opening files
|
|||
|
||||
/* DIR declarations must also be here */
|
||||
#if __cplusplus
|
||||
namespace mbed { class Dir; }
|
||||
namespace mbed {
|
||||
class Dir;
|
||||
class FileHandle;
|
||||
std::FILE *mbed_fdopen(FileHandle *fh, const char *mode);
|
||||
}
|
||||
typedef mbed::Dir DIR;
|
||||
#else
|
||||
typedef struct Dir DIR;
|
||||
|
@ -77,71 +84,60 @@ extern "C" {
|
|||
* Note also that ARMCC errno.h defines some symbol values differently from
|
||||
* the GCC_ARM/IAR/standard POSIX definitions. The definitions guard against
|
||||
* this and future changes by changing the symbol definition as shown below. */
|
||||
#ifdef ENOENT
|
||||
#undef ENOENT
|
||||
#endif
|
||||
#define ENOENT 2 /* No such file or directory. */
|
||||
|
||||
#ifdef EIO
|
||||
#undef EIO
|
||||
#endif
|
||||
#define EIO 5 /* I/O error */
|
||||
|
||||
#ifdef ENXIO
|
||||
#undef ENXIO
|
||||
#endif
|
||||
#define ENXIO 6 /* No such device or address */
|
||||
|
||||
#ifdef ENOEXEC
|
||||
#undef ENOEXEC
|
||||
#endif
|
||||
#define ENOEXEC 8 /* Exec format error */
|
||||
|
||||
#ifdef EBADF
|
||||
#undef EBADF
|
||||
#endif
|
||||
#define EBADF 9 /* Bad file number */
|
||||
|
||||
#ifdef ENOMEM
|
||||
#undef EAGAIN
|
||||
#define EAGAIN 11 /* Resource unavailable, try again */
|
||||
|
||||
#undef EWOULDBLOCK
|
||||
#define EWOULDBLOCK EAGAIN /* Operation would block */
|
||||
|
||||
#undef ENOMEM
|
||||
#endif
|
||||
#define ENOMEM 12 /* Not enough space */
|
||||
|
||||
#ifdef EACCES
|
||||
#undef EACCES
|
||||
#endif
|
||||
#define EACCES 13 /* Permission denied */
|
||||
|
||||
#ifdef EFAULT
|
||||
#undef EFAULT
|
||||
#endif
|
||||
#define EFAULT 14 /* Bad address */
|
||||
|
||||
#ifdef EEXIST
|
||||
#undef EEXIST
|
||||
#endif
|
||||
#define EEXIST 17 /* File exists */
|
||||
|
||||
#ifdef EINVAL
|
||||
#undef EXDEV
|
||||
#define EXDEV 18 /* Cross-device link */
|
||||
|
||||
#undef EINVAL
|
||||
#endif
|
||||
#define EINVAL 22 /* Invalid argument */
|
||||
|
||||
#ifdef ENFILE
|
||||
#undef ENFILE
|
||||
#endif
|
||||
#define ENFILE 23 /* Too many open files in system */
|
||||
|
||||
#ifdef EMFILE
|
||||
#undef EMFILE
|
||||
#endif
|
||||
#define EMFILE 24 /* File descriptor value too large */
|
||||
|
||||
#ifdef ENOSYS
|
||||
#undef ESPIPE
|
||||
#define ESPIPE 29 /* Invalid seek */
|
||||
|
||||
#undef ENOSYS
|
||||
#endif
|
||||
#define ENOSYS 38 /* Function not implemented */
|
||||
|
||||
#undef EOVERFLOW
|
||||
#define EOVERFLOW 75 /* Value too large to be stored in data type */
|
||||
|
||||
/* Missing stat.h defines.
|
||||
* The following are sys/stat.h definitions not currently present in the ARMCC
|
||||
* errno.h. Note, ARMCC errno.h defines some symbol values differing from
|
||||
|
@ -187,5 +183,4 @@ enum {
|
|||
DT_SOCK, // This is a UNIX domain socket.
|
||||
};
|
||||
|
||||
|
||||
#endif /* RETARGET_H */
|
||||
|
|
|
@ -278,6 +278,38 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MBED_PRINTF
|
||||
#if defined(__GNUC__) || defined(__CC_ARM)
|
||||
#define MBED_PRINTF(format_idx, first_param_idx) __attribute__ ((__format__(__printf__, format_idx, first_param_idx)))
|
||||
#else
|
||||
#define MBED_PRINTF(format_idx, first_param_idx)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MBED_PRINTF_METHOD
|
||||
#if defined(__GNUC__) || defined(__CC_ARM)
|
||||
#define MBED_PRINTF_METHOD(format_idx, first_param_idx) __attribute__ ((__format__(__printf__, format_idx+1, first_param_idx+1)))
|
||||
#else
|
||||
#define MBED_PRINTF_METHOD(format_idx, first_param_idx)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MBED_SCANF
|
||||
#if defined(__GNUC__) || defined(__CC_ARM)
|
||||
#define MBED_SCANF(format_idx, first_param_idx) __attribute__ ((__format__(__scanf__, format_idx, first_param_idx)))
|
||||
#else
|
||||
#define MBED_SCANF(format_idx, first_param_idx)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MBED_SCANF_METHOD
|
||||
#if defined(__GNUC__) || defined(__CC_ARM)
|
||||
#define MBED_SCANF_METHOD(format_idx, first_param_idx) __attribute__ ((__format__(__scanf__, format_idx+1, first_param_idx+1)))
|
||||
#else
|
||||
#define MBED_SCANF_METHOD(format_idx, first_param_idx)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// FILEHANDLE declaration
|
||||
#if defined(TOOLCHAIN_ARM)
|
||||
#include <rt_sys.h>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,97 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include "hal/us_ticker_api.h"
|
||||
#include "platform/mbed_wait_api.h"
|
||||
#include "gpio_api.h"
|
||||
#include "ublox_low_level_api.h"
|
||||
|
||||
static bool modemOn;
|
||||
static bool gpsOn;
|
||||
|
||||
void ublox_mdm_init(void)
|
||||
{
|
||||
gpio_t gpio;
|
||||
// start with modem disabled
|
||||
gpio_init_out_ex(&gpio, MDMEN, 0);
|
||||
gpio_init_out_ex(&gpio, MDMRST, 1);
|
||||
gpio_init_out_ex(&gpio, MDMPWRON, 1);
|
||||
gpio_init_out_ex(&gpio, MDMLVLOE, 1); // LVLEN: 1=disabled
|
||||
gpio_init_out_ex(&gpio, MDMILVLOE, 0); // ILVLEN: 0=disabled
|
||||
gpio_init_out_ex(&gpio, MDMUSBDET, 0);
|
||||
gpio_init_out_ex(&gpio, MDMRTS, 0);
|
||||
// start with gps disabled
|
||||
gpio_init_out_ex(&gpio, GPSEN, 0);
|
||||
gpio_init_out_ex(&gpio, GPSRST, 1);
|
||||
// led should be off
|
||||
gpio_init_out_ex(&gpio, LED, 0);
|
||||
|
||||
// Can't use wait_ms() as RTOS isn't initialised yet
|
||||
//wait_ms(50); // when USB cable is inserted the interface chip issues
|
||||
// Here's the code from the non-RTOS version
|
||||
uint32_t start = us_ticker_read();
|
||||
while ((us_ticker_read() - start) < 50000);
|
||||
}
|
||||
|
||||
void ublox_mdm_powerOn(int usb)
|
||||
{
|
||||
gpio_t gpio;
|
||||
// turn on the mode by enabling power with power on pin low and correct USB detect level
|
||||
gpio_init_out_ex(&gpio, MDMUSBDET, usb ? 1 : 0); // USBDET: 0=disabled, 1=enabled
|
||||
if (!modemOn) { // enable modem
|
||||
gpio_init_out_ex(&gpio, MDMEN, 1); // LDOEN: 1=on
|
||||
wait_ms(1); // wait until supply switched off
|
||||
// now we can safely enable the level shifters
|
||||
gpio_init_out_ex(&gpio, MDMLVLOE, 0); // LVLEN: 0=enabled (uart/gpio)
|
||||
if (gpsOn)
|
||||
gpio_init_out_ex(&gpio, MDMILVLOE, 1); // ILVLEN: 1=enabled (i2c)
|
||||
}
|
||||
}
|
||||
|
||||
void ublox_mdm_powerOff(void)
|
||||
{
|
||||
gpio_t gpio;
|
||||
if (modemOn) {
|
||||
// diable all level shifters
|
||||
gpio_init_out_ex(&gpio, MDMILVLOE, 0); // ILVLEN: 0=disabled (i2c)
|
||||
gpio_init_out_ex(&gpio, MDMLVLOE, 1); // LVLEN: 1=disabled (uart/gpio)
|
||||
gpio_init_out_ex(&gpio,MDMUSBDET, 0); // USBDET: 0=disabled
|
||||
// now we can savely switch off the ldo
|
||||
gpio_init_out_ex(&gpio, MDMEN, 0); // LDOEN: 0=off
|
||||
modemOn = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ublox_gps_powerOn(void)
|
||||
{
|
||||
gpio_t gpio;
|
||||
if (!gpsOn) {
|
||||
// switch on power supply
|
||||
gpio_init_out_ex(&gpio, GPSEN, 1); // LDOEN: 1=on
|
||||
wait_ms(1); // wait until supply switched off
|
||||
if (modemOn)
|
||||
gpio_init_out_ex(&gpio, MDMILVLOE, 1); // ILVLEN: 1=enabled (i2c)
|
||||
}
|
||||
}
|
||||
|
||||
void ublox_gps_powerOff(void)
|
||||
{
|
||||
gpio_t gpio;
|
||||
if (gpsOn) {
|
||||
gpio_init_out_ex(&gpio, MDMILVLOE, 0); // ILVLEN: 0=disabled (i2c)
|
||||
gpio_init_out_ex(&gpio, GPSEN, 0); // LDOEN: 0=off
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -71,6 +71,10 @@ struct spi_s {
|
|||
LPC_SSP_TypeDef *spi;
|
||||
};
|
||||
|
||||
struct modem_s {
|
||||
uint32_t state;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue