diff options
Diffstat (limited to 'src/mm-generic-gsm.c')
-rw-r--r-- | src/mm-generic-gsm.c | 663 |
1 files changed, 485 insertions, 178 deletions
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 9cb9690..0b7ea01 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -121,6 +121,9 @@ typedef struct { gboolean loc_signal; MMModemGsmUssdState ussd_state; + + /* SMS */ + GHashTable *sms_present; } MMGenericGsmPrivate; static void get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info); @@ -272,6 +275,11 @@ pin_check_done (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; gboolean parsed = FALSE; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); else if (response && strstr (response->str, "+CPIN: ")) { @@ -510,6 +518,11 @@ real_get_iccid_done (MMAtSerialPort *port, gboolean success = FALSE; char buf[21], swapped[21]; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) { info->error = g_error_copy (error); goto done; @@ -1061,21 +1074,21 @@ cgreg1_done (MMAtSerialPort *port, { MMCallbackInfo *info = user_data; - info->error = mm_modem_check_removed (info->modem, error); - if (info->modem) { - if (info->error) { - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; - g_clear_error (&info->error); + if (error) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); - /* The modem doesn't like unsolicited CGREG, so we'll need to poll */ - priv->cgreg_poll = TRUE; - } else - mm_callback_info_set_data (info, CGREG_NUM_TAG, GUINT_TO_POINTER (1), NULL); + /* The modem doesn't like unsolicited CGREG, so we'll need to poll */ + priv->cgreg_poll = TRUE; + } else + mm_callback_info_set_data (info, CGREG_NUM_TAG, GUINT_TO_POINTER (1), NULL); - /* Success; get initial state */ - mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem); - } + /* Success; get initial state */ + mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem); initial_unsolicited_reg_check_done (info); } @@ -1088,27 +1101,25 @@ cgreg2_done (MMAtSerialPort *port, { MMCallbackInfo *info = user_data; - /* Ignore errors except modem removal errors */ - info->error = mm_modem_check_removed (info->modem, error); - if (info->modem) { - if (info->error) { - g_clear_error (&info->error); - /* Try CGREG=1 instead */ - mm_at_serial_port_queue_command (port, "+CGREG=1", 3, cgreg1_done, info); - } else { - add_loc_capability (MM_GENERIC_GSM (info->modem), MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI); + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; - mm_callback_info_set_data (info, CGREG_NUM_TAG, GUINT_TO_POINTER (2), NULL); + /* Ignore errors */ + if (error) { + /* Try CGREG=1 instead */ + mm_at_serial_port_queue_command (port, "+CGREG=1", 3, cgreg1_done, info); + } else { + add_loc_capability (MM_GENERIC_GSM (info->modem), MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI); - /* Success; get initial state */ - mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem); + mm_callback_info_set_data (info, CGREG_NUM_TAG, GUINT_TO_POINTER (2), NULL); - /* All done */ - initial_unsolicited_reg_check_done (info); - } - } else { - /* Modem got removed */ - mm_callback_info_schedule (info); + /* Success; get initial state */ + mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem); + + /* All done */ + initial_unsolicited_reg_check_done (info); } } @@ -1119,28 +1130,26 @@ creg1_done (MMAtSerialPort *port, gpointer user_data) { MMCallbackInfo *info = user_data; + MMGenericGsmPrivate *priv; - info->error = mm_modem_check_removed (info->modem, error); - if (info->modem) { - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; - if (info->error) { - g_clear_error (&info->error); + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); - /* The modem doesn't like unsolicited CREG, so we'll need to poll */ - priv->creg_poll = TRUE; - } else - mm_callback_info_set_data (info, CREG_NUM_TAG, GUINT_TO_POINTER (1), NULL); + if (error) { + /* The modem doesn't like unsolicited CREG, so we'll need to poll */ + priv->creg_poll = TRUE; + } else + mm_callback_info_set_data (info, CREG_NUM_TAG, GUINT_TO_POINTER (1), NULL); - /* Success; get initial state */ - mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem); + /* Success; get initial state */ + mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem); - /* Now try to set up CGREG messages */ - mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info); - } else { - /* Modem got removed */ - mm_callback_info_schedule (info); - } + /* Now try to set up CGREG messages */ + mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info); } static void @@ -1151,26 +1160,24 @@ creg2_done (MMAtSerialPort *port, { MMCallbackInfo *info = user_data; - /* Ignore errors except modem removal errors */ - info->error = mm_modem_check_removed (info->modem, error); - if (info->modem) { - if (info->error) { - g_clear_error (&info->error); - mm_at_serial_port_queue_command (port, "+CREG=1", 3, creg1_done, info); - } else { - add_loc_capability (MM_GENERIC_GSM (info->modem), MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI); + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; - mm_callback_info_set_data (info, CREG_NUM_TAG, GUINT_TO_POINTER (2), NULL); + /* Ignore errors */ + if (error) + mm_at_serial_port_queue_command (port, "+CREG=1", 3, creg1_done, info); + else { + add_loc_capability (MM_GENERIC_GSM (info->modem), MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI); - /* Success; get initial state */ - mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem); + mm_callback_info_set_data (info, CREG_NUM_TAG, GUINT_TO_POINTER (2), NULL); - /* Now try to set up CGREG messages */ - mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info); - } - } else { - /* Modem got removed */ - mm_callback_info_schedule (info); + /* Success; get initial state */ + mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem); + + /* Now try to set up CGREG messages */ + mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info); } } @@ -1179,20 +1186,23 @@ enable_failed (MMModem *modem, GError *error, MMCallbackInfo *info) { MMGenericGsmPrivate *priv; - info->error = mm_modem_check_removed (modem, error); + /* If modem already removed, do nothing */ + if (!modem || mm_callback_info_check_modem_removed (info)) + return; - if (modem) { - mm_modem_set_state (modem, - MM_MODEM_STATE_DISABLED, - MM_MODEM_STATE_REASON_NONE); + if (error) + info->error = g_error_copy (error); - priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + mm_modem_set_state (modem, + MM_MODEM_STATE_DISABLED, + MM_MODEM_STATE_REASON_NONE); - if (priv->primary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->primary))) - mm_serial_port_close_force (MM_SERIAL_PORT (priv->primary)); - if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) - mm_serial_port_close_force (MM_SERIAL_PORT (priv->secondary)); - } + priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + + if (priv->primary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->primary))) + mm_serial_port_close_force (MM_SERIAL_PORT (priv->primary)); + if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) + mm_serial_port_close_force (MM_SERIAL_PORT (priv->secondary)); mm_callback_info_schedule (info); } @@ -1312,8 +1322,8 @@ cmti_received (MMAtSerialPort *port, gpointer user_data) { MMGenericGsm *self = MM_GENERIC_GSM (user_data); - - guint idx=0; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + guint idx = 0; char *str; str = g_match_info_fetch (info, 2); @@ -1321,6 +1331,13 @@ cmti_received (MMAtSerialPort *port, idx = atoi (str); g_free (str); + /* Don't signal multiple times if there are multiple CMTI notifications for a message */ + if (g_hash_table_lookup_extended (priv->sms_present, GINT_TO_POINTER (idx), NULL, NULL)) + return; + + /* Nothing is currently stored in the hash table - presence is all that matters. */ + g_hash_table_insert (priv->sms_present, GINT_TO_POINTER (idx), NULL); + /* todo: parse pdu to know if the sms is complete */ mm_modem_gsm_sms_received (MM_MODEM_GSM_SMS (self), idx, @@ -1458,6 +1475,11 @@ enable_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + /* Let subclasses handle the power up command response/error; many devices * don't support +CFUN, but for those that do let them handle the error * correctly. @@ -1478,6 +1500,11 @@ init_done (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; char *cmd = NULL; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) { mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); return; @@ -1580,8 +1607,14 @@ disable_done (MMAtSerialPort *port, { MMCallbackInfo *info = user_data; - info->error = mm_modem_check_removed (info->modem, error); - if (!info->error) { + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) + info->error = g_error_copy (error); + else { MMGenericGsm *self = MM_GENERIC_GSM (info->modem); mm_serial_port_close_force (MM_SERIAL_PORT (port)); @@ -1615,15 +1648,19 @@ disable_flash_done (MMSerialPort *port, MMModemState prev_state; char *cmd = NULL; - info->error = mm_modem_check_removed (info->modem, error); - if (info->error) { - if (info->modem) { - /* Reset old state since the operation failed */ - prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_GSM_PREV_STATE_TAG)); - mm_modem_set_state (MM_MODEM (info->modem), - prev_state, - MM_MODEM_STATE_REASON_NONE); - } + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) { + info->error = g_error_copy (error); + + /* Reset old state since the operation failed */ + prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_GSM_PREV_STATE_TAG)); + mm_modem_set_state (MM_MODEM (info->modem), + prev_state, + MM_MODEM_STATE_REASON_NONE); mm_callback_info_schedule (info); return; @@ -1731,6 +1768,11 @@ get_string_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); else @@ -1752,6 +1794,11 @@ get_mnc_length_done (MMAtSerialPort *port, char hex[51]; char *bin; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) { info->error = g_error_copy (error); goto done; @@ -1793,6 +1840,7 @@ get_mnc_length_done (MMAtSerialPort *port, MM_MODEM_ERROR_GENERAL, "SIM returned malformed response '%s'", hex); + g_free (bin); goto done; } @@ -1807,6 +1855,7 @@ get_mnc_length_done (MMAtSerialPort *port, "SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_len); } + g_free (bin); } else { info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, @@ -1844,6 +1893,81 @@ get_operator_id_imsi_done (MMModem *modem, } static void +get_spn_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + int sw1, sw2; + gboolean success = FALSE; + char hex[51]; + char *bin, *utf8; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + memset (hex, 0, sizeof (hex)); + if (sscanf (response->str, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3) + success = TRUE; + else { + /* May not include quotes... */ + if (sscanf (response->str, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3) + success = TRUE; + } + + if (!success) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse the CRSM response"); + goto done; + } + + if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) { + gsize buflen = 0; + + /* Make sure the buffer is only hex characters */ + while (buflen < sizeof (hex) && hex[buflen]) { + if (!isxdigit (hex[buflen])) { + hex[buflen] = 0x0; + break; + } + buflen++; + } + + /* Convert hex string to binary */ + bin = utils_hexstr2bin (hex, &buflen); + if (!bin) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "SIM returned malformed response '%s'", + hex); + goto done; + } + + /* Remove the FF filler at the end */ + while (bin[buflen - 1] == (char)0xff) + buflen--; + + /* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */ + utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 ((guint8 *)bin + 1, buflen - 1); + g_free(bin); + mm_callback_info_set_result(info, utf8, g_free); + } else { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "SIM failed to handle CRSM request (sw1 %d sw2 %d)", + sw1, sw2); + } + +done: + mm_callback_info_schedule (info); +} + + +static void get_imei (MMModemGsmCard *modem, MMModemStringFn callback, gpointer user_data) @@ -1881,6 +2005,24 @@ get_operator_id (MMModemGsmCard *modem, } static void +get_spn (MMModemGsmCard *modem, + MMModemStringFn callback, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMCallbackInfo *info; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + + /* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */ + mm_at_serial_port_queue_command_cached (priv->primary, + "+CRSM=176,28486,0,0,17", + 3, + get_spn_done, + info); +} + +static void get_card_info (MMModem *modem, MMModemInfoFn callback, gpointer user_data) @@ -1913,35 +2055,33 @@ static void pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsmPrivate *priv; MMSerialPort *port; GError *saved_error; + /* Do nothing if modem removed */ + if (!modem || mm_callback_info_check_modem_removed (info)) + return; + + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + /* Clear the pin check timeout to ensure that it won't ever get a * stale MMCallbackInfo if the modem got removed. We'll reschedule it here * anyway if needed. */ - if (info->modem) { - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); - - if (priv->pin_check_timeout) - g_source_remove (priv->pin_check_timeout); - priv->pin_check_timeout = 0; - } + if (priv->pin_check_timeout) + g_source_remove (priv->pin_check_timeout); + priv->pin_check_timeout = 0; - /* modem could have been removed before we get here, in which case - * 'modem' will be NULL. - */ - info->error = mm_modem_check_removed (modem, error); + /* Propagate the error to the info */ + if (error) + info->error = g_error_copy (error); /* If the modem wasn't removed, and the modem isn't ready yet, ask it for * the current PIN status a few times since some devices take a bit to fully * enable themselves after a SIM PIN/PUK unlock. */ - if ( info->modem - && info->error - && !g_error_matches (info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) { - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); - + if (info->error && !g_error_matches (info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) { if (priv->pin_check_tries < 4) { g_clear_error (&info->error); priv->pin_check_tries++; @@ -1952,13 +2092,13 @@ pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data) /* Otherwise, clean up and return the PIN check result */ port = mm_callback_info_get_data (info, PIN_PORT_TAG); - if (modem && port) + if (port) mm_serial_port_close (port); /* If we have a saved error from sending PIN/PUK, return that to callers */ saved_error = mm_callback_info_get_data (info, SAVED_ERROR_TAG); if (saved_error) { - if (info->modem && !mm_modem_base_get_unlock_required (MM_MODEM_BASE (info->modem))) { + if (!mm_modem_base_get_unlock_required (MM_MODEM_BASE (info->modem))) { /* Original unlock failed but the modem is actually unlocked, so * return success. Sometimes happens if the modem doesn't allow * CPIN="xxxx" when it's already unlocked and returns an error. @@ -1982,6 +2122,11 @@ send_puk_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) { if (error->domain != MM_MOBILE_ERROR) { info->error = g_error_copy (error); @@ -2046,6 +2191,11 @@ send_pin_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) { if (error->domain != MM_MOBILE_ERROR) { info->error = g_error_copy (error); @@ -2109,6 +2259,11 @@ enable_pin_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); mm_callback_info_schedule (info); @@ -2139,6 +2294,11 @@ change_pin_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); mm_callback_info_schedule (info); @@ -2631,11 +2791,19 @@ get_reg_status_done (MMAtSerialPort *port, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMGenericGsm *self = MM_GENERIC_GSM (info->modem); - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + MMGenericGsm *self; + MMGenericGsmPrivate *priv; guint id; MMModemGsmNetworkRegStatus status; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + self = MM_GENERIC_GSM (info->modem); + priv = MM_GENERIC_GSM_GET_PRIVATE (self); + /* This function should only get called during the connect sequence when * polling for registration state, since explicit registration requests * from D-Bus clients are filled from the cached registration state. @@ -2703,10 +2871,17 @@ register_done (MMAtSerialPort *port, gpointer user_data) { MMCallbackInfo *info = user_data; - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + MMGenericGsmPrivate *priv; mm_callback_info_unref (info); + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + /* If the registration timed out (and thus pending_reg_info will be NULL) * and the modem eventually got around to sending the response for the * registration request then just ignore the response since the callback is @@ -2883,6 +3058,11 @@ connect_report_done (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; GError *real_error; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + /* If the CEER command was successful, copy that error reason into the * callback's error. If not, use the original error. */ @@ -2912,7 +3092,14 @@ connect_done (MMAtSerialPort *port, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + MMGenericGsmPrivate *priv; + + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); if (error) { info->error = g_error_copy (error); @@ -2964,15 +3151,17 @@ disconnect_done (MMModem *modem, MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMModemState prev_state; - info->error = mm_modem_check_removed (modem, error); - if (info->error) { - if (info->modem && modem) { - /* Reset old state since the operation failed */ - prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_GSM_PREV_STATE_TAG)); - mm_modem_set_state (MM_MODEM (info->modem), - prev_state, - MM_MODEM_STATE_REASON_NONE); - } + /* Do nothing if modem removed */ + if (!modem || mm_callback_info_check_modem_removed (info)) + return; + + if (error) { + info->error = g_error_copy (error); + /* Reset old state since the operation failed */ + prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_GSM_PREV_STATE_TAG)); + mm_modem_set_state (MM_MODEM (info->modem), + prev_state, + MM_MODEM_STATE_REASON_NONE); } else { MMGenericGsm *self = MM_GENERIC_GSM (modem); MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); @@ -2991,7 +3180,14 @@ disconnect_all_done (MMAtSerialPort *port, GError *error, gpointer user_data) { - mm_callback_info_schedule ((MMCallbackInfo *) user_data); + MMCallbackInfo *info = (MMCallbackInfo *)user_data; + + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + mm_callback_info_schedule (info); } static void @@ -3023,17 +3219,21 @@ disconnect_flash_done (MMSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMGenericGsmPrivate *priv; - info->error = mm_modem_check_removed (info->modem, error); - if (info->error) { + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) { /* Ignore "NO CARRIER" response when modem disconnects and any flash * failures we might encounter. Other errors are hard errors. */ - if ( !g_error_matches (info->error, MM_MODEM_CONNECT_ERROR, MM_MODEM_CONNECT_ERROR_NO_CARRIER) - && !g_error_matches (info->error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) { + if ( !g_error_matches (error, MM_MODEM_CONNECT_ERROR, MM_MODEM_CONNECT_ERROR_NO_CARRIER) + && !g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) { + info->error = g_error_copy (error); mm_callback_info_schedule (info); return; } - g_clear_error (&info->error); } priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); @@ -3060,11 +3260,10 @@ disconnect_secondary_cgact_done (MMAtSerialPort *port, MMGenericGsm *self; MMGenericGsmPrivate *priv; - if (!info->modem) { - info->error = mm_modem_check_removed (info->modem, error); - mm_callback_info_schedule (info); + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) return; - } self = MM_GENERIC_GSM (info->modem); priv = MM_GENERIC_GSM_GET_PRIVATE (self); @@ -3154,6 +3353,11 @@ scan_done (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; GPtrArray *results; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); else { @@ -3193,8 +3397,14 @@ set_apn_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - info->error = mm_modem_check_removed (info->modem, error); - if (!info->error) { + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) + info->error = g_error_copy (error); + else { MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); priv->cid = GPOINTER_TO_INT (mm_callback_info_get_data (info, APN_CID_TAG)); @@ -3212,6 +3422,11 @@ cid_range_read (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; guint32 cid = 0; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); else if (g_str_has_prefix (response->str, "+CGDCONT:")) { @@ -3278,10 +3493,14 @@ existing_apns_read (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; gboolean found = FALSE; - info->error = mm_modem_check_removed (info->modem, error); - if (info->error) - goto done; - else if (g_str_has_prefix (response->str, "+CGDCONT:")) { + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) { + info->error = g_error_copy (error); + } else if (g_str_has_prefix (response->str, "+CGDCONT:")) { GRegex *r; GMatchInfo *match_info; @@ -3332,7 +3551,6 @@ existing_apns_read (MMAtSerialPort *port, MM_MODEM_ERROR_GENERAL, "Could not parse the response"); -done: if (found || info->error) mm_callback_info_schedule (info); else { @@ -3439,8 +3657,14 @@ get_cind_signal_done (MMAtSerialPort *port, GByteArray *indicators; guint quality; - info->error = mm_modem_check_removed (info->modem, error); - if (!info->error) { + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) + info->error = g_error_copy (error); + else { priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); indicators = mm_parse_cind_query_response (response->str, &info->error); @@ -3468,9 +3692,15 @@ get_csq_done (MMAtSerialPort *port, char *reply = response->str; gboolean parsed = FALSE; - info->error = mm_modem_check_removed (info->modem, error); - if (info->error) + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) { + info->error = g_error_copy (error); goto done; + } if (!strncmp (reply, "+CSQ: ", 6)) { /* Got valid reply */ @@ -3618,8 +3848,13 @@ set_allowed_mode_done (MMModem *modem, GError *error, gpointer user_data) { MMCallbackInfo *info = user_data; - info->error = mm_modem_check_removed (info->modem, error); - if (!info->error) { + /* Do nothing if modem removed */ + if (!modem || mm_callback_info_check_modem_removed (info)) + return; + + if (error) + info->error = g_error_copy (error); + else { MMModemGsmAllowedMode mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode")); mm_generic_gsm_update_allowed_mode (MM_GENERIC_GSM (info->modem), mode); @@ -3674,8 +3909,13 @@ get_charsets_done (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMGenericGsmPrivate *priv; - info->error = mm_modem_check_removed (info->modem, error); - if (info->error) { + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) { + info->error = g_error_copy (error); mm_callback_info_schedule (info); return; } @@ -3733,8 +3973,13 @@ set_get_charset_done (MMAtSerialPort *port, MMModemCharset tried_charset; const char *p; - info->error = mm_modem_check_removed (info->modem, error); - if (info->error) { + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) { + info->error = g_error_copy (error); mm_callback_info_schedule (info); return; } @@ -3770,13 +4015,18 @@ set_charset_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - info->error = mm_modem_check_removed (info->modem, error); - if (info->error) { + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + + if (error) { gboolean tried_no_quotes = !!mm_callback_info_get_data (info, TRIED_NO_QUOTES_TAG); MMModemCharset charset = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "charset")); char *command; - if (!info->modem || tried_no_quotes) { + if (tried_no_quotes) { + info->error = g_error_copy (error); mm_callback_info_schedule (info); return; } @@ -3856,6 +4106,14 @@ mm_generic_gsm_get_charset (MMGenericGsm *self) #define SMS_TP_MTI_SMS_SUBMIT_REPORT 0x01 #define SMS_TP_MTI_SMS_STATUS_REPORT 0x02 +#define SMS_NUMBER_TYPE_MASK 0x70 +#define SMS_NUMBER_TYPE_UNKNOWN 0x00 +#define SMS_NUMBER_TYPE_INTL 0x10 +#define SMS_NUMBER_TYPE_ALPHA 0x50 + +#define SMS_NUMBER_PLAN_MASK 0x0f +#define SMS_NUMBER_PLAN_TELEPHONE 0x01 + #define SMS_TP_MMS 0x04 #define SMS_TP_SRI 0x20 #define SMS_TP_UDHI 0x40 @@ -3881,6 +4139,11 @@ sms_send_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); @@ -3931,25 +4194,37 @@ sms_semi_octets_to_bcd_string (char *dest, const guint8 *octets, int num_octets) *dest++ = '\0'; } -/* len is in septets for gsm7 and in digits (semi-octets) for others */ +/* len is in semi-octets */ static char * sms_decode_address (const guint8 *address, int len) { - guint8 addrtype; + guint8 addrtype, addrplan; char *utf8; - addrtype = address[0]; + addrtype = address[0] & SMS_NUMBER_TYPE_MASK; + addrplan = address[0] & SMS_NUMBER_PLAN_MASK; address++; - if (addrtype == 0xd0) { + if (addrtype == SMS_NUMBER_TYPE_ALPHA) { guint8 *unpacked; guint32 unpacked_len; - unpacked = gsm_unpack (address + 1, len, 0, &unpacked_len); + unpacked = gsm_unpack (address, (len * 4) / 7, 0, &unpacked_len); utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len); g_free(unpacked); + } else if (addrtype == SMS_NUMBER_TYPE_INTL && + addrplan == SMS_NUMBER_PLAN_TELEPHONE) { + /* International telphone number, format as "+1234567890" */ + utf8 = g_malloc (len + 3); /* '+' + digits + possible trailing 0xf + NUL */ + utf8[0] = '+'; + sms_semi_octets_to_bcd_string (utf8 + 1, address, (len + 1) / 2); } else { - utf8 = g_malloc (len + 2); /* may need one extra for trailing 0xf */ + /* + * All non-alphanumeric types and plans are just digits, but + * don't apply any special formatting if we don't know the + * format. + */ + utf8 = g_malloc (len + 2); /* digits + possible trailing 0xf + NUL */ sms_semi_octets_to_bcd_string (utf8, address, (len + 1) / 2); } @@ -4071,14 +4346,6 @@ sms_parse_pdu (const char *hexpdu) return NULL; } - /* Only handle the basic protocol identifier */ - if (pdu[tp_pid_offset] != 0) { - mm_err ("Unhandled protocol identifier: 0x%02x vs 0x00", - pdu[tp_pid_offset]); - g_free (pdu); - return NULL; - } - smsc_addr = sms_decode_address (&pdu[1], 2 * (pdu[0] - 1)); sender_addr = sms_decode_address (&pdu[msg_start_offset + 2], pdu[msg_start_offset + 1]); @@ -4138,6 +4405,11 @@ sms_get_done (MMAtSerialPort *port, int rv, status, tpdu_len, offset; char pdu[SMS_MAX_PDU_LEN + 1]; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) { info->error = g_error_copy (error); goto out; @@ -4212,6 +4484,11 @@ sms_delete_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); @@ -4227,8 +4504,10 @@ sms_delete (MMModemGsmSms *modem, MMCallbackInfo *info; char *command; MMAtSerialPort *port; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (MM_GENERIC_GSM (modem)); info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + g_hash_table_remove (priv->sms_present, GINT_TO_POINTER (idx)); port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); if (!port) { @@ -4251,6 +4530,11 @@ sms_list_done (MMAtSerialPort *port, int rv, status, tpdu_len, offset; char *rstr; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); else { @@ -4389,6 +4673,11 @@ ussd_send_done (MMAtSerialPort *port, const char *str, *start = NULL, *end = NULL; char *reply = NULL, *converted; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) { info->error = g_error_copy (error); goto done; @@ -4471,6 +4760,7 @@ ussd_send (MMModemGsmUssd *modem, GByteArray *ussd_command = g_byte_array_new(); MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMAtSerialPort *port; + gboolean success; info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); @@ -4481,7 +4771,9 @@ ussd_send (MMModemGsmUssd *modem, } /* encode to cur_charset */ - g_warn_if_fail (mm_modem_charset_byte_array_append (ussd_command, command, FALSE, priv->cur_charset)); + success = mm_modem_charset_byte_array_append (ussd_command, command, FALSE, priv->cur_charset); + g_warn_if_fail (success == TRUE); + /* convert to hex representation */ hex = utils_bin2hexstr (ussd_command->data, ussd_command->len); g_byte_array_free (ussd_command, TRUE); @@ -4502,6 +4794,7 @@ ussd_initiate (MMModemGsmUssd *modem, { MMCallbackInfo *info; MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); if (priv->ussd_state != MM_MODEM_GSM_USSD_STATE_IDLE) { @@ -4509,11 +4802,9 @@ ussd_initiate (MMModemGsmUssd *modem, MM_MODEM_ERROR_GENERAL, "USSD session already active."); mm_callback_info_schedule (info); - return; + } else { + ussd_send (modem, command, callback, user_data); } - - ussd_send (modem, command, callback, user_data); - return; } static void @@ -4524,6 +4815,7 @@ ussd_respond (MMModemGsmUssd *modem, { MMCallbackInfo *info; MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); if (priv->ussd_state != MM_MODEM_GSM_USSD_STATE_USER_RESPONSE) { @@ -4531,11 +4823,9 @@ ussd_respond (MMModemGsmUssd *modem, MM_MODEM_ERROR_GENERAL, "No active USSD session, cannot respond."); mm_callback_info_schedule (info); - return; + } else { + ussd_send (modem, command, callback, user_data); } - - ussd_send (modem, command, callback, user_data); - return; } static void @@ -4546,13 +4836,17 @@ ussd_cancel_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* If the modem has already been removed, return without + * scheduling callback */ + if (mm_callback_info_check_modem_removed (info)) + return; + if (error) info->error = g_error_copy (error); mm_callback_info_schedule (info); - if (info->modem) - ussd_update_state (MM_GENERIC_GSM (info->modem), MM_MODEM_GSM_USSD_STATE_IDLE); + ussd_update_state (MM_GENERIC_GSM (info->modem), MM_MODEM_GSM_USSD_STATE_IDLE); } static void @@ -4710,9 +5004,14 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data) gboolean home_only = FALSE; char *data_device; - info->error = mm_modem_check_removed (modem, error); - if (info->error) + /* Do nothing if modem removed */ + if (!modem || mm_callback_info_check_modem_removed (info)) + return; + + if (error) { + info->error = g_error_copy (error); goto out; + } priv = MM_GENERIC_GSM_GET_PRIVATE (modem); @@ -4929,8 +5228,13 @@ simple_status_got_reg_info (MMModemGsmNetwork *modem, MMCallbackInfo *info = (MMCallbackInfo *) user_data; GHashTable *properties; - info->error = mm_modem_check_removed ((MMModem *) modem, error); - if (!info->error) { + /* Do nothing if modem removed */ + if (!modem || mm_callback_info_check_modem_removed (info)) + return; + + if (error) + info->error = g_error_copy (error); + else { properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG); g_hash_table_insert (properties, "registration_status", simple_uint_value (status)); @@ -5188,6 +5492,7 @@ modem_gsm_card_init (MMModemGsmCard *class) class->get_imei = get_imei; class->get_imsi = get_imsi; class->get_operator_id = get_operator_id; + class->get_spn = get_spn; class->send_pin = send_pin; class->send_puk = send_puk; class->enable_pin = enable_pin; @@ -5238,6 +5543,7 @@ mm_generic_gsm_init (MMGenericGsm *self) priv->act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; priv->reg_regex = mm_gsm_creg_regex_get (TRUE); priv->roam_allowed = TRUE; + priv->sms_present = g_hash_table_new (g_direct_hash, g_direct_equal); mm_properties_changed_signal_register_property (G_OBJECT (self), MM_MODEM_GSM_NETWORK_ALLOWED_MODE, @@ -5455,6 +5761,7 @@ finalize (GObject *object) g_free (priv->oper_code); g_free (priv->oper_name); g_free (priv->simid); + g_hash_table_destroy (priv->sms_present); G_OBJECT_CLASS (mm_generic_gsm_parent_class)->finalize (object); } |