diff options
author | heiher <admin@heiher.info> | 2012-11-27 12:15:54 -0600 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2012-11-28 14:08:38 -0600 |
commit | b0816d114784f6ee87ec05d79809a37775614193 (patch) | |
tree | d1fae5d120928ed780624e58e060bfbf31dd165c | |
parent | 38905d2bc0c4a3e38050ef5995c518a437e08d17 (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.
(dcbw: reorganize a bit; add fallbacks on error; consolidate
signal quality parsing with unsolicited handlers)
-rw-r--r-- | plugins/mm-modem-huawei-cdma.c | 148 |
1 files changed, 141 insertions, 7 deletions
diff --git a/plugins/mm-modem-huawei-cdma.c b/plugins/mm-modem-huawei-cdma.c index 1ec4f4c3..1b51fb19 100644 --- a/plugins/mm-modem-huawei-cdma.c +++ b/plugins/mm-modem-huawei-cdma.c @@ -30,7 +30,10 @@ #include "mm-log.h" #include "mm-modem-helpers.h" -G_DEFINE_TYPE (MMModemHuaweiCdma, mm_modem_huawei_cdma, MM_TYPE_GENERIC_CDMA) +static void modem_cdma_init (MMModemCdma *cdma_class); + +G_DEFINE_TYPE_EXTENDED (MMModemHuaweiCdma, mm_modem_huawei_cdma, MM_TYPE_GENERIC_CDMA, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_CDMA, modem_cdma_init)); MMModem * @@ -67,15 +70,20 @@ mm_modem_huawei_cdma_new (const char *device, NULL)); } +/*****************************************************************************/ + /* Unsolicited message handlers */ -static gint -parse_quality (const char *str, const char *detail) +static int +parse_quality (const char *str, const char *tag, const char *detail) { - long int quality = 0; + unsigned long int quality = 0; + + if (tag) + str = mm_strip_tag (str, tag); errno = 0; - quality = strtol (str, NULL, 10); + quality = strtoul (str, NULL, 10); if (errno == 0) { quality = CLAMP (quality, 0, 100); mm_dbg ("%s: %ld", detail, quality); @@ -94,7 +102,7 @@ handle_1x_quality_change (MMAtSerialPort *port, gint quality; str = g_match_info_fetch (match_info, 1); - quality = parse_quality (str, "1X signal quality"); + quality = parse_quality (str, NULL, "1X unsolicited signal quality"); g_free (str); if (quality >= 0) @@ -111,13 +119,133 @@ handle_evdo_quality_change (MMAtSerialPort *port, gint quality; str = g_match_info_fetch (match_info, 1); - quality = parse_quality (str, "EVDO signal quality"); + quality = parse_quality (str, NULL, "EVDO unsolicited signal quality"); g_free (str); if (quality >= 0) mm_generic_cdma_update_evdo_quality (MM_GENERIC_CDMA (self), (guint32) quality); } +static void +parent_csq_done (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + + if (error) + info->error = g_error_copy (error); + else + mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL); + mm_callback_info_schedule (info); +} + +static void +get_1x_signal_quality_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemCdma *parent_iface; + int quality; + + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error || !response || !response->str) { + /* Fallback to parent's method */ + parent_iface = g_type_interface_peek_parent (MM_MODEM_CDMA_GET_INTERFACE (info->modem)); + parent_iface->get_signal_quality (MM_MODEM_CDMA (info->modem), parent_csq_done, info); + return; + } + + quality = parse_quality (response->str, "^CSQLVL:", "1X requested signal quality"); + if (quality == 0) { + /* 0 means no service */ + info->error = g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_NO_NETWORK, + "No service"); + } else if (quality > 0) { + mm_callback_info_set_result (info, GUINT_TO_POINTER ((guint32) quality), NULL); + mm_generic_cdma_update_cdma1x_quality (MM_GENERIC_CDMA (info->modem), (guint32) quality); + } else { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse signal quality results"); + } + + mm_callback_info_schedule (info); +} + +static void +get_evdo_signal_quality_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemCdma *parent_iface; + int quality; + + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error || !response || !response->str) { + /* Fallback to parent's method */ + parent_iface = g_type_interface_peek_parent (MM_MODEM_CDMA_GET_INTERFACE (info->modem)); + parent_iface->get_signal_quality (MM_MODEM_CDMA (info->modem), parent_csq_done, info); + return; + } + + quality = parse_quality (response->str, "^HDRCSQLVL:", "EVDO requested signal quality"); + if (quality >= 0) { + /* We only get here if EVDO is registered, so we don't treat + * 0 signal as "no service" for EVDO. + */ + mm_callback_info_set_result (info, GUINT_TO_POINTER ((guint32) quality), NULL); + mm_generic_cdma_update_evdo_quality (MM_GENERIC_CDMA (info->modem), (guint32) quality); + } else { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse signal quality results"); + } + + mm_callback_info_schedule (info); +} + +static void +get_signal_quality (MMModemCdma *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMModemCdma *parent_iface; + MMAtSerialPort *port; + MMModemCdmaRegistrationState evdo_reg_state; + + port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), NULL); + if (!port) { + /* Let the superclass handle it */ + parent_iface = g_type_interface_peek_parent (MM_MODEM_CDMA_GET_INTERFACE (modem)); + parent_iface->get_signal_quality (MM_MODEM_CDMA (modem), callback, user_data); + return; + } + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + evdo_reg_state = mm_generic_cdma_evdo_get_registration_state_sync (MM_GENERIC_CDMA (modem)); + if (evdo_reg_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) + mm_at_serial_port_queue_command (port, "^HDRCSQLVL", 3, get_evdo_signal_quality_done, info); + else + mm_at_serial_port_queue_command (port, "^CSQLVL", 3, get_1x_signal_quality_done, info); +} + /*****************************************************************************/ static void @@ -254,6 +382,12 @@ port_grabbed (MMGenericCdma *cdma, /*****************************************************************************/ static void +modem_cdma_init (MMModemCdma *cdma_class) +{ + cdma_class->get_signal_quality = get_signal_quality; +} + +static void mm_modem_huawei_cdma_init (MMModemHuaweiCdma *self) { } |