Page tree
Skip to end of metadata
Go to start of metadata


FreeSWITCH endpoints are designed to create, handle and destroy media streams. 

 Click here to expand Table of Contents

This page describes sessions and channels from the point of view of writing an extension module for FreeSWITCH.

FreeSWITCH Sessions and Channels

A FreeSWITCH session is equivalent to a call-leg when the session is managed by mod_sofia.

FreeSWITCH manages the establishment and destruction of linked sessions between entities. For those of us from a telephony background, FreeSWITCH performs the functions of a PBX or central office. The following document is written with respect voice calls, but the principles apply to the other types of connections that FreeSWITCH can manage.

The voice entities that we're most familiar with are simple telephones. The modules that handles these are called “endpoints”. The most used endpoint modules are mod_sofia (SIP phones) and mod_skinny (Cisco phones).

As modules, they must have the following entry points to be loaded and unloaded.


Usually a module will also implement two interfaces, as arrays of callback functions. This is how FreeSWITCH communicates with the module.

An endpoint module deals a lot with sessions. session contains a channel and private data.


This diagram shows containment, when in fact pointers are used.

The channel is the basic entity for one end of a FreeSWITCH call. The most interesting members of a channel are:

-its session (wrapper)

Channel objects manage the flow of media to the outside world. Channels are where the most interesting things happen. The channcel runs through states that represent the state of call setup, the media flow and the call tear down.

A session object wraps a channel in some management overhead, including:

-thread to run the code
-memory pool
-array of codecs
-message queues
-read/write data buffers

So, the session owns the channel, so in a sense the session is the parent of the channel. But the channel is the interesting object.

The session also owns a private object. The author of the module defines what this object contains and what it's used for. In almost all modules, this object is called session->tech_pvt. Some common elements of a tech_pvt are:

-ip addresses involved in the communication channel
-encryption keys for the communication channel
-the external codecs involved in the media flow


Now, returning to the channel object, it's single most important member is its state. The 'state' member is the one that most FreeSWITCH documentation references. This state runs through the sequence CS_NEW, CS_INIT, CS_ROUTING, CS_EXECUTE, CS_HANGUP, CS_REPORTING and CS_DESTROY. The transitions from one state to the other are handled gracefully by the internals of FS (switch_core_state_machine.c) unless you intercept these transitions. This state describes the highest level connection status of the channel/session.

The channel object also has two other state members (running_state and callstate) which are hardly used at all in the core of FreeSWITCH and which you probably won't need. The running_state is just used in state transitions, usually holds the “previous state”. The callstate is a more fine-grained version of the state. Callstate has its own getter and setter. If you chose to use the setter, it will fire an event. Callstate include CCS_DOWN, CCS_DIALING, CCS_RINGING, CCS_EARLY, CCS_ACTIVE and CCS_HANGUP.

The state variable is not directly what one would normally use in telephony. Because FreeSWITCH can handle many kinds of media, the state variable is designed to handle the connection status. You need also look at CF_RING_READY, CF_EARLY_MEDIA and CF_ANSWERED in order to determine the telephony state.


Here's a table to illustrate some details of the states:


Normal activities

Standard processing

Your module code should….


right after session creation


change to CS_INIT


create any data structures required for the rest of the session/call

changes to CS_ROUTING



inbound: dial tone, accepting digits, ringback


inbound: uses the caller_profile & the dialplan to pick a destination


inbound: your module will get a message SWITCH_INDICATE_ANSWER

outbound: your module should call switch_call_answer()


outbound: ringing




outbound: talking




inbound: media flows; talking


near_end release: your module should call switch_channel_hangup()

far_end release: your module will get a callback on my_module_on_hangup()



Changes to CS_REPORTING*




Changes to CS_DESTROY*



Release any memory




Within the channel is a single caller_profile. This contains descriptions of both the caller and callee. The internal sections of FreeSWITCH only use:

