Subject: [PATCH 6/6] d80211: add sysfs interface for QoS functions

From: Zhu Yi <[email-masked]>
Date: Thu, 14 Dec 2006 12:03:02 +0800

The sysfs interface here is only a proof of concept. It provides a way for
the userspace applications to use the advanced QoS features supported by
d80211 stack. The finial solution should be switched to cfg80211.

Signed-off-by: Zhu Yi <[email-masked]>

---

 net/d80211/ieee80211_i.h     |   13 ++
 net/d80211/ieee80211_sysfs.c |  245 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 258 insertions(+), 0 deletions(-)

83d49f70af1f38c152d8bd3abd69756ec087622e
diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h
index d09f65e..7904033 100644
--- a/net/d80211/ieee80211_i.h
+++ b/net/d80211/ieee80211_i.h
@@ -20,6 +20,7 @@
 #include <linux/workqueue.h>
 #include <linux/types.h>
 #include <linux/spinlock.h>
+#include <net/d80211_mgmt.h>
 #include "ieee80211_key.h"
 #include "sta_info.h"
 
@@ -329,6 +330,7 @@ struct ieee80211_sub_if_data {
         int channel_use_raw;
 
 	struct attribute_group *sysfs_group;
+	struct attribute_group *sysfs_qos_group;
 };
 
 #define IEEE80211_DEV_TO_SUB_IF(dev) netdev_priv(dev)
@@ -702,6 +704,17 @@ struct sta_info * ieee80211_ibss_add_sta
 					 u8 *addr);
 int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason);
 int ieee80211_sta_disassociate(struct net_device *dev, u16 reason);
+void ieee80211_send_addts(struct net_device *dev,
+			  struct ieee802_11_elem_tspec *tspec);
+void wmm_send_addts(struct net_device *dev,
+		    struct ieee802_11_elem_tspec *tspec);
+void ieee80211_send_delts(struct net_device *dev, u8 tsid, u8 direction,
+			  u32 medium_time);
+void wmm_send_delts(struct net_device *dev, u8 tsid, u8 direction,
+		    u32 medium_time);
+void ieee80211_send_dls_req(struct net_device *dev, struct dls_info *dls);
+void ieee80211_send_dls_teardown(struct net_device *dev, u8 *mac, u16 reason);
+void dls_info_add(struct ieee80211_local *local, struct dls_info *dls);
 void dls_info_stop(struct ieee80211_local *local);
 int dls_link_status(struct ieee80211_local *local, u8 *addr);
 
diff --git a/net/d80211/ieee80211_sysfs.c b/net/d80211/ieee80211_sysfs.c
index 6a60077..31dc1f4 100644
--- a/net/d80211/ieee80211_sysfs.c
+++ b/net/d80211/ieee80211_sysfs.c
@@ -13,6 +13,7 @@
 #include <linux/netdevice.h>
 #include <linux/rtnetlink.h>
 #include <net/d80211.h>
+#include <net/d80211_mgmt.h>
 #include "ieee80211_i.h"
 #include "ieee80211_rate.h"
 
@@ -21,6 +22,15 @@
 #define to_net_dev(class) \
 	container_of(class, struct net_device, class_dev)
 
