| /* 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 ret = 0; |
| 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_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); |
| } |
| |
| 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) |
| { |
| 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; |
| 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; |
| } |