aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2012-02-01 01:43:54 -0600
committerDan Williams <dcbw@redhat.com>2012-02-28 10:06:04 -0600
commit4dad94d5004f325e25dc3b09d87585eab38d4c3f (patch)
tree38a5bb148952c333e18bfbd959e27573ab3364d9 /src
parent36ee1b9c76a681b44516852372944b82c7616892 (diff)
core: rework port grabbing and organization
Make port roles more flexible. We have modems that do PPP on interfaces other than the primary interface, and that wasn't possible with the old code. So clean up all that logic and move the port organization code into the core so we can reduce code in the plugins. In the new world order, the plugins say whether the port is a QCDM port, an AT port, or ignored. If it's an AT port the plugins get to tag it as primary, secondary, or PPP, or any combination of the 3. This allows for modems where PPP should really be done on the secondary port (Huawei E220, Sierra devices) so that the primary port stays open for command and status. Modem subclasses no longer get asked to handle port grabbing themselves. Instead, that's now done by the generic classes (MMGenericCdma and MMGenericGsm) and the plugins are notified when a port is grabbed so they can add unsolicited response handlers for it. After all ports are grabbed by the generic classes, they get "organized", which assigns various ports to the roles of PRIMARY, SECONDARY, DATA, and QCDM based on specific rules and hints that the plugin provided (which are expressed as MMAtPortFlags). The plugins then have a chance to perform fixups on the primary port if they choose. The plugin code is responsible for determining the port hints (ie MMAtPortFlags) at probe time, instead of having a combination of the plugin and the modem class do the job. This simplifies things greatly for the plugins at the expense of more complicated logic in the core.
Diffstat (limited to 'src')
-rw-r--r--src/mm-at-serial-port.c24
-rw-r--r--src/mm-at-serial-port.h25
-rw-r--r--src/mm-generic-cdma.c186
-rw-r--r--src/mm-generic-cdma.h27
-rw-r--r--src/mm-generic-gsm.c217
-rw-r--r--src/mm-generic-gsm.h26
-rw-r--r--src/mm-manager.c143
-rw-r--r--src/mm-modem-base.c289
-rw-r--r--src/mm-modem-base.h21
-rw-r--r--src/mm-modem.c15
-rw-r--r--src/mm-modem.h19
-rw-r--r--src/mm-plugin-base.c14
-rw-r--r--src/mm-plugin-base.h2
-rw-r--r--src/mm-port.c10
-rw-r--r--src/mm-port.h3
-rw-r--r--src/mm-qcdm-serial-port.c8
-rw-r--r--src/mm-qcdm-serial-port.h4
-rw-r--r--src/tests/test-qcdm-serial-port.c2
18 files changed, 686 insertions, 349 deletions
diff --git a/src/mm-at-serial-port.c b/src/mm-at-serial-port.c
index cd4bb133..378c79f0 100644
--- a/src/mm-at-serial-port.c
+++ b/src/mm-at-serial-port.c
@@ -35,6 +35,7 @@ typedef struct {
gpointer response_parser_user_data;
GDestroyNotify response_parser_notify;
GSList *unsolicited_msg_handlers;
+ MMAtPortFlags flags;
} MMAtSerialPortPrivate;
@@ -325,15 +326,34 @@ debug_log (MMSerialPort *port, const char *prefix, const char *buf, gsize len)
g_string_truncate (debug, 0);
}
+void
+mm_at_serial_port_set_flags (MMAtSerialPort *self, MMAtPortFlags flags)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+ g_return_if_fail (flags <= (MM_AT_PORT_FLAG_PRIMARY | MM_AT_PORT_FLAG_SECONDARY | MM_AT_PORT_FLAG_PPP));
+
+ MM_AT_SERIAL_PORT_GET_PRIVATE (self)->flags = flags;
+}
+
+MMAtPortFlags
+mm_at_serial_port_get_flags (MMAtSerialPort *self)
+{
+ g_return_val_if_fail (self != NULL, MM_AT_PORT_FLAG_NONE);
+ g_return_val_if_fail (MM_IS_AT_SERIAL_PORT (self), MM_AT_PORT_FLAG_NONE);
+
+ return MM_AT_SERIAL_PORT_GET_PRIVATE (self)->flags;
+}
+
/*****************************************************************************/
MMAtSerialPort *
-mm_at_serial_port_new (const char *name, MMPortType ptype)
+mm_at_serial_port_new (const char *name)
{
return MM_AT_SERIAL_PORT (g_object_new (MM_TYPE_AT_SERIAL_PORT,
MM_PORT_DEVICE, name,
MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY,
- MM_PORT_TYPE, ptype,
+ MM_PORT_TYPE, MM_PORT_TYPE_AT,
NULL));
}
diff --git a/src/mm-at-serial-port.h b/src/mm-at-serial-port.h
index 689c184f..5a631f95 100644
--- a/src/mm-at-serial-port.h
+++ b/src/mm-at-serial-port.h
@@ -32,6 +32,24 @@
typedef struct _MMAtSerialPort MMAtSerialPort;
typedef struct _MMAtSerialPortClass MMAtSerialPortClass;
+/* AT port flags; for example consider a device with two AT ports (ACM0 and ACM1)
+ * which could have the following layouts:
+ *
+ * ACM0(PRIMARY | PPP), ACM1(SECONDARY): port 0 is used for command and status
+ * and for PPP data; while connected port 1 is used for command and status
+ * ACM0(PPP), ACM1(PRIMARY): port 1 is always used for command and status, and
+ * only when connecting is port 0 opened for dialing (ATD) and PPP
+ */
+typedef enum {
+ MM_AT_PORT_FLAG_NONE = 0x0000,
+ /* This port is preferred for command and status */
+ MM_AT_PORT_FLAG_PRIMARY = 0x0001,
+ /* Use port for command and status if the primary port is connected */
+ MM_AT_PORT_FLAG_SECONDARY = 0x0002,
+ /* This port should be used for PPP */
+ MM_AT_PORT_FLAG_PPP = 0x0004
+} MMAtPortFlags;
+
typedef gboolean (*MMAtSerialResponseParserFn) (gpointer user_data,
GString *response,
GError **error);
@@ -55,7 +73,7 @@ struct _MMAtSerialPortClass {
GType mm_at_serial_port_get_type (void);
-MMAtSerialPort *mm_at_serial_port_new (const char *name, MMPortType ptype);
+MMAtSerialPort *mm_at_serial_port_new (const char *name);
void mm_at_serial_port_add_unsolicited_msg_handler (MMAtSerialPort *self,
GRegex *regex,
@@ -83,4 +101,9 @@ void mm_at_serial_port_queue_command_cached (MMAtSerialPort *self,
/* Just for unit tests */
void mm_at_serial_port_remove_echo (GByteArray *response);
+void mm_at_serial_port_set_flags (MMAtSerialPort *self,
+ MMAtPortFlags flags);
+
+MMAtPortFlags mm_at_serial_port_get_flags (MMAtSerialPort *self);
+
#endif /* MM_AT_SERIAL_PORT_H */
diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c
index 523ddee0..5397ce95 100644
--- a/src/mm-generic-cdma.c
+++ b/src/mm-generic-cdma.c
@@ -97,6 +97,7 @@ typedef struct {
MMAtSerialPort *secondary;
MMQcdmSerialPort *qcdm;
MMPort *data;
+ gboolean data_opened_at_connect;
} MMGenericCdmaPrivate;
enum {
@@ -230,85 +231,56 @@ owns_port (MMModem *modem, const char *subsys, const char *name)
return !!mm_modem_base_get_port (MM_MODEM_BASE (modem), subsys, name);
}
-MMPort *
-mm_generic_cdma_grab_port (MMGenericCdma *self,
- const char *subsys,
- const char *name,
- MMPortType suggested_type,
- gpointer user_data,
- GError **error)
+static void
+port_grabbed (MMModemBase *base,
+ MMPort *port,
+ MMAtPortFlags at_pflags,
+ gpointer user_data)
{
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
- MMPortType ptype = MM_PORT_TYPE_IGNORED;
- MMPort *port;
-
- g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), FALSE);
- if (priv->primary)
- g_return_val_if_fail (suggested_type != MM_PORT_TYPE_PRIMARY, FALSE);
-
- if (!strcmp (subsys, "tty")) {
- if (suggested_type != MM_PORT_TYPE_UNKNOWN)
- ptype = suggested_type;
- else {
- if (!priv->primary)
- ptype = MM_PORT_TYPE_PRIMARY;
- else if (!priv->secondary)
- ptype = MM_PORT_TYPE_SECONDARY;
- }
- }
-
- port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype);
- if (!port) {
- g_warn_if_fail (port != NULL);
- return NULL;
- }
+ MMGenericCdma *self = MM_GENERIC_CDMA (base);
if (MM_IS_AT_SERIAL_PORT (port)) {
g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL);
+ mm_at_serial_port_set_flags (MM_AT_SERIAL_PORT (port), at_pflags);
+
mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
mm_serial_parser_v1_e1_parse,
mm_serial_parser_v1_e1_new (),
mm_serial_parser_v1_e1_destroy);
-
- if (ptype == MM_PORT_TYPE_PRIMARY) {
- priv->primary = MM_AT_SERIAL_PORT (port);
- if (!priv->data) {
- priv->data = port;
- g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
- }
-
- /* Get the modem's general info */
- initial_info_check (self);
-
- /* Get modem's ESN number */
- initial_esn_check (self);
-
- } else if (ptype == MM_PORT_TYPE_SECONDARY)
- priv->secondary = MM_AT_SERIAL_PORT (port);
- } else if (MM_IS_QCDM_SERIAL_PORT (port)) {
- if (!priv->qcdm)
- priv->qcdm = MM_QCDM_SERIAL_PORT (port);
- } else if (!strcmp (subsys, "net")) {
- /* Net device (if any) is the preferred data port */
- if (!priv->data || MM_IS_AT_SERIAL_PORT (priv->data)) {
- priv->data = port;
- g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
- check_valid (self);
- }
}
- return port;
+ if (MM_GENERIC_CDMA_GET_CLASS (self)->port_grabbed)
+ MM_GENERIC_CDMA_GET_CLASS (self)->port_grabbed (self, port, at_pflags, user_data);
}
static gboolean
-grab_port (MMModem *modem,
- const char *subsys,
- const char *name,
- MMPortType suggested_type,
- gpointer user_data,
- GError **error)
+organize_ports (MMModem *modem, GError **error)
{
- return !!mm_generic_cdma_grab_port (MM_GENERIC_CDMA (modem), subsys, name, suggested_type, user_data, error);
+ MMGenericCdma *self = MM_GENERIC_CDMA (modem);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+
+ if (!mm_modem_base_organize_ports (MM_MODEM_BASE (modem),
+ &priv->primary,
+ &priv->secondary,
+ &priv->data,
+ &priv->qcdm,
+ error))
+ return FALSE;
+
+ /* Let subclasses twiddle ports if they want */
+ if (MM_GENERIC_CDMA_GET_CLASS (self)->ports_organized)
+ MM_GENERIC_CDMA_GET_CLASS (self)->ports_organized (self, priv->primary);
+
+ g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
+
+ /* Get the modem's general info */
+ initial_info_check (self);
+
+ /* Get modem's ESN number */
+ initial_esn_check (self);
+
+ check_valid (self);
+ return TRUE;
}
static void
@@ -346,15 +318,26 @@ release_port (MMModem *modem, const char *subsys, const char *name)
MMAtSerialPort *
mm_generic_cdma_get_at_port (MMGenericCdma *modem,
- MMPortType ptype)
+ MMAtPortFlags flag)
{
+ MMGenericCdmaPrivate *priv;
+
g_return_val_if_fail (MM_IS_GENERIC_CDMA (modem), NULL);
- g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL);
- if (ptype == MM_PORT_TYPE_PRIMARY)
- return MM_GENERIC_CDMA_GET_PRIVATE (modem)->primary;
- else if (ptype == MM_PORT_TYPE_SECONDARY)
- return MM_GENERIC_CDMA_GET_PRIVATE (modem)->secondary;
+ /* We only search for a single value even though it's a bitfield */
+ g_return_val_if_fail ( flag == MM_AT_PORT_FLAG_NONE
+ || flag == MM_AT_PORT_FLAG_PRIMARY
+ || flag == MM_AT_PORT_FLAG_SECONDARY
+ || flag == MM_AT_PORT_FLAG_PPP, NULL);
+
+ priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
+
+ if (flag == MM_AT_PORT_FLAG_SECONDARY)
+ return priv->secondary;
+ else if (flag == MM_AT_PORT_FLAG_PRIMARY)
+ return priv->primary;
+ else if ((flag == MM_AT_PORT_FLAG_PPP) && MM_IS_AT_SERIAL_PORT (priv->data))
+ return MM_AT_SERIAL_PORT (priv->data);
return NULL;
}
@@ -872,12 +855,31 @@ connect (MMModem *modem,
MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
+ MMAtSerialPort *dial_port;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ /* Dial port might not be the primary port*/
+ priv->data_opened_at_connect = FALSE;
+ dial_port = priv->primary;
+ if (MM_IS_AT_SERIAL_PORT (priv->data)) {
+ dial_port = MM_AT_SERIAL_PORT (priv->data);
+
+ if (!mm_serial_port_open (MM_SERIAL_PORT (dial_port), &info->error)) {
+ g_warning ("%s: failed to open dial port: (%d) %s",
+ __func__,
+ info->error ? info->error->code : -1,
+ info->error && info->error->message ? info->error->message : "(unknown)");
+ mm_callback_info_schedule (info);
+ return;
+ }
+ priv->data_opened_at_connect = TRUE;
+ }
mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE);
- info = mm_callback_info_new (modem, callback, user_data);
command = g_strconcat ("DT", number, NULL);
- mm_at_serial_port_queue_command (priv->primary, command, 90, dial_done, info);
+ mm_at_serial_port_queue_command (dial_port, command, 90, dial_done, info);
g_free (command);
}
@@ -887,6 +889,8 @@ disconnect_flash_done (MMSerialPort *port,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericCdma *self;
+ MMGenericCdmaPrivate *priv;
MMModemState prev_state;
/* If the modem has already been removed, return without
@@ -894,6 +898,8 @@ disconnect_flash_done (MMSerialPort *port,
if (mm_callback_info_check_modem_removed (info))
return;
+ self = MM_GENERIC_CDMA (info->modem);
+ priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
if (error) {
info->error = g_error_copy (error);
@@ -903,8 +909,18 @@ disconnect_flash_done (MMSerialPort *port,
prev_state,
MM_MODEM_STATE_REASON_NONE);
} else {
- mm_port_set_connected (MM_GENERIC_CDMA_GET_PRIVATE (info->modem)->data, FALSE);
- update_enabled_state (MM_GENERIC_CDMA (info->modem), FALSE, MM_MODEM_STATE_REASON_NONE);
+ mm_port_set_connected (priv->data, FALSE);
+ update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE);
+ }
+
+ /* Balance any open from connect(); subclasses may not use the generic
+ * class' connect function and so the dial port may not have been
+ * opened at all.
+ */
+ if (priv->data_opened_at_connect) {
+ if (MM_IS_AT_SERIAL_PORT (port))
+ mm_serial_port_close (port);
+ priv->data_opened_at_connect = FALSE;
}
mm_callback_info_schedule (info);
@@ -918,6 +934,7 @@ disconnect (MMModem *modem,
MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
MMModemState state;
+ MMAtSerialPort *dial_port;
g_return_if_fail (priv->primary != NULL);
@@ -931,7 +948,12 @@ disconnect (MMModem *modem,
NULL);
mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE);
- mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
+
+ dial_port = priv->primary;
+ if (MM_IS_AT_SERIAL_PORT (priv->data))
+ dial_port = MM_AT_SERIAL_PORT (priv->data);
+
+ mm_serial_port_flash (MM_SERIAL_PORT (dial_port), 1000, TRUE, disconnect_flash_done, info);
}
static void
@@ -2439,7 +2461,7 @@ static void
modem_init (MMModem *modem_class)
{
modem_class->owns_port = owns_port;
- modem_class->grab_port = grab_port;
+ modem_class->organize_ports = organize_ports;
modem_class->release_port = release_port;
modem_class->enable = enable;
modem_class->disable = disable;
@@ -2551,18 +2573,20 @@ dispose (GObject *object)
}
static void
-mm_generic_cdma_class_init (MMGenericCdmaClass *klass)
+mm_generic_cdma_class_init (MMGenericCdmaClass *generic_class)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (generic_class);
+ MMModemBaseClass *base_class = MM_MODEM_BASE_CLASS (generic_class);
- mm_generic_cdma_parent_class = g_type_class_peek_parent (klass);
+ mm_generic_cdma_parent_class = g_type_class_peek_parent (generic_class);
g_type_class_add_private (object_class, sizeof (MMGenericCdmaPrivate));
/* Virtual methods */
object_class->set_property = set_property;
object_class->get_property = get_property;
object_class->dispose = dispose;
- klass->query_registration_state = real_query_registration_state;
+ base_class->port_grabbed = port_grabbed;
+ generic_class->query_registration_state = real_query_registration_state;
/* Properties */
g_object_class_override_property (object_class,
diff --git a/src/mm-generic-cdma.h b/src/mm-generic-cdma.h
index 350c58ed..ba7b76f8 100644
--- a/src/mm-generic-cdma.h
+++ b/src/mm-generic-cdma.h
@@ -43,6 +43,23 @@ typedef struct {
typedef struct {
MMModemBaseClass parent;
+ /* Called to allow subclasses to update port flags, attach unsolicited
+ * result code handlers, change port attributes, etc. This is called
+ * after the generic class has installed it's own handlers; if the
+ * generic class' behavior is not desired, subclasses can override the
+ * port_grabbed() method of MMModemBase.
+ */
+ void (*port_grabbed) (MMGenericCdma *self,
+ MMPort *port,
+ MMAtPortFlags at_pflags,
+ gpointer user_data);
+
+ /* Called after all ports have been organized to allow subclasses to
+ * make changes to ports after we've assigned primary, secondary, and data
+ * designations.
+ */
+ void (*ports_organized) (MMGenericCdma *self, MMAtSerialPort *primary);
+
/* Subclasses should implement this function if they can more accurately
* determine the registration state and/or roaming status than the base
* class can (by using manufacturer custom AT commands or whatever).
@@ -94,14 +111,8 @@ MMModem *mm_generic_cdma_new (const char *device,
/* Private, for subclasses */
-MMPort * mm_generic_cdma_grab_port (MMGenericCdma *self,
- const char *subsys,
- const char *name,
- MMPortType suggested_type,
- gpointer user_data,
- GError **error);
-
-MMAtSerialPort *mm_generic_cdma_get_at_port (MMGenericCdma *modem, MMPortType ptype);
+/* Returns the first port (if any) which has the given flag */
+MMAtSerialPort *mm_generic_cdma_get_at_port (MMGenericCdma *modem, MMAtPortFlags flag);
MMAtSerialPort *mm_generic_cdma_get_best_at_port (MMGenericCdma *modem,
GError **error);
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 568cc8fd..0a0a2a1a 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2009 - 2010 Ericsson
*/
@@ -116,6 +116,7 @@ typedef struct {
MMAtSerialPort *secondary;
MMQcdmSerialPort *qcdm;
MMPort *data;
+ gboolean data_opened_at_connect;
/* Location API */
guint32 loc_caps;
@@ -843,39 +844,28 @@ owns_port (MMModem *modem, const char *subsys, const char *name)
return !!mm_modem_base_get_port (MM_MODEM_BASE (modem), subsys, name);
}
-MMPort *
-mm_generic_gsm_grab_port (MMGenericGsm *self,
- const char *subsys,
- const char *name,
- MMPortType ptype,
- GError **error)
+static void
+port_grabbed (MMModemBase *base,
+ MMPort *port,
+ MMAtPortFlags at_pflags,
+ gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
- MMPort *port = NULL;
+ MMGenericGsm *self = MM_GENERIC_GSM (base);
+ GPtrArray *array;
GRegex *regex;
-
- g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), FALSE);
-
- port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype);
- if (!port) {
- g_warn_if_fail (port != NULL);
- return NULL;
- }
+ int i;
if (MM_IS_AT_SERIAL_PORT (port)) {
- GPtrArray *array;
- int i;
-
mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
mm_serial_parser_v1_parse,
mm_serial_parser_v1_new (),
mm_serial_parser_v1_destroy);
+ mm_at_serial_port_set_flags (MM_AT_SERIAL_PORT (port), at_pflags);
/* Set up CREG unsolicited message handlers */
array = mm_gsm_creg_regex_get (FALSE);
for (i = 0; i < array->len; i++) {
regex = g_ptr_array_index (array, i);
-
mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, reg_state_changed, self, NULL);
}
mm_gsm_creg_regex_destroy (array);
@@ -891,72 +881,48 @@ mm_generic_gsm_grab_port (MMGenericGsm *self,
regex = g_regex_new ("\\r\\n\\+CUSD:\\s*(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, cusd_received, self, NULL);
g_regex_unref (regex);
-
- if (ptype == MM_PORT_TYPE_PRIMARY) {
- priv->primary = MM_AT_SERIAL_PORT (port);
- if (!priv->data) {
- priv->data = port;
- g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
- }
-
- /* Get the modem's general info */
- initial_info_check (self);
-
- /* Get modem's IMEI */
- initial_imei_check (self);
-
- /* Get modem's initial lock/unlock state; this also ensures the
- * SIM is ready by waiting if necessary for the SIM to initalize.
- */
- initial_pin_check (self);
-
- /* Determine what facility locks are supported */
- initial_facility_lock_check (self);
-
- } else if (ptype == MM_PORT_TYPE_SECONDARY)
- priv->secondary = MM_AT_SERIAL_PORT (port);
- } else if (MM_IS_QCDM_SERIAL_PORT (port)) {
- if (!priv->qcdm)
- priv->qcdm = MM_QCDM_SERIAL_PORT (port);
- } else if (!strcmp (subsys, "net")) {
- /* Net device (if any) is the preferred data port */
- if (!priv->data || MM_IS_AT_SERIAL_PORT (priv->data)) {
- priv->data = port;
- g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
- check_valid (self);
- }
}
- return port;
+ if (MM_GENERIC_GSM_GET_CLASS (self)->port_grabbed)
+ MM_GENERIC_GSM_GET_CLASS (self)->port_grabbed (self, port, at_pflags, user_data);
}
static gboolean
-grab_port (MMModem *modem,
- const char *subsys,
- const char *name,
- MMPortType suggested_type,
- gpointer user_data,
- GError **error)
+organize_ports (MMModem *modem, GError **error)
{
MMGenericGsm *self = MM_GENERIC_GSM (modem);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
- MMPortType ptype = MM_PORT_TYPE_IGNORED;
- if (priv->primary)
- g_return_val_if_fail (suggested_type != MM_PORT_TYPE_PRIMARY, FALSE);
+ if (!mm_modem_base_organize_ports (MM_MODEM_BASE (modem),
+ &priv->primary,
+ &priv->secondary,
+ &priv->data,
+ &priv->qcdm,
+ error))
+ return FALSE;
+
+ /* Let subclasses twiddle ports if they want */
+ if (MM_GENERIC_GSM_GET_CLASS (self)->ports_organized)
+ MM_GENERIC_GSM_GET_CLASS (self)->ports_organized (self, priv->primary);
- if (!strcmp (subsys, "tty")) {
- if (suggested_type != MM_PORT_TYPE_UNKNOWN)
- ptype = suggested_type;
- else {
- if (!priv->primary)
- ptype = MM_PORT_TYPE_PRIMARY;
- else if (!priv->secondary)
- ptype = MM_PORT_TYPE_SECONDARY;
- }
- }
+ g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
+
+ /* Get the modem's general info */
+ initial_info_check (self);
+
+ /* Get modem's IMEI */
+ initial_imei_check (self);
- return !!mm_generic_gsm_grab_port (self, subsys, name, ptype, error);
+ /* Get modem's initial lock/unlock state; this also ensures the
+ * SIM is ready by waiting if necessary for the SIM to initalize.
+ */
+ initial_pin_check (self);
+
+ /* Determine what facility locks are supported */
+ initial_facility_lock_check (self);
+
+ check_valid (self);
+ return TRUE;
}
static void
@@ -3824,9 +3790,27 @@ connect (MMModem *modem,
MMCallbackInfo *info;
char *command;
gint cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem));
+ MMAtSerialPort *dial_port;
info = mm_callback_info_new (modem, callback, user_data);
+ /* Dial port might not be the primary port*/
+ priv->data_opened_at_connect = FALSE;
+ dial_port = priv->primary;
+ if (MM_IS_AT_SERIAL_PORT (priv->data)) {
+ dial_port = MM_AT_SERIAL_PORT (priv->data);
+
+ if (!mm_serial_port_open (MM_SERIAL_PORT (dial_port), &info->error)) {
+ g_warning ("%s: failed to open dial port: (%d) %s",
+ __func__,
+ info->error ? info->error->code : -1,
+ info->error && info->error->message ? info->error->message : "(unknown)");
+ mm_callback_info_schedule (info);
+ return;
+ }
+ priv->data_opened_at_connect = TRUE;
+ }
+
mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE);
if (cid > 0) {
@@ -3843,7 +3827,7 @@ connect (MMModem *modem,
} else
command = g_strconcat ("DT", number, NULL);
- mm_at_serial_port_queue_command (priv->primary, command, 60, connect_done, info);
+ mm_at_serial_port_queue_command (dial_port, command, 60, connect_done, info);
g_free (command);
}
@@ -3854,11 +3838,13 @@ disconnect_done (MMModem *modem,
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemState prev_state;
+ MMGenericGsmPrivate *priv;
/* Do nothing if modem removed */
if (!modem || mm_callback_info_check_modem_removed (info))
return;
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
if (error) {
info->error = g_error_copy (error);
/* Reset old state since the operation failed */
@@ -3867,12 +3853,19 @@ disconnect_done (MMModem *modem,
prev_state,
MM_MODEM_STATE_REASON_NONE);
} else {
- MMGenericGsm *self = MM_GENERIC_GSM (modem);
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
-
mm_port_set_connected (priv->data, FALSE);
priv->cid = -1;
- mm_generic_gsm_update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE);
+ mm_generic_gsm_update_enabled_state (MM_GENERIC_GSM (modem), FALSE, MM_MODEM_STATE_REASON_NONE);
+ }
+
+ /* Balance any open from connect(); subclasses may not use the generic
+ * class' connect function and so the dial port may not have been
+ * opened at all.
+ */
+ if (priv->data_opened_at_connect) {
+ if (MM_IS_AT_SERIAL_PORT (priv->data))
+ mm_serial_port_close (MM_SERIAL_PORT (priv->data));
+ priv->data_opened_at_connect = FALSE;
}
mm_callback_info_schedule (info);
@@ -3965,6 +3958,7 @@ disconnect_secondary_cgact_done (MMAtSerialPort *port,
MMCallbackInfo *info = user_data;
MMGenericGsm *self;
MMGenericGsmPrivate *priv;
+ MMSerialPort *dial_port;
/* If the modem has already been removed, return without
* scheduling callback */
@@ -3980,7 +3974,11 @@ disconnect_secondary_cgact_done (MMAtSerialPort *port,
if (!error)
mm_callback_info_set_data (info, DISCONNECT_CGACT_DONE_TAG, GUINT_TO_POINTER (TRUE), NULL);
- mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
+ dial_port = MM_SERIAL_PORT (priv->primary);
+ if (MM_IS_AT_SERIAL_PORT (priv->data))
+ dial_port = MM_SERIAL_PORT (priv->data);
+
+ mm_serial_port_flash (dial_port, 1000, TRUE, disconnect_flash_done, info);
}
static void
@@ -3991,6 +3989,7 @@ real_do_disconnect (MMGenericGsm *self,
{
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
MMCallbackInfo *info;
+ MMSerialPort *dial_port;
info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
@@ -4007,8 +4006,12 @@ real_do_disconnect (MMGenericGsm *self,
disconnect_secondary_cgact_done,
info);
} else {
- /* Just flash the primary port */
- mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
+ /* Just flash the dial port */
+ dial_port = MM_SERIAL_PORT (priv->primary);
+ if (MM_IS_AT_SERIAL_PORT (priv->data))
+ dial_port = MM_SERIAL_PORT (priv->data);
+
+ mm_serial_port_flash (dial_port, 1000, TRUE, disconnect_flash_done, info);
}
}
@@ -5487,15 +5490,26 @@ sms_list (MMModemGsmSms *modem,
MMAtSerialPort *
mm_generic_gsm_get_at_port (MMGenericGsm *modem,
- MMPortType ptype)
+ MMAtPortFlags flag)
{
+ MMGenericGsmPrivate *priv;
+
g_return_val_if_fail (MM_IS_GENERIC_GSM (modem), NULL);
- g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL);
- if (ptype == MM_PORT_TYPE_PRIMARY)
- return MM_GENERIC_GSM_GET_PRIVATE (modem)->primary;
- else if (ptype == MM_PORT_TYPE_SECONDARY)
- return MM_GENERIC_GSM_GET_PRIVATE (modem)->secondary;
+ /* We only search for a single value even though it's a bitfield */
+ g_return_val_if_fail ( flag == MM_AT_PORT_FLAG_NONE
+ || flag == MM_AT_PORT_FLAG_PRIMARY
+ || flag == MM_AT_PORT_FLAG_SECONDARY
+ || flag == MM_AT_PORT_FLAG_PPP, NULL);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if (flag == MM_AT_PORT_FLAG_SECONDARY)
+ return priv->secondary;
+ else if (flag == MM_AT_PORT_FLAG_PRIMARY)
+ return priv->primary;
+ else if ((flag == MM_AT_PORT_FLAG_PPP) && MM_IS_AT_SERIAL_PORT (priv->data))
+ return MM_AT_SERIAL_PORT (priv->data);
return NULL;
}
@@ -6481,7 +6495,7 @@ static void
modem_init (MMModem *modem_class)
{
modem_class->owns_port = owns_port;
- modem_class->grab_port = grab_port;
+ modem_class->organize_ports = organize_ports;
modem_class->release_port = release_port;
modem_class->enable = enable;
modem_class->disable = disable;
@@ -6817,11 +6831,12 @@ finalize (GObject *object)
}
static void
-mm_generic_gsm_class_init (MMGenericGsmClass *klass)
+mm_generic_gsm_class_init (MMGenericGsmClass *generic_class)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (generic_class);
+ MMModemBaseClass *base_class = MM_MODEM_BASE_CLASS (generic_class);
- mm_generic_gsm_parent_class = g_type_class_peek_parent (klass);
+ mm_generic_gsm_parent_class = g_type_class_peek_parent (generic_class);
g_type_class_add_private (object_class, sizeof (MMGenericGsmPrivate));
/* Virtual methods */
@@ -6829,12 +6844,14 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
object_class->get_property = get_property;
object_class->finalize = finalize;
- klass->do_enable = real_do_enable;
- klass->do_enable_power_up_done = real_do_enable_power_up_done;
- klass->do_disconnect = real_do_disconnect;
- klass->get_sim_iccid = real_get_sim_iccid;
- klass->get_operator_name = real_get_operator_name;
- klass->get_operator_code = real_get_operator_code;
+ base_class->port_grabbed = port_grabbed;
+
+ generic_class->do_enable = real_do_enable;
+ generic_class->do_enable_power_up_done = real_do_enable_power_up_done;
+ generic_class->do_disconnect = real_do_disconnect;
+ generic_class->get_sim_iccid = real_get_sim_iccid;
+ generic_class->get_operator_name = real_get_operator_name;
+ generic_class->get_operator_code = real_get_operator_code;
/* Properties */
g_object_class_override_property (object_class,
diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
index 92ab7b94..e0c3e6dd 100644
--- a/src/mm-generic-gsm.h
+++ b/src/mm-generic-gsm.h
@@ -83,6 +83,23 @@ typedef struct {
typedef struct {
MMModemBaseClass parent;
+ /* Called to allow subclasses to update port flags, attach unsolicited
+ * result code handlers, change port attributes, etc. This is called
+ * after the generic class has installed it's own handlers; if the
+ * generic class' behavior is not desired, subclasses can override the
+ * port_grabbed() method of MMModemBase.
+ */
+ void (*port_grabbed) (MMGenericGsm *self,
+ MMPort *port,
+ MMAtPortFlags at_pflags,
+ gpointer user_data);
+
+ /* Called after all ports have been organized to allow subclasses to
+ * make changes to ports after we've assigned primary, secondary, and data
+ * designations.
+ */
+ void (*ports_organized) (MMGenericGsm *self, MMAtSerialPort *primary);
+
/* Called after opening the primary serial port and updating the modem's
* state to ENABLING, but before sending any commands to the device. Modems
* that need to perform custom initialization sequences or other setup should
@@ -211,18 +228,13 @@ void mm_generic_gsm_update_access_technology (MMGenericGsm *modem,
*/
void mm_generic_gsm_update_signal_quality (MMGenericGsm *modem, guint32 quality);
+/* Returns the first port (if any) which has the given flag */
MMAtSerialPort *mm_generic_gsm_get_at_port (MMGenericGsm *modem,
- MMPortType ptype);
+ MMAtPortFlags flag);
MMAtSerialPort *mm_generic_gsm_get_best_at_port (MMGenericGsm *modem,
GError **error);
-MMPort *mm_generic_gsm_grab_port (MMGenericGsm *modem,
- const char *subsys,
- const char *name,
- MMPortType ptype,
- GError **error);
-
/* stay_connected should be TRUE for unsolicited registration updates, otherwise
* the registration update will clear connected/connecting/disconnecting state
* which we don't want. stay_connected should be FALSE for other cases like
diff --git a/src/mm-manager.c b/src/mm-manager.c
index 6767353e..5f114bba 100644
--- a/src/mm-manager.c
+++ b/src/mm-manager.c
@@ -247,13 +247,36 @@ remove_modem (MMManager *manager, MMModem *modem)
g_free (device);
}
+/* Return the first outstanding supports task */
+static SupportsInfo *
+find_supports_info (MMManager *self, MMModem *modem)
+{
+ char *modem_physdev;
+ GHashTableIter iter;
+ SupportsInfo *info, *ret = NULL;
+
+ modem_physdev = mm_modem_get_device (modem);
+ g_assert (modem_physdev);
+
+ /* Check for ports that are in the process of being interrogated by plugins */
+ g_hash_table_iter_init (&iter, MM_MANAGER_GET_PRIVATE (self)->supports);
+ while (!ret && g_hash_table_iter_next (&iter, NULL, (gpointer) &info)) {
+ if (g_strcmp0 (info->physdev_path, modem_physdev) == 0)
+ ret = info;
+ }
+ g_free (modem_physdev);
+ return ret;
+}
+
static void
check_export_modem (MMManager *self, MMModem *modem)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
- char *modem_physdev;
- GHashTableIter iter;
- gpointer value;
+ SupportsInfo *info;
+ static guint32 id = 0, vid = 0, pid = 0;
+ char *path, *data_device = NULL, *modem_physdev;
+ GUdevDevice *physdev;
+ const char *subsys = NULL;
/* A modem is only exported to D-Bus when both of the following are true:
*
@@ -269,63 +292,46 @@ check_export_modem (MMManager *self, MMModem *modem)
* all other ports are already handled. That chance is very small though.
*/
- modem_physdev = mm_modem_get_device (modem);
- g_assert (modem_physdev);
-
- /* Check for ports that are in the process of being interrogated by plugins */
- g_hash_table_iter_init (&iter, priv->supports);
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- SupportsInfo *info = value;
-
- if (!strcmp (info->physdev_path, modem_physdev)) {
- mm_dbg ("(%s/%s): outstanding support task prevents export of %s",
- info->subsys, info->name, modem_physdev);
- goto out;
- }
+ info = find_supports_info (self, modem);
+ if (info) {
+ mm_dbg ("(%s/%s): outstanding support task prevents export of %s",
+ info->subsys, info->name, info->physdev_path);
+ return;
}
- /* Already exported? This can happen if the modem is exported and the kernel
- * discovers another of the modem's ports.
- */
- if (g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG))
- goto out;
+ /* Obviously don't re-export a modem, or try to export one that's not valid */
+ if ( g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG)
+ || !mm_modem_get_valid (modem))
+ return;
+
+ path = g_strdup_printf (MM_DBUS_PATH "/Modems/%d", id++);
+ dbus_g_connection_register_g_object (priv->connection, path, G_OBJECT (modem));
+ g_object_set_data_full (G_OBJECT (modem), DBUS_PATH_TAG, path, (GDestroyNotify) g_free);
- /* No outstanding port tasks, so if the modem is valid we can export it */
- if (mm_modem_get_valid (modem)) {
- static guint32 id = 0, vid = 0, pid = 0;
- char *path, *data_device = NULL;
- GUdevDevice *physdev;
- const char *subsys = NULL;
-
- path = g_strdup_printf (MM_DBUS_PATH"/Modems/%d", id++);
- dbus_g_connection_register_g_object (priv->connection, path, G_OBJECT (modem));
- g_object_set_data_full (G_OBJECT (modem), DBUS_PATH_TAG, path, (GDestroyNotify) g_free);
-
- mm_dbg ("Exported modem %s as %s", modem_physdev, path);
-
- physdev = g_udev_client_query_by_sysfs_path (priv->udev, modem_physdev);
- if (physdev)
- subsys = g_udev_device_get_subsystem (physdev);
-
- g_object_get (G_OBJECT (modem),
- MM_MODEM_DATA_DEVICE, &data_device,
- MM_MODEM_HW_VID, &vid,
- MM_MODEM_HW_PID, &pid,
- NULL);
- mm_dbg ("(%s): VID 0x%04X PID 0x%04X (%s)",
- path, (vid & 0xFFFF), (pid & 0xFFFF),
- subsys ? subsys : "unknown");
- mm_dbg ("(%s): data port is %s", path, data_device);
- g_free (data_device);
-
- if (physdev)
- g_object_unref (physdev);
-
- g_signal_emit (self, signals[DEVICE_ADDED], 0, modem);
- }
+ modem_physdev = mm_modem_get_device (modem);
+ g_assert (modem_physdev);
+ mm_dbg ("Exported modem %s as %s", modem_physdev, path);
-out:
+ physdev = g_udev_client_query_by_sysfs_path (priv->udev, modem_physdev);
g_free (modem_physdev);
+ if (physdev)
+ subsys = g_udev_device_get_subsystem (physdev);
+
+ g_object_get (G_OBJECT (modem),
+ MM_MODEM_DATA_DEVICE, &data_device,
+ MM_MODEM_HW_VID, &vid,
+ MM_MODEM_HW_PID, &pid,
+ NULL);
+ mm_dbg ("(%s): VID 0x%04X PID 0x%04X (%s)",
+ path, (vid & 0xFFFF), (pid & 0xFFFF),
+ subsys ? subsys : "unknown");
+ mm_dbg ("(%s): data port is %s", path, data_device);
+ g_free (data_device);
+
+ if (physdev)
+ g_object_unref (physdev);
+
+ g_signal_emit (self, signals[DEVICE_ADDED], 0, modem);
}
static void
@@ -548,6 +554,7 @@ supports_cleanup (MMManager *self,
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
char *key;
+ GError *error = NULL;
g_return_if_fail (subsys != NULL);
g_return_if_fail (name != NULL);
@@ -556,15 +563,35 @@ supports_cleanup (MMManager *self,
g_hash_table_remove (priv->supports, key);
g_free (key);
+ if (modem == NULL)
+ return;
+
+ if ( (find_supports_info (self, modem) == NULL)
+ && !g_object_get_data (G_OBJECT (modem), "organized")) {
+ /* Yay, we're done with supports tasks, tell the modem to organize
+ * all its ports. Guard against multiple organize calls though since
+ * if the kernel or udev is slow, ports may show up long after the
+ * first bunch of supports tasks is done.
+ */
+ g_object_set_data (G_OBJECT (modem), "organized", GUINT_TO_POINTER (1));
+ if (!mm_modem_organize_ports (modem, &error)) {
+ mm_err ("Failed to organize modem ports: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ remove_modem (self, modem);
+ return;
+ }
+ }
+
/* Each time a supports task is cleaned up, check whether the modem is
* now completely probed/handled and should be exported to D-Bus clients.
*
- * IMPORTANT: this must be done after removing the supports into from
+ * IMPORTANT: this must be done after removing the supports info from
* priv->supports since check_export_modem() searches through priv->supports
* for outstanding supports tasks.
*/
- if (modem)
- check_export_modem (self, modem);
+ check_export_modem (self, modem);
}
static gboolean
diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c
index b3b1db2c..bd1943e7 100644
--- a/src/mm-modem-base.c
+++ b/src/mm-modem-base.c
@@ -113,14 +113,21 @@ mm_modem_base_get_port (MMModemBase *self,
return port;
}
-static void
-find_primary (gpointer key, gpointer data, gpointer user_data)
+GSList *
+mm_modem_base_get_ports (MMModemBase *self)
{
- MMPort **found = user_data;
- MMPort *port = MM_PORT (data);
+ GHashTableIter iter;
+ MMPort *port;
+ GSList *list = NULL;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
- if (!*found && (mm_port_get_port_type (port) == MM_PORT_TYPE_PRIMARY))
- *found = port;
+ g_hash_table_iter_init (&iter, MM_MODEM_BASE_GET_PRIVATE (self)->ports);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &port))
+ list = g_slist_append (list, port);
+
+ return list;
}
static gboolean
@@ -159,68 +166,6 @@ serial_port_timed_out_cb (MMSerialPort *port,
}
}
-MMPort *
-mm_modem_base_add_port (MMModemBase *self,
- const char *subsys,
- const char *name,
- MMPortType ptype)
-{
- MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
- MMPort *port = NULL;
- char *key, *device;
-
- g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
- g_return_val_if_fail (subsys != NULL, NULL);
- g_return_val_if_fail (name != NULL, NULL);
- g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL);
-
- g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), NULL);
-
- key = get_hash_key (subsys, name);
- port = g_hash_table_lookup (priv->ports, key);
- g_free (key);
- g_return_val_if_fail (port == NULL, NULL);
-
- if (ptype == MM_PORT_TYPE_PRIMARY) {
- g_hash_table_foreach (priv->ports, find_primary, &port);
- g_return_val_if_fail (port == NULL, FALSE);
- }
-
- if (!strcmp (subsys, "tty")) {
- if (ptype == MM_PORT_TYPE_QCDM)
- port = MM_PORT (mm_qcdm_serial_port_new (name, ptype));
- else
- port = MM_PORT (mm_at_serial_port_new (name, ptype));
-
- /* For serial ports, enable port timeout checks */
- if (port)
- g_signal_connect (port,
- "timed-out",
- G_CALLBACK (serial_port_timed_out_cb),
- self);
- } else if (!strcmp (subsys, "net")) {
- port = MM_PORT (g_object_new (MM_TYPE_PORT,
- MM_PORT_DEVICE, name,
- MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET,
- MM_PORT_TYPE, ptype,
- NULL));
- }
-
- if (!port)
- return NULL;
-
- device = mm_modem_get_device (MM_MODEM (self));
- mm_dbg ("(%s) type %s claimed by %s",
- name,
- mm_port_type_to_name (ptype),
- device);
- g_free (device);
-
- key = get_hash_key (subsys, name);
- g_hash_table_insert (priv->ports, key, port);
- return port;
-}
-
gboolean
mm_modem_base_remove_port (MMModemBase *self, MMPort *port)
{
@@ -252,6 +197,142 @@ mm_modem_base_remove_port (MMModemBase *self, MMPort *port)
return removed;
}
+static inline void
+log_port (MMPort *port, const char *device, const char *desc)
+{
+ if (port) {
+ mm_dbg ("(%s) %s/%s %s",
+ device,
+ mm_port_subsys_to_name (mm_port_get_subsys (port)),
+ mm_port_get_device (port),
+ desc);
+ }
+}
+
+gboolean
+mm_modem_base_organize_ports (MMModemBase *self,
+ MMAtSerialPort **out_primary,
+ MMAtSerialPort **out_secondary,
+ MMPort **out_data,
+ MMQcdmSerialPort **out_qcdm,
+ GError **error)
+{
+ GSList *ports, *iter;
+ MMAtPortFlags flags;
+ MMAtSerialPort *backup_primary = NULL;
+ MMAtSerialPort *primary = NULL;
+ MMAtSerialPort *secondary = NULL;
+ MMAtSerialPort *backup_secondary = NULL;
+ MMQcdmSerialPort *qcdm = NULL;
+ MMPort *data = NULL;
+ char *device;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (out_primary != NULL, FALSE);
+ g_return_val_if_fail (out_secondary != NULL, FALSE);
+ g_return_val_if_fail (out_data != NULL, FALSE);
+
+ ports = mm_modem_base_get_ports (self);
+ for (iter = ports; iter; iter = g_slist_next (iter)) {
+ MMPort *candidate = iter->data;
+ MMPortSubsys subsys = mm_port_get_subsys (candidate);
+
+ if (MM_IS_AT_SERIAL_PORT (candidate)) {
+ flags = mm_at_serial_port_get_flags (MM_AT_SERIAL_PORT (candidate));
+
+ if (flags & MM_AT_PORT_FLAG_PRIMARY) {
+ if (!primary)
+ primary = MM_AT_SERIAL_PORT (candidate);
+ else if (!backup_primary) {
+ /* Just in case the plugin gave us more than one primary
+ * and no secondaries, treat additional primary ports as
+ * secondary.
+ */
+ backup_primary = MM_AT_SERIAL_PORT (candidate);
+ }
+ }
+
+ if (!data && (flags & MM_AT_PORT_FLAG_PPP))
+ data = candidate;
+
+ /* Explicitly flagged secondary ports trump NONE ports for secondary */
+ if (flags & MM_AT_PORT_FLAG_SECONDARY) {
+ if (!secondary || !(mm_at_serial_port_get_flags (secondary) & MM_AT_PORT_FLAG_SECONDARY))
+ secondary = MM_AT_SERIAL_PORT (candidate);
+ }
+
+ /* Fallback secondary */
+ if (flags == MM_AT_PORT_FLAG_NONE) {
+ if (!secondary)
+ secondary = MM_AT_SERIAL_PORT (candidate);
+ else if (!backup_secondary)
+ backup_secondary = MM_AT_SERIAL_PORT (candidate);
+ }
+ } else if (MM_IS_QCDM_SERIAL_PORT (candidate)) {
+ if (!qcdm)
+ qcdm = MM_QCDM_SERIAL_PORT (candidate);
+ } else if (subsys == MM_PORT_SUBSYS_NET) {
+ /* Net device (if any) is the preferred data port */
+ if (!data || MM_IS_AT_SERIAL_PORT (data))
+ data = candidate;
+ }
+ }
+ g_slist_free (ports);
+
+ /* Fall back to a secondary port if we didn't find a primary port */
+ if (!primary) {
+ if (!secondary) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Failed to find primary port.");
+ return FALSE;
+ }
+ primary = secondary;
+ secondary = NULL;
+ }
+ g_assert (primary);
+
+ /* If the plugin didn't give us any secondary ports, use any additional
+ * primary ports or backup secondary ports as secondary.
+ */
+ if (!secondary)
+ secondary = backup_primary ? backup_primary : backup_secondary;
+
+ /* Data port defaults to primary AT port */
+ if (!data)
+ data = MM_PORT (primary);
+ g_assert (data);
+
+ /* Reset flags on all ports; clear data port first since it might also
+ * be the primary or secondary port.
+ */
+ if (MM_IS_AT_SERIAL_PORT (data))
+ mm_at_serial_port_set_flags (MM_AT_SERIAL_PORT (data), MM_AT_PORT_FLAG_NONE);
+
+ mm_at_serial_port_set_flags (primary, MM_AT_PORT_FLAG_PRIMARY);
+ if (secondary)
+ mm_at_serial_port_set_flags (secondary, MM_AT_PORT_FLAG_SECONDARY);
+
+ if (MM_IS_AT_SERIAL_PORT (data)) {
+ flags = mm_at_serial_port_get_flags (MM_AT_SERIAL_PORT (data));
+ mm_at_serial_port_set_flags (MM_AT_SERIAL_PORT (data), flags | MM_AT_PORT_FLAG_PPP);
+ }
+
+ device = mm_modem_get_device (MM_MODEM (self));
+ log_port (MM_PORT (primary), device, "primary");
+ log_port (MM_PORT (secondary), device, "secondary");
+ log_port (MM_PORT (data), device, "data");
+ log_port (MM_PORT (qcdm), device, "qcdm");
+ g_free (device);
+
+ *out_primary = primary;
+ *out_secondary = secondary;
+ *out_data = data;
+ if (out_qcdm)
+ *out_qcdm = qcdm;
+
+ return TRUE;
+}
+
void
mm_modem_base_set_valid (MMModemBase *self, gboolean new_valid)
{
@@ -786,6 +867,77 @@ mm_modem_base_get_card_info (MMModemBase *self,
/*****************************************************************************/
static gboolean
+grab_port (MMModem *modem,
+ const char *subsys,
+ const char *name,
+ MMPortType ptype,
+ MMAtPortFlags at_pflags,
+ gpointer user_data,
+ GError **error)
+{
+ MMModemBase *self = MM_MODEM_BASE (modem);
+ MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ MMPort *port = NULL;
+ char *key, *device;
+
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), FALSE);
+ g_return_val_if_fail (subsys != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), FALSE);
+
+ key = get_hash_key (subsys, name);
+ port = g_hash_table_lookup (priv->ports, key);
+ g_free (key);
+ g_return_val_if_fail (port == NULL, FALSE);
+
+ if (!strcmp (subsys, "tty")) {
+ if (ptype == MM_PORT_TYPE_QCDM)
+ port = MM_PORT (mm_qcdm_serial_port_new (name));
+ else if (ptype == MM_PORT_TYPE_AT)
+ port = MM_PORT (mm_at_serial_port_new (name));
+
+ /* For serial ports, enable port timeout checks */
+ if (port) {
+ g_signal_connect (port,
+ "timed-out",
+ G_CALLBACK (serial_port_timed_out_cb),
+ self);
+ }
+ } else if (!strcmp (subsys, "net")) {
+ port = MM_PORT (g_object_new (MM_TYPE_PORT,
+ MM_PORT_DEVICE, name,
+ MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET,
+ NULL));
+ }
+
+ if (!port) {
+ g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Failed to grab port %s/%s: unknown type?",
+ subsys, name);
+ mm_dbg ("(%s/%s): failed to create %s port",
+ subsys, name, mm_port_type_to_name (ptype));
+ return FALSE;
+ }
+
+ device = mm_modem_get_device (MM_MODEM (self));
+ mm_dbg ("(%s) type %s claimed by %s",
+ name,
+ mm_port_type_to_name (ptype),
+ device);
+ g_free (device);
+
+ key = get_hash_key (subsys, name);
+ g_hash_table_insert (priv->ports, key, port);
+
+ /* Let subclasses know we've grabbed it */
+ if (MM_MODEM_BASE_GET_CLASS (self)->port_grabbed)
+ MM_MODEM_BASE_GET_CLASS (self)->port_grabbed (self, port, at_pflags, user_data);
+
+ return TRUE;
+}
+
+static gboolean
modem_auth_request (MMModem *modem,
const char *authorization,
DBusGMethodInvocation *context,
@@ -876,6 +1028,7 @@ mm_modem_base_init (MMModemBase *self)
static void
modem_init (MMModem *modem_class)
{
+ modem_class->grab_port = grab_port;
modem_class->auth_request = modem_auth_request;
modem_class->auth_finish = modem_auth_finish;
}
diff --git a/src/mm-modem-base.h b/src/mm-modem-base.h
index a386bd76..b3bd679e 100644
--- a/src/mm-modem-base.h
+++ b/src/mm-modem-base.h
@@ -22,6 +22,7 @@
#include "mm-port.h"
#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
#include "mm-modem.h"
#define MM_TYPE_MODEM_BASE (mm_modem_base_get_type ())
@@ -42,6 +43,14 @@ struct _MMModemBase {
struct _MMModemBaseClass {
GObjectClass parent;
+
+ /* Called after the base class grabs a port so that subclasses can
+ * set port flags and other properties on the new port.
+ */
+ void (*port_grabbed) (MMModemBase *self,
+ MMPort *port,
+ MMAtPortFlags at_pflags,
+ gpointer user_data);
};
GType mm_modem_base_get_type (void);
@@ -50,14 +59,18 @@ MMPort *mm_modem_base_get_port (MMModemBase *self,
const char *subsys,
const char *name);
-MMPort *mm_modem_base_add_port (MMModemBase *self,
- const char *subsys,
- const char *name,
- MMPortType ptype);
+GSList *mm_modem_base_get_ports (MMModemBase *self);
gboolean mm_modem_base_remove_port (MMModemBase *self,
MMPort *port);
+gboolean mm_modem_base_organize_ports (MMModemBase *self,
+ MMAtSerialPort **out_primary,
+ MMAtSerialPort **out_secondary,
+ MMPort **out_data,
+ MMQcdmSerialPort **out_qcdm,
+ GError **error);
+
void mm_modem_base_set_valid (MMModemBase *self,
gboolean valid);
diff --git a/src/mm-modem.c b/src/mm-modem.c
index adfbeb26..012e3203 100644
--- a/src/mm-modem.c
+++ b/src/mm-modem.c
@@ -633,7 +633,8 @@ gboolean
mm_modem_grab_port (MMModem *self,
const char *subsys,
const char *name,
- MMPortType suggested_type,
+ MMPortType ptype,
+ MMAtPortFlags at_pflags,
gpointer user_data,
GError **error)
{
@@ -643,7 +644,17 @@ mm_modem_grab_port (MMModem *self,
g_return_val_if_fail (name, FALSE);
g_assert (MM_MODEM_GET_INTERFACE (self)->grab_port);
- return MM_MODEM_GET_INTERFACE (self)->grab_port (self, subsys, name, suggested_type, user_data, error);
+ return MM_MODEM_GET_INTERFACE (self)->grab_port (self, subsys, name, ptype, at_pflags, user_data, error);
+}
+
+gboolean
+mm_modem_organize_ports (MMModem *self, GError **error)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+
+ g_assert (MM_MODEM_GET_INTERFACE (self)->organize_ports);
+ return MM_MODEM_GET_INTERFACE (self)->organize_ports (self, error);
}
void
diff --git a/src/mm-modem.h b/src/mm-modem.h
index 19640ac8..c4200645 100644
--- a/src/mm-modem.h
+++ b/src/mm-modem.h
@@ -23,6 +23,7 @@
#include <ModemManager.h>
#include "mm-port.h"
+#include "mm-at-serial-port.h"
#include "mm-auth-provider.h"
#include "mm-charsets.h"
@@ -118,13 +119,23 @@ struct _MMModem {
const char *subsys,
const char *name);
+ /* Subclasses use this function to claim a particular port */
gboolean (*grab_port) (MMModem *self,
const char *subsys,
const char *name,
- MMPortType suggested_type,
+ MMPortType ptype,
+ MMAtPortFlags at_pflags,
gpointer user_data,
GError **error);
+ /* Subclasses use this function to determine which of their
+ * grabbed ports should be used for data, command and status,
+ * PPP, etc. Called after all ports have been detected and
+ * grabbed by the modem.
+ */
+ gboolean (*organize_ports) (MMModem *self,
+ GError **error);
+
void (*release_port) (MMModem *self,
const char *subsys,
const char *name);
@@ -204,10 +215,14 @@ gboolean mm_modem_owns_port (MMModem *self,
gboolean mm_modem_grab_port (MMModem *self,
const char *subsys,
const char *name,
- MMPortType suggested_type,
+ MMPortType ptype,
+ MMAtPortFlags at_pflags,
gpointer user_data,
GError **error);
+gboolean mm_modem_organize_ports (MMModem *self,
+ GError **error);
+
void mm_modem_release_port (MMModem *self,
const char *subsys,
const char *name);
diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c
index 2c56c7b4..c4b514bb 100644
--- a/src/mm-plugin-base.c
+++ b/src/mm-plugin-base.c
@@ -261,6 +261,16 @@ mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSupportsTask *
return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->probed_caps;
}
+MMPortType
+mm_plugin_base_probed_capabilities_to_port_type (guint32 caps)
+{
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_AT)
+ return MM_PORT_TYPE_AT;
+ else if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ return MM_PORT_TYPE_QCDM;
+ return MM_PORT_TYPE_UNKNOWN;
+}
+
const gchar *
mm_plugin_base_supports_task_get_probed_vendor (MMPluginBaseSupportsTask *task)
{
@@ -680,7 +690,7 @@ try_qcdm_probe (MMPluginBaseSupportsTask *task)
/* Open the QCDM port */
name = g_udev_device_get_name (priv->port);
g_assert (name);
- priv->qcdm_port = mm_qcdm_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
+ priv->qcdm_port = mm_qcdm_serial_port_new (name);
if (priv->qcdm_port == NULL) {
g_warning ("(%s) failed to create new QCDM serial port.", name);
probe_complete (task);
@@ -1064,7 +1074,7 @@ mm_plugin_base_probe_port (MMPluginBase *self,
name = g_udev_device_get_name (port);
g_assert (name);
- serial = mm_at_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
+ serial = mm_at_serial_port_new (name);
if (serial == NULL) {
g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Failed to create new serial port.");
diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h
index 48bec97e..46254749 100644
--- a/src/mm-plugin-base.h
+++ b/src/mm-plugin-base.h
@@ -87,6 +87,8 @@ const char *mm_plugin_base_supports_task_get_driver (MMPluginBaseSupportsTask *t
guint32 mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSupportsTask *task);
+MMPortType mm_plugin_base_probed_capabilities_to_port_type (guint32 caps);
+
const gchar *mm_plugin_base_supports_task_get_probed_vendor (MMPluginBaseSupportsTask *task);
const gchar *mm_plugin_base_supports_task_get_probed_product (MMPluginBaseSupportsTask *task);
diff --git a/src/mm-port.c b/src/mm-port.c
index a1291d0b..720b9c35 100644
--- a/src/mm-port.c
+++ b/src/mm-port.c
@@ -64,10 +64,8 @@ const char *
mm_port_type_to_name (MMPortType ptype)
{
switch (ptype) {
- case MM_PORT_TYPE_PRIMARY:
- return "primary";
- case MM_PORT_TYPE_SECONDARY:
- return "secondary";
+ case MM_PORT_TYPE_AT:
+ return "AT";
case MM_PORT_TYPE_IGNORED:
return "ignored";
case MM_PORT_TYPE_QCDM:
@@ -108,7 +106,9 @@ constructor (GType type,
return NULL;
}
- if (priv->ptype == MM_PORT_TYPE_UNKNOWN) {
+ /* Can't have a TTY subsystem port that's unknown */
+ if ( priv->subsys != MM_PORT_SUBSYS_NET
+ && priv->ptype == MM_PORT_TYPE_UNKNOWN) {
g_warning ("MMPort: invalid port type");
g_object_unref (object);
return NULL;
diff --git a/src/mm-port.h b/src/mm-port.h
index df935db2..21e2f605 100644
--- a/src/mm-port.h
+++ b/src/mm-port.h
@@ -29,8 +29,7 @@ typedef enum {
typedef enum {
MM_PORT_TYPE_UNKNOWN = 0x0,
- MM_PORT_TYPE_PRIMARY,
- MM_PORT_TYPE_SECONDARY,
+ MM_PORT_TYPE_AT,
MM_PORT_TYPE_IGNORED,
MM_PORT_TYPE_QCDM,
diff --git a/src/mm-qcdm-serial-port.c b/src/mm-qcdm-serial-port.c
index 0d763bf5..d209dbb7 100644
--- a/src/mm-qcdm-serial-port.c
+++ b/src/mm-qcdm-serial-port.c
@@ -216,17 +216,17 @@ config_fd (MMSerialPort *port, int fd, GError **error)
/*****************************************************************************/
MMQcdmSerialPort *
-mm_qcdm_serial_port_new (const char *name, MMPortType ptype)
+mm_qcdm_serial_port_new (const char *name)
{
return MM_QCDM_SERIAL_PORT (g_object_new (MM_TYPE_QCDM_SERIAL_PORT,
MM_PORT_DEVICE, name,
MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY,
- MM_PORT_TYPE, ptype,
+ MM_PORT_TYPE, MM_PORT_TYPE_QCDM,
NULL));
}
MMQcdmSerialPort *
-mm_qcdm_serial_port_new_fd (int fd, MMPortType ptype)
+mm_qcdm_serial_port_new_fd (int fd)
{
MMQcdmSerialPort *port;
char *name;
@@ -235,7 +235,7 @@ mm_qcdm_serial_port_new_fd (int fd, MMPortType ptype)
port = MM_QCDM_SERIAL_PORT (g_object_new (MM_TYPE_QCDM_SERIAL_PORT,
MM_PORT_DEVICE, name,
MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY,
- MM_PORT_TYPE, ptype,
+ MM_PORT_TYPE, MM_PORT_TYPE_QCDM,
MM_SERIAL_PORT_FD, fd,
NULL));
g_free (name);
diff --git a/src/mm-qcdm-serial-port.h b/src/mm-qcdm-serial-port.h
index 605016d3..5efccdb1 100644
--- a/src/mm-qcdm-serial-port.h
+++ b/src/mm-qcdm-serial-port.h
@@ -47,9 +47,9 @@ struct _MMQcdmSerialPortClass {
GType mm_qcdm_serial_port_get_type (void);
-MMQcdmSerialPort *mm_qcdm_serial_port_new (const char *name, MMPortType ptype);
+MMQcdmSerialPort *mm_qcdm_serial_port_new (const char *name);
-MMQcdmSerialPort *mm_qcdm_serial_port_new_fd (int fd, MMPortType ptype);
+MMQcdmSerialPort *mm_qcdm_serial_port_new_fd (int fd);
void mm_qcdm_serial_port_queue_command (MMQcdmSerialPort *self,
GByteArray *command,
diff --git a/src/tests/test-qcdm-serial-port.c b/src/tests/test-qcdm-serial-port.c
index c31011b3..8965c66a 100644
--- a/src/tests/test-qcdm-serial-port.c
+++ b/src/tests/test-qcdm-serial-port.c
@@ -215,7 +215,7 @@ qcdm_test_child (int fd, VerInfoCb cb)
loop = g_main_loop_new (NULL, FALSE);
- port = mm_qcdm_serial_port_new_fd (fd, MM_PORT_TYPE_PRIMARY);
+ port = mm_qcdm_serial_port_new_fd (fd);
g_assert (port);
success = mm_serial_port_open (MM_SERIAL_PORT (port), &error);