Skip to content

Commit

Permalink
Cellular PPP change only: PPP over different port.
Browse files Browse the repository at this point in the history
The current implementation of PPP runs over CMUX, ensuring that AT commands and GNSS commands can continue while the PPP link is running.  However, u-blox cellular modules do not support use of CMUX on USB connections; one is intended to use different USB end-points for PPP, AT and GNSS traffic.  This is a problem when using a u-blox module [one that supports AT commands over USB] and only a USB connection is available to the MCU.

With this commit, support is provided for running the PPP interface over a different port to that of the AT interface, which could be a USB end-point, and hence a PPP connection can now be made when only a USB connection to the module is available; see examplePppLinuxSockets() for how to do it.
  • Loading branch information
RobMeades committed Apr 10, 2024
1 parent 8ebf330 commit d251066
Show file tree
Hide file tree
Showing 45 changed files with 903 additions and 252 deletions.
79 changes: 79 additions & 0 deletions cell/api/u_cell_ppp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2019-2024 u-blox
*
* 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 _U_CELL_PPP_H_
#define _U_CELL_PPP_H_

/* Only header files representing a direct and unavoidable
* dependency between the API of this module and the API
* of another module should be included here; otherwise
* please keep #includes to your .c files. */

/** @file
* @brief This header file defines a small number of public functions
* to do with the operation of PPP. USUALLY YOU NEED NONE OF THESE:
* the cellular PPP interface functions are deliberately kept within
* the cellular source code as they are called automagically when a
* cellular connection is brought up or taken down, the application
* does not need to know about them.
*/

#ifdef __cplusplus
extern "C" {
#endif

/* ----------------------------------------------------------------
* COMPILE-TIME MACROS
* -------------------------------------------------------------- */

/* ----------------------------------------------------------------
* TYPES
* -------------------------------------------------------------- */

/* ----------------------------------------------------------------
* FUNCTIONS
* -------------------------------------------------------------- */

/** Provide a serial device that will be used when a PPP connection
* is opened. YOU DO NOT NORMALLY NEED TO USE THIS FUNCTION; the
* serial port on which the existing AT interface is running will
* normally be used for PPP, via the CMUX protocol.
*
* This function is useful if your AT interface is actually the
* USB interface of the cellular module, which does not support
* the CMUX protocol; with this function you can open a virtual
* serial device on another USB end-point and that will be used
* for PPP. This is done automatically for you if you use
* uNetworkInterfaceUp() and give it the details of the end-point
* (i.e. the uNetwork code will call uCellPppDevice() for you).
*
* @param cellHandle the handle of the cellular instance.
* @param[in] pDeviceSerial the serial device to use for the PPP
* connection, NULL to remove a previously
* added serial device.
* @return zero on success, else negative error
* code.
*/
int32_t uCellPppDevice(uDeviceHandle_t cellHandle,
uDeviceSerial_t *pDeviceSerial);

#ifdef __cplusplus
}
#endif

#endif // _U_CELL_PPP_H_

// End of file
134 changes: 106 additions & 28 deletions cell/src/u_cell_ppp.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

#include "u_cfg_sw.h"
#include "u_error_common.h"
#include "u_cfg_os_platform_specific.h" // For U_CFG_OS_PRIORITY_MAX

#include "u_port_clib_platform_specific.h" /* integer stdio, must be included
before the other port files if
Expand Down Expand Up @@ -110,6 +111,26 @@
# define U_CELL_PPP_ERROR_STRING_LENGTH 9
#endif

#ifndef U_CELL_PPP_UART_EVENT_TASK_STACK_SIZE_BYTES
/** The stack size for the event task used when PPP
* is over a real UART, rather than a CMUX channel;
* while the value used by the AT client isn't
* necessarily the right value here we use it to
* keep the planets aligned.
*/
# define U_CELL_PPP_UART_EVENT_TASK_STACK_SIZE_BYTES U_AT_CLIENT_URC_TASK_STACK_SIZE_BYTES
#endif

#ifndef U_CELL_PPP_UART_EVENT_TASK_PRIORITY
/** The task priority for the event task used when PPP
* is over a real UART, rather than a CMUX channel;
* while the value used by the AT client isn't
* necessarily the right value here we use it to
* keep the planets aligned.
*/
# define U_CELL_PPP_UART_EVENT_TASK_PRIORITY U_AT_CLIENT_URC_TASK_PRIORITY
#endif

