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.
This commit is contained in:

committed by
Aleksander Morgado

parent
ea024f2e52
commit
e0c17f936f
@@ -733,36 +733,119 @@ mm_iface_icera_modem_load_unlock_retries (MMIfaceModem *self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Load supported bands (Modem interface) */
|
/* Generic band handling utilities */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MMModemBand band;
|
MMModemBand band;
|
||||||
const char *name;
|
char *name;
|
||||||
} BandTable;
|
gboolean enabled;
|
||||||
|
} Band;
|
||||||
|
|
||||||
static BandTable modem_bands[] = {
|
static void
|
||||||
|
band_free (Band *b)
|
||||||
|
{
|
||||||
|
g_free (b->name);
|
||||||
|
g_free (b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Band modem_bands[] = {
|
||||||
/* Sort 3G first since it's preferred */
|
/* Sort 3G first since it's preferred */
|
||||||
{ MM_MODEM_BAND_U2100, "FDD_BAND_I" },
|
{ MM_MODEM_BAND_U2100, "FDD_BAND_I", FALSE },
|
||||||
{ MM_MODEM_BAND_U1900, "FDD_BAND_II" },
|
{ MM_MODEM_BAND_U1900, "FDD_BAND_II", FALSE },
|
||||||
/*
|
{ MM_MODEM_BAND_U1800, "FDD_BAND_III", FALSE },
|
||||||
* Several bands are forbidden to set and will always read as
|
{ MM_MODEM_BAND_U17IV, "FDD_BAND_IV", FALSE },
|
||||||
* disabled, so we won't present them to the rest of
|
{ MM_MODEM_BAND_U800, "FDD_BAND_VI", FALSE },
|
||||||
* modemmanager.
|
{ MM_MODEM_BAND_U850, "FDD_BAND_V", FALSE },
|
||||||
*/
|
{ MM_MODEM_BAND_U900, "FDD_BAND_VIII", FALSE },
|
||||||
/* { 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 */
|
/* 2G second */
|
||||||
{ MM_MODEM_BAND_DCS, "DCS" },
|
{ MM_MODEM_BAND_G850, "G850", FALSE },
|
||||||
{ MM_MODEM_BAND_EGSM, "EGSM" },
|
{ MM_MODEM_BAND_DCS, "DCS", FALSE },
|
||||||
{ MM_MODEM_BAND_PCS, "PCS" },
|
{ MM_MODEM_BAND_EGSM, "EGSM", FALSE },
|
||||||
|
{ MM_MODEM_BAND_PCS, "PCS", FALSE },
|
||||||
/* And ANY last since it's most inclusive */
|
/* 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 *
|
GArray *
|
||||||
mm_iface_icera_modem_load_supported_bands_finish (MMIfaceModem *self,
|
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)));
|
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
|
void
|
||||||
mm_iface_icera_modem_load_supported_bands (MMIfaceModem *self,
|
mm_iface_icera_modem_load_supported_bands (MMIfaceModem *self,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
GSimpleAsyncResult *result;
|
GSimpleAsyncResult *result;
|
||||||
GArray *bands;
|
|
||||||
guint i;
|
|
||||||
|
|
||||||
result = g_simple_async_result_new (G_OBJECT (self),
|
result = g_simple_async_result_new (G_OBJECT (self),
|
||||||
callback,
|
callback,
|
||||||
user_data,
|
user_data,
|
||||||
mm_iface_icera_modem_load_supported_bands);
|
mm_iface_icera_modem_load_supported_bands);
|
||||||
|
|
||||||
/*
|
/* The modems report some bands as disabled that they don't actually
|
||||||
* The modem doesn't support telling us what bands are supported;
|
* support enabling. Thanks Icera! So we have to try setting each
|
||||||
* list everything we know about.
|
* 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,
|
mm_base_modem_at_command (
|
||||||
bands,
|
MM_BASE_MODEM (self),
|
||||||
(GDestroyNotify)g_array_unref);
|
"%IPBM?",
|
||||||
g_simple_async_result_complete_in_idle (result);
|
3,
|
||||||
g_object_unref (result);
|
FALSE,
|
||||||
|
(GAsyncReadyCallback)load_supported_bands_get_bands_ready,
|
||||||
|
result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -824,11 +1003,11 @@ load_current_bands_ready (MMIfaceModem *self,
|
|||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
GSimpleAsyncResult *operation_result)
|
GSimpleAsyncResult *operation_result)
|
||||||
{
|
{
|
||||||
GRegex *r;
|
|
||||||
GMatchInfo *info;
|
|
||||||
GArray *bands;
|
GArray *bands;
|
||||||
const gchar *response;
|
const gchar *response;
|
||||||
GError *error;
|
GError *error;
|
||||||
|
GSList *parsed, *iter;
|
||||||
|
guint32 len = 0;
|
||||||
|
|
||||||
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
|
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
|
||||||
if (!response) {
|
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_take_error (operation_result, error);
|
||||||
g_simple_async_result_complete (operation_result);
|
g_simple_async_result_complete (operation_result);
|
||||||
g_object_unref (operation_result);
|
g_object_unref (operation_result);
|
||||||
return;
|
} 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;
|
||||||
|
|
||||||
/*
|
g_array_append_val (bands, b->band);
|
||||||
* 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_slist_free_full (parsed, (GDestroyNotify) band_free);
|
||||||
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,
|
g_simple_async_result_set_op_res_gpointer (operation_result,
|
||||||
bands,
|
bands,
|
||||||
(GDestroyNotify)g_array_unref);
|
(GDestroyNotify)g_array_unref);
|
||||||
g_simple_async_result_complete (operation_result);
|
g_simple_async_result_complete (operation_result);
|
||||||
g_object_unref (operation_result);
|
g_object_unref (operation_result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
Reference in New Issue
Block a user