diff options
author | Dan Williams <dcbw@redhat.com> | 2012-07-19 16:05:28 -0500 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-08-06 20:07:27 +0200 |
commit | a532c081d15911a289b837f0e0247e131733130e (patch) | |
tree | 6dbd4f7f7b04a08d4f662e46708ed0e1a72ece0f | |
parent | b1c2e0ba5fc001c20249c76a8698c6b5d8ee7531 (diff) |
icera: add generic band handling functions
-rw-r--r-- | plugins/icera/mm-iface-icera.c | 343 | ||||
-rw-r--r-- | plugins/icera/mm-iface-icera.h | 25 |
2 files changed, 368 insertions, 0 deletions
diff --git a/plugins/icera/mm-iface-icera.c b/plugins/icera/mm-iface-icera.c index c99156ff..89862b23 100644 --- a/plugins/icera/mm-iface-icera.c +++ b/plugins/icera/mm-iface-icera.c @@ -12,6 +12,7 @@ * * Copyright (C) 2010 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org> + * Copyright (C) 2012 Google Inc. */ #include <config.h> @@ -732,6 +733,348 @@ mm_iface_icera_modem_load_unlock_retries (MMIfaceModem *self, } /*****************************************************************************/ +/* Load supported bands (Modem interface) */ + +typedef struct { + MMModemBand band; + const char *name; +} BandTable; + +static BandTable 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" }, + /* 2G second */ + { MM_MODEM_BAND_DCS, "DCS" }, + { MM_MODEM_BAND_EGSM, "EGSM" }, + { MM_MODEM_BAND_PCS, "PCS" }, + /* And ANY last since it's most inclusive */ + { MM_MODEM_BAND_ANY, "ANY" }, +}; + + +GArray * +mm_iface_icera_modem_load_supported_bands_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + /* Never fails */ + return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (res))); +} + +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. + */ + 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); +} + +/*****************************************************************************/ +/* Load current bands (Modem interface) */ + +GArray * +mm_iface_icera_modem_load_current_bands_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return NULL; + return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (res))); +} + +static void +load_current_bands_ready (MMIfaceModem *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + GRegex *r; + GMatchInfo *info; + GArray *bands; + const gchar *response; + GError *error; + + 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; + } + + /* + * 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; + + 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_free (band); + g_free (enabled); + g_match_info_next (info, NULL); + } + g_match_info_free (info); + g_regex_unref (r); + + 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 +mm_iface_icera_modem_load_current_bands (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + mm_iface_icera_modem_load_current_bands); + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + "%IPBM?", + 3, + FALSE, + (GAsyncReadyCallback)load_current_bands_ready, + result); +} + +/*****************************************************************************/ +/* Set bands (Modem interface) */ + +typedef struct { + GSimpleAsyncResult *result; + guint bandbits; + guint enablebits; + guint disablebits; +} SetBandsContext; + +/* + * The modem's band-setting command (%IPBM=) enables or disables one + * band at a time, and one band must always be enabled. Here, we first + * get the set of enabled bands, compute the difference between that + * set and the requested set, enable any added bands, and finally + * disable any removed bands. + */ +gboolean +mm_iface_icera_modem_set_bands_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void set_one_band (MMIfaceModem *self, SetBandsContext *ctx); + +static void +set_bands_context_complete_and_free (SetBandsContext *ctx) +{ + g_simple_async_result_complete (ctx->result); + g_object_unref (ctx->result); + g_free (ctx); +} + +static void +set_bands_next (MMIfaceModem *self, + GAsyncResult *res, + SetBandsContext *ctx) +{ + GError *error = NULL; + + if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { + mm_dbg ("Couldn't set bands: '%s'", error->message); + g_simple_async_result_take_error (ctx->result, error); + set_bands_context_complete_and_free (ctx); + return; + } + + set_one_band (self, ctx); +} + +static void +set_one_band (MMIfaceModem *self, + SetBandsContext *ctx) +{ + guint enable, band; + gchar *command; + + /* Find the next band to enable or disable, always doing enables first */ + enable = 1; + band = ffs (ctx->enablebits); + if (band == 0) { + enable = 0; + band = ffs (ctx->disablebits); + } + if (band == 0) { + /* Both enabling and disabling are done */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + set_bands_context_complete_and_free (ctx); + return; + } + + /* Note that ffs() returning 2 corresponds to 1 << 1, not 1 << 2 */ + band--; + mm_dbg("1. enablebits %x disablebits %x band %d enable %d", + ctx->enablebits, ctx->disablebits, band, enable); + + if (enable) + ctx->enablebits &= ~(1 << band); + else + ctx->disablebits &= ~(1 << band); + mm_dbg("2. enablebits %x disablebits %x", + ctx->enablebits, ctx->disablebits); + + command = g_strdup_printf ("%%IPBM=\"%s\",%d", + modem_bands[band].name, + enable); + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + command, + 10, + FALSE, + (GAsyncReadyCallback)set_bands_next, + ctx); + g_free (command); +} + +static guint +band_array_to_bandbits (GArray *bands) +{ + MMModemBand band; + guint i, j, bandbits; + + bandbits = 0; + for (i = 0 ; i < bands->len ; i++) { + band = g_array_index (bands, MMModemBand, i); + for (j = 0 ; j < G_N_ELEMENTS (modem_bands) ; j++) { + if (modem_bands[j].band == band) { + bandbits |= 1 << j; + break; + } + } + g_assert (j < G_N_ELEMENTS (modem_bands)); + } + + return bandbits; +} + +static void +set_bands_got_current_bands (MMIfaceModem *self, + GAsyncResult *res, + SetBandsContext *ctx) +{ + GArray *bands; + GError *error = NULL; + guint currentbits; + + bands = mm_iface_icera_modem_load_current_bands_finish (self, res, &error); + if (!bands) { + g_simple_async_result_take_error (ctx->result, error); + set_bands_context_complete_and_free (ctx); + return; + } + + currentbits = band_array_to_bandbits (bands); + ctx->enablebits = ctx->bandbits & ~currentbits; + ctx->disablebits = currentbits & ~ctx->bandbits; + + set_one_band (self, ctx); +} + +void +mm_iface_icera_modem_set_bands (MMIfaceModem *self, + GArray *bands_array, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SetBandsContext *ctx; + + ctx = g_new0 (SetBandsContext, 1); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + mm_iface_icera_modem_set_bands); + ctx->bandbits = band_array_to_bandbits (bands_array); + /* + * For the sake of efficiency, convert "ANY" to the actual set of + * bands; this matches what we get from load_current_bands and + * minimizes the number of changes we need to make. + * + * This requires that ANY is last in modem_bands and that all the + * other bits are valid. + */ + if (ctx->bandbits == (1 << (G_N_ELEMENTS (modem_bands) - 1))) + ctx->bandbits--; /* clear the top bit, set all lower bits */ + mm_iface_icera_modem_load_current_bands (self, + (GAsyncReadyCallback)set_bands_got_current_bands, + ctx); +} + +/*****************************************************************************/ /* Load network timezone (Time interface) */ static gboolean diff --git a/plugins/icera/mm-iface-icera.h b/plugins/icera/mm-iface-icera.h index 2844fecf..aeacef00 100644 --- a/plugins/icera/mm-iface-icera.h +++ b/plugins/icera/mm-iface-icera.h @@ -99,6 +99,31 @@ MMUnlockRetries *mm_iface_icera_modem_load_unlock_retries_finish (MMIfaceModem * GAsyncResult *res, GError **error); +void mm_iface_icera_modem_load_supported_bands (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data); + +GArray *mm_iface_icera_modem_load_supported_bands_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error); + +void mm_iface_icera_modem_load_current_bands (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data); + +GArray *mm_iface_icera_modem_load_current_bands_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error); + +void mm_iface_icera_modem_set_bands (MMIfaceModem *self, + GArray *bands_array, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean mm_iface_icera_modem_set_bands_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error); + /*****************************************************************************/ /* Modem 3GPP interface specific implementations */ |