aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2012-01-04 22:47:36 +0100
committerAleksander Morgado <aleksander@lanedo.com>2012-03-15 14:14:45 +0100
commite28183274f6b10af42f2ec9e6e77f1a9cb01ee03 (patch)
treec4a2b30d24a26750865496058fcc317ece8773a3
parent9efabb8fcb9aa0c8e7d8afb3e313082fc9773e64 (diff)
broadband-modem: implement default serving system and service status retrieval
-rw-r--r--src/mm-broadband-modem.c330
-rw-r--r--src/mm-iface-modem-cdma.c12
2 files changed, 340 insertions, 2 deletions
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 03a4bf3a..671d56f1 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -2968,6 +2968,332 @@ get_call_manager_state (MMIfaceModemCdma *self,
}
/*****************************************************************************/
+/* Serving System (CDMA) */
+
+typedef struct {
+ guint sid;
+ guint class;
+ guint band;
+} Cdma1xServingSystemResults;
+
+typedef struct {
+ MMBroadbandModem *self;
+ GSimpleAsyncResult *result;
+ MMQcdmSerialPort *qcdm;
+} Cdma1xServingSystemContext;
+
+static void
+cdma1x_serving_system_context_complete_and_free (Cdma1xServingSystemContext *ctx)
+{
+ g_simple_async_result_complete (ctx->result);
+ if (ctx->qcdm)
+ g_object_unref (ctx->qcdm);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ g_free (ctx);
+}
+
+static gboolean
+get_cdma1x_serving_system_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ guint *class,
+ guint *band,
+ guint *sid,
+ GError **error)
+{
+ Cdma1xServingSystemResults *results;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return FALSE;
+
+ results = (Cdma1xServingSystemResults *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ *sid = results->sid;
+ *class = results->class;
+ *band = results->band;
+ return TRUE;
+}
+
+static void
+get_cdma1x_serving_system_at_ready (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ Cdma1xServingSystemContext *ctx)
+{
+ GError *error = NULL;
+ const gchar *result;
+ gint class = 0;
+ gint sid = MM_MODEM_CDMA_SID_UNKNOWN;
+ gint num;
+ guchar band = 'Z';
+ gboolean class_ok = FALSE;
+ gboolean band_ok = FALSE;
+ gboolean success = FALSE;
+ Cdma1xServingSystemResults *results;
+
+ result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ g_simple_async_result_take_error (ctx->result, error);
+ cdma1x_serving_system_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Strip any leading command tag and spaces */
+ result = mm_strip_tag (result, "+CSS:");
+ num = sscanf (result, "? , %d", &sid);
+ if (num == 1) {
+ /* UTStarcom and Huawei modems that use IS-707-A format; note that
+ * this format obviously doesn't have other indicators like band and
+ * class and thus SID 0 will be reported as "no service" (see below).
+ */
+ class = 0;
+ band = 'Z';
+ success = TRUE;
+ } else {
+ GRegex *r;
+ GMatchInfo *match_info;
+
+ /* Format is "<band_class>,<band>,<sid>" */
+ r = g_regex_new ("\\s*([^,]*?)\\s*,\\s*([^,]*?)\\s*,\\s*(\\d+)", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ if (!r) {
+ g_simple_async_result_set_error (
+ ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse Serving System results (regex creation failed).");
+ cdma1x_serving_system_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_regex_match (r, result, 0, &match_info);
+ if (g_match_info_get_match_count (match_info) >= 3) {
+ gint override_class = 0;
+ gchar *str;
+
+ /* band class */
+ str = g_match_info_fetch (match_info, 1);
+ class = mm_cdma_normalize_class (str);
+ g_free (str);
+
+ /* band */
+ str = g_match_info_fetch (match_info, 2);
+ band = mm_cdma_normalize_band (str, &override_class);
+ if (override_class)
+ class = override_class;
+ g_free (str);
+
+ /* sid */
+ str = g_match_info_fetch (match_info, 3);
+ sid = mm_cdma_convert_sid (str);
+ g_free (str);
+
+ success = TRUE;
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ }
+
+ if (!success) {
+ g_simple_async_result_set_error (
+ ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse Serving System results");
+ cdma1x_serving_system_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Normalize the SID */
+ if (sid < 0 || sid > 32767)
+ sid = MM_MODEM_CDMA_SID_UNKNOWN;
+
+ if (class == 1 || class == 2)
+ class_ok = TRUE;
+ if (band != 'Z')
+ band_ok = TRUE;
+
+ /* Return 'no service' if none of the elements of the +CSS response
+ * indicate that the modem has service. Note that this allows SID 0
+ * when at least one of the other elements indicates service.
+ * Normally we'd treat SID 0 as 'no service' but some modems
+ * (Sierra 5725) sometimes return SID 0 even when registered.
+ */
+ if (sid == 0 && !class_ok && !band_ok)
+ sid = MM_MODEM_CDMA_SID_UNKNOWN;
+
+ /* 99999 means unknown/no service */
+ results = g_new0 (Cdma1xServingSystemResults, 1);
+ results->sid = sid;
+ if (sid != MM_MODEM_CDMA_SID_UNKNOWN) {
+ results->band = band;
+ results->class = class;
+ }
+
+ g_simple_async_result_set_op_res_gpointer (ctx->result, results, (GDestroyNotify)g_free);
+ cdma1x_serving_system_context_complete_and_free (ctx);
+}
+
+static void
+get_cdma1x_serving_system_qcdm_ready (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ Cdma1xServingSystemContext *ctx)
+{
+ Cdma1xServingSystemResults *results;
+ QcdmResult *result;
+ guint32 sid = 0;
+ guint32 rxstate = 0;
+ gint err = QCDM_SUCCESS;
+
+ if (error ||
+ (result = qcdm_cmd_cdma_status_result ((const gchar *) response->data,
+ response->len,
+ &err)) == NULL) {
+ if (err != QCDM_SUCCESS)
+ mm_dbg ("Failed to parse cdma status command result: %d", err);
+ /* If there was some error, fall back to use +CSS like we did before QCDM */
+ mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ "+CSS?",
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)get_cdma1x_serving_system_at_ready,
+ ctx);
+ return;
+ }
+
+ qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, &rxstate);
+ qcdm_result_get_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, &sid);
+ qcdm_result_unref (result);
+
+ /* 99999 means unknown/no service */
+ if (rxstate == QCDM_CMD_CDMA_STATUS_RX_STATE_ENTERING_CDMA)
+ sid = MM_MODEM_CDMA_SID_UNKNOWN;
+
+ results = g_new0 (Cdma1xServingSystemResults, 1);
+ results->sid = sid;
+ if (sid != MM_MODEM_CDMA_SID_UNKNOWN) {
+ results->band = 'Z';
+ results->class = 0;
+ }
+
+ g_simple_async_result_set_op_res_gpointer (ctx->result, results, (GDestroyNotify)g_free);
+ cdma1x_serving_system_context_complete_and_free (ctx);
+}
+
+static void
+get_cdma1x_serving_system (MMIfaceModemCdma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Cdma1xServingSystemContext *ctx;
+
+ /* Setup context */
+ ctx = g_new0 (Cdma1xServingSystemContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ get_cdma1x_serving_system);
+ ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
+
+ if (ctx->qcdm) {
+ GByteArray *cdma_status;
+
+ g_object_ref (ctx->qcdm);
+
+ /* Setup command */
+ cdma_status = g_byte_array_sized_new (25);
+ cdma_status->len = qcdm_cmd_cdma_status_new ((char *) cdma_status->data, 25);
+ g_assert (cdma_status->len);
+ mm_qcdm_serial_port_queue_command (ctx->qcdm,
+ cdma_status,
+ 3,
+ (MMQcdmSerialResponseFn)get_cdma1x_serving_system_qcdm_ready,
+ ctx);
+ return;
+ }
+
+ /* Try with AT if we don't have QCDM */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CSS?",
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)get_cdma1x_serving_system_at_ready,
+ ctx);
+}
+
+/*****************************************************************************/
+/* Service status, analog/digital check (CDMA) */
+
+static gboolean
+get_service_status_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ gboolean *has_cdma_service,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return FALSE;
+
+ *has_cdma_service = g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
+ return TRUE;
+}
+
+static void
+get_service_status_ready (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ GError *error = NULL;
+ const gchar *result;
+
+ result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error)
+ g_simple_async_result_take_error (simple, error);
+ else {
+ gulong int_cad;
+
+ /* Strip any leading command tag and spaces */
+ result = mm_strip_tag (result, "+CAD:");
+ errno = 0;
+ int_cad = strtol (result, NULL, 10);
+ if ((errno == EINVAL) || (errno == ERANGE))
+ g_simple_async_result_set_error (simple,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse +CAD response '%s'",
+ result);
+ else
+ /* 1 == CDMA service */
+ g_simple_async_result_set_op_res_gboolean (simple, (int_cad == 1));
+ }
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+get_service_status (MMIfaceModemCdma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ get_service_status);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CAD?",
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)get_service_status_ready,
+ result);
+}
+
+/*****************************************************************************/
typedef enum {
DISABLING_STEP_FIRST,
@@ -3898,6 +4224,10 @@ iface_modem_cdma_init (MMIfaceModemCdma *iface)
iface->get_call_manager_state_finish = get_call_manager_state_finish;
iface->get_hdr_state = get_hdr_state;
iface->get_hdr_state_finish = get_hdr_state_finish;
+ iface->get_service_status = get_service_status;
+ iface->get_service_status_finish = get_service_status_finish;
+ iface->get_cdma1x_serving_system = get_cdma1x_serving_system;
+ iface->get_cdma1x_serving_system_finish = get_cdma1x_serving_system_finish;
}
static void
diff --git a/src/mm-iface-modem-cdma.c b/src/mm-iface-modem-cdma.c
index b7f05a19..93307314 100644
--- a/src/mm-iface-modem-cdma.c
+++ b/src/mm-iface-modem-cdma.c
@@ -628,8 +628,16 @@ registration_check_step (RunAllRegistrationChecksContext *ctx)
ctx->step++;
case REGISTRATION_CHECK_STEP_AT_CDMA1X_SERVING_SYSTEM:
- /* Plugins may fully disable querying the serving system, for example if
- * they already know that AT+CSS? will return undesired errors. */
+ /* Now that we have some sort of service, check if the the device is
+ * registered on the network.
+ */
+
+ /* Some devices key the AT+CSS? response off the 1X state, but if the
+ * device has EVDO service but no 1X service, then reading AT+CSS? will
+ * error out too early. Let subclasses that know that their AT+CSS?
+ * response is wrong in this case handle more specific registration
+ * themselves; if they do, they'll set these callbacks to NULL..
+ */
if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_cdma1x_serving_system &&
MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_cdma1x_serving_system_finish) {
MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_cdma1x_serving_system (