Appearance
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
| sysprop | description |
|---|---|
| gpio_ri | Ring-Indicator GPIO |
| gpio_dcd | Data Carrier Detect (DCD) GPIO |
| gpio_dtr | Data 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 | |
|---|---|
| struct | gsm_modem_init_t GSM modem initialization parameters. |
| struct | gsm_signal_info_t Signal Strength Information. |
| struct | gsm_registration_info_t Serving-cell registration information. |
| struct | gsm_modem_driver |
| enum | gsm_technology_t Modem Radio Access Technology (RAT). |
| enum | apn_auth_t APN Authentication Type for PPP. |
Functions Overview
| Name | |
|---|---|
| int | gsm_modem_init(int hdev, gsm_modem_init_t * init) Initialize the modem. |
| int | gsm_modem_reset(int hdev) Requests a modem reset. |
| int | gsm_modem_rf_on(int hdev) activates the radio interface of the modem |
| int | gsm_modem_rf_off(int hdev) turns the radio interface of the modem off |
| int | gsm_modem_sleep(int hdev) puts the modem in power save mode immediately |
| int | gsm_modem_wakeup(int hdev) wakes the modem from PSM sleep immediately |
| int | gsm_modem_connect(int hdev, int wait) Dials the data call and brings up the PPP session for the APN's PDP context. |
| int | gsm_modem_disconnect(int hdev) Tears the PPP session down and disconnects the modem from the APN. |
| int | gsm_modem_connected(int hdev) checks the modem's current connection status in the PS domain |
| int | gsm_modem_registered(int hdev) checks the modem's current registration status |
| int | gsm_modem_imei(int hdev, char * imei) Reads the modem's device identifier (IMEI). |
| int | gsm_modem_imsi(int hdev, char * imsi) Reads the SIM's subscriber identity (IMSI). |
| int | gsm_modem_iccid(int hdev, char * iccid) Reads the SIM's card identity (ICCID). |
| int | gsm_modem_signal_quality(int hdev, gsm_signal_info_t * info) Reads the current signal quality from the modem. |
| int | gsm_modem_sim_status(int hdev) Get the status of the SIM card in the GSM modem. |
| 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). |
| int | gsm_modem_version(int hDev, char * version, size_t version_len) Retrieves the modem's firmware/version string. |
| int | gsm_modem_power_off(int hDev) Power down the modem. |
| int | gsm_modem_power_on(int hdev) Power on the modem. |
| int | gsm_modem_note_data_ok(int hdev) Report that user data provably crossed the data path. |
| int | gsm_modem_note_data_lost(int hdev) Report that the data path is not carrying traffic any more. |
Defines
| Name | |
|---|---|
| GSM_MODEM_RUNG_DATA_STALL_MS | Rung 1 (ms): rebuild PPP/PDP once positive data-path evidence is this old. |
| GSM_MODEM_RUNG_MODEM_RESET_MS | Rung 2 (ms): reset the modem when rebuilds keep not helping. |
| GSM_MODEM_RUNG_CEILING_MS | Rung 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_SIZE | Required buffer size for gsm_modem_imei, including the terminating null byte. |
| GSM_MODEM_IMSI_SIZE | Required buffer size for gsm_modem_imsi, including the terminating null byte. |
| GSM_MODEM_ICCID_SIZE | Required buffer size for gsm_modem_iccid, including the terminating null byte. |
| GSM_MODEM_INIT_DEFAULT | Default init parameters for GSM modem, setting it to LTE Cat-M1 (eMTC) on band 8. |
Types Documentation
enum gsm_technology_t
| Enumerator | Description |
|---|---|
| gtNBIOT | LTE Cat-NB1 / Cat-NB2 (NB-IoT) only. |
| gtEMMC | LTE Cat-M1 (eMTC) only. |
| gtEMMC_NBIOT | Cat-M1 preferred, then NB-IoT. |
| gtNBIOT_EMMC | NB-IoT preferred, then Cat-M1. |
| gtGSM | 2G (GPRS/EDGE); module-dependent |
| gtLTE | LTE 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
| Enumerator | Description |
|---|---|
| atNONE | No Authentication required. |
| atPAP | PAP Authentication only. |
| atCHAP | CHAP Authentication only. |
| atPAPCHAP | Both 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:
- hdev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
- info filled with the current registration info (see gsm_registration_info_t); only valid while registered.
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
versionin 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:
- hDev device handle as returned by open(const char*)
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:
- hDev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
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:
- hdev device handle as returned by open(const char*)
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 20define GSM_MODEM_IMSI_MAXLEN
cpp
#define GSM_MODEM_IMSI_MAXLEN 20define GSM_MODEM_ICCID_MAXLEN
cpp
#define GSM_MODEM_ICCID_MAXLEN 20define 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.