@@ -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)
@@ -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;
@@ -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) \
@@ -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_ */