Sorin Bora

Implementing a Kinetis Connectivity Framework Serial Communication Interface (FSCI) Host Module

Discussion created by Sorin Bora Employee on Apr 5, 2017

In most cases, a narrowband wireless connectivity application and a the wireless connectivity stack(s) run together on the same processor. However, there are scenarios where a split solution is required so that a more complex application will run on a platform (embedded or desktop) and the stack on the embedded connectivity SoC, communicating through a serial interface.

In this dual platform scenario, the connectivity stack runs in a configuration called black box which is interfaced through a binary serial command protocol to a host processor running the connectivity application. The most appropriate serial protocol to use in this use case is the Framework Serial Communication Interface (FSCI) and to make the transition from a single chip application to a Host application, an FSCI Host module needs to be implemented.

The host module can have various implementations, among which the NXP Test Tool for Connectivity Products command console, running on Windows systems or the Python-enabled FSCI Host SDK distributed with Kinetis connectivity stacks packages, portable across Python-supporting desktop or embedded systems.

This document aims to offer an example on how to implement a FSCI host in the software running on a microcontroller embedded system within a RTOS environment. For this example, we will consider the Kinetis Bluetooth low energy stack.

 

The interaction between a host and a black box is made possible through the following API concepts:

  1. Command – request sent by the host to the black box
  2. Status – response sent by the black box to the host
  3. Event – additional information sent by the black box to the host, either in response to a command or asynchronous information at any given time

 

When running on a single chip solution, the BLE implementation is provided as a library. To port the application to a Host solution, the library is removed from the project, while keeping only the BLE interface header files. The BLE stack has FSCI Host support and these files need to be added in the project, which provide the following to create a host – black box solution:

  1. BLE API implementation – every function in the BLE interface needs to be implemented as described below
  2. Command monitors – functions that receive the parameters of the BLE API, serialize them into a buffer that is sent to the black box
  3. Status handlers – functions that receive statuses as serialized buffers, unpack the information and return it as a BLE status to the Host application
  4. Event handlers – functions that receive events as serialized buffers, unpack the information and present it as a BLE event to the Host application
  5. FSCI Host BLE module - handles application generic requests and blocks the application function context until the status and eventually any out parameters are received from the black box

Let us consider an example of a BLE function from the interface and describe what is required for it.

/*! *********************************************************************************

* \brief  Returns whether or not a connected peer device is bonded.

* \param[in] deviceId           Device ID of the GAP peer.

* \param[out] pOutIsBonded      Boolean to be filled with the bonded flag.

* \return  gBleSuccess_c or error.

* \remarks This function executes synchronously.

********************************************************************************** */

bleResult_t Gap_CheckIfBonded

(

    deviceId_t  deviceId,

    bool_t*     pOutIsBonded

);

This function has two parameters, one being an OUT parameter. It needs to send a command to the black box, wait for the result and if the result is success, wait for the event that will include the information if the device is bonded and fill the OUT parameter.

 

BLE API Implementation

bleResult_t Gap_CheckIfBonded

(

    deviceId_t deviceId,

    bool_t* pOutIsBonded

)

{

    bleResult_t result = gBleSuccess_c;

    FSCI_HostSyncLock(fsciBleInterfaceId, gFsciBleGapOpcodeGroup_c, gBleGapStatusOpCode_c);

    FsciGapCmdMonitor(CheckIfBonded, deviceId, pOutIsBonded);

    result = Ble_GetCmdStatus(TRUE);

    FSCI_HostSyncUnlock(fsciBleInterfaceId);

    return result;

}

The FSCI Host is a module in the FSCI connectivity framework that supports the FSCI Host BLE module with the following:

  • arbitrates synchronization between multiple callers, either multiple connectivity applications or one running in multiple RTOS tasks, when trying to access the serial interface for a request – response complete sequence
  • stores what is the expected response from the black box and notifies the blocked caller when the serial buffer with the required header has arrived

NOTE: It is mandatory for the BLE FSCI Host implementation to block in the context of the caller application and get the status and OUT parameters, if any, from the black box to allow the application to use them immediately after the call.

 

Command Monitor

The command monitor allocates a serial buffer, fills the parameters and saves the OUT parameter to be filled when the response arrives.

void fsciBleGapDeviceIdParamCmdMonitor(fsciBleGapOpCode_t opCode, deviceId_t deviceId)

