hal/hald/linux net_class_device.c,1.6,1.7

Joe Shaw joe at pdx.freedesktop.org
Wed May 26 12:54:47 PDT 2004


Update of /cvs/hal/hal/hald/linux
In directory pdx:/tmp/cvs-serv18904/hald/linux

Modified Files:
	net_class_device.c 
Log Message:
2004-05-26  Joe Shaw  <joeshaw at novell.com>

	* configure.in: Add --enable-iwlib, and check for it so we can
	build the wireless functionality.

	* hald/linux/net_class_device.c: Add wireless network support.
	Adds a bunch of properties for the current state of things, as
	well as scanning for networks.
	(open_wireless_sysfs_subdir): Open the sysfs "wireless" path and
	read in a bunch of properties.
	(get_wireless_properties): Use the iwlib API to extract a bunch of
	additional properties, like the protocol, frequency, crypto key,
	ESSID, mode (ad-hoc, managed, etc), access point mac address.
	Also start scanning for other available access points.
	(read_scanning_results, parse_scanning_token, aps_to_properties):
	helper functions to read in the scanning data and expose as
	properties the list of networks (not access points!) available.
	(net_class_pre_process): Call get_wireless_properties().
	(net_class_tick): Added.  Gets wireless properties every 5 ticks.
	(net_class_handler): Call net_class_tick() instead of
	class_device_tick().

Index: net_class_device.c
===================================================================
RCS file: /cvs/hal/hal/hald/linux/net_class_device.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- a/net_class_device.c	25 May 2004 20:09:29 -0000	1.6
+++ b/net_class_device.c	26 May 2004 19:54:45 -0000	1.7
@@ -48,7 +48,13 @@
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <sys/ioctl.h>
-#include <net/if.h>
+
+#ifdef HAVE_IWLIB
+#  include <linux/wireless.h>
+#  include <iwlib.h>
+#else
+#  include <net/if.h>
+#endif
 
 #include "../logger.h"
 #include "../device_store.h"
@@ -317,7 +323,7 @@
 	HalDevice *d = HAL_DEVICE (user_data);
 	int fd;
 	int bytes_read;
