aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mm-bearer-qmi.c34
-rw-r--r--src/mm-port-qmi.c861
-rw-r--r--src/mm-port-qmi.h15
3 files changed, 691 insertions, 219 deletions
diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c
index a31ae860..53a31f37 100644
--- a/src/mm-bearer-qmi.c
+++ b/src/mm-bearer-qmi.c
@@ -406,6 +406,7 @@ static void cleanup_event_report_unsolicited_events (MMBearerQmi *self,
typedef enum {
CONNECT_STEP_FIRST,
CONNECT_STEP_OPEN_QMI_PORT,
+ CONNECT_STEP_SETUP_DATA_FORMAT,
CONNECT_STEP_IP_METHOD,
CONNECT_STEP_IPV4,
CONNECT_STEP_WDS_CLIENT_IPV4,
@@ -1284,6 +1285,30 @@ qmi_port_allocate_client_ready (MMPortQmi *qmi,
}
static void
+setup_data_format_ready (MMPortQmi *qmi,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_qmi_setup_data_format_finish (qmi, res, &error)) {
+ /* a failure here could indicate no support for WDA Set Data Format,
+ * if so, just go on with the plain CTL based support */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ complete_connect (task, NULL, g_steal_pointer (&error));
+ return;
+ }
+ }
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
qmi_port_open_ready (MMPortQmi *qmi,
GAsyncResult *res,
GTask *task)
@@ -1359,12 +1384,19 @@ connect_context_step (GTask *task)
ctx->step++;
/* fall through */
+ case CONNECT_STEP_SETUP_DATA_FORMAT:
+ mm_port_qmi_setup_data_format (ctx->qmi,
+ MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT,
+ (GAsyncReadyCallback) setup_data_format_ready,
+ task);
+ return;
+
case CONNECT_STEP_IP_METHOD:
/* Once the QMI port is open, we decide the IP method we're going
* to request. If the LLP is raw-ip, we force Static IP, because not
* all DHCP clients support the raw-ip interfaces; otherwise default
* to DHCP as always. */
- if (mm_port_qmi_llp_is_raw_ip (ctx->qmi))
+ if (mm_port_qmi_get_link_layer_protocol (ctx->qmi) == QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP)
ctx->ip_method = MM_BEARER_IP_METHOD_STATIC;
else
ctx->ip_method = MM_BEARER_IP_METHOD_DHCP;
diff --git a/src/mm-port-qmi.c b/src/mm-port-qmi.c
index cc4b954f..a5f065aa 100644
--- a/src/mm-port-qmi.c
+++ b/src/mm-port-qmi.c
@@ -28,20 +28,24 @@
G_DEFINE_TYPE (MMPortQmi, mm_port_qmi, MM_TYPE_PORT)
typedef struct {
- QmiService service;
- QmiClient *client;
- MMPortQmiFlag flag;
+ QmiService service;
+ QmiClient *client;
+ MMPortQmiFlag flag;
} ServiceInfo;
struct _MMPortQmiPrivate {
gboolean in_progress;
QmiDevice *qmi_device;
GList *services;
- gboolean llp_is_raw_ip;
/* endpoint info */
gulong endpoint_info_signal_id;
QmiDataEndpointType endpoint_type;
gint endpoint_interface_number;
+ /* kernel data format */
+ QmiDeviceExpectedDataFormat kernel_data_format;
+ /* wda settings */
+ gboolean wda_unsupported;
+ QmiWdaLinkLayerProtocol llp;
};
/*****************************************************************************/
@@ -270,10 +274,584 @@ mm_port_qmi_allocate_client (MMPortQmi *self,
/*****************************************************************************/
+QmiWdaLinkLayerProtocol
+mm_port_qmi_get_link_layer_protocol (MMPortQmi *self)
+{
+ return self->priv->llp;
+}
+
+/*****************************************************************************/
+
+static QmiDeviceExpectedDataFormat
+load_kernel_data_format_current (MMPortQmi *self,
+ QmiDevice *device)
+{
+ QmiDeviceExpectedDataFormat value;
+
+ /* For any driver other than qmi_wwan, assume raw-ip */
+ if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_USBMISC)
+ return QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP;
+
+ /* If the expected data format is unknown, it means the kernel in use
+ * doesn't have support for querying it; therefore it's 802.3 */
+ value = qmi_device_get_expected_data_format (device, NULL);
+ if (value == QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN)
+ value = QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3;
+
+ return value;
+}
+
+static void
+load_kernel_data_format_capabilities (MMPortQmi *self,
+ QmiDevice *device,
+ gboolean *supports_802_3,
+ gboolean *supports_raw_ip)
+{
+ /* For any driver other than qmi_wwan, assume raw-ip */
+ if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_USBMISC) {
+ *supports_802_3 = FALSE;
+ *supports_raw_ip = TRUE;
+ return;
+ }
+
+ *supports_802_3 = TRUE;
+ *supports_raw_ip = qmi_device_check_expected_data_format_supported (device,
+ QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP,
+ NULL);}
+
+/*****************************************************************************/
+
+typedef struct {
+ QmiDeviceExpectedDataFormat kernel_data_format;
+ QmiWdaLinkLayerProtocol wda_llp;
+} DataFormatCombination;
+
+static const DataFormatCombination data_format_combinations[] = {
+ { QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP },
+ { QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3, QMI_WDA_LINK_LAYER_PROTOCOL_802_3 },
+};
+
+typedef enum {
+ INTERNAL_SETUP_DATA_FORMAT_STEP_FIRST,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_KERNEL_DATA_FORMAT_CAPABILITIES,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_RETRY,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_KERNEL_DATA_FORMAT_CURRENT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_WDA_CLIENT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_GET_WDA_DATA_FORMAT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_QUERY_DONE,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_CHECK_DATA_FORMAT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_WDA_DATA_FORMAT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_KERNEL_DATA_FORMAT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_LAST,
+} InternalSetupDataFormatStep;
+
+typedef struct {
+ QmiDevice *device;
+ MMPortQmiSetupDataFormatAction action;
+
+ InternalSetupDataFormatStep step;
+ gboolean use_endpoint;
+ gint data_format_combination_i;
+
+ /* configured kernel data format, mainly when using qmi_wwan */
+ QmiDeviceExpectedDataFormat kernel_data_format_current;
+ QmiDeviceExpectedDataFormat kernel_data_format_requested;
+ gboolean kernel_data_format_802_3_supported;
+ gboolean kernel_data_format_raw_ip_supported;
+
+ /* configured device data format */
+ QmiClient *wda;
+ QmiWdaLinkLayerProtocol wda_llp_current;
+ QmiWdaLinkLayerProtocol wda_llp_requested;
+} InternalSetupDataFormatContext;
+
+static void
+internal_setup_data_format_context_free (InternalSetupDataFormatContext *ctx)
+{
+ if (ctx->wda && ctx->device)
+ qmi_device_release_client (ctx->device,
+ ctx->wda,
+ QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID,
+ 3, NULL, NULL, NULL);
+ g_clear_object (&ctx->wda);
+ g_clear_object (&ctx->device);
+ g_slice_free (InternalSetupDataFormatContext, ctx);
+}
+
+static gboolean
+internal_setup_data_format_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ QmiDeviceExpectedDataFormat *out_kernel_data_format,
+ QmiWdaLinkLayerProtocol *out_llp,
+ GError **error)
+{
+ InternalSetupDataFormatContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ *out_kernel_data_format = ctx->kernel_data_format_current;
+ *out_llp = ctx->wda_llp_current;
+ return TRUE;
+}
+
+static void internal_setup_data_format_context_step (GTask *task);
+
+static void
+sync_kernel_data_format (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ mm_obj_dbg (self, "Updating kernel expected data format: %s -> %s",
+ qmi_device_expected_data_format_get_string (ctx->kernel_data_format_current),
+ qmi_device_expected_data_format_get_string (ctx->kernel_data_format_requested));
+
+ if (!qmi_device_set_expected_data_format (ctx->device,
+ ctx->kernel_data_format_requested,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* request reload */
+ ctx->kernel_data_format_current = QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN;
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+set_data_format_ready (QmiClientWda *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InternalSetupDataFormatContext *ctx;
+ g_autoptr(QmiMessageWdaSetDataFormatOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_wda_set_data_format_finish (client, res, &error);
+ if (!output || !qmi_message_wda_set_data_format_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* request reload */
+ ctx->wda_llp_current = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN;
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+sync_wda_data_format (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ g_autoptr(QmiMessageWdaSetDataFormatInput) input = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->wda_llp_current != ctx->wda_llp_requested)
+ mm_obj_dbg (self, "Updating device link layer protocol: %s -> %s",
+ qmi_wda_link_layer_protocol_get_string (ctx->wda_llp_current),
+ qmi_wda_link_layer_protocol_get_string (ctx->wda_llp_requested));
+
+ input = qmi_message_wda_set_data_format_input_new ();
+ qmi_message_wda_set_data_format_input_set_link_layer_protocol (input, ctx->wda_llp_requested, NULL);
+ qmi_message_wda_set_data_format_input_set_uplink_data_aggregation_protocol (input, QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED, NULL);
+ qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_protocol (input, QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED, NULL);
+ if (ctx->use_endpoint)
+ qmi_message_wda_set_data_format_input_set_endpoint_info (input, self->priv->endpoint_type, self->priv->endpoint_interface_number, NULL);
+
+ qmi_client_wda_set_data_format (QMI_CLIENT_WDA (ctx->wda),
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) set_data_format_ready,
+ task);
+}
+
+static gboolean
+setup_data_format_completed (GTask *task)
+{
+ InternalSetupDataFormatContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ /* check whether the current and requested ones are the same */
+ if ((ctx->kernel_data_format_current == ctx->kernel_data_format_requested) &&
+ (ctx->wda_llp_current == ctx->wda_llp_requested)) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+check_data_format (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ gboolean first_iteration;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ first_iteration = (ctx->data_format_combination_i < 0);
+ if (!first_iteration && setup_data_format_completed (task))
+ return;
+
+ /* go on to the next supported combination */
+ for (++ctx->data_format_combination_i;
+ ctx->data_format_combination_i <= (gint)G_N_ELEMENTS (data_format_combinations);
+ ctx->data_format_combination_i++) {
+ const DataFormatCombination *combination;
+
+ combination = &data_format_combinations[ctx->data_format_combination_i];
+
+ if ((combination->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3) &&
+ !ctx->kernel_data_format_802_3_supported)
+ continue;
+ if ((combination->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP) &&
+ !ctx->kernel_data_format_raw_ip_supported)
+ continue;
+
+ mm_obj_dbg (self, "selected data format setup:");
+ mm_obj_dbg (self, " kernel format: %s", qmi_device_expected_data_format_get_string (combination->kernel_data_format));
+ mm_obj_dbg (self, " link layer protocol: %s", qmi_wda_link_layer_protocol_get_string (combination->wda_llp));
+
+ ctx->kernel_data_format_requested = combination->kernel_data_format;
+ ctx->wda_llp_requested = combination->wda_llp;
+
+ if (first_iteration && setup_data_format_completed (task))
+ return;
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No more data format combinations supported");
+ g_object_unref (task);
+}
+
+static gboolean
+process_data_format_output (MMPortQmi *self,
+ QmiMessageWdaGetDataFormatOutput *output,
+ InternalSetupDataFormatContext *ctx,
+ GError **error)
+{
+ /* Let's consider the lack o the LLP TLV a hard error; it really would be strange
+ * a module supporting WDA Get Data Format but not containing the LLP info */
+ if (!qmi_message_wda_get_data_format_output_get_link_layer_protocol (output, &ctx->wda_llp_current, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+get_data_format_ready (QmiClientWda *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ g_autoptr(QmiMessageWdaGetDataFormatOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_wda_get_data_format_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_wda_get_data_format_output_get_result (output, &error) ||
+ !process_data_format_output (self, output, ctx, &error)) {
+ /* A 'missing argument' error when querying data format is seen in new
+ * devices like the Quectel RM500Q, requiring the 'endpoint info' TLV.
+ * When this happens, retry the step with the missing TLV.
+ *
+ * Note that this is not an additional step, we're still in the
+ * GET_WDA_DATA_FORMAT step.
+ */
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_MISSING_ARGUMENT) &&
+ (self->priv->endpoint_type != QMI_DATA_ENDPOINT_TYPE_UNDEFINED)) {
+ /* retry same step with endpoint info */
+ ctx->use_endpoint = TRUE;
+ internal_setup_data_format_context_step (task);
+ return;
+ }
+
+ /* otherwise, fatal */
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+allocate_client_wda_ready (QmiDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InternalSetupDataFormatContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->wda = qmi_device_allocate_client_finish (device, res, &error);
+ if (!ctx->wda) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+internal_setup_data_format_context_step (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_FIRST:
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_KERNEL_DATA_FORMAT_CAPABILITIES:
+ /* Load kernel data format capabilities, only on first loop iteration */
+ load_kernel_data_format_capabilities (self,
+ ctx->device,
+ &ctx->kernel_data_format_802_3_supported,
+ &ctx->kernel_data_format_raw_ip_supported);
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_RETRY:
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_KERNEL_DATA_FORMAT_CURRENT:
+ /* Only reload kernel data format if it was updated or on first loop */
+ if (ctx->kernel_data_format_current == QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN) {
+ ctx->kernel_data_format_current = load_kernel_data_format_current (self, ctx->device);
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_WDA_CLIENT:
+ /* Only allocate new WDA client on first loop */
+ if (ctx->data_format_combination_i < 0) {
+ g_assert (!ctx->wda);
+ qmi_device_allocate_client (ctx->device,
+ QMI_SERVICE_WDA,
+ QMI_CID_NONE,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) allocate_client_wda_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_GET_WDA_DATA_FORMAT:
+ /* Only reload WDA data format if it was updated or on first loop */
+ if (ctx->wda_llp_current == QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN) {
+ g_autoptr(QmiMessageWdaGetDataFormatInput) input = NULL;
+
+ if (ctx->use_endpoint) {
+ input = qmi_message_wda_get_data_format_input_new ();
+ qmi_message_wda_get_data_format_input_set_endpoint_info (input,
+ self->priv->endpoint_type,
+ self->priv->endpoint_interface_number,
+ NULL);
+ }
+ qmi_client_wda_get_data_format (QMI_CLIENT_WDA (ctx->wda),
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) get_data_format_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_QUERY_DONE:
+ mm_obj_dbg (self, "current data format setup:");
+ mm_obj_dbg (self, " kernel format: %s", qmi_device_expected_data_format_get_string (ctx->kernel_data_format_current));
+ mm_obj_dbg (self, " link layer protocol: %s", qmi_wda_link_layer_protocol_get_string (ctx->wda_llp_current));
+
+ if (ctx->action == MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_CHECK_DATA_FORMAT:
+ /* This step is the one that may complete the async operation
+ * successfully */
+ check_data_format (task);
+ return;
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_WDA_DATA_FORMAT:
+ if (ctx->wda_llp_current != ctx->wda_llp_requested) {
+ sync_wda_data_format (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_KERNEL_DATA_FORMAT:
+ if (ctx->kernel_data_format_current != ctx->kernel_data_format_requested) {
+ sync_kernel_data_format (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_LAST:
+ /* jump back to first step to reload current state after
+ * the updates have been done */
+ ctx->step = INTERNAL_SETUP_DATA_FORMAT_STEP_RETRY;
+ internal_setup_data_format_context_step (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+internal_setup_data_format (MMPortQmi *self,
+ QmiDevice *device,
+ MMPortQmiSetupDataFormatAction action,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InternalSetupDataFormatContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Port must be open to setup data format");
+ g_object_unref (task);
+ return;
+ }
+
+ if (self->priv->wda_unsupported) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Setting up data format is not supported");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (InternalSetupDataFormatContext);
+ ctx->device = g_object_ref (device);
+ ctx->action = action;
+ ctx->step = INTERNAL_SETUP_DATA_FORMAT_STEP_FIRST;
+ ctx->data_format_combination_i = -1;
+ ctx->kernel_data_format_current = QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN;
+ ctx->kernel_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN;
+ ctx->wda_llp_current = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN;
+ ctx->wda_llp_requested = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) internal_setup_data_format_context_free);
+
+ internal_setup_data_format_context_step (task);
+}
+
+/*****************************************************************************/
+
gboolean
-mm_port_qmi_llp_is_raw_ip (MMPortQmi *self)
+mm_port_qmi_setup_data_format_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error)
{
- return self->priv->llp_is_raw_ip;
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+internal_setup_data_format_ready (MMPortQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!internal_setup_data_format_finish (self,
+ res,
+ &self->priv->kernel_data_format,
+ &self->priv->llp,
+ &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_port_qmi_setup_data_format (MMPortQmi *self,
+ MMPortQmiSetupDataFormatAction action,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ /* External calls are never query */
+ g_assert (action != MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY);
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->qmi_device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port not open");
+ g_object_unref (task);
+ return;
+ }
+
+ if (self->priv->wda_unsupported) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting up data format is unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ /* call internal method with the already open QmiDevice */
+ internal_setup_data_format (self,
+ self->priv->qmi_device,
+ action,
+ (GAsyncReadyCallback)internal_setup_data_format_ready,
+ task);
}
/*****************************************************************************/
@@ -284,11 +862,7 @@ typedef enum {
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_SYNC_DATA_FORMAT,
+ PORT_OPEN_STEP_SETUP_DATA_FORMAT,
PORT_OPEN_STEP_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT,
PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT,
PORT_OPEN_STEP_LAST
@@ -296,24 +870,16 @@ typedef enum {
typedef struct {
QmiDevice *device;
- QmiClient *wda;
GError *error;
PortOpenStep step;
gboolean set_data_format;
QmiDeviceExpectedDataFormat kernel_data_format;
- QmiWdaLinkLayerProtocol llp;
} PortOpenContext;
static void
port_open_context_free (PortOpenContext *ctx)
{
g_assert (!ctx->error);
- if (ctx->wda && ctx->device)
- qmi_device_release_client (ctx->device,
- ctx->wda,
- QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID,
- 3, NULL, NULL, NULL);
- g_clear_object (&ctx->wda);
g_clear_object (&ctx->device);
g_slice_free (PortOpenContext, ctx);
}
@@ -372,14 +938,17 @@ qmi_device_open_second_ready (QmiDevice *qmi_device,
if (qmi_device_open_finish (qmi_device, res, &ctx->error)) {
/* If the open with CTL data format is sucessful, update */
+ self->priv->kernel_data_format = ctx->kernel_data_format;
if (ctx->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP)
- self->priv->llp_is_raw_ip = TRUE;
+ self->priv->llp = QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP;
+ else if (ctx->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3)
+ self->priv->llp = QMI_WDA_LINK_LAYER_PROTOCOL_802_3;
else
- self->priv->llp_is_raw_ip = FALSE;
+ g_assert_not_reached ();
}
/* In both error and success, we go to last step */
- ctx->step = PORT_OPEN_STEP_LAST;
+ ctx->step++;
port_open_step (task);
}
@@ -403,87 +972,28 @@ qmi_device_close_to_reopen_ready (QmiDevice *qmi_device,
}
static void
-set_data_format_ready (QmiClientWda *client,
- GAsyncResult *res,
- GTask *task)
+open_internal_setup_data_format_ready (MMPortQmi *self,
+ GAsyncResult *res,
+ GTask *task)
{
- MMPortQmi *self;
- PortOpenContext *ctx;
- g_autoptr(QmiMessageWdaSetDataFormatOutput) output = NULL;
- g_autoptr(GError) error = NULL;
+ PortOpenContext *ctx;
+ g_autoptr(GError) error = NULL;
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
+ ctx = g_task_get_task_data (task);
- output = qmi_client_wda_set_data_format_finish (client, res, &error);
- if (!output || !qmi_message_wda_set_data_format_output_get_result (output, &error)) {
- mm_obj_warn (self, "Couldn't set data format: %s", error->message);
- /* If setting WDA data format fails, fallback to LLP requested via CTL */
- ctx->step = PORT_OPEN_STEP_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT;
+ if (!internal_setup_data_format_finish (self,
+ res,
+ &self->priv->kernel_data_format,
+ &self->priv->llp,
+ &error)) {
+ /* Continue with fallback to LLP requested via CTL */
+ mm_obj_warn (self, "Couldn't setup data format: %s", error->message);
+ self->priv->wda_unsupported = TRUE;
+ ctx->step++;
} else {
- self->priv->llp_is_raw_ip = TRUE;
+ /* on success, we're done */
ctx->step = PORT_OPEN_STEP_LAST;
}
-
- port_open_step (task);
-}
-
-static void
-get_data_format_ready (QmiClientWda *client,
- GAsyncResult *res,
- GTask *task)
-{
- MMPortQmi *self;
- PortOpenContext *ctx;
- g_autoptr(QmiMessageWdaGetDataFormatOutput) output = NULL;
- g_autoptr(GError) error = NULL;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
-
- output = qmi_client_wda_get_data_format_finish (client, res, NULL);
- 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, &ctx->llp, NULL)) {
- /* A 'missing argument' error when querying data format is seen in new
- * devices like the Quectel RM500Q, requiring the 'endpoint info' TLV.
- * When this happens, assume the device supports only raw-ip and be done
- * with it. */
- if (error && g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_MISSING_ARGUMENT)) {
- mm_obj_dbg (self, "Querying data format failed: '%s', assuming raw-ip is only supported", error->message);
- ctx->llp = QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP;
- ctx->step++;
- } else {
- /* If loading WDA data format fails, fallback to 802.3 requested via CTL */
- ctx->step = PORT_OPEN_STEP_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT;
- }
- } else
- /* Go on to next step */
- ctx->step++;
-
- port_open_step (task);
-}
-
-static void
-allocate_client_wda_ready (QmiDevice *device,
- GAsyncResult *res,
- GTask *task)
-{
- PortOpenContext *ctx;
-
- ctx = g_task_get_task_data (task);
-
- 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_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT;
- port_open_step (task);
- return;
- }
-
- /* Go on to next step */
- ctx->step++;
port_open_step (task);
}
@@ -590,136 +1100,46 @@ port_open_step (GTask *task)
}
case PORT_OPEN_STEP_OPEN_WITHOUT_DATA_FORMAT:
- /* Now open the QMI device without any data format CTL flag */
- mm_obj_dbg (self, "Opening device without data format update...");
- qmi_device_open (ctx->device,
- (QMI_DEVICE_OPEN_FLAGS_VERSION_INFO |
- QMI_DEVICE_OPEN_FLAGS_PROXY),
- 25,
- g_task_get_cancellable (task),
- (GAsyncReadyCallback) qmi_device_open_first_ready,
- task);
- return;
-
- case PORT_OPEN_STEP_GET_KERNEL_DATA_FORMAT:
- /* Querying kernel data format is only expected when using qmi_wwan */
- if (mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_USBMISC) {
- mm_obj_dbg (self, "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_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT;
- port_open_step (task);
- return;
- }
- }
- /* For any driver other than qmi_wwan, assume raw-ip */
- else {
- ctx->kernel_data_format = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP;
- mm_obj_dbg (self, "Assuming default kernel data format: %s",
- qmi_device_expected_data_format_get_string (ctx->kernel_data_format));
+ if (!self->priv->wda_unsupported) {
+ /* Now open the QMI device without any data format CTL flag */
+ mm_obj_dbg (self, "Opening device without data format update...");
+ qmi_device_open (ctx->device,
+ (QMI_DEVICE_OPEN_FLAGS_VERSION_INFO |
+ QMI_DEVICE_OPEN_FLAGS_PROXY),
+ 25,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) qmi_device_open_first_ready,
+ task);
+ return;
}
-
ctx->step++;
/* Fall through */
- case PORT_OPEN_STEP_ALLOCATE_WDA_CLIENT:
- /* Allocate WDA client */
- mm_obj_dbg (self, "Allocating WDA client...");
- qmi_device_allocate_client (ctx->device,
- QMI_SERVICE_WDA,
- QMI_CID_NONE,
- 10,
- g_task_get_cancellable (task),
- (GAsyncReadyCallback) allocate_client_wda_ready,
- task);
- return;
-
- case PORT_OPEN_STEP_GET_WDA_DATA_FORMAT:
- /* If we have WDA client, query current data format */
- mm_obj_dbg (self, "Querying device data format...");
- qmi_client_wda_get_data_format (QMI_CLIENT_WDA (ctx->wda),
- NULL,
- 10,
- g_task_get_cancellable (task),
- (GAsyncReadyCallback) get_data_format_ready,
+ case PORT_OPEN_STEP_SETUP_DATA_FORMAT:
+ if (qmi_device_is_open (ctx->device)) {
+ internal_setup_data_format (self,
+ ctx->device,
+ MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY,
+ (GAsyncReadyCallback) open_internal_setup_data_format_ready,
task);
- 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_obj_dbg (self, "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) {
- self->priv->llp_is_raw_ip = FALSE;
- ctx->step = PORT_OPEN_STEP_LAST;
- port_open_step (task);
- return;
- }
-
- if (ctx->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP &&
- ctx->llp == QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP) {
- self->priv->llp_is_raw_ip = TRUE;
- ctx->step = PORT_OPEN_STEP_LAST;
- port_open_step (task);
return;
}
-
ctx->step++;
/* Fall through */
- case PORT_OPEN_STEP_SYNC_DATA_FORMAT:
- /* For drivers other than qmi_wwan, the kernel data format was raw-ip
- * by default, we need to ask the module to switch to it */
- if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_USBMISC) {
- g_autoptr(QmiMessageWdaSetDataFormatInput) input = NULL;
-
- g_assert (ctx->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP);
- 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_RAW_IP, NULL);
- qmi_client_wda_set_data_format (QMI_CLIENT_WDA (ctx->wda),
- input,
- 10,
- g_task_get_cancellable (task),
- (GAsyncReadyCallback) set_data_format_ready,
- task);
- return;
- }
-
- /* If using the qmi_wwan driver, we ask the kernel to sync with the
- * data format requested by the module */
- mm_obj_dbg (self, "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;
- self->priv->llp_is_raw_ip = FALSE;
- } else if (ctx->llp == QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP) {
- ctx->kernel_data_format = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP;
- self->priv->llp_is_raw_ip = TRUE;
- } 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_step (task);
- return;
-
case PORT_OPEN_STEP_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT:
/* This fallback only applies when WDA unsupported */
- mm_obj_dbg (self, "Closing device to reopen it right away...");
- qmi_device_close_async (ctx->device,
- 5,
- g_task_get_cancellable (task),
- (GAsyncReadyCallback) qmi_device_close_to_reopen_ready,
- task);
- return;
+ if (qmi_device_is_open (ctx->device)) {
+ mm_obj_dbg (self, "Closing device to reopen it right away...");
+ qmi_device_close_async (ctx->device,
+ 5,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) qmi_device_close_to_reopen_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
case PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT: {
QmiDeviceOpenFlags open_flags;
@@ -729,11 +1149,19 @@ port_open_step (GTask *task)
QMI_DEVICE_OPEN_FLAGS_PROXY |
QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER);
+ ctx->kernel_data_format = load_kernel_data_format_current (self, ctx->device);
+
/* Need to reopen setting 802.3/raw-ip using CTL */
if (ctx->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP)
open_flags |= QMI_DEVICE_OPEN_FLAGS_NET_RAW_IP;
- else
+ else if (ctx->kernel_data_format == QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3)
open_flags |= QMI_DEVICE_OPEN_FLAGS_NET_802_3;
+ else {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected kernel data format: cannot setup using CTL");
+ g_object_unref (task);
+ return;
+ }
mm_obj_dbg (self, "Reopening device with data format: %s...",
qmi_device_expected_data_format_get_string (ctx->kernel_data_format));
@@ -792,7 +1220,6 @@ mm_port_qmi_open (MMPortQmi *self,
ctx = g_slice_new0 (PortOpenContext);
ctx->step = PORT_OPEN_STEP_FIRST;
ctx->set_data_format = set_data_format;
- ctx->llp = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN;
ctx->kernel_data_format = QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN;
task = g_task_new (self, cancellable, callback, user_data);
diff --git a/src/mm-port-qmi.h b/src/mm-port-qmi.h
index 2a353e90..f776951d 100644
--- a/src/mm-port-qmi.h
+++ b/src/mm-port-qmi.h
@@ -97,6 +97,19 @@ QmiDevice *mm_port_qmi_peek_device (MMPortQmi *self);
QmiDataEndpointType mm_port_qmi_get_endpoint_type (MMPortQmi *self);
guint mm_port_qmi_get_endpoint_interface_number (MMPortQmi *self);
-gboolean mm_port_qmi_llp_is_raw_ip (MMPortQmi *self);
+QmiWdaLinkLayerProtocol mm_port_qmi_get_link_layer_protocol (MMPortQmi *self);
+
+typedef enum {
+ MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY,
+ MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT,
+} MMPortQmiSetupDataFormatAction;
+
+void mm_port_qmi_setup_data_format (MMPortQmi *self,
+ MMPortQmiSetupDataFormatAction action,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_qmi_setup_data_format_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error);
#endif /* MM_PORT_QMI_H */