diff options
author | Guido Günther <agx@sigxcpu.org> | 2014-02-05 08:38:24 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2014-02-05 08:38:24 +0100 |
commit | 10d15c65fd10614c4085af390a9fbf390ab958b7 (patch) | |
tree | 79fe721fe5b872ca6042c2ac64dd4059819aaf11 /src | |
parent | 7fbee6ce27176bfc7ae9b34a4de9452cf5f6fa43 (diff) |
Imported Upstream version 0.4+git.20110429t103114.863dbcaupstream/0.4+git.20110429t103114.863dbca
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-charsets.c | 9 | ||||
-rw-r--r-- | src/mm-charsets.h | 2 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 556 | ||||
-rw-r--r-- | src/mm-generic-gsm.h | 2 | ||||
-rw-r--r-- | src/mm-log.c | 16 | ||||
-rw-r--r-- | src/mm-modem-base.c | 6 | ||||
-rw-r--r-- | src/mm-modem-cdma.c | 12 | ||||
-rw-r--r-- | src/mm-modem-gsm-network.c | 31 | ||||
-rw-r--r-- | src/mm-modem-gsm-sms.c | 153 | ||||
-rw-r--r-- | src/mm-modem-gsm-sms.h | 47 | ||||
-rw-r--r-- | src/mm-modem-gsm.h | 3 | ||||
-rw-r--r-- | src/mm-modem-helpers.c | 2 | ||||
-rw-r--r-- | src/mm-modem.c | 2 | ||||
-rw-r--r-- | src/mm-modem.h | 2 | ||||
-rw-r--r-- | src/mm-plugin-base.c | 125 | ||||
-rw-r--r-- | src/mm-plugin-base.h | 29 | ||||
-rw-r--r-- | src/mm-serial-parsers.c | 87 | ||||
-rw-r--r-- | src/mm-serial-parsers.h | 16 | ||||
-rw-r--r-- | src/mm-serial-port.c | 206 | ||||
-rw-r--r-- | src/mm-serial-port.h | 13 | ||||
-rw-r--r-- | src/mm-utils.c | 17 | ||||
-rw-r--r-- | src/mm-utils.h | 2 | ||||
-rw-r--r-- | src/tests/test-charsets.c | 11 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 11 |
24 files changed, 1141 insertions, 219 deletions
diff --git a/src/mm-charsets.c b/src/mm-charsets.c index d2b9a66..cbdf388 100644 --- a/src/mm-charsets.c +++ b/src/mm-charsets.c @@ -427,17 +427,16 @@ mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len) guint8 * gsm_unpack (const guint8 *gsm, - guint32 gsm_len, + guint32 num_septets, guint8 start_offset, /* in _bits_ */ guint32 *out_unpacked_len) { GByteArray *unpacked; - int i, nchars; + int i; - nchars = ((gsm_len * 8) - start_offset) / 7; - unpacked = g_byte_array_sized_new (nchars + 1); + unpacked = g_byte_array_sized_new (num_septets + 1); - for (i = 0; i < nchars; i++) { + for (i = 0; i < num_septets; i++) { guint8 bits_here, bits_in_next, octet, offset, c; guint32 start_bit; diff --git a/src/mm-charsets.h b/src/mm-charsets.h index 661052d..50b0cce 100644 --- a/src/mm-charsets.h +++ b/src/mm-charsets.h @@ -53,7 +53,7 @@ guint8 *mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len); guint8 *mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len); guint8 *gsm_unpack (const guint8 *gsm, - guint32 gsm_len, + guint32 num_septets, guint8 start_offset, /* in bits */ guint32 *out_unpacked_len); diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 98713b0..9cb9690 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -167,6 +167,16 @@ static void ciev_received (MMAtSerialPort *port, GMatchInfo *info, gpointer user_data); +static void cmti_received (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data); + +#define GS_HASH_TAG "get-sms" +static GValue *simple_string_value (const char *str); +static GValue *simple_uint_value (guint32 i); +static GValue *simple_boolean_value (gboolean b); +static void simple_free_gvalue (gpointer data); + MMModem * mm_generic_gsm_new (const char *device, const char *driver, @@ -697,9 +707,10 @@ initial_pin_check (MMGenericGsm *self) g_return_if_fail (priv->primary != NULL); - if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) + if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) { + mm_at_serial_port_queue_command (priv->primary, "+CMEE=1", 2, NULL, NULL); check_pin (self, initial_pin_check_done, NULL); - else { + } else { g_warning ("%s: failed to open serial port: (%d) %s", __func__, error ? error->code : -1, @@ -814,6 +825,9 @@ mm_generic_gsm_grab_port (MMGenericGsm *self, regex = g_regex_new ("\\r\\n\\+CIEV: (\\d+),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, ciev_received, self, NULL); + + regex = g_regex_new ("\\r\\n\\+CMTI: \"(\\S+)\",(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, cmti_received, self, NULL); g_regex_unref (regex); if (ptype == MM_PORT_TYPE_PRIMARY) { @@ -1293,6 +1307,29 @@ ciev_received (MMAtSerialPort *port, } static void +cmti_received (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MMGenericGsm *self = MM_GENERIC_GSM (user_data); + + guint idx=0; + char *str; + + str = g_match_info_fetch (info, 2); + if (str) + idx = atoi (str); + g_free (str); + + /* todo: parse pdu to know if the sms is complete */ + mm_modem_gsm_sms_received (MM_MODEM_GSM_SMS (self), + idx, + TRUE); + + /* todo: send mm_modem_gsm_sms_completed if complete */ +} + +static void cmer_cb (MMAtSerialPort *port, GString *response, GError *error, @@ -1352,6 +1389,7 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self, MMCallbackInfo *info) { MMGenericGsmPrivate *priv; + gchar *cmd = NULL; g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_GENERIC_GSM (self)); @@ -1377,8 +1415,16 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self, } } - /* Try to enable XON/XOFF flow control */ - mm_at_serial_port_queue_command (priv->primary, "+IFC=1,1", 3, NULL, NULL); + /* Try to enable flow control */ + g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_FLOW_CONTROL_CMD, &cmd, NULL); + if (cmd && strlen (cmd)) + mm_at_serial_port_queue_command (priv->primary, cmd, 3, NULL, NULL); + g_free (cmd); + + /* Enable SMS notifications */ + mm_at_serial_port_queue_command (priv->primary, "+CNMI=2,1,2,1,0", 3, NULL, NULL); + /* Set SMS storage location to ME */ + mm_at_serial_port_queue_command (priv->primary, "+CPMS=\"ME\",\"ME\",\"ME\"", 3, NULL, NULL); mm_at_serial_port_queue_command (priv->primary, "+CIND=?", 3, cind_cb, self); @@ -1912,8 +1958,17 @@ pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data) /* 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) { - g_clear_error (&info->error); - info->error = saved_error; + if (info->modem && !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. + * Do nothing. + */ + } else { + /* Unlock failed after recheck, return original error */ + g_clear_error (&info->error); + info->error = g_error_copy (saved_error); + } } mm_callback_info_schedule (info); @@ -1938,7 +1993,8 @@ send_puk_done (MMAtSerialPort *port, * when we're done rechecking CPIN status. */ mm_callback_info_set_data (info, SAVED_ERROR_TAG, - g_error_copy (error), NULL); + g_error_copy (error), + (GDestroyNotify) g_error_free); } } @@ -2001,7 +2057,8 @@ send_pin_done (MMAtSerialPort *port, * when we're done rechecking CPIN status. */ mm_callback_info_set_data (info, SAVED_ERROR_TAG, - g_error_copy (error), NULL); + g_error_copy (error), + (GDestroyNotify) g_error_free); } } @@ -3735,24 +3792,6 @@ set_charset_done (MMAtSerialPort *port, mm_at_serial_port_queue_command (port, "+CSCS?", 3, set_get_charset_done, info); } -static gboolean -check_for_single_value (guint32 value) -{ - gboolean found = FALSE; - guint32 i; - - for (i = 1; i <= 32; i++) { - if (value & 0x1) { - if (found) - return FALSE; /* More than one bit set */ - found = TRUE; - } - value >>= 1; - } - - return TRUE; -} - static void set_charset (MMModem *modem, MMModemCharset charset, @@ -3767,7 +3806,7 @@ set_charset (MMModem *modem, info = mm_callback_info_new (modem, callback, user_data); - if (!(priv->charsets & charset) || !check_for_single_value (charset)) { + if (!(priv->charsets & charset) || !utils_check_for_single_value (charset)) { info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_UNSUPPORTED_CHARSET, "Character set 0x%X not supported", @@ -3811,6 +3850,29 @@ mm_generic_gsm_get_charset (MMGenericGsm *self) /*****************************************************************************/ /* MMModemGsmSms interface */ + +#define SMS_TP_MTI_MASK 0x03 +#define SMS_TP_MTI_SMS_DELIVER 0x00 +#define SMS_TP_MTI_SMS_SUBMIT_REPORT 0x01 +#define SMS_TP_MTI_SMS_STATUS_REPORT 0x02 + +#define SMS_TP_MMS 0x04 +#define SMS_TP_SRI 0x20 +#define SMS_TP_UDHI 0x40 +#define SMS_TP_RP 0x80 + +#define SMS_DCS_CODING_MASK 0xec +#define SMS_DCS_CODING_DEFAULT 0x00 +#define SMS_DCS_CODING_8BIT 0x04 +#define SMS_DCS_CODING_UCS2 0x08 + +#define SMS_DCS_CLASS_VALID 0x10 +#define SMS_DCS_CLASS_MASK 0x03 + +#define SMS_TIMESTAMP_LEN 7 +#define SMS_MIN_PDU_LEN (7 + SMS_TIMESTAMP_LEN) +#define SMS_MAX_PDU_LEN 344 + static void sms_send_done (MMAtSerialPort *port, GString *response, @@ -3855,6 +3917,414 @@ sms_send (MMModemGsmSms *modem, g_free (command); } +static char sms_bcd_chars[] = "0123456789*#abc\0\0"; + +static void +sms_semi_octets_to_bcd_string (char *dest, const guint8 *octets, int num_octets) +{ + int i; + + for (i = 0 ; i < num_octets; i++) { + *dest++ = sms_bcd_chars[octets[i] & 0xf]; + *dest++ = sms_bcd_chars[(octets[i] >> 4) & 0xf]; + } + *dest++ = '\0'; +} + +/* len is in septets for gsm7 and in digits (semi-octets) for others */ +static char * +sms_decode_address (const guint8 *address, int len) +{ + guint8 addrtype; + char *utf8; + + addrtype = address[0]; + address++; + + if (addrtype == 0xd0) { + guint8 *unpacked; + guint32 unpacked_len; + unpacked = gsm_unpack (address + 1, len, 0, &unpacked_len); + utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked, + unpacked_len); + g_free(unpacked); + } else { + utf8 = g_malloc (len + 2); /* may need one extra for trailing 0xf */ + sms_semi_octets_to_bcd_string (utf8, address, (len + 1) / 2); + } + + return utf8; +} + + +static char * +sms_decode_timestamp (const guint8 *timestamp) +{ + /* YYMMDDHHMMSS+ZZ */ + char *timestr; + int quarters, hours; + + timestr = g_malloc0 (16); + sms_semi_octets_to_bcd_string (timestr, timestamp, 6); + quarters = ((timestamp[6] & 0x7) * 10) + ((timestamp[6] >> 4) & 0xf); + hours = quarters / 4; + if (timestamp[6] & 0x08) + timestr[12] = '-'; + else + timestr[12] = '+'; + timestr[13] = (hours / 10) + '0'; + timestr[14] = (hours % 10) + '0'; + /* TODO(njw): Change timestamp rep to something that includes quarter-hours */ + return timestr; +} + +static char * +sms_decode_text (const guint8 *text, int len, int dcs, int bit_offset) +{ + char *utf8; + guint8 coding = dcs & SMS_DCS_CODING_MASK; + guint8 *unpacked; + guint32 unpacked_len; + + if (coding == SMS_DCS_CODING_DEFAULT) { + unpacked = gsm_unpack ((const guint8 *) text, len, bit_offset, &unpacked_len); + utf8 = (char *) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len); + g_free (unpacked); + } else if (coding == SMS_DCS_CODING_UCS2) + utf8 = g_convert ((char *) text, len, "UTF8", "UCS-2BE", NULL, NULL, NULL); + else if (coding == SMS_DCS_CODING_8BIT) + utf8 = g_strndup ((const char *)text, len); + else + utf8 = g_strdup (""); + + return utf8; +} + + +static GHashTable * +sms_parse_pdu (const char *hexpdu) +{ + GHashTable *properties; + gsize pdu_len; + guint8 *pdu; + int smsc_addr_num_octets, variable_length_items, msg_start_offset, + sender_addr_num_digits, sender_addr_num_octets, + tp_pid_offset, tp_dcs_offset, user_data_offset, user_data_len, + user_data_len_offset, user_data_dcs, bit_offset; + char *smsc_addr, *sender_addr, *sc_timestamp, *msg_text; + + /* Convert PDU from hex to binary */ + pdu = (guint8 *) utils_hexstr2bin (hexpdu, &pdu_len); + if (!pdu) { + mm_err("Couldn't parse PDU of SMS GET response from hex"); + return NULL; + } + + /* SMSC, in address format, precedes the TPDU */ + smsc_addr_num_octets = pdu[0]; + variable_length_items = smsc_addr_num_octets; + if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) { + mm_err ("PDU too short (1): %zd vs %d", pdu_len, + variable_length_items + SMS_MIN_PDU_LEN); + g_free (pdu); + return NULL; + } + + /* where in the PDU the actual SMS protocol message begins */ + msg_start_offset = 1 + smsc_addr_num_octets; + sender_addr_num_digits = pdu[msg_start_offset + 1]; + /* + * round the sender address length up to an even number of + * semi-octets, and thus an integral number of octets + */ + sender_addr_num_octets = (sender_addr_num_digits + 1) >> 1; + variable_length_items += sender_addr_num_octets; + if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) { + mm_err ("PDU too short (2): %zd vs %d", pdu_len, + variable_length_items + SMS_MIN_PDU_LEN); + g_free (pdu); + return NULL; + } + + tp_pid_offset = msg_start_offset + 3 + sender_addr_num_octets; + tp_dcs_offset = tp_pid_offset + 1; + + user_data_len_offset = tp_dcs_offset + 1 + SMS_TIMESTAMP_LEN; + user_data_offset = user_data_len_offset + 1; + user_data_len = pdu[user_data_len_offset]; + user_data_dcs = pdu[tp_dcs_offset]; + if ((user_data_dcs & SMS_DCS_CODING_MASK) == SMS_DCS_CODING_DEFAULT) + variable_length_items += (7 * (user_data_len + 1 )) / 8; + else + variable_length_items += user_data_len; + if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) { + mm_err ("PDU too short (3): %zd vs %d", pdu_len, + variable_length_items + SMS_MIN_PDU_LEN); + g_free (pdu); + return NULL; + } + + /* Only handle SMS-DELIVER */ + if ((pdu[msg_start_offset] & SMS_TP_MTI_MASK) != SMS_TP_MTI_SMS_DELIVER) { + mm_err ("Unhandled message type: 0x%02x", pdu[msg_start_offset]); + g_free (pdu); + 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]); + sc_timestamp = sms_decode_timestamp (&pdu[tp_dcs_offset + 1]); + bit_offset = 0; + if (pdu[msg_start_offset] & SMS_TP_UDHI) { + /* + * Skip over the user data headers to prevent it from being + * decoded into garbage text. + */ + int udhl; + udhl = pdu[user_data_offset] + 1; + user_data_offset += udhl; + if ((user_data_dcs & SMS_DCS_CODING_MASK) == SMS_DCS_CODING_DEFAULT) { + bit_offset = 7 - (udhl * 8) % 7; + user_data_len -= (udhl * 8 + bit_offset) / 7; + } else + user_data_len -= udhl; + } + + msg_text = sms_decode_text (&pdu[user_data_offset], user_data_len, + user_data_dcs, bit_offset); + + properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, + simple_free_gvalue); + g_hash_table_insert (properties, "number", + simple_string_value (sender_addr)); + g_hash_table_insert (properties, "text", + simple_string_value (msg_text)); + g_hash_table_insert (properties, "smsc", + simple_string_value (smsc_addr)); + g_hash_table_insert (properties, "timestamp", + simple_string_value (sc_timestamp)); + if (user_data_dcs & SMS_DCS_CLASS_VALID) + g_hash_table_insert (properties, "class", + simple_uint_value (user_data_dcs & + SMS_DCS_CLASS_MASK)); + g_hash_table_insert (properties, "completed", simple_boolean_value (TRUE)); + + g_free (smsc_addr); + g_free (sender_addr); + g_free (sc_timestamp); + g_free (msg_text); + g_free (pdu); + + return properties; +} + +static void +sms_get_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GHashTable *properties; + int rv, status, tpdu_len, offset; + char pdu[SMS_MAX_PDU_LEN + 1]; + + if (error) { + info->error = g_error_copy (error); + goto out; + } + + /* 344 == SMS_MAX_PDU_LEN */ + rv = sscanf (response->str, "+CMGR: %d,,%d %344s %n", + &status, &tpdu_len, pdu, &offset); + if (rv != 4) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse CMGR response (parsed %d items)", + rv); + goto out; + } + + properties = sms_parse_pdu (pdu); + if (!properties) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse SMS PDU"); + goto out; + } + + mm_callback_info_set_data (info, GS_HASH_TAG, properties, + (GDestroyNotify) g_hash_table_unref); + +out: + mm_callback_info_schedule (info); +} + +static void +sms_get_invoke (MMCallbackInfo *info) +{ + MMModemGsmSmsGetFn callback = (MMModemGsmSmsGetFn) info->callback; + + callback (MM_MODEM_GSM_SMS (info->modem), + (GHashTable *) mm_callback_info_get_data (info, GS_HASH_TAG), + info->error, info->user_data); +} + +static void +sms_get (MMModemGsmSms *modem, + guint idx, + MMModemGsmSmsGetFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMAtSerialPort *port; + + info = mm_callback_info_new_full (MM_MODEM (modem), + sms_get_invoke, + G_CALLBACK (callback), + user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + command = g_strdup_printf ("+CMGR=%d\r\n", idx); + mm_at_serial_port_queue_command (port, command, 10, sms_get_done, info); +} + +static void +sms_delete_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +sms_delete (MMModemGsmSms *modem, + guint idx, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMAtSerialPort *port; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + command = g_strdup_printf ("+CMGD=%d\r\n", idx); + mm_at_serial_port_queue_command (port, command, 10, sms_delete_done, info); +} + +static void +sms_list_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GPtrArray *results = NULL; + int rv, status, tpdu_len, offset; + char *rstr; + + if (error) + info->error = g_error_copy (error); + else { + results = g_ptr_array_new (); + rstr = response->str; + + while (*rstr) { + GHashTable *properties; + int idx; + char pdu[SMS_MAX_PDU_LEN + 1]; + + rv = sscanf (rstr, "+CMGL: %d,%d,,%d %344s %n", + &idx, &status, &tpdu_len, pdu, &offset); + if (4 != rv) { + mm_err("Couldn't parse response to SMS LIST (%d)", rv); + break; + } + rstr += offset; + + properties = sms_parse_pdu (pdu); + if (properties) { + g_hash_table_insert (properties, "index", + simple_uint_value (idx)); + g_ptr_array_add (results, properties); + } + } + /* + * todo(njw): mm_gsm_destroy_scan_data does what we want + * (destroys a GPtrArray of g_hash_tables), but it should be + * renamed to describe that or there should be a function + * named for what we're doing here. + */ + if (results) + mm_callback_info_set_data (info, "list-sms", results, + mm_gsm_destroy_scan_data); + } + + mm_callback_info_schedule (info); +} + +static void +sms_list_invoke (MMCallbackInfo *info) +{ + MMModemGsmSmsListFn callback = (MMModemGsmSmsListFn) info->callback; + + callback (MM_MODEM_GSM_SMS (info->modem), + (GPtrArray *) mm_callback_info_get_data (info, "list-sms"), + info->error, info->user_data); +} + +static void +sms_list (MMModemGsmSms *modem, + MMModemGsmSmsListFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMAtSerialPort *port; + + info = mm_callback_info_new_full (MM_MODEM (modem), + sms_list_invoke, + G_CALLBACK (callback), + user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + command = g_strdup_printf ("+CMGL=4\r\n"); + mm_at_serial_port_queue_command (port, command, 10, sms_list_done, info); +} + MMAtSerialPort * mm_generic_gsm_get_at_port (MMGenericGsm *modem, MMPortType ptype) @@ -4063,7 +4533,7 @@ ussd_respond (MMModemGsmUssd *modem, mm_callback_info_schedule (info); return; } - + ussd_send (modem, command, callback, user_data); return; } @@ -4364,7 +4834,7 @@ simple_connect (MMModemSimple *simple, g_free (data_device); info = mm_callback_info_new (MM_MODEM (simple), callback, user_data); - mm_callback_info_set_data (info, "simple-connect-properties", + mm_callback_info_set_data (info, "simple-connect-properties", g_hash_table_ref (properties), (GDestroyNotify) g_hash_table_unref); @@ -4391,6 +4861,18 @@ simple_uint_value (guint32 i) } static GValue * +simple_boolean_value (gboolean b) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_BOOLEAN); + g_value_set_boolean (val, b); + + return val; +} + +static GValue * simple_string_value (const char *str) { GValue *val; @@ -4728,6 +5210,9 @@ static void modem_gsm_sms_init (MMModemGsmSms *class) { class->send = sms_send; + class->get = sms_get; + class->delete = sms_delete; + class->list = sms_list; } static void @@ -4825,6 +5310,7 @@ set_property (GObject *object, guint prop_id, case MM_GENERIC_GSM_PROP_USSD_STATE: case MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST: case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION: + case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -4932,6 +5418,10 @@ get_property (GObject *object, guint prop_id, case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION: g_value_set_string (value, ""); break; + case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD: + /* By default, try to set XOFF/XON flow control */ + g_value_set_string (value, "+IFC=1,1"); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5075,5 +5565,13 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass) "Optional initialization command (errors ignored)", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD, + g_param_spec_string (MM_GENERIC_GSM_FLOW_CONTROL_CMD, + "FlowControlCommand", + "Flow control configuration command (errors ignored)", + "+IFC=1,1", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h index 5712660..c6cb7fb 100644 --- a/src/mm-generic-gsm.h +++ b/src/mm-generic-gsm.h @@ -37,6 +37,7 @@ #define MM_GENERIC_GSM_POWER_DOWN_CMD "power-down-cmd" #define MM_GENERIC_GSM_INIT_CMD "init-cmd" #define MM_GENERIC_GSM_INIT_CMD_OPTIONAL "init-cmd-optional" +#define MM_GENERIC_GSM_FLOW_CONTROL_CMD "flow-control-cmd" typedef enum { MM_GENERIC_GSM_PROP_FIRST = 0x2000, @@ -57,6 +58,7 @@ typedef enum { MM_GENERIC_GSM_PROP_USSD_STATE, MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST, MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION, + MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD } MMGenericGsmProp; typedef enum { diff --git a/src/mm-log.c b/src/mm-log.c index bcf806a..779afe7 100644 --- a/src/mm-log.c +++ b/src/mm-log.c @@ -77,8 +77,8 @@ _mm_log (const char *loc, g_get_current_time (&tv); snprintf (&tsbuf[0], sizeof (tsbuf), " [%09ld.%06ld]", tv.tv_sec, tv.tv_usec); } else if (ts_flags == TS_FLAG_REL) { - time_t secs; - suseconds_t usecs; + glong secs; + glong usecs; g_get_current_time (&tv); secs = tv.tv_sec - rel_start.tv_sec; @@ -92,23 +92,23 @@ _mm_log (const char *loc, } if ((log_level & LOGL_DEBUG) && (level == LOGL_DEBUG)) - prefix = "debug"; + prefix = "<debug>"; else if ((log_level & LOGL_INFO) && (level == LOGL_INFO)) - prefix = "info"; + prefix = "<info> "; else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) { - prefix = "warn"; + prefix = "<warn> "; syslog_priority = LOG_WARNING; } else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) { - prefix = "err"; + prefix = "<error>"; syslog_priority = LOG_ERR; } else g_warn_if_reached (); if (prefix) { if (log_level & LOGL_DEBUG) - snprintf (msgbuf, sizeof (msgbuf), "<%s>%s [%s] %s(): %s\n", prefix, tsbuf, loc, func, msg); + snprintf (msgbuf, sizeof (msgbuf), "%s%s [%s] %s(): %s\n", prefix, tsbuf, loc, func, msg); else - snprintf (msgbuf, sizeof (msgbuf), "<%s>%s %s\n", prefix, tsbuf, msg); + snprintf (msgbuf, sizeof (msgbuf), "%s%s %s\n", prefix, tsbuf, msg); if (logfd < 0) syslog (syslog_priority, "%s", msgbuf); diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c index ee5e1c8..9afc1bf 100644 --- a/src/mm-modem-base.c +++ b/src/mm-modem-base.c @@ -526,12 +526,12 @@ mm_modem_base_get_card_info (MMModemBase *self, MMModemBasePrivate *priv; MMCallbackInfo *info; gboolean cached = FALSE; - GError *error = port_error; + GError *error = NULL; g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_MODEM_BASE (self)); - g_return_if_fail (port != NULL); - g_return_if_fail (MM_IS_AT_SERIAL_PORT (port)); + /* Either we get a proper AT port, or we get a port_error */ + g_return_if_fail ((port != NULL && MM_IS_AT_SERIAL_PORT (port)) || port_error != NULL); g_return_if_fail (callback != NULL); priv = MM_MODEM_BASE_GET_PRIVATE (self); diff --git a/src/mm-modem-cdma.c b/src/mm-modem-cdma.c index 722918e..485e5f2 100644 --- a/src/mm-modem-cdma.c +++ b/src/mm-modem-cdma.c @@ -34,6 +34,7 @@ static void impl_modem_cdma_activate_manual (MMModemCdma *modem, DBusGMethodInvo enum { SIGNAL_QUALITY, REGISTRATION_STATE_CHANGED, + ACTIVATION_STATE_CHANGED, LAST_SIGNAL }; @@ -348,6 +349,8 @@ mm_modem_cdma_emit_registration_state_changed (MMModemCdma *self, /*****************************************************************************/ +#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) + static void mm_modem_cdma_init (gpointer g_iface) { @@ -385,6 +388,15 @@ mm_modem_cdma_init (gpointer g_iface) mm_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + signals[ACTIVATION_STATE_CHANGED] = + g_signal_new ("activation-state-changed", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModemCdma, registration_state_changed), + NULL, NULL, + mm_marshal_VOID__UINT_UINT_BOXED, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, DBUS_TYPE_G_MAP_OF_VARIANT); + initialized = TRUE; } diff --git a/src/mm-modem-gsm-network.c b/src/mm-modem-gsm-network.c index 75ca7de..c152ddf 100644 --- a/src/mm-modem-gsm-network.c +++ b/src/mm-modem-gsm-network.c @@ -21,6 +21,7 @@ #include "mm-errors.h" #include "mm-callback-info.h" #include "mm-marshal.h" +#include "mm-utils.h" static void impl_gsm_modem_register (MMModemGsmNetwork *modem, const char *network_id, @@ -473,39 +474,11 @@ impl_gsm_modem_get_signal_quality (MMModemGsmNetwork *modem, mm_modem_gsm_network_get_signal_quality (modem, uint_call_done, context); } -static gboolean -check_for_single_value (guint32 value) -{ - gboolean found = FALSE; - guint32 i; - - for (i = 1; i <= 32; i++) { - if (value & 0x1) { - if (found) - return FALSE; /* More than one bit set */ - found = TRUE; - } - value >>= 1; - } - - return TRUE; -} - static void impl_gsm_modem_set_band (MMModemGsmNetwork *modem, MMModemGsmBand band, DBusGMethodInvocation *context) { - if (!check_for_single_value (band)) { - GError *error; - - error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, - "Invalid arguments (more than one value given)"); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - mm_modem_gsm_network_set_band (modem, band, async_call_done, context); } @@ -521,7 +494,7 @@ impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem, MMModemDeprecatedMode old_mode, DBusGMethodInvocation *context) { - if (!check_for_single_value (old_mode)) { + if (!utils_check_for_single_value (old_mode)) { GError *error; error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, diff --git a/src/mm-modem-gsm-sms.c b/src/mm-modem-gsm-sms.c index d74c7b3..ab20d3e 100644 --- a/src/mm-modem-gsm-sms.c +++ b/src/mm-modem-gsm-sms.c @@ -103,6 +103,34 @@ async_call_not_supported (MMModemGsmSms *self, mm_callback_info_schedule (info); } +static void +sms_get_done (MMModemGsmSms *self, + GHashTable *properties, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, properties); +} + +static void +sms_list_done (MMModemGsmSms *self, + GPtrArray *results, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, results); +} + /*****************************************************************************/ void @@ -127,6 +155,110 @@ mm_modem_gsm_sms_send (MMModemGsmSms *self, } +static void +sms_get_invoke (MMCallbackInfo *info) +{ + MMModemGsmSmsGetFn callback = (MMModemGsmSmsGetFn) info->callback; + + callback (MM_MODEM_GSM_SMS (info->modem), NULL, info->error, info->user_data); +} + +void +mm_modem_gsm_sms_get (MMModemGsmSms *self, + guint idx, + MMModemGsmSmsGetFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_SMS_GET_INTERFACE (self)->get) + MM_MODEM_GSM_SMS_GET_INTERFACE (self)->get (self, idx, callback, user_data); + else { + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), + sms_get_invoke, + G_CALLBACK (callback), + user_data); + + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); + } +} + +void +mm_modem_gsm_sms_delete (MMModemGsmSms *self, + guint idx, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_SMS_GET_INTERFACE (self)->delete) + MM_MODEM_GSM_SMS_GET_INTERFACE (self)->delete (self, idx, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +static void +sms_list_invoke (MMCallbackInfo *info) +{ + MMModemGsmSmsListFn callback = (MMModemGsmSmsListFn) info->callback; + + callback (MM_MODEM_GSM_SMS (info->modem), NULL, info->error, info->user_data); +} + +void +mm_modem_gsm_sms_list (MMModemGsmSms *self, + MMModemGsmSmsListFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_SMS_GET_INTERFACE (self)->list) + MM_MODEM_GSM_SMS_GET_INTERFACE (self)->list (self, callback, user_data); + else { + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), + sms_list_invoke, + G_CALLBACK (callback), + user_data); + + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); + } +} + +void +mm_modem_gsm_sms_received (MMModemGsmSms *self, + guint idx, + gboolean complete) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + + g_signal_emit (self, signals[SMS_RECEIVED], 0, + idx, + complete); +} + +void +mm_modem_gsm_sms_completed (MMModemGsmSms *self, + guint idx, + gboolean complete) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + + g_signal_emit (self, signals[COMPLETED], 0, + idx, + complete); +} + /*****************************************************************************/ typedef struct { @@ -144,7 +276,8 @@ sms_auth_info_destroy (gpointer data) { SmsAuthInfo *info = data; - g_hash_table_destroy (info->hash); + if (info->hash) + g_hash_table_destroy (info->hash); g_free (info->str); memset (info, 0, sizeof (SmsAuthInfo)); g_free (info); @@ -211,14 +344,18 @@ sms_delete_auth_cb (MMAuthRequest *req, gpointer user_data) { MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner); + SmsAuthInfo *info = user_data; GError *error = NULL; + guint idx; /* Return any authorization error, otherwise delete the SMS */ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); - } else - async_call_not_supported (self, async_call_done, context); + } else { + idx = info->num1; + mm_modem_gsm_sms_delete (self, idx, async_call_done, context); + } } static void @@ -253,14 +390,18 @@ sms_get_auth_cb (MMAuthRequest *req, gpointer user_data) { MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner); + SmsAuthInfo *info = user_data; + guint idx; GError *error = NULL; /* Return any authorization error, otherwise get the SMS */ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); - } else - async_call_not_supported (self, async_call_done, context); + } else { + idx = info->num1; + mm_modem_gsm_sms_get (self, idx, sms_get_done, context); + } } static void @@ -368,7 +509,7 @@ sms_list_auth_cb (MMAuthRequest *req, dbus_g_method_return_error (context, error); g_error_free (error); } else - async_call_not_supported (self, async_call_done, context); + mm_modem_gsm_sms_list (self, sms_list_done, context); } static void diff --git a/src/mm-modem-gsm-sms.h b/src/mm-modem-gsm-sms.h index 79a5bb0..41684d7 100644 --- a/src/mm-modem-gsm-sms.h +++ b/src/mm-modem-gsm-sms.h @@ -25,6 +25,16 @@ typedef struct _MMModemGsmSms MMModemGsmSms; +typedef void (*MMModemGsmSmsGetFn) (MMModemGsmSms *modem, + GHashTable *properties, + GError *error, + gpointer user_data); + +typedef void (*MMModemGsmSmsListFn) (MMModemGsmSms *modem, + GPtrArray *resultlist, + GError *error, + gpointer user_data); + struct _MMModemGsmSms { GTypeInterface g_iface; @@ -38,6 +48,20 @@ struct _MMModemGsmSms { MMModemFn callback, gpointer user_data); + void (*get) (MMModemGsmSms *modem, + guint32 index, + MMModemGsmSmsGetFn callback, + gpointer user_data); + + void (*delete) (MMModemGsmSms *modem, + guint32 index, + MMModemFn callback, + gpointer user_data); + + void (*list) (MMModemGsmSms *modem, + MMModemGsmSmsListFn callback, + gpointer user_data); + /* Signals */ void (*sms_received) (MMModemGsmSms *self, guint32 index, @@ -59,4 +83,27 @@ void mm_modem_gsm_sms_send (MMModemGsmSms *self, MMModemFn callback, gpointer user_data); +void mm_modem_gsm_sms_get (MMModemGsmSms *self, + guint idx, + MMModemGsmSmsGetFn callback, + gpointer user_data); + +void mm_modem_gsm_sms_delete (MMModemGsmSms *self, + guint idx, + MMModemFn callback, + gpointer user_data); + +void mm_modem_gsm_sms_list (MMModemGsmSms *self, + MMModemGsmSmsListFn callback, + gpointer user_data); + +void mm_modem_gsm_sms_received (MMModemGsmSms *self, + guint idx, + gboolean complete); + +void mm_modem_gsm_sms_completed (MMModemGsmSms *self, + guint idx, + gboolean complete); + + #endif /* MM_MODEM_GSM_SMS_H */ diff --git a/src/mm-modem-gsm.h b/src/mm-modem-gsm.h index a427d35..83dfb31 100644 --- a/src/mm-modem-gsm.h +++ b/src/mm-modem-gsm.h @@ -74,8 +74,9 @@ typedef enum { MM_MODEM_GSM_BAND_U900 = 0x00000400, /* WCDMA 3GPP UMTS 900 MHz (Class VIII) */ MM_MODEM_GSM_BAND_U17IX = 0x00000800, /* WCDMA 3GPP UMTS 1700 MHz (Class IX) */ MM_MODEM_GSM_BAND_U1900 = 0x00001000, /* WCDMA 3GPP UMTS 1900 MHz (Class II) */ + MM_MODEM_GSM_BAND_U2600 = 0x00002000, /* WCDMA 3GPP UMTS 2600 MHz (Class VII, internal) */ - MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U1900 + MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U2600 } MMModemGsmBand; diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index f13b4f2..f6a0ffa 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -235,7 +235,7 @@ mm_gsm_destroy_scan_data (gpointer data) #define CREG3 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)" /* +CREG: <n>,<stat>,<lac>,<ci> (GSM 07.07 solicited and some CREG=2 unsolicited) */ -#define CREG4 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)" +#define CREG4 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,]*)\\s*,\\s*([^,\\s]*)" /* +CREG: <stat>,<lac>,<ci>,<AcT> (ETSI 27.007 CREG=2 unsolicited) */ #define CREG5 "\\+(CREG|CGREG):\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})" diff --git a/src/mm-modem.c b/src/mm-modem.c index f823472..d535166 100644 --- a/src/mm-modem.c +++ b/src/mm-modem.c @@ -751,7 +751,7 @@ mm_modem_set_state (MMModem *self, if (new_state != old_state) { g_object_set (G_OBJECT (self), MM_MODEM_STATE, new_state, NULL); - g_signal_emit_by_name (G_OBJECT (self), "state-changed", new_state, old_state, reason); + g_signal_emit_by_name (G_OBJECT (self), "state-changed", old_state, new_state, reason); dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG); if (dbus_path) { diff --git a/src/mm-modem.h b/src/mm-modem.h index 7f0bf58..c478f70 100644 --- a/src/mm-modem.h +++ b/src/mm-modem.h @@ -207,8 +207,8 @@ struct _MMModem { /* Signals */ void (*state_changed) (MMModem *self, - MMModemState new_state, MMModemState old_state, + MMModemState new_state, MMModemStateReason reason); }; diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c index 8d32e2a..336f78c 100644 --- a/src/mm-plugin-base.c +++ b/src/mm-plugin-base.c @@ -93,6 +93,14 @@ G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OB #define MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskPrivate)) typedef struct { + char *command; + guint32 tries; + guint32 delay_seconds; + MMBaseSupportsTaskCustomInitResultFunc callback; + gpointer callback_data; +} CustomInit; + +typedef struct { MMPluginBase *plugin; GUdevDevice *port; char *physdev_path; @@ -110,11 +118,9 @@ typedef struct { char *probe_resp; GError *probe_error; - char *custom_init; - guint32 custom_init_max_tries; - guint32 custom_init_tries; - guint32 custom_init_delay_seconds; - gboolean custom_init_fail_if_timeout; + /* Custom init commands plugins might want */ + GSList *custom; + GSList *cur_custom; /* Pointer to current custom init command */ MMSupportsPortResultFunc callback; gpointer callback_data; @@ -225,24 +231,28 @@ mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task, } void -mm_plugin_base_supports_task_set_custom_init_command (MMPluginBaseSupportsTask *task, +mm_plugin_base_supports_task_add_custom_init_command (MMPluginBaseSupportsTask *task, const char *cmd, guint32 delay_seconds, - guint32 max_tries, - gboolean fail_if_timeout) + MMBaseSupportsTaskCustomInitResultFunc callback, + gpointer callback_data) { MMPluginBaseSupportsTaskPrivate *priv; + CustomInit *custom; g_return_if_fail (task != NULL); g_return_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task)); + g_return_if_fail (callback != NULL); priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); - g_free (priv->custom_init); - priv->custom_init = g_strdup (cmd); - priv->custom_init_max_tries = max_tries; - priv->custom_init_delay_seconds = delay_seconds; - priv->custom_init_fail_if_timeout = fail_if_timeout; + custom = g_malloc0 (sizeof (*custom)); + custom->command = g_strdup (cmd); + custom->delay_seconds = delay_seconds ? delay_seconds : 3; + custom->callback = callback; + custom->callback_data = callback_data; + + priv->custom = g_slist_append (priv->custom, custom); } static void @@ -254,6 +264,7 @@ static void supports_task_dispose (GObject *object) { MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (object); + GSList *iter; if (MM_IS_SERIAL_PORT (priv->probe_port)) mm_serial_port_flash_cancel (MM_SERIAL_PORT (priv->probe_port)); @@ -263,7 +274,14 @@ supports_task_dispose (GObject *object) g_free (priv->driver); g_free (priv->probe_resp); g_clear_error (&(priv->probe_error)); - g_free (priv->custom_init); + + for (iter = priv->custom; iter; iter = g_slist_next (iter)) { + CustomInit *custom = iter->data; + + g_free (custom->command); + memset (custom, 0, sizeof (*custom)); + g_free (custom); + } if (priv->open_id) g_source_remove (priv->open_id); @@ -721,6 +739,12 @@ parse_response (MMAtSerialPort *port, task_priv->probe_id = g_idle_add (handle_probe_response, task); } +static void +start_generic_probing (MMPluginBaseSupportsTask *task, MMAtSerialPort *port) +{ + mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, task); +} + static void flash_done (MMSerialPort *port, GError *error, gpointer user_data); static void @@ -731,24 +755,43 @@ custom_init_response (MMAtSerialPort *port, { MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + CustomInit *custom = task_priv->cur_custom->data; + gboolean retry = FALSE; + gboolean fail = FALSE; + guint32 level = 0; - if (error) { - task_priv->custom_init_tries++; - if (task_priv->custom_init_tries < task_priv->custom_init_max_tries) { - /* Try the custom command again */ - flash_done (MM_SERIAL_PORT (port), NULL, user_data); - return; - } else if (task_priv->custom_init_fail_if_timeout) { - /* Fail the probe if the plugin wanted it and the command timed out */ - if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { - probe_complete (task); - return; - } - } + custom->tries++; + retry = custom->callback (task, response, error, custom->tries, &fail, &level, custom->callback_data); + + if (fail) { + /* Plugin said to fail the probe */ + probe_complete (task); + return; + } + + if (level > 0) { + /* Plugin supports the modem */ + task_priv->probed_caps = level; + probe_complete (task); + return; + } + + if (retry) { + /* Try the custom command again */ + flash_done (MM_SERIAL_PORT (port), NULL, task); + return; + } + + /* Any more custom init commands? */ + task_priv->cur_custom = g_slist_next (task_priv->cur_custom); + if (task_priv->cur_custom) { + /* There are more custom init commands */ + flash_done (MM_SERIAL_PORT (port), NULL, task); + return; } - /* Otherwise proceed to probing */ - mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data); + /* Otherwise continue with generic probing */ + start_generic_probing (task, port); } static void @@ -756,20 +799,19 @@ flash_done (MMSerialPort *port, GError *error, gpointer user_data) { MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); - guint32 delay_secs = task_priv->custom_init_delay_seconds; /* Send the custom init command if any */ - if (task_priv->custom_init) { - if (!delay_secs) - delay_secs = 3; + if (task_priv->cur_custom) { + CustomInit *custom = task_priv->cur_custom->data; + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), - task_priv->custom_init, - delay_secs, + custom->command, + custom->delay_seconds, custom_init_response, - user_data); + task); } else { /* Otherwise start normal probing */ - custom_init_response (MM_AT_SERIAL_PORT (port), NULL, NULL, user_data); + start_generic_probing (task, MM_AT_SERIAL_PORT (port)); } } @@ -820,6 +862,7 @@ try_open (gpointer user_data) gboolean mm_plugin_base_probe_port (MMPluginBase *self, MMPluginBaseSupportsTask *task, + guint64 send_delay_us, GError **error) { MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); @@ -844,8 +887,9 @@ mm_plugin_base_probe_port (MMPluginBase *self, } g_object_set (serial, - MM_SERIAL_PORT_SEND_DELAY, (guint64) 100000, + MM_SERIAL_PORT_SEND_DELAY, send_delay_us, MM_PORT_CARRIER_DETECT, FALSE, + MM_SERIAL_PORT_SPEW_CONTROL, TRUE, NULL); mm_at_serial_port_set_response_parser (serial, @@ -855,6 +899,7 @@ mm_plugin_base_probe_port (MMPluginBase *self, /* Open the port */ task_priv->probe_port = serial; + task_priv->cur_custom = task_priv->custom; task_priv->open_id = g_idle_add (try_open, task); return TRUE; } @@ -933,6 +978,10 @@ mm_plugin_base_get_device_ids (MMPluginBase *self, pid = g_udev_device_get_sysfs_attr (parent, "card_id"); if (!vid || !pid) goto out; + } else if (!strcmp (parent_subsys, "platform")) { + /* Platform devices don't usually have a VID/PID */ + success = TRUE; + goto out; } } } diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h index a32d53b..a5440a5 100644 --- a/src/mm-plugin-base.h +++ b/src/mm-plugin-base.h @@ -56,6 +56,27 @@ typedef struct { GType mm_plugin_base_supports_task_get_type (void); +/* + * response: the response string from the modem, if no error occurred + * error: the error returned by the modem or serial stack, if any + * tries: number of times the custom init command has been sent to the modem + * out_fail: on return, TRUE means fail the probe and close the port + * out_level: if the custom init command has determined that the modem is + * supported, return the support level here, and probing will cease + * + * Function should return TRUE if the custom init command should be retried, + * FALSE if it should not. If FALSE is returned, generic probing will continue + * if out_fail == FALSE and out_level == 0, otherwise if out_fail == FALSE + * probing will stop, or if out_level > 0 the port will be claimed. + */ +typedef gboolean (*MMBaseSupportsTaskCustomInitResultFunc) (MMPluginBaseSupportsTask *task, + GString *response, + GError *error, + guint32 tries, + gboolean *out_fail, + guint32 *out_level, + gpointer user_data); + MMPlugin *mm_plugin_base_supports_task_get_plugin (MMPluginBaseSupportsTask *task); GUdevDevice *mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task); @@ -69,11 +90,11 @@ guint32 mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSuppor void mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task, guint32 level); -void mm_plugin_base_supports_task_set_custom_init_command (MMPluginBaseSupportsTask *task, +void mm_plugin_base_supports_task_add_custom_init_command (MMPluginBaseSupportsTask *task, const char *cmd, guint32 delay_seconds, - guint32 max_tries, - gboolean fail_if_timeout); + MMBaseSupportsTaskCustomInitResultFunc callback, + gpointer callback_data); #define MM_TYPE_PLUGIN_BASE (mm_plugin_base_get_type ()) #define MM_PLUGIN_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BASE, MMPluginBase)) @@ -108,6 +129,7 @@ struct _MMPluginBaseClass { void (*cancel_task) (MMPluginBase *plugin, MMPluginBaseSupportsTask *task); + /* Lets plugins read the probe response before the generic plugin processes it */ void (*handle_probe_response) (MMPluginBase *plugin, MMPluginBaseSupportsTask *task, const char *command, @@ -130,6 +152,7 @@ gboolean mm_plugin_base_get_device_ids (MMPluginBase *self, gboolean mm_plugin_base_probe_port (MMPluginBase *self, MMPluginBaseSupportsTask *task, + guint64 send_delay_us, GError **error); /* Returns TRUE if the port was previously probed, FALSE if not */ diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c index 75bcce4..f83301d 100644 --- a/src/mm-serial-parsers.c +++ b/src/mm-serial-parsers.c @@ -196,13 +196,18 @@ mm_serial_parser_v0_destroy (gpointer data) } typedef struct { + /* Regular expressions for successful replies */ GRegex *regex_ok; GRegex *regex_connect; + GRegex *regex_custom_successful; + /* Regular expressions for error replies */ GRegex *regex_cme_error; + GRegex *regex_cms_error; GRegex *regex_cme_error_str; GRegex *regex_ezx_error; GRegex *regex_unknown_error; GRegex *regex_connect_failed; + GRegex *regex_custom_error; } MMSerialParserV1; gpointer @@ -216,14 +221,36 @@ mm_serial_parser_v1_new (void) parser->regex_ok = g_regex_new ("\\r\\nOK(\\r\\n)+$", flags, 0, NULL); parser->regex_connect = g_regex_new ("\\r\\nCONNECT.*\\r\\n", flags, 0, NULL); parser->regex_cme_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); + parser->regex_cms_error = g_regex_new ("\\r\\n\\+CMS ERROR: (\\d+)\\r\\n$", flags, 0, NULL); parser->regex_cme_error_str = g_regex_new ("\\r\\n\\+CME ERROR: ([^\\n\\r]+)\\r\\n$", flags, 0, NULL); parser->regex_ezx_error = g_regex_new ("\\r\\n\\MODEM ERROR: (\\d+)\\r\\n$", flags, 0, NULL); parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL); parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$", flags, 0, NULL); + parser->regex_custom_successful = NULL; + parser->regex_custom_error = NULL; + return parser; } +void +mm_serial_parser_v1_set_custom_regex (gpointer data, + GRegex *successful, + GRegex *error) +{ + MMSerialParserV1 *parser = (MMSerialParserV1 *) data; + + g_return_if_fail (parser != NULL); + + if (parser->regex_custom_successful) + g_regex_unref (parser->regex_custom_successful); + if (parser->regex_custom_error) + g_regex_unref (parser->regex_custom_error); + + parser->regex_custom_successful = successful ? g_regex_ref (successful) : NULL; + parser->regex_custom_error = error ? g_regex_ref (error) : NULL; +} + gboolean mm_serial_parser_v1_parse (gpointer data, GString *response, @@ -244,11 +271,24 @@ mm_serial_parser_v1_parse (gpointer data, /* First, check for successful responses */ - found = g_regex_match_full (parser->regex_ok, response->str, response->len, 0, 0, NULL, NULL); - if (found) - remove_matches (parser->regex_ok, response); - else - found = g_regex_match_full (parser->regex_connect, response->str, response->len, 0, 0, NULL, NULL); + /* Custom successful replies first, if any */ + if (parser->regex_custom_successful) { + found = g_regex_match_full (parser->regex_custom_successful, + response->str, response->len, + 0, 0, NULL, NULL); + } + + if (!found) { + found = g_regex_match_full (parser->regex_ok, + response->str, response->len, + 0, 0, NULL, NULL); + if (found) + remove_matches (parser->regex_ok, response); + else + found = g_regex_match_full (parser->regex_connect, + response->str, response->len, + 0, 0, NULL, NULL); + } if (found) { response_clean (response); @@ -257,6 +297,21 @@ mm_serial_parser_v1_parse (gpointer data, /* Now failures */ + /* Custom error matches first, if any */ + if (parser->regex_custom_error) { + found = g_regex_match_full (parser->regex_custom_error, + response->str, response->len, + 0, 0, &match_info, NULL); + if (found) { + str = g_match_info_fetch (match_info, 1); + g_assert (str); + local_error = mm_mobile_error_for_code (atoi (str)); + g_free (str); + g_match_info_free (match_info); + goto done; + } + } + /* Numeric CME errors */ found = g_regex_match_full (parser->regex_cme_error, response->str, response->len, @@ -270,6 +325,23 @@ mm_serial_parser_v1_parse (gpointer data, goto done; } + /* Numeric CMS errors */ + /* Todo + * One should probably add message service + * errors explicitly in mm-errors.h/c + */ + found = g_regex_match_full (parser->regex_cms_error, + response->str, response->len, + 0, 0, &match_info, NULL); + if (found) { + str = g_match_info_fetch (match_info, 1); + g_assert (str); + local_error = mm_mobile_error_for_code (atoi (str)); + g_free (str); + g_match_info_free (match_info); + goto done; + } + /* String CME errors */ found = g_regex_match_full (parser->regex_cme_error_str, response->str, response->len, @@ -359,6 +431,11 @@ mm_serial_parser_v1_destroy (gpointer data) g_regex_unref (parser->regex_unknown_error); g_regex_unref (parser->regex_connect_failed); + if (parser->regex_custom_successful) + g_regex_unref (parser->regex_custom_successful); + if (parser->regex_custom_error) + g_regex_unref (parser->regex_custom_error); + g_slice_free (MMSerialParserV1, data); } diff --git a/src/mm-serial-parsers.h b/src/mm-serial-parsers.h index 3e1fb9f..92361c3 100644 --- a/src/mm-serial-parsers.h +++ b/src/mm-serial-parsers.h @@ -22,23 +22,23 @@ gpointer mm_serial_parser_v0_new (void); gboolean mm_serial_parser_v0_parse (gpointer parser, GString *response, GError **error); - void mm_serial_parser_v0_destroy (gpointer parser); -gpointer mm_serial_parser_v1_new (void); -gboolean mm_serial_parser_v1_parse (gpointer parser, - GString *response, - GError **error); - -void mm_serial_parser_v1_destroy (gpointer parser); +gpointer mm_serial_parser_v1_new (void); +void mm_serial_parser_v1_set_custom_regex (gpointer data, + GRegex *successful, + GRegex *error); +gboolean mm_serial_parser_v1_parse (gpointer parser, + GString *response, + GError **error); +void mm_serial_parser_v1_destroy (gpointer parser); gpointer mm_serial_parser_v1_e1_new (void); gboolean mm_serial_parser_v1_e1_parse (gpointer parser, GString *response, GError **error); - void mm_serial_parser_v1_e1_destroy (gpointer parser); #endif /* MM_SERIAL_PARSERS_H */ diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c index bf2a98a..18a616d 100644 --- a/src/mm-serial-port.c +++ b/src/mm-serial-port.c @@ -44,6 +44,7 @@ enum { PROP_STOPBITS, PROP_SEND_DELAY, PROP_FD, + PROP_SPEW_CONTROL, LAST_PROP }; @@ -67,8 +68,9 @@ typedef struct { char parity; guint stopbits; guint64 send_delay; + gboolean spew_control; - guint queue_schedule; + guint queue_id; guint watch_id; guint timeout_id; @@ -76,6 +78,18 @@ typedef struct { guint connected_id; } MMSerialPortPrivate; +typedef struct { + GByteArray *command; + guint32 idx; + guint32 eagain_count; + gboolean started; + gboolean done; + GCallback callback; + gpointer user_data; + guint32 timeout; + gboolean cached; +} MMQueueData; + #if 0 static const char * baud_to_string (int baud) @@ -355,57 +369,67 @@ serial_debug (MMSerialPort *self, const char *prefix, const char *buf, gsize len } static gboolean -mm_serial_port_send_command (MMSerialPort *self, - GByteArray *command, - GError **error) +mm_serial_port_process_command (MMSerialPort *self, + MMQueueData *info, + GError **error) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - int status, i = 0; - int eagain_count = 1000; const guint8 *p; + int status, expected_status, send_len; if (priv->fd < 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, - "%s", "Sending command failed: device is not enabled"); + g_set_error_literal (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, + "Sending command failed: device is not enabled"); return FALSE; } if (mm_port_get_connected (MM_PORT (self))) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, - "%s", "Sending command failed: device is connected"); + g_set_error_literal (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, + "Sending command failed: device is connected"); return FALSE; } - serial_debug (self, "-->", (const char *) command->data, command->len); - - /* Only accept about 3 seconds of EAGAIN */ - if (priv->send_delay > 0) - eagain_count = 3000000 / priv->send_delay; - - while (i < command->len) { - p = &command->data[i]; - status = write (priv->fd, p, 1); - if (status < 0) { - if (errno == EAGAIN) { - eagain_count--; - if (eagain_count <= 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, - "Sending command failed: '%s'", strerror (errno)); - break; - } - } else { + /* Only print command the first time */ + if (info->started == FALSE) { + info->started = TRUE; + serial_debug (self, "-->", (const char *) info->command->data, info->command->len); + } + + if (priv->send_delay == 0) { + /* Send the whole command in one write */ + send_len = expected_status = info->command->len; + p = info->command->data; + } else { + /* Send just one byte of the command */ + send_len = expected_status = 1; + p = &info->command->data[info->idx]; + } + + /* Send a single byte of the command */ + errno = 0; + status = write (priv->fd, p, send_len); + if (status > 0) + info->idx += status; + else { + /* Error or no bytes written */ + if (errno == EAGAIN || status == 0) { + info->eagain_count--; + if (info->eagain_count <= 0) { g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: '%s'", strerror (errno)); - break; + return FALSE; } - } else - i++; - - if (priv->send_delay) - usleep (priv->send_delay); + } else { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, + "Sending command failed: '%s'", strerror (errno)); + return FALSE; + } } - return i == command->len; + if (info->idx >= info->command->len) + info->done = TRUE; + + return TRUE; } static void @@ -436,35 +460,25 @@ mm_serial_port_get_cached_reply (MMSerialPort *self, GByteArray *command) return (const GByteArray *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); } -typedef struct { - GByteArray *command; - GCallback callback; - gpointer user_data; - guint32 timeout; - gboolean cached; -} MMQueueData; - static void -mm_serial_port_schedule_queue_process (MMSerialPort *self) +mm_serial_port_schedule_queue_process (MMSerialPort *self, guint timeout_ms) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - GSource *source; if (priv->timeout_id) { /* A command is already in progress */ return; } - if (priv->queue_schedule) { + if (priv->queue_id) { /* Already scheduled */ return; } - source = g_idle_source_new (); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_queue_process), G_OBJECT (self))); - g_source_attach (source, NULL); - priv->queue_schedule = g_source_get_id (source); - g_source_unref (source); + if (timeout_ms) + priv->queue_id = g_timeout_add (timeout_ms, mm_serial_port_queue_process, self); + else + priv->queue_id = g_idle_add (mm_serial_port_queue_process, self); } static gsize @@ -516,7 +530,7 @@ mm_serial_port_got_response (MMSerialPort *self, GError *error) if (consumed) g_byte_array_remove_range (priv->response, 0, consumed); if (!g_queue_is_empty (priv->queue)) - mm_serial_port_schedule_queue_process (self); + mm_serial_port_schedule_queue_process (self, 0); } static gboolean @@ -547,7 +561,7 @@ mm_serial_port_queue_process (gpointer data) MMQueueData *info; GError *error = NULL; - priv->queue_schedule = 0; + priv->queue_id = 0; info = (MMQueueData *) g_queue_peek_head (priv->queue); if (!info) @@ -557,23 +571,35 @@ mm_serial_port_queue_process (gpointer data) const GByteArray *cached = mm_serial_port_get_cached_reply (self, info->command); if (cached) { + /* Ensure the response array is fully empty before setting the + * cached response. */ + if (priv->response->len > 0) { + g_warning ("%s: (%s) response array is not empty when using " + "cached reply, cleaning up %u bytes", + __func__, + mm_port_get_device (MM_PORT (self)), + priv->response->len); + g_byte_array_set_size (priv->response, 0); + } + g_byte_array_append (priv->response, cached->data, cached->len); mm_serial_port_got_response (self, NULL); return FALSE; } } - if (mm_serial_port_send_command (self, info->command, &error)) { - GSource *source; - - source = g_timeout_source_new_seconds (info->timeout); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_timed_out), G_OBJECT (self))); - g_source_attach (source, NULL); - priv->timeout_id = g_source_get_id (source); - g_source_unref (source); - } else { + if (mm_serial_port_process_command (self, info, &error)) { + if (info->done) { + /* If the command is finished being sent, schedule the timeout */ + priv->timeout_id = g_timeout_add_seconds (info->timeout, + mm_serial_port_timed_out, + self); + } else { + /* Schedule the next byte of the command to be sent */ + mm_serial_port_schedule_queue_process (self, priv->send_delay / 1000); + } + } else mm_serial_port_got_response (self, error); - } return FALSE; } @@ -600,6 +626,7 @@ data_available (GIOChannel *source, char buf[SERIAL_BUF_SIZE + 1]; gsize bytes_read; GIOStatus status; + MMQueueData *info; if (condition & G_IO_HUP) { if (priv->response->len) @@ -614,6 +641,11 @@ data_available (GIOChannel *source, return TRUE; } + /* Don't read any input if the current command isn't done being sent yet */ + info = g_queue_peek_nth (priv->queue, 0); + if (info && (info->started == TRUE) && (info->done == FALSE)) + return TRUE; + do { GError *err = NULL; @@ -638,7 +670,7 @@ data_available (GIOChannel *source, } /* Make sure the response doesn't grow too long */ - if (priv->response->len > SERIAL_BUF_SIZE) { + if ((priv->response->len > SERIAL_BUF_SIZE) && priv->spew_control) { /* Notify listeners and then trim the buffer */ g_signal_emit_by_name (self, "buffer-full", priv->response); g_byte_array_remove_range (priv->response, 0, (SERIAL_BUF_SIZE / 2)); @@ -805,6 +837,8 @@ mm_serial_port_close (MMSerialPort *self) priv->connected_id = 0; } + mm_serial_port_flash_cancel (self); + if (priv->fd >= 0) { GTimeVal tv_start, tv_end; @@ -820,8 +854,6 @@ mm_serial_port_close (MMSerialPort *self) priv->channel = NULL; } - mm_serial_port_flash_cancel (self); - g_get_current_time (&tv_start); tcsetattr (priv->fd, TCSANOW, &priv->old_t); @@ -869,6 +901,16 @@ mm_serial_port_close (MMSerialPort *self) g_slice_free (MMQueueData, item); } g_queue_clear (priv->queue); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + if (priv->queue_id) { + g_source_remove (priv->queue_id); + priv->queue_id = 0; + } } void @@ -909,6 +951,13 @@ internal_queue_command (MMSerialPort *self, info->command = g_byte_array_sized_new (command->len); g_byte_array_append (info->command, command->data, command->len); } + + /* Only accept about 3 seconds of EAGAIN for this command */ + if (priv->send_delay) + info->eagain_count = 3000000 / priv->send_delay; + else + info->eagain_count = 1000; + info->cached = cached; info->timeout = timeout_seconds; info->callback = (GCallback) callback; @@ -921,7 +970,7 @@ internal_queue_command (MMSerialPort *self, g_queue_push_tail (priv->queue, info); if (g_queue_get_length (priv->queue) == 1) - mm_serial_port_schedule_queue_process (self); + mm_serial_port_schedule_queue_process (self, 0); } void @@ -1224,6 +1273,9 @@ set_property (GObject *object, guint prop_id, case PROP_SEND_DELAY: priv->send_delay = g_value_get_uint64 (value); break; + case PROP_SPEW_CONTROL: + priv->spew_control = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1255,6 +1307,9 @@ get_property (GObject *object, guint prop_id, case PROP_SEND_DELAY: g_value_set_uint64 (value, priv->send_delay); break; + case PROP_SPEW_CONTROL: + g_value_set_boolean (value, priv->spew_control); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1264,6 +1319,13 @@ get_property (GObject *object, guint prop_id, static void dispose (GObject *object) { + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + if (mm_serial_port_is_open (MM_SERIAL_PORT (object))) mm_serial_port_close_force (MM_SERIAL_PORT (object)); @@ -1346,10 +1408,18 @@ mm_serial_port_class_init (MMSerialPortClass *klass) (object_class, PROP_SEND_DELAY, g_param_spec_uint64 (MM_SERIAL_PORT_SEND_DELAY, "SendDelay", - "Send delay", + "Send delay for each byte in microseconds", 0, G_MAXUINT64, 0, G_PARAM_READWRITE)); + g_object_class_install_property + (object_class, PROP_SPEW_CONTROL, + g_param_spec_boolean (MM_SERIAL_PORT_SPEW_CONTROL, + "SpewControl", + "Spew control", + FALSE, + G_PARAM_READWRITE)); + /* Signals */ g_signal_new ("buffer-full", G_OBJECT_CLASS_TYPE (object_class), diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h index 5fee1b4..57ef94b 100644 --- a/src/mm-serial-port.h +++ b/src/mm-serial-port.h @@ -30,12 +30,13 @@ #define MM_IS_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL_PORT)) #define MM_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL_PORT, MMSerialPortClass)) -#define MM_SERIAL_PORT_BAUD "baud" -#define MM_SERIAL_PORT_BITS "bits" -#define MM_SERIAL_PORT_PARITY "parity" -#define MM_SERIAL_PORT_STOPBITS "stopbits" -#define MM_SERIAL_PORT_SEND_DELAY "send-delay" -#define MM_SERIAL_PORT_FD "fd" /* Construct-only */ +#define MM_SERIAL_PORT_BAUD "baud" +#define MM_SERIAL_PORT_BITS "bits" +#define MM_SERIAL_PORT_PARITY "parity" +#define MM_SERIAL_PORT_STOPBITS "stopbits" +#define MM_SERIAL_PORT_SEND_DELAY "send-delay" +#define MM_SERIAL_PORT_FD "fd" /* Construct-only */ +#define MM_SERIAL_PORT_SPEW_CONTROL "spew-control" /* Construct-only */ typedef struct _MMSerialPort MMSerialPort; typedef struct _MMSerialPortClass MMSerialPortClass; diff --git a/src/mm-utils.c b/src/mm-utils.c index c435d1d..236c2d9 100644 --- a/src/mm-utils.c +++ b/src/mm-utils.c @@ -90,3 +90,20 @@ utils_bin2hexstr (const guint8 *bin, gsize len) return g_string_free (ret, FALSE); } +gboolean +utils_check_for_single_value (guint32 value) +{ + gboolean found = FALSE; + guint32 i; + + for (i = 1; i <= 32; i++) { + if (value & 0x1) { + if (found) + return FALSE; /* More than one bit set */ + found = TRUE; + } + value >>= 1; + } + + return TRUE; +} diff --git a/src/mm-utils.h b/src/mm-utils.h index 1b9b328..be77f8a 100644 --- a/src/mm-utils.h +++ b/src/mm-utils.h @@ -22,5 +22,7 @@ char *utils_hexstr2bin (const char *hex, gsize *out_len); char *utils_bin2hexstr (const guint8 *bin, gsize len); +gboolean utils_check_for_single_value (guint32 value); + #endif /* MM_UTILS_H */ diff --git a/src/tests/test-charsets.c b/src/tests/test-charsets.c index 656b80c..70c796a 100644 --- a/src/tests/test-charsets.c +++ b/src/tests/test-charsets.c @@ -98,7 +98,7 @@ test_unpack_gsm7 (void *f, gpointer d) guint8 *unpacked; guint32 unpacked_len = 0; - unpacked = gsm_unpack (gsm, sizeof (gsm), 0, &unpacked_len); + unpacked = gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len); g_assert (unpacked); g_assert_cmpint (unpacked_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0); @@ -110,17 +110,16 @@ static void test_unpack_gsm7_7_chars (void *f, gpointer d) { static const guint8 gsm[] = { 0xF1, 0x7B, 0x59, 0x4E, 0xCF, 0xD7, 0x01 }; - static const guint8 expected[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x00 }; + static const guint8 expected[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75}; guint8 *unpacked; guint32 unpacked_len = 0; /* Tests the edge case where there are 7 bits left in the packed * buffer but those 7 bits do not contain a character. In this case - * we expect a trailing NULL byte and the caller must know enough about - * the intended message to remove it when required. + * we expect to get the number of characters that were specified. */ - unpacked = gsm_unpack (gsm, sizeof (gsm), 0, &unpacked_len); + unpacked = gsm_unpack (gsm, 7 , 0, &unpacked_len); g_assert (unpacked); g_assert_cmpint (unpacked_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0); @@ -153,7 +152,7 @@ test_unpack_gsm7_all_chars (void *f, gpointer d) guint32 unpacked_len = 0; int i; - unpacked = gsm_unpack (gsm, sizeof (gsm), 0, &unpacked_len); + unpacked = gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len); g_assert (unpacked); g_assert_cmpint (unpacked_len, ==, 148); diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 946916f..d07f0d4 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -733,6 +733,16 @@ test_creg2_s8500_wave_unsolicited (void *f, gpointer d) } static void +test_creg2_gobi_weird_solicited (void *f, gpointer d) +{ + TestData *data = (TestData *) d; + const char *reply = "\r\n+CREG: 2,1, 0 5, 2715\r\n"; + const CregResult result = { 1, 0x0000, 0x2715, -1, 4, FALSE}; + + test_creg_match ("Qualcomm Gobi 1000 CREG=2", TRUE, reply, data, &result); +} + +static void test_cscs_icon225_support_response (void *f, gpointer d) { const char *reply = "\r\n+CSCS: (\"IRA\",\"GSM\",\"UCS2\")\r\n"; @@ -1251,6 +1261,7 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_creg2_xu870_unsolicited_unregistered, data)); g_test_suite_add (suite, TESTCASE (test_creg2_md400_unsolicited, data)); g_test_suite_add (suite, TESTCASE (test_creg2_s8500_wave_unsolicited, data)); + g_test_suite_add (suite, TESTCASE (test_creg2_gobi_weird_solicited, data)); g_test_suite_add (suite, TESTCASE (test_cgreg1_solicited, data)); g_test_suite_add (suite, TESTCASE (test_cgreg1_unsolicited, data)); |