| /* -*- 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 - 2011 Red Hat, Inc. |
| * Copyright (C) 2011 Google, Inc. |
| */ |
| |
| #include <config.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| |
| #include <ModemManager.h> |
| #include <mm-errors-types.h> |
| #include <mm-gdbus-modem.h> |
| |
| #include "mm-base-modem.h" |
| |
| #include "mm-log.h" |
| #include "mm-serial-enums-types.h" |
| #include "mm-serial-parsers.h" |
| #include "mm-modem-helpers.h" |
| |
| G_DEFINE_ABSTRACT_TYPE (MMBaseModem, mm_base_modem, MM_GDBUS_TYPE_OBJECT_SKELETON); |
| |
| enum { |
| PROP_0, |
| PROP_VALID, |
| PROP_MAX_TIMEOUTS, |
| PROP_DEVICE, |
| PROP_DRIVER, |
| PROP_PLUGIN, |
| PROP_VENDOR_ID, |
| PROP_PRODUCT_ID, |
| PROP_CONNECTION, |
| PROP_LAST |
| }; |
| |
| static GParamSpec *properties[PROP_LAST]; |
| |
| struct _MMBaseModemPrivate { |
| /* The connection to the system bus */ |
| GDBusConnection *connection; |
| |
| /* Modem-wide cancellable. If it ever gets cancelled, no further operations |
| * should be done by the modem. */ |
| GCancellable *cancellable; |
| gulong invalid_if_cancelled; |
| |
| gchar *device; |
| gchar *driver; |
| gchar *plugin; |
| |
| guint vendor_id; |
| guint product_id; |
| |
| gboolean valid; |
| |
| guint max_timeouts; |
| |
| /* The authorization provider */ |
| MMAuthProvider *authp; |
| GCancellable *authp_cancellable; |
| |
| GHashTable *ports; |
| MMAtSerialPort *primary; |
| MMAtSerialPort *secondary; |
| MMQcdmSerialPort *qcdm; |
| MMPort *data; |
| |
| /* GPS-enabled modems will have an AT port for control, and a raw serial |
| * port to receive all GPS traces */ |
| MMAtSerialPort *gps_control; |
| MMGpsSerialPort *gps; |
| }; |
| |
| static gchar * |
| get_hash_key (const gchar *subsys, |
| const gchar *name) |
| { |
| return g_strdup_printf ("%s%s", subsys, name); |
| } |
| |
| MMPort * |
| mm_base_modem_get_port (MMBaseModem *self, |
| const gchar *subsys, |
| const gchar *name) |
| { |
| MMPort *port; |
| gchar *key; |
| |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| g_return_val_if_fail (name != NULL, NULL); |
| g_return_val_if_fail (subsys != NULL, NULL); |
| |
| /* Only 'net' or 'tty' should be given */ |
| g_return_val_if_fail (g_str_equal (subsys, "net") || |
| g_str_equal (subsys, "tty"), |
| NULL); |
| |
| key = get_hash_key (subsys, name); |
| port = g_hash_table_lookup (self->priv->ports, key); |
| g_free (key); |
| |
| return port; |
| } |
| |
| gboolean |
| mm_base_modem_owns_port (MMBaseModem *self, |
| const gchar *subsys, |
| const gchar *name) |
| { |
| return !!mm_base_modem_get_port (self, subsys, name); |
| } |
| |
| static void |
| serial_port_timed_out_cb (MMSerialPort *port, |
| guint n_consecutive_timeouts, |
| gpointer user_data) |
| { |
| MMBaseModem *self = (MM_BASE_MODEM (user_data)); |
| |
| if (self->priv->max_timeouts > 0 && |
| n_consecutive_timeouts >= self->priv->max_timeouts) { |
| mm_warn ("(%s/%s) port timed out %u times, marking modem '%s' as disabled", |
| mm_port_type_get_string (mm_port_get_port_type (MM_PORT (port))), |
| mm_port_get_device (MM_PORT (port)), |
| n_consecutive_timeouts, |
| g_dbus_object_get_object_path (G_DBUS_OBJECT (self))); |
| |
| /* Only set action to invalidate modem if not already done */ |
| g_cancellable_cancel (self->priv->cancellable); |
| } |
| } |
| |
| gboolean |
| mm_base_modem_grab_port (MMBaseModem *self, |
| const gchar *subsys, |
| const gchar *name, |
| MMPortType ptype, |
| MMAtPortFlag at_pflags, |
| GError **error) |
| { |
| MMPort *port; |
| gchar *key; |
| |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); |
| g_return_val_if_fail (subsys != NULL, FALSE); |
| g_return_val_if_fail (name != NULL, FALSE); |
| |
| /* Only allow 'tty' and 'net' ports */ |
| if (!g_str_equal (subsys, "net") && |
| !g_str_equal (subsys, "tty")) { |
| g_set_error (error, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_UNSUPPORTED, |
| "Cannot add port '%s/%s', unhandled subsystem", |
| subsys, |
| name); |
| return FALSE; |
| } |
| |
| /* Check whether we already have it stored */ |
| key = get_hash_key (subsys, name); |
| port = g_hash_table_lookup (self->priv->ports, key); |
| if (port) { |
| g_set_error (error, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_UNSUPPORTED, |
| "Cannot add port '%s/%s', already exists", |
| subsys, |
| name); |
| g_free (key); |
| return FALSE; |
| } |
| |
| /* Serial ports... */ |
| if (g_str_equal (subsys, "tty")) { |
| if (ptype == MM_PORT_TYPE_QCDM) |
| /* QCDM port */ |
| port = MM_PORT (mm_qcdm_serial_port_new (name)); |
| else if (ptype == MM_PORT_TYPE_AT) { |
| /* AT port */ |
| port = MM_PORT (mm_at_serial_port_new (name)); |
| |
| /* Set common response parser */ |
| mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port), |
| mm_serial_parser_v1_parse, |
| mm_serial_parser_v1_new (), |
| mm_serial_parser_v1_destroy); |
| /* Store flags already */ |
| mm_at_serial_port_set_flags (MM_AT_SERIAL_PORT (port), at_pflags); |
| } else if (ptype == MM_PORT_TYPE_GPS) { |
| /* Raw GPS port */ |
| port = MM_PORT (mm_gps_serial_port_new (name)); |
| } else { |
| g_set_error (error, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_UNSUPPORTED, |
| "Cannot add port '%s/%s', unhandled serial type", |
| subsys, |
| name); |
| g_free (key); |
| return FALSE; |
| } |
| |
| /* For serial ports, enable port timeout checks */ |
| g_signal_connect (port, |
| "timed-out", |
| G_CALLBACK (serial_port_timed_out_cb), |
| self); |
| } |
| /* Net ports... */ |
| else if (g_str_equal (subsys, "net")) { |
| port = MM_PORT (g_object_new (MM_TYPE_PORT, |
| MM_PORT_DEVICE, name, |
| MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET, |
| MM_PORT_TYPE, MM_PORT_TYPE_NET, |
| NULL)); |
| } else |
| /* We already filter out before all non-tty, non-net ports */ |
| g_assert_not_reached(); |
| |
| mm_dbg ("(%s) type '%s' claimed by %s", |
| name, |
| mm_port_type_get_string (ptype), |
| mm_base_modem_get_device (self)); |
| |
| /* Add it to the tracking HT. |
| * Note: 'key' and 'port' now owned by the HT. */ |
| g_hash_table_insert (self->priv->ports, key, port); |
| |
| return TRUE; |
| } |
| |
| void |
| mm_base_modem_release_port (MMBaseModem *self, |
| const gchar *subsys, |
| const gchar *name) |
| { |
| gchar *key; |
| MMPort *port; |
| |
| g_return_if_fail (MM_IS_BASE_MODEM (self)); |
| g_return_if_fail (name != NULL); |
| g_return_if_fail (subsys != NULL); |
| |
| if (!g_str_equal (subsys, "tty") && |
| !g_str_equal (subsys, "net")) |
| return; |
| |
| key = get_hash_key (subsys, name); |
| |
| /* Find the port */ |
| port = g_hash_table_lookup (self->priv->ports, key); |
| if (!port) { |
| mm_warn ("(%s/%s): cannot release port, not found", |
| subsys, name); |
| g_free (key); |
| return; |
| } |
| |
| if (port == (MMPort *)self->priv->primary) { |
| /* Cancel modem-wide cancellable; no further actions can be done |
| * without a primary port. */ |
| g_cancellable_cancel (self->priv->cancellable); |
| |
| g_clear_object (&self->priv->primary); |
| } |
| |
| if (port == (MMPort *)self->priv->data) |
| g_clear_object (&self->priv->data); |
| |
| if (port == (MMPort *)self->priv->secondary) |
| g_clear_object (&self->priv->secondary); |
| |
| if (port == (MMPort *)self->priv->qcdm) |
| g_clear_object (&self->priv->qcdm); |
| |
| if (port == (MMPort *)self->priv->gps_control) |
| g_clear_object (&self->priv->gps_control); |
| |
| if (port == (MMPort *)self->priv->gps) |
| g_clear_object (&self->priv->gps); |
| |
| /* Remove it from the tracking HT */ |
| mm_dbg ("(%s/%s) type %s released from %s", |
| subsys, |
| name, |
| mm_port_type_get_string (mm_port_get_port_type (port)), |
| mm_port_get_device (port)); |
| g_hash_table_remove (self->priv->ports, key); |
| g_free (key); |
| } |
| |
| gboolean |
| mm_base_modem_disable_finish (MMBaseModem *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return MM_BASE_MODEM_GET_CLASS (self)->disable_finish (self, res, error); |
| } |
| |
| void |
| mm_base_modem_disable (MMBaseModem *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable != NULL); |
| g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable_finish != NULL); |
| |
| MM_BASE_MODEM_GET_CLASS (self)->disable ( |
| self, |
| self->priv->cancellable, |
| callback, |
| user_data); |
| } |
| |
| gboolean |
| mm_base_modem_enable_finish (MMBaseModem *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return MM_BASE_MODEM_GET_CLASS (self)->enable_finish (self, res, error); |
| } |
| |
| void |
| mm_base_modem_enable (MMBaseModem *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable != NULL); |
| g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable_finish != NULL); |
| |
| MM_BASE_MODEM_GET_CLASS (self)->enable ( |
| self, |
| self->priv->cancellable, |
| callback, |
| user_data); |
| } |
| |
| gboolean |
| mm_base_modem_initialize_finish (MMBaseModem *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return MM_BASE_MODEM_GET_CLASS (self)->initialize_finish (self, res, error); |
| } |
| |
| void |
| mm_base_modem_initialize (MMBaseModem *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| g_assert (MM_BASE_MODEM_GET_CLASS (self)->initialize != NULL); |
| g_assert (MM_BASE_MODEM_GET_CLASS (self)->initialize_finish != NULL); |
| |
| MM_BASE_MODEM_GET_CLASS (self)->initialize ( |
| self, |
| self->priv->cancellable, |
| callback, |
| user_data); |
| } |
| |
| void |
| mm_base_modem_set_valid (MMBaseModem *self, |
| gboolean new_valid) |
| { |
| g_return_if_fail (MM_IS_BASE_MODEM (self)); |
| |
| /* If validity changed OR if both old and new were invalid, notify. This |
| * last case is to cover failures during initialization. */ |
| if (self->priv->valid != new_valid || |
| !new_valid) { |
| self->priv->valid = new_valid; |
| g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALID]); |
| } |
| } |
| |
| gboolean |
| mm_base_modem_get_valid (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); |
| |
| return self->priv->valid; |
| } |
| |
| GCancellable * |
| mm_base_modem_peek_cancellable (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return self->priv->cancellable; |
| } |
| |
| GCancellable * |
| mm_base_modem_get_cancellable (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return g_object_ref (self->priv->cancellable); |
| } |
| |
| MMAtSerialPort * |
| mm_base_modem_get_port_primary (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return (self->priv->primary ? g_object_ref (self->priv->primary) : NULL); |
| } |
| |
| MMAtSerialPort * |
| mm_base_modem_peek_port_primary (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return self->priv->primary; |
| } |
| |
| MMAtSerialPort * |
| mm_base_modem_get_port_secondary (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return (self->priv->secondary ? g_object_ref (self->priv->secondary) : NULL); |
| } |
| |
| MMAtSerialPort * |
| mm_base_modem_peek_port_secondary (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return self->priv->secondary; |
| } |
| |
| MMQcdmSerialPort * |
| mm_base_modem_get_port_qcdm (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return (self->priv->qcdm ? g_object_ref (self->priv->qcdm) : NULL); |
| } |
| |
| MMQcdmSerialPort * |
| mm_base_modem_peek_port_qcdm (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return self->priv->qcdm; |
| } |
| |
| MMAtSerialPort * |
| mm_base_modem_get_port_gps_control (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return (self->priv->gps_control ? g_object_ref (self->priv->gps_control) : NULL); |
| } |
| |
| MMAtSerialPort * |
| mm_base_modem_peek_port_gps_control (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return self->priv->gps_control; |
| } |
| |
| MMGpsSerialPort * |
| mm_base_modem_get_port_gps (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return (self->priv->gps ? g_object_ref (self->priv->gps) : NULL); |
| } |
| |
| MMGpsSerialPort * |
| mm_base_modem_peek_port_gps (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return self->priv->gps; |
| } |
| |
| MMPort * |
| mm_base_modem_get_best_data_port (MMBaseModem *self) |
| { |
| MMPort *port; |
| |
| port = mm_base_modem_peek_best_data_port (self); |
| return (port ? g_object_ref (port) : NULL); |
| } |
| |
| MMPort * |
| mm_base_modem_peek_best_data_port (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| /* TODO: sometime we'll have a list of available data ports to use instead |
| * of a single one */ |
| |
| return (mm_port_get_connected (self->priv->data) ? |
| NULL : |
| self->priv->data); |
| } |
| |
| MMAtSerialPort * |
| mm_base_modem_get_best_at_port (MMBaseModem *self, |
| GError **error) |
| { |
| MMAtSerialPort *best; |
| |
| best = mm_base_modem_peek_best_at_port (self, error); |
| return (best ? g_object_ref (best) : NULL); |
| } |
| |
| MMAtSerialPort * |
| mm_base_modem_peek_best_at_port (MMBaseModem *self, |
| GError **error) |
| { |
| /* Decide which port to use */ |
| if (self->priv->primary && |
| !mm_port_get_connected (MM_PORT (self->priv->primary))) |
| return self->priv->primary; |
| |
| /* If primary port is connected, check if we can get the secondary |
| * port */ |
| if (self->priv->secondary && |
| !mm_port_get_connected (MM_PORT (self->priv->secondary))) |
| return self->priv->secondary; |
| |
| /* Otherwise, we cannot get any port */ |
| g_set_error (error, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_CONNECTED, |
| "No AT port available to run command"); |
| return NULL; |
| } |
| |
| gboolean |
| mm_base_modem_has_at_port (MMBaseModem *self) |
| { |
| GHashTableIter iter; |
| gpointer value; |
| gpointer key; |
| |
| /* We'll iterate the ht of ports, looking for any port which is AT */ |
| g_hash_table_iter_init (&iter, self->priv->ports); |
| while (g_hash_table_iter_next (&iter, &key, &value)) { |
| if (MM_IS_AT_SERIAL_PORT (value)) |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| static void |
| initialize_ready (MMBaseModem *self, |
| GAsyncResult *res) |
| { |
| GError *error = NULL; |
| |
| if (!mm_base_modem_initialize_finish (self, res, &error)) { |
| /* Wrong state is returned when modem is found locked */ |
| if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE)) |
| mm_dbg ("Couldn't finish initialization in the current state: '%s'", |
| error->message); |
| else |
| mm_warn ("couldn't initialize the modem: '%s'", error->message); |
| g_error_free (error); |
| } else |
| mm_dbg ("modem properly initialized"); |
| |
| /* Even with initialization errors, we do set the state to valid, so |
| * that the modem gets exported and the failure notified to the user. |
| */ |
| mm_base_modem_set_valid (self, TRUE); |
| } |
| |
| static inline void |
| log_port (MMBaseModem *self, MMPort *port, const char *desc) |
| { |
| if (port) { |
| mm_dbg ("(%s) %s/%s %s", |
| self->priv->device, |
| mm_port_subsys_get_string (mm_port_get_subsys (port)), |
| mm_port_get_device (port), |
| desc); |
| } |
| } |
| |
| gboolean |
| mm_base_modem_organize_ports (MMBaseModem *self, |
| GError **error) |
| { |
| GHashTableIter iter; |
| MMPort *candidate; |
| MMAtPortFlag flags; |
| MMAtSerialPort *backup_primary = NULL; |
| MMAtSerialPort *primary = NULL; |
| MMAtSerialPort *secondary = NULL; |
| MMAtSerialPort *backup_secondary = NULL; |
| MMQcdmSerialPort *qcdm = NULL; |
| MMAtSerialPort *gps_control = NULL; |
| MMGpsSerialPort *gps = NULL; |
| MMPort *data = NULL; |
| |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); |
| |
| /* If ports have already been organized, just return success */ |
| if (self->priv->primary) |
| return TRUE; |
| |
| g_hash_table_iter_init (&iter, self->priv->ports); |
| while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &candidate)) { |
| switch (mm_port_get_port_type (candidate)) { |
| |
| case MM_PORT_TYPE_AT: |
| g_assert (MM_IS_AT_SERIAL_PORT (candidate)); |
| flags = mm_at_serial_port_get_flags (MM_AT_SERIAL_PORT (candidate)); |
| |
| if (flags & MM_AT_PORT_FLAG_PRIMARY) { |
| if (!primary) |
| primary = MM_AT_SERIAL_PORT (candidate); |
| else if (!backup_primary) { |
| /* Just in case the plugin gave us more than one primary |
| * and no secondaries, treat additional primary ports as |
| * secondary. |
| */ |
| backup_primary = MM_AT_SERIAL_PORT (candidate); |
| } |
| } |
| |
| if (!data && (flags & MM_AT_PORT_FLAG_PPP)) |
| data = candidate; |
| |
| /* Explicitly flagged secondary ports trump NONE ports for secondary */ |
| if (flags & MM_AT_PORT_FLAG_SECONDARY) { |
| if (!secondary || !(mm_at_serial_port_get_flags (secondary) & MM_AT_PORT_FLAG_SECONDARY)) |
| secondary = MM_AT_SERIAL_PORT (candidate); |
| } |
| |
| if (flags & MM_AT_PORT_FLAG_GPS_CONTROL) { |
| if (!gps_control) |
| gps_control = MM_AT_SERIAL_PORT (candidate); |
| } |
| |
| /* Fallback secondary */ |
| if (flags == MM_AT_PORT_FLAG_NONE) { |
| if (!secondary) |
| secondary = MM_AT_SERIAL_PORT (candidate); |
| else if (!backup_secondary) |
| backup_secondary = MM_AT_SERIAL_PORT (candidate); |
| } |
| break; |
| |
| case MM_PORT_TYPE_QCDM: |
| g_assert (MM_IS_QCDM_SERIAL_PORT (candidate)); |
| if (!qcdm) |
| qcdm = MM_QCDM_SERIAL_PORT (candidate); |
| break; |
| |
| case MM_PORT_TYPE_NET: |
| /* Net device (if any) is the preferred data port */ |
| if (!data || MM_IS_AT_SERIAL_PORT (data)) |
| data = candidate; |
| break; |
| |
| case MM_PORT_TYPE_GPS: |
| g_assert (MM_IS_GPS_SERIAL_PORT (candidate)); |
| if (!gps) |
| gps = MM_GPS_SERIAL_PORT (candidate); |
| break; |
| |
| default: |
| /* Ignore port */ |
| break; |
| } |
| } |
| |
| /* Fall back to a secondary port if we didn't find a primary port */ |
| if (!primary) { |
| if (!secondary) { |
| g_set_error_literal (error, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_FAILED, |
| "Failed to find primary port"); |
| return FALSE; |
| } |
| primary = secondary; |
| secondary = NULL; |
| } |
| g_assert (primary); |
| |
| /* If the plugin didn't give us any secondary ports, use any additional |
| * primary ports or backup secondary ports as secondary. |
| */ |
| if (!secondary) |
| secondary = backup_primary ? backup_primary : backup_secondary; |
| |
| /* Data port defaults to primary AT port */ |
| if (!data) |
| data = MM_PORT (primary); |
| g_assert (data); |
| |
| /* Reset flags on all ports; clear data port first since it might also |
| * be the primary or secondary port. |
| */ |
| if (MM_IS_AT_SERIAL_PORT (data)) |
| mm_at_serial_port_set_flags (MM_AT_SERIAL_PORT (data), MM_AT_PORT_FLAG_NONE); |
| |
| mm_at_serial_port_set_flags (primary, MM_AT_PORT_FLAG_PRIMARY); |
| if (secondary) |
| mm_at_serial_port_set_flags (secondary, MM_AT_PORT_FLAG_SECONDARY); |
| |
| if (MM_IS_AT_SERIAL_PORT (data)) { |
| flags = mm_at_serial_port_get_flags (MM_AT_SERIAL_PORT (data)); |
| mm_at_serial_port_set_flags (MM_AT_SERIAL_PORT (data), flags | MM_AT_PORT_FLAG_PPP); |
| } |
| |
| log_port (self, MM_PORT (primary), "primary"); |
| log_port (self, MM_PORT (secondary), "secondary"); |
| log_port (self, MM_PORT (data), "data"); |
| log_port (self, MM_PORT (qcdm), "qcdm"); |
| log_port (self, MM_PORT (gps_control), "gps control"); |
| log_port (self, MM_PORT (gps), "gps"); |
| |
| /* We keep new refs to the objects here */ |
| self->priv->primary = g_object_ref (primary); |
| self->priv->secondary = (secondary ? g_object_ref (secondary) : NULL); |
| self->priv->data = g_object_ref (data); |
| self->priv->qcdm = (qcdm ? g_object_ref (qcdm) : NULL); |
| self->priv->gps_control = (gps_control ? g_object_ref (gps_control) : NULL); |
| self->priv->gps = (gps ? g_object_ref (gps) : NULL); |
| |
| /* As soon as we get the ports organized, we initialize the modem */ |
| mm_base_modem_initialize (self, |
| (GAsyncReadyCallback)initialize_ready, |
| NULL); |
| |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| /* Authorization */ |
| |
| gboolean |
| mm_base_modem_authorize_finish (MMBaseModem *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); |
| } |
| |
| static void |
| authorize_ready (MMAuthProvider *authp, |
| GAsyncResult *res, |
| GSimpleAsyncResult *simple) |
| { |
| GError *error = NULL; |
| |
| if (!mm_auth_provider_authorize_finish (authp, res, &error)) |
| g_simple_async_result_take_error (simple, error); |
| else |
| g_simple_async_result_set_op_res_gboolean (simple, TRUE); |
| |
| g_simple_async_result_complete (simple); |
| g_object_unref (simple); |
| } |
| |
| void |
| mm_base_modem_authorize (MMBaseModem *self, |
| GDBusMethodInvocation *invocation, |
| const gchar *authorization, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GSimpleAsyncResult *result; |
| |
| result = g_simple_async_result_new (G_OBJECT (self), |
| callback, |
| user_data, |
| mm_base_modem_authorize); |
| mm_auth_provider_authorize (self->priv->authp, |
| invocation, |
| authorization, |
| self->priv->authp_cancellable, |
| (GAsyncReadyCallback)authorize_ready, |
| result); |
| } |
| |
| /*****************************************************************************/ |
| |
| const gchar * |
| mm_base_modem_get_device (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return self->priv->device; |
| } |
| |
| const gchar * |
| mm_base_modem_get_driver (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return self->priv->driver; |
| } |
| |
| const gchar * |
| mm_base_modem_get_plugin (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); |
| |
| return self->priv->plugin; |
| } |
| |
| guint |
| mm_base_modem_get_vendor_id (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), 0); |
| |
| return self->priv->vendor_id; |
| } |
| |
| guint |
| mm_base_modem_get_product_id (MMBaseModem *self) |
| { |
| g_return_val_if_fail (MM_IS_BASE_MODEM (self), 0); |
| |
| return self->priv->product_id; |
| } |
| |
| /*****************************************************************************/ |
| |
| static gboolean |
| base_modem_invalid_idle (MMBaseModem *self) |
| { |
| /* Ensure the modem is set invalid if we get the modem-wide cancellable |
| * cancelled */ |
| mm_base_modem_set_valid (self, FALSE); |
| g_object_unref (self); |
| return FALSE; |
| } |
| |
| static void |
| base_modem_cancelled (GCancellable *cancellable, |
| MMBaseModem *self) |
| { |
| /* NOTE: Don't call set_valid() directly here, do it in an idle, and ensure |
| * that we pass a valid reference of the modem object as context. */ |
| g_idle_add ((GSourceFunc)base_modem_invalid_idle, g_object_ref (self)); |
| } |
| |
| /*****************************************************************************/ |
| |
| static void |
| mm_base_modem_init (MMBaseModem *self) |
| { |
| /* Initialize private data */ |
| self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), |
| MM_TYPE_BASE_MODEM, |
| MMBaseModemPrivate); |
| |
| /* Setup authorization provider */ |
| self->priv->authp = mm_auth_get_provider (); |
| self->priv->authp_cancellable = g_cancellable_new (); |
| |
| /* Setup modem-wide cancellable */ |
| self->priv->cancellable = g_cancellable_new (); |
| self->priv->invalid_if_cancelled = |
| g_cancellable_connect (self->priv->cancellable, |
| G_CALLBACK (base_modem_cancelled), |
| self, |
| NULL); |
| |
| self->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) |
| { |
| MMBaseModem *self = MM_BASE_MODEM (object); |
| |
| switch (prop_id) { |
| case PROP_VALID: |
| mm_base_modem_set_valid (self, g_value_get_boolean (value)); |
| break; |
| case PROP_MAX_TIMEOUTS: |
| self->priv->max_timeouts = g_value_get_uint (value); |
| break; |
| case PROP_DEVICE: |
| g_free (self->priv->device); |
| self->priv->device = g_value_dup_string (value); |
| break; |
| case PROP_DRIVER: |
| g_free (self->priv->driver); |
| self->priv->driver = g_value_dup_string (value); |
| break; |
| case PROP_PLUGIN: |
| g_free (self->priv->plugin); |
| self->priv->plugin = g_value_dup_string (value); |
| break; |
| case PROP_VENDOR_ID: |
| self->priv->vendor_id = g_value_get_uint (value); |
| break; |
| case PROP_PRODUCT_ID: |
| self->priv->product_id = g_value_get_uint (value); |
| break; |
| case PROP_CONNECTION: |
| g_clear_object (&self->priv->connection); |
| self->priv->connection = g_value_dup_object (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) |
| { |
| MMBaseModem *self = MM_BASE_MODEM (object); |
| |
| switch (prop_id) { |
| case PROP_VALID: |
| g_value_set_boolean (value, self->priv->valid); |
| break; |
| case PROP_MAX_TIMEOUTS: |
| g_value_set_uint (value, self->priv->max_timeouts); |
| break; |
| case PROP_DEVICE: |
| g_value_set_string (value, self->priv->device); |
| break; |
| case PROP_DRIVER: |
| g_value_set_string (value, self->priv->driver); |
| break; |
| case PROP_PLUGIN: |
| g_value_set_string (value, self->priv->plugin); |
| break; |
| case PROP_VENDOR_ID: |
| g_value_set_uint (value, self->priv->vendor_id); |
| break; |
| case PROP_PRODUCT_ID: |
| g_value_set_uint (value, self->priv->product_id); |
| break; |
| case PROP_CONNECTION: |
| g_value_set_object (value, self->priv->connection); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| finalize (GObject *object) |
| { |
| MMBaseModem *self = MM_BASE_MODEM (object); |
| |
| /* TODO |
| * mm_auth_provider_cancel_for_owner (self->priv->authp, object); |
| */ |
| |
| mm_dbg ("Modem (%s) '%s' completely disposed", |
| self->priv->plugin, |
| self->priv->device); |
| |
| g_free (self->priv->device); |
| g_free (self->priv->driver); |
| g_free (self->priv->plugin); |
| |
| G_OBJECT_CLASS (mm_base_modem_parent_class)->finalize (object); |
| } |
| |
| static void |
| dispose (GObject *object) |
| { |
| MMBaseModem *self = MM_BASE_MODEM (object); |
| |
| /* Cancel all ongoing auth requests */ |
| g_cancellable_cancel (self->priv->authp_cancellable); |
| g_clear_object (&self->priv->authp_cancellable); |
| g_clear_object (&self->priv->authp); |
| |
| /* Ensure we cancel any ongoing operation, but before |
| * disconnect our own signal handler, or we'll end up with |
| * another reference of the modem object around. */ |
| g_cancellable_disconnect (self->priv->cancellable, |
| self->priv->invalid_if_cancelled); |
| g_cancellable_cancel (self->priv->cancellable); |
| g_clear_object (&self->priv->cancellable); |
| |
| g_clear_object (&self->priv->primary); |
| g_clear_object (&self->priv->secondary); |
| g_clear_object (&self->priv->data); |
| g_clear_object (&self->priv->qcdm); |
| g_clear_object (&self->priv->gps_control); |
| g_clear_object (&self->priv->gps); |
| |
| if (self->priv->ports) { |
| g_hash_table_destroy (self->priv->ports); |
| self->priv->ports = NULL; |
| } |
| |
| g_clear_object (&self->priv->connection); |
| |
| G_OBJECT_CLASS (mm_base_modem_parent_class)->dispose (object); |
| } |
| |
| static void |
| mm_base_modem_class_init (MMBaseModemClass *klass) |
| { |
| GObjectClass *object_class = G_OBJECT_CLASS (klass); |
| |
| g_type_class_add_private (object_class, sizeof (MMBaseModemPrivate)); |
| |
| /* Virtual methods */ |
| object_class->get_property = get_property; |
| object_class->set_property = set_property; |
| object_class->finalize = finalize; |
| object_class->dispose = dispose; |
| |
| properties[PROP_MAX_TIMEOUTS] = |
| g_param_spec_uint (MM_BASE_MODEM_MAX_TIMEOUTS, |
| "Max timeouts", |
| "Maximum number of consecutive timed out commands sent to " |
| "the modem before disabling it. If 0, this feature is disabled.", |
| 0, G_MAXUINT, 0, |
| G_PARAM_READWRITE); |
| g_object_class_install_property (object_class, PROP_MAX_TIMEOUTS, properties[PROP_MAX_TIMEOUTS]); |
| |
| properties[PROP_VALID] = |
| g_param_spec_boolean (MM_BASE_MODEM_VALID, |
| "Valid", |
| "Whether the modem is to be considered valid or not.", |
| FALSE, |
| G_PARAM_READWRITE); |
| g_object_class_install_property (object_class, PROP_VALID, properties[PROP_VALID]); |
| |
| properties[PROP_DEVICE] = |
| g_param_spec_string (MM_BASE_MODEM_DEVICE, |
| "Device", |
| "Master modem parent device of all the modem's ports", |
| NULL, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); |
| g_object_class_install_property (object_class, PROP_DEVICE, properties[PROP_DEVICE]); |
| |
| properties[PROP_DRIVER] = |
| g_param_spec_string (MM_BASE_MODEM_DRIVER, |
| "Driver", |
| "Kernel driver", |
| NULL, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); |
| g_object_class_install_property (object_class, PROP_DRIVER, properties[PROP_DRIVER]); |
| |
| properties[PROP_PLUGIN] = |
| g_param_spec_string (MM_BASE_MODEM_PLUGIN, |
| "Plugin", |
| "Name of the plugin managing this modem", |
| NULL, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); |
| g_object_class_install_property (object_class, PROP_PLUGIN, properties[PROP_PLUGIN]); |
| |
| properties[PROP_VENDOR_ID] = |
| g_param_spec_uint (MM_BASE_MODEM_VENDOR_ID, |
| "Hardware vendor ID", |
| "Hardware vendor ID. May be unknown for serial devices.", |
| 0, G_MAXUINT, 0, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); |
| g_object_class_install_property (object_class, PROP_VENDOR_ID, properties[PROP_VENDOR_ID]); |
| |
| properties[PROP_PRODUCT_ID] = |
| g_param_spec_uint (MM_BASE_MODEM_PRODUCT_ID, |
| "Hardware product ID", |
| "Hardware product ID. May be unknown for serial devices.", |
| 0, G_MAXUINT, 0, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); |
| g_object_class_install_property (object_class, PROP_PRODUCT_ID, properties[PROP_PRODUCT_ID]); |
| |
| properties[PROP_CONNECTION] = |
| g_param_spec_object (MM_BASE_MODEM_CONNECTION, |
| "Connection", |
| "GDBus connection to the system bus.", |
| G_TYPE_DBUS_CONNECTION, |
| G_PARAM_READWRITE); |
| g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]); |
| } |