* Re: [PATCH v2 3/4] cfg80211: reg: remove support for built-in regdb
From: Benjamin Beichler @ 2017-11-13 14:48 UTC (permalink / raw)
To: Johannes Berg, linux-wireless
In-Reply-To: <1510584253.30497.45.camel@sipsolutions.net>
[-- Attachment #1: Type: text/plain, Size: 855 bytes --]
Am 13.11.2017 um 15:44 schrieb Johannes Berg:
> On Mon, 2017-11-13 at 15:41 +0100, Benjamin Beichler wrote:
>> I use an Alpine Userspace with no proper CRDA
> Ok.
>
>> and I'm not able to
>> disable signature checking. I deactivated
>> CONFIG_CFG80211_REQUIRE_SIGNED_REGDB, but the load of regulatory.db
>> fails. I thought this is intentionally, or do I need any other Configs ?
> This should work, as long as you can actually properly
> unset CONFIG_CFG80211_REQUIRE_SIGNED_REGDB, which requires that you set
> CONFIG_CFG80211_CERTIFICATION_ONUS.
>
> johannes
Then this may be a bug, since the loading the regulatory.db fails
(platform regulatory.0: Direct firmware load for regulatory.db failed
with error -2) also, when CONFIG_CFG80211_REQUIRE_SIGNED_REGDB is not
set (and of course CONFIG_CFG80211_CERTIFICATION_ONUS is set)
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5151 bytes --]
^ permalink raw reply
* Re: [PATCH v2 3/4] cfg80211: reg: remove support for built-in regdb
From: Johannes Berg @ 2017-11-13 14:36 UTC (permalink / raw)
To: Benjamin Beichler, Seth Forshee; +Cc: linux-wireless
In-Reply-To: <fffbdcf1-631e-d0de-836d-e95266afa686@uni-rostock.de>
On Mon, 2017-11-13 at 15:34 +0100, Benjamin Beichler wrote:
> Am 13.11.2017 um 14:36 schrieb Seth Forshee:
> > On Mon, Nov 13, 2017 at 10:23:09AM +0100, Johannes Berg wrote:
> > > On Mon, 2017-11-13 at 10:19 +0100, Benjamin Beichler wrote:
> > > > Is there already an example regulatory.db file available?
> > >
> > > There isn't yet, Seth?
> >
> > Not yet. I've done some work on the patch you sent to make it generate
> > db files in both formats, not sure I'm ready to start actually releasing
> > files in the new format yet though. I suppose I could push it to a
> > testing branch with a signed regulatory.db file.
>
> I would really appreciate this, since my current tool chain is not
> appropriate for this whole signing stuff and hwsim is failing to set my
> standard frequencies, because of the missing regdb.
You should be able to continue to use CRDA?
And if you don't, you can just disable signature checking and build the
binary yourself?
johannes
^ permalink raw reply
* [PATCH] nl80211: add a few extended error strings to key parsing
From: Johannes Berg @ 2017-11-13 14:35 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This mostly serves as an example for how to add error strings
and erroneous attribute pointers.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/wireless/nl80211.c | 61 +++++++++++++++++++++++++++++++++-----------------
1 file changed, 41 insertions(+), 20 deletions(-)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d23eb5700788..73f0e5524e84 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -734,11 +734,12 @@ struct key_parse {
bool def_uni, def_multi;
};
-static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
+static int nl80211_parse_key_new(struct genl_info *info, struct nlattr *key,
+ struct key_parse *k)
{
struct nlattr *tb[NL80211_KEY_MAX + 1];
int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
- nl80211_key_policy, NULL);
+ nl80211_key_policy, info->extack);
if (err)
return err;
@@ -771,7 +772,8 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
if (tb[NL80211_KEY_TYPE]) {
k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
- return -EINVAL;
+ return genl_err_attr(info, -EINVAL,
+ tb[NL80211_KEY_TYPE]);
}
if (tb[NL80211_KEY_DEFAULT_TYPES]) {
@@ -779,7 +781,8 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
tb[NL80211_KEY_DEFAULT_TYPES],
- nl80211_key_default_policy, NULL);
+ nl80211_key_default_policy,
+ info->extack);
if (err)
return err;
@@ -820,8 +823,10 @@ static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
- if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
+ if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) {
+ GENL_SET_ERR_MSG(info, "key type out of range");
return -EINVAL;
+ }
}
if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
@@ -850,31 +855,42 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
k->type = -1;
if (info->attrs[NL80211_ATTR_KEY])
- err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
+ err = nl80211_parse_key_new(info, info->attrs[NL80211_ATTR_KEY], k);
else
err = nl80211_parse_key_old(info, k);
if (err)
return err;
- if (k->def && k->defmgmt)
+ if (k->def && k->defmgmt) {
+ GENL_SET_ERR_MSG(info, "key with def && defmgmt is invalid");
return -EINVAL;
+ }
if (k->defmgmt) {
- if (k->def_uni || !k->def_multi)
+ if (k->def_uni || !k->def_multi) {
+ GENL_SET_ERR_MSG(info, "defmgmt key must be mcast");
return -EINVAL;
+ }
}
if (k->idx != -1) {
if (k->defmgmt) {
- if (k->idx < 4 || k->idx > 5)
+ if (k->idx < 4 || k->idx > 5) {
+ GENL_SET_ERR_MSG(info,
+ "defmgmt key idx not 4 or 5");
return -EINVAL;
+ }
} else if (k->def) {
- if (k->idx < 0 || k->idx > 3)
+ if (k->idx < 0 || k->idx > 3) {
+ GENL_SET_ERR_MSG(info, "def key idx not 0-3");
return -EINVAL;
+ }
} else {
- if (k->idx < 0 || k->idx > 5)
+ if (k->idx < 0 || k->idx > 5) {
+ GENL_SET_ERR_MSG(info, "key idx not 0-5");
return -EINVAL;
+ }
}
}
@@ -883,8 +899,9 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
static struct cfg80211_cached_keys *
nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
- struct nlattr *keys, bool *no_ht)
+ struct genl_info *info, bool *no_ht)
{
+ struct nlattr *keys = info->attrs[NL80211_ATTR_KEYS];
struct key_parse parse;
struct nlattr *key;
struct cfg80211_cached_keys *result;
@@ -909,17 +926,22 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
memset(&parse, 0, sizeof(parse));
parse.idx = -1;
- err = nl80211_parse_key_new(key, &parse);
+ err = nl80211_parse_key_new(info, key, &parse);
if (err)
goto error;
err = -EINVAL;
if (!parse.p.key)
goto error;
- if (parse.idx < 0 || parse.idx > 3)
+ if (parse.idx < 0 || parse.idx > 3) {
+ GENL_SET_ERR_MSG(info, "key index out of range [0-3]");
goto error;
+ }
if (parse.def) {
- if (def)
+ if (def) {
+ GENL_SET_ERR_MSG(info,
+ "only one key can be default");
goto error;
+ }
def = 1;
result->def = parse.idx;
if (!parse.def_uni || !parse.def_multi)
@@ -932,6 +954,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
goto error;
if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 &&
parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) {
+ GENL_SET_ERR_MSG(info, "connect key must be WEP");
err = -EINVAL;
goto error;
}
@@ -947,6 +970,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
if (result->def < 0) {
err = -EINVAL;
+ GENL_SET_ERR_MSG(info, "need a default/TX key");
goto error;
}
@@ -8611,9 +8635,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
bool no_ht = false;
- connkeys = nl80211_parse_connkeys(rdev,
- info->attrs[NL80211_ATTR_KEYS],
- &no_ht);
+ connkeys = nl80211_parse_connkeys(rdev, info, &no_ht);
if (IS_ERR(connkeys))
return PTR_ERR(connkeys);
@@ -9017,8 +9039,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
}
if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
- connkeys = nl80211_parse_connkeys(rdev,
- info->attrs[NL80211_ATTR_KEYS], NULL);
+ connkeys = nl80211_parse_connkeys(rdev, info, NULL);
if (IS_ERR(connkeys))
return PTR_ERR(connkeys);
}
--
2.14.2
^ permalink raw reply related
* Re: [PATCH v2 3/4] cfg80211: reg: remove support for built-in regdb
From: Benjamin Beichler @ 2017-11-13 14:34 UTC (permalink / raw)
To: Seth Forshee, Johannes Berg; +Cc: linux-wireless
In-Reply-To: <20171113133609.yfysyojr2mwlahfl@ubuntu-hedt>
[-- Attachment #1: Type: text/plain, Size: 1182 bytes --]
Am 13.11.2017 um 14:36 schrieb Seth Forshee:
> On Mon, Nov 13, 2017 at 10:23:09AM +0100, Johannes Berg wrote:
>> On Mon, 2017-11-13 at 10:19 +0100, Benjamin Beichler wrote:
>>> Is there already an example regulatory.db file available?
>> There isn't yet, Seth?
> Not yet. I've done some work on the patch you sent to make it generate
> db files in both formats, not sure I'm ready to start actually releasing
> files in the new format yet though. I suppose I could push it to a
> testing branch with a signed regulatory.db file.
I would really appreciate this, since my current tool chain is not
appropriate for this whole signing stuff and hwsim is failing to set my
standard frequencies, because of the missing regdb.
> Seth
--
M.Sc. Benjamin Beichler
Universität Rostock, Fakultät für Informatik und Elektrotechnik
Institut für Angewandte Mikroelektronik und Datentechnik
University of Rostock, Department of CS and EE
Institute of Applied Microelectronics and CE
Richard-Wagner-Straße 31
18119 Rostock
Deutschland/Germany
phone: +49 (0) 381 498 - 7278
email: Benjamin.Beichler@uni-rostock.de
www: http://www.imd.uni-rostock.de/
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5151 bytes --]
^ permalink raw reply
* Re: [PATCH v2 3/4] cfg80211: reg: remove support for built-in regdb
From: Seth Forshee @ 2017-11-13 13:36 UTC (permalink / raw)
To: Johannes Berg; +Cc: Benjamin Beichler, linux-wireless
In-Reply-To: <1510564989.30497.5.camel@sipsolutions.net>
On Mon, Nov 13, 2017 at 10:23:09AM +0100, Johannes Berg wrote:
> On Mon, 2017-11-13 at 10:19 +0100, Benjamin Beichler wrote:
> >
> > Is there already an example regulatory.db file available?
>
> There isn't yet, Seth?
Not yet. I've done some work on the patch you sent to make it generate
db files in both formats, not sure I'm ready to start actually releasing
files in the new format yet though. I suppose I could push it to a
testing branch with a signed regulatory.db file.
Seth
^ permalink raw reply
* XD3200 (QCA9563 + PA) ath9k TX low
From: David Gyimesi @ 2017-11-13 12:46 UTC (permalink / raw)
To: linux-wireless
Dear Developers,
I have a Yuncore XD3200 device contains QCA9563 and ext. PA
(SKY65174-21). I would like to use LEDE however the TX is very low on
2.4 GHz.
I experience the following on the client (AP TX-Power 20 dBm 100mW):
- with QSDK (the device's factory software) the TX level is -40 dBm
- with LEDE 17.01.4 (ath9k) the TX level is -51 dBm
In the following post there is workaround mentioned to fix the issue on LEDE.
QCA9561 PA bias level: set AR_CH0_TOP_XPABIASLVL instead of
AR_CH0_TOP2_XPABIASLVL
https://forum.lede-project.org/t/xd3200-qca9563-slow-ath9k-2-4ghz-lna-pa/1320
https://github.com/oe1rfc/lede-qca9561-pa-bias/commit/88dcdee6b0b86d489afb9bc54010c04063695e5e
@@ -0,0 +1,15 @@
+--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+@@ -3538,9 +3538,10 @@ static void ar9003_hw_xpa_bias_level_app
+ int bias = ar9003_modal_header(ah, is2ghz)->xpaBiasLvl;
+
+ if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
+- AR_SREV_9531(ah) || AR_SREV_9561(ah))
++ AR_SREV_9531(ah))
+ REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias);
+- else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah))
++ else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah) ||
++ AR_SREV_9561(ah))
+ REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias);
+ else {
+ REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias);
And indeed it seems the patch fixes the problem. TX level becomes the
same on LEDE as on QSDK.
Could you please help me if it is a ath9k issue or there is a problem
with the XD3200 device?
Thank you!
David
^ permalink raw reply
* Re: [RFC PATCH 2/2] nl80211: implement beacon change notifier
From: Johannes Berg @ 2017-11-13 12:22 UTC (permalink / raw)
To: Sergey Matyukevich
Cc: linux-wireless, Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <20171113115841.3dcrcjqnp543kndi@bars>
Hi,
> I apologize for email duplication, but it looks like reply from Vasily
> has been rejected due to Outlook issues. While we choose between mutt
> and gnus, here I briefly repeat the rationale behind these RFC patches.
:)
> The nl80211 get_beacon callback appears to be useful in the case when
> userspace software needs to retrieve current operational parameters of
> AP including HT/VHT IEs.
Do you have any usage in mind though? I can't really see where this
would really make sense, rather than getting direct hooks for the bits
you needed in hostapd.
For example, if you care about the channel bandwidth changing, wouldn't
it be more efficient to just add a signal to hostapd rather than
listening to beacon updates and parsing, etc.?
johannes
^ permalink raw reply
* Re: [RFC PATCH 2/2] nl80211: implement beacon change notifier
From: Sergey Matyukevich @ 2017-11-13 12:00 UTC (permalink / raw)
To: Johannes Berg
Cc: linux-wireless, Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <1510565429.30497.10.camel@sipsolutions.net>
Hello Johannes and all,
> > From: Vasily Ulyanov <vulyanov@quantenna.com>
> >
> > Notify user-space listeners about beacon data change.
>
> What would this be needed for, and why couldn't you just directly talk
> to hostapd/wpa_s?
I apologize for email duplication, but it looks like reply from Vasily
has been rejected due to Outlook issues. While we choose between mutt
and gnus, here I briefly repeat the rationale behind these RFC patches.
The nl80211 get_beacon callback appears to be useful in the case when
userspace software needs to retrieve current operational parameters of
AP including HT/VHT IEs. In fact we considered two implementation options:
to implement this functionality either in hostapd (extending its
hostapd_cli interface) or in kernel (adding nl80211 call).
We decided to try nl80211 since there was a stub for NL80211_CMD_GET_BEACON
operation. Beacon change notifier was introduced due to the following reason:
if anyone is interested in AP operational params, then he might be also
interested to know when those params are changed.
Regards,
Sergey
^ permalink raw reply
* [PATCH 10/10] qtnfmac: support MAC address based access control
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless; +Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
From: Vasily Ulyanov <vulyanov@quantenna.com>
This allows a running AP to blacklist STAs by their MAC addresses
respecting the configured policy (either accept or deny unless listed).
It can be setup on .start_ap or with .set_mac_acl commands.
Signed-off-by: Vasily Ulyanov <vulyanov@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 19 +++++++
drivers/net/wireless/quantenna/qtnfmac/commands.c | 62 ++++++++++++++++++++++
drivers/net/wireless/quantenna/qtnfmac/commands.h | 2 +
drivers/net/wireless/quantenna/qtnfmac/core.h | 1 +
drivers/net/wireless/quantenna/qtnfmac/qlink.h | 37 ++++++++++++-
.../net/wireless/quantenna/qtnfmac/qlink_util.c | 18 +++++++
.../net/wireless/quantenna/qtnfmac/qlink_util.h | 2 +
7 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index 452def343ad3..89dc9cab647d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -778,6 +778,20 @@ static int qtnf_start_radar_detection(struct wiphy *wiphy,
return ret;
}
+static int qtnf_set_mac_acl(struct wiphy *wiphy,
+ struct net_device *dev,
+ const struct cfg80211_acl_data *params)
+{
+ struct qtnf_vif *vif = qtnf_netdev_get_priv(dev);
+ int ret;
+
+ ret = qtnf_cmd_set_mac_acl(vif, params);
+ if (ret)
+ pr_err("%s: failed to set mac ACL ret=%d\n", dev->name, ret);
+
+ return ret;
+}
+
static struct cfg80211_ops qtn_cfg80211_ops = {
.add_virtual_intf = qtnf_add_virtual_intf,
.change_virtual_intf = qtnf_change_virtual_intf,
@@ -803,6 +817,7 @@ static struct cfg80211_ops qtn_cfg80211_ops = {
.get_channel = qtnf_get_channel,
.channel_switch = qtnf_channel_switch,
.start_radar_detection = qtnf_start_radar_detection,
+ .set_mac_acl = qtnf_set_mac_acl,
};
static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in,
@@ -918,6 +933,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN;
wiphy->mgmt_stypes = qtnf_mgmt_stypes;
wiphy->max_remain_on_channel_duration = 5000;
+ wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
wiphy->iface_combinations = iface_comb;
wiphy->n_iface_combinations = 1;
@@ -932,6 +948,9 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
WIPHY_FLAG_AP_UAPSD |
WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+ if (wiphy->max_acl_mac_addrs > 0)
+ wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
+
wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index 38b9c1078058..6ffe4837bbdb 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -162,6 +162,14 @@ static void qtnf_cmd_tlv_ie_set_add(struct sk_buff *cmd_skb, u8 frame_type,
memcpy(tlv->ie_data, buf, len);
}
+static inline size_t qtnf_cmd_acl_data_size(const struct cfg80211_acl_data *acl)
+{
+ size_t size = sizeof(struct qlink_acl_data) +
+ acl->n_acl_entries * sizeof(struct qlink_mac_address);
+
+ return size;
+}
+
static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
const struct cfg80211_ap_settings *s)
{
@@ -178,6 +186,9 @@ static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
if (cfg80211_chandef_valid(&s->chandef))
len += sizeof(struct qlink_tlv_chandef);
+ if (s->acl)
+ len += qtnf_cmd_acl_data_size(s->acl);
+
if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) {
pr_err("VIF%u.%u: can not fit AP settings: %u\n",
vif->mac->macid, vif->vifid, len);
@@ -283,6 +294,16 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
memcpy(tlv->val, s->vht_cap, sizeof(*s->vht_cap));
}
+ if (s->acl) {
+ size_t acl_size = qtnf_cmd_acl_data_size(s->acl);
+ struct qlink_tlv_hdr *tlv =
+ skb_put(cmd_skb, sizeof(*tlv) + acl_size);
+
+ tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA);
+ tlv->len = cpu_to_le16(acl_size);
+ qlink_acl_data_cfg2q(s->acl, (struct qlink_acl_data *)tlv->val);
+ }
+
qtnf_bus_lock(vif->mac->bus);
ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code);
@@ -1206,6 +1227,7 @@ qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac,
mac_info->radar_detect_widths =
qlink_chan_width_mask_to_nl(le16_to_cpu(
resp_info->radar_detect_widths));
+ mac_info->max_acl_mac_addrs = le32_to_cpu(resp_info->max_acl_mac_addrs);
memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask,
sizeof(mac_info->ht_cap_mod_mask));
@@ -2609,3 +2631,43 @@ int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
return ret;
}
+
+int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
+ const struct cfg80211_acl_data *params)
+{
+ struct qtnf_bus *bus = vif->mac->bus;
+ struct sk_buff *cmd_skb;
+ struct qlink_cmd_set_mac_acl *cmd;
+ u16 res_code;
+ int ret;
+
+ cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
+ QLINK_CMD_SET_MAC_ACL,
+ sizeof(*cmd) +
+ qtnf_cmd_acl_data_size(params));
+ if (unlikely(!cmd_skb))
+ return -ENOMEM;
+
+ cmd = (struct qlink_cmd_set_mac_acl *)cmd_skb->data;
+ qlink_acl_data_cfg2q(params, &cmd->acl);
+
+ qtnf_bus_lock(bus);
+ ret = qtnf_cmd_send(bus, cmd_skb, &res_code);
+ qtnf_bus_unlock(bus);
+
+ if (unlikely(ret))
+ return ret;
+
+ switch (res_code) {
+ case QLINK_CMD_RESULT_OK:
+ break;
+ case QLINK_CMD_RESULT_INVALID:
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h
index 07a957af9a58..69a7d56f7e58 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h
@@ -79,5 +79,7 @@ int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef);
int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
const struct cfg80211_chan_def *chdef,
u32 cac_time_ms);
+int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
+ const struct cfg80211_acl_data *params);
#endif /* QLINK_COMMANDS_H_ */
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index e7bd21ed371b..c10900162297 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -103,6 +103,7 @@ struct qtnf_mac_info {
u8 sretry_limit;
u8 coverage_class;
u8 radar_detect_widths;
+ u32 max_acl_mac_addrs;
struct ieee80211_ht_cap ht_cap_mod_mask;
struct ieee80211_vht_cap vht_cap_mod_mask;
struct ieee80211_iface_limit *limits;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 5d98000b0f5b..6a1f960228a1 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -19,7 +19,7 @@
#include <linux/ieee80211.h>
-#define QLINK_PROTO_VER 9
+#define QLINK_PROTO_VER 10
#define QLINK_MACID_RSVD 0xFF
#define QLINK_VIFID_RSVD 0xFF
@@ -239,6 +239,7 @@ enum qlink_cmd_type {
QLINK_CMD_START_CAC = 0x001D,
QLINK_CMD_START_AP = 0x0021,
QLINK_CMD_STOP_AP = 0x0022,
+ QLINK_CMD_SET_MAC_ACL = 0x0023,
QLINK_CMD_GET_STA_INFO = 0x0030,
QLINK_CMD_ADD_KEY = 0x0040,
QLINK_CMD_DEL_KEY = 0x0041,
@@ -640,6 +641,38 @@ struct qlink_cmd_start_cac {
__le32 cac_time_ms;
} __packed;
+enum qlink_acl_policy {
+ QLINK_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+ QLINK_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
+struct qlink_mac_address {
+ u8 addr[ETH_ALEN];
+} __packed;
+
+/**
+ * struct qlink_acl_data - ACL data
+ *
+ * @policy: filter policy, one of &enum qlink_acl_policy.
+ * @num_entries: number of MAC addresses in array.
+ * @mac_address: MAC addresses array.
+ */
+struct qlink_acl_data {
+ __le32 policy;
+ __le32 num_entries;
+ struct qlink_mac_address mac_addrs[0];
+} __packed;
+
+/**
+ * struct qlink_cmd_set_mac_acl - data for QLINK_CMD_SET_MAC_ACL command
+ *
+ * @acl: ACL data.
+ */
+struct qlink_cmd_set_mac_acl {
+ struct qlink_cmd chdr;
+ struct qlink_acl_data acl;
+} __packed;
+
/* QLINK Command Responses messages related definitions
*/
@@ -701,6 +734,7 @@ struct qlink_resp_get_mac_info {
struct ieee80211_ht_cap ht_cap_mod_mask;
__le16 max_ap_assoc_sta;
__le16 radar_detect_widths;
+ __le32 max_acl_mac_addrs;
u8 bands_cap;
u8 rsvd[1];
u8 var_info[0];
@@ -1049,6 +1083,7 @@ enum qlink_tlv_id {
QTN_TLV_ID_SEQ = 0x0303,
QTN_TLV_ID_IE_SET = 0x0305,
QTN_TLV_ID_EXT_CAPABILITY_MASK = 0x0306,
+ QTN_TLV_ID_ACL_DATA = 0x0307,
};
struct qlink_tlv_hdr {
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
index 19981d6440b6..aeeda81b09ea 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
@@ -176,3 +176,21 @@ bool qtnf_utils_is_bit_set(const u8 *arr, unsigned int bit,
return arr[idx] & mask;
}
+
+void qlink_acl_data_cfg2q(const struct cfg80211_acl_data *acl,
+ struct qlink_acl_data *qacl)
+{
+ switch (acl->acl_policy) {
+ case NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED:
+ qacl->policy =
+ cpu_to_le32(QLINK_ACL_POLICY_ACCEPT_UNLESS_LISTED);
+ break;
+ case NL80211_ACL_POLICY_DENY_UNLESS_LISTED:
+ qacl->policy = cpu_to_le32(QLINK_ACL_POLICY_DENY_UNLESS_LISTED);
+ break;
+ }
+
+ qacl->num_entries = cpu_to_le32(acl->n_acl_entries);
+ memcpy(qacl->mac_addrs, acl->mac_addrs,
+ acl->n_acl_entries * sizeof(*qacl->mac_addrs));
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
index 6c24561eb41f..54caeb38917c 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
@@ -71,5 +71,7 @@ void qlink_chandef_cfg2q(const struct cfg80211_chan_def *chdef,
enum qlink_hidden_ssid qlink_hidden_ssid_nl2q(enum nl80211_hidden_ssid nl_val);
bool qtnf_utils_is_bit_set(const u8 *arr, unsigned int bit,
unsigned int arr_max_len);
+void qlink_acl_data_cfg2q(const struct cfg80211_acl_data *acl,
+ struct qlink_acl_data *qacl);
#endif /* _QTN_FMAC_QLINK_UTIL_H_ */
--
2.11.0
^ permalink raw reply related
* [PATCH 09/10] qtnfmac: keeping track of "generation" for STA info
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless; +Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
From: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Keep generation in per-VIF data structure and increment it whenever STA
list is changed. Use generation value to fill struct station_info when
required.
Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 5 ++++-
drivers/net/wireless/quantenna/qtnfmac/core.h | 1 +
drivers/net/wireless/quantenna/qtnfmac/event.c | 5 +++--
drivers/net/wireless/quantenna/qtnfmac/util.c | 8 ++++++--
drivers/net/wireless/quantenna/qtnfmac/util.h | 4 ++--
5 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index f5eb2cdefae2..452def343ad3 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -418,6 +418,7 @@ qtnf_get_station(struct wiphy *wiphy, struct net_device *dev,
{
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev);
+ sinfo->generation = vif->generation;
return qtnf_cmd_get_sta_info(vif, mac, sinfo);
}
@@ -439,11 +440,13 @@ qtnf_dump_station(struct wiphy *wiphy, struct net_device *dev,
ret = qtnf_cmd_get_sta_info(vif, sta_node->mac_addr, sinfo);
if (unlikely(ret == -ENOENT)) {
- qtnf_sta_list_del(&vif->sta_list, mac);
+ qtnf_sta_list_del(vif, mac);
cfg80211_del_sta(vif->netdev, mac, GFP_KERNEL);
sinfo->filled = 0;
}
+ sinfo->generation = vif->generation;
+
return ret;
}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index d7e295efb07d..e7bd21ed371b 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -88,6 +88,7 @@ struct qtnf_vif {
struct work_struct reset_work;
struct qtnf_sta_list sta_list;
unsigned long cons_tx_timeout_cnt;
+ int generation;
};
struct qtnf_mac_info {
diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c
index b3489b5b5d9e..8a3d2b1194e4 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/event.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
@@ -59,10 +59,11 @@ qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif,
pr_debug("VIF%u.%u: MAC:%pM FC:%x\n", mac->macid, vif->vifid, sta_addr,
frame_control);
- qtnf_sta_list_add(&vif->sta_list, sta_addr);
+ qtnf_sta_list_add(vif, sta_addr);
sinfo.assoc_req_ies = NULL;
sinfo.assoc_req_ies_len = 0;
+ sinfo.generation = vif->generation;
payload_len = len - sizeof(*sta_assoc);
tlv = (const struct qlink_tlv_hdr *)sta_assoc->ies;
@@ -132,7 +133,7 @@ qtnf_event_handle_sta_deauth(struct qtnf_wmac *mac, struct qtnf_vif *vif,
pr_debug("VIF%u.%u: MAC:%pM reason:%x\n", mac->macid, vif->vifid,
sta_addr, reason);
- if (qtnf_sta_list_del(&vif->sta_list, sta_addr))
+ if (qtnf_sta_list_del(vif, sta_addr))
cfg80211_del_sta(vif->netdev, sta_deauth->sta_addr,
GFP_KERNEL);
diff --git a/drivers/net/wireless/quantenna/qtnfmac/util.c b/drivers/net/wireless/quantenna/qtnfmac/util.c
index ed38e87471bf..e745733ba417 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/util.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/util.c
@@ -57,9 +57,10 @@ struct qtnf_sta_node *qtnf_sta_list_lookup_index(struct qtnf_sta_list *list,
return NULL;
}
-struct qtnf_sta_node *qtnf_sta_list_add(struct qtnf_sta_list *list,
+struct qtnf_sta_node *qtnf_sta_list_add(struct qtnf_vif *vif,
const u8 *mac)
{
+ struct qtnf_sta_list *list = &vif->sta_list;
struct qtnf_sta_node *node;
if (unlikely(!mac))
@@ -77,13 +78,15 @@ struct qtnf_sta_node *qtnf_sta_list_add(struct qtnf_sta_list *list,
ether_addr_copy(node->mac_addr, mac);
list_add_tail(&node->list, &list->head);
atomic_inc(&list->size);
+ ++vif->generation;
done:
return node;
}
-bool qtnf_sta_list_del(struct qtnf_sta_list *list, const u8 *mac)
+bool qtnf_sta_list_del(struct qtnf_vif *vif, const u8 *mac)
{
+ struct qtnf_sta_list *list = &vif->sta_list;
struct qtnf_sta_node *node;
bool ret = false;
@@ -93,6 +96,7 @@ bool qtnf_sta_list_del(struct qtnf_sta_list *list, const u8 *mac)
list_del(&node->list);
atomic_dec(&list->size);
kfree(node);
+ ++vif->generation;
ret = true;
}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/util.h b/drivers/net/wireless/quantenna/qtnfmac/util.h
index 0359eae8c24b..0d4d92b11540 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/util.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/util.h
@@ -26,9 +26,9 @@ struct qtnf_sta_node *qtnf_sta_list_lookup(struct qtnf_sta_list *list,
const u8 *mac);
struct qtnf_sta_node *qtnf_sta_list_lookup_index(struct qtnf_sta_list *list,
size_t index);
-struct qtnf_sta_node *qtnf_sta_list_add(struct qtnf_sta_list *list,
+struct qtnf_sta_node *qtnf_sta_list_add(struct qtnf_vif *vif,
const u8 *mac);
-bool qtnf_sta_list_del(struct qtnf_sta_list *list, const u8 *mac);
+bool qtnf_sta_list_del(struct qtnf_vif *vif, const u8 *mac);
void qtnf_sta_list_free(struct qtnf_sta_list *list);
--
2.11.0
^ permalink raw reply related
* [PATCH 08/10] qtnfmac: modify GET_STA_STATS cmd format for back/forward compatibility
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless; +Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
From: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
A set of per-STA statistics can potentially change quite often.
To ensure backwards and forward compatibility,
modify GET_STA_STATS command format:
- introduce two TLV types
- first TLV is a variable-sized bitmap of statistics values
that are filled by firmware
- second TLV is a structure with statistics itself
Only values specified in first TLV are valid.
Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/commands.c | 217 +++++++++++----------
drivers/net/wireless/quantenna/qtnfmac/qlink.h | 204 ++++++++++++-------
.../net/wireless/quantenna/qtnfmac/qlink_util.c | 12 ++
.../net/wireless/quantenna/qtnfmac/qlink_util.h | 2 +
4 files changed, 267 insertions(+), 168 deletions(-)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index 839e86b99837..38b9c1078058 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -461,30 +461,8 @@ int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type,
}
static void
-qtnf_sta_info_parse_basic_counters(struct station_info *sinfo,
- const struct qlink_sta_stat_basic_counters *counters)
-{
- sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES) |
- BIT(NL80211_STA_INFO_TX_BYTES);
- sinfo->rx_bytes = get_unaligned_le64(&counters->rx_bytes);
- sinfo->tx_bytes = get_unaligned_le64(&counters->tx_bytes);
-
- sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
- BIT(NL80211_STA_INFO_TX_PACKETS) |
- BIT(NL80211_STA_INFO_BEACON_RX);
- sinfo->rx_packets = get_unaligned_le32(&counters->rx_packets);
- sinfo->tx_packets = get_unaligned_le32(&counters->tx_packets);
- sinfo->rx_beacon = get_unaligned_le64(&counters->rx_beacons);
-
- sinfo->filled |= BIT(NL80211_STA_INFO_RX_DROP_MISC) |
- BIT(NL80211_STA_INFO_TX_FAILED);
- sinfo->rx_dropped_misc = get_unaligned_le32(&counters->rx_dropped);
- sinfo->tx_failed = get_unaligned_le32(&counters->tx_failed);
-}
-
-static void
qtnf_sta_info_parse_rate(struct rate_info *rate_dst,
- const struct qlink_sta_info_rate *rate_src)
+ const struct qlink_sta_info_rate *rate_src)
{
rate_dst->legacy = get_unaligned_le16(&rate_src->rate) * 10;
@@ -493,22 +471,23 @@ qtnf_sta_info_parse_rate(struct rate_info *rate_dst,
rate_dst->flags = 0;
switch (rate_src->bw) {
- case QLINK_STA_INFO_RATE_BW_5:
+ case QLINK_CHAN_WIDTH_5:
rate_dst->bw = RATE_INFO_BW_5;
break;
- case QLINK_STA_INFO_RATE_BW_10:
+ case QLINK_CHAN_WIDTH_10:
rate_dst->bw = RATE_INFO_BW_10;
break;
- case QLINK_STA_INFO_RATE_BW_20:
+ case QLINK_CHAN_WIDTH_20:
+ case QLINK_CHAN_WIDTH_20_NOHT:
rate_dst->bw = RATE_INFO_BW_20;
break;
- case QLINK_STA_INFO_RATE_BW_40:
+ case QLINK_CHAN_WIDTH_40:
rate_dst->bw = RATE_INFO_BW_40;
break;
- case QLINK_STA_INFO_RATE_BW_80:
+ case QLINK_CHAN_WIDTH_80:
rate_dst->bw = RATE_INFO_BW_80;
break;
- case QLINK_STA_INFO_RATE_BW_160:
+ case QLINK_CHAN_WIDTH_160:
rate_dst->bw = RATE_INFO_BW_160;
break;
default:
@@ -578,87 +557,125 @@ qtnf_sta_info_parse_flags(struct nl80211_sta_flag_update *dst,
}
static void
-qtnf_sta_info_parse_generic_info(struct station_info *sinfo,
- const struct qlink_sta_info_generic *info)
+qtnf_cmd_sta_info_parse(struct station_info *sinfo,
+ const struct qlink_tlv_hdr *tlv,
+ size_t resp_size)
{
- sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME) |
- BIT(NL80211_STA_INFO_INACTIVE_TIME);
- sinfo->connected_time = get_unaligned_le32(&info->connected_time);
- sinfo->inactive_time = get_unaligned_le32(&info->inactive_time);
+ const struct qlink_sta_stats *stats = NULL;
+ const u8 *map = NULL;
+ unsigned int map_len = 0;
+ unsigned int stats_len = 0;
+ u16 tlv_len;
+
+#define qtnf_sta_stat_avail(stat_name, bitn) \
+ (qtnf_utils_is_bit_set(map, bitn, map_len) && \
+ (offsetofend(struct qlink_sta_stats, stat_name) <= stats_len))
+
+ while (resp_size >= sizeof(*tlv)) {
+ tlv_len = le16_to_cpu(tlv->len);
+
+ switch (le16_to_cpu(tlv->type)) {
+ case QTN_TLV_ID_STA_STATS_MAP:
+ map_len = tlv_len;
+ map = tlv->val;
+ break;
+ case QTN_TLV_ID_STA_STATS:
+ stats_len = tlv_len;
+ stats = (const struct qlink_sta_stats *)tlv->val;
+ break;
+ default:
+ break;
+ }
+
+ resp_size -= tlv_len + sizeof(*tlv);
+ tlv = (const struct qlink_tlv_hdr *)(tlv->val + tlv_len);
+ }
- sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL) |
- BIT(NL80211_STA_INFO_SIGNAL_AVG);
- sinfo->signal = info->rssi - 120;
- sinfo->signal_avg = info->rssi_avg - QLINK_RSSI_OFFSET;
+ if (!map || !stats)
+ return;
+
+ if (qtnf_sta_stat_avail(inactive_time, QLINK_STA_INFO_INACTIVE_TIME)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME);
+ sinfo->inactive_time = le32_to_cpu(stats->inactive_time);
+ }
- if (info->rx_rate.rate) {
+ if (qtnf_sta_stat_avail(connected_time,
+ QLINK_STA_INFO_CONNECTED_TIME)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
+ sinfo->connected_time = le32_to_cpu(stats->connected_time);
+ }
+
+ if (qtnf_sta_stat_avail(signal, QLINK_STA_INFO_SIGNAL)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ sinfo->signal = stats->signal - QLINK_RSSI_OFFSET;
+ }
+
+ if (qtnf_sta_stat_avail(signal_avg, QLINK_STA_INFO_SIGNAL_AVG)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
+ sinfo->signal_avg = stats->signal_avg - QLINK_RSSI_OFFSET;
+ }
+
+ if (qtnf_sta_stat_avail(rxrate, QLINK_STA_INFO_RX_BITRATE)) {
sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
- qtnf_sta_info_parse_rate(&sinfo->rxrate, &info->rx_rate);
+ qtnf_sta_info_parse_rate(&sinfo->rxrate, &stats->rxrate);
}
- if (info->tx_rate.rate) {
+ if (qtnf_sta_stat_avail(txrate, QLINK_STA_INFO_TX_BITRATE)) {
sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
- qtnf_sta_info_parse_rate(&sinfo->txrate, &info->tx_rate);
+ qtnf_sta_info_parse_rate(&sinfo->txrate, &stats->txrate);
}
- sinfo->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
- qtnf_sta_info_parse_flags(&sinfo->sta_flags, &info->state);
-}
+ if (qtnf_sta_stat_avail(sta_flags, QLINK_STA_INFO_STA_FLAGS)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
+ qtnf_sta_info_parse_flags(&sinfo->sta_flags, &stats->sta_flags);
+ }
-static int qtnf_cmd_sta_info_parse(struct station_info *sinfo,
- const u8 *payload, size_t payload_size)
-{
- const struct qlink_sta_stat_basic_counters *counters;
- const struct qlink_sta_info_generic *sta_info;
- u16 tlv_type;
- u16 tlv_value_len;
- size_t tlv_full_len;
- const struct qlink_tlv_hdr *tlv;
+ if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
+ sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes);
+ }
- sinfo->filled = 0;
+ if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
+ sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes);
+ }
- tlv = (const struct qlink_tlv_hdr *)payload;
- while (payload_size >= sizeof(struct qlink_tlv_hdr)) {
- tlv_type = le16_to_cpu(tlv->type);
- tlv_value_len = le16_to_cpu(tlv->len);
- tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
- if (tlv_full_len > payload_size) {
- pr_warn("malformed TLV 0x%.2X; LEN: %u\n",
- tlv_type, tlv_value_len);
- return -EINVAL;
- }
- switch (tlv_type) {
- case QTN_TLV_ID_STA_BASIC_COUNTERS:
- if (unlikely(tlv_value_len < sizeof(*counters))) {
- pr_err("invalid TLV size %.4X: %u\n",
- tlv_type, tlv_value_len);
- break;
- }
+ if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES64)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64);
+ sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes);
+ }
- counters = (void *)tlv->val;
- qtnf_sta_info_parse_basic_counters(sinfo, counters);
- break;
- case QTN_TLV_ID_STA_GENERIC_INFO:
- if (unlikely(tlv_value_len < sizeof(*sta_info)))
- break;
+ if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES64)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES64);
+ sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes);
+ }
- sta_info = (void *)tlv->val;
- qtnf_sta_info_parse_generic_info(sinfo, sta_info);
- break;
- default:
- pr_warn("unexpected TLV type: %.4X\n", tlv_type);
- break;
- }
- payload_size -= tlv_full_len;
- tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
+ if (qtnf_sta_stat_avail(rx_packets, QLINK_STA_INFO_RX_PACKETS)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
+ sinfo->rx_packets = le32_to_cpu(stats->rx_packets);
}
- if (payload_size) {
- pr_warn("malformed TLV buf; bytes left: %zu\n", payload_size);
- return -EINVAL;
+ if (qtnf_sta_stat_avail(tx_packets, QLINK_STA_INFO_TX_PACKETS)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
+ sinfo->tx_packets = le32_to_cpu(stats->tx_packets);
}
- return 0;
+ if (qtnf_sta_stat_avail(rx_beacon, QLINK_STA_INFO_BEACON_RX)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX);
+ sinfo->rx_beacon = le64_to_cpu(stats->rx_beacon);
+ }
+
+ if (qtnf_sta_stat_avail(rx_dropped_misc, QLINK_STA_INFO_RX_DROP_MISC)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_DROP_MISC);
+ sinfo->rx_dropped_misc = le32_to_cpu(stats->rx_dropped_misc);
+ }
+
+ if (qtnf_sta_stat_avail(tx_failed, QLINK_STA_INFO_TX_FAILED)) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
+ sinfo->tx_failed = le32_to_cpu(stats->tx_failed);
+ }
+
+#undef qtnf_sta_stat_avail
}
int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac,
@@ -715,7 +732,9 @@ int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac,
goto out;
}
- ret = qtnf_cmd_sta_info_parse(sinfo, resp->info, var_resp_len);
+ qtnf_cmd_sta_info_parse(sinfo,
+ (const struct qlink_tlv_hdr *)resp->info,
+ var_resp_len);
out:
qtnf_bus_unlock(vif->mac->bus);
@@ -1992,21 +2011,17 @@ int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac,
cmd = (struct qlink_cmd_change_sta *)cmd_skb->data;
ether_addr_copy(cmd->sta_addr, mac);
+ cmd->flag_update.mask =
+ cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_mask));
+ cmd->flag_update.value =
+ cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_set));
switch (vif->wdev.iftype) {
case NL80211_IFTYPE_AP:
cmd->if_type = cpu_to_le16(QLINK_IFTYPE_AP);
- cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags(
- params->sta_flags_mask));
- cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags(
- params->sta_flags_set));
break;
case NL80211_IFTYPE_STATION:
cmd->if_type = cpu_to_le16(QLINK_IFTYPE_STATION);
- cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags(
- params->sta_flags_mask));
- cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags(
- params->sta_flags_set));
break;
default:
pr_err("unsupported iftype %d\n", vif->wdev.iftype);
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index f4d7d1603e3c..5d98000b0f5b 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -19,7 +19,7 @@
#include <linux/ieee80211.h>
-#define QLINK_PROTO_VER 8
+#define QLINK_PROTO_VER 9
#define QLINK_MACID_RSVD 0xFF
#define QLINK_VIFID_RSVD 0xFF
@@ -185,6 +185,17 @@ struct qlink_auth_encr {
u8 rsvd[2];
} __packed;
+/**
+ * struct qlink_sta_info_state - station flags mask/value
+ *
+ * @mask: STA flags mask, bitmap of &enum qlink_sta_flags
+ * @value: STA flags values, bitmap of &enum qlink_sta_flags
+ */
+struct qlink_sta_info_state {
+ __le32 mask;
+ __le32 value;
+} __packed;
+
/* QLINK Command messages related definitions
*/
@@ -402,16 +413,14 @@ struct qlink_cmd_set_def_mgmt_key {
/**
* struct qlink_cmd_change_sta - data for QLINK_CMD_CHANGE_STA command
*
- * @sta_flags_mask: STA flags mask, bitmap of &enum qlink_sta_flags
- * @sta_flags_set: STA flags values, bitmap of &enum qlink_sta_flags
+ * @flag_update: STA flags to update
* @if_type: Mode of interface operation, one of &enum qlink_iface_type
* @vlanid: VLAN ID to assign to specific STA
* @sta_addr: address of the STA for which parameters are set.
*/
struct qlink_cmd_change_sta {
struct qlink_cmd chdr;
- __le32 sta_flags_mask;
- __le32 sta_flags_set;
+ struct qlink_sta_info_state flag_update;
__le16 if_type;
__le16 vlanid;
u8 sta_addr[ETH_ALEN];
@@ -755,17 +764,27 @@ struct qlink_resp_manage_intf {
struct qlink_intf_info intf_info;
} __packed;
+enum qlink_sta_info_rate_flags {
+ QLINK_STA_INFO_RATE_FLAG_HT_MCS = BIT(0),
+ QLINK_STA_INFO_RATE_FLAG_VHT_MCS = BIT(1),
+ QLINK_STA_INFO_RATE_FLAG_SHORT_GI = BIT(2),
+ QLINK_STA_INFO_RATE_FLAG_60G = BIT(3),
+};
+
/**
* struct qlink_resp_get_sta_info - response for QLINK_CMD_GET_STA_INFO command
*
* Response data containing statistics for specified STA.
*
+ * @filled: a bitmask of &enum qlink_sta_info, specifies which info in response
+ * is valid.
* @sta_addr: MAC address of STA the response carries statistic for.
- * @info: statistics for specified STA.
+ * @info: variable statistics for specified STA.
*/
struct qlink_resp_get_sta_info {
struct qlink_resp rhdr;
u8 sta_addr[ETH_ALEN];
+ u8 rsvd[2];
u8 info[0];
} __packed;
@@ -1003,6 +1022,15 @@ struct qlink_event_radar {
/* QLINK TLVs (Type-Length Values) definitions
*/
+/**
+ * enum qlink_tlv_id - list of TLVs that Qlink messages can carry
+ *
+ * @QTN_TLV_ID_STA_STATS_MAP: a bitmap of &enum qlink_sta_info, used to
+ * indicate which statistic carried in QTN_TLV_ID_STA_STATS is valid.
+ * @QTN_TLV_ID_STA_STATS: per-STA statistics as defined by
+ * &struct qlink_sta_stats. Valid values are marked as such in a bitmap
+ * carried by QTN_TLV_ID_STA_STATS_MAP.
+ */
enum qlink_tlv_id {
QTN_TLV_ID_FRAG_THRESH = 0x0201,
QTN_TLV_ID_RTS_THRESH = 0x0202,
@@ -1011,12 +1039,12 @@ enum qlink_tlv_id {
QTN_TLV_ID_REG_RULE = 0x0207,
QTN_TLV_ID_CHANNEL = 0x020F,
QTN_TLV_ID_CHANDEF = 0x0210,
+ QTN_TLV_ID_STA_STATS_MAP = 0x0211,
+ QTN_TLV_ID_STA_STATS = 0x0212,
QTN_TLV_ID_COVERAGE_CLASS = 0x0213,
QTN_TLV_ID_IFACE_LIMIT = 0x0214,
QTN_TLV_ID_NUM_IFACE_COMB = 0x0215,
QTN_TLV_ID_CHANNEL_STATS = 0x0216,
- QTN_TLV_ID_STA_BASIC_COUNTERS = 0x0300,
- QTN_TLV_ID_STA_GENERIC_INFO = 0x0301,
QTN_TLV_ID_KEY = 0x0302,
QTN_TLV_ID_SEQ = 0x0303,
QTN_TLV_ID_IE_SET = 0x0305,
@@ -1038,67 +1066,8 @@ struct qlink_iface_comb_num {
__le16 iface_comb_num;
} __packed;
-struct qlink_sta_stat_basic_counters {
- __le64 rx_bytes;
- __le64 tx_bytes;
- __le64 rx_beacons;
- __le32 rx_packets;
- __le32 tx_packets;
- __le32 rx_dropped;
- __le32 tx_failed;
-} __packed;
-
-enum qlink_sta_info_rate_flags {
- QLINK_STA_INFO_RATE_FLAG_INVALID = 0,
- QLINK_STA_INFO_RATE_FLAG_HT_MCS = BIT(0),
- QLINK_STA_INFO_RATE_FLAG_VHT_MCS = BIT(1),
- QLINK_STA_INFO_RATE_FLAG_SHORT_GI = BIT(2),
- QLINK_STA_INFO_RATE_FLAG_60G = BIT(3),
-};
-
-enum qlink_sta_info_rate_bw {
- QLINK_STA_INFO_RATE_BW_5 = 0,
- QLINK_STA_INFO_RATE_BW_10 = 1,
- QLINK_STA_INFO_RATE_BW_20 = 2,
- QLINK_STA_INFO_RATE_BW_40 = 3,
- QLINK_STA_INFO_RATE_BW_80 = 4,
- QLINK_STA_INFO_RATE_BW_160 = 5,
-};
-
-/**
- * struct qlink_sta_info_rate - STA rate statistics
- *
- * @rate: data rate in Mbps.
- * @flags: bitmap of &enum qlink_sta_flags.
- * @mcs: 802.11-defined MCS index.
- * nss: Number of Spatial Streams.
- * @bw: bandwidth, one of &enum qlink_sta_info_rate_bw.
- */
-struct qlink_sta_info_rate {
- __le16 rate;
- u8 flags;
- u8 mcs;
- u8 nss;
- u8 bw;
-} __packed;
-
-struct qlink_sta_info_state {
- __le32 mask;
- __le32 value;
-} __packed;
-
#define QLINK_RSSI_OFFSET 120
-struct qlink_sta_info_generic {
- struct qlink_sta_info_state state;
- __le32 connected_time;
- __le32 inactive_time;
- struct qlink_sta_info_rate rx_rate;
- struct qlink_sta_info_rate tx_rate;
- u8 rssi;
- u8 rssi_avg;
-} __packed;
-
struct qlink_tlv_frag_rts_thr {
struct qlink_tlv_hdr hdr;
__le16 thr;
@@ -1243,4 +1212,105 @@ struct qlink_chan_stats {
s8 chan_noise;
} __packed;
+/**
+ * enum qlink_sta_info - station information bitmap
+ *
+ * Used to indicate which statistics values in &struct qlink_sta_stats
+ * are valid. Individual values are used to fill a bitmap carried in a
+ * payload of QTN_TLV_ID_STA_STATS_MAP.
+ *
+ * @QLINK_STA_INFO_CONNECTED_TIME: connected_time value is valid.
+ * @QLINK_STA_INFO_INACTIVE_TIME: inactive_time value is valid.
+ * @QLINK_STA_INFO_RX_BYTES: lower 32 bits of rx_bytes value are valid.
+ * @QLINK_STA_INFO_TX_BYTES: lower 32 bits of tx_bytes value are valid.
+ * @QLINK_STA_INFO_RX_BYTES64: rx_bytes value is valid.
+ * @QLINK_STA_INFO_TX_BYTES64: tx_bytes value is valid.
+ * @QLINK_STA_INFO_RX_DROP_MISC: rx_dropped_misc value is valid.
+ * @QLINK_STA_INFO_BEACON_RX: rx_beacon value is valid.
+ * @QLINK_STA_INFO_SIGNAL: signal value is valid.
+ * @QLINK_STA_INFO_SIGNAL_AVG: signal_avg value is valid.
+ * @QLINK_STA_INFO_RX_BITRATE: rxrate value is valid.
+ * @QLINK_STA_INFO_TX_BITRATE: txrate value is valid.
+ * @QLINK_STA_INFO_RX_PACKETS: rx_packets value is valid.
+ * @QLINK_STA_INFO_TX_PACKETS: tx_packets value is valid.
+ * @QLINK_STA_INFO_TX_RETRIES: tx_retries value is valid.
+ * @QLINK_STA_INFO_TX_FAILED: tx_failed value is valid.
+ * @QLINK_STA_INFO_STA_FLAGS: sta_flags value is valid.
+ */
+enum qlink_sta_info {
+ QLINK_STA_INFO_CONNECTED_TIME,
+ QLINK_STA_INFO_INACTIVE_TIME,
+ QLINK_STA_INFO_RX_BYTES,
+ QLINK_STA_INFO_TX_BYTES,
+ QLINK_STA_INFO_RX_BYTES64,
+ QLINK_STA_INFO_TX_BYTES64,
+ QLINK_STA_INFO_RX_DROP_MISC,
+ QLINK_STA_INFO_BEACON_RX,
+ QLINK_STA_INFO_SIGNAL,
+ QLINK_STA_INFO_SIGNAL_AVG,
+ QLINK_STA_INFO_RX_BITRATE,
+ QLINK_STA_INFO_TX_BITRATE,
+ QLINK_STA_INFO_RX_PACKETS,
+ QLINK_STA_INFO_TX_PACKETS,
+ QLINK_STA_INFO_TX_RETRIES,
+ QLINK_STA_INFO_TX_FAILED,
+ QLINK_STA_INFO_STA_FLAGS,
+ QLINK_STA_INFO_NUM,
+};
+
+/**
+ * struct qlink_sta_info_rate - STA rate statistics
+ *
+ * @rate: data rate in Mbps.
+ * @flags: bitmap of &enum qlink_sta_info_rate_flags.
+ * @mcs: 802.11-defined MCS index.
+ * nss: Number of Spatial Streams.
+ * @bw: bandwidth, one of &enum qlink_channel_width.
+ */
+struct qlink_sta_info_rate {
+ __le16 rate;
+ u8 flags;
+ u8 mcs;
+ u8 nss;
+ u8 bw;
+} __packed;
+
+/**
+ * struct qlink_sta_stats - data for QTN_TLV_ID_STA_STATS
+ *
+ * Carries statistics of a STA. Not all fields may be filled with
+ * valid values. Valid fields should be indicated as such using a bitmap of
+ * &enum qlink_sta_info. Bitmap is carried separately in a payload of
+ * QTN_TLV_ID_STA_STATS_MAP.
+ */
+struct qlink_sta_stats {
+ __le64 rx_bytes;
+ __le64 tx_bytes;
+ __le64 rx_beacon;
+ __le64 rx_duration;
+ __le64 t_offset;
+ __le32 connected_time;
+ __le32 inactive_time;
+ __le32 rx_packets;
+ __le32 tx_packets;
+ __le32 tx_retries;
+ __le32 tx_failed;
+ __le32 rx_dropped_misc;
+ __le32 beacon_loss_count;
+ __le32 expected_throughput;
+ struct qlink_sta_info_state sta_flags;
+ struct qlink_sta_info_rate txrate;
+ struct qlink_sta_info_rate rxrate;
+ __le16 llid;
+ __le16 plid;
+ u8 local_pm;
+ u8 peer_pm;
+ u8 nonpeer_pm;
+ u8 rx_beacon_signal_avg;
+ u8 plink_state;
+ u8 signal;
+ u8 signal_avg;
+ u8 rsvd[1];
+};
+
#endif /* _QTN_QLINK_H_ */
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
index 37b78f00e8e5..19981d6440b6 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
@@ -164,3 +164,15 @@ enum qlink_hidden_ssid qlink_hidden_ssid_nl2q(enum nl80211_hidden_ssid nl_val)
return QLINK_HIDDEN_SSID_NOT_IN_USE;
}
}
+
+bool qtnf_utils_is_bit_set(const u8 *arr, unsigned int bit,
+ unsigned int arr_max_len)
+{
+ unsigned int idx = bit / BITS_PER_BYTE;
+ u8 mask = 1 << (bit - (idx * BITS_PER_BYTE));
+
+ if (idx >= arr_max_len)
+ return false;
+
+ return arr[idx] & mask;
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
index 260383d6d109..6c24561eb41f 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
@@ -69,5 +69,7 @@ void qlink_chandef_q2cfg(struct wiphy *wiphy,
void qlink_chandef_cfg2q(const struct cfg80211_chan_def *chdef,
struct qlink_chandef *qch);
enum qlink_hidden_ssid qlink_hidden_ssid_nl2q(enum nl80211_hidden_ssid nl_val);
+bool qtnf_utils_is_bit_set(const u8 *arr, unsigned int bit,
+ unsigned int arr_max_len);
#endif /* _QTN_FMAC_QLINK_UTIL_H_ */
--
2.11.0
^ permalink raw reply related
* [PATCH 07/10] qtnfmac: fill wiphy's extended capabilities
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless; +Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
From: Vasily Ulyanov <vulyanov@quantenna.com>
These are needed to inform userspace about features the hardware
supports (e.g. BSS Transition Management 802.11v)
Signed-off-by: Vasily Ulyanov <vulyanov@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/commands.c | 44 +++++++++++++++++++++++
drivers/net/wireless/quantenna/qtnfmac/core.c | 2 ++
drivers/net/wireless/quantenna/qtnfmac/core.h | 3 ++
drivers/net/wireless/quantenna/qtnfmac/qlink.h | 1 +
4 files changed, 50 insertions(+)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index 7089f3eb7a87..839e86b99837 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -1029,6 +1029,10 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
struct qlink_iface_comb_num *comb;
size_t tlv_full_len;
const struct qlink_tlv_hdr *tlv;
+ u8 *ext_capa = NULL;
+ u8 *ext_capa_mask = NULL;
+ u8 ext_capa_len = 0;
+ u8 ext_capa_mask_len = 0;
mac->macinfo.n_limits = 0;
@@ -1092,6 +1096,18 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
if (limits[rec].types)
rec++;
break;
+ case WLAN_EID_EXT_CAPABILITY:
+ if (unlikely(tlv_value_len > U8_MAX))
+ return -EINVAL;
+ ext_capa = (u8 *)tlv->val;
+ ext_capa_len = tlv_value_len;
+ break;
+ case QTN_TLV_ID_EXT_CAPABILITY_MASK:
+ if (unlikely(tlv_value_len > U8_MAX))
+ return -EINVAL;
+ ext_capa_mask = (u8 *)tlv->val;
+ ext_capa_mask_len = tlv_value_len;
+ break;
default:
break;
}
@@ -1112,6 +1128,34 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
return -EINVAL;
}
+ if (ext_capa_len != ext_capa_mask_len) {
+ pr_err("MAC%u: ext_capa/_mask lengths mismatch: %u != %u\n",
+ mac->macid, ext_capa_len, ext_capa_mask_len);
+ return -EINVAL;
+ }
+
+ if (ext_capa_len > 0) {
+ ext_capa = kmemdup(ext_capa, ext_capa_len, GFP_KERNEL);
+ if (!ext_capa)
+ return -ENOMEM;
+
+ ext_capa_mask =
+ kmemdup(ext_capa_mask, ext_capa_mask_len, GFP_KERNEL);
+ if (!ext_capa_mask) {
+ kfree(ext_capa);
+ return -ENOMEM;
+ }
+ } else {
+ ext_capa = NULL;
+ ext_capa_mask = NULL;
+ }
+
+ kfree(mac->macinfo.extended_capabilities);
+ kfree(mac->macinfo.extended_capabilities_mask);
+ mac->macinfo.extended_capabilities = ext_capa;
+ mac->macinfo.extended_capabilities_mask = ext_capa_mask;
+ mac->macinfo.extended_capabilities_len = ext_capa_len;
+
return 0;
}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c
index ea850cb7dffc..3de0b26f839d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
@@ -381,6 +381,8 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid)
}
kfree(mac->macinfo.limits);
+ kfree(mac->macinfo.extended_capabilities);
+ kfree(mac->macinfo.extended_capabilities_mask);
kfree(wiphy->iface_combinations);
wiphy_free(wiphy);
bus->mac[macid] = NULL;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index 8e42d1bdc65f..d7e295efb07d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -106,6 +106,9 @@ struct qtnf_mac_info {
struct ieee80211_vht_cap vht_cap_mod_mask;
struct ieee80211_iface_limit *limits;
size_t n_limits;
+ u8 *extended_capabilities;
+ u8 *extended_capabilities_mask;
+ u8 extended_capabilities_len;
};
struct qtnf_chan_stats {
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 1f150be98820..f4d7d1603e3c 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -1020,6 +1020,7 @@ enum qlink_tlv_id {
QTN_TLV_ID_KEY = 0x0302,
QTN_TLV_ID_SEQ = 0x0303,
QTN_TLV_ID_IE_SET = 0x0305,
+ QTN_TLV_ID_EXT_CAPABILITY_MASK = 0x0306,
};
struct qlink_tlv_hdr {
--
2.11.0
^ permalink raw reply related
* [PATCH 06/10] qtnfmac: fix rssi data passed to wireless core
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless
Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov, Sergey Matyukevich
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
Fix RSSI values passed to wireless core by qtnfmac driver:
- fix RSSI values in scan results:
driver registers wiphy with CFG80211_SIGNAL_TYPE_MBM signal type,
so mBm should be passed using DBM_TO_MBM macro
- accompany firmware changes fixing RSSI values in received mgmt frames
update qlink message format and pass correct signed values to core
Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/event.c | 7 +++----
drivers/net/wireless/quantenna/qtnfmac/qlink.h | 11 ++++++-----
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c
index 9843ca36b74b..b3489b5b5d9e 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/event.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
@@ -237,9 +237,8 @@ qtnf_event_handle_mgmt_received(struct qtnf_vif *vif,
pr_debug("%s LEN:%u FC:%.4X SA:%pM\n", vif->netdev->name, frame_len,
le16_to_cpu(frame->frame_control), frame->addr2);
- cfg80211_rx_mgmt(&vif->wdev, le32_to_cpu(rxmgmt->freq),
- le32_to_cpu(rxmgmt->sig_dbm), rxmgmt->frame_data,
- frame_len, flags);
+ cfg80211_rx_mgmt(&vif->wdev, le32_to_cpu(rxmgmt->freq), rxmgmt->sig_dbm,
+ rxmgmt->frame_data, frame_len, flags);
return 0;
}
@@ -324,7 +323,7 @@ qtnf_event_handle_scan_results(struct qtnf_vif *vif,
sr->bssid, get_unaligned_le64(&sr->tsf),
le16_to_cpu(sr->capab),
le16_to_cpu(sr->bintval), ies, ies_len,
- sr->signal, GFP_KERNEL);
+ DBM_TO_MBM(sr->sig_dbm), GFP_KERNEL);
if (!bss)
return -ENOMEM;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 3e3de4629a53..1f150be98820 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -19,7 +19,7 @@
#include <linux/ieee80211.h>
-#define QLINK_PROTO_VER 7
+#define QLINK_PROTO_VER 8
#define QLINK_MACID_RSVD 0xFF
#define QLINK_VIFID_RSVD 0xFF
@@ -916,15 +916,16 @@ enum qlink_rxmgmt_flags {
* struct qlink_event_rxmgmt - data for QLINK_EVENT_MGMT_RECEIVED event
*
* @freq: Frequency on which the frame was received in MHz.
- * @sig_dbm: signal strength in dBm.
* @flags: bitmap of &enum qlink_rxmgmt_flags.
+ * @sig_dbm: signal strength in dBm.
* @frame_data: data of Rx'd frame itself.
*/
struct qlink_event_rxmgmt {
struct qlink_event ehdr;
__le32 freq;
- __le32 sig_dbm;
__le32 flags;
+ s8 sig_dbm;
+ u8 rsvd[3];
u8 frame_data[0];
} __packed;
@@ -936,7 +937,7 @@ struct qlink_event_rxmgmt {
* event was generated was discovered.
* @capab: capabilities field.
* @bintval: beacon interval announced by discovered BSS.
- * @signal: signal strength.
+ * @sig_dbm: signal strength in dBm.
* @bssid: BSSID announced by discovered BSS.
* @ssid_len: length of SSID announced by BSS.
* @ssid: SSID announced by discovered BSS.
@@ -948,7 +949,7 @@ struct qlink_event_scan_result {
__le16 freq;
__le16 capab;
__le16 bintval;
- s8 signal;
+ s8 sig_dbm;
u8 ssid_len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 bssid[ETH_ALEN];
--
2.11.0
^ permalink raw reply related
* [PATCH 05/10] qtnfmac: check for passed channel being NULL in MGMT_TX command
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless; +Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
From: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Parameters passed into .mgmt_tx callback may have a NULL channel in case
userspace wants to send a frame on current channel. Make sure this case
is handled, pass "freq==0" in case channel is not specififed to tell
wlan device to use current channel.
Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index a11278de678d..f5eb2cdefae2 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -381,6 +381,7 @@ qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
const struct ieee80211_mgmt *mgmt_frame = (void *)params->buf;
u32 short_cookie = prandom_u32();
u16 flags = 0;
+ u16 freq;
*cookie = short_cookie;
@@ -393,13 +394,21 @@ qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
if (params->dont_wait_for_ack)
flags |= QLINK_MGMT_FRAME_TX_FLAG_ACK_NOWAIT;
+ /* If channel is not specified, pass "freq = 0" to tell device
+ * firmware to use current channel.
+ */
+ if (params->chan)
+ freq = params->chan->center_freq;
+ else
+ freq = 0;
+
pr_debug("%s freq:%u; FC:%.4X; DA:%pM; len:%zu; C:%.8X; FL:%.4X\n",
- wdev->netdev->name, params->chan->center_freq,
+ wdev->netdev->name, freq,
le16_to_cpu(mgmt_frame->frame_control), mgmt_frame->da,
params->len, short_cookie, flags);
return qtnf_cmd_send_mgmt_frame(vif, short_cookie, flags,
- params->chan->center_freq,
+ freq,
params->buf, params->len);
}
--
2.11.0
^ permalink raw reply related
* [PATCH 04/10] qtnfmac: change default interface mode from AP to STA
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless; +Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
From: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
To mimic mac80211 behaviour, change default interface type from AP to STA.
Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 2 +-
drivers/net/wireless/quantenna/qtnfmac/core.c | 11 ++++-------
drivers/net/wireless/quantenna/qtnfmac/core.h | 3 +--
3 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index 64fc4119b3a6..a11278de678d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -190,7 +190,7 @@ static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy,
goto err_mac;
}
- if (qtnf_core_net_attach(mac, vif, name, name_assign_t, type)) {
+ if (qtnf_core_net_attach(mac, vif, name, name_assign_t)) {
pr_err("VIF%u.%u: failed to attach netdev\n", mac->macid,
vif->vifid);
goto err_net;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c
index 2d2c1ea65cb2..ea850cb7dffc 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
@@ -258,7 +258,7 @@ static void qtnf_mac_init_primary_intf(struct qtnf_wmac *mac)
{
struct qtnf_vif *vif = &mac->iflist[QTNF_PRIMARY_VIF_IDX];
- vif->wdev.iftype = NL80211_IFTYPE_AP;
+ vif->wdev.iftype = NL80211_IFTYPE_STATION;
vif->bss_priority = QTNF_DEF_BSS_PRIORITY;
vif->wdev.wiphy = priv_to_wiphy(mac);
INIT_WORK(&vif->reset_work, qtnf_vif_reset_handler);
@@ -298,8 +298,7 @@ static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus,
}
int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *vif,
- const char *name, unsigned char name_assign_type,
- enum nl80211_iftype iftype)
+ const char *name, unsigned char name_assign_type)
{
struct wiphy *wiphy = priv_to_wiphy(mac);
struct net_device *dev;
@@ -320,7 +319,6 @@ int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *vif,
dev->needs_free_netdev = true;
dev_net_set(dev, wiphy_net(wiphy));
dev->ieee80211_ptr = &vif->wdev;
- dev->ieee80211_ptr->iftype = iftype;
ether_addr_copy(dev->dev_addr, vif->mac_addr);
SET_NETDEV_DEV(dev, wiphy_dev(wiphy));
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
@@ -418,7 +416,7 @@ static int qtnf_core_mac_attach(struct qtnf_bus *bus, unsigned int macid)
goto error;
}
- ret = qtnf_cmd_send_add_intf(vif, NL80211_IFTYPE_AP, vif->mac_addr);
+ ret = qtnf_cmd_send_add_intf(vif, vif->wdev.iftype, vif->mac_addr);
if (ret) {
pr_err("MAC%u: failed to add VIF\n", macid);
goto error;
@@ -446,8 +444,7 @@ static int qtnf_core_mac_attach(struct qtnf_bus *bus, unsigned int macid)
rtnl_lock();
- ret = qtnf_core_net_attach(mac, vif, "wlan%d", NET_NAME_ENUM,
- NL80211_IFTYPE_AP);
+ ret = qtnf_core_net_attach(mac, vif, "wlan%d", NET_NAME_ENUM);
rtnl_unlock();
if (ret) {
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index 1b7bc0318f3e..8e42d1bdc65f 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -144,8 +144,7 @@ struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac);
struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac);
struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus);
int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *priv,
- const char *name, unsigned char name_assign_type,
- enum nl80211_iftype iftype);
+ const char *name, unsigned char name_assign_type);
void qtnf_main_work_queue(struct work_struct *work);
int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed);
int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac);
--
2.11.0
^ permalink raw reply related
* [PATCH 03/10] qtnfmac: add support for radar detection and CAC
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless; +Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
From: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Implement two parts of radar handling logic:
- cfg80211 .start_radar_detect callback to allow nl80211 to initiate CAC
- radar event to allow wlan device to advertize CAC and radar events
Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 18 ++++++-
drivers/net/wireless/quantenna/qtnfmac/commands.c | 38 ++++++++++++++
drivers/net/wireless/quantenna/qtnfmac/commands.h | 3 ++
drivers/net/wireless/quantenna/qtnfmac/event.c | 61 +++++++++++++++++++++++
drivers/net/wireless/quantenna/qtnfmac/qlink.h | 36 +++++++++++++
5 files changed, 155 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index 67a57c841c3f..64fc4119b3a6 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -751,6 +751,21 @@ static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev,
return ret;
}
+static int qtnf_start_radar_detection(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+{
+ struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
+ int ret;
+
+ ret = qtnf_cmd_start_cac(vif, chandef, cac_time_ms);
+ if (ret)
+ pr_err("%s: failed to start CAC ret=%d\n", ndev->name, ret);
+
+ return ret;
+}
+
static struct cfg80211_ops qtn_cfg80211_ops = {
.add_virtual_intf = qtnf_add_virtual_intf,
.change_virtual_intf = qtnf_change_virtual_intf,
@@ -774,7 +789,8 @@ static struct cfg80211_ops qtn_cfg80211_ops = {
.disconnect = qtnf_disconnect,
.dump_survey = qtnf_dump_survey,
.get_channel = qtnf_get_channel,
- .channel_switch = qtnf_channel_switch
+ .channel_switch = qtnf_channel_switch,
+ .start_radar_detection = qtnf_start_radar_detection,
};
static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in,
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index bed81f0cb1cd..7089f3eb7a87 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -2512,3 +2512,41 @@ int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef)
consume_skb(resp_skb);
return ret;
}
+
+int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
+ const struct cfg80211_chan_def *chdef,
+ u32 cac_time_ms)
+{
+ struct qtnf_bus *bus = vif->mac->bus;
+ struct sk_buff *cmd_skb;
+ struct qlink_cmd_start_cac *cmd;
+ int ret;
+ u16 res_code;
+
+ cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
+ QLINK_CMD_START_CAC,
+ sizeof(*cmd));
+ if (unlikely(!cmd_skb))
+ return -ENOMEM;
+
+ cmd = (struct qlink_cmd_start_cac *)cmd_skb->data;
+ cmd->cac_time_ms = cpu_to_le32(cac_time_ms);
+ qlink_chandef_cfg2q(chdef, &cmd->chan);
+
+ qtnf_bus_lock(bus);
+ ret = qtnf_cmd_send(bus, cmd_skb, &res_code);
+ qtnf_bus_unlock(bus);
+
+ if (ret)
+ return ret;
+
+ switch (res_code) {
+ case QLINK_CMD_RESULT_OK:
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h
index d981a76e5835..07a957af9a58 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h
@@ -76,5 +76,8 @@ int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel,
int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif,
struct cfg80211_csa_settings *params);
int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef);
+int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
+ const struct cfg80211_chan_def *chdef,
+ u32 cac_time_ms);
#endif /* QLINK_COMMANDS_H_ */
diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c
index a3a18d8469ae..9843ca36b74b 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/event.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
@@ -395,6 +395,63 @@ qtnf_event_handle_freq_change(struct qtnf_wmac *mac,
return 0;
}
+static int qtnf_event_handle_radar(struct qtnf_vif *vif,
+ const struct qlink_event_radar *ev,
+ u16 len)
+{
+ struct wiphy *wiphy = priv_to_wiphy(vif->mac);
+ struct cfg80211_chan_def chandef;
+
+ if (len < sizeof(*ev)) {
+ pr_err("MAC%u: payload is too short\n", vif->mac->macid);
+ return -EINVAL;
+ }
+
+ if (!wiphy->registered || !vif->netdev)
+ return 0;
+
+ qlink_chandef_q2cfg(wiphy, &ev->chan, &chandef);
+
+ if (!cfg80211_chandef_valid(&chandef)) {
+ pr_err("MAC%u: bad channel f1=%u f2=%u bw=%u\n",
+ vif->mac->macid,
+ chandef.center_freq1, chandef.center_freq2,
+ chandef.width);
+ return -EINVAL;
+ }
+
+ pr_info("%s: radar event=%u f1=%u f2=%u bw=%u\n",
+ vif->netdev->name, ev->event,
+ chandef.center_freq1, chandef.center_freq2,
+ chandef.width);
+
+ switch (ev->event) {
+ case QLINK_RADAR_DETECTED:
+ cfg80211_radar_event(wiphy, &chandef, GFP_KERNEL);
+ break;
+ case QLINK_RADAR_CAC_FINISHED:
+ if (!vif->wdev.cac_started)
+ break;
+
+ cfg80211_cac_event(vif->netdev, &chandef,
+ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+ break;
+ case QLINK_RADAR_CAC_ABORTED:
+ if (!vif->wdev.cac_started)
+ break;
+
+ cfg80211_cac_event(vif->netdev, &chandef,
+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+ break;
+ default:
+ pr_warn("%s: unhandled radar event %u\n",
+ vif->netdev->name, ev->event);
+ break;
+ }
+
+ return 0;
+}
+
static int qtnf_event_parse(struct qtnf_wmac *mac,
const struct sk_buff *event_skb)
{
@@ -449,6 +506,10 @@ static int qtnf_event_parse(struct qtnf_wmac *mac,
ret = qtnf_event_handle_freq_change(mac, (const void *)event,
event_len);
break;
+ case QLINK_EVENT_RADAR:
+ ret = qtnf_event_handle_radar(vif, (const void *)event,
+ event_len);
+ break;
default:
pr_warn("unknown event type: %x\n", event_id);
break;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 534d11e4175a..3e3de4629a53 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -205,6 +205,7 @@ struct qlink_auth_encr {
* @QLINK_CMD_REG_NOTIFY: notify device about regulatory domain change. This
* command is supported only if device reports QLINK_HW_SUPPORTS_REG_UPDATE
* capability.
+ * @QLINK_CMD_START_CAC: start radar detection procedure on a specified channel.
*/
enum qlink_cmd_type {
QLINK_CMD_FW_INIT = 0x0001,
@@ -224,6 +225,7 @@ enum qlink_cmd_type {
QLINK_CMD_BAND_INFO_GET = 0x001A,
QLINK_CMD_CHAN_SWITCH = 0x001B,
QLINK_CMD_CHAN_GET = 0x001C,
+ QLINK_CMD_START_CAC = 0x001D,
QLINK_CMD_START_AP = 0x0021,
QLINK_CMD_STOP_AP = 0x0022,
QLINK_CMD_GET_STA_INFO = 0x0030,
@@ -617,6 +619,18 @@ struct qlink_cmd_start_ap {
u8 info[0];
} __packed;
+/**
+ * struct qlink_cmd_start_cac - data for QLINK_CMD_START_CAC command
+ *
+ * @chan: a channel to start a radar detection procedure on.
+ * @cac_time_ms: CAC time.
+ */
+struct qlink_cmd_start_cac {
+ struct qlink_cmd chdr;
+ struct qlink_chandef chan;
+ __le32 cac_time_ms;
+} __packed;
+
/* QLINK Command Responses messages related definitions
*/
@@ -814,6 +828,7 @@ enum qlink_event_type {
QLINK_EVENT_BSS_JOIN = 0x0026,
QLINK_EVENT_BSS_LEAVE = 0x0027,
QLINK_EVENT_FREQ_CHANGE = 0x0028,
+ QLINK_EVENT_RADAR = 0x0029,
};
/**
@@ -963,6 +978,27 @@ struct qlink_event_scan_complete {
__le32 flags;
} __packed;
+enum qlink_radar_event {
+ QLINK_RADAR_DETECTED,
+ QLINK_RADAR_CAC_FINISHED,
+ QLINK_RADAR_CAC_ABORTED,
+ QLINK_RADAR_NOP_FINISHED,
+ QLINK_RADAR_PRE_CAC_EXPIRED,
+};
+
+/**
+ * struct qlink_event_radar - data for QLINK_EVENT_RADAR event
+ *
+ * @chan: channel on which radar event happened.
+ * @event: radar event type, one of &enum qlink_radar_event.
+ */
+struct qlink_event_radar {
+ struct qlink_event ehdr;
+ struct qlink_chandef chan;
+ u8 event;
+ u8 rsvd[3];
+} __packed;
+
/* QLINK TLVs (Type-Length Values) definitions
*/
--
2.11.0
^ permalink raw reply related
* [PATCH 02/10] qtnfmac: pass complete channel data between driver and firmware
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless
Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov, Sergey Matyukevich
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
Center frequency is not enough to describe the channel in HT and VHT
modes. For 40MHz and 80MHz channels both primary channel and center
frequency should be specified in order to qualify channel completely.
This change adds primary channel info into qlink_chandef structure.
Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 3 +-
drivers/net/wireless/quantenna/qtnfmac/commands.c | 14 +++---
drivers/net/wireless/quantenna/qtnfmac/event.c | 3 +-
drivers/net/wireless/quantenna/qtnfmac/qlink.h | 57 ++++++++++++++++------
.../net/wireless/quantenna/qtnfmac/qlink_util.c | 48 ++++++++----------
5 files changed, 74 insertions(+), 51 deletions(-)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index eeffc7689455..67a57c841c3f 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -717,7 +717,8 @@ qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
}
if (!cfg80211_chandef_valid(chandef)) {
- pr_err("%s: bad chan freq1=%u freq2=%u bw=%u\n", ndev->name,
+ pr_err("%s: bad channel freq=%u cf1=%u cf2=%u bw=%u\n",
+ ndev->name, chandef->chan->center_freq,
chandef->center_freq1, chandef->center_freq2,
chandef->width);
ret = -ENODATA;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index 8bc8dd637315..bed81f0cb1cd 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -247,7 +247,7 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
chtlv->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANDEF);
chtlv->hdr.len = cpu_to_le16(sizeof(*chtlv) -
sizeof(chtlv->hdr));
- qlink_chandef_cfg2q(&s->chandef, &chtlv->chan);
+ qlink_chandef_cfg2q(&s->chandef, &chtlv->chdef);
}
qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_HEAD,
@@ -1186,7 +1186,7 @@ qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band,
size_t tlv_len;
size_t tlv_dlen;
const struct qlink_tlv_hdr *tlv;
- const struct qlink_tlv_channel *qchan;
+ const struct qlink_channel *qchan;
struct ieee80211_channel *chan;
unsigned int chidx = 0;
u32 qflags;
@@ -1232,7 +1232,7 @@ qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band,
switch (tlv_type) {
case QTN_TLV_ID_CHANNEL:
- if (unlikely(tlv_len != sizeof(*qchan))) {
+ if (unlikely(tlv_dlen != sizeof(*qchan))) {
pr_err("invalid channel TLV len %zu\n",
tlv_len);
goto error_ret;
@@ -1243,7 +1243,7 @@ qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band,
goto error_ret;
}
- qchan = (const struct qlink_tlv_channel *)tlv;
+ qchan = (const struct qlink_channel *)tlv->val;
chan = &band->channels[chidx++];
qflags = le32_to_cpu(qchan->flags);
@@ -2037,8 +2037,8 @@ static void qtnf_cmd_channel_tlv_add(struct sk_buff *cmd_skb,
qchan = skb_put_zero(cmd_skb, sizeof(*qchan));
qchan->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL);
qchan->hdr.len = cpu_to_le16(sizeof(*qchan) - sizeof(qchan->hdr));
- qchan->center_freq = cpu_to_le16(sc->center_freq);
- qchan->hw_value = cpu_to_le16(sc->hw_value);
+ qchan->chan.center_freq = cpu_to_le16(sc->center_freq);
+ qchan->chan.hw_value = cpu_to_le16(sc->hw_value);
if (sc->flags & IEEE80211_CHAN_NO_IR)
flags |= QLINK_CHAN_NO_IR;
@@ -2046,7 +2046,7 @@ static void qtnf_cmd_channel_tlv_add(struct sk_buff *cmd_skb,
if (sc->flags & IEEE80211_CHAN_RADAR)
flags |= QLINK_CHAN_RADAR;
- qchan->flags = cpu_to_le32(flags);
+ qchan->chan.flags = cpu_to_le32(flags);
}
int qtnf_cmd_send_scan(struct qtnf_wmac *mac)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c
index 4abc6d9ed560..a3a18d8469ae 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/event.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
@@ -369,7 +369,8 @@ qtnf_event_handle_freq_change(struct qtnf_wmac *mac,
qlink_chandef_q2cfg(wiphy, &data->chan, &chandef);
if (!cfg80211_chandef_valid(&chandef)) {
- pr_err("MAC%u: bad channel f1=%u f2=%u bw=%u\n", mac->macid,
+ pr_err("MAC%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n",
+ mac->macid, chandef.chan->center_freq,
chandef.center_freq1, chandef.center_freq2,
chandef.width);
return -EINVAL;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index a432fb001c41..534d11e4175a 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -19,7 +19,7 @@
#include <linux/ieee80211.h>
-#define QLINK_PROTO_VER 6
+#define QLINK_PROTO_VER 7
#define QLINK_MACID_RSVD 0xFF
#define QLINK_VIFID_RSVD 0xFF
@@ -122,17 +122,49 @@ enum qlink_channel_width {
};
/**
+ * struct qlink_channel - qlink control channel definition
+ *
+ * @hw_value: hardware-specific value for the channel
+ * @center_freq: center frequency in MHz
+ * @flags: channel flags from &enum qlink_channel_flags
+ * @band: band this channel belongs to
+ * @max_antenna_gain: maximum antenna gain in dBi
+ * @max_power: maximum transmission power (in dBm)
+ * @max_reg_power: maximum regulatory transmission power (in dBm)
+ * @dfs_state: current state of this channel.
+ * Only relevant if radar is required on this channel.
+ * @beacon_found: helper to regulatory code to indicate when a beacon
+ * has been found on this channel. Use regulatory_hint_found_beacon()
+ * to enable this, this is useful only on 5 GHz band.
+ */
+struct qlink_channel {
+ __le16 hw_value;
+ __le16 center_freq;
+ __le32 flags;
+ u8 band;
+ u8 max_antenna_gain;
+ u8 max_power;
+ u8 max_reg_power;
+ __le32 dfs_cac_ms;
+ u8 dfs_state;
+ u8 beacon_found;
+ u8 rsvd[2];
+} __packed;
+
+/**
* struct qlink_chandef - qlink channel definition
*
+ * @chan: primary channel definition
* @center_freq1: center frequency of first segment
* @center_freq2: center frequency of second segment (80+80 only)
* @width: channel width, one of @enum qlink_channel_width
*/
struct qlink_chandef {
+ struct qlink_channel chan;
__le16 center_freq1;
__le16 center_freq2;
u8 width;
- u8 rsvd[3];
+ u8 rsvd;
} __packed;
#define QLINK_MAX_NR_CIPHER_SUITES 5
@@ -1113,19 +1145,16 @@ enum qlink_dfs_state {
QLINK_DFS_AVAILABLE,
};
+/**
+ * struct qlink_tlv_channel - data for QTN_TLV_ID_CHANNEL TLV
+ *
+ * Channel settings.
+ *
+ * @channel: ieee80211 channel settings.
+ */
struct qlink_tlv_channel {
struct qlink_tlv_hdr hdr;
- __le16 hw_value;
- __le16 center_freq;
- __le32 flags;
- u8 band;
- u8 max_antenna_gain;
- u8 max_power;
- u8 max_reg_power;
- __le32 dfs_cac_ms;
- u8 dfs_state;
- u8 beacon_found;
- u8 rsvd[2];
+ struct qlink_channel chan;
} __packed;
/**
@@ -1137,7 +1166,7 @@ struct qlink_tlv_channel {
*/
struct qlink_tlv_chandef {
struct qlink_tlv_hdr hdr;
- struct qlink_chandef chan;
+ struct qlink_chandef chdef;
} __packed;
enum qlink_ie_set_type {
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
index 61d999affb09..37b78f00e8e5 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
@@ -100,34 +100,6 @@ static enum nl80211_chan_width qlink_chanwidth_to_nl(u8 qlw)
}
}
-void qlink_chandef_q2cfg(struct wiphy *wiphy,
- const struct qlink_chandef *qch,
- struct cfg80211_chan_def *chdef)
-{
- chdef->center_freq1 = le16_to_cpu(qch->center_freq1);
- chdef->center_freq2 = le16_to_cpu(qch->center_freq2);
- chdef->width = qlink_chanwidth_to_nl(qch->width);
-
- switch (chdef->width) {
- case NL80211_CHAN_WIDTH_20_NOHT:
- case NL80211_CHAN_WIDTH_20:
- case NL80211_CHAN_WIDTH_5:
- case NL80211_CHAN_WIDTH_10:
- chdef->chan = ieee80211_get_channel(wiphy, chdef->center_freq1);
- break;
- case NL80211_CHAN_WIDTH_40:
- case NL80211_CHAN_WIDTH_80:
- case NL80211_CHAN_WIDTH_80P80:
- case NL80211_CHAN_WIDTH_160:
- chdef->chan = ieee80211_get_channel(wiphy,
- chdef->center_freq1 - 10);
- break;
- default:
- chdef->chan = NULL;
- break;
- }
-}
-
static u8 qlink_chanwidth_nl_to_qlink(enum nl80211_chan_width nlwidth)
{
switch (nlwidth) {
@@ -152,9 +124,29 @@ static u8 qlink_chanwidth_nl_to_qlink(enum nl80211_chan_width nlwidth)
}
}
+void qlink_chandef_q2cfg(struct wiphy *wiphy,
+ const struct qlink_chandef *qch,
+ struct cfg80211_chan_def *chdef)
+{
+ struct ieee80211_channel *chan;
+
+ chan = ieee80211_get_channel(wiphy, le16_to_cpu(qch->chan.center_freq));
+
+ chdef->chan = chan;
+ chdef->center_freq1 = le16_to_cpu(qch->center_freq1);
+ chdef->center_freq2 = le16_to_cpu(qch->center_freq2);
+ chdef->width = qlink_chanwidth_to_nl(qch->width);
+}
+
void qlink_chandef_cfg2q(const struct cfg80211_chan_def *chdef,
struct qlink_chandef *qch)
{
+ struct ieee80211_channel *chan = chdef->chan;
+
+ qch->chan.hw_value = cpu_to_le16(chan->hw_value);
+ qch->chan.center_freq = cpu_to_le16(chan->center_freq);
+ qch->chan.flags = cpu_to_le32(chan->flags);
+
qch->center_freq1 = cpu_to_le16(chdef->center_freq1);
qch->center_freq2 = cpu_to_le16(chdef->center_freq2);
qch->width = qlink_chanwidth_nl_to_qlink(chdef->width);
--
2.11.0
^ permalink raw reply related
* [PATCH 01/10] qtnfmac: check that MAC exists in regulatory notifier
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless; +Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
In-Reply-To: <20171113102815.11254-1-sergey.matyukevich.os@quantenna.com>
From: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
It is possible that regulatory notifier is called before MAC data
was allocated. We need to verify that MAC data exists before trying
to send a regulatory change event.
Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
---
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index 7d6dc76c930a..eeffc7689455 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -802,6 +802,9 @@ static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in,
continue;
mac = bus->mac[mac_idx];
+ if (!mac)
+ continue;
+
wiphy = priv_to_wiphy(mac);
for (band = 0; band < NUM_NL80211_BANDS; ++band) {
--
2.11.0
^ permalink raw reply related
* [PATCH 0/10] qtnfmac: regular portion of features and fixes
From: Sergey Matyukevich @ 2017-11-13 10:28 UTC (permalink / raw)
To: linux-wireless; +Cc: Igor Mitsyanko, Avinash Patil, Vasily Ulyanov
Hello Kalle and all,
Here is the next set of patches for qtnfmac driver. This set
enables the following features:
- radar detection and CAC support
- MAC-based ACL support
- make GET_STA_STATS format more flexible and future-proof
- inform wireless core about supported extended capabilities
Besides, several issues has been fixed. The most notable
among them are:
- fix RSSI data passed from qtnfmac to wireless core
- pass complete channel information between firmware and driver,
including both primary channel and center frequency
The last fix enables proper operation channel report by iw tool
and fixes ACS mode in hostapd.
Regards,
Sergey
Igor Mitsyanko (6):
qtnfmac: check that MAC exists in regulatory notifier
qtnfmac: add support for radar detection and CAC
qtnfmac: change default interface mode from AP to STA
qtnfmac: check for passed channel being NULL in MGMT_TX
qtnfmac: modify GET_STA_STATS cmd format for
qtnfmac: keeping track of "generation" for STA info
Sergey Matyukevich (2):
qtnfmac: pass complete channel data between driver and
qtnfmac: fix rssi data passed to wireless core
Vasily Ulyanov (2):
qtnfmac: fill wiphy's extended capabilities
qtnfmac: support MAC address based access control
cfg80211.c | 63 ++++++++-
commands.c | 375 ++++++++++++++++++++++++++++++++++++++++++-----------------
commands.h | 5
core.c | 13 --
core.h | 8 -
event.c | 76 ++++++++++-
qlink.h | 340 ++++++++++++++++++++++++++++++++++++++++-------------
qlink_util.c | 78 +++++++-----
qlink_util.h | 4
util.c | 8 -
util.h | 4
11 files changed, 728 insertions(+), 246 deletions(-)
^ permalink raw reply
* Re: Setting single rate in ath10k broken by "reject/clear user rate mask if not usable"
From: Johannes Berg @ 2017-11-13 10:09 UTC (permalink / raw)
To: Ben Greear, Oleksij Rempel, linux-wireless@vger.kernel.org,
ath10k, kirtika
In-Reply-To: <a1adb2ea-44c4-67ae-1044-08c4dc16bb9a@candelatech.com>
On Fri, 2017-10-27 at 13:41 -0700, Ben Greear wrote:
> ath10k ignores the tx rateset pretty much entirely when sending management
> frames, so even if you set the tx rateset to have only VHT MCS 8,
> management frames are still sent with legacy ratesets.
So that's a driver bug.
> My end goal about this part is to be able to configure a single tx rate
> and have that be allowed again, at least with ath10k.
>
> Maybe a new flag for drivers like ath10k that at least somewhat ignore
> the tx-rateset for management frames, and this flag would allow us to
> bypass the cannot-set-single-rate check?
What? No, I'm not going to put a driver bug into the API like that!
johannes
^ permalink raw reply
* Re: [RFC] cfg80211: Implement Multiple BSSID capability in scanning
From: Johannes Berg @ 2017-11-13 10:07 UTC (permalink / raw)
To: Jouni Malinen; +Cc: linux-wireless, Peng Xu
In-Reply-To: <1509554358-10473-1-git-send-email-jouni@qca.qualcomm.com>
Hi,
Thanks for working on this.
> +u8 *gen_new_bssid(const u8 *bssid, u8 max_bssid, u8 mbssid_index, gfp_t gfp)
I looks like this should be static? I'm also pretty sure you leak the
result, I guess you shouldn't allocate memory here. Just passing in a
pointer to a buffer (that could be on the stack in the caller) would
work fine, afaict.
> +{
> + int i;
> + u64 bssid_a, bssid_b, bssid_tmp = 0;
> + u64 lsb_n;
> + u8 *new_bssid;
> +
> + for (i = 0; i < 5; i++) {
> + bssid_tmp = bssid_tmp | bssid[i];
> + bssid_tmp = bssid_tmp << 8;
> + }
ether_addr_to_u64()
> + bssid_tmp = bssid_tmp | bssid[5];
> +
> + lsb_n = bssid_tmp & ((1 << max_bssid) - 1);
> + bssid_b = (lsb_n + mbssid_index) % (1 << max_bssid);
> + bssid_a = bssid_tmp & ~((1 << max_bssid) - 1);
> + bssid_tmp = bssid_a | bssid_b;
This seems like a somewhat roundabout way of getting this, even if this
is how the spec says it's done. How about just
new_bssid = bssid_tmp;
new_bssid &= ~((1 << max_bssid) - 1);
new_bssid |= (lsb_n + mbssid_index) % (1 << max_bssid);
> + new_bssid = kzalloc(6, gfp);
> + if (!new_bssid)
> + return NULL;
don't allocate
> + for (i = 0; i < 6; i++) {
> + new_bssid[5 - i] = bssid_tmp & 0xFF;
> + bssid_tmp >>= 8;
> + }
u64_to_ether_addr()
Also, wouldn't it be better to handle both the index and list option
from the Co-Located BSSID List subelement in the same function?
> +size_t calculate_new_ie_len(const u8 *ie, size_t ielen, const u8 *subelement,
> + size_t subie_len)
static
> +{
> + size_t new_ie_len;
> + const u8 *old_ie, *new_ie;
> + int delta;
> +
> + new_ie_len = ielen;
> +
> + /* calculate length difference between ssid ie */
> + old_ie = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
> + new_ie = cfg80211_find_ie(WLAN_EID_SSID, subelement, subie_len);
> + if (old_ie && new_ie) {
> + delta = new_ie[1] - old_ie[1];
> + new_ie_len += delta;
> + }
No need for the delta variable. Also, this goes wrong if old_ie is
missing but new_ie is present. Probably can't happen, but still.
> + /* calculate length difference between FMS Descriptor ie */
> + old_ie = cfg80211_find_ie(WLAN_EID_FMS_DESCRIPTOR, ie, ielen);
> + new_ie = cfg80211_find_ie(WLAN_EID_FMS_DESCRIPTOR, subelement,
> + subie_len);
> + if (!old_ie && new_ie)
> + new_ie_len += new_ie[1];
> + else if (old_ie && new_ie)
> + new_ie_len += (new_ie[1] - old_ie[1]);
Technically that's missing teh old_ie && !new_ie case. Perhaps you
should pull that out into a small separate function:
new_ie_len += ie_len_delta(WLAN_EID_SSID, ie, ielen,
subelement, subie_len);
or so.
> + /* calculate length difference for same vendor ie */
> + new_ie = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, subelement,
> + subie_len);
> + while (new_ie && (new_ie - subelement) < subie_len) {
> + old_ie = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, ie, ielen);
> + while (old_ie && ((old_ie - ie) < ielen)) {
> + if (!memcmp(new_ie + 2, old_ie + 2, 5)) {
> + /* same vendor ie, calculate length difference*/
> + new_ie_len += (new_ie[1] - old_ie[1]);
> + break;
> + }
> + old_ie = cfg80211_find_ie(
> + WLAN_EID_VENDOR_SPECIFIC,
> + old_ie + old_ie[1] + 2,
> + ielen - (old_ie + old_ie[1] + 2 - ie));
> + }
> + new_ie = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC,
> + new_ie + new_ie[1] + 2,
> + subie_len -
> + (new_ie + new_ie[1] + 2 -
> + subelement));
> + }
Perhaps that could be used here too, though I'm not really sure exactly
what you're doing here.
Can you add a comment pointing to where this logic is mentioned in the
spec?
> + /* check other ie in subelement */
> + new_ie = cfg80211_find_ie(WLAN_EID_NON_TX_BSSID_CAP, subelement,
> + subie_len);
> + while (new_ie && (new_ie - subelement < subie_len)) {
> + if (new_ie[0] == WLAN_EID_NON_TX_BSSID_CAP ||
> + new_ie[0] == WLAN_EID_SSID ||
> + new_ie[0] == WLAN_EID_FMS_DESCRIPTOR ||
> + new_ie[0] == WLAN_EID_MULTI_BSSID_IDX) {
> + if ((new_ie + 2 + new_ie[1] - subelement) ==
> + subie_len) {
> + /* new_ie is the last ie */
> + break;
> + }
> + new_ie += new_ie[1] + 2;
> + continue;
> + }
> + new_ie_len += (new_ie[1] + 2);
> + if ((new_ie + 2 + new_ie[1] - subelement) == subie_len) {
> + /* new_ie is the last ie */
> + break;
> + }
> + new_ie += new_ie[1] + 2;
> + }
This also seems pretty complex, perhaps pull it out and add comments?
I'm not really sure I understand it at all.
> +u8 *gen_new_ie(const u8 *ie, size_t ielen, const u8 *subelement,
> + size_t subie_len, size_t new_ie_len, bool is_beacon,
> + gfp_t gfp)
Again, static (I'll stop with that, please run sparse).
Also, again, not sure how you free this?
> +{
> + u8 *new_ie, *pos;
> + const u8 *tmp_old, *tmp_new, *tmp;
> + size_t left_len;
> +
> + new_ie = kzalloc(new_ie_len, gfp);
> + if (!new_ie)
> + return NULL;
> +
> + pos = new_ie;
> +
> + /* set new ssid */
> + tmp_new = cfg80211_find_ie(WLAN_EID_SSID, subelement, subie_len);
> + if (tmp_new) {
> + memcpy(pos, tmp_new, tmp_new[1] + 2);
> + pos += (tmp_new[1] + 2);
> + }
> +
> + /* go through IEs in ie and subelement, merge them into new_ie */
> + tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
> + if (!tmp_old)
> + return NULL;
> + tmp_old += tmp_old[1] + 2;
> + tmp_new = cfg80211_find_ie(WLAN_EID_SSID, subelement, subie_len);
> + left_len = subie_len - tmp_new[1];
> + tmp_new += tmp_new[1] + 2;
> +
> + while ((tmp_old - ie) < ielen) {
> + if (tmp_old[0] == 0) {
> + tmp_old++;
> + continue;
> + }
> +
> + tmp = cfg80211_find_ie(tmp_old[0], tmp_new, left_len);
> + if (!tmp) {
> + /* ie in old ie but not in subelement */
> + memcpy(pos, tmp_old, tmp_old[1] + 2);
> + pos += tmp_old[1] + 2;
> + } else {
> + if (tmp_old[0] == WLAN_EID_MULTIPLE_BSSID)
> + continue;
> +
> + if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) {
> + if (!memcmp(tmp_old + 2, tmp + 2, 5)) {
> + /* same vendor ie, copy from new ie */
> + memcpy(pos, tmp, tmp[1] + 2);
> + pos += tmp[1] + 2;
> + } else {
> + memcpy(pos, tmp_old, tmp_old[1] + 2);
> + pos += tmp_old[1] + 2;
> + }
> + tmp_old += tmp_old[1] + 2;
> + continue;
> + }
> +
> + /* copy ie from subelement into new ie */
> + memcpy(pos, tmp, tmp[1] + 2);
> + pos += tmp[1] + 2;
> + }
> + tmp_old += tmp_old[1] + 2;
> + }
> +
> + while ((tmp_new - subelement) < subie_len) {
> + tmp = cfg80211_find_ie(tmp_new[0], new_ie, pos - new_ie);
> + if (!tmp && tmp_new[0] != WLAN_EID_MULTI_BSSID_IDX) {
> + /* ie is new ie but not in new_ie */
> + memcpy(pos, tmp_new, tmp_new[1] + 2);
> + pos += tmp_new[1] + 2;
> + } else {
> + if (tmp && tmp_new[0] == WLAN_EID_VENDOR_SPECIFIC)
> + if (memcmp(tmp_new + 2, tmp + 2, 5)) {
> + memcpy(pos, tmp_new, tmp_new[1] + 2);
> + pos += tmp_new[1] + 2;
> + }
> + }
> + tmp_new += tmp_new[1] + 2;
> + }
> +
> + return new_ie;
> +}
This is pretty similar logic to the length calculations above.
Perhaps this one could be implemented with some kind of making all the
memcpy()s optional, and then you could call it with a NULL output
buffer to calculate the length, or something like that?
> +void add_nontrans_list(struct cfg80211_internal_bss *trans_bss,
> + struct cfg80211_internal_bss *nontrans_bss)
> +{
> + const u8 *ssid;
> + size_t ssid_len;
> + struct cfg80211_internal_bss *bss = NULL;
> +
> + ssid = ieee80211_bss_get_ie(&nontrans_bss->pub, WLAN_EID_SSID);
> + if (!ssid)
> + return;
> + ssid_len = ssid[1];
> + ssid = ssid + 2;
> +
> + /* check if nontrans_bss is in the list */
> + if (!list_empty(&trans_bss->nontrans_list)) {
> + list_for_each_entry(bss, &trans_bss->nontrans_list,
> + nontrans_list) {
There's no value in checking if a list is empty before iterating it, if
you just iterate an empty list you'll iterate zero times :)
> + if (is_bss(&bss->pub, nontrans_bss->pub.bssid,
> + ssid, ssid_len))
> + return;
It seems to me that you need to return some kind of status, to figure
out whether or not the new entry should be freed?
Overall, the memory allocation/tracking here seems to need quite a bit
of work.
> /* Returned bss is reference counted and must be cleaned up appropriately. */
> struct cfg80211_bss *
> -cfg80211_inform_bss_data(struct wiphy *wiphy,
> - struct cfg80211_inform_bss *data,
> - enum cfg80211_bss_frame_type ftype,
> - const u8 *bssid, u64 tsf, u16 capability,
> - u16 beacon_interval, const u8 *ie, size_t ielen,
> - gfp_t gfp)
> +cfg80211_inform_single_bss_data(struct wiphy *wiphy,
> + struct cfg80211_inform_bss *data,
> + enum cfg80211_bss_frame_type ftype,
> + const u8 *bssid, u64 tsf, u16 capability,
> + u16 beacon_interval, const u8 *ie, size_t ielen,
> + struct cfg80211_bss *trans_bss,
> + gfp_t gfp)
I guess this should now be static too.
> -/* cfg80211_inform_bss_width_frame helper */
> +void proc_mbssid_data(struct wiphy *wiphy,
> + struct cfg80211_inform_bss *data,
> + enum cfg80211_bss_frame_type ftype,
> + const u8 *bssid, u64 tsf, u16 capability,
> + u16 beacon_interval, const u8 *ie, size_t ielen,
> + struct cfg80211_bss *trans_bss,
> + gfp_t gfp)
> +{
> + const u8 *pos, *subelement, *new_ie, *new_bssid, *mbssid_end_pos;
> + const u8 *tmp, *mbssid_index_ie;
> + size_t subie_len, new_ie_len;
"proc" is a really bad prefix for anything in Linux ...
> + bool is_beacon = false;
> +
> + if (ftype == CFG80211_BSS_FTYPE_BEACON)
> + is_beacon = true;
You can roll this into a single line :-)
> + while (pos < ie + ielen + 2) {
> + tmp = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, pos,
> + ielen - (pos - ie));
> + if (!tmp)
> + break;
> +
> + mbssid_end_pos = tmp + tmp[1] + 2;
> + if (tmp[1] > 3) {
> + subelement = tmp + 3;
> + subie_len = subelement[1];
> + while (subelement < mbssid_end_pos) {
> + if (subelement[0] != 0) {
> + /* not a BSS profile */
> + subelement += subelement[1] + 2;
> + continue;
> + }
> +
> + /* found a bss profile */
> + mbssid_index_ie = cfg80211_find_ie(
> + WLAN_EID_MULTI_BSSID_IDX,
> + subelement + 2, subie_len);
> + if (!mbssid_index_ie) {
> + subelement += subelement[1] + 2;
> + continue;
> + }
> +
> + new_bssid = gen_new_bssid(bssid, tmp[2],
> + mbssid_index_ie[2],
> + gfp);
> + new_ie_len = calculate_new_ie_len(ie, ielen,
> + subelement +
> + 2,
> + subie_len);
> + new_ie = gen_new_ie(ie, ielen, subelement + 2,
> + subie_len, new_ie_len,
> + is_beacon, gfp);
> + if (!new_ie) {
> + subelement += subelement[1] + 2;
> + continue;
> + }
> +
> + memcpy((u8 *)&capability, subelement + 2, 2);
> + cfg80211_inform_single_bss_data(wiphy, data,
> + ftype,
> + new_bssid, tsf,
> + capability,
> + beacon_interval,
> + new_ie,
> + new_ie_len,
> + trans_bss,
> + gfp);
more allocation/refcounting issues here.
> + tmp = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen);
> + if (tmp)
> + mbssid_present = true;
> +
> + res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf,
> + capability, beacon_interval, ie,
> + ielen, NULL, gfp);
> + if (!mbssid_present)
> + return res;
> +
> + /* process each non-transmitting bss */
> + if (tmp[1] == 1)
> + return res;
> +
> + proc_mbssid_data(wiphy, data, ftype, bssid, tsf, capability,
> + beacon_interval, ie, ielen, res, gfp);
Having the == 1 check there seems really a bit strange - why not just
let the other thing iterate and do nothing?
There's also a bit of a problem here with the return value now, I
guess.
> spin_lock_bh(&rdev->bss_lock);
> if (!list_empty(&bss->list)) {
> + if (!list_empty(&bss->nontrans_list)) {
> + list_for_each_entry_safe(nontrans_bss, tmp,
same as above
johannes
^ permalink raw reply
* [PATCH 3/3] rtlwifi: cleanup the code that check whether TX ring is available
From: pkshih @ 2017-11-13 9:39 UTC (permalink / raw)
To: Larry.Finger, kvalo; +Cc: linux-wireless, steventing, yhchuang
In-Reply-To: <20171113093935.20431-1-pkshih@realtek.com>
From: Ping-Ke Shih <pkshih@realtek.com>
Remove the duplicate checking of TX ring's available number, and remove
the variable to store available number that can be calculated by
read/write pointers.
Signed-off-by: Steven Ting <steventing@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtlwifi/pci.c | 8 ----
drivers/net/wireless/realtek/rtlwifi/pci.h | 1 -
.../net/wireless/realtek/rtlwifi/rtl8192ee/trx.c | 51 +++-------------------
3 files changed, 6 insertions(+), 54 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index ca3f4716c1b6..e544f4bcfae9 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -557,13 +557,6 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio)
else
entry = (u8 *)(&ring->desc[ring->idx]);
- if (rtlpriv->cfg->ops->get_available_desc &&
- rtlpriv->cfg->ops->get_available_desc(hw, prio) <= 1) {
- RT_TRACE(rtlpriv, (COMP_INTR | COMP_SEND), DBG_DMESG,
- "no available desc!\n");
- return;
- }
-
if (!rtlpriv->cfg->ops->is_tx_desc_closed(hw, prio, ring->idx))
return;
ring->idx = (ring->idx + 1) % ring->entries;
@@ -1251,7 +1244,6 @@ static int _rtl_pci_init_tx_ring(struct ieee80211_hw *hw,
rtlpci->tx_ring[prio].cur_tx_rp = 0;
rtlpci->tx_ring[prio].cur_tx_wp = 0;
- rtlpci->tx_ring[prio].avl_desc = entries;
}
/* alloc dma for this ring */
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.h b/drivers/net/wireless/realtek/rtlwifi/pci.h
index ed5de1218bd9..3fb56c845a61 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.h
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.h
@@ -173,7 +173,6 @@ struct rtl8192_tx_ring {
/*add for new trx flow*/
struct rtl_tx_buffer_desc *buffer_desc; /*tx buffer descriptor*/
dma_addr_t buffer_desc_dma; /*tx bufferd desc dma memory*/
- u16 avl_desc; /* available_desc_to_write */
u16 cur_tx_wp; /* current_tx_write_point */
u16 cur_tx_rp; /* current_tx_read_point */
};
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c
index bc44d9d912d8..4f7444331b07 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c
@@ -549,7 +549,6 @@ static u16 get_desc_addr_fr_q_idx(u16 queue_index)
u16 rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 q_idx)
{
- struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_priv *rtlpriv = rtl_priv(hw);
u16 point_diff = 0;
u16 current_tx_read_point = 0, current_tx_write_point = 0;
@@ -564,7 +563,6 @@ u16 rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 q_idx)
current_tx_write_point,
TX_DESC_NUM_92E);
- rtlpci->tx_ring[q_idx].avl_desc = point_diff;
return point_diff;
}
@@ -909,10 +907,6 @@ void rtl92ee_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
u8 desc_name, u8 *val)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- u16 cur_tx_rp = 0;
- u16 cur_tx_wp = 0;
- static bool over_run;
- u32 tmp = 0;
u8 q_idx = *val;
bool dma64 = rtlpriv->cfg->mod_params->dma64;
@@ -933,38 +927,12 @@ void rtl92ee_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
return;
}
+ /* make sure tx desc is available by caller */
ring->cur_tx_wp = ((ring->cur_tx_wp + 1) % max_tx_desc);
- if (over_run) {
- ring->cur_tx_wp = 0;
- over_run = false;
- }
- if (ring->avl_desc > 1) {
- ring->avl_desc--;
-
- rtl_write_word(rtlpriv,
- get_desc_addr_fr_q_idx(q_idx),
- ring->cur_tx_wp);
- }
-
- if (ring->avl_desc < (max_tx_desc - 15)) {
- u16 point_diff = 0;
-
- tmp =
- rtl_read_dword(rtlpriv,
- get_desc_addr_fr_q_idx(q_idx));
- cur_tx_rp = (u16)((tmp >> 16) & 0x0fff);
- cur_tx_wp = (u16)(tmp & 0x0fff);
-
- ring->cur_tx_wp = cur_tx_wp;
- ring->cur_tx_rp = cur_tx_rp;
- point_diff = ((cur_tx_rp > cur_tx_wp) ?
- (cur_tx_rp - cur_tx_wp) :
- (TX_DESC_NUM_92E - 1 -
- cur_tx_wp + cur_tx_rp));
-
- ring->avl_desc = point_diff;
- }
+ rtl_write_word(rtlpriv,
+ get_desc_addr_fr_q_idx(q_idx),
+ ring->cur_tx_wp);
}
break;
}
@@ -1046,13 +1014,12 @@ bool rtl92ee_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue, u16 index)
{
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_priv *rtlpriv = rtl_priv(hw);
- u16 read_point, write_point, available_desc_num;
+ u16 read_point, write_point;
bool ret = false;
static u8 stop_report_cnt;
struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
{
- u16 point_diff = 0;
u16 cur_tx_rp, cur_tx_wp;
u32 tmpu32 = 0;
@@ -1062,18 +1029,12 @@ bool rtl92ee_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue, u16 index)
cur_tx_rp = (u16)((tmpu32 >> 16) & 0x0fff);
cur_tx_wp = (u16)(tmpu32 & 0x0fff);
- ring->cur_tx_wp = cur_tx_wp;
+ /* don't need to update ring->cur_tx_wp */
ring->cur_tx_rp = cur_tx_rp;
- point_diff = ((cur_tx_rp > cur_tx_wp) ?
- (cur_tx_rp - cur_tx_wp) :
- (TX_DESC_NUM_92E - cur_tx_wp + cur_tx_rp));
-
- ring->avl_desc = point_diff;
}
read_point = ring->cur_tx_rp;
write_point = ring->cur_tx_wp;
- available_desc_num = ring->avl_desc;
if (write_point > read_point) {
if (index < write_point && index >= read_point)
--
2.14.1
^ permalink raw reply related
* [PATCH 2/3] rtlwifi: fix the wrong size to calculate fifo space
From: pkshih @ 2017-11-13 9:39 UTC (permalink / raw)
To: Larry.Finger, kvalo; +Cc: linux-wireless, steventing, yhchuang
In-Reply-To: <20171113093935.20431-1-pkshih@realtek.com>
From: Ping-Ke Shih <pkshih@realtek.com>
Give correct fifo size to calculate fifo space. Fortunately, the values of
RTL_PCI_MAX_RX_COUNT and TX_DESC_NUM_92E are the same in old code, so it
still works.
Signed-off-by: Steven Ting <steventing@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtlwifi/pci.h | 4 ++--
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c | 6 ++++--
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.h b/drivers/net/wireless/realtek/rtlwifi/pci.h
index e7d070e8da2d..ed5de1218bd9 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.h
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.h
@@ -320,10 +320,10 @@ static inline void pci_write32_async(struct rtl_priv *rtlpriv,
writel(val, (u8 __iomem *)rtlpriv->io.pci_mem_start + addr);
}
-static inline u16 calc_fifo_space(u16 rp, u16 wp)
+static inline u16 calc_fifo_space(u16 rp, u16 wp, u16 size)
{
if (rp <= wp)
- return RTL_PCI_MAX_RX_COUNT - 1 + rp - wp;
+ return size - 1 + rp - wp;
return rp - wp - 1;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c
index 12255682e890..bc44d9d912d8 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c
@@ -498,7 +498,8 @@ u16 rtl92ee_rx_desc_buff_remained_cnt(struct ieee80211_hw *hw, u8 queue_index)
if (!start_rx)
return 0;
- remind_cnt = calc_fifo_space(read_point, write_point);
+ remind_cnt = calc_fifo_space(read_point, write_point,
+ RTL_PCI_MAX_RX_COUNT);
if (remind_cnt == 0)
return 0;
@@ -560,7 +561,8 @@ u16 rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 q_idx)
current_tx_write_point = (u16)((tmp_4byte) & 0x0fff);
point_diff = calc_fifo_space(current_tx_read_point,
- current_tx_write_point);
+ current_tx_write_point,
+ TX_DESC_NUM_92E);
rtlpci->tx_ring[q_idx].avl_desc = point_diff;
return point_diff;
--
2.14.1
^ permalink raw reply related
* [PATCH 1/3] rtlwifi: Reduce IO in RX interrupt to boost throughput
From: pkshih @ 2017-11-13 9:39 UTC (permalink / raw)
To: Larry.Finger, kvalo; +Cc: linux-wireless, steventing, yhchuang
In-Reply-To: <20171113093935.20431-1-pkshih@realtek.com>
From: Ping-Ke Shih <pkshih@realtek.com>
Check remaining count of RX packets cost a lot of CPU time, so only update
when the counter decreases to zero. In old flow, the counter was updated
once a RX packet is received.
Signed-off-by: Steven Ting <steventing@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtlwifi/pci.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index c2575b0b9440..ca3f4716c1b6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -747,7 +747,7 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
u8 tmp_one;
bool unicast = false;
u8 hw_queue = 0;
- unsigned int rx_remained_cnt;
+ unsigned int rx_remained_cnt = 0;
struct rtl_stats stats = {
.signal = 0,
.rate = 0,
@@ -768,7 +768,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
struct sk_buff *new_skb;
if (rtlpriv->use_new_trx_flow) {
- rx_remained_cnt =
+ if (rx_remained_cnt == 0)
+ rx_remained_cnt =
rtlpriv->cfg->ops->rx_desc_buff_remained_cnt(hw,
hw_queue);
if (rx_remained_cnt == 0)
--
2.14.1
^ permalink raw reply related
* [PATCH 0/3] rtlwifi: Refactor TX/RX flow to improve throughput
From: pkshih @ 2017-11-13 9:39 UTC (permalink / raw)
To: Larry.Finger, kvalo; +Cc: linux-wireless, steventing, yhchuang
From: Ping-Ke Shih <pkshih@realtek.com>
This patchset is aim to reduce IO to improve TX/RX performance, and also I
cleanup the code to make it easier and simpler.
Ping-Ke Shih (3):
rtlwifi: Reduce IO in RX interrupt to boost throughput
rtlwifi: fix the wrong size to calculate fifo space
rtlwifi: cleanup the code that check whether TX ring is available
drivers/net/wireless/realtek/rtlwifi/pci.c | 13 ++---
drivers/net/wireless/realtek/rtlwifi/pci.h | 5 +-
.../net/wireless/realtek/rtlwifi/rtl8192ee/trx.c | 57 ++++------------------
3 files changed, 15 insertions(+), 60 deletions(-)
--
2.14.1
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox