Modem Call Control API

Click here for the API Reference documentation.

Using Profiles
Starting a Call
Answering a call

The Modem Call Control (mcc) API uses profiles. Calls can be initiated or received through these profiles. Each profile represents a call type or specific configuration of a given call (e.g., a profile can represent a given cellular modem/SIM combination - if the modem in question supports multiple SIM operation).

Using Profiles

A given Legato device can support multiple profiles, but usually, the MCC API will be configured with a single profile name that will initiate or receive calls.

Call cle_mcc_profile_GetByName() to access a specific profile by name.

le_mcc_profile_GetState() API allows the application to get the current state of the profile.

le_mcc_profile_AddStateChangeHandler() API installs a handler function that is notified when the profile's state changes.

le_mcc_profile_RemoveStateChangeHandler() API uninstalls the handler function.

When the Profile object is no longer needed, call le_mcc_profile_Release() must be used to release the profile.

Here's a profile code sample:

 void ManageMyProfile(void)
 {

     [...]

     // Get the Modem profile working on SIM card #1
     le_mcc_profile_Ref_t myProfile = le_mcc_profile_GetByName("Modem-Sim1");

     // Create and start a call on that profile
     le_mcc_call_Ref_t myCall = le_mcc_profile_CreateCall(myProfile, "+18008800800");
     le_mcc_call_Start(myCall);

     [...]
     // Set the profile into 'Do Not Disturb' mode: it will disable it's ablity to accept incoming
     // calls.
     le_mcc_profile_SetDoNotDisturb(myProfile);

     [...]
     // Clear the 'Do Not Disturb' mode: the profile gets back to be able to accept incoming calls.
     le_mcc_profile_ClearDoNotDisturb(myProfile);

     [...]
     // Set the profile into 'call forward mode': all calls incoming to this profile will
     // automatically be forwarded to another telephone number.
     le_mcc_profile_SetForwarding(myProfile, "+18008910910");

     [...]

 }

Starting a Call

To initiate a call, create a new call object with a destination telephone number calling the le_mcc_profile_CreateCall() function.

le_mcc_call_Start() must still initiate the call when ready.

