static gboolean serial_probe_qcdm (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; GError *error = NULL; GByteArray *verinfo = NULL; GByteArray *verinfo2; gint len; task->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return FALSE; mm_dbg ("(%s) probing QCDM...", self->priv->name); /* If open, close the AT port */ if (task->serial) { mm_serial_port_close (task->serial); g_object_unref (task->serial); } /* Open the QCDM port */ task->serial = MM_SERIAL_PORT (mm_qcdm_serial_port_new (self->priv->name)); if (!task->serial) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s) Couldn't create QCDM port", self->priv->name)); return FALSE; } /* Try to open the port */ if (!mm_serial_port_open (task->serial, &error)) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s) Failed to open QCDM port: %s", self->priv->name, (error ? error->message : "unknown error"))); g_clear_error (&error); return FALSE; } /* Build up the probe command */ verinfo = g_byte_array_sized_new (50); len = qcdm_cmd_version_info_new ((gchar *) verinfo->data, 50); if (len <= 0) { g_byte_array_free (verinfo, TRUE); port_probe_run_task_complete ( task, FALSE, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s) Failed to create QCDM versin info command", self->priv->name)); return FALSE; } verinfo->len = len; /* Queuing the command takes ownership over it; dup it for the second try */ verinfo2 = g_byte_array_sized_new (verinfo->len); g_byte_array_append (verinfo2, verinfo->data, verinfo->len); /* Send the command twice; the ports often need to be woken up */ mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial), verinfo, 3, NULL, (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response, NULL); mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial), verinfo2, 3, NULL, (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response, self); return FALSE; }
static void serial_probe_qcdm_parse_response (MMQcdmSerialPort *port, GByteArray *response, GError *error, MMPortProbe *self) { QcdmResult *result; gint err = QCDM_SUCCESS; gboolean is_qcdm = FALSE; gboolean retry = FALSE; PortProbeRunTask *task = self->priv->task; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return; if (!error) { /* Parse the response */ result = qcdm_cmd_version_info_result ((const gchar *) response->data, response->len, &err); if (!result) { mm_warn ("(%s/%s) failed to parse QCDM version info command result: %d", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port), err); retry = TRUE; } else { /* yay, probably a QCDM port */ is_qcdm = TRUE; qcdm_result_unref (result); } } else { if (!g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) mm_dbg ("QCDM probe error: (%d) %s", error->code, error->message); retry = TRUE; } if (retry) { GByteArray *cmd2; cmd2 = g_object_steal_data (G_OBJECT (self), "cmd2"); if (cmd2) { /* second try */ mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial), cmd2, 3, NULL, (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response, self); return; } /* no more retries left */ } /* Set probing result */ mm_port_probe_set_result_qcdm (self, is_qcdm); /* Reschedule probing */ serial_probe_schedule (self); }
static gboolean serial_probe_qcdm (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; GError *error = NULL; GByteArray *verinfo = NULL; GByteArray *verinfo2; gint len; guint8 marker = 0x7E; task->source_id = 0; /* If already cancelled, do nothing else */ if (port_probe_run_is_cancelled (self)) return FALSE; mm_dbg ("(%s/%s) probing QCDM...", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port)); /* If open, close the AT port */ if (task->serial) { mm_serial_port_close (task->serial); g_object_unref (task->serial); } /* Open the QCDM port */ task->serial = MM_SERIAL_PORT (mm_qcdm_serial_port_new (g_udev_device_get_name (self->priv->port))); if (!task->serial) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "(%s/%s) Couldn't create QCDM port", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port))); return FALSE; } /* Try to open the port */ if (!mm_serial_port_open (task->serial, &error)) { port_probe_run_task_complete ( task, FALSE, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s/%s) Failed to open QCDM port: %s", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port), (error ? error->message : "unknown error"))); g_clear_error (&error); return FALSE; } /* Build up the probe command; 0x7E is the frame marker, so put one at the * beginning of the buffer to ensure that the device discards any AT * commands that probing might have sent earlier. Should help devices * respond more quickly and speed up QCDM probing. */ verinfo = g_byte_array_sized_new (10); g_byte_array_append (verinfo, &marker, 1); len = qcdm_cmd_version_info_new ((char *) (verinfo->data + 1), 9); if (len <= 0) { g_byte_array_free (verinfo, TRUE); port_probe_run_task_complete ( task, FALSE, g_error_new (MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "(%s/%s) Failed to create QCDM versin info command", g_udev_device_get_subsystem (self->priv->port), g_udev_device_get_name (self->priv->port))); return FALSE; } verinfo->len = len + 1; /* Queuing the command takes ownership over it; save it for the second try */ verinfo2 = g_byte_array_sized_new (verinfo->len); g_byte_array_append (verinfo2, verinfo->data, verinfo->len); g_object_set_data_full (G_OBJECT (self), "cmd2", verinfo2, (GDestroyNotify) g_byte_array_unref); mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial), verinfo, 3, NULL, (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response, self); return FALSE; }
gboolean mm_base_modem_organize_ports (MMBaseModem *self, GError **error) { GHashTableIter iter; MMPort *candidate; MMAtPortFlag flags; MMAtSerialPort *backup_primary = NULL; MMAtSerialPort *primary = NULL; MMAtSerialPort *secondary = NULL; MMAtSerialPort *backup_secondary = NULL; MMQcdmSerialPort *qcdm = NULL; MMAtSerialPort *gps_control = NULL; MMGpsSerialPort *gps = NULL; MMPort *data_primary = NULL; GList *data = NULL; #if defined WITH_QMI MMPort *qmi_primary = NULL; GList *qmi = NULL; #endif GList *l; g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); /* If ports have already been organized, just return success */ if (self->priv->primary) return TRUE; g_hash_table_iter_init (&iter, self->priv->ports); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &candidate)) { switch (mm_port_get_port_type (candidate)) { case MM_PORT_TYPE_AT: g_assert (MM_IS_AT_SERIAL_PORT (candidate)); flags = mm_at_serial_port_get_flags (MM_AT_SERIAL_PORT (candidate)); if (flags & MM_AT_PORT_FLAG_PRIMARY) { if (!primary) primary = MM_AT_SERIAL_PORT (candidate); else if (!backup_primary) { /* Just in case the plugin gave us more than one primary * and no secondaries, treat additional primary ports as * secondary. */ backup_primary = MM_AT_SERIAL_PORT (candidate); } } if (!data_primary && (flags & MM_AT_PORT_FLAG_PPP)) data_primary = candidate; /* Explicitly flagged secondary ports trump NONE ports for secondary */ if (flags & MM_AT_PORT_FLAG_SECONDARY) { if (!secondary || !(mm_at_serial_port_get_flags (secondary) & MM_AT_PORT_FLAG_SECONDARY)) secondary = MM_AT_SERIAL_PORT (candidate); } if (flags & MM_AT_PORT_FLAG_GPS_CONTROL) { if (!gps_control) gps_control = MM_AT_SERIAL_PORT (candidate); } /* Fallback secondary */ if (flags == MM_AT_PORT_FLAG_NONE) { if (!secondary) secondary = MM_AT_SERIAL_PORT (candidate); else if (!backup_secondary) backup_secondary = MM_AT_SERIAL_PORT (candidate); } break; case MM_PORT_TYPE_QCDM: g_assert (MM_IS_QCDM_SERIAL_PORT (candidate)); if (!qcdm) qcdm = MM_QCDM_SERIAL_PORT (candidate); break; case MM_PORT_TYPE_NET: /* Net device (if any) is the preferred data port */ if (!data_primary || MM_IS_AT_SERIAL_PORT (data_primary)) data_primary = candidate; else /* All non-primary net ports get added to the list of data ports */ data = g_list_append (data, candidate); break; case MM_PORT_TYPE_GPS: g_assert (MM_IS_GPS_SERIAL_PORT (candidate)); if (!gps) gps = MM_GPS_SERIAL_PORT (candidate); break; #if defined WITH_QMI case MM_PORT_TYPE_QMI: if (!qmi_primary) qmi_primary = candidate; else /* All non-primary QMI ports get added to the list of QMI ports */ qmi = g_list_append (qmi, candidate); break; #endif default: /* Ignore port */ break; } } /* Fall back to a secondary port if we didn't find a primary port */ if (!primary) { #if defined WITH_QMI /* On QMI-based modems we do allow not having a primary AT port */ if (!secondary && !qmi_primary) { #else if (!secondary) { #endif g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to find primary AT port"); return FALSE; } else { primary = secondary; secondary = NULL; } } /* If the plugin didn't give us any secondary ports, use any additional * primary ports or backup secondary ports as secondary. */ if (!secondary) secondary = backup_primary ? backup_primary : backup_secondary; #if defined WITH_QMI /* On QMI-based modems, we need to have at least a net port */ if (qmi_primary && !data_primary) { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to find a net port in the QMI modem"); return FALSE; } #endif /* Data port defaults to primary AT port */ if (!data_primary) data_primary = MM_PORT (primary); g_assert (data_primary); /* Reset flags on all ports; clear data port first since it might also * be the primary or secondary port. */ if (MM_IS_AT_SERIAL_PORT (data_primary)) mm_at_serial_port_set_flags (MM_AT_SERIAL_PORT (data_primary), MM_AT_PORT_FLAG_NONE); mm_at_serial_port_set_flags (primary, MM_AT_PORT_FLAG_PRIMARY); if (secondary) mm_at_serial_port_set_flags (secondary, MM_AT_PORT_FLAG_SECONDARY); if (MM_IS_AT_SERIAL_PORT (data_primary)) { flags = mm_at_serial_port_get_flags (MM_AT_SERIAL_PORT (data_primary)); mm_at_serial_port_set_flags (MM_AT_SERIAL_PORT (data_primary), flags | MM_AT_PORT_FLAG_PPP); } log_port (self, MM_PORT (primary), "at (primary)"); log_port (self, MM_PORT (secondary), "at (secondary)"); log_port (self, MM_PORT (data_primary), "data (primary)"); for (l = data; l; l = g_list_next (l)) log_port (self, MM_PORT (l->data), "data (secondary)"); log_port (self, MM_PORT (qcdm), "qcdm"); log_port (self, MM_PORT (gps_control), "gps (control)"); log_port (self, MM_PORT (gps), "gps (nmea)"); #if defined WITH_QMI log_port (self, MM_PORT (qmi_primary), "qmi (primary)"); for (l = qmi; l; l = g_list_next (l)) log_port (self, MM_PORT (l->data), "qmi (secondary)"); #endif /* We keep new refs to the objects here */ self->priv->primary = g_object_ref (primary); self->priv->secondary = (secondary ? g_object_ref (secondary) : NULL); self->priv->qcdm = (qcdm ? g_object_ref (qcdm) : NULL); self->priv->gps_control = (gps_control ? g_object_ref (gps_control) : NULL); self->priv->gps = (gps ? g_object_ref (gps) : NULL); /* Build the final list of data ports, primary port first */ self->priv->data = g_list_append (self->priv->data, g_object_ref (data_primary)); g_list_foreach (data, (GFunc)g_object_ref, NULL); self->priv->data = g_list_concat (self->priv->data, data); #if defined WITH_QMI /* Build the final list of QMI ports, primary port first */ if (qmi_primary) { self->priv->qmi = g_list_append (self->priv->qmi, g_object_ref (qmi_primary)); g_list_foreach (qmi, (GFunc)g_object_ref, NULL); self->priv->qmi = g_list_concat (self->priv->qmi, qmi); } #endif /* As soon as we get the ports organized, we initialize the modem */ mm_base_modem_initialize (self, (GAsyncReadyCallback)initialize_ready, NULL); return TRUE; } /*****************************************************************************/ /* Authorization */ gboolean mm_base_modem_authorize_finish (MMBaseModem *self, GAsyncResult *res, GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } static void authorize_ready (MMAuthProvider *authp, GAsyncResult *res, GSimpleAsyncResult *simple) { GError *error = NULL; if (!mm_auth_provider_authorize_finish (authp, res, &error)) g_simple_async_result_take_error (simple, error); else g_simple_async_result_set_op_res_gboolean (simple, TRUE); g_simple_async_result_complete (simple); g_object_unref (simple); }