NetworkManagerDevice.c 103 KB
Newer Older
Dan Williams's avatar
Dan Williams committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* NetworkManager -- Network link manager
 *
 * Dan Williams <dcbw@redhat.com>
 *
 * 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.
 *
 * (C) Copyright 2004 Red Hat, Inc.
 */

#include <errno.h>
#include <glib.h>
#include <dbus/dbus-glib.h>
25
#include <libhal.h>
Dan Williams's avatar
Dan Williams committed
26
#include <iwlib.h>
27
28
#include <signal.h>
#include <string.h>
Dan Williams's avatar
Dan Williams committed
29
30

#include "NetworkManager.h"
31
#include "NetworkManagerMain.h"
Dan Williams's avatar
Dan Williams committed
32
#include "NetworkManagerDevice.h"
33
#include "NetworkManagerDevicePrivate.h"
Dan Williams's avatar
Dan Williams committed
34
#include "NetworkManagerUtils.h"
35
36
37
#include "NetworkManagerDbus.h"
#include "NetworkManagerWireless.h"
#include "NetworkManagerPolicy.h"
38
#include "NetworkManagerAPList.h"
39
40
#include "NetworkManagerSystem.h"
#include "NetworkManagerDHCP.h"
Dan Williams's avatar
Dan Williams committed
41

42
43
#include "nm-utils.h"

44
/* Local static prototypes */
45
46
static gpointer nm_device_worker (gpointer user_data);
static gboolean nm_device_activate (gpointer user_data);
47
static gboolean nm_device_activation_configure_ip (NMDevice *dev, gboolean do_only_autoip);
48
static gboolean nm_device_wireless_scan (gpointer user_data);
49
50
static gboolean supports_mii_carrier_detect (NMDevice *dev);
static gboolean supports_ethtool_carrier_detect (NMDevice *dev);
51
52
static gboolean nm_device_bring_up_wait (NMDevice *dev, gboolean cancelable);
static gboolean nm_device_activation_handle_cancel (NMDevice *dev);
53
54
55
56
57
58

typedef struct
{
	NMDevice					*dev;
	struct wireless_scan_head	 scan_head;
} NMWirelessScanResults;
59
60
61
62
63
64


/******************************************************/


/******************************************************/
Dan Williams's avatar
Dan Williams committed
65
66

/*
67
 * nm_device_test_wireless_extensions
Dan Williams's avatar
Dan Williams committed
68
69
70
71
 *
 * Test whether a given device is a wireless one or not.
 *
 */
72
static gboolean nm_device_test_wireless_extensions (NMDevice *dev)
Dan Williams's avatar
Dan Williams committed
73
{
74
	int		sk;
75
	int		err;
76
	char		ioctl_buf[64];
Dan Williams's avatar
Dan Williams committed
77
78
	
	g_return_val_if_fail (dev != NULL, FALSE);
79
80
81
82
83
84
85

	/* We obviously cannot probe test devices (since they don't
	 * actually exist in hardware).
	 */
	if (dev->test_device)
		return (FALSE);

86
87
88
89
	ioctl_buf[63] = 0;
	strncpy(ioctl_buf, nm_device_get_iface(dev), 63);

	sk = iw_sockets_open ();
90
	err = ioctl(sk, SIOCGIWNAME, ioctl_buf);
91
	close (sk);
92
	return (err == 0);
Dan Williams's avatar
Dan Williams committed
93
94
95
96
97
98
99
100
101
102
103
}


/*
 * nm_device_supports_wireless_scan
 *
 * Test whether a given device is a wireless one or not.
 *
 */
static gboolean nm_device_supports_wireless_scan (NMDevice *dev)
{
104
105
106
107
	int				sk;
	int				err;
	gboolean			can_scan = TRUE;
	wireless_scan_head	scan_data;
Dan Williams's avatar
Dan Williams committed
108
109
	
	g_return_val_if_fail (dev != NULL, FALSE);
110
111
112
113
114
	g_return_val_if_fail (dev->type == DEVICE_TYPE_WIRELESS_ETHERNET, FALSE);

	/* A test wireless device can always scan (we generate fake scan data for it) */
	if (dev->test_device)
		return (TRUE);
Dan Williams's avatar
Dan Williams committed
115
	
116
	sk = iw_sockets_open ();
117
	err = iw_scan (sk, (char *)nm_device_get_iface (dev), WIRELESS_EXT, &scan_data);
Dan Williams's avatar
Dan Williams committed
118
	nm_dispose_scan_results (scan_data.result);
119
	if ((err == -1) && (errno == EOPNOTSUPP))
Dan Williams's avatar
Dan Williams committed
120
		can_scan = FALSE;
121
	close (sk);
Dan Williams's avatar
Dan Williams committed
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
	return (can_scan);
}


/*
 * nm_get_device_by_udi
 *
 * Search through the device list for a device with a given UDI.
 *
 * NOTE: the caller MUST hold the device list mutex already to make
 * this routine thread-safe.
 *
 */
NMDevice *nm_get_device_by_udi (NMData *data, const char *udi)
{
	NMDevice	*dev = NULL;
138
	GSList	*elt;
Dan Williams's avatar
Dan Williams committed
139
140
141
142
	
	g_return_val_if_fail (data != NULL, NULL);
	g_return_val_if_fail (udi  != NULL, NULL);

143
	for (elt = data->dev_list; elt; elt = g_slist_next (elt))
Dan Williams's avatar
Dan Williams committed
144
	{
145
		dev = (NMDevice *)(elt->data);
Dan Williams's avatar
Dan Williams committed
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
		if (dev)
		{
			if (nm_null_safe_strcmp (nm_device_get_udi (dev), udi) == 0)
				break;
		}
	}

	return (dev);
}


/*
 * nm_get_device_by_iface
 *
 * Search through the device list for a device with a given iface.
 *
 * NOTE: the caller MUST hold the device list mutex already to make
 * this routine thread-safe.
 *
 */
