1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Copyright (C) 2009 - 2013 Red Hat, Inc.
21 #include "nm-default.h"
24 #include <gudev/gudev.h>
26 #include "nm-rfkill-manager.h"
31 /* Authoritative rfkill state (RFKILL_* enum) */
32 RfKillState rfkill_states[RFKILL_TYPE_MAX];
35 } NMRfkillManagerPrivate;
37 #define NM_RFKILL_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_RFKILL_MANAGER, NMRfkillManagerPrivate))
39 G_DEFINE_TYPE (NMRfkillManager, nm_rfkill_manager, G_TYPE_OBJECT)
47 static guint signals[LAST_SIGNAL] = { 0 };
61 nm_rfkill_manager_get_rfkill_state (NMRfkillManager *self, RfKillType rtype)
63 g_return_val_if_fail (self != NULL, RFKILL_UNBLOCKED);
64 g_return_val_if_fail (rtype < RFKILL_TYPE_MAX, RFKILL_UNBLOCKED);
66 return NM_RFKILL_MANAGER_GET_PRIVATE (self)->rfkill_states[rtype];
70 rfkill_type_to_desc (RfKillType rtype)
82 rfkill_state_to_desc (RfKillState rstate)
87 return "soft-blocked";
89 return "hard-blocked";
94 killswitch_new (GUdevDevice *device, RfKillType rtype)
97 GUdevDevice *parent = NULL, *grandparent = NULL;
98 const char *driver, *subsys, *parent_subsys = NULL;
100 ks = g_malloc0 (sizeof (Killswitch));
101 ks->name = g_strdup (g_udev_device_get_name (device));
102 ks->seqnum = g_udev_device_get_seqnum (device);
103 ks->path = g_strdup (g_udev_device_get_sysfs_path (device));
106 driver = g_udev_device_get_property (device, "DRIVER");
107 subsys = g_udev_device_get_subsystem (device);
109 /* Check parent for various attributes */
110 parent = g_udev_device_get_parent (device);
112 parent_subsys = g_udev_device_get_subsystem (parent);
114 driver = g_udev_device_get_property (parent, "DRIVER");
116 /* Sigh; try the grandparent */
117 grandparent = g_udev_device_get_parent (parent);
119 driver = g_udev_device_get_property (grandparent, "DRIVER");
124 driver = "(unknown)";
125 ks->driver = g_strdup (driver);
127 if ( g_strcmp0 (subsys, "platform") == 0
128 || g_strcmp0 (parent_subsys, "platform") == 0
129 || g_strcmp0 (subsys, "acpi") == 0
130 || g_strcmp0 (parent_subsys, "acpi") == 0)
134 g_object_unref (grandparent);
136 g_object_unref (parent);
141 killswitch_destroy (Killswitch *ks)
143 g_return_if_fail (ks != NULL);
148 memset (ks, 0, sizeof (Killswitch));
153 nm_rfkill_manager_new (void)
155 return NM_RFKILL_MANAGER (g_object_new (NM_TYPE_RFKILL_MANAGER, NULL));
159 sysfs_state_to_nm_state (gint sysfs_state)
161 switch (sysfs_state) {
163 return RFKILL_SOFT_BLOCKED;
165 return RFKILL_UNBLOCKED;
167 return RFKILL_HARD_BLOCKED;
169 nm_log_warn (LOGD_RFKILL, "unhandled rfkill state %d", sysfs_state);
172 return RFKILL_UNBLOCKED;
176 recheck_killswitches (NMRfkillManager *self)
178 NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
180 RfKillState poll_states[RFKILL_TYPE_MAX];
181 RfKillState platform_states[RFKILL_TYPE_MAX];
182 gboolean platform_checked[RFKILL_TYPE_MAX];
185 /* Default state is unblocked */
186 for (i = 0; i < RFKILL_TYPE_MAX; i++) {
187 poll_states[i] = RFKILL_UNBLOCKED;
188 platform_states[i] = RFKILL_UNBLOCKED;
189 platform_checked[i] = FALSE;
192 /* Poll the states of all killswitches */
193 for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) {
194 Killswitch *ks = iter->data;
196 RfKillState dev_state;
199 device = g_udev_client_query_by_subsystem_and_name (priv->client, "rfkill", ks->name);
201 sysfs_state = g_udev_device_get_property_as_int (device, "RFKILL_STATE");
202 dev_state = sysfs_state_to_nm_state (sysfs_state);
204 nm_log_dbg (LOGD_RFKILL, "%s rfkill%s switch %s state now %d/%u",
205 rfkill_type_to_desc (ks->rtype),
206 ks->platform ? " platform" : "",
211 if (ks->platform == FALSE) {
212 if (dev_state > poll_states[ks->rtype])
213 poll_states[ks->rtype] = dev_state;
215 platform_checked[ks->rtype] = TRUE;
216 if (dev_state > platform_states[ks->rtype])
217 platform_states[ks->rtype] = dev_state;
219 g_object_unref (device);
223 /* Log and emit change signal for final rfkill states */
224 for (i = 0; i < RFKILL_TYPE_MAX; i++) {
225 if (platform_checked[i] == TRUE) {
226 /* blocked platform switch state overrides device state, otherwise
227 * let the device state stand. (bgo #655773)
229 if (platform_states[i] != RFKILL_UNBLOCKED)
230 poll_states[i] = platform_states[i];
233 if (poll_states[i] != priv->rfkill_states[i]) {
234 nm_log_dbg (LOGD_RFKILL, "%s rfkill state now '%s'",
235 rfkill_type_to_desc (i),
236 rfkill_state_to_desc (poll_states[i]));
238 priv->rfkill_states[i] = poll_states[i];
239 g_signal_emit (self, signals[RFKILL_CHANGED], 0, i, priv->rfkill_states[i]);
245 killswitch_find_by_name (NMRfkillManager *self, const char *name)
247 NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
250 g_return_val_if_fail (name != NULL, NULL);
252 for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) {
253 Killswitch *candidate = iter->data;
255 if (!strcmp (name, candidate->name))
261 static const RfKillType
262 rfkill_type_to_enum (const char *str)
264 g_return_val_if_fail (str != NULL, RFKILL_TYPE_UNKNOWN);
266 if (!strcmp (str, "wlan"))
267 return RFKILL_TYPE_WLAN;
268 else if (!strcmp (str, "wwan"))
269 return RFKILL_TYPE_WWAN;
271 return RFKILL_TYPE_UNKNOWN;
275 add_one_killswitch (NMRfkillManager *self, GUdevDevice *device)
277 NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
278 const char *str_type;
282 str_type = g_udev_device_get_property (device, "RFKILL_TYPE");
283 rtype = rfkill_type_to_enum (str_type);
284 if (rtype == RFKILL_TYPE_UNKNOWN)
287 ks = killswitch_new (device, rtype);
288 priv->killswitches = g_slist_prepend (priv->killswitches, ks);
290 nm_log_info (LOGD_RFKILL, "%s: found %s radio killswitch (at %s) (%sdriver %s)",
292 rfkill_type_to_desc (rtype),
294 ks->platform ? "platform " : "",
295 ks->driver ? ks->driver : "<unknown>");
299 rfkill_add (NMRfkillManager *self, GUdevDevice *device)
303 g_return_if_fail (device != NULL);
304 name = g_udev_device_get_name (device);
305 g_return_if_fail (name != NULL);
307 if (!killswitch_find_by_name (self, name))
308 add_one_killswitch (self, device);
312 rfkill_remove (NMRfkillManager *self,
315 NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
319 g_return_if_fail (device != NULL);
320 name = g_udev_device_get_name (device);
321 g_return_if_fail (name != NULL);
323 for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) {
324 Killswitch *ks = iter->data;
326 if (!strcmp (ks->name, name)) {
327 nm_log_info (LOGD_RFKILL, "radio killswitch %s disappeared", ks->path);
328 priv->killswitches = g_slist_remove (priv->killswitches, ks);
329 killswitch_destroy (ks);
336 handle_uevent (GUdevClient *client,
341 NMRfkillManager *self = NM_RFKILL_MANAGER (user_data);
344 g_return_if_fail (action != NULL);
347 subsys = g_udev_device_get_subsystem (device);
348 g_return_if_fail (!g_strcmp0 (subsys, "rfkill"));
350 nm_log_dbg (LOGD_HW, "udev rfkill event: action '%s' device '%s'",
351 action, g_udev_device_get_name (device));
353 if (!strcmp (action, "add"))
354 rfkill_add (self, device);
355 else if (!strcmp (action, "remove"))
356 rfkill_remove (self, device);
358 recheck_killswitches (self);
362 nm_rfkill_manager_init (NMRfkillManager *self)
364 NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
365 const char *subsys[] = { "rfkill", NULL };
366 GList *switches, *iter;
369 for (i = 0; i < RFKILL_TYPE_MAX; i++)
370 priv->rfkill_states[i] = RFKILL_UNBLOCKED;
372 priv->client = g_udev_client_new (subsys);
373 g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self);
375 switches = g_udev_client_query_by_subsystem (priv->client, "rfkill");
376 for (iter = switches; iter; iter = g_list_next (iter)) {
377 add_one_killswitch (self, G_UDEV_DEVICE (iter->data));
378 g_object_unref (G_UDEV_DEVICE (iter->data));
380 g_list_free (switches);
382 recheck_killswitches (self);
386 dispose (GObject *object)
388 NMRfkillManager *self = NM_RFKILL_MANAGER (object);
389 NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
391 g_clear_object (&priv->client);
393 if (priv->killswitches) {
394 g_slist_free_full (priv->killswitches, (GDestroyNotify) killswitch_destroy);
395 priv->killswitches = NULL;
398 G_OBJECT_CLASS (nm_rfkill_manager_parent_class)->dispose (object);
402 nm_rfkill_manager_class_init (NMRfkillManagerClass *klass)
404 GObjectClass *object_class = G_OBJECT_CLASS (klass);
406 g_type_class_add_private (klass, sizeof (NMRfkillManagerPrivate));
408 /* virtual methods */
409 object_class->dispose = dispose;
412 signals[RFKILL_CHANGED] =
413 g_signal_new ("rfkill-changed",
414 G_OBJECT_CLASS_TYPE (object_class),
416 G_STRUCT_OFFSET (NMRfkillManagerClass, rfkill_changed),
418 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);