Search Linux Wireless

[PATCH 2/3] nl80211: Add support for EDMG channels

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



802.11ay specification defines Enhanced Directional Multi-Gigabit
(EDMG) STA and SAP which allow channel bonding of 2 channels and more.
Introduce NL80211_BAND_ATTR_EDMG, NL80211_BAND_ATTR_EDMG_SUPPORTED_CHAN,
NL80211_ATTR_WIPHY_EDMG, NL80211_ATTR_WIPHY_EDMG_CHANNEL and
RATE_INFO_FLAGS_EDMG that needed for enabling and configuring
EDMG support.
Driver is expected to report its EDMG capabilities: whether EDMG
is supported and the supported EDMG channels.
Bitrate calculation is enhanced to take into account EDMG support
according to the 802.11ay specification.
The kernel uses NL80211_BAND_ATTR_EDMG and
NL80211_BAND_ATTR_EDMG_SUPPORTED_CHAN attributes in order to publish
the EDMG capabilities to the userspace, NL80211_BAND_ATTR_EDMG is set
if EDMG is supported and NL80211_BAND_ATTR_EDMG_SUPPORTED_CHAN
contains list of supported EDMG channels.
NL80211_ATTR_WIPHY_EDMG and NL80211_ATTR_WIPHY_EDMG_CHANNEL will be
used by the userspace for AP configuration and connect command.

Signed-off-by: Alexei Avshalom Lazar <ailizaro@xxxxxxxxxxxxxx>
---
 drivers/net/wireless/ath/wil6210/cfg80211.c |  2 +-
 include/net/cfg80211.h                      | 48 +++++++++++++++-
 include/uapi/linux/nl80211.h                | 12 ++++
 net/wireless/chan.c                         | 88 +++++++++++++++++++++++++++++
 net/wireless/nl80211.c                      | 33 +++++++++++
 net/wireless/util.c                         | 42 +++++++++++++-
 6 files changed, 219 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index e63b078..4740b53 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -311,7 +311,7 @@ int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
 			BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
 			BIT_ULL(NL80211_STA_INFO_TX_FAILED);
 
