base-modem: allow grabbing link ports after modem is created

We will grab the ports and make them available through find_ports() or
peek_port() or get_port().

The link ports are not 'organized' so they won't be available in
e.g. get_data_ports(), which is fine, because we don't want to mix
them with the real data ports exposed by the device.

Link port additions and removals are also notified via signals in the
modem object, so that explicit wait for specific ports can be done.
This commit is contained in:
Aleksander Morgado
2021-02-12 12:29:49 +01:00
parent 21174f289f
commit 897e48709d
3 changed files with 200 additions and 33 deletions

View File

@@ -61,7 +61,14 @@ enum {
PROP_LAST PROP_LAST
}; };
enum {
SIGNAL_LINK_PORT_GRABBED,
SIGNAL_LINK_PORT_RELEASED,
SIGNAL_LAST
};
static GParamSpec *properties[PROP_LAST]; static GParamSpec *properties[PROP_LAST];
static guint signals[SIGNAL_LAST];
struct _MMBaseModemPrivate { struct _MMBaseModemPrivate {
/* The connection to the system bus */ /* The connection to the system bus */
@@ -120,6 +127,10 @@ struct _MMBaseModemPrivate {
/* MBIM ports */ /* MBIM ports */
GList *mbim; GList *mbim;
#endif #endif
/* Additional port links grabbed after having
* organized ports */
GHashTable *link_ports;
}; };
guint guint
@@ -259,12 +270,13 @@ base_modem_create_virtual_port (MMBaseModem *self,
return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_UNIX)); return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_UNIX));
} }
gboolean static MMPort *
mm_base_modem_grab_port (MMBaseModem *self, base_modem_internal_grab_port (MMBaseModem *self,
MMKernelDevice *kernel_device, MMKernelDevice *kernel_device,
MMPortType ptype, gboolean link_port,
MMPortSerialAtFlag at_pflags, MMPortType ptype,
GError **error) MMPortSerialAtFlag at_pflags,
GError **error)
{ {
MMPort *port; MMPort *port;
const gchar *subsys; const gchar *subsys;
@@ -280,7 +292,7 @@ mm_base_modem_grab_port (MMBaseModem *self,
if (port) { if (port) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot add port '%s/%s', already exists", subsys, name); "Cannot add port '%s/%s', already exists", subsys, name);
return FALSE; return NULL;
} }
/* Explicitly ignored ports, grab them but explicitly flag them as ignored /* Explicitly ignored ports, grab them but explicitly flag them as ignored
@@ -301,7 +313,7 @@ mm_base_modem_grab_port (MMBaseModem *self,
if (!port) { if (!port) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot add port '%s/%s', unhandled port type", subsys, name); "Cannot add port '%s/%s', unhandled port type", subsys, name);
return FALSE; return NULL;
} }
/* Store kernel device */ /* Store kernel device */
@@ -339,11 +351,96 @@ mm_base_modem_grab_port (MMBaseModem *self,
/* Add it to the tracking HT. /* Add it to the tracking HT.
* Note: 'key' and 'port' now owned by the HT. */ * Note: 'key' and 'port' now owned by the HT. */
mm_obj_dbg (port, "port grabbed"); if (link_port)
g_hash_table_insert (self->priv->ports, g_steal_pointer (&key), port); g_hash_table_insert (self->priv->link_ports, g_steal_pointer (&key), port);
else
g_hash_table_insert (self->priv->ports, g_steal_pointer (&key), port);
return port;
}
gboolean
mm_base_modem_grab_port (MMBaseModem *self,
MMKernelDevice *kernel_device,
MMPortType ptype,
MMPortSerialAtFlag at_pflags,
GError **error)
{
if (!base_modem_internal_grab_port (self, kernel_device, FALSE, ptype, at_pflags, error))
return FALSE;
mm_obj_dbg (self, "port '%s/%s' grabbed",
mm_kernel_device_get_subsystem (kernel_device),
mm_kernel_device_get_name (kernel_device));
return TRUE; return TRUE;
} }
/******************************************************************************/
gboolean
mm_base_modem_grab_link_port (MMBaseModem *self,
MMKernelDevice *kernel_device,
GError **error)
{
const gchar *subsystem;
const gchar *name;
MMPort *port;
/* To simplify things, we only support NET link ports at this point */
subsystem = mm_kernel_device_get_subsystem (kernel_device);
name = mm_kernel_device_get_name (kernel_device);
if (!g_str_equal (subsystem, "net")) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot add port '%s/%s', unexpected link port subsystem", subsystem, name);
return FALSE;
}
/* all the newly added link ports will NOT be 'organized'; i.e. they won't
* be available as 'data ports' in the modem, but they can be looked up
* by name */
port = base_modem_internal_grab_port (self,
kernel_device,
TRUE,
MM_PORT_TYPE_NET,
MM_PORT_SERIAL_AT_FLAG_NONE,
error);
if (!port)
return FALSE;
mm_obj_dbg (self, "link port '%s/%s' grabbed", subsystem, name);
g_signal_emit (self, signals[SIGNAL_LINK_PORT_GRABBED], 0, port);
return TRUE;
}
gboolean
mm_base_modem_release_link_port (MMBaseModem *self,
const gchar *subsystem,
const gchar *name,
GError **error)
{
g_autofree gchar *key = NULL;
MMPort *port;
key = g_strdup_printf ("%s%s", subsystem, name);
port = g_hash_table_lookup (self->priv->link_ports, key);
if (!port) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Cannot release link port '%s/%s', not grabbed", subsystem, name);
return FALSE;
}
/* make sure the port object is valid during the port release signal */
g_object_ref (port);
g_hash_table_remove (self->priv->link_ports, key);
mm_obj_dbg (self, "link port '%s/%s' released", subsystem, name);
g_signal_emit (self, signals[SIGNAL_LINK_PORT_RELEASED], 0, port);
g_object_unref (port);
return TRUE;
}
/******************************************************************************/
gboolean gboolean
mm_base_modem_disable_finish (MMBaseModem *self, mm_base_modem_disable_finish (MMBaseModem *self,
GAsyncResult *res, GAsyncResult *res,
@@ -851,18 +948,18 @@ mm_base_modem_find_ports (MMBaseModem *self,
return g_list_sort (out, (GCompareFunc) port_cmp); return g_list_sort (out, (GCompareFunc) port_cmp);
} }
MMPort * static MMPort *
mm_base_modem_peek_port (MMBaseModem *self, peek_port_in_ht (GHashTable *ht,
const gchar *name) const gchar *name)
{ {
GHashTableIter iter; GHashTableIter iter;
gpointer value; gpointer value;
gpointer key; gpointer key;
if (!self->priv->ports) if (!ht)
return NULL; return NULL;
g_hash_table_iter_init (&iter, self->priv->ports); g_hash_table_iter_init (&iter, ht);
while (g_hash_table_iter_next (&iter, &key, &value)) { while (g_hash_table_iter_next (&iter, &key, &value)) {
MMPort *port = MM_PORT (value); MMPort *port = MM_PORT (value);
@@ -873,6 +970,19 @@ mm_base_modem_peek_port (MMBaseModem *self,
return NULL; return NULL;
} }
MMPort *
mm_base_modem_peek_port (MMBaseModem *self,
const gchar *name)
{
MMPort *found;
found = peek_port_in_ht (self->priv->ports, name);
if (!found)
found = peek_port_in_ht (self->priv->link_ports, name);
return found;
}
MMPort * MMPort *
mm_base_modem_get_port (MMBaseModem *self, mm_base_modem_get_port (MMBaseModem *self,
const gchar *name) const gchar *name)
@@ -1362,13 +1472,10 @@ base_modem_cancelled (GCancellable *cancellable,
/*****************************************************************************/ /*****************************************************************************/
static void static void
setup_ports_table (MMBaseModem *self) setup_ports_table (GHashTable **ht)
{ {
g_assert (!self->priv->ports); g_assert (ht && !*ht);
self->priv->ports = g_hash_table_new_full (g_str_hash, *ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
g_str_equal,
g_free,
g_object_unref);
} }
static void static void
@@ -1406,19 +1513,20 @@ cleanup_modem_port (MMBaseModem *self,
} }
static void static void
teardown_ports_table (MMBaseModem *self) teardown_ports_table (MMBaseModem *self,
GHashTable **ht)
{ {
GHashTableIter iter; GHashTableIter iter;
gpointer value; gpointer value;
gpointer key; gpointer key;
if (!self->priv->ports) if (!*ht)
return; return;
g_hash_table_iter_init (&iter, self->priv->ports); g_hash_table_iter_init (&iter, *ht);
while (g_hash_table_iter_next (&iter, &key, &value)) while (g_hash_table_iter_next (&iter, &key, &value))
cleanup_modem_port (self, MM_PORT (value)); cleanup_modem_port (self, MM_PORT (value));
g_hash_table_destroy (g_steal_pointer (&self->priv->ports)); g_hash_table_destroy (g_steal_pointer (ht));
} }
/*****************************************************************************/ /*****************************************************************************/
@@ -1461,7 +1569,8 @@ mm_base_modem_init (MMBaseModem *self)
self->priv->max_timeouts = DEFAULT_MAX_TIMEOUTS; self->priv->max_timeouts = DEFAULT_MAX_TIMEOUTS;
setup_ports_table (self); setup_ports_table (&self->priv->ports);
setup_ports_table (&self->priv->link_ports);
} }
static void static void
@@ -1618,7 +1727,8 @@ dispose (GObject *object)
g_list_free_full (g_steal_pointer (&self->priv->mbim), g_object_unref); g_list_free_full (g_steal_pointer (&self->priv->mbim), g_object_unref);
#endif #endif
teardown_ports_table (self); teardown_ports_table (self, &self->priv->link_ports);
teardown_ports_table (self, &self->priv->ports);
g_clear_object (&self->priv->connection); g_clear_object (&self->priv->connection);
@@ -1732,4 +1842,22 @@ mm_base_modem_class_init (MMBaseModemClass *klass)
FALSE, FALSE,
G_PARAM_READWRITE); G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_DATA_TTY_SUPPORTED, properties[PROP_DATA_TTY_SUPPORTED]); g_object_class_install_property (object_class, PROP_DATA_TTY_SUPPORTED, properties[PROP_DATA_TTY_SUPPORTED]);
signals[SIGNAL_LINK_PORT_GRABBED] =
g_signal_new (MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMBaseModemClass, link_port_grabbed),
NULL, NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE, 1, MM_TYPE_PORT);
signals[SIGNAL_LINK_PORT_RELEASED] =
g_signal_new (MM_BASE_MODEM_SIGNAL_LINK_PORT_RELEASED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMBaseModemClass, link_port_released),
NULL, NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE, 1, MM_TYPE_PORT);
} }

