aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mm-broadband-modem.c226
1 files changed, 170 insertions, 56 deletions
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 4445fe78..adf6e73d 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -35,6 +35,9 @@
#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-error-helpers.h"
+#include "mm-qcdm-serial-port.h"
+#include "libqcdm/src/errors.h"
+#include "libqcdm/src/commands.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
@@ -828,6 +831,23 @@ load_supported_modes (MMIfaceModem *self,
/*****************************************************************************/
/* SIGNAL QUALITY */
+typedef struct {
+ MMBroadbandModem *self;
+ GSimpleAsyncResult *result;
+ MMSerialPort *port;
+} LoadSignalQualityContext;
+
+static void
+load_signal_quality_context_complete_and_free (LoadSignalQualityContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ if (ctx->port)
+ g_object_unref (ctx->port);
+ g_free (ctx);
+}
+
static guint
load_signal_quality_finish (MMIfaceModem *self,
GAsyncResult *res,
@@ -843,17 +863,16 @@ load_signal_quality_finish (MMIfaceModem *self,
static void
load_signal_quality_csq_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ LoadSignalQualityContext *ctx)
{
GError *error = NULL;
GVariant *result;
const gchar *result_str;
- result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, &error);
+ result = mm_base_modem_at_sequence_in_port_finish (MM_BASE_MODEM (self), res, NULL, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_simple_async_result_take_error (ctx->result, error);
+ load_signal_quality_context_complete_and_free (ctx);
return;
}
@@ -868,28 +887,26 @@ load_signal_quality_csq_ready (MMBroadbandModem *self,
/* 99 means unknown */
if (quality == 99) {
g_simple_async_result_take_error (
- simple,
+ ctx->result,
mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK));
} else {
/* Normalize the quality */
quality = CLAMP (quality, 0, 31) * 100 / 31;
- g_simple_async_result_set_op_res_gpointer (simple,
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
GUINT_TO_POINTER (quality),
NULL);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ load_signal_quality_context_complete_and_free (ctx);
return;
}
}
- g_simple_async_result_set_error (simple,
+ g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Could not parse signal quality results");
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ load_signal_quality_context_complete_and_free (ctx);
}
/* Some modems want +CSQ, others want +CSQ?, and some of both types
@@ -903,23 +920,23 @@ static const MMBaseModemAtCommand signal_quality_csq[] = {
};
static void
-load_signal_quality_csq (MMBroadbandModem *self,
- GSimpleAsyncResult *result)
+load_signal_quality_csq (LoadSignalQualityContext *ctx)
{
- mm_base_modem_at_sequence (
- MM_BASE_MODEM (self),
+ mm_base_modem_at_sequence_in_port (
+ MM_BASE_MODEM (ctx->self),
+ MM_AT_SERIAL_PORT (ctx->port),
signal_quality_csq,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
NULL, /* cancellable */
(GAsyncReadyCallback)load_signal_quality_csq_ready,
- result);
+ ctx);
}
static void
load_signal_quality_cind_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ LoadSignalQualityContext *ctx)
{
GError *error = NULL;
const gchar *result;
@@ -929,49 +946,123 @@ load_signal_quality_cind_ready (MMBroadbandModem *self,
if (!error)
indicators = mm_parse_cind_query_response (result, &error);
- if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- if (indicators->len >= self->priv->cind_indicator_signal_quality) {
+ if (error)
+ g_simple_async_result_take_error (ctx->result, error);
+ else if (indicators->len >= self->priv->cind_indicator_signal_quality) {
guint quality;
+
quality = g_array_index (indicators, guint8, self->priv->cind_indicator_signal_quality);
quality = CLAMP (quality, 0, 5) * 20;
- g_simple_async_result_set_op_res_gpointer (simple,
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
GUINT_TO_POINTER (quality),
NULL);
g_byte_array_free (indicators, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ } else
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse CIND signal quality results "
+ "signal index (%u) outside received range (0-%u)",
+ self->priv->cind_indicator_signal_quality,
+ indicators->len);
+
+ load_signal_quality_context_complete_and_free (ctx);
+}
+
+static void
+load_signal_quality_cind (LoadSignalQualityContext *ctx)
+{
+ mm_base_modem_at_command_in_port (MM_BASE_MODEM (ctx->self),
+ MM_AT_SERIAL_PORT (ctx->port),
+ "+CIND?",
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)load_signal_quality_cind_ready,
+ ctx);
+}
+
+static void
+load_signal_quality_qcdm_ready (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data)
+{
+ LoadSignalQualityContext *ctx = user_data;
+ QcdmResult *result;
+ guint32 num = 0, quality = 0, i;
+ gfloat best_db = -28;
+ gint err = QCDM_SUCCESS;
+
+ if (error) {
+ g_simple_async_result_set_from_error (ctx->result, error);
+ load_signal_quality_context_complete_and_free (ctx);
return;
}
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Could not parse CIND signal quality results "
- "signal index (%u) outside received range (0-%u)",
- self->priv->cind_indicator_signal_quality,
- indicators->len);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ /* Parse the response */
+ result = qcdm_cmd_pilot_sets_result ((const gchar *) response->data,
+ response->len,
+ &err);
+ if (!result) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse pilot sets command result: %d",
+ err);
+ load_signal_quality_context_complete_and_free (ctx);
+ return;
+ }
+
+ qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE, &num);
+ for (i = 0; i < num; i++) {
+ guint32 pn_offset = 0, ecio = 0;
+ gfloat db = 0;
+
+ qcdm_cmd_pilot_sets_result_get_pilot (result,
+ QCDM_CMD_PILOT_SETS_TYPE_ACTIVE,
+ i,
+ &pn_offset,
+ &ecio,
+ &db);
+ best_db = MAX (db, best_db);
+ }
+ qcdm_result_unref (result);
+
+ if (num > 0) {
+ #define BEST_ECIO 3
+ #define WORST_ECIO 25
+
+ /* EC/IO dB ranges from roughly 0 to -31 dB. Lower == worse. We
+ * really only care about -3 to -25 dB though, since that's about what
+ * you'll see in real-world usage.
+ */
+ best_db = CLAMP (ABS (best_db), BEST_ECIO, WORST_ECIO) - BEST_ECIO;
+ quality = (guint32) (100 - (best_db * 100 / (WORST_ECIO - BEST_ECIO)));
+ }
+
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ GUINT_TO_POINTER (quality),
+ NULL);
+ load_signal_quality_context_complete_and_free (ctx);
}
static void
-load_signal_quality_cind (MMBroadbandModem *self,
- GSimpleAsyncResult *result)
+load_signal_quality_qcdm (LoadSignalQualityContext *ctx)
{
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- "+CIND?",
- 3,
- FALSE,
- NULL, /* cancellable */
- (GAsyncReadyCallback)load_signal_quality_cind_ready,
- result);
+ GByteArray *pilot_sets;
+
+ /* Use CDMA1x pilot EC/IO if we can */
+ pilot_sets = g_byte_array_sized_new (25);
+ pilot_sets->len = qcdm_cmd_pilot_sets_new ((char *) pilot_sets->data, 25);
+ g_assert (pilot_sets->len);
+
+ mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (ctx->port),
+ pilot_sets,
+ 3,
+ load_signal_quality_qcdm_ready,
+ ctx);
}
static void
@@ -979,18 +1070,41 @@ load_signal_quality (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ MMSerialPort *port;
+ LoadSignalQualityContext *ctx;
+ GError *error = NULL;
mm_dbg ("loading signal quality...");
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_signal_quality);
+ ctx = g_new0 (LoadSignalQualityContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ load_signal_quality);
- if (MM_BROADBAND_MODEM (self)->priv->cind_supported)
- load_signal_quality_cind (MM_BROADBAND_MODEM (self), result);
- else
- load_signal_quality_csq (MM_BROADBAND_MODEM (self), result);
+ /* Check whether we can get a non-connected AT port */
+ port = (MMSerialPort *)mm_base_modem_get_best_at_port (MM_BASE_MODEM (self), &error);
+ if (port) {
+ ctx->port = g_object_ref (port);
+ if (MM_BROADBAND_MODEM (self)->priv->cind_supported)
+ load_signal_quality_cind (ctx);
+ else
+ load_signal_quality_csq (ctx);
+ return;
+ }
+
+ /* If no best AT port available (all connected), try with QCDM ports */
+ port = (MMSerialPort *)mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
+ if (port) {
+ g_error_free (error);
+ ctx->port = g_object_ref (port);
+ load_signal_quality_qcdm (ctx);
+ return;
+ }
+
+ /* Return the error we got when getting best AT port */
+ g_simple_async_result_take_error (ctx->result, error);
+ load_signal_quality_context_complete_and_free (ctx);
}
/*****************************************************************************/