/* ----------------------------------------------------------------
* TYPES
* -------------------------------------------------------------- */
Expand Down Expand Up @@ -314,10 +335,15 @@ static void closePpp(uCellPrivateInstance_t *pInstance,
pInstance->rebootIsRequired = true;
}
}
// Remove the multiplexer channel
uCellMuxPrivateCloseChannel((uCellMuxPrivateContext_t *) pInstance->pMuxContext,
U_CELL_MUX_PRIVATE_CHANNEL_ID_PPP);
pContext->pDeviceSerial = NULL;
if (pInstance->pPppDeviceSerial != NULL) {
// We were given a serial device to use, close it
pDeviceSerial->close(pDeviceSerial);
} else {
// Must have been using the CMUX; remove the channel
uCellMuxPrivateCloseChannel((uCellMuxPrivateContext_t *) pInstance->pMuxContext,
U_CELL_MUX_PRIVATE_CHANNEL_ID_PPP);
pContext->pDeviceSerial = NULL;
}
}
if (!pContext->muxAlreadyEnabled) {
// Disable the multiplexer if one was in use
Expand Down Expand Up @@ -379,7 +405,7 @@ void uCellPppPrivateRemoveContext(uCellPrivateInstance_t *pInstance)
}

/* ----------------------------------------------------------------
* PUBLIC FUNCTIONS
* PUBLIC FUNCTIONS KEPT WITHIN THE SRC DIRECTORY
* -------------------------------------------------------------- */

// Open the PPP interface of a cellular module.
Expand Down Expand Up @@ -446,44 +472,59 @@ int32_t uCellPppOpen(uDeviceHandle_t cellHandle,
pInstance->pPppContext = pContext;
pContext->pReceiveCallback = pReceiveCallback;
pContext->pReceiveCallbackParam = pReceiveCallbackParam;
// Determine if CMUX and "wake-up on UART data line" UART power saving
// are already enabled
// Determine if CMUX was already enabled
pContext->muxAlreadyEnabled = uCellMuxPrivateIsEnabled(pInstance);
pContext->uartSleepWakeOnDataWasEnabled = uCellPwrPrivateUartSleepIsEnabled(pInstance);
if (uCellPwrPrivateGetDtrPowerSavingPin(pInstance) >= 0) {
pContext->uartSleepWakeOnDataWasEnabled = false;
}
// Enable CMUX
errorCode = uCellMuxPrivateEnable(pInstance);
if (errorCode == 0) {
// Add the PPP channel
errorCode = uCellMuxPrivateAddChannel(pInstance,
U_CELL_MUX_PRIVATE_CHANNEL_ID_PPP,
&(pContext->pDeviceSerial));
}
if (errorCode == 0) {
// If we're on wake-up-on-data UART power saving and CMUX, switch
// UART power saving off, just in case
errorCode = uCellPwrPrivateDisableUartSleep(pInstance);
if (pInstance->pPppDeviceSerial != NULL) {
// We have been given a serial device, open it
errorCode = pInstance->pPppDeviceSerial->open(pInstance->pPppDeviceSerial,
pContext->pReceiveBuffer,
receiveDataSize);
if (errorCode == 0) {
pContext->pDeviceSerial = pInstance->pPppDeviceSerial;
}
} else {
// Determine if "wake-up on UART data line" UART power saving
// was already enabled
pContext->uartSleepWakeOnDataWasEnabled = uCellPwrPrivateUartSleepIsEnabled(pInstance);
if (uCellPwrPrivateGetDtrPowerSavingPin(pInstance) >= 0) {
pContext->uartSleepWakeOnDataWasEnabled = false;
}
// Enable CMUX
errorCode = uCellMuxPrivateEnable(pInstance);
if (errorCode == 0) {
// Add the PPP channel
errorCode = uCellMuxPrivateAddChannel(pInstance,
U_CELL_MUX_PRIVATE_CHANNEL_ID_PPP,
&(pContext->pDeviceSerial));
}
if (errorCode == 0) {
// If we're on wake-up-on-data UART power saving and CMUX,
// switch UART power saving off, just in case
errorCode = uCellPwrPrivateDisableUartSleep(pInstance);
}
}
if (errorCode == 0) {
pDeviceSerial = pContext->pDeviceSerial;
// We now have a second serial interface to
// the module: do a PPP dial-up on it. Could
// attach an AT handler to it but that would be
// an overhead in terms of RAM that we can do
// attach an AT handler but that would be an
// overhead in terms of RAM that we can do
// without, instead just send the dial-up string
// and wait for the response
uPortTaskBlock(1000);
errorCode = connectPpp(pContext, pKeepGoingCallback);
if ((errorCode == 0) && (pReceiveCallback != NULL)) {
pppTerminateRequired = (errorCode == 0);
// Note: the priority and stack size parameters
// to eventCallbackSet() are ignored, hence use of -1
// The stack size and task priority values
// passed to eventCallbackSet will be ignored
// if the serial device is a CMUX channel; they
// will intead be #U_AT_CLIENT_URC_TASK_STACK_SIZE_BYTES
// and #U_AT_CLIENT_URC_TASK_PRIORITY
errorCode = pDeviceSerial->eventCallbackSet(pDeviceSerial,
U_DEVICE_SERIAL_EVENT_BITMASK_DATA_RECEIVED,
callback, pContext,
-1, -1);
U_CELL_PPP_UART_EVENT_TASK_STACK_SIZE_BYTES,
U_CELL_PPP_UART_EVENT_TASK_PRIORITY);
}
}
if (errorCode < 0) {
Expand Down Expand Up @@ -607,4 +648,41 @@ void uCellPppFree(uDeviceHandle_t cellHandle)
}
}