View File

@@ -67,6 +67,9 @@ typedef struct _MMBaseModemPrivate MMBaseModemPrivate;
#define MM_BASE_MODEM_DATA_NET_SUPPORTED "base-modem-data-net-supported" #define MM_BASE_MODEM_DATA_NET_SUPPORTED "base-modem-data-net-supported"
#define MM_BASE_MODEM_DATA_TTY_SUPPORTED "base-modem-data-tty-supported" #define MM_BASE_MODEM_DATA_TTY_SUPPORTED "base-modem-data-tty-supported"
#define MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED "base-modem-link-port-grabbed"
#define MM_BASE_MODEM_SIGNAL_LINK_PORT_RELEASED "base-modem-link-port-released"
struct _MMBaseModem { struct _MMBaseModem {
MmGdbusObjectSkeleton parent; MmGdbusObjectSkeleton parent;
MMBaseModemPrivate *priv; MMBaseModemPrivate *priv;
@@ -104,6 +107,12 @@ struct _MMBaseModemClass {
gboolean (*disable_finish) (MMBaseModem *self, gboolean (*disable_finish) (MMBaseModem *self,
GAsyncResult *res, GAsyncResult *res,
GError **error); GError **error);
/* signals */
void (* link_port_grabbed) (MMBaseModem *self,
MMPort *link_port);
void (* link_port_released) (MMBaseModem *self,
MMPort *link_port);
}; };
GType mm_base_modem_get_type (void); GType mm_base_modem_get_type (void);
@@ -111,11 +120,18 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseModem, g_object_unref)
guint mm_base_modem_get_dbus_id (MMBaseModem *self); guint mm_base_modem_get_dbus_id (MMBaseModem *self);
gboolean mm_base_modem_grab_port (MMBaseModem *self, gboolean mm_base_modem_grab_port (MMBaseModem *self,
MMKernelDevice *kernel_device, MMKernelDevice *kernel_device,
MMPortType ptype, MMPortType ptype,
MMPortSerialAtFlag at_pflags, MMPortSerialAtFlag at_pflags,
GError **error); GError **error);
gboolean mm_base_modem_grab_link_port (MMBaseModem *self,
MMKernelDevice *kernel_device,
GError **error);
gboolean mm_base_modem_release_link_port (MMBaseModem *self,
const gchar *subsystem,
const gchar *name,
GError **error);
gboolean mm_base_modem_has_at_port (MMBaseModem *self); gboolean mm_base_modem_has_at_port (MMBaseModem *self);