-	int total_read = 0;
+	guint total_read = 0;
 	char buf[1024];
 
 	if (cond & ~(G_IO_IN | G_IO_PRI)) {
@@ -345,7 +351,7 @@
 
 	if (total_read > 0) {
 		struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
-		int offset = 0;
+		guint offset = 0;
 
 		while (offset < total_read &&
 		       VALID_NLMSG (hdr, total_read - offset)) {
@@ -400,6 +406,397 @@
 			link_detection_data_ready, d);
 }
 
+#ifdef HAVE_IWLIB
+static void
+open_wireless_sysfs_subdir (HalDevice *d, const char *sysfs_path)
+{
+	char wireless_path[SYSFS_PATH_MAX];
+	struct sysfs_directory *dir;
+	struct sysfs_attribute *cur;
+
+	snprintf (wireless_path, SYSFS_PATH_MAX, "%s/wireless", sysfs_path);
+	dir = sysfs_open_directory (wireless_path);
+
+	/* will fail if the directory doesn't exist */
+	if (sysfs_read_directory (dir) < 0)
+		return;
+
+	/* dir exists but is empty */
+	if (dir->attributes == NULL)
+		return;
+
+	dlist_for_each_data (dir->attributes, cur, struct sysfs_attribute) {
+		char attr_name[SYSFS_NAME_LEN];
+		int len, i;
+		int tmp;
+
+		if (sysfs_get_name_from_path (cur->path, attr_name,
+					      SYSFS_NAME_LEN) != 0)
+			continue;
+
+		/* strip whitespace */
+		len = strlen (cur->value);
+		for (i = len - 1; i >= 0 && isspace (cur->value[i]); --i)
+			cur->value[i] = '\0';
+
+		if (strcmp (attr_name, "level") == 0) {
+			tmp = parse_dec (cur->value);
+
+			hal_device_property_set_int (d,
+						     "net.ethernet.80211.level",
+						     tmp);
+		} else if (strcmp (attr_name, "link") == 0) {
+			tmp = parse_dec (cur->value);
+
+			hal_device_property_set_int (d, "net.ethernet.80211.link",
+						     tmp);
+		} else if (strcmp (attr_name, "noise") == 0) {
+			tmp = parse_dec (cur->value);
+
+			hal_device_property_set_int (d, "net.ethernet.80211.noise",
+						     tmp);
+		} else if (strcmp (attr_name, "status") == 0) {
+			tmp = parse_hex (cur->value);
+
+			hal_device_property_set_int (d, "net.ethernet.80211.status",
+						     tmp);
+		}
+	}
+
+	sysfs_close_directory (dir);
+
+	hal_device_add_capability (d, "net.ethernet.80211");
+}
+
+typedef struct {
+	char address[128];
+	float freq;
+	char essid[IW_ESSID_MAX_SIZE + 1];
+	int link, level, noise;
+} APInfo;
+
+static int
+ap_compare (gconstpointer a, gconstpointer b)
+{
+	APInfo *ap_a = (APInfo *) a;
+	APInfo *ap_b = (APInfo *) b;
+
+	return strcmp (ap_a->essid, ap_b->essid);
+}
+
+static void
+hash_to_list (gpointer key, gpointer value, gpointer user_data)
+{
+	GSList **list = (GSList **) user_data;
+
+	*list = g_slist_prepend (*list, value);
+}
+
+static void
+aps_to_properties (HalDevice *d, GSList *aps)
+{
+	GSList *iter;
+	GHashTable *networks;
+	int ap_num = 0;
+	GSList *network_list = NULL;
+	int i;
+
+	networks = g_hash_table_new_full (g_str_hash, g_str_equal,
+					  NULL, g_free);
+
+	for (iter = aps; iter != NULL; iter = iter->next) {
+		APInfo *ap = (APInfo *) iter->data;
+		APInfo *best_ap;
+
+		best_ap = g_hash_table_lookup (networks, ap->essid);
+
+		if (best_ap != NULL) {
+			if (ap->link > best_ap->link)
+				g_hash_table_replace (networks, ap->essid, ap);
+			else
+				g_free (ap);
+		} else
+			g_hash_table_insert (networks, ap->essid, ap);
+	}
+	
+	hal_device_property_set_int (d, "net.ethernet.80211.available_networks",
+				     g_hash_table_size (networks));
+
+	g_hash_table_foreach (networks, hash_to_list, &network_list);
+	network_list = g_slist_sort (network_list, ap_compare);
+
+	for (iter = network_list; iter != NULL; iter = iter->next) {
+		APInfo *ap = (APInfo *) iter->data;
+		char prop_name[256];
+
+		snprintf (prop_name, 256, "net.ethernet.80211.network%d.address",
+			  ap_num);
+
+		hal_device_property_set_string (d, prop_name, ap->address);
+
+		snprintf (prop_name, 256, "net.ethernet.80211.network%d.frequency",
+			  ap_num);
+
+		hal_device_property_set_double (d, prop_name, ap->freq);
+
+		snprintf (prop_name, 256, "net.ethernet.80211.network%d.essid",
+			  ap_num);
+
+		hal_device_property_set_string (d, prop_name, ap->essid);
+
+		snprintf (prop_name, 256, "net.ethernet.80211.network%d.link",
+			  ap_num);
+
+		hal_device_property_set_int (d, prop_name, ap->link);
+
+		snprintf (prop_name, 256, "net.ethernet.80211.network%d.level",
+			  ap_num);
+
+		hal_device_property_set_int (d, prop_name, ap->level);
+
+		snprintf (prop_name, 256, "net.ethernet.80211.network%d.noise",
+			  ap_num);
+
+		hal_device_property_set_int (d, prop_name, ap->noise);
+
+		ap_num++;
+	}
+
+	g_slist_free (aps);
+	g_hash_table_destroy (networks);
+
+	/* 
+	 * Clean out old properties.  There'll probably never be more than 64
+	 * networks, right?
+	 */
+	for (i = ap_num; i < 64; i++) {
+		char prop_name[256];
+
+		snprintf (prop_name, 256, "net.ethernet.80211.network%d.essid", i);
+
+		if (hal_device_has_property (d, prop_name)) {
+			char *prop_names[] = {
+				"address", "essid", "frequency",
+				"link", "level", "noise", NULL };
+			char **c;
+
+			for (c = prop_names; *c != NULL; c++) {
+				snprintf (prop_name, 256,
+					  "net.ethernet.80211.network%d.%s",
+					  i, *c);
+
+				hal_device_property_remove (d, prop_name);
+			}
+		} else
+			break;
+	}
+}
+
+static APInfo *
+parse_scanning_token (struct iw_event *iwe, APInfo *old_ap)
+{
+	APInfo *ap;
+
+	if (iwe->cmd == SIOCGIWAP)
+		ap = g_new0 (APInfo, 1);
+	else {
+		g_assert (old_ap != NULL);
+		ap = old_ap;
+	}
+
+	switch (iwe->cmd) {
+	case SIOCGIWAP:
+		memset (ap->address, 0, 128);
+		iw_pr_ether (ap->address, iwe->u.ap_addr.sa_data);
+		break;
+
+	case SIOCGIWFREQ:
+		ap->freq = iw_freq2float(&(iwe->u.freq));
+		break;
+
+	case SIOCGIWESSID:
+		memcpy (ap->essid, iwe->u.essid.pointer,
+			IW_ESSID_MAX_SIZE + 1);
+		ap->essid[iwe->u.essid.length] = 0;
+		break;
+
+	case IWEVQUAL:
+		ap->link = iwe->u.qual.qual;
+		ap->level = iwe->u.qual.level;
+		ap->noise = iwe->u.qual.noise;
+		break;
+	}
+
+	return ap;
+}	
+
+typedef struct {
+	HalDevice *d;
+	int skfd;
+} ScanningInfo;
+
+static gboolean
+read_scanning_results (gpointer user_data)
+{
+	ScanningInfo *si = user_data;
+	struct iwreq wrq;
+	char buffer[IW_SCAN_MAX_DATA];
+
+	wrq.u.data.pointer = buffer;
+	wrq.u.data.length = IW_SCAN_MAX_DATA;
+	wrq.u.data.flags = 0;
+
+	strncpy (wrq.ifr_name,
+		 hal_device_property_get_string (si->d, "net.interface"),
+		 IFNAMSIZ);
+
+	if (ioctl (si->skfd, SIOCGIWSCAN, &wrq) < 0) {
+		if (errno == EAGAIN) {
+			/* Results aren't ready yet.  Requeue. */
+			return TRUE;
+		} else {
+			goto cleanup;
+		}
+	}
+
+	if (wrq.u.data.length > 0) {
+		struct iw_event iwe;
+		struct stream_descr stream;
+		int ret;
+		GSList *aps = NULL;
+		APInfo *old_ap = NULL, *ap;
+
+		iw_init_event_stream (&stream, buffer, wrq.u.data.length);
+		do {
+			ret = iw_extract_event_stream (&stream, &iwe);
+
+			if (ret > 0) {
+				ap = parse_scanning_token (&iwe, old_ap);
+
+				if (ap != old_ap)
+					aps = g_slist_prepend (aps, ap);
+
+				old_ap = ap;
+			}
+		} while (ret > 0);
+
+		aps_to_properties (si->d, aps);
+	}
+
+cleanup:
+	g_object_unref (si->d);
+	close (si->skfd);
+	g_free (si);
+
+	return FALSE;
+}
+
+static void
+get_wireless_properties (HalDevice *d, const char *sysfs_path)
+{
+	int skfd;
+	const char *iface;
+	struct iwreq wrq;
+	char essid[IW_ESSID_MAX_SIZE + 1];
+	char key[IW_ENCODING_TOKEN_MAX];
+	gboolean close_skfd = TRUE;
+
+	open_wireless_sysfs_subdir (d, sysfs_path);
+
+	skfd = iw_sockets_open ();
+	if (skfd < 0)
+		return;
+
+	iface = hal_device_property_get_string (d, "net.interface");
+
+	/* Wireless protocol */
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	if (ioctl (skfd, SIOCGIWNAME, &wrq) < 0) {
+		/* no wireless extensions */
+		close (skfd);
+		return;
+	}
+
+	hal_device_property_set_bool (d, "net.ethernet.is_80211", TRUE);
+
+	wrq.u.name[IFNAMSIZ] = 0;
+	hal_device_property_set_string (d, "net.ethernet.80211.protocol",
+					wrq.u.name);
+
+	/* Frequency */
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	if (ioctl (skfd, SIOCGIWFREQ, &wrq) >= 0) {
+		hal_device_property_set_double (d, "net.ethernet.80211.frequency",
+						iw_freq2float(&(wrq.u.freq)));
+	}
+
+	/* Crypto info */
+	memset (key, 0, IW_ENCODING_TOKEN_MAX);
+	wrq.u.data.pointer = (caddr_t) key;
+	wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
+	wrq.u.data.flags = 0;
+
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	if (ioctl (skfd, SIOCGIWENCODE, &wrq) >= 0) {
+		hal_device_property_set_string (d, "net.ethernet.80211.key", key);
+	}
+
+	/* ESSID */
+	memset (essid, 0, IW_ESSID_MAX_SIZE + 1);
+	wrq.u.essid.pointer = (caddr_t) essid;
+	wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
+	wrq.u.essid.flags = 0;
+
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	if (ioctl (skfd, SIOCGIWESSID, &wrq) >= 0) {
+		hal_device_property_set_string (d, "net.ethernet.80211.essid",
+						essid);
+	}
+
+	/* Mode (ad-hoc, managed, etc) */
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	if (ioctl (skfd, SIOCGIWMODE, &wrq) >= 0) {
+		/* stolen from iwlib */
+		const char *modes[] = { "auto", "ad-hoc", "managed",
+					"master", "repeater", "secondary",
+					"monitor" };
+
+		hal_device_property_set_int (d, "net.ethernet.80211.mode",
+					     wrq.u.mode);
+		hal_device_property_set_string (d, "net.ethernet.80211.mode_str",
+						modes[wrq.u.mode]);
+	}
+
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	if (ioctl (skfd, SIOCGIWAP, &wrq) >= 0) {
+		char ap_addr[128];
+
+		memset (ap_addr, 0, 128);
+		iw_pr_ether (ap_addr, wrq.u.ap_addr.sa_data);
+
+		hal_device_property_set_string (d, "net.ethernet.80211.ap_address",
+						ap_addr);
+	}
+	
+	/* Scan for other access points */
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	if (ioctl (skfd, SIOCSIWSCAN, &wrq) >= 0) {
+		ScanningInfo *si = g_new0 (ScanningInfo, 1);
+		
+		si->d = g_object_ref (d);
+		si->skfd = skfd;
+		close_skfd = FALSE;
+			
+		g_timeout_add (100, read_scanning_results, si);
+	}
+	
+	if (close_skfd)
+		close (skfd);
+
+	hal_device_add_capability (d, "net.ethernet.80211");
+}
+#endif
+
 /** This method is called just before the device is either merged
  *  onto the sysdevice or added to the GDL (cf. merge_or_add). 
  *  This is useful for extracting more information about the device
@@ -474,6 +871,11 @@
 	media = media_type_to_string (media_type);
 	hal_device_property_set_string (d, "net.media", media);
 
+#ifdef HAVE_IWLIB
+	/* read any wireless properties */
+	get_wireless_properties (d, sysfs_path);
+#endif
+
 	hal_device_add_capability (d, "net");
 	hal_device_add_capability (d, "net.ethernet");
 	hal_device_property_set_string (d, "info.category", "net.ethernet");
@@ -536,11 +938,51 @@
 	return buf;
 }
 
