aboutsummaryrefslogtreecommitdiff
path: root/src/mm-serial-port.c
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2009-09-08 17:31:54 -0700
committerDan Williams <dcbw@redhat.com>2009-09-08 17:34:04 -0700
commit14e5c52f78e7ad23b18b111e3271cbecad6acf3f (patch)
treeee6929c990c63139e4209a94f3e2d8c617547f4a /src/mm-serial-port.c
parent6cf01d2ab698d05eb58bffa7e85f41024f5c0546 (diff)
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.
Diffstat (limited to 'src/mm-serial-port.c')
-rw-r--r--src/mm-serial-port.c252
1 files changed, 214 insertions, 38 deletions
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;
+ }
}
/*****************************************************************************/