[PATCH 1/3] port-qmi: support new Qualcomm chipsets working always in raw-ip
Aleksander Morgado
aleksander at aleksander.es
Tue Dec 29 08:56:54 PST 2015
The port opening logic is changed completely.
Before this change, the logic would only try 802.3 setting via CTL when the
QmiDevice was being open.
With this new change, instead, we'll use WDA and the new libqmi APIs to query
the link layer protocol expected by both the device and the kernel. If the LLP
matches in both, we assume we're done; if they differ we'll try to update the
LLP expected by the kernel to the one setup in WDA. This change will allow us
to run with the modem using raw-ip if that is what WDA reports by default.
---
src/mm-port-qmi.c | 351 +++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 294 insertions(+), 57 deletions(-)
diff --git a/src/mm-port-qmi.c b/src/mm-port-qmi.c
index cca71b0..fa723db 100644
--- a/src/mm-port-qmi.c
+++ b/src/mm-port-qmi.c
@@ -163,22 +163,53 @@ mm_port_qmi_allocate_client (MMPortQmi *self,
/*****************************************************************************/
+typedef enum {
+ PORT_OPEN_STEP_FIRST,
+ PORT_OPEN_STEP_CHECK_OPENING,
+ PORT_OPEN_STEP_CHECK_ALREADY_OPEN,
+ PORT_OPEN_STEP_DEVICE_NEW,
+ PORT_OPEN_STEP_OPEN_WITHOUT_DATA_FORMAT,
+ PORT_OPEN_STEP_GET_KERNEL_DATA_FORMAT,
+ PORT_OPEN_STEP_ALLOCATE_WDA_CLIENT,
+ PORT_OPEN_STEP_GET_WDA_DATA_FORMAT,
+ PORT_OPEN_STEP_CHECK_DATA_FORMAT,
+ PORT_OPEN_STEP_SET_KERNEL_DATA_FORMAT,
+ PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT,
+ PORT_OPEN_STEP_LAST
+} PortOpenStep;
+
typedef struct {
MMPortQmi *self;
- gboolean set_data_format;
GSimpleAsyncResult *result;
GCancellable *cancellable;
+ QmiDevice *device;
+ QmiClient *wda;
+ GError *error;
+ PortOpenStep step;
+ gboolean set_data_format;
+ QmiDeviceExpectedDataFormat kernel_data_format;
+ QmiWdaLinkLayerProtocol llp;
} PortOpenContext;
static void
port_open_context_complete_and_free (PortOpenContext *ctx)
{
g_simple_async_result_complete_in_idle (ctx->result);
+ if (ctx->wda) {
+ g_assert (ctx->device);
+ qmi_device_release_client (ctx->device,
+ ctx->wda,
+ QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID,
+ 3, NULL, NULL, NULL);
+ g_object_unref (ctx->wda);
+ }
+ if (ctx->device)
+ g_object_unref (ctx->device);
if (ctx->cancellable)
g_object_unref (ctx->cancellable);
g_object_unref (ctx->result);
g_object_unref (ctx->self);
- g_free (ctx);
+ g_slice_free (PortOpenContext, ctx);
}
gboolean
@@ -189,23 +220,77 @@ mm_port_qmi_open_finish (MMPortQmi *self,
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
+static void port_open_context_step (PortOpenContext *ctx);
+
static void
-qmi_device_open_ready (QmiDevice *qmi_device,
+qmi_device_open_second_ready (QmiDevice *qmi_device,
+ GAsyncResult *res,
+ PortOpenContext *ctx)
+{
+ qmi_device_open_finish (qmi_device, res, &ctx->error);
+
+ /* In both error and success, we go to last step */
+ ctx->step = PORT_OPEN_STEP_LAST;
+ port_open_context_step (ctx);
+}
+
+static void
+get_data_format_ready (QmiClientWda *client,
GAsyncResult *res,
PortOpenContext *ctx)
{
- GError *error = NULL;
+ QmiMessageWdaGetDataFormatOutput *output;
+
+ output = qmi_client_wda_get_data_format_finish (client, res, NULL);
+ if (!output ||
+ !qmi_message_wda_get_data_format_output_get_result (output, NULL) ||
+ !qmi_message_wda_get_data_format_output_get_link_layer_protocol (output, &ctx->llp, NULL))
+ /* If loading WDA data format fails, fallback to 802.3 requested via CTL */
+ ctx->step = PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT;
+ else
+ /* Go on to next step */
+ ctx->step++;
+
+ if (output)
+ qmi_message_wda_get_data_format_output_unref (output);
+
+ port_open_context_step (ctx);
+}
- /* Reset the opening flag */
- ctx->self->priv->opening = FALSE;
+static void
+allocate_client_wda_ready (QmiDevice *device,
+ GAsyncResult *res,
+ PortOpenContext *ctx)
+{
+ ctx->wda = qmi_device_allocate_client_finish (device, res, NULL);
+ if (!ctx->wda) {
+ /* If no WDA supported, then we just fallback to reopening explicitly
+ * requesting 802.3 in the CTL service. */
+ ctx->step = PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT;
+ port_open_context_step (ctx);
+ return;
+ }
- 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
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ /* Go on to next step */
+ ctx->step++;
+ port_open_context_step (ctx);
+}
- port_open_context_complete_and_free (ctx);
+static void
+qmi_device_open_first_ready (QmiDevice *qmi_device,
+ GAsyncResult *res,
+ PortOpenContext *ctx)
+{
+ if (!qmi_device_open_finish (qmi_device, res, &ctx->error))
+ /* Error opening the device */
+ ctx->step = PORT_OPEN_STEP_LAST;
+ else if (!ctx->set_data_format)
+ /* If not setting data format, we're done */
+ ctx->step = PORT_OPEN_STEP_LAST;
+ else
+ /* Go on to next step */
+ ctx->step++;
+ port_open_context_step (ctx);
}
static void
@@ -213,26 +298,203 @@ qmi_device_new_ready (GObject *unused,
GAsyncResult *res,
PortOpenContext *ctx)
{
- GError *error = NULL;
- QmiDeviceOpenFlags flags = (QMI_DEVICE_OPEN_FLAGS_VERSION_INFO | QMI_DEVICE_OPEN_FLAGS_PROXY);
+ /* Store the device in the context until the operation is fully done,
+ * so that we return IN_PROGRESS errors until we finish this async
+ * operation. */
+ ctx->device = qmi_device_new_finish (res, &ctx->error);
+ if (!ctx->device)
+ /* Error creating the device */
+ ctx->step = PORT_OPEN_STEP_LAST;
+ else
+ /* Go on to next step */
+ ctx->step++;
+ port_open_context_step (ctx);
+}
- ctx->self->priv->qmi_device = qmi_device_new_finish (res, &error);
- if (!ctx->self->priv->qmi_device) {
- g_simple_async_result_take_error (ctx->result, error);
- port_open_context_complete_and_free (ctx);
+static void
+port_open_context_step (PortOpenContext *ctx)
+{
+ switch (ctx->step) {
+ case PORT_OPEN_STEP_FIRST:
+ mm_dbg ("Opening QMI device...");
+ ctx->step++;
+ /* Fall down to next step */
+
+ case PORT_OPEN_STEP_CHECK_OPENING:
+ mm_dbg ("Checking if QMI device already opening...");
+ if (ctx->self->priv->opening) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "QMI device already being opened");
+ port_open_context_complete_and_free (ctx);
+ return;
+ }
+ ctx->step++;
+ /* Fall down to next step */
+
+ case PORT_OPEN_STEP_CHECK_ALREADY_OPEN:
+ mm_dbg ("Checking if QMI device already open...");
+ if (ctx->self->priv->qmi_device) {
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ port_open_context_complete_and_free (ctx);
+ return;
+ }
+ ctx->step++;
+ /* Fall down to next step */
+
+ case PORT_OPEN_STEP_DEVICE_NEW: {
+ GFile *file;
+ gchar *fullpath;
+
+ fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (ctx->self)));
+ file = g_file_new_for_path (fullpath);
+
+ /* We flag in this point that we're opening. From now on, if we stop
+ * for whatever reason, we should clear this flag. We do this by ensuring
+ * that all callbacks go through the LAST step for completing. */
+ ctx->self->priv->opening = TRUE;
+
+ mm_dbg ("Creating QMI device...");
+ qmi_device_new (file,
+ ctx->cancellable,
+ (GAsyncReadyCallback) qmi_device_new_ready,
+ ctx);
+
+ g_free (fullpath);
+ g_object_unref (file);
return;
}
- if (ctx->set_data_format)
- flags |= (QMI_DEVICE_OPEN_FLAGS_NET_802_3 | QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER);
+ case PORT_OPEN_STEP_OPEN_WITHOUT_DATA_FORMAT:
+ /* Now open the QMI device without any data format CTL flag */
+ mm_dbg ("Opening device without data format update...");
+ qmi_device_open (ctx->device,
+ (QMI_DEVICE_OPEN_FLAGS_VERSION_INFO |
+ QMI_DEVICE_OPEN_FLAGS_PROXY),
+ 10,
+ ctx->cancellable,
+ (GAsyncReadyCallback) qmi_device_open_first_ready,
+ ctx);
+ return;
+
+ case PORT_OPEN_STEP_GET_KERNEL_DATA_FORMAT:
+ mm_dbg ("Querying kernel data format...");
+ /* Try to gather expected data format from the sysfs file */
+ ctx->kernel_data_format = qmi_device_get_expected_data_format (ctx->device, NULL);
+ /* If data format cannot be retrieved, we fallback to 802.3 via CTL */
+ if (ctx->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN) {
+ ctx->step = PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT;
+ port_open_context_step (ctx);
+ return;
+ }
+ ctx->step++;
+ /* Fall down to next step */
+
+ case PORT_OPEN_STEP_ALLOCATE_WDA_CLIENT:
+ /* Allocate WDA client */
+ mm_dbg ("Allocating WDA client...");
+ qmi_device_allocate_client (ctx->device,
+ QMI_SERVICE_WDA,
+ QMI_CID_NONE,
+ 10,
+ ctx->cancellable,
+ (GAsyncReadyCallback) allocate_client_wda_ready,
+ ctx);
+ return;
+
+ case PORT_OPEN_STEP_GET_WDA_DATA_FORMAT:
+ /* If we have WDA client, query current data format */
+ g_assert (ctx->wda);
+ mm_dbg ("Querying device data format...");
+ qmi_client_wda_get_data_format (QMI_CLIENT_WDA (ctx->wda),
+ NULL,
+ 10,
+ ctx->cancellable,
+ (GAsyncReadyCallback) get_data_format_ready,
+ ctx);
+ return;
+
+ case PORT_OPEN_STEP_CHECK_DATA_FORMAT:
+ /* We now have the WDA data format and the kernel data format, if they're
+ * equal, we're done */
+ mm_dbg ("Checking data format: kernel %s, device %s",
+ qmi_device_expected_data_format_get_string (ctx->kernel_data_format),
+ qmi_wda_link_layer_protocol_get_string (ctx->llp));
+ if ((ctx->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3 &&
+ ctx->llp == QMI_WDA_LINK_LAYER_PROTOCOL_802_3) ||
+ (ctx->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP &&
+ ctx->llp == QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP)) {
+ ctx->step = PORT_OPEN_STEP_LAST;
+ port_open_context_step (ctx);
+ return;
+ }
+
+ ctx->step++;
+ /* Fall down to next step */
+
+ case PORT_OPEN_STEP_SET_KERNEL_DATA_FORMAT:
+ /* Update the data format to be expected by the kernel */
+ mm_dbg ("Updating kernel data format: %s", qmi_wda_link_layer_protocol_get_string (ctx->llp));
+ if (ctx->llp == QMI_WDA_LINK_LAYER_PROTOCOL_802_3)
+ ctx->kernel_data_format = QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3;
+ else if (ctx->llp == QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP)
+ ctx->kernel_data_format = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP;
+ else
+ g_assert_not_reached ();
+
+ /* Regardless of the output, we're done after this action */
+ qmi_device_set_expected_data_format (ctx->device,
+ ctx->kernel_data_format,
+ &ctx->error);
+ ctx->step = PORT_OPEN_STEP_LAST;
+ port_open_context_step (ctx);
+ return;
- /* Now open the QMI device */
- qmi_device_open (ctx->self->priv->qmi_device,
- flags,
- 10,
- ctx->cancellable,
- (GAsyncReadyCallback)qmi_device_open_ready,
- ctx);
+ case PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT:
+ /* Need to reopen setting 802.3 using CTL */
+ mm_dbg ("Closing device to reopen it right away...");
+ if (!qmi_device_close (ctx->device, &ctx->error)) {
+ mm_warn ("Couldn't close QMI device to reopen it");
+ ctx->step = PORT_OPEN_STEP_LAST;
+ port_open_context_step (ctx);
+ return;
+ }
+
+ mm_dbg ("Reopening device with data format...");
+ qmi_device_open (ctx->device,
+ (QMI_DEVICE_OPEN_FLAGS_VERSION_INFO |
+ QMI_DEVICE_OPEN_FLAGS_PROXY |
+ QMI_DEVICE_OPEN_FLAGS_NET_802_3 |
+ QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER),
+ 10,
+ ctx->cancellable,
+ (GAsyncReadyCallback) qmi_device_open_second_ready,
+ ctx);
+ return;
+
+ case PORT_OPEN_STEP_LAST:
+ mm_dbg ("QMI port open operation finished");
+
+ /* Reset opening flag */
+ ctx->self->priv->opening = FALSE;
+
+ if (ctx->error) {
+ /* Propagate error */
+ if (ctx->device)
+ qmi_device_close (ctx->device, NULL);
+ g_simple_async_result_take_error (ctx->result, ctx->error);
+ ctx->error = NULL;
+ } else {
+ /* Store device in private info */
+ g_assert (ctx->device);
+ g_assert (!ctx->self->priv->qmi_device);
+ ctx->self->priv->qmi_device = g_object_ref (ctx->device);
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ }
+ port_open_context_complete_and_free (ctx);
+ return;
+ }
}
void
@@ -242,48 +504,23 @@ mm_port_qmi_open (MMPortQmi *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GFile *file;
- gchar *fullpath;
PortOpenContext *ctx;
g_return_if_fail (MM_IS_PORT_QMI (self));
- ctx = g_new0 (PortOpenContext, 1);
+ ctx = g_slice_new0 (PortOpenContext);
ctx->self = g_object_ref (self);
+ ctx->step = PORT_OPEN_STEP_FIRST;
ctx->set_data_format = set_data_format;
+ ctx->kernel_data_format = QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN;
+ ctx->llp = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN;
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_port_qmi_open);
ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
- if (self->priv->opening) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "QMI device already being opened");
- port_open_context_complete_and_free (ctx);
- return;
- }
-
- if (self->priv->qmi_device) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- port_open_context_complete_and_free (ctx);
- return;
- }
-
- fullpath = g_strdup_printf ("/dev/%s",
- mm_port_get_device (MM_PORT (self)));
- file = g_file_new_for_path (fullpath);
-
- self->priv->opening = TRUE;
- qmi_device_new (file,
- ctx->cancellable,
- (GAsyncReadyCallback)qmi_device_new_ready,
- ctx);
-
- g_free (fullpath);
- g_object_unref (file);
+ port_open_context_step (ctx);
}
gboolean
--
2.6.4
More information about the ModemManager-devel
mailing list