/* Copyright (c) 2017-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 Qualcomm Innovation Center, Inc. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause-Clear */ #include "sync.h" #include #include "wifi_hal.h" #include "common.h" #include "cpp_bindings.h" #include "radio_mode.h" #include "vendor_definitions.h" #include #include #include /* Used to handle radio command events from driver/firmware. */ typedef struct radio_event_handler_s { RADIOModeCommand* mRADIOModeCommandInstance; } radio_event_handlers; wifi_error initializeRadioHandler(hal_info *info) { info->radio_handlers = (radio_event_handlers *)malloc( sizeof(radio_event_handlers)); if (info->radio_handlers) { memset(info->radio_handlers, 0, sizeof(radio_event_handlers)); } else { ALOGE("%s: Allocation of radio event handlers failed", __FUNCTION__); return WIFI_ERROR_OUT_OF_MEMORY; } return WIFI_SUCCESS; } wifi_error cleanupRadioHandler(hal_info *info) { radio_event_handlers* event_handlers; if (info && info->radio_handlers) { event_handlers = (radio_event_handlers*) info->radio_handlers; if (event_handlers->mRADIOModeCommandInstance) { delete event_handlers->mRADIOModeCommandInstance; } memset(event_handlers, 0, sizeof(radio_event_handlers)); free(info->radio_handlers); info->radio_handlers = NULL; return WIFI_SUCCESS; } ALOGE ("%s: info or info->radio_handlers NULL", __FUNCTION__); return WIFI_ERROR_UNKNOWN; } /* Used to handle radio mode command events from driver/firmware.*/ void RADIOModeCommand::setCallbackHandler(wifi_radio_mode_change_handler handler) { mHandler = handler; } void RADIOModeCommand::setReqId(wifi_request_id id) { mreqId = id; } RADIOModeCommand::RADIOModeCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd) : WifiVendorCommand(handle, id, vendor_id, subcmd) { memset(&mHandler, 0, sizeof(mHandler)); if (registerVendorHandler(vendor_id, subcmd)) { /* Error case should not happen print log */ ALOGE("%s: Unable to register Vendor Handler Vendor Id=0x%x subcmd=%u", __FUNCTION__, vendor_id, subcmd); } } RADIOModeCommand::~RADIOModeCommand() { unregisterVendorHandler(mVendor_id, mSubcmd); } RADIOModeCommand* RADIOModeCommand::instance(wifi_handle handle, wifi_request_id id) { if (handle == NULL) { ALOGE("Interface Handle is invalid"); return NULL; } hal_info *info = getHalInfo(handle); if (!info) { ALOGE("hal_info is invalid"); return NULL; } RADIOModeCommand* instance = info->radio_handlers->mRADIOModeCommandInstance; if (instance) { if (handle != getWifiHandle(instance->mInfo)) { ALOGV("%s - Handle different, update the handle", __FUNCTION__); instance->mInfo = (hal_info *)handle; } instance->setReqId(id); } else { info->radio_handlers->mRADIOModeCommandInstance = new RADIOModeCommand(handle, id, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO); instance = info->radio_handlers->mRADIOModeCommandInstance; } return instance; } /* This function will be the main handler for incoming event. * Call the appropriate callback handler after parsing the vendor data. */ int RADIOModeCommand::handleEvent(WifiEvent &event) { wifi_error ret = WIFI_ERROR_UNKNOWN; int num_of_mac = 0; wifi_mac_info mode_info; memset(&mode_info, 0, sizeof(mode_info)); WifiVendorCommand::handleEvent(event); /* Parse the vendordata and get the attribute */ switch(mSubcmd) { case QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO: { struct nlattr *mtb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_MAX + 1]; struct nlattr *modeInfo; int rem; nla_parse(mtb_vendor, QCA_WLAN_VENDOR_ATTR_MAC_MAX, (struct nlattr *)mVendorData, mDataLen, NULL); if (mtb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_INFO]) { for (modeInfo = (struct nlattr *) nla_data(mtb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_INFO]), rem = nla_len(mtb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_INFO]); nla_ok(modeInfo, rem);modeInfo = nla_next(modeInfo, &(rem))) { struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX+ 1]; nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX, (struct nlattr *) nla_data(modeInfo), nla_len(modeInfo), NULL); if (!tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID" " not found", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; goto cleanup; } mode_info.wlan_mac_id = nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID]); ALOGV("mac_id[%d]: %d ", num_of_mac, mode_info.wlan_mac_id); if (!tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND" " NOT FOUND", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; goto cleanup; } mode_info.mac_band = (wlan_mac_band) nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND]); ALOGV("mac_band[%d]: %d ", num_of_mac, mode_info.mac_band); if (tb2[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO]) { int num_of_iface = 0; struct nlattr *tb_iface; int rem_info; for (tb_iface = (struct nlattr *) nla_data(tb2[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO]), rem_info = nla_len(tb2[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO]); nla_ok(tb_iface, rem_info);tb_iface = nla_next(tb_iface, &(rem_info))) { struct nlattr *tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX+ 1]; wifi_iface_info miface_info; nla_parse(tb3, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX, (struct nlattr *) nla_data(tb_iface), nla_len(tb_iface), NULL); if (!tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX" " NOT FOUND", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; goto cleanup; } if (if_indextoname(nla_get_u32(tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX]), miface_info.iface_name) == NULL) { ALOGE("%s: Failed to convert %d IFINDEX to IFNAME", __FUNCTION__, nla_get_u32(tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX])); } ALOGV("ifname[%d]: %s ", num_of_iface, miface_info.iface_name); if (!tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ" " NOT FOUND", __FUNCTION__); ret = WIFI_ERROR_INVALID_ARGS; goto cleanup; } miface_info.channel = nla_get_u32(tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ]); ALOGV("channel[%d]: %d ", num_of_iface, miface_info.channel); if (!num_of_iface) mode_info.iface_info = (wifi_iface_info *) malloc(sizeof(wifi_iface_info)); else mode_info.iface_info = (wifi_iface_info *) realloc(mode_info.iface_info, (num_of_iface + 1) * sizeof(wifi_iface_info)); if (mode_info.iface_info != NULL) { memcpy(&mode_info.iface_info[num_of_iface], &miface_info, sizeof(wifi_iface_info)); num_of_iface++; mode_info.num_iface = num_of_iface; } } } if (!num_of_mac) mwifi_iface_mac_info = (wifi_mac_info *) malloc(sizeof(wifi_mac_info)); else mwifi_iface_mac_info = (wifi_mac_info *) realloc(mwifi_iface_mac_info, (num_of_mac + 1) * (sizeof(wifi_mac_info))); if (mwifi_iface_mac_info != NULL) { memcpy(&mwifi_iface_mac_info[num_of_mac], &mode_info, sizeof(wifi_mac_info)); num_of_mac++; } } } if (mHandler.on_radio_mode_change && num_of_mac) { (*mHandler.on_radio_mode_change)(mreqId, num_of_mac, mwifi_iface_mac_info); } else { ALOGE("No Callback registered: on radio mode change"); ret = WIFI_ERROR_UNKNOWN; goto cleanup; } ret = WIFI_SUCCESS; } break; default: /* Error case should not happen print log */ ALOGE("%s: Wrong subcmd received %d", __FUNCTION__, mSubcmd); } cleanup: if (mode_info.iface_info != NULL) { free(mode_info.iface_info); mode_info.iface_info = NULL; } if (mwifi_iface_mac_info != NULL) { free(mwifi_iface_mac_info); mwifi_iface_mac_info = NULL; } return ret; } wifi_error wifi_set_radio_mode_change_handler(wifi_request_id id, wifi_interface_handle iface, wifi_radio_mode_change_handler eh) { wifi_error ret; WifiVendorCommand *vCommand = NULL; wifi_handle wifiHandle = getWifiHandle(iface); RADIOModeCommand *radiomodeCommand; ret = initialize_vendor_cmd(iface, id, QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO, &vCommand); if (ret != WIFI_SUCCESS) { ALOGE("%s: Initialization failed", __FUNCTION__); return ret; } radiomodeCommand = RADIOModeCommand::instance(wifiHandle, id); if (radiomodeCommand == NULL) { ALOGE("%s: Error RadioModeCommand NULL", __FUNCTION__); ret = WIFI_ERROR_OUT_OF_MEMORY; goto cleanup; } radiomodeCommand->setCallbackHandler(eh); radiomodeCommand->setReqId(id); cleanup: delete vCommand; return mapKernelErrortoWifiHalError(ret); }