static void remove_modem (MMManager *manager, MMBaseModem *modem) { gchar *path; gchar *device; device = g_strdup (mm_base_modem_get_device (modem)); path = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (modem))); /* If we get DBus object path, modem was exported */ if (path) { g_dbus_object_manager_server_unexport (manager->priv->object_manager, path); g_object_set (modem, MM_BASE_MODEM_CONNECTION, NULL, NULL); mm_dbg ("Unexported modem '%s' from path '%s'", device, path); g_free (path); } else { mm_dbg ("Removing modem '%s', which wasn't exported yet", device); } /* Run dispose before unref-ing, in order to cleanup the SIM object, * if any (which also holds a reference to the modem object) */ g_object_run_dispose (G_OBJECT (modem)); g_hash_table_remove (manager->priv->modems, device); g_free (device); }
static void device_removed (MMManager *manager, GUdevDevice *device) { MMBaseModem *modem; const char *subsys, *name; g_return_if_fail (device != NULL); subsys = g_udev_device_get_subsystem (device); name = g_udev_device_get_name (device); /* Ensure cached port probe infos get removed when the port is gone */ mm_port_probe_cache_remove (device); if (strcmp (subsys, "usb") != 0) { /* find_modem_for_port handles tty and net removal */ modem = find_modem_for_port (manager, subsys, name); if (modem) { mm_info ("(%s/%s): released by modem %s", subsys, name, mm_base_modem_get_device (modem)); mm_base_modem_release_port (modem, subsys, name); return; } } else { /* This case is designed to handle the case where, at least with kernel 2.6.31, unplugging * an in-use ttyACMx device results in udev generating remove events for the usb, but the * ttyACMx device (subsystem tty) is not removed, since it was in-use. So if we have not * found a modem for the port (above), we're going to look here to see if we have a modem * associated with the newly removed device. If so, we'll remove the modem, since the * device has been removed. That way, if the device is reinserted later, we'll go through * the process of exporting it. */ const char *sysfs_path = g_udev_device_get_sysfs_path (device); modem = find_modem_for_device (manager, sysfs_path); if (modem) { mm_dbg ("Removing modem claimed by removed device %s", sysfs_path); remove_modem (manager, modem); return; } } /* Maybe a plugin is checking whether or not the port is supported. * TODO: Cancel every possible supports check in this port. */ }
static void grab_port (MMManager *manager, MMPlugin *plugin, GUdevDevice *device, GUdevDevice *physical_device) { GError *error = NULL; MMBaseModem *modem; MMBaseModem *existing; existing = g_hash_table_lookup (manager->priv->modems, g_udev_device_get_sysfs_path (physical_device)); /* While grabbing the first port, modem will get created */ modem = mm_plugin_grab_port (plugin, g_udev_device_get_subsystem (device), g_udev_device_get_name (device), existing, &error); if (!modem) { mm_warn ("plugin '%s' claimed to support %s/%s but couldn't: (%d) %s", mm_plugin_get_name (plugin), g_udev_device_get_subsystem (device), g_udev_device_get_name (device), error ? error->code : -1, (error && error->message) ? error->message : "(unknown)"); g_clear_error (&error); if (existing) check_export_modem (manager, existing); return; } mm_info ("(%s): modem %s claimed port %s", mm_plugin_get_name (plugin), mm_base_modem_get_device (modem), g_udev_device_get_name (device)); if (existing) { g_assert (existing == modem); check_export_modem (manager, modem); } else { /* If the modem was just created, store it */ add_modem (manager, modem, plugin); } }
static MMBaseModem * find_modem_for_device (MMManager *manager, const gchar *device) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, manager->priv->modems); while (g_hash_table_iter_next (&iter, &key, &value)) { MMBaseModem *candidate = MM_BASE_MODEM (value); if (g_str_equal (device, mm_base_modem_get_device (candidate))) return candidate; } return NULL; }
static void add_modem (MMManager *manager, MMBaseModem *modem, MMPlugin *plugin) { const gchar *device; device = mm_base_modem_get_device (modem); if (!g_hash_table_lookup (manager->priv->modems, device)) { mm_dbg ("Added modem %s", device); g_hash_table_insert (manager->priv->modems, g_strdup (device), modem); g_object_set_data (G_OBJECT (modem), MANAGER_PLUGIN_TAG, plugin); g_signal_connect (modem, "notify::" MM_BASE_MODEM_VALID, G_CALLBACK (modem_valid), manager); } check_export_modem (manager, modem); }
static void debug_modem_info (MMManager *self, MMBaseModem *modem, const gchar *path) { GUdevDevice *physdev; const gchar *subsys; physdev = g_udev_client_query_by_sysfs_path (self->priv->udev, mm_base_modem_get_device (modem)); subsys = (physdev ? g_udev_device_get_subsystem (physdev) : NULL); mm_dbg ("(%s): '%s' modem, VID 0x%04X PID 0x%04X (%s)", path, mm_base_modem_get_plugin (modem), (mm_base_modem_get_vendor_id (modem) & 0xFFFF), (mm_base_modem_get_product_id (modem) & 0xFFFF), subsys ? subsys : "unknown"); if (physdev) g_object_unref (physdev); }
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', 'net' and 'cdc-wdm' ports */ if (!g_str_equal (subsys, "net") && !g_str_equal (subsys, "tty") && !(g_str_has_prefix (subsys, "usb") && g_str_has_prefix (name, "cdc-wdm"))) { 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)); } /* QMI ports... */ else if (g_str_has_prefix (subsys, "usb") && g_str_has_prefix (name, "cdc-wdm")) { #if defined WITH_QMI port = MM_PORT (mm_qmi_port_new (name)); #else g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot add port '%s/%s', QMI support not available", subsys, name); g_free (key); return FALSE; #endif } else /* We already filter out before all non-tty, non-net, non-qmi 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; }
static void check_export_modem (MMManager *self, MMBaseModem *modem) { GError *error = NULL; static guint32 id = 0; const gchar *modem_physdev; const gchar *name; const gchar *subsys; gchar *path; /* A modem is only exported to D-Bus when both of the following are true: * * 1) the modem is valid * 2) all ports the modem provides have either been grabbed or are * unsupported by any plugin * * This ensures that all the modem's ports are completely ready before * any clients can do anything with it. * * FIXME: if udev or the kernel are really slow giving us ports, there's a * chance that a port could show up after the modem is already created and * all other ports are already handled. That chance is very small though. */ modem_physdev = mm_base_modem_get_device (modem); g_assert (modem_physdev); /* Check for ports that are in the process of being interrogated by plugins */ if (mm_plugin_manager_is_finding_device_support (self->priv->plugin_manager, modem_physdev, &subsys, &name)) { mm_dbg ("(%s/%s): outstanding support task prevents export of '%s'", subsys, name, modem_physdev); return; } /* Plugin manager is not trying to find more ports supported by this device, * so we can organize the ports now (if not done already). */ if (!mm_base_modem_organize_ports (modem, &error)) { /* If the ports were not properly organized, the modem will be marked as * invalid and therefore removed */ mm_err ("Failed to organize modem ports: '%s'", error->message); g_error_free (error); remove_modem (self, modem); return; } /* If modem not yet valid (not fully initialized), don't export it */ if (!mm_base_modem_get_valid (modem)) return; /* Don't export already exported modems */ g_object_get (modem, "g-object-path", &path, NULL); if (path) { g_free (path); mm_dbg ("Modem '%s' already exported", modem_physdev); return; } /* No outstanding port tasks, so if the modem is valid we can export it */ path = g_strdup_printf (MM_DBUS_MODEM_PREFIX "/%d", id++); g_object_set (modem, "g-object-path", path, MM_BASE_MODEM_CONNECTION, self->priv->connection, NULL); g_dbus_object_manager_server_export (self->priv->object_manager, G_DBUS_OBJECT_SKELETON (modem)); mm_dbg ("Exported modem '%s' at path '%s'", modem_physdev, path); /* Once connected, dump additional debug info about the modem */ debug_modem_info (self, modem, path); g_free (path); }