Return-path: Received: from hostap.isc.org ([149.20.54.63]:39857 "EHLO hostap.isc.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752157AbYKZOPl (ORCPT ); Wed, 26 Nov 2008 09:15:41 -0500 Date: Wed, 26 Nov 2008 16:15:24 +0200 From: Jouni Malinen To: "John W. Linville" , Johannes Berg Cc: linux-wireless@vger.kernel.org Subject: [PATCHv2] nl80211: Add frequency configuration (including HT40) Message-ID: <20081126141524.GA8607@jm.kir.nu> (sfid-20081126_151545_607115_5455DD09) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Sender: linux-wireless-owner@vger.kernel.org List-ID: This patch adds new NL80211_CMD_SET_WIPHY attributes NL80211_ATTR_WIPHY_FREQ and NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET to allow userspace to set the operating channel (e.g., hostapd for AP mode). Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Index: wireless-testing/include/linux/nl80211.h =================================================================== --- wireless-testing.orig/include/linux/nl80211.h 2008-11-26 15:15:31.000000000 +0200 +++ wireless-testing/include/linux/nl80211.h 2008-11-26 15:16:59.000000000 +0200 @@ -26,8 +26,9 @@ * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request * to get a list of all present wiphys. * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or - * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME - * and/or %NL80211_ATTR_WIPHY_TXQ_PARAMS. + * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, and/or + * %NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET. * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request * or rename notification. Has attributes %NL80211_ATTR_WIPHY and * %NL80211_ATTR_WIPHY_NAME. @@ -180,6 +181,14 @@ * /sys/class/ieee80211//index * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET: included with NL80211_ATTR_WIPHY_FREQ + * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * NL80211_SEC_CHAN_NO_HT = HT not allowed (i.e., same as not including + * this attribute) + * NL80211_SEC_CHAN_DISABLED = HT20 only + * NL80211_SEC_CHAN_BELOW = secondary channel is below the primary channel + * NL80211_SEC_CHAN_ABOVE = secondary channel is above the primary channel * * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on * @NL80211_ATTR_IFNAME: network interface name @@ -315,6 +324,8 @@ NL80211_ATTR_BSS_BASIC_RATES, NL80211_ATTR_WIPHY_TXQ_PARAMS, + NL80211_ATTR_WIPHY_FREQ, + NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET, /* add attributes here, update the policy in nl80211.c */ @@ -329,6 +340,8 @@ #define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY #define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS +#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ +#define NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 @@ -742,4 +755,10 @@ NL80211_TXQ_Q_BK }; +enum nl80211_sec_chan_offset { + NL80211_SEC_CHAN_NO_HT /* No HT */, + NL80211_SEC_CHAN_DISABLED /* HT20 only */, + NL80211_SEC_CHAN_BELOW /* HT40- */, + NL80211_SEC_CHAN_ABOVE /* HT40+ */ +}; #endif /* __LINUX_NL80211_H */ Index: wireless-testing/include/net/cfg80211.h =================================================================== --- wireless-testing.orig/include/net/cfg80211.h 2008-11-26 15:15:31.000000000 +0200 +++ wireless-testing/include/net/cfg80211.h 2008-11-26 15:29:50.000000000 +0200 @@ -392,6 +392,9 @@ /* from net/wireless.h */ struct wiphy; +/* from net/ieee80211.h */ +struct ieee80211_channel; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -450,6 +453,8 @@ * @change_bss: Modify parameters for a given BSS. * * @set_txq_params: Set TX queue parameters + * + * @set_channel: Set channel */ struct cfg80211_ops { int (*add_virtual_intf)(struct wiphy *wiphy, char *name, @@ -513,6 +518,10 @@ int (*set_txq_params)(struct wiphy *wiphy, struct ieee80211_txq_params *params); + + int (*set_channel)(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_sec_chan_offset); }; #endif /* __NET_CFG80211_H */ Index: wireless-testing/net/mac80211/cfg.c =================================================================== --- wireless-testing.orig/net/mac80211/cfg.c 2008-11-26 15:15:31.000000000 +0200 +++ wireless-testing/net/mac80211/cfg.c 2008-11-26 15:19:34.000000000 +0200 @@ -1095,6 +1095,18 @@ return 0; } +static int ieee80211_set_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_sec_chan_offset sec_chan_offset) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + local->oper_channel = chan; + local->oper_sec_chan_offset = sec_chan_offset; + + return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1122,4 +1134,5 @@ #endif .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, + .set_channel = ieee80211_set_channel, }; Index: wireless-testing/net/wireless/nl80211.c =================================================================== --- wireless-testing.orig/net/wireless/nl80211.c 2008-11-26 15:15:31.000000000 +0200 +++ wireless-testing/net/wireless/nl80211.c 2008-11-26 15:31:31.000000000 +0200 @@ -59,6 +59,8 @@ [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, .len = BUS_ID_SIZE-1 }, [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, + [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET] = { .type = NLA_U32 }, [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, @@ -359,6 +361,61 @@ } } + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + enum nl80211_sec_chan_offset sec_chan_offset = + NL80211_SEC_CHAN_NO_HT; + struct ieee80211_channel *chan; + u32 freq, sec_freq; + + if (!rdev->ops->set_channel) { + result = -EOPNOTSUPP; + goto bad_res; + } + + if (info->attrs[NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]) { + sec_chan_offset = nla_get_u32( + info->attrs[ + NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]); + if (sec_chan_offset != NL80211_SEC_CHAN_NO_HT && + sec_chan_offset != NL80211_SEC_CHAN_DISABLED && + sec_chan_offset != NL80211_SEC_CHAN_BELOW && + sec_chan_offset != NL80211_SEC_CHAN_ABOVE) { + result = -EINVAL; + goto bad_res; + } + } + + freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); + chan = ieee80211_get_channel(&rdev->wiphy, freq); + if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { + /* Primary channel not allowed */ + result = -EINVAL; + goto bad_res; + } + if (sec_chan_offset == NL80211_SEC_CHAN_BELOW) + sec_freq = freq - 20; + else if (sec_chan_offset == NL80211_SEC_CHAN_ABOVE) + sec_freq = freq + 20; + else + sec_freq = 0; + + if (sec_freq) { + struct ieee80211_channel *schan; + schan = ieee80211_get_channel(&rdev->wiphy, sec_freq); + if (!schan || schan->flags & IEEE80211_CHAN_DISABLED) { + /* Secondary channel not allowed */ + result = -EINVAL; + goto bad_res; + } + } + + result = rdev->ops->set_channel(&rdev->wiphy, chan, + sec_chan_offset); + if (result) + goto bad_res; + } + + bad_res: cfg80211_put_dev(rdev); return result; Index: wireless-testing/include/net/mac80211.h =================================================================== --- wireless-testing.orig/include/net/mac80211.h 2008-11-26 15:15:31.000000000 +0200 +++ wireless-testing/include/net/mac80211.h 2008-11-26 15:15:47.000000000 +0200 @@ -507,6 +507,9 @@ struct ieee80211_ht_conf { bool enabled; + int sec_chan_offset; /* 0 = HT40 disabled; -1 = HT40 enabled, secondary + * channel below primary; 1 = HT40 enabled, + * secondary channel above primary */ }; /** Index: wireless-testing/net/mac80211/util.c =================================================================== --- wireless-testing.orig/net/mac80211/util.c 2008-11-26 15:15:31.000000000 +0200 +++ wireless-testing/net/mac80211/util.c 2008-11-26 15:20:26.000000000 +0200 @@ -641,6 +641,7 @@ chan->flags & IEEE80211_CHAN_NO_IBSS) return ret; local->oper_channel = chan; + local->oper_sec_chan_offset = NL80211_SEC_CHAN_NO_HT; if (local->sw_scanning || local->hw_scanning) ret = 0; Index: wireless-testing/net/mac80211/ieee80211_i.h =================================================================== --- wireless-testing.orig/net/mac80211/ieee80211_i.h 2008-11-26 15:15:31.000000000 +0200 +++ wireless-testing/net/mac80211/ieee80211_i.h 2008-11-26 15:20:12.000000000 +0200 @@ -626,6 +626,7 @@ struct delayed_work scan_work; struct ieee80211_sub_if_data *scan_sdata; struct ieee80211_channel *oper_channel, *scan_channel; + enum nl80211_sec_chan_offset oper_sec_chan_offset; u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; size_t scan_ssid_len; struct list_head bss_list; Index: wireless-testing/net/mac80211/main.c =================================================================== --- wireless-testing.orig/net/mac80211/main.c 2008-11-26 15:15:31.000000000 +0200 +++ wireless-testing/net/mac80211/main.c 2008-11-26 15:29:09.000000000 +0200 @@ -195,20 +195,42 @@ struct ieee80211_channel *chan; int ret = 0; int power; + enum nl80211_sec_chan_offset sec_chan_offset; might_sleep(); - if (local->sw_scanning) + if (local->sw_scanning) { chan = local->scan_channel; - else + sec_chan_offset = NL80211_SEC_CHAN_NO_HT; + } else { chan = local->oper_channel; + sec_chan_offset = local->oper_sec_chan_offset; + } - if (chan != local->hw.conf.channel) { + if (chan != local->hw.conf.channel || + sec_chan_offset != local->hw.conf.ht.sec_chan_offset) { local->hw.conf.channel = chan; + switch (sec_chan_offset) { + case NL80211_SEC_CHAN_NO_HT: + local->hw.conf.ht.enabled = false; + local->hw.conf.ht.sec_chan_offset = 0; + break; + case NL80211_SEC_CHAN_DISABLED: + local->hw.conf.ht.enabled = true; + local->hw.conf.ht.sec_chan_offset = 0; + break; + case NL80211_SEC_CHAN_BELOW: + local->hw.conf.ht.enabled = true; + local->hw.conf.ht.sec_chan_offset = -1; + break; + case NL80211_SEC_CHAN_ABOVE: + local->hw.conf.ht.enabled = true; + local->hw.conf.ht.sec_chan_offset = 1; + break; + } changed |= IEEE80211_CONF_CHANGE_CHANNEL; } - if (!local->hw.conf.power_level) power = chan->max_power; else -- Jouni Malinen PGP id EFC895FA