diff options
author | Dan Williams <dcbw@redhat.com> | 2012-11-28 10:23:38 -0600 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2012-12-05 11:27:54 -0600 |
commit | f0ba40f3a1c3ea34c84bfd94cc5f87c4e288f37d (patch) | |
tree | 76fbf04a76ae75478b30a9f8c5f31ed50dca7a76 | |
parent | 8fdcfd5b97e9a2a9e6fd98a9d833691256443498 (diff) |
huawei: implement Huawei-specific CDMA signal quality checks
Many Huawei CDMA modems implement vendor commands for 1x and EVDO
signal quality, so use them since they are more accurate than the
generic signal checking.
(port of a similar patch for MM_06 by heiher <admin@heiher.info>)
-rw-r--r-- | plugins/huawei/mm-broadband-modem-huawei.c | 132 |
1 files changed, 131 insertions, 1 deletions
diff --git a/plugins/huawei/mm-broadband-modem-huawei.c b/plugins/huawei/mm-broadband-modem-huawei.c index 82652617..1c32670c 100644 --- a/plugins/huawei/mm-broadband-modem-huawei.c +++ b/plugins/huawei/mm-broadband-modem-huawei.c @@ -39,12 +39,13 @@ static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface); static void iface_modem_cdma_init (MMIfaceModemCdma *iface); +static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; static MMIfaceModemCdma *iface_modem_cdma_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemHuawei, mm_broadband_modem_huawei, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) @@ -1213,13 +1214,12 @@ decode (MMIfaceModem3gppUssd *self, g_free (bin); g_free (unpacked); return utf8; } /*****************************************************************************/ -/* Setup/Cleanup unsolicited events (CDMA interface) */ static void huawei_1x_signal_changed (MMAtSerialPort *port, GMatchInfo *match_info, MMBroadbandModemHuawei *self) { @@ -1245,12 +1245,138 @@ huawei_evdo_signal_changed (MMAtSerialPort *port, quality = CLAMP (quality, 0, 100); mm_dbg ("EVDO signal quality: %u", quality); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)quality); } +/* Signal quality loading (Modem interface) */ + +static guint +modem_load_signal_quality_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return 0; + + return GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (res))); +} + +static void +parent_load_signal_quality_ready (MMIfaceModem *self, + GAsyncResult *res, + GSimpleAsyncResult *simple) +{ + GError *error = NULL; + guint signal_quality; + + signal_quality = iface_modem_parent->load_signal_quality_finish (self, res, &error); + if (error) + g_simple_async_result_take_error (simple, error); + else + g_simple_async_result_set_op_res_gpointer (simple, + GUINT_TO_POINTER (signal_quality), + NULL); + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +signal_ready (MMBaseModem *self, + GAsyncResult *res, + GSimpleAsyncResult *simple) +{ + const gchar *response, *command; + gchar buf[5]; + guint quality = 0, i = 0; + + response = mm_base_modem_at_command_finish (self, res, NULL); + if (!response) { + /* Fallback to parent's method */ + iface_modem_parent->load_signal_quality ( + MM_IFACE_MODEM (self), + (GAsyncReadyCallback)parent_load_signal_quality_ready, + simple); + return; + } + + command = g_object_get_data (G_OBJECT (simple), "command"); + g_assert (command); + response = mm_strip_tag (response, command); + /* 'command' won't include the trailing ':' in the response, so strip that */ + while ((*response == ':') || isspace (*response)) + response++; + + /* Sanitize response for mm_get_uint_from_str() which wants only digits */ + memset (buf, 0, sizeof (buf)); + while (i < (sizeof (buf) - 1) && isdigit (*response)) + buf[i++] = *response++; + + if (mm_get_uint_from_str (buf, &quality)) { + quality = CLAMP (quality, 0, 100); + g_simple_async_result_set_op_res_gpointer (simple, + GUINT_TO_POINTER (quality), + NULL); + } else { + g_simple_async_result_set_error (simple, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse %s response: '%s'", + command, response); + } + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +modem_load_signal_quality (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + const char *command = "^CSQLVL"; + + mm_dbg ("loading signal quality..."); + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_load_signal_quality); + + /* 3GPP modems can just run parent's signal quality loading */ + if (mm_iface_modem_is_3gpp (self)) { + iface_modem_parent->load_signal_quality ( + self, + (GAsyncReadyCallback)parent_load_signal_quality_ready, + result); + return; + } + + /* CDMA modems need custom signal quality loading */ + + g_object_get (G_OBJECT (self), + MM_IFACE_MODEM_CDMA_EVDO_REGISTRATION_STATE, &evdo_state, + NULL); + if (evdo_state > MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) + command = "^HDRCSQLVL"; + g_object_set_data (G_OBJECT (result), "command", (gpointer) command); + + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + command, + 3, + FALSE, + (GAsyncReadyCallback)signal_ready, + result); +} + +/*****************************************************************************/ +/* Setup/Cleanup unsolicited events (CDMA interface) */ + static void set_cdma_unsolicited_events_handlers (MMBroadbandModemHuawei *self, gboolean enable) { MMAtSerialPort *ports[2]; guint i; @@ -1729,24 +1855,28 @@ finalize (GObject *object) G_OBJECT_CLASS (mm_broadband_modem_huawei_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { + iface_modem_parent = g_type_interface_peek_parent (iface); + iface->load_access_technologies = load_access_technologies; iface->load_access_technologies_finish = load_access_technologies_finish; iface->load_unlock_retries = load_unlock_retries; iface->load_unlock_retries_finish = load_unlock_retries_finish; iface->load_current_bands = load_current_bands; iface->load_current_bands_finish = load_current_bands_finish; iface->set_bands = set_bands; iface->set_bands_finish = set_bands_finish; iface->load_allowed_modes = load_allowed_modes; iface->load_allowed_modes_finish = load_allowed_modes_finish; iface->set_allowed_modes = set_allowed_modes; iface->set_allowed_modes_finish = set_allowed_modes_finish; + iface->load_signal_quality = modem_load_signal_quality; + iface->load_signal_quality_finish = modem_load_signal_quality_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); |