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:
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,8 @@
|
||||
typedef struct _MMModemBase MMModemBase;
|
||||
typedef struct _MMModemBaseClass MMModemBaseClass;
|
||||
|
||||
#define MM_MODEM_BASE_MAX_TIMEOUTS "max-timeouts"
|
||||
|
||||
struct _MMModemBase {
|
||||
GObject parent;
|
||||
};
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user