aboutsummaryrefslogtreecommitdiff
path: root/src/mm-serial-port.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-serial-port.c')
-rw-r--r--src/mm-serial-port.c206
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),