NMDevice *nm_get_device_by_iface (NMData *data, const char *iface)
{
	NMDevice	*iter_dev = NULL;
	NMDevice	*found_dev = NULL;
170
	GSList	*elt;
Dan Williams's avatar
Dan Williams committed
171
172
173
174
	
	g_return_val_if_fail (data  != NULL, NULL);
	g_return_val_if_fail (iface != NULL, NULL);

175
	for (elt = data->dev_list; elt; elt = g_slist_next (elt))
Dan Williams's avatar
Dan Williams committed
176
	{
177
		iter_dev = (NMDevice *)(elt->data);
Dan Williams's avatar
Dan Williams committed
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
		if (iter_dev)
		{
			if (nm_null_safe_strcmp (nm_device_get_iface (iter_dev), iface) == 0)
			{
				found_dev = iter_dev;
				break;
			}
		}
	}

	return (found_dev);
}


/*****************************************************************************/
193
/* NMDevice object routines                                                  */
Dan Williams's avatar
Dan Williams committed
194
195
196
197
198
/*****************************************************************************/

/*
 * nm_device_new
 *
199
200
201
 * Creates and initializes the structure representation of an NM device.  For test
 * devices, a device type other than DEVICE_TYPE_DONT_KNOW must be specified, this
 * argument is ignored for real hardware devices since they are auto-probed.
Dan Williams's avatar
Dan Williams committed
202
203
 *
 */
204
NMDevice *nm_device_new (const char *iface, const char *udi, gboolean test_dev, NMDeviceType test_dev_type, NMData *app_data)
Dan Williams's avatar
Dan Williams committed
205
206
{
	NMDevice	*dev;
207
	GError	*error = NULL;
Dan Williams's avatar
Dan Williams committed
208
209

	g_return_val_if_fail (iface != NULL, NULL);
210
	g_return_val_if_fail (strlen (iface) > 0, NULL);
211
	g_return_val_if_fail (app_data != NULL, NULL);
212

213
214
215
216
217
218
219
	/* Test devices must have a valid type specified */
	if (test_dev && !(test_dev_type != DEVICE_TYPE_DONT_KNOW))
		return (NULL);

	/* Another check to make sure we don't create a test device unless
	 * test devices were enabled on the command line.
	 */
220
	if (!app_data->enable_test_devices && test_dev)
221
	{
222
		nm_warning ("attempt to create a test device, but test devices were not enabled "
223
			    "on the command line.  Will not create the device.");
224
225
226
		return (NULL);
	}

227
	dev = g_malloc0 (sizeof (NMDevice));
Dan Williams's avatar
Dan Williams committed
228
229
	if (!dev)
	{
230
		nm_warning ("could not allocate a new device...  Not enough memory?");
Dan Williams's avatar
Dan Williams committed
231
232
233
		return (NULL);
	}

234
	dev->refcount = 2; /* 1 for starters, and another 1 for the worker thread */
235
	dev->app_data = app_data;
Dan Williams's avatar
Dan Williams committed
236
	dev->iface = g_strdup (iface);
237
	dev->test_device = test_dev;
238
	nm_device_set_udi (dev, udi);
239
240
241
242
243
244
245
246

	/* Real hardware devices are probed for their type, test devices must have
	 * their type specified.
	 */
	if (test_dev)
		dev->type = test_dev_type;
	else
		dev->type = nm_device_test_wireless_extensions (dev) ?
247
						DEVICE_TYPE_WIRELESS_ETHERNET : DEVICE_TYPE_WIRED_ETHERNET;
Dan Williams's avatar
Dan Williams committed
248

249
250
251
252
253
254
255
	/* Device thread's main loop */
	dev->context = g_main_context_new ();
	dev->loop = g_main_loop_new (dev->context, FALSE);

	if (!dev->context || !dev->loop)
		goto err;

256
	/* Have to bring the device up before checking link status and other stuff */
257
258
	nm_device_bring_up_wait (dev, 0);

259
	dev->driver_support_level = nm_get_driver_support_level (dev->app_data->hal_ctx, dev);
260

261
	/* Initialize wireless-specific options */
262
	if (nm_device_is_wireless (dev))
Dan Williams's avatar
Dan Williams committed
263
	{
264
265
		int					sk;
		NMDeviceWirelessOptions	*opts = &(dev->options.wireless);
266

267
		nm_device_set_mode (dev, NETWORK_MODE_INFRA);
268

269
		opts->scan_interval = 20;
270

271
272
273
274
275
		opts->scan_mutex = g_mutex_new ();
		opts->best_ap_mutex = g_mutex_new ();
		opts->ap_list = nm_ap_list_new (NETWORK_TYPE_DEVICE);
		if (!opts->scan_mutex || !opts->best_ap_mutex || !opts->ap_list)
			goto err;
276

277
278
		nm_register_mutex_desc (opts->scan_mutex, "Scan Mutex");
		nm_register_mutex_desc (opts->best_ap_mutex, "Best AP Mutex");
279

280
		opts->supports_wireless_scan = nm_device_supports_wireless_scan (dev);
281
282
283

		if ((sk = iw_sockets_open ()) >= 0)
		{
284
285
286
287
288
			iwrange	range;
			if (iw_get_range_info (sk, nm_device_get_iface (dev), &range) >= 0)
			{
				int i;

289
290
291
292
				opts->max_qual.qual = range.max_qual.qual;
				opts->max_qual.level = range.max_qual.level;
				opts->max_qual.noise = range.max_qual.noise;
				opts->max_qual.updated = range.max_qual.updated;
293

294
295
296
297
				opts->avg_qual.qual = range.avg_qual.qual;
				opts->avg_qual.level = range.avg_qual.level;
				opts->avg_qual.noise = range.avg_qual.noise;
				opts->avg_qual.updated = range.avg_qual.updated;
298

299
300
301
				opts->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES);
				for (i = 0; i < opts->num_freqs; i++)
					opts->freqs[i] = iw_freq2float (&(range.freq[i]));
302
			}
303
304
			close (sk);
		}
Dan Williams's avatar
Dan Williams committed
305
	}
