Skip to content

GSM modem

GSM modem driver.

To use the GSM modem driver, retrieve a handle to the modem device with open(const char*), supplying the device name as declared in the sysconf, and call gsm_modem_init (int, gsm_modem_init_t *). Init powers the module, applies the configuration (bands, RAT, APN) and starts network acquisition; registration and PS attach then proceed autonomously.

cpp
int h_modem = open("gsm0");
// static: the driver keeps a reference to the init structure
static gsm_modem_init_t modem_init = GSM_MODEM_INIT_DEFAULT;
strncpy (modem_init.apn, PDP_CONTEXT_NAME, sizeof (modem_init.apn) - 1);
if (gsm_modem_init (h_modem, &modem_init) != 0) {
    // also returned while a requested modem reset is still running —
    // back off and call gsm_modem_init() again.
}

Connection model

The driver distinguishes three conditions: registered (network registration per CEREG, gsm_modem_registered), connected (PPP session up with an active PDP context, gsm_modem_connected), and data actually flowing. The first two are claims made by the modem; only an answered round trip through the network proves the third. A session can be registered, hold an IP address and still move nothing — which is why the driver relies on the evidence reports below.

Data-path evidence

The layer that talks to a peer reports what it observes: gsm_modem_note_data_ok after an answered round trip (at a cadence well below the 5-minute evidence window), gsm_modem_note_data_lost once its own cheaper recovery has failed. Without these reports the driver can only wait for positive evidence to age out, which takes GSM_MODEM_RUNG_DATA_STALL_MS.

Recovery escalation

When evidence goes stale or is reported lost, the driver escalates in cost order: PPP/PDP rebuild (GSM_MODEM_RUNG_DATA_STALL_MS), then modem reset (GSM_MODEM_RUNG_MODEM_RESET_MS), then device reboot (GSM_MODEM_RUNG_CEILING_MS). Layers above must place their own recovery deadlines above these rungs: a shorter product deadline reboots the device before the cheaper repair was even tried. A running PLMN search is never interrupted — on a roaming network a cold search legitimately takes 30-60 minutes.

Blocking behaviour

Most calls issue AT transactions and serialize on the driver's request mutex. While a PPP session is up, a transaction first has to escape data mode, so a call can block for a long time (the bound is measured in minutes, not milliseconds). Do not call this API from threads that must stay responsive; prefer cached snapshots where a middleware offers them (e.g. coldwave_lte_peek() in libcoldwave).

Sysprops

The Modem needs a parent UART device with RTS&CTS

syspropdescription
gpio_riRing-Indicator GPIO
gpio_dcdData Carrier Detect (DCD) GPIO
gpio_dtrData Terminal Ready (DTR) GPIO
cpp
sysconf_create_device("silabs-gecko-euart", eusart1, 0x500A0000UL ,
                      sysconf_set_int_param (gpio_rx, 206),
                      sysconf_set_int_param (gpio_tx, 205),
                      sysconf_set_int_param (gpio_rts, 204),
                      sysconf_set_int_param (gpio_cts, 203))

sysconf_create_device("quectel-bg77", modem0, 0x0,
                      sysconf_set_parent_dev (eusart1),
                      sysconf_set_int_param (gpio_dcd, 201),
                      sysconf_set_int_param (gpio_dtr, 207),
                      sysconf_set_int_param (gpio_ri, 200),
                      sysconf_set_int_param (quectel_bg77_pwrkey_gpio, 208),
                      sysconf_set_int_param (gpio_status, 202))

Types

Name
structgsm_modem_init_t
GSM modem initialization parameters.
structgsm_signal_info_t
Signal Strength Information.
structgsm_registration_info_t
Serving-cell registration information.
structgsm_modem_driver
enumgsm_technology_t
Modem Radio Access Technology (RAT).
enumapn_auth_t
APN Authentication Type for PPP.

Functions Overview

