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.
This commit is contained in:
Aleksander Morgado
2011-05-11 16:40:45 +02:00
parent f2ba435446
commit 22f15b87b5
4 changed files with 111 additions and 4 deletions

View File

@@ -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,
@@ -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);
}

View File

@@ -35,6 +35,8 @@
typedef struct _MMModemBase MMModemBase;
typedef struct _MMModemBaseClass MMModemBaseClass;
#define MM_MODEM_BASE_MAX_TIMEOUTS "max-timeouts"
struct _MMModemBase {
GObject parent;
};

View File

@@ -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,6 +1448,7 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
G_PARAM_READWRITE));
/* Signals */
signals[BUFFER_FULL] =
g_signal_new ("buffer-full",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
@@ -1428,5 +1456,14 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
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);
}

View File

@@ -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);