aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2011-05-11 16:40:45 +0200
committerAleksander Morgado <aleksander@lanedo.com>2011-06-06 17:20:17 +0200
commit22f15b87b53b60e627b00a51781234a27a677594 (patch)
treeaaa4ae687ede6f81cee3fe8529f2eb40b509ce8f
parentf2ba435446a48407f4a491d0fa0eda6b7d4c22d9 (diff)
base: disable the modem if up to N consecutive commands get timed out
This feature is initially disabled for all modems, but plugins can enable it by setting a value greater than 0 for the "max-timeouts" property when creating the modem object.
-rw-r--r--src/mm-modem-base.c69
-rw-r--r--src/mm-modem-base.h2
-rw-r--r--src/mm-serial-port.c43
-rw-r--r--src/mm-serial-port.h1
4 files changed, 111 insertions, 4 deletions
diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c
index 451e2ebe..788020fe 100644
--- a/src/mm-modem-base.c
+++ b/src/mm-modem-base.c
@@ -41,6 +41,12 @@ G_DEFINE_TYPE_EXTENDED (MMModemBase, mm_modem_base,
#define MM_MODEM_BASE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_BASE, MMModemBasePrivate))
+enum {
+ PROP_0,
+ PROP_MAX_TIMEOUTS,
+ LAST_PROP
+};
+
typedef struct {
char *driver;
char *plugin;
@@ -62,6 +68,9 @@ typedef struct {
char *ati1;
char *gsn;
+ guint max_timeouts;
+ guint set_invalid_unresponsive_modem_id;
+
MMAuthProvider *authp;
GHashTable *ports;
@@ -105,6 +114,42 @@ find_primary (gpointer key, gpointer data, gpointer user_data)
*found = port;
}
+static gboolean
+set_invalid_unresponsive_modem_cb (MMModemBase *self)
+{
+ MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ mm_modem_base_set_valid (self, FALSE);
+ priv->set_invalid_unresponsive_modem_id = 0;
+ return FALSE;
+}
+
+static void
+serial_port_timed_out_cb (MMSerialPort *port,
+ guint n_consecutive_timeouts,
+ gpointer user_data)
+{
+ MMModemBase *self = (MM_MODEM_BASE (user_data));
+ MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ if (priv->max_timeouts > 0 &&
+ n_consecutive_timeouts >= priv->max_timeouts) {
+ const gchar *dbus_path;
+
+ dbus_path = (const gchar *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
+ mm_warn ("Modem %s: Port (%s/%s) timed out %u times, marking modem as disabled",
+ dbus_path,
+ mm_port_type_to_name (mm_port_get_port_type (MM_PORT (port))),
+ mm_port_get_device (MM_PORT (port)),
+ n_consecutive_timeouts);
+
+ /* Only set action to invalidate modem if not already done */
+ if (!priv->set_invalid_unresponsive_modem_id)
+ priv->set_invalid_unresponsive_modem_id =
+ g_idle_add ((GSourceFunc)set_invalid_unresponsive_modem_cb, self);
+ }
+}
+
MMPort *
mm_modem_base_add_port (MMModemBase *self,
const char *subsys,
@@ -137,6 +182,13 @@ mm_modem_base_add_port (MMModemBase *self,
port = MM_PORT (mm_qcdm_serial_port_new (name, ptype));
else
port = MM_PORT (mm_at_serial_port_new (name, ptype));
+
+ /* For serial ports, enable port timeout checks */
+ if (port)
+ g_signal_connect (port,
+ "timed-out",
+ G_CALLBACK (serial_port_timed_out_cb),
+ self);
} else if (!strcmp (subsys, "net")) {
port = MM_PORT (g_object_new (MM_TYPE_PORT,
MM_PORT_DEVICE, name,
@@ -538,7 +590,7 @@ mm_modem_base_get_card_info (MMModemBase *self,
priv = MM_MODEM_BASE_GET_PRIVATE (self);
- /* Cached info and errors schedule the callback immediately and do
+ /* Cached info and errors schedule the callback immediately and do
* not hit up the card for it's model information.
*/
if (priv->manf || priv->model || priv->revision)
@@ -717,6 +769,9 @@ set_property (GObject *object, guint prop_id,
/* Construct only */
priv->pid = g_value_get_uint (value);
break;
+ case PROP_MAX_TIMEOUTS:
+ priv->max_timeouts = g_value_get_uint (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -775,6 +830,9 @@ get_property (GObject *object, guint prop_id,
case MM_MODEM_PROP_HW_PID:
g_value_set_uint (value, priv->pid);
break;
+ case PROP_MAX_TIMEOUTS:
+ g_value_set_uint (value, priv->max_timeouts);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -878,6 +936,15 @@ mm_modem_base_class_init (MMModemBaseClass *klass)
MM_MODEM_PROP_HW_PID,
MM_MODEM_HW_PID);
+ g_object_class_install_property
+ (object_class, PROP_MAX_TIMEOUTS,
+ g_param_spec_uint (MM_MODEM_BASE_MAX_TIMEOUTS,
+ "Max timeouts",
+ "Maximum number of consecutive timed out commands sent to "
+ "the modem before disabling it. If 0, this feature is disabled.",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE));
+
mm_properties_changed_signal_enable (object_class);
}
diff --git a/src/mm-modem-base.h b/src/mm-modem-base.h
index 04099577..933aae11 100644
--- a/src/mm-modem-base.h
+++ b/src/mm-modem-base.h
@@ -35,6 +35,8 @@
typedef struct _MMModemBase MMModemBase;
typedef struct _MMModemBaseClass MMModemBaseClass;
+#define MM_MODEM_BASE_MAX_TIMEOUTS "max-timeouts"
+
struct _MMModemBase {
GObject parent;
};
diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c
index 18a616d9..b116997e 100644
--- a/src/mm-serial-port.c
+++ b/src/mm-serial-port.c
@@ -49,6 +49,15 @@ enum {
LAST_PROP
};
+enum {
+ BUFFER_FULL,
+ TIMED_OUT,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
#define SERIAL_BUF_SIZE 2048
#define MM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL_PORT, MMSerialPortPrivate))
@@ -74,6 +83,8 @@ typedef struct {
guint watch_id;
guint timeout_id;
+ guint n_consecutive_timeouts;
+
guint flash_id;
guint connected_id;
} MMSerialPortPrivate;
@@ -415,6 +426,10 @@ mm_serial_port_process_command (MMSerialPort *self,
if (errno == EAGAIN || status == 0) {
info->eagain_count--;
if (info->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);
+
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
"Sending command failed: '%s'", strerror (errno));
return FALSE;
@@ -542,14 +557,22 @@ mm_serial_port_timed_out (gpointer data)
priv->timeout_id = 0;
+ /* Update number of consecutive timeouts found */
+ priv->n_consecutive_timeouts++;
+
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_serial_port_got_response (self, error);
+ /* Emit a timed out signal, used by upper layers to identify a disconnected
+ * serial port */
+ g_signal_emit (self, signals[TIMED_OUT], 0, priv->n_consecutive_timeouts);
+
return FALSE;
}
@@ -672,12 +695,15 @@ data_available (GIOChannel *source,
/* Make sure the response doesn't grow too long */
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_signal_emit (self, signals[BUFFER_FULL], 0, priv->response);
g_byte_array_remove_range (priv->response, 0, (SERIAL_BUF_SIZE / 2));
}
- if (parse_response (self, priv->response, &err))
+ if (parse_response (self, priv->response, &err)) {
+ /* Reset number of consecutive timeouts only here */
+ priv->n_consecutive_timeouts = 0;
mm_serial_port_got_response (self, err);
+ }
} while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
return TRUE;
@@ -888,6 +914,7 @@ mm_serial_port_close (MMSerialPort *self)
"Serial port is now closed");
response = g_byte_array_sized_new (1);
g_byte_array_append (response, (const guint8 *) "\0", 1);
+
MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self,
response,
error,
@@ -1421,12 +1448,22 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
G_PARAM_READWRITE));
/* Signals */
- g_signal_new ("buffer-full",
+ signals[BUFFER_FULL] =
+ g_signal_new ("buffer-full",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMSerialPortClass, buffer_full),
NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ signals[TIMED_OUT] =
+ g_signal_new ("timed-out",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMSerialPortClass, timed_out),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
}
diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h
index 57ef94be..e9997a58 100644
--- a/src/mm-serial-port.h
+++ b/src/mm-serial-port.h
@@ -97,6 +97,7 @@ struct _MMSerialPortClass {
/* Signals */
void (*buffer_full) (MMSerialPort *port, const GByteArray *buffer);
+ void (*timed_out) (MMSerialPort *port, guint n_consecutive_replies);
};
GType mm_serial_port_get_type (void);