/* Copyright (c) 2014, 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. */ #include "sync.h" #define LOG_TAG "WifiHAL" #include <utils/Log.h> #include <time.h> #include "ifaceeventhandler.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) { int i, numAp, ret = 0; interface_info *ifaceInfo = getIfaceInfo(iface); 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); cleanup: return (wifi_error)ret; } /* Reset monitoring for the NL event*/ wifi_error wifi_reset_iface_event_handler(wifi_request_id id, wifi_interface_handle iface) { int ret = 0; 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); } cleanup: return (wifi_error)ret; } /* 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) { int ret = WIFI_SUCCESS; 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); 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) { /* Initialize the member data variables here */ mSet = 0; mSetSizeMax = 0; mSetSizePtr = NULL; mConcurrencySet = 0; filterVersion = 0; filterLength = 0; firmware_bus_max_size = 0; } WifihalGeneric::~WifihalGeneric() { } int WifihalGeneric::requestResponse() { return WifiCommand::requestResponse(mMsg); } int WifihalGeneric::handleResponse(WifiEvent &reply) { ALOGV("Got a Wi-Fi HAL module message from Driver"); int i = 0; u32 status; 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 WIFI_ERROR_INVALID_ARGS; } mSet = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_SET]); ALOGV("Supported feature set : %x", mSet); 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("%x", *(mConcurrencySet + i)); } } } break; case QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER: { 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_VERSION]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION" " not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } 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_TOTAL_LENGTH]) { ALOGE("%s: QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH" " not found", __FUNCTION__); return WIFI_ERROR_INVALID_ARGS; } filterLength = nla_get_u32( tb_vendor[QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_TOTAL_LENGTH]); ALOGV("Max filter length Supported : %u", filterLength); } 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 WIFI_ERROR_INVALID_ARGS; } 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; default : ALOGE("%s: Wrong Wi-Fi HAL event received %d", __func__, mSubcmd); } return NL_SKIP; } void WifihalGeneric::getResponseparams(feature_set *pset) { *pset = mSet; } void WifihalGeneric::setMaxSetSize(int set_size_max) { mSetSizeMax = set_size_max; } void WifihalGeneric::setConcurrencySet(feature_set set[]) { mConcurrencySet = set; } void WifihalGeneric::setSizePtr(int *set_size) { mSetSizePtr = set_size; } int WifihalGeneric::getFilterVersion() { return filterVersion; } int WifihalGeneric::getFilterLength() { return filterLength; } int WifihalGeneric::getBusSize() { return firmware_bus_max_size; }