+/* For sysfs and debug only */
+static struct ieee802_11_elem_tspec _tspec;
+static u8 _dls_mac[ETH_ALEN];
+
+#define TSID _tspec.ts_info.tsid
+#define TSDIR _tspec.ts_info.direction
+#define TSUP _tspec.ts_info.up
+
+
 static inline int rtnl_lock_local(struct ieee80211_local *local)
 {
 	rtnl_lock();
@@ -657,6 +667,230 @@ static struct class ieee80211_class = {
 #endif
 };
 
+
+/* QoS sysfs entries */
+static ssize_t show_ts_info(struct class_device *dev, char *buf)
+{
+	/* TSID, Direction, UP */
+	return sprintf(buf, "%u %u %u\n", TSID, TSDIR, TSUP);
+}
+
+static ssize_t store_ts_info(struct class_device *dev, const char *buf,
+			     size_t len)
+{
+	unsigned int id, index, up;
+
+	if (sscanf(buf, "%u, %u, %u", &id, &index, &up) != 3) {
+		printk(KERN_ERR "%s: sscanf error\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	if (id < 8 || id > 15) {
+		printk(KERN_ERR "invalid tsid %d\n", id);
+		return -EINVAL;
+	}
+	if ((index != 0) && (index != 1) && (index != 3)) {
+		printk(KERN_ERR "invalid direction %d\n", index);
+		return -EINVAL;
+	}
+	if (up < 0 || up > 7) {
+		printk(KERN_ERR "invalid UP %d\n", up);
+		return -EINVAL;
+	}
+	TSID = id;
+	TSDIR = index;
+	TSUP = up;
+	return len;
+}
+
+static CLASS_DEVICE_ATTR(ts_info, S_IWUSR|S_IRUGO, show_ts_info, store_ts_info);
+
+static ssize_t show_tspec(struct class_device *dev, char *buf)
+{
+	/* Nominal MSDU, Max MSDU, Min int, Max int, Inact int,
+	 * susp int, start, min rate, mean rate, peak rate,
+	 * max burst, delay, min phy, surp band, medium time */
+	return sprintf(buf,"%hu %hu %u %u %u %u %u %u %u %u %u %u %u %hu %hu\n",
+		       _tspec.nominal_msdu_size,
+		       _tspec.max_msdu_size,
+		       _tspec.min_service_interval,
+		       _tspec.max_service_interval,
+		       _tspec.inactivity_interval,
+		       _tspec.suspension_interval,
+		       _tspec.service_start_time,
+		       _tspec.min_data_rate,
+		       _tspec.mean_data_rate,
+		       _tspec.peak_data_rate,
+		       _tspec.burst_size,
+		       _tspec.delay_bound,
+		       _tspec.min_phy_rate,
+		       _tspec.surplus_band_allow,
+		       _tspec.medium_time);
+}
+
+static ssize_t store_tspec(struct class_device *dev, const char *buf,
+			   size_t len)
+{
+	if (sscanf(buf, "%hu %hu %u %u %u %u %u %u %u %u %u %u %u %hu %hu",
+		   &_tspec.nominal_msdu_size,
+		   &_tspec.max_msdu_size,
+		   &_tspec.min_service_interval,
+		   &_tspec.max_service_interval,
+		   &_tspec.inactivity_interval,
+		   &_tspec.suspension_interval,
+		   &_tspec.service_start_time,
+		   &_tspec.min_data_rate,
+		   &_tspec.mean_data_rate,
+		   &_tspec.peak_data_rate,
+		   &_tspec.burst_size,
+		   &_tspec.delay_bound,
+		   &_tspec.min_phy_rate,
+		   &_tspec.surplus_band_allow,
+		   &_tspec.medium_time) != 15) {
+		printk(KERN_ERR "%s: sscanf error\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	/* Set default TSPEC Values */
+	_tspec.ts_info.access_policy = WLAN_TSINFO_EDCA;
+	_tspec.ts_info.apsd = WLAN_TSINFO_PSB_LEGACY;
+
+	return len;
+}
+
+static CLASS_DEVICE_ATTR(tspec, S_IWUSR|S_IRUGO, show_tspec, store_tspec);
+
+static ssize_t store_addts(struct class_device *dev, const char *buf,
+			   size_t len)
+{
+	struct net_device *netdev;
+
+	netdev = to_net_dev(dev);
+	ieee80211_send_addts(netdev, &_tspec);
+	return len;
+}
+
+static CLASS_DEVICE_ATTR(addts, S_IWUSR, NULL, store_addts);
+
+static ssize_t store_addts_wmm(struct class_device *dev, const char *buf,
+			       size_t len)
+{
+	struct net_device *netdev;
+
+	netdev = to_net_dev(dev);
+	wmm_send_addts(netdev, &_tspec);
+	return len;
+}
+
+static CLASS_DEVICE_ATTR(addts_wmm, S_IWUSR, NULL, store_addts_wmm);
+
+static ssize_t store_delts(struct class_device *dev, const char *buf,
+			   size_t len)
+{
+	struct net_device *netdev;
+
+	netdev = to_net_dev(dev);
+	ieee80211_send_delts(netdev, TSID, TSDIR, _tspec.medium_time);
+	return len;
+}
+
+static CLASS_DEVICE_ATTR(delts, S_IWUSR, NULL, store_delts);
+
+static ssize_t store_delts_wmm(struct class_device *dev, const char *buf,
+			       size_t len)
+{
+	struct net_device *netdev;
+
+	netdev = to_net_dev(dev);
+	wmm_send_delts(netdev, TSID, TSDIR, _tspec.medium_time);
+	return len;
+}
+
+static CLASS_DEVICE_ATTR(delts_wmm, S_IWUSR, NULL, store_delts_wmm);
+
+static ssize_t show_dls_mac(struct class_device *dev, char *buf)
+{
+	return sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X\n",
+		       _dls_mac[0], _dls_mac[1], _dls_mac[2],
+		       _dls_mac[3], _dls_mac[4], _dls_mac[5]);
+}
+
+static ssize_t store_dls_mac(struct class_device *dev, const char *buf,
+			     size_t len)
+{
+	if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
+		   (int *)&_dls_mac[0], (int *)&_dls_mac[1],
+		   (int *)&_dls_mac[2], (int *)&_dls_mac[3],
+		   (int *)&_dls_mac[4], (int *)&_dls_mac[5]) != 6) {
+		printk(KERN_ERR "%s: sscanf error\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	return len;
+}
+
+static CLASS_DEVICE_ATTR(dls_mac, S_IWUSR|S_IRUGO, show_dls_mac, store_dls_mac);
+
+static ssize_t show_dls_op(struct class_device *dev, char *buf)
+{
+	return sprintf(buf, "DLS Operation: Setup = 1; Teardown = 2\n");
+}
+
+static ssize_t store_dls_op(struct class_device *dev, const char *buf,
+			    size_t len)
+{
+	struct ieee80211_local *local;
+	struct net_device *netdev;
+	struct dls_info *dls;
+	unsigned int opt;
+
+	netdev = to_net_dev(dev);
+	local = netdev->ieee80211_ptr;
+
+	if (sscanf(buf, "%u", &opt) != 1) {
+		printk(KERN_ERR "%s: sscanf error\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	switch (opt) {
+	case 1:
+		dls = kzalloc(sizeof(struct dls_info), GFP_KERNEL);
+		if (!dls) {
+			printk(KERN_ERR "No memory to allocate dls_info\n");
+			return -ENOMEM;
+		}
+		atomic_set(&dls->refcnt, 1);
+		dls->status = DLS_STATUS_SETUP;
+		dls->timeout = 0;
+		memcpy(dls->addr, _dls_mac, ETH_ALEN);
+		dls_info_add(local, dls);
+		ieee80211_send_dls_req(netdev, dls);
+		break;
+	case 2:
+		ieee80211_send_dls_teardown(netdev, _dls_mac,
+					    WLAN_REASON_QSTA_NOT_USE);
+		break;
+	default:
+		printk(KERN_ERR "Unknown DLS Operation: %d\n", opt);
+		break;
+	}
+	return len;
+}
+
+static CLASS_DEVICE_ATTR(dls_op, S_IWUSR|S_IRUGO, show_dls_op, store_dls_op);
+
+static struct attribute *ieee80211_qos_attrs[] = {
+	&class_device_attr_ts_info.attr,
+	&class_device_attr_tspec.attr,
+	&class_device_attr_addts.attr,
+	&class_device_attr_addts_wmm.attr,
+	&class_device_attr_delts.attr,
+	&class_device_attr_delts_wmm.attr,
+	&class_device_attr_dls_mac.attr,
+	&class_device_attr_dls_op.attr,
+	NULL
+};
+	
+static struct attribute_group ieee80211_qos_group = {
+	.name = "qos",
+	.attrs = ieee80211_qos_attrs,
+};
 void ieee80211_dev_sysfs_init(struct ieee80211_local *local)
 {
 	local->class_dev.class = &ieee80211_class;
@@ -696,6 +930,10 @@ void ieee80211_dev_sysfs_del(struct ieee
 static void __ieee80211_remove_if_group(struct kobject *kobj,
 					struct ieee80211_sub_if_data *sdata)
 {
+	if (sdata->sysfs_qos_group) {
+		sysfs_remove_group(kobj, sdata->sysfs_qos_group);
+		sdata->sysfs_qos_group = NULL;
+	}
 	if (sdata->sysfs_group) {
 		sysfs_remove_group(kobj, sdata->sysfs_group);
 		sdata->sysfs_group = NULL;
@@ -718,6 +956,7 @@ static int ieee80211_add_if_group(struct
 	switch (sdata->type) {
 	case IEEE80211_IF_TYPE_STA:
 		sdata->sysfs_group = &ieee80211_sta_group;
+		sdata->sysfs_qos_group = &ieee80211_qos_group;
 		break;
 	case IEEE80211_IF_TYPE_AP:
 		sdata->sysfs_group = &ieee80211_ap_group;
@@ -737,6 +976,12 @@ static int ieee80211_add_if_group(struct
 	res = sysfs_create_group(kobj, sdata->sysfs_group);
 	if (res)
 		sdata->sysfs_group = NULL;
+	if (sdata->sysfs_qos_group) {
+		res = sysfs_create_group(kobj, sdata->sysfs_qos_group);
+		if (res)
+			sdata->sysfs_qos_group = NULL;
+	}
+
 out:
 	return res;
 }
-- 
1.2.6