Name
intgsm_modem_init(int hdev, gsm_modem_init_t * init)
Initialize the modem.
intgsm_modem_reset(int hdev)
Requests a modem reset.
intgsm_modem_rf_on(int hdev)
activates the radio interface of the modem
intgsm_modem_rf_off(int hdev)
turns the radio interface of the modem off
intgsm_modem_sleep(int hdev)
puts the modem in power save mode immediately
intgsm_modem_wakeup(int hdev)
wakes the modem from PSM sleep immediately
intgsm_modem_connect(int hdev, int wait)
Dials the data call and brings up the PPP session for the APN's PDP context.
intgsm_modem_disconnect(int hdev)
Tears the PPP session down and disconnects the modem from the APN.
intgsm_modem_connected(int hdev)
checks the modem's current connection status in the PS domain
intgsm_modem_registered(int hdev)
checks the modem's current registration status
intgsm_modem_imei(int hdev, char * imei)
Reads the modem's device identifier (IMEI).
intgsm_modem_imsi(int hdev, char * imsi)
Reads the SIM's subscriber identity (IMSI).
intgsm_modem_iccid(int hdev, char * iccid)
Reads the SIM's card identity (ICCID).
intgsm_modem_signal_quality(int hdev, gsm_signal_info_t * info)
Reads the current signal quality from the modem.
intgsm_modem_sim_status(int hdev)
Get the status of the SIM card in the GSM modem.
intgsm_modem_registration_info(int hdev, gsm_registration_info_t * info)
Reads the serving cell's identity (MCC/MNC, area code, cell id, RAT).
intgsm_modem_version(int hDev, char * version, size_t version_len)
Retrieves the modem's firmware/version string.
intgsm_modem_power_off(int hDev)
Power down the modem.
intgsm_modem_power_on(int hdev)
Power on the modem.
intgsm_modem_note_data_ok(int hdev)
Report that user data provably crossed the data path.
intgsm_modem_note_data_lost(int hdev)
Report that the data path is not carrying traffic any more.

Defines

Name
GSM_MODEM_RUNG_DATA_STALL_MSRung 1 (ms): rebuild PPP/PDP once positive data-path evidence is this old.
GSM_MODEM_RUNG_MODEM_RESET_MSRung 2 (ms): reset the modem when rebuilds keep not helping.
GSM_MODEM_RUNG_CEILING_MSRung 3 (ms): device-reboot ceiling; product deadlines must sit above it.
GSM_MODEM_IMEI_MAXLEN
GSM_MODEM_IMSI_MAXLEN
GSM_MODEM_ICCID_MAXLEN
GSM_MODEM_IMEI_SIZERequired buffer size for gsm_modem_imei, including the terminating null byte.
GSM_MODEM_IMSI_SIZERequired buffer size for gsm_modem_imsi, including the terminating null byte.
GSM_MODEM_ICCID_SIZERequired buffer size for gsm_modem_iccid, including the terminating null byte.
GSM_MODEM_INIT_DEFAULTDefault init parameters for GSM modem, setting it to LTE Cat-M1 (eMTC) on band 8.

Types Documentation

enum gsm_technology_t

EnumeratorDescription
gtNBIOTLTE Cat-NB1 / Cat-NB2 (NB-IoT) only.
gtEMMCLTE Cat-M1 (eMTC) only.
gtEMMC_NBIOTCat-M1 preferred, then NB-IoT.
gtNBIOT_EMMCNB-IoT preferred, then Cat-M1.
gtGSM2G (GPRS/EDGE); module-dependent
gtLTELTE Cat 1+ (E-UTRAN); module-dependent.

Modem Radio Access Technology (RAT).

Which values a given module honours is driver-dependent; the Quectel BG77 supports Cat-M1 and NB-IoT only and ignores the others. (The EMMC spelling in the enumerators is historic; the technology is eMTC / LTE Cat-M1.)

enum apn_auth_t

EnumeratorDescription
atNONENo Authentication required.
atPAPPAP Authentication only.
atCHAPCHAP Authentication only.
atPAPCHAPBoth PAP and CHAP are supported.

APN Authentication Type for PPP.

Function Details

function gsm_modem_init

cpp
int gsm_modem_init(
    int hdev,
    gsm_modem_init_t * init
)

