return "SLSI_NL80211_RTT_RESULT_EVENT";
case SLSI_NL80211_RTT_COMPLETE_EVENT:
return "SLSI_NL80211_RTT_COMPLETE_EVENT";
+ case SLSI_NL80211_VENDOR_ACS_EVENT:
+ return "SLSI_NL80211_VENDOR_ACS_EVENT";
default:
return "UNKNOWN_EVENT";
}
#endif /* CONFIG_SCSC_WLAN_ENHANCED_LOGGING */
+static int slsi_acs_validate_width_hw_mode(struct slsi_acs_request *request)
+{
+ if (request->hw_mode != SLSI_ACS_MODE_IEEE80211A && request->hw_mode != SLSI_ACS_MODE_IEEE80211B &&
+ request->hw_mode != SLSI_ACS_MODE_IEEE80211G)
+ return -EINVAL;
+ if (request->ch_width != 20 && request->ch_width != 40 && request->ch_width != 80)
+ return -EINVAL;
+ return 0;
+}
+
+static int slsi_acs_init(struct wiphy *wiphy,
+ struct wireless_dev *wdev, const void *data, int len)
+{
+ struct slsi_dev *sdev = SDEV_FROM_WIPHY(wiphy);
+ struct net_device *dev = wdev->netdev;
+ struct netdev_vif *ndev_vif;
+ struct slsi_acs_request *request;
+ int temp;
+ int type;
+ const struct nlattr *attr;
+ int r = 0;
+ u32 *freq_list;
+ int freq_list_len = 0;
+
+ SLSI_INFO(sdev, "SUBCMD_ACS_INIT Received\n");
+ if (slsi_is_test_mode_enabled()) {
+ SLSI_ERR(sdev, "Not supported in WlanLite mode\n");
+ return -EOPNOTSUPP;
+ }
+ if (wdev->iftype != NL80211_IFTYPE_AP) {
+ SLSI_ERR(sdev, "Invalid iftype: %d\n", wdev->iftype);
+ return -EINVAL;
+ }
+ if (!dev) {
+ SLSI_ERR(sdev, "Dev not found!\n");
+ return -ENODEV;
+ }
+ request = kcalloc(1, sizeof(*request), GFP_KERNEL);
+ if (!request) {
+ SLSI_ERR(sdev, "No memory for request!");
+ return -ENOMEM;
+ }
+ ndev_vif = netdev_priv(dev);
+
+ SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
+ nla_for_each_attr(attr, data, len, temp) {
+ type = nla_type(attr);
+ switch (type) {
+ case SLSI_ACS_ATTR_HW_MODE:
+ {
+ request->hw_mode = nla_get_u8(attr);
+ SLSI_INFO(sdev, "ACS hw mode: %d\n", request->hw_mode);
+ break;
+ }
+ case SLSI_ACS_ATTR_CHWIDTH:
+ {
+ request->ch_width = nla_get_u16(attr);
+ SLSI_INFO(sdev, "ACS ch_width: %d\n", request->ch_width);
+ break;
+ }
+ case SLSI_ACS_ATTR_FREQ_LIST:
+ {
+ freq_list = kmalloc(nla_len(attr), GFP_KERNEL);
+ if (!freq_list) {
+ SLSI_ERR(sdev, "No memory for frequency list!");
+ kfree(request);
+ SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
+ return -ENOMEM;
+ }
+ memcpy(freq_list, nla_data(attr), nla_len(attr));
+ freq_list_len = nla_len(attr);
+ break;
+ }
+ default:
+ SLSI_ERR(sdev, "Invalid type : %d\n", type);
+ break;
+ }
+ }
+
+ r = slsi_acs_validate_width_hw_mode(request);
+ if (r == 0 && freq_list_len) {
+ struct ieee80211_channel *channels[freq_list_len];
+ struct slsi_acs_chan_info ch_info[MAX_CHAN_VALUE_ACS];
+ int i = 0, num_channels = 0;
+ int idx;
+ u32 chan_flags = (IEEE80211_CHAN_INDOOR_ONLY | IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_DISABLED |
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 13)
+ IEEE80211_CHAN_PASSIVE_SCAN
+#else
+ IEEE80211_CHAN_NO_IR
+#endif
+ );
+
+ memset(channels, 0, sizeof(channels));
+ memset(&ch_info, 0, sizeof(ch_info));
+ for (i = 0; i < freq_list_len; i++) {
+ channels[num_channels] = ieee80211_get_channel(wiphy, freq_list[i]);
+ if (!channels[num_channels]) {
+ SLSI_INFO(sdev, "Ignore invalid freq:%d in freq list\n", freq_list[i]);
+ } else if (channels[num_channels]->flags & chan_flags) {
+ SLSI_INFO(sdev, "Skip invalid channel:%d for ACS\n", channels[num_channels]->hw_value);
+ } else {
+ idx = slsi_find_chan_idx(channels[num_channels]->hw_value, request->hw_mode);
+ ch_info[idx].chan = channels[num_channels]->hw_value;
+ num_channels++;
+ }
+ }
+ for (i = 0; i < 25; i++)
+ SLSI_INFO(sdev, "Channel value:%d\n", ch_info[i].chan); /*will remove after testing */
+ if (request->hw_mode == SLSI_ACS_MODE_IEEE80211A)
+ request->ch_list_len = 25;
+ else
+ request->ch_list_len = 14;
+ memcpy(&request->acs_chan_info[0], &ch_info[0], sizeof(ch_info));
+ ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request = request;
+ ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan = false;
+ r = slsi_mlme_add_scan(sdev,
+ dev,
+ FAPI_SCANTYPE_AP_AUTO_CHANNEL_SELECTION,
+ FAPI_REPORTMODE_REAL_TIME,
+ 0, /* n_ssids */
+ NULL, /* ssids */
+ num_channels,
+ channels,
+ NULL,
+ NULL, /* ie */
+ 0, /* ie_len */
+ ndev_vif->scan[SLSI_SCAN_HW_ID].is_blocking_scan);
+ } else {
+ SLSI_ERR(sdev, "Invalid freq_list len:%d or ch_width:%d or hw_mode:%d\n", freq_list_len,
+ request->ch_width, request->hw_mode);
+ r = -EINVAL;
+ kfree(request);
+ }
+ SLSI_INFO(sdev, "SUBCMD_ACS_INIT Received 7 return value:%d\n", r); /*will remove after testing */
+ kfree(freq_list);
+ SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
+ return r;
+}
+
static const struct nl80211_vendor_cmd_info slsi_vendor_events[] = {
{ OUI_GOOGLE, SLSI_NL80211_SIGNIFICANT_CHANGE_EVENT },
{ OUI_GOOGLE, SLSI_NL80211_HOTLIST_AP_FOUND_EVENT },
{ OUI_GOOGLE, SLSI_NL80211_NAN_DISCOVERY_ENGINE_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_NAN_DISABLED_EVENT},
{ OUI_GOOGLE, SLSI_NL80211_RTT_RESULT_EVENT},
- { OUI_GOOGLE, SLSI_NL80211_RTT_COMPLETE_EVENT}
+ { OUI_GOOGLE, SLSI_NL80211_RTT_COMPLETE_EVENT},
+ { OUI_SAMSUNG, SLSI_NL80211_VENDOR_ACS_EVENT}
};
static const struct wiphy_vendor_command slsi_vendor_cmd[] = {
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = slsi_rtt_cancel_config
- }
+ },
+ {
+ {
+ .vendor_id = OUI_SAMSUNG,
+ .subcmd = SLSI_NL80211_VENDOR_SUBCMD_ACS_INIT
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = slsi_acs_init
+ },
};
void slsi_nl80211_vendor_deinit(struct slsi_dev *sdev)
#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);
SLSI_MUTEX_UNLOCK(ndev_vif->scan_result_mutex);
}
+int slsi_send_acs_event(struct slsi_dev *sdev, struct slsi_acs_selected_channels acs_selected_channels)
+{
+ struct sk_buff *skb = NULL;
+ u8 err = 0;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
+ skb = cfg80211_vendor_event_alloc(sdev->wiphy, NULL, NLMSG_DEFAULT_SIZE,
+ SLSI_NL80211_VENDOR_ACS_EVENT, GFP_KERNEL);
+#else
+ skb = cfg80211_vendor_event_alloc(sdev->wiphy, NLMSG_DEFAULT_SIZE,
+ SLSI_NL80211_VENDOR_ACS_EVENT, GFP_KERNEL);
+#endif
+ if (!skb) {
+ SLSI_ERR_NODEV("Failed to allocate skb for VENDOR ACS event\n");
+ return -ENOMEM;
+ }
+ err |= nla_put_u8(skb, SLSI_ACS_ATTR_PRIMARY_CHANNEL, acs_selected_channels.pri_channel);
+ err |= nla_put_u8(skb, SLSI_ACS_ATTR_SECONDARY_CHANNEL, acs_selected_channels.sec_channel);
+ err |= nla_put_u8(skb, SLSI_ACS_ATTR_VHT_SEG0_CENTER_CHANNEL, acs_selected_channels.vht_seg0_center_ch);
+ err |= nla_put_u8(skb, SLSI_ACS_ATTR_VHT_SEG1_CENTER_CHANNEL, acs_selected_channels.vht_seg1_center_ch);
+ err |= nla_put_u16(skb, SLSI_ACS_ATTR_CHWIDTH, acs_selected_channels.ch_width);
+ err |= nla_put_u8(skb, SLSI_ACS_ATTR_HW_MODE, acs_selected_channels.hw_mode);
+ SLSI_DBG3(sdev, SLSI_MLME, "pri_channel=%d,sec_channel=%d,vht_seg0_center_ch=%d,"
+ "vht_seg1_center_ch=%d, ch_width=%d, hw_mode=%d\n",
+ acs_selected_channels.pri_channel, acs_selected_channels.sec_channel,
+ acs_selected_channels.vht_seg0_center_ch, acs_selected_channels.vht_seg1_center_ch,
+ acs_selected_channels.ch_width, acs_selected_channels.hw_mode);
+ if (err) {
+ SLSI_ERR_NODEV("Failed nla_put err=%d\n", err);
+ slsi_kfree_skb(skb);
+ return -EINVAL;
+ }
+ SLSI_INFO(sdev, "Event: SLSI_NL80211_VENDOR_ACS_EVENT(%d)\n", SLSI_NL80211_VENDOR_ACS_EVENT);
+ cfg80211_vendor_event(skb, GFP_KERNEL);
+ return 0;
+}
+
+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, adjacent_rssi, avg_load, total_num_ap, total_rssi;
+ bool all_bss_load = true, none_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 = ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->ch_list_len;
+
+ 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;
+ }
+ none_bss_load = false;
+ } 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 == 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;
+ } else 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;
+ }
+ }
+ 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 = ndev_vif->scan[SLSI_SCAN_HW_ID].acs_request->ch_list_len;
+
+ 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 == 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;
+ } else 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;
+ }
+ } else {
+ SLSI_DBG3(sdev, SLSI_MLME, "Invalid channels: %d, %d\n", ch_info[i].chan,
+ ch_info[i + 1].chan); /*will remove after testing */
+ }
+ }
+ 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;
+ }
+ } else 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;
+ }
+ } else {
+ SLSI_DBG3(sdev, SLSI_MLME, "Invalid channels: %d, %d\n", ch_info[i].chan,
+ ch_info[i + 3].chan); /*will remove after testing */
+ }
+ }
+ 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;
+ }
+ }
+ return ret;
+}
+
+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;
+ int 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);
+ 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 */
+ ch_info[idx].rssi_factor += 10 ^ (fapi_get_s16(scan_res,
+ u.mlme_scan_ind.rssi) / 10) * ch_util;
+ } 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);
+ SLSI_DBG3(sdev, SLSI_MLME, "slsi_acs_scan_results Received end point\n"); /*will remove after testing */
+ 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
+ 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);
+ if (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);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_MUTEX_LOCK(ndev_vif->scan_mutex);
+ SLSI_NET_DBG3(dev, SLSI_GSCAN, "slsi_rx_scan_done_ind Received scan_id:%#x\n", scan_id);
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
if (slsi_is_gscan_id(scan_id)) {
#endif
scan_id = (scan_id & 0xFF);
- if (scan_id == SLSI_SCAN_HW_ID && ndev_vif->scan[SLSI_SCAN_HW_ID].scan_req)
+ 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);
-
- slsi_scan_complete(sdev, dev, scan_id, false);
+ 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);
SLSI_MUTEX_UNLOCK(ndev_vif->scan_mutex);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);