-	sinfo->txrate.flags = RATE_INFO_FLAGS_60G;
+	sinfo->txrate.flags = RATE_INFO_FLAGS_DMG;
 	sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
 	sinfo->rxrate.mcs = stats->last_mcs_rx;
 	sinfo->rx_bytes = stats->rx_bytes;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 73ca446..10f9d76 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -321,6 +321,24 @@ struct ieee80211_sband_iftype_data {
 };
 
 /**
+ * struct ieee80211_sta_edmg_cap - EDMG capabilities
+ *
+ * This structure describes most essential parameters needed
+ * to describe 802.11ay EDMG capabilities
+ *
+ * @supported: is EDMG supported, Device may support EDMG
+ *	without supporting channel bonding. In this case
+ *	supported would be TRUE with n_channels = 0
+ * @channels: supported ieee EDMG channel numbers
+ * @n_channels: Number of channels in @channels
+ */
+struct ieee80211_sta_edmg_cap {
+	bool supported;
+	u8 *channels;
+	int n_channels;
+};
+
+/**
  * struct ieee80211_supported_band - frequency band definition
  *
  * This structure describes a frequency band a wiphy
@@ -336,6 +354,7 @@ struct ieee80211_sband_iftype_data {
  * @n_bitrates: Number of bitrates in @bitrates
  * @ht_cap: HT capabilities in this band
  * @vht_cap: VHT capabilities in this band
+ * @edmg_cap: EDMG capabilities in this band
  * @n_iftype_data: number of iftype data entries
  * @iftype_data: interface type data entries.  Note that the bits in
  *	@types_mask inside this structure cannot overlap (i.e. only
@@ -350,6 +369,7 @@ struct ieee80211_supported_band {
 	int n_bitrates;
 	struct ieee80211_sta_ht_cap ht_cap;
 	struct ieee80211_sta_vht_cap vht_cap;
+	struct ieee80211_sta_edmg_cap edmg_cap;
 	u16 n_iftype_data;
 	const struct ieee80211_sband_iftype_data *iftype_data;
 };
@@ -501,12 +521,16 @@ struct key_params {
  * @center_freq1: center frequency of first segment
  * @center_freq2: center frequency of second segment
  *	(only with 80+80 MHz)
+ * @edmg_mode: if defined, edmg supported and primary channel is EDMG
+ * @edmg_channel: the EDMG channel
  */
 struct cfg80211_chan_def {
 	struct ieee80211_channel *chan;
 	enum nl80211_chan_width width;
 	u32 center_freq1;
 	u32 center_freq2;
+	bool edmg_mode;
+	u8 edmg_channel;
 };
 
 /**
@@ -658,6 +682,18 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
 }
 
 /**
+ * cfg80211_edmg_usable - check if the EDMG channel can be used
+ * @wiphy: the wiphy
+ * @edmg_cap: EDMG capabilities in this band
+ * @edmg_channel: the EDMG channel that need to be verified
+ * @primary_channel: The primary channel for the EDMG channel
+ * Return: %true the EDMG channel is usable. %false otherwise.
+ */
+bool cfg80211_edmg_usable(struct wiphy *wiphy,
+			  struct ieee80211_sta_edmg_cap *edmg_cap,
+			  u8 edmg_channel, int primary_channel);
+
+/**
  * enum survey_info_flags - survey information flags
  *
  * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
@@ -1090,15 +1126,17 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @RATE_INFO_FLAGS_MCS: mcs field filled with HT MCS
  * @RATE_INFO_FLAGS_VHT_MCS: mcs field filled with VHT MCS
  * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
- * @RATE_INFO_FLAGS_60G: 60GHz MCS
+ * @RATE_INFO_FLAGS_DMG: 60GHz MCS
  * @RATE_INFO_FLAGS_HE_MCS: HE MCS information
+ * @RATE_INFO_FLAGS_EDMG: 60GHz MCS in EDMG mode
  */
 enum rate_info_flags {
 	RATE_INFO_FLAGS_MCS			= BIT(0),
 	RATE_INFO_FLAGS_VHT_MCS			= BIT(1),
 	RATE_INFO_FLAGS_SHORT_GI		= BIT(2),
-	RATE_INFO_FLAGS_60G			= BIT(3),
+	RATE_INFO_FLAGS_DMG			= BIT(3),
 	RATE_INFO_FLAGS_HE_MCS			= BIT(4),
+	RATE_INFO_FLAGS_EDMG			= BIT(5),
 };
 
 /**
@@ -1138,6 +1176,7 @@ enum rate_info_bw {
  * @he_dcm: HE DCM value
  * @he_ru_alloc: HE RU allocation (from &enum nl80211_he_ru_alloc,
  *	only valid if bw is %RATE_INFO_BW_HE_RU)
+ * @n_bonded_ch: In case of EDMG the number of bonded channels (1-4)
  */
 struct rate_info {
 	u8 flags;
@@ -1148,6 +1187,7 @@ struct rate_info {
 	u8 he_gi;
 	u8 he_dcm;
 	u8 he_ru_alloc;
+	u8 n_bonded_ch;
 };
 
 /**
@@ -2285,6 +2325,8 @@ struct cfg80211_bss_selection {
  * @fils_erp_rrk_len: Length of @fils_erp_rrk in octets.
  * @want_1x: indicates user-space supports and wants to use 802.1X driver
  *	offload of 4-way handshake.
+ * @edmg: enable EDMG mode.
+ * @edmg_channel: The EDMG channel to use.
  */
 struct cfg80211_connect_params {
 	struct ieee80211_channel *channel;
@@ -2318,6 +2360,8 @@ struct cfg80211_connect_params {
 	const u8 *fils_erp_rrk;
 	size_t fils_erp_rrk_len;
 	bool want_1x;
+	bool edmg;
+	u8 edmg_channel;
 };
 
 /**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 0239896..31b1312 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2240,6 +2240,9 @@ enum nl80211_commands {
  * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from
  *	association request when used with NL80211_CMD_NEW_STATION). Can be set
  *	only if %NL80211_STA_FLAG_WME is set.
+ * @NL80211_ATTR_WIPHY_EDMG: flag attribute. If set it means EDMG mode supported
+ * @NL80211_ATTR_WIPHY_EDMG_CHANNEL: EDMG channel to be used for AP
+ *      configuration and connect command.
  *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
@@ -2682,6 +2685,9 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_HE_CAPABILITY,
 
+	NL80211_ATTR_WIPHY_EDMG,
+	NL80211_ATTR_WIPHY_EDMG_CHANNEL,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3265,6 +3271,9 @@ enum nl80211_band_iftype_attr {
  * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
  * @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using
  *	attributes from &enum nl80211_band_iftype_attr
+ * @NL80211_BAND_ATTR_EDMG: flag attribute. If set it means EDMG mode supported
+ * @NL80211_BAND_ATTR_EDMG_SUPPORTED_CHAN: array of supported EDMG channels in
+ *	this band
  * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
  * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
  */
@@ -3282,6 +3291,9 @@ enum nl80211_band_attr {
 	NL80211_BAND_ATTR_VHT_CAPA,
 	NL80211_BAND_ATTR_IFTYPE_DATA,
 
+	NL80211_BAND_ATTR_EDMG,
+	NL80211_BAND_ATTR_EDMG_SUPPORTED_CHAN,
+
 	/* keep last */
 	__NL80211_BAND_ATTR_AFTER_LAST,
 	NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 2db713d..681f434 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -720,12 +720,94 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
 	return true;
 }
 
+static const struct edmg_chan_table {
+	/* the edmg channel - 9,10,11... */
+	u8 edmg_chan;
+	/* the sub channels represented as a bitfield where the bit-index
+	 * corresponds to the legacy channel (bit 0 not used).
+	 */
+	u8 sub_chans;
+} cfg80211_edmg_table[] = {
+	{9, 0x06},	/* channels 1,2 */
+	{10, 0x0c},	/* channels 2,3 */
+	{11, 0x18},	/* channels 3,4 */
+	{12, 0x30},	/* channels 4,5 */
+	{13, 0x60},	/* channels 5,6 */
+	{17, 0x0e},	/* channels 1,2,3 */
+	{18, 0x1c},	/* channels 2,3,4 */
+	{19, 0x38},	/* channels 3,4,5 */
+	{20, 0x70},	/* channels 4,5,6 */
+	{25, 0x1e},	/* channels 1,2,3,4 */
+	{26, 0x3c},	/* channels 2,3,4,5 */
+	{27, 0x78},	/* channels 3,4,5,6 */
+};
+
+static u8 cfg80211_get_edmg_sub_chans(u8 edmg_channel)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cfg80211_edmg_table); i++)
+		if (cfg80211_edmg_table[i].edmg_chan == edmg_channel)
+			return cfg80211_edmg_table[i].sub_chans;
+
+	return 0;
+}
+
+static bool cfg80211_check_edmg_sub_ch(struct wiphy *wiphy,
+				       u8 edmg_channel,
+				       int primary_channel)
+{
+	struct ieee80211_channel *chan;
+	int i, freq;
+	u8 sub_channels;
+
+	sub_channels = cfg80211_get_edmg_sub_chans(edmg_channel);
+	if (!sub_channels)
+		return false;
+
+	if (!(sub_channels & BIT(primary_channel)))
+		return false;
+
+	/* 60GHz channels 1..6 */
+	for (i = 1; i <= 6; i++) {
+		if (!(sub_channels & BIT(i)))
+			continue;
+
+		freq = ieee80211_channel_to_frequency(i, NL80211_BAND_60GHZ);
+		chan = ieee80211_get_channel(wiphy, freq);
+		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+			return false;
+	}
+
+	return true;
+}
+
+bool cfg80211_edmg_usable(struct wiphy *wiphy,
+			  struct ieee80211_sta_edmg_cap *edmg_cap,
+			  u8 edmg_channel, int primary_channel)
+{
+	int i;
+
+	if (!edmg_channel)
+		return true;
+
+	for (i = 0; i < edmg_cap->n_channels; i++)
+		if (edmg_channel == edmg_cap->channels[i])
+			break;
+
+	if (i == edmg_cap->n_channels)
+		return false;
+
+	return cfg80211_check_edmg_sub_ch(wiphy, edmg_channel, primary_channel);
+}
+
 bool cfg80211_chandef_usable(struct wiphy *wiphy,
 			     const struct cfg80211_chan_def *chandef,
 			     u32 prohibited_flags)
 {
 	struct ieee80211_sta_ht_cap *ht_cap;
 	struct ieee80211_sta_vht_cap *vht_cap;
+	struct ieee80211_sta_edmg_cap *edmg_cap;
 	u32 width, control_freq, cap;
 
 	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
@@ -733,6 +815,12 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
 
 	ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
 	vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
+	edmg_cap = &wiphy->bands[chandef->chan->band]->edmg_cap;
+
+	if (edmg_cap->supported &&
+	    !cfg80211_edmg_usable(wiphy, edmg_cap, chandef->edmg_channel,
+				  chandef->chan->hw_value))
+		return false;
 
 	control_freq = chandef->chan->center_freq;
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5fb9b7d..043b46f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -209,6 +209,8 @@ enum nl80211_multicast_groups {
 
 	[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
 	[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
+	[NL80211_ATTR_WIPHY_EDMG] = { .type = NLA_FLAG },
+	[NL80211_ATTR_WIPHY_EDMG_CHANNEL] = { .type = NLA_U8 },
 	[NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
 	[NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
 	[NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
@@ -1409,6 +1411,13 @@ static int nl80211_send_band_rateinfo(struct sk_buff *msg,
 		nla_nest_end(msg, nl_iftype_data);
 	}
 
+	/* add EDMG info */
+	if (sband->edmg_cap.supported &&
+	    (nla_put_flag(msg, NL80211_BAND_ATTR_EDMG) ||
+	     nla_put(msg, NL80211_BAND_ATTR_EDMG_SUPPORTED_CHAN,
+		     sband->edmg_cap.n_channels, sband->edmg_cap.channels)))
+		return -ENOBUFS;
+
 	/* add bitrates */
 	nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
 	if (!nl_rates)
@@ -2303,6 +2312,8 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
 	chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
 	chandef->center_freq1 = control_freq;
 	chandef->center_freq2 = 0;
+	chandef->edmg_mode = 0;
+	chandef->edmg_channel = 0;
 
 	/* Primary channel not allowed */
 	if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
@@ -2347,6 +2358,14 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
 					info->attrs[NL80211_ATTR_CENTER_FREQ2]);
 	}
 
+	if (info->attrs[NL80211_ATTR_WIPHY_EDMG])
+		chandef->edmg_mode =
+			nla_get_flag(info->attrs[NL80211_ATTR_WIPHY_EDMG]);
+
+	if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNEL])
+		chandef->edmg_channel =
+		       nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNEL]);
+
 	if (!cfg80211_chandef_valid(chandef))
 		return -EINVAL;
 
@@ -9302,6 +9321,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
 	struct cfg80211_connect_params connect;
 	struct wiphy *wiphy;
 	struct cfg80211_cached_keys *connkeys = NULL;
+	struct ieee80211_sta_edmg_cap *edmg_cap;
 	int err;
 
 	memset(&connect, 0, sizeof(connect));
@@ -9392,6 +9412,19 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
 			return -EINVAL;
 	}
 
