594 lines
21 KiB
C
Executable file
594 lines
21 KiB
C
Executable file
/*****************************************************************************
|
|
*
|
|
* Copyright (c) 2012 - 2022 Samsung Electronics Co., Ltd. All rights reserved
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include "mgt.h"
|
|
#include "dev.h"
|
|
#include "debug.h"
|
|
#include "qsfs.h"
|
|
#include "mib.h"
|
|
#include "mlme.h"
|
|
|
|
static char *sol_name = "SLS";
|
|
module_param(sol_name, charp, 0444);
|
|
MODULE_PARM_DESC(sol_name, "Solution Provider Name");
|
|
|
|
static ssize_t sysfs_show_qsf(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
|
|
struct kobj_attribute qsf_attr = __ATTR(feature, 0400, sysfs_show_qsf, NULL);
|
|
static struct kobject *wifi_kobj_ref;
|
|
|
|
static int slsi_qsf_get_wifi_fw_feature_version(struct slsi_dev *sdev, u32 *fw_ver)
|
|
{
|
|
struct slsi_mib_data mibrsp = { 0, NULL };
|
|
struct slsi_mib_value *values = NULL;
|
|
struct slsi_mib_get_entry get_values[] = { { SLSI_PSID_UNIFI_QSFS_VERION, { 0, 0 } },};
|
|
const struct firmware *e = NULL;
|
|
int r = 0;
|
|
|
|
r = mx140_file_request_conf(sdev->maxwell_core, &e, "wlan", SLSI_QSF_WIFI_HCF_FILE_NAME);
|
|
if (r || !e) {
|
|
SLSI_ERR(sdev, "HCF file read failed as file %s is NOT found\n", SLSI_QSF_WIFI_HCF_FILE_NAME);
|
|
return -1;
|
|
}
|
|
mibrsp.dataLength = e->size - SLSI_HCF_HEADER_LEN;
|
|
mibrsp.data = (u8 *)e->data + SLSI_HCF_HEADER_LEN;
|
|
values = slsi_mib_decode_get_list(&mibrsp, ARRAY_SIZE(get_values), get_values);
|
|
if (!values) {
|
|
SLSI_ERR(sdev, "QSFS mib decode failed returned values as NULL\n");
|
|
mx140_file_release_conf(sdev->maxwell_core, e);
|
|
return -1;
|
|
}
|
|
|
|
*fw_ver = values[0].u.uintValue;
|
|
kfree(values);
|
|
mx140_file_release_conf(sdev->maxwell_core, e);
|
|
return 0;
|
|
}
|
|
|
|
void slsi_qsf_init(struct slsi_dev *sdev)
|
|
{
|
|
int r = 0;
|
|
|
|
wifi_kobj_ref = mxman_wifi_kobject_ref_get();
|
|
pr_info("wifi_kobj_ref: 0x%p\n", wifi_kobj_ref);
|
|
|
|
if (!wifi_kobj_ref)
|
|
return;
|
|
|
|
r = sysfs_create_file(wifi_kobj_ref, &qsf_attr.attr);
|
|
if (r) {
|
|
pr_err("Can't create /sys/wifi/feature\n");
|
|
mxman_wifi_kobject_ref_put();
|
|
}
|
|
}
|
|
|
|
void slsi_qsf_deinit(void)
|
|
{
|
|
if (!wifi_kobj_ref)
|
|
return;
|
|
|
|
sysfs_remove_file(wifi_kobj_ref, &qsf_attr.attr);
|
|
mxman_wifi_kobject_ref_put();
|
|
}
|
|
|
|
static u8 slsi_get_wifi_standard(struct slsi_dev *sdev)
|
|
{
|
|
if (sdev->fw_vht_enabled)
|
|
return SLSI_WIFI_5;
|
|
|
|
if (sdev->fw_ht_enabled)
|
|
return SLSI_WIFI_4;
|
|
return 0xFF;
|
|
}
|
|
|
|
static u32 slsi_get_antenna_from_ht_caps(u8 *caps)
|
|
{
|
|
u8 max_nss = 0;
|
|
|
|
if (SLSI_SUPPORTED_TX_MCS_PRESENT_AND_TXRX_MCS_NOT_EQUAL(caps) == 3)
|
|
max_nss = SLSI_GET_NSS_FROM_HT_CAPS(caps);
|
|
else
|
|
return 1;
|
|
|
|
switch (max_nss) {
|
|
case 0:
|
|
return 1;
|
|
case 1:
|
|
return 2;
|
|
case 2:
|
|
return 3;
|
|
case 3:
|
|
return 4;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static u8 slsi_get_nss_from_mcs_nss_map(u16 mcs_map)
|
|
{
|
|
if (SLSI_MAX_MCS_8NSS(mcs_map) != SLSI_MAX_NSS_NOT_SUPPORTED)
|
|
return 8;
|
|
if (SLSI_MAX_MCS_7NSS(mcs_map) != SLSI_MAX_NSS_NOT_SUPPORTED)
|
|
return 7;
|
|
if (SLSI_MAX_MCS_6NSS(mcs_map) != SLSI_MAX_NSS_NOT_SUPPORTED)
|
|
return 6;
|
|
if (SLSI_MAX_MCS_5NSS(mcs_map) != SLSI_MAX_NSS_NOT_SUPPORTED)
|
|
return 5;
|
|
if (SLSI_MAX_MCS_4NSS(mcs_map) != SLSI_MAX_NSS_NOT_SUPPORTED)
|
|
return 4;
|
|
if (SLSI_MAX_MCS_3NSS(mcs_map) != SLSI_MAX_NSS_NOT_SUPPORTED)
|
|
return 3;
|
|
if (SLSI_MAX_MCS_2NSS(mcs_map) != SLSI_MAX_NSS_NOT_SUPPORTED)
|
|
return 2;
|
|
if (SLSI_MAX_MCS_1NSS(mcs_map) != SLSI_MAX_NSS_NOT_SUPPORTED)
|
|
return 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static u32 slsi_get_antenna_from_vht_caps(u8 *caps)
|
|
{
|
|
u16 rx_vht_mcs_map = 0, tx_vht_mcs_map = 0;
|
|
u8 tx_max_nss = 0, rx_max_nss = 0;
|
|
|
|
rx_vht_mcs_map = SLSI_GET_VHT_RX_MCS_MAP(caps);
|
|
tx_vht_mcs_map = SLSI_GET_VHT_TX_MCS_MAP(caps);
|
|
|
|
tx_max_nss = slsi_get_nss_from_mcs_nss_map(tx_vht_mcs_map);
|
|
rx_max_nss = slsi_get_nss_from_mcs_nss_map(rx_vht_mcs_map);
|
|
|
|
if (tx_max_nss == 0 && rx_max_nss == 0)
|
|
return 1;
|
|
return max(tx_max_nss, rx_max_nss);
|
|
}
|
|
|
|
static u32 slsi_qsf_encode_hw_feature(struct slsi_dev *sdev, u8 *misc_features_activated,
|
|
bool he_active, char *buf, u32 bytes, u32 buf_size,
|
|
u8 *he_caps, u8 *vht_caps, u8 *ht_caps)
|
|
{
|
|
u8 wifi_standard = 0;
|
|
u32 hw_feature = 0;
|
|
u8 low_rx_core = 0;
|
|
u32 main_cores = 0;
|
|
u32 antenna = 0;
|
|
u32 hw_feature_len = 4;
|
|
|
|
wifi_standard = slsi_get_wifi_standard(sdev);
|
|
if (wifi_standard <= SLSI_WIFI_5) {
|
|
SLSI_QSF_SET_WIFI_STANDARD(hw_feature, wifi_standard);
|
|
} else {
|
|
SLSI_ERR(sdev, "Error while getting wifi_standard, %d\n", wifi_standard);
|
|
return bytes;
|
|
}
|
|
low_rx_core = misc_features_activated[0] & SLSI_QSF_LOW_RX_CORE_GET_MASK;
|
|
if (low_rx_core <= 1) {
|
|
SLSI_QSF_SET_LOW_PWR_RX_CORE(hw_feature, low_rx_core);
|
|
} else {
|
|
SLSI_ERR(sdev, "Error in getting low_rx_core, %d\n", low_rx_core);
|
|
return bytes;
|
|
}
|
|
main_cores = (misc_features_activated[0] & SLSI_QSF_MAIN_CORES_GET_MASK) >> SLSI_QSF_MAIN_CORES_SHIFT;
|
|
if (main_cores <= 2) {
|
|
SLSI_QSF_SET_NUMBER_OF_CORES(hw_feature, main_cores);
|
|
} else {
|
|
SLSI_ERR(sdev, "Error in getting main_cores, %d\n", main_cores);
|
|
return bytes;
|
|
}
|
|
|
|
switch (wifi_standard) {
|
|
case SLSI_WIFI_5:
|
|
antenna = slsi_get_antenna_from_vht_caps(vht_caps);
|
|
break;
|
|
case SLSI_WIFI_4:
|
|
antenna = slsi_get_antenna_from_ht_caps(ht_caps);
|
|
break;
|
|
default:
|
|
antenna = 1;
|
|
}
|
|
if (antenna <= SLSI_QSF_NUM_ANTENNA_MAX) {
|
|
SLSI_QSF_SET_NUM_ANTENNA(hw_feature, antenna);
|
|
} else {
|
|
SLSI_ERR(sdev, "Error in getting antenna, %d\n", antenna);
|
|
return bytes;
|
|
}
|
|
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%04X", hw_feature_len, cpu_to_le16(hw_feature));
|
|
return bytes;
|
|
}
|
|
|
|
static u32 slsi_qsf_encode_sw_feature_1(struct slsi_dev *sdev, u8 *misc_features_activated,
|
|
u32 twt_control, bool twt_active,
|
|
char *buf, u32 bytes, u32 buf_size)
|
|
{
|
|
u8 wifi_optimizer = 0, scheduled_pm = 0, delayed_wakeup = 0, rfc_8325 = 0, pno = 0;
|
|
u32 pno_enabled = 1, pno_in_un_assocociated = 1, twt = 0;
|
|
|
|
u32 twt_broadcast = SLSI_GET_TWT_BROADCAST(twt_control);
|
|
u32 twt_req = SLSI_GET_TWT_REQ(twt_control);
|
|
u32 twt_flexible = SLSI_GET_TWT_FLEXIBLE(twt_control);
|
|
u32 min_service_period = SLSI_GET_TWT_MIN_SERVICE_PERIOD(misc_features_activated);
|
|
u32 min_sleep_period = SLSI_GET_TWT_min_sleep_period(misc_features_activated);
|
|
u32 pno_in_associated = SLSI_GET_PNO_STATE_IN_ASSOC(misc_features_activated);
|
|
u32 wifi_optimizer_support = SLSI_GET_WIFI_OPTIMIZER_SUPPORT(misc_features_activated);
|
|
u32 dynamic_dwell_control = SLSI_GET_DYNAMIC_DWELL_CONTROL(misc_features_activated);
|
|
u32 enhanced_passive_scan = SLSI_GET_ENHANCED_PASSIVE_SCAN(misc_features_activated);
|
|
u32 sched_pm_support = SLSI_GET_SCHED_PM_SUPPORT(misc_features_activated);
|
|
u32 delayed_wakeup_supp = SLSI_GET_DELAYED_WAKEUP_SUPPORT(misc_features_activated);
|
|
|
|
if (pno_enabled)
|
|
pno |= SLSI_PNO_ENABLED;
|
|
if (pno_in_un_assocociated)
|
|
pno |= SLSI_PNO_UNASSOIATED_ENABED;
|
|
if (pno_in_associated)
|
|
pno |= SLSI_PNO_ASSOIATED_ENABED;
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%02X", SLSI_QSF_SW_FEATURE_PNO_ID,
|
|
SLSI_QSF_SW_FEATURE_PNO_LEN, pno);
|
|
|
|
if (twt_active)
|
|
twt |= SLSI_TWT_ENABLED;
|
|
|
|
if (twt_req)
|
|
twt |= SLSI_TWT_REQ_SUPPORTED;
|
|
if (twt_broadcast)
|
|
twt |= SLSI_TWT_BROADCAST_SUPPORTED;
|
|
if (twt_flexible)
|
|
twt |= SLSI_TWT_FLEXIBLE_SUPPORTED;
|
|
SLSI_SET_TWT_MIN_SERVICE_PERIOD(twt, min_service_period);
|
|
SLSI_SET_TWTMIN_SLEEP_PERIOD(twt, min_sleep_period);
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%04X", SLSI_QSF_SW_FEATURE_TWT_ID,
|
|
SLSI_QSF_SW_FEATURE_TWT_LEN, cpu_to_le16(twt));
|
|
|
|
if (wifi_optimizer_support)
|
|
wifi_optimizer |= SLSI_WIFI_OPTIMIZER_SUPPORTED;
|
|
if (dynamic_dwell_control)
|
|
wifi_optimizer |= SLSI_DYNAMIC_DWELL_CONTROL_SUPPORTED;
|
|
if (enhanced_passive_scan)
|
|
wifi_optimizer |= SLSI_ENHANCED_PASSIVE_SCAN_SUPPORDED;
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%02X", SLSI_QSF_SW_FEATURE_WIFI_OPTIMIZER_ID,
|
|
SLSI_QSF_SW_FEATURE_WIFI_OPTIMIZER_LEN, wifi_optimizer);
|
|
|
|
if (sched_pm_support)
|
|
scheduled_pm = SLSI_SCHED_PM_ENABLED;
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%02X", SLSI_QSF_SW_FEATURE_SCHEDULED_PM_ID,
|
|
SLSI_QSF_SW_FEATURE_SCHEDULED_PM_LEN, scheduled_pm);
|
|
if (delayed_wakeup_supp)
|
|
delayed_wakeup = SLSI_DELAYED_WAKEUP_ENABLED;
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%02X", SLSI_QSF_SW_FEATURE_DELAYED_WAKEUP_ID,
|
|
SLSI_QSF_SW_FEATURE_DELAYED_WAKEUP_LEN, delayed_wakeup);
|
|
#ifndef CONFIG_SCSC_USE_WMM_TOS
|
|
rfc_8325 |= 0x01;
|
|
#endif
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%02X", SLSI_QSF_SW_FEATURE_RFC_8325_ID,
|
|
SLSI_QSF_SW_FEATURE_RFC_8325_LEN, rfc_8325);
|
|
return bytes;
|
|
}
|
|
|
|
static u32 slsi_qsf_encode_sw_feature_2(struct slsi_dev *sdev, u8 *misc_features_activated,
|
|
bool he_softap_active, bool wpa3_active, u32 appendix_versions,
|
|
char *buf, u32 bytes, u32 buf_size)
|
|
{
|
|
#ifdef SCSC_SEP_VERSION
|
|
u16 max_cli = sdev->softap_max_client;
|
|
#endif
|
|
u32 mhs = 0, roaming = 0, country_code_set_hal_api = 1, get_valid_chan_hal_api = 1;
|
|
|
|
u32 dual_interface = SLSI_GET_DUAL_IFACE_SUPPORT(misc_features_activated);
|
|
u32 support_5g = SLSI_GET_SUPPORT_5G(misc_features_activated);
|
|
u32 support_6g = SLSI_GET_SUPPORT_6G(misc_features_activated);
|
|
u32 high_chan_utilization_trigger = SLSI_GET_HIGH_CHAN_UTILIZATON_TRIGGER(misc_features_activated);
|
|
u32 emergency_roaming_trigger = SLSI_GET_EMERGENCY_ROAMING_TRIGGER(misc_features_activated);
|
|
u32 btm_roaming_trigger = SLSI_GET_BTM_ROAMING_TRIGGER(misc_features_activated);
|
|
u32 roaming_major_number = SLSI_GET_ROAMING_MAJOR_NUMBER(appendix_versions);
|
|
u32 roaming_minor_number = SLSI_GET_ROAMING_MINOR_NUMBER(appendix_versions);
|
|
u32 idle_roaming_trogger = SLSI_GET_IDLE_ROAMING_TRIGGER(misc_features_activated);
|
|
u32 wtc_roaming_trigger = SLSI_GET_WTC_ROAMING_TRIGGER(misc_features_activated);
|
|
u32 bt_coex_roaming_trigger = SLSI_GET_BT_COEX_ROAMING(misc_features_activated);
|
|
u32 roaming_bwtween_wpa_and_wpa2 = SLSI_GET_ROAMING_BETWEEN_WPA_AND_WPA2(misc_features_activated);
|
|
u32 roaming_ctrl_api_1_2 = SLSI_GET_ROAMING_CTRL_API_1_2(misc_features_activated);
|
|
u32 roaming_ctrl_api_3_4 = SLSI_GET_ROAMING_CTRL_API_3_4(misc_features_activated);
|
|
u32 roaming_ctrl_api_5 = SLSI_GET_ROAMING_CTRL_API_5(misc_features_activated);
|
|
u32 manage_chan_list_api = SLSI_GET_MANAGE_CHAN_LIST_API(misc_features_activated);
|
|
u32 adaptive_11r = SLSI_GET_ADAPTIVE_11R(misc_features_activated);
|
|
|
|
if (dual_interface)
|
|
mhs |= SLSI_DUAL_IFACE_ENABLED;
|
|
if (support_5g)
|
|
mhs |= SLSI_5G_SUPPORT;
|
|
if (support_6g)
|
|
mhs |= SLSI_6G_SUPPORT;
|
|
#ifdef SCSC_SEP_VERSION
|
|
if (max_cli)
|
|
mhs |= SLSI_SET_MAX_CLIENT(max_cli);
|
|
#endif
|
|
if (country_code_set_hal_api)
|
|
mhs |= SLSI_COUNTRY_CODE_SET_API_SUPPORTED;
|
|
if (get_valid_chan_hal_api)
|
|
mhs |= SLSI_QSF_VALID_CHAN_API_SUPPORTED;
|
|
if (he_softap_active)
|
|
mhs |= SLSI_HE_ENABLED;
|
|
if (wpa3_active)
|
|
mhs |= SLSI_WPA3_ENABLED;
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%04X", SLSI_QSF_SW_FEATURE_MHS_ID,
|
|
SLSI_QSF_SW_FEATURE_MHS_LEN, cpu_to_le16(mhs));
|
|
|
|
roaming |= roaming_major_number;
|
|
roaming |= roaming_minor_number << 8;
|
|
|
|
if (high_chan_utilization_trigger)
|
|
roaming |= SLSI_HIGH_CHAN_UTILIZATON_TRIGGER;
|
|
if (emergency_roaming_trigger)
|
|
roaming |= SLSI_EMERGENCY_ROAMING_TRIGGER;
|
|
if (btm_roaming_trigger)
|
|
roaming |= SLSI_BTM_ROAMING_TRIGGER;
|
|
if (idle_roaming_trogger)
|
|
roaming |= SLSI_IDLE_ROAMING_TRIGGER;
|
|
if (wtc_roaming_trigger)
|
|
roaming |= SLSI_WTC_ROAMING_TRIGGER;
|
|
if (bt_coex_roaming_trigger)
|
|
roaming |= SLSI_BT_COEX_ROAMING;
|
|
if (roaming_bwtween_wpa_and_wpa2)
|
|
roaming |= SLSI_ROAMING_BETWEEN_WPA_AND_WPA2;
|
|
if (manage_chan_list_api)
|
|
roaming |= SLSI_MANAGE_CHAN_LIST_API;
|
|
if (adaptive_11r)
|
|
roaming |= SLSI_ADAPTIVE_11R;
|
|
if (roaming_ctrl_api_1_2)
|
|
roaming |= SLSI_ROAMING_CTRL_API_1_2;
|
|
if (roaming_ctrl_api_3_4)
|
|
roaming |= SLSI_ROAMING_CTRL_API_3_4;
|
|
if (roaming_ctrl_api_5)
|
|
roaming |= SLSI_ROAMING_CTRL_API_5;
|
|
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%08X", SLSI_QSF_SW_FEATURE_ROAMING_ID,
|
|
SLSI_QSF_SW_FEATURE_ROAMING_LEN, cpu_to_be32(roaming));
|
|
return bytes;
|
|
}
|
|
|
|
static u32 slsi_qsf_encode_sw_feature_3(struct slsi_dev *sdev, u8 *misc_features_activated,
|
|
u32 appendix_versions,
|
|
char *buf, u32 bytes, u32 buf_size)
|
|
{
|
|
u32 ncho = 0;
|
|
u32 ncho_major = 0;
|
|
u32 ncho_minor = 0;
|
|
u8 pcap_frame_logging = 0;
|
|
u16 security = 0;
|
|
|
|
u32 mgmt_frame = SLSI_GET_SUPPORT_MGMT_FRAMES(misc_features_activated);
|
|
u32 ctrl_frame = SLSI_GET_SUPPORT_CTRL_FRAMES(misc_features_activated);
|
|
u32 data_frame = SLSI_GET_SUPPORT_DATA_FRAMES(misc_features_activated);
|
|
u8 assurance = misc_features_activated[9] & SLSI_QSF_SW_FEATURE_ASSURANCE_MASK;
|
|
|
|
ncho_major = SLSI_GET_NCHO_MAJOR_NUMBER(appendix_versions);
|
|
ncho_minor = SLSI_GET_NCHO_MINOR_NUMBER(appendix_versions);
|
|
|
|
ncho |= ncho_major;
|
|
ncho |= ncho_minor << 8;
|
|
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%04X", SLSI_QSF_SW_FEATURE_NCHO_ID,
|
|
SLSI_QSF_SW_FEATURE_NCHO_LEN, cpu_to_be16(ncho));
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%02X", SLSI_QSF_SW_FEATURE_ASSURANCE_ID,
|
|
SLSI_QSF_SW_FEATURE_ASSURANCELEN, assurance);
|
|
if (mgmt_frame)
|
|
pcap_frame_logging |= SLSI_MGMT_FRAME_ENABLED;
|
|
if (ctrl_frame)
|
|
pcap_frame_logging |= SLSI_CTRL_FRAME_ENABLED;
|
|
if (data_frame)
|
|
pcap_frame_logging |= SLSI_DATA_FRAME_ENABLED;
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%02X", SLSI_QSF_SW_FEATURE_PCAP_FRAME_LOGGING_ID,
|
|
SLSI_QSF_SW_FEATURE_PCAP_FRAME_LOGGING_LEN, pcap_frame_logging);
|
|
|
|
security = (SLSI_BUFF_LE_TO_U16(misc_features_activated + 11) & SLSI_QSF_SECURITY_FEATURE_MASK);
|
|
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%04X", SLSI_QSF_SW_FEATURE_SECURITY_ID,
|
|
SLSI_QSF_SW_FEATURE_SECURITY_LEN, cpu_to_be16(security));
|
|
return bytes;
|
|
}
|
|
|
|
static u32 slsi_qsf_encode_sw_feature_4(struct slsi_dev *sdev, u8 *misc_features_activated,
|
|
bool tdls_active, u16 max_tdls_cli,
|
|
bool tdls_peer_uapsd_active, bool nan_fast_connect,
|
|
char *buf, u32 bytes, u32 buf_size)
|
|
{
|
|
u32 get_bssi_info_api_supp = 1, get_assoc_reject_info_api_supp = 1;
|
|
u32 get_sta_info_api_supp = 1;
|
|
u8 p2p[6] = {0};
|
|
u8 big_data = 0;
|
|
|
|
u32 max_nan_ndps = SLSI_GET_MAX_NAN_NDPS(misc_features_activated);
|
|
|
|
u32 standard_nan_6e = SLSI_GET_STANDARD_NAN_6E_SUPPORT(misc_features_activated);
|
|
u32 samsung_nan_6e = SLSI_GET_SAMSUNG_NAN_6E_SUPPORT(misc_features_activated);
|
|
u32 nan_version = SLSI_GET_NAN_VERSION(misc_features_activated);
|
|
|
|
if (sdev->nan_enabled)
|
|
p2p[0] |= SLSI_QSF_NAN_SUPPORTED;
|
|
if (tdls_active)
|
|
p2p[0] |= SLSI_QSF_TDLS_SUPPORTED;
|
|
#ifdef CONFIG_SCSC_WLAN_SUPPORT_6G
|
|
if (sdev->band_6g_supported)
|
|
p2p[0] |= SLSI_QSF_6E_SUPPORTED;
|
|
#endif
|
|
if (tdls_peer_uapsd_active)
|
|
p2p[0] |= SLSI_QSF_TDLS_PEER_UAPSD_SUPPORTED;
|
|
p2p[0] |= misc_features_activated[13] & SLSI_QSF_P2P_FEATURE_1ST_BYTE_MASK;
|
|
|
|
if (max_tdls_cli)
|
|
p2p[1] |= max_tdls_cli & 0xF;
|
|
if (max_nan_ndps)
|
|
p2p[1] |= max_nan_ndps;
|
|
|
|
p2p[2] = misc_features_activated[15] & (SLSI_QSF_P2P_FEATURE_3RD_BYTE_MASK);
|
|
|
|
p2p[3] = misc_features_activated[16] & SLSI_QSF_P2P_FEATURE_4TH_BYTE_MASK;
|
|
if (standard_nan_6e)
|
|
p2p[5] |= SLSI_QSF_STANDARD_NAN_6E_SUPPORTED;
|
|
if (samsung_nan_6e)
|
|
p2p[5] |= SLSI_QSF_SAMSUNG_NAN_6E_SUPPORTED;
|
|
if (nan_fast_connect)
|
|
p2p[5] |= SLSI_QSF_NAN_FAST_CONNECT_SUPPORTED;
|
|
if (nan_version)
|
|
p2p[5] |= SLSI_QSF_NAN_VERSION_SET(nan_version);
|
|
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%02X%02X%02X%02X%02X%02X",
|
|
SLSI_QSF_SW_FEATURE_P2P_ID, SLSI_QSF_SW_FEATURE_P2P_LEN, p2p[0],
|
|
p2p[1], p2p[2], p2p[3], p2p[4], p2p[5]);
|
|
|
|
if (get_bssi_info_api_supp)
|
|
big_data |= SLSI_QSF_BSSI_INFO_API_SUPP_ENABLED;
|
|
if (get_assoc_reject_info_api_supp)
|
|
big_data |= SLSI_QSF_ASSOC_REJECT_INFO_API_ENABLED;
|
|
if (get_sta_info_api_supp)
|
|
big_data |= SLSI_QSF_STA_INFO_API_SUPP_ENABLED;
|
|
bytes += scnprintf(buf + bytes, buf_size - bytes, "%02X%02X%02X", SLSI_QSF_SW_FEATURE_BIG_DATA_ID,
|
|
SLSI_QSF_SW_FEATURE_BIG_DATA_LEN, big_data);
|
|
return bytes;
|
|
}
|
|
|
|
static inline void slsi_qsf_extract_bool_data(struct slsi_dev *sdev, struct slsi_mib_value *values,
|
|
int index, bool *val)
|
|
{
|
|
if (values[index].type == SLSI_MIB_TYPE_UINT || values[index].type == SLSI_MIB_TYPE_BOOL)
|
|
*val = values[index].u.boolValue;
|
|
else
|
|
SLSI_ERR(sdev, "Type mismatch for index: %d\n", index);
|
|
}
|
|
|
|
static int slsi_get_qsf_mib_data(struct slsi_dev *sdev, struct slsi_qsf_mib_data *qsf_data)
|
|
{
|
|
struct slsi_mib_data mibrsp = { 0, NULL };
|
|
struct slsi_mib_value *values = NULL;
|
|
struct slsi_mib_get_entry get_values[] = { { SLSI_PSID_UNIFI_HE_ACTIVATED, { 0, 0 } },
|
|
{ SLSI_PSID_UNIFI_TWT_ACTIVATED, {0, 0} },
|
|
{ SLSI_PSID_UNIFI_WP_A3_ACTIVATED, {0, 0} },
|
|
{ SLSI_PSID_UNIFI_TDLS_ACTIVATED, {0, 0} },
|
|
{ SLSI_PSID_DOT11_TDLS_PEER_UAPSD_BUFFER_STA_ACTIVATED, {0, 0} },
|
|
{ SLSI_PSID_UNIFI_HT_CAPABILITIES, {0, 0} },
|
|
{ SLSI_PSID_UNIFI_VHT_CAPABILITIES, {0, 0} },
|
|
{ SLSI_PSID_UNIFI_HE_CAPABILITIES, {0, 0} },
|
|
{ SLSI_PSID_UNIFI_MISC_FEATURES_ACTIVATED, {0, 0} },
|
|
{ SLSI_PSID_UNIFI_APPENDIX_VERSIONS, {0, 0} },
|
|
{ SLSI_PSID_UNIFI_TWT_CONTROL_FLAGS, {0, 0} },
|
|
};
|
|
|
|
mibrsp.dataLength = 32 * ARRAY_SIZE(get_values);
|
|
mibrsp.data = kmalloc(mibrsp.dataLength, GFP_KERNEL);
|
|
if (!mibrsp.data) {
|
|
SLSI_ERR(sdev, "Cannot kmalloc %d bytes\n", mibrsp.dataLength);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
values = slsi_read_mibs(sdev, NULL, get_values, ARRAY_SIZE(get_values), &mibrsp);
|
|
if (!values) {
|
|
kfree(mibrsp.data);
|
|
SLSI_ERR(sdev, "Error in slsi_read_mibs\n");
|
|
return -1;
|
|
}
|
|
|
|
qsf_data->max_tdls_cli = SLSI_MAX_TDLS_LINK;
|
|
slsi_qsf_extract_bool_data(sdev, values, 0, &qsf_data->he_active);
|
|
slsi_qsf_extract_bool_data(sdev, values, 1, &qsf_data->twt_active);
|
|
slsi_qsf_extract_bool_data(sdev, values, 2, &qsf_data->wpa3_active);
|
|
slsi_qsf_extract_bool_data(sdev, values, 3, &qsf_data->tdls_active);
|
|
slsi_qsf_extract_bool_data(sdev, values, 4, &qsf_data->tdls_peer_uapsd_active);
|
|
|
|
if (values[5].type == SLSI_MIB_TYPE_OCTET && values[5].u.octetValue.dataLength >= 21)
|
|
memcpy(qsf_data->ht_caps, values[5].u.octetValue.data, 21);
|
|
else
|
|
SLSI_ERR(sdev, "invalid type or len for index: %d len:%d\n", 5,
|
|
values[5].u.octetValue.dataLength);
|
|
if (values[6].type == SLSI_MIB_TYPE_OCTET && values[6].u.octetValue.dataLength >= 12)
|
|
memcpy(qsf_data->vht_caps, values[6].u.octetValue.data, 12);
|
|
else
|
|
SLSI_ERR(sdev, "invalid type or len for index: %d len: %d\n", 6,
|
|
values[6].u.octetValue.dataLength);
|
|
if (values[7].type == SLSI_MIB_TYPE_OCTET && values[7].u.octetValue.dataLength >= 28)
|
|
memcpy(qsf_data->he_caps, values[7].u.octetValue.data, values[7].u.octetValue.dataLength);
|
|
else
|
|
SLSI_ERR(sdev, "invalid type or len for index: %d len: %d\n", 7,
|
|
values[7].u.octetValue.dataLength);
|
|
if (values[8].type == SLSI_MIB_TYPE_OCTET && values[8].u.octetValue.dataLength >= 18)
|
|
memcpy(qsf_data->misc_features_activated, values[8].u.octetValue.data, 18);
|
|
else
|
|
SLSI_ERR(sdev, "invalid type or len for index: %d len: %d\n", 8,
|
|
values[8].u.octetValue.dataLength);
|
|
if (values[9].type == SLSI_MIB_TYPE_OCTET && values[9].u.octetValue.dataLength >= 4)
|
|
memcpy(&qsf_data->appendix_versions, values[9].u.octetValue.data, 4);
|
|
else
|
|
SLSI_ERR(sdev, "invalid type or len for index: %d len: %d\n", 9,
|
|
values[9].u.octetValue.dataLength);
|
|
|
|
SLSI_CHECK_TYPE(sdev, values[10].type, SLSI_MIB_TYPE_UINT);
|
|
qsf_data->twt_control = values[10].u.uintValue;
|
|
kfree(mibrsp.data);
|
|
kfree(values);
|
|
return 0;
|
|
}
|
|
|
|
void slsi_get_qsfs_feature_set(struct slsi_dev *sdev)
|
|
{
|
|
int sw_feature_len = 0;
|
|
int sw_feature_len_offset = 0;
|
|
u32 pos = sdev->qsf_feature_set_len;
|
|
int ret = 0;
|
|
u32 buf_size = 128;
|
|
struct slsi_qsf_mib_data qsf_data = {0};
|
|
u8 *buf = sdev->qsfs_feature_set;
|
|
char tmp_buf[5] = {0};
|
|
|
|
ret = slsi_get_qsf_mib_data(sdev, &qsf_data);
|
|
if (ret) {
|
|
SLSI_ERR(sdev, "Error in getting thr mib data Error:%d\n", ret);
|
|
return;
|
|
}
|
|
|
|
pos = slsi_qsf_encode_hw_feature(sdev, qsf_data.misc_features_activated, qsf_data.he_active,
|
|
buf, pos, buf_size, qsf_data.he_caps,
|
|
qsf_data.vht_caps, qsf_data.ht_caps);
|
|
sw_feature_len_offset = pos;
|
|
pos += SLSI_QSF_SW_FEATURE_CHAR_LEN;
|
|
pos = slsi_qsf_encode_sw_feature_1(sdev, qsf_data.misc_features_activated, qsf_data.twt_control,
|
|
qsf_data.twt_active, buf, pos, buf_size);
|
|
pos = slsi_qsf_encode_sw_feature_2(sdev, qsf_data.misc_features_activated, qsf_data.he_softap_active,
|
|
qsf_data.wpa3_active, qsf_data.appendix_versions,
|
|
buf, pos, buf_size);
|
|
pos = slsi_qsf_encode_sw_feature_3(sdev, qsf_data.misc_features_activated, qsf_data.appendix_versions,
|
|
buf, pos, buf_size);
|
|
pos = slsi_qsf_encode_sw_feature_4(sdev, qsf_data.misc_features_activated, qsf_data.tdls_active,
|
|
qsf_data.max_tdls_cli, qsf_data.tdls_peer_uapsd_active,
|
|
qsf_data.nan_fast_connect, buf, pos, buf_size);
|
|
sw_feature_len = pos - (sw_feature_len_offset + SLSI_QSF_SW_FEATURE_CHAR_LEN);
|
|
scnprintf(tmp_buf, 5, "%04X", sw_feature_len);
|
|
memcpy(buf + sw_feature_len_offset, tmp_buf, SLSI_QSF_SW_FEATURE_CHAR_LEN);
|
|
sdev->qsf_feature_set_len = pos;
|
|
}
|
|
|
|
static ssize_t sysfs_show_qsf(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct slsi_dev *sdev = slsi_get_sdev();
|
|
int r = 0;
|
|
u32 fw_feature_ver = 0;
|
|
u32 wifi_feature_ver = 0;
|
|
u32 pos = 0;
|
|
u32 buf_size = sizeof(sdev->qsfs_feature_set);
|
|
|
|
if (!sdev->qsf_feature_set_len) {
|
|
r = slsi_qsf_get_wifi_fw_feature_version(sdev, &fw_feature_ver);
|
|
SLSI_INFO(sdev, "fw_feature_ver: %d\n", fw_feature_ver);
|
|
if (r < 0)
|
|
SLSI_ERR(sdev, "QSFS Wifi FW feature version couldn't be identified\n");
|
|
if (fw_feature_ver)
|
|
wifi_feature_ver = fw_feature_ver + SLSI_QSF_WIFI_FEATURE_VERSION;
|
|
|
|
pos += scnprintf(sdev->qsfs_feature_set, buf_size, "%04X", wifi_feature_ver);
|
|
pos += scnprintf(sdev->qsfs_feature_set + pos, buf_size - pos, "%.3s", sol_name);
|
|
sdev->qsf_feature_set_len = pos;
|
|
}
|
|
SLSI_INFO(sdev, "sysfs node for QSF is being read\n");
|
|
memcpy(buf, sdev->qsfs_feature_set, sdev->qsf_feature_set_len);
|
|
return sdev->qsf_feature_set_len;
|
|
}
|