diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2013-11-18 00:07:04 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2014-02-13 13:40:30 +0100 |
commit | e909edcf1fb18d884ebf9915e15a53ff19bbe00d (patch) | |
tree | 66ac2c2e387fe9846e863d8051d5190aba901a92 | |
parent | 8122153a88b6f87962d2702c14b642d4f344eca2 (diff) |
port-serial: use GIO Async API like method for command()
-rw-r--r-- | src/mm-port-serial-at.c | 75 | ||||
-rw-r--r-- | src/mm-port-serial-qcdm.c | 152 | ||||
-rw-r--r-- | src/mm-port-serial.c | 445 | ||||
-rw-r--r-- | src/mm-port-serial.h | 32 |
4 files changed, 318 insertions, 386 deletions
diff --git a/src/mm-port-serial-at.c b/src/mm-port-serial-at.c index 780bfbb9..d454f5ca 100644 --- a/src/mm-port-serial-at.c +++ b/src/mm-port-serial-at.c @@ -151,19 +151,6 @@ parse_response (MMPortSerial *port, GByteArray *response, GError **error) return found; } -static gsize -handle_response (MMPortSerial *port, - GByteArray *response, - GError *error, - GCallback callback, - gpointer callback_data) -{ - MMSerialResponseFn response_callback = (MMSerialResponseFn) callback; - - response_callback (port, response, error, callback_data); - return response->len; -} - /*****************************************************************************/ typedef struct { @@ -365,24 +352,31 @@ string_free (GString *str) static void serial_command_ready (MMPortSerial *port, - GByteArray *response, - GError *error, + GAsyncResult *res, GSimpleAsyncResult *simple) { - if (error) - g_simple_async_result_set_from_error (simple, error); - else if (response) { - GString *str; - - /* Build a GString just with the response we need, and clear the - * processed range from the response buffer */ - str = g_string_new_len ((const gchar *)response->data, response->len); - g_simple_async_result_set_op_res_gpointer (simple, - str, - (GDestroyNotify)string_free); - } else - g_assert_not_reached (); + GByteArray *response_buffer; + GError *error = NULL; + GString *response; + + response_buffer = mm_port_serial_command_finish (port, res, &error); + if (!response_buffer) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete (simple); + g_object_unref (simple); + return; + } + /* Build a GString just with the response we need, and clear the + * processed range from the response buffer */ + response = g_string_new_len ((const gchar *)response_buffer->data, response_buffer->len); + if (response_buffer->len > 0) + g_byte_array_remove_range (response_buffer, 0, response_buffer->len); + g_byte_array_unref (response_buffer); + + g_simple_async_result_set_op_res_gpointer (simple, + response, + (GDestroyNotify)string_free); g_simple_async_result_complete (simple); g_object_unref (simple); } @@ -413,22 +407,14 @@ mm_port_serial_at_command (MMPortSerialAt *self, user_data, mm_port_serial_at_command); - if (!allow_cached) - mm_port_serial_queue_command (MM_PORT_SERIAL (self), - buf, - TRUE, - timeout_seconds, - cancellable, - (MMSerialResponseFn)serial_command_ready, - simple); - else - mm_port_serial_queue_command_cached (MM_PORT_SERIAL (self), - buf, - TRUE, - timeout_seconds, - cancellable, - (MMSerialResponseFn)serial_command_ready, - simple); + mm_port_serial_command (MM_PORT_SERIAL (self), + buf, + timeout_seconds, + allow_cached, + cancellable, + (GAsyncReadyCallback)serial_command_ready, + simple); + g_byte_array_unref (buf); } static void @@ -637,7 +623,6 @@ mm_port_serial_at_class_init (MMPortSerialAtClass *klass) serial_class->parse_unsolicited = parse_unsolicited; serial_class->parse_response = parse_response; - serial_class->handle_response = handle_response; serial_class->debug_log = debug_log; serial_class->config = config; diff --git a/src/mm-port-serial-qcdm.c b/src/mm-port-serial-qcdm.c index 3afcc447..4733f1fc 100644 --- a/src/mm-port-serial-qcdm.c +++ b/src/mm-port-serial-qcdm.c @@ -65,103 +65,92 @@ parse_response (MMPortSerial *port, GByteArray *response, GError **error) return find_qcdm_start (response, NULL); } -static gsize -handle_response (MMPortSerial *port, - GByteArray *response, - GError *error, - GCallback callback, - gpointer callback_data) +/*****************************************************************************/ + +GByteArray * +mm_port_serial_qcdm_command_finish (MMPortSerialQcdm *self, + GAsyncResult *res, + GError **error) { - MMSerialResponseFn response_callback = (MMSerialResponseFn) callback; - GByteArray *unescaped = NULL; - guint8 *unescaped_buffer; - GError *dm_error = NULL; + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return NULL; + + return g_byte_array_ref ((GByteArray *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); +} + +static void +serial_command_ready (MMPortSerial *port, + GAsyncResult *res, + GSimpleAsyncResult *simple) +{ + GByteArray *response_buffer; + GByteArray *response; + GError *error = NULL; gsize used = 0; gsize start = 0; + guint8 *unescaped_buffer = NULL; gboolean success = FALSE; qcdmbool more = FALSE; gsize unescaped_len = 0; - if (error) - goto callback; + response_buffer = mm_port_serial_command_finish (port, res, &error); + if (!response_buffer) + goto out; /* Get the offset into the buffer of where the QCDM frame starts */ - if (!find_qcdm_start (response, &start)) { - g_set_error_literal (&dm_error, - MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Failed to parse QCDM packet."); + start = 0; + if (!find_qcdm_start (response_buffer, &start)) { + error = g_error_new_literal (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to parse QCDM packet"); /* Discard the unparsable data */ - used = response->len; - goto callback; + used = response_buffer->len; + goto out; } unescaped_buffer = g_malloc (1024); - success = dm_decapsulate_buffer ((const char *) (response->data + start), - response->len - start, - (char *) unescaped_buffer, + success = dm_decapsulate_buffer ((const char *)(response_buffer->data + start), + response_buffer->len - start, + (char *)unescaped_buffer, 1024, &unescaped_len, &used, &more); if (!success) { - g_set_error_literal (&dm_error, - MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Failed to unescape QCDM packet."); + error = g_error_new_literal (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to unescape QCDM packet"); g_free (unescaped_buffer); unescaped_buffer = NULL; - } else if (more) { + goto out; + } + + if (more) { /* Need more data; we shouldn't have gotten here since the parse * function checks for the end-of-frame marker, but whatever. */ + error = g_error_new_literal (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "QCDM packet is not complete"); g_free (unescaped_buffer); - return 0; - } else { - /* Successfully decapsulated the DM command */ - g_assert (unescaped_len <= 1024); - unescaped_buffer = g_realloc (unescaped_buffer, unescaped_len); - unescaped = g_byte_array_new_take (unescaped_buffer, unescaped_len); + unescaped_buffer = NULL; + goto out; } -callback: - response_callback (MM_PORT_SERIAL (port), - unescaped, - dm_error ? dm_error : error, - callback_data); - - if (unescaped) - g_byte_array_unref (unescaped); - g_clear_error (&dm_error); + /* Successfully decapsulated the DM command */ + g_assert (error == NULL); + g_assert (unescaped_len <= 1024); + unescaped_buffer = g_realloc (unescaped_buffer, unescaped_len); + response = g_byte_array_new_take (unescaped_buffer, unescaped_len); + g_simple_async_result_set_op_res_gpointer (simple, response, (GDestroyNotify)g_byte_array_unref); - return start + used; -} - -/*****************************************************************************/ - -GByteArray * -mm_port_serial_qcdm_command_finish (MMPortSerialQcdm *self, - GAsyncResult *res, - GError **error) -{ - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return NULL; - - return g_byte_array_ref ((GByteArray *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); -} - -static void -serial_command_ready (MMPortSerial *port, - GByteArray *response, - GError *error, - GSimpleAsyncResult *simple) -{ +out: if (error) - g_simple_async_result_set_from_error (simple, error); - else if (response) - g_simple_async_result_set_op_res_gpointer (simple, - g_byte_array_ref (response), - (GDestroyNotify)g_byte_array_unref); - else - g_assert_not_reached (); + g_simple_async_result_take_error (simple, error); + if (start + used) + g_byte_array_remove_range (response_buffer, 0, start + used); + if (response_buffer) + g_byte_array_unref (response_buffer); g_simple_async_result_complete (simple); g_object_unref (simple); @@ -187,23 +176,13 @@ mm_port_serial_qcdm_command (MMPortSerialQcdm *self, mm_port_serial_qcdm_command); /* 'command' is expected to be already CRC-ed and escaped */ - - if (!allow_cached) - mm_port_serial_queue_command (MM_PORT_SERIAL (self), - g_byte_array_ref (command), - TRUE, - timeout_seconds, - cancellable, - (MMSerialResponseFn)serial_command_ready, - simple); - else - mm_port_serial_queue_command_cached (MM_PORT_SERIAL (self), - g_byte_array_ref (command), - TRUE, - timeout_seconds, - cancellable, - (MMSerialResponseFn)serial_command_ready, - simple); + mm_port_serial_command (MM_PORT_SERIAL (self), + command, + timeout_seconds, + allow_cached, + cancellable, + (GAsyncReadyCallback)serial_command_ready, + simple); } static void @@ -283,7 +262,6 @@ mm_port_serial_qcdm_class_init (MMPortSerialQcdmClass *klass) /* Virtual methods */ port_class->parse_response = parse_response; - port_class->handle_response = handle_response; port_class->config_fd = config_fd; port_class->debug_log = debug_log; } diff --git a/src/mm-port-serial.c b/src/mm-port-serial.c index 63b95141..11eddc58 100644 --- a/src/mm-port-serial.c +++ b/src/mm-port-serial.c @@ -34,9 +34,14 @@ #include "mm-port-serial.h" #include "mm-log.h" -static gboolean mm_port_serial_queue_process (gpointer data); -static void port_serial_close_force (MMPortSerial *self); -static void port_serial_reopen_cancel (MMPortSerial *self); +static gboolean port_serial_queue_process (gpointer data); +static void port_serial_schedule_queue_process (MMPortSerial *self, + guint timeout_ms); +static void port_serial_close_force (MMPortSerial *self); +static void port_serial_reopen_cancel (MMPortSerial *self); +static void port_serial_set_cached_reply (MMPortSerial *self, + const GByteArray *command, + const GByteArray *response); G_DEFINE_TYPE (MMPortSerial, mm_port_serial, MM_TYPE_PORT) @@ -104,18 +109,103 @@ typedef struct { gpointer reopen_ctx; } MMPortSerialPrivate; +/*****************************************************************************/ +/* Command */ + typedef struct { + MMPortSerial *self; + GSimpleAsyncResult *result; + GCancellable *cancellable; GByteArray *command; - guint32 idx; + guint32 timeout; + gboolean allow_cached; guint32 eagain_count; + + guint32 idx; gboolean started; gboolean done; - GCallback callback; - gpointer user_data; - guint32 timeout; - gboolean cached; - GCancellable *cancellable; -} MMQueueData; +} CommandContext; + +static void +command_context_complete_and_free (CommandContext *ctx, gboolean idle) +{ + if (idle) + g_simple_async_result_complete_in_idle (ctx->result); + else + g_simple_async_result_complete (ctx->result); + g_object_unref (ctx->result); + g_byte_array_unref (ctx->command); + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + g_object_unref (ctx->self); + g_slice_free (CommandContext, ctx); +} + +GByteArray * +mm_port_serial_command_finish (MMPortSerial *self, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return NULL; + + return g_byte_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); +} + +void +mm_port_serial_command (MMPortSerial *self, + GByteArray *command, + guint32 timeout_seconds, + gboolean allow_cached, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + CommandContext *ctx; + MMPortSerialPrivate *priv; + + g_return_if_fail (MM_IS_PORT_SERIAL (self)); + g_return_if_fail (command != NULL); + priv = MM_PORT_SERIAL_GET_PRIVATE (self); + + /* Setup command context */ + ctx = g_slice_new0 (CommandContext); + ctx->self = g_object_ref (self); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + mm_port_serial_command); + ctx->command = g_byte_array_ref (command); + ctx->allow_cached = allow_cached; + ctx->timeout = timeout_seconds; + ctx->cancellable = (cancellable ? g_object_ref (cancellable) : NULL); + + /* Only accept about 3 seconds of EAGAIN for this command */ + if (priv->send_delay) + ctx->eagain_count = 3000000 / priv->send_delay; + else + ctx->eagain_count = 1000; + + if (priv->open_count == 0) { + g_simple_async_result_set_error (ctx->result, + MM_SERIAL_ERROR, + MM_SERIAL_ERROR_SEND_FAILED, + "Sending command failed: device is not open"); + command_context_complete_and_free (ctx, TRUE); + return; + } + + /* Clear the cached value for this command if not asking for cached value */ + if (!allow_cached) + port_serial_set_cached_reply (self, ctx->command, NULL); + + g_queue_push_tail (priv->queue, ctx); + + if (g_queue_get_length (priv->queue) == 1) + port_serial_schedule_queue_process (self, 0); +} + +/*****************************************************************************/ #if 0 static const char * @@ -431,9 +521,9 @@ serial_debug (MMPortSerial *self, const char *prefix, const char *buf, gsize len } static gboolean -mm_port_serial_process_command (MMPortSerial *self, - MMQueueData *info, - GError **error) +port_serial_process_command (MMPortSerial *self, + CommandContext *ctx, + GError **error) { MMPortSerialPrivate *priv = MM_PORT_SERIAL_GET_PRIVATE (self); const guint8 *p; @@ -452,31 +542,31 @@ mm_port_serial_process_command (MMPortSerial *self, } /* Only print command the first time */ - if (info->started == FALSE) { - info->started = TRUE; - serial_debug (self, "-->", (const char *) info->command->data, info->command->len); + if (ctx->started == FALSE) { + ctx->started = TRUE; + serial_debug (self, "-->", (const char *) ctx->command->data, ctx->command->len); } if (priv->send_delay == 0) { /* Send the whole command in one write */ - send_len = expected_status = info->command->len; - p = info->command->data; + send_len = expected_status = ctx->command->len; + p = ctx->command->data; } else { /* Send just one byte of the command */ send_len = expected_status = 1; - p = &info->command->data[info->idx]; + p = &ctx->command->data[ctx->idx]; } /* Send a single byte of the command */ errno = 0; status = write (priv->fd, p, send_len); if (status > 0) - info->idx += status; + ctx->idx += status; else { /* Error or no bytes written */ if (errno == EAGAIN || status == 0) { - info->eagain_count--; - if (info->eagain_count <= 0) { + ctx->eagain_count--; + if (ctx->eagain_count <= 0) { /* If we reach the limit of EAGAIN errors, treat as a timeout error. */ priv->n_consecutive_timeouts++; g_signal_emit (self, signals[TIMED_OUT], 0, priv->n_consecutive_timeouts); @@ -492,16 +582,16 @@ mm_port_serial_process_command (MMPortSerial *self, } } - if (info->idx >= info->command->len) - info->done = TRUE; + if (ctx->idx >= ctx->command->len) + ctx->done = TRUE; return TRUE; } static void -mm_port_serial_set_cached_reply (MMPortSerial *self, - const GByteArray *command, - const GByteArray *response) +port_serial_set_cached_reply (MMPortSerial *self, + const GByteArray *command, + const GByteArray *response) { MMPortSerialPrivate *priv = MM_PORT_SERIAL_GET_PRIVATE (self); @@ -521,13 +611,13 @@ mm_port_serial_set_cached_reply (MMPortSerial *self, } static const GByteArray * -mm_port_serial_get_cached_reply (MMPortSerial *self, GByteArray *command) +port_serial_get_cached_reply (MMPortSerial *self, GByteArray *command) { return (const GByteArray *) g_hash_table_lookup (MM_PORT_SERIAL_GET_PRIVATE (self)->reply_cache, command); } static void -mm_port_serial_schedule_queue_process (MMPortSerial *self, guint timeout_ms) +port_serial_schedule_queue_process (MMPortSerial *self, guint timeout_ms) { MMPortSerialPrivate *priv = MM_PORT_SERIAL_GET_PRIVATE (self); @@ -542,30 +632,19 @@ mm_port_serial_schedule_queue_process (MMPortSerial *self, guint timeout_ms) } if (timeout_ms) - priv->queue_id = g_timeout_add (timeout_ms, mm_port_serial_queue_process, self); + priv->queue_id = g_timeout_add (timeout_ms, port_serial_queue_process, self); else - priv->queue_id = g_idle_add (mm_port_serial_queue_process, self); -} - -static gsize -real_handle_response (MMPortSerial *self, - GByteArray *response, - GError *error, - GCallback callback, - gpointer callback_data) -{ - MMSerialResponseFn response_callback = (MMSerialResponseFn) callback; - - response_callback (self, response, error, callback_data); - return response->len; + priv->queue_id = g_idle_add (port_serial_queue_process, self); } static void -mm_port_serial_got_response (MMPortSerial *self, GError *error) +port_serial_got_response (MMPortSerial *self, + const GError *error) { - MMPortSerialPrivate *priv = MM_PORT_SERIAL_GET_PRIVATE (self); - MMQueueData *info; - gsize consumed = priv->response->len; + MMPortSerialPrivate *priv; + CommandContext *ctx; + + priv = MM_PORT_SERIAL_GET_PRIVATE (self); if (priv->timeout_id) { g_source_remove (priv->timeout_id); @@ -581,36 +660,32 @@ mm_port_serial_got_response (MMPortSerial *self, GError *error) g_clear_object (&priv->cancellable); - info = (MMQueueData *) g_queue_pop_head (priv->queue); - if (info) { - if (info->cached && !error) - mm_port_serial_set_cached_reply (self, info->command, priv->response); - - if (info->callback) { - g_warn_if_fail (MM_PORT_SERIAL_GET_CLASS (self)->handle_response != NULL); - consumed = MM_PORT_SERIAL_GET_CLASS (self)->handle_response (self, - priv->response, - error, - info->callback, - info->user_data); + ctx = (CommandContext *) g_queue_pop_head (priv->queue); + if (ctx) { + if (error) + g_simple_async_result_set_from_error (ctx->result, error); + else { + if (ctx->allow_cached && !error) + port_serial_set_cached_reply (self, ctx->command, priv->response); + + /* Upon completion, it is a task of the caller to remove from the response + * buffer the processed data */ + g_simple_async_result_set_op_res_gpointer (ctx->result, + g_byte_array_ref (priv->response), + (GDestroyNotify)g_byte_array_unref); } - g_clear_object (&info->cancellable); - g_byte_array_unref (info->command); - g_slice_free (MMQueueData, info); + /* Don't complete in idle. We need the caller remove the response range which + * was processed, and that must be done before processing any new queued command */ + command_context_complete_and_free (ctx, FALSE); } - if (error) - g_error_free (error); - - if (consumed) - g_byte_array_remove_range (priv->response, 0, consumed); if (!g_queue_is_empty (priv->queue)) - mm_port_serial_schedule_queue_process (self, 0); + port_serial_schedule_queue_process (self, 0); } static gboolean -mm_port_serial_timed_out (gpointer data) +port_serial_timed_out (gpointer data) { MMPortSerial *self = MM_PORT_SERIAL (data); MMPortSerialPrivate *priv = MM_PORT_SERIAL_GET_PRIVATE (self); @@ -621,14 +696,14 @@ mm_port_serial_timed_out (gpointer data) /* Update number of consecutive timeouts found */ priv->n_consecutive_timeouts++; + /* FIXME: This is not completely correct - if the response finally arrives and there's + * some other command waiting for response right now, the other command will + * get the output of the timed out command. Not sure what to do here. */ error = g_error_new_literal (MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT, "Serial command timed out"); - - /* FIXME: This is not completely correct - if the response finally arrives and there's - some other command waiting for response right now, the other command will - get the output of the timed out command. Not sure what to do here. */ - mm_port_serial_got_response (self, error); + port_serial_got_response (self, error); + g_error_free (error); /* Emit a timed out signal, used by upper layers to identify a disconnected * serial port */ @@ -647,33 +722,34 @@ port_serial_response_wait_cancelled (GCancellable *cancellable, /* We don't want to call disconnect () while in the signal handler */ priv->cancellable_id = 0; + /* FIXME: This is not completely correct - if the response finally arrives and there's + * some other command waiting for response right now, the other command will + * get the output of the cancelled command. Not sure what to do here. */ error = g_error_new_literal (MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "Waiting for the reply cancelled"); - - /* FIXME: This is not completely correct - if the response finally arrives and there's - some other command waiting for response right now, the other command will - get the output of the cancelled command. Not sure what to do here. */ - mm_port_serial_got_response (self, error); + port_serial_got_response (self, error); + g_error_free (error); } static gboolean -mm_port_serial_queue_process (gpointer data) +port_serial_queue_process (gpointer data) { MMPortSerial *self = MM_PORT_SERIAL (data); MMPortSerialPrivate *priv = MM_PORT_SERIAL_GET_PRIVATE (self); - MMQueueData *info; + CommandContext *ctx; GError *error = NULL; priv->queue_id = 0; - info = (MMQueueData *) g_queue_peek_head (priv->queue); - if (!info) + ctx = (CommandContext *) g_queue_peek_head (priv->queue); + if (!ctx) return FALSE; - if (info->cached) { - const GByteArray *cached = mm_port_serial_get_cached_reply (self, info->command); + if (ctx->allow_cached) { + const GByteArray *cached; + cached = port_serial_get_cached_reply (self, ctx->command); if (cached) { /* Ensure the response array is fully empty before setting the * cached response. */ @@ -686,41 +762,48 @@ mm_port_serial_queue_process (gpointer data) } g_byte_array_append (priv->response, cached->data, cached->len); - mm_port_serial_got_response (self, NULL); + port_serial_got_response (self, NULL); return FALSE; } + + /* Cached reply wasn't found, keep on */ } - if (mm_port_serial_process_command (self, info, &error)) { - if (info->done) { - /* setup the cancellable so that we can stop waiting for a response */ - if (info->cancellable) { - priv->cancellable = g_object_ref (info->cancellable); - priv->cancellable_id = (g_cancellable_connect ( - info->cancellable, - (GCallback) port_serial_response_wait_cancelled, - self, - NULL)); - if (!priv->cancellable_id) { - error = g_error_new (MM_CORE_ERROR, - MM_CORE_ERROR_CANCELLED, - "Won't wait for the reply"); - mm_port_serial_got_response (self, error); - return FALSE; - } - } + /* If error, report it */ + if (!port_serial_process_command (self, ctx, &error)) { + port_serial_got_response (self, error); + g_error_free (error); + return FALSE; + } - /* If the command is finished being sent, schedule the timeout */ - priv->timeout_id = g_timeout_add_seconds (info->timeout, - mm_port_serial_timed_out, - self); - } else { - /* Schedule the next byte of the command to be sent */ - mm_port_serial_schedule_queue_process (self, priv->send_delay / 1000); + /* Schedule the next byte of the command to be sent */ + if (!ctx->done) { + port_serial_schedule_queue_process (self, priv->send_delay / 1000); + return FALSE; + } + + /* Setup the cancellable so that we can stop waiting for a response */ + if (ctx->cancellable) { + priv->cancellable = g_object_ref (ctx->cancellable); + priv->cancellable_id = (g_cancellable_connect ( + ctx->cancellable, + (GCallback)port_serial_response_wait_cancelled, + self, + NULL)); + if (!priv->cancellable_id) { + error = g_error_new (MM_CORE_ERROR, + MM_CORE_ERROR_CANCELLED, + "Won't wait for the reply"); + port_serial_got_response (self, error); + g_error_free (error); + return FALSE; } - } else - mm_port_serial_got_response (self, error); + } + /* If the command is finished being sent, schedule the timeout */ + priv->timeout_id = g_timeout_add_seconds (ctx->timeout, + port_serial_timed_out, + self); return FALSE; } @@ -746,8 +829,9 @@ data_available (GIOChannel *source, char buf[SERIAL_BUF_SIZE + 1]; gsize bytes_read; GIOStatus status; - MMQueueData *info; + CommandContext *ctx; const char *device; + GError *error = NULL; if (condition & G_IO_HUP) { device = mm_port_get_device (MM_PORT (self)); @@ -766,22 +850,20 @@ data_available (GIOChannel *source, } /* Don't read any input if the current command isn't done being sent yet */ - info = g_queue_peek_nth (priv->queue, 0); - if (info && (info->started == TRUE) && (info->done == FALSE)) + ctx = g_queue_peek_nth (priv->queue, 0); + if (ctx && (ctx->started == TRUE) && (ctx->done == FALSE)) return TRUE; do { - GError *err = NULL; - bytes_read = 0; - status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err); + status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &error); if (status == G_IO_STATUS_ERROR) { - if (err && err->message) { + if (error) { mm_warn ("(%s): read error: %s", mm_port_get_device (MM_PORT (self)), - err->message); + error->message); } - g_clear_error (&err); + g_clear_error (&error); } /* If no bytes read, just let g_io_channel wait for more data */ @@ -799,10 +881,14 @@ data_available (GIOChannel *source, g_byte_array_remove_range (priv->response, 0, (SERIAL_BUF_SIZE / 2)); } - if (parse_response (self, priv->response, &err)) { + /* Parse response. Returns TRUE either if an error is provided or if + * we really have the response to process. */ + if (parse_response (self, priv->response, &error)) { /* Reset number of consecutive timeouts only here */ priv->n_consecutive_timeouts = 0; - mm_port_serial_got_response (self, err); + /* Process response retrieved */ + port_serial_got_response (self, error); + g_clear_error (&error); } } while ( (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN) && (priv->watch_id > 0)); @@ -1085,31 +1171,15 @@ mm_port_serial_close (MMPortSerial *self) /* Clear the command queue */ for (i = 0; i < g_queue_get_length (priv->queue); i++) { - MMQueueData *item = g_queue_peek_nth (priv->queue, i); - - if (item->callback) { - GError *error; - GByteArray *response; + CommandContext *ctx; - g_warn_if_fail (MM_PORT_SERIAL_GET_CLASS (self)->handle_response != NULL); - error = g_error_new_literal (MM_SERIAL_ERROR, + ctx = g_queue_peek_nth (priv->queue, i); + g_simple_async_result_set_error (ctx->result, + MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Serial port is now closed"); - response = g_byte_array_sized_new (1); - g_byte_array_append (response, (const guint8 *) "\0", 1); - - MM_PORT_SERIAL_GET_CLASS (self)->handle_response (self, - response, - error, - item->callback, - item->user_data); - g_error_free (error); - g_byte_array_unref (response); - } - - g_clear_object (&item->cancellable); - g_byte_array_unref (item->command); - g_slice_free (MMQueueData, item); + g_simple_async_result_complete (ctx->result); + command_context_complete_and_free (ctx, FALSE); } g_queue_clear (priv->queue); @@ -1168,86 +1238,6 @@ port_serial_close_force (MMPortSerial *self) g_signal_emit (self, signals[FORCED_CLOSE], 0); } -static void -internal_queue_command (MMPortSerial *self, - GByteArray *command, - gboolean take_command, - gboolean cached, - guint32 timeout_seconds, - GCancellable *cancellable, - MMSerialResponseFn callback, - gpointer user_data) -{ - MMPortSerialPrivate *priv = MM_PORT_SERIAL_GET_PRIVATE (self); - MMQueueData *info; - - g_return_if_fail (MM_IS_PORT_SERIAL (self)); - g_return_if_fail (command != NULL); - - if (priv->open_count == 0) { - GError *error = g_error_new_literal (MM_SERIAL_ERROR, - MM_SERIAL_ERROR_SEND_FAILED, - "Sending command failed: device is not enabled"); - if (callback) - callback (self, NULL, error, user_data); - g_error_free (error); - return; - } - - info = g_slice_new0 (MMQueueData); - if (take_command) - info->command = command; - else { - info->command = g_byte_array_sized_new (command->len); - g_byte_array_append (info->command, command->data, command->len); - } - - /* Only accept about 3 seconds of EAGAIN for this command */ - if (priv->send_delay) - info->eagain_count = 3000000 / priv->send_delay; - else - info->eagain_count = 1000; - - info->cached = cached; - info->timeout = timeout_seconds; - info->cancellable = (cancellable ? g_object_ref (cancellable) : NULL); - info->callback = (GCallback) callback; - info->user_data = user_data; - - /* Clear the cached value for this command if not asking for cached value */ - if (!cached) - mm_port_serial_set_cached_reply (self, info->command, NULL); - - g_queue_push_tail (priv->queue, info); - - if (g_queue_get_length (priv->queue) == 1) - mm_port_serial_schedule_queue_process (self, 0); -} - -void -mm_port_serial_queue_command (MMPortSerial *self, - GByteArray *command, - gboolean take_command, - guint32 timeout_seconds, - GCancellable *cancellable, - MMSerialResponseFn callback, - gpointer user_data) -{ - internal_queue_command (self, command, take_command, FALSE, timeout_seconds, cancellable, callback, user_data); -} - -void -mm_port_serial_queue_command_cached (MMPortSerial *self, - GByteArray *command, - gboolean take_command, - guint32 timeout_seconds, - GCancellable *cancellable, - MMSerialResponseFn callback, - gpointer user_data) -{ - internal_queue_command (self, command, take_command, TRUE, timeout_seconds, cancellable, callback, user_data); -} - /*****************************************************************************/ /* Reopen */ @@ -1820,7 +1810,6 @@ mm_port_serial_class_init (MMPortSerialClass *klass) object_class->finalize = finalize; klass->config_fd = real_config_fd; - klass->handle_response = real_handle_response; /* Properties */ g_object_class_install_property diff --git a/src/mm-port-serial.h b/src/mm-port-serial.h index e724a28b..343e6d31 100644 --- a/src/mm-port-serial.h +++ b/src/mm-port-serial.h @@ -43,11 +43,6 @@ typedef struct _MMPortSerial MMPortSerial; typedef struct _MMPortSerialClass MMPortSerialClass; -typedef void (*MMSerialResponseFn) (MMPortSerial *port, - GByteArray *response, - GError *error, - gpointer user_data); - struct _MMPortSerial { MMPort parent; @@ -73,16 +68,6 @@ struct _MMPortSerialClass { GByteArray *response, GError **error); - /* Called after parsing to allow the command response to be delivered to - * it's callback to be handled. Returns the # of bytes of the response - * consumed. - */ - gsize (*handle_response) (MMPortSerial *self, - GByteArray *response, - GError *error, - GCallback callback, - gpointer callback_data); - /* Called to configure the serial port fd after it's opened. On error, should * return FALSE and set 'error' as appropriate. */ @@ -139,20 +124,15 @@ void mm_port_serial_flash_cancel (MMPortSerial *self); gboolean mm_port_serial_get_flash_ok (MMPortSerial *self); -void mm_port_serial_queue_command (MMPortSerial *self, +void mm_port_serial_command (MMPortSerial *self, GByteArray *command, - gboolean take_command, guint32 timeout_seconds, + gboolean allow_cached, GCancellable *cancellable, - MMSerialResponseFn callback, + GAsyncReadyCallback callback, gpointer user_data); - -void mm_port_serial_queue_command_cached (MMPortSerial *self, - GByteArray *command, - gboolean take_command, - guint32 timeout_seconds, - GCancellable *cancellable, - MMSerialResponseFn callback, - gpointer user_data); +GByteArray *mm_port_serial_command_finish (MMPortSerial *self, + GAsyncResult *res, + GError **error); #endif /* MM_PORT_SERIAL_H */ |