Initialize the modem.

Parameters:

  • hdev device handle as returned by open(const char*)
  • init structure holding the device configuration parameters

Return: 0 on success, -1 on failure. -1 is also returned while a previously requested modem reset is still executing — back off and call again.

This function has to be called before any other gsm_modem_xx functions. If not, behaviour will be unpredictable.

The structure behind init must stay valid for the lifetime of the device; the driver keeps a reference.

function gsm_modem_reset

cpp
int gsm_modem_reset(
    int hdev
)

Requests a modem reset.

Parameters:

Return: 0 if the request was accepted or deliberately suppressed, -1 if the driver could not take it at all

The reset is requested, not executed synchronously, and the request may deliberately be dropped:

  • It is refused while the modem reports CEREG=2 (actively searching for a network). Interrupting a PLMN search restarts it from zero; on a roaming VPLMN a cold search legitimately takes 30-60 minutes, and with COPS=0 the module is free to pick a different operator afterwards.
  • It is rate-limited by the driver's existing reset backoff and circuit breaker, so a caller polling in a loop cannot produce a reset storm.

When the request is carried out, the driver tears the PPP session down under its own lock and discards the cached modem state first — unlike the bare pin reset this call used to perform.

If the modem was connected to the PS domain, it will be disconnected and gsm_modem_connect(int hdev, int wait) has to be called again.

function gsm_modem_rf_on

cpp
int gsm_modem_rf_on(
    int hdev
)

activates the radio interface of the modem

Parameters:

Return: 0 on success, -1 on failure

function gsm_modem_rf_off

cpp
int gsm_modem_rf_off(
    int hdev
)

turns the radio interface of the modem off

Parameters:

Return: 0 on success, -1 on failure

function gsm_modem_sleep

cpp
int gsm_modem_sleep(
    int hdev
)

puts the modem in power save mode immediately

Parameters:

Return: 0 on success, -1 on failure

the modem will not automatically wake up when a previously set PSM timer runs out. It has to be explicitly woken up by gsm_modem_wakeup (int hdev). Any open connection to the PS-Domain will be closed and has to be re-established after waking the modem up again. An attachment to the network will be kept for the time, the MNO confirmed after requesting a PSM setting.

function gsm_modem_wakeup

cpp
int gsm_modem_wakeup(
    int hdev
)

wakes the modem from PSM sleep immediately

Parameters:

Return: 0 on success, -1 on failure

function gsm_modem_connect

cpp
int gsm_modem_connect(
    int hdev,
    int wait
)

Dials the data call and brings up the PPP session for the APN's PDP context.

Parameters:

  • hdev device handle as returned by open(const char*)
  • wait 1 = block until the connection succeeds or fails, 0 = return immediately

Return: 0 on success (with wait == 0: dial accepted, PPP coming up). -1 if the driver is not ready, not registered (wait == 0), or the attach failed (wait == 1); -3 if the dial itself failed.

wait only controls the waiting, not what is started: the dial and the PPP bring-up are initiated in both modes.

  • wait == 1: requires network registration (blocks for the attach if necessary) and returns once the connection either succeeded or failed.
  • wait == 0: never blocks. Requires the modem to be registered already (-1 otherwise); 0 means "dialled, PPP is coming up" — poll gsm_modem_connected(int) for the outcome.

A successful call also arms the driver's autonomous supervision: from here on it re-establishes the session and escalates along the recovery rungs on its own until gsm_modem_disconnect is called.

function gsm_modem_disconnect

cpp
int gsm_modem_disconnect(
    int hdev
)

Tears the PPP session down and disconnects the modem from the APN.

Parameters:

Return: 0 on success (including "nothing to tear down"), -1 if the modem is in PSM sleep.

Also aborts a connection attempt that is still in progress and disarms the driver's autonomous reconnect — this call is the only "user disconnect" the driver knows.

function gsm_modem_connected

cpp
int gsm_modem_connected(
    int hdev
)

checks the modem's current connection status in the PS domain

Parameters:

Return: if the modem is connected to the PS domain the function returns 1, otherwise 0.

