/* * 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 <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; } } 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; } return WIFI_ERROR_UNKNOWN; }