306
307
308
309
310
	else if (nm_device_is_wired (dev))
	{
		if (supports_ethtool_carrier_detect (dev) || supports_mii_carrier_detect (dev))
			dev->options.wired.has_carrier_detect = TRUE;
	}
311

312
313
	if (nm_device_get_driver_support_level (dev) != NM_DRIVER_UNSUPPORTED)
	{
314
315
316
317
318
		if (nm_device_is_wireless (dev))
		{
			nm_device_update_link_state (dev);
			nm_device_update_signal_strength (dev);
		}
319
320
321
322
323
324

		nm_device_update_ip4_address (dev);
		nm_device_update_hw_address (dev);

		/* Grab IP config data for this device from the system configuration files */
		nm_system_device_update_config_info (dev);
325
	}
Dan Williams's avatar
Dan Williams committed
326

327
328
	if (!g_thread_create (nm_device_worker, dev, FALSE, &error))
	{
329
		nm_error ("could not create device worker thread. (glib said: '%s')", error->message);
330
		g_error_free (error);
331
		goto err;
332
333
	}

334
	/* Block until our device thread has actually had a chance to start. */
335
336
337
338
339
340
	nm_wait_for_completion (NM_COMPLETION_TRIES_INFINITY,
			G_USEC_PER_SEC / 20, nm_completion_boolean_test, NULL,
			&dev->worker_started,
			"nm_device_new(): waiting for device's worker thread to start",
			LOG_INFO, 0);
	nm_info ("nm_device_new(): device's worker thread started, continuing.");
341

Dan Williams's avatar
Dan Williams committed
342
	return (dev);
343
344
345
346
347
348

err:
	/* Initial refcount is 2 */
	nm_device_unref (dev);
	nm_device_unref (dev);
	return NULL;
Dan Williams's avatar
Dan Williams committed
349
350
351
352
353
354
355
356
357
358
359
360
361
}


/*
 * Refcounting functions
 */
void nm_device_ref (NMDevice *dev)
{
	g_return_if_fail (dev != NULL);

	dev->refcount++;
}

362
363
364
365
366
367
368
369
370
371
/*
 * nm_device_unref
 *
 * Decreases the refcount on a device by 1, and if the refcount reaches 0,
 * deallocates memory used by the device.
 *
 * Returns:	FALSE if device was not deallocated
 *			TRUE if device was deallocated
 */
gboolean nm_device_unref (NMDevice *dev)
Dan Williams's avatar
Dan Williams committed
372
{
373
374
375
	gboolean	deleted = FALSE;

	g_return_val_if_fail (dev != NULL, TRUE);
Dan Williams's avatar
Dan Williams committed
376
377

	dev->refcount--;
378
	if (dev->refcount <= 0)
Dan Williams's avatar
Dan Williams committed
379
	{
380
381
		nm_device_worker_thread_stop (dev);
		nm_device_bring_down (dev);
Dan Williams's avatar
Dan Williams committed
382

383
		if (nm_device_is_wireless (dev))
Dan Williams's avatar
Dan Williams committed
384
		{
385
386
			nm_device_ap_list_clear (dev);

387
			g_mutex_free (dev->options.wireless.scan_mutex);
388
389
			if (dev->options.wireless.ap_list)
				nm_ap_list_unref (dev->options.wireless.ap_list);
390
391
			if (dev->options.wireless.best_ap)
				nm_ap_unref (dev->options.wireless.best_ap);
392
			g_mutex_free (dev->options.wireless.best_ap_mutex);
Dan Williams's avatar
Dan Williams committed
393
394
		}

395
396
397
398
399
400
401
		/* Get rid of DHCP state data */
		if (dev->dhcp_iface)
		{
			dhcp_interface_free (dev->dhcp_iface);
			dev->dhcp_iface = NULL;
		}

402
403
		g_free (dev->udi);
		g_free (dev->iface);
404
		memset (dev, 0, sizeof (NMDevice));
405
		g_free (dev);
406
		deleted = TRUE;
Dan Williams's avatar
Dan Williams committed
407
	}
408
409
410
411
412

	return deleted;
}


413
414
415
416
417
418
419
420
421
422
/*
 * nm_device_worker
 *
 * Main thread of the device.
 *
 */
static gpointer nm_device_worker (gpointer user_data)
{
	NMDevice *dev = (NMDevice *)user_data;

423
424
	if (!dev)
	{
425
		nm_error ("received NULL device object, NetworkManager cannot continue.");
426
427
		exit (1);
	}
428
429
430
431
432
433
434
435
436
437
438
439

	/* Do an initial wireless scan */
	if (nm_device_is_wireless (dev))
	{
		GSource	*source = g_idle_source_new ();
		guint	 source_id = 0;

		g_source_set_callback (source, nm_device_wireless_scan, dev, NULL);
		source_id = g_source_attach (source, dev->context);
		g_source_unref (source);
	}

440
	dev->worker_started = TRUE;
441
442
	g_main_loop_run (dev->loop);

443
	/* Remove any DHCP timeouts that might have been running */
444
445
	if (nm_device_config_get_use_dhcp (dev))
		nm_device_dhcp_remove_timeouts (dev);
446

447
448
449
450
451
452
453
454
455
456
457
458
459
	g_main_loop_unref (dev->loop);
	g_main_context_unref (dev->context);

	dev->loop = NULL;
	dev->context = NULL;

	dev->worker_done = TRUE;
	nm_device_unref (dev);

	return NULL;
}


460
461
462
463
void nm_device_worker_thread_stop (NMDevice *dev)
{
	g_return_if_fail (dev != NULL);

464
465
466
467
468
	if (dev->loop)
		g_main_loop_quit (dev->loop);
	nm_wait_for_completion(NM_COMPLETION_TRIES_INFINITY, 300,
			nm_completion_boolean_test, NULL, &dev->worker_done,
			NULL, NULL, 0);
469
470
471
}