+	if (info->attrs[NL80211_ATTR_WIPHY_EDMG]) {
+		connect.edmg =
+			nla_get_flag(info->attrs[NL80211_ATTR_WIPHY_EDMG]);
+		if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNEL])
+			connect.edmg_channel =
+		       nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNEL]);
+		edmg_cap = &rdev->wiphy.bands[NL80211_BAND_60GHZ]->edmg_cap;
+		if (edmg_cap && connect.edmg &&
+		    !cfg80211_edmg_usable(wiphy, edmg_cap, connect.edmg_channel,
+					  connect.channel->hw_value))
+			connect.edmg_channel = 0;
+	}
+
 	if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
 		connkeys = nl80211_parse_connkeys(rdev, info, NULL);
 		if (IS_ERR(connkeys))
diff --git a/net/wireless/util.c b/net/wireless/util.c
index a450736..08b43a2 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1009,7 +1009,7 @@ static u32 cfg80211_calculate_bitrate_ht(struct rate_info *rate)
 	return (bitrate + 50000) / 100000;
 }
 
-static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
+static u32 cfg80211_calculate_bitrate_dmg(struct rate_info *rate)
 {
 	static const u32 __mcs2bitrate[] = {
 		/* control PHY */
@@ -1056,6 +1056,40 @@ static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
 	return __mcs2bitrate[rate->mcs];
 }
 
+static u32 cfg80211_calculate_bitrate_edmg(struct rate_info *rate)
+{
+	static const u32 __mcs2bitrate[] = {
+		/* control PHY */
+		[0] =   275,
+		/* SC PHY */
+		[1] =  3850,
+		[2] =  7700,
+		[3] =  9625,
+		[4] = 11550,
+		[5] = 12512, /* 1251.25 mbps */
+		[6] = 13475,
+		[7] = 15400,
+		[8] = 19250,
+		[9] = 23100,
+		[10] = 25025,
+		[11] = 26950,
+		[12] = 30800,
+		[13] = 38500,
+		[14] = 46200,
+		[15] = 50050,
+		[16] = 53900,
+		[17] = 57750,
+		[18] = 69300,
+		[19] = 75075,
+		[20] = 80850,
+	};
+
+	if (WARN_ON_ONCE(rate->mcs >= ARRAY_SIZE(__mcs2bitrate)))
+		return 0;
+
+	return __mcs2bitrate[rate->mcs] * rate->n_bonded_ch;
+}
+
 static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
 {
 	static const u32 base[4][10] = {
@@ -1226,8 +1260,10 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
 {
 	if (rate->flags & RATE_INFO_FLAGS_MCS)
 		return cfg80211_calculate_bitrate_ht(rate);
-	if (rate->flags & RATE_INFO_FLAGS_60G)
-		return cfg80211_calculate_bitrate_60g(rate);
+	if (rate->flags & RATE_INFO_FLAGS_DMG)
+		return cfg80211_calculate_bitrate_dmg(rate);
+	if (rate->flags & RATE_INFO_FLAGS_EDMG)
+		return cfg80211_calculate_bitrate_edmg(rate);
 	if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
 		return cfg80211_calculate_bitrate_vht(rate);
 	if (rate->flags & RATE_INFO_FLAGS_HE_MCS)
-- 
1.9.1




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux