From 3cc8516faec11723a7559ca2da71dfd3bfb29a48 Mon Sep 17 00:00:00 2001 From: Himani Gupta Date: Mon, 24 Dec 2018 19:01:29 +0530 Subject: [PATCH] [RAMEN9610-10619][Common][9610] wlbt: [ACS] Driver changes Added support for ACS in driver. Change-Id: I94fe26c8890cd318e459cc79d15a795a2473d9ed SCSC-Bug-Id: SSB-47573 Signed-off-by: Himani Gupta --- drivers/net/wireless/scsc/Kconfig | 7 + drivers/net/wireless/scsc/cfg80211_ops.c | 3 + drivers/net/wireless/scsc/dev.c | 1 + drivers/net/wireless/scsc/dev.h | 2 + drivers/net/wireless/scsc/mgt.c | 19 + drivers/net/wireless/scsc/mgt.h | 1 + drivers/net/wireless/scsc/netif.c | 1 + drivers/net/wireless/scsc/nl80211_vendor.c | 156 ++++++++- drivers/net/wireless/scsc/nl80211_vendor.h | 59 +++- drivers/net/wireless/scsc/rx.c | 389 ++++++++++++++++++++- 10 files changed, 631 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/scsc/Kconfig b/drivers/net/wireless/scsc/Kconfig index 45f2a725026e..4e5c81f40af8 100755 --- a/drivers/net/wireless/scsc/Kconfig +++ b/drivers/net/wireless/scsc/Kconfig @@ -167,3 +167,10 @@ config SCSC_WLAN_SINGLE_ANTENNA ---help--- This option tells if there is support for single antenna or dual antenna. +config SCSC_WLAN_ACS_ENABLE + bool "ACS Support" + default y + ---help--- + This option tells if automatic channel selection is + supported or not. + diff --git a/drivers/net/wireless/scsc/cfg80211_ops.c b/drivers/net/wireless/scsc/cfg80211_ops.c index 4f9541e9a631..c5fb10ae93b4 100755 --- a/drivers/net/wireless/scsc/cfg80211_ops.c +++ b/drivers/net/wireless/scsc/cfg80211_ops.c @@ -2273,6 +2273,9 @@ int slsi_start_ap(struct wiphy *wiphy, struct net_device *dev, if ((indoor_channel == 1) #ifdef CONFIG_SCSC_WLAN_WIFI_SHARING || (wifi_sharing_channel_switched == 1) +#endif +#ifdef CONFIG_SCSC_WLAN_ACS_ENABLE + || (sdev->acs_channel_switched == true) #endif ) cfg80211_ch_switch_notify(dev, &settings->chandef); diff --git a/drivers/net/wireless/scsc/dev.c b/drivers/net/wireless/scsc/dev.c index 32eff2068740..7d4dbfe1319a 100755 --- a/drivers/net/wireless/scsc/dev.c +++ b/drivers/net/wireless/scsc/dev.c @@ -263,6 +263,7 @@ struct slsi_dev *slsi_dev_attach(struct device *dev, struct scsc_mx *core, struc sdev->local_mib.mib_file_name = local_mib_file; sdev->maddr_file_name = maddr_file; sdev->device_config.qos_info = -1; + sdev->acs_channel_switched = false; memset(&sdev->chip_info_mib, 0xFF, sizeof(struct slsi_chip_info_mib)); #ifdef CONFIG_SCSC_WLAN_WIFI_SHARING diff --git a/drivers/net/wireless/scsc/dev.h b/drivers/net/wireless/scsc/dev.h index 402833a102dc..d1f038f5e603 100755 --- a/drivers/net/wireless/scsc/dev.h +++ b/drivers/net/wireless/scsc/dev.h @@ -315,6 +315,7 @@ struct slsi_scan_result { struct slsi_scan { /* When a Scan is running this not NULL. */ struct cfg80211_scan_request *scan_req; + struct slsi_acs_request *acs_request; struct cfg80211_sched_scan_request *sched_req; bool requeue_timeout_work; @@ -1103,6 +1104,7 @@ struct slsi_dev { struct slsi_traffic_mon_clients traffic_mon_clients; /*Store vif index corresponding to rtt id for FTM*/ u16 rtt_vif[8]; + bool acs_channel_switched; }; /* Compact representation of channels a ESS has been seen on diff --git a/drivers/net/wireless/scsc/mgt.c b/drivers/net/wireless/scsc/mgt.c index b8319d1256ec..8a4b08614bfc 100755 --- a/drivers/net/wireless/scsc/mgt.c +++ b/drivers/net/wireless/scsc/mgt.c @@ -5137,3 +5137,22 @@ void slsi_update_supported_channels_regd_flags(struct slsi_dev *sdev) } } } + +int slsi_find_chan_idx(u16 chan, u8 hw_mode) +{ + int idx = 0, i = 0; + u16 slsi_5ghz_channels_list[25] = {36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, + 136, 140, 144, 149, 153, 157, 161, 165}; + + if (hw_mode == SLSI_ACS_MODE_IEEE80211B || hw_mode == SLSI_ACS_MODE_IEEE80211G) { + idx = chan - 1; + return idx; + } + for (i = 0; i < 25; i++) { + if (chan == slsi_5ghz_channels_list[i]) { + idx = i; + break; + } + } + return idx; +} diff --git a/drivers/net/wireless/scsc/mgt.h b/drivers/net/wireless/scsc/mgt.h index 99bf2e5a29b8..db928f324b18 100755 --- a/drivers/net/wireless/scsc/mgt.h +++ b/drivers/net/wireless/scsc/mgt.h @@ -480,5 +480,6 @@ void slsi_reset_channel_flags(struct slsi_dev *sdev); /* Sysfs based mac address override */ void slsi_create_sysfs_macaddr(void); void slsi_destroy_sysfs_macaddr(void); +int slsi_find_chan_idx(u16 chan, u8 hw_mode); #endif /*__SLSI_MGT_H__*/ diff --git a/drivers/net/wireless/scsc/netif.c b/drivers/net/wireless/scsc/netif.c index ce22fb35b19d..82094c0fd42d 100755 --- a/drivers/net/wireless/scsc/netif.c +++ b/drivers/net/wireless/scsc/netif.c @@ -258,6 +258,7 @@ static int slsi_net_stop(struct net_device *dev) sdev->allow_switch_40_mhz = true; sdev->allow_switch_80_mhz = true; + sdev->acs_channel_switched = false; slsi_wakeunlock(&sdev->wlan_wl); return 0; } diff --git a/drivers/net/wireless/scsc/nl80211_vendor.c b/drivers/net/wireless/scsc/nl80211_vendor.c index fc7be8822547..d011b1db74ff 100755 --- a/drivers/net/wireless/scsc/nl80211_vendor.c +++ b/drivers/net/wireless/scsc/nl80211_vendor.c @@ -85,6 +85,8 @@ char *slsi_print_event_name(int event_id) 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"; } @@ -6160,6 +6162,147 @@ exit: #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 }, @@ -6187,7 +6330,8 @@ static const struct nl80211_vendor_cmd_info slsi_vendor_events[] = { { 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[] = { @@ -6610,7 +6754,15 @@ 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) diff --git a/drivers/net/wireless/scsc/nl80211_vendor.h b/drivers/net/wireless/scsc/nl80211_vendor.h index 68c841c11a5a..1d75011004db 100755 --- a/drivers/net/wireless/scsc/nl80211_vendor.h +++ b/drivers/net/wireless/scsc/nl80211_vendor.h @@ -96,6 +96,33 @@ enum SLSI_ROAM_ATTRIBUTES { SLSI_NL_ATTR_ROAM_STATE }; +enum slsi_acs_attr_offload { + SLSI_ACS_ATTR_CHANNEL_INVALID = 0, + SLSI_ACS_ATTR_PRIMARY_CHANNEL, + SLSI_ACS_ATTR_SECONDARY_CHANNEL, + SLSI_ACS_ATTR_HW_MODE, + SLSI_ACS_ATTR_HT_ENABLED, + SLSI_ACS_ATTR_HT40_ENABLED, + SLSI_ACS_ATTR_VHT_ENABLED, + SLSI_ACS_ATTR_CHWIDTH, + SLSI_ACS_ATTR_CH_LIST, + SLSI_ACS_ATTR_VHT_SEG0_CENTER_CHANNEL, + SLSI_ACS_ATTR_VHT_SEG1_CENTER_CHANNEL, + SLSI_ACS_ATTR_FREQ_LIST, + /* keep last */ + SLSI_ACS_ATTR_AFTER_LAST, + SLSI_ACS_ATTR_MAX = + SLSI_ACS_ATTR_AFTER_LAST - 1 +}; + +enum slsi_acs_hw_mode { + SLSI_ACS_MODE_IEEE80211B, + SLSI_ACS_MODE_IEEE80211G, + SLSI_ACS_MODE_IEEE80211A, + SLSI_ACS_MODE_IEEE80211AD, + SLSI_ACS_MODE_IEEE80211ANY, +}; + enum SLSI_NAN_REPLY_ATTRIBUTES { NAN_REPLY_ATTR_STATUS_TYPE, NAN_REPLY_ATTR_VALUE, @@ -492,6 +519,7 @@ enum slsi_hal_vendor_subcmds { enum slsi_supp_vendor_subcmds { SLSI_NL80211_VENDOR_SUBCMD_UNSPEC = 0, SLSI_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY, + SLSI_NL80211_VENDOR_SUBCMD_ACS_INIT, }; enum slsi_vendor_event_values { @@ -517,7 +545,8 @@ enum slsi_vendor_event_values { SLSI_NL80211_NAN_DISCOVERY_ENGINE_EVENT, SLSI_NL80211_NAN_DISABLED_EVENT, SLSI_NL80211_RTT_RESULT_EVENT, - SLSI_NL80211_RTT_COMPLETE_EVENT + SLSI_NL80211_RTT_COMPLETE_EVENT, + SLSI_NL80211_VENDOR_ACS_EVENT }; enum slsi_lls_interface_mode { @@ -1612,6 +1641,34 @@ struct slsi_rtt_config { u16 LCR_request; /* 1: request LCR, 0: do not request LCR */ }; +#define MAX_CHAN_VALUE_ACS 25 /*Max number of supported channel is 25*/ + +struct slsi_acs_chan_info { + u16 chan; + u8 num_ap; + u8 num_bss_load_ap; + u8 total_chan_utilization; + u8 avg_chan_utilization; + u8 rssi_factor; + u8 adj_rssi_factor; +}; + +struct slsi_acs_selected_channels { + u8 pri_channel; + u8 sec_channel; + u8 vht_seg0_center_ch; + u8 vht_seg1_center_ch; + u16 ch_width; + enum slsi_acs_hw_mode hw_mode; +}; + +struct slsi_acs_request { + struct slsi_acs_chan_info acs_chan_info[MAX_CHAN_VALUE_ACS]; + u8 hw_mode; + u16 ch_width; + u8 ch_list_len; +}; + void slsi_nl80211_vendor_init(struct slsi_dev *sdev); void slsi_nl80211_vendor_deinit(struct slsi_dev *sdev); u8 slsi_gscan_get_scan_policy(enum wifi_band band); diff --git a/drivers/net/wireless/scsc/rx.c b/drivers/net/wireless/scsc/rx.c index 16e3b24673df..9d1fb2f88769 100755 --- a/drivers/net/wireless/scsc/rx.c +++ b/drivers/net/wireless/scsc/rx.c @@ -28,7 +28,6 @@ #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); @@ -535,6 +534,384 @@ void slsi_scan_complete(struct slsi_dev *sdev, struct net_device *dev, u16 scan_ 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); @@ -542,6 +919,7 @@ void slsi_rx_scan_done_ind(struct slsi_dev *sdev, struct net_device *dev, struct 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)) { @@ -556,10 +934,13 @@ void slsi_rx_scan_done_ind(struct slsi_dev *sdev, struct net_device *dev, struct #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); -- 2.20.1