-caller_id_name, caller_id_number, caller_extension


The channel flags hold extra state information above and beyond the basic 13 states that are held in the state variable. The interesting flags are things like

CF_ANSWERED - Channel is answered
CF_OUTBOUND - Channel is an outbound channel
CF_EARLY_MEDIA - Channel is ready for audio before answer
CF_ORIGINATOR - Channel is an originator
CF_TRANSFER - Channel is being transfered
CF_HOLD - Channel is on hold
CF_GEN_RINGBACK - Channel is generating it's own ringback
CF_RING_READY - Channel is ready to send ringback
CF_BREAK - Channel should stop what it's doing


And a channel has a hash of key-value pairs that can hold all kinds of interesting data related to the channel. These are used extensively by mod_sofia to bind things like 'sip_user_agent' and 'sip_profile_name' to a channel. These key-value pairs can be queried by either the dialplan or LUA processing. They can also be a handy way to accumulate data in preparation for issuing a FreeSWITCH event. See channel variables.


Howto Write a FreeSWITCH Endpoint

First follow the New Module Checklist. Earlier directions suggest using freeswitch/src/mod/applications/mod_skel to start off, but for endpoints you're better off starting with freeswitch/src/mod/endpoints/mod_reference.

Module Load is the first entry point called. Usually at this point you will inform the caller about your module, it's callback interface arrays and register any api extensions. You may also want to create a memory pool. You should read your config data, which by convention is XML and buried somewhere in freeswitch/conf.

Making Calls

Lifecycle of a FreeSWITCH call

Making calls from within your module

Below is our current understanding. We are not core developers, so all is guessed --Sathieu 11:14, 18 February 2010 (UTC) – PatB 2015-09-25

For both outgoing and incoming calls, you must allocate the session. The core of FreeSWITCH will automatically allocate and bind a channel to your session. FreeSWITCH does not create the caller_profile; your code must allocate a caller_profile and bind it to the channel. You probably also want to allocate a private data object and bind it to the session.

Outgoing calls are created either by a call coming through the dialplan routing, or when you use the originate command in the console:

  • your_io_routines.outgoing_channel() is called

    • you get a pointer to the other call half (session/channel)

  • create a new session (with switch_core_session_request()) and private data[1]

  • parse outbound_profile->destination_number to get called number

  • create a caller_profile and bind it to the session
  • qualify the session, attached channel and attached private data [2]

  • the channel is currently in CS_NEW; change channel state to CS_INIT

    • make the phone ring (and call switch_channel_ring_ready())

  • your_state_handler.on_init() is called

    • no need to do anything

    • the default handler will change channel state to CS_ROUTING

  • your_state_handler.on_routing() is called

    • no need to do anything

    • the default handler will change channel state to CS_CONSUME_MEDIA

  • when you decide the call is answered

    • call switch_channel_mark_answered(channel) [3] which will set the channel state to CS_EXCHANGE_MEDIA

  • your_state_handler.on_exchange_media() is called

    • and the call is up

Incoming calls are initiated by your code. To start up a call

  • create a new session (with switch_core_session_request()) and private data [1]

    • create a caller_profile (with switch_caller_profile_new()) and fill in the ->destination_number

    • qualify the channel and private data [2]

    • create a caller_profile and bind it to the channel
    • start up the thread that manages the session (with switch_core_session_thread_launch()) [4]

    • change channel state from CS_NEW to CS_INIT

  • your_state_handler.on_init() will be called

    • no need to do anything

    • the default handler will change channel state to CS_ROUTING

  • your_state_handler.on_routing() is called

    • no need to do anything

    • if your caller_profile->destination_number has been set, the default handler will search the dialplan and start an application; if this is a telephone call, usually the 'bridge' application will start an outbound session toward the called party and change change this channel state to CS_EXECUTE

  • when the distant party answers

    • your_state_handler.on_receive_message() is called

      • filter the messages you get for SWITCH_MESSAGE_INDICATE_ANSWER

      • take any action that you need to start up the call

