diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-errors.c | 2 | ||||
-rw-r--r-- | src/mm-errors.h | 4 | ||||
-rw-r--r-- | src/mm-generic-cdma.c | 34 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 42 | ||||
-rw-r--r-- | src/mm-plugin-base.c | 11 | ||||
-rw-r--r-- | src/mm-serial-port.c | 252 | ||||
-rw-r--r-- | src/mm-serial-port.h | 4 |
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 */ |