472
473
474
475
476
477
478
479
480
/*
 * nm_device_get_app_data
 *
 */
NMData *nm_device_get_app_data (const NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, FALSE);

	return (dev->app_data);
Dan Williams's avatar
Dan Williams committed
481
482
483
}


484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
/*
 * Get/Set for "removed" flag
 */
gboolean nm_device_get_removed (const NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, TRUE);

	return (dev->removed);
}

void nm_device_set_removed (NMDevice *dev, const gboolean removed)
{
	g_return_if_fail (dev != NULL);

	dev->removed = removed;
}


502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
/*
 * nm_device_open_sock
 *
 * Get a control socket for network operations.
 *
 */
int nm_device_open_sock (void)
{
	int	fd;

	/* Try to grab a control socket */
	fd = socket(PF_INET, SOCK_DGRAM, 0);
	if (fd >= 0)
	     return (fd);
	fd = socket(PF_PACKET, SOCK_DGRAM, 0);
	if (fd >= 0)
	     return (fd);
	fd = socket(PF_INET6, SOCK_DGRAM, 0);
	if (fd >= 0)
	     return (fd);

523
	nm_warning ("could not get network control socket.");
524
525
526
527
	return (-1);
}


528
529
530
531
532
/*
 * Return the amount of time we should wait for the device
 * to get a link, based on the # of frequencies it has to
 * scan.
 */
533
gint nm_device_get_association_pause_value (NMDevice *dev)
534
{
535
	g_return_val_if_fail (dev != NULL, -1);
536
537
538
539
540
541
542
	g_return_val_if_fail (nm_device_is_wireless (dev), -1);

	/* If the card supports more than 14 channels, we should probably wait
	 * around 10s so it can scan them all. After we set the ESSID on the card, the card
	 * has to scan all channels to find our requested AP (which can take a long time
	 * if it is an A/B/G chipset like the Atheros 5212, for example).
	 */
543
	if (dev->options.wireless.num_freqs > 14)
544
		return 8;
545
546
547
548
549
	else
		return 5;
}


Dan Williams's avatar
Dan Williams committed
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
/*
 * Get/set functions for UDI
 */
char * nm_device_get_udi (NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, NULL);

	return (dev->udi);
}

void nm_device_set_udi (NMDevice *dev, const char *udi)
{
	g_return_if_fail (dev != NULL);
	g_return_if_fail (udi != NULL);

	if (dev->udi)
		g_free (dev->udi);

	dev->udi = g_strdup (udi);
}


/*
 * Get/set functions for iface
 */
575
const char * nm_device_get_iface (NMDevice *dev)
Dan Williams's avatar
Dan Williams committed
576
577
578
579
580
581
582
583
{
	g_return_val_if_fail (dev != NULL, NULL);

	return (dev->iface);
}


/*
584
 * Get/set functions for type
Dan Williams's avatar
Dan Williams committed
585
 */
586
guint nm_device_get_type (NMDevice *dev)
Dan Williams's avatar
Dan Williams committed
587
{
588
	g_return_val_if_fail (dev != NULL, DEVICE_TYPE_DONT_KNOW);
Dan Williams's avatar
Dan Williams committed
589

590
	return (dev->type);
Dan Williams's avatar
Dan Williams committed
591
592
}

593
594
595
596
gboolean nm_device_is_wireless (NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, FALSE);

597
	return (dev->type == DEVICE_TYPE_WIRELESS_ETHERNET);
598
599
600
601
602
603
}

gboolean nm_device_is_wired (NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, FALSE);

604
	return (dev->type == DEVICE_TYPE_WIRED_ETHERNET);
605
606
}

Dan Williams's avatar
Dan Williams committed
607

608
609
610
611
612
613
614
615
616
617
618
/*
 * Accessor for driver support level
 */
NMDriverSupportLevel nm_device_get_driver_support_level (NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, NM_DRIVER_UNSUPPORTED);

	return (dev->driver_support_level);
}


Dan Williams's avatar
Dan Williams committed
619
620
621
/*
 * Get/set functions for link_active
 */
622
gboolean nm_device_has_active_link (NMDevice *dev)
Dan Williams's avatar
Dan Williams committed
623
624
625
626
627
628
629
630
631
632
{
	g_return_val_if_fail (dev != NULL, FALSE);

	return (dev->link_active);
}

void nm_device_set_link_active (NMDevice *dev, const gboolean link_active)
{
	g_return_if_fail (dev != NULL);

633
634
635
636
637
638
639
	if (dev->link_active != link_active)
	{
		dev->link_active = link_active;

		nm_dbus_schedule_device_status_change (dev, DEVICE_STATUS_CHANGE);
		nm_policy_schedule_state_update (dev->app_data);
	}
Dan Williams's avatar
Dan Williams committed
640
641
642
}


643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
/*
 * Get/set functions for now_scanning
 */
gboolean nm_device_get_now_scanning (NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, FALSE);
	g_return_val_if_fail (nm_device_is_wireless (dev), FALSE);

	return (dev->options.wireless.now_scanning);
}

void nm_device_set_now_scanning (NMDevice *dev, const gboolean now_scanning)
{
	gboolean	old_val;

	g_return_if_fail (dev != NULL);
	g_return_if_fail (nm_device_is_wireless (dev));

	old_val = nm_device_get_now_scanning (dev);
	dev->options.wireless.now_scanning = now_scanning;
	if (old_val != now_scanning)
		nm_dbus_schedule_device_status_change (dev, DEVICE_STATUS_CHANGE);
}


Dan Williams's avatar
Dan Williams committed
668
669
670
671
672
673
674
/*
 * Get function for supports_wireless_scan
 */
gboolean nm_device_get_supports_wireless_scan (NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, FALSE);

675
676
677
	if (!nm_device_is_wireless (dev))
		return (FALSE);

678
	return (dev->options.wireless.supports_wireless_scan);
Dan Williams's avatar
Dan Williams committed
679
680
681
}


