diff options
-rw-r--r-- | src/mm-broadband-modem.c | 147 | ||||
-rw-r--r-- | src/mm-broadband-modem.h | 2 |
2 files changed, 142 insertions, 7 deletions
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index a7a4df77..60a9be9b 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -68,6 +68,7 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModem, mm_broadband_modem, MM_TYPE_BASE_MODEM enum { PROP_0, + PROP_USE_WS46, PROP_MODEM_DBUS_SKELETON, PROP_MODEM_3GPP_DBUS_SKELETON, PROP_MODEM_3GPP_USSD_DBUS_SKELETON, @@ -100,6 +101,9 @@ enum { #define CIND_INDICATOR_IS_VALID(u) (u != CIND_INDICATOR_INVALID) struct _MMBroadbandModemPrivate { + /* Broadband modem specific implementation */ + gboolean use_ws46; + /*<--- Modem interface --->*/ /* Properties */ GObject *modem_dbus_skeleton; @@ -917,6 +921,95 @@ modem_load_supported_modes_finish (MMIfaceModem *self, } static void +supported_networks_query_ready (MMBroadbandModem *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + const gchar *response; + GError *error = NULL; + MMModemMode mode = MM_MODEM_MODE_NONE; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (error) { + /* Let the error be critical. */ + g_simple_async_result_take_error (operation_result, error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + /* + * More than one numeric ID may appear in the list, that's why + * they are checked separately. + * + * NOTE: Do not skip WS46 prefix; it would break Cinterion handling. + * + * From 3GPP TS 27.007 v.11.2.0, section 5.9 + * 12 GSM Digital Cellular Systems (GERAN only) + * 22 UTRAN only + * 25 3GPP Systems (GERAN, UTRAN and E-UTRAN) + * 28 E-UTRAN only + * 29 GERAN and UTRAN + * 30 GERAN and E-UTRAN + * 31 UTRAN and E-UTRAN + */ + + if (strstr (response, "12") != NULL) { + mm_dbg ("Device allows 2G-only network mode"); + mode |= MM_MODEM_MODE_2G; + } + + if (strstr (response, "22") != NULL) { + mm_dbg ("Device allows 3G-only network mode"); + mode |= MM_MODEM_MODE_3G; + } + + if (strstr (response, "28") != NULL) { + mm_dbg ("Device allows 4G-only network mode"); + mode |= MM_MODEM_MODE_4G; + } + + if (strstr (response, "29") != NULL) { + mm_dbg ("Device allows 2G/3G network mode"); + mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); + } + + if (strstr (response, "30") != NULL) { + mm_dbg ("Device allows 2G/4G network mode"); + mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G); + } + + if (strstr (response, "31") != NULL) { + mm_dbg ("Device allows 3G/4G network mode"); + mode |= (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); + } + + if (strstr (response, "25") != NULL) { + if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) { + mm_dbg ("Device allows every supported 3GPP network mode (2G/3G/4G)"); + mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G); + } else { + mm_dbg ("Device allows every supported 3GPP network mode (2G/3G)"); + mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); + } + } + + /* If no expected ID found, error */ + if (mode == MM_MODEM_MODE_NONE) + g_simple_async_result_set_error (operation_result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Invalid list of supported networks: '%s'", + response); + else + g_simple_async_result_set_op_res_gpointer (operation_result, + GUINT_TO_POINTER (mode), + NULL); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); +} + +static void modem_load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) @@ -931,11 +1024,27 @@ modem_load_supported_modes (MMIfaceModem *self, user_data, modem_load_supported_modes); + if (broadband->priv->use_ws46) { + /* We try to query the list of supported networks, which gives a more + * detailed view of the supported modes (specifically between 2G and 3G) + */ + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + "+WS46=?", + 3, + FALSE, + (GAsyncReadyCallback)supported_networks_query_ready, + result); + return; + } + + /* Try to guess from capabilities */ + mode = MM_MODEM_MODE_NONE; /* If the modem has +GSM caps... */ - if (mm_iface_modem_is_3gpp (self)) { - /* There are modems which only support CS connections (e.g. Iridium) */ + if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) { + /* Some modems, e.g. Iridium, will only support CS */ if (broadband->priv->modem_3gpp_cs_network_supported) mode |= MM_MODEM_MODE_CS; /* If PS supported, assume we can do both 2G and 3G, even if it may not really @@ -946,7 +1055,7 @@ modem_load_supported_modes (MMIfaceModem *self, } /* If the modem has CDMA caps... */ - if (mm_iface_modem_is_cdma (self)) { + if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) { if (broadband->priv->modem_cdma_cdma1x_network_supported) mode |= MM_MODEM_MODE_2G; if (broadband->priv->modem_cdma_evdo_network_supported) @@ -954,12 +1063,20 @@ modem_load_supported_modes (MMIfaceModem *self, } /* If the modem has LTE caps, it does 4G */ - if (mm_iface_modem_is_3gpp_lte (self)) + if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) mode |= MM_MODEM_MODE_4G; - g_simple_async_result_set_op_res_gpointer (result, - GUINT_TO_POINTER (mode), - NULL); + /* If no mode found, error */ + if (mode == MM_MODEM_MODE_NONE) + g_simple_async_result_set_error (result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't guess modes from capabilities"); + else + g_simple_async_result_set_op_res_gpointer (result, + GUINT_TO_POINTER (mode), + NULL); + g_simple_async_result_complete_in_idle (result); g_object_unref (result); } @@ -6883,6 +7000,9 @@ set_property (GObject *object, MMBroadbandModem *self = MM_BROADBAND_MODEM (object); switch (prop_id) { + case PROP_USE_WS46: + self->priv->use_ws46 = g_value_get_boolean (value); + break; case PROP_MODEM_DBUS_SKELETON: g_clear_object (&self->priv->modem_dbus_skeleton); self->priv->modem_dbus_skeleton = g_value_dup_object (value); @@ -6982,6 +7102,9 @@ get_property (GObject *object, MMBroadbandModem *self = MM_BROADBAND_MODEM (object); switch (prop_id) { + case PROP_USE_WS46: + g_value_set_boolean (value, self->priv->use_ws46); + break; case PROP_MODEM_DBUS_SKELETON: g_value_set_object (value, self->priv->modem_dbus_skeleton); break; @@ -7067,6 +7190,7 @@ mm_broadband_modem_init (MMBroadbandModem *self) self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BROADBAND_MODEM, MMBroadbandModemPrivate); + self->priv->use_ws46 = FALSE; self->priv->modem_state = MM_MODEM_STATE_UNKNOWN; self->priv->modem_3gpp_registration_regex = mm_3gpp_creg_regex_get (TRUE); self->priv->modem_current_charset = MM_MODEM_CHARSET_UNKNOWN; @@ -7363,6 +7487,15 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass) klass->setup_ports = setup_ports; + g_object_class_install_property ( + object_class, + PROP_USE_WS46, + g_param_spec_boolean (MM_BROADBAND_MODEM_USE_WS46, + "Use AT+WS46", + "Whether the modem should use AT+WS46=? when loading supported modes", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_override_property (object_class, PROP_MODEM_DBUS_SKELETON, MM_IFACE_MODEM_DBUS_SKELETON); diff --git a/src/mm-broadband-modem.h b/src/mm-broadband-modem.h index 3b15ec91..ab885300 100644 --- a/src/mm-broadband-modem.h +++ b/src/mm-broadband-modem.h @@ -30,6 +30,8 @@ #define MM_IS_BROADBAND_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM)) #define MM_BROADBAND_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM, MMBroadbandModemClass)) +#define MM_BROADBAND_MODEM_USE_WS46 "broadband-modem-use-ws46" + typedef struct _MMBroadbandModem MMBroadbandModem; typedef struct _MMBroadbandModemClass MMBroadbandModemClass; typedef struct _MMBroadbandModemPrivate MMBroadbandModemPrivate; |