diff options
author | Dan Williams <dcbw@redhat.com> | 2012-07-19 21:27:15 -0500 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-08-06 20:07:27 +0200 |
commit | e0c17f936f83b81296c643d656fa68de36a24883 (patch) | |
tree | cc5b6bae1de922f4516eba5a7084b4a382e3c7a2 | |
parent | ea024f2e520789ac41ec281d3737f74a89df6b74 (diff) |
icera: implement dynamic band support checking
Turns out we can check supported bands by asking the modem what
the enabled state is for the bands it says it supports, and then
setting the enabled state to what it currently is. For bands the
modem actually doesn't support, it'll return an error. Use that
to build up the modem's actual supported band list.
-rw-r--r-- | plugins/icera/mm-iface-icera.c | 311 |
1 files changed, 231 insertions, 80 deletions
diff --git a/plugins/icera/mm-iface-icera.c b/plugins/icera/mm-iface-icera.c index 89862b23..e530d79c 100644 --- a/plugins/icera/mm-iface-icera.c +++ b/plugins/icera/mm-iface-icera.c @@ -733,36 +733,119 @@ mm_iface_icera_modem_load_unlock_retries (MMIfaceModem *self, } /*****************************************************************************/ -/* Load supported bands (Modem interface) */ +/* Generic band handling utilities */ typedef struct { MMModemBand band; - const char *name; -} BandTable; + char *name; + gboolean enabled; +} Band; + +static void +band_free (Band *b) +{ + g_free (b->name); + g_free (b); +} -static BandTable modem_bands[] = { +static const Band modem_bands[] = { /* Sort 3G first since it's preferred */ - { MM_MODEM_BAND_U2100, "FDD_BAND_I" }, - { MM_MODEM_BAND_U1900, "FDD_BAND_II" }, - /* - * Several bands are forbidden to set and will always read as - * disabled, so we won't present them to the rest of - * modemmanager. - */ - /* { MM_MODEM_BAND_U1800, "FDD_BAND_III" }, */ - /* { MM_MODEM_BAND_U17IV, "FDD_BAND_IV" }, */ - /* { MM_MODEM_BAND_U800, "FDD_BAND_VI" }, */ - { MM_MODEM_BAND_U850, "FDD_BAND_V" }, - { MM_MODEM_BAND_U900, "FDD_BAND_VIII" }, - { MM_MODEM_BAND_G850, "G850" }, + { MM_MODEM_BAND_U2100, "FDD_BAND_I", FALSE }, + { MM_MODEM_BAND_U1900, "FDD_BAND_II", FALSE }, + { MM_MODEM_BAND_U1800, "FDD_BAND_III", FALSE }, + { MM_MODEM_BAND_U17IV, "FDD_BAND_IV", FALSE }, + { MM_MODEM_BAND_U800, "FDD_BAND_VI", FALSE }, + { MM_MODEM_BAND_U850, "FDD_BAND_V", FALSE }, + { MM_MODEM_BAND_U900, "FDD_BAND_VIII", FALSE }, /* 2G second */ - { MM_MODEM_BAND_DCS, "DCS" }, - { MM_MODEM_BAND_EGSM, "EGSM" }, - { MM_MODEM_BAND_PCS, "PCS" }, + { MM_MODEM_BAND_G850, "G850", FALSE }, + { MM_MODEM_BAND_DCS, "DCS", FALSE }, + { MM_MODEM_BAND_EGSM, "EGSM", FALSE }, + { MM_MODEM_BAND_PCS, "PCS", FALSE }, /* And ANY last since it's most inclusive */ - { MM_MODEM_BAND_ANY, "ANY" }, + { MM_MODEM_BAND_ANY, "ANY", FALSE }, }; +static MMModemBand +icera_band_to_mm (const char *icera) +{ + int i; + + for (i = 0 ; i < G_N_ELEMENTS (modem_bands); i++) { + if (g_strcmp0 (icera, modem_bands[i].name) == 0) + return modem_bands[i].band; + } + return MM_MODEM_BAND_UNKNOWN; +} + +static GSList * +parse_bands (const gchar *response, guint32 *out_len) +{ + GRegex *r; + GMatchInfo *info; + GSList *bands = NULL; + + g_return_val_if_fail (out_len != NULL, NULL); + + /* + * Response is a number of lines of the form: + * "EGSM": 0 + * "FDD_BAND_I": 1 + * ... + * with 1 and 0 indicating whether the particular band is enabled or not. + */ + r = g_regex_new ("^\"(\\w+)\": (\\d)", + G_REGEX_MULTILINE, G_REGEX_MATCH_NEWLINE_ANY, + NULL); + g_assert (r != NULL); + + g_regex_match (r, response, 0, &info); + while (g_match_info_matches (info)) { + gchar *name, *enabled; + Band *b; + MMModemBand band; + + name = g_match_info_fetch (info, 1); + enabled = g_match_info_fetch (info, 2); + band = icera_band_to_mm (name); + if (band != MM_MODEM_BAND_UNKNOWN) { + b = g_malloc0 (sizeof (Band)); + b->band = band; + b->name = g_strdup (name); + b->enabled = (enabled[0] == '1' ? TRUE : FALSE); + bands = g_slist_append (bands, b); + *out_len = *out_len + 1; + } + g_free (name); + g_free (enabled); + g_match_info_next (info, NULL); + } + g_match_info_free (info); + g_regex_unref (r); + + return bands; +} + +/*****************************************************************************/ +/* Load supported bands (Modem interface) */ + +typedef struct { + MMBaseModemAtCommand *cmds; + GSList *bands; + guint32 idx; +} SupportedBandsContext; + +static void +supported_bands_context_free (SupportedBandsContext *ctx) +{ + guint i; + + for (i = 0; ctx->cmds[i].command; i++) + g_free (ctx->cmds[i].command); + g_free (ctx->cmds); + g_slist_free_full (ctx->bands, (GDestroyNotify) band_free); + g_free (ctx); +} GArray * mm_iface_icera_modem_load_supported_bands_finish (MMIfaceModem *self, @@ -774,35 +857,131 @@ mm_iface_icera_modem_load_supported_bands_finish (MMIfaceModem *self, G_SIMPLE_ASYNC_RESULT (res))); } +static void +load_supported_bands_ready (MMBaseModem *self, + GAsyncResult *res, + GSimpleAsyncResult *simple) +{ + GError *error = NULL; + SupportedBandsContext *ctx = NULL; + GArray *bands; + GSList *iter; + + mm_base_modem_at_sequence_finish (self, res, (gpointer) &ctx, &error); + if (error) + g_simple_async_result_take_error (simple, error); + else { + bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), ctx->idx); + for (iter = ctx->bands; iter; iter = g_slist_next (iter)) { + Band *b = iter->data; + + /* 'enabled' here really means supported/unsupported */ + if (b->enabled) + g_array_append_val (bands, b->band); + } + + g_simple_async_result_set_op_res_gpointer (simple, + bands, + (GDestroyNotify) g_array_unref); + } + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static gboolean +load_supported_bands_response_processor (MMBaseModem *self, + gpointer context, + const gchar *command, + const gchar *response, + gboolean last_command, + const GError *error, + GVariant **result, + GError **result_error) +{ + SupportedBandsContext *ctx = context; + Band *b = g_slist_nth_data (ctx->bands, ctx->idx++); + + /* If there was no error setting the band, that band is supported. We + * abuse the 'enabled' item to mean supported/unsupported. + */ + b->enabled = !error; + + /* Continue to next band */ + return FALSE; +} + +static void +load_supported_bands_get_bands_ready (MMIfaceModem *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + SupportedBandsContext *ctx; + const gchar *response; + GError *error; + GSList *iter; + guint32 len = 0, i; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!response) { + mm_dbg ("Couldn't query current bands: '%s'", error->message); + g_simple_async_result_take_error (operation_result, error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + ctx = g_new0 (SupportedBandsContext, 1); + + /* For each reported band, build up an AT command to set that band + * to its current enabled/disabled state. + */ + ctx->bands = parse_bands (response, &len); + ctx->cmds = g_new0 (MMBaseModemAtCommand, len + 1); + + for (iter = ctx->bands, i = 0; iter; iter = g_slist_next (iter), i++) { + Band *b = iter->data; + + ctx->cmds[i].command = g_strdup_printf ("%%IPBM=\"%s\",%c", + b->name, + b->enabled ? '1' : '0'); + ctx->cmds[i].timeout = 3; + ctx->cmds[i].allow_cached = FALSE; + ctx->cmds[i].response_processor = load_supported_bands_response_processor; + } + + mm_base_modem_at_sequence (MM_BASE_MODEM (self), + ctx->cmds, + ctx, + (GDestroyNotify) supported_bands_context_free, + (GAsyncReadyCallback) load_supported_bands_ready, + operation_result); +} + void mm_iface_icera_modem_load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; - GArray *bands; - guint i; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, mm_iface_icera_modem_load_supported_bands); - /* - * The modem doesn't support telling us what bands are supported; - * list everything we know about. + /* The modems report some bands as disabled that they don't actually + * support enabling. Thanks Icera! So we have to try setting each + * band to it's current enabled/disabled value, and the modem will + * return an error if it doesn't support that band at all. */ - bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), G_N_ELEMENTS (modem_bands)); - for (i = 0 ; i < G_N_ELEMENTS (modem_bands) ; i++) { - if (modem_bands[i].band != MM_MODEM_BAND_ANY) - g_array_append_val(bands, modem_bands[i].band); - } - g_simple_async_result_set_op_res_gpointer (result, - bands, - (GDestroyNotify)g_array_unref); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + "%IPBM?", + 3, + FALSE, + (GAsyncReadyCallback)load_supported_bands_get_bands_ready, + result); } /*****************************************************************************/ @@ -824,11 +1003,11 @@ load_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GSimpleAsyncResult *operation_result) { - GRegex *r; - GMatchInfo *info; GArray *bands; const gchar *response; GError *error; + GSList *parsed, *iter; + guint32 len = 0; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { @@ -836,51 +1015,23 @@ load_current_bands_ready (MMIfaceModem *self, g_simple_async_result_take_error (operation_result, error); g_simple_async_result_complete (operation_result); g_object_unref (operation_result); - return; - } - - /* - * Response is a number of lines of the form: - * "EGSM": 0 - * "FDD_BAND_I": 1 - * ... - * with 1 and 0 indicating whether the particular band is enabled or not. - */ - r = g_regex_new ("^\"(\\w+)\": (\\d)", - G_REGEX_MULTILINE, G_REGEX_MATCH_NEWLINE_ANY, - NULL); - g_assert (r != NULL); - - bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), - G_N_ELEMENTS (modem_bands)); - - g_regex_match (r, response, 0, &info); - while (g_match_info_matches (info)) { - gchar *band, *enabled; + } else { + /* Parse bands from Icera response into MM band numbers */ + parsed = parse_bands (response, &len); + bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), len); + for (iter = parsed; iter; iter = g_slist_next (iter)) { + Band *b = iter->data; - band = g_match_info_fetch (info, 1); - enabled = g_match_info_fetch (info, 2); - if (enabled[0] == '1') { - guint i; - for (i = 0 ; i < G_N_ELEMENTS (modem_bands); i++) { - if (!strcmp (band, modem_bands[i].name)) { - g_array_append_val (bands, modem_bands[i].band); - break; - } - } + g_array_append_val (bands, b->band); } - g_free (band); - g_free (enabled); - g_match_info_next (info, NULL); - } - g_match_info_free (info); - g_regex_unref (r); + g_slist_free_full (parsed, (GDestroyNotify) band_free); - g_simple_async_result_set_op_res_gpointer (operation_result, - bands, - (GDestroyNotify)g_array_unref); - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); + g_simple_async_result_set_op_res_gpointer (operation_result, + bands, + (GDestroyNotify)g_array_unref); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + } } void |