aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mm-errors.c2
-rw-r--r--src/mm-errors.h4
-rw-r--r--src/mm-generic-cdma.c34
-rw-r--r--src/mm-generic-gsm.c42
-rw-r--r--src/mm-plugin-base.c11
-rw-r--r--src/mm-serial-port.c252
-rw-r--r--src/mm-serial-port.h4
7 files changed, 286 insertions, 63 deletions
diff --git a/src/mm-errors.c b/src/mm-errors.c
index 0b0706fd..510fa6d0 100644
--- a/src/mm-errors.c
+++ b/src/mm-errors.c
@@ -69,6 +69,8 @@ mm_modem_error_get_type (void)
ENUM_ENTRY (MM_MODEM_ERROR_GENERAL, "General"),
ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, "OperationNotSupported"),
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 }
};
diff --git a/src/mm-errors.h b/src/mm-errors.h
index 2e452872..bc43d3e9 100644
--- a/src/mm-errors.h
+++ b/src/mm-errors.h
@@ -35,7 +35,9 @@ GType mm_serial_error_get_type (void);
enum {
MM_MODEM_ERROR_GENERAL = 0,
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 ())
diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c
index d81c1b18..be1ad8e9 100644
--- a/src/mm-generic-cdma.c
+++ b/src/mm-generic-cdma.c
@@ -218,8 +218,17 @@ init_done (MMSerialPort *port,
}
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);
}
@@ -240,11 +249,13 @@ enable (MMModem *modem,
return;
}
- if (mm_serial_port_open (priv->primary, &info->error))
- mm_serial_port_flash (priv->primary, 100, flash_done, info);
-
- if (info->error)
+ if (!mm_serial_port_open (priv->primary, &info->error)) {
+ g_assert (info->error);
mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_serial_port_flash (priv->primary, 100, flash_done, info);
}
static void
@@ -283,12 +294,19 @@ connect (MMModem *modem,
}
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;
- 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_callback_info_schedule (info);
}
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 3a0c4993..87900ccc 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -369,11 +369,17 @@ init_done (MMSerialPort *port,
}
static void
-enable_flash_done (MMSerialPort *port, gpointer user_data)
+enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
{
MMCallbackInfo *info = user_data;
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);
mm_serial_port_queue_command (port, cmd, 3, init_done, user_data);
g_free (cmd);
@@ -390,12 +396,20 @@ disable_done (MMSerialPort *port,
}
static void
-disable_flash_done (MMSerialPort *port, gpointer user_data)
+disable_flash_done (MMSerialPort *port,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = user_data;
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))
mm_serial_port_queue_command (port, cmd, 5, disable_done, user_data);
else
@@ -423,13 +437,15 @@ enable (MMModem *modem,
if (mm_port_get_connected (MM_PORT (priv->primary)))
mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info);
else
- disable_flash_done (priv->primary, info);
+ disable_flash_done (priv->primary, NULL, info);
} else {
- if (mm_serial_port_open (priv->primary, &info->error))
- mm_serial_port_flash (priv->primary, 100, enable_flash_done, info);
-
- if (info->error)
+ if (!mm_serial_port_open (priv->primary, &info->error)) {
+ g_assert (info->error);
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
-disconnect_flash_done (MMSerialPort *port, gpointer user_data)
+disconnect_flash_done (MMSerialPort *port,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
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_callback_info_schedule (info);
}
diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c
index 3b219302..7adbe2a2 100644
--- a/src/mm-plugin-base.c
+++ b/src/mm-plugin-base.c
@@ -205,6 +205,9 @@ dispose (GObject *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->physdev);
g_free (priv->driver);
@@ -459,12 +462,8 @@ parse_response (MMSerialPort *port,
}
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);
}
@@ -506,7 +505,7 @@ mm_plugin_base_probe_port (MMPluginBase *self,
g_debug ("(%s): probe requested by plugin '%s'", name, priv->name);
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;
}
diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c
index 5b8b6d52..7de2db41 100644
--- a/src/mm-serial-port.c
+++ b/src/mm-serial-port.c
@@ -77,8 +77,93 @@ typedef struct {
guint queue_schedule;
guint watch_id;
guint timeout_id;
+
+ guint flash_id;
} 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 {
GRegex *regex;
MMSerialUnsolicitedMsgFn callback;
@@ -241,7 +326,7 @@ parse_stopbits (guint i)
}
static gboolean
-config_fd (MMSerialPort *self)
+config_fd (MMSerialPort *self, GError **error)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
struct termio stbuf;
@@ -255,7 +340,13 @@ config_fd (MMSerialPort *self)
parity = parse_parity (priv->parity);
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_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
@@ -269,8 +360,11 @@ config_fd (MMSerialPort *self)
stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits);
if (ioctl (priv->fd, TCSETA, &stbuf) < 0) {
- g_warning ("(%s) cannot control device (errno %d)",
- mm_port_get_device (MM_PORT (self)), errno);
+ g_set_error (error,
+ MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "%s: failed to set serial port attributes; errno %d",
+ __func__, errno);
return FALSE;
}
@@ -401,8 +495,10 @@ mm_serial_port_got_response (MMSerialPort *self, GError *error)
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
MMQueueData *info;
- if (priv->timeout_id)
+ if (priv->timeout_id) {
g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+ }
info = (MMQueueData *) g_queue_pop_head (priv->queue);
if (info) {
@@ -689,9 +785,7 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
return FALSE;
}
- if (!config_fd (self)) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
- "Could not open serial device %s: %s", device, strerror (errno));
+ if (!config_fd (self, error)) {
close (priv->fd);
priv->fd = -1;
return FALSE;
@@ -725,6 +819,11 @@ mm_serial_port_close (MMSerialPort *self)
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);
close (priv->fd);
priv->fd = -1;
@@ -789,81 +888,158 @@ typedef struct {
gpointer user_data;
} FlashInfo;
-static speed_t
-get_speed (MMSerialPort *self)
+static gboolean
+get_speed (MMSerialPort *self, speed_t *speed, GError **error)
{
struct termios options;
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
-set_speed (MMSerialPort *self, speed_t speed)
+static gboolean
+set_speed (MMSerialPort *self, speed_t speed, GError **error)
{
struct termios options;
- int fd;
+ int fd, count = 4;
+ gboolean success = FALSE;
fd = MM_SERIAL_PORT_GET_PRIVATE (self)->fd;
+
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);
cfsetospeed (&options, speed);
-
options.c_cflag |= (CLOCAL | CREAD);
- tcsetattr (fd, TCSANOW, &options);
-}
-static void
-flash_done (gpointer data)
-{
- FlashInfo *info = (FlashInfo *) data;
+ while (count-- > 0) {
+ if (tcsetattr (fd, TCSANOW, &options) == 0) {
+ success = TRUE;
+ 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
flash_do (gpointer 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;
}
-guint
+gboolean
mm_serial_port_flash (MMSerialPort *self,
guint32 flash_time,
MMSerialFlashFn callback,
gpointer user_data)
{
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 (callback != NULL, 0);
+ g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
+ 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->port = self;
- info->current_speed = get_speed (self);
+ info->current_speed = cur_speed;
info->callback = callback;
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;
+ }
+
+ priv->flash_id = g_timeout_add (flash_time, flash_do, info);
+ return TRUE;
+}
+
+void
+mm_serial_port_flash_cancel (MMSerialPort *self)
+{
+ MMSerialPortPrivate *priv;
+
+ g_return_if_fail (MM_IS_SERIAL_PORT (self));
- id = g_timeout_add_full (G_PRIORITY_DEFAULT,
- flash_time,
- flash_do,
- info,
- flash_done);
+ priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- return id;
+ if (priv->flash_id > 0) {
+ g_source_remove (priv->flash_id);
+ priv->flash_id = 0;
+ }
}
/*****************************************************************************/
diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h
index a077f94f..841b4fa3 100644
--- a/src/mm-serial-port.h
+++ b/src/mm-serial-port.h
@@ -53,6 +53,7 @@ typedef void (*MMSerialResponseFn) (MMSerialPort *port,
gpointer user_data);
typedef void (*MMSerialFlashFn) (MMSerialPort *port,
+ GError *error,
gpointer user_data);
struct _MMSerialPort {
@@ -94,10 +95,11 @@ void mm_serial_port_queue_command_cached (MMSerialPort *self,
MMSerialResponseFn callback,
gpointer user_data);
-guint mm_serial_port_flash (MMSerialPort *self,
+gboolean mm_serial_port_flash (MMSerialPort *self,
guint32 flash_time,
MMSerialFlashFn callback,
gpointer user_data);
+void mm_serial_port_flash_cancel (MMSerialPort *self);
#endif /* MM_SERIAL_PORT_H */