* [RFC V3 10/11] brcmfmac: handle gscan events from firmware
From: Arend van Spriel @ 2016-12-12 11:59 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Arend van Spriel
In-Reply-To: <1481543997-24624-1-git-send-email-arend.vanspriel@broadcom.com>
Handle the gscan events similar to scheduled scan. Collect all SSIDs found
in batch scan and add them to BSS list in cfg80211.
Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Reviewed-by: Franky Lin <franky.lin@broadcom.com>
Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 200 ++++++++++++++++++---
.../broadcom/brcm80211/brcmfmac/cfg80211.h | 10 +-
.../wireless/broadcom/brcm80211/brcmfmac/fweh.h | 1 +
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 29 +++
4 files changed, 214 insertions(+), 26 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 2b86c72..61636ad 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -733,6 +733,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
{
struct brcmf_scan_params_le params_le;
struct cfg80211_scan_request *scan_request;
+ enum brcmf_internal_escan_requestor rid;
s32 err = 0;
brcmf_dbg(SCAN, "Enter\n");
@@ -763,7 +764,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
¶ms_le, sizeof(params_le));
if (err)
- brcmf_err("Scan abort failed\n");
+ brcmf_err("Scan abort failed\n");
}
brcmf_scan_config_mpc(ifp, 1);
@@ -772,11 +773,22 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
* e-scan can be initiated internally
* which takes precedence.
*/
- if (cfg->internal_escan) {
- brcmf_dbg(SCAN, "scheduled scan completed\n");
- cfg->internal_escan = false;
+ if (cfg->escan_rid) {
+ rid = cfg->escan_rid;
+ cfg->escan_rid = INT_ESCAN_REQ_NONE;
+ brcmf_dbg(SCAN, "internal scan completed (%d)\n", rid);
if (!aborted)
- cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
+ switch (rid) {
+ case INT_ESCAN_REQ_SCHED_SCAN:
+ cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
+ break;
+ case INT_ESCAN_REQ_GSCAN:
+ cfg80211_gscan_results(cfg_to_wiphy(cfg));
+ break;
+ default:
+ /* never happens */
+ break;
+ }
} else if (scan_request) {
struct cfg80211_scan_info info = {
.aborted = aborted,
@@ -1025,7 +1037,7 @@ static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
if (!ssid_le.SSID_len)
brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
else
- brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
+ brcmf_dbg(SCAN, "%d: scan for %.32s size=%d\n",
i, ssid_le.SSID, ssid_le.SSID_len);
memcpy(ptr, &ssid_le, sizeof(ssid_le));
ptr += sizeof(ssid_le);
@@ -3025,7 +3037,7 @@ void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
struct escan_info *escan = &cfg->escan_info;
set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
- if (cfg->internal_escan || cfg->scan_request) {
+ if (cfg->escan_rid || cfg->scan_request) {
escan->escan_state = WL_ESCAN_STATE_IDLE;
brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
}
@@ -3048,7 +3060,7 @@ static void brcmf_escan_timeout(unsigned long data)
struct brcmf_cfg80211_info *cfg =
(struct brcmf_cfg80211_info *)data;
- if (cfg->internal_escan || cfg->scan_request) {
+ if (cfg->escan_rid || cfg->scan_request) {
brcmf_err("timer expired\n");
schedule_work(&cfg->escan_timeout_work);
}
@@ -3131,7 +3143,7 @@ static void brcmf_escan_timeout(unsigned long data)
if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
goto exit;
- if (!cfg->internal_escan && !cfg->scan_request) {
+ if (!cfg->escan_rid && !cfg->scan_request) {
brcmf_dbg(SCAN, "result without cfg80211 request\n");
goto exit;
}
@@ -3177,7 +3189,7 @@ static void brcmf_escan_timeout(unsigned long data)
cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
goto exit;
- if (cfg->internal_escan || cfg->scan_request) {
+ if (cfg->escan_rid || cfg->scan_request) {
brcmf_inform_bss(cfg);
aborted = status != BRCMF_E_STATUS_SUCCESS;
brcmf_notify_escan_complete(cfg, ifp, aborted, false);
@@ -3225,7 +3237,7 @@ static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,
{
struct ieee80211_channel *chan;
enum nl80211_band band;
- int freq;
+ int freq, i;
if (channel <= CH_MAX_2G_CHANNEL)
band = NL80211_BAND_2GHZ;
@@ -3240,15 +3252,28 @@ static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,
if (!chan)
return -EINVAL;
- req->channels[req->n_channels++] = chan;
- memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len);
- req->ssids[req->n_ssids++].ssid_len = ssid_len;
+ for (i = 0; i < req->n_channels; i++) {
+ if (req->channels[i] == chan)
+ break;
+ }
+ if (i == req->n_channels)
+ req->channels[req->n_channels++] = chan;
+ for (i = 0; i < req->n_ssids; i++) {
+ if (req->ssids[i].ssid_len == ssid_len &&
+ !memcmp(req->ssids[i].ssid, ssid, ssid_len))
+ break;
+ }
+ if (i == req->n_ssids) {
+ memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len);
+ req->ssids[req->n_ssids++].ssid_len = ssid_len;
+ }
return 0;
}
static int brcmf_start_internal_escan(struct brcmf_if *ifp,
- struct cfg80211_scan_request *request)
+ struct cfg80211_scan_request *request,
+ enum brcmf_internal_escan_requestor rid)
{
struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
int err;
@@ -3265,7 +3290,7 @@ static int brcmf_start_internal_escan(struct brcmf_if *ifp,
clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
return err;
}
- cfg->internal_escan = true;
+ cfg->escan_rid = rid;
return 0;
}
@@ -3335,8 +3360,8 @@ static int brcmf_start_internal_escan(struct brcmf_if *ifp,
brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
goto out_err;
}
- request = brcmf_alloc_internal_escan_request(wiphy,
- result_count);
+
+ request = brcmf_alloc_internal_escan_request(wiphy, result_count);
if (!request) {
err = -ENOMEM;
goto out_err;
@@ -3348,23 +3373,22 @@ static int brcmf_start_internal_escan(struct brcmf_if *ifp,
for (i = 0; i < result_count; i++) {
netinfo = &netinfo_start[i];
if (!netinfo) {
- brcmf_err("Invalid netinfo ptr. index: %d\n",
- i);
+ brcmf_err("Invalid netinfo ptr. index: %d\n", i);
err = -EINVAL;
goto out_err;
}
brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n",
netinfo->SSID, netinfo->channel);
- err = brcmf_internal_escan_add_info(request,
- netinfo->SSID,
+ err = brcmf_internal_escan_add_info(request, netinfo->SSID,
netinfo->SSID_len,
netinfo->channel);
if (err)
goto out_err;
}
- err = brcmf_start_internal_escan(ifp, request);
+ err = brcmf_start_internal_escan(ifp, request,
+ INT_ESCAN_REQ_SCHED_SCAN);
if (!err)
goto free_req;
@@ -3409,7 +3433,7 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
brcmf_dbg(SCAN, "enter\n");
brcmf_pno_clean(ifp);
- if (cfg->internal_escan)
+ if (cfg->escan_rid)
brcmf_notify_escan_complete(cfg, ifp, true, true);
return 0;
}
@@ -5081,6 +5105,131 @@ static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
return ret;
}
+#define BRCMF_GSCAN_RESULT_BUFSIZE 1024
+
+struct brcmf_batch_info {
+ struct list_head list;
+ u8 chan;
+ u8 ssid_len;
+ u8 ssid[32];
+};
+
+static int brcmf_add_batch_info(u8 ssid_len, u8 *ssid, u8 chan,
+ struct list_head *l)
+{
+ struct brcmf_batch_info *entry;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+ entry->chan = chan;
+ entry->ssid_len = ssid_len;
+ memcpy(entry->ssid, ssid, ssid_len);
+ list_add(&entry->list, l);
+
+ return 0;
+}
+
+static s32 brcmf_notify_best_batching(struct brcmf_if *ifp,
+ const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct wiphy *wiphy = ifp->drvr->config->wiphy;
+ struct cfg80211_scan_request *request = NULL;
+ struct wl_pfn_lscanresults_v2 *pfn_bestnet;
+ struct wl_pfn_subnet_info_v2 *subnet;
+ struct list_head snlist;
+ struct brcmf_batch_info *batch, *tmp;
+ u32 ts, last_ts, n_subnet;
+ int err, cycle, i, scan_nr;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ INIT_LIST_HEAD(&snlist);
+ n_subnet = 0;
+
+ pfn_bestnet = kmalloc(1024, GFP_KERNEL);
+ if (!pfn_bestnet) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ cycle = 0;
+ pfn_bestnet->status = 0;
+ while (!pfn_bestnet->status) {
+ memset(pfn_bestnet, 0, 1024);
+ err = brcmf_fil_iovar_data_get(ifp, "pfnlbest",
+ pfn_bestnet, 1024);
+ if (err < 0)
+ goto out_err;
+
+ brcmf_dbg(SCAN, "cycle[%d]: status=%u count=%u\n", cycle++,
+ le16_to_cpu(pfn_bestnet->status),
+ le16_to_cpu(pfn_bestnet->count));
+ for (i = 0; i < MAX_CHBKT_PER_RESULT; i++)
+ if (pfn_bestnet->scan_ch_buckets[i]) {
+ brcmf_dbg(SCAN, " buckets[%d]: %08x\n", i,
+ le32_to_cpu(pfn_bestnet->scan_ch_buckets[i]));
+ }
+ scan_nr = 0;
+ brcmf_dbg(SCAN, " #%d\n", scan_nr);
+ for (i = 0; i < le16_to_cpu(pfn_bestnet->count); i++) {
+ ts = le32_to_cpu(pfn_bestnet->netinfo[i].timestamp);
+ if (i && abs(ts - last_ts) > 3000)
+ brcmf_dbg(SCAN, " #%d\n", ++scan_nr);
+ subnet = &pfn_bestnet->netinfo[i].pfnsubnet;
+ brcmf_dbg(SCAN, " %pM: ch=%u ssid=%s (%u) rssi=%d ts=%u rtt=%u rtt-sd=%u\n",
+ subnet->BSSID, subnet->channel,
+ subnet->u.SSID, subnet->SSID_len,
+ (s16)le16_to_cpu(pfn_bestnet->netinfo[i].RSSI),
+ ts, le16_to_cpu(pfn_bestnet->netinfo[i].rtt0),
+ le16_to_cpu(pfn_bestnet->netinfo[i].rtt1));
+
+ last_ts = ts;
+ err = brcmf_add_batch_info(subnet->SSID_len,
+ subnet->u.SSID,
+ subnet->channel, &snlist);
+ if (err < 0)
+ goto out_err;
+ n_subnet++;
+ }
+ }
+ if (!n_subnet)
+ goto done;
+
+ request = brcmf_alloc_internal_escan_request(wiphy, n_subnet);
+ if (!request) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ list_for_each_entry_safe(batch, tmp, &snlist, list) {
+ err = brcmf_internal_escan_add_info(request,
+ batch->ssid,
+ batch->ssid_len,
+ batch->chan);
+ list_del(&batch->list);
+ kfree(batch);
+ if (err < 0)
+ goto out_err;
+ }
+
+ err = brcmf_start_internal_escan(ifp, request, INT_ESCAN_REQ_GSCAN);
+ if (!err)
+ goto free_req;
+
+out_err:
+ cfg80211_gscan_stopped(wiphy);
+ list_for_each_entry_safe(batch, tmp, &snlist, list) {
+ list_del(&batch->list);
+ kfree(batch);
+ }
+free_req:
+ kfree(request);
+done:
+ kfree(pfn_bestnet);
+ return err;
+}
+
static int brcmf_cfg80211_start_gscan(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_gscan_request *req)
@@ -5674,6 +5823,9 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
brcmf_p2p_notify_action_tx_complete);
brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
brcmf_p2p_notify_action_tx_complete);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_BEST_BATCHING,
+ brcmf_notify_best_batching);
+
}
static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 0c9a708..ff65970 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -253,6 +253,12 @@ struct brcmf_cfg80211_wowl {
bool nd_enabled;
};
+enum brcmf_internal_escan_requestor {
+ INT_ESCAN_REQ_NONE,
+ INT_ESCAN_REQ_SCHED_SCAN,
+ INT_ESCAN_REQ_GSCAN,
+};
+
/**
* struct brcmf_cfg80211_info - dongle private data of cfg80211 interface
*
@@ -271,7 +277,7 @@ struct brcmf_cfg80211_wowl {
* @pub: common driver information.
* @channel: current channel.
* @active_scan: current scan mode.
- * @internal_escan: indicates internally initiated e-scan is running.
+ * @escan_rid: indicates current requestor of internally initiated e-scan.
* @ibss_starter: indicates this sta is ibss starter.
* @pwr_save: indicate whether dongle to support power save mode.
* @dongle_up: indicate whether dongle up or not.
@@ -303,7 +309,7 @@ struct brcmf_cfg80211_info {
struct brcmf_pub *pub;
u32 channel;
bool active_scan;
- bool internal_escan;
+ enum brcmf_internal_escan_requestor escan_rid;
bool ibss_starter;
bool pwr_save;
bool dongle_up;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
index 5fba4b4..7315736 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
@@ -84,6 +84,7 @@
BRCMF_ENUM_DEF(IF, 54) \
BRCMF_ENUM_DEF(P2P_DISC_LISTEN_COMPLETE, 55) \
BRCMF_ENUM_DEF(RSSI, 56) \
+ BRCMF_ENUM_DEF(PFN_BEST_BATCHING, 57) \
BRCMF_ENUM_DEF(EXTLOG_MSG, 58) \
BRCMF_ENUM_DEF(ACTION_FRAME, 59) \
BRCMF_ENUM_DEF(ACTION_FRAME_COMPLETE, 60) \
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index 262642d..a1675c0 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -898,4 +898,33 @@ struct brcmf_gscan_config {
struct brcmf_gscan_bucket_config bucket[1];
};
+struct wl_pfn_subnet_info_v2 {
+ u8 BSSID[ETH_ALEN];
+ u8 channel; /**< channel number only */
+ u8 SSID_len;
+ union {
+ u8 SSID[32];
+ __le16 index;
+ } u;
+};
+
+struct wl_pfn_lnet_info_v2 {
+ struct wl_pfn_subnet_info_v2 pfnsubnet; /**< BSSID + channel + SSID len + SSID */
+ __le16 flags; /**< partial scan, etc */
+ __le16 RSSI; /**< receive signal strength (in dBm) */
+ __le32 timestamp; /**< age in miliseconds */
+ __le16 rtt0; /**< estimated distance to this AP in centimeters */
+ __le16 rtt1; /**< standard deviation of the distance to this AP in centimeters */
+};
+
+#define MAX_CHBKT_PER_RESULT 4
+
+struct wl_pfn_lscanresults_v2 {
+ __le32 version;
+ __le16 status;
+ __le16 count;
+ __le32 scan_ch_buckets[MAX_CHBKT_PER_RESULT];
+ struct wl_pfn_lnet_info_v2 netinfo[1];
+};
+
#endif /* FWIL_TYPES_H_ */
--
1.9.1
^ permalink raw reply related
* [RFC V3 06/11] brcmfmac: fix uninitialized field in scheduled scan ssid configuration
From: Arend van Spriel @ 2016-12-12 11:59 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Arend van Spriel
In-Reply-To: <1481543997-24624-1-git-send-email-arend.vanspriel@broadcom.com>
The scheduled scan ssid configuration in firmware has a flags field that
was not initialized resulting in unexpected behaviour.
Fixes: e3bdb7cc0300 ("brcmfmac: fix handling ssids in .sched_scan_start() callback")
Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Reviewed-by: Franky Lin <franky.lin@broadcom.com>
Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
index f273cab..9a25e79 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -137,6 +137,7 @@ static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid,
pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
pfn.wsec = cpu_to_le32(0);
pfn.infra = cpu_to_le32(1);
+ pfn.flags = 0;
if (active)
pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
pfn.ssid.SSID_len = cpu_to_le32(ssid->ssid_len);
--
1.9.1
^ permalink raw reply related
* [RFC V3 09/11] brcmfmac: implement gscan functionality
From: Arend van Spriel @ 2016-12-12 11:59 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Arend van Spriel
In-Reply-To: <1481543997-24624-1-git-send-email-arend.vanspriel@broadcom.com>
This patch implements configuration of gscan in the device. Handling
the results is done in subsequent change. This initial implementation
does not support running scheduled scan and gscan simultaneously.
Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Reviewed-by: Franky Lin <franky.lin@broadcom.com>
Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 31 ++++
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 18 +-
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 184 ++++++++++++++++++++-
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 9 +
4 files changed, 233 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 374b72c..2b86c72 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -5081,6 +5081,35 @@ static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
return ret;
}
+static int brcmf_cfg80211_start_gscan(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct cfg80211_gscan_request *req)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+
+ brcmf_dbg(SCAN, "Enter: n_buckets=%d\n", req->n_buckets);
+
+ if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
+ brcmf_err("Scanning suppressed: status (%lu)\n",
+ cfg->scan_status);
+ return -EAGAIN;
+ }
+
+ /* configure gscan */
+ return brcmf_pno_start_gscan(ifp, req);
+}
+
+static int brcmf_cfg80211_stop_gscan(struct wiphy *wiphy,
+ struct net_device *ndev)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
+
+ return brcmf_pno_clean(ifp);
+}
+
#ifdef CONFIG_PM
static int
brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
@@ -5148,6 +5177,8 @@ static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
.crit_proto_start = brcmf_cfg80211_crit_proto_start,
.crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
.tdls_oper = brcmf_cfg80211_tdls_oper,
+ .start_gscan = brcmf_cfg80211_start_gscan,
+ .stop_gscan = brcmf_cfg80211_stop_gscan,
};
struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index 8c18fad..262642d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -855,16 +855,20 @@ struct brcmf_gscan_bucket_config {
};
/* version supported which must match firmware */
-#define BRCMF_GSCAN_CFG_VERSION 1
+#define BRCMF_GSCAN_CFG_VERSION 2
/**
* enum brcmf_gscan_cfg_flags - bit values for gscan flags.
*
* @BRCMF_GSCAN_CFG_FLAGS_ALL_RESULTS: send probe responses/beacons to host.
+ * @BRCMF_GSCAN_CFG_ALL_BUCKETS_IN_1ST_SCAN: all buckets will be included in
+ * first scan cycle.
* @BRCMF_GSCAN_CFG_FLAGS_CHANGE_ONLY: indicated only flags member is changed.
+ *
*/
enum brcmf_gscan_cfg_flags {
BRCMF_GSCAN_CFG_FLAGS_ALL_RESULTS = BIT(0),
+ BRCMF_GSCAN_CFG_ALL_BUCKETS_IN_1ST_SCAN = BIT(3),
BRCMF_GSCAN_CFG_FLAGS_CHANGE_ONLY = BIT(7),
};
@@ -884,12 +888,12 @@ enum brcmf_gscan_cfg_flags {
*/
struct brcmf_gscan_config {
__le16 version;
- u8 flags;
- u8 buffer_threshold;
- u8 swc_nbssid_threshold;
- u8 swc_rssi_window_size;
- u8 count_of_channel_buckets;
- u8 retry_threshold;
+ u8 flags;
+ u8 buffer_threshold;
+ u8 swc_nbssid_threshold;
+ u8 swc_rssi_window_size;
+ u8 count_of_channel_buckets;
+ u8 retry_threshold;
__le16 lost_ap_window;
struct brcmf_gscan_bucket_config bucket[1];
};
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
index 9a25e79..b868997 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -35,6 +35,9 @@
#define BRCMF_PNO_HIDDEN_BIT 2
#define BRCMF_PNO_SCHED_SCAN_PERIOD 30
+#define GSCAN_BATCH_NO_THR_SET 101
+#define GSCAN_RETRY_THRESHOLD 3
+
static int brcmf_pno_channel_config(struct brcmf_if *ifp,
struct brcmf_pno_config_le *cfg)
{
@@ -182,7 +185,6 @@ int brcmf_pno_clean(struct brcmf_if *ifp)
int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
struct cfg80211_sched_scan_request *req)
{
- struct brcmu_d11inf *d11inf;
struct brcmf_pno_config_le pno_cfg;
struct cfg80211_ssid *ssid;
u16 chan;
@@ -209,7 +211,6 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
}
/* configure channels to use */
- d11inf = &ifp->drvr->config->d11inf;
for (i = 0; i < req->n_channels; i++) {
chan = req->channels[i]->hw_value;
pno_cfg.channel_list[i] = cpu_to_le16(chan);
@@ -241,3 +242,182 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
return ret;
}
+static int brcmf_pno_get_bucket_channels(struct brcmf_if *ifp,
+ struct cfg80211_gscan_bucket *b,
+ struct brcmf_pno_config_le *pno_cfg)
+{
+ struct wiphy *wiphy;
+ struct ieee80211_supported_band *band;
+ u32 n_chan = le32_to_cpu(pno_cfg->channel_num);
+ u16 chan;
+ int i, err = 0;
+
+ wiphy = ifp->drvr->config->wiphy;
+
+ for (i = 0; i < b->n_channels; i++) {
+ if (n_chan >= BRCMF_NUMCHANNELS) {
+ err = -ENOSPC;
+ goto done;
+ }
+ chan = b->channels[i].ch->hw_value;
+ brcmf_dbg(INFO, "[%d] Chan : %u\n",
+ n_chan, chan);
+ pno_cfg->channel_list[n_chan++] = cpu_to_le16(chan);
+ }
+ if (b->band & NL80211_BUCKET_BAND_2GHZ) {
+ band = wiphy->bands[NL80211_BAND_2GHZ];
+ for (i = 0; i < band->n_channels; i++) {
+ if (band->channels[i].flags & IEEE80211_CHAN_DISABLED)
+ continue;
+ if (n_chan >= BRCMF_NUMCHANNELS) {
+ err = -ENOSPC;
+ goto done;
+ }
+ chan = band->channels[i].hw_value;
+ brcmf_dbg(INFO, "[%d] Chan : %u\n",
+ n_chan, chan);
+ pno_cfg->channel_list[n_chan++] = cpu_to_le16(chan);
+ }
+ }
+ if (b->band & NL80211_BUCKET_BAND_5GHZ) {
+ band = wiphy->bands[NL80211_BAND_5GHZ];
+ for (i = 0; i < band->n_channels; i++) {
+ if (band->channels[i].flags & IEEE80211_CHAN_DISABLED)
+ continue;
+ if (band->channels[i].flags & IEEE80211_CHAN_RADAR) {
+ if (b->band & NL80211_BUCKET_BAND_NODFS)
+ continue;
+ } else {
+ if (b->band & NL80211_BUCKET_BAND_DFS_ONLY)
+ continue;
+ }
+ if (n_chan >= BRCMF_NUMCHANNELS) {
+ err = -ENOSPC;
+ goto done;
+ }
+ chan = band->channels[i].hw_value;
+ brcmf_dbg(INFO, "[%d] Chan : %u\n",
+ n_chan, chan);
+ pno_cfg->channel_list[n_chan++] = cpu_to_le16(chan);
+ }
+ }
+ /* return number of channels */
+ err = n_chan;
+done:
+ pno_cfg->channel_num = cpu_to_le32(n_chan);
+ return err;
+}
+
+static int brcmf_pno_prepare_gscan(struct brcmf_if *ifp,
+ struct cfg80211_gscan_request *req,
+ struct brcmf_pno_config_le *pno_cfg,
+ struct brcmf_gscan_bucket_config **buckets)
+{
+ struct brcmf_gscan_bucket_config *fw_buckets;
+ struct cfg80211_gscan_bucket *bucket;
+ int i, err, chidx;
+
+ *buckets = NULL;
+ fw_buckets = kcalloc(req->n_buckets, sizeof(*buckets[0]), GFP_KERNEL);
+ if (!fw_buckets)
+ return -ENOMEM;
+
+ memset(pno_cfg, 0, sizeof(*pno_cfg));
+ bucket = &req->buckets[0];
+ for (i = 0; i < req->n_buckets; i++) {
+ chidx = brcmf_pno_get_bucket_channels(ifp, bucket, pno_cfg);
+ if (chidx < 0) {
+ err = chidx;
+ goto fail;
+ }
+ fw_buckets[i].bucket_end_index = chidx - 1;
+ fw_buckets[i].bucket_freq_multiple = bucket->period / req->base_period;
+ fw_buckets[i].repeat = cpu_to_le16(bucket->step_count);
+ fw_buckets[i].max_freq_multiple = cpu_to_le16(bucket->max_period / req->base_period);
+ fw_buckets[i].flag = bucket->report_events ^ NL80211_BUCKET_REPORT_NO_BATCH;
+ bucket++;
+ }
+ *buckets = fw_buckets;
+ return 0;
+
+fail:
+ kfree(fw_buckets);
+ return err;
+}
+
+int brcmf_pno_start_gscan(struct brcmf_if *ifp,
+ struct cfg80211_gscan_request *req)
+{
+ struct brcmf_gscan_config *gscan_cfg;
+ struct brcmf_gscan_bucket_config *buckets;
+ struct brcmf_pno_config_le pno_cfg;
+ size_t gscan_cfg_size;
+ int err;
+
+ /* clean up everything */
+ err = brcmf_pno_clean(ifp);
+ if (err < 0) {
+ brcmf_err("failed error=%d\n", err);
+ return err;
+ }
+
+ /* configure pno */
+ err = brcmf_pno_config(ifp, req->base_period / 1000,
+ req->report_threshold_num_scans,
+ req->max_ap_per_scan);
+ if (err < 0)
+ return err;
+
+ /* configure random mac */
+ if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ err = brcmf_pno_set_random(ifp, req->mac, req->mac_mask);
+ if (err < 0)
+ return err;
+ }
+
+ err = brcmf_pno_prepare_gscan(ifp, req, &pno_cfg, &buckets);
+ if (err < 0)
+ return err;
+
+ gscan_cfg_size = sizeof(*gscan_cfg) +
+ (req->n_buckets - 1) * sizeof(*buckets);
+ gscan_cfg = kzalloc(gscan_cfg_size, GFP_KERNEL);
+ if (!gscan_cfg) {
+ err = -ENOMEM;
+ goto free_buckets;
+ }
+
+ err = brcmf_pno_channel_config(ifp, &pno_cfg);
+ if (err < 0)
+ goto free_gscan;
+
+ gscan_cfg->version = cpu_to_le16(BRCMF_GSCAN_CFG_VERSION);
+ gscan_cfg->retry_threshold = GSCAN_RETRY_THRESHOLD;
+
+ if (!req->report_threshold_percent)
+ gscan_cfg->buffer_threshold = GSCAN_BATCH_NO_THR_SET;
+ else
+ gscan_cfg->buffer_threshold = req->report_threshold_percent;
+
+ gscan_cfg->flags = BRCMF_GSCAN_CFG_ALL_BUCKETS_IN_1ST_SCAN;
+
+ gscan_cfg->count_of_channel_buckets = req->n_buckets;
+ memcpy(&gscan_cfg->bucket[0], buckets,
+ req->n_buckets * sizeof(*buckets));
+
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_gscan_cfg", gscan_cfg,
+ gscan_cfg_size);
+ if (err < 0)
+ goto free_gscan;
+
+ /* Enable the PNO */
+ err = brcmf_fil_iovar_int_set(ifp, "pfn", 1);
+ if (err < 0)
+ brcmf_err("PNO enable failed!! ret=%d\n", err);
+free_gscan:
+ kfree(gscan_cfg);
+free_buckets:
+ kfree(buckets);
+ return err;
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
index bae55b2..06ad3b0 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
@@ -37,4 +37,13 @@
int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
struct cfg80211_sched_scan_request *req);
+/**
+ * brcmf_pno_start_gscan - initiate gscan on device.
+ *
+ * @ifp: interface object used.
+ * @req: GScan request parameters.
+ */
+int brcmf_pno_start_gscan(struct brcmf_if *ifp,
+ struct cfg80211_gscan_request *req);
+
#endif /* _BRCMF_PNO_H */
--
1.9.1
^ permalink raw reply related
* [RFC V3 05/11] brcmfmac: fix memory leak in brcmf_cfg80211_attach()
From: Arend van Spriel @ 2016-12-12 11:59 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Arend van Spriel
In-Reply-To: <1481543997-24624-1-git-send-email-arend.vanspriel@broadcom.com>
In brcmf_cfg80211_attach() there was one error path not properly
handled as it leaked memory allocated in brcmf_btcoex_attach().
Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Reviewed-by: Franky Lin <franky.lin@broadcom.com>
Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index ccae3bb..7ffc4ab 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -6868,7 +6868,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
err = brcmf_p2p_attach(cfg, p2pdev_forced);
if (err) {
- brcmf_err("P2P initilisation failed (%d)\n", err);
+ brcmf_err("P2P initialisation failed (%d)\n", err);
goto wiphy_unreg_out;
}
err = brcmf_btcoex_attach(cfg);
@@ -6893,7 +6893,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
err = brcmf_fweh_activate_events(ifp);
if (err) {
brcmf_err("FWEH activation failed (%d)\n", err);
- goto wiphy_unreg_out;
+ goto detach;
}
/* Fill in some of the advertised nl80211 supported features */
@@ -6908,6 +6908,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
return cfg;
+detach:
+ brcmf_btcoex_detach(cfg);
+ brcmf_p2p_detach(&cfg->p2p);
wiphy_unreg_out:
wiphy_unregister(cfg->wiphy);
priv_out:
--
1.9.1
^ permalink raw reply related
* [RFC V3 07/11] brcmfmac: add firmware feature detection for gscan feature
From: Arend van Spriel @ 2016-12-12 11:59 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Arend van Spriel
In-Reply-To: <1481543997-24624-1-git-send-email-arend.vanspriel@broadcom.com>
From: Arend van Spriel <arend@broadcom.com>
Detect gscan support in firmware by doing pfn_gscan_cfg iovar with
invalid version.
Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Reviewed-by: Franky Lin <franky.lin@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
---
.../wireless/broadcom/brcm80211/brcmfmac/feature.c | 22 +++++++-
.../wireless/broadcom/brcm80211/brcmfmac/feature.h | 4 +-
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 59 ++++++++++++++++++++++
3 files changed, 83 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index 62985f2..8c7ef59 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -27,6 +27,7 @@
#include "feature.h"
#include "common.h"
+#define BRCMF_FW_UNSUPPORTED 23
/*
* expand feature list to array of feature strings.
@@ -113,6 +114,22 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
}
}
+static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
+ enum brcmf_feat_id id, char *name,
+ const void *data, size_t len)
+{
+ int err;
+
+ err = brcmf_fil_iovar_data_set(ifp, name, data, len);
+ if (err != -BRCMF_FW_UNSUPPORTED) {
+ brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
+ ifp->drvr->feat_flags |= BIT(id);
+ } else {
+ brcmf_dbg(TRACE, "%s feature check failed: %d\n",
+ brcmf_feat_names[id], err);
+ }
+}
+
static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
{
char caps[256];
@@ -136,11 +153,14 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
{
struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
struct brcmf_pno_macaddr_le pfn_mac;
+ struct brcmf_gscan_config gscan_cfg;
u32 wowl_cap;
s32 err;
brcmf_feat_firmware_capabilities(ifp);
-
+ memset(&gscan_cfg, 0, sizeof(gscan_cfg));
+ brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN, "pfn_gscan_cfg",
+ &gscan_cfg, sizeof(gscan_cfg));
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
if (drvr->bus_if->wowl_supported)
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
index db4733a..c1dbd17 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
@@ -31,6 +31,7 @@
* WOWL_GTK: (WOWL) GTK rekeying offload
* WOWL_ARP_ND: ARP and Neighbor Discovery offload support during WOWL.
* MFP: 802.11w Management Frame Protection.
+ * GSCAN: enhanced scan offload feature.
*/
#define BRCMF_FEAT_LIST \
BRCMF_FEAT_DEF(MBSS) \
@@ -44,7 +45,8 @@
BRCMF_FEAT_DEF(WOWL_ND) \
BRCMF_FEAT_DEF(WOWL_GTK) \
BRCMF_FEAT_DEF(WOWL_ARP_ND) \
- BRCMF_FEAT_DEF(MFP)
+ BRCMF_FEAT_DEF(MFP) \
+ BRCMF_FEAT_DEF(GSCAN)
/*
* Quirks:
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index 9a1eb5a..8c18fad 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -835,4 +835,63 @@ struct brcmf_gtk_keyinfo_le {
u8 replay_counter[BRCMF_RSN_REPLAY_LEN];
};
+/**
+ * struct brcmf_gscan_bucket_config - configuration data for channel bucket.
+ *
+ * @bucket_end_index: !unknown!
+ * @bucket_freq_multiple: !unknown!
+ * @flag: !unknown!
+ * @reserved: !unknown!
+ * @repeat: !unknown!
+ * @max_freq_multiple: !unknown!
+ */
+struct brcmf_gscan_bucket_config {
+ u8 bucket_end_index;
+ u8 bucket_freq_multiple;
+ u8 flag;
+ u8 reserved;
+ __le16 repeat;
+ __le16 max_freq_multiple;
+};
+
+/* version supported which must match firmware */
+#define BRCMF_GSCAN_CFG_VERSION 1
+
+/**
+ * enum brcmf_gscan_cfg_flags - bit values for gscan flags.
+ *
+ * @BRCMF_GSCAN_CFG_FLAGS_ALL_RESULTS: send probe responses/beacons to host.
+ * @BRCMF_GSCAN_CFG_FLAGS_CHANGE_ONLY: indicated only flags member is changed.
+ */
+enum brcmf_gscan_cfg_flags {
+ BRCMF_GSCAN_CFG_FLAGS_ALL_RESULTS = BIT(0),
+ BRCMF_GSCAN_CFG_FLAGS_CHANGE_ONLY = BIT(7),
+};
+
+/**
+ * struct brcmf_gscan_config - configuration data for gscan.
+ *
+ * @version: version of the api to match firmware.
+ * @flags: flags according %enum brcmf_gscan_cfg_flags.
+ * @buffer_threshold: percentage threshold of buffer to generate an event.
+ * @swc_nbssid_threshold: number of BSSIDs with significant change that
+ * will generate an event.
+ * @swc_rssi_window_size: size of rssi cache buffer (max=8).
+ * @count_of_channel_buckets: number of array members in @bucket.
+ * @retry_threshold: !unknown!
+ * @lost_ap_window: !unknown!
+ * @bucket: array of channel buckets.
+ */
+struct brcmf_gscan_config {
+ __le16 version;
+ u8 flags;
+ u8 buffer_threshold;
+ u8 swc_nbssid_threshold;
+ u8 swc_rssi_window_size;
+ u8 count_of_channel_buckets;
+ u8 retry_threshold;
+ __le16 lost_ap_window;
+ struct brcmf_gscan_bucket_config bucket[1];
+};
+
#endif /* FWIL_TYPES_H_ */
--
1.9.1
^ permalink raw reply related
* [RFC V3 08/11] brcmfmac: report gscan capabilities if firmware supports it
From: Arend van Spriel @ 2016-12-12 11:59 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Arend van Spriel
In-Reply-To: <1481543997-24624-1-git-send-email-arend.vanspriel@broadcom.com>
From: Arend van Spriel <arend@broadcom.com>
Fill the gscan capability information in the wiphy structure if
it is supported by firmware. The values are hardcoded as it can
not be obtained from firmware.
Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Reviewed-by: Franky Lin <franky.lin@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
---
.../wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 7ffc4ab..374b72c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -207,6 +207,16 @@ static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
};
+static const struct wiphy_gscan_caps brcmf_gscan_caps = {
+ .max_scan_cache_size = 320,
+ .max_scan_buckets = 8,
+ .max_ap_cache_per_scan = 32,
+ .max_rssi_sample_size = 8,
+ .max_scan_reporting_threshold = 100,
+ .max_hotlist_bssids = 64,
+ .max_significant_wifi_change_aps = 16,
+};
+
/* Note: brcmf_cipher_suites is an array of int defining which cipher suites
* are supported. A pointer to this array and the number of entries is passed
* on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
@@ -6426,7 +6436,12 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
wiphy->max_remain_on_channel_duration = 5000;
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
brcmf_wiphy_pno_params(wiphy);
-
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN)) {
+ wiphy->gscan = kmemdup(&brcmf_gscan_caps,
+ sizeof(brcmf_gscan_caps), GFP_KERNEL);
+ if (!wiphy->gscan)
+ return -ENOMEM;
+ }
/* vendor commands/events support */
wiphy->vendor_commands = brcmf_vendor_cmds;
wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
@@ -6735,6 +6750,7 @@ static void brcmf_free_wiphy(struct wiphy *wiphy)
if (!wiphy)
return;
+ kfree(wiphy->gscan);
if (wiphy->iface_combinations) {
for (i = 0; i < wiphy->n_iface_combinations; i++)
kfree(wiphy->iface_combinations[i].limits);
--
1.9.1
^ permalink raw reply related
* [RFC V3 11/11] brcmfmac: allow gscan to run concurrent with scheduled scan
From: Arend van Spriel @ 2016-12-12 11:59 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Arend van Spriel
In-Reply-To: <1481543997-24624-1-git-send-email-arend.vanspriel@broadcom.com>
On Android the gscan functionality is initiated by the wifi framework
whereas the scheduled scan can be initiated by wpa_supplicant. As such
it is required that these two scan types can run simultaneously.
Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
Reviewed-by: Franky Lin <franky.lin@broadcom.com>
Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 12 +-
.../broadcom/brcm80211/brcmfmac/cfg80211.h | 2 +
.../wireless/broadcom/brcm80211/brcmfmac/core.c | 5 +-
.../wireless/broadcom/brcm80211/brcmfmac/debug.h | 2 +
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 278 ++++++++++++++++-----
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 28 +++
6 files changed, 267 insertions(+), 60 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 61636ad..7749610 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -3432,7 +3432,7 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
struct brcmf_if *ifp = netdev_priv(ndev);
brcmf_dbg(SCAN, "enter\n");
- brcmf_pno_clean(ifp);
+ brcmf_pno_stop_sched_scan(ifp);
if (cfg->escan_rid)
brcmf_notify_escan_complete(cfg, ifp, true, true);
return 0;
@@ -5256,7 +5256,7 @@ static int brcmf_cfg80211_stop_gscan(struct wiphy *wiphy,
brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
- return brcmf_pno_clean(ifp);
+ return brcmf_pno_stop_gscan(ifp);
}
#ifdef CONFIG_PM
@@ -7076,6 +7076,13 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
brcmf_p2p_detach(&cfg->p2p);
goto wiphy_unreg_out;
}
+ err = brcmf_pno_attach(ifp);
+ if (err) {
+ brcmf_err("PNO initialisation failed (%d)\n", err);
+ brcmf_btcoex_detach(cfg);
+ brcmf_p2p_detach(&cfg->p2p);
+ goto wiphy_unreg_out;
+ }
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
@@ -7108,6 +7115,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
return cfg;
detach:
+ brcmf_pno_detach(ifp);
brcmf_btcoex_detach(cfg);
brcmf_p2p_detach(&cfg->p2p);
wiphy_unreg_out:
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index ff65970..b9f9375 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -293,6 +293,7 @@ enum brcmf_internal_escan_requestor {
* @vif_cnt: number of vif instances.
* @vif_event: vif event signalling.
* @wowl: wowl related information.
+ * @pi: information of pno module.
*/
struct brcmf_cfg80211_info {
struct wiphy *wiphy;
@@ -326,6 +327,7 @@ struct brcmf_cfg80211_info {
struct brcmu_d11inf d11inf;
struct brcmf_assoclist_le assoclist;
struct brcmf_cfg80211_wowl wowl;
+ struct brcmf_pno_info *pi;
};
/**
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index 9e6f60a..c72653d1 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -30,6 +30,7 @@
#include "debug.h"
#include "fwil_types.h"
#include "p2p.h"
+#include "pno.h"
#include "cfg80211.h"
#include "fwil.h"
#include "fwsignal.h"
@@ -1118,8 +1119,10 @@ void brcmf_detach(struct device *dev)
/* stop firmware event handling */
brcmf_fweh_detach(drvr);
- if (drvr->config)
+ if (drvr->config) {
+ brcmf_pno_detach(bus_if->drvr->iflist[0]);
brcmf_p2p_detach(&drvr->config->p2p);
+ }
brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
index 6687812..efc17b9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
@@ -82,6 +82,7 @@
#define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL)
#define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL)
#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL)
+#define BRCMF_SCAN_ON() (brcmf_msg_level & BRCMF_SCAN_VAL)
#else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
@@ -95,6 +96,7 @@
#define BRCMF_EVENT_ON() 0
#define BRCMF_FIL_ON() 0
#define BRCMF_FWCON_ON() 0
+#define BRCMF_SCAN_ON() 0
#endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
index b868997..c687f21 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -14,6 +14,7 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/netdevice.h>
+#include <linux/gcd.h>
#include <net/cfg80211.h>
#include "core.h"
@@ -38,6 +39,13 @@
#define GSCAN_BATCH_NO_THR_SET 101
#define GSCAN_RETRY_THRESHOLD 3
+struct brcmf_pno_info {
+ struct cfg80211_sched_scan_request *sched;
+ struct cfg80211_gscan_request *gscan;
+};
+
+#define ifp_to_pno(_ifp) (_ifp)->drvr->config->pi
+
static int brcmf_pno_channel_config(struct brcmf_if *ifp,
struct brcmf_pno_config_le *cfg)
{
@@ -182,14 +190,46 @@ int brcmf_pno_clean(struct brcmf_if *ifp)
return ret;
}
+static void brcmf_pno_config_ssids(struct brcmf_if *ifp,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct cfg80211_ssid *ssid;
+ int ret, i;
+
+ /* configure each match set */
+ for (i = 0; i < req->n_match_sets; i++) {
+
+ ssid = &req->match_sets[i].ssid;
+
+ if (!ssid->ssid_len) {
+ brcmf_err("skip broadcast ssid\n");
+ continue;
+ }
+
+ ret = brcmf_pno_add_ssid(ifp, ssid,
+ brcmf_is_ssid_active(ssid, req));
+ if (ret < 0)
+ brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
+ ret == 0 ? "set" : "failed", ssid->ssid);
+ }
+}
+
int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
struct cfg80211_sched_scan_request *req)
{
+ struct brcmf_pno_info *pi;
struct brcmf_pno_config_le pno_cfg;
- struct cfg80211_ssid *ssid;
u16 chan;
int i, ret;
+ pi = ifp_to_pno(ifp);
+
+ /* g-scan + scheduled scan is handled separately */
+ if (pi->gscan) {
+ pi->sched = req;
+ return brcmf_pno_start_gscan(ifp, pi->gscan);
+ }
+
/* clean up everything */
ret = brcmf_pno_clean(ifp);
if (ret < 0) {
@@ -217,31 +257,49 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
}
if (req->n_channels) {
pno_cfg.channel_num = cpu_to_le32(req->n_channels);
- brcmf_pno_channel_config(ifp, &pno_cfg);
+ ret = brcmf_pno_channel_config(ifp, &pno_cfg);
+ if (ret < 0)
+ return ret;
}
- /* configure each match set */
- for (i = 0; i < req->n_match_sets; i++) {
- ssid = &req->match_sets[i].ssid;
- if (!ssid->ssid_len) {
- brcmf_err("skip broadcast ssid\n");
- continue;
- }
+ brcmf_pno_config_ssids(ifp, req);
- ret = brcmf_pno_add_ssid(ifp, ssid,
- brcmf_is_ssid_active(ssid, req));
- if (ret < 0)
- brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
- ret == 0 ? "set" : "failed", ssid->ssid);
- }
/* Enable the PNO */
ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1);
- if (ret < 0)
+ if (ret < 0) {
brcmf_err("PNO enable failed!! ret=%d\n", ret);
+ return ret;
+ }
+ /* keep reference of request */
+ ifp_to_pno(ifp)->sched = req;
return ret;
}
+int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_info *cfg;
+ int ret;
+
+ cfg = ifp->drvr->config;
+ if (!cfg->pi)
+ return 0;
+
+ /* may need to reconfigure gscan */
+ cfg->pi->sched = NULL;
+ if (cfg->pi->gscan) {
+ brcmf_dbg(SCAN, "reconfigure gscan\n");
+ ret = brcmf_pno_start_gscan(ifp, cfg->pi->gscan);
+ if (ret < 0) {
+ brcmf_err("gscan reconfiguration failed: err=%d\n", ret);
+ cfg80211_gscan_stopped_rtnl(cfg->wiphy);
+ }
+ } else {
+ brcmf_pno_clean(ifp);
+ }
+ return 0;
+}
+
static int brcmf_pno_get_bucket_channels(struct brcmf_if *ifp,
struct cfg80211_gscan_bucket *b,
struct brcmf_pno_config_le *pno_cfg)
@@ -260,8 +318,7 @@ static int brcmf_pno_get_bucket_channels(struct brcmf_if *ifp,
goto done;
}
chan = b->channels[i].ch->hw_value;
- brcmf_dbg(INFO, "[%d] Chan : %u\n",
- n_chan, chan);
+ brcmf_dbg(INFO, "[%d] Chan : %u\n", n_chan, chan);
pno_cfg->channel_list[n_chan++] = cpu_to_le16(chan);
}
if (b->band & NL80211_BUCKET_BAND_2GHZ) {
@@ -274,8 +331,7 @@ static int brcmf_pno_get_bucket_channels(struct brcmf_if *ifp,
goto done;
}
chan = band->channels[i].hw_value;
- brcmf_dbg(INFO, "[%d] Chan : %u\n",
- n_chan, chan);
+ brcmf_dbg(INFO, "[%d] Chan : %u\n", n_chan, chan);
pno_cfg->channel_list[n_chan++] = cpu_to_le16(chan);
}
}
@@ -296,8 +352,7 @@ static int brcmf_pno_get_bucket_channels(struct brcmf_if *ifp,
goto done;
}
chan = band->channels[i].hw_value;
- brcmf_dbg(INFO, "[%d] Chan : %u\n",
- n_chan, chan);
+ brcmf_dbg(INFO, "[%d] Chan : %u\n", n_chan, chan);
pno_cfg->channel_list[n_chan++] = cpu_to_le16(chan);
}
}
@@ -311,14 +366,31 @@ static int brcmf_pno_get_bucket_channels(struct brcmf_if *ifp,
static int brcmf_pno_prepare_gscan(struct brcmf_if *ifp,
struct cfg80211_gscan_request *req,
struct brcmf_pno_config_le *pno_cfg,
- struct brcmf_gscan_bucket_config **buckets)
+ struct brcmf_gscan_bucket_config **buckets,
+ u32 *scan_freq)
{
+ struct cfg80211_sched_scan_request *sr;
struct brcmf_gscan_bucket_config *fw_buckets;
struct cfg80211_gscan_bucket *bucket;
- int i, err, chidx;
+ int i, err, chidx, n_fw_buckets;
+ u32 n_chan;
+ u16 chan;
+
+ sr = ifp_to_pno(ifp)->sched;
+ *scan_freq = req->base_period;
+ n_fw_buckets = req->n_buckets;
+ /*
+ * scheduled scan uses an additional bucket in firmware
+ * and the actual scan period must be the gcd.
+ */
+ if (sr) {
+ *scan_freq = gcd(sr->scan_plans[0].interval, *scan_freq);
+ n_fw_buckets++;
+ brcmf_dbg(SCAN, "g-scan+scheduled: period=%u\n", *scan_freq);
+ }
*buckets = NULL;
- fw_buckets = kcalloc(req->n_buckets, sizeof(*buckets[0]), GFP_KERNEL);
+ fw_buckets = kcalloc(n_fw_buckets, sizeof(*buckets[0]), GFP_KERNEL);
if (!fw_buckets)
return -ENOMEM;
@@ -331,14 +403,46 @@ static int brcmf_pno_prepare_gscan(struct brcmf_if *ifp,
goto fail;
}
fw_buckets[i].bucket_end_index = chidx - 1;
- fw_buckets[i].bucket_freq_multiple = bucket->period / req->base_period;
+ fw_buckets[i].bucket_freq_multiple =
+ bucket->period / *scan_freq;
fw_buckets[i].repeat = cpu_to_le16(bucket->step_count);
- fw_buckets[i].max_freq_multiple = cpu_to_le16(bucket->max_period / req->base_period);
- fw_buckets[i].flag = bucket->report_events ^ NL80211_BUCKET_REPORT_NO_BATCH;
+ fw_buckets[i].max_freq_multiple =
+ cpu_to_le16(bucket->max_period / *scan_freq);
+ fw_buckets[i].flag =
+ bucket->report_events ^ NL80211_BUCKET_REPORT_NO_BATCH;
bucket++;
}
+
+ /* additional scheduled scan bucket */
+ if (sr) {
+ fw_buckets[i].bucket_freq_multiple =
+ sr->scan_plans[0].interval / *scan_freq;
+ n_chan = le32_to_cpu(pno_cfg->channel_num);
+ for (chidx = 0; chidx < sr->n_channels; chidx++) {
+ if (n_chan >= BRCMF_NUMCHANNELS) {
+ err = -ENOSPC;
+ goto fail;
+ }
+ chan = sr->channels[chidx]->hw_value;
+ brcmf_dbg(INFO, "[%d] Chan : %u\n", n_chan, chan);
+ pno_cfg->channel_list[n_chan++] = cpu_to_le16(chan);
+ }
+ pno_cfg->channel_num = cpu_to_le32(n_chan);
+ fw_buckets[i].bucket_end_index = n_chan - 1;
+ }
+
+ if (BRCMF_SCAN_ON()) {
+ brcmf_err("base period=%u\n", *scan_freq);
+ for (i = 0; i < n_fw_buckets; i++) {
+ brcmf_err("[%d] period %u max %u repeat %u flag %x idx %u\n",
+ i, fw_buckets[i].bucket_freq_multiple,
+ le16_to_cpu(fw_buckets[i].max_freq_multiple),
+ fw_buckets[i].repeat, fw_buckets[i].flag,
+ fw_buckets[i].bucket_end_index);
+ }
+ }
*buckets = fw_buckets;
- return 0;
+ return n_fw_buckets;
fail:
kfree(fw_buckets);
@@ -348,11 +452,25 @@ static int brcmf_pno_prepare_gscan(struct brcmf_if *ifp,
int brcmf_pno_start_gscan(struct brcmf_if *ifp,
struct cfg80211_gscan_request *req)
{
+ struct brcmf_pno_info *pi;
struct brcmf_gscan_config *gscan_cfg;
struct brcmf_gscan_bucket_config *buckets;
struct brcmf_pno_config_le pno_cfg;
- size_t gscan_cfg_size;
- int err;
+ size_t gsz;
+ u32 scan_freq;
+ int err, n_buckets;
+
+ n_buckets = brcmf_pno_prepare_gscan(ifp, req, &pno_cfg, &buckets,
+ &scan_freq);
+ if (n_buckets < 0)
+ return n_buckets;
+
+ gsz = sizeof(*gscan_cfg) + (n_buckets - 1) * sizeof(*buckets);
+ gscan_cfg = kzalloc(gsz, GFP_KERNEL);
+ if (!gscan_cfg) {
+ err = -ENOMEM;
+ goto free_buckets;
+ }
/* clean up everything */
err = brcmf_pno_clean(ifp);
@@ -362,31 +480,11 @@ int brcmf_pno_start_gscan(struct brcmf_if *ifp,
}
/* configure pno */
- err = brcmf_pno_config(ifp, req->base_period / 1000,
- req->report_threshold_num_scans,
+ err = brcmf_pno_config(ifp, scan_freq, req->report_threshold_num_scans,
req->max_ap_per_scan);
if (err < 0)
return err;
- /* configure random mac */
- if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
- err = brcmf_pno_set_random(ifp, req->mac, req->mac_mask);
- if (err < 0)
- return err;
- }
-
- err = brcmf_pno_prepare_gscan(ifp, req, &pno_cfg, &buckets);
- if (err < 0)
- return err;
-
- gscan_cfg_size = sizeof(*gscan_cfg) +
- (req->n_buckets - 1) * sizeof(*buckets);
- gscan_cfg = kzalloc(gscan_cfg_size, GFP_KERNEL);
- if (!gscan_cfg) {
- err = -ENOMEM;
- goto free_buckets;
- }
-
err = brcmf_pno_channel_config(ifp, &pno_cfg);
if (err < 0)
goto free_gscan;
@@ -401,19 +499,35 @@ int brcmf_pno_start_gscan(struct brcmf_if *ifp,
gscan_cfg->flags = BRCMF_GSCAN_CFG_ALL_BUCKETS_IN_1ST_SCAN;
- gscan_cfg->count_of_channel_buckets = req->n_buckets;
+ gscan_cfg->count_of_channel_buckets = n_buckets;
memcpy(&gscan_cfg->bucket[0], buckets,
- req->n_buckets * sizeof(*buckets));
+ n_buckets * sizeof(*buckets));
- err = brcmf_fil_iovar_data_set(ifp, "pfn_gscan_cfg", gscan_cfg,
- gscan_cfg_size);
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_gscan_cfg", gscan_cfg, gsz);
if (err < 0)
goto free_gscan;
+ /* configure random mac */
+ if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ err = brcmf_pno_set_random(ifp, req->mac, req->mac_mask);
+ if (err < 0)
+ return err;
+ }
+
+ pi = ifp_to_pno(ifp);
+ if (pi->sched)
+ brcmf_pno_config_ssids(ifp, pi->sched);
+
/* Enable the PNO */
err = brcmf_fil_iovar_int_set(ifp, "pfn", 1);
- if (err < 0)
+ if (err < 0) {
brcmf_err("PNO enable failed!! ret=%d\n", err);
+ goto free_gscan;
+ }
+
+ /* keep reference of request */
+ pi->gscan = req;
+
free_gscan:
kfree(gscan_cfg);
free_buckets:
@@ -421,3 +535,53 @@ int brcmf_pno_start_gscan(struct brcmf_if *ifp,
return err;
}
+int brcmf_pno_stop_gscan(struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_info *cfg;
+ int ret;
+
+ cfg = ifp->drvr->config;
+ if (!cfg->pi)
+ return 0;
+
+ cfg->pi->gscan = NULL;
+ if (cfg->pi->sched) {
+ brcmf_dbg(SCAN, "reconfigure scheduled scan\n");
+ ret = brcmf_pno_start_sched_scan(ifp, cfg->pi->sched);
+ if (ret < 0) {
+ brcmf_err("scheduled scan reconfiguration failed: err=%d\n",
+ ret);
+ cfg80211_sched_scan_stopped_rtnl(cfg->wiphy);
+ }
+ } else {
+ brcmf_pno_clean(ifp);
+ }
+ return 0;
+}
+
+int brcmf_pno_attach(struct brcmf_if *ifp)
+{
+ struct brcmf_pno_info *pi;
+
+ brcmf_err("enter\n");
+ pi = kzalloc(sizeof(*pi), GFP_KERNEL);
+ if (!pi)
+ return -ENOMEM;
+
+ ifp_to_pno(ifp) = pi;
+ return 0;
+}
+
+void brcmf_pno_detach(struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_info *cfg;
+ struct brcmf_pno_info *pi;
+
+ brcmf_err("enter\n");
+ cfg = ifp->drvr->config;
+ pi = cfg->pi;
+ cfg->pi = NULL;
+
+ kfree(pi);
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
index 06ad3b0..5690ac2 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
@@ -38,6 +38,13 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
struct cfg80211_sched_scan_request *req);
/**
+ * brcmf_pno_stop_sched_scan - terminate scheduled scan on device.
+ *
+ * @ifp: interface object used.
+ */
+int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp);
+
+/**
* brcmf_pno_start_gscan - initiate gscan on device.
*
* @ifp: interface object used.
@@ -46,4 +53,25 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
int brcmf_pno_start_gscan(struct brcmf_if *ifp,
struct cfg80211_gscan_request *req);
+/**
+ * brcmf_pno_stop_gscan - terminate gscan on device.
+ *
+ * @ifp: interface object used.
+ */
+int brcmf_pno_stop_gscan(struct brcmf_if *ifp);
+
+/**
+ * brcmf_pno_attach - allocate and attach module information.
+ *
+ * @ifp: interface object used.
+ */
+int brcmf_pno_attach(struct brcmf_if *ifp);
+
+/**
+ * brcmf_pno_detach - detach and free module information.
+ *
+ * @ifp: interface object used.
+ */
+void brcmf_pno_detach(struct brcmf_if *ifp);
+
#endif /* _BRCMF_PNO_H */
--
1.9.1
^ permalink raw reply related
* Re: Could we have request_firmware_nowait with FW_OPT_NO_WARN?
From: Luis R. Rodriguez @ 2016-12-12 14:07 UTC (permalink / raw)
To: Rafał Miłecki
Cc: Arend Van Spriel, Johannes Berg, Ming Lei, Luis R. Rodriguez,
Linux Kernel Mailing List, linux-wireless@vger.kernel.org,
brcm80211 development
In-Reply-To: <CACna6rzF3=A_FT6QBiO8_uDE5-Ey-AD-Gfh0OU+Z1G_N_fKv-A@mail.gmail.com>
On Mon, Dec 12, 2016 at 10:53:38AM +0100, Rafał Miłecki wrote:
> On 12 December 2016 at 10:26, Arend Van Spriel
> <arend.vanspriel@broadcom.com> wrote:
> > On 12-12-2016 9:32, Rafał Miłecki wrote:
> >> On 12 December 2016 at 09:12, Johannes Berg <johannes@sipsolutions.net> wrote:
> >>> On Sat, 2016-12-10 at 16:54 +0100, Rafał Miłecki wrote:
> >>>> In brcmfmac we use request_firmware_nowait and if fetching firmware
> >>>> with NVRAM variables fails then we try to fallback to the platform
> >>>> one (see brcmf_fw_request_code_done & brcmf_fw_request_nvram_done).
> >>>>
> >>>> Some problem for us is that on devices with platform NVRAM we get
> >>>> this error:
> >>>> Direct firmware load for brcm/brcmfmac43602-pcie.txt failed with error -2
> >>>
> >>> This also happens with iwlwifi, because it requests multiple firmware
> >>> versions starting at the most recent supported one (which is often not
> >>> released at the same time).
> >>
> >> Good to know it may help others as well!
> >>
> >>
> >>> So yeah, this would be really useful - why don't you just make a patch
> >>> with some kind of flags, whether it's FW_OPT_* or new flags?
> >>
> >> OK! If noone will come with any special comments/ideas soon, I'll
> >> propose a patch for using some flags.
> >>
> >> FWIW, meanwhile I submitted
> >> [PATCH V2] firmware: simplify defining and handling FW_OPT_FALLBACK
> >> https://patchwork.kernel.org/patch/9469875/
> >
> > Similar thread couple of months ago [1]
> >
> > (...)
> >
> > [1] http://lists.infradead.org/pipermail/ath10k/2016-July/thread.html#8026
>
> Oh, now I see it's a bit messy topic and not clearly maintained class.
> It seems more ppl were confused by the API. I think having many
> unrelated behavior bounded to few functions caused some of this
> confusion. Let's hope adding some flags will let us use function the
> way they were designed, I'll definitely try working on this.
4.9 was just released, this means the merge window opened and no functional
changes will be merged for a while. I'll revamp my new API whcih would allow
what you describe to be an add-on flag without having to extend the API with
yet another series of exported symbols just for a new option. I'll also
CC you on a documentation revamp because as you note its much needed.
Luis
^ permalink raw reply
* Re: Could we have request_firmware_nowait with FW_OPT_NO_WARN?
From: Luis R. Rodriguez @ 2016-12-12 14:13 UTC (permalink / raw)
To: Rafał Miłecki
Cc: Ming Lei, Luis R. Rodriguez, Linux Kernel Mailing List,
linux-wireless@vger.kernel.org, brcm80211 development
In-Reply-To: <CACna6rxOGo0e9U7eXpUgnnBuxL+x1B0JBf9ZBq2WPbaBE=YZ-g@mail.gmail.com>
On Sat, Dec 10, 2016 at 04:54:41PM +0100, Rafał Miłecki wrote:
> So it would be nice to have version of request_firmware_nowait with
> FW_OPT_NO_WARN. If requesting firmware NVRAM fails *and* getting
> platform NVRAM fails, then I could to print error on my own.
> Does it make sense? Can you see a point of my request?
request_firmware_direct() does hat you describe but this is only
available for synchronous requests. My old sysdata patches -- which
I need to "rebrand" as the only issue found was the naming -- added
an equivalent to request_firmware_direct() for async calls. The newer
API simply has 2 API exported calls, the idea is we'd enable the caller
to configure the request as per their requirements instead of adding
a new exported routine per new feature.
I'll update docs, rebase my patches by rebranding them, and also add
a bit more as per some recent discussion to resolve the pivot_root()
races upon init if you do not use initramfs. I'll be sure to Cc you.
Luis
^ permalink raw reply
* Re: Could we have request_firmware_nowait with FW_OPT_NO_WARN?
From: Rafał Miłecki @ 2016-12-12 14:16 UTC (permalink / raw)
To: Luis R. Rodriguez
Cc: Arend Van Spriel, Johannes Berg, Ming Lei,
Linux Kernel Mailing List, linux-wireless@vger.kernel.org,
brcm80211 development
In-Reply-To: <20161212140739.GB1402@wotan.suse.de>
On 12 December 2016 at 15:07, Luis R. Rodriguez <mcgrof@kernel.org> wrote:
> On Mon, Dec 12, 2016 at 10:53:38AM +0100, Rafa=C5=82 Mi=C5=82ecki wrote:
>> On 12 December 2016 at 10:26, Arend Van Spriel
>> <arend.vanspriel@broadcom.com> wrote:
>> > On 12-12-2016 9:32, Rafa=C5=82 Mi=C5=82ecki wrote:
>> >> On 12 December 2016 at 09:12, Johannes Berg <johannes@sipsolutions.ne=
t> wrote:
>> >>> On Sat, 2016-12-10 at 16:54 +0100, Rafa=C5=82 Mi=C5=82ecki wrote:
>> >>>> In brcmfmac we use request_firmware_nowait and if fetching firmware
>> >>>> with NVRAM variables fails then we try to fallback to the platform
>> >>>> one (see brcmf_fw_request_code_done & brcmf_fw_request_nvram_done).
>> >>>>
>> >>>> Some problem for us is that on devices with platform NVRAM we get
>> >>>> this error:
>> >>>> Direct firmware load for brcm/brcmfmac43602-pcie.txt failed with er=
ror -2
>> >>>
>> >>> This also happens with iwlwifi, because it requests multiple firmwar=
e
>> >>> versions starting at the most recent supported one (which is often n=
ot
>> >>> released at the same time).
>> >>
>> >> Good to know it may help others as well!
>> >>
>> >>
>> >>> So yeah, this would be really useful - why don't you just make a pat=
ch
>> >>> with some kind of flags, whether it's FW_OPT_* or new flags?
>> >>
>> >> OK! If noone will come with any special comments/ideas soon, I'll
>> >> propose a patch for using some flags.
>> >>
>> >> FWIW, meanwhile I submitted
>> >> [PATCH V2] firmware: simplify defining and handling FW_OPT_FALLBACK
>> >> https://patchwork.kernel.org/patch/9469875/
>> >
>> > Similar thread couple of months ago [1]
>> >
>> > (...)
>> >
>> > [1] http://lists.infradead.org/pipermail/ath10k/2016-July/thread.html#=
8026
>>
>> Oh, now I see it's a bit messy topic and not clearly maintained class.
>> It seems more ppl were confused by the API. I think having many
>> unrelated behavior bounded to few functions caused some of this
>> confusion. Let's hope adding some flags will let us use function the
>> way they were designed, I'll definitely try working on this.
>
> 4.9 was just released, this means the merge window opened and no function=
al
> changes will be merged for a while. I'll revamp my new API whcih would al=
low
> what you describe to be an add-on flag without having to extend the API w=
ith
> yet another series of exported symbols just for a new option. I'll also
> CC you on a documentation revamp because as you note its much needed.
I started working on request_firmware_async with "unsigned int
opt_flags" argument and making request_firmware_nowait an inline
function just passing proper flags.
Well, I guess it's always disappointing having to drop your work, but
let it be... Please Cc me with your patches as well as documentation
update.
--=20
Rafa=C5=82
^ permalink raw reply
* Re: ATH9 driver issues on ARM64
From: Bjorn Helgaas @ 2016-12-12 16:31 UTC (permalink / raw)
To: Bharat Kumar Gogada
Cc: Tobias Klausmann, Kalle Valo, linux-kernel@vger.kernel.org,
linux-pci@vger.kernel.org, Marc Zyngier,
Janusz.Dziedzic@tieto.com, rmanohar@qti.qualcomm.com,
ath9k-devel@qca.qualcomm.com, linux-wireless@vger.kernel.org
In-Reply-To: <8520D5D51A55D047800579B094147198263A8EF9@XAP-PVEXMBX02.xlnx.xilinx.com>
On Sat, Dec 10, 2016 at 02:40:48PM +0000, Bharat Kumar Gogada wrote:
> Hi,
>
> After taking some more lecroy traces, we see that after 2nd ASSERT from EP on ARM64 we see continuous data movement of 32 dwords or 12 dwords and never sign of DEASSERT.
> Comparatively on working traces (x86) after 2nd assert there are only BAR register reads and writes and then DEASSERT, for almost most of the interrupts and we haven't seen 12 or 32 dwords data movement on this trace.
>
> I did not work on EP wifi/network drivers, any help why EP needs those many number of data at scan time ?
The device doesn't know whether it's in an x86 or an arm64 system. If
it works differently, it must be because the PCI core or the driver is
programming the device differently.
You should be able to match up Memory transactions from the host in
the trace with things the driver does. For example, if you see an
Assert_INTx message from the device, you should eventually see a
Memory Read from the host to get the ISR, i.e., some read done in the
bowels of ath9k_hw_getisr().
I don't know how the ath9k device works, but there must be some Memory
Read or Write done by the driver that tells the device "we've handled
this interrupt". The device should then send a Deassert_INTx; of
course, if the device still requires service, e.g., because it has
received more packets, it might leave the INTx asserted.
I doubt you'd see exactly the same traces on x86 and arm64 because
they aren't seeing the same network packets and the driver is
executing at different rates. But you should at least be able to
identify interrupt assertion and the actions of the driver's interrupt
service routine.
> > Hello there,
> >
> > as this is a thread about ath9k and ARM64, i'm not sure if i should answer here
> > or not, but i have similar "stalls" with ath9k on x86_64 (starting with 4.9rc), stack
> > trace is posted down below where the original ARM64 stall traces are.
> >
> > Greetings,
> >
> > Tobias
> >
> >
> > On 08.12.2016 18:36, Kalle Valo wrote:
> > > Bharat Kumar Gogada <bharat.kumar.gogada@xilinx.com> writes:
> > >
> > >> > [+cc Kalle, ath9k list]
> > > Thanks, but please also CC linux-wireless. Full thread below for the
> > > folks there.
> > >
> > >>> On Thu, Dec 08, 2016 at 01:49:42PM +0000, Bharat Kumar Gogada wrote:
> > >>>> Hi,
> > >>>>
> > >>>> Did anyone test Atheros ATH9
> > >>>> driver(drivers/net/wireless/ath/ath9k/)
> > >>>> on ARM64. The end point is TP link wifi card with which supports
> > >>>> only legacy interrupts.
> > >>> If it works on other arches and the arm64 PCI enumeration works, my
> > >>> first guess would be an INTx issue, e.g., maybe the driver is
> > >>> waiting for an interrupt that never arrives.
> > >> We are not sure for now.
> > >>>> We are trying to test it on ARM64 with
> > >>>> (drivers/pci/host/pcie-xilinx-nwl.c) as root port.
> > >>>>
> > >>>> EP is getting enumerated and able to link up.
> > >>>>
> > >>>> But when we start scan system gets hanged.
> > >>> When you say the system hangs when you start a scan, I assume you
> > >>> mean a wifi scan, not the PCI enumeration. A problem with a wifi
> > >>> scan might cause a *process* to hang, but it shouldn't hang the
> > >>> entire system.
> > >>>
> > >> Yes wifi scan.
> > >>>> When we took trace we see that after we start scan assert message
> > >>>> is sent but there is no de assert from end point.
> > >>> Are you talking about a trace from a PCIe analyzer? Do you see an
> > >>> Assert_INTx PCIe message on the link?
> > >>>
> > >> Yes lecroy trace, yes we do see Assert_INTx and Deassert_INTx happening
> > when we do interface link up.
> > >> When we have less debug prints in Atheros driver, and do wifi scan we
> > >> see Assert_INTx but never Deassert_INTx,
> > >>>> What might cause end point not sending de assert ?
> > >>> If the endpoint doesn't send a Deassert_INTx message, I expect that
> > >>> would mean the driver didn't service the interrupt and remove the
> > >>> condition that caused the device to assert the interrupt in the
> > >>> first place.
> > >>>
> > >>> If the driver didn't receive the interrupt, it couldn't service it,
> > >>> of course. You could add a printk in the ath9k interrupt service
> > >>> routine to see if you ever get there.
> > >>>
> > >> The interrupt behavior is changing w.r.t amount of debug prints we
> > >> add. (I kept many prints to aid debug) root@Xilinx-ZCU102-2016_3:~# iw dev
> > wlan0 scan
> > >> [ 83.064675] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.069486] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.074257] ath9k_hw_kill_interrupts 793
> > >> [ 83.078260] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.083107] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.087882] ath9k_hw_kill_interrupts 793
> > >> [ 83.095450] ath9k_hw_enable_interrupts 821
> > >> [ 83.099557] ath9k_hw_enable_interrupts 825
> > >> [ 83.103721] ath9k_hw_enable_interrupts 832
> > >> [ 83.107887] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.112748] AR_SREV_9100 0
> > >> [ 83.115438] ath9k_hw_enable_interrupts 848
> > >> [ 83.119607] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.124389] ath9k_hw_intrpend 762
> > >> [ 83.127761] (AR_SREV_9340(ah) val 0
> > >> [ 83.131234] ath9k_hw_intrpend 767
> > >> [ 83.134628] ath_isr 603
> > >> [ 83.137134] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.141995] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.146771] ath9k_hw_kill_interrupts 793
> > >> [ 83.150864] ath9k_hw_enable_interrupts 821
> > >> [ 83.154971] ath9k_hw_enable_interrupts 825
> > >> [ 83.159135] ath9k_hw_enable_interrupts 832
> > >> [ 83.163300] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.168161] AR_SREV_9100 0
> > >> [ 83.170852] ath9k_hw_enable_interrupts 848
> > >> [ 83.170855] ath9k_hw_intrpend 762
> > >> [ 83.178398] (AR_SREV_9340(ah) val 0
> > >> [ 83.181873] ath9k_hw_intrpend 767
> > >> [ 83.185265] ath_isr 603
> > >> [ 83.187773] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.192635] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.197411] ath9k_hw_kill_interrupts 793
> > >> [ 83.201414] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.206258] ath9k_hw_enable_interrupts 821
> > >> [ 83.210368] ath9k_hw_enable_interrupts 825
> > >> [ 83.214531] ath9k_hw_enable_interrupts 832
> > >> [ 83.218698] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.223558] AR_SREV_9100 0
> > >> [ 83.226243] ath9k_hw_enable_interrupts 848
> > >> [ 83.226246] ath9k_hw_intrpend 762
> > >> [ 83.233794] (AR_SREV_9340(ah) val 0
> > >> [ 83.237268] ath9k_hw_intrpend 767
> > >> [ 83.240661] ath_isr 603
> > >> [ 83.243169] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.248030] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.252806] ath9k_hw_kill_interrupts 793
> > >> [ 83.256811] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.261651] ath9k_hw_enable_interrupts 821
> > >> [ 83.265753] ath9k_hw_enable_interrupts 825
> > >> [ 83.269919] ath9k_hw_enable_interrupts 832
> > >> [ 83.274083] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.278945] AR_SREV_9100 0
> > >> [ 83.281630] ath9k_hw_enable_interrupts 848
> > >> [ 83.281633] ath9k_hw_intrpend 762
> > >> [ 83.281634] (AR_SREV_9340(ah) val 0
> > >> [ 83.281637] ath9k_hw_intrpend 767
> > >> [ 83.281648] ath_isr 603
> > >> [ 83.281649] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.281651] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.281654] ath9k_hw_kill_interrupts 793
> > >> [ 83.312192] ath9k: ath9k_ioread32 ffffff800a400024
> > >> [ 83.317030] ath9k_hw_enable_interrupts 821
> > >> [ 83.321132] ath9k_hw_enable_interrupts 825
> > >> [ 83.325297] ath9k_hw_enable_interrupts 832
> > >> [ 83.329463] ath9k: ath9k_iowrite32 ffffff800a400024
> > >> [ 83.334324] AR_SREV_9100 0
> > >> [ 83.337014] ath9k_hw_enable_interrupts 848
> > >> ..
> > >> ..
> > >> This log continues until I turn off board without obtaining scanning result.
> > >>
> > >> In between I get following cpu stall outputs :
> > >> 230.457179] INFO: rcu_sched self-detected stall on CPU
> > >> [ 230.457185] 2-...: (31314 ticks this GP)
> > idle=2d1/140000000000001/0 softirq=1400/1400 fqs=36713
> > >> [ 230.457189] (t=36756 jiffies g=161 c=160 q=16169)
> > >> [ 230.457191] Task dump for CPU 2:
> > >> [ 230.457196] kworker/u8:4 R running task 0 1342 2 0x00000002
> > >> [ 230.457207] Workqueue: phy0 ieee80211_scan_work [ 230.457208]
> > >> Call trace:
> > >> [ 230.457214] [<ffffff8008089860>] dump_backtrace+0x0/0x198 [
> > >> 230.457219] [<ffffff8008089a0c>] show_stack+0x14/0x20 [ 230.457224]
> > >> [<ffffff80080c0930>] sched_show_task+0x98/0xf8 [ 230.457228]
> > >> [<ffffff80080c2628>] dump_cpu_task+0x40/0x50 [ 230.457233]
> > >> [<ffffff80080e14a8>] rcu_dump_cpu_stacks+0xa0/0xf0 [ 230.457239]
> > >> [<ffffff80080e4cd8>] rcu_check_callbacks+0x468/0x748 [ 230.457243]
> > >> [<ffffff80080e7cfc>] update_process_times+0x3c/0x68 [ 230.457249]
> > >> [<ffffff80080f6dfc>] tick_sched_handle.isra.5+0x3c/0x50
> > >> [ 230.457253] [<ffffff80080f6e54>] tick_sched_timer+0x44/0x90 [
> > >> 230.457257] [<ffffff80080e86b0>] __hrtimer_run_queues+0xf0/0x178
> > >> ** 10 printk messages dropped ** [ 230.457302] f8c0:
> > >> 0000000000000000 0000000005f5e0ff 000000000001379a
> > 3866666666666620 [
> > >> 230.457306] f8e0: ffffff800a1b4065 0000000000000006 ffffff800a129000
> > >> ffffffc87b8010a8 [ 230.457310] f900: ffffff808a1b4057
> > >> ffffff800a1c3000 ffffff800a1b3000 ffffff800a13b000 [ 230.457314]
> > >> f920: 0000000000000140 0000000000000006 ffffff800a1b3b10
> > >> ffffff800a1c39e8 [ 230.457318] f940: 000000000000002f
> > >> ffffff800a1b8a98 ffffff800a1b3ae8 ffffffc87b07f990 [ 230.457322]
> > >> f960: ffffff80080d6230 ffffffc87b07f990 ffffff80080d6234
> > >> 0000000060000145
> > >> ** 1 printk messages dropped ** [ 230.457329] [<ffffff8008085720>]
> > >> el1_irq+0xa0/0x100
> > >> ** 9 printk messages dropped ** [ 230.457373] [<ffffff800885ad60>]
> > >> ieee80211_hw_config+0x50/0x290 [ 230.457377] [<ffffff8008863690>]
> > >> ieee80211_scan_work+0x1f8/0x480 [ 230.457383] [<ffffff80080b15d0>]
> > >> process_one_work+0x120/0x378 [ 230.457386] [<ffffff80080b1870>]
> > >> worker_thread+0x48/0x4b0 [ 230.457391] [<ffffff80080b7108>]
> > >> kthread+0xd0/0xe8 [ 230.457395] [<ffffff8008085dd0>]
> > ret_from_fork+0x10/0x40
> > >> [ 230.480389] ath9k_hw_intrpend 762
> > >>
> > >>
> > >> [ 545.487987] ath9k: ath9k_ioread32 ffffff800a400024 [ 545.526189]
> > >> INFO: rcu_sched self-detected stall on CPU
> > >> [ 545.526195] 2-...: (97636 ticks this GP)
> > idle=2d1/140000000000001/0 softirq=1400/1400 fqs=115374
> > >> [ 545.526199] (t=115523 jiffies g=161 c=160 q=51066)
> > >> [ 545.526201] Task dump for CPU 2:
> > >> [ 545.526206] kworker/u8:4 R running task 0 1342 2 0x00000002
> > >> ** 3 printk messages dropped ** [ 545.526231] [<ffffff8008089a0c>]
> > >> show_stack+0x14/0x20
> > >> ** 9 printk messages dropped ** [ 545.526280] [<ffffff80086a71e8>]
> > >> arch_timer_handler_phys+0x30/0x40 [ 545.526284] [<ffffff80080dbe18>]
> > >> handle_percpu_devid_irq+0x78/0xa0 [ 545.526291] [<ffffff80080d760c>]
> > >> generic_handle_irq+0x24/0x38 [ 545.526296] [<ffffff80080d7944>]
> > >> __handle_domain_irq+0x5c/0xb8 [ 545.526299] [<ffffff80080824bc>]
> > >> gic_handle_irq+0x64/0xc0 [ 545.526302] Exception stack(0xffffffc87b07f870
> > to 0xffffffc87b07f990)
> > >> [ 545.526306] f860: 0000000000009732 ffffff800a1eaaa8
> > >> ** 8 printk messages dropped ** [ 545.526341] f980: ffffff800a1c39e8
> > >> 0000000000000036 [ 545.526345] [<ffffff8008085720>]
> > >> el1_irq+0xa0/0x100 [ 545.526349] [<ffffff80080d6234>]
> > >> console_unlock+0x384/0x5b0 [ 545.526353] [<ffffff80080d673c>]
> > >> vprintk_emit+0x2dc/0x4b0 [ 545.526357] [<ffffff80080d6a50>]
> > >> vprintk_default+0x38/0x40 [ 545.526362] [<ffffff8008129704>]
> > >> printk+0x58/0x60 [ 545.526366] [<ffffff800859e3e4>]
> > >> ath9k_iowrite32+0x9c/0xa8 [ 545.526372] [<ffffff80085c7ca8>]
> > >> ath9k_hw_kill_interrupts+0x28/0xf0
> > >> [ 545.526376] [<ffffff80085a18ec>] ath_reset+0x24/0x68
> > >> ** 2 printk messages dropped ** [ 545.526391] [<ffffff800885ad60>]
> > ieee80211_hw_config+0x50/0x290
> > >> ** 11 printk messages dropped ** [ 545.532834] ath9k_hw_kill_interrupts
> > 793
> > >> [ 545.532890] ath9k_hw_enable_interrupts 821
> >
> > [ 81.876902] INFO: rcu_preempt detected stalls on CPUs/tasks:
> > [ 81.876912] Tasks blocked on level-0 rcu_node (CPUs 0-7): P0
> > [ 81.876932] (detected by 4, t=60002 jiffies, g=1873, c=1872, q=4967)
> > [ 81.876936] swapper/4 R running task 0 0 1
> > 0x00000000
> > [ 81.876941] 0000000000000001 ffffffff810725f6 ffff88017edbc240
> > ffffffff81a3dc40
> > [ 81.876945] ffffffff81101e46 ffff88025ef173c0 ffffffff81a3dc40
> > ffffffff81a3dc40
> > [ 81.876948] 00000000ffffffff ffffffff810a7333 ffff88017ecee698
> > ffff88017edbc240
> > [ 81.876951] Call Trace:
> > [ 81.876970] <IRQ>
> > [ 81.876979] [<ffffffff810725f6>] ? sched_show_task+0xd6/0x140
> > [ 81.876983] [<ffffffff81101e46>] ?
> > rcu_print_detail_task_stall_rnp+0x40/0x61
> > [ 81.876989] [<ffffffff810a7333>] ? rcu_check_callbacks+0x6b3/0x8c0
> > [ 81.876993] [<ffffffff810b8350>] ? tick_sched_handle.isra.14+0x40/0x40
> > [ 81.876996] [<ffffffff810aa4c3>] ? update_process_times+0x23/0x50
> > [ 81.876999] [<ffffffff810b8383>] ? tick_sched_timer+0x33/0x60
> > [ 81.877002] [<ffffffff810aaf09>] ? __hrtimer_run_queues+0xb9/0x150
> > [ 81.877004] [<ffffffff810ab198>] ? hrtimer_interrupt+0x98/0x1a0
> > [ 81.877008] [<ffffffff81031b1e>] ?
> > smp_trace_apic_timer_interrupt+0x5e/0x90
> > [ 81.877012] [<ffffffff815b31bf>] ? apic_timer_interrupt+0x7f/0x90
> > [ 81.877013] <EOI>
> > [ 81.877017] [<ffffffff8147f28d>] ? cpuidle_enter_state+0x13d/0x1f0
> > [ 81.877019] [<ffffffff8147f289>] ? cpuidle_enter_state+0x139/0x1f0
> > [ 81.877021] [<ffffffff81088c19>] ? cpu_startup_entry+0x139/0x210
> > [ 81.877027] [<ffffffff8102fc9e>] ? start_secondary+0x13e/0x170
> > [ 81.877029] swapper/4 R running task 0 0 1
> > 0x00000000
> > [ 81.877032] 0000000000000001 ffffffff810725f6 ffff88017edbc240
> > ffffffff81a3dc40
> > [ 81.877035] ffffffff81101e46 ffff88025ef173c0 ffffffff81a3dc40
> > ffffffff81a3dc40
> > [ 81.877038] 00000000ffffffff ffffffff810a7368 ffff88017ecee698
> > ffff88017edbc240
> > [ 81.877041] Call Trace:
> > [ 81.877045] <IRQ>
> > [ 81.877049] [<ffffffff810725f6>] ? sched_show_task+0xd6/0x140
> > [ 81.877051] [<ffffffff81101e46>] ?
> > rcu_print_detail_task_stall_rnp+0x40/0x61
> > [ 81.877055] [<ffffffff810a7368>] ? rcu_check_callbacks+0x6e8/0x8c0
> > [ 81.877058] [<ffffffff810b8350>] ? tick_sched_handle.isra.14+0x40/0x40
> > [ 81.877060] [<ffffffff810aa4c3>] ? update_process_times+0x23/0x50
> > [ 81.877063] [<ffffffff810b8383>] ? tick_sched_timer+0x33/0x60
> > [ 81.877065] [<ffffffff810aaf09>] ? __hrtimer_run_queues+0xb9/0x150
> > [ 81.877068] [<ffffffff810ab198>] ? hrtimer_interrupt+0x98/0x1a0
> > [ 81.877070] [<ffffffff81031b1e>] ?
> > smp_trace_apic_timer_interrupt+0x5e/0x90
> > [ 81.877073] [<ffffffff815b31bf>] ? apic_timer_interrupt+0x7f/0x90
> > [ 81.877074] <EOI>
> > [ 81.877076] [<ffffffff8147f28d>] ? cpuidle_enter_state+0x13d/0x1f0
> > [ 81.877078] [<ffffffff8147f289>] ? cpuidle_enter_state+0x139/0x1f0
> > [ 81.877080] [<ffffffff81088c19>] ? cpu_startup_entry+0x139/0x210
> > [ 81.877084] [<ffffffff8102fc9e>] ? start_secondary+0x13e/0x170
> > [ 91.132787] INFO: rcu_preempt detected expedited stalls on
> > CPUs/tasks: { P0 } 63785 jiffies s: 505 root: 0x0/T
> > [ 91.132796] blocking rcu_node structures:
> >
> > >>
> > >>
> > >> But if we have less debug prints it does not reach EP handler
> > >> sometimes, due to following Condition in "kernel/irq/chip.c" in
> > >> function handle_simple_irq
> > >>
> > >> if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
> > >> desc->istate |= IRQS_PENDING;
> > >> goto out_unlock;
> > >> }
> > >> Here irqd_irq_disabled is being set to 1.
> > >>
> > >> With lesser debug prints it stops after following prints:
> > >> root@Xilinx-ZCU102-2016_3:~# iw dev wlan0 scan
> > >> [ 54.781045] ath9k_hw_kill_interrupts 793
> > >> [ 54.785007] ath9k_hw_kill_interrupts 793
> > >> [ 54.792535] ath9k_hw_enable_interrupts 821
> > >> [ 54.796642] ath9k_hw_enable_interrupts 825
> > >> [ 54.800807] ath9k_hw_enable_interrupts 832
> > >> [ 54.804973] AR_SREV_9100 0
> > >> [ 54.807663] ath9k_hw_enable_interrupts 848
> > >> [ 54.811843] ath9k_hw_intrpend 762
> > >> [ 54.815211] (AR_SREV_9340(ah) val 0
> > >> [ 54.818684] ath9k_hw_intrpend 767
> > >> [ 54.822078] ath_isr 603
> > >> [ 54.824587] ath9k_hw_kill_interrupts 793
> > >> [ 54.828601] ath9k_hw_enable_interrupts 821
> > >> [ 54.832750] ath9k_hw_enable_interrupts 825
> > >> [ 54.836916] ath9k_hw_enable_interrupts 832
> > >> [ 54.841082] AR_SREV_9100 0
> > >> [ 54.843772] ath9k_hw_enable_interrupts 848
> > >> [ 54.843775] ath9k_hw_intrpend 762
> > >> [ 54.851319] (AR_SREV_9340(ah) val 0
> > >> [ 54.854793] ath9k_hw_intrpend 767
> > >> [ 54.858185] ath_isr 603
> > >> [ 54.860696] ath9k_hw_kill_interrupts 793
> > >> [ 54.864776] ath9k_hw_enable_interrupts 821
> > >> [ 54.867061] ath9k_hw_kill_interrupts 793
> > >> [ 54.872870] ath9k_hw_enable_interrupts 825
> > >> [ 54.877036] ath9k_hw_enable_interrupts 832
> > >> [ 54.881202] AR_SREV_9100 0
> > >> [ 54.883892] ath9k_hw_enable_interrupts 848
> > >> [ 75.963129] INFO: rcu_sched detected stalls on CPUs/tasks:
> > >> [ 75.968602] 0-...: (2 GPs behind) idle=9d5/140000000000001/0
> > softirq=1103/1109 fqs=519
> > >> [ 75.976675] (detected by 2, t=5274 jiffies, g=64, c=63, q=11)
> > >> [ 75.982485] Task dump for CPU 0:
> > >> [ 75.985696] ksoftirqd/0 R running task 0 3 2 0x00000002
> > >> [ 75.992726] Call trace:
> > >> [ 75.995165] [<ffffff8008086b3c>] __switch_to+0xc4/0xd0
> > >> [ 76.000281] [<ffffffc87b830500>] 0xffffffc87b830500
> > >> [ 139.059027] INFO: rcu_sched detected stalls on CPUs/tasks:
> > >> [ 139.064430] 0-...: (2 GPs behind) idle=9d5/140000000000001/0
> > softirq=1103/1109 fqs=2097
> > >> [ 139.072593] (detected by 2, t=21049 jiffies, g=64, c=63, q=11)
> > >> [ 139.078489] Task dump for CPU 0:
> > >> [ 139.081700] ksoftirqd/0 R running task 0 3 2 0x00000002
> > >> [ 139.088731] Call trace:
> > >> [ 139.091165] [<ffffff8008086b3c>] __switch_to+0xc4/0xd0 [
> > >> 139.096285] [<ffffffc87b830500>] 0xffffffc87b830500
> > >>
> > >>
> > >>>> We are not seeing any issues on 32-bit ARM platform and X86
> > >>>> platform.
> > >>> Can you collect a dmesg log (or, if the system hang means you can't
> > >>> collect that, a console log with "ignore_loglevel"), and "lspci -vv"
> > >>> output as root? That should have clues about whether the INTx got
> > >>> routed correctly. /proc/interrupts should also show whether we're
> > >>> receiving interrupts from the device.
> > >> Here is the lspci output:
> > >> 00:00.0 PCI bridge: Xilinx Corporation Device d022 (prog-if 00 [Normal
> > decode])
> > >> Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop-
> > ParErr- Stepping- SERR- FastB2B- DisINTx-
> > >> Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
> > <TAbort- <MAbort- >SERR- <PERR- INTx-
> > >> Latency: 0
> > >> Interrupt: pin A routed to IRQ 224
> > >> Bus: primary=00, secondary=01, subordinate=0c, sec-latency=0
> > >> I/O behind bridge: 00000000-00000fff
> > >> Memory behind bridge: e0000000-e00fffff
> > >> Prefetchable memory behind bridge: 00000000fff00000-
> > 00000000000fffff
> > >> Secondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort-
> > <TAbort- <MAbort- <SERR- <PERR-
> > >> BridgeCtl: Parity- SERR- NoISA- VGA- MAbort- >Reset- FastB2B-
> > >> PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
> > >> Capabilities: [40] Power Management version 3
> > >> Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA
> > PME(D0+,D1+,D2+,D3hot+,D3cold-)
> > >> Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
> > >> Capabilities: [60] Express (v2) Root Port (Slot-), MSI 00
> > >> DevCap: MaxPayload 256 bytes, PhantFunc 0
> > >> ExtTag- RBE+
> > >> DevCtl: Report errors: Correctable- Non-Fatal- Fatal-
> > Unsupported-
> > >> RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop+
> > >> MaxPayload 128 bytes, MaxReadReq 512 bytes
> > >> DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr-
> > TransPend+
> > >> LnkCap: Port #0, Speed 5GT/s, Width x2, ASPM not supported,
> > Exit Latency L0s unlimited, L1 unlimited
> > >> ClockPM- Surprise- LLActRep- BwNot+ ASPMOptComp+
> > >> LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk-
> > >> ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
> > >> LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk+
> > DLActive- BWMgmt- ABWMgmt-
> > >> RootCtl: ErrCorrectable- ErrNon-Fatal- ErrFatal- PMEIntEna-
> > CRSVisible+
> > >> RootCap: CRSVisible+
> > >> RootSta: PME ReqID 0000, PMEStatus- PMEPending-
> > >> DevCap2: Completion Timeout: Range B, TimeoutDis+, LTR-,
> > OBFF Not Supported ARIFwd-
> > >> DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-,
> > OBFF Disabled ARIFwd-
> > >> LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis-
> > >> Transmit Margin: Normal Operating Range,
> > EnterModifiedCompliance- ComplianceSOS-
> > >> Compliance De-emphasis: -6dB
> > >> LnkSta2: Current De-emphasis Level: -3.5dB,
> > EqualizationComplete-, EqualizationPhase1-
> > >> EqualizationPhase2-, EqualizationPhase3-,
> > LinkEqualizationRequest-
> > >> Capabilities: [100 v1] Device Serial Number 00-00-00-00-00-00-00-00
> > >> Capabilities: [10c v1] Virtual Channel
> > >> Caps: LPEVC=0 RefClk=100ns PATEntryBits=1
> > >> Arb: Fixed- WRR32- WRR64- WRR128-
> > >> Ctrl: ArbSelect=Fixed
> > >> Status: InProgress-
> > >> VC0: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
> > >> Arb: Fixed- WRR32- WRR64- WRR128- TWRR128-
> > WRR256-
> > >> Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VC=ff
> > >> Status: NegoPending- InProgress-
> > >> Capabilities: [128 v1] Vendor Specific Information: ID=1234 Rev=1
> > >> Len=018 <?>
> > >>
> > >> 01:00.0 Network controller: Qualcomm Atheros AR93xx Wireless Network
> > Adapter (rev 01)
> > >> Subsystem: Qualcomm Atheros Device 3112
> > >> Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop-
> > ParErr- Stepping- SERR- FastB2B- DisINTx-
> > >> Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
> > <TAbort- <MAbort- >SERR- <PERR- INTx-
> > >> Latency: 0, Cache Line Size: 128 bytes
> > >> Interrupt: pin A routed to IRQ 224
> > >> Region 0: Memory at e0000000 (64-bit, non-prefetchable) [size=128K]
> > >> [virtual] Expansion ROM at e0020000 [disabled] [size=64K]
> > >> Capabilities: [40] Power Management version 3
> > >> Flags: PMEClk- DSI- D1+ D2- AuxCurrent=375mA
> > PME(D0+,D1+,D2-,D3hot+,D3cold-)
> > >> Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
> > >> Capabilities: [50] MSI: Enable- Count=1/4 Maskable+ 64bit+
> > >> Address: 0000000000000000 Data: 0000
> > >> Masking: 00000000 Pending: 00000000
> > >> Capabilities: [70] Express (v2) Endpoint, MSI 00
> > >> DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency
> > L0s <1us, L1 <8us
> > >> ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset-
> > SlotPowerLimit 0.000W
> > >> DevCtl: Report errors: Correctable- Non-Fatal- Fatal-
> > Unsupported-
> > >> RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop-
> > >> MaxPayload 128 bytes, MaxReadReq 512 bytes
> > >> DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr-
> > TransPend-
> > >> LnkCap: Port #0, Speed 2.5GT/s, Width x1, ASPM L0s L1, Exit
> > Latency L0s <2us, L1 <64us
> > >> ClockPM- Surprise- LLActRep- BwNot- ASPMOptComp-
> > >> LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk-
> > >> ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
> > >> LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk+
> > DLActive- BWMgmt- ABWMgmt-
> > >> DevCap2: Completion Timeout: Not Supported, TimeoutDis+,
> > LTR-, OBFF Not Supported
> > >> DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-,
> > OBFF Disabled
> > >> LnkCtl2: Target Link Speed: 2.5GT/s, EnterCompliance-
> > SpeedDis-
> > >> Transmit Margin: Normal Operating Range,
> > EnterModifiedCompliance- ComplianceSOS-
> > >> Compliance De-emphasis: -6dB
> > >> LnkSta2: Current De-emphasis Level: -6dB,
> > EqualizationComplete-, EqualizationPhase1-
> > >> EqualizationPhase2-, EqualizationPhase3-,
> > LinkEqualizationRequest-
> > >> Capabilities: [100 v1] Advanced Error Reporting
> > >> UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt-
> > RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
> > >> UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt-
> > RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
> > >> UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt-
> > RxOF+ MalfTLP+ ECRC- UnsupReq- ACSViol-
> > >> CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout-
> > NonFatalErr-
> > >> CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout-
> > NonFatalErr+
> > >> AERCap: First Error Pointer: 00, GenCap- CGenEn-
> > ChkCap- ChkEn-
> > >> Capabilities: [140 v1] Virtual Channel
> > >> Caps: LPEVC=0 RefClk=100ns PATEntryBits=1
> > >> Arb: Fixed- WRR32- WRR64- WRR128-
> > >> Ctrl: ArbSelect=Fixed
> > >> Status: InProgress-
> > >> VC0: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
> > >> Arb: Fixed- WRR32- WRR64- WRR128- TWRR128-
> > WRR256-
> > >> Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VC=ff
> > >> Status: NegoPending- InProgress-
> > >> Capabilities: [300 v1] Device Serial Number 00-00-00-00-00-00-00-00
> > >> Kernel driver in use: ath9k
> > >>
> > >> Here is the cat /proc/interrupts (after we do interface up):
> > >>
> > >> root@:~# ifconfig wlan0 up
> > >> [ 1548.926601] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready
> > >> root@Xilinx-ZCU102-2016_3:~# cat /proc/interrupts
> > >> CPU0 CPU1 CPU2 CPU3
> > >> 1: 0 0 0 0 GICv2 29 Edge arch_timer
> > >> 2: 19873 20058 19089 17435 GICv2 30 Edge arch_timer
> > >> 12: 0 0 0 0 GICv2 156 Level zynqmp-dma
> > >> 13: 0 0 0 0 GICv2 157 Level zynqmp-dma
> > >> 14: 0 0 0 0 GICv2 158 Level zynqmp-dma
> > >> 15: 0 0 0 0 GICv2 159 Level zynqmp-dma
> > >> 16: 0 0 0 0 GICv2 160 Level zynqmp-dma
> > >> 17: 0 0 0 0 GICv2 161 Level zynqmp-dma
> > >> 18: 0 0 0 0 GICv2 162 Level zynqmp-dma
> > >> 19: 0 0 0 0 GICv2 163 Level zynqmp-dma
> > >> 20: 0 0 0 0 GICv2 164 Level Mali_GP_MMU,
> > Mali_GP, Mali_PP0_MMU, Mali_PP0, Mali_PP1_MMU, Mali_PP1
> > >> 30: 0 0 0 0 GICv2 95 Level eth0, eth0
> > >> 206: 314 0 0 0 GICv2 49 Level cdns-i2c
> > >> 207: 40 0 0 0 GICv2 50 Level cdns-i2c
> > >> 209: 0 0 0 0 GICv2 150 Level nwl_pcie:misc
> > >> 214: 12 0 0 0 GICv2 47 Level ff0f0000.spi
> > >> 215: 0 0 0 0 GICv2 58 Level ffa60000.rtc
> > >> 216: 0 0 0 0 GICv2 59 Level ffa60000.rtc
> > >> 217: 0 0 0 0 GICv2 165 Level ahci-
> > ceva[fd0c0000.ahci]
> > >> 218: 61 0 0 0 GICv2 81 Level mmc0
> > >> 219: 0 0 0 0 GICv2 187 Level arm-smmu global fault
> > >> 220: 471 0 0 0 GICv2 53 Level xuartps
> > >> 223: 0 0 0 0 GICv2 154 Level fd4c0000.dma
> > >> 224: 3 0 0 0 dummy 1 Edge ath9k
> > >> 225: 0 0 0 0 GICv2 97 Level xhci-hcd:usb1
> > >>
> > >> Regards,
> > >> Bharat
>
^ permalink raw reply
* Re: [PATCH] ath10k: free host-mem with DMA_BIRECTIONAL flag.
From: Valo, Kalle @ 2016-12-12 16:35 UTC (permalink / raw)
To: greearb@candelatech.com
Cc: linux-wireless@vger.kernel.org, ath10k@lists.infradead.org
In-Reply-To: <1480962519-13027-1-git-send-email-greearb@candelatech.com>
greearb@candelatech.com writes:
> From: Ben Greear <greearb@candelatech.com>
>
> Hopefully this fixes the problem reported by Kalle:
>
> Noticed this in my log, but I don't have time to investigate this in
> detail right now:
>
> [ 413.795346] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready
> [ 414.158755] IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
> [ 477.439659] ath10k_pci 0000:02:00.0: could not get mac80211 beacon
> [ 481.666630] ------------[ cut here ]------------
> [ 481.666669] WARNING: CPU: 0 PID: 1978 at lib/dma-debug.c:1155 check_un=
map+0x320/0x8e0
> [ 481.666688] ath10k_pci 0000:02:00.0: DMA-API: device driver frees DMA =
memory with different direction [device address=3D0x000000002d130000] [size=
=3D63800 bytes] [mapped with DMA_BIDIRECTIONAL] [unmapped with DMA_TO_DEVIC=
E]
> [ 481.666703] Modules linked in: ctr ccm ath10k_pci(E-) ath10k_core(E) a=
th(E) mac80211(E) cfg80211(E) snd_hda_codec_hdmi snd_hda_codec_idt snd_hda_=
codec_generic snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep snd_pcm sn=
d_seq_midi arc4 snd_rawmidi snd_seq_midi_event snd_seq btusb btintel snd_se=
q_device joydev coret
> [ 481.671468] CPU: 0 PID: 1978 Comm: rmmod Tainted: G E 4.9=
.0-rc7-wt+ #54
> [ 481.671478] Hardware name: Hewlett-Packard HP ProBook 6540b/1722, BIOS=
68CDD Ver. F.04 01/27/2010
> [ 481.671489] ef49dcec c842ee92 c8b5830e ef49dd34 ef49dd20 c80850f5 c8b=
5a13c ef49dd50
> [ 481.671560] 000007ba c8b5830e 00000483 c8461830 c8461830 00000483 ef4=
9ddcc f34e64b8
> [ 481.671641] c8b58360 ef49dd3c c80851bb 00000009 00000000 ef49dd34 c8b=
5a13c ef49dd50
> [ 481.671716] Call Trace:
> [ 481.671731] [<c842ee92>] dump_stack+0x76/0xb4
> [ 481.671745] [<c80850f5>] __warn+0xe5/0x100
> [ 481.671757] [<c8461830>] ? check_unmap+0x320/0x8e0
> [ 481.671769] [<c8461830>] ? check_unmap+0x320/0x8e0
> [ 481.671780] [<c80851bb>] warn_slowpath_fmt+0x3b/0x40
> [ 481.671791] [<c8461830>] check_unmap+0x320/0x8e0
> [ 481.671804] [<c8462054>] debug_dma_unmap_page+0x84/0xa0
> [ 481.671835] [<f937cd7a>] ath10k_wmi_free_host_mem+0x9a/0xe0 [ath10k_c=
ore]
> [ 481.671861] [<f9363400>] ath10k_core_destroy+0x50/0x60 [ath10k_core]
> [ 481.671875] [<f8e13969>] ath10k_pci_remove+0x79/0xa0 [ath10k_pci]
> [ 481.671889] [<c848d8d8>] pci_device_remove+0x38/0xb0
> [ 481.671901] [<c859fe4b>] __device_release_driver+0x7b/0x110
> [ 481.671913] [<c85a00e7>] driver_detach+0x97/0xa0
> [ 481.671923] [<c859ef8b>] bus_remove_driver+0x4b/0xb0
> [ 481.671934] [<c85a0cda>] driver_unregister+0x2a/0x60
> [ 481.671949] [<c848c888>] pci_unregister_driver+0x18/0x70
> [ 481.671965] [<f8e14dae>] ath10k_pci_exit+0xd/0x25f [ath10k_pci]
> [ 481.671979] [<c812bb84>] SyS_delete_module+0xf4/0x180
> [ 481.671995] [<c81f801b>] ? __might_fault+0x8b/0xa0
> [ 481.672009] [<c80037d0>] do_fast_syscall_32+0xa0/0x1e0
> [ 481.672025] [<c88d4c88>] sysenter_past_esp+0x45/0x74
> [ 481.672037] ---[ end trace 3fd23759e17e1622 ]---
> [ 481.672049] Mapped at:
> [ 481.672060] [ 481.672072] [<c846062c>] debug_dma_map_page.part.25+0x=
1c/0xf0
> [ 481.672083] [ 481.672095] [<c8460799>] debug_dma_map_page+0x99/0xc0
> [ 481.672106] [ 481.672132] [<f93745ec>] ath10k_wmi_alloc_chunk+0x12c/=
0x1f0 [ath10k_core]
> [ 481.672142] [ 481.672168] [<f937d0c4>] ath10k_wmi_event_service_read=
y_work+0x304/0x540 [ath10k_core]
> [ 481.672178] [ 481.672190] [<c80a3643>] process_one_work+0x1c3/0x670
> [ 482.137134] ath10k_pci 0000:02:00.0: pci irq msi oper_irq_mode 2 irq_m=
ode 0 reset_mode 0
> [ 482.313144] ath10k_pci 0000:02:00.0: Direct firmware load for ath10k/p=
re-cal-pci-0000:02:00.0.bin failed with error -2
> [ 482.313274] ath10k_pci 0000:02:00.0: Direct firmware load for ath10k/c=
al-pci-0000:02:00.0.bin failed with error -2
> [ 482.313768] ath10k_pci 0000:02:00.0: qca988x hw2.0 target 0x4100016c c=
hip_id 0x043202ff sub 0000:0000
> [ 482.313777] ath10k_pci 0000:02:00.0: kconfig debug 1 debugfs 1 tracing=
1 dfs 0 testmode 1
> [ 482.313974] ath10k_pci 0000:02:00.0: firmware ver 10.2.4.70.59-2 api 5=
features no-p2p,raw-mode,mfp,allows-mesh-bcast crc32 4159f498
> [ 482.369858] ath10k_pci 0000:02:00.0: Direct firmware load for ath10k/Q=
CA988X/hw2.0/board-2.bin failed with error -2
> [ 482.370011] ath10k_pci 0000:02:00.0: board_file api 1 bmi_id N/A crc32=
bebc7c08
> [ 483.596770] ath10k_pci 0000:02:00.0: htt-ver 2.1 wmi-op 5 htt-op 2 cal=
otp max-sta 128 raw 0 hwcrypto 1
> [ 483.701686] ath: EEPROM regdomain: 0x0
> [ 483.701706] ath: EEPROM indicates default country code should be used
> [ 483.701713] ath: doing EEPROM country->regdmn map search
> [ 483.701721] ath: country maps to regdmn code: 0x3a
> [ 483.701730] ath: Country alpha2 being used: US
> [ 483.701737] ath: Regpair used: 0x3a
>
> Reported-by: Kalle Valo <kvalo@qca.qualcomm.com>
> Signed-off-by: Ben Greear <greearb@candelatech.com>
Thanks, I don't see the warning anymore so this seems to be fixed. I'll
push this to 4.10.
--=20
Kalle Valo=
^ permalink raw reply
* [PATCH v3] cfg80211: NL80211_ATTR_SOCKET_OWNER support for CMD_CONNECT
From: Andrew Zaborowski @ 2016-12-12 16:45 UTC (permalink / raw)
To: linux-wireless
Disconnect or deauthenticate when the owning socket is closed if this
flag is supplied to CMD_CONNECT or CMD_ASSOCIATE. This may be used
to ensure userspace daemon doesn't leave an unmanaged connection behind.
In some situations it would be possible to account for that, to some
degree, in the deamon restart code or in the up/down scripts without
the use of this attribute. But there will be systems where the daemon
can go away for varying periods without a warning due to local resource
management.
Signed-off-by: Andrew Zaborowski <andrew.zaborowski@intel.com>
---
changes in v2:
- add wdev.disconnect_bssid so we can deauthenticate from a BSS before
association finishes
- dropped CMD_AUTHENTICATE changes
changes in v3:
- commit message fix
---
include/net/cfg80211.h | 7 +++++++
include/uapi/linux/nl80211.h | 2 ++
net/wireless/core.c | 33 +++++++++++++++++++++++++++++++++
net/wireless/mlme.c | 2 ++
net/wireless/nl80211.c | 28 +++++++++++++++++++++++++++-
net/wireless/sme.c | 4 ++++
6 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index bd19faa..ca2e252 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3764,6 +3764,9 @@ struct cfg80211_cached_keys;
* @conn: (private) cfg80211 software SME connection state machine data
* @connect_keys: (private) keys to set after connection is established
* @conn_bss_type: connecting/connected BSS type
+ * @conn_owner_nlportid: (private) connection owner socket port ID
+ * @disconnect_wk: (private) auto-disconnect work
+ * @disconnect_bssid: (private) the BSSID to use for auto-disconnect
* @ibss_fixed: (private) IBSS is using fixed BSSID
* @ibss_dfs_possible: (private) IBSS may change to a DFS channel
* @event_list: (private) list for internal event processing
@@ -3795,6 +3798,10 @@ struct wireless_dev {
struct cfg80211_conn *conn;
struct cfg80211_cached_keys *connect_keys;
enum ieee80211_bss_type conn_bss_type;
+ u32 conn_owner_nlportid;
+
+ struct work_struct disconnect_wk;
+ u8 disconnect_bssid[ETH_ALEN];
struct list_head event_list;
spinlock_t event_lock;
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 56368e9..84db1f0 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1788,6 +1788,8 @@ enum nl80211_commands {
* and remove functions. NAN notifications will be sent in unicast to that
* socket. Without this attribute, any socket can add functions and the
* notifications will be sent to the %NL80211_MCGRP_NAN multicast group.
+ * If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the
+ * station will deauthenticate when the socket is closed.
*
* @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
* the TDLS link initiator.
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 8201e6d..6b8fd68 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -357,6 +357,36 @@ static void cfg80211_sched_scan_stop_wk(struct work_struct *work)
rtnl_unlock();
}
+static void cfg80211_disconnect_wk(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+
+ wdev = container_of(work, struct wireless_dev, disconnect_wk);
+ rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (!wdev->netdev)
+ return;
+
+ wdev_lock(wdev);
+
+ if (wdev->conn_owner_nlportid) {
+ /*
+ * Use disconnect_bssid if still connecting and ops->disconnect
+ * not implemented. Otherwise we can use cfg80211_disconnect.
+ */
+ if (rdev->ops->disconnect || wdev->current_bss)
+ cfg80211_disconnect(rdev, wdev->netdev,
+ WLAN_REASON_DEAUTH_LEAVING, true);
+ else
+ cfg80211_mlme_deauth(rdev, wdev->netdev,
+ wdev->disconnect_bssid, NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING, false);
+ }
+
+ wdev_unlock(wdev);
+}
+
/* exported functions */
struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
@@ -1117,6 +1147,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
dev->priv_flags |= IFF_DONT_BRIDGE;
+ INIT_WORK(&wdev->disconnect_wk, cfg80211_disconnect_wk);
+
nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
break;
case NETDEV_GOING_DOWN:
@@ -1205,6 +1237,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
#ifdef CONFIG_CFG80211_WEXT
kzfree(wdev->wext.keys);
#endif
+ flush_work(&wdev->disconnect_wk);
}
/*
* synchronise (so that we won't find this netdev
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index cbb48e2..9923244 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -328,6 +328,8 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
ASSERT_WDEV_LOCK(wdev);
+ wdev->conn_owner_nlportid = 0;
+
if (local_state_change &&
(!wdev->current_bss ||
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c510810..502ae92 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -8003,6 +8003,12 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
wdev_unlock(dev->ieee80211_ptr);
}
+ if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
+ dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid;
+
+ memcpy(dev->ieee80211_ptr->disconnect_bssid, bssid, ETH_ALEN);
+ }
+
return err;
}
@@ -8050,6 +8056,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
local_state_change);
wdev_unlock(dev->ieee80211_ptr);
+
+ if (!err)
+ dev->ieee80211_ptr->conn_owner_nlportid = 0;
+
return err;
}
@@ -8097,6 +8107,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
local_state_change);
wdev_unlock(dev->ieee80211_ptr);
+
+ if (!err)
+ dev->ieee80211_ptr->conn_owner_nlportid = 0;
+
return err;
}
@@ -8723,6 +8737,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
wdev_unlock(dev->ieee80211_ptr);
if (err)
kzfree(connkeys);
+
+ if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER])
+ dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid;
+
return err;
}
@@ -14425,13 +14443,21 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
spin_unlock(&rdev->destroy_list_lock);
schedule_work(&rdev->destroy_work);
}
- } else if (schedule_scan_stop) {
+
+ continue;
+ }
+
+ if (schedule_scan_stop) {
sched_scan_req->owner_nlportid = 0;
if (rdev->ops->sched_scan_stop &&
rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
schedule_work(&rdev->sched_scan_stop_wk);
}
+
+ list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list)
+ if (wdev->conn_owner_nlportid == notify->portid)
+ schedule_work(&wdev->disconnect_wk);
}
rcu_read_unlock();
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index a77db33..f5cc067 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -713,6 +713,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
kzfree(wdev->connect_keys);
wdev->connect_keys = NULL;
wdev->ssid_len = 0;
+ wdev->conn_owner_nlportid = 0;
if (bss) {
cfg80211_unhold_bss(bss_from_pub(bss));
cfg80211_put_bss(wdev->wiphy, bss);
@@ -941,6 +942,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
wdev->current_bss = NULL;
wdev->ssid_len = 0;
+ wdev->conn_owner_nlportid = 0;
nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
@@ -1084,6 +1086,8 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
kzfree(wdev->connect_keys);
wdev->connect_keys = NULL;
+ wdev->conn_owner_nlportid = 0;
+
if (wdev->conn)
err = cfg80211_sme_disconnect(wdev, reason);
else if (!rdev->ops->disconnect)
--
2.9.3
^ permalink raw reply related
* Re: [RFC V3 03/11] nl80211: add support for gscan
From: Dan Williams @ 2016-12-12 17:43 UTC (permalink / raw)
To: Arend van Spriel, Johannes Berg; +Cc: linux-wireless
In-Reply-To: <1481543997-24624-4-git-send-email-arend.vanspriel@broadcom.com>
On Mon, 2016-12-12 at 11:59 +0000, Arend van Spriel wrote:
> This patch adds support for GScan which is a scan offload feature
> used in Android.
>
> Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
> Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.co
> m>
> Reviewed-by: Franky Lin <franky.lin@broadcom.com>
> Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
> ---
> Changes:
> V2
> - remove pr_err() statement from nl80211.c
> - get rid of #if 0 code.
> V3
> - change and document storage type of gscan attributes.
> - remove base period attribute from nl80211.
> - bucket periods are changed to be seconds.
> - change NO_IR attribute to PASSIVE.
> - check for NL80211_ATTR_MAC{,_MASK} if random mac support is
> requested.
> - remove NL80211_SCAN_FLAG_IE_DATA.
> ---
> include/net/cfg80211.h | 91 +++++++++++
> include/uapi/linux/nl80211.h | 146 ++++++++++++++++++
> net/wireless/core.c | 31 ++++
> net/wireless/core.h | 4 +
> net/wireless/nl80211.c | 356
> ++++++++++++++++++++++++++++++++++++++++++-
> net/wireless/rdev-ops.h | 25 +++
> net/wireless/scan.c | 28 ++++
> net/wireless/trace.h | 9 ++
> 8 files changed, 685 insertions(+), 5 deletions(-)
>
> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
> index b78377f..8bc8842 100644
> --- a/include/net/cfg80211.h
> +++ b/include/net/cfg80211.h
> @@ -2453,6 +2453,92 @@ struct cfg80211_nan_func {
> };
>
> /**
> + * struct cfg80211_gscan_channel - GScan channel parameters.
> + *
> +
> + * @ch: specific channel.
> + * @dwell_time: hint for dwell time in milliseconds.
> + * @passive: indicates passive scan is requested.
> + */
> +struct cfg80211_gscan_channel {
> + struct ieee80211_channel *ch;
> + u8 dwell_time;
> + bool passive;
> +};
> +
> +/**
> + * struct cfg80211_gscan_bucket - GScan bucket parameters.
> + *
> + * @idx: unique bucket index.
> + * @band: bit flags for band(s) to use, see %enum
> nl80211_bucket_band.
> + * @report_events: This is a bit field according %enum
> nl80211_bucket_report_event.
> + * @period: period in which the bucket is scheduled to be scanned.
> If the
> + * period is too small for driver it should not fail but
> report results
> + * as fast as it can. For exponential backoff bucket this is
> the minimum
> + * period.
> + * @max_period: used only for the exponential backoff bucket whose
> scan period
> + * will grow exponentially to a maximum period of max_period.
> + * @exponent: used only for the exponential backoff bucket.
> + * @step_count: used only for the exponential backoff bucket.
> + * @n_channels: number of channels in @channels array.
> + * @channels: channels to scan which may include DFS channels.
> + */
> +struct cfg80211_gscan_bucket {
> + u32 idx;
> + u16 period;
> + u8 band;
> + u8 report_events;
> + u16 max_period;
> + u8 exponent;
> + u8 step_count;
> + u8 n_channels;
> + struct cfg80211_gscan_channel *channels;
> +};
> +
> +/**
> + * struct cfg80211_gscan_request - GScan request parameters.
> + *
> + * @flags: scan request flags according %enum nl80211_scan_flags.
> + * @base_period: base timer period in milliseconds.
> + * @max_ap_per_scan: number of APs to store in each scan entry in
> the BSSID/RSSI
> + * history buffer (keep APS with highest RSSI).
> + * @report_threshold_percent: wake up system when scan buffer is
> filled to this
> + * percentage.
> + * @report_threshold_num_scans: wake up system when this many scans
> are stored
> + * in scan buffer.
> + * @mac: MAC address used for randomisation.
> + * @mac_mask: MAC address mask. bits that are 0 in the mask should
> be
> + * randomised, bits that are 1 should be taken as is from
> @mac.
> + * @n_buckets: number of entries in @buckets array.
> + * @buckets: array of GScan buckets.
> + *
> + * @dev: net device for which GScan is requested.
> + * @rcu_head: RCU callback used to free the struct.
> + * @owner_nlportid: netlink port which initiated this request.
> + * @scan_start: start time of this scan in jiffies.
> + */
> +struct cfg80211_gscan_request {
> + u32 flags;
> + u16 base_period;
> + u8 max_ap_per_scan;
> + u8 report_threshold_percent;
> + u8 report_threshold_num_scans;
> + u8 mac[ETH_ALEN];
> + u8 mac_mask[ETH_ALEN];
> +
> + u8 n_buckets;
> +
> + /* internal */
> + struct net_device *dev;
> + struct rcu_head rcu_head;
> + u32 owner_nlportid;
> + unsigned long scan_start;
> +
> + /* keep last */
> + struct cfg80211_gscan_bucket buckets[0];
> +};
> +
> +/**
> * struct cfg80211_ops - backend description for wireless
> configuration
> *
> * This struct is registered by fullmac card drivers and/or wireless
> stacks
> @@ -2764,6 +2850,8 @@ struct cfg80211_nan_func {
> * All other parameters must be ignored.
> *
> * @set_multicast_to_unicast: configure multicast to unicast
> conversion for BSS
> + * @start_gscan: start the GSCAN scanning offload.
> + * @stop_gscan: stop the GSCAN scanning offload.
> */
> struct cfg80211_ops {
> int (*suspend)(struct wiphy *wiphy, struct
> cfg80211_wowlan *wow);
> @@ -3048,6 +3136,9 @@ struct cfg80211_ops {
> int (*set_multicast_to_unicast)(struct wiphy *wiphy,
> struct net_device *dev,
> const bool enabled);
> + int (*start_gscan)(struct wiphy *wiphy, struct
> net_device *dev,
> + struct cfg80211_gscan_request
> *gscan_req);
> + int (*stop_gscan)(struct wiphy *wiphy, struct
> net_device *dev);
> };
>
> /*
> diff --git a/include/uapi/linux/nl80211.h
> b/include/uapi/linux/nl80211.h
> index 01ab2f7..5e42383 100644
> --- a/include/uapi/linux/nl80211.h
> +++ b/include/uapi/linux/nl80211.h
> @@ -894,6 +894,12 @@
> * does not result in a change for the current association.
> Currently,
> * only the %NL80211_ATTR_IE data is used and updated with
> this command.
> *
> + * @NL80211_CMD_START_GSCAN: start GScan.
> + * @NL80211_CMD_STOP_GSCAN: request to stop current GScan.
> + * @NL80211_CMD_GSCAN_STOPPED: indicates that the currently running
> GScan
> + * has stopped. This event is generated upon
> @NL80211_CMD_STOP_GSCAN and
> + * the driver may issue this event at any time when a GScan
> is running.
> + *
> * @NL80211_CMD_MAX: highest used command number
> * @__NL80211_CMD_AFTER_LAST: internal use
> */
> @@ -1093,6 +1099,10 @@ enum nl80211_commands {
>
> NL80211_CMD_UPDATE_CONNECT_PARAMS,
>
> + NL80211_CMD_START_GSCAN,
> + NL80211_CMD_STOP_GSCAN,
> + NL80211_CMD_GSCAN_STOPPED,
> +
> /* add new commands above here */
>
> /* used to define NL80211_CMD_MAX below */
> @@ -2389,6 +2399,7 @@ enum nl80211_attrs {
>
> NL80211_ATTR_BSSID,
> NL80211_ATTR_GSCAN_CAPS,
> + NL80211_ATTR_GSCAN_PARAMS,
>
> /* add attributes here, update the policy in nl80211.c */
>
> @@ -5246,4 +5257,139 @@ enum nl80211_gscan_caps_attr {
> NL80211_GSCAN_CAPS_ATTR_MAX =
> __NL80211_GSCAN_CAPS_ATTR_AFTER_LAST - 1
> };
>
> +/**
> + * enum nl80211_gscan_attr - common GScan parameters.
> + *
> + * @__NL80211_GSCAN_ATTR_INVALID: reserved.
> + * @NL80211_GSCAN_ATTR_MAX_AP_PER_SCAN: number of APs that are kept
> per
> + * scan. The kept APs are the ones with strongest RSSI level
> (u8).
> + * @NL80211_GSCAN_ATTR_REPORT_PERC: threshold specifying percentage
> of
> + * scan cache filled that should trigger event for scan
> results (u8).
> + * @NL80211_GSCAN_ATTR_REPORT_SCANS: threshold specifying number of
> scans
> + * after which an event is expected for scan results (u8).
> + * @NL80211_GSCAN_ATTR_BUCKETS: nested attribute specifying
> + * per-bucket parameters for GScan. See %enum
> nl80211_gscan_bucket_attr
> + * for description.
> + * @NL80211_GSCAN_ATTR_MAX: highest GScan attribute.
> + * @__NL80211_GSCAN_ATTR_AFTER_LAST: internal use.
> + */
> +enum nl80211_gscan_attr {
> + __NL80211_GSCAN_ATTR_INVALID,
> + NL80211_GSCAN_ATTR_MAX_AP_PER_SCAN,
> + NL80211_GSCAN_ATTR_REPORT_PERC,
> + NL80211_GSCAN_ATTR_REPORT_SCANS,
> + NL80211_GSCAN_ATTR_BUCKETS,
> +
> + /* keep last */
> + __NL80211_GSCAN_ATTR_AFTER_LAST,
> + NL80211_GSCAN_ATTR_MAX = __NL80211_GSCAN_ATTR_AFTER_LAST - 1
> +};
> +
> +/**
> + * enum nl80211_gscan_bucket_attr - per-bucket GScan parameters.
> + *
> + * @__NL80211_GSCAN_BUCKET_ATTR_INVALID,
> + * @NL80211_GSCAN_BUCKET_ATTR_ID: unique bucket id (u32).
> + * @NL80211_GSCAN_BUCKET_ATTR_BAND: specifies the band to be scanned
> + * according %enum nl80211_bucket_band. If specified
> + * @NL80211_GSCAN_BUCKET_ATTR_CHANNELS is ignored (u8).
> + * @NL80211_GSCAN_BUCKET_ATTR_PERIOD: specifies the period between
> consecutive
> + * scans of this bucket in seconds. For the backoff bucket
> this is
> + * period(0) (u16).
> + * @NL80211_GSCAN_BUCKET_ATTR_REPORT: specifies reporting flags
> according
> + * %enum nl80211_bucket_report_event (u8).
> + * @NL80211_GSCAN_BUCKET_ATTR_MAX_PERIOD: maximum period between
> + * consecutive scans. If specified this is a backoff bucket
> in
> + * which the period increases according formula:
> + * period(N) = period(0) * (base ^ (N/step_count)) (u16)
> + * @NL80211_GSCAN_BUCKET_ATTR_EXPONENT: exponential base value as
> used
> + * in given formula. This attribute is required when
> + * @NL80211_GSCAN_BUCKET_ATTR_MAX_PERIOD is specified (u8).
> + * @NL80211_GSCAN_BUCKET_ATTR_STEPS: step count as used in given
> formula.
> + * This attribute is required when
> @NL80211_GSCAN_BUCKET_ATTR_MAX_PERIOD
> + * is specified (u8).
> + * @NL80211_GSCAN_BUCKET_ATTR_CHANNELS: nested attribute specifying
> the
> + * channels that are to be scanned for this bucket.
> + * @NL80211_GSCAN_BUCKET_ATTR_MAX: highest GScan bucket attribute.
> + * @__NL80211_GSCAN_BUCKET_ATTR_AFTER_LAST: internal use.
> + */
> +enum nl80211_gscan_bucket_attr {
> + __NL80211_GSCAN_BUCKET_ATTR_INVALID,
> + NL80211_GSCAN_BUCKET_ATTR_ID,
> + NL80211_GSCAN_BUCKET_ATTR_BAND,
> + NL80211_GSCAN_BUCKET_ATTR_PERIOD,
> + NL80211_GSCAN_BUCKET_ATTR_REPORT,
> + NL80211_GSCAN_BUCKET_ATTR_MAX_PERIOD,
> + NL80211_GSCAN_BUCKET_ATTR_EXPONENT,
> + NL80211_GSCAN_BUCKET_ATTR_STEPS,
> + NL80211_GSCAN_BUCKET_ATTR_CHANNELS,
> +
> + /* keep last */
> + __NL80211_GSCAN_BUCKET_ATTR_AFTER_LAST,
> + NL80211_GSCAN_BUCKET_ATTR_MAX =
> __NL80211_GSCAN_BUCKET_ATTR_AFTER_LAST - 1
> +};
> +
> +/**
> + * enum nl80211_gscan_chan_attr - GScan bucket channel parameters.
> + *
> + * @__NL80211_GSCAN_CHAN_ATTR_INVALID: reserved.
> + * @NL80211_GSCAN_CHAN_ATTR_FREQ: frequency of channel to be scanned
> (u32).
> + * @NL80211_GSCAN_CHAN_ATTR_DWELL_TIME: dwell time in milliseconds
> to stay
> + * on this channel during scanning (u8).
> + * @NL80211_GSCAN_CHAN_ATTR_PASSIVE: flag attribute indicating that
> scanning
> + * should be done passive for this channel.
> + * @NL80211_GSCAN_CHAN_ATTR_MAX: highest GScan channel attribute.
> + * @__NL80211_GSCAN_CHAN_ATTR_AFTER_LAST: internal use.
> + *
> + * Apart from the channel itself the attributes
> %NL80211_GSCAN_CHAN_ATTR_DWELL_TIME
> + * and %NL80211_GSCAN_CHAN_ATTR_PASSIVE are advisory values. The
> driver may or
> + * may not comply.
> + */
> +enum nl80211_gscan_chan_attr {
> + __NL80211_GSCAN_CHAN_ATTR_INVALID,
> + NL80211_GSCAN_CHAN_ATTR_FREQ,
> + NL80211_GSCAN_CHAN_ATTR_DWELL_TIME,
> + NL80211_GSCAN_CHAN_ATTR_PASSIVE,
> +
> + /* keep last */
> + __NL80211_GSCAN_CHAN_ATTR_AFTER_LAST,
> + NL80211_GSCAN_CHAN_ATTR_MAX =
> __NL80211_GSCAN_CHAN_ATTR_AFTER_LAST - 1
> +};
> +
> +/**
> + * enum nl80211_bucket_band - GScan bucket band selection.
Quite possibly this was already covered and somebody requested you to
change this to the current name. If that's the case, ignore this.
But shouldn't this enum and bucket_event_report include "gscan" in
their name, like the other gscan specific stuff does? Are these going
to get used for something else too, and will that thing make sense with
the word "bucket"? Just "nl80211_bucket_band" is pretty generic.
Dan
> + * @NL80211_BUCKET_BAND_2GHZ: consider all device supported channels
> + * in 2G band.
> + * @NL80211_BUCKET_BAND_5GHZ: consider all device supported channels
> + * in 5G band, ie. both DFS and non-DFS when
> @NL80211_BUCKET_BAND_NODFS
> + * and @NL80211_BUCKET_BAND_DFS_ONLY are not set.
> + * @NL80211_BUCKET_BAND_NODFS: only consider non-DFS channels. Only
> + * applicable when 5G band is selected, otherwise ignored.
> + * @NL80211_BUCKET_BAND_DFS_ONLY: only consider DFS channels. Only
> + * applicable when 5G band is selected, otherwise ignored.
> + *
> + * Setting both @NL80211_BUCKET_BAND_NODFS and
> @NL80211_BUCKET_BAND_DFS_ONLY
> + * is considerd invalid.
> + */
> +enum nl80211_bucket_band {
> + NL80211_BUCKET_BAND_2GHZ = (1 << 0),
> + NL80211_BUCKET_BAND_5GHZ = (1 << 1),
> + NL80211_BUCKET_BAND_NODFS = (1 << 2),
> + NL80211_BUCKET_BAND_DFS_ONLY = (1 << 3),
> +};
> +
> +/**
> + * enum nl80211_bucket_report_event - GScan bucket report flags.
> + *
> + * @NL80211_BUCKET_REPORT_EACH_SCAN: report each bucket scan
> completion.
> + * @NL80211_BUCKET_REPORT_FULL_RESULTS: report full scan results.
> + * @NL80211_BUCKET_REPORT_NO_BATCH: no batching required.
> + */
> +enum nl80211_bucket_report_event {
> + NL80211_BUCKET_REPORT_EACH_SCAN = (1 << 0),
> + NL80211_BUCKET_REPORT_FULL_RESULTS = (1 << 1),
> + NL80211_BUCKET_REPORT_NO_BATCH = (1 << 2),
> +};
> +
> #endif /* __LINUX_NL80211_H */
> diff --git a/net/wireless/core.c b/net/wireless/core.c
> index 158c59e..760a2fb 100644
> --- a/net/wireless/core.c
> +++ b/net/wireless/core.c
> @@ -357,6 +357,20 @@ static void cfg80211_sched_scan_stop_wk(struct
> work_struct *work)
> rtnl_unlock();
> }
>
> +static void cfg80211_gscan_stop_wk(struct work_struct *work)
> +{
> + struct cfg80211_registered_device *rdev;
> +
> + rdev = container_of(work, struct cfg80211_registered_device,
> + gscan_stop_wk);
> +
> + rtnl_lock();
> +
> + __cfg80211_stop_gscan(rdev, false);
> +
> + rtnl_unlock();
> +}
> +
> /* exported functions */
>
> struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int
> sizeof_priv,
> @@ -383,6 +397,7 @@ struct wiphy *wiphy_new_nm(const struct
> cfg80211_ops *ops, int sizeof_priv,
> WARN_ON(ops->remain_on_channel && !ops-
> >cancel_remain_on_channel);
> WARN_ON(ops->tdls_channel_switch && !ops-
> >tdls_cancel_channel_switch);
> WARN_ON(ops->add_tx_ts && !ops->del_tx_ts);
> + WARN_ON(ops->start_gscan && !ops->stop_gscan);
>
> alloc_size = sizeof(*rdev) + sizeof_priv;
>
> @@ -456,6 +471,7 @@ struct wiphy *wiphy_new_nm(const struct
> cfg80211_ops *ops, int sizeof_priv,
> spin_lock_init(&rdev->destroy_list_lock);
> INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
> INIT_WORK(&rdev->sched_scan_stop_wk,
> cfg80211_sched_scan_stop_wk);
> + INIT_WORK(&rdev->gscan_stop_wk, cfg80211_gscan_stop_wk);
>
> #ifdef CONFIG_CFG80211_DEFAULT_PS
> rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
> @@ -690,6 +706,12 @@ int wiphy_register(struct wiphy *wiphy)
> (wiphy->bss_select_support &
> ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2))))
> return -EINVAL;
>
> + /* buckets must have unique index and in nl80211 parsing
> + * a u32 is used to verify that hence this limit.
> + */
> + if (WARN_ON(wiphy->gscan && wiphy->gscan->max_scan_buckets >
> 32))
> + return -EINVAL;
> +
> if (wiphy->addresses)
> memcpy(wiphy->perm_addr, wiphy->addresses[0].addr,
> ETH_ALEN);
>
> @@ -1001,6 +1023,7 @@ void __cfg80211_leave(struct
> cfg80211_registered_device *rdev,
> {
> struct net_device *dev = wdev->netdev;
> struct cfg80211_sched_scan_request *sched_scan_req;
> + struct cfg80211_gscan_request *gscan_req;
>
> ASSERT_RTNL();
> ASSERT_WDEV_LOCK(wdev);
> @@ -1014,6 +1037,9 @@ void __cfg80211_leave(struct
> cfg80211_registered_device *rdev,
> sched_scan_req = rtnl_dereference(rdev-
> >sched_scan_req);
> if (sched_scan_req && dev == sched_scan_req->dev)
> __cfg80211_stop_sched_scan(rdev, false);
> + gscan_req = rtnl_dereference(rdev->gscan_req);
> + if (gscan_req && dev == gscan_req->dev)
> + __cfg80211_stop_gscan(rdev, false);
>
> #ifdef CONFIG_CFG80211_WEXT
> kfree(wdev->wext.ie);
> @@ -1089,6 +1115,7 @@ static int cfg80211_netdev_notifier_call(struct
> notifier_block *nb,
> struct wireless_dev *wdev = dev->ieee80211_ptr;
> struct cfg80211_registered_device *rdev;
> struct cfg80211_sched_scan_request *sched_scan_req;
> + struct cfg80211_gscan_request *gscan_req;
>
> if (!wdev)
> return NOTIFY_DONE;
> @@ -1160,6 +1187,10 @@ static int
> cfg80211_netdev_notifier_call(struct notifier_block *nb,
> sched_scan_req->dev == wdev->netdev)) {
> __cfg80211_stop_sched_scan(rdev, false);
> }
> + gscan_req = rtnl_dereference(rdev->gscan_req);
> + if (WARN_ON(gscan_req && gscan_req->dev == wdev-
> >netdev)) {
> + __cfg80211_stop_gscan(rdev, false);
> + }
>
> rdev->opencount--;
> wake_up(&rdev->dev_wait);
> diff --git a/net/wireless/core.h b/net/wireless/core.h
> index ec5f333..ee1d162 100644
> --- a/net/wireless/core.h
> +++ b/net/wireless/core.h
> @@ -75,6 +75,7 @@ struct cfg80211_registered_device {
> struct cfg80211_scan_request *scan_req; /* protected by RTNL
> */
> struct sk_buff *scan_msg;
> struct cfg80211_sched_scan_request __rcu *sched_scan_req;
> + struct cfg80211_gscan_request __rcu *gscan_req;
> unsigned long suspend_at;
> struct work_struct scan_done_wk;
> struct work_struct sched_scan_results_wk;
> @@ -96,6 +97,7 @@ struct cfg80211_registered_device {
> struct work_struct destroy_work;
>
> struct work_struct sched_scan_stop_wk;
> + struct work_struct gscan_stop_wk;
>
> /* must be last because of the way we do wiphy_priv(),
> * and it should at least be aligned to NETDEV_ALIGN */
> @@ -422,6 +424,8 @@ void ___cfg80211_scan_done(struct
> cfg80211_registered_device *rdev,
> void __cfg80211_sched_scan_results(struct work_struct *wk);
> int __cfg80211_stop_sched_scan(struct cfg80211_registered_device
> *rdev,
> bool driver_initiated);
> +int __cfg80211_stop_gscan(struct cfg80211_registered_device *rdev,
> + bool driver_initiated);
> void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
> int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
> struct net_device *dev, enum
> nl80211_iftype ntype,
> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
> index 14e1940..4186ece 100644
> --- a/net/wireless/nl80211.c
> +++ b/net/wireless/nl80211.c
> @@ -9,6 +9,7 @@
> #include <linux/if.h>
> #include <linux/module.h>
> #include <linux/err.h>
> +#include <linux/gcd.h>
> #include <linux/slab.h>
> #include <linux/list.h>
> #include <linux/if_ether.h>
> @@ -405,6 +406,7 @@ enum nl80211_multicast_groups {
> [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN },
> [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type =
> NLA_FLAG, },
> [NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
> + [NL80211_ATTR_GSCAN_PARAMS] = { .type = NLA_NESTED },
> };
>
> /* policy for the key attributes */
> @@ -11860,6 +11862,322 @@ static int
> nl80211_set_multicast_to_unicast(struct sk_buff *skb,
> return rdev_set_multicast_to_unicast(rdev, dev, enabled);
> }
>
> +static const
> +struct nla_policy nl80211_gscan_policy[NL80211_GSCAN_ATTR_MAX + 1] =
> {
> + [NL80211_GSCAN_ATTR_MAX_AP_PER_SCAN] = { .type = NLA_U8 },
> + [NL80211_GSCAN_ATTR_REPORT_PERC] = { .type = NLA_U8 },
> + [NL80211_GSCAN_ATTR_REPORT_SCANS] = { .type = NLA_U8 },
> + [NL80211_GSCAN_ATTR_BUCKETS] = { .type = NLA_NESTED },
> +};
> +
> +static const struct nla_policy
> +nl80211_gscan_bucket_policy[NL80211_GSCAN_BUCKET_ATTR_MAX + 1] = {
> + [NL80211_GSCAN_BUCKET_ATTR_ID] = { .type = NLA_U32 },
> + [NL80211_GSCAN_BUCKET_ATTR_BAND] = { .type = NLA_U8 },
> + [NL80211_GSCAN_BUCKET_ATTR_PERIOD] = { .type = NLA_U16 },
> + [NL80211_GSCAN_BUCKET_ATTR_REPORT] = { .type = NLA_U8 },
> + [NL80211_GSCAN_BUCKET_ATTR_MAX_PERIOD] = { .type = NLA_U16
> },
> + [NL80211_GSCAN_BUCKET_ATTR_EXPONENT] = { .type = NLA_U8 },
> + [NL80211_GSCAN_BUCKET_ATTR_STEPS] = { .type = NLA_U8 },
> + [NL80211_GSCAN_BUCKET_ATTR_CHANNELS] = { .type = NLA_NESTED
> },
> +};
> +
> +static const struct nla_policy
> +nl80211_gscan_channel_policy[NL80211_GSCAN_CHAN_ATTR_MAX + 1] = {
> + [NL80211_GSCAN_CHAN_ATTR_FREQ] = { .type = NLA_U32 },
> + [NL80211_GSCAN_CHAN_ATTR_DWELL_TIME] = { .type = NLA_U8 },
> + [NL80211_GSCAN_CHAN_ATTR_PASSIVE] = { .type = NLA_FLAG },
> +};
> +
> +static int nl80211_parse_gscan_channel(struct
> cfg80211_registered_device *rdev,
> + struct nlattr *nattr,
> + struct cfg80211_gscan_channel
> *chan)
> +{
> + struct nlattr *tb[NL80211_GSCAN_CHAN_ATTR_MAX + 1];
> + struct ieee80211_channel *ch;
> + int err;
> +
> + err = nla_parse(tb, NL80211_GSCAN_CHAN_ATTR_MAX,
> nla_data(nattr),
> + nla_len(nattr),
> nl80211_gscan_channel_policy);
> + if (err)
> + return err;
> +
> + if (!tb[NL80211_GSCAN_CHAN_ATTR_FREQ])
> + return -EINVAL;
> +
> + ch = ieee80211_get_channel(&rdev->wiphy,
> + nla_get_u32(tb[NL80211_GSCAN_CHAN
> _ATTR_FREQ]));
> + if (!ch || (ch->flags & IEEE80211_CHAN_DISABLED))
> + return -EINVAL;
> +
> + chan->ch = ch;
> +
> + if (tb[NL80211_GSCAN_CHAN_ATTR_DWELL_TIME])
> + chan->dwell_time =
> nla_get_u8(tb[NL80211_GSCAN_CHAN_ATTR_DWELL_TIME]);
> +
> + chan->passive =
> nla_get_flag(tb[NL80211_GSCAN_CHAN_ATTR_PASSIVE]);
> + return 0;
> +}
> +
> +static int nl80211_parse_gscan_bucket(struct
> cfg80211_registered_device *rdev,
> + struct nlattr *nattr,
> + struct cfg80211_gscan_bucket
> *bucket,
> + struct cfg80211_gscan_channel
> *channels)
> +{
> + struct nlattr *tb[NL80211_GSCAN_BUCKET_ATTR_MAX + 1];
> + struct nlattr *chan;
> + struct cfg80211_gscan_channel *ch;
> + int err, rem;
> + int num_chans = 0;
> + u32 band_select = 0;
> + u32 dfs_invalid_mask;
> +
> + err = nla_parse(tb, NL80211_GSCAN_BUCKET_ATTR_MAX,
> nla_data(nattr),
> + nla_len(nattr),
> nl80211_gscan_bucket_policy);
> + if (err)
> + return err;
> +
> + if (!tb[NL80211_GSCAN_BUCKET_ATTR_ID] ||
> + !tb[NL80211_GSCAN_BUCKET_ATTR_PERIOD])
> + return -EINVAL;
> +
> + bucket->idx = nla_get_u32(tb[NL80211_GSCAN_BUCKET_ATTR_ID]);
> + if (tb[NL80211_GSCAN_BUCKET_ATTR_BAND]) {
> + band_select =
> nla_get_u8(tb[NL80211_GSCAN_BUCKET_ATTR_BAND]);
> +
> + /* only makes sense if a band is selected */
> + if (!(band_select & (NL80211_BUCKET_BAND_2GHZ |
> NL80211_BUCKET_BAND_5GHZ)))
> + return -EINVAL;
> + } else if (!tb[NL80211_GSCAN_BUCKET_ATTR_CHANNELS]) {
> + return -EINVAL;
> + }
> +
> + dfs_invalid_mask = NL80211_BUCKET_BAND_5GHZ |
> NL80211_BUCKET_BAND_NODFS |
> + NL80211_BUCKET_BAND_DFS_ONLY;
> + if ((band_select & dfs_invalid_mask) == dfs_invalid_mask)
> + return -EINVAL;
> +
> + bucket->band = band_select;
> + bucket->period =
> nla_get_u16(tb[NL80211_GSCAN_BUCKET_ATTR_PERIOD]);
> +
> + if (tb[NL80211_GSCAN_BUCKET_ATTR_REPORT])
> + bucket->report_events =
> nla_get_u8(tb[NL80211_GSCAN_BUCKET_ATTR_REPORT]);
> +
> + if (tb[NL80211_GSCAN_BUCKET_ATTR_MAX_PERIOD])
> + bucket->max_period =
> nla_get_u16(tb[NL80211_GSCAN_BUCKET_ATTR_MAX_PERIOD]);
> +
> + if (bucket->max_period) {
> + if (bucket->max_period < bucket->period)
> + return -EINVAL;
> + /* additional attributes required for backoff bucket
> */
> + if (bucket->max_period > bucket->period) {
> + if (!tb[NL80211_GSCAN_BUCKET_ATTR_EXPONENT]
> ||
> + !tb[NL80211_GSCAN_BUCKET_ATTR_STEPS])
> + return -EINVAL;
> +
> + bucket->exponent =
> nla_get_u8(tb[NL80211_GSCAN_BUCKET_ATTR_EXPONENT]);
> + bucket->step_count =
> nla_get_u8(tb[NL80211_GSCAN_BUCKET_ATTR_STEPS]);
> + }
> + }
> +
> + /* ignore channels if band is specified */
> + if (band_select)
> + return 0;
> +
> + nla_for_each_nested(chan,
> tb[NL80211_GSCAN_BUCKET_ATTR_CHANNELS], rem) {
> + num_chans++;
> + }
> + if (num_chans > 16)
> + return -EINVAL;
> +
> + bucket->n_channels = num_chans;
> + if (!num_chans)
> + return 0;
> +
> + bucket->channels = channels;
> + ch = &bucket->channels[0];
> + nla_for_each_nested(chan,
> tb[NL80211_GSCAN_BUCKET_ATTR_CHANNELS], rem) {
> + err = nl80211_parse_gscan_channel(rdev, chan, ch);
> + if (err) {
> + return err;
> + }
> + ch++;
> + }
> +
> + return 0;
> +}
> +
> +static struct cfg80211_gscan_request *
> +nl80211_alloc_gscan_request(struct cfg80211_registered_device *rdev,
> + struct nlattr *buckets_attr)
> +{
> + struct cfg80211_gscan_request *req;
> + struct cfg80211_gscan_bucket *b;
> + struct cfg80211_gscan_channel *ch;
> + int n_buckets, n_channels;
> + struct nlattr *attr, *bucket, *channel;
> + int rem, rem_b, rem_c;
> + size_t reqsize;
> +
> + if (!buckets_attr)
> + return ERR_PTR(-EINVAL);
> +
> + n_buckets = 0;
> + n_channels = 0;
> + nla_for_each_nested(bucket, buckets_attr, rem) {
> + n_buckets++;
> + if (n_buckets > rdev->wiphy.gscan->max_scan_buckets)
> + return ERR_PTR(-EINVAL);
> +
> + nla_for_each_nested(attr, bucket, rem_b) {
> + if (nla_type(attr) ==
> NL80211_GSCAN_BUCKET_ATTR_CHANNELS) {
> + nla_for_each_nested(channel, attr,
> rem_c)
> + n_channels++;
> + }
> + }
> + }
> +
> + reqsize = sizeof(*req) +
> + sizeof(*b) * n_buckets +
> + sizeof(*ch) * n_channels;
> +
> + req = kzalloc(reqsize, GFP_KERNEL);
> + if (!req)
> + return ERR_PTR(-ENOMEM);
> +
> + req->n_buckets = n_buckets;
> + return req;
> +}
> +
> +static int nl80211_parse_gscan_params(struct
> cfg80211_registered_device *rdev,
> + struct nlattr *attrs[],
> + struct cfg80211_gscan_request
> **request)
> +{
> + struct cfg80211_gscan_request *req;
> + struct nlattr *tb[NL80211_GSCAN_ATTR_MAX + 1];
> + struct nlattr *bucket;
> + struct cfg80211_gscan_bucket *b;
> + struct cfg80211_gscan_channel *ch;
> + int err, rem, i;
> + u32 bucket_map;
> +
> + if (!attrs[NL80211_ATTR_GSCAN_PARAMS])
> + return -EINVAL;
> +
> + err = nla_parse(tb, NL80211_GSCAN_ATTR_MAX,
> + nla_data(attrs[NL80211_ATTR_GSCAN_PARAMS]),
> + nla_len(attrs[NL80211_ATTR_GSCAN_PARAMS]),
> + nl80211_gscan_policy);
> + if (err)
> + return err;
> +
> + req = nl80211_alloc_gscan_request(rdev,
> tb[NL80211_GSCAN_ATTR_BUCKETS]);
> + if (IS_ERR(req))
> + return PTR_ERR(req);
> +
> + if (tb[NL80211_GSCAN_ATTR_MAX_AP_PER_SCAN])
> + req->max_ap_per_scan =
> nla_get_u8(tb[NL80211_GSCAN_ATTR_MAX_AP_PER_SCAN]);
> + if (tb[NL80211_GSCAN_ATTR_REPORT_PERC])
> + req->report_threshold_percent =
> nla_get_u8(tb[NL80211_GSCAN_ATTR_REPORT_PERC]);
> + if (tb[NL80211_GSCAN_ATTR_REPORT_SCANS])
> + req->report_threshold_num_scans =
> nla_get_u8(tb[NL80211_GSCAN_ATTR_REPORT_SCANS]);
> +
> + b = &req->buckets[0];
> + ch = (struct cfg80211_gscan_channel *)(&req->buckets[req-
> >n_buckets]);
> + nla_for_each_nested(bucket, tb[NL80211_GSCAN_ATTR_BUCKETS],
> rem) {
> + err = nl80211_parse_gscan_bucket(rdev, bucket, b,
> ch);
> + if (err)
> + goto free_req;
> + ch += b->n_channels;
> + b++;
> + }
> + bucket_map = 0;
> + for (i = 0; i < req->n_buckets; i++) {
> + if (BIT(req->buckets[i].idx) & bucket_map) {
> + err = -EINVAL;
> + goto free_req;
> + }
> + bucket_map |= BIT(req->buckets[i].idx);
> +
> + if (req->base_period)
> + req->base_period = gcd(req-
> >buckets[i].period,
> + req->base_period);
> + else
> + req->base_period = req->buckets[i].period;
> + }
> + *request = req;
> + return 0;
> +
> +free_req:
> + kfree(req);
> + return err;
> +}
> +
> +static int nl80211_start_gscan(struct sk_buff *skb, struct genl_info
> *info)
> +{
> + struct cfg80211_gscan_request *request;
> + struct cfg80211_registered_device *rdev = info->user_ptr[0];
> + struct net_device *dev = info->user_ptr[1];
> + struct wireless_dev *wdev = dev->ieee80211_ptr;
> + int err;
> +
> + if (!rdev->wiphy.gscan ||
> + !rdev->ops->start_gscan)
> + return -EOPNOTSUPP;
> +
> + if (rdev->gscan_req)
> + return -EINPROGRESS;
> +
> + err = nl80211_parse_gscan_params(rdev, info->attrs,
> &request);
> + if (err)
> + return err;
> +
> + if (info->attrs[NL80211_ATTR_MAC])
> + memcpy(request->mac, nla_data(info-
> >attrs[NL80211_ATTR_MAC]),
> + ETH_ALEN);
> + if (info->attrs[NL80211_ATTR_MAC_MASK])
> + memcpy(request->mac_mask,
> + nla_data(info->attrs[NL80211_ATTR_MAC_MASK]),
> ETH_ALEN);
> + if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
> + request->flags = nla_get_u32(info-
> >attrs[NL80211_ATTR_SCAN_FLAGS]);
> + if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR
> &&
> + (!info->attrs[NL80211_ATTR_MAC] ||
> + !info->attrs[NL80211_ATTR_MAC_MASK])) {
> + kfree(request);
> + return -EINVAL;
> + }
> + }
> +
> + wdev_lock(wdev);
> + err = rdev_start_gscan(rdev, dev, request);
> + wdev_unlock(wdev);
> + if (err) {
> + kfree(request);
> + return err;
> + }
> +
> + request->scan_start = jiffies;
> + request->dev = dev;
> + if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
> + request->owner_nlportid = info->snd_portid;
> +
> + rcu_assign_pointer(rdev->gscan_req, request);
> +
> + nl80211_send_scan_event(rdev, dev, NL80211_CMD_START_GSCAN);
> + return 0;
> +}
> +
> +static int nl80211_stop_gscan(struct sk_buff *skb, struct genl_info
> *info)
> +{
> + struct cfg80211_registered_device *rdev = info->user_ptr[0];
> +
> + if (!rdev->wiphy.gscan ||
> + !rdev->ops->stop_gscan)
> + return -EOPNOTSUPP;
> +
> + return __cfg80211_stop_gscan(rdev, false);
> +}
> +
> #define NL80211_FLAG_NEED_WIPHY 0x01
> #define NL80211_FLAG_NEED_NETDEV 0x02
> #define NL80211_FLAG_NEED_RTNL 0x04
> @@ -12735,6 +13053,22 @@ static void nl80211_post_doit(const struct
> genl_ops *ops, struct sk_buff *skb,
> .internal_flags = NL80211_FLAG_NEED_NETDEV |
> NL80211_FLAG_NEED_RTNL,
> },
> + {
> + .cmd = NL80211_CMD_START_GSCAN,
> + .doit = nl80211_start_gscan,
> + .policy = nl80211_policy,
> + .flags = GENL_UNS_ADMIN_PERM,
> + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> + NL80211_FLAG_NEED_RTNL,
> + },
> + {
> + .cmd = NL80211_CMD_STOP_GSCAN,
> + .doit = nl80211_stop_gscan,
> + .policy = nl80211_policy,
> + .flags = GENL_UNS_ADMIN_PERM,
> + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> + NL80211_FLAG_NEED_RTNL,
> + },
> };
>
> static struct genl_family nl80211_fam __ro_after_init = {
> @@ -14540,12 +14874,18 @@ static int nl80211_netlink_notify(struct
> notifier_block * nb,
> list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
> bool schedule_destroy_work = false;
> bool schedule_scan_stop = false;
> + bool schedule_gscan_stop = false;
> struct cfg80211_sched_scan_request *sched_scan_req =
> rcu_dereference(rdev->sched_scan_req);
> + struct cfg80211_gscan_request *gscan_req =
> + rcu_dereference(rdev->gscan_req);
>
> if (sched_scan_req && notify->portid &&
> sched_scan_req->owner_nlportid == notify-
> >portid)
> schedule_scan_stop = true;
> + if (gscan_req && notify->portid &&
> + gscan_req->owner_nlportid == notify->portid)
> + schedule_gscan_stop = true;
>
> list_for_each_entry_rcu(wdev, &rdev-
> >wiphy.wdev_list, list) {
> cfg80211_mlme_unregister_socket(wdev,
> notify->portid);
> @@ -14576,12 +14916,18 @@ static int nl80211_netlink_notify(struct
> notifier_block * nb,
> spin_unlock(&rdev-
> >destroy_list_lock);
> schedule_work(&rdev->destroy_work);
> }
> - } else if (schedule_scan_stop) {
> - sched_scan_req->owner_nlportid = 0;
> + } else {
> + if (schedule_scan_stop) {
> + sched_scan_req->owner_nlportid = 0;
>
> - if (rdev->ops->sched_scan_stop &&
> - rdev->wiphy.flags &
> WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
> - schedule_work(&rdev-
> >sched_scan_stop_wk);
> + if (rdev->ops->sched_scan_stop &&
> + rdev->wiphy.flags &
> WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
> + schedule_work(&rdev-
> >sched_scan_stop_wk);
> + }
> + if (schedule_gscan_stop) {
> + gscan_req->owner_nlportid = 0;
> + schedule_work(&rdev->gscan_stop_wk);
> + }
> }
> }
>
> diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
> index 2f42507..196e6a7 100644
> --- a/net/wireless/rdev-ops.h
> +++ b/net/wireless/rdev-ops.h
> @@ -1153,4 +1153,29 @@ static inline int rdev_set_qos_map(struct
> cfg80211_registered_device *rdev,
> trace_rdev_return_int(&rdev->wiphy, ret);
> return ret;
> }
> +
> +static inline int
> +rdev_start_gscan(struct cfg80211_registered_device *rdev,
> + struct net_device *dev,
> + struct cfg80211_gscan_request *request)
> +{
> + int ret;
> +
> + trace_rdev_start_gscan(&rdev->wiphy, dev);
> + ret = rdev->ops->start_gscan(&rdev->wiphy, dev, request);
> + trace_rdev_return_int(&rdev->wiphy, ret);
> + return ret;
> +}
> +
> +static inline int
> +rdev_stop_gscan(struct cfg80211_registered_device *rdev,
> + struct net_device *dev)
> +{
> + int ret;
> +
> + trace_rdev_stop_gscan(&rdev->wiphy, dev);
> + ret = rdev->ops->stop_gscan(&rdev->wiphy, dev);
> + trace_rdev_return_int(&rdev->wiphy, ret);
> + return ret;
> +}
> #endif /* __CFG80211_RDEV_OPS */
> diff --git a/net/wireless/scan.c b/net/wireless/scan.c
> index 174076b..8c141c2 100644
> --- a/net/wireless/scan.c
> +++ b/net/wireless/scan.c
> @@ -386,6 +386,34 @@ int __cfg80211_stop_sched_scan(struct
> cfg80211_registered_device *rdev,
> return 0;
> }
>
> +int __cfg80211_stop_gscan(struct cfg80211_registered_device *rdev,
> + bool driver_initiated)
> +{
> + struct cfg80211_gscan_request *gscan_req;
> + struct net_device *dev;
> +
> + ASSERT_RTNL();
> +
> + if (!rdev->gscan_req)
> + return -ENOENT;
> +
> + gscan_req = rtnl_dereference(rdev->gscan_req);
> + dev = gscan_req->dev;
> +
> + if (!driver_initiated) {
> + int err = rdev_stop_gscan(rdev, dev);
> + if (err)
> + return err;
> + }
> +
> + nl80211_send_scan_event(rdev, dev,
> NL80211_CMD_GSCAN_STOPPED);
> +
> + RCU_INIT_POINTER(rdev->gscan_req, NULL);
> + kfree_rcu(gscan_req, rcu_head);
> +
> + return 0;
> +}
> +
> void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
> unsigned long age_secs)
> {
> diff --git a/net/wireless/trace.h b/net/wireless/trace.h
> index ea1b47e..1d0fde9 100644
> --- a/net/wireless/trace.h
> +++ b/net/wireless/trace.h
> @@ -3067,6 +3067,15 @@
> WIPHY_PR_ARG, NETDEV_PR_ARG,
> BOOL_TO_STR(__entry->enabled))
> );
> +
> +DEFINE_EVENT(wiphy_netdev_evt, rdev_start_gscan,
> + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
> + TP_ARGS(wiphy, netdev)
> +);
> +DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_gscan,
> + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
> + TP_ARGS(wiphy, netdev)
> +);
> #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
>
> #undef TRACE_INCLUDE_PATH
> --
> 1.9.1
>
^ permalink raw reply
* [PATCH] ath9k: unlock rcu read when returning early
From: Tobias Klausmann @ 2016-12-12 18:50 UTC (permalink / raw)
To: kvalo, helgaas, linux-kernel, linux-pci, marc.zyngier,
Janusz.Dziedzic, rmanohar, ath9k-devel, linux-wireless, rmanohar,
bharat.kumar.gogada
Cc: Tobias Klausmann
In-Reply-To: <1cFMHb-00BdWd-Ks>
Starting with ath9k: use ieee80211_tx_status_noskb where possible
[d94a461d7a7df68991fb9663531173f60ef89c68] the driver uses rcu_read_lock() &&
rcu_read_unlock() yet on returning early in ath_tx_edma_tasklet() the unlock is
missing leading to stalls and suspicious RCU usage:
===============================
[ INFO: suspicious RCU usage. ]
4.9.0-rc8 #11 Not tainted
-------------------------------
kernel/rcu/tree.c:705 Illegal idle entry in RCU read-side critical section.!
other info that might help us debug this:
RCU used illegally from idle CPU!
rcu_scheduler_active = 1, debug_locks = 0
RCU used illegally from extended quiescent state!
1 lock held by swapper/7/0:
#0:
(
rcu_read_lock
){......}
, at:
[<ffffffffa06ed110>] ath_tx_edma_tasklet+0x0/0x450 [ath9k]
stack backtrace:
CPU: 7 PID: 0 Comm: swapper/7 Not tainted 4.9.0-rc8 #11
Hardware name: Acer Aspire V3-571G/VA50_HC_CR, BIOS V2.21 12/16/2013
ffff88025efc3f38 ffffffff8132b1e5 ffff88017ede4540 0000000000000001
ffff88025efc3f68 ffffffff810a25f7 ffff88025efcee60 ffff88017edebdd8
ffff88025eeb5400 0000000000000091 ffff88025efc3f88 ffffffff810c3cd4
Call Trace:
<IRQ>
[<ffffffff8132b1e5>] dump_stack+0x68/0x93
[<ffffffff810a25f7>] lockdep_rcu_suspicious+0xd7/0x110
[<ffffffff810c3cd4>] rcu_eqs_enter_common.constprop.85+0x154/0x200
[<ffffffff810c5a54>] rcu_irq_exit+0x44/0xa0
[<ffffffff81058631>] irq_exit+0x61/0xd0
[<ffffffff81018d25>] do_IRQ+0x65/0x110
[<ffffffff81672189>] common_interrupt+0x89/0x89
<EOI>
[<ffffffff814ffe11>] ? cpuidle_enter_state+0x151/0x200
[<ffffffff814ffee2>] cpuidle_enter+0x12/0x20
[<ffffffff8109a6ae>] call_cpuidle+0x1e/0x40
[<ffffffff8109a8f6>] cpu_startup_entry+0x146/0x220
[<ffffffff810336f8>] start_secondary+0x148/0x170
Signed-off-by: Tobias Klausmann <tobias.johannes.klausmann@mni.thm.de>
---
drivers/net/wireless/ath/ath9k/xmit.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 52bfbb988611..857d5ae09a1d 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2787,6 +2787,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
fifo_list = &txq->txq_fifo[txq->txq_tailidx];
if (list_empty(fifo_list)) {
ath_txq_unlock(sc, txq);
+ rcu_read_unlock();
return;
}
--
2.11.0
^ permalink raw reply related
* Re: [RFC V3 03/11] nl80211: add support for gscan
From: Arend Van Spriel @ 2016-12-12 20:01 UTC (permalink / raw)
To: Dan Williams, Johannes Berg; +Cc: linux-wireless
In-Reply-To: <1481564592.6887.2.camel@redhat.com>
On 12-12-2016 18:43, Dan Williams wrote:
>> +
>> +/**
>> + * enum nl80211_bucket_band - GScan bucket band selection.
> Quite possibly this was already covered and somebody requested you to
> change this to the current name. If that's the case, ignore this.
Nope. You are the first ;-)
> But shouldn't this enum and bucket_event_report include "gscan" in
> their name, like the other gscan specific stuff does? Are these going
> to get used for something else too, and will that thing make sense with
> the word "bucket"? Just "nl80211_bucket_band" is pretty generic.
I figured the term bucket was making it gscan specific, but you are
right. Will change it.
Thanks,
Arend
^ permalink raw reply
* Re: [PATCH v2 0/7] ath9k: EEPROM swapping improvements
From: Martin Blumenstingl @ 2016-12-12 20:05 UTC (permalink / raw)
To: Valo, Kalle
Cc: ath9k-devel, linux-wireless@vger.kernel.org,
ath9k-devel@lists.ath9k.org, devicetree@vger.kernel.org,
arnd@arndb.de, chunkeey@googlemail.com, nbd@nbd.name
In-Reply-To: <871sxza9al.fsf@kamboji.qca.qualcomm.com>
Hello Kalle,
On Fri, Nov 25, 2016 at 4:06 PM, Valo, Kalle <kvalo@qca.qualcomm.com> wrote:
> Kalle Valo <kvalo@codeaurora.org> writes:
>
>> Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes:
>>
>>> There are two types of swapping the EEPROM data in the ath9k driver.
>>> Before this series one type of swapping could not be used without the
>>> other.
>>>
>>> The first type of swapping looks at the "magic bytes" at the start of
>>> the EEPROM data and performs swab16 on the EEPROM contents if needed.
>>> The second type of swapping is EEPROM format specific and swaps
>>> specific fields within the EEPROM itself (swab16, swab32 - depends on
>>> the EEPROM format).
>>>
>>> With this series the second part now looks at the EEPMISC register
>>> inside the EEPROM, which uses a bit to indicate if the EEPROM data
>>> is Big Endian (this is also done by the FreeBSD kernel).
>>> This has a nice advantage: currently there are some out-of-tree hacks
>>> (in OpenWrt and LEDE) where the EEPROM has a Big Endian header on a
>>> Big Endian system (= no swab16 is performed) but the EEPROM itself
>>> indicates that it's data is Little Endian. Until now the out-of-tree
>>> code simply did a swab16 before passing the data to ath9k, so ath9k
>>> first did the swab16 - this also enabled the format specific swapping.
>>> These out-of-tree hacks are still working with the new logic, but it
>>> is recommended to remove them. This implementation is based on a
>>> discussion with Arnd Bergmann who raised concerns about the
>>> robustness and portability of the swapping logic in the original OF
>>> support patch review, see [0].
>>>
>>> After a second round of patches (= v1 of this series) neither Arnd
>>> Bergmann nor I were really happy with the complexity of the EEPROM
>>> swapping logic. Based on a discussion (see [1] and [2]) we decided
>>> that ath9k should use a defined format (specifying the endianness
>>> of the data - I went with __le16 and __le32) when accessing the
>>> EEPROM fields. A benefit of this is that we enable the EEPMISC based
>>> swapping logic by default, just like the FreeBSD driver, see [3]. On
>>> the devices which I have tested (see below) ath9k now works without
>>> having to specify the "endian_check" field in ath9k_platform_data (or
>>> a similar logic which could provide this via devicetree) as ath9k now
>>> detects the endianness automatically. Only EEPROMs which are mangled
>>> by some out-of-tree code still need the endian_check flag (or one can
>>> simply remove that mangling from the out-of-tree code).
>>>
>>> Testing:
>>> - tested by myself on AR9287 with Big Endian EEPROM
>>> - tested by myself on AR9227 with Little Endian EEPROM
>>> - tested by myself on AR9381 (using the ar9003_eeprom implementation,
>>> which did not suffer from this whole problem)
>>> - how do we proceed with testing? maybe we could keep this in a
>>> feature-branch and add these patches to LEDE once we have an ACK to
>>> get more people to test this
>>>
>>> This series depends on my other series (v7):
>>> "add devicetree support to ath9k" - see [4]
>>
>> I think this looks pretty good. If there's a bug somewhere it should be
>> quite easy to fix so I'm not that worried and would be willing to take
>> these as soon as I have applied the dependency series. IIRC your
>> devicetree patches will have at least one more review round so that will
>> take some time still. In the meantime it would be great if LEDE folks
>> could take a look at these and comment (or test).
>
> So are everyone happy with this? I haven't seen any comments. If I don't
> here anything I'm planning to take these, most likely for 4.11.
the patches have been in LEDE for almost two weeks now and I did not
see any reports of ath9k breakage (footnote below).
It seems that there are a few devices out there where the whole EEPROM
is swab16'ed which switches the position of the 1-byte fields
opCapFlags and eepMisc.
those still work fine with the new code, however I had a second patch
in LEDE [0] which results in ath9k_platform_data.endian_check NOT
being set anymore.
that endian_check flag was used before to swab16 the whole EEPROM, to
correct the position of the 1-byte fields again.
Currently we are fixing this in the firmware hotplug script: [1]
This is definitely not a blocker for this series though (if we want to
have a devicetree replacement for "ath9k_platform_data.endian_check"
then I'd work on that within a separate series, but I somewhat
consider these EEPROMs as "broken" so fixing them in
userspace/firmware hotplug script is fine for me)
Regards,
Martin
[0] https://git.lede-project.org/?p=source.git;a=commitdiff;h=a20616863d32d91163043b6657a63c836bd9c5ba
[1] https://git.lede-project.org/?p=source.git;a=commitdiff;h=afa37092663d00aa0abf8c61943d9a1b5558b144
^ permalink raw reply
* [PATCH] orinoco: Use shash instead of ahash for MIC calculations
From: Andy Lutomirski @ 2016-12-12 20:55 UTC (permalink / raw)
To: linux-kernel, linux-usb, linux-wireless
Cc: Eric Biggers, linux-crypto, Herbert Xu, Stephan Mueller,
Andy Lutomirski
In-Reply-To: <8c273c9c41f51b34bb3115086f1d776895580637.1481575835.git.luto@kernel.org>
Eric Biggers pointed out that the orinoco driver pointed scatterlists
at the stack.
Fix it by switching from ahash to shash. The result should be
simpler, faster, and more correct.
Cc: stable@vger.kernel.org # 4.9 only
Reported-by: Eric Biggers <ebiggers3@gmail.com>
Signed-off-by: Andy Lutomirski <luto@kernel.org>
---
Compile-tested only.
drivers/net/wireless/intersil/orinoco/mic.c | 44 +++++++++++++++----------
drivers/net/wireless/intersil/orinoco/mic.h | 3 +-
drivers/net/wireless/intersil/orinoco/orinoco.h | 4 +--
3 files changed, 30 insertions(+), 21 deletions(-)
diff --git a/drivers/net/wireless/intersil/orinoco/mic.c b/drivers/net/wireless/intersil/orinoco/mic.c
index bc7397d709d3..08bc7822f820 100644
--- a/drivers/net/wireless/intersil/orinoco/mic.c
+++ b/drivers/net/wireless/intersil/orinoco/mic.c
@@ -16,7 +16,7 @@
/********************************************************************/
int orinoco_mic_init(struct orinoco_private *priv)
{
- priv->tx_tfm_mic = crypto_alloc_ahash("michael_mic", 0,
+ priv->tx_tfm_mic = crypto_alloc_shash("michael_mic", 0,
CRYPTO_ALG_ASYNC);
if (IS_ERR(priv->tx_tfm_mic)) {
printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
@@ -25,7 +25,7 @@ int orinoco_mic_init(struct orinoco_private *priv)
return -ENOMEM;
}
- priv->rx_tfm_mic = crypto_alloc_ahash("michael_mic", 0,
+ priv->rx_tfm_mic = crypto_alloc_shash("michael_mic", 0,
CRYPTO_ALG_ASYNC);
if (IS_ERR(priv->rx_tfm_mic)) {
printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
@@ -40,17 +40,16 @@ int orinoco_mic_init(struct orinoco_private *priv)
void orinoco_mic_free(struct orinoco_private *priv)
{
if (priv->tx_tfm_mic)
- crypto_free_ahash(priv->tx_tfm_mic);
+ crypto_free_shash(priv->tx_tfm_mic);
if (priv->rx_tfm_mic)
- crypto_free_ahash(priv->rx_tfm_mic);
+ crypto_free_shash(priv->rx_tfm_mic);
}
-int orinoco_mic(struct crypto_ahash *tfm_michael, u8 *key,
+int orinoco_mic(struct crypto_shash *tfm_michael, u8 *key,
u8 *da, u8 *sa, u8 priority,
u8 *data, size_t data_len, u8 *mic)
{
- AHASH_REQUEST_ON_STACK(req, tfm_michael);
- struct scatterlist sg[2];
+ SHASH_DESC_ON_STACK(desc, tfm_michael);
u8 hdr[ETH_HLEN + 2]; /* size of header + padding */
int err;
@@ -67,18 +66,27 @@ int orinoco_mic(struct crypto_ahash *tfm_michael, u8 *key,
hdr[ETH_ALEN * 2 + 2] = 0;
hdr[ETH_ALEN * 2 + 3] = 0;
- /* Use scatter gather to MIC header and data in one go */
- sg_init_table(sg, 2);
- sg_set_buf(&sg[0], hdr, sizeof(hdr));
- sg_set_buf(&sg[1], data, data_len);
+ desc->tfm = tfm_michael;
+ desc->flags = 0;
- if (crypto_ahash_setkey(tfm_michael, key, MIC_KEYLEN))
- return -1;
+ err = crypto_shash_setkey(tfm_michael, key, MIC_KEYLEN);
+ if (err)
+ return err;
+
+ err = crypto_shash_init(desc);
+ if (err)
+ return err;
+
+ err = crypto_shash_update(desc, hdr, sizeof(hdr));
+ if (err)
+ return err;
+
+ err = crypto_shash_update(desc, data, data_len);
+ if (err)
+ return err;
+
+ err = crypto_shash_final(desc, mic);
+ shash_desc_zero(desc);
- ahash_request_set_tfm(req, tfm_michael);
- ahash_request_set_callback(req, 0, NULL, NULL);
- ahash_request_set_crypt(req, sg, mic, data_len + sizeof(hdr));
- err = crypto_ahash_digest(req);
- ahash_request_zero(req);
return err;
}
diff --git a/drivers/net/wireless/intersil/orinoco/mic.h b/drivers/net/wireless/intersil/orinoco/mic.h
index ce731d05cc98..e8724e889219 100644
--- a/drivers/net/wireless/intersil/orinoco/mic.h
+++ b/drivers/net/wireless/intersil/orinoco/mic.h
@@ -6,6 +6,7 @@
#define _ORINOCO_MIC_H_
#include <linux/types.h>
+#include <crypto/hash.h>
#define MICHAEL_MIC_LEN 8
@@ -15,7 +16,7 @@ struct crypto_ahash;
int orinoco_mic_init(struct orinoco_private *priv);
void orinoco_mic_free(struct orinoco_private *priv);
-int orinoco_mic(struct crypto_ahash *tfm_michael, u8 *key,
+int orinoco_mic(struct crypto_shash *tfm_michael, u8 *key,
u8 *da, u8 *sa, u8 priority,
u8 *data, size_t data_len, u8 *mic);
diff --git a/drivers/net/wireless/intersil/orinoco/orinoco.h b/drivers/net/wireless/intersil/orinoco/orinoco.h
index 2f0c84b1c440..5fa1c3e3713f 100644
--- a/drivers/net/wireless/intersil/orinoco/orinoco.h
+++ b/drivers/net/wireless/intersil/orinoco/orinoco.h
@@ -152,8 +152,8 @@ struct orinoco_private {
u8 *wpa_ie;
int wpa_ie_len;
- struct crypto_ahash *rx_tfm_mic;
- struct crypto_ahash *tx_tfm_mic;
+ struct crypto_shash *rx_tfm_mic;
+ struct crypto_shash *tx_tfm_mic;
unsigned int wpa_enabled:1;
unsigned int tkip_cm_active:1;
--
2.9.3
^ permalink raw reply related
* Re: [PATCH] orinoco: Use shash instead of ahash for MIC calculations
From: Eric Biggers @ 2016-12-13 7:54 UTC (permalink / raw)
To: Andy Lutomirski
Cc: linux-kernel, linux-usb, linux-wireless, linux-crypto, Herbert Xu,
Stephan Mueller
In-Reply-To: <8818c45b9ec6a04d85fabf9bb437cf119fd23659.1481575835.git.luto@kernel.org>
On Mon, Dec 12, 2016 at 12:55:55PM -0800, Andy Lutomirski wrote:
> +int orinoco_mic(struct crypto_shash *tfm_michael, u8 *key,
> u8 *da, u8 *sa, u8 priority,
> u8 *data, size_t data_len, u8 *mic)
> {
> - AHASH_REQUEST_ON_STACK(req, tfm_michael);
> - struct scatterlist sg[2];
> + SHASH_DESC_ON_STACK(desc, tfm_michael);
> u8 hdr[ETH_HLEN + 2]; /* size of header + padding */
> int err;
>
> @@ -67,18 +66,27 @@ int orinoco_mic(struct crypto_ahash *tfm_michael, u8 *key,
> hdr[ETH_ALEN * 2 + 2] = 0;
> hdr[ETH_ALEN * 2 + 3] = 0;
>
> - /* Use scatter gather to MIC header and data in one go */
> - sg_init_table(sg, 2);
> - sg_set_buf(&sg[0], hdr, sizeof(hdr));
> - sg_set_buf(&sg[1], data, data_len);
> + desc->tfm = tfm_michael;
> + desc->flags = 0;
>
> - if (crypto_ahash_setkey(tfm_michael, key, MIC_KEYLEN))
> - return -1;
> + err = crypto_shash_setkey(tfm_michael, key, MIC_KEYLEN);
> + if (err)
> + return err;
> +
> + err = crypto_shash_init(desc);
> + if (err)
> + return err;
> +
> + err = crypto_shash_update(desc, hdr, sizeof(hdr));
> + if (err)
> + return err;
> +
> + err = crypto_shash_update(desc, data, data_len);
> + if (err)
> + return err;
> +
> + err = crypto_shash_final(desc, mic);
> + shash_desc_zero(desc);
>
> - ahash_request_set_tfm(req, tfm_michael);
> - ahash_request_set_callback(req, 0, NULL, NULL);
> - ahash_request_set_crypt(req, sg, mic, data_len + sizeof(hdr));
> - err = crypto_ahash_digest(req);
> - ahash_request_zero(req);
> return err;
It's probably a good idea to always do shash_desc_zero(), even when something
above it fails. Otherwise this looks fine. Thanks for sending these patches!
Eric
^ permalink raw reply
* [PATCH] rfkill: simplify rfkill_set_hw_state() slightly
From: Johannes Berg @ 2016-12-13 8:39 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Simplify the two conditions gating the schedule_work() into
a single one and get rid of the additional exit point from
the function in doing so.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/rfkill/core.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index 884027f62783..184bb711a06d 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -478,10 +478,7 @@ bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
rfkill_led_trigger_event(rfkill);
- if (!rfkill->registered)
- return ret;
-
- if (prev != blocked)
+ if (rfkill->registered && prev != blocked)
schedule_work(&rfkill->uevent_work);
return ret;
--
2.9.3
^ permalink raw reply related
* [PATCH] mac80211: don't call drv_set_default_unicast_key() for VLANs
From: Johannes Berg @ 2016-12-13 8:41 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Since drivers know nothing about AP_VLAN interfaces, trying to
call drv_set_default_unicast_key() just results in a warning
and no call to the driver. Avoid the warning by not calling the
driver for this on AP_VLAN interfaces.
This means that drivers that somehow need this call for AP mode
will fail to work properly in the presence of VLAN interfaces,
but the current drivers don't seem to use it, and mac80211 will
select and indicate the key - so drivers should be OK now.
Reported-by: Jouni Malinen <j@w1.fi>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/mac80211/key.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index edd6f2945f69..a98fc2b5e0dc 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -265,7 +265,8 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
if (uni) {
rcu_assign_pointer(sdata->default_unicast_key, key);
ieee80211_check_fast_xmit_iface(sdata);
- drv_set_default_unicast_key(sdata->local, sdata, idx);
+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
+ drv_set_default_unicast_key(sdata->local, sdata, idx);
}
if (multi)
--
2.9.3
^ permalink raw reply related
* Re: [PATCH] ath9k: unlock rcu read when returning early
From: Kalle Valo @ 2016-12-13 9:59 UTC (permalink / raw)
To: Tobias Klausmann
Cc: helgaas, linux-kernel, linux-pci, marc.zyngier, Janusz.Dziedzic,
rmanohar, ath9k-devel, linux-wireless, rmanohar,
bharat.kumar.gogada, Felix Fietkau
In-Reply-To: <20161212185001.3857-1-tobias.johannes.klausmann@mni.thm.de>
Tobias Klausmann <tobias.johannes.klausmann@mni.thm.de> writes:
> Starting with ath9k: use ieee80211_tx_status_noskb where possible
> [d94a461d7a7df68991fb9663531173f60ef89c68]
The correct format to reference a commit in the commit log is:
Starting with commit d94a461d7a7d ("ath9k: use ieee80211_tx_status_noskb
where possible") the...
> the driver uses rcu_read_lock() && rcu_read_unlock() yet on returning
> early in ath_tx_edma_tasklet() the unlock is missing leading to stalls
> and suspicious RCU usage:
>
> ===============================
> [ INFO: suspicious RCU usage. ]
> 4.9.0-rc8 #11 Not tainted
> -------------------------------
> kernel/rcu/tree.c:705 Illegal idle entry in RCU read-side critical section.!
>
> other info that might help us debug this:
>
> RCU used illegally from idle CPU!
> rcu_scheduler_active = 1, debug_locks = 0
> RCU used illegally from extended quiescent state!
> 1 lock held by swapper/7/0:
> #0:
> (
> rcu_read_lock
> ){......}
> , at:
> [<ffffffffa06ed110>] ath_tx_edma_tasklet+0x0/0x450 [ath9k]
>
> stack backtrace:
> CPU: 7 PID: 0 Comm: swapper/7 Not tainted 4.9.0-rc8 #11
> Hardware name: Acer Aspire V3-571G/VA50_HC_CR, BIOS V2.21 12/16/2013
> ffff88025efc3f38 ffffffff8132b1e5 ffff88017ede4540 0000000000000001
> ffff88025efc3f68 ffffffff810a25f7 ffff88025efcee60 ffff88017edebdd8
> ffff88025eeb5400 0000000000000091 ffff88025efc3f88 ffffffff810c3cd4
> Call Trace:
> <IRQ>
> [<ffffffff8132b1e5>] dump_stack+0x68/0x93
> [<ffffffff810a25f7>] lockdep_rcu_suspicious+0xd7/0x110
> [<ffffffff810c3cd4>] rcu_eqs_enter_common.constprop.85+0x154/0x200
> [<ffffffff810c5a54>] rcu_irq_exit+0x44/0xa0
> [<ffffffff81058631>] irq_exit+0x61/0xd0
> [<ffffffff81018d25>] do_IRQ+0x65/0x110
> [<ffffffff81672189>] common_interrupt+0x89/0x89
> <EOI>
> [<ffffffff814ffe11>] ? cpuidle_enter_state+0x151/0x200
> [<ffffffff814ffee2>] cpuidle_enter+0x12/0x20
> [<ffffffff8109a6ae>] call_cpuidle+0x1e/0x40
> [<ffffffff8109a8f6>] cpu_startup_entry+0x146/0x220
> [<ffffffff810336f8>] start_secondary+0x148/0x170
>
> Signed-off-by: Tobias Klausmann <tobias.johannes.klausmann@mni.thm.de>
A fixes line and cc stable would be good to have:
Fixes: d94a461d7a7d ("ath9k: use ieee80211_tx_status_noskb where possible")
Cc: <stable@vger.kernel.org> # v4.9
I can add those.
I'm planning to push this to 4.10 but would prefer to see an ack from
Felix (the author of d94a461d7a7d) first. I added him to Cc.
--
Kalle Valo
^ permalink raw reply
* Re: [PATCH] ath9k: unlock rcu read when returning early
From: Felix Fietkau @ 2016-12-13 10:41 UTC (permalink / raw)
To: Tobias Klausmann, kvalo, helgaas, linux-kernel, linux-pci,
marc.zyngier, Janusz.Dziedzic, rmanohar, ath9k-devel,
linux-wireless, rmanohar, bharat.kumar.gogada
In-Reply-To: <20161212185001.3857-1-tobias.johannes.klausmann@mni.thm.de>
On 2016-12-12 19:50, Tobias Klausmann wrote:
> Starting with ath9k: use ieee80211_tx_status_noskb where possible
> [d94a461d7a7df68991fb9663531173f60ef89c68] the driver uses rcu_read_lock() &&
> rcu_read_unlock() yet on returning early in ath_tx_edma_tasklet() the unlock is
> missing leading to stalls and suspicious RCU usage:
>
> ===============================
> [ INFO: suspicious RCU usage. ]
> 4.9.0-rc8 #11 Not tainted
> -------------------------------
> kernel/rcu/tree.c:705 Illegal idle entry in RCU read-side critical section.!
>
> other info that might help us debug this:
>
> RCU used illegally from idle CPU!
> rcu_scheduler_active = 1, debug_locks = 0
> RCU used illegally from extended quiescent state!
> 1 lock held by swapper/7/0:
> #0:
> (
> rcu_read_lock
> ){......}
> , at:
> [<ffffffffa06ed110>] ath_tx_edma_tasklet+0x0/0x450 [ath9k]
>
> stack backtrace:
> CPU: 7 PID: 0 Comm: swapper/7 Not tainted 4.9.0-rc8 #11
> Hardware name: Acer Aspire V3-571G/VA50_HC_CR, BIOS V2.21 12/16/2013
> ffff88025efc3f38 ffffffff8132b1e5 ffff88017ede4540 0000000000000001
> ffff88025efc3f68 ffffffff810a25f7 ffff88025efcee60 ffff88017edebdd8
> ffff88025eeb5400 0000000000000091 ffff88025efc3f88 ffffffff810c3cd4
> Call Trace:
> <IRQ>
> [<ffffffff8132b1e5>] dump_stack+0x68/0x93
> [<ffffffff810a25f7>] lockdep_rcu_suspicious+0xd7/0x110
> [<ffffffff810c3cd4>] rcu_eqs_enter_common.constprop.85+0x154/0x200
> [<ffffffff810c5a54>] rcu_irq_exit+0x44/0xa0
> [<ffffffff81058631>] irq_exit+0x61/0xd0
> [<ffffffff81018d25>] do_IRQ+0x65/0x110
> [<ffffffff81672189>] common_interrupt+0x89/0x89
> <EOI>
> [<ffffffff814ffe11>] ? cpuidle_enter_state+0x151/0x200
> [<ffffffff814ffee2>] cpuidle_enter+0x12/0x20
> [<ffffffff8109a6ae>] call_cpuidle+0x1e/0x40
> [<ffffffff8109a8f6>] cpu_startup_entry+0x146/0x220
> [<ffffffff810336f8>] start_secondary+0x148/0x170
>
> Signed-off-by: Tobias Klausmann <tobias.johannes.klausmann@mni.thm.de>
> ---
> drivers/net/wireless/ath/ath9k/xmit.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
> index 52bfbb988611..857d5ae09a1d 100644
> --- a/drivers/net/wireless/ath/ath9k/xmit.c
> +++ b/drivers/net/wireless/ath/ath9k/xmit.c
> @@ -2787,6 +2787,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
> fifo_list = &txq->txq_fifo[txq->txq_tailidx];
> if (list_empty(fifo_list)) {
> ath_txq_unlock(sc, txq);
> + rcu_read_unlock();
Technically this is fine as well, but I'd prefer a fix where you replace
the 'return' with 'break', thus avoiding the duplication of
rcu_read_unlock()
Thanks,
- Felix
^ permalink raw reply
* Re: [PATCH] orinoco: Use shash instead of ahash for MIC calculations
From: Kalle Valo @ 2016-12-13 11:35 UTC (permalink / raw)
To: Andy Lutomirski
Cc: linux-kernel, linux-usb, linux-wireless, Eric Biggers,
linux-crypto, Herbert Xu, Stephan Mueller
In-Reply-To: <8818c45b9ec6a04d85fabf9bb437cf119fd23659.1481575835.git.luto@kernel.org>
Andy Lutomirski <luto@kernel.org> writes:
> Eric Biggers pointed out that the orinoco driver pointed scatterlists
> at the stack.
>
> Fix it by switching from ahash to shash. The result should be
> simpler, faster, and more correct.
>
> Cc: stable@vger.kernel.org # 4.9 only
> Reported-by: Eric Biggers <ebiggers3@gmail.com>
> Signed-off-by: Andy Lutomirski <luto@kernel.org>
"more correct"? Does this fix a real user visible bug or what? And why
just stable 4.9, does this maybe have something to do with
CONFIG_VMAP_STACK?
I'm just wondering should I push this to 4.10 or -next. This is a driver
for ancient hardware so I'm starting to lean for -next.
--
Kalle Valo
^ permalink raw reply
* Re: [PATCH v2 0/7] ath9k: EEPROM swapping improvements
From: Valo, Kalle @ 2016-12-13 12:03 UTC (permalink / raw)
To: Martin Blumenstingl
Cc: ath9k-devel, linux-wireless@vger.kernel.org,
ath9k-devel@lists.ath9k.org, devicetree@vger.kernel.org,
arnd@arndb.de, chunkeey@googlemail.com, nbd@nbd.name
In-Reply-To: <CAFBinCC6JWBhZwma=66fBi3_to2SaHOMNDQS23jHNhcc+RUcYQ@mail.gmail.com>
Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes:
> Hello Kalle,
>
> On Fri, Nov 25, 2016 at 4:06 PM, Valo, Kalle <kvalo@qca.qualcomm.com> wro=
te:
>> Kalle Valo <kvalo@codeaurora.org> writes:
>>
>>> Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes:
>>>
>>>> There are two types of swapping the EEPROM data in the ath9k driver.
>>>> Before this series one type of swapping could not be used without the
>>>> other.
>>>>
>>>> The first type of swapping looks at the "magic bytes" at the start of
>>>> the EEPROM data and performs swab16 on the EEPROM contents if needed.
>>>> The second type of swapping is EEPROM format specific and swaps
>>>> specific fields within the EEPROM itself (swab16, swab32 - depends on
>>>> the EEPROM format).
>>>>
>>>> With this series the second part now looks at the EEPMISC register
>>>> inside the EEPROM, which uses a bit to indicate if the EEPROM data
>>>> is Big Endian (this is also done by the FreeBSD kernel).
>>>> This has a nice advantage: currently there are some out-of-tree hacks
>>>> (in OpenWrt and LEDE) where the EEPROM has a Big Endian header on a
>>>> Big Endian system (=3D no swab16 is performed) but the EEPROM itself
>>>> indicates that it's data is Little Endian. Until now the out-of-tree
>>>> code simply did a swab16 before passing the data to ath9k, so ath9k
>>>> first did the swab16 - this also enabled the format specific swapping.
>>>> These out-of-tree hacks are still working with the new logic, but it
>>>> is recommended to remove them. This implementation is based on a
>>>> discussion with Arnd Bergmann who raised concerns about the
>>>> robustness and portability of the swapping logic in the original OF
>>>> support patch review, see [0].
>>>>
>>>> After a second round of patches (=3D v1 of this series) neither Arnd
>>>> Bergmann nor I were really happy with the complexity of the EEPROM
>>>> swapping logic. Based on a discussion (see [1] and [2]) we decided
>>>> that ath9k should use a defined format (specifying the endianness
>>>> of the data - I went with __le16 and __le32) when accessing the
>>>> EEPROM fields. A benefit of this is that we enable the EEPMISC based
>>>> swapping logic by default, just like the FreeBSD driver, see [3]. On
>>>> the devices which I have tested (see below) ath9k now works without
>>>> having to specify the "endian_check" field in ath9k_platform_data (or
>>>> a similar logic which could provide this via devicetree) as ath9k now
>>>> detects the endianness automatically. Only EEPROMs which are mangled
>>>> by some out-of-tree code still need the endian_check flag (or one can
>>>> simply remove that mangling from the out-of-tree code).
>>>>
>>>> Testing:
>>>> - tested by myself on AR9287 with Big Endian EEPROM
>>>> - tested by myself on AR9227 with Little Endian EEPROM
>>>> - tested by myself on AR9381 (using the ar9003_eeprom implementation,
>>>> which did not suffer from this whole problem)
>>>> - how do we proceed with testing? maybe we could keep this in a
>>>> feature-branch and add these patches to LEDE once we have an ACK to
>>>> get more people to test this
>>>>
>>>> This series depends on my other series (v7):
>>>> "add devicetree support to ath9k" - see [4]
>>>
>>> I think this looks pretty good. If there's a bug somewhere it should be
>>> quite easy to fix so I'm not that worried and would be willing to take
>>> these as soon as I have applied the dependency series. IIRC your
>>> devicetree patches will have at least one more review round so that wil=
l
>>> take some time still. In the meantime it would be great if LEDE folks
>>> could take a look at these and comment (or test).
>>
>> So are everyone happy with this? I haven't seen any comments. If I don't
>> here anything I'm planning to take these, most likely for 4.11.
>
> the patches have been in LEDE for almost two weeks now and I did not
> see any reports of ath9k breakage (footnote below).
>
> It seems that there are a few devices out there where the whole EEPROM
> is swab16'ed which switches the position of the 1-byte fields
> opCapFlags and eepMisc.
> those still work fine with the new code, however I had a second patch
> in LEDE [0] which results in ath9k_platform_data.endian_check NOT
> being set anymore.
> that endian_check flag was used before to swab16 the whole EEPROM, to
> correct the position of the 1-byte fields again.
> Currently we are fixing this in the firmware hotplug script: [1]
> This is definitely not a blocker for this series though (if we want to
> have a devicetree replacement for "ath9k_platform_data.endian_check"
> then I'd work on that within a separate series, but I somewhat
> consider these EEPROMs as "broken" so fixing them in
> userspace/firmware hotplug script is fine for me)
Sounds good to me, thanks for the thorough followup. I'm planning to
apply these any day.
--=20
Kalle Valo=
^ 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