core: don't allow concurrent flashes on the same device

Previously, a few operations (like disable) could trigger a modem
flash in parallel with another flash.  That's wrong, don't allow
that.  At the same time, add in finer-grained error checking on
serial port speed operations, and fix a GSM generic bug that would
send the POWER_UP string on disable.
This commit is contained in:
Dan Williams
2009-09-08 17:31:54 -07:00
parent 6cf01d2ab6
commit 14e5c52f78
12 changed files with 403 additions and 92 deletions

View File

@@ -15,4 +15,22 @@
</tp:docstring> </tp:docstring>
</tp:error> </tp:error>
<tp:error name="Connected">
<tp:docstring>
Operation could not be performed while the modem is connected.
</tp:docstring>
</tp:error>
<tp:error name="Disconnected">
<tp:docstring>
Operation could not be performed while the modem is disconnected.
</tp:docstring>
</tp:error>
<tp:error name="OperationInProgress">
<tp:docstring>
Operation could not be performed because it is already in progress.
</tp:docstring>
</tp:error>
</tp:errors> </tp:errors>

View File

@@ -402,11 +402,17 @@ mbm_emrdy_done (MMSerialPort *port,
} }
static void static void
enable_flash_done (MMSerialPort *port, gpointer user_data) enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = user_data; MMCallbackInfo *info = user_data;
MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem); MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem);
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
if (priv->have_emrdy) { if (priv->have_emrdy) {
/* Modem is ready, no need to check EMRDY */ /* Modem is ready, no need to check EMRDY */
do_init (port, info); do_init (port, info);
@@ -427,8 +433,16 @@ disable_done (MMSerialPort *port,
} }
static void static void
disable_flash_done (MMSerialPort *port, gpointer user_data) disable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
mm_serial_port_queue_command (port, "+CMER=0", 5, disable_done, user_data); mm_serial_port_queue_command (port, "+CMER=0", 5, disable_done, user_data);
} }
@@ -449,18 +463,20 @@ enable (MMModem *modem,
g_assert (primary); g_assert (primary);
if (do_enable) { if (do_enable) {
if (mm_serial_port_open (primary, &info->error)) if (!mm_serial_port_open (primary, &info->error)) {
mm_serial_port_flash (primary, 100, enable_flash_done, info); g_assert (info->error);
if (info->error)
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
return;
}
mm_serial_port_flash (primary, 100, enable_flash_done, info);
} else { } else {
mm_serial_port_queue_command (primary, "+CREG=0", 100, NULL, NULL); mm_serial_port_queue_command (primary, "+CREG=0", 100, NULL, NULL);
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
if (mm_port_get_connected (MM_PORT (primary))) if (mm_port_get_connected (MM_PORT (primary)))
mm_serial_port_flash (primary, 1000, disable_flash_done, info); mm_serial_port_flash (primary, 1000, disable_flash_done, info);
else else
disable_flash_done (primary, info); disable_flash_done (primary, NULL, info);
} }
} }

View File

@@ -94,8 +94,16 @@ pre_init_done (MMSerialPort *port,
} }
static void static void
enable_flash_done (MMSerialPort *port, gpointer user_data) enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data); mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data);
} }
@@ -110,8 +118,16 @@ disable_done (MMSerialPort *port,
} }
static void static void
disable_flash_done (MMSerialPort *port, gpointer user_data) disable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
mm_serial_port_queue_command (port, "+CFUN=0", 5, disable_done, user_data); mm_serial_port_queue_command (port, "+CFUN=0", 5, disable_done, user_data);
} }
@@ -136,13 +152,15 @@ enable (MMModem *modem,
if (mm_port_get_connected (MM_PORT (primary))) if (mm_port_get_connected (MM_PORT (primary)))
mm_serial_port_flash (primary, 1000, disable_flash_done, info); mm_serial_port_flash (primary, 1000, disable_flash_done, info);
else else
disable_flash_done (primary, info); disable_flash_done (primary, NULL, info);
} else { } else {
if (mm_serial_port_open (primary, &info->error)) if (!mm_serial_port_open (primary, &info->error)) {
mm_serial_port_flash (primary, 100, enable_flash_done, info); g_assert (info->error);
if (info->error)
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
return;
}
mm_serial_port_flash (primary, 100, enable_flash_done, info);
} }
} }

