diff options
Diffstat (limited to 'src/mm-generic-cdma.c')
-rw-r--r-- | src/mm-generic-cdma.c | 975 |
1 files changed, 688 insertions, 287 deletions
diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c index 50cd86c..9fe897f 100644 --- a/src/mm-generic-cdma.c +++ b/src/mm-generic-cdma.c @@ -23,10 +23,15 @@ #include "mm-generic-cdma.h" #include "mm-modem-cdma.h" #include "mm-modem-simple.h" -#include "mm-serial-port.h" +#include "mm-at-serial-port.h" +#include "mm-qcdm-serial-port.h" #include "mm-errors.h" #include "mm-callback-info.h" #include "mm-serial-parsers.h" +#include "mm-modem-helpers.h" +#include "libqcdm/src/commands.h" + +#define MM_GENERIC_CDMA_PREV_STATE_TAG "prev-state" static void simple_reg_callback (MMModemCdma *modem, MMModemCdmaRegistrationState cdma_1x_reg_state, @@ -58,6 +63,10 @@ typedef struct { gboolean evdo_rev0; gboolean evdo_revA; gboolean reg_try_css; + gboolean has_spservice; + gboolean has_speri; + + guint poll_id; MMModemCdmaRegistrationState cdma_1x_reg_state; MMModemCdmaRegistrationState evdo_reg_state; @@ -67,8 +76,9 @@ typedef struct { guint reg_state_changed_id; MMCallbackInfo *simple_connect_info; - MMSerialPort *primary; - MMSerialPort *secondary; + MMAtSerialPort *primary; + MMAtSerialPort *secondary; + MMQcdmSerialPort *qcdm; MMPort *data; } MMGenericCdmaPrivate; @@ -114,6 +124,44 @@ check_valid (MMGenericCdma *self) mm_modem_base_set_valid (MM_MODEM_BASE (self), new_valid); } +static void +get_esn_cb (MMModem *modem, + const char *result, + GError *error, + gpointer user_data) +{ + if (modem) { + mm_modem_base_set_equipment_identifier (MM_MODEM_BASE (modem), error ? "" : result); + mm_serial_port_close (MM_SERIAL_PORT (MM_GENERIC_CDMA_GET_PRIVATE (modem)->primary)); + check_valid (MM_GENERIC_CDMA (modem)); + } +} + +static void +initial_esn_check (MMGenericCdma *self) +{ + GError *error = NULL; + MMGenericCdmaPrivate *priv; + + g_return_if_fail (MM_IS_GENERIC_CDMA (self)); + priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + + g_return_if_fail (priv->primary != NULL); + + if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) { + /* Make sure echoing is off */ + mm_at_serial_port_queue_command (priv->primary, "E0", 3, NULL, NULL); + mm_modem_cdma_get_esn (MM_MODEM_CDMA (self), get_esn_cb, NULL); + } else { + g_warning ("%s: failed to open serial port: (%d) %s", + __func__, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + check_valid (self); + } +} + static gboolean owns_port (MMModem *modem, const char *subsys, const char *name) { @@ -148,25 +196,36 @@ mm_generic_cdma_grab_port (MMGenericCdma *self, } port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype); - if (port && MM_IS_SERIAL_PORT (port)) { + if (!port) { + g_warn_if_fail (port != NULL); + return NULL; + } + + if (MM_IS_AT_SERIAL_PORT (port)) { g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); - mm_serial_port_set_response_parser (MM_SERIAL_PORT (port), - mm_serial_parser_v1_parse, - mm_serial_parser_v1_new (), - mm_serial_parser_v1_destroy); + mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port), + mm_serial_parser_v1_e1_parse, + mm_serial_parser_v1_e1_new (), + mm_serial_parser_v1_e1_destroy); if (ptype == MM_PORT_TYPE_PRIMARY) { - priv->primary = MM_SERIAL_PORT (port); + priv->primary = MM_AT_SERIAL_PORT (port); if (!priv->data) { priv->data = port; g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); } - check_valid (self); + + /* Get modem's ESN number */ + initial_esn_check (self); + } else if (ptype == MM_PORT_TYPE_SECONDARY) - priv->secondary = MM_SERIAL_PORT (port); - } else { + priv->secondary = MM_AT_SERIAL_PORT (port); + } else if (MM_IS_QCDM_SERIAL_PORT (port)) { + if (!priv->qcdm) + priv->qcdm = MM_QCDM_SERIAL_PORT (port); + } else if (!strcmp (subsys, "net")) { /* Net device (if any) is the preferred data port */ - if (!priv->data || MM_IS_SERIAL_PORT (priv->data)) { + if (!priv->data || MM_IS_AT_SERIAL_PORT (priv->data)) { priv->data = port; g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); check_valid (self); @@ -197,7 +256,7 @@ release_port (MMModem *modem, const char *subsys, const char *name) if (!port) return; - if (port == MM_PORT (priv->primary)) { + if (port == (MMPort *) priv->primary) { mm_modem_base_remove_port (MM_MODEM_BASE (modem), port); priv->primary = NULL; } @@ -207,17 +266,22 @@ release_port (MMModem *modem, const char *subsys, const char *name) g_object_notify (G_OBJECT (modem), MM_MODEM_DATA_DEVICE); } - if (port == MM_PORT (priv->secondary)) { + if (port == (MMPort *) priv->secondary) { mm_modem_base_remove_port (MM_MODEM_BASE (modem), port); priv->secondary = NULL; } + if (port == (MMPort *) priv->qcdm) { + mm_modem_base_remove_port (MM_MODEM_BASE (modem), port); + priv->qcdm = NULL; + } + check_valid (MM_GENERIC_CDMA (modem)); } -MMSerialPort * -mm_generic_cdma_get_port (MMGenericCdma *modem, - MMPortType ptype) +MMAtSerialPort * +mm_generic_cdma_get_at_port (MMGenericCdma *modem, + MMPortType ptype) { g_return_val_if_fail (MM_IS_GENERIC_CDMA (modem), NULL); g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL); @@ -230,6 +294,27 @@ mm_generic_cdma_get_port (MMGenericCdma *modem, return NULL; } +MMAtSerialPort * +mm_generic_cdma_get_best_at_port (MMGenericCdma *self, GError **error) +{ + MMGenericCdmaPrivate *priv; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (MM_IS_GENERIC_CDMA (self), NULL); + + priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + + if (!mm_port_get_connected (MM_PORT (priv->primary))) + return priv->primary; + + if (!priv->secondary) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot perform this operation while connected"); + } + + return priv->secondary; +} + /*****************************************************************************/ void @@ -301,6 +386,38 @@ mm_generic_cdma_evdo_get_registration_state_sync (MMGenericCdma *self) /*****************************************************************************/ static void +periodic_poll_reg_cb (MMModemCdma *modem, + MMModemCdmaRegistrationState cdma_1x_reg_state, + MMModemCdmaRegistrationState evdo_reg_state, + GError *error, + gpointer user_data) +{ + /* cached reg state already updated */ +} + +static void +periodic_poll_signal_quality_cb (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ + /* cached signal quality already updated */ +} + +static gboolean +periodic_poll_cb (gpointer user_data) +{ + MMGenericCdma *self = MM_GENERIC_CDMA (user_data); + + mm_modem_cdma_get_registration_state (MM_MODEM_CDMA (self), periodic_poll_reg_cb, NULL); + mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (self), periodic_poll_signal_quality_cb, NULL); + + return TRUE; +} + +/*****************************************************************************/ + +static void update_enabled_state (MMGenericCdma *self, gboolean stay_connected, MMModemStateReason reason) @@ -349,6 +466,43 @@ registration_cleanup (MMGenericCdma *self, GQuark error_class, guint32 error_num } static void +get_enable_info_done (MMModem *modem, + const char *manufacturer, + const char *model, + const char *version, + GError *error, + gpointer user_data) +{ + /* Modem base class handles the response for us */ +} + +static void +spservice_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + if (!error) { + MM_GENERIC_CDMA_GET_PRIVATE (user_data)->has_spservice = TRUE; + + /* +SPSERVICE provides a better indicator of registration status than + * +CSS, which some devices implement inconsistently. + */ + MM_GENERIC_CDMA_GET_PRIVATE (user_data)->reg_try_css = FALSE; + } +} + +static void +speri_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + if (!error) + MM_GENERIC_CDMA_GET_PRIVATE (user_data)->has_speri = TRUE; +} + +static void enable_all_done (MMModem *modem, GError *error, gpointer user_data) { MMCallbackInfo *info = user_data; @@ -358,15 +512,33 @@ enable_all_done (MMModem *modem, GError *error, gpointer user_data) if (error) info->error = g_error_copy (error); else { + /* Try to enable XON/XOFF flow control */ + mm_at_serial_port_queue_command (priv->primary, "+IFC=1,1", 3, NULL, NULL); + /* Open up the second port, if one exists */ if (priv->secondary) { - if (!mm_serial_port_open (priv->secondary, &info->error)) { + if (!mm_serial_port_open (MM_SERIAL_PORT (priv->secondary), &info->error)) { + g_assert (info->error); + goto out; + } + } + + /* Open up the second port, if one exists */ + if (priv->qcdm) { + if (!mm_serial_port_open (MM_SERIAL_PORT (priv->qcdm), &info->error)) { g_assert (info->error); goto out; } } update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE); + + /* Grab device info right away */ + mm_modem_get_info (modem, get_enable_info_done, NULL); + + /* Check for support of Sprint-specific phone commands */ + mm_at_serial_port_queue_command (priv->primary, "+SPSERVICE?", 3, spservice_done, self); + mm_at_serial_port_queue_command (priv->primary, "$SPERI?", 3, speri_done, self); } out: @@ -380,7 +552,7 @@ out: } static void -enable_error_reporting_done (MMSerialPort *port, +enable_error_reporting_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -399,7 +571,7 @@ enable_error_reporting_done (MMSerialPort *port, } static void -init_done (MMSerialPort *port, +init_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -419,7 +591,7 @@ init_done (MMSerialPort *port, FIXME: It's mandatory by spec, so it really shouldn't be optional. Figure out which CDMA modems have problems with it and implement plugin for them. */ - mm_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data); + mm_at_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data); } } @@ -439,7 +611,7 @@ flash_done (MMSerialPort *port, GError *error, gpointer user_data) return; } - mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1", 3, init_done, user_data); + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "Z E0 V1 X4 &C1", 3, init_done, user_data); } static void @@ -453,7 +625,7 @@ enable (MMModem *modem, info = mm_callback_info_new (modem, callback, user_data); - if (!mm_serial_port_open (priv->primary, &info->error)) { + if (!mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &info->error)) { g_assert (info->error); mm_callback_info_schedule (info); return; @@ -463,7 +635,7 @@ enable (MMModem *modem, MM_MODEM_STATE_ENABLING, MM_MODEM_STATE_REASON_NONE); - mm_serial_port_flash (priv->primary, 100, flash_done, info); + mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 100, FALSE, flash_done, info); } static void @@ -489,7 +661,7 @@ disable_all_done (MMModem *modem, GError *error, gpointer user_data) MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); - mm_serial_port_close (priv->primary); + mm_serial_port_close_force (MM_SERIAL_PORT (priv->primary)); mm_modem_set_state (modem, MM_MODEM_STATE_DISABLED, MM_MODEM_STATE_REASON_NONE); priv->cdma_1x_reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; @@ -545,21 +717,24 @@ disable (MMModem *modem, GUINT_TO_POINTER (state), NULL); + /* Close auxiliary serial ports */ if (priv->secondary) - mm_serial_port_close (priv->secondary); + mm_serial_port_close_force (MM_SERIAL_PORT (priv->secondary)); + if (priv->qcdm) + mm_serial_port_close_force (MM_SERIAL_PORT (priv->qcdm)); mm_modem_set_state (MM_MODEM (info->modem), MM_MODEM_STATE_DISABLING, MM_MODEM_STATE_REASON_NONE); if (mm_port_get_connected (MM_PORT (priv->primary))) - mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info); + mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disable_flash_done, info); else - disable_flash_done (priv->primary, NULL, info); + disable_flash_done (MM_SERIAL_PORT (priv->primary), NULL, info); } static void -dial_done (MMSerialPort *port, +dial_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -598,7 +773,7 @@ connect (MMModem *modem, info = mm_callback_info_new (modem, callback, user_data); command = g_strconcat ("DT", number, NULL); - mm_serial_port_queue_command (priv->primary, command, 90, dial_done, info); + mm_at_serial_port_queue_command (priv->primary, command, 90, dial_done, info); g_free (command); } @@ -648,83 +823,7 @@ disconnect (MMModem *modem, NULL); mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE); - mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info); -} - -static void -card_info_invoke (MMCallbackInfo *info) -{ - MMModemInfoFn callback = (MMModemInfoFn) info->callback; - - callback (info->modem, - (char *) mm_callback_info_get_data (info, "card-info-manufacturer"), - (char *) mm_callback_info_get_data (info, "card-info-model"), - (char *) mm_callback_info_get_data (info, "card-info-version"), - info->error, info->user_data); -} - -static const char * -strip_response (const char *resp, const char *cmd) -{ - const char *p = resp; - - if (p) { - if (!strncmp (p, cmd, strlen (cmd))) - p += strlen (cmd); - while (*p == ' ') - p++; - } - return p; -} - -static void -get_version_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - const char *p; - - if (!error) { - p = strip_response (response->str, "+GMR:"); - mm_callback_info_set_data (info, "card-info-version", g_strdup (p), g_free); - } else if (!info->error) - info->error = g_error_copy (error); - - mm_callback_info_schedule (info); -} - -static void -get_model_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - const char *p; - - if (!error) { - p = strip_response (response->str, "+GMM:"); - mm_callback_info_set_data (info, "card-info-model", g_strdup (p), g_free); - } else if (!info->error) - info->error = g_error_copy (error); -} - -static void -get_manufacturer_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - const char *p; - - if (!error) { - p = strip_response (response->str, "+GMI:"); - mm_callback_info_set_data (info, "card-info-manufacturer", g_strdup (p), g_free); - } else - info->error = g_error_copy (error); + mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info); } static void @@ -732,30 +831,12 @@ get_card_info (MMModem *modem, MMModemInfoFn callback, gpointer user_data) { - MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); - MMCallbackInfo *info; - MMSerialPort *port = priv->primary; - - info = mm_callback_info_new_full (MM_MODEM (modem), - card_info_invoke, - G_CALLBACK (callback), - user_data); - - if (mm_port_get_connected (MM_PORT (priv->primary))) { - if (!priv->secondary) { - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, - "Cannot modem info while connected"); - mm_callback_info_schedule (info); - return; - } - - /* Use secondary port if primary is connected */ - port = priv->secondary; - } + MMAtSerialPort *port; + GError *error = NULL; - mm_serial_port_queue_command_cached (port, "+GMI", 3, get_manufacturer_done, info); - mm_serial_port_queue_command_cached (port, "+GMM", 3, get_model_done, info); - mm_serial_port_queue_command_cached (port, "+GMR", 3, get_version_done, info); + port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &error); + mm_modem_base_get_card_info (MM_MODEM_BASE (modem), port, error, callback, user_data); + g_clear_error (&error); } /*****************************************************************************/ @@ -793,7 +874,7 @@ mm_generic_cdma_update_evdo_quality (MMGenericCdma *self, guint32 quality) #define CSQ2_TRIED "csq?-tried" static void -get_signal_quality_done (MMSerialPort *port, +get_signal_quality_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -811,7 +892,7 @@ get_signal_quality_done (MMSerialPort *port, * try the other command if the first one fails. */ mm_callback_info_set_data (info, CSQ2_TRIED, GUINT_TO_POINTER (1), NULL); - mm_serial_port_queue_command (port, "+CSQ?", 3, get_signal_quality_done, info); + mm_at_serial_port_queue_command (port, "+CSQ?", 3, get_signal_quality_done, info); return; } } else { @@ -847,31 +928,102 @@ get_signal_quality_done (MMSerialPort *port, } static void +qcdm_pilot_sets_cb (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMGenericCdmaPrivate *priv; + QCDMResult *result; + guint32 num = 0, quality = 0, i; + float best_db = -28; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); + + /* Parse the response */ + result = qcdm_cmd_pilot_sets_result ((const char *) response->data, response->len, &info->error); + if (!result) + goto done; + + 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; + float 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))); + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); + + if (priv->cdma1x_quality != quality) { + priv->cdma1x_quality = quality; + mm_modem_cdma_emit_signal_quality_changed (MM_MODEM_CDMA (info->modem), quality); + } + +done: + mm_callback_info_schedule (info); +} + +static void get_signal_quality (MMModemCdma *modem, MMModemUIntFn callback, gpointer user_data) { MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); MMCallbackInfo *info; - MMSerialPort *port = priv->primary; + MMAtSerialPort *at_port; - if (mm_port_get_connected (MM_PORT (priv->primary))) { - if (!priv->secondary) { - g_message ("Returning saved signal quality %d", priv->cdma1x_quality); - callback (MM_MODEM (modem), priv->cdma1x_quality, NULL, user_data); - return; - } + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - /* Use secondary port if primary is connected */ - port = priv->secondary; + at_port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error); + if (!at_port && !priv->qcdm) { + g_message ("Returning saved signal quality %d", priv->cdma1x_quality); + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->cdma1x_quality), NULL); + mm_callback_info_schedule (info); + return; + } + g_clear_error (&info->error); + + if (at_port) + mm_at_serial_port_queue_command (at_port, "+CSQ", 3, get_signal_quality_done, info); + else if (priv->qcdm) { + 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, NULL); + g_assert (pilot_sets->len); + mm_qcdm_serial_port_queue_command (priv->qcdm, pilot_sets, 3, qcdm_pilot_sets_cb, info); } - - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - mm_serial_port_queue_command (port, "+CSQ", 3, get_signal_quality_done, info); } static void -get_string_done (MMSerialPort *port, +get_string_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -882,7 +1034,7 @@ get_string_done (MMSerialPort *port, if (error) info->error = g_error_copy (error); else { - p = strip_response (response->str, "+GSN:"); + p = mm_strip_tag (response->str, "+GSN:"); mm_callback_info_set_result (info, g_strdup (p), g_free); } @@ -894,26 +1046,18 @@ get_esn (MMModemCdma *modem, MMModemStringFn callback, gpointer user_data) { - MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); MMCallbackInfo *info; - GError *error; - MMSerialPort *port = priv->primary; - - if (mm_port_get_connected (MM_PORT (priv->primary))) { - if (!priv->secondary) { - error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, - "Cannot get ESN while connected"); - callback (MM_MODEM (modem), NULL, error, user_data); - g_error_free (error); - return; - } + MMAtSerialPort *port; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); - /* Use secondary port if primary is connected */ - port = priv->secondary; + port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; } - info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); - mm_serial_port_queue_command_cached (port, "+GSN", 3, get_string_done, info); + mm_at_serial_port_queue_command_cached (port, "+GSN", 3, get_string_done, info); } static void @@ -996,8 +1140,17 @@ convert_sid (const char *sid) return (int) tmp_sid; } +static GError * +new_css_no_service_error (void) +{ + /* NOTE: update reg_state_css_response() if this error changes */ + return g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_NO_NETWORK, + "No service"); +} + static void -serving_system_done (MMSerialPort *port, +serving_system_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -1089,12 +1242,9 @@ serving_system_done (MMSerialPort *port, sid = 99999; /* 99999 means unknown/no service */ - if (sid == 99999) { - /* NOTE: update reg_state_css_response() if this error changes */ - info->error = g_error_new_literal (MM_MOBILE_ERROR, - MM_MOBILE_ERROR_NO_NETWORK, - "No service"); - } else { + if (sid == 99999) + info->error = new_css_no_service_error (); + else { mm_callback_info_set_data (info, "class", GUINT_TO_POINTER (class), NULL); mm_callback_info_set_data (info, "band", GUINT_TO_POINTER ((guint32) band), NULL); mm_callback_info_set_data (info, "sid", GUINT_TO_POINTER (sid), NULL); @@ -1109,39 +1259,97 @@ serving_system_done (MMSerialPort *port, } static void +legacy_get_serving_system (MMGenericCdma *self, MMCallbackInfo *info) +{ + MMAtSerialPort *port; + + port = mm_generic_cdma_get_best_at_port (self, &info->error); + if (port) + mm_at_serial_port_queue_command (port, "+CSS?", 3, serving_system_done, info); + else + mm_callback_info_schedule (info); +} + +static void +cdma_status_cb (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + QCDMResult *result; + guint32 sid, rxstate; + + if (error) + goto error; + + /* Parse the response */ + result = qcdm_cmd_cdma_status_result ((const char *) response->data, response->len, &info->error); + if (!result) + goto error; + + qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, &rxstate); + qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, &sid); + qcdm_result_unref (result); + + if (rxstate == QCDM_CMD_CDMA_STATUS_RX_STATE_ENTERING_CDMA) + info->error = new_css_no_service_error (); + else { + mm_callback_info_set_data (info, "class", GUINT_TO_POINTER (0), NULL); + mm_callback_info_set_data (info, "band", GUINT_TO_POINTER ((guint32) 'Z'), NULL); + mm_callback_info_set_data (info, "sid", GUINT_TO_POINTER (sid), NULL); + } + + mm_callback_info_schedule (info); + return; + +error: + /* If there was some error, fall back to use +CSS like we did before QCDM */ + legacy_get_serving_system (MM_GENERIC_CDMA (info->modem), info); +} + +static void get_serving_system (MMModemCdma *modem, MMModemCdmaServingSystemFn callback, gpointer user_data) { - MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMGenericCdma *self = MM_GENERIC_CDMA (modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); MMCallbackInfo *info; - GError *error; - MMSerialPort *port = priv->primary; - - if (mm_port_get_connected (MM_PORT (priv->primary))) { - if (!priv->secondary) { - error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, - "Cannot get serving system while connected"); - callback (modem, 0, 0, 0, error, user_data); - g_error_free (error); - return; - } - - /* Use secondary port if primary is connected */ - port = priv->secondary; - } info = mm_callback_info_new_full (MM_MODEM (modem), serving_system_invoke, G_CALLBACK (callback), user_data); - mm_serial_port_queue_command (port, "+CSS?", 3, serving_system_done, info); + if (priv->qcdm) { + GByteArray *cdma_status; + + cdma_status = g_byte_array_sized_new (25); + cdma_status->len = qcdm_cmd_cdma_status_new ((char *) cdma_status->data, 25, NULL); + g_assert (cdma_status->len); + mm_qcdm_serial_port_queue_command (priv->qcdm, cdma_status, 3, cdma_status_cb, info); + } else + legacy_get_serving_system (self, info); } +/*****************************************************************************/ + +/* Registration state stuff */ + #define CDMA_1X_STATE_TAG "cdma-1x-reg-state" #define EVDO_STATE_TAG "evdo-reg-state" +MMModemCdmaRegistrationState +mm_generic_cdma_query_reg_state_get_callback_1x_state (MMCallbackInfo *info) +{ + g_return_val_if_fail (info != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + g_return_val_if_fail (info->modem != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + g_return_val_if_fail (MM_IS_GENERIC_CDMA (info->modem), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + + return GPOINTER_TO_UINT (mm_callback_info_get_data (info, CDMA_1X_STATE_TAG)); +} + void mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info, MMModemCdmaRegistrationState new_state) @@ -1153,6 +1361,16 @@ mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info, mm_callback_info_set_data (info, CDMA_1X_STATE_TAG, GUINT_TO_POINTER (new_state), NULL); } +MMModemCdmaRegistrationState +mm_generic_cdma_query_reg_state_get_callback_evdo_state (MMCallbackInfo *info) +{ + g_return_val_if_fail (info != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + g_return_val_if_fail (info->modem != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + g_return_val_if_fail (MM_IS_GENERIC_CDMA (info->modem), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + + return GPOINTER_TO_UINT (mm_callback_info_get_data (info, EVDO_STATE_TAG)); +} + void mm_generic_cdma_query_reg_state_set_callback_evdo_state (MMCallbackInfo *info, MMModemCdmaRegistrationState new_state) @@ -1171,18 +1389,19 @@ registration_state_invoke (MMCallbackInfo *info) /* note: This is the MMModemCdma interface callback */ callback (MM_MODEM_CDMA (info->modem), - GPOINTER_TO_UINT (mm_callback_info_get_data (info, CDMA_1X_STATE_TAG)), - GPOINTER_TO_UINT (mm_callback_info_get_data (info, EVDO_STATE_TAG)), + mm_generic_cdma_query_reg_state_get_callback_1x_state (info), + mm_generic_cdma_query_reg_state_get_callback_evdo_state (info), info->error, info->user_data); } MMCallbackInfo * mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self, + MMModemCdmaRegistrationState cur_cdma_state, + MMModemCdmaRegistrationState cur_evdo_state, MMModemCdmaRegistrationStateFn callback, gpointer user_data) { - MMGenericCdmaPrivate *priv; MMCallbackInfo *info; g_return_val_if_fail (self != NULL, NULL); @@ -1195,15 +1414,8 @@ mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self, user_data); /* Fill with current state */ - priv = MM_GENERIC_CDMA_GET_PRIVATE (self); - mm_callback_info_set_data (info, - CDMA_1X_STATE_TAG, - GUINT_TO_POINTER (priv->cdma_1x_reg_state), - NULL); - mm_callback_info_set_data (info, - EVDO_STATE_TAG, - GUINT_TO_POINTER (priv->evdo_reg_state), - NULL); + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, cur_cdma_state); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, cur_evdo_state); return info; } @@ -1211,60 +1423,138 @@ static void set_callback_1x_state_helper (MMCallbackInfo *info, MMModemCdmaRegistrationState new_state) { - MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); - MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); + if (info->modem) { + MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); - mm_generic_cdma_set_1x_registration_state (self, new_state); - mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state); + mm_generic_cdma_set_1x_registration_state (self, new_state); + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state); + } } static void set_callback_evdo_state_helper (MMCallbackInfo *info, MMModemCdmaRegistrationState new_state) { - MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); - MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); + if (info->modem) { + MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); - mm_generic_cdma_set_evdo_registration_state (self, new_state); - mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state); + mm_generic_cdma_set_evdo_registration_state (self, new_state); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state); + } } static void -reg_state_query_done (MMModemCdma *cdma, - MMModemCdmaRegistrationState cdma_1x_reg_state, - MMModemCdmaRegistrationState evdo_reg_state, +subclass_reg_query_done (MMModemCdma *cdma, + MMModemCdmaRegistrationState cdma_reg_state, + MMModemCdmaRegistrationState evdo_reg_state, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + info->error = mm_modem_check_removed (info->modem, error); + if (!info->error) { + /* Set final registration state */ + set_callback_1x_state_helper (info, cdma_reg_state); + set_callback_evdo_state_helper (info, evdo_reg_state); + } + + mm_callback_info_schedule (info); +} + +static void +reg_query_speri_done (MMAtSerialPort *port, + GString *response, GError *error, gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMCallbackInfo *info = user_data; + gboolean roam = FALSE; + const char *p; + + if (error) + goto done; + + p = mm_strip_tag (response->str, "$SPERI:"); + if (!p || !mm_cdma_parse_eri (p, &roam, NULL, NULL)) + goto done; + + /* Change the 1x and EVDO registration states to roaming if they were + * anything other than UNKNOWN. + */ + if (roam) { + if (mm_generic_cdma_query_reg_state_get_callback_1x_state (info)) + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING); + + if (mm_generic_cdma_query_reg_state_get_callback_evdo_state (info)) + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING); + } + +done: + mm_callback_info_schedule (info); +} + +static void +reg_query_spservice_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemCdmaRegistrationState cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; if (error) info->error = g_error_copy (error); - else { - set_callback_1x_state_helper (info, cdma_1x_reg_state); - set_callback_evdo_state_helper (info, evdo_reg_state); + else if (mm_cdma_parse_spservice_response (response->str, &cdma_state, &evdo_state)) { + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, cdma_state); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, evdo_state); + + if (MM_GENERIC_CDMA_GET_PRIVATE (info->modem)->has_speri) { + /* Get roaming status to override generic registration state */ + mm_at_serial_port_queue_command (port, "$SPERI?", 3, reg_query_speri_done, info); + return; + } } mm_callback_info_schedule (info); } static void -query_subclass_registration_state (MMGenericCdma *self, MMCallbackInfo *info) +real_query_registration_state (MMGenericCdma *self, + MMModemCdmaRegistrationState cur_cdma_state, + MMModemCdmaRegistrationState cur_evdo_state, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data) { - /* Let subclasses figure out roaming and detailed registration state */ - if (MM_GENERIC_CDMA_GET_CLASS (self)->query_registration_state) { - MM_GENERIC_CDMA_GET_CLASS (self)->query_registration_state (self, - reg_state_query_done, - info); + MMCallbackInfo *info; + MMAtSerialPort *port; + + /* Seed this CallbackInfo with any previously determined registration state */ + info = mm_generic_cdma_query_reg_state_callback_info_new (self, + cur_cdma_state, + cur_evdo_state, + callback, + user_data); + + port = mm_generic_cdma_get_best_at_port (self, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + if (MM_GENERIC_CDMA_GET_PRIVATE (self)->has_spservice) { + /* Try Sprint-specific commands */ + mm_at_serial_port_queue_command (port, "+SPSERVICE?", 3, reg_query_spservice_done, info); } else { - /* Or if the subclass doesn't implement more specific checking, - * assume we're registered. + /* Assume we're registered on the 1x network if we passed +CAD, +CSS, + * and QCDM Call Manager checking. */ - reg_state_query_done (MM_MODEM_CDMA (self), - MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED, - MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED, - NULL, - info); + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + mm_callback_info_schedule (info); } } @@ -1282,8 +1572,7 @@ reg_state_css_response (MMModemCdma *cdma, * report unknown registration state. */ if (error) { - if ( (error->domain == MM_MOBILE_ERROR) - && (error->code == MM_MOBILE_ERROR_NO_NETWORK)) { + if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_NO_NETWORK)) { set_callback_1x_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); set_callback_evdo_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); } else { @@ -1291,14 +1580,20 @@ reg_state_css_response (MMModemCdma *cdma, info->error = g_error_copy (error); } mm_callback_info_schedule (info); - return; + } else { + /* We're registered on the CDMA 1x network at least, but let subclasses + * do more specific registration checking. + */ + MM_GENERIC_CDMA_GET_CLASS (cdma)->query_registration_state (MM_GENERIC_CDMA (info->modem), + MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED, + MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, + subclass_reg_query_done, + info); } - - query_subclass_registration_state (MM_GENERIC_CDMA (info->modem), info); } static void -get_analog_digital_done (MMSerialPort *port, +get_analog_digital_done (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -1313,7 +1608,7 @@ get_analog_digital_done (MMSerialPort *port, } /* Strip any leading command tag and spaces */ - reply = strip_response (response->str, "+CAD:"); + reply = mm_strip_tag (response->str, "+CAD:"); errno = 0; int_cad = strtol (reply, NULL, 10); @@ -1345,7 +1640,11 @@ get_analog_digital_done (MMSerialPort *port, /* Subclass knows that AT+CSS? will respond incorrectly to EVDO * state, so skip AT+CSS? query. */ - query_subclass_registration_state (MM_GENERIC_CDMA (info->modem), info); + MM_GENERIC_CDMA_GET_CLASS (info->modem)->query_registration_state (MM_GENERIC_CDMA (info->modem), + MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, + MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, + subclass_reg_query_done, + info); } return; } else { @@ -1359,28 +1658,113 @@ error: } static void +reg_cmstate_cb (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMAtSerialPort *at_port; + QCDMResult *result; + guint32 opmode = 0, sysmode = 0; + MMModemCdmaRegistrationState cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + + if (error) + goto error; + + /* Parse the response */ + result = qcdm_cmd_cm_subsys_state_info_result ((const char *) response->data, response->len, &info->error); + if (!result) + goto error; + + qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, &opmode); + qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, &sysmode); + qcdm_result_unref (result); + + if (opmode == QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE) { + switch (sysmode) { + case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA: + cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_HDR: + evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_AMPS: + case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE: + case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA: + default: + break; + } + + if (cdma_state || evdo_state) { + /* Device is registered to something; see if the subclass has a + * better idea of whether we're roaming or not and what the + * access technology is. + */ + if (MM_GENERIC_CDMA_GET_CLASS (info->modem)->query_registration_state) { + MM_GENERIC_CDMA_GET_CLASS (info->modem)->query_registration_state (MM_GENERIC_CDMA (info->modem), + cdma_state, + evdo_state, + subclass_reg_query_done, + info); + return; + } + } + } + + set_callback_1x_state_helper (info, cdma_state); + set_callback_evdo_state_helper (info, evdo_state); + mm_callback_info_schedule (info); + return; + +error: + /* If there was some error, fall back to use +CAD like we did before QCDM */ + at_port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (info->modem), &info->error); + if (at_port) + mm_at_serial_port_queue_command (at_port, "+CAD?", 3, get_analog_digital_done, info); + else + mm_callback_info_schedule (info); +} + +static void get_registration_state (MMModemCdma *modem, MMModemCdmaRegistrationStateFn callback, gpointer user_data) { MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); MMCallbackInfo *info; - MMSerialPort *port = priv->primary; - - if (mm_port_get_connected (MM_PORT (priv->primary))) { - if (!priv->secondary) { - g_message ("Returning saved registration states: 1x: %d EVDO: %d", - priv->cdma_1x_reg_state, priv->evdo_reg_state); - callback (MM_MODEM_CDMA (modem), priv->cdma_1x_reg_state, priv->evdo_reg_state, NULL, user_data); - return; - } - - /* Use secondary port if primary is connected */ - port = priv->secondary; + MMAtSerialPort *port; + + info = mm_generic_cdma_query_reg_state_callback_info_new (MM_GENERIC_CDMA (modem), + MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, + MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN, + callback, + user_data); + + port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error); + if (!port && !priv->qcdm) { + g_message ("Returning saved registration states: 1x: %d EVDO: %d", + priv->cdma_1x_reg_state, priv->evdo_reg_state); + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state); + mm_callback_info_schedule (info); + return; } - info = mm_generic_cdma_query_reg_state_callback_info_new (MM_GENERIC_CDMA (modem), callback, user_data); - mm_serial_port_queue_command (port, "+CAD?", 3, get_analog_digital_done, info); + /* Use QCDM for Call Manager state or HDR state before trying CAD, since + * CAD doesn't always reflect the state of the HDR radio's registration + * status. + */ + if (priv->qcdm) { + GByteArray *cmstate; + + cmstate = g_byte_array_sized_new (25); + cmstate->len = qcdm_cmd_cm_subsys_state_info_new ((char *) cmstate->data, 25, NULL); + g_assert (cmstate->len); + mm_qcdm_serial_port_queue_command (priv->qcdm, cmstate, 3, reg_cmstate_cb, info); + } else + mm_at_serial_port_queue_command (port, "+CAD?", 3, get_analog_digital_done, info); } /*****************************************************************************/ @@ -1512,10 +1896,9 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data) const char *str; guint id; - if (error) { - info->error = g_error_copy (error); + info->error = mm_modem_check_removed (modem, error); + if (info->error) goto out; - } switch (state) { case SIMPLE_STATE_BEGIN: @@ -1550,7 +1933,8 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data) out: if (info->error || state == SIMPLE_STATE_DONE) { - registration_cleanup (MM_GENERIC_CDMA (modem), 0, 0); + if (modem) + registration_cleanup (MM_GENERIC_CDMA (modem), 0, 0); mm_callback_info_schedule (info); } } @@ -1609,16 +1993,26 @@ simple_uint_value (guint32 i) return val; } +#define SS_HASH_TAG "simple-get-status" + static void simple_status_got_signal_quality (MMModem *modem, guint32 result, GError *error, gpointer user_data) { - if (error) + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GHashTable *properties; + + if (error) { + info->error = g_error_copy (error); g_warning ("Error getting signal quality: %s", error->message); - else - g_hash_table_insert ((GHashTable *) user_data, "signal_quality", simple_uint_value (result)); + } else { + properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG); + g_hash_table_insert (properties, "signal_quality", simple_uint_value (result)); + } + + mm_callback_info_schedule (info); } static void @@ -1627,7 +2021,7 @@ simple_get_status_invoke (MMCallbackInfo *info) MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback; callback (MM_MODEM_SIMPLE (info->modem), - (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"), + (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG), info->error, info->user_data); } @@ -1644,9 +2038,9 @@ simple_get_status (MMModemSimple *simple, G_CALLBACK (callback), user_data); - properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue); - mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref); - mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (simple), simple_status_got_signal_quality, properties); + properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, simple_free_gvalue); + mm_callback_info_set_data (info, SS_HASH_TAG, properties, (GDestroyNotify) g_hash_table_unref); + mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (simple), simple_status_got_signal_quality, info); } /*****************************************************************************/ @@ -1659,6 +2053,28 @@ modem_valid_changed (MMGenericCdma *self, GParamSpec *pspec, gpointer user_data) registration_cleanup (self, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL); } +static void +modem_state_changed (MMGenericCdma *self, GParamSpec *pspec, gpointer user_data) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + MMModemState state; + + /* Start polling registration status and signal quality when enabled */ + + state = mm_modem_get_state (MM_MODEM (self)); + if (state >= MM_MODEM_STATE_ENABLED) { + if (!priv->poll_id) { + priv->poll_id = g_timeout_add_seconds (30, periodic_poll_cb, self); + /* Kick one off immediately */ + periodic_poll_cb (self); + } + } else { + if (priv->poll_id) + g_source_remove (priv->poll_id); + priv->poll_id = 0; + } +} + /*****************************************************************************/ static void @@ -1690,27 +2106,13 @@ modem_simple_init (MMModemSimple *class) class->get_status = simple_get_status; } -static GObject* -constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) -{ - GObject *object; - - object = G_OBJECT_CLASS (mm_generic_cdma_parent_class)->constructor (type, - n_construct_params, - construct_params); - if (object) { - g_signal_connect (object, "notify::" MM_MODEM_VALID, - G_CALLBACK (modem_valid_changed), NULL); - } - - return object; -} - static void mm_generic_cdma_init (MMGenericCdma *self) { + g_signal_connect (self, "notify::" MM_MODEM_VALID, + G_CALLBACK (modem_valid_changed), NULL); + g_signal_connect (self, "notify::" MM_MODEM_STATE, + G_CALLBACK (modem_state_changed), NULL); } static void @@ -1771,15 +2173,15 @@ get_property (GObject *object, guint prop_id, static void dispose (GObject *object) { - registration_cleanup (MM_GENERIC_CDMA (object), MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL); + MMGenericCdma *self = MM_GENERIC_CDMA (object); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); - G_OBJECT_CLASS (mm_generic_cdma_parent_class)->dispose (object); -} + registration_cleanup (self, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL); -static void -finalize (GObject *object) -{ - G_OBJECT_CLASS (mm_generic_cdma_parent_class)->finalize (object); + if (priv->poll_id) + g_source_remove (priv->poll_id); + + G_OBJECT_CLASS (mm_generic_cdma_parent_class)->dispose (object); } static void @@ -1794,8 +2196,7 @@ mm_generic_cdma_class_init (MMGenericCdmaClass *klass) object_class->set_property = set_property; object_class->get_property = get_property; object_class->dispose = dispose; - object_class->finalize = finalize; - object_class->constructor = constructor; + klass->query_registration_state = real_query_registration_state; /* Properties */ g_object_class_override_property (object_class, |