682
683
684
685
686
687
688
689
690
691
692
693
694
/*
 * nm_device_get_supports_carrier_detect
 */
gboolean nm_device_get_supports_carrier_detect (NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, FALSE);

	if (!nm_device_is_wired (dev))
		return (FALSE);

	return (dev->options.wired.has_carrier_detect);
}

695
696
697
698
699
700
701
702
/*
 * nm_device_wireless_is_associated
 *
 * Figure out whether or not we're associated to an access point
 */
static gboolean nm_device_wireless_is_associated (NMDevice *dev)
{
	struct iwreq	wrq;
703
	int			sk;
704
705
706
707
708
709
710
	gboolean		associated = FALSE;

	g_return_val_if_fail (dev != NULL, FALSE);
	g_return_val_if_fail (dev->app_data != NULL, FALSE);

	/* Test devices have their link state set through DBUS */
	if (dev->test_device)
711
		return (nm_device_has_active_link (dev));
712
713
714
715
716
717
718
719

	if ((sk = iw_sockets_open ()) < 0)
		return (FALSE);

	/* Some cards, for example ipw2x00 cards, can short-circuit the MAC
	 * address check using this check on IWNAME.  Its faster.
	 */
	if (iw_get_ext (sk, nm_device_get_iface (dev), SIOCGIWNAME, &wrq) >= 0)
720
721
722
723
724
725
726
727
	{
		if (!strcmp(wrq.u.name, "unassociated"))
		{
			associated = FALSE;
			goto out;
		}
	}

728
	if (!associated)
729
730
731
732
733
734
735
	{
		/*
		 * For all other wireless cards, the best indicator of a "link" at this time
		 * seems to be whether the card has a valid access point MAC address.
		 * Is there a better way?  Some cards don't work too well with this check, ie
		 * Lucent WaveLAN.
		 */
736
737
738
		if (iw_get_ext (sk, nm_device_get_iface (dev), SIOCGIWAP, &wrq) >= 0)
			if (nm_ethernet_address_is_valid ((struct ether_addr *)(&(wrq.u.ap_addr.sa_data))))
				associated = TRUE;
739
	}
740

741
out:
742
	close (sk);
743

744
745
746
	return (associated);
}

Dan Williams's avatar
Dan Williams committed
747
/*
748
 * nm_device_probe_wireless_link_state
Dan Williams's avatar
Dan Williams committed
749
 *
750
 * Gets the link state of a wireless device
Dan Williams's avatar
Dan Williams committed
751
752
 *
 */
753
static gboolean nm_device_probe_wireless_link_state (NMDevice *dev)
Dan Williams's avatar
Dan Williams committed
754
{
755
756
	gboolean 		 link = FALSE;
	NMAccessPoint	*best_ap;
Dan Williams's avatar
Dan Williams committed
757

758
759
760
761
762
	g_return_val_if_fail (dev != NULL, FALSE);
	g_return_val_if_fail (dev->app_data != NULL, FALSE);

	/* Test devices have their link state set through DBUS */
	if (dev->test_device)
763
		return (nm_device_has_active_link (dev));
Dan Williams's avatar
Dan Williams committed
764

765
	if (!nm_device_wireless_is_associated (dev))
766
767
		return (FALSE);

768
769
770
771
	/* If we don't have a "best" ap, we can't logically have a valid link
	 * that we want to use.
	 */
	if ((best_ap = nm_device_get_best_ap (dev)))
772
	{
773
774
775
		if (!nm_device_need_ap_switch (dev))
			link = TRUE;
		nm_ap_unref (best_ap);
776
777
778
779
780
781
782
	}

	return (link);
}


/*
783
 * nm_device_probe_wired_link_state
784
 *
785
 * 
786
787
 *
 */
788
static gboolean nm_device_probe_wired_link_state (NMDevice *dev)
789
790
{
	gboolean	link = FALSE;
791
792
	gchar *contents, *carrier_path;
	gsize length;
793
794

	g_return_val_if_fail (dev != NULL, FALSE);
795
	g_return_val_if_fail (nm_device_is_wired (dev) == TRUE, FALSE);
796
797
798
799
	g_return_val_if_fail (dev->app_data != NULL, FALSE);

	/* Test devices have their link state set through DBUS */
	if (dev->test_device)
800
801
802
803
804
		return (nm_device_has_active_link (dev));

	if (dev->removed)
		return FALSE;

805
	carrier_path = g_strdup_printf ("/sys/class/net/%s/carrier", dev->iface);
806
807
808
	if (g_file_get_contents (carrier_path, &contents, &length, NULL)) {
		link = (gboolean) atoi (contents);
		g_free (contents);
809
	} 
810

811
812
813
814
	/* We say that non-carrier-detect devices always have a link, because
	 * they never get auto-selected by NM.  User has to force them on us,
	 * so we just hope the user knows whether or not the cable's plugged in.
	 */
815
	if (dev->options.wired.has_carrier_detect != TRUE)
816
		link = TRUE;
817
818
819
820
821

	return (link);
}

/*
822
 * nm_device_update_link_state
823
824
825
826
 *
 * Updates the link state for a particular device.
 *
 */
827
void nm_device_update_link_state (NMDevice *dev)
828
829
830
831
832
833
{
	gboolean		link = FALSE;

	g_return_if_fail (dev != NULL);
	g_return_if_fail (dev->app_data != NULL);

834
	switch (nm_device_get_type (dev))
Dan Williams's avatar
Dan Williams committed
835
	{
836
		case DEVICE_TYPE_WIRELESS_ETHERNET:
837
			nm_device_set_link_active (dev, nm_device_probe_wireless_link_state (dev));
Dan Williams's avatar
Dan Williams committed
838
839
			break;

840
		case DEVICE_TYPE_WIRED_ETHERNET:
841
			nm_device_set_link_active (dev, nm_device_probe_wired_link_state (dev));
842
			break;
Dan Williams's avatar
Dan Williams committed
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860

		default:
			break;
	}
}


