static int modem_probe_caps(int fd, glong timeout_ms) { const char *gcap_responses[] = { GCAP_TAG, NULL }; const char *terminators[] = { "OK", "ERROR", "ERR", "+CME ERROR", NULL }; char *reply = NULL; int idx = -1, term_idx = -1, ret = 0; gboolean try_ati = FALSE; GTimeVal start, end; gboolean send_success; /* If a delay was specified, start a bit later */ if (timeout_ms > 500) { g_usleep (500000); timeout_ms -= 500; } /* Standard response timeout case */ timeout_ms += 3000; while (timeout_ms > 0) { GTimeVal diff; gulong sleep_time = 100000; g_get_current_time (&start); idx = term_idx = 0; send_success = modem_send_command (fd, "AT+GCAP\r\n"); if (send_success) idx = modem_wait_reply (fd, 2, gcap_responses, terminators, &term_idx, &reply); else sleep_time = 300000; g_get_current_time (&end); g_timeval_subtract (&diff, &end, &start); timeout_ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); if (send_success) { if (0 == term_idx && 0 == idx) { /* Success */ verbose ("GCAP response: %s", reply); ret = parse_gcap (reply); break; } else if (0 == term_idx && -1 == idx) { /* Just returned "OK" but no GCAP (Sierra) */ try_ati = TRUE; break; } else if (3 == term_idx && -1 == idx) { /* No SIM (Huawei) */ try_ati = TRUE; break; } else if (1 == term_idx || 2 == term_idx) { try_ati = TRUE; } else verbose ("timed out waiting for GCAP reply (idx %d, term_idx %d)", idx, term_idx); g_free (reply); reply = NULL; } g_usleep (sleep_time); timeout_ms -= sleep_time / 1000; } if (!ret && try_ati) { const char *ati_responses[] = { GCAP_TAG, NULL }; /* Many cards (ex Sierra 860 & 875) won't accept AT+GCAP but * accept ATI when the SIM is missing. Often the GCAP info is * in the ATI response too. */ g_free (reply); reply = NULL; verbose ("GCAP failed, trying ATI...%s", ""); if (modem_send_command (fd, "ATI\r\n")) { idx = modem_wait_reply (fd, 3, ati_responses, terminators, &term_idx, &reply); if (0 == term_idx && 0 == idx) { verbose ("ATI response: %s", reply); ret = parse_gcap (reply); } } } g_free (reply); reply = NULL; /* Try an alternate method on some hardware (ex BUSlink SCWi275u) */ if ((idx != -2) && !(ret & MODEM_CAP_GSM) && !(ret & MODEM_CAP_IS707_A)) { const char *gmm_responses[] = { GMM_TAG, NULL }; if (modem_send_command (fd, "AT+GMM\r\n")) { idx = modem_wait_reply (fd, 5, gmm_responses, terminators, &term_idx, &reply); if (0 == term_idx && 0 == idx) { verbose ("GMM response: %s", reply); ret |= parse_gmm (reply); } g_free (reply); } } return ret; }
static void real_handle_probe_response (MMPluginBase *self, MMPluginBaseSupportsTask *task, const char *cmd, const char *response, const GError *error) { MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); MMAtSerialPort *port = task_priv->probe_port; gboolean ignore_error = FALSE; /* Some modems (Huawei E160g) won't respond to +GCAP with no SIM, but * will respond to ATI. */ if (response && strstr (response, "+CME ERROR:")) ignore_error = TRUE; if (error && !ignore_error) { if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { /* Try GCAP again */ if (task_priv->probe_state < PROBE_STATE_GCAP_TRY3) { task_priv->probe_state++; mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, task); } else { /* Otherwise, if all the GCAP tries timed out, ignore the port * as it's probably not an AT-capable port. Try QCDM. */ try_qcdm_probe (task); } return; } /* Otherwise proceed to the next command */ } else if (response) { /* Parse the response */ switch (task_priv->probe_state) { case PROBE_STATE_GCAP_TRY1: case PROBE_STATE_GCAP_TRY2: case PROBE_STATE_GCAP_TRY3: case PROBE_STATE_ATI: /* Some modems don't respond to AT+GCAP, but often they put a * GCAP-style response as a line in the ATI response. */ task_priv->probed_caps = parse_gcap (response); break; case PROBE_STATE_CPIN: /* Some devices (ZTE MF628/ONDA MT503HS for example) reply to * anything but AT+CPIN? with ERROR if the device has a PIN set. * Since no known CDMA modems support AT+CPIN? we can consider the * device a GSM device if it returns a non-error response to AT+CPIN?. */ task_priv->probed_caps = parse_cpin (response); break; case PROBE_STATE_CGMM: /* Some models (BUSlink SCWi275u) stick stupid stuff in the CGMM * response but at least it allows us to identify them. */ task_priv->probed_caps = parse_cgmm (response); break; default: break; } if (task_priv->probed_caps & CAP_GSM_OR_CDMA) { probe_complete (task); return; } } task_priv->probe_state++; /* Try a different command */ switch (task_priv->probe_state) { case PROBE_STATE_GCAP_TRY2: case PROBE_STATE_GCAP_TRY3: mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, task); break; case PROBE_STATE_ATI: /* After the last GCAP attempt, try ATI */ mm_at_serial_port_queue_command (port, "I", 3, parse_response, task); break; case PROBE_STATE_CPIN: /* After the ATI attempt, try CPIN */ mm_at_serial_port_queue_command (port, "+CPIN?", 3, parse_response, task); break; case PROBE_STATE_CGMM: /* After the CPIN attempt, try CGMM */ mm_at_serial_port_queue_command (port, "+CGMM", 3, parse_response, task); break; default: /* Probably not GSM or CDMA */ probe_complete (task); break; } }