/* ----------------------------------------------------------------
* PUBLIC FUNCTIONS EXPOSED VIA THE API DIRECTORY
* -------------------------------------------------------------- */

// Provide a serial device for the PPP connection.
int32_t uCellPppDevice(uDeviceHandle_t cellHandle,
uDeviceSerial_t *pDeviceSerial)
{
int32_t errorCode = (int32_t) U_ERROR_COMMON_NOT_INITIALISED;
uCellPrivateInstance_t *pInstance;

if (gUCellPrivateMutex != NULL) {

U_PORT_MUTEX_LOCK(gUCellPrivateMutex);

errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER;
pInstance = pUCellPrivateGetInstance(cellHandle);
if (pInstance != NULL) {
errorCode = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED;
if (U_CELL_PRIVATE_HAS(pInstance->pModule,
U_CELL_PRIVATE_FEATURE_PPP)) {
// Close PPP if we might be already running it
if ((pInstance->pPppDeviceSerial != NULL) &&
(pInstance->pPppDeviceSerial != pDeviceSerial)) {
closePpp(pInstance, true);
}
pInstance->pPppDeviceSerial = pDeviceSerial;
errorCode = (int32_t) U_ERROR_COMMON_SUCCESS;
}
}

U_PORT_MUTEX_UNLOCK(gUCellPrivateMutex);
}

return errorCode;
}

// End of file
9 changes: 5 additions & 4 deletions cell/src/u_cell_ppp_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,11 @@ extern "C" {
* Note: this will invoke multiplexer mode in the cellular device
* and hence will only work on interfaces that support multiplexer
* mode (for example the USB interface of a cellular device does not
* support multiplexer mode). Also, since multiplexer mode is a
* frame-oriented protocol it will be broken if a character is lost
* on the interface and hence, on a UART interface, it is HIGHLY
* RECOMMENDED that the UART flow control lines are connected.
* support multiplexer mode); for that case see uCellPppDevice().
* Also, since multiplexer mode is a frame-oriented protocol it will
* be broken if a character is lost on the interface and hence, on
* a UART interface, it is HIGHLY RECOMMENDED that the UART flow
* control lines are connected.
*
* Note: this function will allocate memory that is not released,
* for thread-safety reasons, until the cellular device is closed.
Expand Down
5 changes: 2 additions & 3 deletions cell/src/u_cell_private.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,8 @@ const uCellPrivateModule_t gUCellPrivateModuleList[] = {
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SNR_REPORTED) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_LWM2M) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_UCGED) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_HTTP) /* features */
// PPP is supported by the module but we do not test its integration with
// ubxlib and hence it is not marked as supported
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_HTTP) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_PPP) /* features */
),
3, /* Default CMUX channel for GNSS */
15, /* AT+CFUN reboot command */
Expand Down
1 change: 1 addition & 0 deletions cell/src/u_cell_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ typedef struct uCellPrivateInstance_t {
uCellPrivateSleep_t *pSleepContext; /**< Context for sleep stuff. */
uCellPrivateUartSleepCache_t uartSleepCache; /**< Used only by uCellPwrEnable/DisableUartSleep(). */
uCellPrivateProfileState_t profileState; /**< To track whether a profile is meant to be active. */
uDeviceSerial_t *pPppDeviceSerial; /**< Only used if a separate serial port is used for PPP. */
void *pFotaContext; /**< FOTA context, lodged here as a void * to
avoid spreading its types all over. */
void *pHttpContext; /**< Hook for a HTTP context. */
Expand Down
9 changes: 5 additions & 4 deletions cell/src/u_cell_pwr.c
Original file line number Diff line number Diff line change
Expand Up @@ -950,10 +950,12 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance,
// control and power saving mode correctly for it
// TODO: check if AT&K3 requires both directions
// of flow control to be on or just one of them
// Note: we don't check the return code of moduleConfigureOne()
// here since AT&K is not supported on the USB interface
// of a cellular module
if (uPortUartIsRtsFlowControlEnabled(stream.handle.int32) &&
uPortUartIsCtsFlowControlEnabled(stream.handle.int32)) {
success = moduleConfigureOne(atHandle, "AT&K3",
U_CELL_PWR_CONFIGURATION_COMMAND_TRIES);
moduleConfigureOne(atHandle, "AT&K3", 1);
if (uAtClientWakeUpHandlerIsSet(atHandle)) {
// The RTS/CTS handshaking lines are being used
// for flow control by the UART HW. This complicates
Expand All @@ -976,8 +978,7 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance,
}
}
} else {
success = moduleConfigureOne(atHandle, "AT&K0",
U_CELL_PWR_CONFIGURATION_COMMAND_TRIES);
moduleConfigureOne(atHandle, "AT&K0", 1);
// RTS/CTS handshaking is not used by the UART HW, we
// can use the wake-up on TX line feature without any
// complications
Expand Down
Loading

0 comments on commit d251066

Please sign in to comment.