/*
 * nm_device_get_essid
 *
 * If a device is wireless, return the essid that it is attempting
 * to use.
 *
 * Returns:	allocated string containing essid.  Must be freed by caller.
 *
 */
char * nm_device_get_essid (NMDevice *dev)
{
861
862
	int	sk;
	int	err;
Dan Williams's avatar
Dan Williams committed
863
864
	
	g_return_val_if_fail (dev != NULL, NULL);
865
	g_return_val_if_fail (nm_device_is_wireless (dev), NULL);
866
867
868
869
870
871

	/* Test devices return the essid of their "best" access point
	 * or if there is none, the contents of the cur_essid field.
	 */
	if (dev->test_device)
	{
872
873
874
875
876
877
878
879
880
881
		NMAccessPoint	*best_ap = nm_device_get_best_ap (dev);
		char			*essid = dev->options.wireless.cur_essid;

		/* Or, if we've got a best ap, use that ESSID instead */
		if (best_ap)
		{
			essid = nm_ap_get_essid (best_ap);
			nm_ap_unref (best_ap);
		}
		return (essid);
882
	}
Dan Williams's avatar
Dan Williams committed
883
	
884
885
	sk = iw_sockets_open ();
	if (sk >= 0)
Dan Williams's avatar
Dan Williams committed
886
	{
887
888
		wireless_config	info;

889
		err = iw_get_basic_config(sk, nm_device_get_iface (dev), &info);
890
891
892
893
894
895
896
		if (err >= 0)
		{
			if (dev->options.wireless.cur_essid)
				g_free (dev->options.wireless.cur_essid);
			dev->options.wireless.cur_essid = g_strdup (info.essid);
		}
		else
897
			nm_warning ("nm_device_get_essid(): error getting ESSID for device %s.  errno = %d", nm_device_get_iface (dev), errno);
898

899
		close (sk);
Dan Williams's avatar
Dan Williams committed
900
901
	}

902
	return (dev->options.wireless.cur_essid);
Dan Williams's avatar
Dan Williams committed
903
904
905
906
907
908
909
910
911
912
}


/*
 * nm_device_set_essid
 *
 * If a device is wireless, set the essid that it should use.
 */
void nm_device_set_essid (NMDevice *dev, const char *essid)
{
913
	int				sk;
Dan Williams's avatar
Dan Williams committed
914
915
916
917
918
	int				err;
	struct iwreq		wreq;
	unsigned char		safe_essid[IW_ESSID_MAX_SIZE + 1] = "\0";
	
	g_return_if_fail (dev != NULL);
919
	g_return_if_fail (nm_device_is_wireless (dev));
Dan Williams's avatar
Dan Williams committed
920

921
922
923
924
925
926
927
928
929
	/* Test devices directly set cur_essid */
	if (dev->test_device)
	{
		if (dev->options.wireless.cur_essid)
			g_free (dev->options.wireless.cur_essid);
		dev->options.wireless.cur_essid = g_strdup (essid);
		return;
	}

Dan Williams's avatar
Dan Williams committed
930
931
932
933
934
935
936
937
938
	/* Make sure the essid we get passed is a valid size */
	if (!essid)
		safe_essid[0] = '\0';
	else
	{
		strncpy (safe_essid, essid, IW_ESSID_MAX_SIZE);
		safe_essid[IW_ESSID_MAX_SIZE] = '\0';
	}

939
940
	sk = iw_sockets_open ();
	if (sk >= 0)
Dan Williams's avatar
Dan Williams committed
941
942
943
944
945
	{
		wreq.u.essid.pointer = (caddr_t) safe_essid;
		wreq.u.essid.length	 = strlen (safe_essid) + 1;
		wreq.u.essid.flags	 = 1;	/* Enable essid on card */
	
946
		err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWESSID, &wreq);
Dan Williams's avatar
Dan Williams committed
947
		if (err == -1)
948
			nm_warning ("nm_device_set_essid(): error setting ESSID '%s' for device %s.  errno = %d", safe_essid, nm_device_get_iface (dev), errno);
Dan Williams's avatar
Dan Williams committed
949

950
		close (sk);
Dan Williams's avatar
Dan Williams committed
951
952
953
954
	}
}


955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
/*
 * nm_device_get_frequency
 *
 * For wireless devices, get the frequency we broadcast/receive on.
 *
 */
double nm_device_get_frequency (NMDevice *dev)
{
	int		sk;
	int		err;
	double	freq = 0;

	g_return_val_if_fail (dev != NULL, 0);
	g_return_val_if_fail (nm_device_is_wireless (dev), 0);

	/* Test devices don't really have a frequency, they always succeed */
	if (dev->test_device)
		return 703000000;

	sk = iw_sockets_open ();
	if (sk >= 0)
	{
		struct iwreq		wrq;

		err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCGIWFREQ, &wrq);
		if (err >= 0)
			freq = iw_freq2float (&wrq.u.freq);
		if (err == -1)
983
			nm_warning ("nm_device_get_frequency(): error getting frequency for device %s.  errno = %d", nm_device_get_iface (dev), errno);
984
985
986
987
988
989
990
991
992
993
994

		close (sk);
	}
	return (freq);
}


/*
 * nm_device_set_frequency
 *
 * For wireless devices, set the frequency to broadcast/receive on.
995
 * A frequency <= 0 means "auto".
996
997
998
999
1000
1001
1002
 *
 */
