[RFC PATCH] port-qmi: automatically try to set 802.3 link layer protocol when opening

Aleksander Morgado aleksander at aleksander.es
Mon Jun 8 02:00:02 PDT 2015


We do this as early as possible, because it looks like the WDA operation
isn't allowed if the modem is in use.

Also, this operation is only needed when the CTL Set Data Format command
is ignored by the modem, like the Netgear Aircard 341U.

Last thing, we setup retries because the modem may ignore the requests for
some time.
---

Comments, anyone?

With this change I can reliably use the Netgear Aircard 341U in QMI mode, even
if it takes quite a long time to do the switch to 802.3 and get it exposed in
DBus (more than one minute).

---
 src/mm-port-qmi.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/mm-port-qmi.h |   8 ++
 2 files changed, 242 insertions(+), 2 deletions(-)

diff --git a/src/mm-port-qmi.c b/src/mm-port-qmi.c
index bc5c738..589af78 100644
--- a/src/mm-port-qmi.c
+++ b/src/mm-port-qmi.c
@@ -162,6 +162,214 @@ mm_port_qmi_allocate_client (MMPortQmi *self,
 }

 /*****************************************************************************/
+/* Data format setting
+ *
+ * Up to 5 mins...
+ * the modem will not be exported until this operation finishes
+ */
+
+#define DATA_FORMAT_RETRY_TIMEOUT_SECS 5
+#define DATA_FORMAT_RETRY_NUMBER       60
+
+typedef struct {
+    MMPortQmi *self;
+    QmiClient *wda;
+    guint n_retries;
+    GSimpleAsyncResult *result;
+    GCancellable *cancellable;
+} PortDataFormatContext;
+
+static void
+port_data_format_context_complete_and_free (PortDataFormatContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    if (ctx->cancellable)
+        g_object_unref (ctx->cancellable);
+    if (ctx->wda) {
+        qmi_device_release_client (ctx->self->priv->qmi_device,
+                                   ctx->wda,
+                                   QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID,
+                                   3, NULL, NULL, NULL);
+        g_object_unref (ctx->wda);
+    }
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->self);
+    g_slice_free (PortDataFormatContext, ctx);
+}
+
+static gboolean
+port_qmi_data_format_finish (MMPortQmi *self,
+                             GAsyncResult *res,
+                             GError **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void data_format_context_retry (PortDataFormatContext *ctx);
+
+static gboolean
+schedule_data_format_retry (PortDataFormatContext *ctx)
+{
+    data_format_context_retry (ctx);
+    return FALSE;
+}
+
+static void
+set_data_format_ready (QmiClientWda *client,
+                       GAsyncResult *res,
+                       PortDataFormatContext *ctx)
+{
+    QmiMessageWdaSetDataFormatOutput *output;
+    GError *error = NULL;
+    QmiWdaLinkLayerProtocol link_layer_protocol;
+
+    output = qmi_client_wda_set_data_format_finish (client, res, &error);
+    if (output &&
+        qmi_message_wda_set_data_format_output_get_result (output, &error) &&
+        qmi_message_wda_set_data_format_output_get_link_layer_protocol (output, &link_layer_protocol, &error)) {
+        if (link_layer_protocol == QMI_WDA_LINK_LAYER_PROTOCOL_802_3) {
+            mm_dbg ("net interface is updated with 802.3 link layer protocol");
+            g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+            port_data_format_context_complete_and_free (ctx);
+            return;
+        }
+        mm_dbg ("couldn't setup net interface with 802.3 link layer protocol: request ignored");
+    } else {
+        mm_dbg ("couldn't setup net interface with 802.3 link layer protocol: %s", error->message);
+        g_error_free (error);
+    }
+
+    /* Retry? */
+    ctx->n_retries--;
+    if (ctx->n_retries == 0) {
+        g_simple_async_result_set_error (ctx->result,
+                                         MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+                                         "Couldn't update net interface with 802.3 link layer protocol after %u retries",
+                                         DATA_FORMAT_RETRY_NUMBER);
+        port_data_format_context_complete_and_free (ctx);
+        return;
+    }
+
+    if (output)
+        qmi_message_wda_set_data_format_output_unref (output);
+
+    /* Schedule retry */
+    mm_dbg ("scheduling net interface link layer protocol check... (%u retries left)", ctx->n_retries);
+    g_timeout_add_seconds (DATA_FORMAT_RETRY_TIMEOUT_SECS, (GSourceFunc) schedule_data_format_retry, ctx);
+}
+
+static void
+get_data_format_ready (QmiClientWda *client,
+                       GAsyncResult *res,
+                       PortDataFormatContext *ctx)
+{
+    QmiMessageWdaGetDataFormatOutput *output;
+    GError *error = NULL;
+    QmiWdaLinkLayerProtocol link_layer_protocol;
+    QmiMessageWdaSetDataFormatInput *input;
+
+    output = qmi_client_wda_get_data_format_finish (client, res, &error);
+    if (output &&
+        qmi_message_wda_get_data_format_output_get_result (output, &error) &&
+        qmi_message_wda_get_data_format_output_get_link_layer_protocol (output, &link_layer_protocol, &error)) {
+        /* If we already have 802.3 setup, we're done */
+        if (link_layer_protocol == QMI_WDA_LINK_LAYER_PROTOCOL_802_3) {
+            mm_dbg ("net interface is already setup with 802.3 link layer protocol");
+            qmi_message_wda_get_data_format_output_unref (output);
+            g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+            port_data_format_context_complete_and_free (ctx);
+            return;
+        }
+        mm_dbg ("need to setup interface with 802.3 link layer protocol");
+    } else {
+        mm_dbg ("couldn't check whether link layer protocol was correctly set in the net interface: %s",
+                error->message);
+        g_error_free (error);
+    }
+
+    if (output)
+        qmi_message_wda_get_data_format_output_unref (output);
+
+    /* Try to setup 802.3 link layer protocol */
+    mm_dbg ("setting net interface link layer protocol...");
+    input = qmi_message_wda_set_data_format_input_new ();
+    qmi_message_wda_set_data_format_input_set_link_layer_protocol (input, QMI_WDA_LINK_LAYER_PROTOCOL_802_3, NULL);
+    qmi_client_wda_set_data_format (QMI_CLIENT_WDA (ctx->wda),
+                                    input,
+                                    10,
+                                    ctx->cancellable,
+                                    (GAsyncReadyCallback)set_data_format_ready,
+                                    ctx);
+    qmi_message_wda_set_data_format_input_unref (input);
+}
+
+static void
+data_format_context_retry (PortDataFormatContext *ctx)
+{
+    g_assert (ctx->n_retries > 0);
+
+    /* Query current data format */
+    mm_dbg ("checking net interface link layer protocol...");
+    qmi_client_wda_get_data_format (QMI_CLIENT_WDA (ctx->wda),
+                                    NULL,
+                                    10,
+                                    ctx->cancellable,
+                                    (GAsyncReadyCallback)get_data_format_ready,
+                                    ctx);
+}
+
+static void
+data_format_allocate_wda_client_ready (QmiDevice *qmi_device,
+                                       GAsyncResult *res,
+                                       PortDataFormatContext *ctx)
+{
+    GError *error = NULL;
+
+    ctx->wda = qmi_device_allocate_client_finish (qmi_device, res, &error);
+    if (!ctx->wda) {
+        /* Ignore error and finish operation */
+        mm_dbg ("couldn't allocate WDA client to check whether link layer protocol "
+                "was correctly set in the net interface: %s",
+                error->message);
+        g_error_free (error);
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        port_data_format_context_complete_and_free (ctx);
+        return;
+    }
+
+    data_format_context_retry (ctx);
+}
+
+static void
+port_qmi_data_format (MMPortQmi *self,
+                      GCancellable *cancellable,
+                      GAsyncReadyCallback callback,
+                      gpointer user_data)
+{
+    PortDataFormatContext *ctx;
+
+    g_return_if_fail (MM_IS_PORT_QMI (self));
+
+    ctx = g_slice_new0 (PortDataFormatContext);
+    ctx->self = g_object_ref (self);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             port_qmi_data_format);
+    ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+    ctx->n_retries = DATA_FORMAT_RETRY_NUMBER;
+
+    /* Try to get a WDA client, in order to check the network data format set */
+    qmi_device_allocate_client (self->priv->qmi_device,
+                                QMI_SERVICE_WDA,
+                                QMI_CID_NONE,
+                                10,
+                                ctx->cancellable,
+                                (GAsyncReadyCallback)data_format_allocate_wda_client_ready,
+                                ctx);
+}
+
+/*****************************************************************************/

 typedef struct {
     MMPortQmi *self;
@@ -190,6 +398,20 @@ mm_port_qmi_open_finish (MMPortQmi *self,
 }

 static void
+data_format_ready (MMPortQmi *self,
+                   GAsyncResult *res,
+                   PortOpenContext *ctx)
+{
+    GError *error = NULL;
+
+    if (!port_qmi_data_format_finish (self, res, &error))
+        g_simple_async_result_take_error (ctx->result, error);
+    else
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+    port_open_context_complete_and_free (ctx);
+}
+
+static void
 qmi_device_open_ready (QmiDevice *qmi_device,
                        GAsyncResult *res,
                        PortOpenContext *ctx)
@@ -202,10 +424,20 @@ qmi_device_open_ready (QmiDevice *qmi_device,
     if (!qmi_device_open_finish (qmi_device, res, &error)) {
         g_clear_object (&ctx->self->priv->qmi_device);
         g_simple_async_result_take_error (ctx->result, error);
-    } else
+        port_open_context_complete_and_free (ctx);
+        return;
+    }
+
+    if (!ctx->set_data_format) {
         g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        port_open_context_complete_and_free (ctx);
+        return;
+    }

-    port_open_context_complete_and_free (ctx);
+    port_qmi_data_format (ctx->self,
+                          ctx->cancellable,
+                          (GAsyncReadyCallback) data_format_ready,
+                          ctx);
 }

 static void
diff --git a/src/mm-port-qmi.h b/src/mm-port-qmi.h
index 9c38b0c..b693ce9 100644
--- a/src/mm-port-qmi.h
+++ b/src/mm-port-qmi.h
@@ -82,4 +82,12 @@ QmiClient *mm_port_qmi_get_client  (MMPortQmi *self,
                                     QmiService service,
                                     MMPortQmiFlag flag);

+void     mm_port_qmi_data_format        (MMPortQmi *self,
+                                         GCancellable *cancellable,
+                                         GAsyncReadyCallback callback,
+                                         gpointer user_data);
+gboolean mm_port_qmi_data_format_finish (MMPortQmi *self,
+                                         GAsyncResult *res,
+                                         GError **error);
+
 #endif /* MM_PORT_QMI_H */
--
2.4.2


More information about the ModemManager-devel mailing list