broadband-modem: implement default serving system and service status retrieval

This commit is contained in:
Aleksander Morgado
2012-01-04 22:47:36 +01:00
parent 9efabb8fcb
commit e28183274f
2 changed files with 340 additions and 2 deletions

View File

@@ -2967,6 +2967,332 @@ get_call_manager_state (MMIfaceModemCdma *self,
ctx);
}
/*****************************************************************************/
/* 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 {
@@ -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

View File

@@ -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 (