Reflects the driver's session state (PPP up, PDP context active) — not proof that data actually flows; see the group notes on data-path evidence.

function gsm_modem_registered

cpp
int gsm_modem_registered(
    int hdev
)

checks the modem's current registration status

Parameters:

Return: if the modem is registered to the network the function returns 1, otherwise 0.

function gsm_modem_imei

cpp
int gsm_modem_imei(
    int hdev,
    char * imei
)

Reads the modem's device identifier (IMEI).

Parameters:

  • hdev device handle as returned by open(const char*)
  • imei buffer of at least GSM_MODEM_IMEI_SIZE bytes, allocated by the caller. Filled with the device identifier ("IMEI") and null-terminated on success.

Return: 0 on success, -1 on failure

function gsm_modem_imsi

cpp
int gsm_modem_imsi(
    int hdev,
    char * imsi
)

Reads the SIM's subscriber identity (IMSI).

Parameters:

  • hdev device handle as returned by open(const char*)
  • imsi buffer of at least GSM_MODEM_IMSI_SIZE bytes, allocated by the caller. Filled with the subscriber identity ("IMSI") and null-terminated on success.

Return: 0 on success, -1 on failure

function gsm_modem_iccid

cpp
int gsm_modem_iccid(
    int hdev,
    char * iccid
)

Reads the SIM's card identity (ICCID).

Parameters:

  • hdev device handle as returned by open(const char*)
  • iccid buffer of at least GSM_MODEM_ICCID_SIZE bytes, allocated by the caller. Filled with the card identity ("ICCID") and null-terminated on success.

Return: 0 on success, -1 on failure

function gsm_modem_signal_quality

cpp
int gsm_modem_signal_quality(
    int hdev,
    gsm_signal_info_t * info
)

Reads the current signal quality from the modem.

Parameters:

  • hdev device handle as returned by open(const char*)
  • info filled with the modem's current signal readings; which fields are populated is vendor-dependent (see gsm_signal_info_t).

Return: 0 on success, -1 on failure

Issues an AT transaction on the calling thread — see the group notes on blocking behaviour.

function gsm_modem_sim_status

cpp
int gsm_modem_sim_status(
    int hdev
)

Get the status of the SIM card in the GSM modem.

Parameters:

  • hdev The handle to the GSM modem device

Return: The SIM card status code:

  • 0: SIM card is active
  • -1: SIM card is defective or not inserted

This function retrieves the status of the SIM card inserted in the GSM modem.

function gsm_modem_registration_info

cpp
int gsm_modem_registration_info(
    int hdev,
    gsm_registration_info_t * info
)

Reads the serving cell's identity (MCC/MNC, area code, cell id, RAT).

Parameters:

Return: 0 on success, -1 on failure

Issues an AT transaction on the calling thread — see the group notes on blocking behaviour.

function gsm_modem_version

cpp
int gsm_modem_version(
    int hDev,
    char * version,
    size_t version_len
)

Retrieves the modem's firmware/version string.

Parameters:

  • hDev device handle as returned by open(const char*)
  • version Buffer that receives the version string (null-terminated on success).
  • version_len Capacity of version in bytes.

Return: 0 on success, -1 on failure.

function gsm_modem_power_off

cpp
int gsm_modem_power_off(
    int hDev
)

Power down the modem.

Parameters:

Return: 0 on success, -1 on failure.

function gsm_modem_power_on

cpp
int gsm_modem_power_on(
    int hdev
)

Power on the modem.

Parameters:

Return: 0 on success, -1 on failure.

function gsm_modem_note_data_ok

cpp
int gsm_modem_note_data_ok(
    int hdev
)

Report that user data provably crossed the data path.

Parameters:

Return: 0 on success, -1 if the device does not support the report.

Call this whenever a round trip has been ANSWERED by a peer — not when a datagram was handed to the stack. Only an answer proves that packets reach the network and come back.

The driver cannot obtain this evidence itself: it has no peer, and with ATD*99***1# the PPP link terminates inside the modem, so LCP echo only proves the module is alive. Without this report the driver cannot tell a healthy session from one that is registered, has an active PDP context and an IP address, and still moves nothing.

