/* nm-device.c - * * Copyright 2007 OpenMoko, Inc. * Authored by Chia-I Wu * * Based on libnm-glib by: Dan Williams * Copyright 2005 Red Hat, Inc. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "nm-client.h" /* hack for nm_client_get_device_by_path */ #include "nm-device.h" #include "nm-marshal.h" #include /* strcmp */ /* from diversity-nm-backend.c */ void nm_update_strength_grid(DiversityAp *ap, gint8 strength); G_DEFINE_TYPE(NMDevice, nm_device, G_TYPE_OBJECT) #define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), NM_TYPE_DEVICE, NMDevicePrivate)) typedef struct { DBusGConnection *connection; gchar *path; DBusGProxy *client_proxy; DBusGProxy *device_proxy; gboolean have_ap_list; GHashTable *aps; DiversityAp *active_ap; gboolean disposed; } NMDevicePrivate; enum { STATE_CHANGED, ACCESS_POINT_ADDED, ACCESS_POINT_REMOVED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; static void dispose (GObject *object) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); if (priv->disposed) { G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); return; } priv->disposed = TRUE; g_object_unref (priv->client_proxy); g_object_unref (priv->device_proxy); G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); } static void finalize (GObject *object) { NMDevice *dev = NM_DEVICE(object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); g_free(priv->path); dbus_g_connection_unref(priv->connection); g_hash_table_destroy(priv->aps); g_free(dev->op); g_free((gchar *) dev->iface); g_free((gchar *) dev->udi); g_free(dev->ip4_address); g_free(dev->subnetmask); g_free(dev->broadcast); g_free(dev->hw_addr); g_free(dev->route); g_free(dev->primary_dns); g_free(dev->secondary_dns); g_free(dev->active_network_path); g_strfreev(dev->networks); G_OBJECT_CLASS (nm_device_parent_class)->finalize (object); } static void nm_device_class_init (NMDeviceClass *device_class) { GObjectClass *object_class = G_OBJECT_CLASS (device_class); g_type_class_add_private (device_class, sizeof (NMDevicePrivate)); object_class->dispose = dispose; object_class->finalize = finalize; /* signals */ signals[STATE_CHANGED] = g_signal_new ("state-changed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMDeviceClass, state_changed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[ACCESS_POINT_ADDED] = g_signal_new ("access-point-added", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMDeviceClass, access_point_added), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[ACCESS_POINT_REMOVED] = g_signal_new ("access-point-removed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMDeviceClass, access_point_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); } static void nm_device_init (NMDevice *device) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); priv->disposed = FALSE; priv->have_ap_list = FALSE; priv->aps = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); priv->active_ap = NULL; device->state = NM_DEVICE_STATE_UNKNOWN; device->op = NULL; device->iface = NULL; device->type = 0; device->udi = NULL; device->active = FALSE; device->act_stage = NM_ACT_STAGE_UNKNOWN; device->ip4_address = NULL; device->subnetmask = NULL; device->broadcast = NULL; device->hw_addr = NULL; device->route = NULL; device->primary_dns = NULL; device->secondary_dns = NULL; device->mode = 0; device->strength = -1; device->link_active = FALSE; device->speed = 0; device->caps = NM_DEVICE_CAP_NONE; device->type_caps = NM_DEVICE_CAP_NONE; device->active_network_path = NULL; device->networks = NULL; } static void client_device_set_state(NMDevice *dev, const gchar *path, NMState state) { NMDevicePrivate *priv; if (dev != nm_client_get_device_by_path(dev->client, path)) return; priv = NM_DEVICE_GET_PRIVATE(dev); priv->active_ap = NULL; if (dev->state != state) { dev->state = state; g_signal_emit(dev, signals[STATE_CHANGED], 0, state); } } static void client_device_prepare_proxy(DBusGProxy *proxy, const gchar *path, gpointer user_data) { client_device_set_state(user_data, path, NM_DEVICE_STATE_PREPARE); } static void client_device_disconnected_proxy(DBusGProxy *proxy, const gchar *path, gpointer user_data) { client_device_set_state(user_data, path, NM_DEVICE_STATE_DISCONNECTED); } static void client_device_activated_proxy(DBusGProxy *proxy, const gchar *path, const gchar *essid, gpointer user_data) { client_device_set_state(user_data, path, NM_DEVICE_STATE_ACTIVATED); } static guint32 caps_to_flags(gint caps) { guint32 flags = 0; if (caps & NM_802_11_CAP_CIPHER_WEP40) flags |= DIVERSITY_AP_FLAG_PAIR_WEP40; if (caps & NM_802_11_CAP_CIPHER_WEP104) flags |= DIVERSITY_AP_FLAG_PAIR_WEP104; if (caps & NM_802_11_CAP_CIPHER_TKIP) flags |= DIVERSITY_AP_FLAG_PAIR_TKIP; if (caps & NM_802_11_CAP_CIPHER_CCMP) flags |= DIVERSITY_AP_FLAG_PAIR_CCMP; if (caps & NM_802_11_CAP_KEY_MGMT_802_1X) flags |= DIVERSITY_AP_FLAG_KEY_MGMT_802_1X; if (caps & NM_802_11_CAP_KEY_MGMT_PSK) flags |= DIVERSITY_AP_FLAG_KEY_MGMT_PSK; return flags; } static DiversityAp *nm_device_ap_new(NMDevice *dev, const char *path) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(dev); gchar *op = NULL; gchar *essid = NULL; gchar *hw_addr = NULL; gint strength = -1; gdouble freq = 0; gint32 rate = 0; gint capabilities = NM_802_11_CAP_NONE; guint32 flags; guint mode = 0; gboolean broadcast = TRUE; DBusGProxy *proxy; DiversityAp *ap; GError *error = NULL; proxy = dbus_g_proxy_new_for_name(priv->connection, NM_DBUS_SERVICE, path, NM_DBUS_INTERFACE_DEVICES); if (!dbus_g_proxy_call(proxy, "getProperties", &error, G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH, &op, G_TYPE_STRING, &essid, G_TYPE_STRING, &hw_addr, G_TYPE_INT, &strength, G_TYPE_DOUBLE, &freq, G_TYPE_INT, &rate, G_TYPE_INT, &mode, G_TYPE_INT, &capabilities, G_TYPE_BOOLEAN, &broadcast, G_TYPE_INVALID)) { g_warning("failed to get ap properties: %s", error->message); g_error_free(error); g_object_unref(proxy); return NULL; } /* XXX monitor ap changes */ g_object_unref(proxy); flags = caps_to_flags(capabilities); ap = g_object_new(DIVERSITY_TYPE_AP, "ssid", essid, "hw_address", hw_addr, "strength", strength, "frequency", (guint32) (freq / 1000000.), "rate", rate, "mode", mode, "flags", (flags) ? DIVERSITY_AP_FLAG_PRIVACY : 0, "wpa_flags", flags, "rsn_flags", flags, NULL); g_object_ref(ap); g_free(op); g_free(essid); g_free(hw_addr); return ap; } static DiversityAp * get_access_point(NMDevice *device, const gchar *path, gboolean create_if_not_found) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(device); DiversityAp *ap; ap = g_hash_table_lookup(priv->aps, path); if (!ap && create_if_not_found) { ap = nm_device_ap_new(device, path); g_hash_table_insert(priv->aps, g_strdup(path), ap); } return ap; } static void access_point_added_proxy(DBusGProxy *proxy, const gchar *dev_path, const gchar *ap_path, gpointer user_data) { NMDevice *dev = NM_DEVICE(user_data); DiversityAp *ap; if (dev != nm_client_get_device_by_path(dev->client, dev_path)) return; ap = get_access_point(dev, ap_path, TRUE); if (ap) g_signal_emit(dev, signals[ACCESS_POINT_ADDED], 0, ap); } static void access_point_removed_proxy(DBusGProxy *proxy, const gchar *dev_path, const gchar *ap_path, gpointer user_data) { NMDevice *dev = NM_DEVICE(user_data); DiversityAp *ap; if (dev != nm_client_get_device_by_path(dev->client, dev_path)) return; ap = get_access_point(dev, ap_path, FALSE); if (ap) { g_signal_emit(dev, signals[ACCESS_POINT_REMOVED], 0, ap); g_hash_table_remove(NM_DEVICE_GET_PRIVATE(dev)->aps, ap_path); } } static void access_point_strength_proxy(DBusGProxy *proxy, const gchar *dev_path, const gchar *ap_path, gint strength, gpointer user_data) { NMDevice *dev = NM_DEVICE(user_data); DiversityAp *ap; if (dev != nm_client_get_device_by_path(dev->client, dev_path)) return; ap = get_access_point(dev, ap_path, FALSE); if (ap) nm_update_strength_grid(ap, strength); } static void nm_device_get_properties(NMDevice *dev) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(dev); GError *error = NULL; if (!dbus_g_proxy_call(priv->device_proxy, "getProperties", &error, G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH, &dev->op, G_TYPE_STRING, &dev->iface, G_TYPE_UINT, &dev->type, G_TYPE_STRING, &dev->udi, G_TYPE_BOOLEAN, &dev->active, G_TYPE_UINT, &dev->act_stage, G_TYPE_STRING, &dev->ip4_address, G_TYPE_STRING, &dev->subnetmask, G_TYPE_STRING, &dev->broadcast, G_TYPE_STRING, &dev->hw_addr, G_TYPE_STRING, &dev->route, G_TYPE_STRING, &dev->primary_dns, G_TYPE_STRING, &dev->secondary_dns, G_TYPE_INT, &dev->mode, G_TYPE_INT, &dev->strength, G_TYPE_BOOLEAN, &dev->link_active, G_TYPE_INT, &dev->speed, G_TYPE_UINT, &dev->caps, G_TYPE_UINT, &dev->type_caps, G_TYPE_STRING, &dev->active_network_path, G_TYPE_STRV, &dev->networks, G_TYPE_INVALID)) { g_warning("failed to get device properties: %s", error->message); g_error_free(error); } } NMDevice *nm_device_new(DBusGConnection *connection, const char *path) { NMDevice *dev; NMDevicePrivate *priv; dev = g_object_new(NM_TYPE_DEVICE, NULL); priv = NM_DEVICE_GET_PRIVATE(dev); priv->connection = dbus_g_connection_ref(connection); priv->path = g_strdup(path); priv->client_proxy = dbus_g_proxy_new_for_name(connection, NM_DBUS_SERVICE, NM_DBUS_PATH, NM_DBUS_INTERFACE); priv->device_proxy = dbus_g_proxy_new_for_name(priv->connection, NM_DBUS_SERVICE, priv->path, NM_DBUS_INTERFACE_DEVICES); dbus_g_object_register_marshaller( nm_marshal_VOID__STRING_STRING, G_TYPE_NONE, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_INVALID); dbus_g_object_register_marshaller( nm_marshal_VOID__STRING_STRING, G_TYPE_NONE, DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); dbus_g_object_register_marshaller( nm_marshal_VOID__STRING_STRING_INT, G_TYPE_NONE, DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INT, G_TYPE_INVALID); dbus_g_proxy_add_signal(priv->client_proxy, "DeviceActivating", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); dbus_g_proxy_connect_signal(priv->client_proxy, "DeviceActivating", G_CALLBACK(client_device_prepare_proxy), dev, NULL); dbus_g_proxy_add_signal(priv->client_proxy, "DeviceNoLongerActive", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); dbus_g_proxy_connect_signal(priv->client_proxy, "DeviceNoLongerActive", G_CALLBACK(client_device_disconnected_proxy), dev, NULL); dbus_g_proxy_add_signal(priv->client_proxy, "DeviceNowActive", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_INVALID); dbus_g_proxy_connect_signal(priv->client_proxy, "DeviceNowActive", G_CALLBACK(client_device_activated_proxy), dev, NULL); dbus_g_proxy_add_signal(priv->client_proxy, "WirelessNetworkAppeared", DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); dbus_g_proxy_connect_signal(priv->client_proxy, "WirelessNetworkAppeared", G_CALLBACK(access_point_added_proxy), dev, NULL); dbus_g_proxy_add_signal(priv->client_proxy, "WirelessNetworkDisappeared", DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); dbus_g_proxy_connect_signal(priv->client_proxy, "WirelessNetworkDisappeared", G_CALLBACK(access_point_removed_proxy), dev, NULL); dbus_g_proxy_add_signal(priv->client_proxy, "WirelessNetworkStrengthChanged", DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INT, G_TYPE_INVALID); dbus_g_proxy_connect_signal(priv->client_proxy, "WirelessNetworkStrengthChanged", G_CALLBACK(access_point_strength_proxy), dev, NULL); nm_device_get_properties(dev); return dev; } static void access_points_to_slist (gpointer key, gpointer value, gpointer user_data) { GSList **list = (GSList **) user_data; *list = g_slist_prepend (*list, value); } DiversityAp *nm_device_get_active_access_point(NMDevice *dev) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(dev); GError *error = NULL; gchar *ap_path; if (dev->state < NM_DEVICE_STATE_PREPARE && dev->state > NM_DEVICE_STATE_ACTIVATED) return NULL; if (!priv->active_ap) { if (!dbus_g_proxy_call(priv->device_proxy, "getActiveNetwork", &error, G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH, &ap_path, G_TYPE_INVALID)) { g_print("failed to get active network: %s\n", error->message); g_error_free(error); } priv->active_ap = g_hash_table_lookup(priv->aps, ap_path); g_free(ap_path); } return priv->active_ap; } GSList * nm_device_get_access_points (NMDevice *dev) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(dev); GSList *list = NULL; gchar **path; if (dev->type != DEVICE_TYPE_802_11_WIRELESS) return NULL; if (priv->have_ap_list) { g_hash_table_foreach(priv->aps, access_points_to_slist, &list); return list; } path = dev->networks; while (*path) { DiversityAp *ap; ap = get_access_point(dev, *path, TRUE); if (ap) list = g_slist_prepend(list, ap); path++; } g_strfreev(dev->networks); dev->networks = NULL; priv->have_ap_list = TRUE; return list; } void nm_device_deactivate (NMDevice *dev) { } typedef struct { gpointer match; gpointer key; gpointer val; } HashCrawler; static gboolean _g_hash_table_crawl(gpointer key, gpointer value, gpointer user_data) { HashCrawler *crawler = user_data; if (value != crawler->match) return FALSE; crawler->key = key; crawler->val = value; return TRUE; } void nm_device_replace_ap(NMDevice *dev, DiversityAp *src, DiversityAp *dst) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(dev); HashCrawler crawler; crawler.match = src; crawler.key = NULL; crawler.val = NULL; g_hash_table_find(priv->aps, _g_hash_table_crawl, &crawler); if (!crawler.val) { g_warning("try to replace non-existing ap %p", src); return; } g_object_ref(dst); g_hash_table_insert(priv->aps, g_strdup(crawler.key), dst); }