View File

@@ -84,16 +84,32 @@ init_done (MMSerialPort *port,
} }
static void static void
enable_flash_done (MMSerialPort *port, gpointer user_data) enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1 +CMEE=1", 3, init_done, user_data); mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1 +CMEE=1", 3, init_done, user_data);
} }
static void static void
disable_flash_done (MMSerialPort *port, gpointer user_data) disable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
mm_serial_port_close (port); mm_serial_port_close (port);
mm_callback_info_schedule ((MMCallbackInfo *) user_data); mm_callback_info_schedule (info);
} }
static void static void
@@ -118,13 +134,15 @@ enable (MMModem *modem,
if (mm_port_get_connected (MM_PORT (primary))) if (mm_port_get_connected (MM_PORT (primary)))
mm_serial_port_flash (primary, 1000, disable_flash_done, info); mm_serial_port_flash (primary, 1000, disable_flash_done, info);
else else
disable_flash_done (primary, info); disable_flash_done (primary, NULL, info);
} else { } else {
if (mm_serial_port_open (primary, &info->error)) if (!mm_serial_port_open (primary, &info->error)) {
mm_serial_port_flash (primary, 100, enable_flash_done, info); g_assert (info->error);
if (info->error)
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
return;
}
mm_serial_port_flash (primary, 100, enable_flash_done, info);
} }
} }

View File

@@ -95,8 +95,16 @@ pre_init_done (MMSerialPort *port,
} }
static void static void
enable_flash_done (MMSerialPort *port, gpointer user_data) enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data); mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data);
} }
@@ -111,8 +119,16 @@ disable_done (MMSerialPort *port,
} }
static void static void
disable_flash_done (MMSerialPort *port, gpointer user_data) disable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
mm_serial_port_queue_command (port, "+CFUN=0", 5, disable_done, user_data); mm_serial_port_queue_command (port, "+CFUN=0", 5, disable_done, user_data);
} }
@@ -137,13 +153,15 @@ enable (MMModem *modem,
if (mm_port_get_connected (MM_PORT (primary))) if (mm_port_get_connected (MM_PORT (primary)))
mm_serial_port_flash (primary, 1000, disable_flash_done, info); mm_serial_port_flash (primary, 1000, disable_flash_done, info);
else else
disable_flash_done (primary, info); disable_flash_done (primary, NULL, info);
} else { } else {
if (mm_serial_port_open (primary, &info->error)) if (!mm_serial_port_open (primary, &info->error)) {
mm_serial_port_flash (primary, 100, enable_flash_done, info); g_assert (info->error);
if (info->error)
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
return;
}
mm_serial_port_flash (primary, 100, enable_flash_done, info);
} }
} }

View File

@@ -69,6 +69,8 @@ mm_modem_error_get_type (void)
ENUM_ENTRY (MM_MODEM_ERROR_GENERAL, "General"), ENUM_ENTRY (MM_MODEM_ERROR_GENERAL, "General"),
ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, "OperationNotSupported"), ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, "OperationNotSupported"),
ENUM_ENTRY (MM_MODEM_ERROR_CONNECTED, "Connected"), ENUM_ENTRY (MM_MODEM_ERROR_CONNECTED, "Connected"),
ENUM_ENTRY (MM_MODEM_ERROR_DISCONNECTED, "Disconnected"),
ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_IN_PROGRESS, "OperationInProgress"),
{ 0, 0, 0 } { 0, 0, 0 }
}; };

View File

@@ -35,7 +35,9 @@ GType mm_serial_error_get_type (void);
enum { enum {
MM_MODEM_ERROR_GENERAL = 0, MM_MODEM_ERROR_GENERAL = 0,
MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED = 1, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED = 1,
MM_MODEM_ERROR_CONNECTED = 2 MM_MODEM_ERROR_CONNECTED = 2,
MM_MODEM_ERROR_DISCONNECTED = 3,
MM_MODEM_ERROR_OPERATION_IN_PROGRESS = 4
}; };
#define MM_MODEM_ERROR (mm_modem_error_quark ()) #define MM_MODEM_ERROR (mm_modem_error_quark ())