View File

@@ -211,11 +211,28 @@ void
mm_device_grab_port (MMDevice *self, mm_device_grab_port (MMDevice *self,
MMKernelDevice *kernel_port) MMKernelDevice *kernel_port)
{ {
MMPortProbe *probe; MMPortProbe *probe;
MMKernelDevice *lower_port;
if (mm_device_owns_port (self, kernel_port)) if (mm_device_owns_port (self, kernel_port))
return; return;
lower_port = mm_kernel_device_peek_lower_device (kernel_port);
if (lower_port) {
g_autoptr(GError) error = NULL;
/* No port probing done, at this point this is not something we require
* as all the virtual instantiated ports are net devices. We also avoid
* emitting the PORT_GRABBED signal in the MMDevice, because that is
* exclusively linked to a port being added to the list of probes, which
* we don't do here. */
if (self->priv->modem && !mm_base_modem_grab_link_port (self->priv->modem, kernel_port, &error))
mm_obj_dbg (self, "fully ignoring link port %s from now on: %s",
mm_kernel_device_get_name (kernel_port),
error->message);
return;
}
/* Get the vendor/product IDs out of the first one that gives us /* Get the vendor/product IDs out of the first one that gives us
* some valid value (it seems we may get NULL reported for VID in QMI * some valid value (it seems we may get NULL reported for VID in QMI
* ports, e.g. Huawei E367) */ * ports, e.g. Huawei E367) */
@@ -242,6 +259,12 @@ mm_device_release_port_name (MMDevice *self,
{ {
MMPortProbe *probe; MMPortProbe *probe;
/* If modem exists, try to remove it as a link port. We also avoid emitting
* the PORT_RELEASED signal in this case, as the link ports are not associated
* to the port probe list */
if (self->priv->modem && mm_base_modem_release_link_port (self->priv->modem, subsystem, name, NULL))
return;
probe = device_find_probe_with_name (self, subsystem, name); probe = device_find_probe_with_name (self, subsystem, name);
if (probe) { if (probe) {
/* Found, remove from lists and destroy probe */ /* Found, remove from lists and destroy probe */