void nm_device_set_frequency (NMDevice *dev, const double freq)
{
	int				sk;
	int				err;
	
1003
1004
1005
1006
	/* HACK FOR NOW */
	if (freq <= 0)
		return;

1007
1008
1009
1010
1011
1012
1013
	g_return_if_fail (dev != NULL);
	g_return_if_fail (nm_device_is_wireless (dev));

	/* Test devices don't really have a frequency, they always succeed */
	if (dev->test_device)
		return;

1014
1015
1016
	if (nm_device_get_frequency (dev) == freq)
		return;

1017
1018
1019
1020
1021
	sk = iw_sockets_open ();
	if (sk >= 0)
	{
		struct iwreq		wrq;

1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
		if (freq <= 0)
		{
			/* Auto */
			/* People like to make things hard for us.  Even though iwlib/iwconfig say
			 * that wrq.u.freq.m should be -1 for "auto" mode, nobody actually supports
			 * that.  Madwifi actually uses "0" to mean "auto".  So, we'll try 0 first
			 * and if that doesn't work, fall back to the iwconfig method and use -1.
			 *
			 * As a further note, it appears that Atheros/Madwifi cards can't go back to
			 * any-channel operation once you force set the channel on them.  For example,
			 * if you set a prism54 card to a specific channel, but then set the ESSID to
			 * something else later, it will scan for the ESSID and switch channels just fine.
			 * Atheros cards, however, just stay at the channel you previously set and don't
			 * budge, no matter what you do to them, until you tell them to go back to
			 * any-channel operation.
			 */
			wrq.u.freq.m = 0;
			wrq.u.freq.e = 0;
			wrq.u.freq.flags = 0;
		}
		else
		{
			/* Fixed */
			wrq.u.freq.flags = IW_FREQ_FIXED;
			iw_float2freq (freq, &wrq.u.freq);
		}
1048
1049
		err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWFREQ, &wrq);
		if (err == -1)
1050
1051
		{
			gboolean	success = FALSE;
1052
			if ((freq <= 0) && ((errno == EINVAL) || (errno == EOPNOTSUPP)))
1053
1054
			{
				/* Ok, try "auto" the iwconfig way if the Atheros way didn't work */
1055
				wrq.u.freq.m = -1;
1056
1057
1058
1059
1060
1061
				wrq.u.freq.e = 0;
				wrq.u.freq.flags = 0;
				if (iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWFREQ, &wrq) != -1)
					success = TRUE;
			}
		}
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083

		close (sk);
	}
}


/*
 * nm_device_get_bitrate
 *
 * For wireless devices, get the bitrate to broadcast/receive at.
 * Returned value is rate in KHz.
 *
 */
int nm_device_get_bitrate (NMDevice *dev)
{
	int				sk;
	int				err = -1;
	struct iwreq		wrq;
	
	g_return_val_if_fail (dev != NULL, 0);
	g_return_val_if_fail (nm_device_is_wireless (dev), 0);

1084
	/* Test devices don't really have a bitrate, they always succeed */
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
	if (dev->test_device)
		return 11;

	sk = iw_sockets_open ();
	if (sk >= 0)
	{
		err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCGIWRATE, &wrq);
		close (sk);
	}

	return ((err >= 0) ? wrq.u.bitrate.value / 1000 : 0);
}


/*
 * nm_device_set_bitrate
 *
 * For wireless devices, set the bitrate to broadcast/receive at.
1103
 * Rate argument should be in Mbps (mega-bits per second), or 0 for automatic.
1104
1105
 *
 */
1106
void nm_device_set_bitrate (NMDevice *dev, const int Mbps)
1107
1108
1109
1110
1111
1112
{
	int				sk;
	
	g_return_if_fail (dev != NULL);
	g_return_if_fail (nm_device_is_wireless (dev));

1113
	/* Test devices don't really have a bitrate, they always succeed */
1114
1115
1116
	if (dev->test_device)
		return;

1117
	if (nm_device_get_bitrate (dev) == Mbps)
1118
1119
		return;

1120
1121
1122
1123
1124
	sk = iw_sockets_open ();
	if (sk >= 0)
	{
		struct iwreq		wrq;

1125
		if (Mbps != 0)
1126
		{
1127
			wrq.u.bitrate.value = Mbps * 1000;
1128
1129
1130
1131
			wrq.u.bitrate.fixed = 1;
		}
		else
		{
1132
			/* Auto bitrate */
1133
1134
1135
			wrq.u.bitrate.value = -1;
			wrq.u.bitrate.fixed = 0;
		}
1136
		/* Silently fail as not all drivers support setting bitrate yet (ipw2x00 for example) */
1137
		iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWRATE, &wrq);
1138
1139
1140
1141
1142
1143

		close (sk);
	}
}


1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
/*
 * nm_device_get_ap_address
 *
 * If a device is wireless, get the access point's ethernet address
 * that the card is associated with.
 */
void nm_device_get_ap_address (NMDevice *dev, struct ether_addr *addr)
{
	int			iwlib_socket;
	struct iwreq	wrq;

	g_return_if_fail (dev != NULL);
	g_return_if_fail (addr != NULL);
1157
	g_return_if_fail (nm_device_is_wireless (dev));
1158

1159
1160
1161
1162
1163
1164
1165
	/* Test devices return an invalid address when there's no link,
	 * and a made-up address when there is a link.
	 */
	if (dev->test_device)
	{
		struct ether_addr	good_addr = { {0x70, 0x37, 0x03, 0x70, 0x37, 0x03} };
		struct ether_addr	bad_addr = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} };
1166
		gboolean			link = nm_device_has_active_link (dev);
1167
1168
1169
1170
1171

		memcpy ((link ? &good_addr : &bad_addr), &(wrq.u.ap_addr.sa_data), sizeof (struct ether_addr));
		return;
	}

1172
1173
1174
1175
1176
1177
1178
1179
1180
	iwlib_socket = iw_sockets_open ();
	if (iw_get_ext (iwlib_socket, nm_device_get_iface (dev), SIOCGIWAP, &wrq) >= 0)
		memcpy (addr, &(wrq.u.ap_addr.sa_data), sizeof (struct ether_addr));
	else
		memset (addr, 0, sizeof (struct ether_addr));
	close (iwlib_socket);
}


Dan Williams's avatar
Dan Williams committed
1181
/*
1182
 * nm_device_set_enc_key
Dan Williams's avatar
Dan Williams committed
1183
 *
1184
 * If a device is wireless, set the encryption key that it should use.
Dan Williams's avatar
Dan Williams committed
1185
 *
1186
1187
1188
 * key:	encryption key to use, or NULL or "" to disable encryption.
 *		NOTE that at this time, the key must be the raw HEX key, not
 *		a passphrase.
Dan Williams's avatar
Dan Williams committed
1189
 */
