| /***************************************************************************** |
| * |
| * Copyright (c) 2012 - 2020 Samsung Electronics Co., Ltd. All rights reserved |
| * |
| ****************************************************************************/ |
| #include <linux/etherdevice.h> |
| #include <linux/time.h> |
| |
| #include "debug.h" |
| #include "dev.h" |
| #include "mgt.h" |
| #include "mlme.h" |
| #include "src_sink.h" |
| #include "const.h" |
| #include "ba.h" |
| #include "mib.h" |
| #include "cac.h" |
| #include "nl80211_vendor.h" |
| |
| #include "scsc_wifilogger_rings.h" |
| #ifdef CONFIG_SCSC_LOG_COLLECTION |
| #include <scsc/scsc_log_collector.h> |
| #endif |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) |
| #include "porting_imx.h" |
| #endif |
| |
| struct ieee80211_channel *slsi_find_scan_channel(struct slsi_dev *sdev, struct ieee80211_mgmt *mgmt, size_t mgmt_len, u16 freq) |
| { |
| int ielen = mgmt_len - (mgmt->u.beacon.variable - (u8 *)mgmt); |
| const u8 *scan_ds = cfg80211_find_ie(WLAN_EID_DS_PARAMS, mgmt->u.beacon.variable, ielen); |
| const u8 *scan_ht = cfg80211_find_ie(WLAN_EID_HT_OPERATION, mgmt->u.beacon.variable, ielen); |
| u8 chan = 0; |
| |
| /* Use the DS or HT channel where possible as the Offchannel results mean the RX freq is not reliable */ |
| if (scan_ds) |
| chan = scan_ds[2]; |
| else if (scan_ht) |
| chan = scan_ht[2]; |
| |
| if (chan) { |
| enum ieee80211_band band = IEEE80211_BAND_2GHZ; |
| |
| if (chan > 14) |
| band = IEEE80211_BAND_5GHZ; |
| freq = (u16)ieee80211_channel_to_frequency(chan, band); |
| } |
| if (!freq) |
| return NULL; |
| |
| return ieee80211_get_channel(sdev->wiphy, freq); |
| } |
| |
| static struct ieee80211_mgmt *slsi_rx_scan_update_ssid(struct slsi_dev *sdev, struct net_device *dev, |
| struct ieee80211_mgmt *mgmt, size_t mgmt_len, size_t *new_len, |
| u16 freq) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| u8 *new_mgmt; |
| size_t offset; |
| const u8 *mgmt_pos; |
| const u8 *ssid; |
| int i; |
| int band; |
| |
| if (!SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) |
| return NULL; |
| |
| /* update beacon, not probe response as probe response will always have actual ssid.*/ |
| if (!ieee80211_is_beacon(mgmt->frame_control)) |
| return NULL; |
| |
| ssid = cfg80211_find_ie(WLAN_EID_SSID, mgmt->u.beacon.variable, |
| mgmt_len - (mgmt->u.beacon.variable - (u8 *)mgmt)); |
| if (!ssid) { |
| SLSI_WARN(sdev, "beacon with NO SSID IE\n"); |
| return NULL; |
| } |
| /* update beacon only if hidden ssid. So, Skip if not hidden ssid*/ |
| if (ssid[1] > 0 && ssid[2] != '\0') |
| return NULL; |
| |
| band = (freq / 1000) == 2 ? SLSI_FREQ_BAND_2GHZ : SLSI_FREQ_BAND_5GHZ; |
| |
| /* check we have a known ssid for a bss */ |
| for (i = 0; i < SLSI_SCAN_SSID_MAP_MAX; i++) { |
| if (SLSI_ETHER_EQUAL(sdev->ssid_map[i].bssid, mgmt->bssid) && sdev->ssid_map[i].band == band) { |
| new_mgmt = kmalloc(mgmt_len + 34, GFP_KERNEL); |
| if (!new_mgmt) { |
| SLSI_ERR_NODEV("malloc failed(len:%ld)\n", mgmt_len + 34); |
| return NULL; |
| } |
| |
| /* copy frame till ssid element */ |
| memcpy(new_mgmt, mgmt, ssid - (u8 *)mgmt); |
| offset = ssid - (u8 *)mgmt; |
| /* copy bss ssid into new frame */ |
| new_mgmt[offset++] = WLAN_EID_SSID; |
| new_mgmt[offset++] = sdev->ssid_map[i].ssid_len; |
| memcpy(new_mgmt + offset, sdev->ssid_map[i].ssid, sdev->ssid_map[i].ssid_len); |
| offset += sdev->ssid_map[i].ssid_len; |
| /* copy rest of the frame following ssid */ |
| mgmt_pos = ssid + ssid[1] + 2; |
| memcpy(new_mgmt + offset, mgmt_pos, mgmt_len - (mgmt_pos - (u8 *)mgmt)); |
| offset += mgmt_len - (mgmt_pos - (u8 *)mgmt); |
| *new_len = offset; |
| |
| return (struct ieee80211_mgmt *)new_mgmt; |
| } |
| } |
| return NULL; |
| } |
| |
| struct ieee80211_channel *slsi_rx_scan_pass_to_cfg80211(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| size_t mgmt_len = fapi_get_mgmtlen(skb); |
| s32 signal = fapi_get_s16(skb, u.mlme_scan_ind.rssi) * 100; |
| u16 freq = SLSI_FREQ_FW_TO_HOST(fapi_get_u16(skb, u.mlme_scan_ind.channel_frequency)); |
| struct ieee80211_channel *channel = slsi_find_scan_channel(sdev, mgmt, mgmt_len, freq); |
| struct timespec uptime; |
| |
| get_monotonic_boottime(&uptime); |
| SLSI_UNUSED_PARAMETER_NOT_DEBUG(dev); |
| |
| /* update timestamp with device uptime in micro sec */ |
| mgmt->u.beacon.timestamp = (uptime.tv_sec * 1000000) + (uptime.tv_nsec / 1000); |
| |
| if (channel) { |
| struct cfg80211_bss *bss; |
| struct ieee80211_mgmt *mgmt_new; |
| size_t mgmt_new_len = 0; |
| |
| mgmt_new = slsi_rx_scan_update_ssid(sdev, dev, mgmt, mgmt_len, &mgmt_new_len, freq); |
| if (mgmt_new) |
| bss = cfg80211_inform_bss_frame(sdev->wiphy, channel, mgmt_new, mgmt_new_len, signal, GFP_KERNEL); |
| else |
| bss = cfg80211_inform_bss_frame(sdev->wiphy, channel, mgmt, mgmt_len, signal, GFP_KERNEL); |
| |
| slsi_cfg80211_put_bss(sdev->wiphy, bss); |
| kfree(mgmt_new); |
| } else { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "No Channel info found for freq:%d\n", freq); |
| } |
| |
| slsi_kfree_skb(skb); |
| return channel; |
| } |
| |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| static int slsi_populate_bssid_info(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| struct sk_buff *skb, struct list_head *bssid_list) |
| { |
| struct list_head *pos; |
| struct slsi_bssid_info *current_result; |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| int current_rssi; |
| u16 current_freq; |
| |
| current_rssi = fapi_get_s16(skb, u.mlme_scan_ind.rssi); |
| current_freq = fapi_get_s16(skb, u.mlme_scan_ind.channel_frequency); |
| |
| list_for_each(pos, bssid_list) { |
| struct slsi_bssid_info *bssid_info = list_entry(pos, struct slsi_bssid_info, list); |
| |
| if (SLSI_ETHER_EQUAL(bssid_info->bssid, mgmt->bssid)) { |
| /*entry exists for bssid*/ |
| bssid_info->rssi = current_rssi; |
| bssid_info->freq = current_freq; |
| return 0; |
| } |
| } |
| |
| current_result = kzalloc(sizeof(*current_result), GFP_KERNEL); |
| if (!current_result) { |
| SLSI_ERR(sdev, "Failed to allocate node for bssid info\n"); |
| return -1; |
| } |
| SLSI_ETHER_COPY(current_result->bssid, mgmt->bssid); |
| current_result->rssi = current_rssi; |
| current_result->freq = current_freq; |
| current_result->connect_attempted = false; |
| list_add_tail(¤t_result->list, bssid_list); |
| |
| return 0; |
| } |
| |
| #if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 10, 17)) |
| static inline u64 ether_addr_to_u64(const u8 *addr) |
| { |
| u64 u = 0; |
| int i; |
| |
| for (i = 0; i < ETH_ALEN; i++) |
| u = u << 8 | addr[i]; |
| |
| return u; |
| } |
| |
| static inline void u64_to_ether_addr(u64 u, u8 *addr) |
| { |
| int i; |
| |
| for (i = ETH_ALEN - 1; i >= 0; i--) { |
| addr[i] = u & 0xff; |
| u = u >> 8; |
| } |
| } |
| #endif |
| |
| static inline void slsi_gen_new_bssid(const u8 *bssid, u8 max_bssid, |
| u8 mbssid_index, u8 *new_bssid) |
| { |
| u64 bssid_u64 = ether_addr_to_u64(bssid); |
| u64 mask = GENMASK_ULL(max_bssid - 1, 0); |
| u64 new_bssid_u64; |
| |
| new_bssid_u64 = bssid_u64 & ~mask; |
| |
| new_bssid_u64 |= ((bssid_u64 & mask) + mbssid_index) & mask; |
| |
| u64_to_ether_addr(new_bssid_u64, new_bssid); |
| } |
| |
| static int slsi_mbssid_to_ssid_list(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| u8 *scan_ssid, int ssid_len, |
| u8 *bssid, int freq, int rssi, u8 akm_type) |
| { |
| struct list_head *pos; |
| int found = 0; |
| |
| list_for_each(pos, &ndev_vif->sta.ssid_info) { |
| struct slsi_ssid_info *ssid_info = list_entry(pos, struct slsi_ssid_info, list); |
| struct list_head *pos_bssid; |
| struct slsi_bssid_info *current_result; |
| |
| if (ssid_info->ssid.ssid_len == ssid_len && |
| memcmp(ssid_info->ssid.ssid, scan_ssid, ssid_len) == 0 && |
| ssid_info->akm_type & akm_type) { |
| found = 1; |
| |
| list_for_each(pos_bssid, &ssid_info->bssid_list) { |
| struct slsi_bssid_info *bssid_info = list_entry(pos_bssid, struct slsi_bssid_info, list); |
| |
| if (SLSI_ETHER_EQUAL(bssid_info->bssid, bssid)) { |
| /*entry exists for bssid*/ |
| bssid_info->rssi = rssi; |
| bssid_info->freq = freq; |
| return 0; |
| } |
| } |
| current_result = kzalloc(sizeof(*current_result), GFP_KERNEL); |
| if (!current_result) { |
| SLSI_ERR(sdev, "Failed to allocate node for bssid info\n"); |
| return -1; |
| } |
| SLSI_ETHER_COPY(current_result->bssid, bssid); |
| SLSI_DBG3_NODEV(SLSI_MLME, "BSSID Entry : %pM\n", bssid); |
| current_result->rssi = rssi; |
| current_result->freq = freq; |
| current_result->connect_attempted = false; |
| list_add_tail(¤t_result->list, &ssid_info->bssid_list); |
| break; |
| } |
| } |
| if (!found) { |
| struct slsi_ssid_info *ssid_info; |
| struct slsi_bssid_info *current_result; |
| |
| SLSI_DBG3_NODEV(SLSI_MLME, "SSID Entry : %.*s\n", ssid_len, scan_ssid); |
| ssid_info = kmalloc(sizeof(*ssid_info), GFP_KERNEL); |
| if (ssid_info) { |
| ssid_info->ssid.ssid_len = ssid_len; |
| memcpy(ssid_info->ssid.ssid, scan_ssid, ssid_len); |
| ssid_info->akm_type = akm_type; |
| INIT_LIST_HEAD(&ssid_info->bssid_list); |
| current_result = kzalloc(sizeof(*current_result), GFP_KERNEL); |
| if (!current_result) { |
| SLSI_ERR(sdev, "Failed to allocate node for bssid info\n"); |
| return -1; |
| } |
| SLSI_ETHER_COPY(current_result->bssid, bssid); |
| SLSI_DBG3_NODEV(SLSI_MLME, "New BSSID Entry : %pM\n", bssid); |
| current_result->rssi = rssi; |
| current_result->freq = freq; |
| current_result->connect_attempted = false; |
| list_add_tail(¤t_result->list, &ssid_info->bssid_list); |
| list_add(&ssid_info->list, &ndev_vif->sta.ssid_info); |
| } else { |
| SLSI_ERR(sdev, "Failed to allocate node for ssid info\n"); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int slsi_extract_mbssids(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| const struct ieee80211_mgmt *mgmt, |
| struct sk_buff *skb, u8 akm_type) |
| { |
| u8 *transmitter_bssid; |
| const u8 *sub_elem; |
| int elen; |
| const u8 *pos; |
| const u8 *probe_beacon; |
| const u8 *ie; |
| int ie_len; |
| int current_rssi; |
| u16 current_freq; |
| size_t mgmt_len; |
| |
| mgmt_len = fapi_get_mgmtlen(skb); |
| current_rssi = fapi_get_s16(skb, u.mlme_scan_ind.rssi); |
| current_freq = fapi_get_s16(skb, u.mlme_scan_ind.channel_frequency); |
| |
| transmitter_bssid = (u8 *)mgmt->bssid; |
| if (ieee80211_is_beacon(mgmt->frame_control)) { |
| probe_beacon = (u8 *)mgmt->u.beacon.variable; |
| ie_len = mgmt_len - (mgmt->u.beacon.variable - (u8 *)mgmt); |
| } else { |
| probe_beacon = (u8 *)mgmt->u.probe_resp.variable; |
| ie_len = mgmt_len - (mgmt->u.probe_resp.variable - (u8 *)mgmt); |
| } |
| ie = probe_beacon; |
| |
| while (ie && (ie_len > ie - probe_beacon)) { |
| ie = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ie_len - (ie - probe_beacon)); |
| if (!ie) |
| break; |
| |
| SLSI_DBG1_NODEV(SLSI_MLME, "MBSSID IE Found\n"); |
| pos = &ie[2]; |
| elen = ie[1]; |
| for (sub_elem = pos + 1; sub_elem < pos + elen - 1; |
| sub_elem += 2 + sub_elem[1]) { |
| u8 sub_len = sub_elem[1]; |
| u8 new_bssid[ETH_ALEN]; |
| const u8 *scan_ssid; |
| const u8 *index; |
| const u8 *ssid_ie; |
| int ssid_len = 0; |
| |
| if (sub_elem[0] != 0 || sub_elem[1] < 4) { |
| /* not a valid BSS profile */ |
| continue; |
| } |
| |
| if (sub_elem[2] != WLAN_EID_NON_TX_BSSID_CAP || |
| sub_elem[3] != 2) { |
| /* The first element of the |
| * Nontransmitted BSSID Profile is not |
| * the Nontransmitted BSSID Capability |
| * element. |
| */ |
| continue; |
| } |
| |
| /* found a Nontransmitted BSSID Profile */ |
| index = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, |
| sub_elem + 2, sub_len); |
| if (!index || index[1] < 1 || index[2] == 0) { |
| /* Invalid MBSSID Index element */ |
| continue; |
| } |
| ssid_ie = cfg80211_find_ie(WLAN_EID_SSID, sub_elem + 2, sub_len); |
| if (!ssid_ie || ssid_ie[1] >= IEEE80211_MAX_SSID_LEN) |
| continue; |
| ssid_len = ssid_ie[1]; |
| scan_ssid = &ssid_ie[2]; |
| slsi_gen_new_bssid(transmitter_bssid, |
| pos[0], index[2], new_bssid); |
| slsi_mbssid_to_ssid_list(sdev, ndev_vif, (u8 *)scan_ssid, ssid_len, new_bssid, current_freq, |
| current_rssi, akm_type); |
| } |
| ie = ie + ie[1]; |
| } |
| return 0; |
| } |
| |
| static void slsi_remove_assoc_disallowed_bssid(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| struct slsi_scan_result *scan_result) |
| { |
| struct list_head *pos; |
| |
| list_for_each(pos, &ndev_vif->sta.ssid_info) { |
| struct slsi_ssid_info *ssid_info = list_entry(pos, struct slsi_ssid_info, list); |
| struct list_head *pos_bssid; |
| |
| if (ssid_info->ssid.ssid_len != scan_result->ssid_length || |
| memcmp(ssid_info->ssid.ssid, &scan_result->ssid, scan_result->ssid_length) != 0 || |
| !(ssid_info->akm_type & scan_result->akm_type)) |
| continue; |
| |
| list_for_each(pos_bssid, &ssid_info->bssid_list) { |
| struct slsi_bssid_info *bssid_info = list_entry(pos_bssid, struct slsi_bssid_info, list); |
| |
| if (!SLSI_ETHER_EQUAL(bssid_info->bssid, scan_result->bssid)) |
| continue; |
| list_del(pos_bssid); |
| kfree(bssid_info); |
| break; |
| } |
| } |
| } |
| |
| static int slsi_reject_ap_for_scan_info(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| const struct ieee80211_mgmt *mgmt, |
| size_t mgmt_len, struct slsi_scan_result *scan_result) |
| { |
| const u8 *vendor_ie; |
| u8 ie_length; |
| bool disassoc_attr = false; |
| |
| if (ieee80211_is_beacon(mgmt->frame_control)) |
| vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, SLSI_WLAN_OUI_TYPE_WFA_MBO, |
| mgmt->u.beacon.variable, |
| mgmt_len - (mgmt->u.beacon.variable - (u8 *)mgmt)); |
| else |
| vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, SLSI_WLAN_OUI_TYPE_WFA_MBO, |
| mgmt->u.probe_resp.variable, |
| mgmt_len - (mgmt->u.probe_resp.variable - (u8 *)mgmt)); |
| |
| if (vendor_ie) |
| ie_length = vendor_ie[1]; |
| else |
| return 0; |
| disassoc_attr = cfg80211_find_ie(SLSI_MBO_ASSOC_DISALLOWED_ATTR_ID, vendor_ie + 6, ie_length - 2); |
| if (disassoc_attr) { |
| slsi_remove_assoc_disallowed_bssid(sdev, ndev_vif, scan_result); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int slsi_populate_ssid_info(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, u16 scan_id) |
| { |
| struct list_head *pos; |
| int found = 0; |
| struct sk_buff *beacon_probe_skb = NULL; |
| struct ieee80211_mgmt *mgmt = NULL; |
| struct slsi_scan_result *scan_result = ndev_vif->scan[scan_id].scan_results; |
| struct slsi_ssid_info *ssid_info; |
| |
| while (scan_result) { |
| if (scan_result->beacon) { |
| beacon_probe_skb = scan_result->beacon; |
| } else if (scan_result->probe_resp) { |
| beacon_probe_skb = scan_result->probe_resp; |
| } else { |
| SLSI_ERR_NODEV("Scan entry with no beacon /probe resp!!\n"); |
| scan_result = scan_result->next; |
| continue; |
| } |
| |
| mgmt = fapi_get_mgmt(beacon_probe_skb); |
| if (!scan_result->ssid_length || |
| slsi_reject_ap_for_scan_info(sdev, ndev_vif, mgmt, fapi_get_mgmtlen(beacon_probe_skb), scan_result)) { |
| scan_result = scan_result->next; |
| continue; |
| } |
| found = 0; |
| list_for_each(pos, &ndev_vif->sta.ssid_info) { |
| struct slsi_ssid_info *ssid_info = list_entry(pos, struct slsi_ssid_info, list); |
| |
| if (ssid_info->ssid.ssid_len != scan_result->ssid_length || |
| memcmp(ssid_info->ssid.ssid, &scan_result->ssid, scan_result->ssid_length) != 0 || |
| !(ssid_info->akm_type & scan_result->akm_type)) |
| continue; |
| found = 1; |
| slsi_populate_bssid_info(sdev, ndev_vif, beacon_probe_skb, &ssid_info->bssid_list); |
| break; |
| } |
| if (found) { |
| slsi_extract_mbssids(sdev, ndev_vif, mgmt, beacon_probe_skb, scan_result->akm_type); |
| scan_result = scan_result->next; |
| continue; |
| } |
| |
| ssid_info = kmalloc(sizeof(*ssid_info), GFP_ATOMIC); |
| if (ssid_info) { |
| ssid_info->ssid.ssid_len = scan_result->ssid_length; |
| memcpy(ssid_info->ssid.ssid, &scan_result->ssid, scan_result->ssid_length); |
| ssid_info->akm_type = scan_result->akm_type; |
| INIT_LIST_HEAD(&ssid_info->bssid_list); |
| slsi_populate_bssid_info(sdev, ndev_vif, beacon_probe_skb, &ssid_info->bssid_list); |
| list_add(&ssid_info->list, &ndev_vif->sta.ssid_info); |
| } else { |
| SLSI_ERR(sdev, "Failed to allocate entry : %.*s kmalloc() failed\n", scan_result->ssid_length, scan_result->ssid); |
| } |
| slsi_extract_mbssids(sdev, ndev_vif, mgmt, beacon_probe_skb, scan_result->akm_type); |
| scan_result = scan_result->next; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static int slsi_add_to_scan_list(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| struct sk_buff *skb, const u8 *scan_ssid, u16 scan_id) |
| { |
| struct slsi_scan_result *head; |
| struct slsi_scan_result *scan_result, *current_result, *prev = NULL; |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| bool found = 0, skb_stored = 0; |
| int current_rssi, current_band; |
| bool is_hidden = 0, ssid_matched = 0; |
| |
| SLSI_MUTEX_LOCK(ndev_vif->scan_result_mutex); |
| head = ndev_vif->scan[scan_id].scan_results; |
| scan_result = head; |
| current_rssi = fapi_get_s16(skb, u.mlme_scan_ind.rssi); |
| current_band = (fapi_get_s16(skb, |
| u.mlme_scan_ind.channel_frequency) / |
| 2000) == 2 ? SLSI_FREQ_BAND_2GHZ : SLSI_FREQ_BAND_5GHZ; |
| |
| while (scan_result) { |
| is_hidden = scan_result->hidden && (!scan_ssid || !scan_ssid[1] || scan_ssid[2] == '\0'); |
| ssid_matched = scan_ssid && scan_ssid[1] && scan_result->ssid_length && |
| (scan_ssid[1] == scan_result->ssid_length) && |
| !memcmp(&scan_ssid[2], scan_result->ssid, scan_ssid[1]); |
| if ((SLSI_ETHER_EQUAL(scan_result->bssid, mgmt->bssid) && scan_result->band == current_band) && |
| (is_hidden || ssid_matched)) { |
| /*entry exists for bssid*/ |
| if (!scan_result->probe_resp && ieee80211_is_probe_resp(mgmt->frame_control)) { |
| scan_result->probe_resp = skb; |
| skb_stored = 1; |
| } else if (!scan_result->beacon && ieee80211_is_beacon(mgmt->frame_control)) { |
| scan_result->beacon = skb; |
| skb_stored = 1; |
| if (!scan_ssid || !scan_ssid[1] || scan_ssid[2] == '\0') |
| scan_result->hidden = 1; |
| } |
| |
| /* Use the best RSSI value from all beacons/probe resp for a bssid. If no improvment |
| * in RSSI and beacon and probe response exist, ignore this result |
| */ |
| if (current_rssi < scan_result->rssi) { |
| if (!skb_stored) |
| slsi_kfree_skb(skb); |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex); |
| return 0; |
| } |
| |
| scan_result->rssi = current_rssi; |
| if (!skb_stored) { |
| if (ieee80211_is_beacon(mgmt->frame_control)) { |
| slsi_kfree_skb(scan_result->beacon); |
| scan_result->beacon = skb; |
| } else { |
| slsi_kfree_skb(scan_result->probe_resp); |
| scan_result->probe_resp = skb; |
| } |
| } |
| |
| /*No change in position if rssi is still less than prev node*/ |
| if (!prev || prev->rssi > current_rssi) { |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex); |
| return 0; |
| } |
| |
| /*remove and re-insert*/ |
| found = 1; |
| prev->next = scan_result->next; |
| scan_result->next = NULL; |
| current_result = scan_result; |
| |
| break; |
| } |
| |
| prev = scan_result; |
| scan_result = scan_result->next; |
| } |
| |
| if (!found) { |
| /*add_new node*/ |
| current_result = kzalloc(sizeof(*current_result), GFP_KERNEL); |
| if (!current_result) { |
| SLSI_ERR(sdev, "Failed to allocate node for scan result\n"); |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex); |
| return -1; |
| } |
| SLSI_ETHER_COPY(current_result->bssid, mgmt->bssid); |
| |
| current_result->rssi = current_rssi; |
| current_result->band = current_band; |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| if (ieee80211_is_beacon(mgmt->frame_control)) |
| current_result->akm_type = slsi_bss_connect_type_get(sdev, mgmt->u.beacon.variable, |
| fapi_get_mgmtlen(skb) - (mgmt->u.beacon.variable - (u8 *)mgmt)); |
| else |
| current_result->akm_type = slsi_bss_connect_type_get(sdev, mgmt->u.probe_resp.variable, |
| fapi_get_mgmtlen(skb) - (mgmt->u.probe_resp.variable - (u8 *)mgmt)); |
| #endif |
| if (scan_ssid && scan_ssid[1]) { |
| memcpy(current_result->ssid, &scan_ssid[2], scan_ssid[1]); |
| current_result->ssid_length = scan_ssid[1]; |
| } else { |
| current_result->ssid_length = 0; |
| } |
| if (ieee80211_is_beacon(mgmt->frame_control)) { |
| current_result->beacon = skb; |
| if (!scan_ssid || !scan_ssid[1] || scan_ssid[2] == '\0') |
| current_result->hidden = 1; |
| } else { |
| current_result->probe_resp = skb; |
| } |
| current_result->next = NULL; |
| |
| if (!head) { /*first node*/ |
| ndev_vif->scan[scan_id].scan_results = current_result; |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex); |
| return 0; |
| } |
| } |
| |
| scan_result = head; |
| prev = NULL; |
| /* insert based on rssi in descending order*/ |
| while (scan_result) { |
| if (current_result->rssi > scan_result->rssi) { |
| current_result->next = scan_result; |
| if (prev) |
| prev->next = current_result; |
| else |
| ndev_vif->scan[scan_id].scan_results = current_result; |
| break; |
| } |
| prev = scan_result; |
| scan_result = scan_result->next; |
| } |
| if (!scan_result) { |
| /*insert at the end*/ |
| prev->next = current_result; |
| current_result->next = NULL; |
| } |
| |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex); |
| return 0; |
| } |
| |
| static int slsi_add_to_p2p_scan_list(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| struct sk_buff *skb, u16 scan_id) |
| { |
| struct slsi_scan_result *current_result; |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| struct slsi_scan *scan; |
| |
| /*add_new node*/ |
| current_result = kzalloc(sizeof(*current_result), GFP_KERNEL); |
| if (!current_result) { |
| SLSI_ERR(sdev, "Failed to allocate node for scan result\n"); |
| return -1; |
| } |
| SLSI_ETHER_COPY(current_result->bssid, mgmt->bssid); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->scan_result_mutex); |
| scan = &ndev_vif->scan[scan_id]; |
| if (ieee80211_is_beacon(mgmt->frame_control)) |
| current_result->beacon = skb; |
| else |
| current_result->probe_resp = skb; |
| |
| if (!scan->scan_results) { |
| scan->scan_results = current_result; |
| current_result->next = NULL; |
| } else { |
| current_result->next = scan->scan_results; |
| scan->scan_results = current_result; |
| } |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex); |
| |
| return 0; |
| } |
| |
| void slsi_rx_scan_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| u16 scan_id = fapi_get_u16(skb, u.mlme_scan_ind.scan_id); |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| size_t mgmt_len = fapi_get_mgmtlen(skb); |
| size_t ie_len = mgmt_len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); |
| const u8 *scan_ssid = NULL; |
| |
| #ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE |
| if (slsi_is_gscan_id(scan_id)) { |
| SLSI_NET_DBG3(dev, SLSI_GSCAN, "scan_id:%#x bssid:%pM\n", scan_id, fapi_get_mgmt(skb)->bssid); |
| SLSI_MUTEX_LOCK(ndev_vif->scan_mutex); |
| slsi_gscan_handle_scan_result(sdev, dev, skb, scan_id, false); |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex); |
| return; |
| } |
| #endif |
| |
| scan_ssid = cfg80211_find_ie(WLAN_EID_SSID, mgmt->u.probe_resp.variable, ie_len); |
| |
| if (scan_ssid && scan_ssid[1] > IEEE80211_MAX_SSID_LEN) { |
| SLSI_NET_ERR(dev, "Dropping scan result due to unexpected ssid length(%d)\n", scan_ssid[1]); |
| slsi_kfree_skb(skb); |
| return; |
| } |
| |
| if (scan_ssid && scan_ssid[1] && ((ie_len - (scan_ssid - mgmt->u.probe_resp.variable) + 2) < scan_ssid[1])) { |
| SLSI_NET_ERR(dev, "Dropping scan result due to skb data is less than ssid len(%d)\n", scan_ssid[1]); |
| slsi_kfree_skb(skb); |
| return; |
| } |
| |
| if (sdev->p2p_certif && ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT && |
| (scan_id == (ndev_vif->ifnum << 8 | SLSI_SCAN_HW_ID))) { |
| /* When supplicant receives a peer GO probe response with selected registrar set and group |
| * capability as 0, which is invalid, it is unable to store persistent network block. Hence |
| * such probe response is getting ignored here. |
| * This is mainly for an inter-op with Realtek P2P GO in P2P certification |
| */ |
| if (scan_ssid && scan_ssid[1] > 7) { |
| const u8 *p2p_ie = NULL; |
| |
| p2p_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, mgmt->u.probe_resp.variable, ie_len); |
| #define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1) |
| if (p2p_ie && !(p2p_ie[10] & P2P_GROUP_CAPAB_PERSISTENT_GROUP)) { |
| SLSI_NET_INFO(dev, "Ignoring a peer GO probe response with group_capab as 0\n"); |
| slsi_kfree_skb(skb); |
| return; |
| } |
| } |
| } |
| |
| scan_id = (scan_id & 0xFF); |
| |
| if (WARN_ON(scan_id >= SLSI_SCAN_MAX)) { |
| slsi_kfree_skb(skb); |
| return; |
| } |
| |
| /* Blocking scans already taken scan mutex. |
| * So scan mutex only incase of non blocking scans. |
| */ |
| if (!ndev_vif->scan[scan_id].is_blocking_scan) |
| SLSI_MUTEX_LOCK(ndev_vif->scan_mutex); |
| |
| if (fapi_get_vif(skb) != 0 && fapi_get_u16(skb, u.mlme_scan_ind.scan_id) == 0) { |
| /* Connect/Roaming scan data : Save for processing later */ |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Connect/Roaming scan indication received, bssid:%pM\n", fapi_get_mgmt(skb)->bssid); |
| slsi_kfree_skb(ndev_vif->sta.mlme_scan_ind_skb); |
| ndev_vif->sta.mlme_scan_ind_skb = skb; |
| } else if (ndev_vif->scan[scan_id].scan_req || ndev_vif->scan[scan_id].sched_req || |
| ndev_vif->scan[scan_id].acs_request || |
| ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan) { |
| slsi_roam_channel_cache_add(sdev, dev, skb); |
| if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) |
| slsi_add_to_scan_list(sdev, ndev_vif, skb, scan_ssid, scan_id); |
| else |
| slsi_add_to_p2p_scan_list(sdev, ndev_vif, skb, scan_id); |
| } |
| |
| if (!ndev_vif->scan[scan_id].is_blocking_scan) |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex); |
| } |
| |
| #ifdef CONFIG_SLSI_WLAN_STA_FWD_BEACON |
| void slsi_rx_beacon_reporting_event_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| u16 reason_code = fapi_get_u16(skb, u.mlme_beacon_reporting_event_ind.abort_reason) - |
| SLSI_FORWARD_BEACON_ABORT_REASON_OFFSET; |
| int ret = 0; |
| |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| kfree_skb(skb); |
| return; |
| } |
| if (!ndev_vif->is_wips_running) { |
| SLSI_ERR(sdev, "WIPS is not running. Ignore beacon_reporting_event_ind(%u)\n", reason_code); |
| slsi_kfree_skb(skb); |
| return; |
| } |
| |
| ndev_vif->is_wips_running = false; |
| |
| if (reason_code <= SLSI_FORWARD_BEACON_ABORT_REASON_SUSPENDED) |
| SLSI_INFO(sdev, "received abort_event from FW with reason(%u)\n", reason_code); |
| else |
| SLSI_ERR(sdev, "received abort_event unsupporting reason(%u)\n", reason_code); |
| |
| ret = slsi_send_forward_beacon_abort_vendor_event(sdev, reason_code); |
| if (ret) |
| SLSI_ERR(sdev, "Failed to send forward_beacon_abort_event(err=%d)\n", ret); |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_handle_wips_beacon(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb, |
| struct ieee80211_mgmt *mgmt, int mgmt_len) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| size_t ie_len = mgmt_len - offsetof(struct ieee80211_mgmt, u.beacon.variable); |
| const u8 *ssid_ie = NULL; |
| const u8 *scan_ssid = NULL; |
| const u8 *scan_bssid = NULL; |
| u16 beacon_int = 0; |
| u64 timestamp = 0; |
| int ssid_len = 0; |
| struct timespec sys_time; |
| int ret = 0; |
| |
| u8 channel = (u8)(ndev_vif->chan->hw_value); |
| |
| get_monotonic_boottime(&sys_time); |
| scan_bssid = fapi_get_mgmt(skb)->bssid; |
| |
| ssid_ie = cfg80211_find_ie(WLAN_EID_SSID, mgmt->u.beacon.variable, ie_len); |
| ssid_len = ssid_ie[1]; |
| scan_ssid = &ssid_ie[2]; |
| beacon_int = mgmt->u.beacon.beacon_int; |
| timestamp = mgmt->u.beacon.timestamp; |
| |
| SLSI_NET_DBG2(dev, SLSI_RX, |
| "forward_beacon from bssid:%pM beacon_int:%u timestamp:%llu system_time:%llu\n", |
| fapi_get_mgmt(skb)->bssid, beacon_int, timestamp, |
| (u64)TIMESPEC_TO_US(sys_time)); |
| |
| ret = slsi_send_forward_beacon_vendor_event(sdev, scan_ssid, ssid_len, scan_bssid, |
| channel, beacon_int, timestamp, |
| (u64)TIMESPEC_TO_US(sys_time)); |
| if (ret) |
| SLSI_ERR(sdev, "Failed to forward beacon_event\n"); |
| } |
| #endif |
| |
| static void slsi_scan_update_ssid_map(struct slsi_dev *sdev, struct net_device *dev, u16 scan_id) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct ieee80211_mgmt *mgmt; |
| const u8 *ssid_ie = NULL, *connected_ssid = NULL; |
| int i, found = 0, is_connected = 0; |
| struct slsi_scan_result *scan_result = NULL; |
| int band; |
| |
| WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex)); |
| WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->scan_result_mutex)); |
| |
| if (ndev_vif->activated && ndev_vif->vif_type == FAPI_VIFTYPE_STATION && ndev_vif->sta.sta_bss) { |
| band = (ndev_vif->sta.sta_bss->channel->center_freq / |
| 1000) == 2 ? SLSI_FREQ_BAND_2GHZ : SLSI_FREQ_BAND_5GHZ; |
| is_connected = 1; |
| connected_ssid = cfg80211_find_ie(WLAN_EID_SSID, ndev_vif->sta.sta_bss->ies->data, ndev_vif->sta.sta_bss->ies->len); |
| } |
| |
| /* sanitize map: [remove any old entries] */ |
| for (i = 0; i < SLSI_SCAN_SSID_MAP_MAX; i++) { |
| found = 0; |
| if (!sdev->ssid_map[i].ssid_len) |
| continue; |
| |
| /* We are connected to this hidden AP. So no need to check if this AP is present in scan results */ |
| if (is_connected && SLSI_ETHER_EQUAL(ndev_vif->sta.sta_bss->bssid, sdev->ssid_map[i].bssid) && |
| sdev->ssid_map[i].band == band) |
| continue; |
| |
| /* If this entry AP is found to be non-hidden, remove entry. */ |
| scan_result = ndev_vif->scan[scan_id].scan_results; |
| while (scan_result) { |
| if (SLSI_ETHER_EQUAL(sdev->ssid_map[i].bssid, scan_result->bssid) && |
| sdev->ssid_map[i].band == scan_result->band) { |
| /* AP is no more hidden. OR AP is hidden but did not |
| * receive probe resp. Go for expiry. |
| */ |
| if (!scan_result->hidden || (scan_result->hidden && !scan_result->probe_resp)) |
| sdev->ssid_map[i].age = SLSI_SCAN_SSID_MAP_EXPIRY_AGE; |
| else |
| found = 1; |
| break; |
| } |
| scan_result = scan_result->next; |
| } |
| |
| if (!found) { |
| sdev->ssid_map[i].age++; |
| if (sdev->ssid_map[i].age > SLSI_SCAN_SSID_MAP_EXPIRY_AGE) { |
| sdev->ssid_map[i].ssid_len = 0; |
| sdev->ssid_map[i].age = 0; |
| } |
| } |
| } |
| |
| scan_result = ndev_vif->scan[scan_id].scan_results; |
| /* update/add hidden bss with known ssid */ |
| while (scan_result) { |
| ssid_ie = NULL; |
| |
| if (scan_result->hidden) { |
| if (is_connected && SLSI_ETHER_EQUAL(ndev_vif->sta.sta_bss->bssid, scan_result->bssid) && |
| scan_result->band == band) { |
| ssid_ie = connected_ssid; |
| } else if (scan_result->probe_resp) { |
| mgmt = fapi_get_mgmt(scan_result->probe_resp); |
| ssid_ie = cfg80211_find_ie(WLAN_EID_SSID, mgmt->u.beacon.variable, fapi_get_mgmtlen(scan_result->probe_resp) - (mgmt->u.beacon.variable - (u8 *)mgmt)); |
| } |
| } |
| |
| if (!ssid_ie) { |
| scan_result = scan_result->next; |
| continue; |
| } |
| |
| found = 0; |
| /* if this bss is in map, update map */ |
| for (i = 0; i < SLSI_SCAN_SSID_MAP_MAX; i++) { |
| if (!sdev->ssid_map[i].ssid_len) |
| continue; |
| if (SLSI_ETHER_EQUAL(scan_result->bssid, sdev->ssid_map[i].bssid) && |
| scan_result->band == sdev->ssid_map[i].band) { |
| sdev->ssid_map[i].ssid_len = ssid_ie[1]; |
| memcpy(sdev->ssid_map[i].ssid, &ssid_ie[2], ssid_ie[1]); |
| found = 1; |
| break; |
| } |
| } |
| if (!found) { |
| /* add a new entry in map */ |
| for (i = 0; i < SLSI_SCAN_SSID_MAP_MAX; i++) { |
| if (sdev->ssid_map[i].ssid_len) |
| continue; |
| SLSI_ETHER_COPY(sdev->ssid_map[i].bssid, scan_result->bssid); |
| sdev->ssid_map[i].age = 0; |
| sdev->ssid_map[i].ssid_len = ssid_ie[1]; |
| sdev->ssid_map[i].band = scan_result->band; |
| memcpy(sdev->ssid_map[i].ssid, &ssid_ie[2], ssid_ie[1]); |
| break; |
| } |
| } |
| scan_result = scan_result->next; |
| } |
| } |
| |
| void slsi_scan_complete(struct slsi_dev *sdev, struct net_device *dev, u16 scan_id, bool aborted, |
| bool flush_scan_results) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct sk_buff *scan; |
| int count = 0; |
| int *result_count = NULL, max_count = 0; |
| int scan_results_count = 0; |
| int more_than_max_count = 0; |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| struct list_head *pos, *q, *blacklist_pos, *blacklist_q; |
| #endif |
| |
| if (WARN_ON(scan_id >= SLSI_SCAN_MAX)) |
| return; |
| |
| if (scan_id == SLSI_SCAN_HW_ID && !ndev_vif->scan[scan_id].scan_req) |
| return; |
| |
| if (WARN_ON(scan_id == SLSI_SCAN_SCHED_ID && !ndev_vif->scan[scan_id].sched_req)) |
| return; |
| |
| SLSI_MUTEX_LOCK(ndev_vif->scan_result_mutex); |
| if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) { |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| if (flush_scan_results) { |
| list_for_each_safe(pos, q, &ndev_vif->sta.ssid_info) { |
| struct slsi_ssid_info *ssid_info; |
| struct list_head *bssid_pos, *p; |
| |
| ssid_info = list_entry(pos, struct slsi_ssid_info, list); |
| list_for_each_safe(bssid_pos, p, &ssid_info->bssid_list) { |
| struct slsi_bssid_info *bssid_info; |
| |
| bssid_info = list_entry(bssid_pos, struct slsi_bssid_info, list); |
| list_del(bssid_pos); |
| kfree(bssid_info); |
| } |
| list_del(pos); |
| kfree(ssid_info); |
| } |
| INIT_LIST_HEAD(&ndev_vif->sta.ssid_info); |
| } |
| list_for_each_safe(blacklist_pos, blacklist_q, &ndev_vif->sta.blacklist_head) { |
| struct slsi_bssid_blacklist_info *blacklist_info; |
| |
| blacklist_info = list_entry(blacklist_pos, struct slsi_bssid_blacklist_info, list); |
| if (blacklist_info && (jiffies_to_msecs(jiffies) > blacklist_info->end_time)) { |
| list_del(blacklist_pos); |
| kfree(blacklist_info); |
| } |
| } |
| slsi_populate_ssid_info(sdev, ndev_vif, scan_id); |
| #endif |
| slsi_scan_update_ssid_map(sdev, dev, scan_id); |
| max_count = slsi_dev_get_scan_result_count(); |
| } |
| |
| result_count = &count; |
| scan = slsi_dequeue_cached_scan_result(&ndev_vif->scan[scan_id], result_count); |
| while (scan) { |
| scan_results_count++; |
| /* skb freed inside slsi_rx_scan_pass_to_cfg80211 */ |
| slsi_rx_scan_pass_to_cfg80211(sdev, dev, scan); |
| |
| if ((SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) && (*result_count >= max_count)) { |
| more_than_max_count = 1; |
| slsi_purge_scan_results_locked(ndev_vif, scan_id); |
| break; |
| } |
| scan = slsi_dequeue_cached_scan_result(&ndev_vif->scan[scan_id], result_count); |
| } |
| SLSI_INFO(sdev, "Scan count:%d APs\n", scan_results_count); |
| SLSI_NET_DBG3(dev, SLSI_MLME, "interface:%d, scan_id:%d,%s\n", ndev_vif->ifnum, scan_id, |
| more_than_max_count ? "Scan results overflow" : ""); |
| slsi_roam_channel_cache_prune(dev, SLSI_ROAMING_CHANNEL_CACHE_TIMEOUT); |
| |
| if (scan_id == SLSI_SCAN_HW_ID) { |
| if (SLSI_IS_VIF_INDEX_P2P(ndev_vif) && (!SLSI_IS_P2P_GROUP_STATE(sdev))) { |
| /* Check for unsync vif as it could be present during the cycle of social channel |
| * scan and listen |
| */ |
| if (ndev_vif->activated) |
| SLSI_P2P_STATE_CHANGE(sdev, P2P_IDLE_VIF_ACTIVE); |
| else |
| SLSI_P2P_STATE_CHANGE(sdev, P2P_IDLE_NO_VIF); |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) |
| cfg80211_scan_done(ndev_vif->scan[scan_id].scan_req, &info); |
| #else |
| cfg80211_scan_done(ndev_vif->scan[scan_id].scan_req, aborted); |
| #endif |
| |
| ndev_vif->scan[scan_id].scan_req = NULL; |
| ndev_vif->scan[scan_id].requeue_timeout_work = false; |
| } |
| |
| if (scan_id == SLSI_SCAN_SCHED_ID) |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) |
| cfg80211_sched_scan_results(sdev->wiphy, ndev_vif->scan[scan_id].sched_req->reqid); |
| #else |
| cfg80211_sched_scan_results(sdev->wiphy); |
| #endif |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex); |
| } |
| |
| int slsi_set_2g_auto_channel(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| struct slsi_acs_selected_channels *acs_selected_channels, |
| struct slsi_acs_chan_info *ch_info) |
| { |
| int i = 0, j = 0, avg_load, total_num_ap, total_rssi, adjacent_rssi; |
| bool all_bss_load = true; |
| int min_avg_chan_utilization = INT_MAX, min_adjacent_rssi = INT_MAX; |
| int ch_idx_min_load = 0, ch_idx_min_rssi = 0; |
| int min_avg_chan_utilization_20 = INT_MAX, min_adjacent_rssi_20 = INT_MAX; |
| int ch_idx_min_load_20 = 0, ch_idx_min_rssi_20 = 0; |
| int ret = 0; |
| int ch_list_len = MAX_24G_CHANNELS; |
| |
| acs_selected_channels->ch_width = ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->ch_width; |
| acs_selected_channels->hw_mode = ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->hw_mode; |
| |
| SLSI_DBG3(sdev, SLSI_MLME, "ch_lis_len:%d\n", ch_list_len); |
| for (i = 0; i < ch_list_len; i++) { |
| if (!ch_info[i].chan) |
| continue; |
| adjacent_rssi = 0; /* Assuming ch_list is in sorted order. */ |
| for (j = -2; j <= 2; j++) |
| if (i + j >= 0 && i + j < ch_list_len) |
| adjacent_rssi += ch_info[i + j].rssi_factor; |
| ch_info[i].adj_rssi_factor = adjacent_rssi; |
| if (ch_info[i].num_bss_load_ap != 0) { |
| ch_info[i].avg_chan_utilization = ch_info[i].total_chan_utilization / |
| ch_info[i].num_bss_load_ap; |
| if (ch_info[i].avg_chan_utilization < min_avg_chan_utilization_20) { |
| min_avg_chan_utilization_20 = ch_info[i].avg_chan_utilization; |
| ch_idx_min_load_20 = i; |
| } else if (ch_info[i].avg_chan_utilization == min_avg_chan_utilization_20 && |
| ch_info[i].num_ap < ch_info[ch_idx_min_load_20].num_ap) { |
| ch_idx_min_load_20 = i; |
| } |
| } else { |
| SLSI_DBG3(sdev, SLSI_MLME, "BSS load IE not found\n"); |
| all_bss_load = false; |
| } |
| if (adjacent_rssi < min_adjacent_rssi_20) { |
| min_adjacent_rssi_20 = adjacent_rssi; |
| ch_idx_min_rssi_20 = i; |
| } else if (adjacent_rssi == min_adjacent_rssi_20 && |
| ch_info[i].num_ap < ch_info[ch_idx_min_rssi_20].num_ap) { |
| ch_idx_min_rssi_20 = i; |
| } |
| SLSI_DBG3(sdev, SLSI_MLME, "min rssi:%d min_rssi_idx:%d\n", min_adjacent_rssi_20, ch_idx_min_rssi_20); |
| SLSI_DBG3(sdev, SLSI_MLME, "num_ap:%d,chan:%d,total_util:%d,avg_util:%d,rssi_fac:%d,adj_rssi_fac:%d," |
| "bss_ap:%d\n", ch_info[i].num_ap, ch_info[i].chan, ch_info[i].total_chan_utilization, |
| ch_info[i].avg_chan_utilization, ch_info[i].rssi_factor, ch_info[i].adj_rssi_factor, |
| ch_info[i].num_bss_load_ap); |
| } |
| |
| if (acs_selected_channels->ch_width == 40) { |
| for (i = 0; i < ch_list_len; i++) { |
| if (i + 4 >= ch_list_len || !ch_info[i + 4].chan || !ch_info[i].chan) |
| continue; |
| avg_load = ch_info[i].avg_chan_utilization + ch_info[i + 4].avg_chan_utilization; |
| total_num_ap = ch_info[i].num_ap + ch_info[i + 4].num_ap; |
| total_rssi = ch_info[i].adj_rssi_factor + ch_info[i + 4].adj_rssi_factor; |
| |
| if (avg_load < min_avg_chan_utilization) { |
| min_avg_chan_utilization = avg_load; |
| ch_idx_min_load = i; |
| } else if (avg_load == min_avg_chan_utilization && |
| total_num_ap < ch_info[ch_idx_min_load].num_ap + |
| ch_info[ch_idx_min_load + 4].num_ap) { |
| ch_idx_min_load = i; |
| } |
| if (total_rssi < min_adjacent_rssi) { |
| min_adjacent_rssi = total_rssi; |
| ch_idx_min_rssi = i; |
| } else if (total_rssi == min_adjacent_rssi && |
| total_num_ap < ch_info[ch_idx_min_rssi].num_ap + |
| ch_info[ch_idx_min_rssi + 4].num_ap) { |
| ch_idx_min_rssi = i; |
| } |
| } |
| if (all_bss_load) { |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_load].chan; |
| acs_selected_channels->sec_channel = ch_info[ch_idx_min_load].chan + 4; |
| } else { |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_rssi].chan; |
| acs_selected_channels->sec_channel = ch_info[ch_idx_min_rssi].chan + 4; |
| } |
| |
| if (!acs_selected_channels->pri_channel) |
| acs_selected_channels->ch_width = 20; |
| } |
| |
| if (acs_selected_channels->ch_width == 20) { |
| if (all_bss_load) |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_load_20].chan; |
| else |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_rssi_20].chan; |
| } |
| return ret; |
| } |
| |
| int slsi_is_40mhz_5gchan(u8 pri_channel, u8 sec_channel) |
| { |
| int slsi_40mhz_chan[12] = {38, 46, 54, 62, 102, 110, 118, 126, 134, 142, 151, 159}; |
| int i; |
| |
| for (i = 0; i < 12; i++) { |
| if (pri_channel == slsi_40mhz_chan[i] - 2 && sec_channel == slsi_40mhz_chan[i] + 2) |
| return 1; |
| else if (pri_channel < slsi_40mhz_chan[i]) |
| return 0; |
| } |
| return 0; |
| } |
| |
| int slsi_is_80mhz_5gchan(u8 pri_channel, u8 last_channel) |
| { |
| int slsi_80mhz_chan[6] = {42, 58, 106, 122, 138, 155}; |
| int i; |
| |
| for (i = 0; i < 6; i++) { |
| if (pri_channel == slsi_80mhz_chan[i] - 6 && last_channel == slsi_80mhz_chan[i] + 6) |
| return 1; |
| else if (pri_channel < slsi_80mhz_chan[i]) |
| return 0; |
| } |
| return 0; |
| } |
| |
| int slsi_set_5g_auto_channel(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| struct slsi_acs_selected_channels *acs_selected_channels, |
| struct slsi_acs_chan_info *ch_info) |
| { |
| int i = 0, avg_load, total_num_ap; |
| bool all_bss_load = true, none_bss_load = true; |
| int min_num_ap = INT_MAX, min_avg_chan_utilization = INT_MAX; |
| int ch_idx_min_load = 0, ch_idx_min_ap = 0; |
| int min_avg_chan_utilization_20 = INT_MAX, min_num_ap_20 = INT_MAX; |
| int ch_idx_min_load_20 = 0, ch_idx_min_ap_20 = 0; |
| int ret = 0; |
| int ch_list_len = MAX_5G_CHANNELS; |
| |
| acs_selected_channels->ch_width = ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->ch_width; |
| acs_selected_channels->hw_mode = ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->hw_mode; |
| |
| SLSI_DBG3(sdev, SLSI_MLME, "ch_lis_len:%d\n", ch_list_len); |
| for (i = 0; i < ch_list_len; i++) { |
| if (!ch_info[i].chan) |
| continue; |
| if (ch_info[i].num_bss_load_ap != 0) { |
| ch_info[i].avg_chan_utilization = ch_info[i].total_chan_utilization / |
| ch_info[i].num_bss_load_ap; |
| if (ch_info[i].avg_chan_utilization < min_avg_chan_utilization_20) { |
| min_avg_chan_utilization_20 = ch_info[i].avg_chan_utilization; |
| ch_idx_min_load_20 = i; |
| } else if (ch_info[i].avg_chan_utilization == min_avg_chan_utilization_20 && |
| ch_info[i].num_ap < ch_info[ch_idx_min_load_20].num_ap) { |
| ch_idx_min_load_20 = i; |
| } |
| none_bss_load = false; |
| } else { |
| if (ch_info[i].num_ap < min_num_ap_20) { |
| min_num_ap_20 = ch_info[i].num_ap; |
| ch_idx_min_ap_20 = i; |
| } |
| SLSI_DBG3(sdev, SLSI_MLME, "BSS load IE not found\n"); |
| ch_info[i].avg_chan_utilization = 128; |
| all_bss_load = false; |
| } |
| SLSI_DBG3(sdev, SLSI_MLME, "num_ap:%d chan:%d, total_chan_util:%d, avg_chan_util:%d, bss_load_ap:%d\n", |
| ch_info[i].num_ap, ch_info[i].chan, ch_info[i].total_chan_utilization, |
| ch_info[i].avg_chan_utilization, ch_info[i].num_bss_load_ap); |
| } |
| |
| if (acs_selected_channels->ch_width == 80) { |
| for (i = 0; i < ch_list_len; i++) { |
| if (i + 3 >= ch_list_len) |
| continue; |
| if (!ch_info[i].chan || !ch_info[i + 1].chan || !ch_info[i + 2].chan || !ch_info[i + 3].chan) |
| continue; |
| if (slsi_is_80mhz_5gchan(ch_info[i].chan, ch_info[i + 3].chan)) { |
| avg_load = ch_info[i].avg_chan_utilization + ch_info[i + 1].avg_chan_utilization + |
| ch_info[i + 2].avg_chan_utilization + ch_info[i + 3].avg_chan_utilization; |
| total_num_ap = ch_info[i].num_ap + ch_info[i + 1].num_ap + ch_info[i + 2].num_ap + |
| ch_info[i + 3].num_ap; |
| if (avg_load < min_avg_chan_utilization) { |
| min_avg_chan_utilization = avg_load; |
| ch_idx_min_load = i; |
| } else if (avg_load == min_avg_chan_utilization && total_num_ap < |
| (ch_info[ch_idx_min_load].num_ap + ch_info[ch_idx_min_load + 1].num_ap + |
| ch_info[ch_idx_min_load + 2].num_ap + |
| ch_info[ch_idx_min_load + 3].num_ap)) { |
| ch_idx_min_load = i; |
| } |
| if (total_num_ap < min_num_ap) { |
| min_num_ap = total_num_ap; |
| ch_idx_min_ap = i; |
| } |
| } |
| } |
| if (all_bss_load || min_avg_chan_utilization <= 512) { |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_load].chan; |
| acs_selected_channels->vht_seg0_center_ch = ch_info[ch_idx_min_load].chan + 6; |
| } else if (none_bss_load || min_avg_chan_utilization > 512) { |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_ap].chan; |
| acs_selected_channels->vht_seg0_center_ch = ch_info[ch_idx_min_ap].chan + 6; |
| } |
| |
| if (!acs_selected_channels->pri_channel) |
| acs_selected_channels->ch_width = 40; |
| } |
| |
| if (acs_selected_channels->ch_width == 40) { |
| for (i = 0; i < ch_list_len; i++) { |
| if (!ch_info[i].chan || i + 1 >= ch_list_len || !ch_info[i + 1].chan) |
| continue; |
| if (slsi_is_40mhz_5gchan(ch_info[i].chan, ch_info[i + 1].chan)) { |
| avg_load = ch_info[i].avg_chan_utilization + ch_info[i + 1].avg_chan_utilization; |
| total_num_ap = ch_info[i].num_ap + ch_info[i + 1].num_ap; |
| if (avg_load < min_avg_chan_utilization) { |
| min_avg_chan_utilization = avg_load; |
| ch_idx_min_load = i; |
| } else if (avg_load == min_avg_chan_utilization && total_num_ap < |
| ch_info[ch_idx_min_load].num_ap + ch_info[ch_idx_min_load + 1].num_ap) { |
| ch_idx_min_load = i; |
| } |
| if (total_num_ap < min_num_ap) { |
| min_num_ap = total_num_ap; |
| ch_idx_min_ap = i; |
| } |
| } |
| } |
| if (all_bss_load || min_avg_chan_utilization <= 256) { |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_load].chan; |
| acs_selected_channels->sec_channel = ch_info[ch_idx_min_load + 1].chan; |
| } else if (none_bss_load || min_avg_chan_utilization > 256) { |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_ap].chan; |
| acs_selected_channels->sec_channel = ch_info[ch_idx_min_ap + 1].chan; |
| } |
| |
| if (!acs_selected_channels->pri_channel) |
| acs_selected_channels->ch_width = 20; |
| } |
| |
| if (acs_selected_channels->ch_width == 20) { |
| if (all_bss_load || min_avg_chan_utilization_20 < 128) |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_load_20].chan; |
| else if (none_bss_load || min_avg_chan_utilization_20 >= 128) |
| acs_selected_channels->pri_channel = ch_info[ch_idx_min_ap_20].chan; |
| } |
| return ret; |
| } |
| |
| int slsi_set_band_any_auto_channel(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, |
| struct slsi_acs_selected_channels *acs_selected_channels, |
| struct slsi_acs_chan_info *ch_info) |
| { |
| struct slsi_acs_chan_info ch_info_2g[MAX_24G_CHANNELS]; |
| struct slsi_acs_chan_info ch_info_5g[MAX_5G_CHANNELS]; |
| struct slsi_acs_selected_channels acs_selected_channels_5g; |
| struct slsi_acs_selected_channels acs_selected_channels_2g; |
| int best_channel_5g = -1; |
| int best_channel_5g_num_ap = 0; |
| int best_channel_2g = -1; |
| int best_channel_2g_num_ap = 0; |
| int i, ret = 0; |
| int j = 0; |
| |
| memset(&acs_selected_channels_5g, 0, sizeof(acs_selected_channels_5g)); |
| memset(&acs_selected_channels_2g, 0, sizeof(acs_selected_channels_2g)); |
| memset(&ch_info_5g, 0, sizeof(ch_info_5g)); |
| memset(&ch_info_2g, 0, sizeof(ch_info_2g)); |
| |
| for (i = MAX_24G_CHANNELS; i < MAX_CHAN_VALUE_ACS; i++) { |
| ch_info_5g[j] = ch_info[i]; |
| j++; |
| } |
| ret = slsi_set_5g_auto_channel(sdev, ndev_vif, &acs_selected_channels_5g, ch_info_5g); |
| |
| if (ret == 0) { |
| best_channel_5g = acs_selected_channels_5g.pri_channel; |
| for (i = 0; i < MAX_5G_CHANNELS; i++) { |
| if (ch_info_5g[i].chan == best_channel_5g) { |
| best_channel_5g_num_ap = ch_info_5g[i].num_ap; |
| break; |
| } |
| } |
| SLSI_DBG3(sdev, SLSI_MLME, "Best 5G channel = %d, num_ap = %d\n", best_channel_5g, |
| best_channel_5g_num_ap); |
| |
| if (best_channel_5g_num_ap < MAX_AP_THRESHOLD) { |
| *acs_selected_channels = acs_selected_channels_5g; |
| acs_selected_channels->hw_mode = SLSI_ACS_MODE_IEEE80211A; |
| return ret; |
| } |
| } |
| |
| SLSI_DBG3(sdev, SLSI_MLME, "5G AP threshold exceed, trying to select from 2G band\n"); |
| |
| for (i = 0; i < MAX_24G_CHANNELS; i++) |
| ch_info_2g[i] = ch_info[i]; |
| ret = slsi_set_2g_auto_channel(sdev, ndev_vif, &acs_selected_channels_2g, ch_info_2g); |
| |
| if (ret == 0) { |
| best_channel_2g = acs_selected_channels_2g.pri_channel; |
| for (i = 0; i < MAX_24G_CHANNELS; i++) { |
| if (ch_info_2g[i].chan == best_channel_2g) { |
| best_channel_2g_num_ap = ch_info_2g[i].num_ap; |
| break; |
| } |
| } |
| SLSI_DBG3(sdev, SLSI_MLME, "Best 2G channel = %d, num_ap = %d\n", best_channel_2g, |
| best_channel_2g_num_ap); |
| if (best_channel_5g == -1) { |
| *acs_selected_channels = acs_selected_channels_2g; |
| acs_selected_channels->hw_mode = SLSI_ACS_MODE_IEEE80211G; |
| return ret; |
| } |
| /* Based on min no of APs selecting channel from that band */ |
| /* If no. of APs are equal, selecting the 5G channel */ |
| if (best_channel_5g_num_ap > best_channel_2g_num_ap) { |
| *acs_selected_channels = acs_selected_channels_2g; |
| acs_selected_channels->hw_mode = SLSI_ACS_MODE_IEEE80211G; |
| } else { |
| *acs_selected_channels = acs_selected_channels_5g; |
| acs_selected_channels->hw_mode = SLSI_ACS_MODE_IEEE80211A; |
| } |
| } |
| return ret; |
| } |
| |
| int slsi_acs_get_rssi_factor(struct slsi_dev *sdev, int rssi, u8 ch_util) |
| { |
| int frac_pow_val[10] = {10, 12, 15, 19, 25, 31, 39, 50, 63, 79}; |
| int res = 1; |
| int i; |
| |
| if (rssi < 0) |
| rssi = 0 - rssi; |
| else |
| return INT_MAX; |
| for (i = 0; i < rssi / 10; i++) |
| res *= 10; |
| res = (10000000 * ch_util / res) / frac_pow_val[rssi % 10]; |
| |
| SLSI_DBG3(sdev, SLSI_MLME, "ch_util:%d\n", ch_util); |
| return res; |
| } |
| |
| struct slsi_acs_chan_info *slsi_acs_scan_results(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, u16 scan_id) |
| { |
| struct sk_buff *scan_res; |
| struct sk_buff *unique_scan; |
| struct sk_buff_head unique_scan_results; |
| struct slsi_acs_chan_info *ch_info = ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->acs_chan_info; |
| |
| SLSI_DBG3(sdev, SLSI_MLME, "Received acs_results\n"); |
| skb_queue_head_init(&unique_scan_results); |
| SLSI_MUTEX_LOCK(ndev_vif->scan_result_mutex); |
| scan_res = slsi_dequeue_cached_scan_result(&ndev_vif->scan[SLSI_SCAN_HW_ID], NULL); |
| |
| while (scan_res) { |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(scan_res); |
| size_t mgmt_len = fapi_get_mgmtlen(scan_res); |
| struct ieee80211_channel *scan_channel; |
| int idx = 0; |
| const u8 *ie_data; |
| const u8 *ie; |
| int ie_len; |
| u8 ch_util = 128; |
| /* ieee80211_mgmt structure is similar for Probe Response and Beacons */ |
| size_t ies_len = mgmt_len - offsetof(struct ieee80211_mgmt, u.beacon.variable); |
| /* make sure this BSSID has not already been used */ |
| skb_queue_walk(&unique_scan_results, unique_scan) { |
| struct ieee80211_mgmt *unique_mgmt = fapi_get_mgmt(unique_scan); |
| |
| if (compare_ether_addr(mgmt->bssid, unique_mgmt->bssid) == 0) |
| goto next_scan; |
| } |
| slsi_skb_queue_head(&unique_scan_results, scan_res); |
| scan_channel = slsi_find_scan_channel(sdev, mgmt, mgmt_len, |
| fapi_get_u16(scan_res, u.mlme_scan_ind.channel_frequency) / 2); |
| if (!scan_channel) |
| goto next_scan; |
| SLSI_DBG3(sdev, SLSI_MLME, "scan result (scan_id:%d, %pM, channel:%d, rssi:%d, ie_len = %zu)\n", |
| fapi_get_u16(scan_res, u.mlme_scan_ind.scan_id), |
| fapi_get_mgmt(scan_res)->bssid, scan_channel->hw_value, |
| fapi_get_s16(scan_res, u.mlme_scan_ind.rssi), |
| ies_len); |
| |
| idx = slsi_find_chan_idx(scan_channel->hw_value, ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->hw_mode); |
| if (idx < 0) { |
| SLSI_DBG3(sdev, SLSI_MLME, "idx is not in range idx=%d\n", idx); |
| goto next_scan; |
| } |
| SLSI_DBG3(sdev, SLSI_MLME, "chan_idx:%d chan_value: %d\n", idx, ch_info[idx].chan); |
| |
| if (ch_info[idx].chan) { |
| ch_info[idx].num_ap += 1; |
| ie = cfg80211_find_ie(WLAN_EID_QBSS_LOAD, mgmt->u.beacon.variable, ies_len); |
| if (ie) { |
| ie_len = ie[1]; |
| ie_data = &ie[2]; |
| if (ie_len >= 3) { |
| ch_util = ie_data[2]; |
| ch_info[idx].num_bss_load_ap += 1; |
| ch_info[idx].total_chan_utilization += ch_util; |
| } |
| } |
| if (idx == scan_channel->hw_value - 1) { /*if 2.4GHZ channel */ |
| int res = 0; |
| |
| res = slsi_acs_get_rssi_factor(sdev, fapi_get_s16(scan_res, u.mlme_scan_ind.rssi), |
| ch_util); |
| ch_info[idx].rssi_factor += res; |
| SLSI_DBG3(sdev, SLSI_MLME, "ch_info[idx].rssi_factor:%d\n", ch_info[idx].rssi_factor); |
| } |
| } else { |
| goto next_scan; |
| } |
| next_scan: |
| scan_res = slsi_dequeue_cached_scan_result(&ndev_vif->scan[scan_id], NULL); |
| } |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex); |
| slsi_skb_queue_purge(&unique_scan_results); |
| return ch_info; |
| } |
| |
| void slsi_acs_scan_complete(struct slsi_dev *sdev, struct netdev_vif *ndev_vif, u16 scan_id) |
| { |
| struct slsi_acs_selected_channels acs_selected_channels; |
| struct slsi_acs_chan_info *ch_info; |
| int r = 0; |
| |
| memset(&acs_selected_channels, 0, sizeof(acs_selected_channels)); |
| ch_info = slsi_acs_scan_results(sdev, ndev_vif, scan_id); |
| if (ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->hw_mode == SLSI_ACS_MODE_IEEE80211A) |
| r = slsi_set_5g_auto_channel(sdev, ndev_vif, &acs_selected_channels, ch_info); |
| else if (ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->hw_mode == SLSI_ACS_MODE_IEEE80211B || |
| ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->hw_mode == SLSI_ACS_MODE_IEEE80211G) |
| r = slsi_set_2g_auto_channel(sdev, ndev_vif, &acs_selected_channels, ch_info); |
| else if (ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->hw_mode == SLSI_ACS_MODE_IEEE80211ANY) |
| r = slsi_set_band_any_auto_channel(sdev, ndev_vif, &acs_selected_channels, ch_info); |
| else |
| r = -EINVAL; |
| if (!r) { |
| r = slsi_send_acs_event(sdev, acs_selected_channels); |
| if (r != 0) |
| SLSI_ERR(sdev, "Could not send ACS vendor event up\n"); |
| } else { |
| SLSI_ERR(sdev, "set_auto_channel failed: %d\n", r); |
| } |
| sdev->acs_channel_switched = true; |
| kfree(ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request); |
| ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request = NULL; |
| } |
| |
| void slsi_rx_scan_done_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| u16 scan_id = fapi_get_u16(skb, u.mlme_scan_done_ind.scan_id); |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| SLSI_MUTEX_LOCK(ndev_vif->scan_mutex); |
| SLSI_NET_DBG3(dev, SLSI_GSCAN, "Received scan_id:%#x\n", scan_id); |
| |
| #ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE |
| if (slsi_is_gscan_id(scan_id)) { |
| SLSI_NET_DBG3(dev, SLSI_GSCAN, "scan_id:%#x\n", scan_id); |
| |
| slsi_gscan_handle_scan_result(sdev, dev, skb, scan_id, true); |
| |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| return; |
| } |
| #endif |
| /* set_cached_channels should be called here as well , apart from connect_ind as */ |
| /* we can get an AP with the same SSID in the scan results after connection. */ |
| /* This should only be done if we are in connected state.*/ |
| if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION && ndev_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED && |
| ndev_vif->iftype != NL80211_IFTYPE_P2P_CLIENT) { |
| const u8 *connected_ssid = NULL; |
| struct slsi_roaming_network_map_entry *network_map; |
| u32 channels_count = 0; |
| u8 channels[SLSI_ROAMING_CHANNELS_MAX]; |
| bool channel_bitmaps_matched = false; |
| |
| connected_ssid = cfg80211_find_ie(WLAN_EID_SSID, ndev_vif->sta.sta_bss->ies->data, |
| ndev_vif->sta.sta_bss->ies->len); |
| network_map = slsi_roam_channel_cache_get(dev, connected_ssid); |
| if (network_map) { |
| channel_bitmaps_matched = !(network_map->channels_24_ghz & ~ndev_vif->sta.channels_24_ghz) && |
| !(network_map->channels_5_ghz & ~ndev_vif->sta.channels_5_ghz); |
| if (!channel_bitmaps_matched) |
| channels_count = slsi_roam_channel_cache_get_channels_int(dev, network_map, channels); |
| } |
| |
| if (channels_count) { |
| ndev_vif->sta.channels_24_ghz = network_map->channels_24_ghz; |
| ndev_vif->sta.channels_5_ghz = network_map->channels_5_ghz; |
| if (slsi_mlme_set_cached_channels(sdev, dev, channels_count, channels) != 0) |
| SLSI_NET_ERR(dev, "MLME-SET-CACHED-CHANNELS.req failed\n"); |
| } |
| } |
| |
| scan_id = (scan_id & 0xFF); |
| |
| if (scan_id == SLSI_SCAN_HW_ID && (ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req || |
| ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request)) |
| cancel_delayed_work(&ndev_vif->scan_timeout_work); |
| if (ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request) |
| slsi_acs_scan_complete(sdev, ndev_vif, scan_id); |
| else |
| slsi_scan_complete(sdev, dev, scan_id, false, true); |
| |
| SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_rx_channel_switched_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| u16 freq = 0; |
| int width; |
| int primary_chan_pos; |
| u16 temp_chan_info; |
| struct cfg80211_chan_def chandef; |
| u16 cf1 = 0; |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| kfree_skb(skb); |
| return; |
| } |
| |
| temp_chan_info = fapi_get_u16(skb, u.mlme_channel_switched_ind.channel_information); |
| cf1 = fapi_get_u16(skb, u.mlme_channel_switched_ind.channel_frequency); |
| cf1 = cf1 / 2; |
| |
| primary_chan_pos = (temp_chan_info >> 8); |
| width = (temp_chan_info & 0x00FF); |
| |
| /* If width is 80MHz/40MHz then do frequency calculation, else store as it is */ |
| if (width == 40) |
| freq = cf1 + (primary_chan_pos * 20) - 10; |
| else if (width == 80) |
| freq = cf1 + (primary_chan_pos * 20) - 30; |
| else |
| freq = cf1; |
| |
| if (width == 20) |
| width = NL80211_CHAN_WIDTH_20; |
| else if (width == 40) |
| width = NL80211_CHAN_WIDTH_40; |
| else if (width == 80) |
| width = NL80211_CHAN_WIDTH_80; |
| else if (width == 160) |
| width = NL80211_CHAN_WIDTH_160; |
| |
| chandef.chan = ieee80211_get_channel(sdev->wiphy, freq); |
| if (!chandef.chan) { |
| SLSI_NET_WARN(dev, "invalid freq received (cf1=%d, temp_chan_info=%d, freq=%d)\n", |
| (int)cf1, (int)temp_chan_info, (int)freq); |
| goto exit; |
| } |
| chandef.width = width; |
| chandef.center_freq1 = cf1; |
| chandef.center_freq2 = 0; |
| |
| ndev_vif->ap.channel_freq = freq; /* updated for GETSTAINFO */ |
| ndev_vif->chan = chandef.chan; |
| |
| cfg80211_ch_switch_notify(dev, &chandef); |
| |
| exit: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| void __slsi_rx_blockack_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_peer *peer; |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, |
| "ma_blockack_ind(vif:%d, peer_qsta_address:%pM, parameter_set:%d," |
| "sequence_number:%d, reason_code:%d, direction:%d)\n", |
| fapi_get_vif(skb), |
| fapi_get_buff(skb, u.ma_blockack_ind.peer_qsta_address), |
| fapi_get_u16(skb, u.ma_blockack_ind.blockack_parameter_set), |
| fapi_get_u16(skb, u.ma_blockack_ind.sequence_number), |
| fapi_get_u16(skb, u.ma_blockack_ind.reason_code), |
| fapi_get_u16(skb, u.ma_blockack_ind.direction)); |
| |
| peer = slsi_get_peer_from_mac(sdev, dev, fapi_get_buff(skb, u.ma_blockack_ind.peer_qsta_address)); |
| |
| if (peer) { |
| /* Buffering of frames before the mlme_connected_ind */ |
| if (ndev_vif->vif_type == FAPI_VIFTYPE_AP && peer->connected_state == SLSI_STA_CONN_STATE_CONNECTING) { |
| SLSI_DBG3(sdev, SLSI_MLME, "Buffering MA-BlockAck.Indication\n"); |
| slsi_skb_queue_tail(&peer->buffered_frames, skb); |
| return; |
| } |
| slsi_handle_blockack( |
| dev, |
| peer, |
| fapi_get_vif(skb), |
| fapi_get_buff(skb, u.ma_blockack_ind.peer_qsta_address), |
| fapi_get_u16(skb, u.ma_blockack_ind.blockack_parameter_set), |
| fapi_get_u16(skb, u.ma_blockack_ind.sequence_number), |
| fapi_get_u16(skb, u.ma_blockack_ind.reason_code), |
| fapi_get_u16(skb, u.ma_blockack_ind.direction) |
| ); |
| } else { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "no Peer found, cannot handle BA indication\n"); |
| } |
| |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_rx_blockack_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| kfree_skb(skb); |
| return; |
| } |
| __slsi_rx_blockack_ind(sdev, dev, skb); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| } |
| |
| static bool get_wmm_ie_from_resp_ie(struct slsi_dev *sdev, struct net_device *dev, u8 *resp_ie, size_t resp_ie_len, const u8 **wmm_elem, u16 *wmm_elem_len) |
| { |
| struct ieee80211_vendor_ie *ie; |
| |
| SLSI_UNUSED_PARAMETER(sdev); |
| |
| if (!resp_ie) { |
| SLSI_NET_ERR(dev, "Received invalid pointer to the ie's of the association response\n"); |
| return false; |
| } |
| |
| *wmm_elem = resp_ie; |
| while (*wmm_elem && (*wmm_elem - resp_ie < resp_ie_len)) { |
| /* parse response ie elements and return the wmm ie */ |
| *wmm_elem = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WMM, *wmm_elem, |
| resp_ie_len - (*wmm_elem - resp_ie)); |
| /* re-assoc-res can contain wmm parameter IE and wmm TSPEC IE. |
| * we want wmm parameter Element) |
| */ |
| if (*wmm_elem && (*wmm_elem)[1] > 6 && (*wmm_elem)[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) |
| break; |
| if (*wmm_elem) |
| *wmm_elem += (*wmm_elem)[1]; |
| } |
| |
| if (!(*wmm_elem)) { |
| SLSI_NET_DBG2(dev, SLSI_MLME, "No WMM IE\n"); |
| return false; |
| } |
| ie = (struct ieee80211_vendor_ie *)*wmm_elem; |
| *wmm_elem_len = ie->len + 2; |
| |
| SLSI_NET_DBG3(dev, SLSI_MLME, "WMM IE received and parsed successfully\n"); |
| return true; |
| } |
| |
| static bool sta_wmm_update_uapsd(struct slsi_dev *sdev, struct net_device *dev, struct slsi_peer *peer, u8 *assoc_req_ie, size_t assoc_req_ie_len) |
| { |
| const u8 *wmm_information_ie; |
| |
| if (!assoc_req_ie) { |
| SLSI_NET_ERR(dev, "null reference to IE\n"); |
| return false; |
| } |
| |
| wmm_information_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WMM, assoc_req_ie, assoc_req_ie_len); |
| if (!wmm_information_ie) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "no WMM IE\n"); |
| return false; |
| } |
| |
| peer->uapsd = wmm_information_ie[8]; |
| SLSI_NET_DBG1(dev, SLSI_MLME, "peer->uapsd = 0x%x\n", peer->uapsd); |
| return true; |
| } |
| |
| static bool sta_wmm_update_wmm_ac_ies(struct slsi_dev *sdev, struct net_device *dev, struct slsi_peer *peer, |
| u8 *assoc_rsp_ie, size_t assoc_rsp_ie_len) |
| { |
| u16 left; |
| const u8 *pos; |
| const u8 *wmm_elem = NULL; |
| u16 wmm_elem_len = 0; |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_wmm_ac *wmm_ac = &ndev_vif->sta.wmm_ac[0]; |
| |
| if (!get_wmm_ie_from_resp_ie(sdev, dev, assoc_rsp_ie, assoc_rsp_ie_len, &wmm_elem, &wmm_elem_len)) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "No WMM IE received\n"); |
| return false; |
| } |
| |
| if (wmm_elem_len < 10 || wmm_elem[7] /* version */ != 1) { |
| SLSI_NET_WARN(dev, "Invalid WMM IE: wmm_elem_len=%lu, wmm_elem[7]=%d\n", (unsigned long int)wmm_elem_len, (int)wmm_elem[7]); |
| return false; |
| } |
| |
| pos = wmm_elem + 10; |
| left = wmm_elem_len - 10; |
| |
| for (; left >= 4; left -= 4, pos += 4) { |
| int aci = (pos[0] >> 5) & 0x03; |
| int acm = (pos[0] >> 4) & 0x01; |
| |
| memcpy(wmm_ac, pos, sizeof(struct slsi_wmm_ac)); |
| |
| switch (aci) { |
| case 1: /* AC_BK */ |
| if (acm) |
| peer->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ |
| break; |
| case 2: /* AC_VI */ |
| if (acm) |
| peer->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ |
| break; |
| case 3: /* AC_VO */ |
| if (acm) |
| peer->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ |
| break; |
| case 0: /* AC_BE */ |
| default: |
| if (acm) |
| peer->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ |
| break; |
| } |
| wmm_ac++; |
| } |
| |
| SLSI_NET_DBG3(dev, SLSI_MLME, "WMM ies have been updated successfully\n"); |
| return true; |
| } |
| |
| #ifdef CONFIG_SCSC_WLAN_KEY_MGMT_OFFLOAD |
| enum slsi_wlan_vendor_attr_roam_auth { |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0, |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE, |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE, |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, |
| SLSI_WLAN_VENDOR_ATTR_ROAM_BEACON_IE, |
| /* keep last */ |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = |
| SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1 |
| }; |
| |
| int slsi_send_roam_vendor_event(struct slsi_dev *sdev, const u8 *bssid, |
| const u8 *req_ie, u32 req_ie_len, const u8 *resp_ie, u32 resp_ie_len, |
| const u8 *beacon_ie, u32 beacon_ie_len, bool authorized) |
| { |
| bool is_secured_bss; |
| struct sk_buff *skb = NULL; |
| u8 err = 0; |
| |
| is_secured_bss = cfg80211_find_ie(WLAN_EID_RSN, req_ie, req_ie_len) || |
| cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPA, req_ie, req_ie_len); |
| |
| SLSI_DBG2(sdev, SLSI_MLME, "authorized:%d, is_secured_bss:%d\n", authorized, is_secured_bss); |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) |
| skb = cfg80211_vendor_event_alloc(sdev->wiphy, NULL, NLMSG_DEFAULT_SIZE, |
| SLSI_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH, GFP_KERNEL); |
| #else |
| skb = cfg80211_vendor_event_alloc(sdev->wiphy, NLMSG_DEFAULT_SIZE, |
| SLSI_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH, GFP_KERNEL); |
| #endif |
| if (!skb) { |
| SLSI_ERR_NODEV("Failed to allocate skb for VENDOR Roam event\n"); |
| return -ENOMEM; |
| } |
| |
| err |= nla_put(skb, SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, ETH_ALEN, bssid) ? BIT(1) : 0; |
| err |= nla_put(skb, SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, 1, &authorized) ? BIT(2) : 0; |
| err |= (req_ie && nla_put(skb, SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE, req_ie_len, req_ie)) ? BIT(3) : 0; |
| err |= (resp_ie && nla_put(skb, SLSI_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE, resp_ie_len, resp_ie)) ? BIT(4) : 0; |
| err |= (beacon_ie && nla_put(skb, SLSI_WLAN_VENDOR_ATTR_ROAM_BEACON_IE, beacon_ie_len, beacon_ie)) ? BIT(5) : 0; |
| if (err) { |
| SLSI_ERR_NODEV("Failed nla_put ,req_ie_len=%d,resp_ie_len=%d,beacon_ie_len=%d,condition_failed=%d\n", |
| req_ie_len, resp_ie_len, beacon_ie_len, err); |
| slsi_kfree_skb(skb); |
| return -EINVAL; |
| } |
| SLSI_DBG3_NODEV(SLSI_MLME, "Event: KEY_MGMT_ROAM_AUTH(%d)\n", SLSI_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH); |
| cfg80211_vendor_event(skb, GFP_KERNEL); |
| return 0; |
| } |
| #endif /* offload */ |
| |
| void slsi_rx_roamed_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| struct slsi_peer *peer; |
| u16 temporal_keys_required = fapi_get_u16(skb, u.mlme_roamed_ind.temporal_keys_required); |
| struct ieee80211_channel *cur_channel = NULL; |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) |
| enum ieee80211_privacy bss_privacy; |
| #endif |
| |
| rtnl_lock(); |
| if (cancel_work_sync(&ndev_vif->set_multicast_filter_work)) |
| slsi_wakeunlock(&sdev->wlan_wl); |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_roamed_ind(vif:%d) Roaming to %pM\n", |
| fapi_get_vif(skb), |
| mgmt->bssid); |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit; |
| } |
| |
| peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET); |
| if (WARN_ON(!peer)) |
| goto exit; |
| |
| if (WARN_ON(!ndev_vif->sta.sta_bss)) |
| goto exit; |
| |
| slsi_rx_ba_stop_all(dev, peer); |
| |
| SLSI_ETHER_COPY(peer->address, mgmt->bssid); |
| |
| if (ndev_vif->sta.mlme_scan_ind_skb) { |
| /* saved skb [mlme_scan_ind] freed inside slsi_rx_scan_pass_to_cfg80211 */ |
| cur_channel = slsi_rx_scan_pass_to_cfg80211(sdev, dev, ndev_vif->sta.mlme_scan_ind_skb); |
| ndev_vif->sta.mlme_scan_ind_skb = NULL; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) |
| if (ndev_vif->sta.sta_bss->capability & WLAN_CAPABILITY_PRIVACY) |
| bss_privacy = IEEE80211_PRIVACY_ON; |
| else |
| bss_privacy = IEEE80211_PRIVACY_OFF; |
| #endif |
| |
| slsi_cfg80211_put_bss(sdev->wiphy, ndev_vif->sta.sta_bss); |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) |
| ndev_vif->sta.sta_bss = cfg80211_get_bss(sdev->wiphy, cur_channel, peer->address, NULL, 0, |
| IEEE80211_BSS_TYPE_ANY, bss_privacy); |
| #else |
| ndev_vif->sta.sta_bss = cfg80211_get_bss(sdev->wiphy, cur_channel, peer->address, NULL, 0, 0, 0); |
| #endif |
| |
| if (!ndev_vif->sta.sta_bss || !ndev_vif->sta.roam_mlme_procedure_started_ind) { |
| if (!ndev_vif->sta.sta_bss) |
| SLSI_INFO(sdev, "BSS not updated in cfg80211\n"); |
| if (!ndev_vif->sta.roam_mlme_procedure_started_ind) |
| SLSI_INFO(sdev, "procedure-started-ind not received before roamed-ind\n"); |
| netif_carrier_off(dev); |
| slsi_mlme_disconnect(sdev, dev, peer->address, 0, true); |
| slsi_handle_disconnect(sdev, dev, peer->address, 0, NULL, 0); |
| } else { |
| u8 *assoc_ie = NULL; |
| size_t assoc_ie_len = 0; |
| u8 *assoc_rsp_ie = NULL; |
| size_t assoc_rsp_ie_len = 0; |
| |
| slsi_peer_reset_stats(sdev, dev, peer); |
| slsi_peer_update_assoc_req(sdev, dev, peer, ndev_vif->sta.roam_mlme_procedure_started_ind); |
| slsi_peer_update_assoc_rsp(sdev, dev, peer, skb); |
| |
| /* skb is consumed by slsi_peer_update_assoc_rsp. So do not access this anymore. */ |
| skb = NULL; |
| |
| if (peer->assoc_ie) { |
| assoc_ie = peer->assoc_ie->data; |
| assoc_ie_len = peer->assoc_ie->len; |
| } |
| |
| if (peer->assoc_resp_ie) { |
| assoc_rsp_ie = peer->assoc_resp_ie->data; |
| assoc_rsp_ie_len = peer->assoc_resp_ie->len; |
| } |
| |
| /* this is the right place to initialize the bitmasks for |
| * acm bit and tspec establishment |
| */ |
| peer->wmm_acm = 0; |
| peer->tspec_established = 0; |
| peer->uapsd = 0; |
| |
| /* update the uapsd bitmask according to the bit values |
| * in wmm information element of association request |
| */ |
| if (!sta_wmm_update_uapsd(sdev, dev, peer, assoc_ie, assoc_ie_len)) |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Fail to update WMM uapsd\n"); |
| |
| /* update the acm bitmask according to the acm bit values that |
| * are included in wmm ie element of association response |
| */ |
| if (!sta_wmm_update_wmm_ac_ies(sdev, dev, peer, assoc_rsp_ie, assoc_rsp_ie_len)) |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Fail to update WMM AC ies\n"); |
| |
| ndev_vif->sta.roam_mlme_procedure_started_ind = NULL; |
| |
| if (temporal_keys_required) { |
| peer->pairwise_key_set = 0; |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DOING_KEY_CONFIG); |
| } |
| |
| WARN_ON(assoc_ie_len && !assoc_ie); |
| WARN_ON(assoc_rsp_ie_len && !assoc_rsp_ie); |
| |
| SLSI_NET_DBG3(dev, SLSI_MLME, "cfg80211_roamed()\n"); |
| cfg80211_roamed(dev, |
| ndev_vif->sta.sta_bss->channel, |
| peer->address, |
| assoc_ie, |
| assoc_ie_len, |
| assoc_rsp_ie, |
| assoc_rsp_ie_len, |
| GFP_KERNEL); |
| #ifdef CONFIG_SCSC_WLAN_KEY_MGMT_OFFLOAD |
| if (slsi_send_roam_vendor_event(sdev, peer->address, assoc_ie, assoc_ie_len, |
| assoc_rsp_ie, assoc_rsp_ie_len, |
| ndev_vif->sta.sta_bss->ies->data, ndev_vif->sta.sta_bss->ies->len, |
| !temporal_keys_required) != 0) { |
| SLSI_NET_ERR(dev, "Could not send Roam vendor event up"); |
| } |
| #endif |
| SLSI_NET_DBG3(dev, SLSI_MLME, "cfg80211_roamed() Done\n"); |
| |
| ndev_vif->sta.roam_in_progress = false; |
| ndev_vif->chan = ndev_vif->sta.sta_bss->channel; |
| SLSI_ETHER_COPY(ndev_vif->sta.bssid, peer->address); |
| #ifndef SLSI_TEST_DEV |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Taking a wakelock for dhcp to finish after roaming\n"); |
| wake_lock_timeout(&sdev->wlan_roam_wl, msecs_to_jiffies(10 * 1000)); |
| SCSC_WLOG_WAKELOCK(WLOG_NORMAL, WL_TAKEN, "wlan_roam_wl", WL_REASON_ROAM); |
| #endif |
| |
| if (!temporal_keys_required) { |
| slsi_mlme_roamed_resp(sdev, dev); |
| cac_update_roam_traffic_params(sdev, dev); |
| } else { |
| ndev_vif->sta.resp_id = MLME_ROAMED_RES; |
| } |
| } |
| |
| exit: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| rtnl_unlock(); |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_rx_roam_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| |
| SLSI_UNUSED_PARAMETER(sdev); |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_roam_ind(vif:%d, aid:0, result:0x%04x )\n", |
| fapi_get_vif(skb), |
| fapi_get_u16(skb, u.mlme_roam_ind.result_code)); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit_with_lock; |
| } |
| |
| WARN(ndev_vif->vif_type != FAPI_VIFTYPE_STATION, "Not a Station VIF\n"); |
| |
| exit_with_lock: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| static void slsi_tdls_event_discovered(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| u16 tdls_event = fapi_get_u16(skb, u.mlme_tdls_peer_ind.tdls_event); |
| u16 peer_index = fapi_get_u16(skb, u.mlme_tdls_peer_ind.peer_index); |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| int len = fapi_get_mgmtlen(skb); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| SLSI_INFO(sdev, "\n"); |
| |
| if (len != 0) { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) |
| cfg80211_rx_mgmt(&ndev_vif->wdev, ndev_vif->chan->center_freq, 0, (const u8 *)mgmt, len, GFP_ATOMIC); |
| #else |
| cfg80211_rx_mgmt(dev, ndev_vif->chan->center_freq, 0, (const u8 *)mgmt, len, GFP_ATOMIC); |
| #endif |
| /* Handling MLME-TDLS-PEER.response */ |
| slsi_mlme_tdls_peer_resp(sdev, dev, peer_index, tdls_event); |
| } |
| |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| |
| slsi_kfree_skb(skb); |
| } |
| |
| static void slsi_tdls_event_connected(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct slsi_peer *peer = NULL; |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| u16 peer_index = fapi_get_u16(skb, u.mlme_tdls_peer_ind.peer_index); |
| u16 tdls_event = fapi_get_u16(skb, u.mlme_tdls_peer_ind.tdls_event); |
| |
| rtnl_lock(); |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| ndev_vif->sta.tdls_enabled = true; |
| |
| SLSI_INFO(sdev, "(vif:%d, peer_index:%d mac[%pM])\n", |
| fapi_get_vif(skb), peer_index, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address)); |
| |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit_with_lock; |
| } |
| |
| if (WARN(ndev_vif->vif_type != FAPI_VIFTYPE_STATION, "STA VIF")) |
| goto exit_with_lock; |
| |
| if (peer_index < SLSI_TDLS_PEER_INDEX_MIN || peer_index > SLSI_TDLS_PEER_INDEX_MAX) { |
| SLSI_NET_ERR(dev, "Received incorrect peer_index: %d\n", peer_index); |
| goto exit_with_lock; |
| } |
| |
| /* slsi_tdls_move_packets() accesses netdev_vif->ack_suppression records which is protected |
| * by (&ndev_vif->tcp_ack_lock), but due to order dependency it can NOT take (&ndev_vif->tcp_ack_lock) |
| * after (&ndev_vif->peer_lock). |
| * so acquire (&ndev_vif->tcp_ack_lock) first and then (&ndev_vif->peer_lock) |
| */ |
| slsi_spinlock_lock(&ndev_vif->tcp_ack_lock); |
| slsi_spinlock_lock(&ndev_vif->peer_lock); |
| /* Check for MAX client */ |
| if (ndev_vif->sta.tdls_peer_sta_records + 1 > SLSI_TDLS_PEER_CONNECTIONS_MAX) { |
| SLSI_NET_ERR(dev, "MAX TDLS peer limit reached. Ignore ind for peer_index:%d\n", peer_index); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| slsi_spinlock_unlock(&ndev_vif->tcp_ack_lock); |
| goto exit_with_lock; |
| } |
| |
| peer = slsi_peer_add(sdev, dev, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address), peer_index); |
| |
| if (!peer) { |
| SLSI_NET_ERR(dev, "Peer NOT Created\n"); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| slsi_spinlock_unlock(&ndev_vif->tcp_ack_lock); |
| goto exit_with_lock; |
| } |
| |
| /* QoS is mandatory for TDLS - enable QoS for TDLS peer by default */ |
| peer->qos_enabled = true; |
| |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED); |
| |
| /* Move TDLS packets from STA_Q to TDLS_Q */ |
| slsi_tdls_move_packets(sdev, dev, ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET], peer, true); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| slsi_spinlock_unlock(&ndev_vif->tcp_ack_lock); |
| |
| /* Handling MLME-TDLS-PEER.response */ |
| slsi_mlme_tdls_peer_resp(sdev, dev, peer_index, tdls_event); |
| |
| exit_with_lock: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| rtnl_unlock(); |
| slsi_kfree_skb(skb); |
| } |
| |
| static void slsi_tdls_event_disconnected(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct slsi_peer *peer = NULL; |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| u16 pid = fapi_get_u16(skb, u.mlme_tdls_peer_ind.peer_index); |
| u16 tdls_event = fapi_get_u16(skb, u.mlme_tdls_peer_ind.tdls_event); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| if (WARN_ON(!dev)) |
| goto exit; |
| |
| SLSI_INFO(sdev, "(vif:%d, MAC:%pM)\n", ndev_vif->ifnum, |
| fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address)); |
| |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit; |
| } |
| |
| /* slsi_tdls_move_packets() accesses netdev_vif->ack_suppression records which is protected |
| * by (&ndev_vif->tcp_ack_lock), but due to order dependency it can NOT take (&ndev_vif->tcp_ack_lock) |
| * after (&ndev_vif->peer_lock). |
| * so acquire (&ndev_vif->tcp_ack_lock) first and then (&ndev_vif->peer_lock) |
| */ |
| slsi_spinlock_lock(&ndev_vif->tcp_ack_lock); |
| slsi_spinlock_lock(&ndev_vif->peer_lock); |
| peer = slsi_get_peer_from_mac(sdev, dev, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address)); |
| |
| if (!peer || peer->aid == 0) { |
| WARN_ON(!peer || (peer->aid == 0)); |
| SLSI_NET_DBG1(dev, SLSI_MLME, "peer NOT found by MAC address\n"); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| slsi_spinlock_unlock(&ndev_vif->tcp_ack_lock); |
| goto exit; |
| } |
| |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DISCONNECTED); |
| |
| /* Move TDLS packets from TDLS_Q to STA_Q */ |
| slsi_tdls_move_packets(sdev, dev, ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET], peer, false); |
| |
| slsi_peer_remove(sdev, dev, peer); |
| slsi_spinlock_unlock(&ndev_vif->peer_lock); |
| slsi_spinlock_unlock(&ndev_vif->tcp_ack_lock); |
| |
| slsi_mlme_tdls_peer_resp(sdev, dev, pid, tdls_event); |
| exit: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| /* Handling for MLME-TDLS-PEER.indication |
| */ |
| void slsi_tdls_peer_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| u16 tdls_event = fapi_get_u16(skb, u.mlme_tdls_peer_ind.tdls_event); |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_tdls_peer_ind tdls_event: %d\n", tdls_event); |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| kfree_skb(skb); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| return; |
| } |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| |
| switch (tdls_event) { |
| case FAPI_TDLSEVENT_CONNECTED: |
| slsi_tdls_event_connected(sdev, dev, skb); |
| break; |
| case FAPI_TDLSEVENT_DISCONNECTED: |
| slsi_tdls_event_disconnected(sdev, dev, skb); |
| break; |
| case FAPI_TDLSEVENT_DISCOVERED: |
| slsi_tdls_event_discovered(sdev, dev, skb); |
| break; |
| default: |
| WARN_ON((tdls_event == 0) || (tdls_event > 4)); |
| slsi_kfree_skb(skb); |
| break; |
| } |
| } |
| |
| /* Retrieve any buffered frame before connected_ind and pass them up. */ |
| void slsi_rx_buffered_frames(struct slsi_dev *sdev, struct net_device *dev, struct slsi_peer *peer) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct sk_buff *buff_frame = NULL; |
| |
| WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex)); |
| if (WARN(!peer, "Peer is NULL")) |
| return; |
| WARN(peer->connected_state == SLSI_STA_CONN_STATE_CONNECTING, "Wrong state"); |
| |
| SLSI_NET_DBG2(dev, SLSI_MLME, |
| "Processing buffered RX frames received before mlme_connected_ind for (vif:%d, aid:%d)\n", |
| ndev_vif->ifnum, peer->aid); |
| buff_frame = slsi_skb_dequeue(&peer->buffered_frames); |
| while (buff_frame) { |
| slsi_debug_frame(sdev, dev, buff_frame, "RX_BUFFERED"); |
| switch (fapi_get_sigid(buff_frame)) { |
| case MA_BLOCKACK_IND: |
| SLSI_NET_DBG2(dev, SLSI_MLME, "Transferring buffered MA_BLOCKACK_IND frame"); |
| __slsi_rx_blockack_ind(sdev, dev, buff_frame); |
| break; |
| default: |
| SLSI_NET_WARN(dev, "Unexpected Data: 0x%.4x\n", fapi_get_sigid(buff_frame)); |
| slsi_kfree_skb(buff_frame); |
| break; |
| } |
| buff_frame = slsi_skb_dequeue(&peer->buffered_frames); |
| } |
| } |
| |
| #ifdef CONFIG_SCSC_WLAN_SAE_CONFIG |
| void slsi_rx_synchronised_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| const u8 *connecting_ssid = NULL; |
| struct cfg80211_external_auth_params auth_request; |
| int r; |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Received synchronised_ind, bssid:%pM\n", fapi_get_mgmt(skb)->bssid); |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| kfree_skb(skb); |
| return; |
| } |
| |
| auth_request.action = NL80211_EXTERNAL_AUTH_START; |
| memcpy(auth_request.bssid, fapi_get_mgmt(skb)->bssid, ETH_ALEN); |
| memcpy(auth_request.ssid.ssid, ndev_vif->sta.ssid, ndev_vif->sta.ssid_len); |
| auth_request.ssid.ssid_len = ndev_vif->sta.ssid_len; |
| auth_request.key_mgmt_suite = ndev_vif->sta.crypto.akm_suites[0]; |
| |
| r = cfg80211_external_auth_request(dev, &auth_request, GFP_KERNEL); |
| if (r) |
| SLSI_NET_DBG1(dev, SLSI_MLME, "cfg80211_external_auth_request failed"); |
| |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| ndev_vif->sta.wpa3_auth_state = SLSI_WPA3_AUTHENTICATING; |
| #endif |
| |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| #endif |
| |
| void slsi_rx_connected_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_peer *peer = NULL; |
| u16 peer_index = fapi_get_u16(skb, u.mlme_connected_ind.peer_index); |
| |
| /* For AP mode, peer_index value is equivalent to peer_index value */ |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_connected_ind(vif:%d, peer_index:%d)\n", |
| fapi_get_vif(skb), |
| peer_index); |
| SLSI_INFO(sdev, "Received Association Response\n"); |
| |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit_with_lock; |
| } |
| |
| if (WARN(ndev_vif->vif_type == FAPI_VIFTYPE_STATION, "STA VIF and Not Roaming")) |
| goto exit_with_lock; |
| |
| switch (ndev_vif->vif_type) { |
| case FAPI_VIFTYPE_AP: |
| { |
| if (peer_index < SLSI_PEER_INDEX_MIN || peer_index > SLSI_PEER_INDEX_MAX) { |
| SLSI_NET_ERR(dev, "Received incorrect peer_index: %d\n", peer_index); |
| goto exit_with_lock; |
| } |
| |
| peer = slsi_get_peer_from_qs(sdev, dev, peer_index - 1); |
| if (!peer) { |
| SLSI_NET_ERR(dev, "Peer (peer_index:%d) Not Found - Disconnect peer\n", peer_index); |
| goto exit_with_lock; |
| } |
| |
| cfg80211_new_sta(dev, peer->address, &peer->sinfo, GFP_KERNEL); |
| |
| if (ndev_vif->ap.privacy) { |
| peer->connected_state = SLSI_STA_CONN_STATE_DOING_KEY_CONFIG; |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DOING_KEY_CONFIG); |
| } else { |
| peer->connected_state = SLSI_STA_CONN_STATE_CONNECTED; |
| slsi_mlme_connected_resp(sdev, dev, peer_index); |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED); |
| } |
| slsi_rx_buffered_frames(sdev, dev, peer); |
| break; |
| } |
| |
| default: |
| SLSI_NET_WARN(dev, "mlme_connected_ind(vif:%d, unexpected vif type:%d)\n", fapi_get_vif(skb), ndev_vif->vif_type); |
| break; |
| } |
| exit_with_lock: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_rx_reassoc_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| enum ieee80211_statuscode status = WLAN_STATUS_SUCCESS; |
| struct slsi_peer *peer = NULL; |
| u8 *assoc_ie = NULL; |
| size_t assoc_ie_len = 0; |
| u8 *reassoc_rsp_ie = NULL; |
| size_t reassoc_rsp_ie_len = 0; |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_reassoc_ind(vif:%d, result:0x%04x)\n", |
| fapi_get_vif(skb), |
| fapi_get_u16(skb, u.mlme_reassociate_ind.result_code)); |
| |
| rtnl_lock(); |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit_with_lock; |
| } |
| |
| if (WARN(ndev_vif->vif_type != FAPI_VIFTYPE_STATION, "Not a Station VIF\n")) |
| goto exit_with_lock; |
| |
| peer = slsi_get_peer_from_qs(sdev, dev, 0); |
| if (WARN_ON(!peer)) { |
| SLSI_NET_ERR(dev, "PEER Not found\n"); |
| goto exit_with_lock; |
| } |
| |
| if (fapi_get_u16(skb, u.mlme_reassociate_ind.result_code) != FAPI_RESULTCODE_SUCCESS) { |
| status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| slsi_rx_ba_stop_all(dev, peer); |
| } else { |
| peer->pairwise_key_set = 0; |
| |
| if (peer->assoc_ie) { |
| assoc_ie = peer->assoc_ie->data; |
| assoc_ie_len = peer->assoc_ie->len; |
| WARN_ON(assoc_ie_len && !assoc_ie); |
| } |
| |
| slsi_peer_reset_stats(sdev, dev, peer); |
| |
| peer->sinfo.assoc_req_ies = assoc_ie; |
| peer->sinfo.assoc_req_ies_len = assoc_ie_len; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) |
| peer->sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; |
| #endif |
| slsi_peer_update_assoc_rsp(sdev, dev, peer, skb); |
| /* skb is consumed by slsi_peer_update_assoc_rsp. So do not access this anymore. */ |
| skb = NULL; |
| if (peer->assoc_resp_ie) { |
| reassoc_rsp_ie = peer->assoc_resp_ie->data; |
| reassoc_rsp_ie_len = peer->assoc_resp_ie->len; |
| WARN_ON(reassoc_rsp_ie_len && !reassoc_rsp_ie); |
| } |
| |
| /* update the uapsd bitmask according to the bit values |
| * in wmm information element of association request |
| */ |
| if (!sta_wmm_update_uapsd(sdev, dev, peer, assoc_ie, assoc_ie_len)) |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Fail to update WMM uapsd\n"); |
| |
| /* update the acm bitmask according to the acm bit values that |
| * are included in wmm ie elements of re-association response |
| */ |
| if (!sta_wmm_update_wmm_ac_ies(sdev, dev, peer, reassoc_rsp_ie, reassoc_rsp_ie_len)) |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Fail to update WMM AC ies\n"); |
| } |
| |
| /* cfg80211_connect_result will take a copy of any ASSOC or (RE)ASSOC RSP IEs passed to it */ |
| cfg80211_connect_result(dev, |
| peer->address, |
| assoc_ie, assoc_ie_len, |
| reassoc_rsp_ie, reassoc_rsp_ie_len, |
| status, |
| GFP_KERNEL); |
| |
| if (status == WLAN_STATUS_SUCCESS) { |
| ndev_vif->sta.vif_status = SLSI_VIF_STATUS_CONNECTED; |
| |
| /* For Open & WEP AP,send reassoc response. |
| * For secured AP, all this would be done after handshake |
| */ |
| if ((peer->capabilities & WLAN_CAPABILITY_PRIVACY) && |
| (cfg80211_find_ie(WLAN_EID_RSN, assoc_ie, assoc_ie_len) || |
| cfg80211_find_ie(SLSI_WLAN_EID_WAPI, assoc_ie, assoc_ie_len) || |
| cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPA, assoc_ie, assoc_ie_len))) { |
| /*secured AP*/ |
| ndev_vif->sta.resp_id = MLME_REASSOCIATE_RES; |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DOING_KEY_CONFIG); |
| peer->connected_state = SLSI_STA_CONN_STATE_DOING_KEY_CONFIG; |
| } else { |
| /*Open/WEP AP*/ |
| slsi_mlme_reassociate_resp(sdev, dev); |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED); |
| peer->connected_state = SLSI_STA_CONN_STATE_CONNECTED; |
| } |
| } else { |
| netif_carrier_off(dev); |
| if (slsi_mlme_del_vif(sdev, dev) != 0) |
| SLSI_NET_ERR(dev, "slsi_mlme_del_vif failed\n"); |
| slsi_vif_deactivated(sdev, dev); |
| } |
| |
| exit_with_lock: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| rtnl_unlock(); |
| slsi_kfree_skb(skb); |
| } |
| |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| int slsi_retry_connection(struct slsi_dev *sdev, struct net_device *dev) |
| { |
| /* retry success return 1 else return 0 */ |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_peer *peer; |
| int r = 0; |
| bool ap_found = false; |
| u8 device_address[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
| const u8 slsi_extended_cap_mask[] = { 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
| u8 *probe_req_ies; |
| size_t probe_req_ie_len; |
| |
| ap_found = slsi_select_ap_for_connection(sdev, dev, NULL, NULL, true); |
| if (!ap_found) { |
| SLSI_NET_ERR(dev, "Could not find BSSID for reconnection\n"); |
| return 0; |
| } |
| probe_req_ie_len = ndev_vif->probe_req_ie_len; |
| probe_req_ies = kmalloc(probe_req_ie_len, GFP_KERNEL); |
| if (probe_req_ies && probe_req_ie_len) |
| memcpy(probe_req_ies, ndev_vif->probe_req_ies, ndev_vif->probe_req_ie_len); |
| if (slsi_mlme_del_vif(sdev, dev) != 0) { |
| SLSI_NET_ERR(dev, "slsi_mlme_del_vif failed\n"); |
| return 0; |
| } |
| if (slsi_mlme_add_vif(sdev, dev, dev->dev_addr, device_address) != 0) { |
| SLSI_NET_ERR(dev, "slsi_mlme_add_vif failed\n"); |
| return 0; |
| } |
| if (slsi_mlme_register_action_frame(sdev, dev, ndev_vif->sta.action_frame_bmap, |
| ndev_vif->sta.action_frame_suspend_bmap) != 0) { |
| SLSI_NET_ERR(dev, "Action frame registration failed for bitmap value 0x%x 0x%x\n", |
| ndev_vif->sta.action_frame_bmap, ndev_vif->sta.action_frame_bmap); |
| return 0; |
| } |
| |
| r = slsi_set_boost(sdev, dev); |
| if (r != 0) |
| SLSI_NET_ERR(dev, "Rssi Boost set failed: %d\n", r); |
| if (probe_req_ies && probe_req_ie_len) { |
| (void)slsi_mlme_add_info_elements(sdev, dev, FAPI_PURPOSE_PROBE_REQUEST, probe_req_ies, |
| probe_req_ie_len); |
| } |
| r = slsi_set_ext_cap(sdev, dev, ndev_vif->sta.sme.ie, ndev_vif->sta.sme.ie_len, slsi_extended_cap_mask); |
| if (r != 0) |
| SLSI_NET_ERR(dev, "Failed to set extended capability MIB: %d\n", r); |
| r = slsi_mlme_connect(sdev, dev, &ndev_vif->sta.sme, ndev_vif->sta.sme.channel, ndev_vif->sta.sme.bssid); |
| if (r != 0) { |
| SLSI_NET_ERR(dev, "Reconnect failed: %d\n", r); |
| return 0; |
| } |
| peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET); |
| if (!peer) { |
| SLSI_NET_ERR(dev, "peer not found!\n"); |
| return 0; |
| } |
| SLSI_ETHER_COPY(peer->address, ndev_vif->sta.sme.bssid); |
| |
| return 1; |
| } |
| |
| void slsi_free_connection_params(struct slsi_dev *sdev, struct net_device *dev) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| |
| if (ndev_vif->sta.sme.ssid) { |
| kfree(ndev_vif->sta.sme.ssid); |
| ndev_vif->sta.sme.ssid = NULL; |
| } |
| if (ndev_vif->sta.connected_bssid) { |
| kfree(ndev_vif->sta.connected_bssid); |
| ndev_vif->sta.connected_bssid = NULL; |
| } |
| if (ndev_vif->sta.sme.key) { |
| kfree(ndev_vif->sta.sme.key); |
| ndev_vif->sta.sme.key = NULL; |
| } |
| if (ndev_vif->sta.sme.ssid) { |
| kfree(ndev_vif->sta.sme.ssid); |
| ndev_vif->sta.sme.ssid = NULL; |
| } |
| if (ndev_vif->sta.sme.ie) { |
| kfree(ndev_vif->sta.sme.ie); |
| ndev_vif->sta.sme.ie = NULL; |
| } |
| } |
| #endif |
| |
| void slsi_rx_connect_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| enum ieee80211_statuscode status = WLAN_STATUS_SUCCESS; |
| struct slsi_peer *peer = NULL; |
| u8 *assoc_ie = NULL; |
| int assoc_ie_len = 0; |
| u8 *assoc_rsp_ie = NULL; |
| int assoc_rsp_ie_len = 0; |
| u8 bssid[ETH_ALEN]; |
| u16 fw_result_code; |
| |
| if (cancel_work_sync(&ndev_vif->set_multicast_filter_work)) |
| slsi_wakeunlock(&sdev->wlan_wl); |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| fw_result_code = fapi_get_u16(skb, u.mlme_connect_ind.result_code); |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_connect_ind(vif:%d, result:0x%04x)\n", |
| fapi_get_vif(skb), fw_result_code); |
| |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit_with_lock; |
| } |
| |
| if (WARN(ndev_vif->vif_type != FAPI_VIFTYPE_STATION, "Not a Station VIF\n")) |
| goto exit_with_lock; |
| |
| if (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTING) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not connecting\n"); |
| goto exit_with_lock; |
| } |
| |
| peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET); |
| if (peer) { |
| SLSI_ETHER_COPY(bssid, peer->address); |
| } else { |
| SLSI_NET_ERR(dev, "!!NO peer record for AP\n"); |
| eth_zero_addr(bssid); |
| } |
| sdev->assoc_result_code = fw_result_code; |
| if (fw_result_code != FAPI_RESULTCODE_SUCCESS) { |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| #ifdef CONFIG_SCSC_WLAN_SAE_CONFIG |
| if (ndev_vif->sta.crypto.wpa_versions == 3 && ndev_vif->sta.wpa3_auth_state == SLSI_WPA3_AUTHENTICATING && |
| (fw_result_code == FAPI_RESULTCODE_AUTH_TIMEOUT || fw_result_code == FAPI_RESULTCODE_AUTH_NO_ACK || |
| fw_result_code == FAPI_RESULTCODE_AUTH_TX_FAIL)) { |
| int r; |
| struct cfg80211_external_auth_params auth_request; |
| |
| auth_request.action = NL80211_EXTERNAL_AUTH_ABORT; |
| memcpy(auth_request.bssid, ndev_vif->sta.bssid, ETH_ALEN); |
| memcpy(auth_request.ssid.ssid, ndev_vif->sta.ssid, ndev_vif->sta.ssid_len); |
| auth_request.ssid.ssid_len = ndev_vif->sta.ssid_len; |
| auth_request.key_mgmt_suite = ndev_vif->sta.crypto.akm_suites[0]; |
| r = cfg80211_external_auth_request(dev, &auth_request, GFP_KERNEL); |
| if (r) |
| SLSI_NET_DBG1(dev, SLSI_MLME, "cfg80211_external_auth_request Abort failed"); |
| } |
| #endif |
| if (ndev_vif->sta.drv_bss_selection && slsi_retry_connection(sdev, dev)) { |
| SLSI_INFO(sdev, "Connect ind : retrying connection\n"); |
| goto exit_with_lock; |
| } else { |
| slsi_free_connection_params(sdev, dev); |
| } |
| #endif |
| if (fw_result_code == FAPI_RESULTCODE_AUTH_NO_ACK) { |
| SLSI_INFO(sdev, "Connect failed,Result code:AUTH_NO_ACK\n"); |
| } else if (fw_result_code == FAPI_RESULTCODE_ASSOC_NO_ACK) { |
| SLSI_INFO(sdev, "Connect failed,Result code:ASSOC_NO_ACK\n"); |
| } else if (fw_result_code >= 0x8100 && fw_result_code <= 0x81FF) { |
| if (fw_result_code != 0x8100) |
| fw_result_code = fw_result_code & 0x00FF; |
| SLSI_INFO(sdev, "Connect failed(Auth failure), Result code:0x%04x\n", fw_result_code); |
| } else if (fw_result_code >= 0x8200 && fw_result_code <= 0x82FF) { |
| if (fw_result_code != 0x8200) |
| fw_result_code = fw_result_code & 0x00FF; |
| SLSI_INFO(sdev, "Connect failed(Assoc Failure), Result code:0x%04x\n", fw_result_code); |
| if (fapi_get_datalen(skb)) { |
| int mgmt_hdr_len; |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| |
| if (ieee80211_is_assoc_resp(mgmt->frame_control)) { |
| mgmt_hdr_len = (mgmt->u.assoc_resp.variable - (u8 *)mgmt); |
| } else if (ieee80211_is_reassoc_resp(mgmt->frame_control)) { |
| mgmt_hdr_len = (mgmt->u.reassoc_resp.variable - (u8 *)mgmt); |
| } else { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Assoc/Reassoc response not found!\n"); |
| goto exit_with_lock; |
| } |
| |
| assoc_rsp_ie = (char *)mgmt + mgmt_hdr_len; |
| assoc_rsp_ie_len = fapi_get_datalen(skb) - mgmt_hdr_len; |
| } |
| } else { |
| SLSI_INFO(sdev, "Connect failed,Result code:0x%04x\n", fw_result_code); |
| } |
| status = fw_result_code; |
| #ifndef CONFIG_SCSC_WLAN_BSS_SELECTION |
| #ifdef CONFIG_SCSC_WLAN_SAE_CONFIG |
| if (ndev_vif->sta.crypto.wpa_versions == 3) { |
| int r; |
| struct cfg80211_external_auth_params auth_request; |
| |
| auth_request.action = NL80211_EXTERNAL_AUTH_ABORT; |
| memcpy(auth_request.bssid, ndev_vif->sta.bssid, ETH_ALEN); |
| memcpy(auth_request.ssid.ssid, ndev_vif->sta.ssid, ndev_vif->sta.ssid_len); |
| auth_request.ssid.ssid_len = ndev_vif->sta.ssid_len; |
| auth_request.key_mgmt_suite = ndev_vif->sta.crypto.akm_suites[0]; |
| r = cfg80211_external_auth_request(dev, &auth_request, GFP_KERNEL); |
| if (r) |
| SLSI_NET_DBG1(dev, SLSI_MLME, "cfg80211_external_auth_request Abort failed"); |
| } |
| #endif |
| #endif |
| } else { |
| SLSI_INFO(sdev, "Received Association Response\n"); |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| slsi_free_connection_params(sdev, dev); |
| slsi_set_reset_connect_attempted_flag(sdev, dev, NULL); |
| #endif |
| if (!peer || !peer->assoc_ie) { |
| if (peer) |
| WARN(!peer->assoc_ie, "proc-started-ind not received before connect-ind"); |
| status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| } else { |
| if (peer->assoc_ie) { |
| assoc_ie = peer->assoc_ie->data; |
| assoc_ie_len = peer->assoc_ie->len; |
| } |
| |
| slsi_peer_update_assoc_rsp(sdev, dev, peer, skb); |
| /* skb is consumed by slsi_peer_update_assoc_rsp. So do not access this anymore. */ |
| skb = NULL; |
| |
| if (peer->assoc_resp_ie) { |
| assoc_rsp_ie = peer->assoc_resp_ie->data; |
| assoc_rsp_ie_len = peer->assoc_resp_ie->len; |
| } |
| |
| /* this is the right place to initialize the bitmasks for |
| * acm bit and tspec establishment |
| */ |
| peer->wmm_acm = 0; |
| peer->tspec_established = 0; |
| peer->uapsd = 0; |
| |
| /* update the uapsd bitmask according to the bit values |
| * in wmm information element of association request |
| */ |
| if (!sta_wmm_update_uapsd(sdev, dev, peer, assoc_ie, assoc_ie_len)) |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Fail to update WMM uapsd\n"); |
| |
| /* update the wmm ac bitmasks according to the bit values that |
| * are included in wmm ie elements of association response |
| */ |
| if (!sta_wmm_update_wmm_ac_ies(sdev, dev, peer, assoc_rsp_ie, assoc_rsp_ie_len)) |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Fail to update WMM AC ies\n"); |
| |
| WARN_ON(!assoc_rsp_ie_len && !assoc_rsp_ie); |
| } |
| |
| WARN(!ndev_vif->sta.mlme_scan_ind_skb, "mlme_scan.ind not received before connect-ind"); |
| |
| if (ndev_vif->sta.mlme_scan_ind_skb) { |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(ndev_vif->sta.mlme_scan_ind_skb); |
| |
| ndev_vif->sta.beacon_int = mgmt->u.beacon.beacon_int; |
| #endif |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Sending scan indication to cfg80211, bssid: %pM\n", fapi_get_mgmt(ndev_vif->sta.mlme_scan_ind_skb)->bssid); |
| /* saved skb [mlme_scan_ind] freed inside slsi_rx_scan_pass_to_cfg80211 */ |
| slsi_rx_scan_pass_to_cfg80211(sdev, dev, ndev_vif->sta.mlme_scan_ind_skb); |
| ndev_vif->sta.mlme_scan_ind_skb = NULL; |
| } |
| |
| if (!ndev_vif->sta.sta_bss) { |
| if (peer) |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) |
| ndev_vif->sta.sta_bss = cfg80211_get_bss(sdev->wiphy, ndev_vif->chan, peer->address, |
| NULL, 0, IEEE80211_BSS_TYPE_ANY, |
| IEEE80211_PRIVACY_ANY); |
| #else |
| ndev_vif->sta.sta_bss = cfg80211_get_bss(sdev->wiphy, ndev_vif->chan, peer->address, |
| NULL, 0, 0, 0); |
| #endif |
| if (!ndev_vif->sta.sta_bss) { |
| SLSI_NET_ERR(dev, "sta_bss is not available, terminating the connection (peer: %p)\n", peer); |
| status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| } |
| } |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| cfg80211_ref_bss(sdev->wiphy, ndev_vif->sta.sta_bss); |
| cfg80211_connect_bss(dev, bssid, ndev_vif->sta.sta_bss, assoc_ie, assoc_ie_len, assoc_rsp_ie, |
| assoc_rsp_ie_len, status, GFP_KERNEL, NL80211_TIMEOUT_UNSPECIFIED); |
| #else |
| /* cfg80211_connect_result will take a copy of any ASSOC or |
| * ASSOC RSP IEs passed to it |
| */ |
| cfg80211_connect_result(dev, |
| bssid, |
| assoc_ie, assoc_ie_len, |
| assoc_rsp_ie, assoc_rsp_ie_len, |
| status, |
| GFP_KERNEL); |
| #endif |
| if (status == WLAN_STATUS_SUCCESS) { |
| ndev_vif->sta.vif_status = SLSI_VIF_STATUS_CONNECTED; |
| |
| /* For Open & WEP AP,set the power mode (static IP scenario), |
| * send connect response and install the packet filters . |
| * For secured AP, all this would be done after handshake |
| */ |
| if ((peer->capabilities & WLAN_CAPABILITY_PRIVACY) && |
| (cfg80211_find_ie(WLAN_EID_RSN, assoc_ie, assoc_ie_len) || |
| cfg80211_find_ie(SLSI_WLAN_EID_WAPI, assoc_ie, assoc_ie_len) || |
| cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPA, assoc_ie, assoc_ie_len))) { |
| /*secured AP*/ |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DOING_KEY_CONFIG); |
| ndev_vif->sta.resp_id = MLME_CONNECT_RES; |
| } else { |
| /*Open/WEP AP*/ |
| slsi_mlme_connect_resp(sdev, dev); |
| slsi_set_packet_filters(sdev, dev); |
| |
| if (ndev_vif->ipaddress) |
| slsi_mlme_powermgt(sdev, dev, ndev_vif->set_power_mode); |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED); |
| } |
| |
| /* For P2PCLI, set the Connection Timeout (beacon miss) mib to 10 seconds |
| * This MIB set failure does not cause any fatal isuue. It just varies the |
| * detection time of GO's absence from 10 sec to FW default. So Do not disconnect |
| */ |
| if (ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT) |
| SLSI_P2P_STATE_CHANGE(sdev, P2P_GROUP_FORMED_CLI); |
| |
| /*Update the firmware with cached channels*/ |
| #ifdef CONFIG_SCSC_WLAN_WES_NCHO |
| if (!sdev->device_config.roam_scan_mode && ndev_vif->vif_type == FAPI_VIFTYPE_STATION && ndev_vif->activated && ndev_vif->iftype != NL80211_IFTYPE_P2P_CLIENT) { |
| #else |
| if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION && ndev_vif->activated && ndev_vif->iftype != NL80211_IFTYPE_P2P_CLIENT) { |
| #endif |
| const u8 *ssid = cfg80211_find_ie(WLAN_EID_SSID, assoc_ie, assoc_ie_len); |
| struct slsi_roaming_network_map_entry *network_map; |
| u8 channels[SLSI_ROAMING_CHANNELS_MAX]; |
| u32 channels_count = slsi_roaming_scan_configure_channels(sdev, dev, ssid, channels); |
| |
| network_map = slsi_roam_channel_cache_get(dev, ssid); |
| if (network_map) { |
| ndev_vif->sta.channels_24_ghz = network_map->channels_24_ghz; |
| ndev_vif->sta.channels_5_ghz = network_map->channels_5_ghz; |
| } |
| if (channels_count) |
| if (slsi_mlme_set_cached_channels(sdev, dev, channels_count, channels) != 0) |
| SLSI_NET_ERR(dev, "MLME-SET-CACHED-CHANNELS.req failed\n"); |
| } |
| } else { |
| /* Firmware reported connection success, but driver reported failure to cfg80211: |
| * send mlme-disconnect.req to firmware |
| */ |
| if (fw_result_code == FAPI_RESULTCODE_SUCCESS && peer) { |
| slsi_mlme_disconnect(sdev, dev, peer->address, FAPI_REASONCODE_UNSPECIFIED_REASON, true); |
| slsi_handle_disconnect(sdev, dev, peer->address, FAPI_REASONCODE_UNSPECIFIED_REASON, NULL, 0); |
| } else { |
| slsi_handle_disconnect(sdev, dev, NULL, FAPI_REASONCODE_UNSPECIFIED_REASON, NULL, 0); |
| } |
| } |
| |
| exit_with_lock: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_rx_disconnect_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| |
| if (cancel_work_sync(&ndev_vif->set_multicast_filter_work)) |
| slsi_wakeunlock(&sdev->wlan_wl); |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_disconnect_ind(vif:%d, MAC:%pM)\n", |
| fapi_get_vif(skb), |
| fapi_get_buff(skb, u.mlme_disconnect_ind.peer_sta_address)); |
| |
| #ifdef CONFIG_SCSC_LOG_COLLECTION |
| scsc_log_collector_schedule_collection(SCSC_LOG_HOST_WLAN, SCSC_LOG_HOST_WLAN_REASON_DISCONNECT_IND); |
| #else |
| mx140_log_dump(); |
| #endif |
| |
| SLSI_INFO(sdev, "Received DEAUTH, reason = 0\n"); |
| slsi_handle_disconnect(sdev, |
| dev, |
| fapi_get_buff(skb, u.mlme_disconnect_ind.peer_sta_address), |
| 0, |
| NULL, |
| 0); |
| |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_rx_disconnected_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| u16 reason; |
| u8 *disassoc_rsp_ie = NULL; |
| u32 disassoc_rsp_ie_len = 0; |
| |
| if (cancel_work_sync(&ndev_vif->set_multicast_filter_work)) |
| slsi_wakeunlock(&sdev->wlan_wl); |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit; |
| } |
| reason = fapi_get_u16(skb, u.mlme_disconnected_ind.reason_code); |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_disconnected_ind(vif:%d, reason:%d, MAC:%pM)\n", |
| fapi_get_vif(skb), |
| fapi_get_u16(skb, u.mlme_disconnected_ind.reason_code), |
| fapi_get_buff(skb, u.mlme_disconnected_ind.peer_sta_address)); |
| |
| #ifdef CONFIG_SCSC_LOG_COLLECTION |
| scsc_log_collector_schedule_collection(SCSC_LOG_HOST_WLAN, SCSC_LOG_HOST_WLAN_REASON_DISCONNECTED_IND); |
| #else |
| mx140_log_dump(); |
| #endif |
| if (reason <= 0xFF) { |
| SLSI_INFO(sdev, "Received DEAUTH, reason = %d\n", reason); |
| } else if (reason >= 0x8100 && reason <= 0x81FF) { |
| reason = reason & 0x00FF; |
| SLSI_INFO(sdev, "Received DEAUTH, reason = %d\n", reason); |
| } else if (reason >= 0x8200 && reason <= 0x82FF) { |
| reason = reason & 0x00FF; |
| SLSI_INFO(sdev, "Received DISASSOC, reason = %d\n", reason); |
| } else { |
| SLSI_INFO(sdev, "Received DEAUTH, reason = Local Disconnect <%d>\n", reason); |
| } |
| |
| if (fapi_get_datalen(skb)) { |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| |
| if (ieee80211_is_deauth(mgmt->frame_control)) { |
| disassoc_rsp_ie = (char *)&mgmt->u.deauth.reason_code + 2; |
| disassoc_rsp_ie_len = fapi_get_datalen(skb) - |
| (u32)(disassoc_rsp_ie - (u8 *)fapi_get_data(skb)); |
| } else if (ieee80211_is_disassoc(mgmt->frame_control)) { |
| disassoc_rsp_ie = (char *)&mgmt->u.disassoc.reason_code + 2; |
| disassoc_rsp_ie_len = fapi_get_datalen(skb) - |
| (u32)(disassoc_rsp_ie - (u8 *)fapi_get_data(skb)); |
| } else { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Not a disassoc/deauth packet\n"); |
| } |
| } |
| |
| if (ndev_vif->vif_type == FAPI_VIFTYPE_AP) { |
| if (fapi_get_u16(skb, u.mlme_disconnected_ind.reason_code) == |
| FAPI_REASONCODE_HOTSPOT_MAX_CLIENT_REACHED) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, |
| "Sending max hotspot client reached notification to user space\n"); |
| cfg80211_conn_failed(dev, fapi_get_buff(skb, u.mlme_disconnected_ind.peer_sta_address), |
| NL80211_CONN_FAIL_MAX_CLIENTS, GFP_KERNEL); |
| goto exit; |
| } |
| } |
| |
| slsi_handle_disconnect(sdev, |
| dev, |
| fapi_get_buff(skb, u.mlme_disconnected_ind.peer_sta_address), |
| fapi_get_u16(skb, u.mlme_disconnected_ind.reason_code), |
| disassoc_rsp_ie, |
| disassoc_rsp_ie_len); |
| |
| exit: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| /* Handle Procedure Started (Type = Device Discovered) indication for P2P */ |
| static void slsi_rx_p2p_device_discovered_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| int mgmt_len; |
| |
| SLSI_UNUSED_PARAMETER(sdev); |
| |
| if (ndev_vif->chan) { |
| SLSI_NET_DBG2(dev, SLSI_CFG80211, "Freq = %d\n", ndev_vif->chan->center_freq); |
| } else { |
| SLSI_NET_ERR(dev, "ndev_vif->chan is NULL\n"); |
| return; |
| } |
| |
| /* Only Probe Request is expected as of now */ |
| mgmt_len = fapi_get_mgmtlen(skb); |
| if (mgmt_len) { |
| struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb); |
| |
| if (ieee80211_is_mgmt(mgmt->frame_control)) { |
| if (ieee80211_is_probe_req(mgmt->frame_control)) { |
| SLSI_NET_DBG3(dev, SLSI_CFG80211, "Received Probe Request\n"); |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) |
| cfg80211_rx_mgmt(&ndev_vif->wdev, ndev_vif->chan->center_freq, 0, (const u8 *)mgmt, mgmt_len, GFP_ATOMIC); |
| #else |
| cfg80211_rx_mgmt(dev, ndev_vif->chan->center_freq, 0, (const u8 *)mgmt, mgmt_len, GFP_ATOMIC); |
| #endif |
| } else { |
| SLSI_NET_ERR(dev, "Ignore Indication - Not Probe Request frame\n"); |
| } |
| } else { |
| SLSI_NET_ERR(dev, "Ignore Indication - Not Management frame\n"); |
| } |
| } |
| } |
| |
| void slsi_rx_procedure_started_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_peer *peer = NULL; |
| |
| rtnl_lock(); |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_procedure_started_ind(vif:%d, type:%d, peer_index:%d)\n", |
| fapi_get_vif(skb), |
| fapi_get_u16(skb, u.mlme_procedure_started_ind.procedure_type), |
| fapi_get_u16(skb, u.mlme_procedure_started_ind.peer_index)); |
| SLSI_INFO(sdev, "Send Association Request\n"); |
| |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit_with_lock; |
| } |
| |
| switch (fapi_get_u16(skb, u.mlme_procedure_started_ind.procedure_type)) { |
| case FAPI_PROCEDURETYPE_CONNECTION_STARTED: |
| switch (ndev_vif->vif_type) { |
| case FAPI_VIFTYPE_AP: |
| { |
| u16 peer_index = fapi_get_u16(skb, u.mlme_procedure_started_ind.peer_index); |
| |
| /* Check for MAX client */ |
| if ((ndev_vif->peer_sta_records + 1) > SLSI_AP_PEER_CONNECTIONS_MAX) { |
| SLSI_NET_ERR(dev, "MAX Station limit reached. Ignore ind for peer_index:%d\n", peer_index); |
| goto exit_with_lock; |
| } |
| |
| if (peer_index < SLSI_PEER_INDEX_MIN || peer_index > SLSI_PEER_INDEX_MAX) { |
| SLSI_NET_ERR(dev, "Received incorrect peer_index: %d\n", peer_index); |
| goto exit_with_lock; |
| } |
| |
| peer = slsi_peer_add(sdev, dev, (fapi_get_mgmt(skb))->sa, peer_index); |
| if (!peer) { |
| SLSI_NET_ERR(dev, "Peer NOT Created\n"); |
| goto exit_with_lock; |
| } |
| slsi_peer_update_assoc_req(sdev, dev, peer, skb); |
| /* skb is consumed by slsi_peer_update_assoc_req. So do not access this anymore. */ |
| skb = NULL; |
| peer->connected_state = SLSI_STA_CONN_STATE_CONNECTING; |
| |
| if (ndev_vif->iftype == NL80211_IFTYPE_P2P_GO && |
| peer->assoc_ie && |
| cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPS, peer->assoc_ie->data, peer->assoc_ie->len)) { |
| SLSI_NET_DBG2(dev, SLSI_MLME, "WPS IE is present. Setting peer->is_wps to TRUE\n"); |
| peer->is_wps = true; |
| } |
| |
| /* Take a wakelock to avoid platform suspend before |
| * EAPOL exchanges (to avoid connection delay) |
| */ |
| slsi_wakelock_timeout(&sdev->wlan_wl_to, SLSI_WAKELOCK_TIME_MSEC_EAPOL); |
| break; |
| } |
| case FAPI_VIFTYPE_STATION: |
| { |
| peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET); |
| if (WARN_ON(!peer)) { |
| SLSI_NET_ERR(dev, "Peer NOT FOUND\n"); |
| goto exit_with_lock; |
| } |
| slsi_peer_update_assoc_req(sdev, dev, peer, skb); |
| /* skb is consumed by slsi_peer_update_assoc_rsp. So do not access this anymore. */ |
| skb = NULL; |
| break; |
| } |
| default: |
| SLSI_NET_ERR(dev, "Incorrect vif type for proceduretype_connection_started\n"); |
| break; |
| } |
| break; |
| case FAPI_PROCEDURETYPE_DEVICE_DISCOVERED: |
| /* Expected only in P2P Device and P2P GO role */ |
| if (!SLSI_IS_VIF_INDEX_P2P(ndev_vif) && ndev_vif->iftype != NL80211_IFTYPE_P2P_GO) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "PROCEDURETYPE_DEVICE_DISCOVERED recd in non P2P role\n"); |
| goto exit_with_lock; |
| } |
| /* Send probe request to supplicant only if in listening state. Issues were seen earlier if |
| * Probe request was sent to supplicant while waiting for GO Neg Req from peer. |
| * Send Probe request to supplicant if received in GO mode |
| */ |
| if (sdev->p2p_state == P2P_LISTENING || ndev_vif->iftype == NL80211_IFTYPE_P2P_GO) |
| slsi_rx_p2p_device_discovered_ind(sdev, dev, skb); |
| break; |
| case FAPI_PROCEDURETYPE_ROAMING_STARTED: |
| { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Roaming Procedure Starting with %pM\n", (fapi_get_mgmt(skb))->bssid); |
| if (WARN_ON(ndev_vif->vif_type != FAPI_VIFTYPE_STATION)) |
| goto exit_with_lock; |
| if (WARN_ON(!ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET] || !ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET]->valid)) |
| goto exit_with_lock; |
| slsi_kfree_skb(ndev_vif->sta.roam_mlme_procedure_started_ind); |
| ndev_vif->sta.roam_mlme_procedure_started_ind = skb; |
| /* skb is consumed here. So remove reference to this.*/ |
| skb = NULL; |
| break; |
| } |
| default: |
| SLSI_NET_DBG1(dev, SLSI_MLME, "Unknown Procedure: %d\n", fapi_get_u16(skb, u.mlme_procedure_started_ind.procedure_type)); |
| goto exit_with_lock; |
| } |
| |
| exit_with_lock: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| rtnl_unlock(); |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_rx_frame_transmission_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| struct slsi_peer *peer; |
| u16 host_tag = fapi_get_u16(skb, u.mlme_frame_transmission_ind.host_tag); |
| u16 tx_status = fapi_get_u16(skb, u.mlme_frame_transmission_ind.transmission_status); |
| bool ack = true; |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_frame_transmission_ind(vif:%d, host_tag:%d, transmission_status:%d)\n", fapi_get_vif(skb), |
| host_tag, |
| tx_status); |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| kfree_skb(skb); |
| return; |
| } |
| |
| if (ndev_vif->mgmt_tx_data.host_tag == host_tag) { |
| struct netdev_vif *ndev_vif_to_cfg = ndev_vif; |
| |
| /* If frame tx failed allow del_vif work to take care of vif deletion. |
| * This work would be queued as part of frame_tx with the wait duration |
| */ |
| if (tx_status != FAPI_TRANSMISSIONSTATUS_SUCCESSFUL) { |
| ack = false; |
| if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) { |
| if (sdev->wlan_unsync_vif_state == WLAN_UNSYNC_VIF_TX) |
| sdev->wlan_unsync_vif_state = WLAN_UNSYNC_VIF_ACTIVE; /*We wouldn't delete VIF*/ |
| } else { |
| if (sdev->p2p_group_exp_frame != SLSI_PA_INVALID) |
| slsi_clear_offchannel_data(sdev, false); |
| else if (ndev_vif->mgmt_tx_data.exp_frame != SLSI_PA_INVALID) |
| (void)slsi_mlme_reset_dwell_time(sdev, dev); |
| ndev_vif->mgmt_tx_data.exp_frame = SLSI_PA_INVALID; |
| } |
| } |
| |
| /* Change state if frame tx was in Listen as peer response is not expected */ |
| if (SLSI_IS_VIF_INDEX_P2P(ndev_vif) && ndev_vif->mgmt_tx_data.exp_frame == SLSI_PA_INVALID) { |
| if (delayed_work_pending(&ndev_vif->unsync.roc_expiry_work)) |
| SLSI_P2P_STATE_CHANGE(sdev, P2P_LISTENING); |
| else |
| SLSI_P2P_STATE_CHANGE(sdev, P2P_IDLE_VIF_ACTIVE); |
| } else if (SLSI_IS_VIF_INDEX_P2P_GROUP(sdev, ndev_vif)) { |
| const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *)ndev_vif->mgmt_tx_data.buf; |
| |
| /* If frame transmission was initiated on P2P device vif by supplicant, |
| * then use the net_dev of that vif (i.e. p2p0) |
| */ |
| if ((mgmt) && (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0)) { |
| struct net_device *ndev = slsi_get_netdev(sdev, SLSI_NET_INDEX_P2P); |
| |
| SLSI_NET_DBG2(dev, SLSI_MLME, |
| "Frame Tx was requested with device address" |
| " - Change ndev_vif for tx_status\n"); |
| |
| ndev_vif_to_cfg = netdev_priv(ndev); |
| if (!ndev_vif_to_cfg) { |
| SLSI_NET_ERR(dev, "Getting P2P Index netdev failed\n"); |
| ndev_vif_to_cfg = ndev_vif; |
| } |
| } |
| } |
| #ifdef CONFIG_SCSC_WLAN_WES_NCHO |
| if (!sdev->device_config.wes_mode) { |
| #endif |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) |
| cfg80211_mgmt_tx_status(&ndev_vif_to_cfg->wdev, ndev_vif->mgmt_tx_data.cookie, ndev_vif->mgmt_tx_data.buf, ndev_vif->mgmt_tx_data.buf_len, ack, GFP_KERNEL); |
| #else |
| cfg80211_mgmt_tx_status(ndev_vif_to_cfg->wdev.netdev, ndev_vif->mgmt_tx_data.cookie, ndev_vif->mgmt_tx_data.buf, ndev_vif->mgmt_tx_data.buf_len, ack, GFP_KERNEL); |
| #endif |
| |
| #ifdef CONFIG_SCSC_WLAN_WES_NCHO |
| } |
| #endif |
| (void)slsi_set_mgmt_tx_data(ndev_vif, 0, 0, NULL, 0); |
| } |
| |
| if (tx_status == FAPI_TRANSMISSIONSTATUS_SUCCESSFUL || tx_status == FAPI_TRANSMISSIONSTATUS_RETRY_LIMIT) { |
| #ifdef CONFIG_SCSC_WLAN_STA_ENHANCED_ARP_DETECT |
| if (ndev_vif->enhanced_arp_detect_enabled && ndev_vif->vif_type == FAPI_VIFTYPE_STATION) { |
| int i = 0; |
| |
| for (i = 0; i < SLSI_MAX_ARP_SEND_FRAME; i++) { |
| if (ndev_vif->enhanced_arp_host_tag[i] == host_tag) { |
| ndev_vif->enhanced_arp_host_tag[i] = 0; |
| ndev_vif->enhanced_arp_stats.arp_req_rx_count_by_lower_mac++; |
| if (tx_status == FAPI_TRANSMISSIONSTATUS_SUCCESSFUL) |
| ndev_vif->enhanced_arp_stats.arp_req_count_tx_success++; |
| break; |
| } |
| } |
| } |
| #endif |
| if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION && |
| ndev_vif->sta.m4_host_tag == host_tag && |
| tx_status == FAPI_TRANSMISSIONSTATUS_SUCCESSFUL) { |
| switch (ndev_vif->sta.resp_id) { |
| case MLME_ROAMED_RES: |
| slsi_mlme_roamed_resp(sdev, dev); |
| peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET); |
| if (WARN_ON(!peer)) |
| break; |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED); |
| cac_update_roam_traffic_params(sdev, dev); |
| break; |
| case MLME_CONNECT_RES: |
| slsi_mlme_connect_resp(sdev, dev); |
| slsi_set_packet_filters(sdev, dev); |
| peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET); |
| if (WARN_ON(!peer)) |
| break; |
| slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED); |
| break; |
| case MLME_REASSOCIATE_RES: |
| slsi_mlme_reassociate_resp(sdev, dev); |
| break; |
| default: |
| break; |
| } |
| ndev_vif->sta.m4_host_tag = 0; |
| ndev_vif->sta.resp_id = 0; |
| } |
| if (tx_status == FAPI_TRANSMISSIONSTATUS_RETRY_LIMIT) { |
| if (ndev_vif->iftype == NL80211_IFTYPE_STATION && |
| ndev_vif->sta.eap_hosttag == host_tag) { |
| if (ndev_vif->sta.sta_bss) { |
| SLSI_NET_WARN(dev, "Disconnect as EAP frame transmission failed\n"); |
| slsi_mlme_disconnect(sdev, dev, ndev_vif->sta.sta_bss->bssid, FAPI_REASONCODE_UNSPECIFIED_REASON, false); |
| } else { |
| SLSI_NET_WARN(dev, "EAP frame transmission failed, sta_bss not available\n"); |
| } |
| } |
| ndev_vif->stats.tx_errors++; |
| } |
| } else { |
| ndev_vif->stats.tx_errors++; |
| } |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_rx_received_frame_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| u16 data_unit_descriptor = fapi_get_u16(skb, u.mlme_received_frame_ind.data_unit_descriptor); |
| u16 frequency = SLSI_FREQ_FW_TO_HOST(fapi_get_u16(skb, u.mlme_received_frame_ind.channel_frequency)); |
| u8 *eapol = NULL; |
| u8 *eap = NULL; |
| u16 protocol = 0; |
| u32 dhcp_message_type = SLSI_DHCP_MESSAGE_TYPE_INVALID; |
| u16 eap_length = 0; |
| int subtype = SLSI_PA_INVALID; |
| |
| SLSI_NET_DBG2(dev, SLSI_MLME, "mlme_received_frame_ind(vif:%d, data descriptor:%d, freq:%d)\n", |
| fapi_get_vif(skb), |
| data_unit_descriptor, |
| frequency); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| if (data_unit_descriptor == FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME) { |
| struct ieee80211_mgmt *mgmt; |
| int mgmt_len; |
| |
| mgmt_len = fapi_get_mgmtlen(skb); |
| if (!mgmt_len) |
| goto exit; |
| mgmt = fapi_get_mgmt(skb); |
| if (ieee80211_is_auth(mgmt->frame_control)) { |
| cfg80211_rx_mgmt(&ndev_vif->wdev, frequency, 0, (const u8 *)mgmt, mgmt_len, GFP_ATOMIC); |
| SLSI_INFO(sdev, "Received Auth Frame\n"); |
| goto exit; |
| } |
| #ifdef CONFIG_SLSI_WLAN_STA_FWD_BEACON |
| if (ndev_vif->is_wips_running && ieee80211_is_beacon(mgmt->frame_control) && |
| SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) { |
| slsi_handle_wips_beacon(sdev, dev, skb, mgmt, mgmt_len); |
| goto exit; |
| } |
| #endif |
| if (!(ieee80211_is_action(mgmt->frame_control))) { |
| SLSI_NET_ERR(dev, "Expected an Action Frame\n"); |
| goto exit; |
| } |
| |
| subtype = slsi_get_public_action_subtype(mgmt); |
| if (SLSI_IS_VIF_INDEX_WLAN(ndev_vif)) { |
| #ifdef CONFIG_SCSC_WLAN_WES_NCHO |
| if (slsi_is_wes_action_frame(mgmt)) { |
| SLSI_NET_DBG1(dev, SLSI_CFG80211, "Received NCHO WES VS action frame\n"); |
| if (!sdev->device_config.wes_mode) |
| goto exit; |
| } else { |
| #endif |
| if (mgmt->u.action.category == WLAN_CATEGORY_WMM) { |
| cac_rx_wmm_action(sdev, dev, mgmt, mgmt_len); |
| } else if (mgmt->u.action.category == WLAN_CATEGORY_WNM) { |
| #ifdef CONFIG_SCSC_WLAN_BSS_SELECTION |
| slsi_parse_bss_transition_mgmt_req(sdev, mgmt, mgmt_len, ndev_vif); |
| #endif |
| slsi_wlan_dump_public_action_subtype(sdev, mgmt, false); |
| } else { |
| slsi_wlan_dump_public_action_subtype(sdev, mgmt, false); |
| if (sdev->wlan_unsync_vif_state == WLAN_UNSYNC_VIF_TX) |
| sdev->wlan_unsync_vif_state = WLAN_UNSYNC_VIF_ACTIVE; |
| if (ndev_vif->mgmt_tx_data.exp_frame != SLSI_PA_INVALID && subtype == ndev_vif->mgmt_tx_data.exp_frame) { |
| ndev_vif->mgmt_tx_data.exp_frame = SLSI_PA_INVALID; |
| (void)slsi_mlme_reset_dwell_time(sdev, dev); |
| } |
| } |
| #ifdef CONFIG_SCSC_WLAN_WES_NCHO |
| } |
| #endif |
| } else { |
| SLSI_NET_DBG2(dev, SLSI_CFG80211, "Received action frame (%s)\n", slsi_pa_subtype_text(subtype)); |
| |
| if (SLSI_IS_P2P_UNSYNC_VIF(ndev_vif) && ndev_vif->mgmt_tx_data.exp_frame != SLSI_PA_INVALID && |
| subtype == ndev_vif->mgmt_tx_data.exp_frame) { |
| if (sdev->p2p_state == P2P_LISTENING) |
| SLSI_NET_WARN(dev, "Driver in incorrect P2P state (P2P_LISTENING)"); |
| |
| cancel_delayed_work(&ndev_vif->unsync.del_vif_work); |
| |
| ndev_vif->mgmt_tx_data.exp_frame = SLSI_PA_INVALID; |
| (void)slsi_mlme_reset_dwell_time(sdev, dev); |
| if (delayed_work_pending(&ndev_vif->unsync.roc_expiry_work)) { |
| SLSI_P2P_STATE_CHANGE(sdev, P2P_LISTENING); |
| } else { |
| queue_delayed_work(sdev->device_wq, &ndev_vif->unsync.del_vif_work, |
| msecs_to_jiffies(SLSI_P2P_UNSYNC_VIF_EXTRA_MSEC)); |
| SLSI_P2P_STATE_CHANGE(sdev, P2P_IDLE_VIF_ACTIVE); |
| } |
| } else if ((sdev->p2p_group_exp_frame != SLSI_PA_INVALID) && (sdev->p2p_group_exp_frame == subtype)) { |
| SLSI_NET_DBG2(dev, SLSI_MLME, "Expected action frame (%s) received on Group VIF\n", slsi_pa_subtype_text(subtype)); |
| slsi_clear_offchannel_data(sdev, |
| (!SLSI_IS_VIF_INDEX_P2P_GROUP(sdev, |
| ndev_vif)) ? true : false); |
| } |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) |
| cfg80211_rx_mgmt(&ndev_vif->wdev, frequency, 0, (const u8 *)mgmt, mgmt_len, GFP_ATOMIC); |
| #else |
| cfg80211_rx_mgmt(dev, frequency, 0, (const u8 *)mgmt, mgmt_len, GFP_ATOMIC); |
| #endif |
| } else if (data_unit_descriptor == FAPI_DATAUNITDESCRIPTOR_IEEE802_3_FRAME) { |
| struct slsi_peer *peer = NULL; |
| struct ethhdr *ehdr = (struct ethhdr *)fapi_get_data(skb); |
| |
| peer = slsi_get_peer_from_mac(sdev, dev, ehdr->h_source); |
| if (!peer) { |
| SLSI_DBG1(sdev, SLSI_RX, "drop packet as No peer found\n"); |
| goto exit; |
| } |
| |
| /* strip signal and any signal/bulk roundings/offsets */ |
| skb_pull(skb, fapi_get_siglen(skb)); |
| |
| skb->dev = dev; |
| skb->ip_summed = CHECKSUM_NONE; |
| |
| ndev_vif->stats.rx_packets++; |
| ndev_vif->stats.rx_bytes += skb->len; |
| dev->last_rx = jiffies; |
| |
| /* Storing Data for Logging Information */ |
| if ((skb->len - sizeof(struct ethhdr)) >= 99) |
| eapol = skb->data + sizeof(struct ethhdr); |
| |
| if ((skb->len - sizeof(struct ethhdr)) >= 9) { |
| eap_length = (skb->len - sizeof(struct ethhdr)) - 4; |
| eap = skb->data + sizeof(struct ethhdr); |
| } |
| if (skb->len >= 285 && slsi_is_dhcp_packet(skb->data) != SLSI_TX_IS_NOT_DHCP) |
| dhcp_message_type = skb->data[284]; |
| |
| skb->protocol = eth_type_trans(skb, dev); |
| protocol = ntohs(skb->protocol); |
| if (protocol == ETH_P_PAE) { |
| if (eapol && eapol[SLSI_EAPOL_IEEE8021X_TYPE_POS] == SLSI_IEEE8021X_TYPE_EAPOL_KEY) { |
| if ((eapol[SLSI_EAPOL_TYPE_POS] == SLSI_EAPOL_TYPE_RSN_KEY || |
| eapol[SLSI_EAPOL_TYPE_POS] == SLSI_EAPOL_TYPE_WPA_KEY) && |
| (eapol[SLSI_EAPOL_KEY_INFO_LOWER_BYTE_POS] & |
| SLSI_EAPOL_KEY_INFO_KEY_TYPE_BIT_IN_LOWER_BYTE) && |
| (eapol[SLSI_EAPOL_KEY_INFO_HIGHER_BYTE_POS] & |
| SLSI_EAPOL_KEY_INFO_MIC_BIT_IN_HIGHER_BYTE) && |
| eapol[SLSI_EAPOL_KEY_DATA_LENGTH_HIGHER_BYTE_POS] == 0 && |
| eapol[SLSI_EAPOL_KEY_DATA_LENGTH_LOWER_BYTE_POS] == 0) { |
| SLSI_INFO(sdev, "Received 4way-H/S, M4\n"); |
| } else if (!(eapol[SLSI_EAPOL_KEY_INFO_HIGHER_BYTE_POS] & |
| SLSI_EAPOL_KEY_INFO_MIC_BIT_IN_HIGHER_BYTE)) { |
| SLSI_INFO(sdev, "Received 4way-H/S, M1\n"); |
| } else if (eapol[SLSI_EAPOL_KEY_INFO_HIGHER_BYTE_POS] & |
| SLSI_EAPOL_KEY_INFO_SECURE_BIT_IN_HIGHER_BYTE) { |
| SLSI_INFO(sdev, "Received 4way-H/S, M3\n"); |
| } else { |
| SLSI_INFO(sdev, "Received 4way-H/S, M2\n"); |
| } |
| } else if (eap && eap[SLSI_EAPOL_IEEE8021X_TYPE_POS] == SLSI_IEEE8021X_TYPE_EAP_PACKET) { |
| if (eap[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_REQUEST) |
| SLSI_INFO(sdev, "Received EAP-Request (%d)\n", eap_length); |
| else if (eap[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_RESPONSE) |
| SLSI_INFO(sdev, "Received EAP-Response (%d)\n", eap_length); |
| else if (eap[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_SUCCESS) |
| SLSI_INFO(sdev, "Received EAP-Success (%d)\n", eap_length); |
| else if (eap[SLSI_EAP_CODE_POS] == SLSI_EAP_PACKET_FAILURE) |
| SLSI_INFO(sdev, "Received EAP-Failure (%d)\n", eap_length); |
| } |
| } else if (protocol == ETH_P_IP) { |
| if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_DISCOVER) |
| SLSI_INFO(sdev, "Received DHCP [DISCOVER]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_OFFER) |
| SLSI_INFO(sdev, "Received DHCP [OFFER]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_REQUEST) |
| SLSI_INFO(sdev, "Received DHCP [REQUEST]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_DECLINE) |
| SLSI_INFO(sdev, "Received DHCP [DECLINE]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_ACK) |
| SLSI_INFO(sdev, "Received DHCP [ACK]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_NAK) |
| SLSI_INFO(sdev, "Received DHCP [NAK]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_RELEASE) |
| SLSI_INFO(sdev, "Received DHCP [RELEASE]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_INFORM) |
| SLSI_INFO(sdev, "Received DHCP [INFORM]\n"); |
| else if (dhcp_message_type == SLSI_DHCP_MESSAGE_TYPE_FORCERENEW) |
| SLSI_INFO(sdev, "Received DHCP [FORCERENEW]\n"); |
| else |
| SLSI_INFO(sdev, "Received DHCP [INVALID]\n"); |
| } |
| slsi_dbg_untrack_skb(skb); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| SLSI_DBG2(sdev, SLSI_MLME, "pass %u bytes up (proto:%d)\n", skb->len, ntohs(skb->protocol)); |
| netif_rx_ni(skb); |
| slsi_wakelock_timeout(&sdev->wlan_wl_to, SLSI_RX_WAKELOCK_TIME); |
| return; |
| } |
| exit: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| void slsi_rx_mic_failure_ind(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| u8 *mac_addr; |
| u16 key_type, key_id; |
| enum nl80211_key_type nl_key_type; |
| |
| SLSI_UNUSED_PARAMETER(sdev); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| goto exit; |
| } |
| |
| mac_addr = fapi_get_buff(skb, u.mlme_disconnected_ind.peer_sta_address); |
| key_type = fapi_get_u16(skb, u.mlme_mic_failure_ind.key_type); |
| key_id = fapi_get_u16(skb, u.mlme_mic_failure_ind.key_id); |
| |
| SLSI_NET_DBG1(dev, SLSI_MLME, "mlme_mic_failure_ind(vif:%d, MAC:%pM, key_type:%d, key_id:%d)\n", |
| fapi_get_vif(skb), mac_addr, key_type, key_id); |
| |
| if (WARN_ON(key_type != FAPI_KEYTYPE_GROUP && key_type != FAPI_KEYTYPE_PAIRWISE)) |
| goto exit; |
| |
| nl_key_type = (key_type == FAPI_KEYTYPE_GROUP) ? NL80211_KEYTYPE_GROUP : NL80211_KEYTYPE_PAIRWISE; |
| |
| cfg80211_michael_mic_failure(dev, mac_addr, nl_key_type, key_id, NULL, GFP_KERNEL); |
| |
| exit: |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| /** |
| * Handler for mlme_listen_end_ind. |
| * The listen_end_ind would be received when the total Listen Offloading time is over. |
| * Indicate completion of Listen Offloading to supplicant by sending Cancel-ROC event |
| * with cookie 0xffff. Queue delayed work for unsync vif deletion. |
| */ |
| void slsi_rx_listen_end_ind(struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| |
| SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); |
| |
| SLSI_NET_DBG2(dev, SLSI_CFG80211, "Inform completion of P2P Listen Offloading\n"); |
| if (!ndev_vif->activated) { |
| SLSI_NET_DBG1(dev, SLSI_MLME, "VIF not activated\n"); |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| kfree_skb(skb); |
| return; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 9)) |
| cfg80211_remain_on_channel_expired(&ndev_vif->wdev, 0xffff, ndev_vif->chan, GFP_KERNEL); |
| #else |
| cfg80211_remain_on_channel_expired(ndev_vif->wdev.netdev, 0xffff, ndev_vif->chan, ndev_vif->channel_type, GFP_KERNEL); |
| #endif |
| |
| ndev_vif->unsync.listen_offload = false; |
| |
| slsi_p2p_queue_unsync_vif_del_work(ndev_vif, SLSI_P2P_UNSYNC_VIF_EXTRA_MSEC); |
| |
| SLSI_P2P_STATE_CHANGE(ndev_vif->sdev, P2P_IDLE_VIF_ACTIVE); |
| |
| SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); |
| slsi_kfree_skb(skb); |
| } |
| |
| #ifdef CONFIG_SCSC_WLAN_RX_NAPI |
| static int slsi_rx_msdu_napi(struct net_device *dev, struct sk_buff *skb) |
| { |
| struct netdev_vif *ndev_vif = netdev_priv(dev); |
| |
| slsi_skb_queue_tail(&ndev_vif->napi.rx_data, skb); |
| slsi_spinlock_lock(&ndev_vif->napi.lock); |
| if (ndev_vif->napi.interrupt_enabled) { |
| ndev_vif->napi.interrupt_enabled = false; |
| napi_schedule(&ndev_vif->napi.napi); |
| } |
| slsi_spinlock_unlock(&ndev_vif->napi.lock); |
| } |
| #endif |
| |
| static int slsi_rx_wait_ind_match(u16 recv_id, u16 wait_id) |
| { |
| if (recv_id == wait_id) |
| return 1; |
| if (wait_id == MLME_DISCONNECT_IND && recv_id == MLME_DISCONNECTED_IND) |
| return 1; |
| return 0; |
| } |
| |
| int slsi_rx_blocking_signals(struct slsi_dev *sdev, struct sk_buff *skb) |
| { |
| u16 pid, id; |
| struct slsi_sig_send *sig_wait; |
| u16 vif = fapi_get_vif(skb); |
| |
| sig_wait = &sdev->sig_wait; |
| id = fapi_get_sigid(skb); |
| pid = fapi_get_u16(skb, receiver_pid); |
| |
| /* ALL mlme cfm signals MUST have blocking call waiting for it (Per Vif or Global) */ |
| if (fapi_is_cfm(skb)) { |
| struct net_device *dev; |
| struct netdev_vif *ndev_vif; |
| |
| rcu_read_lock(); |
| dev = slsi_get_netdev_rcu(sdev, vif); |
| if (dev) { |
| ndev_vif = netdev_priv(dev); |
| sig_wait = &ndev_vif->sig_wait; |
| } |
| spin_lock_bh(&sig_wait->send_signal_lock); |
| if (id == sig_wait->cfm_id && pid == sig_wait->process_id) { |
| if (WARN_ON(sig_wait->cfm)) |
| slsi_kfree_skb(sig_wait->cfm); |
| sig_wait->cfm = skb; |
| spin_unlock_bh(&sig_wait->send_signal_lock); |
| complete(&sig_wait->completion); |
| rcu_read_unlock(); |
| return 0; |
| } |
| /** |
| * Important data frames such as EAPOL, ARP, DHCP are send |
| * over MLME. For these frames driver does not block on confirms. |
| * So there can be unexpected confirms here for such data frames. |
| * These confirms are treated as normal and is silently dropped |
| * here |
| */ |
| if (id == MLME_SEND_FRAME_CFM) { |
| spin_unlock_bh(&sig_wait->send_signal_lock); |
| rcu_read_unlock(); |
| slsi_kfree_skb(skb); |
| return 0; |
| } |
| |
| SLSI_DBG1(sdev, SLSI_MLME, "Unexpected cfm(0x%.4x, pid:0x%.4x, vif:%d)\n", id, pid, vif); |
| spin_unlock_bh(&sig_wait->send_signal_lock); |
| rcu_read_unlock(); |
| return -EINVAL; |
| } |
| /* Some mlme ind signals have a blocking call waiting (Per Vif or Global) */ |
| if (fapi_is_ind(skb)) { |
| struct net_device *dev; |
| struct netdev_vif *ndev_vif; |
| |
| rcu_read_lock(); |
| dev = slsi_get_netdev_rcu(sdev, vif); |
| if (dev) { |
| ndev_vif = netdev_priv(dev); |
| sig_wait = &ndev_vif->sig_wait; |
| } |
| spin_lock_bh(&sig_wait->send_signal_lock); |
| if (slsi_rx_wait_ind_match(id, sig_wait->ind_id) && pid == sig_wait->process_id) { |
| if (WARN_ON(sig_wait->ind)) |
| slsi_kfree_skb(sig_wait->ind); |
| sig_wait->ind = skb; |
| spin_unlock_bh(&sig_wait->send_signal_lock); |
| complete(&sig_wait->completion); |
| rcu_read_unlock(); |
| return 0; |
| } |
| spin_unlock_bh(&sig_wait->send_signal_lock); |
| rcu_read_unlock(); |
| } |
| return -EINVAL; |
| } |