huawei: use the cdc-wdm port dialing if available

With the new 'huawei-cdc-ncm' driver in the kernel, we now may get a
/dev/cdc-wdm AT-capable port exposed by the Huawei device. If so, we must use
this port for NDISDUP dialling in order to get the network interface connected.
This commit is contained in:
Aleksander Morgado
2014-06-23 18:12:27 +02:00
committed by Aleksander Morgado
parent 293b032149
commit 75a5c48345
3 changed files with 177 additions and 27 deletions
+30 -4
View File
@@ -41,6 +41,25 @@ struct _MMBroadbandBearerHuaweiPrivate {
guint network_disconnect_pending_id;
};
/*****************************************************************************/
static MMPortSerialAt *
get_dial_port (MMBroadbandModemHuawei *modem,
MMPort *data,
MMPortSerialAt *primary)
{
MMPortSerialAt *dial_port;
/* See if we have a cdc-wdm AT port for the interface */
dial_port = (mm_broadband_modem_huawei_peek_port_at_for_data (
MM_BROADBAND_MODEM_HUAWEI (modem), data));
if (dial_port)
return g_object_ref (dial_port);
/* Otherwise, fallback to using the primary port for dialing */
return g_object_ref (primary);
}
/*****************************************************************************/
/* Connect 3GPP */
@@ -67,12 +86,15 @@ static void
connect_3gpp_context_complete_and_free (Connect3gppContext *ctx)
{
g_simple_async_result_complete_in_idle (ctx->result);
g_object_unref (ctx->cancellable);
g_object_unref (ctx->result);
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
g_object_unref (ctx->self);
g_clear_object (&ctx->data);
g_clear_object (&ctx->primary);
g_slice_free (Connect3gppContext, ctx);
}
@@ -410,7 +432,6 @@ connect_3gpp (MMBroadbandBearer *self,
ctx = g_slice_new0 (Connect3gppContext);
ctx->self = g_object_ref (self);
ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
ctx->data = g_object_ref (data);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
@@ -422,6 +443,9 @@ connect_3gpp (MMBroadbandBearer *self,
g_assert (ctx->self->priv->connect_pending == NULL);
g_assert (ctx->self->priv->disconnect_pending == NULL);
/* Get correct dial port to use */
ctx->primary = get_dial_port (MM_BROADBAND_MODEM_HUAWEI (ctx->modem), ctx->data, primary);
/* Run! */
connect_3gpp_context_step (ctx);
}
@@ -654,7 +678,6 @@ disconnect_3gpp (MMBroadbandBearer *self,
ctx = g_slice_new0 (Disconnect3gppContext);
ctx->self = g_object_ref (self);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
@@ -664,6 +687,9 @@ disconnect_3gpp (MMBroadbandBearer *self,
g_assert (ctx->self->priv->connect_pending == NULL);
g_assert (ctx->self->priv->disconnect_pending == NULL);
/* Get correct dial port to use */
ctx->primary = get_dial_port (MM_BROADBAND_MODEM_HUAWEI (ctx->modem), data, primary);
/* Start! */
disconnect_3gpp_context_step (ctx);
}
+144 -23
View File
@@ -2098,6 +2098,149 @@ create_bearer_for_net_port (CreateBearerContext *ctx)
}
}
static MMPortSerialAt *
peek_port_at_for_data (MMBroadbandModemHuawei *self,
MMPort *port)
{
GUdevClient *client;
GUdevDevice *data_device;
GUdevDevice *data_device_parent = NULL;
GList *cdc_wdm_at_ports = NULL;
GList *l;
MMPortSerialAt *found = NULL;
client = g_udev_client_new (NULL);
data_device = (g_udev_client_query_by_subsystem_and_name (
client,
"net",
mm_port_get_device (port)));
cdc_wdm_at_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
MM_PORT_SUBSYS_USB,
MM_PORT_TYPE_AT,
NULL);
/* Get parent of the data device */
data_device_parent = g_udev_device_get_parent (data_device);
if (!data_device_parent) {
mm_dbg ("Cannot get parent device (%s)", mm_port_get_device (port));
goto out;
}
/* Now walk the list of cdc-wdm AT ports looking for a match */
for (l = cdc_wdm_at_ports; l && !found; l = g_list_next (l)) {
GUdevDevice *cdc_wdm_device;
GUdevDevice *cdc_wdm_device_parent;
g_assert (MM_IS_PORT_SERIAL_AT (l->data));
/* Get udev device for the cdc-wdm port */
cdc_wdm_device = (g_udev_client_query_by_subsystem_and_name (
client,
"usb",
mm_port_get_device (MM_PORT (l->data))));
if (!cdc_wdm_device) {
cdc_wdm_device = (g_udev_client_query_by_subsystem_and_name (
client,
"usbmisc",
mm_port_get_device (MM_PORT (l->data))));
if (!cdc_wdm_device) {
mm_warn ("Couldn't get udev device for cdc-wdm port '%s'",
mm_port_get_device (MM_PORT (l->data)));
continue;
}
}
/* Get parent of the cdc-wdm device */
cdc_wdm_device_parent = g_udev_device_get_parent (cdc_wdm_device);
g_object_unref (cdc_wdm_device);
if (!cdc_wdm_device_parent) {
mm_warn ("Couldn't get udev device parent for cdc-wdm port '%s'",
mm_port_get_device (MM_PORT (l->data)));
continue;
}
if (g_str_equal (g_udev_device_get_sysfs_path (data_device_parent),
g_udev_device_get_sysfs_path (cdc_wdm_device_parent)))
found = MM_PORT_SERIAL_AT (l->data);
g_object_unref (cdc_wdm_device_parent);
}
out:
if (data_device_parent)
g_object_unref (data_device_parent);
if (data_device)
g_object_unref (data_device);
if (client)
g_object_unref (client);
if (cdc_wdm_at_ports)
g_list_free_full (cdc_wdm_at_ports, (GDestroyNotify)g_object_unref);
return found;
}
MMPortSerialAt *
mm_broadband_modem_huawei_peek_port_at_for_data (MMBroadbandModemHuawei *self,
MMPort *port)
{
MMPortSerialAt *found;
g_assert (self->priv->ndisdup_support == FEATURE_SUPPORTED);
found = peek_port_at_for_data (self, port);
if (!found)
mm_warn ("Couldn't find associated cdc-wdm port for 'net/%s'",
mm_port_get_device (port));
return found;
}
static void
ensure_ndisdup_support_checked (MMBroadbandModemHuawei *self,
MMPort *port)
{
GUdevClient *client;
GUdevDevice *data_device;
/* Check NDISDUP support the first time we need it */
if (self->priv->ndisdup_support != FEATURE_SUPPORT_UNKNOWN)
return;
/* First, check for devices which support NDISDUP on any AT port. These
* devices are tagged by udev */
client = g_udev_client_new (NULL);
data_device = (g_udev_client_query_by_subsystem_and_name (
client,
"net",
mm_port_get_device (port)));
if (data_device && g_udev_device_get_property_as_boolean (data_device, "ID_MM_HUAWEI_NDISDUP_SUPPORTED")) {
mm_dbg ("This device (%s) can support ndisdup feature", mm_port_get_device (port));
self->priv->ndisdup_support = FEATURE_SUPPORTED;
goto out;
}
/* Then, look for devices which have both a net port and a cdc-wdm
* AT-capable port. We assume that these devices allow NDISDUP only
* when issued in the cdc-wdm port. */
if (peek_port_at_for_data (self, port)) {
mm_dbg ("This device (%s) can support ndisdup feature on non-serial AT port",
mm_port_get_device (port));
self->priv->ndisdup_support = FEATURE_SUPPORTED;
goto out;
}
mm_dbg ("This device (%s) can not support ndisdup feature", mm_port_get_device (port));
self->priv->ndisdup_support = FEATURE_NOT_SUPPORTED;
out:
if (data_device)
g_object_unref (data_device);
if (client)
g_object_unref (client);
}
static void
huawei_modem_create_bearer (MMIfaceModem *self,
@@ -2118,28 +2261,7 @@ huawei_modem_create_bearer (MMIfaceModem *self,
port = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET);
if (port) {
/* Check NDISDUP support the first time we need it */
if (ctx->self->priv->ndisdup_support == FEATURE_SUPPORT_UNKNOWN) {
GUdevDevice *net_port;
GUdevClient *client;
client = g_udev_client_new (NULL);
net_port = (g_udev_client_query_by_subsystem_and_name (
client,
"net",
mm_port_get_device (port)));
if (net_port && g_udev_device_get_property_as_boolean (net_port, "ID_MM_HUAWEI_NDISDUP_SUPPORTED")) {
mm_dbg ("This device (%s) can support ndisdup feature", mm_port_get_device (port));
ctx->self->priv->ndisdup_support = FEATURE_SUPPORTED;
} else {
mm_dbg ("This device (%s) can not support ndisdup feature", mm_port_get_device (port));
ctx->self->priv->ndisdup_support = FEATURE_NOT_SUPPORTED;
}
if (net_port)
g_object_unref (net_port);
g_object_unref (client);
}
ensure_ndisdup_support_checked (ctx->self, port);
create_bearer_for_net_port (ctx);
return;
}
@@ -2152,7 +2274,6 @@ huawei_modem_create_bearer (MMIfaceModem *self,
ctx);
}
/*****************************************************************************/
/* USSD encode/decode (3GPP-USSD interface) */
@@ -48,4 +48,7 @@ MMBroadbandModemHuawei *mm_broadband_modem_huawei_new (const gchar *device,
guint16 vendor_id,
guint16 product_id);
MMPortSerialAt *mm_broadband_modem_huawei_peek_port_at_for_data (MMBroadbandModemHuawei *self,
MMPort *port);
#endif /* MM_BROADBAND_MODEM_HUAWEI_H */