1190
void nm_device_set_enc_key (NMDevice *dev, const char *key, NMDeviceAuthMethod auth_method)
Dan Williams's avatar
Dan Williams committed
1191
{
1192
	int				sk;
Dan Williams's avatar
Dan Williams committed
1193
1194
1195
	int				err;
	struct iwreq		wreq;
	int				keylen;
1196
	unsigned char		safe_key[IW_ENCODING_TOKEN_MAX + 1];
Dan Williams's avatar
Dan Williams committed
1197
1198
1199
	gboolean			set_key = FALSE;
	
	g_return_if_fail (dev != NULL);
1200
	g_return_if_fail (nm_device_is_wireless (dev));
Dan Williams's avatar
Dan Williams committed
1201

1202
1203
1204
1205
	/* Test devices just ignore encryption keys */
	if (dev->test_device)
		return;

Dan Williams's avatar
Dan Williams committed
1206
	/* Make sure the essid we get passed is a valid size */
1207
	if (!key)
Dan Williams's avatar
Dan Williams committed
1208
1209
1210
		safe_key[0] = '\0';
	else
	{
1211
		strncpy (safe_key, key, IW_ENCODING_TOKEN_MAX);
Dan Williams's avatar
Dan Williams committed
1212
1213
1214
		safe_key[IW_ENCODING_TOKEN_MAX] = '\0';
	}

1215
1216
	sk = iw_sockets_open ();
	if (sk >= 0)
Dan Williams's avatar
Dan Williams committed
1217
1218
1219
	{
		wreq.u.data.pointer = (caddr_t) NULL;
		wreq.u.data.length = 0;
1220
1221
1222
1223
1224
1225
1226
		wreq.u.data.flags = IW_ENCODE_ENABLED;

		/* Unfortunately, some drivers (Cisco) don't make a distinction between
		 * Open System authentication mode and whether or not to use WEP.  You
		 * DON'T have to use WEP when using Open System, but these cards force
		 * it.  Therefore, we have to set Open System mode when using WEP.
		 */
Dan Williams's avatar
Dan Williams committed
1227
1228
1229

		if (strlen (safe_key) == 0)
		{
1230
			wreq.u.data.flags |= IW_ENCODE_DISABLED | IW_ENCODE_NOKEY;
Dan Williams's avatar
Dan Williams committed
1231
1232
1233
1234
			set_key = TRUE;
		}
		else
		{
1235
1236
			unsigned char		parsed_key[IW_ENCODING_TOKEN_MAX + 1];

1237
			keylen = iw_in_key_full (sk, nm_device_get_iface (dev), safe_key, &parsed_key[0], &wreq.u.data.flags);
Dan Williams's avatar
Dan Williams committed
1238
1239
			if (keylen > 0)
			{
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
				switch (auth_method)
				{
					case NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM:
						wreq.u.data.flags |= IW_ENCODE_OPEN;
						break;
					case NM_DEVICE_AUTH_METHOD_SHARED_KEY:
						wreq.u.data.flags |= IW_ENCODE_RESTRICTED;
						break;
					default:
						wreq.u.data.flags |= IW_ENCODE_RESTRICTED;
						break;
				}
1252
				wreq.u.data.pointer	=  (caddr_t) &parsed_key;
Dan Williams's avatar
Dan Williams committed
1253
1254
1255
1256
1257
1258
1259
				wreq.u.data.length	=  keylen;
				set_key = TRUE;
			}
		}

		if (set_key)
		{
1260
			err = iw_set_ext (sk, nm_device_get_iface (dev), SIOCSIWENCODE, &wreq);
Dan Williams's avatar
Dan Williams committed
1261
			if (err == -1)
1262
				nm_warning ("nm_device_set_enc_key(): error setting key for device %s.  errno = %d", nm_device_get_iface (dev), errno);
Dan Williams's avatar
Dan Williams committed
1263
1264
		}

1265
		close (sk);
1266
	} else nm_warning ("nm_device_set_enc_key(): could not get wireless control socket.");
Dan Williams's avatar
Dan Williams committed
1267
1268
1269
}


1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
/*
 * nm_device_get_signal_strength
 *
 * Get the current signal strength of a wireless device.  This only works when
 * the card is associated with an access point, so will only work for the
 * active device.
 *
 * Returns:	-1 on error
 *			0 - 100  strength percentage of the connection to the current access point
 *
 */
gint8 nm_device_get_signal_strength (NMDevice *dev)
{
	g_return_val_if_fail (dev != NULL, -1);
	g_return_val_if_fail (nm_device_is_wireless (dev), -1);

	return (dev->options.wireless.strength);
}


/*
 * nm_device_update_signal_strength
 *
 * Update the device's idea of the strength of its connection to the
 * current access point.
 *
 */
void nm_device_update_signal_strength (NMDevice *dev)
{
	gboolean	has_range;
1300
	int		sk;
1301
1302
1303
1304
1305
1306
1307
1308
	iwrange	range;
	iwstats	stats;
	int		percent = -1;

	g_return_if_fail (dev != NULL);
	g_return_if_fail (nm_device_is_wireless (dev));
	g_return_if_fail (dev->app_data != NULL);

1309
1310
1311
1312
	/* Grab the scan lock since our strength is meaningless during a scan. */
	if (!nm_try_acquire_mutex (dev->options.wireless.scan_mutex, __FUNCTION__))
		return;

1313
1314
1315
1316
1317
1318
	/* If we aren't the active device, we don't really have a signal strength
	 * that would mean anything.
	 */
	if (dev != dev->app_data->active_device)
	{
		dev->options.wireless.strength = -1;
1319
		goto out;
1320
1321
1322
1323
1324
1325
	}

	/* Fake a value for test devices */
	if (dev->test_device)
	{
		dev->options.wireless.strength = 75;