The le_mcc_call_Start() function initiates a call attempt (it's asynchronous because it can take time for a call to connect).

It's essential to register a handler function to get the call events. Use le_mcc_profile_AddCallEventHandler() API to install that handler function. As the call attempt proceeds, the profile's registered call event handler will receive events.

The le_mcc_profile_RemoveCallEventHandler() API uninstalls the handler function.

The following APIs can be used to manage incoming or outgoing calls:

When finished with the call object, call le_mcc_call_Delete() to free all the allocated resources associated with the object. This will frees the reference, but remains active if other holders are using it.

This code example uses CallAndPlay() to dial a phone number, and if successful, play a sound file. Once the file has played, the call hangs up.

 typedef struct
 {
     le_mcc_profile_Ref_t   profileRef;
     le_mcc_call_Ref_t      callRef;
     char                   filePath[MAX_FILE_PATH_BYTES];
 }
 PlayContext_t;

 void CallAndPlay
 (
     const char* destinationTelPtr,
     const char* filePathPtr
 )
 {
     PlayContext_t* contextPtr = (PlayContext_t*)le_mem_ForceAlloc(PlayContextPoolRef);

     le_str_Copy(contextPtr->filePath, filePathPtr, sizeof(contextPtr->filePath), NULL);

     contextPtr->profileRef = le_mcc_profile_GetByName("Modem-Sim1");

     le_mcc_profile_AddCallEventHandler(contextPtr->profileRef,
                                        MyCallEventHandler,
                                        contextPtr);

     contextPtr->callRef = le_mcc_profile_CreateCall(contextPtr->profileRef, destinationTelPtr);

     le_mcc_call_Start(contextPtr->callRef);
 }


 static void MyCallEventHandler
 (
     le_mcc_call_Ref_t    callRef,
     le_mcc_call_Event_t  event,
     void*                contextPtr
 )
 {
     PlayContext_t*      myContextPtr = (PlayContext_t*)contextPtr;

     switch (event)
     {
         case LE_MCC_EVENT_CONNECTED:
             {
                 le_audio_StreamRef_t callAudioTxRef = le_mcc_call_GetTxAudioStream(callRef);

                 le_audio_PlaySoundFromFile(callAudioTxRef,
                                            myContextPtr->filePath,
                                            MyAudioFinishedHandler,
                                            myContextPtr);
             }
             break;

         case LE_MCC_EVENT_TERMINATED:
             le_mcc_call_Delete(callRef);
             // I don't release the Profile for further use

             le_mcc_profile_RemoveCallEventHandler(myContextPtr->profileRef);

             le_mem_Release(myContextPtr);
             break;
     }
 }


 static void MyAudioFinishedHandler
 (
     le_audio_PlayComplete reason,
     void*                 contextPtr
 )
 {
     PlayContext_t* myContextPtr = (PlayContext_t*)contextPtr;

     le_mcc_call_HangUp(myContextPtr->callRef);  // This will trigger a TERMINATED event.
 }

Answering a call

Receiving calls is similar sending calls. Add a handler through le_mcc_profile_AddCallEventHandler() to be notified of incoming calls.

To answer, call le_mcc_call_Answer(). To reject it, call le_mcc_call_Delete().

This code example uses InstallAutomaticAnswer() to install a call event handler that automatically answers incoming calls. The handler function verifies the incoming call is permitted (through a predefined list), and then decides whether to answer or terminate it. If a call is already active, it can add the new incoming call creating a conference call.

 typedef struct
 {
     le_mcc_profile_Ref_t   profileRef;
     le_mcc_call_Ref_t      callRef;
     uint32_t               calledPartyCount;
 }
 VoiceContext_t;

 static le_audio_ConnectorRef_t AudioRxConnectorRef = NULL;
 static le_audio_ConnectorRef_t AudioTxConnectorRef = NULL;


 void InstallAutomaticAnswer
 (
     void
 )
 {
     VoiceContext_t* contextPtr = (VoiceContext_t*)le_mem_ForceAlloc(VoiceContextPoolRef);

     contextPtr->profileRef = le_mcc_profile_GetByName("Modem-Sim1");

     le_mcc_profile_AddCallEventHandler(contextPtr->profileRef,
                                        MyVoiceCallEventHandler,
                                        contextPtr);
 }


 static void MyVoiceCallEventHandler
 (
     le_mcc_call_Ref_t    callRef,
     le_mcc_call_Event_t  event,
     void*                contextPtr
 )
 {
     char                tel[TEL_NMBR_MAX_LEN];
     VoiceContext_t*     myContextPtr = (VoiceContext_t*)contextPtr;

     switch (event)
     {
         case LE_MCC_EVENT_INCOMING:
             {
                 le_mcc_profile_State_t myProfileState = LE_MCC_PROFILE_NOT_AVAILABLE;

                 le_mcc_call_GetRemoteTel(callRef, &tel, sizeof(tel));
                 if (IsAnAuthorizedIncomingCall(tel) == TRUE)
                 {
                     le_mcc_profile_GetState(myProfile, &myProfileState);
                     if(myProfileState == LE_MCC_PROFILE_IN_USE)
                     {
                         // Another incoming call, turn this into a x-way conference, mixed locally:
                         myContextPtr->calledPartyCount++;

                         le_audio_StreamRef_t otherCallRxAudioRef = le_mcc_call_GetRxAudioStream(callRef);
                         le_audio_StreamRef_t otherCallTxAudioRef = le_mcc_call_GetTxAudioStream(callRef);

                         le_audio_connector_AddStream(AudioRxConnectorRef, otherCallRxAudioRef);
                         le_audio_connector_AddStream (AudioTxConnectorRef, otherCallTxAudioRef);
                     }
                     else if(myProfileState == LE_MCC_PROFILE_IDLE)
                     {
                         // First incoming call
                         myContextPtr->calledPartyCount = 1;

                         le_audio_StreamRef_t speakerphoneRef = le_audio_OpenSpeakerphone();
                         le_audio_StreamRef_t callRxAudioRef = le_mcc_call_GetRxAudioStream(callRef);
                         le_audio_StreamRef_t micphoneRef = le_audio_OpenMicphone();
                         le_audio_StreamRef_t callTxAudioRef = le_mcc_call_GetTxAudioStream(callRef);

                         AudioRxConnectorRef = le_audio_CreateConnector();
                         AudioTxConnectorRef = le_audio_CreateConnector();

                         le_audio_connector_AddStream(AudioRxConnectorRef, speakerphoneRef);
                         le_audio_connector_AddStream (AudioRxConnectorRef, callRxAudioRef);

                         le_audio_connector_AddStream(AudioTxConnectorRef, micphoneRef);
                         le_audio_connector_AddStream (AudioTxConnectorRef, callTxAudioRef);
                     }

                     le_mcc_call_Answer(callRef);
                 }
                 else
                 {
                     // Reject the incoming call
                     le_mcc_call_Delete(callRef);
                 }
              }
              break;

          case LE_MCC_EVENT_TERMINATED:
              le_mcc_call_Delete(callRef);

              // I delete the Audio connector references if it remains only one called party.
              if (myContextPtr->calledPartyCount == 1)
              {
                  le_audio_DeleteConnector(AudioRxConnectorRef);
                  le_audio_DeleteConnector(AudioTxConnectorRef);
              }
              else
              {
                  myContextPtr->calledPartyCount--;
              }
              break;
      }
  }

Copyright (C) Sierra Wireless, Inc. 2013. All rights reserved. Use of this work is subject to license.

 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines