serial-port: allow specifying some wait time between closing and reopening

We will now by default wait some time (1s) between port getting fully closed and
the port being reopened again. This logic seems to work for multiple modems
where there is a single port for both AT and data, like my Nokia C7 and Iridium
modems. If this wait time is not applied, the port ends up returning EAGAIN for
every write that we try to do afterwards.
This commit is contained in:
Aleksander Morgado
2013-02-20 12:15:19 +01:00
parent 9a6fce0a08
commit 000bb64225
3 changed files with 144 additions and 40 deletions

View File

@@ -1294,6 +1294,27 @@ data_flash_cdma_ready (MMSerialPort *data,
detailed_disconnect_context_complete_and_free (ctx); detailed_disconnect_context_complete_and_free (ctx);
} }
static void
data_reopen_cdma_ready (MMSerialPort *data,
GError *error,
DetailedDisconnectContext *ctx)
{
if (error) {
/* Fatal */
g_simple_async_result_set_from_error (ctx->result, error);
detailed_disconnect_context_complete_and_free (ctx);
return;
}
/* Just flash the data port */
mm_dbg ("Flashing data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
mm_serial_port_flash (MM_SERIAL_PORT (ctx->data),
1000,
TRUE,
(MMSerialFlashFn)data_flash_cdma_ready,
ctx);
}
static void static void
disconnect_cdma (MMBroadbandBearer *self, disconnect_cdma (MMBroadbandBearer *self,
MMBroadbandModem *modem, MMBroadbandModem *modem,
@@ -1304,7 +1325,6 @@ disconnect_cdma (MMBroadbandBearer *self,
gpointer user_data) gpointer user_data)
{ {
DetailedDisconnectContext *ctx; DetailedDisconnectContext *ctx;
GError *error = NULL;
g_assert (primary != NULL); g_assert (primary != NULL);
@@ -1320,18 +1340,11 @@ disconnect_cdma (MMBroadbandBearer *self,
user_data); user_data);
/* Fully reopen the port before flashing */ /* Fully reopen the port before flashing */
mm_dbg ("Reopening primary port..."); mm_dbg ("Reopening data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
if (!mm_serial_port_reopen (MM_SERIAL_PORT (ctx->primary), &error)) { mm_serial_port_reopen (MM_SERIAL_PORT (ctx->data),
mm_warn ("Disconnecting CDMA: %s", error->message); 1000,
g_error_free (error); (MMSerialReopenFn)data_reopen_cdma_ready,
} ctx);
/* Just flash the data port */
mm_serial_port_flash (MM_SERIAL_PORT (ctx->data),
1000,
TRUE,
(MMSerialFlashFn)data_flash_cdma_ready,
ctx);
} }
/*****************************************************************************/ /*****************************************************************************/
@@ -1410,18 +1423,19 @@ data_flash_3gpp_ready (MMSerialPort *data,
} }
static void static void
data_flash_3gpp (DetailedDisconnectContext *ctx) data_reopen_3gpp_ready (MMSerialPort *data,
GError *error,
DetailedDisconnectContext *ctx)
{ {
GError *error = NULL; if (error) {
/* Fatal */
/* Fully reopen the port before flashing */ g_simple_async_result_set_from_error (ctx->result, error);
mm_dbg ("Reopening data port..."); detailed_disconnect_context_complete_and_free (ctx);
if (!mm_serial_port_reopen (MM_SERIAL_PORT (ctx->data), &error)) { return;
mm_warn ("Disconnecting 3GPP: %s", error->message);
g_error_free (error);
} }
mm_dbg ("Flash data port..."); /* Just flash the data port */
mm_dbg ("Flashing data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
mm_serial_port_flash (MM_SERIAL_PORT (ctx->data), mm_serial_port_flash (MM_SERIAL_PORT (ctx->data),
1000, 1000,
TRUE, TRUE,
@@ -1429,6 +1443,17 @@ data_flash_3gpp (DetailedDisconnectContext *ctx)
ctx); ctx);
} }
static void
data_reopen_3gpp (DetailedDisconnectContext *ctx)
{
/* Fully reopen the port before flashing */
mm_dbg ("Reopening data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
mm_serial_port_reopen (MM_SERIAL_PORT (ctx->data),
1000,
(MMSerialReopenFn)data_reopen_3gpp_ready,
ctx);
}
static void static void
cgact_ready (MMBaseModem *modem, cgact_ready (MMBaseModem *modem,
GAsyncResult *res, GAsyncResult *res,
@@ -1444,7 +1469,7 @@ cgact_ready (MMBaseModem *modem,
g_error_free (error); g_error_free (error);
} }
data_flash_3gpp (ctx); data_reopen_3gpp (ctx);
} }
static void static void
@@ -1512,8 +1537,8 @@ disconnect_3gpp (MMBroadbandBearer *self,
return; return;
} }
/* If no secondary port, go on to flash the data/primary port */ /* If no secondary port, go on to reopen & flash the data/primary port */
data_flash_3gpp (ctx); data_reopen_3gpp (ctx);
} }
/*****************************************************************************/ /*****************************************************************************/

View File

