broadband-modem: query supported networks to get a better supported modes value

If we base our supported modes default guessing only on capabilities listed by
AT+GCAP, we find that we don't know how to differenciate between 2G and 3G 3GPP
modems. So, if supported, we will try to query the list of supported networks
with AT+WS46=?, which explicitly tells us if the modem supports GERAN and/or
UTRAN and/or E-UTRAN. Note that plugins need to request this new behaviour by
setting the `MM_BROADBAND_MODEM_USE_WS46' property to TRUE when creating the
modem object.
This commit is contained in:
Aleksander Morgado
2012-04-10 17:20:32 +02:00
parent b49ddfe930
commit bc0d9ddf5f
2 changed files with 142 additions and 7 deletions

View File

@@ -68,6 +68,7 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModem, mm_broadband_modem, MM_TYPE_BASE_MODEM
enum { enum {
PROP_0, PROP_0,
PROP_USE_WS46,
PROP_MODEM_DBUS_SKELETON, PROP_MODEM_DBUS_SKELETON,
PROP_MODEM_3GPP_DBUS_SKELETON, PROP_MODEM_3GPP_DBUS_SKELETON,
PROP_MODEM_3GPP_USSD_DBUS_SKELETON, PROP_MODEM_3GPP_USSD_DBUS_SKELETON,
@@ -100,6 +101,9 @@ enum {
#define CIND_INDICATOR_IS_VALID(u) (u != CIND_INDICATOR_INVALID) #define CIND_INDICATOR_IS_VALID(u) (u != CIND_INDICATOR_INVALID)
struct _MMBroadbandModemPrivate { struct _MMBroadbandModemPrivate {
/* Broadband modem specific implementation */
gboolean use_ws46;
/*<--- Modem interface --->*/ /*<--- Modem interface --->*/
/* Properties */ /* Properties */
GObject *modem_dbus_skeleton; GObject *modem_dbus_skeleton;
@@ -916,6 +920,95 @@ modem_load_supported_modes_finish (MMIfaceModem *self,
G_SIMPLE_ASYNC_RESULT (res))); G_SIMPLE_ASYNC_RESULT (res)));
} }
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 static void
modem_load_supported_modes (MMIfaceModem *self, modem_load_supported_modes (MMIfaceModem *self,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
@@ -931,11 +1024,27 @@ modem_load_supported_modes (MMIfaceModem *self,
user_data, user_data,
modem_load_supported_modes); 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; mode = MM_MODEM_MODE_NONE;
/* If the modem has +GSM caps... */ /* If the modem has +GSM caps... */
if (mm_iface_modem_is_3gpp (self)) { if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) {
/* There are modems which only support CS connections (e.g. Iridium) */ /* Some modems, e.g. Iridium, will only support CS */
if (broadband->priv->modem_3gpp_cs_network_supported) if (broadband->priv->modem_3gpp_cs_network_supported)
mode |= MM_MODEM_MODE_CS; mode |= MM_MODEM_MODE_CS;
/* If PS supported, assume we can do both 2G and 3G, even if it may not really /* 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 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) if (broadband->priv->modem_cdma_cdma1x_network_supported)
mode |= MM_MODEM_MODE_2G; mode |= MM_MODEM_MODE_2G;
if (broadband->priv->modem_cdma_evdo_network_supported) 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 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; mode |= MM_MODEM_MODE_4G;
/* 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, g_simple_async_result_set_op_res_gpointer (result,
GUINT_TO_POINTER (mode), GUINT_TO_POINTER (mode),
NULL); NULL);
g_simple_async_result_complete_in_idle (result); g_simple_async_result_complete_in_idle (result);
g_object_unref (result); g_object_unref (result);
} }
@@ -6883,6 +7000,9 @@ set_property (GObject *object,
MMBroadbandModem *self = MM_BROADBAND_MODEM (object); MMBroadbandModem *self = MM_BROADBAND_MODEM (object);
switch (prop_id) { switch (prop_id) {
case PROP_USE_WS46:
self->priv->use_ws46 = g_value_get_boolean (value);
break;
case PROP_MODEM_DBUS_SKELETON: case PROP_MODEM_DBUS_SKELETON:
g_clear_object (&self->priv->modem_dbus_skeleton); g_clear_object (&self->priv->modem_dbus_skeleton);
self->priv->modem_dbus_skeleton = g_value_dup_object (value); self->priv->modem_dbus_skeleton = g_value_dup_object (value);
@@ -6982,6 +7102,9 @@ get_property (GObject *object,
MMBroadbandModem *self = MM_BROADBAND_MODEM (object); MMBroadbandModem *self = MM_BROADBAND_MODEM (object);
switch (prop_id) { switch (prop_id) {
case PROP_USE_WS46:
g_value_set_boolean (value, self->priv->use_ws46);
break;
case PROP_MODEM_DBUS_SKELETON: case PROP_MODEM_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_dbus_skeleton); g_value_set_object (value, self->priv->modem_dbus_skeleton);
break; break;
@@ -7067,6 +7190,7 @@ mm_broadband_modem_init (MMBroadbandModem *self)
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
MM_TYPE_BROADBAND_MODEM, MM_TYPE_BROADBAND_MODEM,
MMBroadbandModemPrivate); MMBroadbandModemPrivate);
self->priv->use_ws46 = FALSE;
self->priv->modem_state = MM_MODEM_STATE_UNKNOWN; self->priv->modem_state = MM_MODEM_STATE_UNKNOWN;
self->priv->modem_3gpp_registration_regex = mm_3gpp_creg_regex_get (TRUE); self->priv->modem_3gpp_registration_regex = mm_3gpp_creg_regex_get (TRUE);
self->priv->modem_current_charset = MM_MODEM_CHARSET_UNKNOWN; 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; 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, g_object_class_override_property (object_class,
PROP_MODEM_DBUS_SKELETON, PROP_MODEM_DBUS_SKELETON,
MM_IFACE_MODEM_DBUS_SKELETON); MM_IFACE_MODEM_DBUS_SKELETON);

View File

@@ -30,6 +30,8 @@
#define MM_IS_BROADBAND_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM)) #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_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 _MMBroadbandModem MMBroadbandModem;
typedef struct _MMBroadbandModemClass MMBroadbandModemClass; typedef struct _MMBroadbandModemClass MMBroadbandModemClass;
typedef struct _MMBroadbandModemPrivate MMBroadbandModemPrivate; typedef struct _MMBroadbandModemPrivate MMBroadbandModemPrivate;