diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 22 | ||||
-rw-r--r-- | src/main.c | 93 | ||||
-rw-r--r-- | src/mm-generic-cdma.c | 217 | ||||
-rw-r--r-- | src/mm-generic-cdma.h | 17 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 457 | ||||
-rw-r--r-- | src/mm-generic-gsm.h | 17 | ||||
-rw-r--r-- | src/mm-manager.c | 376 | ||||
-rw-r--r-- | src/mm-manager.h | 6 | ||||
-rw-r--r-- | src/mm-modem.c | 97 | ||||
-rw-r--r-- | src/mm-modem.h | 54 | ||||
-rw-r--r-- | src/mm-plugin.c | 48 | ||||
-rw-r--r-- | src/mm-plugin.h | 58 | ||||
-rw-r--r-- | src/mm-serial-port.c | 1107 | ||||
-rw-r--r-- | src/mm-serial-port.h | 114 | ||||
-rw-r--r-- | src/mm-serial.c | 1024 | ||||
-rw-r--r-- | src/mm-serial.h | 80 |
16 files changed, 2192 insertions, 1595 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d6d4e73f..e1557341 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,27 +2,35 @@ sbin_PROGRAMS = modem-manager modem_manager_CPPFLAGS = \ $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ -I${top_builddir}/marshallers \ -DPLUGINDIR=\"$(pkglibdir)\" modem_manager_LDADD = \ $(MM_LIBS) \ + $(GUDEV_LIBS) \ $(top_builddir)/marshallers/libmarshallers.la modem_manager_SOURCES = \ main.c \ mm-callback-info.c \ mm-callback-info.h \ - mm-generic-cdma.c \ - mm-generic-cdma.h \ - mm-generic-gsm.c \ - mm-generic-gsm.h \ mm-errors.c \ mm-errors.h \ mm-manager.c \ mm-manager.h \ mm-modem.c \ mm-modem.h \ + mm-serial.c \ + mm-serial.h \ + mm-serial-port.c \ + mm-serial-port.h \ + mm-serial-parsers.c \ + mm-serial-parsers.h \ + mm-generic-cdma.c \ + mm-generic-cdma.h \ + mm-generic-gsm.c \ + mm-generic-gsm.h \ mm-modem-cdma.c \ mm-modem-cdma.h \ mm-modem-gsm-card.c \ @@ -36,11 +44,7 @@ modem_manager_SOURCES = \ mm-options.c \ mm-options.h \ mm-plugin.c \ - mm-plugin.h \ - mm-serial.c \ - mm-serial.h \ - mm-serial-parsers.c \ - mm-serial-parsers.h + mm-plugin.h mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $< @@ -5,7 +5,6 @@ #include <string.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> -#include <libhal.h> #include "mm-manager.h" #include "mm-options.h" @@ -135,87 +134,11 @@ create_dbus_proxy (DBusGConnection *bus) return proxy; } -static void -hal_init (MMManager *manager) -{ - LibHalContext *hal_ctx; - DBusError dbus_error; - - hal_ctx = libhal_ctx_new (); - if (!hal_ctx) { - g_warning ("Could not get connection to the HAL service."); - } - - libhal_ctx_set_dbus_connection (hal_ctx, dbus_g_connection_get_connection (mm_manager_get_bus (manager))); - - dbus_error_init (&dbus_error); - if (!libhal_ctx_init (hal_ctx, &dbus_error)) { - g_warning ("libhal_ctx_init() failed: %s", dbus_error.message); - dbus_error_free (&dbus_error); - } - - mm_manager_set_hal_ctx (manager, hal_ctx); -} - -static void -hal_deinit (MMManager *manager) -{ - LibHalContext *hal_ctx; - - hal_ctx = mm_manager_get_hal_ctx (manager); - if (hal_ctx) { - libhal_ctx_shutdown (hal_ctx, NULL); - libhal_ctx_free (hal_ctx); - mm_manager_set_hal_ctx (manager, NULL); - } -} - static gboolean -hal_on_bus (DBusGProxy *proxy) +start_manager (gpointer user_data) { - GError *err = NULL; - gboolean has_owner = FALSE; - - if (!dbus_g_proxy_call (proxy, - "NameHasOwner", &err, - G_TYPE_STRING, HAL_DBUS_SERVICE, - G_TYPE_INVALID, - G_TYPE_BOOLEAN, &has_owner, - G_TYPE_INVALID)) { - g_warning ("Error on NameHasOwner DBUS call: %s", err->message); - g_error_free (err); - } - - return has_owner; -} - -static void -name_owner_changed (DBusGProxy *proxy, - const char *name, - const char *old_owner, - const char *new_owner, - gpointer user_data) -{ - MMManager *manager; - gboolean old_owner_good; - gboolean new_owner_good; - - /* Only care about signals from HAL */ - if (strcmp (name, HAL_DBUS_SERVICE)) - return; - - manager = MM_MANAGER (user_data); - old_owner_good = (old_owner && (strlen (old_owner) > 0)); - new_owner_good = (new_owner && (strlen (new_owner) > 0)); - - if (!old_owner_good && new_owner_good) { - g_message ("HAL appeared"); - hal_init (manager); - } else if (old_owner_good && !new_owner_good) { - /* HAL went away. Bad HAL. */ - g_message ("HAL disappeared"); - hal_deinit (manager); - } + mm_manager_start (MM_MANAGER (user_data)); + return FALSE; } int @@ -249,21 +172,13 @@ main (int argc, char *argv[]) return -1; manager = mm_manager_new (bus); - - dbus_g_proxy_connect_signal (proxy, - "NameOwnerChanged", - G_CALLBACK (name_owner_changed), - manager, NULL); - - if (hal_on_bus (proxy)) - hal_init (manager); + g_idle_add (start_manager, manager); loop = g_main_loop_new (NULL, FALSE); g_signal_connect (proxy, "destroy", G_CALLBACK (destroy_cb), loop); g_main_loop_run (loop); - hal_deinit (manager); g_object_unref (manager); g_object_unref (proxy); dbus_g_connection_unref (bus); diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c index de8c916a..984b8aa8 100644 --- a/src/mm-generic-cdma.c +++ b/src/mm-generic-cdma.c @@ -16,25 +16,124 @@ static gpointer mm_generic_cdma_parent_class = NULL; typedef struct { char *driver; + char *plugin; + char *device; + + guint32 ip_method; + gboolean valid; + + MMSerialPort *primary; + MMSerialPort *secondary; } MMGenericCdmaPrivate; MMModem * -mm_generic_cdma_new (const char *serial_device, const char *driver) +mm_generic_cdma_new (const char *device, + const char *driver, + const char *plugin) { - g_return_val_if_fail (serial_device != NULL, NULL); + g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); return MM_MODEM (g_object_new (MM_TYPE_GENERIC_CDMA, - MM_SERIAL_DEVICE, serial_device, + MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, - MM_MODEM_TYPE, MM_MODEM_TYPE_CDMA, + MM_MODEM_PLUGIN, plugin, NULL)); } /*****************************************************************************/ static void -enable_error_reporting_done (MMSerial *serial, +check_valid (MMGenericCdma *self) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + gboolean new_valid = FALSE; + + if (priv->primary) + new_valid = TRUE; + + if (priv->valid != new_valid) { + priv->valid = new_valid; + g_object_notify (G_OBJECT (self), MM_MODEM_VALID); + } +} + +static gboolean +owns_port (MMModem *modem, const char *subsys, const char *name) +{ + MMGenericCdma *self = MM_GENERIC_CDMA (modem); + + if (strcmp (subsys, "tty")) + return FALSE; + + return !!mm_serial_get_port (MM_SERIAL (self), name); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + GError **error) +{ + MMGenericCdma *self = MM_GENERIC_CDMA (modem); + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + MMSerialPortType ptype = MM_SERIAL_PORT_TYPE_IGNORED; + MMSerialPort *port; + + if (strcmp (subsys, "tty")) + return FALSE; + + if (!priv->primary) + ptype = MM_SERIAL_PORT_TYPE_PRIMARY; + else if (!priv->secondary) + ptype = MM_SERIAL_PORT_TYPE_SECONDARY; + + port = mm_serial_add_port (MM_SERIAL (self), name, ptype); + g_object_set (G_OBJECT (port), MM_SERIAL_PORT_CARRIER_DETECT, FALSE, NULL); + mm_serial_port_set_response_parser (port, + mm_serial_parser_v1_parse, + mm_serial_parser_v1_new (), + mm_serial_parser_v1_destroy); + + if (ptype == MM_SERIAL_PORT_TYPE_PRIMARY) { + priv->primary = port; + g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); + check_valid (self); + } else if (ptype == MM_SERIAL_PORT_TYPE_SECONDARY) + priv->secondary = port; + + return TRUE; +} + +static void +release_port (MMModem *modem, const char *subsys, const char *name) +{ + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); + MMSerialPort *port; + + if (strcmp (subsys, "tty")) + return; + + port = mm_serial_get_port (MM_SERIAL (modem), name); + if (!port) + return; + + if (port == priv->primary) { + mm_serial_remove_port (MM_SERIAL (modem), port); + priv->primary = NULL; + } + + if (port == priv->secondary) { + mm_serial_remove_port (MM_SERIAL (modem), port); + priv->secondary = NULL; + } + + check_valid (MM_GENERIC_CDMA (modem)); +} + +static void +enable_error_reporting_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -49,7 +148,7 @@ enable_error_reporting_done (MMSerial *serial, } static void -init_done (MMSerial *serial, +init_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -65,14 +164,14 @@ init_done (MMSerial *serial, FIXME: It's mandatory by spec, so it really shouldn't be optional. Figure out which CDMA modems have problems with it and implement plugin for them. */ - mm_serial_queue_command (serial, "+CMEE=1", 3, enable_error_reporting_done, user_data); + mm_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data); } } static void -flash_done (MMSerial *serial, gpointer user_data) +flash_done (MMSerialPort *port, gpointer user_data) { - mm_serial_queue_command (serial, "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); } static void @@ -81,25 +180,26 @@ enable (MMModem *modem, MMModemFn callback, gpointer user_data) { + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); MMCallbackInfo *info; info = mm_callback_info_new (modem, callback, user_data); if (!do_enable) { - mm_serial_close (MM_SERIAL (modem)); + mm_serial_port_close (priv->primary); mm_callback_info_schedule (info); return; } - if (mm_serial_open (MM_SERIAL (modem), &info->error)) - mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info); + if (mm_serial_port_open (priv->primary, &info->error)) + mm_serial_port_flash (priv->primary, 100, flash_done, info); if (info->error) mm_callback_info_schedule (info); } static void -dial_done (MMSerial *serial, +dial_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -118,17 +218,18 @@ connect (MMModem *modem, MMModemFn callback, gpointer user_data) { + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); MMCallbackInfo *info; char *command; info = mm_callback_info_new (modem, callback, user_data); command = g_strconcat ("DT", number, NULL); - mm_serial_queue_command (MM_SERIAL (modem), command, 60, dial_done, info); + mm_serial_port_queue_command (priv->primary, command, 60, dial_done, info); g_free (command); } static void -disconnect_flash_done (MMSerial *serial, gpointer user_data) +disconnect_flash_done (MMSerialPort *port, gpointer user_data) { mm_callback_info_schedule ((MMCallbackInfo *) user_data); } @@ -138,14 +239,17 @@ disconnect (MMModem *modem, MMModemFn callback, gpointer user_data) { + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); MMCallbackInfo *info; + g_return_if_fail (priv->primary != NULL); + info = mm_callback_info_new (modem, callback, user_data); - mm_serial_flash (MM_SERIAL (modem), 1000, disconnect_flash_done, info); + mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info); } static void -get_signal_quality_done (MMSerial *serial, +get_signal_quality_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -182,10 +286,15 @@ get_signal_quality (MMModemCdma *modem, MMModemUIntFn callback, gpointer user_data) { + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem); MMCallbackInfo *info; info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - mm_serial_queue_command (MM_SERIAL (modem), "+CSQ", 3, get_signal_quality_done, info); + /* Prefer secondary port for signal strength */ + mm_serial_port_queue_command (priv->secondary ? priv->secondary : priv->primary, + "+CSQ", + 3, + get_signal_quality_done, info); } /*****************************************************************************/ @@ -261,6 +370,7 @@ simple_connect (MMModemSimple *simple, gpointer user_data) { MMCallbackInfo *info; + GError *error = NULL; info = mm_callback_info_new (MM_MODEM (simple), callback, user_data); mm_callback_info_set_data (info, "simple-connect-properties", @@ -268,12 +378,12 @@ simple_connect (MMModemSimple *simple, (GDestroyNotify) g_hash_table_unref); /* At least number must be present */ - if (!simple_get_string_property (info, "number", &info->error)) { - if (!info->error) - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Missing number property"); + if (!simple_get_string_property (info, "number", &error)) { + if (!error) + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Missing number property"); } - simple_state_machine (MM_MODEM (simple), NULL, info); + simple_state_machine (MM_MODEM (simple), error, info); } static void @@ -340,6 +450,9 @@ simple_get_status (MMModemSimple *simple, static void modem_init (MMModem *modem_class) { + modem_class->owns_port = owns_port; + modem_class->grab_port = grab_port; + modem_class->release_port = release_port; modem_class->enable = enable; modem_class->connect = connect; modem_class->disconnect = disconnect; @@ -361,24 +474,32 @@ modem_simple_init (MMModemSimple *class) static void mm_generic_cdma_init (MMGenericCdma *self) { - mm_serial_set_response_parser (MM_SERIAL (self), - mm_serial_parser_v1_parse, - mm_serial_parser_v1_new (), - mm_serial_parser_v1_destroy); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (object); + switch (prop_id) { case MM_MODEM_PROP_DRIVER: /* Construct only */ - MM_GENERIC_CDMA_GET_PRIVATE (object)->driver = g_value_dup_string (value); + priv->driver = g_value_dup_string (value); + break; + case MM_MODEM_PROP_PLUGIN: + /* Construct only */ + priv->plugin = g_value_dup_string (value); + break; + case MM_MODEM_PROP_MASTER_DEVICE: + /* Constrcut only */ + priv->device = g_value_dup_string (value); break; - case MM_MODEM_PROP_DEVICE: - case MM_MODEM_PROP_TYPE: case MM_MODEM_PROP_IP_METHOD: + priv->ip_method = g_value_get_uint (value); + break; + case MM_MODEM_PROP_TYPE: + case MM_MODEM_PROP_VALID: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -390,18 +511,32 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { + MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (object); + switch (prop_id) { - case MM_MODEM_PROP_DEVICE: - g_value_set_string (value, mm_serial_get_device (MM_SERIAL (object))); + case MM_MODEM_PROP_DATA_DEVICE: + if (priv->primary) + g_value_set_string (value, mm_serial_port_get_device (priv->primary)); + else + g_value_set_string (value, NULL); + break; + case MM_MODEM_PROP_MASTER_DEVICE: + g_value_set_string (value, priv->device); break; case MM_MODEM_PROP_DRIVER: - g_value_set_string (value, MM_GENERIC_CDMA_GET_PRIVATE (object)->driver); + g_value_set_string (value, priv->driver); + break; + case MM_MODEM_PROP_PLUGIN: + g_value_set_string (value, priv->plugin); break; case MM_MODEM_PROP_TYPE: g_value_set_uint (value, MM_MODEM_TYPE_CDMA); break; case MM_MODEM_PROP_IP_METHOD: - g_value_set_uint (value, MM_MODEM_IP_METHOD_PPP); + g_value_set_uint (value, priv->ip_method); + break; + case MM_MODEM_PROP_VALID: + g_value_set_boolean (value, priv->valid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -434,20 +569,32 @@ mm_generic_cdma_class_init (MMGenericCdmaClass *klass) /* Properties */ g_object_class_override_property (object_class, - MM_MODEM_PROP_DEVICE, - MM_MODEM_DEVICE); + MM_MODEM_PROP_DATA_DEVICE, + MM_MODEM_DATA_DEVICE); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_MASTER_DEVICE, + MM_MODEM_MASTER_DEVICE); g_object_class_override_property (object_class, MM_MODEM_PROP_DRIVER, MM_MODEM_DRIVER); g_object_class_override_property (object_class, + MM_MODEM_PROP_PLUGIN, + MM_MODEM_PLUGIN); + + g_object_class_override_property (object_class, MM_MODEM_PROP_TYPE, MM_MODEM_TYPE); g_object_class_override_property (object_class, MM_MODEM_PROP_IP_METHOD, MM_MODEM_IP_METHOD); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_VALID, + MM_MODEM_VALID); } GType diff --git a/src/mm-generic-cdma.h b/src/mm-generic-cdma.h index 125b540a..c8d25518 100644 --- a/src/mm-generic-cdma.h +++ b/src/mm-generic-cdma.h @@ -6,12 +6,12 @@ #include "mm-modem.h" #include "mm-serial.h" -#define MM_TYPE_GENERIC_CDMA (mm_generic_cdma_get_type ()) -#define MM_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdma)) -#define MM_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass)) -#define MM_IS_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_CDMA)) -#define MM_IS_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_CDMA)) -#define MM_GENERIC_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass)) +#define MM_TYPE_GENERIC_CDMA (mm_generic_cdma_get_type ()) +#define MM_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdma)) +#define MM_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass)) +#define MM_IS_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_CDMA)) +#define MM_IS_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_CDMA)) +#define MM_GENERIC_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass)) typedef struct { MMSerial parent; @@ -23,7 +23,8 @@ typedef struct { GType mm_generic_cdma_get_type (void); -MMModem *mm_generic_cdma_new (const char *serial_device, - const char *driver); +MMModem *mm_generic_cdma_new (const char *device, + const char *driver, + const char *plugin); #endif /* MM_GENERIC_CDMA_H */ diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index a566fb6e..3cf8ab8c 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -18,10 +18,14 @@ static gpointer mm_generic_gsm_parent_class = NULL; typedef struct { char *driver; + char *plugin; + char *device; + + gboolean valid; + char *data_device; char *oper_code; char *oper_name; - guint32 modem_type; guint32 ip_method; gboolean unsolicited_registration; @@ -30,24 +34,39 @@ typedef struct { guint32 signal_quality; guint32 cid; + + MMSerialPort *primary; + MMSerialPort *secondary; } MMGenericGsmPrivate; -static void get_registration_status (MMSerial *serial, MMCallbackInfo *info); -static void read_operator_done (MMSerial *serial, - GString *response, - GError *error, - gpointer user_data); +static void get_registration_status (MMSerialPort *port, MMCallbackInfo *info); +static void read_operator_code_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data); + +static void read_operator_name_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data); + +static void reg_state_changed (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data); MMModem * -mm_generic_gsm_new (const char *serial_device, const char *driver) +mm_generic_gsm_new (const char *device, + const char *driver, + const char *plugin) { - g_return_val_if_fail (serial_device != NULL, NULL); + g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); return MM_MODEM (g_object_new (MM_TYPE_GENERIC_GSM, - MM_SERIAL_DEVICE, serial_device, + MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, - MM_MODEM_TYPE, MM_MODEM_TYPE_GSM, + MM_MODEM_PLUGIN, plugin, NULL)); } @@ -101,8 +120,8 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *modem, if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME || status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) { - mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,2;+COPS?", 3, read_operator_done, GINT_TO_POINTER (0)); - mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,0;+COPS?", 3, read_operator_done, GINT_TO_POINTER (1)); + mm_serial_port_queue_command (priv->primary, "+COPS=3,2;+COPS?", 3, read_operator_code_done, modem); + mm_serial_port_queue_command (priv->primary, "+COPS=3,0;+COPS?", 3, read_operator_name_done, modem); mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), got_signal_quality, NULL); } else { g_free (priv->oper_code); @@ -116,7 +135,7 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *modem, } static void -pin_check_done (MMSerial *serial, +pin_check_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -150,18 +169,113 @@ mm_generic_gsm_check_pin (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv; MMCallbackInfo *info; g_return_if_fail (MM_IS_GENERIC_GSM (modem)); + priv = MM_GENERIC_GSM_GET_PRIVATE (modem); info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); - mm_serial_queue_command (MM_SERIAL (modem), "+CPIN?", 3, pin_check_done, info); + mm_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info); } /*****************************************************************************/ static void -enable_done (MMSerial *serial, +check_valid (MMGenericGsm *self) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + gboolean new_valid = FALSE; + + if (priv->primary) + new_valid = TRUE; + + if (priv->valid != new_valid) { + priv->valid = new_valid; + g_object_notify (G_OBJECT (self), MM_MODEM_VALID); + } +} + +static gboolean +owns_port (MMModem *modem, const char *subsys, const char *name) +{ + MMGenericGsm *self = MM_GENERIC_GSM (modem); + + if (strcmp (subsys, "tty")) + return FALSE; + + return !!mm_serial_get_port (MM_SERIAL (self), name); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + GError **error) +{ + MMGenericGsm *self = MM_GENERIC_GSM (modem); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + MMSerialPortType ptype = MM_SERIAL_PORT_TYPE_IGNORED; + MMSerialPort *port; + GRegex *regex; + + if (strcmp (subsys, "tty")) + return FALSE; + + if (!priv->primary) + ptype = MM_SERIAL_PORT_TYPE_PRIMARY; + else if (!priv->secondary) + ptype = MM_SERIAL_PORT_TYPE_SECONDARY; + + port = mm_serial_add_port (MM_SERIAL (self), name, ptype); + mm_serial_port_set_response_parser (port, + mm_serial_parser_v1_parse, + mm_serial_parser_v1_new (), + mm_serial_parser_v1_destroy); + + regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (port, regex, reg_state_changed, self, NULL); + g_regex_unref (regex); + + + if (ptype == MM_SERIAL_PORT_TYPE_PRIMARY) { + priv->primary = port; + g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); + check_valid (self); + } else if (ptype == MM_SERIAL_PORT_TYPE_SECONDARY) + priv->secondary = port; + + return TRUE; +} + +static void +release_port (MMModem *modem, const char *subsys, const char *name) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMSerialPort *port; + + if (strcmp (subsys, "tty")) + return; + + port = mm_serial_get_port (MM_SERIAL (modem), name); + if (!port) + return; + + if (port == priv->primary) { + mm_serial_remove_port (MM_SERIAL (modem), port); + priv->primary = NULL; + check_valid (MM_GENERIC_GSM (modem)); + } + + if (port == priv->secondary) { + mm_serial_remove_port (MM_SERIAL (modem), port); + priv->secondary = NULL; + check_valid (MM_GENERIC_GSM (modem)); + } +} + +static void +enable_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -175,7 +289,7 @@ enable_done (MMSerial *serial, } static void -init_done (MMSerial *serial, +init_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -186,35 +300,35 @@ init_done (MMSerial *serial, info->error = g_error_copy (error); mm_callback_info_schedule (info); } else { - if (MM_GENERIC_GSM_GET_PRIVATE (serial)->unsolicited_registration) - mm_serial_queue_command (serial, "+CREG=1", 5, NULL, NULL); + if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->unsolicited_registration) + mm_serial_port_queue_command (port, "+CREG=1", 5, NULL, NULL); else - mm_serial_queue_command (serial, "+CREG=0", 5, NULL, NULL); + mm_serial_port_queue_command (port, "+CREG=0", 5, NULL, NULL); - mm_serial_queue_command (serial, "+CFUN=1", 5, enable_done, user_data); + mm_serial_port_queue_command (port, "+CFUN=1", 5, enable_done, user_data); } } static void -enable_flash_done (MMSerial *serial, gpointer user_data) +enable_flash_done (MMSerialPort *port, gpointer user_data) { - mm_serial_queue_command (serial, "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 -disable_done (MMSerial *serial, +disable_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) { - mm_serial_close (serial); + mm_serial_port_close (port); mm_callback_info_schedule ((MMCallbackInfo *) user_data); } static void -disable_flash_done (MMSerial *serial, gpointer user_data) +disable_flash_done (MMSerialPort *port, gpointer user_data) { - mm_serial_queue_command (serial, "+CFUN=0", 5, disable_done, user_data); + mm_serial_port_queue_command (port, "+CFUN=0", 5, disable_done, user_data); } static void @@ -223,6 +337,7 @@ enable (MMModem *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; /* First, reset the previously used CID */ @@ -233,13 +348,13 @@ enable (MMModem *modem, if (!do_enable) { mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); - if (mm_serial_is_connected (MM_SERIAL (modem))) - mm_serial_flash (MM_SERIAL (modem), 1000, disable_flash_done, info); + if (mm_serial_port_is_connected (priv->primary)) + mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info); else - disable_flash_done (MM_SERIAL (modem), info); + disable_flash_done (priv->primary, info); } else { - if (mm_serial_open (MM_SERIAL (modem), &info->error)) - mm_serial_flash (MM_SERIAL (modem), 100, enable_flash_done, info); + if (mm_serial_port_open (priv->primary, &info->error)) + mm_serial_port_flash (priv->primary, 100, enable_flash_done, info); if (info->error) mm_callback_info_schedule (info); @@ -247,7 +362,7 @@ enable (MMModem *modem, } static void -get_string_done (MMSerial *serial, +get_string_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -267,10 +382,11 @@ get_imei (MMModemGsmCard *modem, MMModemStringFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); - mm_serial_queue_command_cached (MM_SERIAL (modem), "+CGSN", 3, get_string_done, info); + mm_serial_port_queue_command_cached (priv->primary, "+CGSN", 3, get_string_done, info); } static void @@ -278,10 +394,11 @@ get_imsi (MMModemGsmCard *modem, MMModemStringFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); - mm_serial_queue_command_cached (MM_SERIAL (modem), "+CIMI", 3, get_string_done, info); + mm_serial_port_queue_command_cached (priv->primary, "+CIMI", 3, get_string_done, info); } static void @@ -297,7 +414,7 @@ gsm_card_info_invoke (MMCallbackInfo *info) } static void -get_version_done (MMSerial *serial, +get_version_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -313,7 +430,7 @@ get_version_done (MMSerial *serial, } static void -get_model_done (MMSerial *serial, +get_model_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -327,7 +444,7 @@ get_model_done (MMSerial *serial, } static void -get_manufacturer_done (MMSerial *serial, +get_manufacturer_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -345,21 +462,21 @@ get_card_info (MMModemGsmCard *modem, MMModemGsmCardInfoFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; - MMSerial *serial = MM_SERIAL (modem); info = mm_callback_info_new_full (MM_MODEM (modem), gsm_card_info_invoke, G_CALLBACK (callback), user_data); - mm_serial_queue_command_cached (serial, "+CGMI", 3, get_manufacturer_done, info); - mm_serial_queue_command_cached (serial, "+CGMM", 3, get_model_done, info); - mm_serial_queue_command_cached (serial, "+CGMR", 3, get_version_done, info); + mm_serial_port_queue_command_cached (priv->primary, "+CGMI", 3, get_manufacturer_done, info); + mm_serial_port_queue_command_cached (priv->primary, "+CGMM", 3, get_model_done, info); + mm_serial_port_queue_command_cached (priv->primary, "+CGMR", 3, get_version_done, info); } static void -send_puk_done (MMSerial *serial, +send_puk_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -378,17 +495,18 @@ send_puk (MMModemGsmCard *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; char *command; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); command = g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin); - mm_serial_queue_command (MM_SERIAL (modem), command, 3, send_puk_done, info); + mm_serial_port_queue_command (priv->primary, command, 3, send_puk_done, info); g_free (command); } static void -send_pin_done (MMSerial *serial, +send_pin_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -406,17 +524,18 @@ send_pin (MMModemGsmCard *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; char *command; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); command = g_strdup_printf ("+CPIN=\"%s\"", pin); - mm_serial_queue_command (MM_SERIAL (modem), command, 3, send_pin_done, info); + mm_serial_port_queue_command (priv->primary, command, 3, send_pin_done, info); g_free (command); } static void -enable_pin_done (MMSerial *serial, +enable_pin_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -435,17 +554,18 @@ enable_pin (MMModemGsmCard *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; char *command; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"", enabled ? 1 : 0, pin); - mm_serial_queue_command (MM_SERIAL (modem), command, 3, enable_pin_done, info); + mm_serial_port_queue_command (priv->primary, command, 3, enable_pin_done, info); g_free (command); } static void -change_pin_done (MMSerial *serial, +change_pin_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -464,12 +584,13 @@ change_pin (MMModemGsmCard *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; char *command; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin); - mm_serial_queue_command (MM_SERIAL (modem), command, 3, change_pin_done, info); + mm_serial_port_queue_command (priv->primary, command, 3, change_pin_done, info); g_free (command); } @@ -500,30 +621,48 @@ parse_operator (const char *reply) } static void -read_operator_done (MMSerial *serial, - GString *response, - GError *error, - gpointer user_data) +read_operator_code_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { - if (!error) { - char *oper; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data); + char *oper; - oper = parse_operator (response->str); - if (oper) { - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial); + if (error) + return; - if (GPOINTER_TO_INT (user_data) == 0) { - g_free (priv->oper_code); - priv->oper_code = oper; - } else { - g_free (priv->oper_name); - priv->oper_name = oper; + oper = parse_operator (response->str); + if (!oper) + return; - mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (serial), priv->reg_status, - priv->oper_code, priv->oper_name); - } - } - } + g_free (priv->oper_code); + priv->oper_code = oper; +} + +static void +read_operator_name_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data); + char *oper; + + if (error) + return; + + oper = parse_operator (response->str); + if (!oper) + return; + + g_free (priv->oper_name); + priv->oper_name = oper; + + mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (user_data), + priv->reg_status, + priv->oper_code, + priv->oper_name); } /* Registration */ @@ -618,24 +757,27 @@ reg_status_updated (MMGenericGsm *self, int new_value) } static void -reg_state_changed (MMSerial *serial, +reg_state_changed (MMSerialPort *port, GMatchInfo *match_info, gpointer user_data) { + MMGenericGsm *self = MM_GENERIC_GSM (user_data); char *str; str = g_match_info_fetch (match_info, 1); - reg_status_updated (MM_GENERIC_GSM (serial), atoi (str)); + reg_status_updated (self, atoi (str)); g_free (str); } static gboolean reg_status_again (gpointer data) { + MMGenericGsmPrivate *priv; MMCallbackInfo *info = (MMCallbackInfo *) data; - if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pending_registration) - get_registration_status (MM_SERIAL (info->modem), info); + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + if (priv->pending_registration) + get_registration_status (priv->primary, info); return FALSE; } @@ -647,13 +789,13 @@ reg_status_again_remove (gpointer data) } static void -get_reg_status_done (MMSerial *serial, +get_reg_status_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); if (error) { info->error = g_error_copy (error); @@ -666,7 +808,7 @@ get_reg_status_done (MMSerial *serial, int unsolicited, stat; if (sscanf (response->str + 7, "%d,%d", &unsolicited, &stat)) { - reg_status_updated (MM_GENERIC_GSM (serial), stat); + reg_status_updated (MM_GENERIC_GSM (info->modem), stat); if (!unsolicited && priv->pending_registration) { guint id; @@ -687,19 +829,19 @@ get_reg_status_done (MMSerial *serial, } static void -get_registration_status (MMSerial *serial, MMCallbackInfo *info) +get_registration_status (MMSerialPort *port, MMCallbackInfo *info) { - mm_serial_queue_command (serial, "+CREG?", 3, get_reg_status_done, info); + mm_serial_port_queue_command (port, "+CREG?", 3, get_reg_status_done, info); } static void -register_done (MMSerial *serial, +register_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) { /* Ignore errors here, get the actual registration status */ - get_registration_status (serial, (MMCallbackInfo *) user_data); + get_registration_status (port, (MMCallbackInfo *) user_data); } static void @@ -708,12 +850,13 @@ do_register (MMModemGsmNetwork *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; char *command; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); - MM_GENERIC_GSM_GET_PRIVATE (modem)->pending_registration = + priv->pending_registration = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 60, pending_registration_timed_out, info, @@ -724,7 +867,7 @@ do_register (MMModemGsmNetwork *modem, else command = g_strdup ("+COPS=0,,"); - mm_serial_queue_command (MM_SERIAL (modem), command, 5, register_done, info); + mm_serial_port_queue_command (priv->primary, command, 5, register_done, info); g_free (command); } @@ -758,7 +901,7 @@ get_registration_info (MMModemGsmNetwork *self, } static void -connect_report_done (MMSerial *serial, +connect_report_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -774,17 +917,19 @@ connect_report_done (MMSerial *serial, } static void -connect_done (MMSerial *serial, +connect_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) { + MMGenericGsmPrivate *priv; MMCallbackInfo *info = (MMCallbackInfo *) user_data; if (error) { info->error = g_error_copy (error); /* Try to get more information why it failed */ - mm_serial_queue_command (serial, "+CEER", 3, connect_report_done, info); + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + mm_serial_port_queue_command (priv->primary, "+CEER", 3, connect_report_done, info); } else /* Done */ mm_callback_info_schedule (info); @@ -796,6 +941,7 @@ connect (MMModem *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; char *command; guint32 cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem)); @@ -816,12 +962,12 @@ connect (MMModem *modem, } else command = g_strconcat ("DT", number, NULL); - mm_serial_queue_command (MM_SERIAL (modem), command, 60, connect_done, info); + mm_serial_port_queue_command (priv->primary, command, 60, connect_done, info); g_free (command); } static void -disconnect_flash_done (MMSerial *serial, gpointer user_data) +disconnect_flash_done (MMSerialPort *port, gpointer user_data) { mm_callback_info_schedule ((MMCallbackInfo *) user_data); } @@ -831,13 +977,14 @@ disconnect (MMModem *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; /* First, reset the previously used CID */ mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0); info = mm_callback_info_new (modem, callback, user_data); - mm_serial_flash (MM_SERIAL (modem), 1000, disconnect_flash_done, info); + mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info); } static void @@ -861,7 +1008,7 @@ destroy_scan_data (gpointer data) } static void -scan_done (MMSerial *serial, +scan_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -880,8 +1027,13 @@ scan_done (MMSerial *serial, reply += 7; - /* Pattern without crazy escaping using | for matching: (|\d|,"|.+|","|.+|","|.+|",|\d|) */ - r = g_regex_new ("\\((\\d),\"(.+)\",\"(.+)\",\"(.+)\",(\\d)\\)", G_REGEX_UNGREEDY, 0, &err); + /* Pattern without crazy escaping using | for matching: (|\d|,"|.+|","|.+|","|.+|"\)?,|\d|) */ + + /* Quirk: Sony-Ericsson TM-506 sometimes includes a stray ')' like so: + * + * +COPS: (2,"","T-Mobile","31026",0),(1,"AT&T","AT&T","310410"),0) + */ + r = g_regex_new ("\\((\\d),\"(.*)\",\"(.*)\",\"(.*)\"\\)?,(\\d)\\)", G_REGEX_UNGREEDY, 0, &err); if (err) { g_error ("Invalid regular expression: %s", err->message); g_error_free (err); @@ -922,6 +1074,7 @@ scan (MMModemGsmNetwork *modem, MMModemGsmNetworkScanFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; info = mm_callback_info_new_full (MM_MODEM (modem), @@ -929,13 +1082,13 @@ scan (MMModemGsmNetwork *modem, G_CALLBACK (callback), user_data); - mm_serial_queue_command (MM_SERIAL (modem), "+COPS=?", 60, scan_done, info); + mm_serial_port_queue_command (priv->primary, "+COPS=?", 60, scan_done, info); } /* SetApn */ static void -set_apn_done (MMSerial *serial, +set_apn_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -945,14 +1098,14 @@ set_apn_done (MMSerial *serial, if (error) info->error = g_error_copy (error); else - mm_generic_gsm_set_cid (MM_GENERIC_GSM (serial), + mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem), GPOINTER_TO_UINT (mm_callback_info_get_data (info, "cid"))); mm_callback_info_schedule (info); } static void -cid_range_read (MMSerial *serial, +cid_range_read (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -1012,13 +1165,13 @@ cid_range_read (MMSerial *serial, mm_callback_info_set_data (info, "cid", GUINT_TO_POINTER (cid), NULL); command = g_strdup_printf ("+CGDCONT=%d, \"IP\", \"%s\"", cid, apn); - mm_serial_queue_command (serial, command, 3, set_apn_done, info); + mm_serial_port_queue_command (port, command, 3, set_apn_done, info); g_free (command); } } static void -existing_apns_read (MMSerial *serial, +existing_apns_read (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -1051,7 +1204,7 @@ existing_apns_read (MMSerial *serial, apn = g_match_info_fetch (match_info, 3); if (!strcmp (apn, new_apn)) { - mm_generic_gsm_set_cid (MM_GENERIC_GSM (serial), (guint32) num_cid); + mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem), (guint32) num_cid); found = TRUE; } @@ -1083,7 +1236,7 @@ existing_apns_read (MMSerial *serial, mm_callback_info_schedule (info); else /* APN not configured on the card. Get the allowed CID range */ - mm_serial_queue_command_cached (serial, "+CGDCONT=?", 3, cid_range_read, info); + mm_serial_port_queue_command_cached (port, "+CGDCONT=?", 3, cid_range_read, info); } static void @@ -1092,23 +1245,25 @@ set_apn (MMModemGsmNetwork *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); mm_callback_info_set_data (info, "apn", g_strdup (apn), g_free); /* Start by searching if the APN is already in card */ - mm_serial_queue_command (MM_SERIAL (modem), "+CGDCONT?", 3, existing_apns_read, info); + mm_serial_port_queue_command (priv->primary, "+CGDCONT?", 3, existing_apns_read, info); } /* GetSignalQuality */ static void -get_signal_quality_done (MMSerial *serial, +get_signal_quality_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) { + MMGenericGsmPrivate *priv; MMCallbackInfo *info = (MMCallbackInfo *) user_data; char *reply = response->str; @@ -1127,7 +1282,8 @@ get_signal_quality_done (MMSerial *serial, /* Normalize the quality */ quality = quality * 100 / 31; - MM_GENERIC_GSM_GET_PRIVATE (serial)->signal_quality = quality; + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + priv->signal_quality = quality; mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); } else info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, @@ -1142,23 +1298,26 @@ get_signal_quality (MMModemGsmNetwork *modem, MMModemUIntFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; + gboolean connected; - if (mm_serial_is_connected (MM_SERIAL (modem))) { - g_message ("Returning saved signal quality %d", MM_GENERIC_GSM_GET_PRIVATE (modem)->signal_quality); - callback (MM_MODEM (modem), MM_GENERIC_GSM_GET_PRIVATE (modem)->signal_quality, NULL, user_data); + connected = mm_serial_port_is_connected (priv->primary); + if (connected && !priv->secondary) { + g_message ("Returning saved signal quality %d", priv->signal_quality); + callback (MM_MODEM (modem), priv->signal_quality, NULL, user_data); return; } info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - mm_serial_queue_command (MM_SERIAL (modem), "+CSQ", 3, get_signal_quality_done, info); + mm_serial_port_queue_command (connected ? priv->secondary : priv->primary, "+CSQ", 3, get_signal_quality_done, info); } /*****************************************************************************/ /* MMModemGsmSms interface */ static void -sms_send_done (MMSerial *serial, +sms_send_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) @@ -1181,16 +1340,33 @@ sms_send (MMModemGsmSms *modem, MMModemFn callback, gpointer user_data) { + MMGenericGsmPrivate *priv; MMCallbackInfo *info; char *command; + gboolean connected; + MMSerialPort *port = NULL; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + connected = mm_serial_port_is_connected (priv->primary); + if (connected) + port = priv->secondary; + else + port = priv->primary; + + if (!port) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Cannot send SMS while connected"); + mm_callback_info_schedule (info); + return; + } + /* FIXME: use the PDU mode instead */ - mm_serial_queue_command (MM_SERIAL (modem), "AT+CMGF=1", 3, NULL, NULL); + mm_serial_port_queue_command (port, "AT+CMGF=1", 3, NULL, NULL); command = g_strdup_printf ("+CMGS=\"%s\"\r%s\x1a", number, text); - mm_serial_queue_command (MM_SERIAL (modem), command, 10, sms_send_done, info); + mm_serial_port_queue_command (port, command, 10, sms_send_done, info); g_free (command); } @@ -1441,6 +1617,9 @@ simple_get_status (MMModemSimple *simple, static void modem_init (MMModem *modem_class) { + modem_class->owns_port = owns_port; + modem_class->grab_port = grab_port; + modem_class->release_port = release_port; modem_class->enable = enable; modem_class->connect = connect; modem_class->disconnect = disconnect; @@ -1484,16 +1663,6 @@ modem_simple_init (MMModemSimple *class) static void mm_generic_gsm_init (MMGenericGsm *self) { - GRegex *regex; - - mm_serial_set_response_parser (MM_SERIAL (self), - mm_serial_parser_v1_parse, - mm_serial_parser_v1_new (), - mm_serial_parser_v1_destroy); - - regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, reg_state_changed, NULL, NULL); - g_regex_unref (regex); } static void @@ -1507,16 +1676,20 @@ set_property (GObject *object, guint prop_id, /* Construct only */ priv->driver = g_value_dup_string (value); break; - case MM_MODEM_PROP_DEVICE: - g_free (priv->data_device); - priv->data_device = g_value_dup_string (value); + case MM_MODEM_PROP_PLUGIN: + /* Construct only */ + priv->plugin = g_value_dup_string (value); break; - case MM_MODEM_PROP_TYPE: - priv->modem_type = g_value_get_uint (value); + case MM_MODEM_PROP_MASTER_DEVICE: + /* Constrcut only */ + priv->device = g_value_dup_string (value); break; case MM_MODEM_PROP_IP_METHOD: priv->ip_method = g_value_get_uint (value); break; + case MM_MODEM_PROP_TYPE: + case MM_MODEM_PROP_VALID: + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1530,21 +1703,30 @@ get_property (GObject *object, guint prop_id, MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object); switch (prop_id) { - case MM_MODEM_PROP_DEVICE: - if (priv->data_device) - g_value_set_string (value, priv->data_device); + case MM_MODEM_PROP_DATA_DEVICE: + if (priv->primary) + g_value_set_string (value, mm_serial_port_get_device (priv->primary)); else - g_value_set_string (value, mm_serial_get_device (MM_SERIAL (object))); + g_value_set_string (value, NULL); + break; + case MM_MODEM_PROP_MASTER_DEVICE: + g_value_set_string (value, priv->device); break; case MM_MODEM_PROP_DRIVER: - g_value_set_string (value, MM_GENERIC_GSM_GET_PRIVATE (object)->driver); + g_value_set_string (value, priv->driver); + break; + case MM_MODEM_PROP_PLUGIN: + g_value_set_string (value, priv->plugin); break; case MM_MODEM_PROP_TYPE: - g_value_set_uint (value, priv->modem_type); + g_value_set_uint (value, MM_MODEM_TYPE_GSM); break; case MM_MODEM_PROP_IP_METHOD: g_value_set_uint (value, priv->ip_method); break; + case MM_MODEM_PROP_VALID: + g_value_set_boolean (value, priv->valid); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1581,14 +1763,22 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass) /* Properties */ g_object_class_override_property (object_class, - MM_MODEM_PROP_DEVICE, - MM_MODEM_DEVICE); + MM_MODEM_PROP_DATA_DEVICE, + MM_MODEM_DATA_DEVICE); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_MASTER_DEVICE, + MM_MODEM_MASTER_DEVICE); g_object_class_override_property (object_class, MM_MODEM_PROP_DRIVER, MM_MODEM_DRIVER); g_object_class_override_property (object_class, + MM_MODEM_PROP_PLUGIN, + MM_MODEM_PLUGIN); + + g_object_class_override_property (object_class, MM_MODEM_PROP_TYPE, MM_MODEM_TYPE); @@ -1596,6 +1786,9 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass) MM_MODEM_PROP_IP_METHOD, MM_MODEM_IP_METHOD); + g_object_class_override_property (object_class, + MM_MODEM_PROP_VALID, + MM_MODEM_VALID); } GType diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h index 3f857365..a98afefa 100644 --- a/src/mm-generic-gsm.h +++ b/src/mm-generic-gsm.h @@ -6,12 +6,12 @@ #include "mm-modem-gsm-network.h" #include "mm-serial.h" -#define MM_TYPE_GENERIC_GSM (mm_generic_gsm_get_type ()) -#define MM_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsm)) -#define MM_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_GSM, MMGenericGsmClass)) -#define MM_IS_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_GSM)) -#define MM_IS_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_GSM)) -#define MM_GENERIC_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsmClass)) +#define MM_TYPE_GENERIC_GSM (mm_generic_gsm_get_type ()) +#define MM_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsm)) +#define MM_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_GSM, MMGenericGsmClass)) +#define MM_IS_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_GSM)) +#define MM_IS_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_GSM)) +#define MM_GENERIC_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsmClass)) typedef struct { MMSerial parent; @@ -23,8 +23,9 @@ typedef struct { GType mm_generic_gsm_get_type (void); -MMModem *mm_generic_gsm_new (const char *serial_device, - const char *driver); +MMModem *mm_generic_gsm_new (const char *device, + const char *driver, + const char *plugin); void mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem, gboolean enabled); diff --git a/src/mm-manager.c b/src/mm-manager.c index a19c9446..beed08cc 100644 --- a/src/mm-manager.c +++ b/src/mm-manager.c @@ -2,12 +2,12 @@ #include <string.h> #include <gmodule.h> +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include "mm-manager.h" #include "mm-errors.h" -#include "mm-generic-gsm.h" -#include "mm-generic-cdma.h" #include "mm-plugin.h" static gboolean impl_manager_enumerate_devices (MMManager *manager, @@ -29,9 +29,11 @@ static guint signals[LAST_SIGNAL] = { 0 }; #define MM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MANAGER, MMManagerPrivate)) +#define DBUS_PATH_TAG "dbus-path" + typedef struct { DBusGConnection *connection; - LibHalContext *hal_ctx; + GUdevClient *udev; GSList *plugins; GHashTable *modems; } MMManagerPrivate; @@ -147,169 +149,78 @@ mm_manager_new (DBusGConnection *bus) return manager; } -static char * -get_driver_name (LibHalContext *ctx, const char *udi) -{ - char *parent_udi; - char *driver = NULL; - - parent_udi = libhal_device_get_property_string (ctx, udi, "info.parent", NULL); - if (parent_udi) { - driver = libhal_device_get_property_string (ctx, parent_udi, "info.linux.driver", NULL); - libhal_free_string (parent_udi); - } - - return driver; -} - -static MMModem * -create_generic_modem (MMManager *manager, const char *udi) -{ - MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - MMModem *modem; - char **capabilities; - char **iter; - char *serial_device; - char *driver; - gboolean type_gsm = FALSE; - gboolean type_cdma = FALSE; - - capabilities = libhal_device_get_property_strlist (priv->hal_ctx, udi, "modem.command_sets", NULL); - for (iter = capabilities; iter && *iter; iter++) { - if (!strcmp (*iter, "GSM-07.07")) { - type_gsm = TRUE; - break; - } - if (!strcmp (*iter, "IS-707-A")) { - type_cdma = TRUE; - break; - } - } - g_strfreev (capabilities); - - if (!type_gsm && !type_cdma) - return NULL; - - serial_device = libhal_device_get_property_string (priv->hal_ctx, udi, "serial.device", NULL); - g_return_val_if_fail (serial_device != NULL, NULL); - - driver = get_driver_name (priv->hal_ctx, udi); - g_return_val_if_fail (driver != NULL, NULL); - - if (type_gsm) - modem = mm_generic_gsm_new (serial_device, driver); - else - modem = mm_generic_cdma_new (serial_device, driver); - - g_free (serial_device); - g_free (driver); - - if (modem) - g_debug ("Created new generic modem (%s)", udi); - else - g_warning ("Failed to create generic modem (%s)", udi); - - return modem; -} - static void -add_modem (MMManager *manager, const char *udi, MMModem *modem) +remove_modem (MMManager *manager, MMModem *modem) { MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + char *device; - g_hash_table_insert (priv->modems, g_strdup (udi), modem); - dbus_g_connection_register_g_object (priv->connection, udi, G_OBJECT (modem)); + device = mm_modem_get_device (modem); + g_assert (device); + g_debug ("Removed modem %s", device); - g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem); + g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem); + g_hash_table_remove (priv->modems, device); + g_free (device); } -static MMModem * -modem_exists (MMManager *manager, const char *udi) +static void +modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data) { + MMManager *manager = MM_MANAGER (user_data); MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + static guint32 id = 0; + char *path, *device; - return (MMModem *) g_hash_table_lookup (priv->modems, udi); -} + if (mm_modem_get_valid (modem)) { + path = g_strdup_printf (MM_DBUS_PATH"/Modems/%d", id++); + dbus_g_connection_register_g_object (priv->connection, path, G_OBJECT (modem)); + g_object_set_data_full (G_OBJECT (modem), DBUS_PATH_TAG, path, (GDestroyNotify) g_free); -static void -create_initial_modems_from_plugins (MMManager *manager) -{ - MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - GSList *iter; + device = mm_modem_get_device (modem); + g_assert (device); + g_debug ("Exported modem %s", device); + g_free (device); - for (iter = priv->plugins; iter; iter = iter->next) { - MMPlugin *plugin = MM_PLUGIN (iter->data); - char **udis; - int i; - - udis = mm_plugin_list_supported_udis (plugin, priv->hal_ctx); - if (udis) { - for (i = 0; udis[i]; i++) { - char *udi = udis[i]; - MMModem *modem; - - if (modem_exists (manager, udi)) { - g_warning ("Modem for UDI %s already exists, ignoring", udi); - continue; - } - - modem = mm_plugin_create_modem (plugin, priv->hal_ctx, udi); - if (modem) - add_modem (manager, udi, modem); - } - - g_strfreev (udis); - } - } + g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem); + } else + remove_modem (manager, modem); } static void -create_initial_modems_generic (MMManager *manager) +add_modem (MMManager *manager, MMModem *modem) { MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - char **devices; - int num_devices; - int i; - DBusError err; - - dbus_error_init (&err); - devices = libhal_find_device_by_capability (priv->hal_ctx, "modem", &num_devices, &err); - if (dbus_error_is_set (&err)) { - g_warning ("Could not list HAL devices: %s", err.message); - dbus_error_free (&err); - } - - if (devices) { - for (i = 0; i < num_devices; i++) { - char *udi = devices[i]; - MMModem *modem; - - if (modem_exists (manager, udi)) - /* Already exists, most likely handled by a plugin */ - continue; - - modem = create_generic_modem (manager, udi); - if (modem) - add_modem (manager, g_strdup (udi), modem); - } + char *device; + gboolean valid = FALSE; + + device = mm_modem_get_device (modem); + g_assert (device); + if (!g_hash_table_lookup (priv->modems, device)) { + g_hash_table_insert (priv->modems, g_strdup (device), modem); + g_debug ("Added modem %s", device); + g_signal_connect (modem, "notify::valid", G_CALLBACK (modem_valid), manager); + g_object_get (modem, MM_MODEM_VALID, &valid, NULL); + if (valid) + modem_valid (modem, NULL, manager); } - - g_strfreev (devices); -} - -static void -create_initial_modems (MMManager *manager) -{ - create_initial_modems_from_plugins (manager); - create_initial_modems_generic (manager); + g_free (device); } static void enumerate_devices_cb (gpointer key, gpointer val, gpointer user_data) { + MMModem *modem = MM_MODEM (val); GPtrArray **devices = (GPtrArray **) user_data; - - g_ptr_array_add (*devices, g_strdup ((char *) key)); + const char *path; + gboolean valid = FALSE; + + g_object_get (G_OBJECT (modem), MM_MODEM_VALID, &valid, NULL); + if (valid) { + path = g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG); + g_return_if_fail (path != NULL); + g_ptr_array_add (*devices, g_strdup (path)); + } } static gboolean @@ -325,118 +236,164 @@ impl_manager_enumerate_devices (MMManager *manager, return TRUE; } +typedef struct { + MMModem *modem; + const char *subsys; + const char *name; +} FindPortInfo; + +static void +find_port (gpointer key, gpointer data, gpointer user_data) +{ + FindPortInfo *info = user_data; + MMModem *modem = MM_MODEM (data); + + if (!info->modem && mm_modem_owns_port (modem, info->subsys, info->name)) + info->modem = modem; +} + +static MMModem * +find_modem_for_port (MMManager *manager, const char *subsys, const char *name) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + FindPortInfo info = { NULL, subsys, name }; + + g_hash_table_foreach (priv->modems, find_port, &info); + return info.modem; +} + static void -device_added (LibHalContext *ctx, const char *udi) +device_added (MMManager *manager, GUdevDevice *device) { - MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx)); MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); GSList *iter; MMModem *modem = NULL; + const char *subsys, *name; + MMPlugin *best_plugin = NULL; + guint32 best_level = 0; + GError *error = NULL; - if (modem_exists (manager, udi)) - /* Shouldn't happen */ + g_return_if_fail (device != NULL); + + subsys = g_udev_device_get_subsystem (device); + name = g_udev_device_get_name (device); + + if (find_modem_for_port (manager, subsys, name)) return; - for (iter = priv->plugins; iter && modem == NULL; iter = iter->next) { + /* Build up the list of plugins that support this port */ + for (iter = priv->plugins; iter; iter = iter->next) { MMPlugin *plugin = MM_PLUGIN (iter->data); + guint32 level; - if (mm_plugin_supports_udi (plugin, ctx, udi)) { - modem = mm_plugin_create_modem (plugin, ctx, udi); - if (modem) - break; + level = mm_plugin_supports_port (plugin, subsys, name); + if (level > best_level) { + best_plugin = plugin; + best_level = level; } } - if (!modem) - /* None of the plugins supported the udi, try generic devices */ - modem = create_generic_modem (manager, udi); + /* Let the best plugin handle this port */ + if (!best_plugin) + return; - if (modem) - add_modem (manager, udi, modem); + modem = mm_plugin_grab_port (best_plugin, subsys, name, &error); + if (modem) { + guint32 modem_type = MM_MODEM_TYPE_UNKNOWN; + const char *type_name = "UNKNOWN"; + + g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL); + if (modem_type == MM_MODEM_TYPE_GSM) + type_name = "GSM"; + else if (modem_type == MM_MODEM_TYPE_CDMA) + type_name = "CDMA"; + + g_message ("(%s): %s modem %s claimed port %s", + mm_plugin_get_name (best_plugin), + type_name, + mm_modem_get_device (modem), + name); + } else { + g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s", + __func__, mm_plugin_get_name (best_plugin), subsys, name, + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); + return; + } + + add_modem (manager, modem); } static void -device_removed (LibHalContext *ctx, const char *udi) +device_removed (MMManager *manager, GUdevDevice *device) { - MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx)); MMModem *modem; + const char *subsys, *name; - modem = modem_exists (manager, udi); - if (modem) { - g_debug ("Removed modem %s", udi); - g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem); - g_hash_table_remove (MM_MANAGER_GET_PRIVATE (manager)->modems, udi); - } + g_return_if_fail (device != NULL); + + subsys = g_udev_device_get_subsystem (device); + name = g_udev_device_get_name (device); + modem = find_modem_for_port (manager, subsys, name); + if (modem) + mm_modem_release_port (modem, subsys, name); } static void -device_new_capability (LibHalContext *ctx, const char *udi, const char *capability) +handle_uevent (GUdevClient *client, + const char *action, + GUdevDevice *device, + gpointer user_data) { - device_added (ctx, udi); -} + MMManager *self = MM_MANAGER (user_data); + const char *subsys; + g_return_if_fail (action != NULL); -DBusGConnection * -mm_manager_get_bus (MMManager *manager) -{ - g_return_val_if_fail (MM_IS_MANAGER (manager), NULL); - - return MM_MANAGER_GET_PRIVATE (manager)->connection; -} + /* A bit paranoid */ + subsys = g_udev_device_get_subsystem (device); + g_return_if_fail (subsys != NULL); -static gboolean -remove_one (gpointer key, - gpointer value, - gpointer user_data) -{ - const char *udi = (char *) key; - MMModem *modem = MM_MODEM (value); - MMManager *manager = MM_MANAGER (user_data); + g_return_if_fail (!strcmp (subsys, "tty") || !strcmp (subsys, "net")); - g_debug ("Removed modem %s", udi); - g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem); - - return TRUE; + if (!strcmp (action, "add")) + device_added (self, device); + else if (!strcmp (action, "remove")) + device_removed (self, device); } void -mm_manager_set_hal_ctx (MMManager *manager, - LibHalContext *hal_ctx) +mm_manager_start (MMManager *manager) { MMManagerPrivate *priv; + GList *devices, *iter; + g_return_if_fail (manager != NULL); g_return_if_fail (MM_IS_MANAGER (manager)); priv = MM_MANAGER_GET_PRIVATE (manager); - priv->hal_ctx = hal_ctx; - if (hal_ctx) { - libhal_ctx_set_user_data (hal_ctx, manager); - libhal_ctx_set_device_added (hal_ctx, device_added); - libhal_ctx_set_device_removed (hal_ctx, device_removed); - libhal_ctx_set_device_new_capability (hal_ctx, device_new_capability); + devices = g_udev_client_query_by_subsystem (priv->udev, "tty"); + for (iter = devices; iter; iter = g_list_next (iter)) + device_added (manager, G_UDEV_DEVICE (iter->data)); - create_initial_modems (manager); - } else { - g_hash_table_foreach_remove (priv->modems, remove_one, manager); - } -} - -LibHalContext * -mm_manager_get_hal_ctx (MMManager *manager) -{ - g_return_val_if_fail (MM_IS_MANAGER (manager), NULL); - - return MM_MANAGER_GET_PRIVATE (manager)->hal_ctx; + devices = g_udev_client_query_by_subsystem (priv->udev, "net"); + for (iter = devices; iter; iter = g_list_next (iter)) + device_added (manager, G_UDEV_DEVICE (iter->data)); } static void mm_manager_init (MMManager *manager) { MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + const char *subsys[3] = { "tty", "net", NULL }; priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); load_plugins (manager); + + priv->udev = g_udev_client_new (subsys); + g_assert (priv->udev); + g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), manager); } static void @@ -449,11 +406,8 @@ finalize (GObject *object) g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL); g_slist_free (priv->plugins); - if (priv->hal_ctx) { - mm_manager_set_hal_ctx (MM_MANAGER (object), NULL); - libhal_ctx_shutdown (priv->hal_ctx, NULL); - libhal_ctx_free (priv->hal_ctx); - } + if (priv->udev) + g_object_unref (priv->udev); if (priv->connection) dbus_g_connection_unref (priv->connection); diff --git a/src/mm-manager.h b/src/mm-manager.h index 4426d0f1..0e7564ff 100644 --- a/src/mm-manager.h +++ b/src/mm-manager.h @@ -6,7 +6,6 @@ #include <glib/gtypes.h> #include <glib-object.h> #include <dbus/dbus-glib.h> -#include <libhal.h> #include "mm-modem.h" #define MM_TYPE_MANAGER (mm_manager_get_type ()) @@ -34,10 +33,7 @@ typedef struct { GType mm_manager_get_type (void); MMManager *mm_manager_new (DBusGConnection *bus); -DBusGConnection *mm_manager_get_bus (MMManager *manager); -void mm_manager_set_hal_ctx (MMManager *manager, - LibHalContext *hal_ctx); -LibHalContext *mm_manager_get_hal_ctx (MMManager *manager); +void mm_manager_start (MMManager *manager); #endif /* MM_MANAGER_H */ diff --git a/src/mm-modem.c b/src/mm-modem.c index 7bf913ec..be87b79f 100644 --- a/src/mm-modem.c +++ b/src/mm-modem.c @@ -199,6 +199,75 @@ impl_modem_disconnect (MMModem *modem, /*****************************************************************************/ +gboolean +mm_modem_owns_port (MMModem *self, + const char *subsys, + const char *name) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_MODEM (self), FALSE); + g_return_val_if_fail (subsys, FALSE); + g_return_val_if_fail (name, FALSE); + + g_assert (MM_MODEM_GET_INTERFACE (self)->owns_port); + return MM_MODEM_GET_INTERFACE (self)->owns_port (self, subsys, name); +} + +gboolean +mm_modem_grab_port (MMModem *self, + const char *subsys, + const char *name, + GError **error) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_MODEM (self), FALSE); + g_return_val_if_fail (subsys, FALSE); + g_return_val_if_fail (name, FALSE); + + g_assert (MM_MODEM_GET_INTERFACE (self)->grab_port); + return MM_MODEM_GET_INTERFACE (self)->grab_port (self, subsys, name, error); +} + +void +mm_modem_release_port (MMModem *self, + const char *subsys, + const char *name) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_MODEM (self)); + g_return_if_fail (subsys); + g_return_if_fail (name); + + g_assert (MM_MODEM_GET_INTERFACE (self)->release_port); + MM_MODEM_GET_INTERFACE (self)->release_port (self, subsys, name); +} + +gboolean +mm_modem_get_valid (MMModem *self) +{ + gboolean valid = FALSE; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (MM_IS_MODEM (self), FALSE); + + g_object_get (G_OBJECT (self), MM_MODEM_VALID, &valid, NULL); + return valid; +} + +char * +mm_modem_get_device (MMModem *self) +{ + char *device; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (MM_IS_MODEM (self), NULL); + + g_object_get (G_OBJECT (self), MM_MODEM_MASTER_DEVICE, &device, NULL); + return device; +} + +/*****************************************************************************/ + static void mm_modem_init (gpointer g_iface) { @@ -210,9 +279,17 @@ mm_modem_init (gpointer g_iface) /* Properties */ g_object_interface_install_property (g_iface, - g_param_spec_string (MM_MODEM_DEVICE, - "Device", + g_param_spec_string (MM_MODEM_DATA_DEVICE, "Device", + "Data device", + NULL, + G_PARAM_READWRITE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_MASTER_DEVICE, + "MasterDevice", + "Master modem parent device of all the modem's ports", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); @@ -226,6 +303,14 @@ mm_modem_init (gpointer g_iface) g_object_interface_install_property (g_iface, + g_param_spec_string (MM_MODEM_PLUGIN, + "Plugin", + "Plugin name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, g_param_spec_uint (MM_MODEM_TYPE, "Type", "Type", @@ -242,6 +327,14 @@ mm_modem_init (gpointer g_iface) MM_MODEM_IP_METHOD_PPP, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_interface_install_property + (g_iface, + g_param_spec_boolean (MM_MODEM_VALID, + "Valid", + "Modem is valid", + FALSE, + G_PARAM_READABLE)); + initialized = TRUE; } diff --git a/src/mm-modem.h b/src/mm-modem.h index 914b86a3..19ec284d 100644 --- a/src/mm-modem.h +++ b/src/mm-modem.h @@ -10,13 +10,17 @@ #define MM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM)) #define MM_MODEM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM, MMModem)) -#define MM_MODEM_DEVICE "device" -#define MM_MODEM_DRIVER "driver" -#define MM_MODEM_TYPE "type" -#define MM_MODEM_IP_METHOD "ip-method" - -#define MM_MODEM_TYPE_GSM 1 -#define MM_MODEM_TYPE_CDMA 2 +#define MM_MODEM_DATA_DEVICE "device" +#define MM_MODEM_MASTER_DEVICE "master-device" +#define MM_MODEM_DRIVER "driver" +#define MM_MODEM_TYPE "type" +#define MM_MODEM_IP_METHOD "ip-method" +#define MM_MODEM_VALID "valid" /* not exported */ +#define MM_MODEM_PLUGIN "plugin" /* not exported */ + +#define MM_MODEM_TYPE_UNKNOWN 0 +#define MM_MODEM_TYPE_GSM 1 +#define MM_MODEM_TYPE_CDMA 2 #define MM_MODEM_IP_METHOD_PPP 0 #define MM_MODEM_IP_METHOD_STATIC 1 @@ -25,10 +29,13 @@ typedef enum { MM_MODEM_PROP_FIRST = 0x1000, - MM_MODEM_PROP_DEVICE = MM_MODEM_PROP_FIRST, + MM_MODEM_PROP_DATA_DEVICE = MM_MODEM_PROP_FIRST, + MM_MODEM_PROP_MASTER_DEVICE, MM_MODEM_PROP_DRIVER, MM_MODEM_PROP_TYPE, MM_MODEM_PROP_IP_METHOD, + MM_MODEM_PROP_VALID, /* Not exported */ + MM_MODEM_PROP_PLUGIN, /* Not exported */ } MMModemProp; typedef struct _MMModem MMModem; @@ -57,6 +64,19 @@ struct _MMModem { GTypeInterface g_iface; /* Methods */ + gboolean (*owns_port) (MMModem *self, + const char *subsys, + const char *name); + + gboolean (*grab_port) (MMModem *self, + const char *subsys, + const char *name, + GError **error); + + void (*release_port) (MMModem *self, + const char *subsys, + const char *name); + void (*enable) (MMModem *self, gboolean enable, MMModemFn callback, @@ -78,6 +98,19 @@ struct _MMModem { GType mm_modem_get_type (void); +gboolean mm_modem_owns_port (MMModem *self, + const char *subsys, + const char *name); + +gboolean mm_modem_grab_port (MMModem *self, + const char *subsys, + const char *name, + GError **error); + +void mm_modem_release_port (MMModem *self, + const char *subsys, + const char *name); + void mm_modem_enable (MMModem *self, gboolean enable, MMModemFn callback, @@ -96,4 +129,9 @@ void mm_modem_disconnect (MMModem *self, MMModemFn callback, gpointer user_data); +gboolean mm_modem_get_valid (MMModem *self); + +char *mm_modem_get_device (MMModem *self); + #endif /* MM_MODEM_H */ + diff --git a/src/mm-plugin.c b/src/mm-plugin.c index e1fd21c1..c3425538 100644 --- a/src/mm-plugin.c +++ b/src/mm-plugin.c @@ -10,49 +10,31 @@ mm_plugin_get_name (MMPlugin *plugin) return MM_PLUGIN_GET_INTERFACE (plugin)->get_name (plugin); } -char ** -mm_plugin_list_supported_udis (MMPlugin *plugin, - LibHalContext *hal_ctx) -{ - g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL); - g_return_val_if_fail (hal_ctx != NULL, NULL); - - return MM_PLUGIN_GET_INTERFACE (plugin)->list_supported_udis (plugin, hal_ctx); -} - -gboolean -mm_plugin_supports_udi (MMPlugin *plugin, - LibHalContext *hal_ctx, - const char *udi) +guint32 +mm_plugin_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name) { g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE); - g_return_val_if_fail (hal_ctx != NULL, FALSE); - g_return_val_if_fail (udi != NULL, FALSE); + g_return_val_if_fail (subsys != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); - return MM_PLUGIN_GET_INTERFACE (plugin)->supports_udi (plugin, hal_ctx, udi); + return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin, subsys, name); } MMModem * -mm_plugin_create_modem (MMPlugin *plugin, - LibHalContext *hal_ctx, - const char *udi) +mm_plugin_grab_port (MMPlugin *plugin, + const char *subsys, + const char *name, + GError **error) { - MMModem *modem; - - g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL); - g_return_val_if_fail (hal_ctx != NULL, NULL); - g_return_val_if_fail (udi != NULL, NULL); - - modem = MM_PLUGIN_GET_INTERFACE (plugin)->create_modem (plugin, hal_ctx, udi); - if (modem) - g_debug ("Created new %s modem (%s)", mm_plugin_get_name (plugin), udi); - else - g_warning ("Failed to create %s modem (%s)", mm_plugin_get_name (plugin), udi); + g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE); + g_return_val_if_fail (subsys != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); - return modem; + return MM_PLUGIN_GET_INTERFACE (plugin)->grab_port (plugin, subsys, name, error); } - /*****************************************************************************/ static void diff --git a/src/mm-plugin.h b/src/mm-plugin.h index 0f9a600b..07f7f60c 100644 --- a/src/mm-plugin.h +++ b/src/mm-plugin.h @@ -4,10 +4,9 @@ #define MM_PLUGIN_H #include <glib-object.h> -#include <libhal.h> #include <mm-modem.h> -#define MM_PLUGIN_MAJOR_VERSION 1 +#define MM_PLUGIN_MAJOR_VERSION 2 #define MM_PLUGIN_MINOR_VERSION 0 #define MM_TYPE_PLUGIN (mm_plugin_get_type ()) @@ -23,33 +22,44 @@ struct _MMPlugin { GTypeInterface g_iface; /* Methods */ - const char *(*get_name) (MMPlugin *self); - - char **(*list_supported_udis) (MMPlugin *self, - LibHalContext *hal_ctx); - - gboolean (*supports_udi) (MMPlugin *self, - LibHalContext *hal_ctx, - const char *udi); - - MMModem *(*create_modem) (MMPlugin *self, - LibHalContext *hal_ctx, - const char *udi); + const char *(*get_name) (MMPlugin *self); + + /* Check whether a plugin supports a particular modem port, and what level + * of support the plugin has for the device. The plugin should return a + * value between 0 and 100 inclusive, where 0 means the plugin has no + * support for the device, and 100 means the plugin has full support for the + * device. + */ + guint32 (*supports_port) (MMPlugin *self, + const char *subsys, + const char *name); + + /* Will only be called if the plugin returns a value greater than 0 for + * the supports_port() method for this port. The plugin should create and + * return a new modem for the port's device if there is no existing modem + * to handle the port's hardware device, or should add the port to an + * existing modem and return that modem object. If an error is encountered + * while claiming the port, the error information should be returned in the + * error argument, and the plugin should return NULL. + */ + MMModem * (*grab_port) (MMPlugin *self, + const char *subsys, + const char *name, + GError **error); }; GType mm_plugin_get_type (void); -const char *mm_plugin_get_name (MMPlugin *plugin); +const char *mm_plugin_get_name (MMPlugin *plugin); -char **mm_plugin_list_supported_udis (MMPlugin *plugin, - LibHalContext *hal_ctx); +guint32 mm_plugin_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name); -gboolean mm_plugin_supports_udi (MMPlugin *plugin, - LibHalContext *hal_ctx, - const char *udi); - -MMModem *mm_plugin_create_modem (MMPlugin *plugin, - LibHalContext *hal_ctx, - const char *udi); +MMModem *mm_plugin_grab_port (MMPlugin *plugin, + const char *subsys, + const char *name, + GError **error); #endif /* MM_PLUGIN_H */ + diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c new file mode 100644 index 00000000..e825d597 --- /dev/null +++ b/src/mm-serial-port.c @@ -0,0 +1,1107 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#define _GNU_SOURCE /* for strcasestr() */ + +#include <stdio.h> +#include <stdlib.h> +#include <termio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <string.h> + +#include "mm-serial-port.h" +#include "mm-errors.h" +#include "mm-options.h" + +static gboolean mm_serial_port_queue_process (gpointer data); + +G_DEFINE_TYPE (MMSerialPort, mm_serial_port, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_DEVICE, + PROP_BAUD, + PROP_BITS, + PROP_PARITY, + PROP_STOPBITS, + PROP_SEND_DELAY, + PROP_CARRIER_DETECT, + + LAST_PROP +}; + +#define SERIAL_BUF_SIZE 2048 + +#define MM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL_PORT, MMSerialPortPrivate)) + +typedef struct { + int fd; + GHashTable *reply_cache; + GIOChannel *channel; + GQueue *queue; + GString *command; + GString *response; + + /* Response parser data */ + MMSerialResponseParserFn response_parser_fn; + gpointer response_parser_user_data; + GDestroyNotify response_parser_notify; + GSList *unsolicited_msg_handlers; + + struct termios old_t; + + char *device; + guint baud; + guint bits; + char parity; + guint stopbits; + guint64 send_delay; + gboolean carrier_detect; + + guint queue_schedule; + guint watch_id; + guint timeout_id; +} MMSerialPortPrivate; + +typedef struct { + GRegex *regex; + MMSerialUnsolicitedMsgFn callback; + gpointer user_data; + GDestroyNotify notify; +} MMUnsolicitedMsgHandler; + +const char * +mm_serial_port_get_device (MMSerialPort *self) +{ + g_return_val_if_fail (MM_IS_SERIAL_PORT (self), NULL); + + return MM_SERIAL_PORT_GET_PRIVATE (self)->device; +} + +static void +mm_serial_port_set_cached_reply (MMSerialPort *self, + const char *command, + const char *reply) +{ + if (reply) + g_hash_table_insert (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, + g_strdup (command), + g_strdup (reply)); + else + g_hash_table_remove (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); +} + +static const char * +mm_serial_port_get_cached_reply (MMSerialPort *self, + const char *command) +{ + return (char *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command); +} + +static int +parse_baudrate (guint i) +{ + int speed; + + switch (i) { + case 0: + speed = B0; + break; + case 50: + speed = B50; + break; + case 75: + speed = B75; + break; + case 110: + speed = B110; + break; + case 150: + speed = B150; + break; + case 300: + speed = B300; + break; + case 600: + speed = B600; + break; + case 1200: + speed = B1200; + break; + case 2400: + speed = B2400; + break; + case 4800: + speed = B4800; + break; + case 9600: + speed = B9600; + break; + case 19200: + speed = B19200; + break; + case 38400: + speed = B38400; + break; + case 57600: + speed = B57600; + break; + case 115200: + speed = B115200; + break; + case 460800: + speed = B460800; + break; + default: + g_warning ("Invalid baudrate '%d'", i); + speed = B9600; + } + + return speed; +} + +static int +parse_bits (guint i) +{ + int bits; + + switch (i) { + case 5: + bits = CS5; + break; + case 6: + bits = CS6; + break; + case 7: + bits = CS7; + break; + case 8: + bits = CS8; + break; + default: + g_warning ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i); + bits = CS8; + } + + return bits; +} + +static int +parse_parity (char c) +{ + int parity; + + switch (c) { + case 'n': + case 'N': + parity = 0; + break; + case 'e': + case 'E': + parity = PARENB; + break; + case 'o': + case 'O': + parity = PARENB | PARODD; + break; + default: + g_warning ("Invalid parity (%c). Valid values are n, e, o", c); + parity = 0; + } + + return parity; +} + +static int +parse_stopbits (guint i) +{ + int stopbits; + + switch (i) { + case 1: + stopbits = 0; + break; + case 2: + stopbits = CSTOPB; + break; + default: + g_warning ("Invalid stop bits (%d). Valid values are 1 and 2)", i); + stopbits = 0; + } + + return stopbits; +} + +static gboolean +config_fd (MMSerialPort *self) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + struct termio stbuf; + int speed; + int bits; + int parity; + int stopbits; + + speed = parse_baudrate (priv->baud); + bits = parse_bits (priv->bits); + parity = parse_parity (priv->parity); + stopbits = parse_stopbits (priv->stopbits); + + ioctl (priv->fd, TCGETA, &stbuf); + + stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR ); + stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET); + stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL); + stbuf.c_lflag &= ~(ECHO | ECHOE); + stbuf.c_cc[VMIN] = 1; + stbuf.c_cc[VTIME] = 0; + stbuf.c_cc[VEOF] = 1; + + stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB); + stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits); + + if (ioctl (priv->fd, TCSETA, &stbuf) < 0) { + g_warning ("(%s) cannot control device (errno %d)", priv->device, errno); + return FALSE; + } + + return TRUE; +} + +static void +serial_debug (const char *prefix, const char *buf, int len) +{ + static GString *debug = NULL; + const char *s; + + if (!mm_options_debug ()) + return; + + if (len < 0) + len = strlen (buf); + + if (!debug) + debug = g_string_sized_new (256); + + g_string_append (debug, prefix); + g_string_append (debug, " '"); + + s = buf; + while (len--) { + if (g_ascii_isprint (*s)) + g_string_append_c (debug, *s); + else if (*s == '\r') + g_string_append (debug, "<CR>"); + else if (*s == '\n') + g_string_append (debug, "<LF>"); + else + g_string_append_printf (debug, "\\%d", *s); + + s++; + } + + g_string_append_c (debug, '\''); + g_debug ("%s", debug->str); + g_string_truncate (debug, 0); +} + +static gboolean +mm_serial_port_send_command (MMSerialPort *self, + const char *command, + GError **error) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + const char *s; + int status; + int eagain_count = 1000; + + if (priv->fd < 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + "%s", "Sending command failed: device is not enabled"); + return FALSE; + } + + if (mm_serial_port_is_connected (self)) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + "%s", "Sending command failed: device is connected"); + return FALSE; + } + + g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2); + g_string_append (priv->command, command); + + if (command[strlen (command)] != '\r') + g_string_append_c (priv->command, '\r'); + + serial_debug ("-->", priv->command->str, -1); + + s = priv->command->str; + while (*s) { + status = write (priv->fd, s, 1); + if (status < 0) { + if (errno == EAGAIN) { + eagain_count--; + if (eagain_count <= 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + "Sending command failed: '%s'", strerror (errno)); + break; + } + } else { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, + "Sending command failed: '%s'", strerror (errno)); + break; + } + } else + s++; + + if (priv->send_delay) + usleep (priv->send_delay); + } + + return *s == '\0'; +} + +typedef struct { + char *command; + MMSerialResponseFn callback; + gpointer user_data; + guint32 timeout; + gboolean cached; +} MMQueueData; + +static void +mm_serial_port_schedule_queue_process (MMSerialPort *self) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + GSource *source; + + if (priv->queue_schedule) + /* Already scheduled */ + return; + + source = g_idle_source_new (); + g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_queue_process), G_OBJECT (self))); + g_source_attach (source, NULL); + priv->queue_schedule = g_source_get_id (source); + g_source_unref (source); +} + +static void +mm_serial_port_got_response (MMSerialPort *self, GError *error) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + MMQueueData *info; + + if (priv->timeout_id) + g_source_remove (priv->timeout_id); + + info = (MMQueueData *) g_queue_pop_head (priv->queue); + if (info) { + if (info->cached && !error) + mm_serial_port_set_cached_reply (self, info->command, priv->response->str); + + if (info->callback) + info->callback (self, priv->response, error, info->user_data); + + g_free (info->command); + g_slice_free (MMQueueData, info); + } + + if (error) + g_error_free (error); + + g_string_truncate (priv->response, 0); + if (!g_queue_is_empty (priv->queue)) + mm_serial_port_schedule_queue_process (self); +} + +static gboolean +mm_serial_port_timed_out (gpointer data) +{ + MMSerialPort *self = MM_SERIAL_PORT (data); + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + GError *error; + + priv->timeout_id = 0; + + error = g_error_new_literal (MM_SERIAL_ERROR, + MM_SERIAL_RESPONSE_TIMEOUT, + "Serial command timed out"); + /* FIXME: This is not completely correct - if the response finally arrives and there's + some other command waiting for response right now, the other command will + get the output of the timed out command. Not sure what to do here. */ + mm_serial_port_got_response (self, error); + + return FALSE; +} + +static gboolean +mm_serial_port_queue_process (gpointer data) +{ + MMSerialPort *self = MM_SERIAL_PORT (data); + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + MMQueueData *info; + GError *error = NULL; + + priv->queue_schedule = 0; + + info = (MMQueueData *) g_queue_peek_head (priv->queue); + if (!info) + return FALSE; + + if (info->cached) { + const char *cached = mm_serial_port_get_cached_reply (self, info->command); + + if (cached) { + g_string_append (priv->response, cached); + mm_serial_port_got_response (self, NULL); + return FALSE; + } + } + + if (mm_serial_port_send_command (self, info->command, &error)) { + GSource *source; + + source = g_timeout_source_new (info->timeout); + g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_timed_out), G_OBJECT (self))); + g_source_attach (source, NULL); + priv->timeout_id = g_source_get_id (source); + g_source_unref (source); + } else { + mm_serial_port_got_response (self, error); + } + + return FALSE; +} + +void +mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self, + GRegex *regex, + MMSerialUnsolicitedMsgFn callback, + gpointer user_data, + GDestroyNotify notify) +{ + MMUnsolicitedMsgHandler *handler; + MMSerialPortPrivate *priv; + + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + g_return_if_fail (regex != NULL); + + handler = g_slice_new (MMUnsolicitedMsgHandler); + handler->regex = g_regex_ref (regex); + handler->callback = callback; + handler->user_data = user_data; + handler->notify = notify; + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler); +} + +void +mm_serial_port_set_response_parser (MMSerialPort *self, + MMSerialResponseParserFn fn, + gpointer user_data, + GDestroyNotify notify) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + + if (priv->response_parser_notify) + priv->response_parser_notify (priv->response_parser_user_data); + + priv->response_parser_fn = fn; + priv->response_parser_user_data = user_data; + priv->response_parser_notify = notify; +} + +static gboolean +remove_eval_cb (const GMatchInfo *match_info, + GString *result, + gpointer user_data) +{ + int *result_len = (int *) user_data; + int start; + int end; + + if (g_match_info_fetch_pos (match_info, 0, &start, &end)) + *result_len -= (end - start); + + return FALSE; +} + +static void +parse_unsolicited_messages (MMSerialPort *self, + GString *response) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) { + MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) iter->data; + GMatchInfo *match_info; + gboolean matches; + + matches = g_regex_match_full (handler->regex, response->str, response->len, 0, 0, &match_info, NULL); + if (handler->callback) { + while (g_match_info_matches (match_info)) { + handler->callback (self, match_info, handler->user_data); + g_match_info_next (match_info, NULL); + } + } + + g_match_info_free (match_info); + + if (matches) { + /* Remove matches */ + char *str; + int result_len = response->len; + + str = g_regex_replace_eval (handler->regex, response->str, response->len, 0, 0, + remove_eval_cb, &result_len, NULL); + + g_string_truncate (response, 0); + g_string_append_len (response, str, result_len); + g_free (str); + } + } +} + +static gboolean +parse_response (MMSerialPort *self, + GString *response, + GError **error) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE); + + parse_unsolicited_messages (self, response); + + return priv->response_parser_fn (priv->response_parser_user_data, response, error); +} + +static gboolean +data_available (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + MMSerialPort *self = MM_SERIAL_PORT (data); + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + char buf[SERIAL_BUF_SIZE + 1]; + gsize bytes_read; + GIOStatus status; + + if (condition & G_IO_HUP) { + g_string_truncate (priv->response, 0); + mm_serial_port_close (self); + return FALSE; + } + + if (condition & G_IO_ERR) { + g_string_truncate (priv->response, 0); + return TRUE; + } + + do { + GError *err = NULL; + + status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err); + if (status == G_IO_STATUS_ERROR) { + g_warning ("%s", err->message); + g_error_free (err); + err = NULL; + } + + if (bytes_read > 0) { + serial_debug ("<--", buf, bytes_read); + g_string_append_len (priv->response, buf, bytes_read); + } + + /* Make sure the string doesn't grow too long */ + if (priv->response->len > SERIAL_BUF_SIZE) { + g_warning ("%s (%s): response buffer filled before repsonse received", + G_STRFUNC, mm_serial_port_get_device (self)); + g_string_erase (priv->response, 0, (SERIAL_BUF_SIZE / 2)); + } + + if (parse_response (self, priv->response, &err)) + mm_serial_port_got_response (self, err); + } while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN); + + return TRUE; +} + +gboolean +mm_serial_port_open (MMSerialPort *self, GError **error) +{ + MMSerialPortPrivate *priv; + char *devfile; + + g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE); + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + if (priv->fd >= 0) + /* Already open */ + return TRUE; + + g_debug ("(%s) opening serial device...", priv->device); + devfile = g_strdup_printf ("/dev/%s", priv->device); + priv->fd = open (devfile, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); + g_free (devfile); + + if (priv->fd < 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + "Could not open serial device %s: %s", priv->device, strerror (errno)); + return FALSE; + } + + if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + "Could not open serial device %s: %s", priv->device, strerror (errno)); + close (priv->fd); + priv->fd = -1; + return FALSE; + } + + if (!config_fd (self)) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + "Could not open serial device %s: %s", priv->device, strerror (errno)); + close (priv->fd); + priv->fd = -1; + return FALSE; + } + + priv->channel = g_io_channel_unix_new (priv->fd); + priv->watch_id = g_io_add_watch (priv->channel, + G_IO_IN | G_IO_ERR | G_IO_HUP, + data_available, self); + + return TRUE; +} + +void +mm_serial_port_close (MMSerialPort *self) +{ + MMSerialPortPrivate *priv; + + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + if (priv->fd >= 0) { + g_message ("Closing device '%s'", priv->device); + + if (priv->channel) { + g_source_remove (priv->watch_id); + g_io_channel_shutdown (priv->channel, TRUE, NULL); + g_io_channel_unref (priv->channel); + priv->channel = NULL; + } + + ioctl (priv->fd, TCSETA, &priv->old_t); + close (priv->fd); + priv->fd = -1; + } +} + +static void +internal_queue_command (MMSerialPort *self, + const char *command, + gboolean cached, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + MMQueueData *info; + + g_return_if_fail (MM_IS_SERIAL_PORT (self)); + g_return_if_fail (command != NULL); + + info = g_slice_new0 (MMQueueData); + info->command = g_strdup (command); + info->cached = cached; + info->timeout = timeout_seconds * 1000; + info->callback = callback; + info->user_data = user_data; + + /* Clear the cached value for this command if not asking for cached value */ + if (!cached) + mm_serial_port_set_cached_reply (self, command, NULL); + + g_queue_push_tail (priv->queue, info); + + if (g_queue_get_length (priv->queue) == 1) + mm_serial_port_schedule_queue_process (self); +} + +void +mm_serial_port_queue_command (MMSerialPort *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data) +{ + internal_queue_command (self, command, FALSE, timeout_seconds, callback, user_data); +} + +void +mm_serial_port_queue_command_cached (MMSerialPort *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data) +{ + internal_queue_command (self, command, TRUE, timeout_seconds, callback, user_data); +} + +typedef struct { + MMSerialPort *port; + speed_t current_speed; + MMSerialFlashFn callback; + gpointer user_data; +} FlashInfo; + +static speed_t +get_speed (MMSerialPort *self) +{ + struct termios options; + + tcgetattr (MM_SERIAL_PORT_GET_PRIVATE (self)->fd, &options); + + return cfgetospeed (&options); +} + +static void +set_speed (MMSerialPort *self, speed_t speed) +{ + struct termios options; + int fd; + + fd = MM_SERIAL_PORT_GET_PRIVATE (self)->fd; + tcgetattr (fd, &options); + + 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; + + info->callback (info->port, info->user_data); + + g_slice_free (FlashInfo, info); +} + +static gboolean +flash_do (gpointer data) +{ + FlashInfo *info = (FlashInfo *) data; + + set_speed (info->port, info->current_speed); + + return FALSE; +} + +guint +mm_serial_port_flash (MMSerialPort *self, + guint32 flash_time, + MMSerialFlashFn callback, + gpointer user_data) +{ + FlashInfo *info; + guint id; + + g_return_val_if_fail (MM_IS_SERIAL_PORT (self), 0); + g_return_val_if_fail (callback != NULL, 0); + + info = g_slice_new0 (FlashInfo); + info->port = self; + info->current_speed = get_speed (self); + info->callback = callback; + info->user_data = user_data; + + set_speed (self, B0); + + id = g_timeout_add_full (G_PRIORITY_DEFAULT, + flash_time, + flash_do, + info, + flash_done); + + return id; +} + +gboolean +mm_serial_port_is_connected (MMSerialPort *self) +{ + MMSerialPortPrivate *priv; + int mcs = 0; + + g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE); + + priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + if (!priv->carrier_detect) + return FALSE; + + if (priv->fd < 0) + return FALSE; + + if (ioctl (priv->fd, TIOCMGET, &mcs) < 0) + return FALSE; + + return mcs & TIOCM_CAR ? TRUE : FALSE; +} + +/*****************************************************************************/ + +MMSerialPort * +mm_serial_port_new (const char *name) +{ + return MM_SERIAL_PORT (g_object_new (MM_TYPE_SERIAL_PORT, + MM_SERIAL_PORT_DEVICE, name, + NULL)); +} + +static void +mm_serial_port_init (MMSerialPort *self) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + priv->reply_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + priv->fd = -1; + priv->baud = 57600; + priv->bits = 8; + priv->parity = 'n'; + priv->stopbits = 1; + priv->send_delay = 1000; + priv->carrier_detect = TRUE; + + priv->queue = g_queue_new (); + priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE); + priv->response = g_string_sized_new (SERIAL_BUF_SIZE); +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + MMSerialPortPrivate *priv; + + object = G_OBJECT_CLASS (mm_serial_port_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + priv = MM_SERIAL_PORT_GET_PRIVATE (object); + + if (!priv->device) { + g_warning ("No device provided"); + g_object_unref (object); + return NULL; + } + + return object; +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DEVICE: + /* Construct only */ + priv->device = g_path_get_basename (g_value_get_string (value)); + break; + case PROP_BAUD: + priv->baud = g_value_get_uint (value); + break; + case PROP_BITS: + priv->bits = g_value_get_uint (value); + break; + case PROP_PARITY: + priv->parity = g_value_get_char (value); + break; + case PROP_STOPBITS: + priv->stopbits = g_value_get_uint (value); + break; + case PROP_SEND_DELAY: + priv->send_delay = g_value_get_uint64 (value); + break; + case PROP_CARRIER_DETECT: + priv->carrier_detect = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DEVICE: + g_value_set_string (value, priv->device); + break; + case PROP_BAUD: + g_value_set_uint (value, priv->baud); + break; + case PROP_BITS: + g_value_set_uint (value, priv->bits); + break; + case PROP_PARITY: + g_value_set_char (value, priv->parity); + break; + case PROP_STOPBITS: + g_value_set_uint (value, priv->stopbits); + break; + case PROP_SEND_DELAY: + g_value_set_uint64 (value, priv->send_delay); + break; + case PROP_CARRIER_DETECT: + g_value_set_boolean (value, priv->carrier_detect); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + MMSerialPort *self = MM_SERIAL_PORT (object); + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); + + mm_serial_port_close (self); + + g_hash_table_destroy (priv->reply_cache); + g_queue_free (priv->queue); + g_string_free (priv->command, TRUE); + g_string_free (priv->response, TRUE); + g_free (priv->device); + + while (priv->unsolicited_msg_handlers) { + MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data; + + if (handler->notify) + handler->notify (handler->user_data); + + g_regex_unref (handler->regex); + g_slice_free (MMUnsolicitedMsgHandler, handler); + priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers, + priv->unsolicited_msg_handlers); + } + + if (priv->response_parser_notify) + priv->response_parser_notify (priv->response_parser_user_data); + + G_OBJECT_CLASS (mm_serial_port_parent_class)->finalize (object); +} + +static void +mm_serial_port_class_init (MMSerialPortClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMSerialPortPrivate)); + + /* Virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_DEVICE, + g_param_spec_string (MM_SERIAL_PORT_DEVICE, + "Device", + "Serial device", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_BAUD, + g_param_spec_uint (MM_SERIAL_PORT_BAUD, + "Baud", + "Baud rate", + 0, G_MAXUINT, 57600, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_BITS, + g_param_spec_uint (MM_SERIAL_PORT_BITS, + "Bits", + "Bits", + 5, 8, 8, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_PARITY, + g_param_spec_char (MM_SERIAL_PORT_PARITY, + "Parity", + "Parity", + 'E', 'o', 'n', + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_STOPBITS, + g_param_spec_uint (MM_SERIAL_PORT_STOPBITS, + "Stopbits", + "Stopbits", + 1, 2, 1, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_SEND_DELAY, + g_param_spec_uint64 (MM_SERIAL_PORT_SEND_DELAY, + "SendDelay", + "Send delay", + 0, G_MAXUINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_CARRIER_DETECT, + g_param_spec_boolean (MM_SERIAL_PORT_CARRIER_DETECT, + "CarrierDetect", + "Has carrier detect", + TRUE, + G_PARAM_READWRITE)); +} diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h new file mode 100644 index 00000000..7056527a --- /dev/null +++ b/src/mm-serial-port.h @@ -0,0 +1,114 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_SERIAL_PORT_H +#define MM_SERIAL_PORT_H + +#include <glib.h> +#include <glib/gtypes.h> +#include <glib-object.h> + +typedef enum { + MM_SERIAL_PORT_TYPE_UNKNOWN = 0x0, + MM_SERIAL_PORT_TYPE_PRIMARY, + MM_SERIAL_PORT_TYPE_SECONDARY, + MM_SERIAL_PORT_TYPE_IGNORED +} MMSerialPortType; + +#define MM_TYPE_SERIAL_PORT (mm_serial_port_get_type ()) +#define MM_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SERIAL_PORT, MMSerialPort)) +#define MM_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SERIAL_PORT, MMSerialPortClass)) +#define MM_IS_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SERIAL_PORT)) +#define MM_IS_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL_PORT)) +#define MM_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL_PORT, MMSerialPortClass)) + +#define MM_SERIAL_PORT_DEVICE "serial-device" +#define MM_SERIAL_PORT_BAUD "baud" +#define MM_SERIAL_PORT_BITS "bits" +#define MM_SERIAL_PORT_PARITY "parity" +#define MM_SERIAL_PORT_STOPBITS "stopbits" +#define MM_SERIAL_PORT_SEND_DELAY "send-delay" +#define MM_SERIAL_PORT_CARRIER_DETECT "carrier-detect" + +typedef struct _MMSerialPort MMSerialPort; +typedef struct _MMSerialPortClass MMSerialPortClass; + +typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data, + GString *response, + GError **error); + +typedef void (*MMSerialUnsolicitedMsgFn) (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data); + +typedef void (*MMSerialResponseFn) (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data); + +typedef void (*MMSerialFlashFn) (MMSerialPort *port, + gpointer user_data); + +struct _MMSerialPort { + GObject parent; +}; + +struct _MMSerialPortClass { + GObjectClass parent; +}; + +GType mm_serial_port_get_type (void); + +MMSerialPort *mm_serial_port_new (const char *name); + +void mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self, + GRegex *regex, + MMSerialUnsolicitedMsgFn callback, + gpointer user_data, + GDestroyNotify notify); + +void mm_serial_port_set_response_parser (MMSerialPort *self, + MMSerialResponseParserFn fn, + gpointer user_data, + GDestroyNotify notify); + +gboolean mm_serial_port_open (MMSerialPort *self, + GError **error); + +void mm_serial_port_close (MMSerialPort *self); +void mm_serial_port_queue_command (MMSerialPort *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data); + +void mm_serial_port_queue_command_cached (MMSerialPort *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data); + +guint mm_serial_port_flash (MMSerialPort *self, + guint32 flash_time, + MMSerialFlashFn callback, + gpointer user_data); + +gboolean mm_serial_port_is_connected (MMSerialPort *self); + +const char *mm_serial_port_get_device (MMSerialPort *self); + +#endif /* MM_SERIAL_PORT_H */ + diff --git a/src/mm-serial.c b/src/mm-serial.c index 0301a592..48492ce4 100644 --- a/src/mm-serial.c +++ b/src/mm-serial.c @@ -1,864 +1,100 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -#define _GNU_SOURCE /* for strcasestr() */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ #include <stdio.h> #include <stdlib.h> -#include <termio.h> #include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/ioctl.h> #include <string.h> #include "mm-serial.h" #include "mm-errors.h" #include "mm-options.h" -static gboolean mm_serial_queue_process (gpointer data); +#define TYPE_TAG "type" G_DEFINE_TYPE (MMSerial, mm_serial, G_TYPE_OBJECT) enum { PROP_0, - PROP_DEVICE, - PROP_BAUD, - PROP_BITS, - PROP_PARITY, - PROP_STOPBITS, - PROP_SEND_DELAY, - PROP_CARRIER_DETECT, LAST_PROP }; -#define SERIAL_BUF_SIZE 2048 - #define MM_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL, MMSerialPrivate)) typedef struct { - int fd; - GHashTable *reply_cache; - GIOChannel *channel; - GQueue *queue; - GString *command; - GString *response; - - /* Response parser data */ - MMSerialResponseParserFn response_parser_fn; - gpointer response_parser_user_data; - GDestroyNotify response_parser_notify; - GSList *unsolicited_msg_handlers; - - struct termios old_t; - - char *device; - guint baud; - guint bits; - char parity; - guint stopbits; - guint64 send_delay; - gboolean carrier_detect; - - guint queue_schedule; - guint watch_id; - guint timeout_id; + GHashTable *ports; } MMSerialPrivate; -typedef struct { - GRegex *regex; - MMSerialUnsolicitedMsgFn callback; - gpointer user_data; - GDestroyNotify notify; -} MMUnsolicitedMsgHandler; - -const char * -mm_serial_get_device (MMSerial *serial) -{ - g_return_val_if_fail (MM_IS_SERIAL (serial), NULL); - - return MM_SERIAL_GET_PRIVATE (serial)->device; -} - -static void -mm_serial_set_cached_reply (MMSerial *self, - const char *command, - const char *reply) -{ - if (reply) - g_hash_table_insert (MM_SERIAL_GET_PRIVATE (self)->reply_cache, - g_strdup (command), - g_strdup (reply)); - else - g_hash_table_remove (MM_SERIAL_GET_PRIVATE (self)->reply_cache, command); -} - -static const char * -mm_serial_get_cached_reply (MMSerial *self, - const char *command) -{ - return (char *) g_hash_table_lookup (MM_SERIAL_GET_PRIVATE (self)->reply_cache, command); -} - -static int -parse_baudrate (guint i) -{ - int speed; - - switch (i) { - case 0: - speed = B0; - break; - case 50: - speed = B50; - break; - case 75: - speed = B75; - break; - case 110: - speed = B110; - break; - case 150: - speed = B150; - break; - case 300: - speed = B300; - break; - case 600: - speed = B600; - break; - case 1200: - speed = B1200; - break; - case 2400: - speed = B2400; - break; - case 4800: - speed = B4800; - break; - case 9600: - speed = B9600; - break; - case 19200: - speed = B19200; - break; - case 38400: - speed = B38400; - break; - case 57600: - speed = B57600; - break; - case 115200: - speed = B115200; - break; - case 460800: - speed = B460800; - break; - default: - g_warning ("Invalid baudrate '%d'", i); - speed = B9600; - } - - return speed; -} - -static int -parse_bits (guint i) -{ - int bits; - - switch (i) { - case 5: - bits = CS5; - break; - case 6: - bits = CS6; - break; - case 7: - bits = CS7; - break; - case 8: - bits = CS8; - break; - default: - g_warning ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i); - bits = CS8; - } - - return bits; -} - -static int -parse_parity (char c) -{ - int parity; - - switch (c) { - case 'n': - case 'N': - parity = 0; - break; - case 'e': - case 'E': - parity = PARENB; - break; - case 'o': - case 'O': - parity = PARENB | PARODD; - break; - default: - g_warning ("Invalid parity (%c). Valid values are n, e, o", c); - parity = 0; - } - - return parity; -} - -static int -parse_stopbits (guint i) -{ - int stopbits; - - switch (i) { - case 1: - stopbits = 0; - break; - case 2: - stopbits = CSTOPB; - break; - default: - g_warning ("Invalid stop bits (%d). Valid values are 1 and 2)", i); - stopbits = 0; - } - - return stopbits; -} - -static gboolean -config_fd (MMSerial *self) -{ - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - struct termio stbuf; - int speed; - int bits; - int parity; - int stopbits; - - speed = parse_baudrate (priv->baud); - bits = parse_bits (priv->bits); - parity = parse_parity (priv->parity); - stopbits = parse_stopbits (priv->stopbits); - - ioctl (priv->fd, TCGETA, &stbuf); - - stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR ); - stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET); - stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL); - stbuf.c_lflag &= ~(ECHO | ECHOE); - stbuf.c_cc[VMIN] = 1; - stbuf.c_cc[VTIME] = 0; - stbuf.c_cc[VEOF] = 1; - - stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB); - stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits); - - if (ioctl (priv->fd, TCSETA, &stbuf) < 0) { - g_warning ("(%s) cannot control device (errno %d)", priv->device, errno); - return FALSE; - } - - return TRUE; -} - -static void -serial_debug (const char *prefix, const char *buf, int len) -{ - static GString *debug = NULL; - const char *s; - - if (!mm_options_debug ()) - return; - - if (len < 0) - len = strlen (buf); - - if (!debug) - debug = g_string_sized_new (256); - - g_string_append (debug, prefix); - g_string_append (debug, " '"); - - s = buf; - while (len--) { - if (g_ascii_isprint (*s)) - g_string_append_c (debug, *s); - else if (*s == '\r') - g_string_append (debug, "<CR>"); - else if (*s == '\n') - g_string_append (debug, "<LF>"); - else - g_string_append_printf (debug, "\\%d", *s); - - s++; - } - - g_string_append_c (debug, '\''); - g_debug ("%s", debug->str); - g_string_truncate (debug, 0); -} - -static gboolean -mm_serial_send_command (MMSerial *self, - const char *command, - GError **error) +MMSerialPort * +mm_serial_get_port (MMSerial *self, const char *name) { - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - const char *s; - int status; - int eagain_count = 1000; - - if (priv->fd == 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, - "%s", "Sending command failed: device is not enabled"); - return FALSE; - } - - if (mm_serial_is_connected (self)) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, - "%s", "Sending command failed: device is connected"); - return FALSE; - } - - g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2); - g_string_append (priv->command, command); - - if (command[strlen (command)] != '\r') - g_string_append_c (priv->command, '\r'); - - serial_debug ("-->", priv->command->str, -1); - - s = priv->command->str; - while (*s) { - status = write (priv->fd, s, 1); - if (status < 0) { - if (errno == EAGAIN) { - eagain_count--; - if (eagain_count <= 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, - "Sending command failed: '%s'", strerror (errno)); - break; - } - } else { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED, - "Sending command failed: '%s'", strerror (errno)); - break; - } - } else - s++; - - if (priv->send_delay) - usleep (priv->send_delay); - } - - return *s == '\0'; + return g_hash_table_lookup (MM_SERIAL_GET_PRIVATE (self)->ports, name); } -typedef struct { - char *command; - MMSerialResponseFn callback; - gpointer user_data; - guint32 timeout; - gboolean cached; -} MMQueueData; - static void -mm_serial_schedule_queue_process (MMSerial *self) +find_primary (gpointer key, gpointer data, gpointer user_data) { - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - GSource *source; + MMSerialPort **found = user_data; + MMSerialPort *port = MM_SERIAL_PORT (data); + MMSerialPortType ptype; - if (priv->queue_schedule) - /* Already scheduled */ + if (*found) return; - source = g_idle_source_new (); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_queue_process), G_OBJECT (self))); - g_source_attach (source, NULL); - priv->queue_schedule = g_source_get_id (source); - g_source_unref (source); -} - -static void -mm_serial_got_response (MMSerial *self, GError *error) -{ - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - MMQueueData *info; - - if (priv->timeout_id) - g_source_remove (priv->timeout_id); - - info = (MMQueueData *) g_queue_pop_head (priv->queue); - if (info) { - if (info->cached && !error) - mm_serial_set_cached_reply (self, info->command, priv->response->str); - - if (info->callback) - info->callback (self, priv->response, error, info->user_data); - - g_free (info->command); - g_slice_free (MMQueueData, info); - } - - if (error) - g_error_free (error); - - g_string_truncate (priv->response, 0); - if (!g_queue_is_empty (priv->queue)) - mm_serial_schedule_queue_process (self); -} - -static gboolean -mm_serial_timed_out (gpointer data) -{ - MMSerial *self = MM_SERIAL (data); - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - GError *error; - - priv->timeout_id = 0; - - error = g_error_new_literal (MM_SERIAL_ERROR, - MM_SERIAL_RESPONSE_TIMEOUT, - "Serial command timed out"); - /* FIXME: This is not completely correct - if the response finally arrives and there's - some other command waiting for response right now, the other command will - get the output of the timed out command. Not sure what to do here. */ - mm_serial_got_response (self, error); - - return FALSE; -} - -static gboolean -mm_serial_queue_process (gpointer data) -{ - MMSerial *self = MM_SERIAL (data); - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - MMQueueData *info; - GError *error = NULL; - - priv->queue_schedule = 0; - - info = (MMQueueData *) g_queue_peek_head (priv->queue); - if (!info) - return FALSE; - - if (info->cached) { - const char *cached = mm_serial_get_cached_reply (self, info->command); - - if (cached) { - g_string_append (priv->response, cached); - mm_serial_got_response (self, NULL); - return FALSE; - } - } - - if (mm_serial_send_command (self, info->command, &error)) { - GSource *source; - - source = g_timeout_source_new (info->timeout); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_timed_out), G_OBJECT (self))); - g_source_attach (source, NULL); - priv->timeout_id = g_source_get_id (source); - g_source_unref (source); - } else { - mm_serial_got_response (self, error); - } - - return FALSE; -} - -void -mm_serial_add_unsolicited_msg_handler (MMSerial *self, - GRegex *regex, - MMSerialUnsolicitedMsgFn callback, - gpointer user_data, - GDestroyNotify notify) -{ - MMUnsolicitedMsgHandler *handler; - MMSerialPrivate *priv; - - g_return_if_fail (MM_IS_SERIAL (self)); - g_return_if_fail (regex != NULL); - - handler = g_slice_new (MMUnsolicitedMsgHandler); - handler->regex = g_regex_ref (regex); - handler->callback = callback; - handler->user_data = user_data; - handler->notify = notify; - - priv = MM_SERIAL_GET_PRIVATE (self); - priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler); -} - -void -mm_serial_set_response_parser (MMSerial *self, - MMSerialResponseParserFn fn, - gpointer user_data, - GDestroyNotify notify) -{ - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - - g_return_if_fail (MM_IS_SERIAL (self)); - - if (priv->response_parser_notify) - priv->response_parser_notify (priv->response_parser_user_data); - - priv->response_parser_fn = fn; - priv->response_parser_user_data = user_data; - priv->response_parser_notify = notify; -} - -static gboolean -remove_eval_cb (const GMatchInfo *match_info, - GString *result, - gpointer user_data) -{ - int *result_len = (int *) user_data; - int start; - int end; - - if (g_match_info_fetch_pos (match_info, 0, &start, &end)) - *result_len -= (end - start); - - return FALSE; -} - -static void -parse_unsolicited_messages (MMSerial *self, - GString *response) -{ - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - GSList *iter; - - for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) { - MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) iter->data; - GMatchInfo *match_info; - gboolean matches; - - matches = g_regex_match_full (handler->regex, response->str, response->len, 0, 0, &match_info, NULL); - if (handler->callback) { - while (g_match_info_matches (match_info)) { - handler->callback (self, match_info, handler->user_data); - g_match_info_next (match_info, NULL); - } - } - - g_match_info_free (match_info); - - if (matches) { - /* Remove matches */ - char *str; - int result_len = response->len; - - str = g_regex_replace_eval (handler->regex, response->str, response->len, 0, 0, - remove_eval_cb, &result_len, NULL); - - g_string_truncate (response, 0); - g_string_append_len (response, str, result_len); - g_free (str); - } - } -} - -static gboolean -parse_response (MMSerial *self, - GString *response, - GError **error) -{ - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - - g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE); - - parse_unsolicited_messages (self, response); - - return priv->response_parser_fn (priv->response_parser_user_data, response, error); + ptype = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (port), TYPE_TAG)); + if (ptype == MM_SERIAL_PORT_TYPE_PRIMARY) + *found = port; } -static gboolean -data_available (GIOChannel *source, - GIOCondition condition, - gpointer data) +MMSerialPort * +mm_serial_add_port (MMSerial *self, + const char *name, + MMSerialPortType ptype) { - MMSerial *self = MM_SERIAL (data); MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - char buf[SERIAL_BUF_SIZE + 1]; - gsize bytes_read; - GIOStatus status; - - if (condition & G_IO_HUP) { - g_string_truncate (priv->response, 0); - mm_serial_close (self); - return FALSE; - } - - if (condition & G_IO_ERR) { - g_string_truncate (priv->response, 0); - return TRUE; - } - - do { - GError *err = NULL; - - status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err); - if (status == G_IO_STATUS_ERROR) { - g_warning ("%s", err->message); - g_error_free (err); - err = NULL; - } - - if (bytes_read > 0) { - serial_debug ("<--", buf, bytes_read); - g_string_append_len (priv->response, buf, bytes_read); - } - - /* Make sure the string doesn't grow too long */ - if (priv->response->len > SERIAL_BUF_SIZE) { - g_warning ("%s (%s): response buffer filled before repsonse received", - G_STRFUNC, mm_serial_get_device (self)); - g_string_erase (priv->response, 0, (SERIAL_BUF_SIZE / 2)); - } - - if (parse_response (self, priv->response, &err)) - mm_serial_got_response (self, err); - } while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN); - - return TRUE; -} - -gboolean -mm_serial_open (MMSerial *self, GError **error) -{ - MMSerialPrivate *priv; + MMSerialPort *port = NULL; g_return_val_if_fail (MM_IS_SERIAL (self), FALSE); + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (ptype != MM_SERIAL_PORT_TYPE_UNKNOWN, FALSE); - priv = MM_SERIAL_GET_PRIVATE (self); - - if (priv->fd) - /* Already open */ - return TRUE; - - g_debug ("(%s) opening serial device...", priv->device); - priv->fd = open (priv->device, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); - - if (priv->fd < 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, - "Could not open serial device %s: %s", priv->device, strerror (errno)); - return FALSE; - } - - if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, - "Could not open serial device %s: %s", priv->device, strerror (errno)); - close (priv->fd); - return FALSE; - } - - if (!config_fd (self)) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, - "Could not open serial device %s: %s", priv->device, strerror (errno)); - close (priv->fd); - priv->fd = 0; - return FALSE; - } - - priv->channel = g_io_channel_unix_new (priv->fd); - priv->watch_id = g_io_add_watch (priv->channel, - G_IO_IN | G_IO_ERR | G_IO_HUP, - data_available, self); - - return TRUE; -} - -void -mm_serial_close (MMSerial *self) -{ - MMSerialPrivate *priv; - - g_return_if_fail (MM_IS_SERIAL (self)); - - priv = MM_SERIAL_GET_PRIVATE (self); - - if (priv->fd) { - g_message ("Closing device '%s'", priv->device); + g_return_val_if_fail (g_hash_table_lookup (priv->ports, name) == NULL, FALSE); - if (priv->channel) { - g_source_remove (priv->watch_id); - g_io_channel_shutdown (priv->channel, TRUE, NULL); - g_io_channel_unref (priv->channel); - priv->channel = NULL; - } - - ioctl (priv->fd, TCSETA, &priv->old_t); - close (priv->fd); - priv->fd = 0; + if (ptype == MM_SERIAL_PORT_TYPE_PRIMARY) { + g_hash_table_foreach (priv->ports, find_primary, &port); + g_return_val_if_fail (port == NULL, FALSE); } -} - -static void -internal_queue_command (MMSerial *self, - const char *command, - gboolean cached, - guint32 timeout_seconds, - MMSerialResponseFn callback, - gpointer user_data) -{ - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - MMQueueData *info; - - g_return_if_fail (MM_IS_SERIAL (self)); - g_return_if_fail (command != NULL); - - info = g_slice_new0 (MMQueueData); - info->command = g_strdup (command); - info->cached = cached; - info->timeout = timeout_seconds * 1000; - info->callback = callback; - info->user_data = user_data; - - /* Clear the cached value for this command if not asking for cached value */ - if (!cached) - mm_serial_set_cached_reply (self, command, NULL); - - g_queue_push_tail (priv->queue, info); - - if (g_queue_get_length (priv->queue) == 1) - mm_serial_schedule_queue_process (self); -} - -void -mm_serial_queue_command (MMSerial *self, - const char *command, - guint32 timeout_seconds, - MMSerialResponseFn callback, - gpointer user_data) -{ - internal_queue_command (self, command, FALSE, timeout_seconds, callback, user_data); -} - -void -mm_serial_queue_command_cached (MMSerial *self, - const char *command, - guint32 timeout_seconds, - MMSerialResponseFn callback, - gpointer user_data) -{ - internal_queue_command (self, command, TRUE, timeout_seconds, callback, user_data); -} - -typedef struct { - MMSerial *serial; - speed_t current_speed; - MMSerialFlashFn callback; - gpointer user_data; -} FlashInfo; - -static speed_t -get_speed (MMSerial *self) -{ - struct termios options; - - tcgetattr (MM_SERIAL_GET_PRIVATE (self)->fd, &options); - - return cfgetospeed (&options); -} - -static void -set_speed (MMSerial *self, speed_t speed) -{ - struct termios options; - int fd; - - fd = MM_SERIAL_GET_PRIVATE (self)->fd; - tcgetattr (fd, &options); - - 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; - - info->callback (info->serial, info->user_data); - - g_slice_free (FlashInfo, info); -} -static gboolean -flash_do (gpointer data) -{ - FlashInfo *info = (FlashInfo *) data; - - set_speed (info->serial, info->current_speed); - - return FALSE; -} - -guint -mm_serial_flash (MMSerial *self, - guint32 flash_time, - MMSerialFlashFn callback, - gpointer user_data) -{ - FlashInfo *info; - guint id; - - g_return_val_if_fail (MM_IS_SERIAL (self), 0); - g_return_val_if_fail (callback != NULL, 0); - - info = g_slice_new0 (FlashInfo); - info->serial = self; - info->current_speed = get_speed (self); - info->callback = callback; - info->user_data = user_data; - - set_speed (self, B0); - - id = g_timeout_add_full (G_PRIORITY_DEFAULT, - flash_time, - flash_do, - info, - flash_done); + port = mm_serial_port_new (name); + if (!port) + return NULL; - return id; + g_object_set_data (G_OBJECT (port), TYPE_TAG, GUINT_TO_POINTER (ptype)); + g_hash_table_insert (priv->ports, g_strdup (name), port); + return port; } gboolean -mm_serial_is_connected (MMSerial *self) +mm_serial_remove_port (MMSerial *self, MMSerialPort *port) { - MMSerialPrivate *priv; - int mcs = 0; - g_return_val_if_fail (MM_IS_SERIAL (self), FALSE); + g_return_val_if_fail (port != NULL, FALSE); - priv = MM_SERIAL_GET_PRIVATE (self); - - if (!priv->carrier_detect) - return FALSE; - - if (priv->fd == 0) - return FALSE; - - if (ioctl (priv->fd, TIOCMGET, &mcs) < 0) - return FALSE; - - return mcs & TIOCM_CAR ? TRUE : FALSE; + return g_hash_table_remove (MM_SERIAL_GET_PRIVATE (self)->ports, port); } /*****************************************************************************/ @@ -868,74 +104,14 @@ mm_serial_init (MMSerial *self) { MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - priv->reply_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - priv->baud = 57600; - priv->bits = 8; - priv->parity = 'n'; - priv->stopbits = 1; - priv->send_delay = 1000; - priv->carrier_detect = TRUE; - - priv->queue = g_queue_new (); - priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE); - priv->response = g_string_sized_new (SERIAL_BUF_SIZE); -} - -static GObject* -constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) -{ - GObject *object; - MMSerialPrivate *priv; - - object = G_OBJECT_CLASS (mm_serial_parent_class)->constructor (type, - n_construct_params, - construct_params); - if (!object) - return NULL; - - priv = MM_SERIAL_GET_PRIVATE (object); - - if (!priv->device) { - g_warning ("No device provided"); - g_object_unref (object); - return NULL; - } - - return object; + priv->ports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (object); - switch (prop_id) { - case PROP_DEVICE: - /* Construct only */ - priv->device = g_value_dup_string (value); - break; - case PROP_BAUD: - priv->baud = g_value_get_uint (value); - break; - case PROP_BITS: - priv->bits = g_value_get_uint (value); - break; - case PROP_PARITY: - priv->parity = g_value_get_char (value); - break; - case PROP_STOPBITS: - priv->stopbits = g_value_get_uint (value); - break; - case PROP_SEND_DELAY: - priv->send_delay = g_value_get_uint64 (value); - break; - case PROP_CARRIER_DETECT: - priv->carrier_detect = g_value_get_boolean (value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -946,30 +122,7 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (object); - switch (prop_id) { - case PROP_DEVICE: - g_value_set_string (value, priv->device); - break; - case PROP_BAUD: - g_value_set_uint (value, priv->baud); - break; - case PROP_BITS: - g_value_set_uint (value, priv->bits); - break; - case PROP_PARITY: - g_value_set_char (value, priv->parity); - break; - case PROP_STOPBITS: - g_value_set_uint (value, priv->stopbits); - break; - case PROP_SEND_DELAY: - g_value_set_uint64 (value, priv->send_delay); - break; - case PROP_CARRIER_DETECT: - g_value_set_boolean (value, priv->carrier_detect); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -982,28 +135,7 @@ finalize (GObject *object) MMSerial *self = MM_SERIAL (object); MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - mm_serial_close (self); - - g_hash_table_destroy (priv->reply_cache); - g_queue_free (priv->queue); - g_string_free (priv->command, TRUE); - g_string_free (priv->response, TRUE); - g_free (priv->device); - - while (priv->unsolicited_msg_handlers) { - MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data; - - if (handler->notify) - handler->notify (handler->user_data); - - g_regex_unref (handler->regex); - g_slice_free (MMUnsolicitedMsgHandler, handler); - priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers, - priv->unsolicited_msg_handlers); - } - - if (priv->response_parser_notify) - priv->response_parser_notify (priv->response_parser_user_data); + g_hash_table_destroy (priv->ports); G_OBJECT_CLASS (mm_serial_parent_class)->finalize (object); } @@ -1016,65 +148,7 @@ mm_serial_class_init (MMSerialClass *klass) g_type_class_add_private (object_class, sizeof (MMSerialPrivate)); /* Virtual methods */ - object_class->constructor = constructor; object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; - - /* Properties */ - g_object_class_install_property - (object_class, PROP_DEVICE, - g_param_spec_string (MM_SERIAL_DEVICE, - "Device", - "Serial device", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_BAUD, - g_param_spec_uint (MM_SERIAL_BAUD, - "Baud", - "Baud rate", - 0, G_MAXUINT, 57600, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_BITS, - g_param_spec_uint (MM_SERIAL_BITS, - "Bits", - "Bits", - 5, 8, 8, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_PARITY, - g_param_spec_char (MM_SERIAL_PARITY, - "Parity", - "Parity", - 'E', 'o', 'n', - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_STOPBITS, - g_param_spec_uint (MM_SERIAL_STOPBITS, - "Stopbits", - "Stopbits", - 1, 2, 1, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_SEND_DELAY, - g_param_spec_uint64 (MM_SERIAL_SEND_DELAY, - "SendDelay", - "Send delay", - 0, G_MAXUINT64, 0, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_CARRIER_DETECT, - g_param_spec_boolean (MM_SERIAL_CARRIER_DETECT, - "CarrierDetect", - "Has carrier detect", - TRUE, - G_PARAM_READWRITE)); } diff --git a/src/mm-serial.h b/src/mm-serial.h index e3479eed..b6c2b5ec 100644 --- a/src/mm-serial.h +++ b/src/mm-serial.h @@ -1,4 +1,18 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ #ifndef MM_SERIAL_H #define MM_SERIAL_H @@ -7,6 +21,8 @@ #include <glib/gtypes.h> #include <glib-object.h> +#include "mm-serial-port.h" + #define MM_TYPE_SERIAL (mm_serial_get_type ()) #define MM_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SERIAL, MMSerial)) #define MM_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SERIAL, MMSerialClass)) @@ -14,33 +30,9 @@ #define MM_IS_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL)) #define MM_SERIAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL, MMSerialClass)) -#define MM_SERIAL_DEVICE "serial-device" -#define MM_SERIAL_BAUD "baud" -#define MM_SERIAL_BITS "bits" -#define MM_SERIAL_PARITY "parity" -#define MM_SERIAL_STOPBITS "stopbits" -#define MM_SERIAL_SEND_DELAY "send-delay" -#define MM_SERIAL_CARRIER_DETECT "carrier-detect" - typedef struct _MMSerial MMSerial; typedef struct _MMSerialClass MMSerialClass; -typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data, - GString *response, - GError **error); - -typedef void (*MMSerialUnsolicitedMsgFn) (MMSerial *serial, - GMatchInfo *match_info, - gpointer user_data); - -typedef void (*MMSerialResponseFn) (MMSerial *serial, - GString *response, - GError *error, - gpointer user_data); - -typedef void (*MMSerialFlashFn) (MMSerial *serial, - gpointer user_data); - struct _MMSerial { GObject parent; }; @@ -51,39 +43,15 @@ struct _MMSerialClass { GType mm_serial_get_type (void); -void mm_serial_add_unsolicited_msg_handler (MMSerial *self, - GRegex *regex, - MMSerialUnsolicitedMsgFn callback, - gpointer user_data, - GDestroyNotify notify); +MMSerialPort *mm_serial_get_port (MMSerial *self, + const char *name); -void mm_serial_set_response_parser (MMSerial *self, - MMSerialResponseParserFn fn, - gpointer user_data, - GDestroyNotify notify); +MMSerialPort *mm_serial_add_port (MMSerial *self, + const char *name, + MMSerialPortType ptype); -gboolean mm_serial_open (MMSerial *self, - GError **error); - -void mm_serial_close (MMSerial *self); -void mm_serial_queue_command (MMSerial *self, - const char *command, - guint32 timeout_seconds, - MMSerialResponseFn callback, - gpointer user_data); - -void mm_serial_queue_command_cached (MMSerial *self, - const char *command, - guint32 timeout_seconds, - MMSerialResponseFn callback, - gpointer user_data); - -guint mm_serial_flash (MMSerial *self, - guint32 flash_time, - MMSerialFlashFn callback, - gpointer user_data); - -gboolean mm_serial_is_connected (MMSerial *self); -const char *mm_serial_get_device (MMSerial *self); +gboolean mm_serial_remove_port (MMSerial *self, + MMSerialPort *port); #endif /* MM_SERIAL_H */ + |