@@ -98,6 +98,7 @@ typedef struct {
guint n_consecutive_timeouts; guint n_consecutive_timeouts;
guint flash_id; guint flash_id;
guint reopen_id;
guint connected_id; guint connected_id;
} MMSerialPortPrivate; } MMSerialPortPrivate;
@@ -871,9 +872,17 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE); g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
priv = MM_SERIAL_PORT_GET_PRIVATE (self); priv = MM_SERIAL_PORT_GET_PRIVATE (self);
device = mm_port_get_device (MM_PORT (self)); device = mm_port_get_device (MM_PORT (self));
if (priv->reopen_id) {
g_set_error (error,
MM_SERIAL_ERROR,
MM_SERIAL_ERROR_OPEN_FAILED,
"Could not open serial device %s: reopen operation in progress",
device);
return FALSE;
}
if (priv->open_count) { if (priv->open_count) {
/* Already open */ /* Already open */
goto success; goto success;
@@ -1204,32 +1213,95 @@ mm_serial_port_queue_command_cached (MMSerialPort *self,
internal_queue_command (self, command, take_command, TRUE, timeout_seconds, cancellable, callback, user_data); internal_queue_command (self, command, take_command, TRUE, timeout_seconds, cancellable, callback, user_data);
} }
gboolean typedef struct {
mm_serial_port_reopen (MMSerialPort *self, MMSerialPort *port;
GError **error) guint initial_open_count;
MMSerialReopenFn callback;
gpointer user_data;
} ReopenInfo;
static void
serial_port_reopen_cancel (MMSerialPort *self)
{ {
MMSerialPortPrivate *priv; MMSerialPortPrivate *priv;
guint initial_open_count;
g_return_if_fail (MM_IS_SERIAL_PORT (self));
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
if (priv->reopen_id > 0) {
g_source_remove (priv->reopen_id);
priv->reopen_id = 0;
}
}
static gboolean
reopen_do (gpointer data)
{
ReopenInfo *info = (ReopenInfo *) data;
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (info->port);
GError *error = NULL;
guint i;
priv->reopen_id = 0;
for (i = 0; i < info->initial_open_count; i++) {
if (!mm_serial_port_open (info->port, &error)) {
g_prefix_error (&error, "Couldn't reopen port (%u): ", i);
break;
}
}
info->callback (info->port, error, info->user_data);
if (error)
g_error_free (error);
g_slice_free (ReopenInfo, info);
return FALSE;
}
gboolean
mm_serial_port_reopen (MMSerialPort *self,
guint32 reopen_time,
MMSerialReopenFn callback,
gpointer user_data)
{
ReopenInfo *info;
MMSerialPortPrivate *priv;
guint i; guint i;
g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE); g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
if (priv->reopen_id > 0) {
GError *error;
error = g_error_new_literal (MM_CORE_ERROR,
MM_CORE_ERROR_IN_PROGRESS,
"Modem is already being reopened.");
callback (self, error, user_data);
g_error_free (error);
return FALSE;
}
info = g_slice_new0 (ReopenInfo);
info->port = self;
info->callback = callback;
info->user_data = user_data;
priv = MM_SERIAL_PORT_GET_PRIVATE (self); priv = MM_SERIAL_PORT_GET_PRIVATE (self);
initial_open_count = priv->open_count; info->initial_open_count = priv->open_count;
mm_dbg ("(%s) reopening port (%u)", mm_dbg ("(%s) reopening port (%u)",
mm_port_get_device (MM_PORT (self)), mm_port_get_device (MM_PORT (self)),
initial_open_count); info->initial_open_count);
for (i = 0; i < initial_open_count; i++) for (i = 0; i < info->initial_open_count; i++)
mm_serial_port_close (self); mm_serial_port_close (self);
for (i = 0; i < initial_open_count; i++) { if (reopen_time > 0)
if (!mm_serial_port_open (self, error)) { priv->reopen_id = g_timeout_add (reopen_time, reopen_do, info);
g_prefix_error (error, "Couldn't reopen port (%u): ", i); else
return FALSE; priv->reopen_id = g_idle_add (reopen_do, info);
}
}
return TRUE; return TRUE;
} }
@@ -1603,6 +1675,7 @@ dispose (GObject *object)
mm_serial_port_close_force (MM_SERIAL_PORT (object)); mm_serial_port_close_force (MM_SERIAL_PORT (object));
serial_port_reopen_cancel (MM_SERIAL_PORT (object));
mm_serial_port_flash_cancel (MM_SERIAL_PORT (object)); mm_serial_port_flash_cancel (MM_SERIAL_PORT (object));
G_OBJECT_CLASS (mm_serial_port_parent_class)->dispose (object); G_OBJECT_CLASS (mm_serial_port_parent_class)->dispose (object);

View File

@@ -43,6 +43,10 @@
typedef struct _MMSerialPort MMSerialPort; typedef struct _MMSerialPort MMSerialPort;
typedef struct _MMSerialPortClass MMSerialPortClass; typedef struct _MMSerialPortClass MMSerialPortClass;
typedef void (*MMSerialReopenFn) (MMSerialPort *port,
GError *error,
gpointer user_data);
typedef void (*MMSerialFlashFn) (MMSerialPort *port, typedef void (*MMSerialFlashFn) (MMSerialPort *port,
GError *error, GError *error,
gpointer user_data); gpointer user_data);
@@ -123,7 +127,9 @@ gboolean mm_serial_port_open (MMSerialPort *self,
void mm_serial_port_close (MMSerialPort *self); void mm_serial_port_close (MMSerialPort *self);
gboolean mm_serial_port_reopen (MMSerialPort *self, gboolean mm_serial_port_reopen (MMSerialPort *self,
GError **error); guint32 reopen_time,
MMSerialReopenFn callback,
gpointer user_data);
gboolean mm_serial_port_flash (MMSerialPort *self, gboolean mm_serial_port_flash (MMSerialPort *self,
guint32 flash_time, guint32 flash_time,