aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2011-05-05 10:32:48 +0200
committerAleksander Morgado <aleksander@lanedo.com>2011-06-06 17:20:17 +0200
commit5a2078a2a5f012772612dea0921b147bf6d75d88 (patch)
tree9dfbb4ecbf7a15c2ba2d656a5d2d2bc776f38b9c
parent9578e1b9cab988b11bf9a4cd1af3842cdfb1eb28 (diff)
plugin base: include vendor ID and product ID retrieval during AT port probing
Port probing is extended to also query for Vendor ID and Product ID. This allows plugins to check whether the reported IDs are expected, and thus we enable plugins to handle modems connected via RS232 ports (where udev doesn't give any vendor ID) or modems connected via a USB adapter (where udev gives the vendor ID of the adapter). Note that this effectively means that a plugin which expects these kind of modem connections will end up always launching port probing as they won't only rely on the vendor ID reported by udev.
-rw-r--r--src/mm-plugin-base.c371
-rw-r--r--src/mm-plugin-base.h12
2 files changed, 290 insertions, 93 deletions
diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c
index 8d033a72..09d72394 100644
--- a/src/mm-plugin-base.c
+++ b/src/mm-plugin-base.c
@@ -45,11 +45,18 @@ G_DEFINE_TYPE_EXTENDED (MMPluginBase, mm_plugin_base, G_TYPE_OBJECT,
#define MM_PLUGIN_BASE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_BASE, MMPluginBasePrivate))
+/* Struct to be stored in the cached hash table */
+typedef struct {
+ guint32 capabilities;
+ gchar *vendor;
+ gchar *product;
+} MMPluginBaseProbedInfo;
+
/* A hash table shared between all instances of the plugin base that
- * caches the probed capabilities so that only one plugin has to actually
- * probe a port.
+ * caches the probed capabilities and reported vendor/model (when available)
+ * so that only one plugin has to actually probe a port.
*/
-static GHashTable *cached_caps = NULL;
+static GHashTable *probed_info = NULL;
/* Virtual port corresponding to the embeded modem */
static gchar *virtual_port[] = {"smd0", NULL};
@@ -73,18 +80,65 @@ enum {
static guint signals[LAST_SIGNAL] = { 0 };
-
typedef enum {
- PROBE_STATE_GCAP_TRY1 = 0,
- PROBE_STATE_GCAP_TRY2,
- PROBE_STATE_GCAP_TRY3,
- PROBE_STATE_ATI,
- PROBE_STATE_CPIN,
- PROBE_STATE_CGMM,
+ /* Probing capabilities... */
+ PROBE_STATE_CAPS_GCAP_TRY1 = 0,
+ PROBE_STATE_CAPS_GCAP_TRY2,
+ PROBE_STATE_CAPS_GCAP_TRY3,
+ PROBE_STATE_CAPS_ATI,
+ PROBE_STATE_CAPS_CPIN,
+ PROBE_STATE_CAPS_CGMM,
+ /* Probing vendor... */
+ PROBE_STATE_VENDOR_CGMI,
+ PROBE_STATE_VENDOR_GMI,
+ PROBE_STATE_VENDOR_ATI,
+ /* Probing product... */
+ PROBE_STATE_PRODUCT_CGMM,
+ PROBE_STATE_PRODUCT_GMM,
+ PROBE_STATE_PRODUCT_ATI,
PROBE_STATE_LAST
} ProbeState;
+/* Additional helper IDs for the states, to be updated whenever new intermediate
+ * states are added in each probing group */
+#define PROBE_STATE_CAPS_FIRST PROBE_STATE_CAPS_GCAP_TRY1
+#define PROBE_STATE_CAPS_LAST PROBE_STATE_CAPS_CGMM
+#define PROBE_STATE_VENDOR_FIRST PROBE_STATE_VENDOR_CGMI
+#define PROBE_STATE_VENDOR_LAST PROBE_STATE_VENDOR_ATI
+#define PROBE_STATE_PRODUCT_FIRST PROBE_STATE_PRODUCT_CGMM
+#define PROBE_STATE_PRODUCT_LAST PROBE_STATE_PRODUCT_ATI
+
+typedef struct {
+ /* The command to send in this probing state */
+ gchar *cmd;
+ /* Whether the command reply should be cached */
+ gboolean cached;
+} ProbeStateCmd;
+
+/* List of commands sent in each state */
+static const ProbeStateCmd state_commands[PROBE_STATE_LAST] = {
+ /* Probing capabilities... */
+ { "+GCAP", FALSE }, /* PROBE_STATE_CAPS_GCAP_TRY1 */
+ { "+GCAP", FALSE }, /* PROBE_STATE_CAPS_GCAP_TRY2 */
+ { "+GCAP", FALSE }, /* PROBE_STATE_CAPS_GCAP_TRY3 */
+ { "I", TRUE }, /* PROBE_STATE_CAPS_ATI */
+ { "+CPIN?", FALSE }, /* PROBE_STATE_CAPS_CPIN */
+ { "+CGMM", TRUE }, /* PROBE_STATE_CAPS_CGMM */
+ /* Probing vendor... */
+ { "+CGMI", TRUE }, /* PROBE_STATE_VENDOR_CGMI */
+ { "+GMI", TRUE }, /* PROBE_STATE_VENDOR_GMI */
+ { "I", TRUE }, /* PROBE_STATE_VENDOR_ATI */
+ /* Probing product... */
+ { "+CGMM", TRUE }, /* PROBE_STATE_PRODUCT_CGMM */
+ { "+GMM", TRUE }, /* PROBE_STATE_PRODUCT_GMM */
+ { "I", TRUE } /* PROBE_STATE_PRODUCT_ATI */
+};
+
static void probe_complete (MMPluginBaseSupportsTask *task);
+static void parse_response (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
/*****************************************************************************/
@@ -113,6 +167,8 @@ typedef struct {
MMAtSerialPort *probe_port;
MMQcdmSerialPort *qcdm_port;
guint32 probed_caps;
+ gchar *probed_vendor;
+ gchar *probed_product;
ProbeState probe_state;
guint probe_id;
char *probe_resp;
@@ -202,6 +258,24 @@ mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSupportsTask *
return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->probed_caps;
}
+const gchar *
+mm_plugin_base_supports_task_get_probed_vendor (MMPluginBaseSupportsTask *task)
+{
+ g_return_val_if_fail (task != NULL, NULL);
+ g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL);
+
+ return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->probed_vendor;
+}
+
+const gchar *
+mm_plugin_base_supports_task_get_probed_product (MMPluginBaseSupportsTask *task)
+{
+ g_return_val_if_fail (task != NULL, NULL);
+ g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL);
+
+ return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->probed_product;
+}
+
void
mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task,
guint32 level)
@@ -299,6 +373,9 @@ supports_task_dispose (GObject *object)
g_object_unref (priv->qcdm_port);
}
+ g_free (priv->probed_vendor);
+ g_free (priv->probed_product);
+
G_OBJECT_CLASS (mm_plugin_base_supports_task_parent_class)->dispose (object);
}
@@ -346,7 +423,7 @@ static struct modem_caps modem_caps[] = {
};
static guint32
-parse_gcap (const char *buf)
+parse_caps_gcap (const char *buf)
{
struct modem_caps *cap = modem_caps;
guint32 ret = 0;
@@ -360,7 +437,7 @@ parse_gcap (const char *buf)
}
static guint32
-parse_cpin (const char *buf)
+parse_caps_cpin (const char *buf)
{
if ( strcasestr (buf, "SIM PIN")
|| strcasestr (buf, "SIM PUK")
@@ -384,7 +461,7 @@ parse_cpin (const char *buf)
}
static guint32
-parse_cgmm (const char *buf)
+parse_caps_cgmm (const char *buf)
{
if (strstr (buf, "GSM900") || strstr (buf, "GSM1800") ||
strstr (buf, "GSM1900") || strstr (buf, "GSM850"))
@@ -392,6 +469,18 @@ parse_cgmm (const char *buf)
return 0;
}
+static gchar *
+parse_vendor_cgmi (const gchar *buf)
+{
+ return g_strstrip (g_strdelimit (g_strdup (buf), "\r\n", ' '));
+}
+
+static gchar *
+parse_product_cgmm (const gchar *buf)
+{
+ return g_strstrip (g_strdelimit (g_strdup (buf), "\r\n", ' '));
+}
+
static const char *dq_strings[] = {
/* Option Icera-based devices */
"option/faema_",
@@ -402,6 +491,17 @@ static const char *dq_strings[] = {
};
static void
+probed_info_free (MMPluginBaseProbedInfo *info)
+{
+ if (!info)
+ return;
+
+ g_free (info->vendor);
+ g_free (info->product);
+ g_free (info);
+}
+
+static void
port_buffer_full (MMSerialPort *port, GByteArray *buffer, gpointer user_data)
{
MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data);
@@ -427,6 +527,8 @@ port_buffer_full (MMSerialPort *port, GByteArray *buffer, gpointer user_data)
if (!memcmp (&buffer->data[i], *iter, iter_len)) {
/* Immediately close the port and complete probing */
priv->probed_caps = 0;
+ priv->probed_vendor = NULL;
+ priv->probed_product = NULL;
mm_serial_port_close (MM_SERIAL_PORT (priv->probe_port));
probe_complete (task);
return;
@@ -461,13 +563,20 @@ static void
probe_complete (MMPluginBaseSupportsTask *task)
{
MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
+ MMPluginBaseProbedInfo *info;
MMPort *port;
port = priv->probe_port ? MM_PORT (priv->probe_port) : MM_PORT (priv->qcdm_port);
g_assert (port);
- g_hash_table_insert (cached_caps,
+
+ /* Allocate new cached info struct and store it in the hash table */
+ info = g_malloc0 (sizeof (*info));
+ info->capabilities = priv->probed_caps;
+ info->vendor = g_strdup (priv->probed_vendor);
+ info->product = g_strdup (priv->probed_product);
+ g_hash_table_insert (probed_info,
g_strdup (mm_port_get_device (port)),
- GUINT_TO_POINTER (priv->probed_caps));
+ info);
priv->probe_id = g_idle_add (emit_probe_result, task);
}
@@ -509,7 +618,6 @@ qcdm_verinfo_cb (MMQcdmSerialPort *port,
/* yay, probably a QCDM port */
qcdm_result_unref (result);
priv->probed_caps |= MM_PLUGIN_BASE_PORT_CAP_QCDM;
-
done:
probe_complete (task);
}
@@ -575,12 +683,6 @@ try_qcdm_probe (MMPluginBaseSupportsTask *task)
}
static void
-parse_response (MMAtSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data);
-
-static void
real_handle_probe_response (MMPluginBase *self,
MMPluginBaseSupportsTask *task,
const char *cmd,
@@ -598,83 +700,149 @@ real_handle_probe_response (MMPluginBase *self,
ignore_error = TRUE;
if (error && !ignore_error) {
+ /* Only allow timeout errors in the initial AT+GCAP queries. If all AT+GCAP
+ * get timed out, assume it's not an AT port. */
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) {
+ if (task_priv->probe_state < PROBE_STATE_CAPS_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);
+ mm_at_serial_port_queue_command (port,
+ state_commands[task_priv->probe_state].cmd,
+ 3,
+ parse_response,
+ task);
+ return;
}
- return;
+
+ /* If all the GCAP tries timed out, ignore the port as it's
+ * probably not an AT-capable port. Try QCDM.
+ */
+ if (task_priv->probe_state == PROBE_STATE_CAPS_GCAP_TRY3) {
+ try_qcdm_probe (task);
+ return;
+ }
+
+ /* Otherwise, it's a timeout in some other command being probed, so try the
+ * next one if any */
}
- /* Otherwise proceed to the next command */
+ /* Non-timeout error, so proceed to the next command if any */
} 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:
+ case PROBE_STATE_CAPS_GCAP_TRY1:
+ case PROBE_STATE_CAPS_GCAP_TRY2:
+ case PROBE_STATE_CAPS_GCAP_TRY3:
+ case PROBE_STATE_CAPS_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);
+ task_priv->probed_caps = parse_caps_gcap (response);
break;
- case PROBE_STATE_CPIN:
+ case PROBE_STATE_CAPS_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);
+ task_priv->probed_caps = parse_caps_cpin (response);
break;
- case PROBE_STATE_CGMM:
+ case PROBE_STATE_CAPS_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);
+ task_priv->probed_caps = parse_caps_cgmm (response);
+ break;
+ case PROBE_STATE_VENDOR_CGMI:
+ case PROBE_STATE_VENDOR_GMI:
+ case PROBE_STATE_VENDOR_ATI:
+ /* These replies parsed the same way, just removing EOLs */
+ task_priv->probed_vendor = parse_vendor_cgmi (response);
+ break;
+ case PROBE_STATE_PRODUCT_CGMM:
+ case PROBE_STATE_PRODUCT_GMM:
+ case PROBE_STATE_PRODUCT_ATI:
+ /* These replies parsed the same way, just removing EOLs */
+ task_priv->probed_product = parse_product_cgmm (response);
break;
default:
break;
}
+ }
+
+ /* Now, choose the proper next state */
+ if (task_priv->probe_state <= PROBE_STATE_CAPS_LAST) {
+ /* Probing capabilities */
if (task_priv->probed_caps & CAP_GSM_OR_CDMA) {
+ /* Got capabilities probed, go on with vendor probing */
+ task_priv->probe_state = PROBE_STATE_VENDOR_FIRST;
+ } else if (task_priv->probe_state < PROBE_STATE_CAPS_LAST) {
+ /* Didn't get capabilities probed yet, go on to next caps
+ * probing command */
+ task_priv->probe_state++;
+ } else {
+ /* Tried probing all commands for capabilities and didn't get
+ * any: just end probing here, it probably isn't a GSM or CDMA
+ * modem */
+ mm_warn ("Couldn't probe for capabilities, probably not a GSM or CDMA modem");
+ probe_complete (task);
+ return;
+ }
+ } else if (task_priv->probe_state <= PROBE_STATE_VENDOR_LAST) {
+ /* Probing vendors */
+ if (task_priv->probed_vendor) {
+ /* Got vendor probed, go on with product probing. */
+ task_priv->probe_state = PROBE_STATE_PRODUCT_FIRST;
+ } else if (task_priv->probe_state < PROBE_STATE_VENDOR_LAST) {
+ /* Didn't get vendor probed yet, go on to next vendor
+ * probing command */
+ task_priv->probe_state++;
+ } else {
+ /* Tried probing all commands for vendor and didn't get
+ * any: just end probing here, product ID is useless without
+ * vendor ID */
+ probe_complete (task);
+ return;
+ }
+ } else if (task_priv->probe_state <= PROBE_STATE_PRODUCT_LAST) {
+ /* Probing products */
+ if (!task_priv->probed_product &&
+ task_priv->probe_state < PROBE_STATE_PRODUCT_LAST) {
+ /* Didn't get probed product yet but more commands available,
+ * go on with the next one */
+ task_priv->probe_state++;
+ } else {
+ /* Got product probed or no more commands to probe, so end. */
probe_complete (task);
return;
}
+ } else {
+ g_warn_if_reached ();
}
- 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 */
+ /* Out of last state? */
+ if (task_priv->probe_state >= PROBE_STATE_LAST) {
+ /* Probing ended */
probe_complete (task);
- break;
+ return;
}
+
+ /* Go on with the command in next state */
+
+ if (state_commands[task_priv->probe_state].cached)
+ mm_at_serial_port_queue_command_cached (port,
+ state_commands[task_priv->probe_state].cmd,
+ 3,
+ parse_response,
+ task);
+ else
+ mm_at_serial_port_queue_command (port,
+ state_commands[task_priv->probe_state].cmd,
+ 3,
+ parse_response,
+ task);
}
static gboolean
@@ -683,29 +851,10 @@ handle_probe_response (gpointer user_data)
MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data);
MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
MMPluginBase *self = MM_PLUGIN_BASE (mm_plugin_base_supports_task_get_plugin (task));
- const char *cmd = NULL;
-
- switch (task_priv->probe_state) {
- case PROBE_STATE_GCAP_TRY1:
- case PROBE_STATE_GCAP_TRY2:
- case PROBE_STATE_GCAP_TRY3:
- cmd = "+GCAP";
- break;
- case PROBE_STATE_ATI:
- cmd = "I";
- break;
- case PROBE_STATE_CPIN:
- cmd = "+CPIN?";
- break;
- case PROBE_STATE_CGMM:
- default:
- cmd = "+CGMM";
- break;
- }
MM_PLUGIN_BASE_GET_CLASS (self)->handle_probe_response (self,
task,
- cmd,
+ state_commands[task_priv->probe_state].cmd,
task_priv->probe_resp,
task_priv->probe_error);
return FALSE;
@@ -902,12 +1051,44 @@ mm_plugin_base_get_cached_port_capabilities (MMPluginBase *self,
GUdevDevice *port,
guint32 *capabilities)
{
- gpointer tmp = NULL;
- gboolean found;
+ MMPluginBaseProbedInfo *info;
+
+ if (g_hash_table_lookup_extended (probed_info,
+ g_udev_device_get_name (port),
+ NULL,
+ (gpointer *)&info)) {
+ *capabilities = info->capabilities;
+ return TRUE;
+ }
+
+ *capabilities = 0;
+ return FALSE;
+}
+
+gboolean
+mm_plugin_base_get_cached_product_info (MMPluginBase *self,
+ GUdevDevice *port,
+ gchar **vendor,
+ gchar **product)
+{
+ MMPluginBaseProbedInfo *info;
+
+ if (g_hash_table_lookup_extended (probed_info,
+ g_udev_device_get_name (port),
+ NULL,
+ (gpointer *)&info)) {
+ if (vendor)
+ *vendor = (info->vendor ? g_strdup (info->vendor) : NULL);
+ if (product)
+ *product = (info->product ? g_strdup (info->product) : NULL);
+ return TRUE;
+ }
- found = g_hash_table_lookup_extended (cached_caps, g_udev_device_get_name (port), NULL, tmp);
- *capabilities = GPOINTER_TO_UINT (tmp);
- return found;
+ if (vendor)
+ *vendor = NULL;
+ if (product)
+ *product = NULL;
+ return FALSE;
}
/*****************************************************************************/
@@ -922,7 +1103,7 @@ modem_destroyed (gpointer data, GObject *modem)
* or something and then only removing cached capabilities for ports
* that the modem that was just removed owned, but whatever.
*/
- g_hash_table_remove_all (cached_caps);
+ g_hash_table_remove_all (probed_info);
}
gboolean
@@ -1217,12 +1398,18 @@ mm_plugin_base_init (MMPluginBase *self)
MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
const char *subsys[] = { "tty", "net", NULL };
- if (!cached_caps)
- cached_caps = g_hash_table_new (g_str_hash, g_str_equal);
+ if (!probed_info) {
+ probed_info = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) probed_info_free);
+ }
priv->client = g_udev_client_new (subsys);
- priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ priv->tasks = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
(GDestroyNotify) g_object_unref);
}
diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h
index 799f6813..91b3b8df 100644
--- a/src/mm-plugin-base.h
+++ b/src/mm-plugin-base.h
@@ -56,7 +56,7 @@ 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
@@ -86,6 +86,10 @@ const char *mm_plugin_base_supports_task_get_driver (MMPluginBaseSupportsTask *t
guint32 mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSupportsTask *task);
+const gchar *mm_plugin_base_supports_task_get_probed_vendor (MMPluginBaseSupportsTask *task);
+
+const gchar *mm_plugin_base_supports_task_get_probed_product (MMPluginBaseSupportsTask *task);
+
void mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task,
guint32 level);
@@ -159,5 +163,11 @@ gboolean mm_plugin_base_get_cached_port_capabilities (MMPluginBase *self,
GUdevDevice *port,
guint32 *capabilities);
+/* Returns TRUE if the port was previously probed, FALSE if not */
+gboolean mm_plugin_base_get_cached_product_info (MMPluginBase *self,
+ GUdevDevice *port,
+ gchar **vendor,
+ gchar **product);
+
#endif /* MM_PLUGIN_BASE_H */