{

    clientPacketStructured_t*   pClientPacket;

    uint8_t*                    pBuffer;

    /* Allocate the packet to be sent over UART */

    pClientPacket = fsciBleGapAllocFsciPacket(opCode,

                                              fsciBleGetDeviceIdBufferSize(&deviceId));

    if(NULL == pClientPacket)

    {

        return;

    }

    pBuffer = &pClientPacket->payload[0];

    /* Set command parameters in the buffer */

    fsciBleGetBufferFromDeviceId(&deviceId, &pBuffer);

    /* Transmit the packet over UART */

    FSCI_transmitFormatedPacket(pClientPacket, fsciBleInterfaceId);

}

void fsciBleGapCheckIfBondedCmdMonitor(deviceId_t deviceId, bool_t* pOutIsBonded)

{

    /* Monitor deviceId parameter */

    fsciBleGapDeviceIdParamCmdMonitor(gBleGapCmdCheckIfBondedOpCode_c, deviceId);                             

    /* Save out parameters pointers */

    fsciBleGapSaveOutParams(pOutIsBonded, NULL);

}

 

Status and Event Handler

The GAP handler is registered in the FSCI connectivity framework module and is called whenever a GAP status or event is received.

void fsciBleGapHandler(void* pData, void* param, uint32_t fsciBleInterfaceId)

{

    clientPacket_t* pClientPacket   = (clientPacket_t*)pData;

    uint8_t*        pBuffer         = &pClientPacket->structured.payload[0];

    /* Select the GAP function to be called (using the FSCI opcode) */

    switch(pClientPacket->structured.header.opCode)

    {

        case gBleGapStatusOpCode_c:                      

            {

                bleResult_t status;

                /* Get status from buffer */

                fsciBleGetEnumValueFromBuffer(status, pBuffer, bleResult_t);

                if(gBleSuccess_c != status)

                {

                    /* Clean out parameters pointers kept in FSCI */

                    fsciBleGapCleanOutParams();

                }

            }

            break;

 

        case gBleGapEvtCheckIfBondedOpCode_c:                                       

            {

                fsciBleGapCheckIfBondedOutParams_t* pOutParams = (fsciBleGapCheckIfBondedOutParams_t*)fsciBleGapRestoreOutParams();

                if(NULL != pOutParams->pIsBonded)

                {

                    /* Get out parameter of the Gap_CheckIfBonded function from buffer */

                    fsciBleGetBoolValueFromBuffer(*pOutParams->pIsBonded, pBuffer);

                    /* Clean out parameters pointers kept in FSCI */

                    fsciBleGapCleanOutParams();

                    /* Signal out parameters ready */

                    Ble_OutParamsReady();

                }

            }

            break;

    }

    MEM_BufferFree(pData);

}

 

FSCI Host BLE module

This function is called by all the BLE API and performs the following:

  • it blocks the Host Application calling context and polls for the packet reception until response arrives or timeout occurs
  • if the function has OUT parameters it continues to wait for the event that contains them or timeout occurs

bleResult_t Ble_GetCmdStatus(bool_t bHasOutParams)

{

    bleResult_t result = gBleSuccess_c;

    uint64_t tStamp = 0;

    /* Set out parameter flag if any */

    bFunctionHasOutParams = bHasOutParams;

    /* Wait for result from the serial interface */

    while( !pFsciHostSyncRsp )

    {

        tStamp = TMR_GetTimestamp();

        while( !pFsciHostSyncRsp )

        {

            FSCI_receivePacket((void*)fsciBleInterfaceId);

            if( TMR_GetTimestamp() - tStamp > mFsciHost_WaitForStatusTimeout_us_c )

            {

                break;

            }   

        }

    }

    if( pFsciHostSyncRsp )

    {

        result = (bleResult_t)pFsciHostSyncRsp->structured.payload[0];

        /* Check status and wait for outParameters */

        if( gBleSuccess_c == result )

        {

            tStamp = TMR_GetTimestamp();

            while( bFunctionHasOutParams )

            {

                FSCI_receivePacket((void*)fsciBleInterfaceId);

                if( TMR_GetTimestamp() - tStamp > mFsciHost_WaitForStatusTimeout_us_c )

                {

                    /* Timeout on the receiving the response */

                    result = gBleUnexpectedError_c;

                    break;

                }

            } 

        }

        /* Free FSCI packet */

        MEM_BufferFree(pFsciHostSyncRsp);

    }

    else

    {

        /* Timeout on the receiving the response */

        result = gBleUnexpectedError_c;

    }

    return result;

}

 

The rest of the BLE API is built in a very similar manner like the one in this example

Outcomes