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))
|
#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 {
|
typedef struct {
|
||||||
char *driver;
|
char *driver;
|
||||||
char *plugin;
|
char *plugin;
|
||||||
@@ -62,6 +68,9 @@ typedef struct {
|
|||||||
char *ati1;
|
char *ati1;
|
||||||
char *gsn;
|
char *gsn;
|
||||||
|
|
||||||
|
guint max_timeouts;
|
||||||
|
guint set_invalid_unresponsive_modem_id;
|
||||||
|
|
||||||
MMAuthProvider *authp;
|
MMAuthProvider *authp;
|
||||||
|
|
||||||
GHashTable *ports;
|
GHashTable *ports;
|
||||||
@@ -105,6 +114,42 @@ find_primary (gpointer key, gpointer data, gpointer user_data)
|
|||||||
*found = port;
|
*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 *
|
MMPort *
|
||||||
mm_modem_base_add_port (MMModemBase *self,
|
mm_modem_base_add_port (MMModemBase *self,
|
||||||
const char *subsys,
|
const char *subsys,
|
||||||
@@ -137,6 +182,13 @@ mm_modem_base_add_port (MMModemBase *self,
|
|||||||
port = MM_PORT (mm_qcdm_serial_port_new (name, ptype));
|
port = MM_PORT (mm_qcdm_serial_port_new (name, ptype));
|
||||||
else
|
else
|
||||||
port = MM_PORT (mm_at_serial_port_new (name, ptype));
|
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")) {
|
} else if (!strcmp (subsys, "net")) {
|
||||||
port = MM_PORT (g_object_new (MM_TYPE_PORT,
|
port = MM_PORT (g_object_new (MM_TYPE_PORT,
|
||||||
MM_PORT_DEVICE, name,
|
MM_PORT_DEVICE, name,
|
||||||
@@ -538,7 +590,7 @@ mm_modem_base_get_card_info (MMModemBase *self,
|
|||||||
|
|
||||||
priv = MM_MODEM_BASE_GET_PRIVATE (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.
|
* not hit up the card for it's model information.
|
||||||
*/
|
*/
|
||||||
if (priv->manf || priv->model || priv->revision)
|
if (priv->manf || priv->model || priv->revision)
|
||||||
@@ -717,6 +769,9 @@ set_property (GObject *object, guint prop_id,
|
|||||||
/* Construct only */
|
/* Construct only */
|
||||||
priv->pid = g_value_get_uint (value);
|
priv->pid = g_value_get_uint (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_MAX_TIMEOUTS:
|
||||||
|
priv->max_timeouts = g_value_get_uint (value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -775,6 +830,9 @@ get_property (GObject *object, guint prop_id,
|
|||||||
case MM_MODEM_PROP_HW_PID:
|
case MM_MODEM_PROP_HW_PID:
|
||||||
g_value_set_uint (value, priv->pid);
|
g_value_set_uint (value, priv->pid);
|
||||||
break;
|
break;
|
||||||
|
case PROP_MAX_TIMEOUTS:
|
||||||
|
g_value_set_uint (value, priv->max_timeouts);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -878,6 +936,15 @@ mm_modem_base_class_init (MMModemBaseClass *klass)
|
|||||||
MM_MODEM_PROP_HW_PID,
|
MM_MODEM_PROP_HW_PID,
|
||||||
MM_MODEM_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);
|
mm_properties_changed_signal_enable (object_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -35,6 +35,8 @@
|
|||||||
typedef struct _MMModemBase MMModemBase;
|
typedef struct _MMModemBase MMModemBase;
|
||||||
typedef struct _MMModemBaseClass MMModemBaseClass;
|
typedef struct _MMModemBaseClass MMModemBaseClass;
|
||||||
|
|
||||||
|
#define MM_MODEM_BASE_MAX_TIMEOUTS "max-timeouts"
|
||||||
|
|
||||||
struct _MMModemBase {
|
struct _MMModemBase {
|
||||||
GObject parent;
|
GObject parent;
|
||||||
};
|
};
|
||||||
|
@@ -49,6 +49,15 @@ enum {
|
|||||||
LAST_PROP
|
LAST_PROP
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BUFFER_FULL,
|
||||||
|
TIMED_OUT,
|
||||||
|
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
#define SERIAL_BUF_SIZE 2048
|
#define SERIAL_BUF_SIZE 2048
|
||||||
|
|
||||||
#define MM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL_PORT, MMSerialPortPrivate))
|
#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 watch_id;
|
||||||
guint timeout_id;
|
guint timeout_id;
|
||||||
|
|
||||||
|
guint n_consecutive_timeouts;
|
||||||
|
|
||||||
guint flash_id;
|
guint flash_id;
|
||||||
guint connected_id;
|
guint connected_id;
|
||||||
} MMSerialPortPrivate;
|
} MMSerialPortPrivate;
|
||||||
@@ -415,6 +426,10 @@ mm_serial_port_process_command (MMSerialPort *self,
|
|||||||
if (errno == EAGAIN || status == 0) {
|
if (errno == EAGAIN || status == 0) {
|
||||||
info->eagain_count--;
|
info->eagain_count--;
|
||||||
if (info->eagain_count <= 0) {
|
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,
|
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
|
||||||
"Sending command failed: '%s'", strerror (errno));
|
"Sending command failed: '%s'", strerror (errno));
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -542,14 +557,22 @@ mm_serial_port_timed_out (gpointer data)
|
|||||||
|
|
||||||
priv->timeout_id = 0;
|
priv->timeout_id = 0;
|
||||||
|
|
||||||
|
/* Update number of consecutive timeouts found */
|
||||||
|
priv->n_consecutive_timeouts++;
|
||||||
|
|
||||||
error = g_error_new_literal (MM_SERIAL_ERROR,
|
error = g_error_new_literal (MM_SERIAL_ERROR,
|
||||||
MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
|
MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
|
||||||
"Serial command timed out");
|
"Serial command timed out");
|
||||||
|
|
||||||
/* FIXME: This is not completely correct - if the response finally arrives and there's
|
/* 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
|
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. */
|
get the output of the timed out command. Not sure what to do here. */
|
||||||
mm_serial_port_got_response (self, error);
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -672,12 +695,15 @@ data_available (GIOChannel *source,
|
|||||||
/* Make sure the response doesn't grow too long */
|
/* Make sure the response doesn't grow too long */
|
||||||
if ((priv->response->len > SERIAL_BUF_SIZE) && priv->spew_control) {
|
if ((priv->response->len > SERIAL_BUF_SIZE) && priv->spew_control) {
|
||||||
/* Notify listeners and then trim the buffer */
|
/* 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));
|
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);
|
mm_serial_port_got_response (self, err);
|
||||||
|
}
|
||||||
} while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
|
} while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -888,6 +914,7 @@ mm_serial_port_close (MMSerialPort *self)
|
|||||||
"Serial port is now closed");
|
"Serial port is now closed");
|
||||||
response = g_byte_array_sized_new (1);
|
response = g_byte_array_sized_new (1);
|
||||||
g_byte_array_append (response, (const guint8 *) "\0", 1);
|
g_byte_array_append (response, (const guint8 *) "\0", 1);
|
||||||
|
|
||||||
MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self,
|
MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self,
|
||||||
response,
|
response,
|
||||||
error,
|
error,
|
||||||
@@ -1421,12 +1448,22 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
|
|||||||
G_PARAM_READWRITE));
|
G_PARAM_READWRITE));
|
||||||
|
|
||||||
/* Signals */
|
/* Signals */
|
||||||
g_signal_new ("buffer-full",
|
signals[BUFFER_FULL] =
|
||||||
|
g_signal_new ("buffer-full",
|
||||||
G_OBJECT_CLASS_TYPE (object_class),
|
G_OBJECT_CLASS_TYPE (object_class),
|
||||||
G_SIGNAL_RUN_FIRST,
|
G_SIGNAL_RUN_FIRST,
|
||||||
G_STRUCT_OFFSET (MMSerialPortClass, buffer_full),
|
G_STRUCT_OFFSET (MMSerialPortClass, buffer_full),
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
g_cclosure_marshal_VOID__POINTER,
|
g_cclosure_marshal_VOID__POINTER,
|
||||||
G_TYPE_NONE, 1, G_TYPE_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 */
|
/* Signals */
|
||||||
void (*buffer_full) (MMSerialPort *port, const GByteArray *buffer);
|
void (*buffer_full) (MMSerialPort *port, const GByteArray *buffer);
|
||||||
|
void (*timed_out) (MMSerialPort *port, guint n_consecutive_replies);
|
||||||
};
|
};
|
||||||
|
|
||||||
GType mm_serial_port_get_type (void);
|
GType mm_serial_port_get_type (void);
|
||||||
|
Reference in New Issue
Block a user