View File

@@ -218,8 +218,17 @@ init_done (MMSerialPort *port,
} }
static void static void
flash_done (MMSerialPort *port, gpointer user_data) flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error) {
/* Flash failed for some reason */
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1", 3, init_done, user_data); mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1", 3, init_done, user_data);
} }
@@ -240,11 +249,13 @@ enable (MMModem *modem,
return; return;
} }
if (mm_serial_port_open (priv->primary, &info->error)) if (!mm_serial_port_open (priv->primary, &info->error)) {
mm_serial_port_flash (priv->primary, 100, flash_done, info); g_assert (info->error);
if (info->error)
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
return;
}
mm_serial_port_flash (priv->primary, 100, flash_done, info);
} }
static void static void
@@ -283,12 +294,19 @@ connect (MMModem *modem,
} }
static void static void
disconnect_flash_done (MMSerialPort *port, gpointer user_data) disconnect_flash_done (MMSerialPort *port,
GError *error,
gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMGenericCdmaPrivate *priv; MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
mm_port_set_connected (priv->data, FALSE); mm_port_set_connected (priv->data, FALSE);
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
} }

View File

@@ -369,11 +369,17 @@ init_done (MMSerialPort *port,
} }
static void static void
enable_flash_done (MMSerialPort *port, gpointer user_data) enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMCallbackInfo *info = user_data; MMCallbackInfo *info = user_data;
char *cmd = NULL; char *cmd = NULL;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_INIT_CMD, &cmd, NULL); g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_INIT_CMD, &cmd, NULL);
mm_serial_port_queue_command (port, cmd, 3, init_done, user_data); mm_serial_port_queue_command (port, cmd, 3, init_done, user_data);
g_free (cmd); g_free (cmd);
@@ -390,12 +396,20 @@ disable_done (MMSerialPort *port,
} }
static void static void
disable_flash_done (MMSerialPort *port, gpointer user_data) disable_flash_done (MMSerialPort *port,
GError *error,
gpointer user_data)
{ {
MMCallbackInfo *info = user_data; MMCallbackInfo *info = user_data;
char *cmd = NULL; char *cmd = NULL;
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_UP_CMD, &cmd, NULL); if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_DOWN_CMD, &cmd, NULL);
if (cmd && strlen (cmd)) if (cmd && strlen (cmd))
mm_serial_port_queue_command (port, cmd, 5, disable_done, user_data); mm_serial_port_queue_command (port, cmd, 5, disable_done, user_data);
else else
@@ -423,13 +437,15 @@ enable (MMModem *modem,
if (mm_port_get_connected (MM_PORT (priv->primary))) if (mm_port_get_connected (MM_PORT (priv->primary)))
mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info); mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info);
else else
disable_flash_done (priv->primary, info); disable_flash_done (priv->primary, NULL, info);
} else { } else {
if (mm_serial_port_open (priv->primary, &info->error)) if (!mm_serial_port_open (priv->primary, &info->error)) {
mm_serial_port_flash (priv->primary, 100, enable_flash_done, info); g_assert (info->error);
if (info->error)
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
return;
}
mm_serial_port_flash (priv->primary, 100, enable_flash_done, info);
} }
} }
@@ -1037,11 +1053,19 @@ connect (MMModem *modem,
} }
static void static void
disconnect_flash_done (MMSerialPort *port, gpointer user_data) disconnect_flash_done (MMSerialPort *port,
GError *error,
gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
return;
}
mm_port_set_connected (priv->data, FALSE); mm_port_set_connected (priv->data, FALSE);
mm_callback_info_schedule (info); mm_callback_info_schedule (info);
} }

View File