Notice that in a normal call, the inbound/originating channel ends up in the CS_EXECUTE state and the outbound/called channel ends up in the CS_EXCHANGE_MEDIA state. [Once we start playing with 'hold' and 'transfer' this statement gets muddied.]

If your module wants to shut down the call, just use switch_channel_hangup(). You will get a callback to your on_hangup() entry point and the call will shut down.

If the other end wants to shut down the call, you will get a SIG_KILL sent to your on_channel_kill() entry point. Shortly thereafter you will get a call to your on_hangup() entry point and the call will shut down.

Notes: [1] you don't need to create the channel….it is created inside the session

[2] at minimum, you should set the .name member of the channel

[3] or if you wish to abort, call switch_channel_hangup(channel)

[4] strangely, you only call s_c_s_thread_launch() on incoming calls; it's called automatically for you on outgoing calls (deep in switch_ivr.c)

From the directory that you installed freeswitch, you can simply type “make mod_my_module” to just remake your loadable module. In the freeswitch console, you can “load mod_my_module” and “unload mod_my_module” as often as you want, fixing bugs between builds.

The Babel tower of the API

When trying to learn FreeSWITCH programming, you're going to be overwhelmed by the number of API methods that are available. Here's a list on the one's you're likely to need in session and channel management.


alloc and initialize a new session


given a session, get the associated channel


given a session, get the private data


used to coerce a channel state; usually the only one you will do is CS_NEW→CS_INIT


alloc space, usually for tech_pvt


on outgoing, clone the caller's caller_profile


bind a caller_profile to a channel

switch_channel_test_flag() switch_channel_set_flag_value()



give your channel a name, like “superphone/1001”




ignored if the channel is already answered

if inbound, sends the message SWITCH_MESSAGE_INDICATE_RINGING

calls switch_channel_mark_ring_ready()



sends a PROGRESS event

sets callstate=CCS_RINGING



ignored if the channel is already answered

if inbound, sends the message


calls switch_channel_mark_pre_answer()



sends a PROGRESS_MEDIA event

sets callstate=CCS_EARLY



ignores outbound calls or calls already anwered


call switch_channel_mark_answered()



ignores calls already answered


sends CHANNEL_ANSWER event

sets callstate=CCS_ACTIVE



change channel state to CS_HANGUP

sends the HANGUP event

sends SIG_KILL(1) to itself




starts up the state machine; inbound only


check inbound or outbound



There are several similar calls with the word “_perform_” in the name; these are just versions that include the filename/linenumber for debugging. Don't call those directly.

There are three pairs in the middle, with and without the _mark_ in the name. You call




when you want to issue the SWITCH_MESSAGE_INDICATE_* messages into the message queue. If you're responding to an INDICATE message, you don't need to re-issue that message, so you would call these below:






For example, if you just got a SWITCH_MESSAGE_INDICATE_ANSWER, you would only call the switch_channel_mark_answered() version. If you are working on something else, and you decide the call should be answered, call the non-_mark_ version switch_channel_anwer()


In practice, the standard state code does a pretty good job of managing channel flags, channel variable and channel states. The only routines that you will probably need to call are

  switch_channel_set_state() // once, during session creation
  switch_channel_mark_answered() // when your device answers
  switch_channel_hangup() // to shut down


Source code for those UML diagrams above:

using PlantUML syntax

Fig 1

session *-- channel
channel *-- caller_profile
session *-- tech_pvt

Fig 2

session *-- channel
channel *-- caller_profile
session *-- tech_pvt
session : thread
session : memory pool
session : codec[]
session: messages[]
channel: name
channel: uuid
channel: direction
channel: flags[]
channel: variables[]
caller_profile: destination_number
caller_profile: caller_id
tech_pvt: ip addresses
tech_pvt: other data that you may need