void
mm_base_modem_at_command_full (MMBaseModem *self,
                               MMAtSerialPort *port,
                               const gchar *command,
                               guint timeout,
                               gboolean allow_cached,
                               GCancellable *cancellable,
                               GAsyncReadyCallback callback,
                               gpointer user_data)
{
    AtCommandContext *ctx;

    /* Ensure that we have an open port */
    if (!abort_async_if_port_unusable (self, port, callback, user_data))
        return;

    ctx = g_new0 (AtCommandContext, 1);
    ctx->self = g_object_ref (self);
    ctx->port = g_object_ref (port);
    ctx->result = g_simple_async_result_new (G_OBJECT (self),
                  callback,
                  user_data,
                  mm_base_modem_at_command_full);

    /* Setup cancellables */
    ctx->modem_cancellable = mm_base_modem_get_cancellable (self);
    ctx->user_cancellable = cancellable ? g_object_ref (cancellable) : NULL;
    if (!ctx->user_cancellable)
        /* Just the modem-wide one, use it directly */
        ctx->cancellable = g_object_ref (ctx->modem_cancellable);
    else {
        /* Use the user provided one, which will also get cancelled if the modem
         * wide-one gets cancelled */
        ctx->cancellable = g_object_ref (ctx->user_cancellable);
        ctx->cancelled_id = g_cancellable_connect (ctx->modem_cancellable,
                            G_CALLBACK (modem_cancellable_cancelled),
                            ctx->user_cancellable,
                            NULL);
    }


    /* Go on with the command */
    if (allow_cached)
        mm_at_serial_port_queue_command_cached (
            port,
            command,
            timeout,
            ctx->cancellable,
            (MMAtSerialResponseFn)at_command_parse_response,
            ctx);
    else
        mm_at_serial_port_queue_command (
            port,
            command,
            timeout,
            ctx->cancellable,
            (MMAtSerialResponseFn)at_command_parse_response,
            ctx);
}
void
mm_base_modem_at_sequence_full (MMBaseModem *self,
                                MMAtSerialPort *port,
                                const MMBaseModemAtCommand *sequence,
                                gpointer response_processor_context,
                                GDestroyNotify response_processor_context_free,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data)
{
    AtSequenceContext *ctx;

    /* Ensure that we have an open port */
    if (!abort_async_if_port_unusable (self, port, callback, user_data))
        return;

    /* Setup context */
    ctx = g_new0 (AtSequenceContext, 1);
    ctx->self = g_object_ref (self);
    ctx->port = g_object_ref (port);
    ctx->simple = g_simple_async_result_new (G_OBJECT (self),
                                             callback,
                                             user_data,
                                             mm_base_modem_at_sequence_full);
    ctx->current = ctx->sequence = sequence;
    ctx->response_processor_context = response_processor_context;
    ctx->response_processor_context_free = response_processor_context_free;

    /* Setup cancellables */
    ctx->modem_cancellable = mm_base_modem_get_cancellable (self);
    ctx->user_cancellable = cancellable ? g_object_ref (cancellable) : NULL;
    if (!ctx->user_cancellable)
        /* Just the modem-wide one, use it directly */
        ctx->cancellable = g_object_ref (ctx->modem_cancellable);
    else {
        /* Use the user provided one, which will also get cancelled if the modem
         * wide-one gets cancelled */
        ctx->cancellable = g_object_ref (ctx->user_cancellable);
        ctx->cancelled_id = g_cancellable_connect (ctx->modem_cancellable,
                                                   G_CALLBACK (modem_cancellable_cancelled),
                                                   ctx->user_cancellable,
                                                   NULL);
    }

    /* Go on with the first one in the sequence */
    mm_at_serial_port_queue_command (
        ctx->port,
        ctx->current->command,
        ctx->current->timeout,
        FALSE,
        ctx->cancellable,
        (MMAtSerialResponseFn)at_sequence_parse_response,
        ctx);
}