static void
scan_devices_auth_ready (MMAuthProvider *authp,
                         GAsyncResult *res,
                         ScanDevicesContext *ctx)
{
    GError *error = NULL;

    if (!mm_auth_provider_authorize_finish (authp, res, &error))
        g_dbus_method_invocation_take_error (ctx->invocation, error);
    else {
        /* Otherwise relaunch device scan */
        mm_base_manager_start (MM_BASE_MANAGER (ctx->self), TRUE);
        mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices (
            MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
            ctx->invocation);
    }

    scan_devices_context_free (ctx);
}
static void
set_logging_auth_ready (MMAuthProvider *authp,
                        GAsyncResult *res,
                        SetLoggingContext *ctx)
{
    GError *error = NULL;

    if (!mm_auth_provider_authorize_finish (authp, res, &error))
        g_dbus_method_invocation_take_error (ctx->invocation, error);
    else if (!mm_log_set_level(ctx->level, &error))
        g_dbus_method_invocation_take_error (ctx->invocation, error);
    else {
        mm_info ("logging: level '%s'", ctx->level);
        mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging (
            MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
            ctx->invocation);
    }

    set_logging_context_free (ctx);
}
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);
}