summaryrefslogtreecommitdiff
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.c1025
1 files changed, 560 insertions, 465 deletions
diff --git a/src/mm-port-serial.c b/src/mm-port-serial.c
index 3af1b8e2..8b6e34ec 100644
--- a/src/mm-port-serial.c
+++ b/src/mm-port-serial.c
@@ -34,7 +34,8 @@
#include <mm-errors-types.h>
#include "mm-port-serial.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
+#include "mm-helper-enums-types.h"
static gboolean port_serial_queue_process (gpointer data);
static void port_serial_schedule_queue_process (MMPortSerial *self,
@@ -53,10 +54,10 @@ enum {
PROP_BITS,
PROP_PARITY,
PROP_STOPBITS,
+ PROP_FLOW_CONTROL,
PROP_SEND_DELAY,
PROP_FD,
PROP_SPEW_CONTROL,
- PROP_RTS_CTS,
PROP_FLASH_OK,
LAST_PROP
@@ -90,15 +91,14 @@ struct _MMPortSerialPrivate {
GSocket *socket;
GSource *socket_source;
- struct termios old_t;
guint baud;
guint bits;
char parity;
guint stopbits;
+ MMFlowControl flow_control;
guint64 send_delay;
gboolean spew_control;
- gboolean rts_cts;
gboolean flash_ok;
guint queue_id;
@@ -111,8 +111,8 @@ struct _MMPortSerialPrivate {
guint connected_id;
- gpointer flash_ctx;
- gpointer reopen_ctx;
+ GTask *flash_task;
+ GTask *reopen_task;
};
/*****************************************************************************/
@@ -163,6 +163,7 @@ mm_port_serial_command (MMPortSerial *self,
GByteArray *command,
guint32 timeout_seconds,
gboolean allow_cached,
+ gboolean run_next,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -203,7 +204,12 @@ mm_port_serial_command (MMPortSerial *self,
if (!allow_cached)
port_serial_set_cached_reply (self, ctx->command, NULL);
- g_queue_push_tail (self->priv->queue, ctx);
+ /* If requested to run next, push to the head of the queue so that it really is
+ * the next one sent */
+ if (run_next)
+ g_queue_push_head (self->priv->queue, ctx);
+ else
+ g_queue_push_tail (self->priv->queue, ctx);
if (g_queue_get_length (self->priv->queue) == 1)
port_serial_schedule_queue_process (self, 0);
@@ -211,95 +217,13 @@ mm_port_serial_command (MMPortSerial *self,
/*****************************************************************************/
-#if 0
-static const char *
-baud_to_string (int baud)
-{
- const char *speed = NULL;
-
- switch (baud) {
- case B0:
- speed = "0";
- break;
- case B50:
- speed = "50";
- break;
- case B75:
- speed = "75";
- break;
- case B110:
- speed = "110";
- break;
- case B150:
- speed = "150";
- break;
- case B300:
- speed = "300";
- break;
- case B600:
- speed = "600";
- break;
- case B1200:
- speed = "1200";
- break;
- case B2400:
- speed = "2400";
- break;
- case B4800:
- speed = "4800";
- break;
- case B9600:
- speed = "9600";
- break;
- case B19200:
- speed = "19200";
- break;
- case B38400:
- speed = "38400";
- break;
- case B57600:
- speed = "57600";
- break;
- case B115200:
- speed = "115200";
- break;
- case B460800:
- speed = "460800";
- break;
- default:
- break;
- }
-
- return speed;
-}
-
-void
-mm_port_serial_print_config (MMPortSerial *port,
- const char *detail)
-{
- struct termios stbuf;
- int err;
-
- err = tcgetattr (self->priv->fd, &stbuf);
- if (err) {
- mm_warn ("*** %s (%s): (%s) tcgetattr() error %d",
- __func__, detail, mm_port_get_device (MM_PORT (port)), errno);
- return;
- }
-
- mm_info ("(%s): (%s) baud rate: %d (%s)",
- detail, mm_port_get_device (MM_PORT (port)),
- stbuf.c_cflag & CBAUD,
- baud_to_string (stbuf.c_cflag & CBAUD));
-}
-#endif
-
-static int
-parse_baudrate (guint i)
+static gboolean
+parse_baudrate (guint baudrate_num,
+ guint *out_baudrate_speed)
{
- int speed;
+ guint speed;
- switch (i) {
+ switch (baudrate_num) {
case 0:
speed = B0;
break;
@@ -345,21 +269,28 @@ parse_baudrate (guint i)
case 115200:
speed = B115200;
break;
+ case 230400:
+ speed = B230400;
+ break;
case 460800:
speed = B460800;
break;
+ case 921600:
+ speed = B921600;
+ break;
default:
- mm_warn ("Invalid baudrate '%d'", i);
- speed = B9600;
+ return FALSE;
}
- return speed;
+ if (out_baudrate_speed)
+ *out_baudrate_speed = speed;
+ return TRUE;
}
static int
parse_bits (guint i)
{
- int bits;
+ int bits = -1;
switch (i) {
case 5:
@@ -375,8 +306,7 @@ parse_bits (guint i)
bits = CS8;
break;
default:
- mm_warn ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i);
- bits = CS8;
+ g_assert_not_reached ();
}
return bits;
@@ -385,7 +315,7 @@ parse_bits (guint i)
static int
parse_parity (char c)
{
- int parity;
+ int parity = -1;
switch (c) {
case 'n':
@@ -401,8 +331,7 @@ parse_parity (char c)
parity = PARENB | PARODD;
break;
default:
- mm_warn ("Invalid parity (%c). Valid values are n, e, o", c);
- parity = 0;
+ g_assert_not_reached ();
}
return parity;
@@ -411,7 +340,7 @@ parse_parity (char c)
static int
parse_stopbits (guint i)
{
- int stopbits;
+ int stopbits = -1;
switch (i) {
case 1:
@@ -421,54 +350,145 @@ parse_stopbits (guint i)
stopbits = CSTOPB;
break;
default:
- mm_warn ("Invalid stop bits (%d). Valid values are 1 and 2)", i);
- stopbits = 0;
+ g_assert_not_reached ();
}
return stopbits;
}
static gboolean
+internal_tcsetattr (MMPortSerial *self,
+ gint fd,
+ const struct termios *options,
+ GError **error)
+{
+ guint count;
+ struct termios other;
+
+#define MAX_TCSETATTR_RETRIES 4
+
+ for (count = 0; count < MAX_TCSETATTR_RETRIES; count++) {
+ /* try to set the new port attributes */
+ errno = 0;
+ if (tcsetattr (fd, TCSANOW, options) == 0)
+ break;
+
+ /* hard error if not EAGAIN */
+ if (errno != EAGAIN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't set serial port attributes: %s", g_strerror (errno));
+ return FALSE;
+ }
+
+ /* try a few times if EAGAIN */
+ g_usleep (100000);
+ }
+
+ /* too many retries? */
+ if (count == MAX_TCSETATTR_RETRIES) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't set serial port attributes: too many retries (%u)", count);
+ return FALSE;
+ }
+
+ /* tcsetattr() returns 0 if any of the requested attributes could be set,
+ * so we should double-check that all were set and log if not. Just with
+ * debug level, as we're ignoring this issue all together anyway.
+ */
+ memset (&other, 0, sizeof (struct termios));
+ errno = 0;
+ if (tcgetattr (fd, &other) != 0)
+ mm_obj_dbg (self, "couldn't get serial port attributes after setting them: %s", g_strerror (errno));
+ else if (memcmp (options, &other, sizeof (struct termios)) != 0)
+ mm_obj_dbg (self, "port attributes not fully set");
+
+#undef MAX_TCSETATTR_RETRIES
+
+ return TRUE;
+}
+
+static gboolean
+set_flow_control_termios (MMPortSerial *self,
+ MMFlowControl flow_control,
+ struct termios *options)
+{
+ gboolean had_xon_xoff;
+ gboolean had_rts_cts;
+ tcflag_t iflag_orig, cflag_orig;
+
+ iflag_orig = options->c_iflag;
+ cflag_orig = options->c_cflag;
+
+ had_xon_xoff = !!(options->c_iflag & (IXON | IXOFF));
+ options->c_iflag &= ~(IXON | IXOFF | IXANY);
+
+ had_rts_cts = !!(options->c_cflag & (CRTSCTS));
+ options->c_cflag &= ~(CRTSCTS);
+
+ /* setup the requested flags */
+ switch (flow_control) {
+ case MM_FLOW_CONTROL_XON_XOFF:
+ mm_obj_dbg (self, "enabling XON/XOFF flow control");
+ options->c_iflag |= (IXON | IXOFF | IXANY);
+ break;
+ case MM_FLOW_CONTROL_RTS_CTS:
+ mm_obj_dbg (self, "enabling RTS/CTS flow control");
+ options->c_cflag |= (CRTSCTS);
+ break;
+ case MM_FLOW_CONTROL_NONE:
+ case MM_FLOW_CONTROL_UNKNOWN:
+ if (had_xon_xoff)
+ mm_obj_dbg (self, "disabling XON/XOFF flow control");
+ if (had_rts_cts)
+ mm_obj_dbg (self, "disabling RTS/CTS flow control");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return iflag_orig != options->c_iflag || cflag_orig != options->c_cflag;
+}
+
+static gboolean
real_config_fd (MMPortSerial *self, int fd, GError **error)
{
- struct termios stbuf, other;
- int speed;
- int bits;
- int parity;
- int stopbits;
+ struct termios stbuf;
+ guint speed;
+ gint bits;
+ gint parity;
+ gint stopbits;
/* No setup if not a tty */
if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY)
return TRUE;
- speed = parse_baudrate (self->priv->baud);
+ mm_obj_dbg (self, "setting up baudrate: %u", self->priv->baud);
+ if (!parse_baudrate (self->priv->baud, &speed) || speed == B0) {
+ mm_obj_warn (self, "baudrate invalid: %u; defaulting to 57600", self->priv->baud);
+ speed = B57600;
+ }
+
bits = parse_bits (self->priv->bits);
parity = parse_parity (self->priv->parity);
stopbits = parse_stopbits (self->priv->stopbits);
memset (&stbuf, 0, sizeof (struct termios));
- if (tcgetattr (fd, &stbuf) != 0) {
- mm_warn ("(%s): tcgetattr() error: %d",
- mm_port_get_device (MM_PORT (self)),
- errno);
- }
+ if (tcgetattr (fd, &stbuf) != 0)
+ mm_obj_warn (self, "error getting serial port attributes: %s", g_strerror (errno));
- stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY );
+ stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS);
+ stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXOFF | IXANY );
stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
- stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL);
- stbuf.c_lflag &= ~(ECHO | ECHOE);
+ stbuf.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL);
stbuf.c_cc[VMIN] = 1;
stbuf.c_cc[VTIME] = 0;
stbuf.c_cc[VEOF] = 1;
- /* Use software handshaking and ignore parity/framing errors */
- stbuf.c_iflag |= (IXON | IXOFF | IXANY | IGNPAR);
+ /* Ignore parity/framing errors */
+ stbuf.c_iflag |= IGNPAR;
- /* Set up port speed and serial attributes; also ignore modem control
- * lines since most drivers don't implement RTS/CTS anyway.
- */
- stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | CRTSCTS);
- stbuf.c_cflag |= (bits | CREAD | 0 | parity | stopbits | CLOCAL);
+ /* Set up port speed and serial attributes and enable receiver in local mode */
+ stbuf.c_cflag |= (bits | parity | stopbits | CLOCAL | CREAD);
errno = 0;
if (cfsetispeed (&stbuf, speed) != 0) {
@@ -490,36 +510,25 @@ real_config_fd (MMPortSerial *self, int fd, GError **error)
return FALSE;
}
- if (tcsetattr (fd, TCSANOW, &stbuf) < 0) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "%s: failed to set serial port attributes; errno %d",
- __func__, errno);
- return FALSE;
- }
+ if (self->priv->flow_control != MM_FLOW_CONTROL_UNKNOWN) {
+ gchar *str;
- /* tcsetattr() returns 0 if any of the requested attributes could be set,
- * so we should double-check that all were set and log a warning if not.
- */
- memset (&other, 0, sizeof (struct termios));
- errno = 0;
- if (tcgetattr (fd, &other) != 0) {
- mm_warn ("(%s): tcgetattr() error: %d",
- mm_port_get_device (MM_PORT (self)),
- errno);
- }
+ str = mm_flow_control_build_string_from_mask (self->priv->flow_control);
+ mm_obj_dbg (self, "flow control explicitly requested for device is: %s", str ? str : "unknown");
+ g_free (str);
+ } else
+ mm_obj_dbg (self, "no flow control explicitly requested for device");
- if (memcmp (&stbuf, &other, sizeof (other)) != 0) {
- mm_warn ("(%s): port attributes not fully set",
- mm_port_get_device (MM_PORT (self)));
- }
+ set_flow_control_termios (self, self->priv->flow_control, &stbuf);
- return TRUE;
+ return internal_tcsetattr (self, fd, &stbuf, error);
}
static void
-serial_debug (MMPortSerial *self, const char *prefix, const char *buf, gsize len)
+serial_debug (MMPortSerial *self,
+ const gchar *prefix,
+ const gchar *buf,
+ gsize len)
{
g_return_if_fail (len > 0);
@@ -551,7 +560,7 @@ port_serial_process_command (MMPortSerial *self,
/* Only print command the first time */
if (ctx->started == FALSE) {
ctx->started = TRUE;
- serial_debug (self, "-->", (const char *) ctx->command->data, ctx->command->len);
+ serial_debug (self, "-->", (const gchar *) ctx->command->data, ctx->command->len);
}
if (self->priv->send_delay == 0 || mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY) {
@@ -585,7 +594,8 @@ port_serial_process_command (MMPortSerial *self,
ctx->idx += written;
break;
}
- /* If written == 0, treat as EAGAIN, so fall down */
+ /* If written == 0 treat as EAGAIN */
+ /* Fall through */
case G_IO_STATUS_AGAIN:
/* We're in a non-blocking channel and therefore we're up to receive
@@ -597,10 +607,13 @@ port_serial_process_command (MMPortSerial *self,
g_signal_emit (self, signals[TIMED_OUT], 0, self->priv->n_consecutive_timeouts);
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
- "Sending command failed: '%s'", strerror (errno));
+ "Sending command failed: '%s'", g_strerror (errno));
return FALSE;
}
break;
+
+ default:
+ g_assert_not_reached ();
}
}
/* Socket based setup */
@@ -629,14 +642,14 @@ port_serial_process_command (MMPortSerial *self,
self->priv->n_consecutive_timeouts++;
g_signal_emit (self, signals[TIMED_OUT], 0, self->priv->n_consecutive_timeouts);
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
- "Sending command failed: '%s'", strerror (errno));
+ "Sending command failed: '%s'", g_strerror (errno));
return FALSE;
}
/* Just keep on, will retry... */
written = 0;
} else
- written = bytes_sent;
+ written = bytes_sent;
ctx->idx += written;
} else
@@ -696,9 +709,11 @@ 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);
@@ -714,28 +729,36 @@ port_serial_got_response (MMPortSerial *self,
g_clear_object (&self->priv->cancellable);
- ctx = (CommandContext *) g_queue_pop_head (self->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, self->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 (self->priv->response),
- (GDestroyNotify)g_byte_array_unref);
+ /* The completion of the command context may end up fully disposing the
+ * serial port object. In order to cope with that, we make sure we have
+ * our own reference to the object while the completion and follow up
+ * setup runs. */
+ g_object_ref (self);
+ {
+ CommandContext *ctx;
+
+ ctx = (CommandContext *) g_queue_pop_head (self->priv->queue);
+ if (ctx) {
+ /* Complete the command context with the appropriate result */
+ if (error)
+ g_simple_async_result_set_from_error (ctx->result, error);
+ else {
+ if (ctx->allow_cached)
+ port_serial_set_cached_reply (self, ctx->command, parsed_response);
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ 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
+ * was processed, and that must be done before processing any new queued command */
+ command_context_complete_and_free (ctx, FALSE);
}
- /* 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 (!g_queue_is_empty (self->priv->queue))
+ port_serial_schedule_queue_process (self, 0);
}
-
- if (!g_queue_is_empty (self->priv->queue))
- port_serial_schedule_queue_process (self, 0);
+ g_object_unref (self);
}
static gboolean
@@ -755,14 +778,21 @@ 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);
- g_error_free (error);
- /* Emit a timed out signal, used by upper layers to identify a disconnected
- * serial port */
- g_signal_emit (self, signals[TIMED_OUT], 0, self->priv->n_consecutive_timeouts);
+ /* Make sure we have a valid reference when emitting the signal */
+ g_object_ref (self);
+ {
+ port_serial_got_response (self, NULL, error);
- return FALSE;
+ /* Emit a timed out signal, used by upper layers to identify a disconnected
+ * serial port */
+ g_signal_emit (self, signals[TIMED_OUT], 0, self->priv->n_consecutive_timeouts);
+ }
+ g_object_unref (self);
+
+ g_error_free (error);
+
+ return G_SOURCE_REMOVE;
}
static void
@@ -777,10 +807,10 @@ port_serial_response_wait_cancelled (GCancellable *cancellable,
/* 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,
+ error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED,
"Waiting for the reply cancelled");
- port_serial_got_response (self, error);
+ /* Note: may complete last operation and unref the MMPortSerial */
+ port_serial_got_response (self, NULL, error);
g_error_free (error);
}
@@ -795,26 +825,21 @@ port_serial_queue_process (gpointer data)
ctx = (CommandContext *) g_queue_peek_head (self->priv->queue);
if (!ctx)
- return FALSE;
+ return G_SOURCE_REMOVE;
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. */
- 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);
- }
-
- g_byte_array_append (self->priv->response, cached->data, cached->len);
- port_serial_got_response (self, NULL);
- return FALSE;
+ GByteArray *parsed_response;
+
+ parsed_response = g_byte_array_sized_new (cached->len);
+ g_byte_array_append (parsed_response, cached->data, cached->len);
+ /* Note: may complete last operation and unref the MMPortSerial */
+ port_serial_got_response (self, parsed_response, NULL);
+ g_byte_array_unref (parsed_response);
+ return G_SOURCE_REMOVE;
}
/* Cached reply wasn't found, keep on */
@@ -822,9 +847,10 @@ port_serial_queue_process (gpointer data)
/* If error, report it */
if (!port_serial_process_command (self, ctx, &error)) {
- port_serial_got_response (self, error);
+ /* Note: may complete last operation and unref the MMPortSerial */
+ port_serial_got_response (self, NULL, error);
g_error_free (error);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
/* Schedule the next byte of the command to be sent */
@@ -833,44 +859,87 @@ port_serial_queue_process (gpointer data)
(mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY ?
self->priv->send_delay / 1000 :
0));
- return FALSE;
+ return G_SOURCE_REMOVE;
}
/* Setup the cancellable so that we can stop waiting for a response */
if (ctx->cancellable) {
+ gulong cancellable_id;
+
self->priv->cancellable = g_object_ref (ctx->cancellable);
- self->priv->cancellable_id = (g_cancellable_connect (
- ctx->cancellable,
- (GCallback)port_serial_response_wait_cancelled,
- self,
- NULL));
- if (!self->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;
- }
+
+ /* If the GCancellable is already cancelled here, the callback will be
+ * called right away, and a GError will be propagated as response. In
+ * this case we need to completely avoid doing anything else with the
+ * MMPortSerial, as it may already be disposed.
+ * So, use an intermediate variable to store the cancellable id, and
+ * just return without further processing if we're already cancelled.
+ */
+ cancellable_id = g_cancellable_connect (ctx->cancellable,
+ (GCallback)port_serial_response_wait_cancelled,
+ self,
+ NULL);
+ if (!cancellable_id)
+ return G_SOURCE_REMOVE;
+
+ self->priv->cancellable_id = cancellable_id;
}
/* If the command is finished being sent, schedule the timeout */
self->priv->timeout_id = g_timeout_add_seconds (ctx->timeout,
port_serial_timed_out,
self);
- return FALSE;
+ 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;
+ /* Note: may complete last operation and unref the MMPortSerial */
+ 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;
+ /* Note: may complete last operation and unref the MMPortSerial */
+ port_serial_got_response (self, NULL, error);
+ g_error_free (error);
+ break;
+ case MM_PORT_SERIAL_RESPONSE_NONE:
+ /* Nothing to do this time */
+ break;
+ default:
+ g_assert_not_reached ();
+ }
}
static gboolean
@@ -881,31 +950,30 @@ common_input_available (MMPortSerial *self,
gsize bytes_read;
GIOStatus status = G_IO_STATUS_NORMAL;
CommandContext *ctx;
- const char *device;
GError *error = NULL;
+ gboolean iterate = TRUE;
+ gboolean keep_source = G_SOURCE_CONTINUE;
if (condition & G_IO_HUP) {
- device = mm_port_get_device (MM_PORT (self));
- mm_dbg ("(%s) unexpected port hangup!", device);
-
+ mm_obj_dbg (self, "unexpected port hangup!");
if (self->priv->response->len)
g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len);
port_serial_close_force (self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
if (condition & G_IO_ERR) {
if (self->priv->response->len)
g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len);
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
/* Don't read any input if the current command isn't done being sent yet */
ctx = g_queue_peek_nth (self->priv->queue, 0);
if (ctx && (ctx->started == TRUE) && (ctx->done == FALSE))
- return TRUE;
+ return G_SOURCE_CONTINUE;
- do {
+ while (iterate) {
bytes_read = 0;
if (self->priv->iochannel) {
@@ -915,35 +983,32 @@ common_input_available (MMPortSerial *self,
&bytes_read,
&error);
if (status == G_IO_STATUS_ERROR) {
- if (error) {
- mm_warn ("(%s): read error: %s",
- mm_port_get_device (MM_PORT (self)),
- error->message);
- }
+ if (error)
+ mm_obj_warn (self, "read error: %s", error->message);
g_clear_error (&error);
}
} else if (self->priv->socket) {
- bytes_read = g_socket_receive (self->priv->socket,
- buf,
- SERIAL_BUF_SIZE,
- NULL, /* cancellable */
- &error);
- if (bytes_read == -1) {
+ gssize sbytes_read;
+
+ sbytes_read = g_socket_receive (self->priv->socket,
+ buf,
+ SERIAL_BUF_SIZE,
+ NULL, /* cancellable */
+ &error);
+ if (sbytes_read < 0) {
bytes_read = 0;
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
status = G_IO_STATUS_AGAIN;
else
status = G_IO_STATUS_ERROR;
- mm_warn ("(%s): receive error: %s",
- mm_port_get_device (MM_PORT (self)),
- error->message);
+ mm_obj_warn (self, "receive error: %s", error->message);
g_clear_error (&error);
- } else
+ } else {
+ bytes_read = (gsize) sbytes_read;
status = G_IO_STATUS_NORMAL;
+ }
}
-
-
/* If no bytes read, just wait for more data */
if (bytes_read == 0)
break;
@@ -959,19 +1024,30 @@ 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. The response parsing may actually
+ * schedule the completion of a serial command, and that in turn may end
+ * up fully disposing this serial port object. In order to cope with
+ * that we make sure we have our own reference to the object while the
+ * response buffer operation is run, and then we check ourselves whether
+ * we should be keeping this socket/iochannel source or not. */
+ g_object_ref (self);
+ {
+ parse_response_buffer (self);
+
+ /* If we didn't end up closing the iochannel/socket in the previous
+ * operation, we keep this source. */
+ keep_source = ((self->priv->iochannel_id > 0 || self->priv->socket_source != NULL) ?
+ G_SOURCE_CONTINUE : G_SOURCE_REMOVE);
+
+ /* If we're keeping the source and we still may have bytes to read,
+ * iterate. */
+ iterate = ((keep_source == G_SOURCE_CONTINUE) &&
+ (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN));
}
- } while ( (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN)
- && (self->priv->iochannel_id > 0 || self->priv->socket_source != NULL));
+ g_object_unref (self);
+ }
- return TRUE;
+ return keep_source;
}
static gboolean
@@ -996,7 +1072,6 @@ data_watch_enable (MMPortSerial *self, gboolean enable)
if (self->priv->iochannel_id) {
if (enable)
g_warn_if_fail (self->priv->iochannel_id == 0);
-
g_source_remove (self->priv->iochannel_id);
self->priv->iochannel_id = 0;
}
@@ -1045,11 +1120,9 @@ port_connected (MMPortSerial *self, GParamSpec *pspec, gpointer user_data)
connected = mm_port_get_connected (MM_PORT (self));
if (self->priv->fd >= 0 && ioctl (self->priv->fd, (connected ? TIOCNXCL : TIOCEXCL)) < 0) {
- mm_warn ("(%s): could not %s serial port lock: (%d) %s",
- mm_port_get_device (MM_PORT (self)),
- connected ? "drop" : "re-acquire",
- errno,
- strerror (errno));
+ mm_obj_warn (self, "could not %s serial port lock: %s",
+ connected ? "drop" : "re-acquire",
+ g_strerror (errno));
if (!connected) {
// FIXME: do something here, maybe try again in a few seconds or
// close the port and error out?
@@ -1082,7 +1155,7 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
return FALSE;
}
- if (self->priv->reopen_ctx) {
+ if (self->priv->reopen_task) {
g_set_error (error,
MM_SERIAL_ERROR,
MM_SERIAL_ERROR_OPEN_FAILED,
@@ -1091,12 +1164,21 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
return FALSE;
}
+ if (mm_port_get_connected (MM_PORT (self))) {
+ g_set_error (error,
+ MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_OPEN_FAILED,
+ "Could not open serial device %s: port is connected",
+ device);
+ return FALSE;
+ }
+
if (self->priv->open_count) {
/* Already open */
goto success;
}
- mm_dbg ("(%s) opening serial port...", device);
+ mm_obj_dbg (self, "opening serial port...");
g_get_current_time (&tv_start);
@@ -1120,7 +1202,6 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
MM_SERIAL_ERROR,
(errno == ENODEV) ? MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE : MM_SERIAL_ERROR_OPEN_FAILED,
"Could not open serial device %s: %s", device, strerror (errno_save));
- mm_warn ("(%s) could not open serial device (%d)", device, errno_save);
return FALSE;
}
}
@@ -1132,21 +1213,12 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
errno_save = errno;
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED,
"Could not lock serial device %s: %s", device, strerror (errno_save));
- mm_warn ("(%s) could not lock serial device (%d)", device, errno_save);
goto error;
}
/* Flush any waiting IO */
tcflush (self->priv->fd, TCIOFLUSH);
- if (tcgetattr (self->priv->fd, &self->priv->old_t) < 0) {
- errno_save = errno;
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED,
- "Could not set attributes on serial device %s: %s", device, strerror (errno_save));
- mm_warn ("(%s) could not set attributes on serial device (%d)", device, errno_save);
- goto error;
- }
-
/* Don't wait for pending data when closing the port; this can cause some
* stupid devices that don't respond to URBs on a particular port to hang
* for 30 seconds when probing fails. See GNOME bug #630670.
@@ -1154,21 +1226,21 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
if (ioctl (self->priv->fd, TIOCGSERIAL, &sinfo) == 0) {
sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE;
if (ioctl (self->priv->fd, TIOCSSERIAL, &sinfo) < 0)
- mm_warn ("(%s): couldn't set serial port closing_wait to none: %s",
- device, g_strerror (errno));
+ mm_obj_warn (self, "couldn't set serial port closing_wait to none: %s", g_strerror (errno));
}
}
g_warn_if_fail (MM_PORT_SERIAL_GET_CLASS (self)->config_fd);
- if (self->priv->fd >= 0 && !MM_PORT_SERIAL_GET_CLASS (self)->config_fd (self, self->priv->fd, error)) {
- mm_dbg ("(%s) failed to configure serial device", device);
+ if (self->priv->fd >= 0 && mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_WWAN &&
+ !MM_PORT_SERIAL_GET_CLASS (self)->config_fd (self, self->priv->fd, error)) {
+ mm_obj_dbg (self, "failed to configure serial device");
goto error;
}
g_get_current_time (&tv_end);
if (tv_end.tv_sec - tv_start.tv_sec > 7)
- mm_warn ("(%s): open blocked by driver for more than 7 seconds!", device);
+ mm_obj_warn (self, "open blocked by driver for more than 7 seconds!");
if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_UNIX) {
/* Create new GIOChannel */
@@ -1234,7 +1306,7 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
success:
self->priv->open_count++;
- mm_dbg ("(%s) device open count is %d (open)", device, self->priv->open_count);
+ mm_obj_dbg (self, "device open count is %d (open)", self->priv->open_count);
/* Run additional port config if just opened */
if (self->priv->open_count == 1 && MM_PORT_SERIAL_GET_CLASS (self)->config)
@@ -1243,10 +1315,9 @@ success:
return TRUE;
error:
- mm_warn ("(%s) failed to open serial device", device);
+ mm_obj_warn (self, "failed to open serial device");
if (self->priv->iochannel) {
- g_io_channel_shutdown (self->priv->iochannel, FALSE, NULL);
g_io_channel_unref (self->priv->iochannel);
self->priv->iochannel = NULL;
}
@@ -1277,8 +1348,7 @@ mm_port_serial_is_open (MMPortSerial *self)
static void
_close_internal (MMPortSerial *self, gboolean force)
{
- const char *device;
- int i;
+ guint i;
g_return_if_fail (MM_IS_PORT_SERIAL (self));
@@ -1289,15 +1359,16 @@ _close_internal (MMPortSerial *self, gboolean force)
self->priv->open_count--;
}
- device = mm_port_get_device (MM_PORT (self));
-
- mm_dbg ("(%s) device open count is %d (close)", device, self->priv->open_count);
+ mm_obj_dbg (self, "device open count is %d (close)", self->priv->open_count);
if (self->priv->open_count > 0)
return;
if (self->priv->connected_id) {
- g_signal_handler_disconnect (self, self->priv->connected_id);
+ /* Don't assume it's always connected, because it may be automatically connected during
+ * object disposal, and this method is also called in finalize() */
+ if (g_signal_handler_is_connected (self, self->priv->connected_id))
+ g_signal_handler_disconnect (self, self->priv->connected_id);
self->priv->connected_id = 0;
}
@@ -1307,7 +1378,7 @@ _close_internal (MMPortSerial *self, gboolean force)
GTimeVal tv_start, tv_end;
struct serial_struct sinfo = { 0 };
- mm_dbg ("(%s) closing serial port...", device);
+ mm_obj_dbg (self, "closing serial port...");
mm_port_set_connected (MM_PORT (self), FALSE);
@@ -1320,22 +1391,22 @@ _close_internal (MMPortSerial *self, gboolean force)
*/
if (ioctl (self->priv->fd, TIOCGSERIAL, &sinfo) == 0) {
if (sinfo.closing_wait != ASYNC_CLOSING_WAIT_NONE) {
- mm_warn ("(%s): serial port closing_wait was reset!", device);
+ mm_obj_warn (self, "serial port closing_wait was reset!");
sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE;
if (ioctl (self->priv->fd, TIOCSSERIAL, &sinfo) < 0)
- mm_warn ("(%s): couldn't set serial port closing_wait to none: %s",
- device, g_strerror (errno));
+ mm_obj_warn (self, "couldn't set serial port closing_wait to none: %s", g_strerror (errno));
}
}
- tcsetattr (self->priv->fd, TCSANOW, &self->priv->old_t);
tcflush (self->priv->fd, TCIOFLUSH);
}
/* Destroy channel */
if (self->priv->iochannel) {
data_watch_enable (self, FALSE);
- g_io_channel_shutdown (self->priv->iochannel, TRUE, NULL);
+ /* unref() without g_io_channel_shutdown() to destroy the channel
+ * without closing the fd. The close() is called explicitly after.
+ */
g_io_channel_unref (self->priv->iochannel);
self->priv->iochannel = NULL;
}
@@ -1356,7 +1427,7 @@ _close_internal (MMPortSerial *self, gboolean force)
g_get_current_time (&tv_end);
- mm_dbg ("(%s) serial port closed", device);
+ mm_obj_dbg (self, "serial port closed");
/* Some ports don't respond to data and when close is called
* the serial layer waits up to 30 second (closing_wait) for
@@ -1364,7 +1435,7 @@ _close_internal (MMPortSerial *self, gboolean force)
* Log that. See GNOME bug #630670 for more details.
*/
if (tv_end.tv_sec - tv_start.tv_sec > 7)
- mm_warn ("(%s): close blocked by driver for more than 7 seconds!", device);
+ mm_obj_warn (self, "close blocked by driver for more than 7 seconds!");
}
/* Clear the command queue */
@@ -1418,7 +1489,7 @@ port_serial_close_force (MMPortSerial *self)
if (self->priv->forced_close)
return;
- mm_dbg ("(%s) forced to close port", mm_port_get_device (MM_PORT (self)));
+ mm_obj_dbg (self, "forced to close port");
/* Mark as having forced the close, so that we don't warn about incorrect
* open counts */
@@ -1440,20 +1511,15 @@ port_serial_close_force (MMPortSerial *self)
/* Reopen */
typedef struct {
- MMPortSerial *self;
- GSimpleAsyncResult *result;
guint initial_open_count;
guint reopen_id;
} ReopenContext;
static void
-reopen_context_complete_and_free (ReopenContext *ctx)
+reopen_context_free (ReopenContext *ctx)
{
if (ctx->reopen_id)
g_source_remove (ctx->reopen_id);
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_slice_free (ReopenContext, ctx);
}
@@ -1462,56 +1528,64 @@ mm_port_serial_reopen_finish (MMPortSerial *port,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
port_serial_reopen_cancel (MMPortSerial *self)
{
- ReopenContext *ctx;
+ GTask *task;
- if (!self->priv->reopen_ctx)
+ if (!self->priv->reopen_task)
return;
- /* Recover context */
- ctx = (ReopenContext *)self->priv->reopen_ctx;
- self->priv->reopen_ctx = NULL;
+ /* Recover task */
+ task = self->priv->reopen_task;
+ self->priv->reopen_task = NULL;
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Reopen cancelled");
- reopen_context_complete_and_free (ctx);
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Reopen cancelled");
+ g_object_unref (task);
}
static gboolean
reopen_do (MMPortSerial *self)
{
+ GTask *task;
ReopenContext *ctx;
GError *error = NULL;
guint i;
- /* Recover context */
- g_assert (self->priv->reopen_ctx != NULL);
- ctx = (ReopenContext *)self->priv->reopen_ctx;
- self->priv->reopen_ctx = NULL;
+ /* Recover task */
+ g_assert (self->priv->reopen_task != NULL);
+ task = self->priv->reopen_task;
+ self->priv->reopen_task = NULL;
+ ctx = g_task_get_task_data (task);
ctx->reopen_id = 0;
for (i = 0; i < ctx->initial_open_count; i++) {
- if (!mm_port_serial_open (ctx->self, &error)) {
+ if (!mm_port_serial_open (self, &error)) {
g_prefix_error (&error, "Couldn't reopen port (%u): ", i);
break;
}
}
- if (error)
- g_simple_async_result_take_error (ctx->result, error);
- else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- reopen_context_complete_and_free (ctx);
+ if (error) {
+ /* An error during port reopening may mean that the device is
+ * already gone. Note that we won't get a HUP in the TTY when
+ * the port is gone during the reopen wait time, because there's
+ * no channel I/O monitoring in place.
+ *
+ * If we ever see this, we'll flag the port as forced close right
+ * away, because the open count would anyway be broken afterwards.
+ */
+ port_serial_close_force (self);
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
void
@@ -1521,41 +1595,38 @@ mm_port_serial_reopen (MMPortSerial *self,
gpointer user_data)
{
ReopenContext *ctx;
+ GTask *task;
guint i;
g_return_if_fail (MM_IS_PORT_SERIAL (self));
/* Setup context */
ctx = g_slice_new0 (ReopenContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_port_serial_reopen);
ctx->initial_open_count = self->priv->open_count;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)reopen_context_free);
+
if (self->priv->forced_close) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Serial port has been forced close.");
- reopen_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Serial port has been forced close.");
+ g_object_unref (task);
return;
}
/* If already reopening, halt */
- if (self->priv->reopen_ctx) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "Modem is already being reopened");
- reopen_context_complete_and_free (ctx);
+ if (self->priv->reopen_task) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "Modem is already being reopened");
+ g_object_unref (task);
return;
}
- mm_dbg ("(%s) reopening port (%u)",
- mm_port_get_device (MM_PORT (self)),
- ctx->initial_open_count);
+ mm_obj_dbg (self, "reopening port (%u)", ctx->initial_open_count);
for (i = 0; i < ctx->initial_open_count; i++)
mm_port_serial_close (self);
@@ -1566,7 +1637,7 @@ mm_port_serial_reopen (MMPortSerial *self,
ctx->reopen_id = g_idle_add ((GSourceFunc)reopen_do, self);
/* Store context in private info */
- self->priv->reopen_ctx = ctx;
+ self->priv->reopen_task = task;
}
static gboolean
@@ -1594,15 +1665,11 @@ static gboolean
set_speed (MMPortSerial *self, speed_t speed, GError **error)
{
struct termios options;
- int fd, count = 4;
- gboolean success = FALSE;
g_assert (self->priv->fd >= 0);
- fd = self->priv->fd;
-
memset (&options, 0, sizeof (struct termios));
- if (tcgetattr (fd, &options) != 0) {
+ if (tcgetattr (self->priv->fd, &options) != 0) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
@@ -1615,60 +1682,22 @@ set_speed (MMPortSerial *self, speed_t speed, GError **error)
cfsetospeed (&options, speed);
options.c_cflag |= (CLOCAL | CREAD);
- /* Configure flow control as well here */
- if (self->priv->rts_cts)
- options.c_cflag |= (CRTSCTS);
-
- while (count-- > 0) {
- if (tcsetattr (fd, TCSANOW, &options) == 0) {
- success = TRUE;
- break; /* Operation successful */
- }
-
- /* Try a few times if EAGAIN */
- if (errno == EAGAIN)
- g_usleep (100000);
- else {
- /* If not EAGAIN, hard error */
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "%s: tcsetattr() error %d",
- __func__, errno);
- return FALSE;
- }
- }
-
- if (!success) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "%s: tcsetattr() retry timeout",
- __func__);
- return FALSE;
- }
-
- return TRUE;
+ return internal_tcsetattr (self, self->priv->fd, &options, error);
}
/*****************************************************************************/
/* Flash */
typedef struct {
- GSimpleAsyncResult *result;
- MMPortSerial *self;
speed_t current_speed;
guint flash_id;
} FlashContext;
static void
-flash_context_complete_and_free (FlashContext *ctx)
+flash_context_free (FlashContext *ctx)
{
if (ctx->flash_id)
g_source_remove (ctx->flash_id);
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_slice_free (FlashContext, ctx);
}
@@ -1677,44 +1706,63 @@ mm_port_serial_flash_finish (MMPortSerial *port,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static gboolean
+flash_cancel_cb (GTask *task)
+{
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Flash cancelled");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
void
mm_port_serial_flash_cancel (MMPortSerial *self)
{
FlashContext *ctx;
+ GTask *task;
- if (!self->priv->flash_ctx)
+ /* Do nothing if there is no flash task */
+ if (!self->priv->flash_task)
return;
- /* Recover context */
- ctx = (FlashContext *)self->priv->flash_ctx;
- self->priv->flash_ctx = NULL;
+ /* Recover task */
+ task = self->priv->flash_task;
+ self->priv->flash_task = NULL;
+
+ /* If flash operation is scheduled, unschedule it */
+ ctx = g_task_get_task_data (task);
+ if (ctx->flash_id) {
+ g_source_remove (ctx->flash_id);
+ ctx->flash_id = 0;
+ }
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Flash cancelled");
- flash_context_complete_and_free (ctx);
+ /* Schedule task to be cancelled in an idle.
+ * We do NOT want this cancellation to happen right away,
+ * because the object reference in the flashing task may
+ * be the last one valid. */
+ g_idle_add ((GSourceFunc)flash_cancel_cb, task);
}
static gboolean
flash_do (MMPortSerial *self)
{
+ GTask *task;
FlashContext *ctx;
GError *error = NULL;
- /* Recover context */
- g_assert (self->priv->flash_ctx != NULL);
- ctx = (FlashContext *)self->priv->flash_ctx;
- self->priv->flash_ctx = NULL;
+ /* Recover task */
+ g_assert (self->priv->flash_task != NULL);
+ task = self->priv->flash_task;
+ self->priv->flash_task = NULL;
+ ctx = g_task_get_task_data (task);
ctx->flash_id = 0;
if (self->priv->flash_ok && mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY) {
if (ctx->current_speed) {
- if (!set_speed (ctx->self, ctx->current_speed, &error))
+ if (!set_speed (self, ctx->current_speed, &error))
g_assert (error);
} else {
error = g_error_new_literal (MM_SERIAL_ERROR,
@@ -1724,12 +1772,12 @@ flash_do (MMPortSerial *self)
}
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- flash_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
void
@@ -1740,6 +1788,7 @@ mm_port_serial_flash (MMPortSerial *self,
gpointer user_data)
{
FlashContext *ctx;
+ GTask *task;
GError *error = NULL;
gboolean success;
@@ -1747,33 +1796,31 @@ mm_port_serial_flash (MMPortSerial *self,
/* Setup context */
ctx = g_slice_new0 (FlashContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_port_serial_flash);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)flash_context_free);
if (!mm_port_serial_is_open (self)) {
- g_simple_async_result_set_error (ctx->result,
- MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_NOT_OPEN,
- "The serial port is not open.");
- flash_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_NOT_OPEN,
+ "The serial port is not open.");
+ g_object_unref (task);
return;
}
- if (self->priv->flash_ctx) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "Modem is already being flashed.");
- flash_context_complete_and_free (ctx);
+ if (self->priv->flash_task) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "Modem is already being flashed.");
+ g_object_unref (task);
return;
}
/* Flashing only in TTY */
if (!self->priv->flash_ok || mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY) {
- self->priv->flash_ctx = ctx;
+ self->priv->flash_task = task;
ctx->flash_id = g_idle_add ((GSourceFunc)flash_do, self);
return;
}
@@ -1781,26 +1828,74 @@ mm_port_serial_flash (MMPortSerial *self,
/* Grab current speed so we can reset it after flashing */
success = get_speed (self, &ctx->current_speed, &error);
if (!success && !ignore_errors) {
- g_simple_async_result_take_error (ctx->result, error);
- flash_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
g_clear_error (&error);
success = set_speed (self, B0, &error);
if (!success && !ignore_errors) {
- g_simple_async_result_take_error (ctx->result, error);
- flash_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
g_clear_error (&error);
- self->priv->flash_ctx = ctx;
+ self->priv->flash_task = task;
ctx->flash_id = g_timeout_add (flash_time, (GSourceFunc)flash_do, self);
}
/*****************************************************************************/
+gboolean
+mm_port_serial_set_flow_control (MMPortSerial *self,
+ MMFlowControl flow_control,
+ GError **error)
+{
+ struct termios options;
+ gchar *flow_control_str = NULL;
+ GError *inner_error = NULL;
+
+ /* retrieve current settings */
+ memset (&options, 0, sizeof (struct termios));
+ if (tcgetattr (self->priv->fd, &options) != 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't get serial port attributes: %s", g_strerror (errno));
+ goto out;
+ }
+
+ flow_control_str = mm_flow_control_build_string_from_mask (flow_control);
+
+ /* Return if current settings are already what we want */
+ if (!set_flow_control_termios (self, flow_control, &options)) {
+ mm_obj_dbg (self, "no need to change flow control settings: already %s", flow_control_str);
+ goto out;
+ }
+
+ if (!internal_tcsetattr (self, self->priv->fd, &options, &inner_error))
+ goto out;
+
+ mm_obj_dbg (self, "flow control settings updated to %s", flow_control_str);
+
+ out:
+ g_free (flow_control_str);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+MMFlowControl
+mm_port_serial_get_flow_control (MMPortSerial *self)
+{
+ return self->priv->flow_control;
+}
+
+/*****************************************************************************/
+
MMPortSerial *
mm_port_serial_new (const char *name, MMPortType ptype)
{
@@ -1865,6 +1960,7 @@ mm_port_serial_init (MMPortSerial *self)
self->priv->bits = 8;
self->priv->parity = 'n';
self->priv->stopbits = 1;
+ self->priv->flow_control = MM_FLOW_CONTROL_UNKNOWN;
self->priv->send_delay = 1000;
self->priv->queue = g_queue_new ();
@@ -1895,15 +1991,15 @@ set_property (GObject *object,
case PROP_STOPBITS:
self->priv->stopbits = g_value_get_uint (value);
break;
+ case PROP_FLOW_CONTROL:
+ self->priv->flow_control = g_value_get_flags (value);
+ break;
case PROP_SEND_DELAY:
self->priv->send_delay = g_value_get_uint64 (value);
break;
case PROP_SPEW_CONTROL:
self->priv->spew_control = g_value_get_boolean (value);
break;
- case PROP_RTS_CTS:
- self->priv->rts_cts = g_value_get_boolean (value);
- break;
case PROP_FLASH_OK:
self->priv->flash_ok = g_value_get_boolean (value);
break;
@@ -1937,15 +2033,15 @@ get_property (GObject *object,
case PROP_STOPBITS:
g_value_set_uint (value, self->priv->stopbits);
break;
+ case PROP_FLOW_CONTROL:
+ g_value_set_flags (value, self->priv->flow_control);
+ break;
case PROP_SEND_DELAY:
g_value_set_uint64 (value, self->priv->send_delay);
break;
case PROP_SPEW_CONTROL:
g_value_set_boolean (value, self->priv->spew_control);
break;
- case PROP_RTS_CTS:
- g_value_set_boolean (value, self->priv->rts_cts);
- break;
case PROP_FLASH_OK:
g_value_set_boolean (value, self->priv->flash_ok);
break;
@@ -1956,25 +2052,24 @@ get_property (GObject *object,
}
static void
-dispose (GObject *object)
+finalize (GObject *object)
{
MMPortSerial *self = MM_PORT_SERIAL (object);
- if (self->priv->timeout_id) {
- g_source_remove (self->priv->timeout_id);
- self->priv->timeout_id = 0;
- }
-
- port_serial_close_force (MM_PORT_SERIAL (object));
+ port_serial_close_force (MM_PORT_SERIAL (object));
mm_port_serial_flash_cancel (MM_PORT_SERIAL (object));
- G_OBJECT_CLASS (mm_port_serial_parent_class)->dispose (object);
-}
+ /* These are disposed during port closing */
+ g_assert (self->priv->iochannel == NULL);
+ g_assert (self->priv->iochannel_id == 0);
+ g_assert (self->priv->socket == NULL);
+ g_assert (self->priv->socket_source == NULL);
-static void
-finalize (GObject *object)
-{
- MMPortSerial *self = MM_PORT_SERIAL (object);
+ if (self->priv->timeout_id)
+ g_source_remove (self->priv->timeout_id);
+
+ if (self->priv->queue_id)
+ g_source_remove (self->priv->queue_id);
g_hash_table_destroy (self->priv->reply_cache);
g_byte_array_unref (self->priv->response);
@@ -1993,8 +2088,7 @@ mm_port_serial_class_init (MMPortSerialClass *klass)
/* Virtual methods */
object_class->set_property = set_property;
object_class->get_property = get_property;
- object_class->dispose = dispose;
- object_class->finalize = finalize;
+ object_class->finalize = finalize;
klass->config_fd = real_config_fd;
@@ -2040,6 +2134,15 @@ mm_port_serial_class_init (MMPortSerialClass *klass)
G_PARAM_READWRITE));
g_object_class_install_property
+ (object_class, PROP_FLOW_CONTROL,
+ g_param_spec_flags (MM_PORT_SERIAL_FLOW_CONTROL,
+ "FlowControl",
+ "Select flow control",
+ MM_TYPE_FLOW_CONTROL,
+ MM_FLOW_CONTROL_UNKNOWN,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
(object_class, PROP_SEND_DELAY,
g_param_spec_uint64 (MM_PORT_SERIAL_SEND_DELAY,
"SendDelay",
@@ -2056,21 +2159,13 @@ mm_port_serial_class_init (MMPortSerialClass *klass)
G_PARAM_READWRITE));
g_object_class_install_property
- (object_class, PROP_RTS_CTS,
- g_param_spec_boolean (MM_PORT_SERIAL_RTS_CTS,
- "RTSCTS",
- "Enable RTS/CTS flow control",
- FALSE,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
(object_class, PROP_FLASH_OK,
g_param_spec_boolean (MM_PORT_SERIAL_FLASH_OK,
"FlashOk",
"Flashing the port (0 baud for a short period) "
"is allowed.",
TRUE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE));
/* Signals */
signals[BUFFER_FULL] =