aboutsummaryrefslogtreecommitdiff
path: root/src/mm-port-serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-port-serial.c')
-rw-r--r--src/mm-port-serial.c118
1 files changed, 64 insertions, 54 deletions
diff --git a/src/mm-port-serial.c b/src/mm-port-serial.c
index 53f8725b..92ad4812 100644
--- a/src/mm-port-serial.c
+++ b/src/mm-port-serial.c
@@ -696,10 +696,14 @@ port_serial_schedule_queue_process (MMPortSerial *self, guint timeout_ms)
static void
port_serial_got_response (MMPortSerial *self,
+ GByteArray *parsed_response,
const GError *error)
{
CommandContext *ctx;
+ /* Either one or the other, not both */
+ g_assert ((parsed_response && !error) || (!parsed_response && error));
+
if (self->priv->timeout_id) {
g_source_remove (self->priv->timeout_id);
self->priv->timeout_id = 0;
@@ -716,29 +720,15 @@ port_serial_got_response (MMPortSerial *self,
ctx = (CommandContext *) g_queue_pop_head (self->priv->queue);
if (ctx) {
- if (error) {
- /* If we're returning an error parsed in a generic way from the inputs,
- * we fully avoid returning a response bytearray. This really applies
- * only to AT, not to QCDM, so we shouldn't be worried of losing chunks
- * of the next QCDM message. And given that the caller won't get the
- * response array, we're the ones in charge of removing the processed
- * data (otherwise ERROR replies may get fed to the next response
- * parser).
- */
+ /* Complete the command context with the appropriate result */
+ if (error)
g_simple_async_result_set_from_error (ctx->result, error);
- g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len);
- } else {
+ else {
if (ctx->allow_cached)
- port_serial_set_cached_reply (self, ctx->command, self->priv->response);
-
- /* Upon completion, it is a task of the caller to remove from the response
- * buffer the processed data. This may seem unnecessary in the case of AT
- * commands, as it'll remove the full received string, but the step is
- * a key thing in the case of QCDM, where we want to read just until the
- * next marker, not more. */
+ port_serial_set_cached_reply (self, ctx->command, parsed_response);
g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_byte_array_ref (self->priv->response),
- (GDestroyNotify)g_byte_array_unref);
+ g_byte_array_ref (parsed_response),
+ (GDestroyNotify) g_byte_array_unref);
}
/* Don't complete in idle. We need the caller remove the response range which
@@ -767,7 +757,7 @@ port_serial_timed_out (gpointer data)
error = g_error_new_literal (MM_SERIAL_ERROR,
MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
"Serial command timed out");
- port_serial_got_response (self, error);
+ port_serial_got_response (self, NULL, error);
g_error_free (error);
/* Emit a timed out signal, used by upper layers to identify a disconnected
@@ -792,7 +782,7 @@ port_serial_response_wait_cancelled (GCancellable *cancellable,
error = g_error_new_literal (MM_CORE_ERROR,
MM_CORE_ERROR_CANCELLED,
"Waiting for the reply cancelled");
- port_serial_got_response (self, error);
+ port_serial_got_response (self, NULL, error);
g_error_free (error);
}
@@ -814,18 +804,12 @@ port_serial_queue_process (gpointer data)
cached = port_serial_get_cached_reply (self, ctx->command);
if (cached) {
- /* Ensure the response array is fully empty before setting the
- * cached response. */
- if (self->priv->response->len > 0) {
- mm_warn ("(%s) response array is not empty when using cached "
- "reply, cleaning up %u bytes",
- mm_port_get_device (MM_PORT (self)),
- self->priv->response->len);
- g_byte_array_set_size (self->priv->response, 0);
- }
+ GByteArray *parsed_response;
- g_byte_array_append (self->priv->response, cached->data, cached->len);
- port_serial_got_response (self, NULL);
+ parsed_response = g_byte_array_sized_new (cached->len);
+ g_byte_array_append (parsed_response, cached->data, cached->len);
+ port_serial_got_response (self, parsed_response, NULL);
+ g_byte_array_unref (parsed_response);
return G_SOURCE_REMOVE;
}
@@ -834,7 +818,7 @@ port_serial_queue_process (gpointer data)
/* If error, report it */
if (!port_serial_process_command (self, ctx, &error)) {
- port_serial_got_response (self, error);
+ port_serial_got_response (self, NULL, error);
g_error_free (error);
return G_SOURCE_REMOVE;
}
@@ -860,7 +844,7 @@ port_serial_queue_process (gpointer data)
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_CANCELLED,
"Won't wait for the reply");
- port_serial_got_response (self, error);
+ port_serial_got_response (self, NULL, error);
g_error_free (error);
return G_SOURCE_REMOVE;
}
@@ -873,16 +857,50 @@ port_serial_queue_process (gpointer data)
return G_SOURCE_REMOVE;
}
-static gboolean
-parse_response (MMPortSerial *self,
- GByteArray *response,
- GError **error)
+static void
+parse_response_buffer (MMPortSerial *self)
{
- if (MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited)
- MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited (self, response);
+ GError *error = NULL;
+ GByteArray *parsed_response = NULL;
- g_return_val_if_fail (MM_PORT_SERIAL_GET_CLASS (self)->parse_response, FALSE);
- return MM_PORT_SERIAL_GET_CLASS (self)->parse_response (self, response, error);
+ /* Parse unsolicited messages in the subclass.
+ *
+ * If any message found, it's processed immediately and the message is
+ * removed from the response buffer.
+ */
+ if (MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited)
+ MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited (self,
+ self->priv->response);
+
+ /* Parse response in the subclass.
+ *
+ * Returns TRUE either if an error is provided or if we really have the
+ * response to process. The parsed string is returned already out of the
+ * response buffer, and the response buffer is cleaned up accordingly.
+ */
+ g_assert (MM_PORT_SERIAL_GET_CLASS (self)->parse_response != NULL);
+ switch (MM_PORT_SERIAL_GET_CLASS (self)->parse_response (self,
+ self->priv->response,
+ &parsed_response,
+ &error)) {
+ case MM_PORT_SERIAL_RESPONSE_BUFFER:
+ /* We have a valid response to process */
+ g_assert (parsed_response);
+ self->priv->n_consecutive_timeouts = 0;
+ port_serial_got_response (self, parsed_response, NULL);
+ g_byte_array_unref (parsed_response);
+ break;
+ case MM_PORT_SERIAL_RESPONSE_ERROR:
+ /* We have an error to process */
+ g_assert (error);
+ self->priv->n_consecutive_timeouts = 0;
+ port_serial_got_response (self, NULL, error);
+ g_error_free (error);
+ break;
+ case MM_PORT_SERIAL_RESPONSE_NONE:
+ /* Nothing to do this time */
+ break;
+ }
}
static gboolean
@@ -954,8 +972,6 @@ common_input_available (MMPortSerial *self,
status = G_IO_STATUS_NORMAL;
}
-
-
/* If no bytes read, just wait for more data */
if (bytes_read == 0)
break;
@@ -971,15 +987,9 @@ common_input_available (MMPortSerial *self,
g_byte_array_remove_range (self->priv->response, 0, (SERIAL_BUF_SIZE / 2));
}
- /* Parse response. Returns TRUE either if an error is provided or if
- * we really have the response to process. */
- if (parse_response (self, self->priv->response, &error)) {
- /* Reset number of consecutive timeouts only here */
- self->priv->n_consecutive_timeouts = 0;
- /* Process response retrieved */
- port_serial_got_response (self, error);
- g_clear_error (&error);
- }
+ /* See if we can parse anything */
+ parse_response_buffer (self);
+
} while ( (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN)
&& (self->priv->iochannel_id > 0 || self->priv->socket_source != NULL));