The caller must guarantee a cadence well below the 5-minute evidence-freshness window that feeds GSM_MODEM_RUNG_DATA_STALL_MS, otherwise a healthy but quiet link is mistaken for a dead one. libcoldwave does this by writing P_UPTIME unconditionally every 2 minutes.

Cheap and lock-free; safe to call from any thread.

function gsm_modem_note_data_lost

cpp
int gsm_modem_note_data_lost(
    int hdev
)

Report that the data path is not carrying traffic any more.

Parameters:

Return: 0 on success, -1 if the device does not support the report.

The counterpart to gsm_modem_note_data_ok. Call this only after your own cheaper repair has already failed — a rebuilt socket, a fresh handshake. The driver treats it as "the layer that can see the peer says it is broken, and it has already tried what it can", and rebuilds the PPP session and the PDP context.

This is the fast path. Without it the driver can only wait for the positive evidence to rot, which takes GSM_MODEM_RUNG_DATA_STALL_MS — a bound sized for the quietest possible reporter and therefore far too slow for one that talks. With it, detection to action is roughly a minute.

Repeat calls are cheap and idempotent: the driver enforces its own minimum spacing between rebuilds (2 min, doubling while they keep not helping), because from down here a dead backend and a dead bearer look exactly alike.

Macros Documentation

define GSM_MODEM_RUNG_DATA_STALL_MS

cpp
#define GSM_MODEM_RUNG_DATA_STALL_MS (570000U)  /* 5 min evidence age + 3 ticks of 90 s */

Rung 1 (ms): rebuild PPP/PDP once positive data-path evidence is this old.

define GSM_MODEM_RUNG_MODEM_RESET_MS

cpp
#define GSM_MODEM_RUNG_MODEM_RESET_MS (1800000U) /* 20 ticks of 90 s */

Rung 2 (ms): reset the modem when rebuilds keep not helping.

define GSM_MODEM_RUNG_CEILING_MS

cpp
#define GSM_MODEM_RUNG_CEILING_MS (7200000U)     /* 80 ticks of 90 s */

Rung 3 (ms): device-reboot ceiling; product deadlines must sit above it.

define GSM_MODEM_IMEI_MAXLEN

cpp
#define GSM_MODEM_IMEI_MAXLEN 20

define GSM_MODEM_IMSI_MAXLEN

cpp
#define GSM_MODEM_IMSI_MAXLEN 20

define GSM_MODEM_ICCID_MAXLEN

cpp
#define GSM_MODEM_ICCID_MAXLEN 20

define GSM_MODEM_IMEI_SIZE

cpp
#define GSM_MODEM_IMEI_SIZE (GSM_MODEM_IMEI_MAXLEN + 1)

Required buffer size for gsm_modem_imei, including the terminating null byte.

define GSM_MODEM_IMSI_SIZE

cpp
#define GSM_MODEM_IMSI_SIZE (GSM_MODEM_IMSI_MAXLEN + 1)

Required buffer size for gsm_modem_imsi, including the terminating null byte.

define GSM_MODEM_ICCID_SIZE

cpp
#define GSM_MODEM_ICCID_SIZE (GSM_MODEM_ICCID_MAXLEN + 1)

Required buffer size for gsm_modem_iccid, including the terminating null byte.

define GSM_MODEM_INIT_DEFAULT

cpp
#define GSM_MODEM_INIT_DEFAULT   {    \
  .bands = LTE_B8,                  \
  .oper = {0},                      \
  .tech = gtEMMC,                   \
  .power_on_reset = 1,              \
  .edrx = 0,                        \
  .psm = 0,                         \
  .apn = {0},                       \
  .apn_auth_type = atCHAP,          \
  .apn_user = {0},                  \
  .apn_pwd = {0},                   \
  .scanseq = {0},                   \
  .use_peer_dns = 1,                \
  .baudrate = 921600                \
  }

Default init parameters for GSM modem, setting it to LTE Cat-M1 (eMTC) on band 8.