|
@@ -53,6 +53,11 @@
|
|
|
|
|
|
#define DEFAULT_BG_SCAN_PERIOD 60
|
|
|
|
|
|
+struct ath6kl_cfg80211_match_probe_ssid {
|
|
|
+ struct cfg80211_ssid ssid;
|
|
|
+ u8 flag;
|
|
|
+};
|
|
|
+
|
|
|
static struct ieee80211_rate ath6kl_rates[] = {
|
|
|
RATETAB_ENT(10, 0x1, 0),
|
|
|
RATETAB_ENT(20, 0x2, 0),
|
|
@@ -576,6 +581,9 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
|
|
vif->nw_type = vif->next_mode;
|
|
|
|
|
|
+ /* enable enhanced bmiss detection if applicable */
|
|
|
+ ath6kl_cfg80211_sta_bmiss_enhance(vif, true);
|
|
|
+
|
|
|
if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
|
|
|
nw_subtype = SUBTYPE_P2PCLIENT;
|
|
|
|
|
@@ -852,20 +860,6 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * Send a disconnect command to target when a disconnect event is
|
|
|
- * received with reason code other than 3 (DISCONNECT_CMD - disconnect
|
|
|
- * request from host) to make the firmware stop trying to connect even
|
|
|
- * after giving disconnect event. There will be one more disconnect
|
|
|
- * event for this disconnect command with reason code DISCONNECT_CMD
|
|
|
- * which will be notified to cfg80211.
|
|
|
- */
|
|
|
-
|
|
|
- if (reason != DISCONNECT_CMD) {
|
|
|
- ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
clear_bit(CONNECT_PEND, &vif->flags);
|
|
|
|
|
|
if (vif->sme_state == SME_CONNECTING) {
|
|
@@ -875,32 +869,96 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
|
|
|
WLAN_STATUS_UNSPECIFIED_FAILURE,
|
|
|
GFP_KERNEL);
|
|
|
} else if (vif->sme_state == SME_CONNECTED) {
|
|
|
- cfg80211_disconnected(vif->ndev, reason,
|
|
|
+ cfg80211_disconnected(vif->ndev, proto_reason,
|
|
|
NULL, 0, GFP_KERNEL);
|
|
|
}
|
|
|
|
|
|
vif->sme_state = SME_DISCONNECTED;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Send a disconnect command to target when a disconnect event is
|
|
|
+ * received with reason code other than 3 (DISCONNECT_CMD - disconnect
|
|
|
+ * request from host) to make the firmware stop trying to connect even
|
|
|
+ * after giving disconnect event. There will be one more disconnect
|
|
|
+ * event for this disconnect command with reason code DISCONNECT_CMD
|
|
|
+ * which won't be notified to cfg80211.
|
|
|
+ */
|
|
|
+ if (reason != DISCONNECT_CMD)
|
|
|
+ ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
|
|
|
}
|
|
|
|
|
|
static int ath6kl_set_probed_ssids(struct ath6kl *ar,
|
|
|
struct ath6kl_vif *vif,
|
|
|
- struct cfg80211_ssid *ssids, int n_ssids)
|
|
|
+ struct cfg80211_ssid *ssids, int n_ssids,
|
|
|
+ struct cfg80211_match_set *match_set,
|
|
|
+ int n_match_ssid)
|
|
|
{
|
|
|
- u8 i;
|
|
|
+ u8 i, j, index_to_add, ssid_found = false;
|
|
|
+ struct ath6kl_cfg80211_match_probe_ssid ssid_list[MAX_PROBED_SSIDS];
|
|
|
+
|
|
|
+ memset(ssid_list, 0, sizeof(ssid_list));
|
|
|
|
|
|
- if (n_ssids > MAX_PROBED_SSID_INDEX)
|
|
|
+ if (n_ssids > MAX_PROBED_SSIDS ||
|
|
|
+ n_match_ssid > MAX_PROBED_SSIDS)
|
|
|
return -EINVAL;
|
|
|
|
|
|
for (i = 0; i < n_ssids; i++) {
|
|
|
+ memcpy(ssid_list[i].ssid.ssid,
|
|
|
+ ssids[i].ssid,
|
|
|
+ ssids[i].ssid_len);
|
|
|
+ ssid_list[i].ssid.ssid_len = ssids[i].ssid_len;
|
|
|
+
|
|
|
+ if (ssids[i].ssid_len)
|
|
|
+ ssid_list[i].flag = SPECIFIC_SSID_FLAG;
|
|
|
+ else
|
|
|
+ ssid_list[i].flag = ANY_SSID_FLAG;
|
|
|
+
|
|
|
+ if (n_match_ssid == 0)
|
|
|
+ ssid_list[i].flag |= MATCH_SSID_FLAG;
|
|
|
+ }
|
|
|
+
|
|
|
+ index_to_add = i;
|
|
|
+
|
|
|
+ for (i = 0; i < n_match_ssid; i++) {
|
|
|
+ ssid_found = false;
|
|
|
+
|
|
|
+ for (j = 0; j < n_ssids; j++) {
|
|
|
+ if ((match_set[i].ssid.ssid_len ==
|
|
|
+ ssid_list[j].ssid.ssid_len) &&
|
|
|
+ (!memcmp(ssid_list[j].ssid.ssid,
|
|
|
+ match_set[i].ssid.ssid,
|
|
|
+ match_set[i].ssid.ssid_len))) {
|
|
|
+ ssid_list[j].flag |= MATCH_SSID_FLAG;
|
|
|
+ ssid_found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ssid_found)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (index_to_add >= MAX_PROBED_SSIDS)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ssid_list[index_to_add].ssid.ssid_len =
|
|
|
+ match_set[i].ssid.ssid_len;
|
|
|
+ memcpy(ssid_list[index_to_add].ssid.ssid,
|
|
|
+ match_set[i].ssid.ssid,
|
|
|
+ match_set[i].ssid.ssid_len);
|
|
|
+ ssid_list[index_to_add].flag |= MATCH_SSID_FLAG;
|
|
|
+ index_to_add++;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < index_to_add; i++) {
|
|
|
ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
|
|
|
- ssids[i].ssid_len ?
|
|
|
- SPECIFIC_SSID_FLAG : ANY_SSID_FLAG,
|
|
|
- ssids[i].ssid_len,
|
|
|
- ssids[i].ssid);
|
|
|
+ ssid_list[i].flag,
|
|
|
+ ssid_list[i].ssid.ssid_len,
|
|
|
+ ssid_list[i].ssid.ssid);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
/* Make sure no old entries are left behind */
|
|
|
- for (i = n_ssids; i < MAX_PROBED_SSID_INDEX; i++) {
|
|
|
+ for (i = index_to_add; i < MAX_PROBED_SSIDS; i++) {
|
|
|
ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
|
|
|
DISABLE_SSID_FLAG, 0, NULL);
|
|
|
}
|
|
@@ -934,7 +992,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
}
|
|
|
|
|
|
ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
|
|
|
- request->n_ssids);
|
|
|
+ request->n_ssids, NULL, 0);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -943,7 +1001,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
|
|
|
WMI_FRAME_PROBE_REQ,
|
|
|
request->ie, request->ie_len);
|
|
|
if (ret) {
|
|
|
- ath6kl_err("failed to set Probe Request appie for scan");
|
|
|
+ ath6kl_err("failed to set Probe Request appie for scan\n");
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1512,6 +1570,9 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* need to clean up enhanced bmiss detection fw state */
|
|
|
+ ath6kl_cfg80211_sta_bmiss_enhance(vif, false);
|
|
|
+
|
|
|
set_iface_type:
|
|
|
switch (type) {
|
|
|
case NL80211_IFTYPE_STATION:
|
|
@@ -2074,7 +2135,9 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
|
|
|
if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) {
|
|
|
+ if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) &&
|
|
|
+ test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
|
|
|
+ ar->fw_capabilities)) {
|
|
|
ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
|
|
|
vif->fw_vif_idx, false);
|
|
|
if (ret)
|
|
@@ -2209,7 +2272,9 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
|
|
|
|
|
|
ar->state = ATH6KL_STATE_ON;
|
|
|
|
|
|
- if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags)) {
|
|
|
+ if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) &&
|
|
|
+ test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
|
|
|
+ ar->fw_capabilities)) {
|
|
|
ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
|
|
|
vif->fw_vif_idx, true);
|
|
|
if (ret)
|
|
@@ -2475,7 +2540,7 @@ void ath6kl_check_wow_status(struct ath6kl *ar)
|
|
|
static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
|
|
|
bool ht_enable)
|
|
|
{
|
|
|
- struct ath6kl_htcap *htcap = &vif->htcap;
|
|
|
+ struct ath6kl_htcap *htcap = &vif->htcap[band];
|
|
|
|
|
|
if (htcap->ht_enable == ht_enable)
|
|
|
return 0;
|
|
@@ -2585,6 +2650,30 @@ static int ath6kl_set_ies(struct ath6kl_vif *vif,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag)))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (vif->nw_type != INFRA_NETWORK)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE,
|
|
|
+ vif->ar->fw_capabilities))
|
|
|
+ return;
|
|
|
+
|
|
|
+ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n",
|
|
|
+ enable ? "enable" : "disable");
|
|
|
+
|
|
|
+ err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi,
|
|
|
+ vif->fw_vif_idx, enable);
|
|
|
+ if (err)
|
|
|
+ ath6kl_err("failed to %s enhanced bmiss detection: %d\n",
|
|
|
+ enable ? "enable" : "disable", err);
|
|
|
+}
|
|
|
+
|
|
|
static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
|
|
|
u8 *rsn_capab)
|
|
|
{
|
|
@@ -2665,9 +2754,15 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
|
|
/* TODO:
|
|
|
* info->interval
|
|
|
- * info->dtim_period
|
|
|
*/
|
|
|
|
|
|
+ ret = ath6kl_wmi_ap_set_dtim_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ info->dtim_period);
|
|
|
+
|
|
|
+ /* ignore error, just print a warning and continue normally */
|
|
|
+ if (ret)
|
|
|
+ ath6kl_warn("Failed to set dtim_period in beacon: %d\n", ret);
|
|
|
+
|
|
|
if (info->beacon.head == NULL)
|
|
|
return -EINVAL;
|
|
|
mgmt = (struct ieee80211_mgmt *) info->beacon.head;
|
|
@@ -3131,10 +3226,24 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
|
|
|
ath6kl_cfg80211_scan_complete_event(vif, true);
|
|
|
|
|
|
ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
|
|
|
- request->n_ssids);
|
|
|
+ request->n_ssids,
|
|
|
+ request->match_sets,
|
|
|
+ request->n_match_sets);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
+ if (!request->n_match_sets) {
|
|
|
+ ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ ALL_BSS_FILTER, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ } else {
|
|
|
+ ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ MATCHED_SSID_FILTER, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
/* fw uses seconds, also make sure that it's >0 */
|
|
|
interval = max_t(u16, 1, request->interval / 1000);
|
|
|
|
|
@@ -3156,7 +3265,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
|
|
|
WMI_FRAME_PROBE_REQ,
|
|
|
request->ie, request->ie_len);
|
|
|
if (ret) {
|
|
|
- ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
|
|
|
+ ath6kl_warn("Failed to set probe request IE for scheduled scan: %d\n",
|
|
|
ret);
|
|
|
return ret;
|
|
|
}
|
|
@@ -3188,6 +3297,18 @@ static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy,
|
|
|
+ struct net_device *dev,
|
|
|
+ const u8 *addr,
|
|
|
+ const struct cfg80211_bitrate_mask *mask)
|
|
|
+{
|
|
|
+ struct ath6kl *ar = ath6kl_priv(dev);
|
|
|
+ struct ath6kl_vif *vif = netdev_priv(dev);
|
|
|
+
|
|
|
+ return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx,
|
|
|
+ mask);
|
|
|
+}
|
|
|
+
|
|
|
static const struct ieee80211_txrx_stypes
|
|
|
ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
|
|
|
[NL80211_IFTYPE_STATION] = {
|
|
@@ -3253,6 +3374,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
|
|
|
.mgmt_frame_register = ath6kl_mgmt_frame_register,
|
|
|
.sched_scan_start = ath6kl_cfg80211_sscan_start,
|
|
|
.sched_scan_stop = ath6kl_cfg80211_sscan_stop,
|
|
|
+ .set_bitrate_mask = ath6kl_cfg80211_set_bitrate,
|
|
|
};
|
|
|
|
|
|
void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
|
|
@@ -3380,7 +3502,8 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
|
|
|
vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
|
|
|
vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
|
|
|
vif->bg_scan_period = 0;
|
|
|
- vif->htcap.ht_enable = true;
|
|
|
+ vif->htcap[IEEE80211_BAND_2GHZ].ht_enable = true;
|
|
|
+ vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true;
|
|
|
|
|
|
memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
|
|
|
if (fw_vif_idx != 0)
|
|
@@ -3440,7 +3563,13 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
|
|
|
}
|
|
|
|
|
|
/* max num of ssids that can be probed during scanning */
|
|
|
- wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
|
|
|
+ wiphy->max_scan_ssids = MAX_PROBED_SSIDS;
|
|
|
+
|
|
|
+ /* max num of ssids that can be matched after scan */
|
|
|
+ if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST,
|
|
|
+ ar->fw_capabilities))
|
|
|
+ wiphy->max_match_sets = MAX_PROBED_SSIDS;
|
|
|
+
|
|
|
wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
|
|
|
switch (ar->hw.cap) {
|
|
|
case WMI_11AN_CAP:
|
|
@@ -3477,6 +3606,17 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
|
|
|
ath6kl_band_5ghz.ht_cap.cap = 0;
|
|
|
ath6kl_band_5ghz.ht_cap.ht_supported = false;
|
|
|
}
|
|
|
+
|
|
|
+ if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) {
|
|
|
+ ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
|
|
|
+ ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
|
|
|
+ ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
|
|
|
+ ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
|
|
|
+ } else {
|
|
|
+ ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
|
|
|
+ ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
|
|
|
+ }
|
|
|
+
|
|
|
if (band_2gig)
|
|
|
wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
|
|
|
if (band_5gig)
|
|
@@ -3497,7 +3637,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
|
|
|
wiphy->wowlan.pattern_min_len = 1;
|
|
|
wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
|
|
|
|
|
|
- wiphy->max_sched_scan_ssids = MAX_PROBED_SSID_INDEX;
|
|
|
+ wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
|
|
|
|
|
|
ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
|
|
|
WIPHY_FLAG_HAVE_AP_SME |
|