diff options
Diffstat (limited to 'wcn6740/qcwcn/wifi_hal/common.cpp')
-rw-r--r-- | wcn6740/qcwcn/wifi_hal/common.cpp | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/wcn6740/qcwcn/wifi_hal/common.cpp b/wcn6740/qcwcn/wifi_hal/common.cpp new file mode 100644 index 0000000..263043b --- /dev/null +++ b/wcn6740/qcwcn/wifi_hal/common.cpp @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <linux/pkt_sched.h> +#include <linux-private/linux/fib_rules.h> +#include <netlink/object-api.h> +#include <netlink-private/object-api.h> +#include <netlink-private/types.h> +#include <dlfcn.h> +#include <pthread.h> +#include "wifi_hal.h" +#include "common.h" +#include <errno.h> + +interface_info *getIfaceInfo(wifi_interface_handle handle) +{ + return (interface_info *)handle; +} + +wifi_handle getWifiHandle(wifi_interface_handle handle) +{ + return getIfaceInfo(handle)->handle; +} + +hal_info *getHalInfo(wifi_handle handle) +{ + return (hal_info *)handle; +} + +hal_info *getHalInfo(wifi_interface_handle handle) +{ + return getHalInfo(getWifiHandle(handle)); +} + +wifi_handle getWifiHandle(hal_info *info) +{ + return (wifi_handle)info; +} + +wifi_interface_handle getIfaceHandle(interface_info *info) +{ + return (wifi_interface_handle)info; +} + +wifi_error wifi_register_handler(wifi_handle handle, int cmd, nl_recvmsg_msg_cb_t func, void *arg) +{ + hal_info *info = (hal_info *)handle; + + pthread_mutex_lock(&info->cb_lock); + + wifi_error result = WIFI_ERROR_OUT_OF_MEMORY; + + for (int i = 0; i < info->num_event_cb; i++) { + if(info->event_cb[i].nl_cmd == cmd && + info->event_cb[i].cb_arg == arg) { + info->event_cb[i].cb_func = func; + ALOGV("Updated event handler %p for nl_cmd 0x%0x" + " and arg %p", func, cmd, arg); + pthread_mutex_unlock(&info->cb_lock); + return WIFI_SUCCESS; + } + } + + if (info->num_event_cb < info->alloc_event_cb) { + info->event_cb[info->num_event_cb].nl_cmd = cmd; + info->event_cb[info->num_event_cb].vendor_id = 0; + info->event_cb[info->num_event_cb].vendor_subcmd = 0; + info->event_cb[info->num_event_cb].cb_func = func; + info->event_cb[info->num_event_cb].cb_arg = arg; + info->num_event_cb++; + ALOGV("Successfully added event handler %p for command %d", func, cmd); + result = WIFI_SUCCESS; + } else { + result = WIFI_ERROR_OUT_OF_MEMORY; + } + + pthread_mutex_unlock(&info->cb_lock); + return result; +} + +wifi_error wifi_register_vendor_handler(wifi_handle handle, + uint32_t id, int subcmd, nl_recvmsg_msg_cb_t func, void *arg) +{ + hal_info *info = (hal_info *)handle; + + pthread_mutex_lock(&info->cb_lock); + + wifi_error result = WIFI_ERROR_OUT_OF_MEMORY; + + for (int i = 0; i < info->num_event_cb; i++) { + if(info->event_cb[i].vendor_id == id && + info->event_cb[i].vendor_subcmd == subcmd) + { + info->event_cb[i].cb_func = func; + info->event_cb[i].cb_arg = arg; + ALOGV("Updated event handler %p for vendor 0x%0x, subcmd 0x%0x" + " and arg %p", func, id, subcmd, arg); + pthread_mutex_unlock(&info->cb_lock); + return WIFI_SUCCESS; + } + } + + if (info->num_event_cb < info->alloc_event_cb) { + info->event_cb[info->num_event_cb].nl_cmd = NL80211_CMD_VENDOR; + info->event_cb[info->num_event_cb].vendor_id = id; + info->event_cb[info->num_event_cb].vendor_subcmd = subcmd; + info->event_cb[info->num_event_cb].cb_func = func; + info->event_cb[info->num_event_cb].cb_arg = arg; + info->num_event_cb++; + ALOGV("Added event handler %p for vendor 0x%0x, subcmd 0x%0x and arg" + " %p", func, id, subcmd, arg); + result = WIFI_SUCCESS; + } else { + result = WIFI_ERROR_OUT_OF_MEMORY; + } + + pthread_mutex_unlock(&info->cb_lock); + return result; +} + +void wifi_unregister_handler(wifi_handle handle, int cmd) +{ + hal_info *info = (hal_info *)handle; + + if (cmd == NL80211_CMD_VENDOR) { + ALOGE("Must use wifi_unregister_vendor_handler to remove vendor handlers"); + return; + } + + pthread_mutex_lock(&info->cb_lock); + + for (int i = 0; i < info->num_event_cb; i++) { + if (info->event_cb[i].nl_cmd == cmd) { + if(i < info->num_event_cb-1) { + /* No need to memmove if only one entry exist and deleting + * the same, as the num_event_cb will become 0 in this case. + */ + memmove(&info->event_cb[i], &info->event_cb[i+1], + (info->num_event_cb - i) * sizeof(cb_info)); + } + info->num_event_cb--; + ALOGV("Successfully removed event handler for command %d", cmd); + break; + } + } + + pthread_mutex_unlock(&info->cb_lock); +} + +void wifi_unregister_vendor_handler(wifi_handle handle, uint32_t id, int subcmd) +{ + hal_info *info = (hal_info *)handle; + + pthread_mutex_lock(&info->cb_lock); + + for (int i = 0; i < info->num_event_cb; i++) { + + if (info->event_cb[i].nl_cmd == NL80211_CMD_VENDOR + && info->event_cb[i].vendor_id == id + && info->event_cb[i].vendor_subcmd == subcmd) { + if(i < info->num_event_cb-1) { + /* No need to memmove if only one entry exist and deleting + * the same, as the num_event_cb will become 0 in this case. + */ + memmove(&info->event_cb[i], &info->event_cb[i+1], + (info->num_event_cb - i) * sizeof(cb_info)); + } + info->num_event_cb--; + ALOGV("Successfully removed event handler for vendor 0x%0x", id); + break; + } + } + + pthread_mutex_unlock(&info->cb_lock); +} + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +void hexdump(void *buf, u16 len) +{ + int i=0; + char *bytes = (char *)buf; + + if (len) { + ALOGV("******HexDump len:%d*********", len); + for (i = 0; ((i + 7) < len); i+=8) { + ALOGV("%02x %02x %02x %02x %02x %02x %02x %02x", + bytes[i], bytes[i+1], + bytes[i+2], bytes[i+3], + bytes[i+4], bytes[i+5], + bytes[i+6], bytes[i+7]); + } + if ((len - i) >= 4) { + ALOGV("%02x %02x %02x %02x", + bytes[i], bytes[i+1], + bytes[i+2], bytes[i+3]); + i+=4; + } + for (;i < len;i++) { + ALOGV("%02x", bytes[i]); + } + ALOGV("******HexDump End***********"); + } else { + return; + } +} + +/* Firmware sends RSSI value without noise floor. + * Add noise floor to the same and return absolute values. + */ +u8 get_rssi(u8 rssi_wo_noise_floor) +{ + return abs((int)rssi_wo_noise_floor - 96); +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/* Pointer to the table of LOWI callback funcs */ +lowi_cb_table_t *LowiWifiHalApi = NULL; +/* LowiSupportedCapabilities read */ +u32 lowiSupportedCapabilities = 0; + +int compareLowiVersion(u16 major, u16 minor, u16 micro) +{ + u32 currVersion = 0x10000*(WIFIHAL_LOWI_MAJOR_VERSION) + \ + 0x100*(WIFIHAL_LOWI_MINOR_VERSION) + \ + WIFIHAL_LOWI_MICRO_VERSION; + + u32 lowiVersion = 0x10000*(major) + \ + 0x100*(minor) + \ + micro; + + return (memcmp(&currVersion, &lowiVersion, sizeof(u32))); +} + +/* + * This function will open the lowi shared library and obtain the + * Lowi Callback table and the capabilities supported. + * A version check is also performed in this function and if the version + * check fails then the callback table returned will be NULL. + */ +wifi_error fetchLowiCbTableAndCapabilities(lowi_cb_table_t **lowi_wifihal_api, + bool *lowi_get_capa_supported) +{ + getCbTable_t* lowiCbTable = NULL; + int ret = 0; + wifi_error retVal = WIFI_SUCCESS; + + *lowi_wifihal_api = NULL; + *lowi_get_capa_supported = false; + +#if __WORDSIZE == 64 + void* lowi_handle = dlopen("/vendor/lib64/liblowi_wifihal.so", RTLD_NOW); +#else + void* lowi_handle = dlopen("/vendor/lib/liblowi_wifihal.so", RTLD_NOW); +#endif + if (!lowi_handle) { + ALOGE("%s: NULL lowi_handle, err: %s", __FUNCTION__, dlerror()); + return WIFI_ERROR_UNKNOWN; + } + + lowiCbTable = (getCbTable_t*)dlsym(lowi_handle, + "lowi_wifihal_get_cb_table"); + if (!lowiCbTable) { + ALOGE("%s: NULL lowi callback table", __FUNCTION__); + return WIFI_ERROR_UNKNOWN; + } + + *lowi_wifihal_api = lowiCbTable(); + + /* First check whether lowi module implements the get_lowi_version + * function. All the functions in lowi module starts with + * "lowi_wifihal_" prefix thus the below function name. + */ + if ((dlsym(lowi_handle, "lowi_wifihal_get_lowi_version") != NULL) && + ((*lowi_wifihal_api)->get_lowi_version != NULL)) { + u16 lowiMajorVersion = WIFIHAL_LOWI_MAJOR_VERSION; + u16 lowiMinorVersion = WIFIHAL_LOWI_MINOR_VERSION; + u16 lowiMicroVersion = WIFIHAL_LOWI_MICRO_VERSION; + int versionCheck = -1; + + ret = (*lowi_wifihal_api)->get_lowi_version(&lowiMajorVersion, + &lowiMinorVersion, + &lowiMicroVersion); + if (ret) { + ALOGE("%s: get_lowi_version returned error:%d", + __FUNCTION__, ret); + retVal = WIFI_ERROR_NOT_SUPPORTED; + goto cleanup; + } + ALOGV("%s: Lowi version:%d.%d.%d", __FUNCTION__, + lowiMajorVersion, lowiMinorVersion, + lowiMicroVersion); + + /* Compare the version with version in wifihal_internal.h */ + versionCheck = compareLowiVersion(lowiMajorVersion, + lowiMinorVersion, + lowiMicroVersion); + if (versionCheck < 0) { + ALOGE("%s: Version Check failed:%d", __FUNCTION__, + versionCheck); + retVal = WIFI_ERROR_NOT_SUPPORTED; + goto cleanup; + } + } + else { + ALOGV("%s: lowi_wifihal_get_lowi_version not present", + __FUNCTION__); + } + + + /* Check if get_lowi_capabilities func pointer exists in + * the lowi lib and populate lowi_get_capa_supported + * All the functions in lowi modules starts with + * "lowi_wifihal_ prefix" thus the below function name. + */ + if (dlsym(lowi_handle, "lowi_wifihal_get_lowi_capabilities") != NULL) { + *lowi_get_capa_supported = true; + } + else { + ALOGV("lowi_wifihal_get_lowi_capabilities() is not supported."); + *lowi_get_capa_supported = false; + } +cleanup: + if (retVal) { + *lowi_wifihal_api = NULL; + } + return retVal; +} + +lowi_cb_table_t *getLowiCallbackTable(u32 requested_lowi_capabilities) +{ + int ret = WIFI_SUCCESS; + bool lowi_get_capabilities_support = false; + + if (LowiWifiHalApi == NULL) { + ALOGV("%s: LowiWifiHalApi Null, Initialize Lowi", + __FUNCTION__); + ret = fetchLowiCbTableAndCapabilities(&LowiWifiHalApi, + &lowi_get_capabilities_support); + if (ret != WIFI_SUCCESS || LowiWifiHalApi == NULL || + LowiWifiHalApi->init == NULL) { + ALOGE("%s: LOWI is not supported.", __FUNCTION__); + goto cleanup; + } + /* Initialize LOWI if it isn't up already. */ + ret = LowiWifiHalApi->init(); + if (ret) { + ALOGE("%s: failed lowi initialization. " + "Returned error:%d. Exit.", __FUNCTION__, ret); + goto cleanup; + } + if (!lowi_get_capabilities_support || + LowiWifiHalApi->get_lowi_capabilities == NULL) { + ALOGV("%s: Allow rtt APIs thru LOWI to proceed even though " + "get_lowi_capabilities() is not supported. Returning", + __FUNCTION__); + lowiSupportedCapabilities |= + (ONE_SIDED_RANGING_SUPPORTED|DUAL_SIDED_RANGING_SUPPORED); + return LowiWifiHalApi; + } + ret = + LowiWifiHalApi->get_lowi_capabilities(&lowiSupportedCapabilities); + if (ret) { + ALOGV("%s: failed to get lowi supported capabilities." + "Returned error:%d. Exit.", __FUNCTION__, ret); + goto cleanup; + } + } else if (lowiSupportedCapabilities == 0 && + LowiWifiHalApi->get_lowi_capabilities) { + LowiWifiHalApi->get_lowi_capabilities(&lowiSupportedCapabilities); + } + + if ((lowiSupportedCapabilities & requested_lowi_capabilities) == 0) { + return NULL; + } + return LowiWifiHalApi; + +cleanup: + if (LowiWifiHalApi && LowiWifiHalApi->destroy) { + ret = LowiWifiHalApi->destroy(); + } + LowiWifiHalApi = NULL; + lowiSupportedCapabilities = 0; + return LowiWifiHalApi; +} + +wifi_error mapKernelErrortoWifiHalError(int kern_err) +{ + if (kern_err >= 0) + return WIFI_SUCCESS; + + switch (kern_err) { + case -EOPNOTSUPP: + return WIFI_ERROR_NOT_SUPPORTED; + case -EAGAIN: + return WIFI_ERROR_NOT_AVAILABLE; + case -EINVAL: + return WIFI_ERROR_INVALID_ARGS; + case -ETIMEDOUT: + return WIFI_ERROR_TIMED_OUT; + case -ENOMEM: + return WIFI_ERROR_OUT_OF_MEMORY; + case -EBUSY: + return WIFI_ERROR_BUSY; + case -ENOBUFS: + return WIFI_ERROR_TOO_MANY_REQUESTS; + } + return WIFI_ERROR_UNKNOWN; +} |