diff options
Diffstat (limited to 'src/mm-serial-port.c')
-rw-r--r-- | src/mm-serial-port.c | 206 |
1 files changed, 138 insertions, 68 deletions
diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c index bf2a98a..18a616d 100644 --- a/src/mm-serial-port.c +++ b/src/mm-serial-port.c @@ -44,6 +44,7 @@ enum { PROP_STOPBITS, PROP_SEND_DELAY, PROP_FD, + PROP_SPEW_CONTROL, LAST_PROP }; @@ -67,8 +68,9 @@ typedef struct { char parity; guint stopbits; guint64 send_delay; + gboolean spew_control; - guint queue_schedule; + guint queue_id; guint watch_id; guint timeout_id; @@ -76,6 +78,18 @@ typedef struct { guint connected_id; } MMSerialPortPrivate; +typedef struct { + GByteArray *command; + guint32 idx; + guint32 eagain_count; + gboolean started; + gboolean done; + GCallback callback; + gpointer user_data; + guint32 timeout; + gboolean cached; +} MMQueueData; + #if 0 static const char * baud_to_string (int baud) @@ -355,57 +369,67 @@ serial_debug (MMSerialPort *self, const char *prefix, const char *buf, gsize len } static gboolean -mm_serial_port_send_command (MMSerialPort *self, - GByteArray *command, - GError **error) +mm_serial_port_process_command (MMSerialPort *self, + MMQueueData *info, + GError **error) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - int status, i = 0; - int eagain_count = 1000; const guint8 *p; + int status, expected_status, send_len; if (priv->fd < 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, - "%s", "Sending command failed: device is not enabled"); + g_set_error_literal (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, + "Sending command failed: device is not enabled"); return FALSE; } if (mm_port_get_connected (MM_PORT (self))) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, - "%s", "Sending command failed: device is connected"); + g_set_error_literal (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, + "Sending command failed: device is connected"); return FALSE; } - serial_debug (self, "-->", (const char *) command->data, command->len); - - /* Only accept about 3 seconds of EAGAIN */ - if (priv->send_delay > 0) - eagain_count = 3000000 / priv->send_delay; - - while (i < command->len) { - p = &command->data[i]; - status = write (priv->fd, p, 1); - if (status < 0) { - if (errno == EAGAIN) { - eagain_count--; - if (eagain_count <= 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, - "Sending command failed: '%s'", strerror (errno)); - break; - } - } else { + /* 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 (priv->send_delay == 0) { + /* Send the whole command in one write */ + send_len = expected_status = info->command->len; + p = info->command->data; + } else { + /* Send just one byte of the command */ + send_len = expected_status = 1; + p = &info->command->data[info->idx]; + } + + /* Send a single byte of the command */ + errno = 0; + status = write (priv->fd, p, send_len); + if (status > 0) + info->idx += status; + else { + /* Error or no bytes written */ + if (errno == EAGAIN || status == 0) { + info->eagain_count--; + if (info->eagain_count <= 0) { g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: '%s'", strerror (errno)); - break; + return FALSE; } - } else - i++; - - if (priv->send_delay) - usleep (priv->send_delay); + } else { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, + "Sending command failed: '%s'", strerror (errno)); + return FALSE; + } } - return i == command->len; + if (info->idx >= info->command->len) + info->done = TRUE; + + return TRUE; } static void @@ -436,35 +460,25 @@ mm_serial_port_get_cached_reply (MMSerialPort *self, GByteArray *command) return (const GByteArray *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); } -typedef struct { - GByteArray *command; - GCallback callback; - gpointer user_data; - guint32 timeout; - gboolean cached; -} MMQueueData; - static void -mm_serial_port_schedule_queue_process (MMSerialPort *self) +mm_serial_port_schedule_queue_process (MMSerialPort *self, guint timeout_ms) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - GSource *source; if (priv->timeout_id) { /* A command is already in progress */ return; } - if (priv->queue_schedule) { + if (priv->queue_id) { /* Already scheduled */ return; } - source = g_idle_source_new (); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_queue_process), G_OBJECT (self))); - g_source_attach (source, NULL); - priv->queue_schedule = g_source_get_id (source); - g_source_unref (source); + if (timeout_ms) + priv->queue_id = g_timeout_add (timeout_ms, mm_serial_port_queue_process, self); + else + priv->queue_id = g_idle_add (mm_serial_port_queue_process, self); } static gsize @@ -516,7 +530,7 @@ mm_serial_port_got_response (MMSerialPort *self, GError *error) if (consumed) g_byte_array_remove_range (priv->response, 0, consumed); if (!g_queue_is_empty (priv->queue)) - mm_serial_port_schedule_queue_process (self); + mm_serial_port_schedule_queue_process (self, 0); } static gboolean @@ -547,7 +561,7 @@ mm_serial_port_queue_process (gpointer data) MMQueueData *info; GError *error = NULL; - priv->queue_schedule = 0; + priv->queue_id = 0; info = (MMQueueData *) g_queue_peek_head (priv->queue); if (!info) @@ -557,23 +571,35 @@ mm_serial_port_queue_process (gpointer data) const GByteArray *cached = mm_serial_port_get_cached_reply (self, info->command); if (cached) { + /* Ensure the response array is fully empty before setting the + * cached response. */ + if (priv->response->len > 0) { + g_warning ("%s: (%s) response array is not empty when using " + "cached reply, cleaning up %u bytes", + __func__, + mm_port_get_device (MM_PORT (self)), + priv->response->len); + g_byte_array_set_size (priv->response, 0); + } + g_byte_array_append (priv->response, cached->data, cached->len); mm_serial_port_got_response (self, NULL); return FALSE; } } - if (mm_serial_port_send_command (self, info->command, &error)) { - GSource *source; - - source = g_timeout_source_new_seconds (info->timeout); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_timed_out), G_OBJECT (self))); - g_source_attach (source, NULL); - priv->timeout_id = g_source_get_id (source); - g_source_unref (source); - } else { + if (mm_serial_port_process_command (self, info, &error)) { + if (info->done) { + /* If the command is finished being sent, schedule the timeout */ + priv->timeout_id = g_timeout_add_seconds (info->timeout, + mm_serial_port_timed_out, + self); + } else { + /* Schedule the next byte of the command to be sent */ + mm_serial_port_schedule_queue_process (self, priv->send_delay / 1000); + } + } else mm_serial_port_got_response (self, error); - } return FALSE; } @@ -600,6 +626,7 @@ data_available (GIOChannel *source, char buf[SERIAL_BUF_SIZE + 1]; gsize bytes_read; GIOStatus status; + MMQueueData *info; if (condition & G_IO_HUP) { if (priv->response->len) @@ -614,6 +641,11 @@ data_available (GIOChannel *source, return TRUE; } + /* 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)) + return TRUE; + do { GError *err = NULL; @@ -638,7 +670,7 @@ data_available (GIOChannel *source, } /* Make sure the response doesn't grow too long */ - if (priv->response->len > SERIAL_BUF_SIZE) { + if ((priv->response->len > SERIAL_BUF_SIZE) && priv->spew_control) { /* Notify listeners and then trim the buffer */ g_signal_emit_by_name (self, "buffer-full", priv->response); g_byte_array_remove_range (priv->response, 0, (SERIAL_BUF_SIZE / 2)); @@ -805,6 +837,8 @@ mm_serial_port_close (MMSerialPort *self) priv->connected_id = 0; } + mm_serial_port_flash_cancel (self); + if (priv->fd >= 0) { GTimeVal tv_start, tv_end; @@ -820,8 +854,6 @@ mm_serial_port_close (MMSerialPort *self) priv->channel = NULL; } - mm_serial_port_flash_cancel (self); - g_get_current_time (&tv_start); tcsetattr (priv->fd, TCSANOW, &priv->old_t); @@ -869,6 +901,16 @@ mm_serial_port_close (MMSerialPort *self) g_slice_free (MMQueueData, item); } g_queue_clear (priv->queue); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + if (priv->queue_id) { + g_source_remove (priv->queue_id); + priv->queue_id = 0; + } } void @@ -909,6 +951,13 @@ internal_queue_command (MMSerialPort *self, 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->callback = (GCallback) callback; @@ -921,7 +970,7 @@ internal_queue_command (MMSerialPort *self, g_queue_push_tail (priv->queue, info); if (g_queue_get_length (priv->queue) == 1) - mm_serial_port_schedule_queue_process (self); + mm_serial_port_schedule_queue_process (self, 0); } void @@ -1224,6 +1273,9 @@ set_property (GObject *object, guint prop_id, case PROP_SEND_DELAY: priv->send_delay = g_value_get_uint64 (value); break; + case PROP_SPEW_CONTROL: + priv->spew_control = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1255,6 +1307,9 @@ get_property (GObject *object, guint prop_id, case PROP_SEND_DELAY: g_value_set_uint64 (value, priv->send_delay); break; + case PROP_SPEW_CONTROL: + g_value_set_boolean (value, priv->spew_control); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1264,6 +1319,13 @@ get_property (GObject *object, guint prop_id, static void dispose (GObject *object) { + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + if (mm_serial_port_is_open (MM_SERIAL_PORT (object))) mm_serial_port_close_force (MM_SERIAL_PORT (object)); @@ -1346,10 +1408,18 @@ mm_serial_port_class_init (MMSerialPortClass *klass) (object_class, PROP_SEND_DELAY, g_param_spec_uint64 (MM_SERIAL_PORT_SEND_DELAY, "SendDelay", - "Send delay", + "Send delay for each byte in microseconds", 0, G_MAXUINT64, 0, G_PARAM_READWRITE)); + g_object_class_install_property + (object_class, PROP_SPEW_CONTROL, + g_param_spec_boolean (MM_SERIAL_PORT_SPEW_CONTROL, + "SpewControl", + "Spew control", + FALSE, + G_PARAM_READWRITE)); + /* Signals */ g_signal_new ("buffer-full", G_OBJECT_CLASS_TYPE (object_class), |