+#ifdef HAVE_IWLIB
+static gboolean
+rehash_wireless (HalDeviceStore *gdl, HalDevice *d, gpointer user_data)
+{
+	const char *sysfs_path;
+
+	if (!hal_device_has_property (d, "net.ethernet.80211"))
+		return TRUE;
+
+	if (hal_device_property_get_bool (d, "net.ethernet.80211") == FALSE)
+		return TRUE;
+
+	sysfs_path = hal_device_property_get_string (d, "linux.sysfs_path");
+
+	get_wireless_properties (d, sysfs_path);
+
+	return TRUE;
+}
+#endif
+
+static void
+net_class_tick (ClassDeviceHandler *self)
+{
+#ifdef HAVE_IWLIB
+	/*
+	 * We only want this to happen once every 10 seconds, not the
+	 * normal 2.
+	 */
+	static int tick_count = 0;
+
+	tick_count++;
+
+	if (tick_count >= 5) {
+		hal_device_store_foreach (hald_get_gdl (), rehash_wireless,
+					  NULL);
+		tick_count = 0;
+	}
+#endif
+}
+
 /** Method specialisations for input device class */
 ClassDeviceHandler net_class_handler = {
 	class_device_init,                  /**< init function */
 	class_device_shutdown,              /**< shutdown function */
-	class_device_tick,                  /**< timer function */
+	net_class_tick,                     /**< timer function */
 	net_class_accept,                   /**< accept function */
 	class_device_visit,                 /**< visitor function */
 	class_device_removed,               /**< class device is removed */





More information about the hal-commit mailing list