| /* Copyright (c) 2014, 2018 The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Changes from Qualcomm Innovation Center are provided under the following license: |
| * |
| * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted (subject to the limitations in the |
| * disclaimer below) provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * |
| * * Neither the name of Qualcomm Innovation Center, Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE |
| * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT |
| * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "sync.h" |
| #define LOG_TAG "WifiHAL" |
| #include <utils/Log.h> |
| #include <time.h> |
| #include <errno.h> |
| |
| #include "ifaceeventhandler.h" |
| #include "common.h" |
| |
| /* Used to handle NL command events from driver/firmware. */ |
| IfaceEventHandlerCommand *mwifiEventHandler = NULL; |
| |
| /* Set the interface event monitor handler*/ |
| wifi_error wifi_set_iface_event_handler(wifi_request_id id, |
| wifi_interface_handle iface, |
| wifi_event_handler eh) |
| { |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| |
| /* Check if a similar request to set iface event handler was made earlier. |
| * Right now we don't differentiate between the case where (i) the new |
| * Request Id is different from the current one vs (ii) both new and |
| * Request Ids are the same. |
| */ |
| if (mwifiEventHandler) |
| { |
| if (id == mwifiEventHandler->get_request_id()) { |
| ALOGE("%s: Iface Event Handler Set for request Id %d is still" |
| "running. Exit", __func__, id); |
| return WIFI_ERROR_TOO_MANY_REQUESTS; |
| } else { |
| ALOGE("%s: Iface Event Handler Set for a different Request " |
| "Id:%d is requested. Not supported. Exit", __func__, id); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| } |
| |
| mwifiEventHandler = new IfaceEventHandlerCommand( |
| wifiHandle, |
| id, |
| NL80211_CMD_REG_CHANGE); |
| if (mwifiEventHandler == NULL) { |
| ALOGE("%s: Error mwifiEventHandler NULL", __func__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| mwifiEventHandler->setCallbackHandler(eh); |
| |
| return WIFI_SUCCESS; |
| } |
| |
| /* Reset monitoring for the NL event*/ |
| wifi_error wifi_reset_iface_event_handler(wifi_request_id id, |
| wifi_interface_handle iface) |
| { |
| if (mwifiEventHandler) |
| { |
| if (id == mwifiEventHandler->get_request_id()) { |
| ALOGV("Delete Object mwifiEventHandler for id = %d", id); |
| delete mwifiEventHandler; |
| mwifiEventHandler = NULL; |
| } else { |
| ALOGE("%s: Iface Event Handler Set for a different Request " |
| "Id:%d is requested. Not supported. Exit", __func__, id); |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| } else { |
| ALOGV("Object mwifiEventHandler for id = %d already Deleted", id); |
| } |
| |
| return WIFI_SUCCESS; |
| } |
| |
| /* This function will be the main handler for the registered incoming |
| * (from driver) Commads. Calls the appropriate callback handler after |
| * parsing the vendor data. |
| */ |
| int IfaceEventHandlerCommand::handleEvent(WifiEvent &event) |
| { |
| wifiEventHandler::handleEvent(event); |
| |
| switch(mSubcmd) |
| { |
| case NL80211_CMD_REG_CHANGE: |
| { |
| char code[2]; |
| memset(&code[0], 0, 2); |
| if(tb[NL80211_ATTR_REG_ALPHA2]) |
| { |
| memcpy(&code[0], (char *) nla_data(tb[NL80211_ATTR_REG_ALPHA2]), 2); |
| } else { |
| ALOGE("%s: NL80211_ATTR_REG_ALPHA2 not found", __func__); |
| } |
| ALOGV("Country : %c%c", code[0], code[1]); |
| if(mHandler.on_country_code_changed) |
| { |
| mHandler.on_country_code_changed(code); |
| } |
| } |
| break; |
| default: |
| ALOGV("NL Event : %d Not supported", mSubcmd); |
| } |
| |
| return NL_SKIP; |
| } |
| |
| IfaceEventHandlerCommand::IfaceEventHandlerCommand(wifi_handle handle, int id, u32 subcmd) |
| : wifiEventHandler(handle, id, subcmd) |
| { |
| ALOGV("wifiEventHandler %p constructed", this); |
| registerHandler(mSubcmd); |
| memset(&mHandler, 0, sizeof(wifi_event_handler)); |
| mEventData = NULL; |
| mDataLen = 0; |
| } |
| |
| IfaceEventHandlerCommand::~IfaceEventHandlerCommand() |
| { |
| ALOGV("IfaceEventHandlerCommand %p destructor", this); |
| unregisterHandler(mSubcmd); |
| } |
| |
| void IfaceEventHandlerCommand::setCallbackHandler(wifi_event_handler nHandler) |
| { |
| mHandler = nHandler; |
| } |
| |
| int wifiEventHandler::get_request_id() |
| { |
| return mRequestId; |
| } |
| |
| int IfaceEventHandlerCommand::get_request_id() |
| { |
| return wifiEventHandler::get_request_id(); |
| } |
| |
| wifiEventHandler::wifiEventHandler(wifi_handle handle, int id, u32 subcmd) |
| : WifiCommand(handle, id) |
| { |
| mRequestId = id; |
| mSubcmd = subcmd; |
| registerHandler(mSubcmd); |
| memset(tb, 0, sizeof(struct nlattr *) * (NL80211_ATTR_MAX + 1)); |
| ALOGV("wifiEventHandler %p constructed", this); |
| } |
| |
| wifiEventHandler::~wifiEventHandler() |
| { |
| ALOGV("wifiEventHandler %p destructor", this); |
| unregisterHandler(mSubcmd); |
| } |
| |
| int wifiEventHandler::handleEvent(WifiEvent &event) |
| { |
| struct genlmsghdr *gnlh = event.header(); |
| mSubcmd = gnlh->cmd; |
| nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
| genlmsg_attrlen(gnlh, 0), NULL); |
| ALOGV("Got NL Event : %d from the Driver.", gnlh->cmd); |
| |
| return NL_SKIP; |
| } |
| |
| WifihalGeneric::WifihalGeneric(wifi_handle handle, int id, u32 vendor_id, |
| u32 subcmd) |
| : WifiVendorCommand(handle, id, vendor_id, subcmd) |
| { |
| hal_info *info = getHalInfo(handle); |
| |
| /* Initialize the member data variables here */ |
| mSet = 0; |
| mSetSizeMax = 0; |
| mSetSizePtr = NULL; |
| mConcurrencySet = 0; |
| filterVersion = 0; |
| filterLength = 0; |
| firmware_bus_max_size = 0; |
| mCapa = &(info->capa); |
| mfilter_packet_read_buffer = NULL; |
| mfilter_packet_length = 0; |
| res_size = 0; |
| channel_buff = NULL; |
| mRadio_matrix = NULL; |
| mRadio_matrix_max_size = 0; |
| mRadio_matrix_size = NULL; |
| memset(&mDriverFeatures, 0, sizeof(mDriverFeatures)); |
| memset(&mRadarResultParams, 0, sizeof(RadarHistoryResultsParams)); |
| } |
| |
| WifihalGeneric::~WifihalGeneric() |
| { |
| mCapa = NULL; |
| if (mDriverFeatures.flags != NULL) { |
| free(mDriverFeatures.flags); |
| mDriverFeatures.flags = NULL; |
| } |
| } |
| |
| wifi_error WifihalGeneric::requestResponse() |
| { |
| return WifiCommand::requestResponse(mMsg); |
| } |
| |
| static u32 get_wifi_iftype_masks(u32 in_mask) |
| { |
| u32 op_mask = 0; |
| |
| if (in_mask & BIT(NL80211_IFTYPE_STATION)) { |
| op_mask |= BIT(WIFI_INTERFACE_STA); |
| op_mask |= BIT(WIFI_INTERFACE_TDLS); |
| } |
| if (in_mask & BIT(NL80211_IFTYPE_AP)) |
| op_mask |= BIT(WIFI_INTERFACE_SOFTAP); |
| if (in_mask & BIT(NL80211_IFTYPE_P2P_CLIENT)) |
| op_mask |= BIT(WIFI_INTERFACE_P2P_CLIENT); |
| if (in_mask & BIT(NL80211_IFTYPE_P2P_GO)) |
| op_mask |= BIT(WIFI_INTERFACE_P2P_GO); |
| if (in_mask & BIT(NL80211_IFTYPE_NAN)) |
| op_mask |= BIT(WIFI_INTERFACE_NAN); |
| |
| return op_mask; |
| } |
| |
| static wifi_channel_width get_channel_width(u32 nl_width) |
| { |
| switch(nl_width) { |
| case NL80211_CHAN_WIDTH_20: |
| return WIFI_CHAN_WIDTH_20; |
| case NL80211_CHAN_WIDTH_40: |
| return WIFI_CHAN_WIDTH_40; |
| case NL80211_CHAN_WIDTH_80: |
| return WIFI_CHAN_WIDTH_80; |
| case NL80211_CHAN_WIDTH_160: |
| return WIFI_CHAN_WIDTH_160; |
| case NL80211_CHAN_WIDTH_80P80: |
| return WIFI_CHAN_WIDTH_80P80; |
| case NL80211_CHAN_WIDTH_5: |
| return WIFI_CHAN_WIDTH_5; |
| case NL80211_CHAN_WIDTH_10: |
| return WIFI_CHAN_WIDTH_10; |
| default: |
| return WIFI_CHAN_WIDTH_INVALID; |
| } |
| } |
| |
| int WifihalGeneric::handle_response_usable_channels(struct nlattr *VendorData, |
| u32 mDataLen) |
| { |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_MAX + 1]; |
| struct nlattr *curr_attr; |
| wifi_usable_channel *chan_info = NULL; |
| int rem; |
| u32 currSize = 0; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_MAX, |
| (struct nlattr *)mVendorData, mDataLen, NULL)) { |
| ALOGE("Failed to parse NL channels list"); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO]) { |
| ALOGE("%s: USABLE_CHANNELS_CHAN_INFO not found", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| for_each_nested_attribute(curr_attr, |
| tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO], rem) { |
| struct nlattr *ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_MAX + 1]; |
| |
| if (currSize >= mSetSizeMax) { |
| ALOGE("Got max channels %d completed", mSetSizeMax); |
| break; |
| } |
| |
| if (nla_parse_nested(ch_info, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_MAX, |
| curr_attr, NULL)) { |
| ALOGE("Failed to get usable channel info"); |
| return NL_SKIP; |
| } |
| |
| chan_info = &channel_buff[currSize]; |
| if (!ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ]) { |
| ALOGE("%s: CHAN_INFO_PRIMARY_FREQ not found", |
| __FUNCTION__); |
| return NL_SKIP; |
| } |
| |
| chan_info->freq = nla_get_u32(ch_info[ |
| QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ]); |
| if (!ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH]) { |
| ALOGE("%s: CHAN_INFO_BANDWIDTH not found", |
| __FUNCTION__); |
| return NL_SKIP; |
| } |
| |
| chan_info->width = get_channel_width(nla_get_u32( |
| ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH])); |
| if (!ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK]) { |
| ALOGE("%s: CHAN_INFO_IFACE_MODE_MASK not found", |
| __FUNCTION__); |
| return NL_SKIP; |
| } |
| |
| chan_info->iface_mode_mask = get_wifi_iftype_masks(nla_get_u32( |
| ch_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK])); |
| ALOGV("Primary freq %d BW %d iface mask %d", chan_info->freq, |
| chan_info->width, chan_info->iface_mode_mask); |
| currSize++; |
| } |
| |
| res_size = currSize; |
| ALOGV("%s: Result size %d", __FUNCTION__, res_size); |
| |
| return NL_SKIP; |
| } |
| |
| int WifihalGeneric::handleResponse(WifiEvent &reply) |
| { |
| ALOGV("Got a Wi-Fi HAL module message from Driver"); |
| int i = 0; |
| WifiVendorCommand::handleResponse(reply); |
| |
| // Parse the vendordata and get the attribute |
| switch(mSubcmd) |
| { |
| case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES: |
| { |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_SET_MAX + 1]; |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_FEATURE_SET_MAX, |
| (struct nlattr *)mVendorData, |
| mDataLen, NULL); |
| |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_SET]) |
| { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_FEATURE_SET not found", __func__); |
| return -EINVAL; |
| } |
| mSet = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_SET]); |
| ALOGV("Supported feature set : %" PRIx64, mSet); |
| |
| break; |
| } |
| case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: |
| { |
| struct nlattr *attr; |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, |
| (struct nlattr *)mVendorData, mDataLen, NULL); |
| attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS]; |
| if (attr) { |
| int len = nla_len(attr); |
| mDriverFeatures.flags = (u8 *)malloc(len); |
| if (mDriverFeatures.flags != NULL) { |
| memcpy(mDriverFeatures.flags, nla_data(attr), len); |
| mDriverFeatures.flags_len = len; |
| } |
| } |
| break; |
| } |
| case QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX: |
| { |
| struct nlattr *tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX + 1]; |
| nla_parse(tb_vendor, |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX, |
| (struct nlattr *)mVendorData,mDataLen, NULL); |
| |
| if (tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET_SIZE]) { |
| u32 val; |
| val = nla_get_u32( |
| tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET_SIZE]); |
| |
| ALOGV("%s: Num of concurrency combinations: %d", |
| __func__, val); |
| val = val > (unsigned int)mSetSizeMax ? |
| (unsigned int)mSetSizeMax : val; |
| *mSetSizePtr = val; |
| |
| /* Extract the list of channels. */ |
| if (*mSetSizePtr > 0 && |
| tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET]) { |
| nla_memcpy(mConcurrencySet, |
| tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET], |
| sizeof(feature_set) * (*mSetSizePtr)); |
| } |
| |
| ALOGV("%s: Get concurrency matrix response received.", |
| __func__); |
| ALOGV("%s: Num of concurrency combinations : %d", |
| __func__, *mSetSizePtr); |
| ALOGV("%s: List of valid concurrency combinations is: ", |
| __func__); |
| for(i = 0; i < *mSetSizePtr; i++) |
| { |
| ALOGV("%" PRIx64, *(mConcurrencySet + i)); |
| } |
| } |
| } |
| break; |
| case QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER: |
| { |
| int subCmd; |
| struct nlattr *tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX + 1]; |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX, |
| (struct nlattr *)mVendorData, |
| mDataLen, NULL); |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD]) |
| { |
| subCmd = nla_get_u32( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD]); |
| } else { |
| /* |
| * The older drivers may not send PACKET_FILTER_SUB_CMD as |
| * they support QCA_WLAN_GET_PACKET_FILTER only. |
| */ |
| subCmd = QCA_WLAN_GET_PACKET_FILTER; |
| } |
| if (subCmd == QCA_WLAN_GET_PACKET_FILTER) { |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION]) |
| { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION" |
| " not found", __FUNCTION__); |
| return -EINVAL; |
| } |
| filterVersion = nla_get_u32( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION]); |
| ALOGV("Current version : %u", filterVersion); |
| |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE]) |
| { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE" |
| " not found", __FUNCTION__); |
| return -EINVAL; |
| } |
| filterLength = nla_get_u32( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE]); |
| ALOGV("Max filter length Supported : %u", filterLength); |
| } else if (subCmd == QCA_WLAN_READ_PACKET_FILTER) { |
| |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM]) |
| { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM" |
| " not found", __FUNCTION__); |
| return -EINVAL; |
| } |
| if (nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM]) |
| < mfilter_packet_length) |
| { |
| ALOGE("%s: Expected packet filter length :%d but received only: %d bytes", |
| __FUNCTION__, mfilter_packet_length, |
| nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM])); |
| return -EINVAL; |
| } |
| memcpy(mfilter_packet_read_buffer, |
| nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM]), |
| mfilter_packet_length); |
| ALOGV("Filter Program length : %u", mfilter_packet_length); |
| } else { |
| ALOGE("%s: Unknown APF sub command received", |
| __FUNCTION__); |
| return -EINVAL; |
| } |
| |
| } |
| break; |
| case QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE: |
| { |
| struct nlattr *tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX + 1]; |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX, |
| (struct nlattr *)mVendorData, mDataLen, NULL); |
| |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE]) |
| { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE" |
| " not found", __FUNCTION__); |
| return -EINVAL; |
| } |
| firmware_bus_max_size = nla_get_u32( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE]); |
| ALOGV("Max BUS size Supported: %d", firmware_bus_max_size); |
| } |
| break; |
| case QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES: |
| { |
| struct nlattr *tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX + 1]; |
| nla_parse(tbVendor, QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX, |
| (struct nlattr *)mVendorData,mDataLen, NULL); |
| |
| if (wifiParseCapabilities(tbVendor) == WIFI_SUCCESS) { |
| ALOGV("%s: GSCAN Capabilities:\n" |
| " max_ap_cache_per_scan:%d\n" |
| " max_bssid_history_entries:%d\n" |
| " max_hotlist_bssids:%d\n" |
| " max_hotlist_ssids:%d\n" |
| " max_rssi_sample_size:%d\n" |
| " max_scan_buckets:%d\n" |
| " max_scan_cache_size:%d\n" |
| " max_scan_reporting_threshold:%d\n" |
| " max_significant_wifi_change_aps:%d\n" |
| " max_number_epno_networks:%d\n" |
| " max_number_epno_networks_by_ssid:%d\n" |
| " max_number_of_white_listed_ssid:%d.", |
| __FUNCTION__, mCapa->gscan_capa.max_ap_cache_per_scan, |
| mCapa->gscan_capa.max_bssid_history_entries, |
| mCapa->gscan_capa.max_hotlist_bssids, |
| mCapa->gscan_capa.max_hotlist_ssids, |
| mCapa->gscan_capa.max_rssi_sample_size, |
| mCapa->gscan_capa.max_scan_buckets, |
| mCapa->gscan_capa.max_scan_cache_size, |
| mCapa->gscan_capa.max_scan_reporting_threshold, |
| mCapa->gscan_capa.max_significant_wifi_change_aps, |
| mCapa->gscan_capa.max_number_epno_networks, |
| mCapa->gscan_capa.max_number_epno_networks_by_ssid, |
| mCapa->gscan_capa.max_number_of_white_listed_ssid); |
| |
| ALOGV("%s: Roaming Capabilities:\n" |
| " max_blacklist_size: %d\n" |
| " max_whitelist_size: %d\n", |
| __FUNCTION__, mCapa->roaming_capa.max_blacklist_size, |
| mCapa->roaming_capa.max_whitelist_size); |
| } |
| } |
| break; |
| case QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS: |
| return handle_response_usable_channels((struct nlattr *)mVendorData, |
| mDataLen); |
| case QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY: |
| { |
| wifiParseRadarHistory(); |
| } |
| break; |
| case QCA_NL80211_VENDOR_SUBCMD_GET_RADIO_COMBINATION_MATRIX: |
| { |
| wifi_parse_radio_combinations_matrix(); |
| } |
| break; |
| case QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY: |
| { |
| struct nlattr *tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_MAX + 1]; |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_MAX, |
| (struct nlattr *)mVendorData,mDataLen, NULL); |
| |
| if(tb_vendor[QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_VERSION]) |
| { |
| mInfo->sar_version = (qca_wlan_vendor_sar_version) nla_get_u32(tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_VERSION]); |
| } |
| ALOGV("%s: sar_version return %d", __func__, mInfo->sar_version); |
| } |
| break; |
| default : |
| ALOGE("%s: Wrong Wi-Fi HAL event received %d", __func__, mSubcmd); |
| } |
| return NL_SKIP; |
| } |
| |
| /* Parses and extract capabilities results. */ |
| wifi_error WifihalGeneric::wifiParseCapabilities(struct nlattr **tbVendor) |
| { |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE not found", |
| __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| mCapa->gscan_capa.max_scan_cache_size = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS not found", |
| __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| mCapa->gscan_capa.max_scan_buckets = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN not found", |
| __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| mCapa->gscan_capa.max_ap_cache_per_scan = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE not found", |
| __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| mCapa->gscan_capa.max_rssi_sample_size = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD not" |
| " found", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| mCapa->gscan_capa.max_scan_reporting_threshold = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS not found", |
| __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| mCapa->gscan_capa.max_hotlist_bssids = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS] |
| ) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS " |
| "not found", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| mCapa->gscan_capa.max_significant_wifi_change_aps = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES not " |
| "found", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| mCapa->gscan_capa.max_bssid_history_entries = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES]); |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS not found. Set" |
| " to 0.", __FUNCTION__); |
| mCapa->gscan_capa.max_hotlist_ssids = 0; |
| } else { |
| mCapa->gscan_capa.max_hotlist_ssids = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS]); |
| } |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS not found. Set" |
| " to 0.", __FUNCTION__); |
| mCapa->gscan_capa.max_number_epno_networks = 0; |
| } else { |
| mCapa->gscan_capa.max_number_epno_networks |
| = nla_get_u32(tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS |
| ]); |
| } |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID not " |
| "found. Set to 0.", __FUNCTION__); |
| mCapa->gscan_capa.max_number_epno_networks_by_ssid = 0; |
| } else { |
| mCapa->gscan_capa.max_number_epno_networks_by_ssid = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID]); |
| } |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID not " |
| "found. Set to 0.", __FUNCTION__); |
| mCapa->gscan_capa.max_number_of_white_listed_ssid = 0; |
| mCapa->roaming_capa.max_whitelist_size = 0; |
| } else { |
| mCapa->gscan_capa.max_number_of_white_listed_ssid = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID]); |
| mCapa->roaming_capa.max_whitelist_size = mCapa->gscan_capa.max_number_of_white_listed_ssid; |
| } |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID]) { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX" |
| "_NUM_BLACKLIST_BSSID not found. Set to 0.", __FUNCTION__); |
| mCapa->roaming_capa.max_blacklist_size = 0; |
| } else { |
| mCapa->roaming_capa.max_blacklist_size = nla_get_u32(tbVendor[ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID]); |
| } |
| return WIFI_SUCCESS; |
| } |
| |
| wifi_error WifihalGeneric::wifi_parse_radio_combinations_matrix() { |
| // tbVendor |
| struct nlattr *tbVendor[QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_MAX + 1]; |
| int rem = 0, rem_radio = 0; |
| u32 num_radio_combinations = 0, num_radio_configurations = 0; |
| // nested radio matrix configurations |
| struct nlattr *radio_combination[QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_MAX + 1]; |
| struct nlattr *attr, *attr_cfg; |
| struct nlattr *radio_cfg[QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_MAX + 1]; |
| u8 band, antenna; |
| u32 result_size = sizeof(u32); /* num_radio_combinations */ |
| bool max_size_exceeded = false; |
| u32 size_needed = 0, radio_cfg_size = 0; |
| bool invalid_radio_cfg = false; |
| wifi_radio_combination *radio_combination_ptr; |
| wifi_radio_configuration *radio_config; |
| u8 *buff_ptr; |
| |
| static struct nla_policy |
| radio_combination_policy[QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_CFGS] = { .type = NLA_NESTED }, |
| }; |
| |
| static struct nla_policy |
| radio_cfg_policy[QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_BAND] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_ANTENNA] = { .type = NLA_U8 }, |
| }; |
| |
| if (nla_parse(tbVendor, QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_MAX, |
| (struct nlattr *)mVendorData,mDataLen, NULL)) { |
| ALOGE("%s: nla_parse ATTR_RADIO_MATRIX fail", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_SUPPORTED_CFGS]) { |
| ALOGE("%s: radio matrix attr entries not present", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| if (!mRadio_matrix) { |
| ALOGE("%s: radio matrix buffer is null", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| buff_ptr = (u8 *)mRadio_matrix; |
| buff_ptr += sizeof(u32); /* num_radio_combinations */ |
| nla_for_each_nested(attr, |
| tbVendor[QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_SUPPORTED_CFGS], |
| rem) { |
| if (nla_parse_nested(radio_combination, |
| QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_MAX, |
| attr, radio_combination_policy)) { |
| ALOGI("%s: nla_parse_nested radio combination fail", __FUNCTION__); |
| continue; |
| } |
| radio_combination_ptr = (wifi_radio_combination *)buff_ptr; |
| |
| num_radio_configurations = 0; |
| radio_cfg_size = 0; |
| nla_for_each_nested(attr_cfg, |
| radio_combination[QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_CFGS], |
| rem_radio) { |
| if (nla_parse_nested(radio_cfg, |
| QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_MAX, |
| attr_cfg, radio_cfg_policy)) { |
| ALOGI("%s: nla_parse_nested radio cfg attr fail", __FUNCTION__); |
| continue; |
| } |
| |
| if (!radio_cfg[QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_BAND] || |
| !radio_cfg[QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_ANTENNA]) { |
| ALOGI("%s: band/antenna info not present", __FUNCTION__); |
| continue; |
| } |
| |
| if (!invalid_radio_cfg) { |
| if (!num_radio_configurations) { |
| size_needed = sizeof(wifi_radio_configuration) + |
| sizeof(u32); |
| buff_ptr += sizeof(u32); /* num_radio_configurations */ |
| } else { |
| size_needed = sizeof(wifi_radio_configuration); |
| buff_ptr += sizeof(wifi_radio_configuration); |
| } |
| } |
| if (mRadio_matrix_max_size < result_size + size_needed) { |
| ALOGI("%s: Max size reached, max size %u, needed %u", |
| __FUNCTION__, mRadio_matrix_max_size, |
| result_size + size_needed); |
| max_size_exceeded = true; |
| break; |
| } |
| invalid_radio_cfg = false; |
| radio_config = (wifi_radio_configuration *)buff_ptr; |
| |
| if (radio_cfg[QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_BAND]) { |
| band = nla_get_u32( |
| radio_cfg[QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_BAND]); |
| if (band == QCA_SETBAND_5G) { |
| radio_config->band = WLAN_MAC_5_0_BAND; |
| } else if (band == QCA_SETBAND_6G) { |
| radio_config->band = WLAN_MAC_6_0_BAND; |
| } else if (band == QCA_SETBAND_2G) { |
| radio_config->band = WLAN_MAC_2_4_BAND; |
| } else { |
| ALOGI("%s: Invalid band value %d", __FUNCTION__, band); |
| invalid_radio_cfg = true; |
| continue; |
| } |
| } |
| if (radio_cfg[QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_ANTENNA]) { |
| antenna = nla_get_u8( |
| radio_cfg[QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_ANTENNA]); |
| if (antenna == 1) { |
| radio_config->antenna_cfg = WIFI_ANTENNA_1X1; |
| } else if (antenna == 2) { |
| radio_config->antenna_cfg = WIFI_ANTENNA_2X2; |
| } else if (antenna == 3) { |
| radio_config->antenna_cfg = WIFI_ANTENNA_3X3; |
| } else if (antenna == 4) { |
| radio_config->antenna_cfg = WIFI_ANTENNA_4X4; |
| } else { |
| ALOGI("%s: Invalid antenna cfg value %d", __FUNCTION__, |
| antenna); |
| invalid_radio_cfg = true; |
| continue; |
| } |
| } |
| radio_cfg_size += size_needed; |
| num_radio_configurations++; |
| result_size += size_needed; |
| } /* RADIO_COMBINATIONS_CFGS */ |
| if (num_radio_configurations) { |
| num_radio_combinations++; |
| radio_combination_ptr->num_radio_configurations = |
| num_radio_configurations; |
| ALOGI("%s: radio_combinations[%d]: num_radio_configurations %d", |
| __FUNCTION__, num_radio_combinations, |
| num_radio_configurations); |
| } |
| |
| buff_ptr = (u8 *)radio_combination_ptr + radio_cfg_size; |
| |
| if (max_size_exceeded) |
| break; |
| } /* RADIO_MATRIX_SUPPORTED_CFGS */ |
| mRadio_matrix->num_radio_combinations = num_radio_combinations; |
| *mRadio_matrix_size = result_size; |
| ALOGI("%s: num_radio_combinations %d, radio_matrix_size %d", __FUNCTION__, |
| num_radio_combinations, result_size); |
| |
| return WIFI_SUCCESS; |
| } |
| |
| wifi_error WifihalGeneric::wifiParseRadarHistory() { |
| { |
| // tbVendor |
| struct nlattr *tbVendor[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX + 1]; |
| int rem = 0, num_dfs_entries = 0; |
| |
| if (nla_parse(tbVendor, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX, |
| (struct nlattr *)mVendorData,mDataLen, NULL)) { |
| ALOGE("%s: nla_parse fail", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| if (!tbVendor[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES]) { |
| ALOGE("%s: radar attr entries not present", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| // nested radar history |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX + 1]; |
| struct nlattr *attr; |
| static struct nla_policy |
| policy[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP] = { .type = NLA_U64 }, |
| [QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED] = { .type = NLA_FLAG }, |
| }; |
| radar_history_result *newEntry; |
| radar_history_result *temp; |
| u32 totalEntrySize = 0; |
| u32 newEntrySize = sizeof(radar_history_result); |
| |
| nla_for_each_nested(attr, |
| tbVendor[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES], |
| rem) { |
| if ((num_dfs_entries ++) > MAX_NUM_RADAR_HISTORY) { |
| ALOGE("%s: exceeded max entries, drop others", __FUNCTION__); |
| break; |
| } |
| if (nla_parse_nested(tb, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX, |
| attr, policy)) { |
| ALOGI("%s: nla_parse_nested fail", __FUNCTION__); |
| continue; |
| } |
| if (!tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ]) { |
| ALOGI("%s: radar attr freq not present", __FUNCTION__); |
| continue; |
| } |
| if (!tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP]) { |
| ALOGI("%s: radar attr timestamp not present", __FUNCTION__); |
| continue; |
| } |
| |
| // realloc buffer for new entry |
| temp = (radar_history_result *) realloc( |
| mRadarResultParams.entries, totalEntrySize + newEntrySize); |
| if (temp == NULL) { |
| ALOGE("%s: failed to realloc memory", __FUNCTION__); |
| free(mRadarResultParams.entries); |
| mRadarResultParams.entries = NULL; |
| return WIFI_ERROR_OUT_OF_MEMORY; |
| } |
| mRadarResultParams.entries = temp; |
| |
| newEntry = (radar_history_result *)( |
| (u8 *) mRadarResultParams.entries + totalEntrySize); |
| memset(newEntry, 0, newEntrySize); |
| totalEntrySize += newEntrySize; |
| |
| // save to current radar entry |
| newEntry->freq = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ]); |
| newEntry->clock_boottime = nla_get_u64( |
| tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP]); |
| newEntry->radar_detected = false; |
| if (tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED]) { |
| newEntry->radar_detected = nla_get_flag( |
| tb[QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED]); |
| } |
| mRadarResultParams.num_entries ++; |
| |
| ALOGI("Radar history: freq:%d boottime: %" PRId64 " detected:%d", |
| newEntry->freq, |
| newEntry->clock_boottime, |
| newEntry->radar_detected); |
| } |
| } |
| |
| return WIFI_SUCCESS; |
| } |
| |
| |
| void WifihalGeneric::getResponseparams(feature_set *pset) |
| { |
| *pset = mSet; |
| } |
| |
| void WifihalGeneric::getDriverFeatures(features_info *pfeatures) |
| { |
| if (!pfeatures) |
| return; |
| |
| if (mDriverFeatures.flags != NULL) { |
| pfeatures->flags = (u8 *)malloc(mDriverFeatures.flags_len); |
| if (pfeatures->flags) { |
| memcpy(pfeatures->flags, mDriverFeatures.flags, |
| mDriverFeatures.flags_len); |
| pfeatures->flags_len = mDriverFeatures.flags_len; |
| return; |
| } |
| } |
| |
| pfeatures->flags_len = 0; |
| pfeatures->flags = NULL; |
| } |
| |
| void WifihalGeneric::setMaxSetSize(int set_size_max) { |
| mSetSizeMax = set_size_max; |
| } |
| |
| void WifihalGeneric::setConcurrencySet(feature_set set[]) { |
| mConcurrencySet = set; |
| } |
| |
| void WifihalGeneric::set_radio_matrix_max_size(u32 max_size) { |
| mRadio_matrix_max_size = max_size; |
| } |
| |
| void WifihalGeneric::set_radio_matrix_size(u32 *size){ |
| mRadio_matrix_size = size; |
| } |
| |
| void WifihalGeneric::set_radio_matrix( |
| wifi_radio_combination_matrix *radio_combination_matrix){ |
| mRadio_matrix = radio_combination_matrix; |
| } |
| |
| void WifihalGeneric::setSizePtr(int *set_size) { |
| mSetSizePtr = set_size; |
| } |
| |
| int WifihalGeneric::getFilterVersion() { |
| return filterVersion; |
| } |
| |
| int WifihalGeneric::getFilterLength() { |
| return filterLength; |
| } |
| void WifihalGeneric::setPacketBufferParams(u8 *host_packet_buffer, int packet_length) { |
| mfilter_packet_read_buffer = host_packet_buffer; |
| mfilter_packet_length = packet_length; |
| } |
| |
| int WifihalGeneric::getBusSize() { |
| return firmware_bus_max_size; |
| } |
| |
| void WifihalGeneric::set_channels_buff(wifi_usable_channel* channels) |
| { |
| channel_buff = channels; |
| memset(channel_buff, 0, sizeof(wifi_usable_channel) * mSetSizeMax); |
| } |
| |
| u32 WifihalGeneric::get_results_size(void) |
| { |
| return res_size; |
| } |
| |
| wifi_error WifihalGeneric::wifiGetCapabilities(wifi_interface_handle handle) |
| { |
| wifi_error ret; |
| struct nlattr *nlData; |
| interface_info *ifaceInfo = getIfaceInfo(handle); |
| |
| /* Create the NL message. */ |
| ret = create(); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: Failed to create NL message, Error:%d", __FUNCTION__, ret); |
| return ret; |
| } |
| |
| /* Set the interface Id of the message. */ |
| ret = set_iface_id(ifaceInfo->name); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: Failed to set interface Id of message, Error:%d", __FUNCTION__, ret); |
| return ret; |
| } |
| |
| /* Add the vendor specific attributes for the NL command. */ |
| nlData = attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nlData) |
| return WIFI_ERROR_OUT_OF_MEMORY; |
| |
| ret = put_u32(QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID, mId); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: Failed to add request_ID to NL command, Error:%d", __FUNCTION__, ret); |
| return ret; |
| } |
| |
| attr_end(nlData); |
| |
| ret = requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: Failed to send request, Error:%d", __FUNCTION__, ret); |
| |
| return ret; |
| } |
| |
| wifi_error WifihalGeneric::copyCachedRadarHistory( |
| radar_history_result *resultBuf, int resultBufSize, int *numResults) { |
| *numResults = 0; |
| |
| if (mRadarResultParams.entries) { |
| radar_history_result *sEntry = NULL; |
| radar_history_result *tEntry = NULL; |
| u32 offset = 0; |
| int i; |
| |
| for (i = 0; i < mRadarResultParams.num_entries; i ++) { |
| if (resultBufSize < (offset + sizeof(radar_history_result))) { |
| break; |
| } |
| |
| sEntry = (radar_history_result *)( |
| (u8 *) mRadarResultParams.entries + offset); |
| tEntry = (radar_history_result *)( |
| (u8 *) resultBuf + offset); |
| memcpy(tEntry, sEntry, sizeof(radar_history_result)); |
| (*numResults) += 1; |
| offset += sizeof(radar_history_result); |
| } |
| } |
| |
| return WIFI_SUCCESS; |
| } |
| |
| void WifihalGeneric::freeCachedRadarHistory() { |
| if (mRadarResultParams.entries) { |
| free(mRadarResultParams.entries); |
| mRadarResultParams.entries = NULL; |
| mRadarResultParams.num_entries = 0; |
| } |
| } |
| |
| |
| |
| wifi_error WifihalGeneric::getSarVersion(wifi_interface_handle handle) |
| { |
| wifi_error ret; |
| interface_info *ifaceInfo = getIfaceInfo(handle); |
| |
| |
| /* Create the NL message. */ |
| ret = create(); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: Failed to create NL message, Error:%d", __FUNCTION__, ret); |
| return ret; |
| } |
| |
| /* Set the interface Id of the message. */ |
| ret = set_iface_id(ifaceInfo->name); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: Failed to set interface Id of message, Error:%d", __FUNCTION__, ret); |
| return ret; |
| } |
| |
| ret = requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: Failed to send request, Error:%d", __FUNCTION__, ret); |
| |
| return ret; |
| } |
| |
| |