@@ -205,6 +205,9 @@ dispose (GObject *object)
{ {
MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (object); MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (object);
if (MM_IS_SERIAL_PORT (priv->port))
mm_serial_port_flash_cancel (MM_SERIAL_PORT (priv->port));
g_object_unref (priv->port); g_object_unref (priv->port);
g_object_unref (priv->physdev); g_object_unref (priv->physdev);
g_free (priv->driver); g_free (priv->driver);
@@ -459,12 +462,8 @@ parse_response (MMSerialPort *port,
} }
static void static void
flash_done (MMSerialPort *port, gpointer user_data) flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{ {
MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data);
MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
task_priv->probe_id = 0;
mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data); mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data);
} }
@@ -506,7 +505,7 @@ mm_plugin_base_probe_port (MMPluginBase *self,
g_debug ("(%s): probe requested by plugin '%s'", name, priv->name); g_debug ("(%s): probe requested by plugin '%s'", name, priv->name);
task_priv->probe_port = serial; task_priv->probe_port = serial;
task_priv->probe_id = mm_serial_port_flash (serial, 100, flash_done, task); mm_serial_port_flash (serial, 100, flash_done, task);
return TRUE; return TRUE;
} }

View File

@@ -77,8 +77,93 @@ typedef struct {
guint queue_schedule; guint queue_schedule;
guint watch_id; guint watch_id;
guint timeout_id; guint timeout_id;
guint flash_id;
} MMSerialPortPrivate; } MMSerialPortPrivate;
#if 0
static const char *
baud_to_string (int baud)
{
const char *speed = NULL;
switch (baud) {
case B0:
speed = "0";
break;
case B50:
speed = "50";
break;
case B75:
speed = "75";
break;
case B110:
speed = "110";
break;
case B150:
speed = "150";
break;
case B300:
speed = "300";
break;
case B600:
speed = "600";
break;
case B1200:
speed = "1200";
break;
case B2400:
speed = "2400";
break;
case B4800:
speed = "4800";
break;
case B9600:
speed = "9600";
break;
case B19200:
speed = "19200";
break;
case B38400:
speed = "38400";
break;
case B57600:
speed = "57600";
break;
case B115200:
speed = "115200";
break;
case B460800:
speed = "460800";
break;
default:
break;
}
return speed;
}
void
mm_serial_port_print_config (MMSerialPort *port, const char *detail)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (port);
struct termio stbuf;
int err;
err = ioctl (priv->fd, TCGETA, &stbuf);
if (err) {
g_warning ("*** %s (%s): (%s) TCGETA error %d",
__func__, detail, mm_port_get_device (MM_PORT (port)), errno);
return;
}
g_message ("*** %s (%s): (%s) baud rate: %d (%s)",
__func__, detail, mm_port_get_device (MM_PORT (port)),
stbuf.c_cflag & CBAUD,
baud_to_string (stbuf.c_cflag & CBAUD));
}
#endif
typedef struct { typedef struct {
GRegex *regex; GRegex *regex;
MMSerialUnsolicitedMsgFn callback; MMSerialUnsolicitedMsgFn callback;
@@ -241,7 +326,7 @@ parse_stopbits (guint i)
} }
static gboolean static gboolean
config_fd (MMSerialPort *self) config_fd (MMSerialPort *self, GError **error)
{ {
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
struct termio stbuf; struct termio stbuf;
@@ -255,7 +340,13 @@ config_fd (MMSerialPort *self)
parity = parse_parity (priv->parity); parity = parse_parity (priv->parity);
stopbits = parse_stopbits (priv->stopbits); stopbits = parse_stopbits (priv->stopbits);
ioctl (priv->fd, TCGETA, &stbuf); memset (&stbuf, 0, sizeof (struct termio));
if (ioctl (priv->fd, TCGETA, &stbuf) != 0) {
g_warning ("%s (%s): TCGETA error: %d",
__func__,
mm_port_get_device (MM_PORT (self)),
errno);
}
stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR ); stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR );
stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET); stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
@@ -269,8 +360,11 @@ config_fd (MMSerialPort *self)
stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits); stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits);
if (ioctl (priv->fd, TCSETA, &stbuf) < 0) { if (ioctl (priv->fd, TCSETA, &stbuf) < 0) {
g_warning ("(%s) cannot control device (errno %d)", g_set_error (error,
mm_port_get_device (MM_PORT (self)), errno); MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"%s: failed to set serial port attributes; errno %d",
__func__, errno);
return FALSE; return FALSE;
} }
@@ -401,8 +495,10 @@ mm_serial_port_got_response (MMSerialPort *self, GError *error)
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
MMQueueData *info; MMQueueData *info;
if (priv->timeout_id) if (priv->timeout_id) {
g_source_remove (priv->timeout_id); g_source_remove (priv->timeout_id);
priv->timeout_id = 0;
}
info = (MMQueueData *) g_queue_pop_head (priv->queue); info = (MMQueueData *) g_queue_pop_head (priv->queue);
if (info) { if (info) {
@@ -689,9 +785,7 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
return FALSE; return FALSE;
} }
if (!config_fd (self)) { if (!config_fd (self, error)) {
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
"Could not open serial device %s: %s", device, strerror (errno));
close (priv->fd); close (priv->fd);
priv->fd = -1; priv->fd = -1;
return FALSE; return FALSE;
@@ -725,6 +819,11 @@ mm_serial_port_close (MMSerialPort *self)
priv->channel = NULL; priv->channel = NULL;
} }
if (priv->flash_id > 0) {
g_source_remove (priv->flash_id);
priv->flash_id = 0;
}
ioctl (priv->fd, TCSETA, &priv->old_t); ioctl (priv->fd, TCSETA, &priv->old_t);
close (priv->fd); close (priv->fd);
priv->fd = -1; priv->fd = -1;
@@ -789,81 +888,158 @@ typedef struct {
gpointer user_data; gpointer user_data;
} FlashInfo; } FlashInfo;
static speed_t static gboolean
get_speed (MMSerialPort *self) get_speed (MMSerialPort *self, speed_t *speed, GError **error)
{ {
struct termios options; struct termios options;
memset (&options, 0, sizeof (struct termios)); memset (&options, 0, sizeof (struct termios));
tcgetattr (MM_SERIAL_PORT_GET_PRIVATE (self)->fd, &options); if (tcgetattr (MM_SERIAL_PORT_GET_PRIVATE (self)->fd, &options) != 0) {
g_set_error (error,
MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"%s: tcgetattr() error %d",
__func__, errno);
return FALSE;
}
return cfgetospeed (&options); *speed = cfgetospeed (&options);
return TRUE;
} }
static void static gboolean
set_speed (MMSerialPort *self, speed_t speed) set_speed (MMSerialPort *self, speed_t speed, GError **error)
{ {
struct termios options; struct termios options;
int fd; int fd, count = 4;
gboolean success = FALSE;
fd = MM_SERIAL_PORT_GET_PRIVATE (self)->fd; fd = MM_SERIAL_PORT_GET_PRIVATE (self)->fd;
memset (&options, 0, sizeof (struct termios)); memset (&options, 0, sizeof (struct termios));
tcgetattr (fd, &options); if (tcgetattr (fd, &options) != 0) {
g_set_error (error,
MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"%s: tcgetattr() error %d",
__func__, errno);
return FALSE;
}
cfsetispeed (&options, speed); cfsetispeed (&options, speed);
cfsetospeed (&options, speed); cfsetospeed (&options, speed);
options.c_cflag |= (CLOCAL | CREAD); options.c_cflag |= (CLOCAL | CREAD);
tcsetattr (fd, TCSANOW, &options);
}
static void while (count-- > 0) {
flash_done (gpointer data) if (tcsetattr (fd, TCSANOW, &options) == 0) {
{ success = TRUE;
FlashInfo *info = (FlashInfo *) data; break; /* Operation successful */
}
info->callback (info->port, info->user_data); /* Try a few times if EAGAIN */
if (errno == EAGAIN)
g_usleep (100000);
else {
/* If not EAGAIN, hard error */
g_set_error (error,
MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"%s: tcsetattr() error %d",
__func__, errno);
return FALSE;
}
}
g_slice_free (FlashInfo, info); if (!success) {
g_set_error (error,
MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"%s: tcsetattr() retry timeout",
__func__);
return FALSE;
}
return TRUE;
} }
static gboolean static gboolean
flash_do (gpointer data) flash_do (gpointer data)
{ {
FlashInfo *info = (FlashInfo *) data; FlashInfo *info = (FlashInfo *) data;
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (info->port);
GError *error = NULL;
set_speed (info->port, info->current_speed); priv->flash_id = 0;
if (!set_speed (info->port, info->current_speed, &error))
g_assert (error);
info->callback (info->port, error, info->user_data);
g_clear_error (&error);
g_slice_free (FlashInfo, info);
return FALSE; return FALSE;
} }
guint gboolean
mm_serial_port_flash (MMSerialPort *self, mm_serial_port_flash (MMSerialPort *self,
guint32 flash_time, guint32 flash_time,
MMSerialFlashFn callback, MMSerialFlashFn callback,
gpointer user_data) gpointer user_data)
{ {
FlashInfo *info; FlashInfo *info;
guint id; MMSerialPortPrivate *priv;
speed_t cur_speed = 0;
GError *error = NULL;
g_return_val_if_fail (MM_IS_SERIAL_PORT (self), 0); g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
g_return_val_if_fail (callback != NULL, 0); g_return_val_if_fail (callback != NULL, FALSE);
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
if (priv->flash_id > 0) {
error = g_error_new_literal (MM_MODEM_ERROR,
MM_MODEM_ERROR_OPERATION_IN_PROGRESS,
"Modem is already being flashed.");
callback (self, error, user_data);
g_error_free (error);
return FALSE;
}
if (!get_speed (self, &cur_speed, &error)) {
callback (self, error, user_data);
g_error_free (error);
return FALSE;
}
info = g_slice_new0 (FlashInfo); info = g_slice_new0 (FlashInfo);
info->port = self; info->port = self;
info->current_speed = get_speed (self); info->current_speed = cur_speed;
info->callback = callback; info->callback = callback;
info->user_data = user_data; info->user_data = user_data;
set_speed (self, B0); if (!set_speed (self, B0, &error)) {
callback (self, error, user_data);
g_error_free (error);
return FALSE;
}
id = g_timeout_add_full (G_PRIORITY_DEFAULT, priv->flash_id = g_timeout_add (flash_time, flash_do, info);
flash_time, return TRUE;
flash_do, }
info,
flash_done);
return id; void
mm_serial_port_flash_cancel (MMSerialPort *self)
{
MMSerialPortPrivate *priv;
g_return_if_fail (MM_IS_SERIAL_PORT (self));
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
if (priv->flash_id > 0) {
g_source_remove (priv->flash_id);
priv->flash_id = 0;
}
} }
/*****************************************************************************/ /*****************************************************************************/

View File

@@ -53,6 +53,7 @@ typedef void (*MMSerialResponseFn) (MMSerialPort *port,
gpointer user_data); gpointer user_data);
typedef void (*MMSerialFlashFn) (MMSerialPort *port, typedef void (*MMSerialFlashFn) (MMSerialPort *port,
GError *error,
gpointer user_data); gpointer user_data);
struct _MMSerialPort { struct _MMSerialPort {
@@ -94,10 +95,11 @@ void mm_serial_port_queue_command_cached (MMSerialPort *self,
MMSerialResponseFn callback, MMSerialResponseFn callback,
gpointer user_data); gpointer user_data);
guint mm_serial_port_flash (MMSerialPort *self, gboolean mm_serial_port_flash (MMSerialPort *self,
guint32 flash_time, guint32 flash_time,
MMSerialFlashFn callback, MMSerialFlashFn callback,
gpointer user_data); gpointer user_data);
void mm_serial_port_flash_cancel (MMSerialPort *self);
#endif /* MM_SERIAL_PORT_H */ #endif /* MM_SERIAL_PORT_H */